LUADOC - Farming Simulator 19

WheelsUtil

Description
Wheels util Util class to manage wheels of a vehicle
Functions

computeDifferentialRotSpeedNonMotor

Description
Compute differential rot speed from properties of vehicle other than the motor, e.g. rot speed of wheels or linear speed of vehicle
Definition
computeDifferentialRotSpeedNonMotor()
Return Values
floatdiffRotSpeedrot speed [rad/sec]
Code
407function WheelsUtil.computeDifferentialRotSpeedNonMotor(self)
408 if self.isServer and self.spec_wheels ~= nil and #self.spec_wheels.wheels ~= 0 then
409 local wheelSpeed = 0
410 local numWheels = 0
411 for _, wheel in pairs(self.spec_wheels.wheels) do
412 local axleSpeed = getWheelShapeAxleSpeed(wheel.node, wheel.wheelShape) -- rad/sec
413 if wheel.hasGroundContact then
414 wheelSpeed = wheelSpeed + axleSpeed * wheel.radius
415 numWheels = numWheels+1
416 end
417 end
418
419 if numWheels > 0 then
420 return wheelSpeed/numWheels
421 end
422 return 0
423 else
424 -- v = w*r => w = v/r
425 -- differentials have embeded gear so that r can be considered 1
426 return self.lastSpeedReal*1000
427 end
428end

getGroundType

Description
Get ground type
Definition
getGroundType(boolean isField, boolean isRoad, float depth)
Arguments
booleanisFieldis on field
booleanisRoadis on road
floatdepthdepth of terrain
Return Values
IntegergroundTypeground type
Code
607function WheelsUtil.getGroundType(isField, isRoad, depth)
608 -- terrain softness:
609 -- [ 0, 0.1]: road
610 -- [0.1, 0.8]: hard terrain
611 -- [0.8, 1 ]: soft terrain
612 if isField then
613 return WheelsUtil.GROUND_FIELD
614 elseif isRoad or depth < 0.1 then
615 return WheelsUtil.GROUND_ROAD
616 else
617 if depth > 0.8 then
618 return WheelsUtil.GROUND_SOFT_TERRAIN
619 else
620 return WheelsUtil.GROUND_HARD_TERRAIN
621 end
622 end
623end

getSmoothedAcceleratorAndBrakePedals

Description
Definition
getSmoothedAcceleratorAndBrakePedals()
Code
144function WheelsUtil.getSmoothedAcceleratorAndBrakePedals(self, acceleratorPedal, brakePedal, dt)
145
146 if self.wheelsUtilSmoothedAcceleratorPedal == nil then
147 self.wheelsUtilSmoothedAcceleratorPedal = 0
148 end
149
150 local appliedAcc = 0
151 if acceleratorPedal > 0 then
152 if acceleratorPedal > self.wheelsUtilSmoothedAcceleratorPedal then
153 appliedAcc = math.min(math.max(self.wheelsUtilSmoothedAcceleratorPedal + 0.001*dt, 0.001), acceleratorPedal) -- full acceleration in 1sec
154 else
155 appliedAcc = acceleratorPedal
156 end
157 self.wheelsUtilSmoothedAcceleratorPedal = appliedAcc
158 elseif acceleratorPedal < 0 then
159 if acceleratorPedal < self.wheelsUtilSmoothedAcceleratorPedal then
160 appliedAcc = math.max(math.min(self.wheelsUtilSmoothedAcceleratorPedal - 0.001*dt, -0.001), acceleratorPedal) -- full acceleration in 1sec
161 else
162 appliedAcc = acceleratorPedal
163 end
164 self.wheelsUtilSmoothedAcceleratorPedal = appliedAcc
165 else
166 -- Decrease smoothed acceleration towards 0 with different speeds based on if we are braking
167 local decSpeed = 0.0005 + 0.001 * brakePedal -- scale between 2sec and 0.66s (full brake)
168 if self.wheelsUtilSmoothedAcceleratorPedal > 0 then
169 self.wheelsUtilSmoothedAcceleratorPedal = math.max(self.wheelsUtilSmoothedAcceleratorPedal - decSpeed*dt, 0)
170 else
171 self.wheelsUtilSmoothedAcceleratorPedal = math.min(self.wheelsUtilSmoothedAcceleratorPedal + decSpeed*dt, 0)
172 end
173 end
174
175 if self.wheelsUtilSmoothedBrakePedal == nil then
176 self.wheelsUtilSmoothedBrakePedal = 0
177 end
178
179 local appliedBrake = 0
180 if brakePedal > 0 then
181 if brakePedal > self.wheelsUtilSmoothedBrakePedal then
182 appliedBrake = math.min(self.wheelsUtilSmoothedBrakePedal + 0.0025*dt, brakePedal) -- full brake in 0.4sec
183 else
184 appliedBrake = brakePedal
185 end
186 self.wheelsUtilSmoothedBrakePedal = appliedBrake
187 else
188 -- Decrease smoothed brake towards 0 with different speeds based on if we are accelerating
189 local decSpeed = 0.0005 + 0.001 * acceleratorPedal -- scale between 2sec and 0.66s (full acceleration)
190 self.wheelsUtilSmoothedBrakePedal = math.max(self.wheelsUtilSmoothedBrakePedal - decSpeed*dt, 0)
191 end
192
193 --print(string.format("input: %.2f %.2f applied: %.2f %.2f", acceleratorPedal, brakePedal, appliedAcc, appliedBrake))
194
195 return appliedAcc, appliedBrake
196end

getTireFriction

Description
Returns tire friction
Definition
getTireFriction(Integer tireType, Integer groundType, float wetScale)
Arguments
IntegertireTypetire type index
IntegergroundTypeground type index
floatwetScalewet scale
Return Values
floattireFrictiontire friction
Code
592function WheelsUtil.getTireFriction(tireType, groundType, wetScale)
593 if wetScale == nil then
594 wetScale = 0
595 end
596 local coeff = WheelsUtil.tireTypes[tireType].frictionCoeffs[groundType]
597 local coeffWet = WheelsUtil.tireTypes[tireType].frictionCoeffsWet[groundType]
598 return coeff + (coeffWet-coeff)*wetScale
599end

getTireType

Description
Returns tire type index
Definition
getTireType(string name)
Arguments
stringnamename of tire type
Return Values
Integeriindex of tire type
Code
76function WheelsUtil.getTireType(name)
77 for i, t in pairs(WheelsUtil.tireTypes) do
78 if t.name == name then
79 return i
80 end
81 end
82 return nil
83end

registerTireType

Description
Register new tire type
Definition
registerTireType(string name, table frictionCoeffs, table frictionCoeffsWer)
Arguments
stringnamename of new tire type
tablefrictionCoeffsfriction coeffs
tablefrictionCoeffsWerfriction coeffs wet
Code
29function WheelsUtil.registerTireType(name, frictionCoeffs, frictionCoeffsWet)
30 local tireType = WheelsUtil.getTireType(name)
31 if tireType ~= nil then
32 print("Warning: Adding duplicate tire type '"..name.."'")
33 return
34 end
35
36 local function getNoNilCoeffs(frictionCoeffs)
37 local localCoeffs = {}
38 if frictionCoeffs[1] == nil then
39 localCoeffs[1] = 1.15
40 for i=2,WheelsUtil.NUM_GROUNDS do
41 if frictionCoeffs[i] ~= nil then
42 localCoeffs[1] = frictionCoeffs[i]
43 break
44 end
45 end
46 else
47 localCoeffs[1] = frictionCoeffs[1]
48 end
49 for i=2,WheelsUtil.NUM_GROUNDS do
50 localCoeffs[i] = frictionCoeffs[i] or frictionCoeffs[i-1]
51 end
52 return localCoeffs
53 end
54
55 local tireType = {}
56 tireType.name = name
57 tireType.frictionCoeffs = getNoNilCoeffs(frictionCoeffs)
58 tireType.frictionCoeffsWet = getNoNilCoeffs(frictionCoeffsWet or frictionCoeffs)
59 table.insert(WheelsUtil.tireTypes, tireType)
60end

updateVisualWheel

Description
Update wheel graphics
Definition
updateVisualWheel(table wheel, float x, float y, float z, float x, float suspensionLength)
Arguments
tablewheelwheel
floatxx position
floatyy position
floatzz position
floatxx drive rotation
floatsuspensionLengthlength of suspension
Code
492function WheelsUtil.updateVisualWheel(self, wheel, x, y, z, xDrive, suspensionLength)
493 local changed = false
494
495 local steeringAngle = wheel.steeringAngle
496 if not wheel.showSteeringAngle then
497 steeringAngle = 0
498 end
499
500 local _, oldY, _ = getRotation(wheel.repr)
501 local dirX, dirY, dirZ = localDirectionToLocal(wheel.repr, getParent(wheel.repr), 0, -1, 0)
502 if math.abs(steeringAngle-oldY) > WheelsUtil.STEERING_ANGLE_THRESHOLD then
503 setRotation(wheel.repr, 0, steeringAngle, 0)
504 changed = true
505 end
506
507 local oldX, _, _ = getRotation(wheel.driveNode)
508 if math.abs(xDrive-oldX) > WheelsUtil.STEERING_ANGLE_THRESHOLD then
509 setRotation(wheel.driveNode, xDrive, 0, 0)
510 changed = true
511 end
512
513 if wheel.wheelTire ~= nil then
514 local x, y, z, _ = getShaderParameter(wheel.wheelTire, "morphPosition")
515 local deformation = MathUtil.clamp((wheel.deltaY+0.04-suspensionLength)*0.7, 0, wheel.maxDeformation)
516 if math.abs(deformation - wheel.deformation) > 0.01 then
517 wheel.deformation = deformation
518 setShaderParameter(wheel.wheelTire, "morphPosition", x, y, z, deformation, false)
519
520 if wheel.additionalWheels ~= nil then
521 for _, additionalWheel in pairs(wheel.additionalWheels) do
522 local x, y, z, _ = getShaderParameter(additionalWheel.wheelTire, "morphPosition")
523 setShaderParameter(additionalWheel.wheelTire, "morphPosition", x, y, z, deformation, false)
524 end
525 end
526 changed = true
527 end
528
529 suspensionLength = suspensionLength+deformation
530 end
531
532 suspensionLength = suspensionLength - wheel.deltaY
533
534 if math.abs(wheel.lastMovement-suspensionLength) > WheelsUtil.SUSPENSION_THRESHOLD then
535 local transRatio = wheel.transRatio
536 local movement = suspensionLength * transRatio
537 setTranslation(wheel.repr, wheel.startPositionX + dirX*movement, wheel.startPositionY + dirY*movement, wheel.startPositionZ + dirZ*movement)
538 changed = true
539 if transRatio < 1 then
540 movement = suspensionLength*(1-transRatio)
541 setTranslation(wheel.driveNode, wheel.driveNodeStartPosX + dirX*movement, wheel.driveNodeStartPosY + dirY*movement, wheel.driveNodeStartPosZ + dirZ*movement)
542 end
543
544 wheel.lastMovement = suspensionLength
545 end
546
547 if wheel.steeringNode ~= nil then
548 local refAngle = wheel.steeringNodeMaxRot
549 local refTrans = wheel.steeringNodeMaxTransX
550 local refRot = wheel.steeringNodeMaxRotY
551 if steeringAngle < 0 then
552 refAngle = wheel.steeringNodeMinRot
553 refTrans = wheel.steeringNodeMinTransX
554 refRot = wheel.steeringNodeMinRotY
555 end
556 local steering = 0
557 if refAngle ~= 0 then
558 steering = steeringAngle / refAngle
559 end
560
561 if wheel.steeringNodeMinTransX ~= nil then
562 local x,y,z = getTranslation(wheel.steeringNode)
563 x = refTrans * steering
564 setTranslation(wheel.steeringNode, x, y, z)
565 end
566 if wheel.steeringNodeMinRotY ~= nil then
567 local rotX,rotY,rotZ = getRotation(wheel.steeringNode)
568 rotY = refRot * steering
569 setRotation(wheel.steeringNode, rotX, rotY, rotZ)
570 end
571 end
572
573 if wheel.fenderNode ~= nil then
574 local angleDif = 0
575 if steeringAngle > wheel.fenderRotMax then
576 angleDif = wheel.fenderRotMax - steeringAngle
577 elseif steeringAngle < wheel.fenderRotMin then
578 angleDif = wheel.fenderRotMin - steeringAngle
579 end
580 setRotation(wheel.fenderNode, 0, angleDif, 0)
581 end
582
583 return changed
584end

updateWheelGraphics

Description
Definition
updateWheelGraphics()
Code
432function WheelsUtil.updateWheelGraphics(self, wheel, dt)
433
434 local x,y,z, xDrive, suspensionLength
435
436 if self.isServer and self.isAddedToPhysics then
437 WheelsUtil.updateWheelHasGroundContact(wheel)
438 if wheel.updateWheel then
439 x,y,z, xDrive, suspensionLength = getWheelShapePosition(wheel.node, wheel.wheelShape)
440 xDrive = xDrive + wheel.xDriveOffset
441
442 if wheel.dirtyFlag ~= nil and (wheel.netInfo.x ~= x or wheel.netInfo.z ~= z) then
443 self:raiseDirtyFlags(wheel.dirtyFlag)
444 end
445 --fill netinfo (on server)
446 wheel.netInfo.x = x
447 wheel.netInfo.y = y
448 wheel.netInfo.z = z
449 wheel.netInfo.xDrive = xDrive
450 wheel.netInfo.suspensionLength = suspensionLength
451 else
452 wheel.updateWheel = true
453 end
454 else
455 -- client code
456 x, y, z = wheel.netInfo.x, wheel.netInfo.y, wheel.netInfo.z
457 xDrive = wheel.netInfo.xDrive
458 suspensionLength = wheel.netInfo.suspensionLength
459 end
460
461 if x ~= nil then
462 -- calculate xDriveSpeed
463 if wheel.netInfo.xDriveBefore == nil then
464 wheel.netInfo.xDriveBefore = xDrive
465 end
466
467 local xDriveDiff = xDrive - wheel.netInfo.xDriveBefore
468 if xDriveDiff > math.pi then
469 wheel.netInfo.xDriveBefore = wheel.netInfo.xDriveBefore + (2*math.pi)
470 elseif xDriveDiff < -math.pi then
471 wheel.netInfo.xDriveBefore = wheel.netInfo.xDriveBefore - (2*math.pi)
472 end
473 wheel.netInfo.xDriveDiff = xDrive - wheel.netInfo.xDriveBefore
474 wheel.netInfo.xDriveSpeed = wheel.netInfo.xDriveDiff / (0.001 * dt)
475 wheel.netInfo.xDriveBefore = xDrive
476
477 -- update visual wheel node
478 return WheelsUtil.updateVisualWheel(self, wheel, x, y, z, xDrive, suspensionLength)
479 end
480
481 return false
482end

updateWheelHasGroundContact

Description
Update check if wheel has ground contact
Definition
updateWheelHasGroundContact(table wheel)
Arguments
tablewheelwheel
Code
357function WheelsUtil.updateWheelHasGroundContact(wheel)
358 local x = nil
359 if wheel.wheelShapeCreated then
360 x, _, _ = getWheelShapeContactPoint(wheel.node, wheel.wheelShape)
361 end
362 wheel.hasGroundContact = x ~= nil
363end

updateWheelPhysics

Description
Update wheel physics
Definition
updateWheelPhysics(table wheel, boolean doHandbrake, float brakePedal, float dt)
Arguments
tablewheelwheel
booleandoHandbrakedoHandbrake
floatbrakePedalbrake pedal
floatdtdt
Code
345function WheelsUtil.updateWheelPhysics(self, wheel, brakePedal, dt)
346 WheelsUtil.updateWheelSteeringAngle(self, wheel, dt)
347
348 if self.isServer and self.isAddedToPhysics then
349 local brakeForce = self:getBrakeForce() * brakePedal
350 setWheelShapeProps(wheel.node, wheel.wheelShape, wheel.torque, brakeForce*wheel.brakeFactor, wheel.steeringAngle, wheel.rotationDamping)
351 end
352end

updateWheelsPhysics

Description
Updates wheel physics
Definition
updateWheelsPhysics(float dt, float currentSpeed, float acceleration, boolean doHandbrake, boolean stopAndGoBraking)
Arguments
floatdttime since last call in ms
floatcurrentSpeedsigned current speed (m/ms)
floataccelerationtarget acceleration [-1,1]
booleandoHandbrakedo handbrake
booleanstopAndGoBrakingif false, the acceleration needs to be 0 before a change of direction is allowed
Code
205function WheelsUtil.updateWheelsPhysics(self, dt, currentSpeed, acceleration, doHandbrake, stopAndGoBraking)
206--print("function WheelsUtil.updateWheelsPhysics("..tostring(self)..", "..tostring(dt)..", "..tostring(currentSpeed)..", "..tostring(acceleration)..", "..tostring(doHandbrake)..", "..tostring(stopAndGoBraking))
207
208 local acceleratorPedal = 0
209 local brakePedal = 0
210
211 local reverserDirection = 1
212 if self.spec_drivable ~= nil then
213 reverserDirection = self.spec_drivable.reverserDirection
214 acceleration = acceleration * reverserDirection
215 end
216
217 local motor = self.spec_motorized.motor
218
219 local absCurrentSpeed = math.abs(currentSpeed)
220 local accSign = MathUtil.sign(acceleration)
221
222 self.nextMovingDirection = Utils.getNoNil(self.nextMovingDirection, 0)
223
224 local automaticBrake = false
225
226 if math.abs(acceleration) < 0.001 then
227 automaticBrake = true
228
229 -- Non-stop&go only allows change of direction if the vehicle speed is smaller than 1km/h or the direction has already changed (e.g. because the brakes are not hard enough)
230 if stopAndGoBraking or currentSpeed * self.nextMovingDirection < 0.0003 then
231 self.nextMovingDirection = 0
232 end
233 else
234 -- Disable the known moving direction if the vehicle is driving more than 5km/h (0.0014 * 3600 = 5.04km/h) in the opposite direction
235 if self.nextMovingDirection * currentSpeed < -0.0014 then
236 self.nextMovingDirection = 0
237 end
238
239 -- Continue accelerating if we want to go in the same direction
240 -- or if the vehicle is only moving slowly in the wrong direction (0.0003 * 3600 = 1.08 km/h) and we are allowed to change direction
241 if accSign == self.nextMovingDirection or (currentSpeed * accSign > -0.0003 and (stopAndGoBraking or self.nextMovingDirection == 0)) then
242 acceleratorPedal = acceleration
243 brakePedal = 0
244 self.nextMovingDirection = accSign
245 else
246 acceleratorPedal = 0
247 brakePedal = math.abs(acceleration)
248 if stopAndGoBraking then
249 self.nextMovingDirection = accSign
250 end
251 end
252 end
253
254 if automaticBrake then
255 acceleratorPedal = 0
256 end
257
258 acceleratorPedal = motor:updateGear(acceleratorPedal, dt)
259
260 if motor.gear == 0 and motor.targetGear ~= 0 then
261 -- brake automatically if the vehicle is rolling backwards while shifting
262 if currentSpeed * MathUtil.sign(motor.targetGear) < 0 then
263 automaticBrake = true
264 end
265 end
266
267 if automaticBrake then
268 local isSlow = absCurrentSpeed < motor.lowBrakeForceSpeedLimit
269 local isArticulatedSteering = self.spec_articulatedAxis ~= nil and self.spec_articulatedAxis.componentJoint ~= nil and math.abs(self.rotatedTime) > 0.01
270
271 if (isSlow or doHandbrake) and not isArticulatedSteering then
272 brakePedal = 1
273 else
274 -- interpolate between lowBrakeForce and 1 if speed is below 3.6 km/h
275 local factor = math.min(absCurrentSpeed / 0.001, 1)
276 brakePedal = MathUtil.lerp(1, motor.lowBrakeForceScale, factor)
277 end
278 end
279
280 -- ToDo: move to Lights ?!
281 if self.spec_lights ~= nil then
282 if self.setBrakeLightsVisibility ~= nil then
283 self:setBrakeLightsVisibility(not automaticBrake and math.abs(brakePedal) > 0)
284 end
285
286 if self.setReverseLightsVisibility ~= nil then
287 self:setReverseLightsVisibility((currentSpeed < -0.0006 or acceleratorPedal < 0) and reverserDirection == 1)
288 end
289 end
290
291 acceleratorPedal, brakePedal = WheelsUtil.getSmoothedAcceleratorAndBrakePedals(self, acceleratorPedal, brakePedal, dt)
292
293 local maxSpeed = motor:getMaximumForwardSpeed() * 3.6
294 if self.movingDirection < 0 then
295 maxSpeed = motor:getMaximumBackwardSpeed() * 3.6
296 end
297
298 --active braking if over the speed limit
299 local overSpeedLimit = self:getLastSpeed() - math.min(motor:getSpeedLimit(), maxSpeed)
300 if overSpeedLimit > 0 then
301 brakePedal = math.max(math.min(math.pow(overSpeedLimit+0.8, 2)-1, 1), brakePedal) -- start to brake over 0.2km/h
302 acceleratorPedal = 0.3 * math.max(1 - overSpeedLimit/0.2, 0) * acceleratorPedal -- fadeout the accelerator pedal over 0.2km/h, but immediately reduce to 30% (don't set to 0 directly so that the physics engine can still compensate if the brakes are too hard)
303 end
304
305 if next(self.spec_motorized.differentials) ~= nil and self.spec_motorized.motorizedNode ~= nil then
306
307 local absAcceleratorPedal = math.abs(acceleratorPedal)
308 local minGearRatio, maxGearRatio = motor:getMinMaxGearRatio()
309
310 local maxSpeed;
311 if maxGearRatio >= 0 then
312 maxSpeed = motor:getMaximumForwardSpeed()
313 else
314 maxSpeed = motor:getMaximumBackwardSpeed()
315 end
316
317 local acceleratorPedalControlsSpeed = false
318 if acceleratorPedalControlsSpeed then
319 maxSpeed = maxSpeed * absAcceleratorPedal
320 if absAcceleratorPedal > 0.001 then
321 absAcceleratorPedal = 1
322 end
323 end
324 maxSpeed = math.min(maxSpeed, motor:getSpeedLimit() / 3.6)
325 local maxAcceleration = motor:getAccelerationLimit()
326 local maxMotorRotAcceleration = motor:getMotorRotationAccelerationLimit()
327 local minMotorRpm, maxMotorRpm = motor:getRequiredMotorRpmRange()
328
329 local neededPtoTorque = PowerConsumer.getTotalConsumedPtoTorque(self) / motor:getPtoMotorRpmRatio();
330
331 --print(string.format("set vehicle props: accPed=%.1f speed=%.1f gearRatio=[%.1f %.1f] rpm=[%.1f %.1f]", absAcceleratorPedal, maxSpeed, minGearRatio, maxGearRatio, minMotorRpm, maxMotorRpm))
332 controlVehicle(self.spec_motorized.motorizedNode, absAcceleratorPedal, maxSpeed, maxAcceleration, minMotorRpm*math.pi/30, maxMotorRpm*math.pi/30, maxMotorRotAcceleration, minGearRatio, maxGearRatio, motor:getMaxClutchTorque(), neededPtoTorque)
333 end
334
335 self:brake(brakePedal)
336end

updateWheelSteeringAngle

Description
Update wheel steering angle
Definition
updateWheelSteeringAngle(table wheel, float dt)
Arguments
tablewheelwheel
floatdttime since last call in ms
Code
369function WheelsUtil.updateWheelSteeringAngle(self, wheel, dt)
370
371 local steeringAngle = wheel.steeringAngle
372 local rotatedTime = self.rotatedTime
373
374 if wheel.steeringAxleScale ~= nil and wheel.steeringAxleScale ~= 0 then
375 local steeringAxleAngle = 0
376 if self.spec_attachable ~= nil then
377 steeringAxleAngle = self.spec_attachable.steeringAxleAngle
378 end
379 steeringAngle = MathUtil.clamp(steeringAxleAngle * wheel.steeringAxleScale, wheel.steeringAxleRotMin, wheel.steeringAxleRotMax)
380 elseif wheel.versatileYRot and self:getIsVersatileYRotActive(wheel) then
381 if self.isServer then
382 if wheel.forceVersatility or wheel.hasGroundContact then
383 steeringAngle = Utils.getVersatileRotation(wheel.repr, wheel.node, dt, wheel.positionX, wheel.positionY, wheel.positionZ, wheel.steeringAngle, wheel.rotMin, wheel.rotMax)
384 end
385 end
386 elseif wheel.rotSpeed ~= nil and wheel.rotMax ~= nil and wheel.rotMin ~= nil then
387 if rotatedTime > 0 or wheel.rotSpeedNeg == nil then
388 steeringAngle = rotatedTime * wheel.rotSpeed
389 else
390 steeringAngle = rotatedTime * wheel.rotSpeedNeg
391 end
392 if steeringAngle > wheel.rotMax then
393 steeringAngle = wheel.rotMax
394 elseif steeringAngle < wheel.rotMin then
395 steeringAngle = wheel.rotMin
396 end
397 if self.updateSteeringAngle ~= nil then
398 steeringAngle = self:updateSteeringAngle(wheel, dt, steeringAngle)
399 end
400 end
401 wheel.steeringAngle = steeringAngle
402end