27 | function WheelsUtil.registerTireType(name, frictionCoeffs, frictionCoeffsWet) |
28 | local tireType = WheelsUtil.getTireType(name); |
29 | if tireType ~= nil then |
30 | print("Warning: Adding duplicate tire type '"..name.."'"); |
31 | return; |
32 | end |
33 | |
34 | local function getNoNilCoeffs(frictionCoeffs) |
35 | local localCoeffs = {}; |
36 | if frictionCoeffs[1] == nil then |
37 | localCoeffs[1] = 1.15; |
38 | for i=2,WheelsUtil.NUM_GROUNDS do |
39 | if frictionCoeffs[i] ~= nil then |
40 | localCoeffs[1] = frictionCoeffs[i]; |
41 | break; |
42 | end |
43 | end |
44 | else |
45 | localCoeffs[1] = frictionCoeffs[1]; |
46 | end |
47 | for i=2,WheelsUtil.NUM_GROUNDS do |
48 | localCoeffs[i] = frictionCoeffs[i] or frictionCoeffs[i-1]; |
49 | end |
50 | return localCoeffs |
51 | end |
52 | |
53 | local tireType = {}; |
54 | tireType.name = name; |
55 | tireType.frictionCoeffs = getNoNilCoeffs(frictionCoeffs); |
56 | tireType.frictionCoeffsWet = getNoNilCoeffs(frictionCoeffsWet or frictionCoeffs); |
57 | table.insert(WheelsUtil.tireTypes, tireType); |
58 | end |
137 | function WheelsUtil.updateWheelsPhysics(self, dt, currentSpeed, acceleration, doHandbrake, requiredDriveMode) |
138 | local accelerationPedal = 0; |
139 | local brakePedal = 0; |
140 | |
141 | if not g_currentMission.missionInfo.stopAndGoBraking then |
142 | |
143 | self.hasStopped = Utils.getNoNil(self.hasStopped, false); |
144 | self.nextMovingDirection = Utils.getNoNil(self.nextMovingDirection, 0); |
145 | |
146 | if math.abs(acceleration) < 0.001 and math.abs(self.lastSpeedAcceleration) < 0.0001 and math.abs(self.lastSpeedReal) < 0.0001 and math.abs(self.lastMovedDistance) < 0.001 then |
147 | self.hasStopped = true; |
148 | accelerationPedal = 0; |
149 | brakePedal = 1; |
150 | elseif math.abs(self.lastSpeedReal) > 0.0001 then |
151 | self.hasStopped = false; |
152 | end |
153 | |
154 | if self.hasStopped and math.abs(acceleration) > 0.001 then |
155 | self.nextMovingDirection = acceleration; |
156 | end |
157 | if self.nextMovingDirection * acceleration > 0.001 then |
158 | accelerationPedal = acceleration; |
159 | brakePedal = 0; |
160 | elseif not self.hasStopped then |
161 | accelerationPedal = 0; |
162 | if self.nextMovingDirection == 0 then |
163 | brakePedal = 1; |
164 | else |
165 | --brakePedal = math.abs(acceleration); |
166 | |
167 | if math.abs(acceleration) < 0.001 then |
168 | if currentSpeed < self.motor.lowBrakeForceSpeedLimit or doHandbrake then |
169 | if math.abs(self.rotatedTime) < 0.01 or self.articulatedAxis == nil then |
170 | brakePedal = 1; |
171 | end |
172 | else |
173 | brakePedal = self.motor.lowBrakeForceScale; |
174 | end |
175 | else |
176 | brakePedal = math.abs(acceleration); |
177 | end |
178 | |
179 | end |
180 | end |
181 | |
182 | else |
183 | |
184 | local brakeAcc = false; |
185 | if (self.movingDirection*currentSpeed*Utils.sign(acceleration*self.reverserDirection)) < -0.0003 then -- 0.0003 * 3600 = 1.08 km/h |
186 | -- do we want to accelerate in the opposite direction of the vehicle speed? |
187 | brakeAcc = true; |
188 | end; |
189 | |
190 | if math.abs(acceleration) < 0.001 then |
191 | accelerationPedal = 0; |
192 | |
193 | if currentSpeed < self.motor.lowBrakeForceSpeedLimit or doHandbrake then |
194 | if math.abs(self.rotatedTime) < 0.01 or self.articulatedAxis == nil then |
195 | brakePedal = 1; |
196 | end; |
197 | else |
198 | brakePedal = self.motor.lowBrakeForceScale; |
199 | end; |
200 | else |
201 | if not brakeAcc then |
202 | accelerationPedal = acceleration; |
203 | brakePedal = 0; |
204 | else |
205 | accelerationPedal = 0; |
206 | brakePedal = math.abs(acceleration); |
207 | end; |
208 | end; |
209 | |
210 | end |
211 | |
212 | local automaticBrake = self.motor.lowBrakeForceScale == brakePedal; |
213 | |
214 | if g_currentMission.missionInfo.stopAndGoBraking then |
215 | self:setBrakeLightsVisibility(not automaticBrake and math.abs(brakePedal) > 0 and currentSpeed > 0.0006); |
216 | else |
217 | self:setBrakeLightsVisibility(not automaticBrake and math.abs(brakePedal) > 0 and not self.hasStopped); |
218 | end |
219 | |
220 | self:setReverseLightsVisibility(self.movingDirection < 0 and (currentSpeed > 0.0006 or accelerationPedal < 0) and self.reverserDirection == 1); |
221 | |
222 | self.motor:updateMotorRpm(dt); |
223 | self.motor:updateGear(accelerationPedal, dt); |
224 | |
225 | local absAccelerationPedal = math.abs(accelerationPedal); |
226 | local wheelDriveTorque = 0; |
227 | |
228 | if next(self.differentials) ~= nil and self.motorizedNode ~= nil then |
229 | --print(string.format("set vehicle props: %.2fkN %.1frpm ratio: %.1f", self.motor:getTorque(accelerationPedal, false), self.motor:getCurMaxRpm(), self.motor:getGearRatio())); |
230 | local maxRotSpeed = self.motor:getCurMaxRpm() * math.pi / 30; |
231 | local torque = self.motor:getTorque(accelerationPedal * self.reverserDirection, false); |
232 | |
233 | setVehicleProps(self.motorizedNode, torque, maxRotSpeed, self.motor:getGearRatio() * self.reverserDirection, self.motor:getMaxClutchTorque(), self.motor:getRotInertia(), self.motor:getDampingRate()); |
234 | else |
235 | local numTouching = 0; |
236 | local numNotTouching = 0; |
237 | local numHandbrake = 0; |
238 | |
239 | for _, wheel in pairs(self.wheels) do |
240 | if wheel.driveMode >= requiredDriveMode then |
241 | if doHandbrake and wheel.hasHandbrake then |
242 | numHandbrake = numHandbrake +1; |
243 | else |
244 | if wheel.hasGroundContact then |
245 | numTouching = numTouching+1; |
246 | else |
247 | numNotTouching = numNotTouching+1; |
248 | end |
249 | end |
250 | end |
251 | end |
252 | |
253 | if numTouching > 0 and absAccelerationPedal > 0.01 then |
254 | local axisTorque, brakePedalMotor = WheelsUtil.getWheelTorque(self, accelerationPedal); |
255 | if axisTorque ~= 0 then |
256 | wheelDriveTorque = axisTorque / (numTouching+numNotTouching); --*0.7); |
257 | else |
258 | brakePedal = brakePedalMotor; |
259 | end |
260 | end |
261 | end |
262 | |
263 | local doBrake = brakePedal > 0; |
264 | for _, implement in pairs(self.attachedImplements) do |
265 | if implement.object ~= nil then |
266 | if doBrake then |
267 | implement.object:onBrake(brakePedal); |
268 | else |
269 | implement.object:onReleaseBrake(); |
270 | end |
271 | end |
272 | end |
273 | |
274 | for _, wheel in pairs(self.wheels) do |
275 | WheelsUtil.updateWheelPhysics(self, wheel, doHandbrake, wheelDriveTorque, brakePedal, requiredDriveMode, dt) |
276 | end |
277 | |
278 | end |
335 | function WheelsUtil.updateWheelSteeringAngle(self, wheel, dt) |
336 | local steeringAngle = wheel.steeringAngle; |
337 | local rotatedTime = self.rotatedTime; |
338 | if wheel.steeringAxleScale ~= 0 then |
339 | steeringAngle = Utils.clamp(self.steeringAxleAngle * wheel.steeringAxleScale, wheel.steeringAxleRotMin, wheel.steeringAxleRotMax); |
340 | elseif wheel.versatileYRot and self:getIsVersatileYRotActive(wheel) then |
341 | if self.isServer then |
342 | if wheel.forceVersatility or wheel.hasGroundContact then |
343 | steeringAngle = Utils.getVersatileRotation(wheel.repr, wheel.node, dt, wheel.positionX, wheel.positionY, wheel.positionZ, wheel.steeringAngle, wheel.rotMin, wheel.rotMax); |
344 | end |
345 | end |
346 | elseif wheel.rotSpeed ~= nil and wheel.rotMax ~= nil and wheel.rotMin ~= nil then |
347 | if rotatedTime > 0 or wheel.rotSpeedNeg == nil then |
348 | steeringAngle = rotatedTime * wheel.rotSpeed; |
349 | else |
350 | steeringAngle = rotatedTime * wheel.rotSpeedNeg; |
351 | end |
352 | if steeringAngle > wheel.rotMax then |
353 | steeringAngle = wheel.rotMax; |
354 | elseif steeringAngle < wheel.rotMin then |
355 | steeringAngle = wheel.rotMin; |
356 | end |
357 | if self.updateSteeringAngle ~= nil then |
358 | steeringAngle = self:updateSteeringAngle(wheel, dt, steeringAngle); |
359 | end |
360 | end |
361 | wheel.steeringAngle = steeringAngle; |
362 | end |
397 | function WheelsUtil.updateWheelsGraphics(self, dt) |
398 | if self.isServer then |
399 | self.hasWheelGroundContact = false; |
400 | end |
401 | |
402 | for _, wheel in pairs(self.wheels) do |
403 | WheelsUtil.updateWheelSteeringAngle(self, wheel, dt); |
404 | if self.isServer and self.isAddedToPhysics then |
405 | WheelsUtil.updateWheelHasGroundContact(wheel); |
406 | if wheel.hasGroundContact then |
407 | self.hasWheelGroundContact = true; |
408 | end |
409 | |
410 | if wheel.updateWheel then |
411 | local x,y,z, xDrive, suspensionLength = getWheelShapePosition(wheel.node, wheel.wheelShape); |
412 | xDrive = xDrive + wheel.xDriveOffset |
413 | WheelsUtil.updateWheelGraphics(self, wheel, x, y, z, xDrive, suspensionLength); |
414 | |
415 | --fill netinfo (on server) |
416 | wheel.netInfo.x = x; |
417 | wheel.netInfo.y = y; |
418 | wheel.netInfo.z = z; |
419 | wheel.netInfo.xDrive = xDrive; |
420 | wheel.netInfo.suspensionLength = suspensionLength; |
421 | else |
422 | wheel.updateWheel = true |
423 | end |
424 | else |
425 | -- client code |
426 | local x, y, z = wheel.netInfo.x, wheel.netInfo.y, wheel.netInfo.z; |
427 | local xDrive = wheel.netInfo.xDrive; |
428 | local suspensionLength = wheel.netInfo.suspensionLength |
429 | WheelsUtil.updateWheelGraphics(self, wheel, x, y, z, xDrive, suspensionLength); |
430 | end |
431 | end |
432 | end |
442 | function WheelsUtil.updateWheelGraphics(self, wheel, x, y, z, xDrive, suspensionLength) |
443 | local steeringAngle = wheel.steeringAngle; |
444 | if not wheel.showSteeringAngle then |
445 | steeringAngle = 0; |
446 | end |
447 | |
448 | local dirX, dirY, dirZ = 0, -1, 0 |
449 | if wheel.repr == wheel.driveNode then |
450 | setRotation(wheel.repr, xDrive, steeringAngle, 0); |
451 | else |
452 | dirX, dirY, dirZ = localDirectionToLocal(wheel.repr, getParent(wheel.repr), 0, -1, 0); |
453 | setRotation(wheel.repr, 0, steeringAngle, 0); |
454 | setRotation(wheel.driveNode, xDrive, 0, 0); |
455 | end |
456 | |
457 | if wheel.wheelTire ~= nil then |
458 | local x, y, z, _ = getShaderParameter(wheel.wheelTire, "morphPosition"); |
459 | local deformation = Utils.clamp((wheel.deltaY+0.04-suspensionLength)*0.7, 0, wheel.maxDeformation) |
460 | |
461 | setShaderParameter(wheel.wheelTire, "morphPosition", x, y, z, deformation, false); |
462 | |
463 | if wheel.additionalWheels ~= nil then |
464 | for _, additionalWheel in pairs(wheel.additionalWheels) do |
465 | local x, y, z, _ = getShaderParameter(additionalWheel.wheelTire, "morphPosition"); |
466 | setShaderParameter(additionalWheel.wheelTire, "morphPosition", x, y, z, deformation, false); |
467 | end |
468 | end |
469 | |
470 | suspensionLength = suspensionLength+deformation |
471 | end |
472 | |
473 | suspensionLength = suspensionLength - wheel.deltaY |
474 | setTranslation(wheel.repr, wheel.startPositionX + dirX*suspensionLength, wheel.startPositionY + dirY*suspensionLength, wheel.startPositionZ + dirZ*suspensionLength); |
475 | |
476 | if wheel.steeringNode ~= nil then |
477 | local refAngle = wheel.steeringNodeMaxRot; |
478 | local refTrans = wheel.steeringNodeMaxTransX |
479 | local refRot = wheel.steeringNodeMaxRotY; |
480 | if steeringAngle < 0 then |
481 | refAngle = wheel.steeringNodeMinRot; |
482 | refTrans = wheel.steeringNodeMinTransX; |
483 | refRot = wheel.steeringNodeMinRotY; |
484 | end |
485 | local steering = 0; |
486 | if refAngle ~= 0 then |
487 | steering = steeringAngle / refAngle; |
488 | end |
489 | |
490 | if wheel.steeringNodeMinTransX ~= nil then |
491 | local x,y,z = getTranslation(wheel.steeringNode); |
492 | x = refTrans * steering; |
493 | setTranslation(wheel.steeringNode, x, y, z); |
494 | end |
495 | if wheel.steeringNodeMinRotY ~= nil then |
496 | local rotX,rotY,rotZ = getRotation(wheel.steeringNode); |
497 | rotY = refRot * steering; |
498 | setRotation(wheel.steeringNode, rotX, rotY, rotZ); |
499 | end |
500 | end |
501 | |
502 | if wheel.fenderNode ~= nil then |
503 | local angleDif = 0; |
504 | if steeringAngle > wheel.fenderRotMax then |
505 | angleDif = wheel.fenderRotMax - steeringAngle; |
506 | elseif steeringAngle < wheel.fenderRotMin then |
507 | angleDif = wheel.fenderRotMin - steeringAngle; |
508 | end |
509 | setRotation(wheel.fenderNode, 0, angleDif, 0); |
510 | end |
511 | end |