From c8a817277a29ce9a4d6fd7a581ffd7a322674582 Mon Sep 17 00:00:00 2001 From: Ivan Grachyov Date: Sat, 23 Jul 2022 23:16:03 +0300 Subject: [PATCH 01/10] Implemented basic functionality for applyforce suspension. Works fine? --- koptilnya/suspension/main.txt | 99 +++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 koptilnya/suspension/main.txt diff --git a/koptilnya/suspension/main.txt b/koptilnya/suspension/main.txt new file mode 100644 index 0000000..e113dd3 --- /dev/null +++ b/koptilnya/suspension/main.txt @@ -0,0 +1,99 @@ +--@name apply force suspension +--@author Koptilnya1337 +--@shared + + local _restLength = 26.0 + local _springTravel = 20.0 + local _springStiffness = 35000 + local _compressionDamper = 140000 -- 45000 + local _reboundDamper = 35000 -- 196000 + local _maxForce = 500000 + + local _minLength = 0 + local _maxLength = 0 + local _lastLength = 0 + local _springLength = 0 + local _springVelocity = 0 + local _springForce = 0 + local _damperForce = 0 + +if SERVER then + wire.adjustInputs( + { + 'Wheel1', + 'Wheel2', + 'Wheel3', + 'Wheel4', + 'Strut1', + 'Strut2', + 'Strut3', + 'Strut4', + 'Base' + }, + { + 'entity', + 'entity', + 'entity', + 'entity', + 'entity', + 'entity', + 'entity', + 'entity', + 'entity' + } + ) + + local wheel1 = wire.ports.Wheel1 + local wheel2 = wire.ports.Wheel2 + local wheel3 = wire.ports.Wheel3 + local wheel4 = wire.ports.Wheel4 + + local strut1 = wire.ports.Strut1 + local strut2 = wire.ports.Strut2 + local strut3 = wire.ports.Strut3 + local strut4 = wire.ports.Strut4 + + local wheels = { wheel1, wheel2, wheel3, wheel4 } + local struts = { strut1, strut2, strut3, strut4 } + + local base = wire.ports.Base + + function handleWheel(base, wheel, strut) + local lastLength = 0 + + return function () + if not base:isValid() then return end + if not wheel:isValid() then return end + if not strut:isValid() then return end + + local distance = strut:getPos():getDistance(wheel:getPos()) - _restLength + local strutToWheelDirection = (strut:getPos() - wheel:getPos()):setX(0):setY(0):getNormalized() + local springVelocity = (distance - lastLength) + + local usedDamper = springVelocity > 0 and _compressionDamper or _reboundDamper + local spring = distance * _springStiffness + local dampener = springVelocity * usedDamper + + local force = math.clamp((spring + dampener) * game.getTickInterval(), -_maxForce, _maxForce) + local direction = (strut:getPos() - wheel:getPos()):getNormalized() --base:getUp() + + lastLength = distance + + base:applyForceOffset(-force * direction, wheel:getPos()) + wheel:applyForceCenter(force * direction * strutToWheelDirection) + end + end + + local wheelHandlers = {} + + for idx, wheel in ipairs(wheels) do + table.insert(wheelHandlers, handleWheel(base, wheel, struts[idx])) + end + + hook.add('tick', 'runtime', function() + for _, handler in ipairs(wheelHandlers) do + handler() + end + end) + +end \ No newline at end of file From 22fad8994e90ed3fb247137861b536bc7cee8bed Mon Sep 17 00:00:00 2001 From: Ivan Grachyov Date: Wed, 27 Jul 2022 23:38:51 +0300 Subject: [PATCH 02/10] Added model for golf mk7.5 --- koptilnya/data/models/vw_golf_mk7_5.txt | 151 ++++++++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 koptilnya/data/models/vw_golf_mk7_5.txt diff --git a/koptilnya/data/models/vw_golf_mk7_5.txt b/koptilnya/data/models/vw_golf_mk7_5.txt new file mode 100644 index 0000000..5da5e2a --- /dev/null +++ b/koptilnya/data/models/vw_golf_mk7_5.txt @@ -0,0 +1,151 @@ +-- @shared +-- @name VW Golf Mk 7.5 +-- @author Koptilnya1337 +-- @include /koptilnya/mesh_loader/builder.txt +require("/koptilnya/mesh_loader/builder.txt") + +DEBUG_MODE = true + +local LINK = "https://drive.google.com/u/0/uc?id=1FNn5_6PMmurvraVqlJNNV3WXTMnozMJM&export=download" +local SCALE = Vector(1) + +local Materials = { + { + Match = "Shadow", + Material = "", + Color = Color(20,20,20) + }, + { + Match = "BumpersVents", + Material = "models/debug/debugwhite", + Color = Color(20,20,20) + }, + { + Match = { "Body", ".*Body*" }, + Material = "phoenix_storms/fender_white", + Color = Color(230,229,231) + }, + { + Match = "Badges", + Material = "debug/env_cubemap_model", + Color = Color(255,255,255) + }, + { + Match = ".*Lights_Glass*", + Material = "phoenix_storms/mrref2", + Color = Color(255,255,255,120) + }, + { + Match = { ".*Lights_Base*", "RearLights_Chrome" }, + Material = "models/debug/debugwhite", + Color = Color(20,20,20) + }, + { + Match = { "WindowsRubber", "WindshieldOutline" }, + Material = "phoenix_storms/mrref2", + Color = Color(25,25,25) + }, + { + Match = ".*Seat.*", + Material = "models/debug/debugwhite", + Color = Color(60,60,60) + }, + { + Match = "Interior_DoorMaps", + Material = "models/debug/debugwhite", + Color = Color(40,40,40) + }, + { + Match = "Interior_Plastic", + Material = "models/debug/debugwhite", + Color = Color(50,50,50) + } +} + +local Bodyparts = { + "Body", + "Bonnet_Body", + "DoorHandles", + "BumpersVents", + "ExhaustPipes", + "ExhaustPipes", + "FrontBumper_Body", + "FrontLeftDoor_Body", + "FrontQuaterpanels_Body", + "FrontRightDoor_Body", + "FrontRightDoor_Body", + "Mirrors_Plastic", + "RearBumper_Body", + "RearLeftDoor_Body", + "RearRightDoor_Body", + "RearRightDoor_Body", + "Shadow", + "Sidepanels_Body", + "Sideskirts_Body", + "Trunk_Body", +} + +local Details = { + "Badges", +} + +local FrontLights = { + "FrontLights_Base", + -- "FrontLights_Glass", +} + +local RearLights = { + "RearLights", + "RearLights_Base", + "RearLights_Chrome", + -- "RearLights_Glass", +} + +local Windows = { + "WindowsRubber", + "WindshieldOutline", +} + +local Interior = { + "FrontLeftSeat", + "FrontRightSeat", + "RearSeats", + -- "Interior_Decals", + "Interior_DoorMaps", + "Interior_Plastic", + "SoundDynamics", + "SteeringWheel", + "SteeringWheel_Badge", + -- "SteeringWheel_Decals", + "SteeringWheel_Plastic", +} + +local Meshes = {} + +table.add(Meshes, Bodyparts) +table.add(Meshes, Details) +table.add(Meshes, FrontLights) +table.add(Meshes, RearLights) +table.add(Meshes, Windows) +table.add(Meshes, Interior) + +local builder = {} + +if SERVER then + local this = chip() + + builder = MeshBuilder:new(LINK) + :useGlobalScale(SCALE) + :useGlobalParent(this) + :useGlobalRelative(this) + + builder:buildAll(Meshes) + builder:getResult() + +else + PERMA.onPermissionsGained = function() + builder = MeshBuilder:new(LINK) + builder:useMaterialPipeline(Materials) + end + PERMA.build() +end From 98fe60a7656e0737cd53c5a7c301d504a923cb84 Mon Sep 17 00:00:00 2001 From: Ivan Grachyov Date: Thu, 28 Jul 2022 00:18:35 +0300 Subject: [PATCH 03/10] Fixed suspension breaking when car was flipped --- koptilnya/suspension/main.txt | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/koptilnya/suspension/main.txt b/koptilnya/suspension/main.txt index e113dd3..e7fde2f 100644 --- a/koptilnya/suspension/main.txt +++ b/koptilnya/suspension/main.txt @@ -4,10 +4,10 @@ local _restLength = 26.0 local _springTravel = 20.0 - local _springStiffness = 35000 - local _compressionDamper = 140000 -- 45000 - local _reboundDamper = 35000 -- 196000 - local _maxForce = 500000 + local _springStiffness = 45000 + local _compressionDamper = 250000 -- 45000 + local _reboundDamper = 150000 -- 196000 + local _maxForce = 800000 local _minLength = 0 local _maxLength = 0 @@ -70,17 +70,18 @@ if SERVER then local strutToWheelDirection = (strut:getPos() - wheel:getPos()):setX(0):setY(0):getNormalized() local springVelocity = (distance - lastLength) - local usedDamper = springVelocity > 0 and _compressionDamper or _reboundDamper + local usedDamper = springVelocity > 0 and _reboundDamper or _compressionDamper local spring = distance * _springStiffness local dampener = springVelocity * usedDamper local force = math.clamp((spring + dampener) * game.getTickInterval(), -_maxForce, _maxForce) - local direction = (strut:getPos() - wheel:getPos()):getNormalized() --base:getUp() + local direction = (strut:getPos() - wheel:getPos()):getNormalized() + local baseDirection = direction * Vector(0, 0, math.clamp(base:getUp().z, 0, 1)) lastLength = distance - base:applyForceOffset(-force * direction, wheel:getPos()) - wheel:applyForceCenter(force * direction * strutToWheelDirection) + base:applyForceOffset(-force * baseDirection, wheel:getPos()) + wheel:applyForceCenter(force * direction) end end From b884b06d44d1ab76c91793d392f72bfbf2422916 Mon Sep 17 00:00:00 2001 From: Ivan Grachyov Date: Thu, 28 Jul 2022 00:31:16 +0300 Subject: [PATCH 04/10] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8c207c7..7d65d7a 100644 --- a/README.md +++ b/README.md @@ -1 +1,2 @@ Не забудьте установить форматтер ```koihik.vscode-lua-format``` +Для WebStorm нужно поставить EmmyLua From c7933b47ace8d771509b1c1377b47abc80b04e87 Mon Sep 17 00:00:00 2001 From: Ivan Grachyov Date: Thu, 28 Jul 2022 00:31:49 +0300 Subject: [PATCH 05/10] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7d65d7a..1119fc4 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,3 @@ Не забудьте установить форматтер ```koihik.vscode-lua-format``` -Для WebStorm нужно поставить EmmyLua +
+Для WebStorm нужно поставить ```EmmyLua``` From b4dccd73a63a9b043d1958f4f28a097735b2808e Mon Sep 17 00:00:00 2001 From: Nikita Kruglickiy Date: Thu, 28 Jul 2022 15:44:31 +0300 Subject: [PATCH 06/10] Refactored vehicle components --- koptilnya/engine_remastered/clutch.txt | 101 +++--- koptilnya/engine_remastered/differential.txt | 327 +++++++----------- koptilnya/engine_remastered/engine.txt | 109 +++--- .../engine_remastered/gearboxes/base.txt | 90 ++--- .../engine_remastered/gearboxes/manual.txt | 50 ++- .../engine_remastered/novojiloff_diff.txt | 99 ------ .../powertrain_component.txt | 49 +++ koptilnya/engine_remastered/vehicle.txt | 101 +++--- koptilnya/engine_remastered/wheel.txt | 61 ++++ .../engine_remastered/wire_component.txt | 11 +- koptilnya/libs/entity.txt | 10 + 11 files changed, 485 insertions(+), 523 deletions(-) delete mode 100644 koptilnya/engine_remastered/novojiloff_diff.txt create mode 100644 koptilnya/engine_remastered/powertrain_component.txt create mode 100644 koptilnya/engine_remastered/wheel.txt diff --git a/koptilnya/engine_remastered/clutch.txt b/koptilnya/engine_remastered/clutch.txt index 9bb9ec4..e02ec6e 100644 --- a/koptilnya/engine_remastered/clutch.txt +++ b/koptilnya/engine_remastered/clutch.txt @@ -1,72 +1,67 @@ --- @include ./wire_component.txt --- @include ./enums/gearbox.txt --- @include /koptilnya/libs/constants.txt +--@include ./powertrain_component.txt +--@include ./enums/gearbox.txt +--@include /koptilnya/libs/constants.txt + require('/koptilnya/libs/constants.txt') -require('./wire_component.txt') +require('./powertrain_component.txt') local gearboxTypes = require('./enums/gearbox.txt') -Clutch = class('Clutch', WireComponent) +Clutch = class('Clutch', PowertrainComponent) function Clutch:initialize(config) - self.stiffness = config.Stiffness - self.damping = config.Damping - self.maxTorque = config.MaxTorque + PowertrainComponent.initialize(self, 'Clutch') - self.press = 0 - self.slip = 0 - self.targetTorque = 0 - self.torque = 0 - - self.engine = nil - self.gearbox = nil -end - -function Clutch:linkEngine(eng) - self.engine = eng -end - -function Clutch:linkGearbox(gbox) - self.gearbox = gbox -end - -function Clutch:getInputs() - if self.gearbox ~= nil and self.gearbox.type == gearboxTypes.MANUAL then - return { - Clutch = 'number' - } - else - return {} - end -end - -function Clutch:getOutputs() - return { - Clutch_Torque = 'number', - Clutch_Slip = 'number' + self.wireInputs = { + Clutch = 'number' } + self.wireOutputs = { + Clutch_Torque = 'number' + } + + self.inertia = config.Inertia or 0.1 + self.slipTorque = config.SlipTorque or 1000 end -function Clutch:updateOutputs() +function Clutch:updateWireOutputs() wire.ports.Clutch_Torque = self.torque - wire.ports.Clutch_Slip = self.slip end function Clutch:getPress() - if self.gearbox ~= nil and self.gearbox.type == gearboxTypes.MANUAL then - return wire.ports.Clutch - else - return self.press + return 1 - wire.ports.Clutch +end + +function Clutch:queryInertia() + if self.output == nil then + return self.inertia end + + return self.inertia + self.output:queryInertia() * self:getPress() end -function Clutch:update() - local engRPM = self.engine and self.engine.rpm or 0 - local gboxRPM = self.gearbox and self.gearbox.rpm or 0 - local gboxRatio = self.gearbox and self.gearbox.ratio or 0 - local gboxRatioNotZero = gboxRatio ~= 0 and 1 or 0 +function Clutch:queryAngularVelocity(angularVelocity) + self.angularVelocity = angularVelocity - self.slip = ((engRPM - gboxRPM) * RPM_TO_RAD) * gboxRatioNotZero / 2 - self.targetTorque = math.clamp(self.slip * self.stiffness * (1 - self:getPress()), -self.maxTorque, self.maxTorque) + if self.output == nil then + return angularVelocity + end - self.torque = math.lerp(self.damping, self.torque, self.targetTorque) + local outputW = self.output:queryAngularVelocity(angularVelocity) * self:getPress() + local inputW = angularVelocity * (1 - self:getPress()) + + return outputW + inputW +end + +function Clutch:forwardStep(torque, inertia) + if self.output == nil then + return torque + end + + local press = self:getPress() + + self.torque = math.clamp(torque, -self.slipTorque, self.slipTorque) + self.torque = self.torque * (1 - (1 - math.pow(press, 0.3))) + + local returnTorque = self.output:forwardStep(self.torque, inertia * press + self.inertia) * press + + return math.clamp(returnTorque, -self.slipTorque, self.slipTorque) end diff --git a/koptilnya/engine_remastered/differential.txt b/koptilnya/engine_remastered/differential.txt index d92361f..e1d8dea 100644 --- a/koptilnya/engine_remastered/differential.txt +++ b/koptilnya/engine_remastered/differential.txt @@ -1,207 +1,148 @@ --- @server --- @include /koptilnya/libs/constants.txt --- @include ./wire_component.txt -require('/koptilnya/libs/constants.txt') -require('./wire_component.txt') +--@include /koptilnya/libs/constants.txt +--@include ./powertrain_component.txt +--@include ./wheel.txt -Differential = class('Differential', WireComponent) +require('/koptilnya/libs/constants.txt') +require('./powertrain_component.txt') +require('./wheel.txt') + +Differential = class('Differential', PowertrainComponent) function Differential:initialize(config, order) - self.power = config.Power or 1 - self.coast = config.Coast or 1 - self.finalDrive = config.FinalDrive or 1 + PowertrainComponent.initialize(self, 'Axle') + + local prefix = 'Axle' .. (order or '') .. '_' + + self.finalDrive = config.FinalDrive or 4 + self.biasAB = config.Bias or 0.5 + self.coastRamp = config.CoastRamp or 0.5 + self.powerRamp = config.PowerRamp or 1 self.preload = config.Preload or 10 - self.order = order or 1 + self.slipTorque = config.SlipTorque or 1000 - self.viscousCoeff = config.ViscousCoeff or 0.9 + local wheelConfig = { + CalculateInertia = true + } + self.outputA = Wheel:new(wheelConfig, prefix .. 'Left') + self.outputB = Wheel:new(wheelConfig, prefix .. 'Right') - self.distributionCoeff = config.DistributionCoeff or 1 - self.maxCorrectingTorque = config.MaxCorrectingTorque or 200 - - self.avgRPM = 0 - - self.prefix = 'Axle' .. self.order .. '_' - - self.leftWheel = wire.ports[self.prefix .. 'LeftWheel'] - self.rightWheel = wire.ports[self.prefix .. 'RightWheel'] - - self.lwPowerCoeff = 0 - self.rwPowerCoeff = 0 - - self.lwtq_div_rwtq = 0 - self.rwtq_div_lwtq = 0 - - self.lwtq = 0 - self.rwtq = 0 - - self.lwav = 0 - self.rwav = 0 - - hook.add('input', 'wire_axle_update' .. self.order, function(key, val) - if key == self.prefix .. 'LeftWheel' then - self.leftWheel = val - end - - if key == self.prefix .. 'RightWheel' then - self.rightWheel = val - end - end) + table.merge(self.wireInputs, self.outputA.wireInputs) + table.merge(self.wireInputs, self.outputB.wireInputs) + table.merge(self.wireOutputs, self.outputA.wireOutputs) + table.merge(self.wireOutputs, self.outputB.wireOutputs) end -function Differential:getInputs() - local inputs = {} +function Differential:updateWireOutputs() + if self.outputA ~= nil then + self.outputA:updateWireOutputs() + end - inputs[self.prefix .. 'LeftWheel'] = 'entity' - inputs[self.prefix .. 'RightWheel'] = 'entity' - - return inputs -end - -function Differential:getOutputs() - local outputs = {} - - outputs[self.prefix .. 'LWPowerCoeff'] = 'number' - outputs[self.prefix .. 'RWPowerCoeff'] = 'number' - outputs[self.prefix .. 'LWTq'] = 'number' - outputs[self.prefix .. 'RWTq'] = 'number' - outputs[self.prefix .. 'LWAV'] = 'number' - outputs[self.prefix .. 'RWAV'] = 'number' - outputs[self.prefix .. 'AvgRPM'] = 'number' - outputs[self.prefix .. 'LW_div_RW'] = 'number' - outputs[self.prefix .. 'RW_div_LW'] = 'number' - -- outputs[self.prefix .. 'SGAV'] = 'number' - - return outputs -end - -function Differential:updateOutputs() - wire.ports[self.prefix .. 'LWPowerCoeff'] = self.lwPowerCoeff - wire.ports[self.prefix .. 'RWPowerCoeff'] = self.rwPowerCoeff - wire.ports[self.prefix .. 'LWTq'] = self.lwtq - wire.ports[self.prefix .. 'RWTq'] = self.rwtq - wire.ports[self.prefix .. 'RWTq'] = self.rwtq - wire.ports[self.prefix .. 'LWAV'] = self.lwav - wire.ports[self.prefix .. 'RWAV'] = self.rwav - wire.ports[self.prefix .. 'AvgRPM'] = self.avgRPM - wire.ports[self.prefix .. 'LW_div_RW'] = self.lwtq_div_rwtq - wire.ports[self.prefix .. 'RW_div_LW'] = self.rwtq_div_lwtq - -- wire.ports[self.prefix .. 'SGAV'] = self.sgav -end - -function Differential:linkGearbox(gbox) - self.gearbox = gbox -end - --- this is a test function that can spin wheels in different directions -function Differential:testUpdate() - if isValid(self.leftWheel) and isValid(self.rightWheel) then - local lwav = math.rad(self.leftWheel:getAngleVelocity()[2]) - local rwav = math.rad(self.rightWheel:getAngleVelocity()[2]) - - self.avgRPM = (lwav - rwav) / 2 * RAD_TO_RPM * self.finalDrive - - self.lwav = lwav - self.rwav = rwav - - -- spider gear angular velocity - local sgav = (lwav + rwav) / 2 - - self.sgav = sgav - - -- RAV = relative angular velocity - local lwrav = lwav - sgav - local rwrav = rwav - sgav - - local lwInertia = self.leftWheel:getInertia():getLength() - local rwInertia = self.rightWheel:getInertia():getLength() - - local lwtq = lwrav * lwInertia * 2 - local rwtq = rwrav * rwInertia * 2 - - self.lwtq = lwtq - self.rwtq = rwtq - - local lwDampingTorque = self.viscousCoeff * lwtq - local rwDampingTorque = self.viscousCoeff * rwtq - - local gboxTorque = self.gearbox and self.gearbox.torque / 10 or 0 - - local usedCoeff = gboxTorque > self.preload and self.power or self.coast - - local lwCorrectingTorque = math.clamp(lwDampingTorque * usedCoeff, -self.maxCorrectingTorque, - self.maxCorrectingTorque) - local rwCorrectingTorque = math.clamp(rwDampingTorque * usedCoeff, -self.maxCorrectingTorque, - self.maxCorrectingTorque) - - local driveTorque = gboxTorque * self.distributionCoeff * 52.49 * self.finalDrive - - local lwCorrectedTorque = lwCorrectingTorque * 600 - driveTorque - local rwCorrectedTorque = rwCorrectingTorque * 600 + driveTorque - - self.leftWheel:applyTorque(lwCorrectedTorque * self.leftWheel:getRight() * TICK_INTERVAL) - self.rightWheel:applyTorque(rwCorrectedTorque * self.rightWheel:getRight() * TICK_INTERVAL) + if self.outputB ~= nil then + self.outputB:updateWireOutputs() end end -function Differential:update() - if isValid(self.leftWheel) and isValid(self.rightWheel) then - local lwav = math.rad(self.leftWheel:getAngleVelocity()[2]) - local rwav = math.rad(self.rightWheel:getAngleVelocity()[2]) * -1 - - self.lwav = lwav - self.rwav = rwav - - -- RAV = relative anglar velocity - local lwrav = rwav - lwav - local rwrav = lwav - rwav - - local lwInertia = self.leftWheel:getInertia()[2] - local rwInertia = self.rightWheel:getInertia()[2] - - local lwtq = lwrav * lwInertia - local rwtq = rwrav * rwInertia - - self.lwtq = lwtq - self.rwtq = rwtq - - -- Whether use diff power or diff coast - local usePower = self.gearbox and self.gearbox.torque > self.preload and false - local usedCoeff = usePower and self.power or self.coast - - -- Calculating damping torque here - local lwDampingTorque = self.viscousCoeff * lwtq * usedCoeff - local rwDampingTorque = self.viscousCoeff * rwtq * usedCoeff - - local lwav_abs = math.abs(lwav) - local rwav_abs = math.abs(rwav) - - local avg_abs = lwav_abs + rwav_abs - - local lwav_to_avg = avg_abs ~= 0 and 2 * lwav_abs / avg_abs or 0 - local rwav_to_avg = avg_abs ~= 0 and 2 * rwav_abs / avg_abs or 0 - - self.lwtq_div_rwtq = lwav_to_avg - self.rwtq_div_lwtq = rwav_to_avg - - local lwPowerCoeff = math.clamp(lwav_to_avg, usedCoeff, 1 + (1 - usedCoeff)) - local rwPowerCoeff = math.clamp(rwav_to_avg, usedCoeff, 1 + (1 - usedCoeff)) - - self.lwPowerCoeff = lwPowerCoeff - self.rwPowerCoeff = rwPowerCoeff - - local lwCorrectingTorque = math.clamp(lwDampingTorque, -self.maxCorrectingTorque, self.maxCorrectingTorque) - local rwCorrectingTorque = math.clamp(rwDampingTorque, -self.maxCorrectingTorque, self.maxCorrectingTorque) - - local gboxTorque = self.gearbox and self.gearbox.torque or 0 - - local driveTorque = gboxTorque / 2 * self.distributionCoeff * self.finalDrive - - local lwCorrectedTorque = lwCorrectingTorque * 20 + driveTorque * lwPowerCoeff -- * lwPowerCoeff - local rwCorrectedTorque = rwCorrectingTorque * 20 + driveTorque * rwPowerCoeff -- * rwPowerCoeff - - self.leftWheel:applyTorque(lwCorrectedTorque * -self.leftWheel:getRight()) - self.rightWheel:applyTorque(rwCorrectedTorque * self.rightWheel:getRight()) - - self.avgRPM = (lwav + rwav) / 2 * RAD_TO_RPM * self.finalDrive +function Differential:queryInertia() + if self.outputA == nil or self.outputB == nil then + return self.inertia end + + return self.inertia + self.outputA:queryInertia() + self.outputB:queryInertia() +end + +function Differential:queryAngularVelocity(angularVelocity) + self.angularVelocity = angularVelocity + + if self.outputA == nil or self.outputB == nil then + return angularVelocity + end + + local aW = self.outputA:queryAngularVelocity(angularVelocity) + local bW = self.outputB:queryAngularVelocity(angularVelocity) + + return (aW - bW) * self.finalDrive * 0.5 +end + +function Differential:forwardStep(torque, inertia) + if self.outputA == nil or self.outputB == nil then + return torque + end + + local aW = self.outputA:queryAngularVelocity(self.angularVelocity) + local bW = -self.outputB:queryAngularVelocity(self.angularVelocity) + local aI = self.outputA:queryInertia() + local bI = self.outputB:queryInertia() + + local tqA, tqB = self:_openDiffTorqueSplit( + torque * self.finalDrive, + aW, + bW, + aI, + bI, + self.biasAB, + self.preload, + self.stiffness, + self.powerRamp, + self.coastRamp, + self.slipTorque + ) + + --tqA = tqA * aI * TICK_INTERVAL + --tqB = tqB * bI * TICK_INTERVAL + + tqA = self.outputA:forwardStep(tqA, inertia * 0.5 + aI) + tqB = self.outputB:forwardStep(tqB, inertia * 0.5 + bI) + + return tqA + tqB +end + +function Differential:_openDiffTorqueSplit(tq, aW, bW, aI, bI, biasAB, preload, stiffness, powerRamp, coastRamp, slipTorque) + return tq * (1 - biasAB), tq * biasAB; +end + +function Differential:_lockingDiffTorqueSplit(tq, aW, bW, aI, bI, biasAB, preload, stiffness, powerRamp, coastRamp, slipTorque) + local sumI = aI + bI + local w = aI / sumI * aW + bI / sumI * bW + local aTqCorr = (w - aW) * aI -- / dt + aTqCorr = aTqCorr * stiffness + + local bTqCorr = (w - bW) * bI -- / dt + bTqCorr = bTqCorr * stiffness + + local biasA = clamp(0.5 + (bW - aW) * 0.1 * stiffness, 0, 1) + + return tq * biasA + aTqCorr, tq * (1 - biasA) * bTqCorr +end + +function Differential:_VLSDTorqueSplit(tq, aW, bW, aI, bI, biasAB, preload, stiffness, powerRamp, coastRamp, slipTorque) + if aW < 0 or bW < 0 then + return tq * (1 - biasAB), tq * biasAB + end + + local c = tq > 0 and powerRamp or coastRamp + local totalW = math.abs(aW) + math.abs(bW) + local slip = 0 + if aW > 0 or bW > 0 then + slip = (aW - bW) / totalW + end + local dTq = slip * stiffness * c * slipTorque + + return tq * (1 - biasAB) - dTq, tq * biasAB + dTq +end + +function Differential:_HLSDTorqueSplit(tq, aW, bW, aI, bI, biasAB, preload, stiffness, powerRamp, coastRamp, slipTorque) + if aW < 0 or bW < 0 then + return tq * (1 - biasAB), tq * biasAB + end + + local c = tq > 0 and powerRamp or coastRamp + local tqFactor = math.clamp(math.abs(tq) / slipTorque, -1, 1) + local totalW = math.abs(aW) + math.abs(bW) + local slip = 0 + if aW > 0 or bW > 0 then + slip = (aW - bW) / totalW + end + local dTq = slip * stiffness * c * slipTorque * tqFactor + + return tq * (1 - biasAB) - dTq, tq * biasAB + dTq end diff --git a/koptilnya/engine_remastered/engine.txt b/koptilnya/engine_remastered/engine.txt index 134c919..ff990a2 100644 --- a/koptilnya/engine_remastered/engine.txt +++ b/koptilnya/engine_remastered/engine.txt @@ -1,53 +1,40 @@ --- @include ./wire_component.txt --- @include /koptilnya/libs/constants.txt -require('./wire_component.txt') +--@include ./powertrain_component.txt +--@include /koptilnya/libs/constants.txt + +require('./powertrain_component.txt') require('/koptilnya/libs/constants.txt') -Engine = class('Engine', WireComponent) +Engine = class('Engine', PowertrainComponent) function Engine:initialize(config, clutch) - self.idleRPM = config.IdleRPM - self.maxRPM = config.MaxRPM + PowertrainComponent.initialize(self, 'Engine') - self.flywheelMass = config.FlywheelMass - self.flywheelRadius = config.FlywheelRadius - - self.startFriction = config.StartFriction - self.frictionCoeff = config.FrictionCoeff - - self.torque = 0 - self.rpmFrac = 0 - self.rpm = self.idleRPM - self.friction = 0 - self.masterThrottle = 0 - - self.limiterDuration = config.LimiterDuration - - self.torqueMap = config.TorqueMap or {} - - self._fwInertia = self.flywheelMass * self.flywheelRadius ^ 2 / 2 - self._limiterTime = 0 - - self.clutch = clutch - clutch:linkEngine(self) -end - -function Engine:getInputs() - return { + self.wireInputs = { Throttle = 'number' } -end - -function Engine:getOutputs() - return { + self.wireOutputs = { Engine_RPM = 'number', Engine_Torque = 'number', Engine_MasterThrottle = 'number' } + + self.idleRPM = config.IdleRPM or 900 + self.maxRPM = config.MaxRPM or 7000 + self.inertia = config.Inertia or 0.4 + self.startFriction = config.StartFriction or -50 + self.frictionCoeff = config.FrictionCoeff or 0.02 + self.limiterDuration = config.LimiterDuration or 0.05 + self.torqueMap = config.TorqueMap or {} + + self.rpmFrac = 0 + self.friction = 0 + self.masterThrottle = 0 + + self.limiterTimer = 0 end -function Engine:updateOutputs() - wire.ports.Engine_RPM = self.rpm +function Engine:updateWireOutputs() + wire.ports.Engine_RPM = self:getRPM() wire.ports.Engine_Torque = self.torque wire.ports.Engine_MasterThrottle = self.masterThrottle end @@ -56,35 +43,53 @@ function Engine:getThrottle() return wire.ports.Throttle end -function Engine:update() +function Engine:_generateTorque() local throttle = self:getThrottle() + local rpm = self:getRPM() - self.rpmFrac = math.clamp((self.rpm - self.idleRPM) / (self.maxRPM - self.idleRPM), 0, 1) - self.friction = self.startFriction - self.rpm * self.frictionCoeff + local rpmFrac = math.clamp((rpm - self.idleRPM) / (self.maxRPM - self.idleRPM), 0, 1) + self.friction = self.startFriction - self:getRPM() * self.frictionCoeff - local tqIdx = math.clamp(math.floor(self.rpmFrac * #self.torqueMap), 1, #self.torqueMap) + local tqIdx = math.clamp(math.floor(rpmFrac * #self.torqueMap), 1, #self.torqueMap) local maxInitialTorque = self.torqueMap[tqIdx] - self.friction - - local idleFadeStart = math.clamp(math.remap(self.rpm, self.idleRPM - 300, self.idleRPM, 1, 0), 0, 1) - local idleFadeEnd = math.clamp(math.remap(self.rpm, self.idleRPM, self.idleRPM + 600, 1, 0), 0, 1) - + local idleFadeStart = math.clamp(math.remap(rpm, self.idleRPM - 300, self.idleRPM, 1, 0), 0, 1) + local idleFadeEnd = math.clamp(math.remap(rpm, self.idleRPM, self.idleRPM + 600, 1, 0), 0, 1) local additionalEnergySupply = idleFadeEnd * (-self.friction / maxInitialTorque) + idleFadeStart - if self.rpm > self.maxRPM then + if rpm > self.maxRPM then throttle = 0 - self._limiterTime = timer.systime() + self.limiterTimer = timer.systime() else - throttle = timer.systime() >= self._limiterTime + self.limiterDuration and throttle or 0 + throttle = timer.systime() >= self.limiterTimer + self.limiterDuration and throttle or 0 end self.masterThrottle = math.clamp(additionalEnergySupply + throttle, 0, 1) local realInitialTorque = maxInitialTorque * self.masterThrottle - local loadTorque = self.clutch and self.clutch.torque or 0 - self.torque = realInitialTorque + self.friction - self.rpm = self.rpm + (self.torque - loadTorque) / self._fwInertia * RAD_TO_RPM * TICK_INTERVAL - self.rpm = math.max(self.rpm, 0) + return self.torque +end + +function Engine:integrateDownwards() + if self.output == nil then + local generatedTorque = self:_generateTorque() + + self.angularVelocity = self.angularVelocity + generatedTorque / self.inertia * TICK_INTERVAL + self.angularVelocity = math.max(self.angularVelocity, 0) + end + + local outputInertia = self.output:queryInertia() + local inertiaSum = self.inertia + outputInertia + local outputW = self.output:queryAngularVelocity(self.angularVelocity) + local targetW = self.inertia / inertiaSum * self.angularVelocity + outputInertia / inertiaSum * outputW + + local generatedTorque = self:_generateTorque() + local reactTorque = (targetW - self.angularVelocity) * self.inertia / TICK_INTERVAL + local returnedTorque = self.output:forwardStep(generatedTorque - reactTorque, 0) -- ??? 0 -> self.inertia + local finalTorque = generatedTorque + returnedTorque + reactTorque + + self.angularVelocity = self.angularVelocity + finalTorque / inertiaSum * TICK_INTERVAL + self.angularVelocity = math.max(self.angularVelocity, 0) end diff --git a/koptilnya/engine_remastered/gearboxes/base.txt b/koptilnya/engine_remastered/gearboxes/base.txt index cbb4e2c..795c4c1 100644 --- a/koptilnya/engine_remastered/gearboxes/base.txt +++ b/koptilnya/engine_remastered/gearboxes/base.txt @@ -1,82 +1,64 @@ --- @include ../wire_component.txt -require('../wire_component.txt') +--@include ../powertrain_component.txt -Gearbox = class('Gearbox', WireComponent) +require('../powertrain_component.txt') -function Gearbox:initialize(config, clutch, axles) - self.ratios = config.Ratios - self.reverse = config.Reverse +Gearbox = class('Gearbox', PowertrainComponent) - self.rpm = 0 - self.gear = 0 - self.torque = 0 +function Gearbox:initialize(config) + PowertrainComponent.initialize(self, 'Gearbox') - self.axles = axles or {} - self.clutch = clutch - - self.type = config.Type - - clutch:linkGearbox(self) - - for _, axle in pairs(axles) do - axle:linkGearbox(self) - end - - self:recalcRatio() -end - -function Gearbox:getInputs() - return { + self.wireInputs = { Upshift = 'number', Downshift = 'number' } -end - -function Gearbox:getOutputs() - return { + self.wireOutputs = { Gearbox_RPM = 'number', Gearbox_Torque = 'number', - Gearbox_Gear = 'number', Gearbox_Ratio = 'number' } + + self.type = config.Type or 'MANUAL' + self.inertia = config.Inertia or 1000 + + self.ratio = 0 end -function Gearbox:updateOutputs() - wire.ports.Gearbox_RPM = self.rpm +function Gearbox:updateWireOutputs() + wire.ports.Gearbox_RPM = self:getRPM() wire.ports.Gearbox_Torque = self.torque - wire.ports.Gearbox_Gear = self.gear wire.ports.Gearbox_Ratio = self.ratio end -function Gearbox:setGear(gear) - if gear >= -1 and gear <= #self.ratios then - self.gear = gear - self:recalcRatio() +function Gearbox:queryInertia() + if self.output == nil or self.ratio == 0 then + return self.inertia end + + return self.inertia + self.output:queryInertia() / math.pow(self.ratio, 2) end -function Gearbox:recalcRatio() - if self.gear == -1 then - self.ratio = -self.reverse - elseif self.gear == 0 then - self.ratio = 0 - else - self.ratio = self.ratios[self.gear] +function Gearbox:queryAngularVelocity(angularVelocity) + self.angularVelocity = angularVelocity + + if self.output == nil or self.ratio == 0 then + return angularVelocity end + + return self.output:queryAngularVelocity(angularVelocity) * self.ratio end -function Gearbox:update() - if self.clutch ~= nil then - self.torque = self.clutch.torque * self.ratio +function Gearbox:forwardStep(torque, inertia) + if self.output == nil then + return torque end - local maxAxlesRPM = 0 + if self.ratio == 0 then + self.output:forwardStep(0, inertia) - if #self.axles > 0 then - maxAxlesRPM = math.max(unpack(table.map(self.axles, function(diff) - return diff.avgRPM - end))) + return torque end - self.rpm = maxAxlesRPM * self.ratio -end \ No newline at end of file + self.torque = torque * self.ratio + + return self.output:forwardStep(self.torque, (inertia + self.inertia) * math.pow(self.ratio, 2)) / self.ratio +end diff --git a/koptilnya/engine_remastered/gearboxes/manual.txt b/koptilnya/engine_remastered/gearboxes/manual.txt index 5688eed..1a5e3be 100644 --- a/koptilnya/engine_remastered/gearboxes/manual.txt +++ b/koptilnya/engine_remastered/gearboxes/manual.txt @@ -1,12 +1,22 @@ --- @include /koptilnya/libs/watcher.txt --- @include ./base.txt +--@include /koptilnya/libs/watcher.txt +--@include ./base.txt + require('/koptilnya/libs/watcher.txt') require('./base.txt') ManualGearbox = class('ManualGearbox', Gearbox) -function ManualGearbox:initialize(...) - Gearbox.initialize(self, ...) +function ManualGearbox:initialize(config) + Gearbox.initialize(self, config) + + table.merge(self.wireOutputs, { + Gearbox_Gear = 'number' + }) + + self.ratios = config.Ratios or { 3.6, 2.2, 1.5, 1.2, 1.0, 0.8} + self.reverse = config.Reverse or 3.4 + + self.gear = 0 function shiftFunc() local upshift = wire.ports.Upshift or 0 @@ -20,6 +30,21 @@ function ManualGearbox:initialize(...) self:shift(val) end end) + + self:recalcRatio() +end + +function ManualGearbox:updateWireOutputs() + Gearbox.updateWireOutputs(self) + + wire.ports.Gearbox_Gear = self.gear +end + +function ManualGearbox:setGear(gear) + if gear >= -1 and gear <= #self.ratios then + self.gear = gear + self:recalcRatio() + end end function ManualGearbox:recalcRatio() @@ -36,19 +61,10 @@ function ManualGearbox:shift(dir) self:setGear(self.gear + dir) end --- function ManualGearbox:getOutputs() --- return { --- Gearbox_RPM = 'number', --- Gearbox_Torque = 'number' --- } --- end - --- function ManualGearbox:updateOutputs() - --- end - -function ManualGearbox:update() - Gearbox.update(self) +function ManualGearbox:forwardStep(torque, inertia) + local result = Gearbox.forwardStep(self, torque, inertia) self.shiftWatcher() + + return result end diff --git a/koptilnya/engine_remastered/novojiloff_diff.txt b/koptilnya/engine_remastered/novojiloff_diff.txt deleted file mode 100644 index 80c01c6..0000000 --- a/koptilnya/engine_remastered/novojiloff_diff.txt +++ /dev/null @@ -1,99 +0,0 @@ --- @server --- @include /koptilnya/libs/constants.txt --- @include ./wire_component.txt -require('/koptilnya/libs/constants.txt') -require('./wire_component.txt') - -Differential = class('Differential', WireComponent) - -function Differential:initialize(config, order) - self.finalDrive = config.FinalDrive or 1 - self.distributionCoeff = config.DistributionCoeff or 1 - self.axle = config.Axle or Vector(1, 0, 0) - - self.order = order - - self.avgRPM = 0 - - self.prefix = 'Axle' .. self.order .. '_' - - self.leftWheel = wire.ports[self.prefix .. 'LeftWheel'] - self.rightWheel = wire.ports[self.prefix .. 'RightWheel'] - - self.lwav = 0 - self.rwav = 0 - - hook.add('input', 'wire_axle_update' .. self.order, function(key, val) - if key == self.prefix .. 'LeftWheel' then - self.leftWheel = val - end - - if key == self.prefix .. 'RightWheel' then - self.rightWheel = val - end - end) -end - -function Differential:getInputs() - local inputs = {} - - inputs[self.prefix .. 'LeftWheel'] = 'entity' - inputs[self.prefix .. 'RightWheel'] = 'entity' - - return inputs -end - -function Differential:getOutputs() - local outputs = {} - - outputs[self.prefix .. 'LWAV'] = 'number' - outputs[self.prefix .. 'RWAV'] = 'number' - outputs[self.prefix .. 'AvgRPM'] = 'number' - outputs[self.prefix .. 'Test'] = 'number' - - return outputs -end - -function Differential:updateOutputs() - wire.ports[self.prefix .. 'LWAV'] = self.lwav - wire.ports[self.prefix .. 'RWAV'] = self.rwav - wire.ports[self.prefix .. 'AvgRPM'] = self.avgRPM -end - -function Differential:linkGearbox(gbox) - self.gearbox = gbox -end - -function Differential:getAngleVelocity() - local lwav = math.rad(self.leftWheel:getAngleVelocity()[2]) - local rwav = math.rad(-self.rightWheel:getAngleVelocity()[2]) - local awav = (lwav + rwav) / 2 - - return lwav, rwav, awav -end - -function Differential:getRPM() - local lwav, rwav, awav = self:getAngleVelocity() - - return lwav * RAD_TO_RPM, rwav * RAD_TO_RPM, awav * RAD_TO_RPM -end - -function Differential:update() - if isValid(self.leftWheel) and isValid(self.rightWheel) then - local lwav, rwav = self:getAngleVelocity() - - local inertia = self.leftWheel:getInertia().y + self.rightWheel:getInertia().y - local gboxTorque = self.gearbox and self.gearbox.torque or 0 - local simmetric = gboxTorque * self.distributionCoeff * self.finalDrive / 2 - local lock100 = (lwav - rwav) / 2 * inertia * TICK_INTERVAL - - wire.ports[self.prefix .. 'Test'] = (gboxTorque * self.distributionCoeff * self.finalDrive) - ((simmetric - lock100) + (simmetric + lock100)) - - self.leftWheel:applyTorque((simmetric - lock100) * 1.33 * -self.leftWheel:getRight()) - self.rightWheel:applyTorque((simmetric + lock100) * 1.33 * self.rightWheel:getRight()) - - self.lwav, self.rwav = self:getAngleVelocity() - _, _, self.avgRPM = self:getRPM() - self.avgRPM = self.avgRPM * self.finalDrive - end -end diff --git a/koptilnya/engine_remastered/powertrain_component.txt b/koptilnya/engine_remastered/powertrain_component.txt new file mode 100644 index 0000000..7580a77 --- /dev/null +++ b/koptilnya/engine_remastered/powertrain_component.txt @@ -0,0 +1,49 @@ +--@include /koptilnya/libs/constants.txt +--@include ./wire_component.txt + +require('/koptilnya/libs/constants.txt') +require('./wire_component.txt') + +PowertrainComponent = class('PowertrainComponent', WireComponent) + +function PowertrainComponent:initialize(name) + WireComponent.initialize(self) + + self.name = name or 'PowertrainComponent' + self.input = nil + self.output = nil + + self.inertia = 0.02 + self.angularVelocity = 0 + self.torque = 0 +end + +function PowertrainComponent:getRPM() + return self.angularVelocity * RAD_TO_RPM +end + +function PowertrainComponent:queryInertia() + if self.output == nil then + return self.inertia + end + + return self.inertia + self.output:queryInertia() +end + +function PowertrainComponent:queryAngularVelocity(angularVelocity) + self.angularVelocity = angularVelocity + + if self.output == nil then + return 0 + end + + return self.output:queryAngularVelocity(angularVelocity) +end + +function PowertrainComponent:forwardStep(torque, inertia) + if self.output == nil then + return self.torque + end + + return self.output:forwardStep(self.torque, self.inertia + inertia) +end diff --git a/koptilnya/engine_remastered/vehicle.txt b/koptilnya/engine_remastered/vehicle.txt index 9fd7daa..9816c21 100644 --- a/koptilnya/engine_remastered/vehicle.txt +++ b/koptilnya/engine_remastered/vehicle.txt @@ -1,75 +1,80 @@ --- Core components --- --- @include ./engine.txt --- @include ./clutch.txt --- @include ./differential.txt --- @include ./novojiloff_diff.txt --- --- Helpers & stuff --- @include ./factories/gearbox.txt --- @include /koptilnya/libs/table.txt --- @include /koptilnya/libs/constants.txt +--@server +--@include ./engine.txt +--@include ./clutch.txt +--@include ./differential.txt +--@include ./factories/gearbox.txt +--@include /koptilnya/libs/table.txt +--@include /koptilnya/libs/constants.txt + require('./engine.txt') require('./clutch.txt') ---require('./differential.txt') -require('./novojiloff_diff.txt') +require('./differential.txt') require('./factories/gearbox.txt') - require('/koptilnya/libs/table.txt') require('/koptilnya/libs/constants.txt') Vehicle = class('Vehicle') function Vehicle:initialize(config) - -- should probably validate config here if config == nil then throw('Vehicle config not provided') end + self.engine = Engine:new(config.Engine) self.clutch = Clutch:new(config.Clutch) - self.engine = Engine:new(config.Engine, self.clutch) - - local axleOrder = 0 - self.axles = table.map(config.Axles, function(config) - axleOrder = axleOrder + 1 - return Differential:new(config, axleOrder) + self.gearbox = GearboxFactory.create(config.Gearbox) + self.axles = table.map(config.Axles, function(config, idx) + return Differential:new(config, idx) end) - self.gearbox = GearboxFactory.create(config.Gearbox, self.clutch, self.axles) + self.components = { self.engine, self.clutch, self.gearbox } + table.add(self.components, self.axles) - -- self.systems = table.map(config.Systems, function(config) - -- return SystemsFactory.create(config) - -- end)s - self.components = {self.clutch, self.gearbox} - self.components = table.add(self.components, self.axles) - self.components = table.add(self.components, {self.gearbox, self.clutch, self.engine}) - -- self.components = table.add(self.components, self.systems) + self:linkComponents() + self:createIO() + hook.add('tick', 'vehicle_update', function() + self:update() + end) +end + +function Vehicle:createIO() local inputs = { Base = "entity" } local outputs = {} - for _, comp in ipairs(self.components) do - inputs = table.merge(inputs, comp:getInputs()) - outputs = table.merge(outputs, comp:getOutputs()) + for _, component in ipairs(self.components) do + inputs = table.merge(inputs, component.wireInputs) + outputs = table.merge(outputs, component.wireOutputs) end wire.adjustPorts(inputs, outputs) - - hook.add('tick', 'vehicle_update', function() - local base = wire.ports.Base - - for _, comp in pairs(self.components) do - comp:update() - comp:updateOutputs() - end - - if isValid(base) and (self.clutch:getPress() == 1 or self.gearbox.ratio == 0) then - local tiltForce = self.engine.torque * (-1 + self.engine.masterThrottle * 2) - - base:applyForceOffset(base:getUp() * tiltForce, base:getMassCenter() + base:getRight() * UNITS_PER_METER) - base:applyForceOffset(-base:getUp() * tiltForce, base:getMassCenter() - base:getRight() * UNITS_PER_METER) - end - end) +end + +function Vehicle:linkComponents() + self.engine.output = self.clutch + self.clutch.output = self.gearbox + self.gearbox.output = self.axles[1] + + self.axles[1].input = self.gearbox + self.gearbox.input = self.clutch + self.clutch.input = self.engine +end + +function Vehicle:update() + local base = wire.ports.Base + + self.engine:integrateDownwards() + + for _, component in pairs(self.components) do + component:updateWireOutputs() + end + + if isValid(base) and (self.clutch:getPress() == 1 or self.gearbox.ratio == 0) then + local tiltForce = self.engine.torque * (-1 + self.engine.masterThrottle * 2) + + base:applyForceOffset(base:getUp() * tiltForce, base:getMassCenter() + base:getRight() * UNITS_PER_METER) + base:applyForceOffset(-base:getUp() * tiltForce, base:getMassCenter() - base:getRight() * UNITS_PER_METER) + end end diff --git a/koptilnya/engine_remastered/wheel.txt b/koptilnya/engine_remastered/wheel.txt new file mode 100644 index 0000000..8be36fd --- /dev/null +++ b/koptilnya/engine_remastered/wheel.txt @@ -0,0 +1,61 @@ +--@include ./powertrain_component.txt +--@include /koptilnya/libs/constants.txt +--@include /koptilnya/libs/entity.txt + +require('./powertrain_component.txt') +require('/koptilnya/libs/constants.txt') +require('/koptilnya/libs/entity.txt') + +Wheel = class('Wheel', PowertrainComponent) + +function Wheel:initialize(config, prefix) + PowertrainComponent.initialize(self) + + self.prefix = prefix + + self.wireInputs = { + [prefix .. 'Wheel'] = 'entity' + } + self.wireOutputs = { + [prefix .. 'RPM'] = 'number' + } + + self.entity = NULL_ENTITY + + hook.add('input', 'vehicle_wheel_update' .. prefix, function(key, val) + if key == prefix .. 'Wheel' then + self.entity = val + + if not isValid(val) then + return + end + + if config.CalculateInertia then + --self.entity:setInertia(calculateWheelInertia(val)) + --print(calculateWheelInertia(val)) + end + + self.inertia = self.entity:getInertia().y + end + end) +end + +function Wheel:updateWireOutputs() + wire.ports[self.prefix .. 'RPM'] = self:selfRPM() +end + +function Wheel:queryAngularVelocity() + return self.angularVelocity +end + +function Wheel:forwardStep(torque, inertia) + if not isValid(self.entity) then + return 0 + end + + --self.entity:setInertia(self.entity:getInertia():setY(inertia)) + self.entity:applyTorque(torque * self.entity:getRight()) + self.angularVelocity = self.entity:getAngleVelocity().y + + return self.angularVelocity * self.inertia +end diff --git a/koptilnya/engine_remastered/wire_component.txt b/koptilnya/engine_remastered/wire_component.txt index 9f442c8..67e9aae 100644 --- a/koptilnya/engine_remastered/wire_component.txt +++ b/koptilnya/engine_remastered/wire_component.txt @@ -1,12 +1,9 @@ WireComponent = class('WireComponent') -function WireComponent:getInputs() - return {} +function WireComponent:initialize() + self.wireInputs = {} + self.wireOutputs = {} end -function WireComponent:getOutputs() - return {} -end - -function WireComponent:updateOutputs() +function WireComponent:updateWireOutputs() end diff --git a/koptilnya/libs/entity.txt b/koptilnya/libs/entity.txt index 2db67fe..b7a08db 100644 --- a/koptilnya/libs/entity.txt +++ b/koptilnya/libs/entity.txt @@ -5,3 +5,13 @@ function getLocalVelocity(entity) return entity:worldToLocal(entity:getVelocity() + entity:getPos()) end + +function calculateWheelInertia(entity) + if not isValid(entity) then + return Vector(0) + end + + local originalInertia = entity:getInertia() + + return Vector(originalInertia) +end From d432b3220ad8c5e03258278c4313e8d55ac263f3 Mon Sep 17 00:00:00 2001 From: Nikita Kruglickiy Date: Thu, 28 Jul 2022 15:57:51 +0300 Subject: [PATCH 07/10] Added missing WireComponent script --- koptilnya/engine_remastered/wire_component.txt | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 koptilnya/engine_remastered/wire_component.txt diff --git a/koptilnya/engine_remastered/wire_component.txt b/koptilnya/engine_remastered/wire_component.txt new file mode 100644 index 0000000..8276421 --- /dev/null +++ b/koptilnya/engine_remastered/wire_component.txt @@ -0,0 +1,9 @@ +WireComponent = class('WireComponent') + +function WireComponent:initialize() + self.wireInputs = {} + self.wireOutputs = {} +end + +function WireComponent:updateWireOutputs() +end From ca83cc251580dafa2e2896f61d5097b0645be758 Mon Sep 17 00:00:00 2001 From: Nikita Kruglickiy Date: Thu, 28 Jul 2022 16:06:48 +0300 Subject: [PATCH 08/10] Added direction option to wheel config --- koptilnya/engine_remastered/differential.txt | 2 +- koptilnya/engine_remastered/wheel.txt | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/koptilnya/engine_remastered/differential.txt b/koptilnya/engine_remastered/differential.txt index e1d8dea..02f02c1 100644 --- a/koptilnya/engine_remastered/differential.txt +++ b/koptilnya/engine_remastered/differential.txt @@ -24,7 +24,7 @@ function Differential:initialize(config, order) CalculateInertia = true } self.outputA = Wheel:new(wheelConfig, prefix .. 'Left') - self.outputB = Wheel:new(wheelConfig, prefix .. 'Right') + self.outputB = Wheel:new(table.merge(wheelConfig, { Direction = -1 }), prefix .. 'Right') table.merge(self.wireInputs, self.outputA.wireInputs) table.merge(self.wireInputs, self.outputB.wireInputs) diff --git a/koptilnya/engine_remastered/wheel.txt b/koptilnya/engine_remastered/wheel.txt index 8be36fd..50f2491 100644 --- a/koptilnya/engine_remastered/wheel.txt +++ b/koptilnya/engine_remastered/wheel.txt @@ -12,12 +12,13 @@ function Wheel:initialize(config, prefix) PowertrainComponent.initialize(self) self.prefix = prefix + self.direction = config.Direction or 1 self.wireInputs = { [prefix .. 'Wheel'] = 'entity' } self.wireOutputs = { - [prefix .. 'RPM'] = 'number' + [prefix .. 'WheelRPM'] = 'number' } self.entity = NULL_ENTITY @@ -41,7 +42,7 @@ function Wheel:initialize(config, prefix) end function Wheel:updateWireOutputs() - wire.ports[self.prefix .. 'RPM'] = self:selfRPM() + wire.ports[self.prefix .. 'WheelRPM'] = self:getRPM() end function Wheel:queryAngularVelocity() @@ -54,8 +55,8 @@ function Wheel:forwardStep(torque, inertia) end --self.entity:setInertia(self.entity:getInertia():setY(inertia)) - self.entity:applyTorque(torque * self.entity:getRight()) - self.angularVelocity = self.entity:getAngleVelocity().y + self.entity:applyTorque(torque * self.direction * self.entity:getRight()) + self.angularVelocity = self.entity:getAngleVelocity().y * self.direction * TICK_INTERVAL return self.angularVelocity * self.inertia end From ec65efcafc5813b906e3e63dbc8a2abbe2a1c8ce Mon Sep 17 00:00:00 2001 From: Nikita Kruglickiy Date: Fri, 29 Jul 2022 22:06:02 +0300 Subject: [PATCH 09/10] Attempts to fix new vehicle system --- koptilnya/engine_remastered/differential.txt | 23 ++++++++++------ koptilnya/engine_remastered/wheel.txt | 28 +++++++++++++++----- koptilnya/libs/constants.txt | 1 + koptilnya/libs/entity.txt | 8 +++--- 4 files changed, 42 insertions(+), 18 deletions(-) diff --git a/koptilnya/engine_remastered/differential.txt b/koptilnya/engine_remastered/differential.txt index 02f02c1..48d1464 100644 --- a/koptilnya/engine_remastered/differential.txt +++ b/koptilnya/engine_remastered/differential.txt @@ -11,13 +11,19 @@ Differential = class('Differential', PowertrainComponent) function Differential:initialize(config, order) PowertrainComponent.initialize(self, 'Axle') + self.wireOutputs = { + Diff_Torque = 'number' + } + local prefix = 'Axle' .. (order or '') .. '_' self.finalDrive = config.FinalDrive or 4 + self.inertia = config.Inertia or 0.2 self.biasAB = config.Bias or 0.5 self.coastRamp = config.CoastRamp or 0.5 self.powerRamp = config.PowerRamp or 1 self.preload = config.Preload or 10 + self.stiffness = config.Stiffness or 0.1 self.slipTorque = config.SlipTorque or 1000 local wheelConfig = { @@ -33,6 +39,8 @@ function Differential:initialize(config, order) end function Differential:updateWireOutputs() + wire.ports.Diff_Torque = self.torque + if self.outputA ~= nil then self.outputA:updateWireOutputs() end @@ -60,7 +68,7 @@ function Differential:queryAngularVelocity(angularVelocity) local aW = self.outputA:queryAngularVelocity(angularVelocity) local bW = self.outputB:queryAngularVelocity(angularVelocity) - return (aW - bW) * self.finalDrive * 0.5 + return (aW + bW) * self.finalDrive * 0.5 end function Differential:forwardStep(torque, inertia) @@ -69,12 +77,14 @@ function Differential:forwardStep(torque, inertia) end local aW = self.outputA:queryAngularVelocity(self.angularVelocity) - local bW = -self.outputB:queryAngularVelocity(self.angularVelocity) + local bW = self.outputB:queryAngularVelocity(self.angularVelocity) local aI = self.outputA:queryInertia() local bI = self.outputB:queryInertia() + self.torque = torque * self.finalDrive + local tqA, tqB = self:_openDiffTorqueSplit( - torque * self.finalDrive, + self.torque, aW, bW, aI, @@ -87,13 +97,10 @@ function Differential:forwardStep(torque, inertia) self.slipTorque ) - --tqA = tqA * aI * TICK_INTERVAL - --tqB = tqB * bI * TICK_INTERVAL - tqA = self.outputA:forwardStep(tqA, inertia * 0.5 + aI) tqB = self.outputB:forwardStep(tqB, inertia * 0.5 + bI) - return tqA + tqB + return (tqA + tqB) / self.finalDrive end function Differential:_openDiffTorqueSplit(tq, aW, bW, aI, bI, biasAB, preload, stiffness, powerRamp, coastRamp, slipTorque) @@ -109,7 +116,7 @@ function Differential:_lockingDiffTorqueSplit(tq, aW, bW, aI, bI, biasAB, preloa local bTqCorr = (w - bW) * bI -- / dt bTqCorr = bTqCorr * stiffness - local biasA = clamp(0.5 + (bW - aW) * 0.1 * stiffness, 0, 1) + local biasA = math.clamp(0.5 + (bW - aW) * 0.1 * stiffness, 0, 1) return tq * biasA + aTqCorr, tq * (1 - biasA) * bTqCorr end diff --git a/koptilnya/engine_remastered/wheel.txt b/koptilnya/engine_remastered/wheel.txt index 50f2491..ed92581 100644 --- a/koptilnya/engine_remastered/wheel.txt +++ b/koptilnya/engine_remastered/wheel.txt @@ -18,7 +18,8 @@ function Wheel:initialize(config, prefix) [prefix .. 'Wheel'] = 'entity' } self.wireOutputs = { - [prefix .. 'WheelRPM'] = 'number' + [prefix .. 'WheelRPM'] = 'number', + [prefix .. 'WheelTorque'] = 'number' } self.entity = NULL_ENTITY @@ -32,8 +33,7 @@ function Wheel:initialize(config, prefix) end if config.CalculateInertia then - --self.entity:setInertia(calculateWheelInertia(val)) - --print(calculateWheelInertia(val)) + self.entity:setInertia(calculateWheelInertia(val)) end self.inertia = self.entity:getInertia().y @@ -41,8 +41,17 @@ function Wheel:initialize(config, prefix) end) end +function Wheel:getRadius() + if not isValid(self.entity) then + return 0 + end + + return self.entity:getModelRadius() * UNITS_TO_METERS +end + function Wheel:updateWireOutputs() wire.ports[self.prefix .. 'WheelRPM'] = self:getRPM() + wire.ports[self.prefix .. 'WheelTorque'] = self.torque end function Wheel:queryAngularVelocity() @@ -54,9 +63,14 @@ function Wheel:forwardStep(torque, inertia) return 0 end - --self.entity:setInertia(self.entity:getInertia():setY(inertia)) - self.entity:applyTorque(torque * self.direction * self.entity:getRight()) - self.angularVelocity = self.entity:getAngleVelocity().y * self.direction * TICK_INTERVAL + local initialAngularVelocity = self.angularVelocity - return self.angularVelocity * self.inertia + --self.entity:setInertia(self.entity:getInertia():setY(inertia)) + self.torque = math.deg(torque) * 1.33 * -self.direction + self.entity:applyTorque(self.torque * TICK_INTERVAL * self.entity:getRight()) + self.angularVelocity = math.rad(self.entity:getAngleVelocity().y) * self.direction + + local deltaOmegaTorque = (self.angularVelocity - initialAngularVelocity) * self.inertia / TICK_INTERVAL + + return torque end diff --git a/koptilnya/libs/constants.txt b/koptilnya/libs/constants.txt index 20abe70..8124b78 100644 --- a/koptilnya/libs/constants.txt +++ b/koptilnya/libs/constants.txt @@ -6,3 +6,4 @@ TICK_INTERVAL = game.getTickInterval() RAD_TO_RPM = 9.5493 RPM_TO_RAD = 0.10472 UNITS_PER_METER = 39.37 +UNITS_TO_METERS = 0.01905 diff --git a/koptilnya/libs/entity.txt b/koptilnya/libs/entity.txt index b7a08db..a6ec779 100644 --- a/koptilnya/libs/entity.txt +++ b/koptilnya/libs/entity.txt @@ -1,3 +1,7 @@ +--@include ./constants.txt + +require('./constants.txt') + function getLocalVelocity(entity) if not entity:isValid() then return @@ -11,7 +15,5 @@ function calculateWheelInertia(entity) return Vector(0) end - local originalInertia = entity:getInertia() - - return Vector(originalInertia) + return entity:getInertia():setY(0.5 * entity:getMass() * math.pow(entity:getModelRadius() * UNITS_TO_METERS, 2)) end From 3d30f0738de72452ad32177485a963b2419f8107 Mon Sep 17 00:00:00 2001 From: Nikita Kruglickiy Date: Tue, 2 Aug 2022 19:55:58 +0300 Subject: [PATCH 10/10] MAMA --- .../engine_remastered/configs/new_sx240.txt | 46 +++++++++++++++++++ koptilnya/engine_remastered/differential.txt | 3 ++ koptilnya/engine_remastered/wheel.txt | 31 +++++++++++-- 3 files changed, 75 insertions(+), 5 deletions(-) create mode 100644 koptilnya/engine_remastered/configs/new_sx240.txt diff --git a/koptilnya/engine_remastered/configs/new_sx240.txt b/koptilnya/engine_remastered/configs/new_sx240.txt new file mode 100644 index 0000000..fed1e6c --- /dev/null +++ b/koptilnya/engine_remastered/configs/new_sx240.txt @@ -0,0 +1,46 @@ +-- @name SX 240 +-- @author Koptilnya1337 +-- @server +-- @include /koptilnya/engine_remastered/vehicle.txt +require('/koptilnya/engine_remastered/vehicle.txt') + +Vehicle:new({ + Engine = { + IdleRPM = 900, + MaxRPM = 7500, + Inertia = 0.151, + StartFriction = -30, + FrictionCoeff = 0.02, + LimiterDuration = 0.08, + TorqueMap = { + 118.89918444138, 122.0751393736, 125.25109430583, 128.42704923806, 131.60300417029, 134.77895910251, 137.95491403474, 141.13086896697, 144.3068238992, 147.48277883143, 150.65873376365, 153.83468869588, 157.01064362811, 160.18659856034, 163.36255349256, + 166.53850842479, 169.71446335702, 172.89041828925, 176.06637322148, 179.2423281537, 182.41828308593, 185.59423801816, 188.77019295039, 191.94614788261, 195.12210281484, 198.29805774707, 201.4740126793, 204.64996761153, 207.82592254375, 211.00187747598, + 214.17783240821, 217.35378734044, 220.52974227266, 223.70569720489, 226.88165213712, 227.9621903219, 229.04272850669, 230.12326669147, 231.20380487625, 232.28434306104, 233.36488124582, 234.4454194306, 235.52595761538, 236.60649580017, 237.68703398495, + 238.76757216973, 239.84811035452, 240.9286485393, 242.00918672408, 243.08972490886, 244.17026309365, 245.25080127843, 246.33133946321, 247.411877648, 248.49241583278, 249.57295401756, 250.65349220234, 251.73403038713, 252.81456857191, 253.89510675669, 254.97564494148, + 256.05618312626, 257.13672131104, 258.21725949582, 259.29779768061, 260.37833586539, 261.29278066787, 262.20722547034, 263.12167027282, 264.0361150753, 264.95055987777, 265.86500468025, 266.77944948273, 267.6938942852, 268.60833908768, 269.52278389016, + 270.43722869263, 271.35167349511, 272.26611829758, 273.18056310006, 274.09500790254, 275.00945270501, 275.92389750749, 276.83834230997, 275.50329427594, 274.16824624192, 272.8331982079, 271.49815017388, 270.16310213985, 268.82805410583, 267.49300607181, + 266.15795803778, 264.82291000376, 263.48786196974, 262.15281393572, 260.81776590169, 259.48271786767, 250.23203287321, 240.98134787874, 231.73066288428, 222.47997788981 + } + }, + Clutch = { + Inertia = 0.002, + SlipTorque = 1000 + }, + Gearbox = { + Type = 'MANUAL', + Inertia = 0.01, + Ratios = { 3.626, 2.200, 1.541, 1.213, 1.000, 0.767 }, + Reverse = 3.437 + }, + Axles = { + { + FinalDrive = 3.392, + Inertia = 0.01, + BiasAB = 0.5, + CoastRamp = 0.5, + PowerRamp = 1, + Stiffness = 0.1, + SlipTorque = 1000 + } + } +}) diff --git a/koptilnya/engine_remastered/differential.txt b/koptilnya/engine_remastered/differential.txt index 48d1464..ba97feb 100644 --- a/koptilnya/engine_remastered/differential.txt +++ b/koptilnya/engine_remastered/differential.txt @@ -32,6 +32,9 @@ function Differential:initialize(config, order) self.outputA = Wheel:new(wheelConfig, prefix .. 'Left') self.outputB = Wheel:new(table.merge(wheelConfig, { Direction = -1 }), prefix .. 'Right') + self.outputA.input = self + self.outputB.input = self + table.merge(self.wireInputs, self.outputA.wireInputs) table.merge(self.wireInputs, self.outputB.wireInputs) table.merge(self.wireOutputs, self.outputA.wireOutputs) diff --git a/koptilnya/engine_remastered/wheel.txt b/koptilnya/engine_remastered/wheel.txt index ed92581..32c07eb 100644 --- a/koptilnya/engine_remastered/wheel.txt +++ b/koptilnya/engine_remastered/wheel.txt @@ -12,6 +12,7 @@ function Wheel:initialize(config, prefix) PowertrainComponent.initialize(self) self.prefix = prefix + self.rollingResistance = config.RollingResistance or -50 self.direction = config.Direction or 1 self.wireInputs = { @@ -33,7 +34,8 @@ function Wheel:initialize(config, prefix) end if config.CalculateInertia then - self.entity:setInertia(calculateWheelInertia(val)) + self.entity:setInertia(Vector(7.7)) + --self.entity:setInertia(calculateWheelInertia(val)) end self.inertia = self.entity:getInertia().y @@ -63,14 +65,33 @@ function Wheel:forwardStep(torque, inertia) return 0 end - local initialAngularVelocity = self.angularVelocity + local initAngularVelocity = self.angularVelocity - --self.entity:setInertia(self.entity:getInertia():setY(inertia)) + self.entity:setInertia(Vector(inertia)) self.torque = math.deg(torque) * 1.33 * -self.direction self.entity:applyTorque(self.torque * TICK_INTERVAL * self.entity:getRight()) self.angularVelocity = math.rad(self.entity:getAngleVelocity().y) * self.direction - local deltaOmegaTorque = (self.angularVelocity - initialAngularVelocity) * self.inertia / TICK_INTERVAL + local tq = (self.angularVelocity - initAngularVelocity) * inertia * TICK_INTERVAL - return torque + return self.rollingResistance - (tq - torque) end + +--local physObj = self.entity:getPhysicsObject() +--local load = physObj:getStress() +--local frictionSnapshot = physObj:getFrictionSnapshot() +--local forwardFrictionSpeed = 0 +--local contactVelocity = physObj:getVelocityAtPoint(self.entity:getPos() + Vector(0, 0, self.entity:getModelRadius())) / 39.37 +-- +--if #table.getKeys(frictionSnapshot) > 0 then +-- local data = frictionSnapshot[1] +-- local forwardDir = data['Normal']:cross(self.entity:getRight() * self.direction):getNormalized() +-- +-- forwardFrictionSpeed = contactVelocity:dot(forwardDir) +--end +-- +--local errorTorque = (self.angularVelocity - forwardFrictionSpeed / self:getRadius()) * inertia / TICK_INTERVAL +--local rollingResistance = -40 +--local deltaOmegaTorque = (self.angularVelocity - initialAngularVelocity) * self.inertia / TICK_INTERVAL +-- +--return rollingResistance - deltaOmegaTorque