144 | function 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 |
196 | end |
492 | function 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 |
584 | end |
432 | function 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 |
482 | end |
205 | function 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) |
336 | end |
369 | function 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 |
402 | end |