UPDATE
This commit is contained in:
@@ -1,39 +1,65 @@
|
||||
-- @name SX 240
|
||||
-- @author Koptilnya1337
|
||||
-- @server
|
||||
-- @include /koptilnya/engine_remastered/vehicle.txt
|
||||
require('/koptilnya/engine_remastered/vehicle.txt')
|
||||
--@name SX 240
|
||||
--@author Koptilnya
|
||||
--@server
|
||||
--@include /koptilnya/engine_remastered/vehicle.txt
|
||||
|
||||
local Vehicle, POWERTRAIN_COMPONENT = unpack(require('/koptilnya/engine_remastered/vehicle.txt'))
|
||||
|
||||
|
||||
local WheelConfig = {
|
||||
BrakePower = 800,
|
||||
CustomWheel = {}
|
||||
}
|
||||
local FrontWheelsConfig = table.merge(table.copy(WheelConfig), { SteerLock = 10 })
|
||||
local RearWheelsConfig = table.merge(table.copy(WheelConfig), { HandbrakePower = 2000 })
|
||||
|
||||
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
|
||||
{
|
||||
Name = 'Engine',
|
||||
Type = POWERTRAIN_COMPONENT.Engine,
|
||||
Config = {
|
||||
IdleRPM = 900,
|
||||
MaxRPM = 7500,
|
||||
Inertia = 0.151,
|
||||
StartFriction = -10,
|
||||
FrictionCoeff = 0.01,
|
||||
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
|
||||
{
|
||||
Name = 'Clutch',
|
||||
Input = 'Engine',
|
||||
Type = POWERTRAIN_COMPONENT.Clutch,
|
||||
Config = {
|
||||
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
|
||||
{
|
||||
Name = 'Gearbox',
|
||||
Input = 'Clutch',
|
||||
Type = POWERTRAIN_COMPONENT.Gearbox,
|
||||
Config = {
|
||||
Type = 'MANUAL',
|
||||
Inertia = 0.01,
|
||||
Ratios = { 3.626, 2.200, 1.541, 1.213, 1.000, 0.767 },
|
||||
Reverse = 3.437
|
||||
}
|
||||
},
|
||||
Axles = {
|
||||
{
|
||||
{
|
||||
Name = 'Axle1',
|
||||
Input = 'Gearbox',
|
||||
Type = POWERTRAIN_COMPONENT.Differential,
|
||||
Config = {
|
||||
FinalDrive = 3.392,
|
||||
Inertia = 0.01,
|
||||
BiasAB = 0.5,
|
||||
@@ -42,5 +68,27 @@ Vehicle:new({
|
||||
Stiffness = 0.1,
|
||||
SlipTorque = 1000
|
||||
}
|
||||
},
|
||||
{
|
||||
Name = 'WheelFL',
|
||||
Input = 'Axle1',
|
||||
Type = POWERTRAIN_COMPONENT.Wheel,
|
||||
Config = FrontWheelsConfig
|
||||
},
|
||||
{
|
||||
Name = 'WheelFR',
|
||||
Input = 'Axle1',
|
||||
Type = POWERTRAIN_COMPONENT.Wheel,
|
||||
Config = FrontWheelsConfig
|
||||
},
|
||||
{
|
||||
Name = 'WheelRL',
|
||||
Type = POWERTRAIN_COMPONENT.Wheel,
|
||||
Config = RearWheelsConfig
|
||||
},
|
||||
{
|
||||
Name = 'WheelRR',
|
||||
Type = POWERTRAIN_COMPONENT.Wheel,
|
||||
Config = RearWheelsConfig
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
types = {
|
||||
local Types = {
|
||||
MANUAL = 'MANUAL',
|
||||
AUTO = 'AUTOMATIC/ROBOT',
|
||||
CVT = 'CVT'
|
||||
}
|
||||
|
||||
return types
|
||||
return Types
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
return {
|
||||
Engine = 1,
|
||||
Clutch = 2,
|
||||
Gearbox = 3,
|
||||
Differential = 4,
|
||||
Wheel = 5
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
-- @include ../gearboxes/manual.txt
|
||||
require('../gearboxes/manual.txt')
|
||||
-- require('../gearboxes/auto.txt')
|
||||
-- require('../gearboxes/cvt.txt')
|
||||
-- @include ../powertrain/gearboxes/manual.txt
|
||||
|
||||
GearboxFactory = class('GearboxFactory')
|
||||
local ManualGearbox = require('../powertrain/gearboxes/manual.txt')
|
||||
|
||||
function GearboxFactory.create(...)
|
||||
local GearboxFactory = class('GearboxFactory')
|
||||
|
||||
function GearboxFactory.static:create(...)
|
||||
return ManualGearbox:new(...)
|
||||
end
|
||||
|
||||
return GearboxFactory
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
--@include ../powertrain/engine.txt
|
||||
--@include ../powertrain/clutch.txt
|
||||
--@include ../powertrain/differential.txt
|
||||
--@include ../powertrain/wheel.txt
|
||||
--@include ./gearbox.txt
|
||||
--@include ../enums/powertrain_component.txt
|
||||
|
||||
local Engine = require('../powertrain/engine.txt')
|
||||
local Clutch = require('../powertrain/clutch.txt')
|
||||
local Differential = require('../powertrain/differential.txt')
|
||||
local Wheel = require('../powertrain/wheel.txt')
|
||||
local GearboxFactory = require('./gearbox.txt')
|
||||
|
||||
local POWERTRAIN_COMPONENT = require('../enums/powertrain_component.txt')
|
||||
|
||||
local PowertrainComponentFactory = class('PowertrainComponentFactory')
|
||||
|
||||
function PowertrainComponentFactory.static:create(vehicle, type, name, config)
|
||||
local args = { vehicle, name, config }
|
||||
|
||||
if type == POWERTRAIN_COMPONENT.Engine then
|
||||
return Engine:new(unpack(args))
|
||||
elseif type == POWERTRAIN_COMPONENT.Clutch then
|
||||
return Clutch:new(unpack(args))
|
||||
elseif type == POWERTRAIN_COMPONENT.Gearbox then
|
||||
return GearboxFactory:create(unpack(args))
|
||||
elseif type == POWERTRAIN_COMPONENT.Differential then
|
||||
return Differential:new(unpack(args))
|
||||
elseif type == POWERTRAIN_COMPONENT.Wheel then
|
||||
return Wheel:new(unpack(args))
|
||||
end
|
||||
end
|
||||
|
||||
return PowertrainComponentFactory
|
||||
@@ -1,15 +1,14 @@
|
||||
--@include ./powertrain_component.txt
|
||||
--@include ./enums/gearbox.txt
|
||||
--@include /koptilnya/libs/constants.txt
|
||||
|
||||
local PowertrainComponent = require('./powertrain_component.txt')
|
||||
|
||||
require('/koptilnya/libs/constants.txt')
|
||||
require('./powertrain_component.txt')
|
||||
local gearboxTypes = require('./enums/gearbox.txt')
|
||||
|
||||
Clutch = class('Clutch', PowertrainComponent)
|
||||
local Clutch = class('Clutch', PowertrainComponent)
|
||||
|
||||
function Clutch:initialize(config)
|
||||
PowertrainComponent.initialize(self, 'Clutch')
|
||||
function Clutch:initialize(vehicle, name, config)
|
||||
PowertrainComponent.initialize(self, vehicle, name, config)
|
||||
|
||||
self.wireInputs = {
|
||||
Clutch = 'number'
|
||||
@@ -65,3 +64,5 @@ function Clutch:forwardStep(torque, inertia)
|
||||
|
||||
return math.clamp(returnTorque, -self.slipTorque, self.slipTorque)
|
||||
end
|
||||
|
||||
return Clutch
|
||||
@@ -1,22 +1,21 @@
|
||||
--@include /koptilnya/libs/constants.txt
|
||||
--@include ./powertrain_component.txt
|
||||
--@include ./wheel.txt
|
||||
|
||||
|
||||
local PowertrainComponent = require('./powertrain_component.txt')
|
||||
local WheelComponent = require('./wheel.txt')
|
||||
|
||||
require('/koptilnya/libs/constants.txt')
|
||||
require('./powertrain_component.txt')
|
||||
require('./wheel.txt')
|
||||
|
||||
Differential = class('Differential', PowertrainComponent)
|
||||
local Differential = class('Differential', PowertrainComponent)
|
||||
|
||||
function Differential:initialize(config, order)
|
||||
PowertrainComponent.initialize(self, 'Axle')
|
||||
function Differential:initialize(vehicle, name, config)
|
||||
PowertrainComponent.initialize(self, vehicle, name, config)
|
||||
|
||||
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
|
||||
@@ -25,20 +24,20 @@ function Differential:initialize(config, order)
|
||||
self.preload = config.Preload or 10
|
||||
self.stiffness = config.Stiffness or 0.1
|
||||
self.slipTorque = config.SlipTorque or 1000
|
||||
end
|
||||
|
||||
local wheelConfig = {
|
||||
CalculateInertia = true
|
||||
}
|
||||
self.outputA = Wheel:new(wheelConfig, prefix .. 'Left')
|
||||
self.outputB = Wheel:new(table.merge(wheelConfig, { Direction = -1 }), prefix .. 'Right')
|
||||
function Differential:linkComponent(component)
|
||||
if not component:isInstanceOf(PowertrainComponent) then
|
||||
return
|
||||
end
|
||||
|
||||
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)
|
||||
table.merge(self.wireOutputs, self.outputB.wireOutputs)
|
||||
if self.outputA == nil then
|
||||
self.outputA = component
|
||||
component.input = self
|
||||
elseif self.outputB == nil then
|
||||
self.outputB = component
|
||||
component.input = self
|
||||
end
|
||||
end
|
||||
|
||||
function Differential:updateWireOutputs()
|
||||
@@ -58,7 +57,7 @@ function Differential:queryInertia()
|
||||
return self.inertia
|
||||
end
|
||||
|
||||
return self.inertia + self.outputA:queryInertia() + self.outputB:queryInertia()
|
||||
return self.inertia + (self.outputA:queryInertia() + self.outputB:queryInertia()) / math.pow(self.finalDrive, 2)
|
||||
end
|
||||
|
||||
function Differential:queryAngularVelocity(angularVelocity)
|
||||
@@ -100,8 +99,8 @@ function Differential:forwardStep(torque, inertia)
|
||||
self.slipTorque
|
||||
)
|
||||
|
||||
tqA = self.outputA:forwardStep(tqA, inertia * 0.5 + aI)
|
||||
tqB = self.outputB:forwardStep(tqB, inertia * 0.5 + bI)
|
||||
tqA = self.outputA:forwardStep(tqA, inertia * 0.5 * math.pow(self.finalDrive, 2) + aI) / self.finalDrive
|
||||
tqB = self.outputB:forwardStep(tqB, inertia * 0.5 * math.pow(self.finalDrive, 2) + bI) / self.finalDrive
|
||||
|
||||
return (tqA + tqB) / self.finalDrive
|
||||
end
|
||||
@@ -156,3 +155,5 @@ function Differential:_HLSDTorqueSplit(tq, aW, bW, aI, bI, biasAB, preload, stif
|
||||
|
||||
return tq * (1 - biasAB) - dTq, tq * biasAB + dTq
|
||||
end
|
||||
|
||||
return Differential
|
||||
@@ -1,13 +1,14 @@
|
||||
--@include ./powertrain_component.txt
|
||||
--@include /koptilnya/libs/constants.txt
|
||||
|
||||
require('./powertrain_component.txt')
|
||||
local PowertrainComponent = require('./powertrain_component.txt')
|
||||
|
||||
require('/koptilnya/libs/constants.txt')
|
||||
|
||||
Engine = class('Engine', PowertrainComponent)
|
||||
local Engine = class('Engine', PowertrainComponent)
|
||||
|
||||
function Engine:initialize(config, clutch)
|
||||
PowertrainComponent.initialize(self, 'Engine')
|
||||
function Engine:initialize(vehicle, name, config)
|
||||
PowertrainComponent.initialize(self, vehicle, name, config)
|
||||
|
||||
self.wireInputs = {
|
||||
Throttle = 'number'
|
||||
@@ -15,7 +16,10 @@ function Engine:initialize(config, clutch)
|
||||
self.wireOutputs = {
|
||||
Engine_RPM = 'number',
|
||||
Engine_Torque = 'number',
|
||||
Engine_MasterThrottle = 'number'
|
||||
Engine_MasterThrottle = 'number',
|
||||
Engine_ReactTorque = 'number',
|
||||
Engine_ReturnedTorque = 'number',
|
||||
Engine_FinalTorque = 'number'
|
||||
}
|
||||
|
||||
self.idleRPM = config.IdleRPM or 900
|
||||
@@ -31,12 +35,18 @@ function Engine:initialize(config, clutch)
|
||||
self.masterThrottle = 0
|
||||
|
||||
self.limiterTimer = 0
|
||||
|
||||
self.reactTorque = 0
|
||||
self.returnedTorque = 0
|
||||
end
|
||||
|
||||
function Engine:updateWireOutputs()
|
||||
wire.ports.Engine_RPM = self:getRPM()
|
||||
wire.ports.Engine_Torque = self.torque
|
||||
wire.ports.Engine_MasterThrottle = self.masterThrottle
|
||||
wire.ports.Engine_ReactTorque = self.reactTorque
|
||||
wire.ports.Engine_ReturnedTorque = self.returnedTorque
|
||||
wire.ports.Engine_FinalTorque = self.finalTorque
|
||||
end
|
||||
|
||||
function Engine:getThrottle()
|
||||
@@ -72,7 +82,7 @@ function Engine:_generateTorque()
|
||||
return self.torque
|
||||
end
|
||||
|
||||
function Engine:integrateDownwards()
|
||||
function Engine:forwardStep()
|
||||
if self.output == nil then
|
||||
local generatedTorque = self:_generateTorque()
|
||||
|
||||
@@ -88,8 +98,16 @@ function Engine:integrateDownwards()
|
||||
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.reactTorque = reactTorque
|
||||
self.returnedTorque = returnedTorque
|
||||
|
||||
local finalTorque = generatedTorque + reactTorque + returnedTorque
|
||||
|
||||
self.finalTorque = finalTorque
|
||||
|
||||
self.angularVelocity = self.angularVelocity + finalTorque / inertiaSum * TICK_INTERVAL
|
||||
self.angularVelocity = math.max(self.angularVelocity, 0)
|
||||
end
|
||||
|
||||
return Engine
|
||||
@@ -1,11 +1,11 @@
|
||||
--@include ../powertrain_component.txt
|
||||
|
||||
require('../powertrain_component.txt')
|
||||
local PowertrainComponent = require('../powertrain_component.txt')
|
||||
|
||||
Gearbox = class('Gearbox', PowertrainComponent)
|
||||
local Gearbox = class('Gearbox', PowertrainComponent)
|
||||
|
||||
function Gearbox:initialize(config)
|
||||
PowertrainComponent.initialize(self, 'Gearbox')
|
||||
function Gearbox:initialize(vehicle, name, config)
|
||||
PowertrainComponent.initialize(self, vehicle, name, config)
|
||||
|
||||
self.wireInputs = {
|
||||
Upshift = 'number',
|
||||
@@ -53,7 +53,7 @@ function Gearbox:forwardStep(torque, inertia)
|
||||
end
|
||||
|
||||
if self.ratio == 0 then
|
||||
self.output:forwardStep(0, inertia)
|
||||
self.output:forwardStep(0, self.inertia)
|
||||
|
||||
return torque
|
||||
end
|
||||
@@ -62,3 +62,5 @@ function Gearbox:forwardStep(torque, inertia)
|
||||
|
||||
return self.output:forwardStep(self.torque, (inertia + self.inertia) * math.pow(self.ratio, 2)) / self.ratio
|
||||
end
|
||||
|
||||
return Gearbox
|
||||
@@ -1,13 +1,14 @@
|
||||
--@include /koptilnya/libs/watcher.txt
|
||||
--@include ./base.txt
|
||||
|
||||
local BaseGearbox = require('./base.txt')
|
||||
|
||||
require('/koptilnya/libs/watcher.txt')
|
||||
require('./base.txt')
|
||||
|
||||
ManualGearbox = class('ManualGearbox', Gearbox)
|
||||
local ManualGearbox = class('ManualGearbox', BaseGearbox)
|
||||
|
||||
function ManualGearbox:initialize(config)
|
||||
Gearbox.initialize(self, config)
|
||||
function ManualGearbox:initialize(vehicle, name, config)
|
||||
BaseGearbox.initialize(self, vehicle, name, config)
|
||||
|
||||
table.merge(self.wireOutputs, {
|
||||
Gearbox_Gear = 'number'
|
||||
@@ -35,7 +36,7 @@ function ManualGearbox:initialize(config)
|
||||
end
|
||||
|
||||
function ManualGearbox:updateWireOutputs()
|
||||
Gearbox.updateWireOutputs(self)
|
||||
BaseGearbox.updateWireOutputs(self)
|
||||
|
||||
wire.ports.Gearbox_Gear = self.gear
|
||||
end
|
||||
@@ -62,9 +63,11 @@ function ManualGearbox:shift(dir)
|
||||
end
|
||||
|
||||
function ManualGearbox:forwardStep(torque, inertia)
|
||||
local result = Gearbox.forwardStep(self, torque, inertia)
|
||||
local result = BaseGearbox.forwardStep(self, torque, inertia)
|
||||
|
||||
self.shiftWatcher()
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
return ManualGearbox
|
||||
@@ -1,15 +1,20 @@
|
||||
--@include /koptilnya/libs/constants.txt
|
||||
--@include ./wire_component.txt
|
||||
--@include ../wire_component.txt
|
||||
|
||||
local WireComponent = require('../wire_component.txt')
|
||||
|
||||
require('/koptilnya/libs/constants.txt')
|
||||
require('./wire_component.txt')
|
||||
|
||||
PowertrainComponent = class('PowertrainComponent', WireComponent)
|
||||
local PowertrainComponent = class('PowertrainComponent', WireComponent)
|
||||
|
||||
function PowertrainComponent:initialize(vehicle, name, config)
|
||||
config = config or {}
|
||||
|
||||
function PowertrainComponent:initialize(name)
|
||||
WireComponent.initialize(self)
|
||||
|
||||
self.vehicle = vehicle
|
||||
self.name = name or 'PowertrainComponent'
|
||||
self.CONFIG = config
|
||||
self.input = nil
|
||||
self.output = nil
|
||||
|
||||
@@ -18,6 +23,20 @@ function PowertrainComponent:initialize(name)
|
||||
self.torque = 0
|
||||
end
|
||||
|
||||
function PowertrainComponent:start()
|
||||
end
|
||||
|
||||
function PowertrainComponent:linkComponent(component)
|
||||
if not component:isInstanceOf(PowertrainComponent) then
|
||||
return
|
||||
end
|
||||
|
||||
if self.output == nil then
|
||||
self.output = component
|
||||
component.input = self
|
||||
end
|
||||
end
|
||||
|
||||
function PowertrainComponent:getRPM()
|
||||
return self.angularVelocity * RAD_TO_RPM
|
||||
end
|
||||
@@ -47,3 +66,5 @@ function PowertrainComponent:forwardStep(torque, inertia)
|
||||
|
||||
return self.output:forwardStep(self.torque, self.inertia + inertia)
|
||||
end
|
||||
|
||||
return PowertrainComponent
|
||||
122
koptilnya/engine_remastered/powertrain/wheel.txt
Normal file
122
koptilnya/engine_remastered/powertrain/wheel.txt
Normal file
@@ -0,0 +1,122 @@
|
||||
--@include ./powertrain_component.txt
|
||||
--@include ../wheel/wheel.txt
|
||||
--@include /koptilnya/libs/constants.txt
|
||||
--@include /koptilnya/libs/entity.txt
|
||||
|
||||
local PowertrainComponent = require('./powertrain_component.txt')
|
||||
local CustomWheel = require('../wheel/wheel.txt')
|
||||
|
||||
require('/koptilnya/libs/constants.txt')
|
||||
require('/koptilnya/libs/entity.txt')
|
||||
|
||||
local Wheel = class('Wheel', PowertrainComponent)
|
||||
|
||||
function Wheel:initialize(vehicle, name, config)
|
||||
PowertrainComponent.initialize(self, vehicle, name, config)
|
||||
|
||||
self.steerLock = config.SteerLock or 0
|
||||
self.brakePower = config.BrakePower or 0
|
||||
self.handbrakePower = config.HandbrakePower or 0
|
||||
|
||||
self.wireInputs = {
|
||||
[self.name] = 'entity'
|
||||
}
|
||||
self.wireOutputs = {
|
||||
[string.format('%s_RPM', self.name)] = 'number',
|
||||
[string.format('%s_Mz', self.name)] = 'number',
|
||||
[string.format('%s_Fz', self.name)] = 'number'
|
||||
}
|
||||
|
||||
self.entity = config.Entity or NULL_ENTITY
|
||||
self.holo = self:createHolo(self.entity)
|
||||
|
||||
self.customWheel = CustomWheel:new(config.CustomWheel)
|
||||
|
||||
hook.add('input', 'vehicle_wheel_update' .. self.name, function(name, value)
|
||||
if name == self.name then
|
||||
self.entity = value
|
||||
self.customWheel:setEntity(value)
|
||||
|
||||
if isValid(self.holo) then
|
||||
self.holo:remove()
|
||||
end
|
||||
|
||||
self.holo = self:createHolo(self.entity)
|
||||
|
||||
if not config.Radius then
|
||||
self.customWheel.radius = self:getEntityRadius()
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function Wheel:getEntityRadius()
|
||||
if not isValid(self.entity) then
|
||||
return 0
|
||||
end
|
||||
|
||||
return self.entity:getModelRadius() * UNITS_TO_METERS
|
||||
end
|
||||
|
||||
function Wheel:createHolo(entity)
|
||||
if not isValid(entity) then
|
||||
return NULL_ENTITY
|
||||
end
|
||||
|
||||
local holo = holograms.create(entity:getPos(), entity:getAngles(), 'models/props_c17/pulleywheels_small01.mdl')
|
||||
holo:setParent(entity)
|
||||
|
||||
return holo
|
||||
end
|
||||
|
||||
function Wheel:updateWireOutputs()
|
||||
wire.ports[string.format('%s_RPM', self.name)] = self.customWheel:getRPM()
|
||||
wire.ports[string.format('%s_Mz', self.name)] = self.customWheel.mz
|
||||
wire.ports[string.format('%s_Fz', self.name)] = self.customWheel.load
|
||||
end
|
||||
|
||||
function Wheel:queryInertia()
|
||||
return self.customWheel.inertia
|
||||
end
|
||||
|
||||
function Wheel:queryAngularVelocity()
|
||||
return self.angularVelocity
|
||||
end
|
||||
|
||||
function Wheel:forwardStep(torque, inertia)
|
||||
if not isValid(self.customWheel) then
|
||||
return 0
|
||||
end
|
||||
|
||||
self.customWheel.steerAngle = self.vehicle.steer * self.steerLock
|
||||
--self.customWheel.inertia = inertia
|
||||
--self.customWheel:setInertia(inertia)
|
||||
self.customWheel.motorTorque = torque
|
||||
self.customWheel.brakeTorque = math.max(self.brakePower * self.vehicle.brake, self.handbrakePower * self.vehicle.handbrake)
|
||||
|
||||
self.customWheel:update()
|
||||
|
||||
self.angularVelocity = self.customWheel.angularVelocity
|
||||
|
||||
if self.customWheel.hasHit and isValid(self.vehicle.basePhysObject) then
|
||||
local surfaceForceVector = self.customWheel.right * self.customWheel.sideFriction.force + self.customWheel.forward * self.customWheel.forwardFriction.force
|
||||
|
||||
self.vehicle.basePhysObject:applyForceOffset(surfaceForceVector, self.entity:getPos() - Vector(0, 0, self.customWheel.radius))
|
||||
end
|
||||
|
||||
if isValid(self.holo) then
|
||||
local angles = self.holo:getAngles()
|
||||
--local rot = angles:rotateAroundAxis(self.entity:getForward(), nil, 1)
|
||||
local rot = angles:rotateAroundAxis(self.entity:getRight(), nil, -self.angularVelocity * TICK_INTERVAL)
|
||||
--local steer = Angle():rotateAroundAxis(self.entity:getUp(), self.customWheel.steerAngle)
|
||||
--local rot = self.entity:getRight():getQuaternionFromAxis(-self.angularVelocity * TICK_INTERVAL)
|
||||
--local steer = self.entity:getUp():getQuaternionFromAxis(self.steerAngle)
|
||||
|
||||
--self.holo:setAngles(self.entity:localToWorldAngles(Angle(0, 90 - self.customWheel.steerAngle, 0)))
|
||||
self.holo:setAngles(rot)
|
||||
end
|
||||
|
||||
return self.customWheel.counterTorque
|
||||
end
|
||||
|
||||
return Wheel
|
||||
@@ -1,46 +1,86 @@
|
||||
--@server
|
||||
--@include ./engine.txt
|
||||
--@include ./clutch.txt
|
||||
--@include ./differential.txt
|
||||
--@include ./factories/gearbox.txt
|
||||
--@include ./enums/powertrain_component.txt
|
||||
--@include ./factories/powertrain_component.txt
|
||||
--@include /koptilnya/libs/table.txt
|
||||
--@include /koptilnya/libs/constants.txt
|
||||
|
||||
require('./engine.txt')
|
||||
require('./clutch.txt')
|
||||
require('./differential.txt')
|
||||
require('./factories/gearbox.txt')
|
||||
local PowertrainComponentFactory = require('./factories/powertrain_component.txt')
|
||||
local CustomWheel = require('./wheel/wheel.txt')
|
||||
|
||||
local POWERTRAIN_COMPONENT = require('./enums/powertrain_component.txt')
|
||||
|
||||
require('/koptilnya/libs/table.txt')
|
||||
require('/koptilnya/libs/constants.txt')
|
||||
|
||||
Vehicle = class('Vehicle')
|
||||
local Vehicle = class('Vehicle')
|
||||
|
||||
function Vehicle:initialize(config)
|
||||
if config == nil then
|
||||
throw('Vehicle config not provided')
|
||||
if not Vehicle:validateConfig(config) then
|
||||
throw('Please check your vehicle configuration!')
|
||||
end
|
||||
|
||||
self.engine = Engine:new(config.Engine)
|
||||
self.clutch = Clutch:new(config.Clutch)
|
||||
self.gearbox = GearboxFactory.create(config.Gearbox)
|
||||
self.axles = table.map(config.Axles, function(config, idx)
|
||||
return Differential:new(config, idx)
|
||||
end)
|
||||
|
||||
self.components = { self.engine, self.clutch, self.gearbox }
|
||||
table.add(self.components, self.axles)
|
||||
self.config = config
|
||||
self.components = {}
|
||||
self.independentComponents = {}
|
||||
|
||||
self:createComponents()
|
||||
self:linkComponents()
|
||||
self:createIO()
|
||||
|
||||
self.rootComponent = self:getRootComponent()
|
||||
self.steer = 0
|
||||
self.brake = 0
|
||||
self.handbrake = 0
|
||||
|
||||
hook.add('input', 'vehicle_wire_input', function(name, value)
|
||||
self:handleWireInput(name, value)
|
||||
end)
|
||||
|
||||
hook.add('tick', 'vehicle_update', function()
|
||||
self:update()
|
||||
end)
|
||||
end
|
||||
|
||||
function Vehicle.static:validateConfig(config)
|
||||
return type(config) == 'table'
|
||||
end
|
||||
|
||||
function Vehicle:getComponentByName(name)
|
||||
return table.find(self.components, function(component)
|
||||
return component.name == name
|
||||
end)
|
||||
end
|
||||
|
||||
function Vehicle:createComponents()
|
||||
for _, componentConfig in pairs(self.config) do
|
||||
local component = PowertrainComponentFactory:create(self, componentConfig.Type, componentConfig.Name, componentConfig.Config)
|
||||
|
||||
table.insert(self.components, component)
|
||||
end
|
||||
end
|
||||
|
||||
function Vehicle:linkComponents()
|
||||
for _, componentConfig in pairs(self.config) do
|
||||
local component = self:getComponentByName(componentConfig.Name)
|
||||
|
||||
if componentConfig.Type == POWERTRAIN_COMPONENT.Wheel and componentConfig.Input == nil then
|
||||
table.insert(self.independentComponents, component)
|
||||
else
|
||||
local inputComponent = self:getComponentByName(componentConfig.Input)
|
||||
|
||||
if inputComponent ~= nil then
|
||||
inputComponent:linkComponent(component)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Vehicle:createIO()
|
||||
local inputs = {
|
||||
Base = "entity"
|
||||
Base = 'entity',
|
||||
Steer = 'number',
|
||||
Brake = 'number',
|
||||
Handbrake = 'number'
|
||||
}
|
||||
local outputs = {}
|
||||
|
||||
@@ -52,29 +92,47 @@ function Vehicle:createIO()
|
||||
wire.adjustPorts(inputs, outputs)
|
||||
end
|
||||
|
||||
function Vehicle:linkComponents()
|
||||
self.engine.output = self.clutch
|
||||
self.clutch.output = self.gearbox
|
||||
self.gearbox.output = self.axles[1]
|
||||
function Vehicle:getRootComponent()
|
||||
return table.find(self.components, function(component)
|
||||
return component.input == nil
|
||||
end)
|
||||
end
|
||||
|
||||
self.axles[1].input = self.gearbox
|
||||
self.gearbox.input = self.clutch
|
||||
self.clutch.input = self.engine
|
||||
function Vehicle:handleWireInput(name, value)
|
||||
if name == 'Base' then
|
||||
self.base = value
|
||||
self.basePhysObject = isValid(value) and value:getPhysicsObject() or nil
|
||||
elseif name == 'Steer' then
|
||||
self.steer = value
|
||||
elseif name == 'Brake' then
|
||||
self.brake = value
|
||||
elseif name == 'Handbrake' then
|
||||
self.handbrake = value
|
||||
end
|
||||
end
|
||||
|
||||
function Vehicle:update()
|
||||
local base = wire.ports.Base
|
||||
|
||||
self.engine:integrateDownwards()
|
||||
self.rootComponent:forwardStep()
|
||||
|
||||
for _, component in pairs(self.independentComponents) do
|
||||
component:forwardStep(0, component:queryInertia())
|
||||
end
|
||||
|
||||
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
|
||||
--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
|
||||
|
||||
return {
|
||||
Vehicle,
|
||||
POWERTRAIN_COMPONENT
|
||||
}
|
||||
|
||||
@@ -1,97 +0,0 @@
|
||||
--@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
|
||||
14
koptilnya/engine_remastered/wheel/friction.txt
Normal file
14
koptilnya/engine_remastered/wheel/friction.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
local Friction = class('Friction')
|
||||
|
||||
function Friction:initialize(config)
|
||||
config = config or {}
|
||||
|
||||
self.slipCoefficient = config.SlipCoefficient or 0.8
|
||||
self.forceCoefficient = config.ForceCoefficient or 1.2
|
||||
|
||||
self.force = 0
|
||||
self.slip = 0
|
||||
self.speed = 0
|
||||
end
|
||||
|
||||
return Friction
|
||||
18
koptilnya/engine_remastered/wheel/friction_preset.txt
Normal file
18
koptilnya/engine_remastered/wheel/friction_preset.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
local FrictionPreset = class('FrictionPreset')
|
||||
|
||||
function FrictionPreset:initialize(config)
|
||||
config = config or {}
|
||||
|
||||
self.B = config.B or 10.86
|
||||
self.C = config.C or 2.15
|
||||
self.D = config.D or 0.933
|
||||
self.E = config.E or 0.992
|
||||
end
|
||||
|
||||
function FrictionPreset:evaluate(slip)
|
||||
local t = math.abs(slip)
|
||||
|
||||
return self.D * math.sin(self.C * math.atan(self.B * t - self.E * (self.B * t - math.atan(self.B * t))))
|
||||
end
|
||||
|
||||
return FrictionPreset
|
||||
313
koptilnya/engine_remastered/wheel/wheel.txt
Normal file
313
koptilnya/engine_remastered/wheel/wheel.txt
Normal file
@@ -0,0 +1,313 @@
|
||||
--@include ./friction.txt
|
||||
--@include ./friction_preset.txt
|
||||
--@include /koptilnya/libs/constants.txt
|
||||
|
||||
local Friction = require('./friction.txt')
|
||||
local FrictionPreset = require('./friction_preset.txt')
|
||||
|
||||
require('/koptilnya/libs/constants.txt')
|
||||
|
||||
local Wheel = class('Wheel')
|
||||
|
||||
function Wheel:initialize(config)
|
||||
config = config or {}
|
||||
|
||||
self.mass = config.Mass or 20
|
||||
self.radius = config.Radius or 0.27
|
||||
self.rollingResistance = config.RollingResistance or 40
|
||||
self.squat = config.Squat or 0.1
|
||||
self.slipCircleShape = config.SlipCircleShape or 1.05
|
||||
|
||||
self.forwardFriction = Friction:new(config.ForwardFriction)
|
||||
self.sideFriction = Friction:new(config.SideFriction)
|
||||
self.frictionPreset = FrictionPreset:new(config.FrictionPreset)
|
||||
self.satFrictionPreset = FrictionPreset:new({ B = 13, C = 2.4, D = 1, E = 0.48 })
|
||||
|
||||
self.motorTorque = 0
|
||||
self.brakeTorque = 0
|
||||
self.steerAngle = 0
|
||||
self.hasHit = false
|
||||
self.counterTorque = 0
|
||||
self.inertia = 0
|
||||
self.load = 0
|
||||
self.angularVelocity = 0
|
||||
self.mz = 0
|
||||
|
||||
self.forward = Vector(0)
|
||||
self.right = Vector(0)
|
||||
self.entity = NULL_ENTITY
|
||||
self.physObj = nil
|
||||
self.baseInertia = 0.5 * self.mass * math.pow(self.radius, 2)
|
||||
self.inertia = self.baseInertia
|
||||
end
|
||||
|
||||
function Wheel:setEntity(entity)
|
||||
self.entity = entity
|
||||
self.entity:setNoDraw(false)
|
||||
|
||||
self.physObj = isValid(entity) and entity:getPhysicsObject() or nil
|
||||
|
||||
if isValid(self.physObj) then
|
||||
self.physObj:setMaterial('friction_00')
|
||||
self.physObj:setMass(self.mass)
|
||||
self.physObj:enableDrag(false)
|
||||
self.physObj:setDragCoefficient(0)
|
||||
self.physObj:setAngleDragCoefficient(0)
|
||||
end
|
||||
end
|
||||
|
||||
function Wheel:setInertia(inertia)
|
||||
if isValid(self.physObj) then
|
||||
self.physObj:setInertia(Vector(inertia))
|
||||
self.inertia = inertia
|
||||
end
|
||||
end
|
||||
|
||||
function Wheel:isValid()
|
||||
return isValid(self.entity) and isValid(self.physObj)
|
||||
end
|
||||
|
||||
function Wheel:getRPM()
|
||||
return self.angularVelocity * RAD_TO_RPM
|
||||
end
|
||||
|
||||
function Wheel:getLongitudinalLoadCoefficient(load)
|
||||
return 11000 * (1 - math.exp(-0.00014 * load));
|
||||
end
|
||||
|
||||
function Wheel:getLateralLoadCoefficient(load)
|
||||
return 18000 * (1 - math.exp(-0.0001 * load));
|
||||
end
|
||||
|
||||
function Wheel:stepLongitudinal(Tm, Tb, Vx, W, Lc, R, I, kFx, kSx)
|
||||
local Winit = W
|
||||
local VxAbs = math.abs(Vx)
|
||||
local Sx = 0
|
||||
|
||||
if VxAbs >= 0.1 then
|
||||
Sx = (Vx - W * R) / VxAbs
|
||||
else
|
||||
Sx = (Vx - W * R) * 0.6
|
||||
end
|
||||
|
||||
Sx = Sx * kSx;
|
||||
Sx = math.clamp(Sx, -1, 1)
|
||||
|
||||
W = W + Tm / I * TICK_INTERVAL
|
||||
|
||||
Tb = Tb * (W > 0 and -1 or 1)
|
||||
local TbCap = math.abs(W) * I / TICK_INTERVAL
|
||||
local Tbr = math.abs(Tb) - math.abs(TbCap)
|
||||
Tbr = math.max(Tbr, 0)
|
||||
Tb = math.clamp(Tb, -TbCap, TbCap)
|
||||
W = W + Tb / I * TICK_INTERVAL
|
||||
|
||||
local maxTorque = self.frictionPreset:evaluate(math.abs(Sx)) * Lc * kFx * R
|
||||
local errorTorque = (W - Vx / R) * I / TICK_INTERVAL
|
||||
local surfaceTorque = math.clamp(errorTorque, -maxTorque, maxTorque)
|
||||
|
||||
W = W - surfaceTorque / I * TICK_INTERVAL
|
||||
local Fx = surfaceTorque / R
|
||||
|
||||
Tbr = Tbr * (W > 0 and -1 or 1)
|
||||
local TbCap2 = math.abs(W) * I / TICK_INTERVAL
|
||||
local Tb2 = math.clamp(Tbr, -TbCap2, TbCap2)
|
||||
W = W + Tb2 / I * TICK_INTERVAL
|
||||
|
||||
local deltaOmegaTorque = (W - Winit) * I / TICK_INTERVAL
|
||||
local Tcnt = -surfaceTorque + Tb + Tb2 - deltaOmegaTorque
|
||||
|
||||
if Lc < 0.001 then
|
||||
Sx = 0
|
||||
end
|
||||
|
||||
return W, Sx, Fx, Tcnt
|
||||
end
|
||||
|
||||
function Wheel:stepLateral(Vx, Vy, Lc, kFy, kSy)
|
||||
local VxAbs = math.abs(Vx)
|
||||
local Sy = 0
|
||||
|
||||
if VxAbs > 0.3 then
|
||||
Sy = math.deg(math.atan(Vy / VxAbs))
|
||||
Sy = Sy / 50
|
||||
else
|
||||
Sy = Vy * (0.003 / TICK_INTERVAL)
|
||||
end
|
||||
|
||||
Sy = Sy * kSy * 0.95
|
||||
Sy = math.clamp(Sy, -1, 1)
|
||||
local slipSign = Sy < 0 and -1 or 1
|
||||
local Fy = -slipSign * self.frictionPreset:evaluate(math.abs(Sy)) * Lc * kFy
|
||||
|
||||
if Lc < 0.0001 then
|
||||
Sy = 0
|
||||
end
|
||||
|
||||
return Sy, Fy
|
||||
end
|
||||
|
||||
function Wheel:slipCircle(Sx, Sy, Fx, Fy, slipCircleShape)
|
||||
local SxAbs = math.abs(Sx)
|
||||
|
||||
if SxAbs > 0.01 then
|
||||
local SxClamped = math.clamp(Sx, -1, 1)
|
||||
local SyClamped = math.clamp(Sy, -1, 1)
|
||||
local combinedSlip = Vector2(
|
||||
SxClamped * slipCircleShape,
|
||||
SyClamped
|
||||
)
|
||||
local slipDir = combinedSlip:getNormalized()
|
||||
|
||||
local F = math.sqrt(Fx * Fx + Fy * Fy)
|
||||
local absSlipDirY = math.abs(slipDir.y)
|
||||
|
||||
Fy = F * absSlipDirY * (Fy < 0 and -1 or 1)
|
||||
end
|
||||
|
||||
return Sx, Sy, Fx, Fy
|
||||
end
|
||||
|
||||
function Wheel:selfAligningTorque(Vx, Vy, Lc)
|
||||
local VxAbs = math.abs(Vx)
|
||||
local Sy = 0
|
||||
|
||||
if VxAbs > 0.3 then
|
||||
Sy = math.deg(math.atan(Vy / VxAbs))
|
||||
else
|
||||
Sy = Vy * (0.003 / TICK_INTERVAL)
|
||||
end
|
||||
|
||||
Sy = math.clamp(Sy, -90, 90)
|
||||
--local phi = (1 - self.E) * Sy + (self.E / self.B) * math.atan(self.B * Sy)
|
||||
--local Mz = -slipSign * self.satFrictionPreset:evaluate(math.abs(phi), self.B, C, self.D, 0) * Lc
|
||||
|
||||
return self.satFrictionPreset:evaluate(math.abs(Sy)) * -math.sign(Sy)
|
||||
end
|
||||
|
||||
function Wheel:selfAligningTorque2(Vx, Vy, Fz)
|
||||
|
||||
if Fz == 0 then
|
||||
return 0
|
||||
end
|
||||
|
||||
local VxAbs = math.abs(Vx)
|
||||
local Sy = 0
|
||||
|
||||
if VxAbs > 0.3 then
|
||||
Sy = math.deg(math.atan(Vy / VxAbs))
|
||||
else
|
||||
Sy = Vy * (0.003 / TICK_INTERVAL)
|
||||
end
|
||||
|
||||
Sy = math.clamp(Sy, -1, 1)
|
||||
|
||||
local a = {
|
||||
-2.72,
|
||||
-2.28,
|
||||
-1.86,
|
||||
-2.73,
|
||||
0.110,
|
||||
-0.070,
|
||||
0.643,
|
||||
-4.04,
|
||||
0.015,
|
||||
-0.066,
|
||||
0.945,
|
||||
0.030,
|
||||
0.070
|
||||
}
|
||||
local FzSqr = math.pow(Fz, 2)
|
||||
|
||||
local C = 2.4
|
||||
local D = a[1] * FzSqr + a[2] * Fz
|
||||
local BCD = (a[3] * FzSqr + a[4] * Fz) / math.pow(math.exp(1), a[5] * Fz)
|
||||
local B = BCD / (C * D)
|
||||
local E = a[6] * FzSqr + a[7] * Fz + a[8]
|
||||
|
||||
local phi = (1 - E) * Sy + (E / B) * math.atan(B * Sy)
|
||||
self.phi = phi
|
||||
|
||||
return D * math.sin(C * math.atan(B * phi))
|
||||
end
|
||||
|
||||
function Wheel:selfAligningTorque3(Mx, My, Sx, Sy)
|
||||
local M = Mx * math.cos(Sy) + My * math.cos(Sx)
|
||||
|
||||
return self.radius * M
|
||||
end
|
||||
|
||||
function Wheel:update()
|
||||
if not isValid(self.entity) or not isValid(self.physObj) then
|
||||
return
|
||||
end
|
||||
|
||||
local externalStress = self.physObj:getStress()
|
||||
local velocity = self.physObj:getVelocity() * UNITS_TO_METERS
|
||||
|
||||
self.hasHit = externalStress ~= 0
|
||||
self.load = externalStress * KG_TO_KN
|
||||
|
||||
self.longitudinalLoadCoefficient = self:getLongitudinalLoadCoefficient(self.load * 1000)
|
||||
self.lateralLoadCoefficient = self:getLateralLoadCoefficient(self.load * 1000)
|
||||
|
||||
--self.steerAngle = self.steerAngle + self.mz / self.inertia
|
||||
|
||||
self.forward = self.entity:getForward():rotateAroundAxis(self.entity:getUp(), -self.steerAngle)
|
||||
self.right = self.entity:getRight():rotateAroundAxis(self.entity:getUp(), -self.steerAngle)
|
||||
|
||||
local forwardSpeed = 0
|
||||
local sideSpeed = 0
|
||||
|
||||
if self.hasHit then
|
||||
forwardSpeed = velocity:dot(self.forward)
|
||||
sideSpeed = velocity:dot(self.right)
|
||||
end
|
||||
|
||||
local W, Sx, Fx, CounterTq = self:stepLongitudinal(
|
||||
self.motorTorque,
|
||||
self.brakeTorque + self.rollingResistance,
|
||||
forwardSpeed,
|
||||
self.angularVelocity,
|
||||
self.longitudinalLoadCoefficient,
|
||||
self.radius,
|
||||
self.inertia,
|
||||
0.95, -- Force coeff
|
||||
0.9 -- Slip coeff
|
||||
)
|
||||
|
||||
local Sy, Fy = self:stepLateral(
|
||||
forwardSpeed,
|
||||
sideSpeed,
|
||||
self.lateralLoadCoefficient,
|
||||
0.95, -- Force coeff
|
||||
0.9 -- Slip coeff
|
||||
|
||||
)
|
||||
|
||||
Sx, Sy, Fx, Fy = self:slipCircle(
|
||||
Sx,
|
||||
Sy,
|
||||
Fx,
|
||||
Fy,
|
||||
1.05 -- Shape of the slip circle / ellipse.
|
||||
)
|
||||
|
||||
--local Mx = Fy * (math.cos(Sx) - 1) + Fx * math.sin(Sx)
|
||||
--local My = Fx * (math.cos(Sy) - 1) + Fy * math.sin(Sy)
|
||||
--local Mz = self:selfAligningTorque3(Mx, My, Sx, Sy)
|
||||
local Mz = self:selfAligningTorque2(forwardSpeed, sideSpeed, self.load)
|
||||
|
||||
self.mz = Mz
|
||||
|
||||
self.angularVelocity = W
|
||||
self.counterTorque = CounterTq
|
||||
self.forwardFriction.slip = Sx
|
||||
self.forwardFriction.force = Fx
|
||||
self.forwardFriction.speed = forwardSpeed
|
||||
self.sideFriction.slip = Sy
|
||||
self.sideFriction.force = Fy
|
||||
self.sideFriction.speed = sideSpeed
|
||||
end
|
||||
|
||||
return Wheel
|
||||
@@ -1,4 +1,4 @@
|
||||
WireComponent = class('WireComponent')
|
||||
local WireComponent = class('WireComponent')
|
||||
|
||||
function WireComponent:initialize()
|
||||
self.wireInputs = {}
|
||||
@@ -7,3 +7,5 @@ end
|
||||
|
||||
function WireComponent:updateWireOutputs()
|
||||
end
|
||||
|
||||
return WireComponent
|
||||
|
||||
Reference in New Issue
Block a user