Merge branch 'master' of https://github.com/koptilnya/starfall-modules
This commit is contained in:
commit
87413724e1
@ -1 +1,3 @@
|
|||||||
Не забудьте установить форматтер ```koihik.vscode-lua-format```
|
Не забудьте установить форматтер ```koihik.vscode-lua-format```
|
||||||
|
<br>
|
||||||
|
Для WebStorm нужно поставить ```EmmyLua```
|
||||||
|
|||||||
151
koptilnya/data/models/vw_golf_mk7_5.txt
Normal file
151
koptilnya/data/models/vw_golf_mk7_5.txt
Normal file
@ -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
|
||||||
@ -1,72 +1,67 @@
|
|||||||
-- @include /koptilnya/libs/wire_component.txt
|
--@include ./powertrain_component.txt
|
||||||
-- @include ./enums/gearbox.txt
|
--@include ./enums/gearbox.txt
|
||||||
-- @include /koptilnya/libs/constants.txt
|
--@include /koptilnya/libs/constants.txt
|
||||||
|
|
||||||
require('/koptilnya/libs/constants.txt')
|
require('/koptilnya/libs/constants.txt')
|
||||||
require('/koptilnya/libs/wire_component.txt')
|
require('./powertrain_component.txt')
|
||||||
local gearboxTypes = require('./enums/gearbox.txt')
|
local gearboxTypes = require('./enums/gearbox.txt')
|
||||||
|
|
||||||
Clutch = class('Clutch', WireComponent)
|
Clutch = class('Clutch', PowertrainComponent)
|
||||||
|
|
||||||
function Clutch:initialize(config)
|
function Clutch:initialize(config)
|
||||||
self.stiffness = config.Stiffness
|
PowertrainComponent.initialize(self, 'Clutch')
|
||||||
self.damping = config.Damping
|
|
||||||
self.maxTorque = config.MaxTorque
|
|
||||||
|
|
||||||
self.press = 0
|
self.wireInputs = {
|
||||||
self.slip = 0
|
Clutch = 'number'
|
||||||
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.wireOutputs = {
|
||||||
|
Clutch_Torque = 'number'
|
||||||
|
}
|
||||||
|
|
||||||
|
self.inertia = config.Inertia or 0.1
|
||||||
|
self.slipTorque = config.SlipTorque or 1000
|
||||||
end
|
end
|
||||||
|
|
||||||
function Clutch:updateOutputs()
|
function Clutch:updateWireOutputs()
|
||||||
wire.ports.Clutch_Torque = self.torque
|
wire.ports.Clutch_Torque = self.torque
|
||||||
wire.ports.Clutch_Slip = self.slip
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function Clutch:getPress()
|
function Clutch:getPress()
|
||||||
if self.gearbox ~= nil and self.gearbox.type == gearboxTypes.MANUAL then
|
return 1 - wire.ports.Clutch
|
||||||
return wire.ports.Clutch
|
end
|
||||||
else
|
|
||||||
return self.press
|
function Clutch:queryInertia()
|
||||||
|
if self.output == nil then
|
||||||
|
return self.inertia
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return self.inertia + self.output:queryInertia() * self:getPress()
|
||||||
end
|
end
|
||||||
|
|
||||||
function Clutch:update()
|
function Clutch:queryAngularVelocity(angularVelocity)
|
||||||
local engRPM = self.engine and self.engine.rpm or 0
|
self.angularVelocity = angularVelocity
|
||||||
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
|
|
||||||
|
|
||||||
self.slip = ((engRPM - gboxRPM) * RPM_TO_RAD) * gboxRatioNotZero / 2
|
if self.output == nil then
|
||||||
self.targetTorque = math.clamp(self.slip * self.stiffness * (1 - self:getPress()), -self.maxTorque, self.maxTorque)
|
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
|
end
|
||||||
|
|||||||
46
koptilnya/engine_remastered/configs/new_sx240.txt
Normal file
46
koptilnya/engine_remastered/configs/new_sx240.txt
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
@ -1,207 +1,158 @@
|
|||||||
-- @server
|
--@include /koptilnya/libs/constants.txt
|
||||||
-- @include /koptilnya/libs/constants.txt
|
--@include ./powertrain_component.txt
|
||||||
-- @include /koptilnya/libs/wire_component.txt
|
--@include ./wheel.txt
|
||||||
require('/koptilnya/libs/constants.txt')
|
|
||||||
require('/koptilnya/libs/wire_component.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)
|
function Differential:initialize(config, order)
|
||||||
self.power = config.Power or 1
|
PowertrainComponent.initialize(self, 'Axle')
|
||||||
self.coast = config.Coast or 1
|
|
||||||
self.finalDrive = config.FinalDrive or 1
|
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.preload = config.Preload or 10
|
||||||
self.order = order or 1
|
self.stiffness = config.Stiffness or 0.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(table.merge(wheelConfig, { Direction = -1 }), prefix .. 'Right')
|
||||||
|
|
||||||
self.distributionCoeff = config.DistributionCoeff or 1
|
self.outputA.input = self
|
||||||
self.maxCorrectingTorque = config.MaxCorrectingTorque or 200
|
self.outputB.input = self
|
||||||
|
|
||||||
self.avgRPM = 0
|
table.merge(self.wireInputs, self.outputA.wireInputs)
|
||||||
|
table.merge(self.wireInputs, self.outputB.wireInputs)
|
||||||
self.prefix = 'Axle' .. self.order .. '_'
|
table.merge(self.wireOutputs, self.outputA.wireOutputs)
|
||||||
|
table.merge(self.wireOutputs, self.outputB.wireOutputs)
|
||||||
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)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function Differential:getInputs()
|
function Differential:updateWireOutputs()
|
||||||
local inputs = {}
|
wire.ports.Diff_Torque = self.torque
|
||||||
|
|
||||||
inputs[self.prefix .. 'LeftWheel'] = 'entity'
|
if self.outputA ~= nil then
|
||||||
inputs[self.prefix .. 'RightWheel'] = 'entity'
|
self.outputA:updateWireOutputs()
|
||||||
|
end
|
||||||
|
|
||||||
return inputs
|
if self.outputB ~= nil then
|
||||||
end
|
self.outputB:updateWireOutputs()
|
||||||
|
|
||||||
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)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Differential:update()
|
function Differential:queryInertia()
|
||||||
if isValid(self.leftWheel) and isValid(self.rightWheel) then
|
if self.outputA == nil or self.outputB == nil then
|
||||||
local lwav = math.rad(self.leftWheel:getAngleVelocity()[2])
|
return self.inertia
|
||||||
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
|
|
||||||
end
|
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()
|
||||||
|
|
||||||
|
self.torque = torque * self.finalDrive
|
||||||
|
|
||||||
|
local tqA, tqB = self:_openDiffTorqueSplit(
|
||||||
|
self.torque,
|
||||||
|
aW,
|
||||||
|
bW,
|
||||||
|
aI,
|
||||||
|
bI,
|
||||||
|
self.biasAB,
|
||||||
|
self.preload,
|
||||||
|
self.stiffness,
|
||||||
|
self.powerRamp,
|
||||||
|
self.coastRamp,
|
||||||
|
self.slipTorque
|
||||||
|
)
|
||||||
|
|
||||||
|
tqA = self.outputA:forwardStep(tqA, inertia * 0.5 + aI)
|
||||||
|
tqB = self.outputB:forwardStep(tqB, inertia * 0.5 + bI)
|
||||||
|
|
||||||
|
return (tqA + tqB) / self.finalDrive
|
||||||
|
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 = math.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
|
end
|
||||||
|
|||||||
@ -1,53 +1,40 @@
|
|||||||
-- @include /koptilnya/libs/wire_component.txt
|
--@include ./powertrain_component.txt
|
||||||
-- @include /koptilnya/libs/constants.txt
|
--@include /koptilnya/libs/constants.txt
|
||||||
require('/koptilnya/libs/wire_component.txt')
|
|
||||||
|
require('./powertrain_component.txt')
|
||||||
require('/koptilnya/libs/constants.txt')
|
require('/koptilnya/libs/constants.txt')
|
||||||
|
|
||||||
Engine = class('Engine', WireComponent)
|
Engine = class('Engine', PowertrainComponent)
|
||||||
|
|
||||||
function Engine:initialize(config, clutch)
|
function Engine:initialize(config, clutch)
|
||||||
self.idleRPM = config.IdleRPM
|
PowertrainComponent.initialize(self, 'Engine')
|
||||||
self.maxRPM = config.MaxRPM
|
|
||||||
|
|
||||||
self.flywheelMass = config.FlywheelMass
|
self.wireInputs = {
|
||||||
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 {
|
|
||||||
Throttle = 'number'
|
Throttle = 'number'
|
||||||
}
|
}
|
||||||
end
|
self.wireOutputs = {
|
||||||
|
|
||||||
function Engine:getOutputs()
|
|
||||||
return {
|
|
||||||
Engine_RPM = 'number',
|
Engine_RPM = 'number',
|
||||||
Engine_Torque = 'number',
|
Engine_Torque = 'number',
|
||||||
Engine_MasterThrottle = '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
|
end
|
||||||
|
|
||||||
function Engine:updateOutputs()
|
function Engine:updateWireOutputs()
|
||||||
wire.ports.Engine_RPM = self.rpm
|
wire.ports.Engine_RPM = self:getRPM()
|
||||||
wire.ports.Engine_Torque = self.torque
|
wire.ports.Engine_Torque = self.torque
|
||||||
wire.ports.Engine_MasterThrottle = self.masterThrottle
|
wire.ports.Engine_MasterThrottle = self.masterThrottle
|
||||||
end
|
end
|
||||||
@ -56,35 +43,53 @@ function Engine:getThrottle()
|
|||||||
return wire.ports.Throttle
|
return wire.ports.Throttle
|
||||||
end
|
end
|
||||||
|
|
||||||
function Engine:update()
|
function Engine:_generateTorque()
|
||||||
local throttle = self:getThrottle()
|
local throttle = self:getThrottle()
|
||||||
|
local rpm = self:getRPM()
|
||||||
|
|
||||||
self.rpmFrac = math.clamp((self.rpm - self.idleRPM) / (self.maxRPM - self.idleRPM), 0, 1)
|
local rpmFrac = math.clamp((rpm - self.idleRPM) / (self.maxRPM - self.idleRPM), 0, 1)
|
||||||
self.friction = self.startFriction - self.rpm * self.frictionCoeff
|
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 maxInitialTorque = self.torqueMap[tqIdx] - self.friction
|
||||||
|
local idleFadeStart = math.clamp(math.remap(rpm, self.idleRPM - 300, self.idleRPM, 1, 0), 0, 1)
|
||||||
local idleFadeStart = math.clamp(math.remap(self.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 idleFadeEnd = math.clamp(math.remap(self.rpm, self.idleRPM, self.idleRPM + 600, 1, 0), 0, 1)
|
|
||||||
|
|
||||||
local additionalEnergySupply = idleFadeEnd * (-self.friction / maxInitialTorque) + idleFadeStart
|
local additionalEnergySupply = idleFadeEnd * (-self.friction / maxInitialTorque) + idleFadeStart
|
||||||
|
|
||||||
if self.rpm > self.maxRPM then
|
if rpm > self.maxRPM then
|
||||||
throttle = 0
|
throttle = 0
|
||||||
self._limiterTime = timer.systime()
|
self.limiterTimer = timer.systime()
|
||||||
else
|
else
|
||||||
throttle = timer.systime() >= self._limiterTime + self.limiterDuration and throttle or 0
|
throttle = timer.systime() >= self.limiterTimer + self.limiterDuration and throttle or 0
|
||||||
end
|
end
|
||||||
|
|
||||||
self.masterThrottle = math.clamp(additionalEnergySupply + throttle, 0, 1)
|
self.masterThrottle = math.clamp(additionalEnergySupply + throttle, 0, 1)
|
||||||
|
|
||||||
local realInitialTorque = maxInitialTorque * self.masterThrottle
|
local realInitialTorque = maxInitialTorque * self.masterThrottle
|
||||||
|
|
||||||
local loadTorque = self.clutch and self.clutch.torque or 0
|
|
||||||
|
|
||||||
self.torque = realInitialTorque + self.friction
|
self.torque = realInitialTorque + self.friction
|
||||||
|
|
||||||
self.rpm = self.rpm + (self.torque - loadTorque) / self._fwInertia * RAD_TO_RPM * TICK_INTERVAL
|
return self.torque
|
||||||
self.rpm = math.max(self.rpm, 0)
|
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
|
end
|
||||||
|
|||||||
@ -1,82 +1,64 @@
|
|||||||
-- @include /koptilnya/libs/wire_component.txt
|
--@include ../powertrain_component.txt
|
||||||
require('/koptilnya/libs/wire_component.txt')
|
|
||||||
|
|
||||||
Gearbox = class('Gearbox', WireComponent)
|
require('../powertrain_component.txt')
|
||||||
|
|
||||||
function Gearbox:initialize(config, clutch, axles)
|
Gearbox = class('Gearbox', PowertrainComponent)
|
||||||
self.ratios = config.Ratios
|
|
||||||
self.reverse = config.Reverse
|
|
||||||
|
|
||||||
self.rpm = 0
|
function Gearbox:initialize(config)
|
||||||
self.gear = 0
|
PowertrainComponent.initialize(self, 'Gearbox')
|
||||||
self.torque = 0
|
|
||||||
|
|
||||||
self.axles = axles or {}
|
self.wireInputs = {
|
||||||
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 {
|
|
||||||
Upshift = 'number',
|
Upshift = 'number',
|
||||||
Downshift = 'number'
|
Downshift = 'number'
|
||||||
}
|
}
|
||||||
end
|
self.wireOutputs = {
|
||||||
|
|
||||||
function Gearbox:getOutputs()
|
|
||||||
return {
|
|
||||||
Gearbox_RPM = 'number',
|
Gearbox_RPM = 'number',
|
||||||
Gearbox_Torque = 'number',
|
Gearbox_Torque = 'number',
|
||||||
Gearbox_Gear = 'number',
|
|
||||||
Gearbox_Ratio = 'number'
|
Gearbox_Ratio = 'number'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.type = config.Type or 'MANUAL'
|
||||||
|
self.inertia = config.Inertia or 1000
|
||||||
|
|
||||||
|
self.ratio = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
function Gearbox:updateOutputs()
|
function Gearbox:updateWireOutputs()
|
||||||
wire.ports.Gearbox_RPM = self.rpm
|
wire.ports.Gearbox_RPM = self:getRPM()
|
||||||
wire.ports.Gearbox_Torque = self.torque
|
wire.ports.Gearbox_Torque = self.torque
|
||||||
wire.ports.Gearbox_Gear = self.gear
|
|
||||||
wire.ports.Gearbox_Ratio = self.ratio
|
wire.ports.Gearbox_Ratio = self.ratio
|
||||||
end
|
end
|
||||||
|
|
||||||
function Gearbox:setGear(gear)
|
function Gearbox:queryInertia()
|
||||||
if gear >= -1 and gear <= #self.ratios then
|
if self.output == nil or self.ratio == 0 then
|
||||||
self.gear = gear
|
return self.inertia
|
||||||
self:recalcRatio()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return self.inertia + self.output:queryInertia() / math.pow(self.ratio, 2)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Gearbox:recalcRatio()
|
function Gearbox:queryAngularVelocity(angularVelocity)
|
||||||
if self.gear == -1 then
|
self.angularVelocity = angularVelocity
|
||||||
self.ratio = -self.reverse
|
|
||||||
elseif self.gear == 0 then
|
if self.output == nil or self.ratio == 0 then
|
||||||
self.ratio = 0
|
return angularVelocity
|
||||||
else
|
|
||||||
self.ratio = self.ratios[self.gear]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return self.output:queryAngularVelocity(angularVelocity) * self.ratio
|
||||||
end
|
end
|
||||||
|
|
||||||
function Gearbox:update()
|
function Gearbox:forwardStep(torque, inertia)
|
||||||
if self.clutch ~= nil then
|
if self.output == nil then
|
||||||
self.torque = self.clutch.torque * self.ratio
|
return torque
|
||||||
end
|
end
|
||||||
|
|
||||||
local maxAxlesRPM = 0
|
if self.ratio == 0 then
|
||||||
|
self.output:forwardStep(0, inertia)
|
||||||
|
|
||||||
if #self.axles > 0 then
|
return torque
|
||||||
maxAxlesRPM = math.max(unpack(table.map(self.axles, function(diff)
|
|
||||||
return diff.avgRPM
|
|
||||||
end)))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
self.rpm = maxAxlesRPM * self.ratio
|
self.torque = torque * self.ratio
|
||||||
|
|
||||||
|
return self.output:forwardStep(self.torque, (inertia + self.inertia) * math.pow(self.ratio, 2)) / self.ratio
|
||||||
end
|
end
|
||||||
@ -1,12 +1,22 @@
|
|||||||
-- @include /koptilnya/libs/watcher.txt
|
--@include /koptilnya/libs/watcher.txt
|
||||||
-- @include ./base.txt
|
--@include ./base.txt
|
||||||
|
|
||||||
require('/koptilnya/libs/watcher.txt')
|
require('/koptilnya/libs/watcher.txt')
|
||||||
require('./base.txt')
|
require('./base.txt')
|
||||||
|
|
||||||
ManualGearbox = class('ManualGearbox', Gearbox)
|
ManualGearbox = class('ManualGearbox', Gearbox)
|
||||||
|
|
||||||
function ManualGearbox:initialize(...)
|
function ManualGearbox:initialize(config)
|
||||||
Gearbox.initialize(self, ...)
|
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()
|
function shiftFunc()
|
||||||
local upshift = wire.ports.Upshift or 0
|
local upshift = wire.ports.Upshift or 0
|
||||||
@ -20,6 +30,21 @@ function ManualGearbox:initialize(...)
|
|||||||
self:shift(val)
|
self:shift(val)
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
||||||
function ManualGearbox:recalcRatio()
|
function ManualGearbox:recalcRatio()
|
||||||
@ -36,19 +61,10 @@ function ManualGearbox:shift(dir)
|
|||||||
self:setGear(self.gear + dir)
|
self:setGear(self.gear + dir)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- function ManualGearbox:getOutputs()
|
function ManualGearbox:forwardStep(torque, inertia)
|
||||||
-- return {
|
local result = Gearbox.forwardStep(self, torque, inertia)
|
||||||
-- Gearbox_RPM = 'number',
|
|
||||||
-- Gearbox_Torque = 'number'
|
|
||||||
-- }
|
|
||||||
-- end
|
|
||||||
|
|
||||||
-- function ManualGearbox:updateOutputs()
|
|
||||||
|
|
||||||
-- end
|
|
||||||
|
|
||||||
function ManualGearbox:update()
|
|
||||||
Gearbox.update(self)
|
|
||||||
|
|
||||||
self.shiftWatcher()
|
self.shiftWatcher()
|
||||||
|
|
||||||
|
return result
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,99 +0,0 @@
|
|||||||
-- @server
|
|
||||||
-- @include /koptilnya/libs/constants.txt
|
|
||||||
-- @include /koptilnya/libs/wire_component.txt
|
|
||||||
require('/koptilnya/libs/constants.txt')
|
|
||||||
require('/koptilnya/libs/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
|
|
||||||
49
koptilnya/engine_remastered/powertrain_component.txt
Normal file
49
koptilnya/engine_remastered/powertrain_component.txt
Normal file
@ -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
|
||||||
@ -1,75 +1,80 @@
|
|||||||
-- Core components
|
--@server
|
||||||
--
|
--@include ./engine.txt
|
||||||
-- @include ./engine.txt
|
--@include ./clutch.txt
|
||||||
-- @include ./clutch.txt
|
--@include ./differential.txt
|
||||||
-- @include ./differential.txt
|
--@include ./factories/gearbox.txt
|
||||||
-- @include ./novojiloff_diff.txt
|
--@include /koptilnya/libs/table.txt
|
||||||
--
|
--@include /koptilnya/libs/constants.txt
|
||||||
-- Helpers & stuff
|
|
||||||
-- @include ./factories/gearbox.txt
|
|
||||||
-- @include /koptilnya/libs/table.txt
|
|
||||||
-- @include /koptilnya/libs/constants.txt
|
|
||||||
require('./engine.txt')
|
require('./engine.txt')
|
||||||
require('./clutch.txt')
|
require('./clutch.txt')
|
||||||
--require('./differential.txt')
|
require('./differential.txt')
|
||||||
require('./novojiloff_diff.txt')
|
|
||||||
require('./factories/gearbox.txt')
|
require('./factories/gearbox.txt')
|
||||||
|
|
||||||
require('/koptilnya/libs/table.txt')
|
require('/koptilnya/libs/table.txt')
|
||||||
require('/koptilnya/libs/constants.txt')
|
require('/koptilnya/libs/constants.txt')
|
||||||
|
|
||||||
Vehicle = class('Vehicle')
|
Vehicle = class('Vehicle')
|
||||||
|
|
||||||
function Vehicle:initialize(config)
|
function Vehicle:initialize(config)
|
||||||
-- should probably validate config here
|
|
||||||
if config == nil then
|
if config == nil then
|
||||||
throw('Vehicle config not provided')
|
throw('Vehicle config not provided')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
self.engine = Engine:new(config.Engine)
|
||||||
self.clutch = Clutch:new(config.Clutch)
|
self.clutch = Clutch:new(config.Clutch)
|
||||||
self.engine = Engine:new(config.Engine, self.clutch)
|
self.gearbox = GearboxFactory.create(config.Gearbox)
|
||||||
|
self.axles = table.map(config.Axles, function(config, idx)
|
||||||
local axleOrder = 0
|
return Differential:new(config, idx)
|
||||||
self.axles = table.map(config.Axles, function(config)
|
|
||||||
axleOrder = axleOrder + 1
|
|
||||||
return Differential:new(config, axleOrder)
|
|
||||||
end)
|
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)
|
self:linkComponents()
|
||||||
-- return SystemsFactory.create(config)
|
self:createIO()
|
||||||
-- 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)
|
|
||||||
|
|
||||||
|
hook.add('tick', 'vehicle_update', function()
|
||||||
|
self:update()
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Vehicle:createIO()
|
||||||
local inputs = {
|
local inputs = {
|
||||||
Base = "entity"
|
Base = "entity"
|
||||||
}
|
}
|
||||||
local outputs = {}
|
local outputs = {}
|
||||||
|
|
||||||
for _, comp in ipairs(self.components) do
|
for _, component in ipairs(self.components) do
|
||||||
inputs = table.merge(inputs, comp:getInputs())
|
inputs = table.merge(inputs, component.wireInputs)
|
||||||
outputs = table.merge(outputs, comp:getOutputs())
|
outputs = table.merge(outputs, component.wireOutputs)
|
||||||
end
|
end
|
||||||
|
|
||||||
wire.adjustPorts(inputs, outputs)
|
wire.adjustPorts(inputs, outputs)
|
||||||
|
end
|
||||||
hook.add('tick', 'vehicle_update', function()
|
|
||||||
local base = wire.ports.Base
|
function Vehicle:linkComponents()
|
||||||
|
self.engine.output = self.clutch
|
||||||
for _, comp in pairs(self.components) do
|
self.clutch.output = self.gearbox
|
||||||
comp:update()
|
self.gearbox.output = self.axles[1]
|
||||||
comp:updateOutputs()
|
|
||||||
end
|
self.axles[1].input = self.gearbox
|
||||||
|
self.gearbox.input = self.clutch
|
||||||
if isValid(base) and (self.clutch:getPress() == 1 or self.gearbox.ratio == 0) then
|
self.clutch.input = self.engine
|
||||||
local tiltForce = self.engine.torque * (-1 + self.engine.masterThrottle * 2)
|
end
|
||||||
|
|
||||||
base:applyForceOffset(base:getUp() * tiltForce, base:getMassCenter() + base:getRight() * UNITS_PER_METER)
|
function Vehicle:update()
|
||||||
base:applyForceOffset(-base:getUp() * tiltForce, base:getMassCenter() - base:getRight() * UNITS_PER_METER)
|
local base = wire.ports.Base
|
||||||
end
|
|
||||||
end)
|
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
|
end
|
||||||
|
|||||||
97
koptilnya/engine_remastered/wheel.txt
Normal file
97
koptilnya/engine_remastered/wheel.txt
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
--@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.rollingResistance = config.RollingResistance or -50
|
||||||
|
self.direction = config.Direction or 1
|
||||||
|
|
||||||
|
self.wireInputs = {
|
||||||
|
[prefix .. 'Wheel'] = 'entity'
|
||||||
|
}
|
||||||
|
self.wireOutputs = {
|
||||||
|
[prefix .. 'WheelRPM'] = 'number',
|
||||||
|
[prefix .. 'WheelTorque'] = '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(Vector(7.7))
|
||||||
|
--self.entity:setInertia(calculateWheelInertia(val))
|
||||||
|
end
|
||||||
|
|
||||||
|
self.inertia = self.entity:getInertia().y
|
||||||
|
end
|
||||||
|
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()
|
||||||
|
return self.angularVelocity
|
||||||
|
end
|
||||||
|
|
||||||
|
function Wheel:forwardStep(torque, inertia)
|
||||||
|
if not isValid(self.entity) then
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
local initAngularVelocity = self.angularVelocity
|
||||||
|
|
||||||
|
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 tq = (self.angularVelocity - initAngularVelocity) * inertia * TICK_INTERVAL
|
||||||
|
|
||||||
|
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
|
||||||
9
koptilnya/engine_remastered/wire_component.txt
Normal file
9
koptilnya/engine_remastered/wire_component.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
WireComponent = class('WireComponent')
|
||||||
|
|
||||||
|
function WireComponent:initialize()
|
||||||
|
self.wireInputs = {}
|
||||||
|
self.wireOutputs = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
function WireComponent:updateWireOutputs()
|
||||||
|
end
|
||||||
@ -6,3 +6,4 @@ TICK_INTERVAL = game.getTickInterval()
|
|||||||
RAD_TO_RPM = 9.5493
|
RAD_TO_RPM = 9.5493
|
||||||
RPM_TO_RAD = 0.10472
|
RPM_TO_RAD = 0.10472
|
||||||
UNITS_PER_METER = 39.37
|
UNITS_PER_METER = 39.37
|
||||||
|
UNITS_TO_METERS = 0.01905
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
|
--@include ./constants.txt
|
||||||
|
|
||||||
|
require('./constants.txt')
|
||||||
|
|
||||||
function getLocalVelocity(entity)
|
function getLocalVelocity(entity)
|
||||||
if not entity:isValid() then
|
if not entity:isValid() then
|
||||||
return
|
return
|
||||||
@ -5,3 +9,11 @@ function getLocalVelocity(entity)
|
|||||||
|
|
||||||
return entity:worldToLocal(entity:getVelocity() + entity:getPos())
|
return entity:worldToLocal(entity:getVelocity() + entity:getPos())
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function calculateWheelInertia(entity)
|
||||||
|
if not isValid(entity) then
|
||||||
|
return Vector(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
return entity:getInertia():setY(0.5 * entity:getMass() * math.pow(entity:getModelRadius() * UNITS_TO_METERS, 2))
|
||||||
|
end
|
||||||
|
|||||||
@ -1,12 +1,9 @@
|
|||||||
WireComponent = class('WireComponent')
|
WireComponent = class('WireComponent')
|
||||||
|
|
||||||
function WireComponent:getInputs()
|
function WireComponent:initialize()
|
||||||
return {}
|
self.wireInputs = {}
|
||||||
|
self.wireOutputs = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
function WireComponent:getOutputs()
|
function WireComponent:updateWireOutputs()
|
||||||
return {}
|
|
||||||
end
|
|
||||||
|
|
||||||
function WireComponent:updateOutputs()
|
|
||||||
end
|
end
|
||||||
|
|||||||
100
koptilnya/suspension/main.txt
Normal file
100
koptilnya/suspension/main.txt
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
--@name apply force suspension
|
||||||
|
--@author Koptilnya1337
|
||||||
|
--@shared
|
||||||
|
|
||||||
|
local _restLength = 26.0
|
||||||
|
local _springTravel = 20.0
|
||||||
|
local _springStiffness = 45000
|
||||||
|
local _compressionDamper = 250000 -- 45000
|
||||||
|
local _reboundDamper = 150000 -- 196000
|
||||||
|
local _maxForce = 800000
|
||||||
|
|
||||||
|
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 _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()
|
||||||
|
local baseDirection = direction * Vector(0, 0, math.clamp(base:getUp().z, 0, 1))
|
||||||
|
|
||||||
|
lastLength = distance
|
||||||
|
|
||||||
|
base:applyForceOffset(-force * baseDirection, wheel:getPos())
|
||||||
|
wheel:applyForceCenter(force * direction)
|
||||||
|
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
|
||||||
Loading…
x
Reference in New Issue
Block a user