LUADOC - Farming Simulator 19

Script v1.7.1.0

Engine v1.7.1.0

Foundation Reference

Wheels

Description
Specialization extending a vehicle with wheels, including fruit destruction, wheel chocks, driving particles and tiretracks
Functions

addToPhysics

Description
Definition
addToPhysics()
Code
1329function Wheels:addToPhysics(superFunc)
1330 if not superFunc(self) then
1331 return false
1332 end
1333
1334 local spec = self.spec_wheels
1335
1336 for _, wheel in pairs(spec.wheels) do
1337 wheel.xDriveOffset = wheel.netInfo.xDrive
1338 wheel.updateWheel = false
1339 self:updateWheelBase(wheel)
1340 self:updateWheelTireFriction(wheel)
1341 end
1342 if self.isServer then
1343 local brakeForce = self:getBrakeForce()
1344 for _,wheel in pairs(spec.wheels) do
1345 setWheelShapeProps(wheel.node, wheel.wheelShape, 0, brakeForce*wheel.brakeFactor, wheel.steeringAngle, wheel.rotationDamping)
1346 setWheelShapeAutoHoldBrakeForce(wheel.node, wheel.wheelShape, brakeForce*wheel.autoHoldBrakeFactor)
1347 end
1348
1349 self:brake(brakeForce)
1350
1351 spec.wheelCreationTimer = 2
1352 end
1353
1354 return true
1355end

brake

Description
Definition
brake()
Code
2577function Wheels:brake(brakePedal)
2578 local spec = self.spec_wheels
2579 spec.brakePedal = brakePedal
2580
2581 for _,wheel in pairs(spec.wheels) do
2582 WheelsUtil.updateWheelPhysics(self, wheel, spec.brakePedal, 0)
2583 end
2584
2585 SpecializationUtil.raiseEvent(self, "onBrake", spec.brakePedal)
2586end

deleteVisualWheel

Description
Definition
deleteVisualWheel()
Code
2533function Wheels:deleteVisualWheel(wheel)
2534 if wheel.tireFilename ~= nil then
2535 g_i3DManager:releaseSharedI3DFile(wheel.tireFilename, self.baseDirectory, true)
2536 end
2537 if wheel.innerRimFilename ~= nil then
2538 g_i3DManager:releaseSharedI3DFile(wheel.innerRimFilename, self.baseDirectory, true)
2539 end
2540 if wheel.outerRimFilename ~= nil then
2541 g_i3DManager:releaseSharedI3DFile(wheel.outerRimFilename, self.baseDirectory, true)
2542 end
2543 if wheel.additionalFilename ~= nil then
2544 g_i3DManager:releaseSharedI3DFile(wheel.additionalFilename, self.baseDirectory, true)
2545 end
2546 if wheel.connector ~= nil then
2547 g_i3DManager:releaseSharedI3DFile(wheel.connector.filename, self.baseDirectory, true)
2548 end
2549end

destroyFruitArea

Description
Definition
destroyFruitArea()
Code
2571function Wheels:destroyFruitArea(x0,z0, x1,z1, x2,z2)
2572 FSDensityMapUtil.updateWheelDestructionArea(x0,z0, x1,z1, x2,z2)
2573end

finalizeConnector

Description
Definition
finalizeConnector()
Code
627function Wheels:finalizeConnector(wheel, connector, diameter, baseWheelWidth, wheelDistance, offsetDir, dualWheelWidth)
628 local i3dNode = g_i3DManager:loadSharedI3DFile(connector.filename, self.baseDirectory, false, false, false)
629 if i3dNode ~= 0 then
630 local node = I3DUtil.indexToObject(i3dNode, connector.nodeStr, self.i3dMappings)
631 if node ~= nil then
632 connector.node = node
633 connector.linkNode = wheel.wheelTire
634
635 link(wheel.driveNode, connector.node)
636
637 if not connector.useWidthAndDiam then
638 if getHasShaderParameter(connector.node, "connectorPos") then
639 setShaderParameter(connector.node, "connectorPos", 0, baseWheelWidth, wheelDistance, dualWheelWidth, false)
640 end
641
642 local x,_,z,w = getShaderParameter(connector.node, "widthAndDiam")
643 setShaderParameter(connector.node, "widthAndDiam", x, diameter, z, w, false)
644 else
645 local connectorOffset = offsetDir*(((0.5*baseWheelWidth + 0.5*wheelDistance) * 0.0254) + connector.additionalOffset) -- in meters
646 local connectorDiameter = connector.diameter or diameter
647 setTranslation(connector.node, connectorOffset, 0, 0)
648 setShaderParameter(connector.node, "widthAndDiam", connector.width, connectorDiameter, 0, 0, false)
649 end
650 if connector.usePosAndScale and getHasShaderParameter(connector.node, "connectorPosAndScale") then
651 local _,_,_,w = getShaderParameter(connector.node, "connectorPosAndScale")
652 setShaderParameter(connector.node, "connectorPosAndScale", connector.startPos, connector.endPos, connector.scale, w, false)
653 end
654 if connector.color ~= nil and getHasShaderParameter(connector.node, "colorMat0") then
655 local r, g, b, mat = unpack(connector.color)
656 if mat == nil then
657 _,_,_,mat = getShaderParameter(connector.node, "colorMat0")
658 end
659 setShaderParameter(connector.node, "colorMat0", r, g, b, mat, false)
660 end
661 end
662
663 delete(i3dNode)
664 end
665end

finalizeWheel

Description
load i3d files for wheel, perform checks
Definition
finalizeWheel()
Code
398function Wheels:finalizeWheel(wheel, parentWheel)
399 local spec = self.spec_wheels
400
401 if parentWheel == nil and wheel.repr ~= nil then
402 wheel.startPositionX, wheel.startPositionY, wheel.startPositionZ = getTranslation(wheel.repr)
403 wheel.driveNodeStartPosX, wheel.driveNodeStartPosY, wheel.driveNodeStartPosZ = getTranslation(wheel.driveNode)
404 wheel.dirtAmount = 0
405 wheel.xDriveOffset = 0
406 wheel.lastColor = {0,0,0,0}
407 wheel.lastTerrainAttribute = 0
408 wheel.contact = Wheels.WHEEL_NO_CONTACT
409 wheel.steeringAngle = 0
410 wheel.lastMovement = 0
411 wheel.hasGroundContact = false
412 wheel.hasHandbrake = true
413
414 local vehicleNode = self.vehicleNodes[wheel.node]
415 if vehicleNode ~= nil and vehicleNode.component ~= nil and vehicleNode.component.motorized == nil then
416 vehicleNode.component.motorized = true
417 end
418
419 if wheel.useReprDirection then
420 wheel.directionX, wheel.directionY, wheel.directionZ = localDirectionToLocal(wheel.repr, wheel.node, 0,-1,0)
421 wheel.axleX, wheel.axleY, wheel.axleZ = localDirectionToLocal(wheel.repr, wheel.node, 1,0,0)
422 elseif wheel.useDriveNodeDirection then
423 wheel.directionX, wheel.directionY, wheel.directionZ = localDirectionToLocal(wheel.driveNodeDirectionNode, wheel.node, 0,-1,0)
424 wheel.axleX, wheel.axleY, wheel.axleZ = localDirectionToLocal(wheel.driveNodeDirectionNode, wheel.node, 1,0,0)
425 else
426 wheel.directionX, wheel.directionY, wheel.directionZ = 0,-1,0
427 wheel.axleX, wheel.axleY, wheel.axleZ = 1,0,0
428 end
429 wheel.steeringCenterOffsetX, wheel.steeringCenterOffsetY, wheel.steeringCenterOffsetZ = 0,0,0
430 if wheel.repr ~= wheel.driveNode then
431 wheel.steeringCenterOffsetX, wheel.steeringCenterOffsetY, wheel.steeringCenterOffsetZ = localToLocal(wheel.driveNode, wheel.repr, 0, 0, 0)
432 wheel.steeringCenterOffsetX = -wheel.steeringCenterOffsetX
433 wheel.steeringCenterOffsetY = -wheel.steeringCenterOffsetY
434 wheel.steeringCenterOffsetZ = -wheel.steeringCenterOffsetZ
435 end
436
437 if g_currentMission.tireTrackSystem ~= nil and wheel.hasTireTracks then
438 wheel.tireTrackIndex = g_currentMission.tireTrackSystem:createTrack(wheel.width, wheel.tireTrackAtlasIndex)
439 end
440
441 wheel.maxLatStiffness = wheel.maxLatStiffness*wheel.restLoad
442 wheel.maxLatStiffnessLoad = wheel.maxLatStiffnessLoad*wheel.restLoad
443
444 wheel.mass = wheel.mass + wheel.additionalMass
445
446 wheel.lastTerrainValue = 0
447 wheel.sink = 0
448 wheel.sinkTarget = 0
449 wheel.radiusOriginal = wheel.radius
450 wheel.sinkFrictionScaleFactor = 1
451 wheel.sinkLongStiffnessFactor = 1
452 wheel.sinkLatStiffnessFactor = 1
453
454 local positionY = wheel.positionY+wheel.deltaY
455 wheel.netInfo = {}
456 wheel.netInfo.xDrive = 0
457 wheel.netInfo.x = wheel.positionX
458 wheel.netInfo.y = positionY
459 wheel.netInfo.z = wheel.positionZ
460 wheel.netInfo.suspensionLength = wheel.suspTravel*0.5
461
462 -- The suspension elongates by 20% of the specified susp travel
463 wheel.netInfo.sync = {yMin = -5, yRange = 10}
464 wheel.netInfo.yMin = positionY-1.2*wheel.suspTravel
465
466 --
467 self:updateWheelBase(wheel)
468 self:updateWheelTireFriction(wheel)
469
470 wheel.networkInterpolators = {}
471 wheel.networkInterpolators.xDrive = InterpolatorAngle:new(wheel.netInfo.xDrive)
472 wheel.networkInterpolators.position = InterpolatorPosition:new(wheel.netInfo.x, wheel.netInfo.y, wheel.netInfo.z)
473 wheel.networkInterpolators.suspensionLength = InterpolatorValue:new(wheel.netInfo.suspensionLength)
474 end
475
476 local loadWheelPart = function(wheel, parent, name, filename, index, offset, widthAndDiam, scale)
477 if filename == nil then
478 return
479 end
480
481 local i3dNode = g_i3DManager:loadSharedI3DFile(filename, self.baseDirectory, false, false, false)
482 if i3dNode ~= 0 then
483 wheel[name] = I3DUtil.indexToObject(i3dNode, index, self.i3dMappings)
484 if wheel[name] ~= nil then
485 link(parent, wheel[name])
486 delete(i3dNode)
487
488 if offset ~= 0 then
489 local dir = 1
490 if not wheel.isLeft then
491 dir = -1
492 end
493 setTranslation(wheel[name], offset*dir, 0, 0)
494 end
495 if scale ~= nil then
496 setScale(wheel[name], scale[1], scale[2], scale[3])
497 end
498 if widthAndDiam ~= nil then
499 if getHasShaderParameter(wheel[name], "widthAndDiam") then
500 setShaderParameter(wheel[name], "widthAndDiam", widthAndDiam[1], widthAndDiam[2], 0, 0, false)
501 else
502 -- convert width and diam to scale (mesh is normalized to 1 meter)
503 local scaleX = MathUtil.inchToM(widthAndDiam[1])
504 local scaleZY = MathUtil.inchToM(widthAndDiam[2])
505 setScale(wheel[name], scaleX, scaleZY, scaleZY)
506 end
507 end
508 else
509 g_logManager:xmlWarning(self.configFileName, "Failed to load node '%s' for file '%s'", index, filename)
510 end
511 else
512 g_logManager:xmlWarning(self.configFileName, "Failed to load file '%s' wheel part '%s'", filename, name)
513 end
514 end
515
516 if parentWheel ~= nil then
517 wheel.linkNode = createTransformGroup("linkNode")
518 link(parentWheel.driveNode, wheel.linkNode)
519 end
520
521 loadWheelPart(wheel, wheel.linkNode, "wheelTire", wheel.tireFilename, wheel.tireNodeStr, 0, nil, nil)
522 loadWheelPart(wheel, wheel.linkNode, "wheelOuterRim", wheel.outerRimFilename, wheel.outerRimNodeStr, 0, wheel.outerRimWidthAndDiam, wheel.outerRimScale)
523 loadWheelPart(wheel, wheel.linkNode, "wheelInnerRim", wheel.innerRimFilename, wheel.innerRimNodeStr, wheel.innerRimOffset, wheel.innerRimWidthAndDiam, wheel.innerRimScale)
524 loadWheelPart(wheel, wheel.linkNode, "wheelAdditional", wheel.additionalFilename, wheel.additionalNodeStr, wheel.additionalOffset, wheel.additionalWidthAndDiam, wheel.additionalScale)
525
526 if wheel.wheelTire ~= nil then
527 local zRot = 0
528 if wheel.tireIsInverted then
529 zRot = MathUtil.degToRad(180)
530 end
531 setRotation(wheel.wheelTire, wheel.xRotOffset, 0, zRot)
532
533 local x, y, z, _ = getShaderParameter(wheel.wheelTire, "morphPosition")
534 setShaderParameter(wheel.wheelTire, "morphPosition", x, y, z, 0, false)
535 end
536
537 local configColor = ConfigurationUtil.getColorByConfigId(self, "rimColor", self.configurations["rimColor"])
538 local color = wheel.color or configColor or spec.rimColor
539 if color ~= nil then
540 local r, g, b, mat = unpack(color)
541 if wheel.wheelOuterRim ~= nil then
542 -- never use material from config color since it is always '1'
543 if mat == nil then
544 _,_,_,mat = getShaderParameter(wheel.wheelOuterRim, "colorMat0")
545 end
546 setShaderParameter(wheel.wheelOuterRim, "colorMat0", r, g, b, mat, false)
547 end
548 if wheel.wheelInnerRim ~= nil then
549 -- never use material from config color since it is always '1'
550 if mat == nil then
551 _,_,_,mat = getShaderParameter(wheel.wheelInnerRim, "colorMat0")
552 end
553 setShaderParameter(wheel.wheelInnerRim, "colorMat0", r, g, b, mat, false)
554 end
555 end
556
557 local additionalColor = Utils.getNoNil(wheel.additionalColor, color)
558 if wheel.wheelAdditional ~= nil and additionalColor ~= nil then
559 local r,g,b,_ = unpack(additionalColor)
560 local _,_,_,w = getShaderParameter(wheel.wheelAdditional, "colorMat0")
561 setShaderParameter(wheel.wheelAdditional, "colorMat0", r, g, b, w, false)
562 end
563
564 if wheel.additionalWheels ~= nil then
565 local outmostWheelWidth = 0
566 local totalWheelshapeOffset = 0
567 local offsetDir = -1
568 for _, additionalWheel in pairs(wheel.additionalWheels) do
569 self:finalizeWheel(additionalWheel, wheel)
570
571 local baseWheelWidth = MathUtil.mToInch(wheel.width)
572 local dualWheelWidth = MathUtil.mToInch(additionalWheel.width)
573 local diameter = 0
574 local wheelOffset = MathUtil.mToInch(additionalWheel.offset)
575
576 if wheel.outerRimWidthAndDiam ~= nil then
577 baseWheelWidth = wheel.outerRimWidthAndDiam[1]
578 diameter = wheel.outerRimWidthAndDiam[2]
579 end
580 if additionalWheel.outerRimWidthAndDiam ~= nil then
581 dualWheelWidth = additionalWheel.outerRimWidthAndDiam[1]
582 end
583
584 if additionalWheel.isLeft then
585 offsetDir = 1
586 end
587
588 if wheel.tireIsInverted then
589 setRotation(additionalWheel.wheelTire, 0, 0, math.pi)
590 end
591
592 local totalOffset = 0
593 totalOffset = totalOffset + offsetDir * MathUtil.inchToM(0.5*baseWheelWidth + wheelOffset + 0.5*dualWheelWidth )
594
595 -- get wheel with furthest offset
596 if math.abs(totalOffset) > math.abs(totalWheelshapeOffset) then
597 totalWheelshapeOffset = math.abs(totalOffset)
598 outmostWheelWidth = additionalWheel.width
599 end
600
601 if additionalWheel.connector ~= nil then
602 self:finalizeConnector(wheel, additionalWheel.connector, diameter, baseWheelWidth, wheelOffset, offsetDir, dualWheelWidth)
603 end
604
605 local x,y,z = getTranslation(additionalWheel.linkNode)
606 setTranslation(additionalWheel.linkNode, x+totalOffset,y,z)
607
608 if additionalWheel.driveGroundParticleSystems ~= nil then
609 for name, ps in pairs(additionalWheel.driveGroundParticleSystems) do
610 ps.offsets[1] = ps.offsets[1] + totalOffset
611 local wx, wy, wz = worldToLocal(wheel.node, getWorldTranslation(wheel.driveNode))
612 setTranslation(ps.emitterShape, wx + ps.offsets[1], wy + ps.offsets[2], wz + ps.offsets[3])
613 table.insert(wheel.driveGroundParticleSystems[name], ps)
614 end
615 end
616 end
617
618 wheel.widthOffset = wheel.widthOffset + offsetDir * (totalWheelshapeOffset/2)
619 local wheelX, _, _ = getTranslation(wheel.wheelTire)
620 local additionalWheelX = wheelX + totalWheelshapeOffset
621 wheel.wheelshapeWidth = (wheel.width/2 + math.abs(wheelX-additionalWheelX) + outmostWheelWidth/2)
622 end
623end

getAIVehicleDirectionNode

Description
Definition
getAIVehicleDirectionNode()
Code
1410function Wheels:getAIVehicleDirectionNode(superFunc)
1411 return self.spec_wheels.steeringCenterNode
1412end

getAllowTireTracks

Description
Definition
getAllowTireTracks()
Code
1449function Wheels:getAllowTireTracks()
1450 return true
1451end

getAreSurfaceSoundsActive

Description
Definition
getAreSurfaceSoundsActive()
Code
1627function Wheels:getAreSurfaceSoundsActive()
1628 return self:getIsActive()
1629end

getBrakeForce

Description
Definition
getBrakeForce()
Code
2590function Wheels:getBrakeForce()
2591 return 0
2592end

getBrands

Description
Definition
getBrands()
Code
2718function Wheels.getBrands(items)
2719 local brands = {}
2720 local addedBrands = {}
2721 for _, item in ipairs(items) do
2722 if item.wheelBrandName ~= nil and addedBrands[item.wheelBrandName] == nil then
2723 table.insert(brands, item.wheelBrandName)
2724 addedBrands[item.wheelBrandName] = true
2725 end
2726 end
2727
2728 return brands
2729end

getCurrentSurfaceSound

Description
Definition
getCurrentSurfaceSound()
Code
1603function Wheels:getCurrentSurfaceSound()
1604 local spec = self.spec_wheels
1605
1606 for _, wheel in ipairs(spec.wheels) do
1607 if wheel.contact == Wheels.WHEEL_GROUND_CONTACT then
1608 local isOnField = wheel.densityType ~= 0
1609 local shallowWater = wheel.shallowWater
1610 if isOnField then
1611 return spec.surfaceNameToSound["field"]
1612 elseif shallowWater then
1613 return spec.surfaceNameToSound["shallowWater"]
1614 else
1615 return spec.surfaceIdToSound[wheel.lastTerrainAttribute]
1616 end
1617 elseif wheel.contact == Wheels.WHEEL_OBJ_CONTACT then
1618 return spec.surfaceNameToSound["asphalt"]
1619 elseif wheel.contact ~= Wheels.WHEEL_NO_CONTACT then
1620 break
1621 end
1622 end
1623end

getDriveGroundParticleSystemsScale

Description
Definition
getDriveGroundParticleSystemsScale()
Code
1980function Wheels:getDriveGroundParticleSystemsScale(particleSystem, speed)
1981 local wheel = particleSystem.wheel
1982 if wheel ~= nil then
1983 if particleSystem.onlyActiveOnGroundContact and wheel.contact ~= Wheels.WHEEL_GROUND_CONTACT then
1984 return 0
1985 end
1986 if not Wheels.GROUND_PARTICLES[wheel.lastTerrainAttribute] then
1987 return 0
1988 end
1989 if wheel.densityType == g_currentMission.grassValue then
1990 return 0
1991 end
1992 end
1993
1994 local minSpeed = particleSystem.minSpeed
1995 local direction = particleSystem.direction
1996 if speed > minSpeed and (direction == 0 or (direction > 0) == (self.movingDirection > 0)) then
1997 local maxSpeed = particleSystem.maxSpeed
1998 local alpha = math.min((speed - minSpeed) / (maxSpeed - minSpeed), 1)
1999 local scale = MathUtil.lerp(particleSystem.minScale, particleSystem.maxScale, alpha)
2000 return scale
2001 end
2002 return 0
2003end

getIsVersatileYRotActive

Description
Definition
getIsVersatileYRotActive()
Code
2553function Wheels:getIsVersatileYRotActive(wheel)
2554 return true
2555end

getIsWheelFoliageDestructionAllowed

Description
Definition
getIsWheelFoliageDestructionAllowed()
Code
1757function Wheels:getIsWheelFoliageDestructionAllowed(wheel)
1758 if not g_currentMission.missionInfo.fruitDestruction then
1759 return false
1760 end
1761
1762 if self:getIsAIActive() then
1763 return false
1764 end
1765
1766 if wheel.contact ~= Wheels.WHEEL_GROUND_CONTACT then
1767 return false
1768 end
1769
1770 if wheel.isCareWheel then
1771 return false
1772 end
1773
1774 if self.getBlockFoliageDestruction ~= nil then
1775 if self:getBlockFoliageDestruction() then
1776 return false
1777 end
1778 end
1779
1780 return true
1781end

getTireTrackColor

Description
Definition
getTireTrackColor()
Code
1509function Wheels:getTireTrackColor(wheel, wx, wy, wz)
1510 local r, g, b, a, t = nil, nil, nil, 0, nil
1511
1512 if wheel.contact == Wheels.WHEEL_GROUND_CONTACT then
1513 local isOnField = wheel.densityType ~= 0
1514 local dirtAmount = 1
1515
1516 if isOnField then
1517 r, g, b, a = FSDensityMapUtil.getTireTrackColorFromDensityBits(wheel.densityBits)
1518 t = 1.0
1519
1520 if wheel.densityType == g_currentMission.grassValue then
1521 dirtAmount = 0.7
1522 end
1523 else
1524 r, g, b, a, t = getTerrainAttributesAtWorldPos(g_currentMission.terrainRootNode, wx, wy, wz, true, true, true, true, false)
1525 dirtAmount = 0.5
1526 end
1527
1528 wheel.dirtAmount = dirtAmount
1529 wheel.lastColor[1] = r
1530 wheel.lastColor[2] = g
1531 wheel.lastColor[3] = b
1532 wheel.lastColor[4] = a
1533 wheel.lastTerrainAttribute = t
1534 elseif wheel.contact == Wheels.WHEEL_OBJ_CONTACT then
1535 if wheel.dirtAmount > 0 then
1536 local maxTrackLength = 30 * (1 + g_currentMission.environment.weather:getGroundWetness())
1537 local speedFactor = math.min(self:getLastSpeed(), 20) / 20
1538 maxTrackLength = maxTrackLength * (2 - speedFactor)
1539 wheel.dirtAmount = math.max(wheel.dirtAmount - self.lastMovedDistance/maxTrackLength, 0)
1540 r, g, b = wheel.lastColor[1], wheel.lastColor[2], wheel.lastColor[3]
1541 a = 0 -- no depth to tyre tracks on road etc.
1542 end
1543 end
1544
1545 return r, g, b, a, t
1546end

getTotalMass

Description
Returns total mass of vehicle (optional including attached vehicles)
Definition
getTotalMass(boolean onlyGivenVehicle)
Arguments
booleanonlyGivenVehicleuse only the given vehicle, if false or nil it includes all attachables
Return Values
floattotalMasstotal mass
Code
1376function Wheels:getTotalMass(superFunc, onlyGivenVehicle)
1377 local mass = superFunc(self)
1378
1379 local spec = self.spec_wheels
1380 for _, wheel in pairs(spec.wheels) do
1381 mass = mass + wheel.mass
1382 end
1383
1384 return mass
1385end

getWheelFromWheelIndex

Description
Definition
getWheelFromWheelIndex()
Code
2559function Wheels:getWheelFromWheelIndex(wheelIndex)
2560 return self.spec_wheels.wheels[wheelIndex]
2561end

getWheels

Description
Definition
getWheels()
Code
2565function Wheels:getWheels()
2566 return self.spec_wheels.wheels
2567end

getWheelsByBrand

Description
Definition
getWheelsByBrand()
Code
2733function Wheels.getWheelsByBrand(items, brand)
2734 local wheels = {}
2735 for _, item in ipairs(items) do
2736 if item.wheelBrandName == brand then
2737 table.insert(wheels, item)
2738 end
2739 end
2740
2741 return wheels
2742end

initSpecialization

Description
Definition
initSpecialization()
Code
134function Wheels.initSpecialization()
135 g_configurationManager:addConfigurationType("wheel", g_i18n:getText("configuration_wheelSetup"), "wheels", nil, Wheels.loadBrandName, Wheels.loadedBrandNames, ConfigurationUtil.SELECTOR_MULTIOPTION, g_i18n:getText("configuration_wheelBrand"), Wheels.getBrands, Wheels.getWheelsByBrand)
136 g_configurationManager:addConfigurationType("rimColor", g_i18n:getText("configuration_rimColor"), nil, nil, ConfigurationUtil.getConfigColorSingleItemLoad, ConfigurationUtil.getConfigColorPostLoad, ConfigurationUtil.SELECTOR_COLOR)
137end

loadBrandName

Description
Definition
loadBrandName()
Code
2683function Wheels.loadBrandName(xmlFile, key, baseDir, customEnvironment, isMod, configItem)
2684 local name = getXMLString(xmlFile, key.."#brand")
2685 configItem.wheelBrandKey = key
2686 if name ~= nil then
2687 local brandDesc = g_brandManager:getBrandByName(name)
2688 if brandDesc ~= nil then
2689 configItem.wheelBrandName = brandDesc.title
2690 else
2691 g_logManager:warning("Wheel brand '%s' is not defined for '%s'!", name, key)
2692 end
2693 end
2694end

loadConnectorFromXML

Description
Definition
loadConnectorFromXML()
Code
2167function Wheels:loadConnectorFromXML(wheel, additionalWheel, xmlFile, wheelKey)
2168 local spec = self.spec_wheels
2169
2170 local connectorFilename = getXMLString(xmlFile, wheelKey..".connector#filename")
2171 if connectorFilename ~= nil and connectorFilename ~= "" then
2172 XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, wheelKey..".connector#index", wheelKey..".connector#node")
2173
2174 local connector = {}
2175
2176 if StringUtil.endsWith(connectorFilename, ".xml") then
2177 local xmlFilename = Utils.getFilename(connectorFilename, self.baseDirectory)
2178 local connectorXmlFile = loadXMLFile("TempConfig", xmlFilename)
2179
2180 if connectorXmlFile ~= nil then
2181 local nodeKey = "leftNode"
2182 if not wheel.isLeft then
2183 nodeKey = "rightNode"
2184 end
2185 connector.filename = getXMLString(connectorXmlFile, "connector.file#name")
2186 connector.nodeStr = getXMLString(connectorXmlFile, "connector.file#"..nodeKey)
2187 delete(connectorXmlFile)
2188 else
2189 g_logManager:xmlError(self.configFileName, "Unable to load connector xml file '%s'!", connectorFilename)
2190 end
2191 else
2192 connector.filename = connectorFilename
2193 connector.nodeStr = getXMLString(xmlFile, wheelKey..".connector#node")
2194 end
2195
2196 if connector.filename ~= nil and connector.filename ~= "" then
2197 connector.useWidthAndDiam = Utils.getNoNil(getXMLBool(xmlFile, wheelKey..".connector#useWidthAndDiam"), false)
2198 connector.usePosAndScale = Utils.getNoNil(getXMLBool(xmlFile, wheelKey..".connector#usePosAndScale"), false)
2199
2200 connector.diameter = getXMLFloat(xmlFile, wheelKey..".connector#diameter")
2201 connector.additionalOffset = Utils.getNoNil(getXMLFloat(xmlFile, wheelKey..".connector#offset"), 0)
2202 connector.width = getXMLFloat(xmlFile, wheelKey..".connector#width")
2203 connector.distance = getXMLFloat(xmlFile, wheelKey..".connector#distance")
2204 connector.startPos = getXMLFloat(xmlFile, wheelKey..".connector#startPos")
2205 connector.endPos = getXMLFloat(xmlFile, wheelKey..".connector#endPos")
2206 connector.scale = getXMLFloat(xmlFile, wheelKey..".connector#uniformScale")
2207 connector.color = ConfigurationUtil.getColorFromString(getXMLString(xmlFile, wheelKey..".connector#color")) or ConfigurationUtil.getColorByConfigId(self, "rimColor", self.configurations["rimColor"]) or wheel.color or spec.rimColor
2208 additionalWheel.connector = connector
2209 end
2210 end
2211end

loadDynamicWheelDataFromXML

Description
loads a specified wheel from vehicle xml loads external wheels if specified
Definition
loadDynamicWheelDataFromXML()
Code
2008function Wheels:loadDynamicWheelDataFromXML(xmlFile, key, wheelnamei, wheel)
2009 local spec = self.spec_wheels
2010
2011 XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, string.format("vehicle.wheels%s#hasTyreTracks", wheelnamei), string.format("vehicle.wheels.wheelConfigurations.wheelConfiguration.wheels%s#hasTireTracks", wheelnamei))
2012 XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, string.format("vehicle.wheels%s#tyreTrackAtlasIndex", wheelnamei), string.format("vehicle.wheels.wheelConfigurations.wheelConfiguration.wheels%s#tireTrackAtlasIndex", wheelnamei))
2013 XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, string.format("vehicle.wheels%s#configIndex", wheelnamei), string.format("vehicle.wheels.wheelConfigurations.wheelConfiguration.wheels%s#configId", wheelnamei))
2014
2015 local colorStr = ConfigurationUtil.getConfigurationValue(xmlFile, key, wheelnamei, "#color", getXMLString, nil, nil, nil)
2016 if colorStr ~= nil then
2017 wheel.color = ConfigurationUtil.getColorFromString(colorStr)
2018 end
2019
2020 local additionalColorStr = ConfigurationUtil.getConfigurationValue(xmlFile, key, wheelnamei, "#additionalColor", getXMLString, nil, nil, nil)
2021 if additionalColorStr ~= nil then
2022 wheel.additionalColor = ConfigurationUtil.getColorFromString(additionalColorStr)
2023 end
2024
2025 wheel.isLeft = ConfigurationUtil.getConfigurationValue(xmlFile, key, wheelnamei, "#isLeft", getXMLBool, true, nil, nil)
2026
2027 local wheelXmlFilename = ConfigurationUtil.getConfigurationValue(xmlFile, key, wheelnamei, "#filename", getXMLString, nil, nil, nil)
2028 if wheelXmlFilename ~= nil and wheelXmlFilename ~= "" then
2029 local wheelConfigId = ConfigurationUtil.getConfigurationValue(xmlFile, key, wheelnamei, "#configId", getXMLString, "default", nil, nil)
2030 wheel.xRotOffset = ConfigurationUtil.getConfigurationValue(xmlFile, key, wheelnamei, "#xRotOffset", getXMLFloat, 0, nil, nil)
2031 self:loadWheelDataFromExternalXML(wheel, wheelXmlFilename, wheelConfigId, true)
2032 end
2033
2034 self:loadWheelData(wheel, xmlFile, key .. wheelnamei)
2035
2036 if wheel.mass == nil then
2037 g_logManager:xmlWarning(self.configFileName, "Missing 'mass' for wheel '%s'. Using default '0.1'!", string.format(key.."%s", wheelnamei))
2038 wheel.mass = 0.1
2039 end
2040
2041 self:loadWheelPhysicsData(self.xmlFile, key, wheelnamei, wheel)
2042
2043 local key, _ = ConfigurationUtil.getXMLConfigurationKey(xmlFile, self.configurations["wheel"], "vehicle.wheels.wheelConfigurations.wheelConfiguration", "vehicle", "wheels")
2044 key = key .. ".wheels"
2045 local i = 0
2046 while true do
2047 local additionalWheelKey = string.format(key..wheelnamei..".additionalWheel(%d)", i)
2048 if not hasXMLProperty(xmlFile, additionalWheelKey) then
2049 break
2050 end
2051
2052 local wheelXmlFilename = getXMLString(xmlFile, additionalWheelKey.."#filename")
2053 if wheelXmlFilename ~= nil and wheelXmlFilename ~= "" then
2054
2055 XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, additionalWheelKey.."#configIndex", additionalWheelKey.."#configId")
2056 XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, additionalWheelKey.."#addRaycast", nil)
2057
2058 if wheel.additionalWheels == nil then
2059 wheel.additionalWheels = {}
2060 end
2061 local additionalWheel = {}
2062 additionalWheel.node = wheel.node
2063 additionalWheel.key = additionalWheelKey
2064 additionalWheel.linkNode = wheel.linkNode
2065
2066 local wheelConfigId = Utils.getNoNil(getXMLString(xmlFile, additionalWheelKey.."#configId"), "default")
2067 additionalWheel.isLeft = Utils.getNoNil(getXMLBool(xmlFile, additionalWheelKey.."#isLeft"), wheel.isLeft) or false
2068 additionalWheel.xRotOffset = Utils.getNoNilRad(getXMLFloat(xmlFile, additionalWheelKey.."#xRotOffset"), 0)
2069 additionalWheel.color = Utils.getNoNil(ConfigurationUtil.getColorFromString(getXMLString(xmlFile, additionalWheelKey.."#color")), wheel.color)
2070
2071 self:loadWheelDataFromExternalXML(additionalWheel, wheelXmlFilename, wheelConfigId, false)
2072
2073 additionalWheel.hasParticles = Utils.getNoNil(Utils.getNoNil(getXMLBool(xmlFile, additionalWheelKey.."#hasParticles"), wheel.hasParticles), false)
2074 additionalWheel.hasTireTracks = Utils.getNoNil(Utils.getNoNil(getXMLBool(xmlFile, additionalWheelKey.."#hasTireTracks"), wheel.hasTireTracks), false)
2075 if g_currentMission.tireTrackSystem ~= nil and additionalWheel.hasTireTracks then
2076 additionalWheel.tireTrackIndex = g_currentMission.tireTrackSystem:createTrack(additionalWheel.width, additionalWheel.tireTrackAtlasIndex)
2077 end
2078
2079 additionalWheel.offset = Utils.getNoNil(getXMLFloat(xmlFile, additionalWheelKey.."#offset"), 0)
2080
2081 self:loadConnectorFromXML(wheel, additionalWheel, xmlFile, additionalWheelKey)
2082
2083 table.insert(wheel.additionalWheels, additionalWheel)
2084
2085 wheel.mass = wheel.mass + additionalWheel.mass
2086 wheel.maxLatStiffness = wheel.maxLatStiffness + additionalWheel.maxLatStiffness
2087 wheel.maxLongStiffness = wheel.maxLongStiffness + additionalWheel.maxLongStiffness
2088 end
2089
2090 i = i + 1
2091 end
2092
2093 local i = 0
2094 while true do
2095 local chockKey = string.format("%s%s.wheelChock(%d)", key, wheelnamei, i)
2096 if not hasXMLProperty(self.xmlFile, chockKey) then
2097 break
2098 end
2099
2100 local filename = Utils.getNoNil(getXMLString(xmlFile, chockKey.."#filename"), "$data/shared/assets/wheelChocks/wheelChock01.i3d")
2101 local i3dNode = g_i3DManager:loadSharedI3DFile(filename, self.baseDirectory, false, false, false)
2102 if i3dNode ~= 0 then
2103 local chockNode = getChildAt(i3dNode, 0)
2104 local posRefNode = I3DUtil.indexToObject(chockNode, getUserAttribute(chockNode, "posRefNode"), self.i3dMappings)
2105 if posRefNode ~= nil then
2106 local chock = {}
2107 chock.wheel = wheel
2108 chock.node = chockNode
2109 chock.filename = filename
2110 chock.scale = Utils.getNoNil(StringUtil.getVectorNFromString(getXMLString(xmlFile, chockKey.."#scale"), 3), {1,1,1})
2111 setScale(chock.node, unpack(chock.scale))
2112
2113 chock.parkingNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, chockKey.."#parkingNode"), self.i3dMappings)
2114 chock.isInverted = Utils.getNoNil(getXMLBool(xmlFile, chockKey.."#isInverted"), false)
2115 chock.isParked = Utils.getNoNil(getXMLBool(xmlFile, chockKey.."#isParked"), false)
2116 _, chock.height, chock.zOffset = localToLocal(posRefNode, chock.node, 0, 0, 0)
2117 chock.height = chock.height / chock.scale[2]
2118 chock.zOffset = chock.zOffset / chock.scale[3]
2119
2120 chock.offset = Utils.getNoNil(StringUtil.getVectorNFromString(getXMLString(xmlFile, chockKey.."#offset"), 3), {0,0,0})
2121
2122 chock.parkedNode = I3DUtil.indexToObject(chockNode, getUserAttribute(chockNode, "parkedNode"), self.i3dMappings)
2123 chock.linkedNode = I3DUtil.indexToObject(chockNode, getUserAttribute(chockNode, "linkedNode"), self.i3dMappings)
2124
2125 local color = g_brandColorManager:loadColorAndMaterialFromXML(self.configFileName, chockNode, "colorMat0", xmlFile, chockKey, "color", false)
2126 if color ~= nil then
2127 I3DUtil.setShaderParameterRec(chockNode, "colorMat0", color[1], color[2], color[3], color[4])
2128 end
2129
2130 self:updateWheelChockPosition(chock, chock.isParked)
2131
2132 wheel.updateWheelChock = false
2133 if wheel.wheelChocks == nil then
2134 wheel.wheelChocks = {}
2135 end
2136 table.insert(wheel.wheelChocks, chock)
2137
2138 table.insert(spec.wheelChocks, chock)
2139 else
2140 g_logManager:xmlWarning(self.configFileName, "Missing 'posRefNode'-userattribute for wheel-chock '%s'!", chockKey)
2141 end
2142
2143 delete(i3dNode)
2144 end
2145 i=i+1
2146 end
2147
2148 if wheel.hasParticles then
2149 self:loadWheelParticleSystem(wheel, xmlFile, key .. wheelnamei)
2150 end
2151
2152 if wheel.driveGroundParticleSystems ~= nil then
2153 local wx, wy, wz = worldToLocal(wheel.node, getWorldTranslation(wheel.driveNode))
2154 for _,typedPs in pairs(wheel.driveGroundParticleSystems) do
2155 for _, ps in ipairs(typedPs) do
2156 setTranslation(ps.rootNode, wx + ps.offsets[1], wy - wheel.radius + ps.offsets[2], wz + ps.offsets[3])
2157 end
2158 end
2159 end
2160
2161 wheel.wheelShape = 0
2162 wheel.wheelShapeCreated = false
2163end

loadedBrandNames

Description
Definition
loadedBrandNames()
Code
2698function Wheels.loadedBrandNames(xmlFile, baseKey, baseDir, customEnvironment, isMod, configurationItems, storeItem)
2699 local hasWheelBrands = false
2700 for _, item in ipairs(configurationItems) do
2701 if item.wheelBrandName ~= nil then
2702 hasWheelBrands = true
2703 break
2704 end
2705 end
2706
2707 if hasWheelBrands then
2708 for _, item in ipairs(configurationItems) do
2709 if item.wheelBrandName == nil then
2710 g_logManager:xmlWarning(storeItem.xmlFilename, "Wheel brand missing for wheel configuration '%s'!", item.wheelBrandKey)
2711 end
2712 end
2713 end
2714end

loadHubFromXML

Description
Definition
loadHubFromXML()
Code
669function Wheels:loadHubFromXML(xmlFilename, isLeft, hub, linkNode)
670 local xmlFilename = Utils.getFilename(xmlFilename, self.baseDirectory)
671 local xmlFile = loadXMLFile("TempConfig", xmlFilename)
672
673 local key = "hub"
674 local nodeKey = "left"
675 if not isLeft then
676 nodeKey = "right"
677 end
678 local i3dFilename = getXMLString(xmlFile, key .. ".filename")
679 if i3dFilename == nil then
680 g_logManager:xmlError(xmlFilename, "Unable to retrieve hub i3d filename!")
681 return false
682 end
683 local hubi3dNode = g_i3DManager:loadSharedI3DFile(i3dFilename, self.baseDirectory, false, false, false)
684 if hubi3dNode == 0 then
685 return false
686 end
687 local nodeStr = getXMLString(xmlFile, key .. ".nodes#" .. nodeKey)
688 hub.node = I3DUtil.indexToObject(hubi3dNode, nodeStr, self.i3dMappings)
689
690 if hub.node ~= nil then
691 link(linkNode, hub.node)
692 delete(hubi3dNode)
693 local colors = {}
694 for j = 0, 7 do
695 -- save supported colorMat shaders to be able to check for invalid usage
696 colors[j] = ConfigurationUtil.getColorFromString(getXMLString(xmlFile, key .. string.format(".color%d", j)))
697 end
698 hub.colors = colors
699 end
700 delete(xmlFile)
701 return true
702end

loadHubs

Description
Definition
loadHubs()
Code
311function Wheels:loadHubs()
312 local spec = self.spec_wheels
313
314 spec.hubsColors = {}
315
316 -- load hubs colors (global)
317 for j = 0, 7 do
318 local hubsColorsKey = string.format("vehicle.wheels.hubs.color%d", j)
319 local colorStr = getXMLString(self.xmlFile, hubsColorsKey)
320 local material = getXMLInt(self.xmlFile, hubsColorsKey.."#material")
321 if colorStr ~= nil then
322 spec.hubsColors[j] = ConfigurationUtil.getColorFromString(colorStr)
323 spec.hubsColors[j][4] = material
324 elseif getXMLBool(self.xmlFile, hubsColorsKey .. "#useBaseColor") then
325 spec.hubsColors[j] = ConfigurationUtil.getColorByConfigId(self, "baseColor", self.configurations["baseColor"]) or ConfigurationUtil.getColorByConfigId(self, "baseMaterial", self.configurations["baseMaterial"])
326 elseif getXMLBool(self.xmlFile, hubsColorsKey .. "#useRimColor") then
327 spec.hubsColors[j] = Utils.getNoNil(ConfigurationUtil.getColorByConfigId(self, "rimColor", self.configurations["rimColor"]), spec.rimColor)
328 end
329 end
330
331 spec.hubs = {}
332
333 -- load and link hubs to reprs
334 local i = 0
335 while true do
336 local key = string.format("vehicle.wheels.hubs.hub(%d)", i)
337 local reprNodeStr = getXMLString(self.xmlFile, key .. "#linkNode")
338 if reprNodeStr == nil then
339 break
340 end
341 local hubFilename = getXMLString(self.xmlFile, key .. "#filename")
342 local isLeft = getXMLBool(self.xmlFile, key .. "#isLeft")
343
344 local reprNode = I3DUtil.indexToObject(self.components, reprNodeStr, self.i3dMappings)
345
346 local hub = {}
347 if self:loadHubFromXML(hubFilename, isLeft, hub, reprNode) then
348 -- load color per hub and and apply to mesh
349 for j = 0, 7 do
350 -- only use global hub color if hub color wasn't reset explicitly (== nil)
351 local color = XMLUtil.getXMLOverwrittenValue(self.xmlFile, key, string.format(".color%d", j), "", getXMLString, "global")
352 local material = XMLUtil.getXMLOverwrittenValue(self.xmlFile, key, string.format(".color%d#material", j), "", getXMLInt, nil)
353 if color == "global" then
354 color = spec.hubsColors[j]
355 else
356 color = ConfigurationUtil.getColorFromString(color)
357 if color ~= nil then
358 color[4] = material
359 end
360 end
361 -- check if color is defined in vehicle xml but not support by hub
362 if color ~= nil and hub.colors[j] == nil then
363 g_logManager:xmlWarning(self.configFileName, "ColorShader 'color%d' is not supported by '%s'.", j, hubFilename)
364 else
365 color = color or hub.colors[j]
366 if color ~= nil then
367 local r, g, b, mat = unpack(color)
368 if mat == nil then
369 _, _, _, mat = getShaderParameter(hub.node, string.format("colorMat%d", j))
370 end
371 setShaderParameter(hub.node, string.format("colorMat%d", j), r, g, b, mat, false)
372 end
373 end
374 end
375
376 local offset = getXMLFloat(self.xmlFile, key .. "#offset")
377 if offset ~= nil then
378 if not isLeft then
379 offset = offset * -1
380 end
381 setTranslation(hub.node, offset, 0, 0)
382 end
383
384 local scale = StringUtil.getVectorNFromString(getXMLString(self.xmlFile, key .. "#scale"), 3)
385 if scale ~= nil then
386 setScale(hub.node, scale[1], scale[2], scale[3])
387 end
388
389 table.insert(spec.hubs, hub)
390 end
391
392 i = i + 1
393 end
394end

loadNonPhysicalWheelFromXML

Description
Definition
loadNonPhysicalWheelFromXML()
Code
2503function Wheels:loadNonPhysicalWheelFromXML(dynamicallyLoadedWheel, xmlFile, key)
2504
2505 dynamicallyLoadedWheel.linkNode = I3DUtil.indexToObject(self.components, Utils.getNoNil(getXMLString(xmlFile, key .. "#linkNode"), "0>"), self.i3dMappings)
2506
2507 local wheelXmlFilename = getXMLString(xmlFile, key.."#filename")
2508 if wheelXmlFilename ~= nil and wheelXmlFilename ~= "" then
2509 local wheelConfigId = Utils.getNoNil(getXMLString(xmlFile, key.."#configId"), "default")
2510 dynamicallyLoadedWheel.isLeft = Utils.getNoNil(getXMLBool(xmlFile, key.."#isLeft"), true)
2511 dynamicallyLoadedWheel.tireIsInverted = Utils.getNoNil(getXMLBool(xmlFile, key.."#isInverted"), false)
2512 dynamicallyLoadedWheel.xRotOffset = Utils.getNoNilRad(getXMLFloat(xmlFile, key.."#xRotOffset"), 0)
2513 local colorStr = getXMLString(xmlFile, key.."#color")
2514 if colorStr ~= nil then
2515 dynamicallyLoadedWheel.color = ConfigurationUtil.getColorFromString(colorStr)
2516 end
2517 local additionalColorStr = getXMLString(xmlFile, key.."#additionalColor")
2518 if additionalColorStr ~= nil then
2519 dynamicallyLoadedWheel.additionalColor = ConfigurationUtil.getColorFromString(additionalColorStr)
2520 end
2521 self:loadWheelDataFromExternalXML(dynamicallyLoadedWheel, wheelXmlFilename, wheelConfigId)
2522
2523 self:finalizeWheel(dynamicallyLoadedWheel)
2524
2525 return true
2526 end
2527
2528 return false
2529end

loadWheelData

Description
reads single xml wheel element
Definition
loadWheelData()
Code
2310function Wheels:loadWheelData(wheel, xmlFile, configKey)
2311 local key = "nodeLeft"
2312 if not wheel.isLeft then
2313 key = "nodeRight"
2314 end
2315
2316 wheel.radius = getXMLFloat(xmlFile, configKey..".physics#radius") or wheel.radius
2317 if wheel.radius == nil then
2318 g_logManager:xmlWarning(self.configFileName, "No radius defined for wheel '%s'! Using default value of 0.5!", configKey..".physics#radius")
2319 wheel.radius = 0.5
2320 end
2321 wheel.width = getXMLFloat(xmlFile, configKey..".physics#width") or wheel.width
2322 if wheel.width == nil then
2323 g_logManager:xmlWarning(self.configFileName, "No width defined for wheel '%s'! Using default value of 0.5!", configKey..".physics#width")
2324 wheel.width = 0.5
2325 end
2326 wheel.mass = getXMLFloat(xmlFile, configKey..".physics#mass") or wheel.mass or 0.1
2327
2328 local tireTypeName = getXMLString(xmlFile, configKey..".tire#tireType")
2329 if tireTypeName ~= nil then
2330 local tireType = WheelsUtil.getTireType(tireTypeName)
2331 if tireType ~= nil then
2332 wheel.tireType = tireType
2333 else
2334 g_logManager:xmlWarning(self.configFileName, "Tire type '%s' not defined!", tireTypeName)
2335 end
2336 end
2337
2338 wheel.frictionScale = getXMLFloat(xmlFile, configKey..".physics#frictionScale") or wheel.frictionScale
2339 wheel.maxLongStiffness = getXMLFloat(xmlFile, configKey..".physics#maxLongStiffness") or wheel.maxLongStiffness -- [t / rad]
2340 wheel.maxLatStiffness = getXMLFloat(xmlFile, configKey..".physics#maxLatStiffness") or wheel.maxLatStiffness -- xml is ratio to restLoad [1/rad], final value is [t / rad]
2341 wheel.maxLatStiffnessLoad = getXMLFloat(xmlFile, configKey..".physics#maxLatStiffnessLoad") or wheel.maxLatStiffnessLoad -- xml is ratio to restLoad, final value is [t]
2342
2343 wheel.tireTrackAtlasIndex = getXMLInt(xmlFile, configKey..".tire#tireTrackAtlasIndex") or wheel.tireTrackAtlasIndex or 0
2344 wheel.widthOffset = getXMLFloat(xmlFile, configKey..".tire#widthOffset") or wheel.widthOffset or 0.0
2345 wheel.xOffset = getXMLFloat(xmlFile, configKey..".tire#xOffset") or wheel.xOffset or 0
2346 wheel.maxDeformation = getXMLFloat(xmlFile, configKey..".tire#maxDeformation") or wheel.maxDeformation or 0
2347 wheel.deformation = 0
2348 wheel.isCareWheel = Utils.getNoNil(Utils.getNoNil(getXMLBool(xmlFile, configKey..".tire#isCareWheel"), wheel.isCareWheel), true)
2349 wheel.smoothGroundRadius = getXMLFloat(xmlFile, configKey..".physics#smoothGroundRadius") or math.max(0.6, wheel.width*0.75)
2350
2351 wheel.tireFilename = XMLUtil.getXMLOverwrittenValue(xmlFile, configKey, ".tire#filename", "", getXMLString, wheel.tireFilename)
2352 wheel.tireIsInverted = XMLUtil.getXMLOverwrittenValue(xmlFile, configKey, ".tire#isInverted", "", getXMLBool, wheel.tireIsInverted)
2353 wheel.tireNodeStr = XMLUtil.getXMLOverwrittenValue(xmlFile, configKey, ".tire#node", "", getXMLString, nil) or XMLUtil.getXMLOverwrittenValue(xmlFile, configKey, ".tire#"..key, "", getXMLString, wheel.tireNodeStr)
2354 wheel.outerRimFilename = XMLUtil.getXMLOverwrittenValue(xmlFile, configKey, ".outerRim#filename", "", getXMLString, wheel.outerRimFilename)
2355 wheel.outerRimNodeStr = XMLUtil.getXMLOverwrittenValue(xmlFile, configKey, ".outerRim#node", "", getXMLString, wheel.outerRimNodeStr) or "0|0"
2356 wheel.outerRimWidthAndDiam = XMLUtil.getXMLOverwrittenValue(xmlFile, configKey, ".outerRim#widthAndDiam", "", getXMLString, wheel.outerRimWidthAndDiam, StringUtil.getVectorNFromString, 2)
2357 wheel.outerRimScale = XMLUtil.getXMLOverwrittenValue(xmlFile, configKey, ".outerRim#scale", "", getXMLString, wheel.outerRimScale, StringUtil.getVectorNFromString, 3)
2358 wheel.innerRimFilename = XMLUtil.getXMLOverwrittenValue(xmlFile, configKey, ".innerRim#filename", "", getXMLString, wheel.innerRimFilename)
2359 wheel.innerRimNodeStr = XMLUtil.getXMLOverwrittenValue(xmlFile, configKey, ".innerRim#node", "", getXMLString, nil) or XMLUtil.getXMLOverwrittenValue(xmlFile, configKey, ".innerRim#"..key, "", getXMLString, wheel.innerRimNodeStr)
2360 wheel.innerRimWidthAndDiam = XMLUtil.getXMLOverwrittenValue(xmlFile, configKey, ".innerRim#widthAndDiam", "", getXMLString, wheel.innerRimWidthAndDiam, StringUtil.getVectorNFromString, 2)
2361 wheel.innerRimOffset = XMLUtil.getXMLOverwrittenValue(xmlFile, configKey, ".innerRim#offset", "", getXMLFloat, wheel.innerRimOffset) or 0
2362 wheel.innerRimScale = XMLUtil.getXMLOverwrittenValue(xmlFile, configKey, ".innerRim#scale", "", getXMLString, wheel.innerRimScale, StringUtil.getVectorNFromString, 3);
2363 wheel.additionalFilename = XMLUtil.getXMLOverwrittenValue(xmlFile, configKey, ".additional#filename", "", getXMLString, wheel.additionalFilename)
2364 wheel.additionalNodeStr = XMLUtil.getXMLOverwrittenValue(xmlFile, configKey, ".additional#node", "", getXMLString, nil) or XMLUtil.getXMLOverwrittenValue(xmlFile, configKey, ".additional#"..key, "", getXMLString, wheel.additionalNodeStr)
2365 wheel.additionalOffset = XMLUtil.getXMLOverwrittenValue(xmlFile, configKey, ".additional#offset", "", getXMLFloat, wheel.additionalOffset) or 0
2366 wheel.additionalScale = XMLUtil.getXMLOverwrittenValue(xmlFile, configKey, ".additional#scale", "", getXMLString, wheel.additionalScale, StringUtil.getVectorNFromString, 3)
2367 wheel.additionalMass = XMLUtil.getXMLOverwrittenValue(xmlFile, configKey, ".additional#mass", "", getXMLFloat, wheel.additionalMass) or 0
2368 wheel.additionalWidthAndDiam = XMLUtil.getXMLOverwrittenValue(xmlFile, configKey, ".additional#widthAndDiam", "", getXMLString, wheel.additionalWidthAndDiam, StringUtil.getVectorNFromString, 2)
2369end

loadWheelDataFromExternalXML

Description
loads external wheel xml for specified config and writes values into table
Definition
loadWheelDataFromExternalXML()
Code
2271function Wheels:loadWheelDataFromExternalXML(wheel, xmlFilename, wheelConfigId)
2272 xmlFilename = Utils.getFilename(xmlFilename, self.baseDirectory)
2273 local xmlFile = loadXMLFile("TempConfig", xmlFilename)
2274
2275 if xmlFile ~= nil then
2276 -- load default values
2277 local defaultKey = "wheel.default"
2278 self:loadWheelData(wheel, xmlFile, defaultKey)
2279
2280 -- load config specific values and overwrite existing ones
2281 if wheelConfigId ~= "default" then
2282 local i = 0
2283 local wheelConfigFound = false
2284 while true do
2285 local configKey = string.format("wheel.configurations.configuration(%d)", i)
2286 if not hasXMLProperty(xmlFile, configKey) then
2287 break
2288 end
2289 if getXMLString(xmlFile, configKey.."#id") == wheelConfigId then
2290 wheelConfigFound = true
2291 self:loadWheelData(wheel, xmlFile, configKey)
2292 break
2293 end
2294 i = i + 1
2295 end
2296
2297 if not wheelConfigFound then
2298 g_logManager:xmlError(xmlFilename, "WheelConfigId '%s' not found!", wheelConfigId)
2299 end
2300 end
2301
2302 delete(xmlFile)
2303 else
2304 g_logManager:xmlError(xmlFilename, "Unable to load xml file '%s'!", wheelConfigId)
2305 end
2306end

loadWheelParticleSystem

Description
Definition
loadWheelParticleSystem()
Code
2215function Wheels:loadWheelParticleSystem(wheel, xmlFile, key)
2216
2217 wheel.driveGroundParticleSystems = {}
2218 wheel.driveGroundParticleStates = {driving_dust=false, driving_dry=false, driving_wet=false}
2219
2220 local getParticleSystem = function(xmlFile, wheelKey, wheelData, particleSystem)
2221 local emitterShapeRoot = g_i3DManager:loadSharedI3DFile("$data/particleSystems/shared/wheelEmitterShape.i3d", self.baseDirectory, false, false, false)
2222 local emitterShape = getChildAt(emitterShapeRoot, 0)
2223 link(wheel.node, emitterShape)
2224 delete(emitterShapeRoot)
2225
2226 local ps = ParticleUtil.copyParticleSystem(xmlFile, wheelKey, particleSystem, emitterShape)
2227 ps.particleSpeed = ParticleUtil.getParticleSystemSpeed(ps)
2228 ps.particleRandomSpeed = ParticleUtil.getParticleSystemSpeedRandom(ps)
2229
2230 ps.isTintable = Utils.getNoNil(getUserAttribute(ps.shape, "tintable"), true)
2231 ps.offsets = StringUtil.getVectorNFromString(Utils.getNoNil(getXMLString(xmlFile, wheelKey..".wheelParticleSystem#psOffset"), "0 0 0"), 3)
2232 local wx, wy, wz = worldToLocal(wheel.node, getWorldTranslation(wheel.driveNode))
2233 setTranslation(ps.emitterShape, wx + ps.offsets[1], wy + ps.offsets[2], wz + ps.offsets[3])
2234 setScale(ps.emitterShape, wheelData.width, wheelData.radius*2, wheelData.radius*2)
2235 ps.wheel = wheel
2236 ps.rootNode = ps.emitterShape
2237 ps.minSpeed = Utils.getNoNil(getXMLFloat(xmlFile, wheelKey..".wheelParticleSystem#minSpeed"), 3)/3600
2238 ps.maxSpeed = Utils.getNoNil(getXMLFloat(self.xmlFile, wheelKey..".wheelParticleSystem#maxSpeed"), 20)/3600
2239 ps.minScale = Utils.getNoNil(getXMLFloat(self.xmlFile, wheelKey..".wheelParticleSystem#minScale"), 0.1)
2240 ps.maxScale = Utils.getNoNil(getXMLFloat(self.xmlFile, wheelKey..".wheelParticleSystem#maxScale"), 1)
2241 ps.direction = Utils.getNoNil(getXMLFloat(self.xmlFile, wheelKey..".wheelParticleSystem#direction"), 0)
2242 ps.onlyActiveOnGroundContact = Utils.getNoNil(getXMLBool(self.xmlFile, wheelKey..".wheelParticleSystem#onlyActiveOnGroundContact"), true)
2243 return ps
2244 end
2245
2246 for name, _ in pairs(wheel.driveGroundParticleStates) do
2247 local particleSystem = g_particleSystemManager:getParticleSystem(FillType.UNKNOWN, name)
2248 if particleSystem ~= nil then
2249
2250 local wheelParticles = {}
2251 table.insert(wheelParticles, getParticleSystem(xmlFile, key, wheel, particleSystem))
2252
2253 if wheel.additionalWheels ~= nil then
2254 for _, additionalWheel in ipairs(wheel.additionalWheels) do
2255 if additionalWheel.hasParticles then
2256 if additionalWheel.driveGroundParticleSystems == nil then
2257 additionalWheel.driveGroundParticleSystems = {}
2258 end
2259 additionalWheel.driveGroundParticleSystems[name] = getParticleSystem(xmlFile, additionalWheel.key, additionalWheel, particleSystem)
2260 end
2261 end
2262 end
2263
2264 wheel.driveGroundParticleSystems[name] = wheelParticles
2265 end
2266 end
2267end

loadWheelPhysicsData

Description
Definition
loadWheelPhysicsData()
Code
706function Wheels:loadWheelPhysicsData(xmlFile, key, wheelnamei, wheel)
707 local physicsKey = wheelnamei .. ".physics"
708 if wheel.repr ~= nil then
709 wheel.node = self:getParentComponent(wheel.repr)
710 if wheel.node ~= 0 then
711 XMLUtil.checkDeprecatedXMLElements(xmlFile, self.configFileName, key..wheelnamei.."#steeringNode", string.format("vehicle.wheels.wheelConfigurations.wheelConfiguration.wheels%s.steering#node", wheelnamei))
712
713 local driveNodeStr = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#driveNode", getXMLString, nil, nil, nil)
714 wheel.driveNode = I3DUtil.indexToObject(self.components, driveNodeStr, self.i3dMappings)
715
716 if wheel.driveNode == wheel.repr then
717 g_logManager:xmlWarning(self.configFileName, "repr and driveNode may not be equal for '%s'. Using default driveNode instead!", key.."."..physicsKey)
718 wheel.driveNode = nil
719 end
720
721 wheel.linkNode = I3DUtil.indexToObject(self.components, ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#linkNode", getXMLString, nil, nil, nil), self.i3dMappings)
722
723 if wheel.driveNode == nil then
724 -- create a new repr and use repr as drivenode
725 local newRepr = createTransformGroup("wheelReprNode")
726 local reprIndex = getChildIndex(wheel.repr)
727 link(getParent(wheel.repr), newRepr, reprIndex)
728 setTranslation(newRepr, getTranslation(wheel.repr))
729 setRotation(newRepr, getRotation(wheel.repr))
730 setScale(newRepr, getScale(wheel.repr))
731 wheel.driveNode = wheel.repr
732
733 link(newRepr, wheel.driveNode)
734 setTranslation(wheel.driveNode, 0, 0, 0)
735 setRotation(wheel.driveNode, 0, 0, 0)
736 setScale(wheel.driveNode, 1, 1, 1)
737 wheel.repr = newRepr
738 end
739
740 if wheel.driveNode ~= nil then
741 local driveNodeDirectionNode = createTransformGroup("driveNodeDirectionNode")
742 link(getParent(wheel.repr), driveNodeDirectionNode)
743 setWorldTranslation(driveNodeDirectionNode, getWorldTranslation(wheel.driveNode))
744 setWorldRotation(driveNodeDirectionNode, getWorldRotation(wheel.driveNode))
745 wheel.driveNodeDirectionNode = driveNodeDirectionNode
746 end
747
748 if wheel.linkNode == nil then
749 wheel.linkNode = wheel.driveNode
750 end
751
752 wheel.yOffset = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#yOffset", getXMLFloat, 0.0, nil, nil)
753 if wheel.yOffset ~= 0 then
754 -- move drivenode in y direction. Use convert yOffset from driveNode local space to driveNodeParent local space to translate according to directions
755 setTranslation(wheel.driveNode, localToLocal(wheel.driveNode, getParent(wheel.driveNode), 0, wheel.yOffset, 0))
756 end
757
758 wheel.showSteeringAngle = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#showSteeringAngle", getXMLBool, true, nil, nil)
759 wheel.suspTravel = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#suspTravel", getXMLFloat, 0.01, nil, nil)
760 local initialCompression = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#initialCompression", getXMLFloat, nil, nil, nil)
761 if initialCompression ~= nil then
762 wheel.deltaY = (1-initialCompression*0.01)*wheel.suspTravel
763 else
764 wheel.deltaY = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#deltaY", getXMLFloat, 0.0, nil, nil)
765 end
766 wheel.spring = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#spring", getXMLFloat, 0, nil, nil)*Vehicle.SPRING_SCALE
767
768 wheel.torque = 0
769 wheel.brakeFactor = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#brakeFactor", getXMLFloat, 1, nil, nil)
770 wheel.autoHoldBrakeFactor = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#autoHoldBrakeFactor", getXMLFloat, wheel.brakeFactor, nil, nil)
771
772 wheel.damperCompressionLowSpeed = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#damperCompressionLowSpeed", getXMLFloat, nil, nil, nil)
773 wheel.damperRelaxationLowSpeed = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#damperRelaxationLowSpeed", getXMLFloat, nil, nil, nil)
774 if wheel.damperRelaxationLowSpeed == nil then
775 wheel.damperRelaxationLowSpeed = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#damper", getXMLFloat, Utils.getNoNil(wheel.damperCompressionLowSpeed, 0), nil, nil)
776 end
777 -- by default, the high speed relaxation damper is set to 90% of the low speed relaxation damper
778 wheel.damperRelaxationHighSpeed = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#damperRelaxationHighSpeed", getXMLFloat, wheel.damperRelaxationLowSpeed * 0.7, nil, nil)
779
780 -- by default, we set the low speed compression damper to 90% of the low speed relaxation damper
781 if wheel.damperCompressionLowSpeed == nil then
782 wheel.damperCompressionLowSpeed = wheel.damperRelaxationLowSpeed * 0.9
783 end
784 -- by default, the high speed compression damper is set to 20% of the low speed compression damper
785 wheel.damperCompressionHighSpeed = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#damperCompressionHighSpeed", getXMLFloat, wheel.damperCompressionLowSpeed * 0.2, nil, nil)
786 wheel.damperCompressionLowSpeedThreshold = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#damperCompressionLowSpeedThreshold", getXMLFloat, 0.1016, nil, nil) -- default 4 inch / s
787 wheel.damperRelaxationLowSpeedThreshold = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#damperRelaxationLowSpeedThreshold", getXMLFloat, 0.1524, nil, nil) -- default 6 inch / s
788
789 wheel.forcePointRatio = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#forcePointRatio", getXMLFloat, 0, nil, nil)
790 wheel.driveMode = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#driveMode", getXMLInt, 0, nil, nil)
791 wheel.xOffset = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#xOffset", getXMLFloat, 0, nil, nil)
792
793
794 wheel.transRatio = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#transRatio", getXMLFloat, 0.0, nil, nil)
795
796 wheel.isSynchronized = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#isSynchronized", getXMLBool, true, nil, nil)
797 wheel.tipOcclusionAreaGroupId = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#tipOcclusionAreaGroupId", getXMLInt, nil, nil, nil)
798
799 wheel.positionX, wheel.positionY, wheel.positionZ = localToLocal(wheel.driveNode, wheel.node, 0,0,0)
800 wheel.useReprDirection = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#useReprDirection", getXMLBool, false, nil, nil)
801 wheel.useDriveNodeDirection = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#useDriveNodeDirection", getXMLBool, false, nil, nil)
802
803
804 wheel.mass = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#mass", getXMLFloat, wheel.mass, nil, nil)
805 wheel.radius = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#radius", getXMLFloat, Utils.getNoNil(wheel.radius, 0.5), nil, nil)
806 wheel.width = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#width", getXMLFloat, Utils.getNoNil(wheel.width, 0.6), nil, nil)
807 wheel.wheelshapeWidth = wheel.width
808 wheel.widthOffset = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#widthOffset", getXMLFloat, 0, nil, nil)
809 wheel.restLoad = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#restLoad", getXMLFloat, Utils.getNoNil(wheel.restLoad, 1.0), nil, nil) -- [t]
810 wheel.maxLongStiffness = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#maxLongStiffness", getXMLFloat, Utils.getNoNil(wheel.maxLongStiffness, 30.0), nil, nil) -- [t / rad]
811 wheel.maxLatStiffness = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#maxLatStiffness", getXMLFloat, Utils.getNoNil(wheel.maxLatStiffness, 40.0), nil, nil) -- xml is ratio to restLoad [1/rad], final value is [t / rad]
812 wheel.maxLatStiffnessLoad = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#maxLatStiffnessLoad", getXMLFloat, Utils.getNoNil(wheel.maxLatStiffnessLoad, 2), nil, nil) -- xml is ratio to restLoad, final value is [t]
813 wheel.frictionScale = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#frictionScale", getXMLFloat, Utils.getNoNil(wheel.frictionScale, 1.0), nil, nil)
814 wheel.rotationDamping = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#rotationDamping", getXMLFloat, wheel.mass * 0.035, nil, nil)
815 wheel.tireGroundFrictionCoeff = 1.0 -- This will be changed dynamically based on the tire-ground pair
816
817 local tireTypeName = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#tireType", getXMLString, "mud", nil, nil)
818 wheel.tireType = WheelsUtil.getTireType(tireTypeName)
819 if wheel.tireType == nil then
820 g_logManager:xmlWarning(self.configFileName, "Failed to find tire type '%s'. Defaulting to 'mud'!", tireTypeName)
821 wheel.tireType = WheelsUtil.getTireType("mud")
822 end
823 wheel.fieldDirtMultiplier = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#fieldDirtMultiplier", getXMLFloat, 75, nil, nil)
824 wheel.streetDirtMultiplier = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#streetDirtMultiplier", getXMLFloat, -150, nil, nil)
825 wheel.minDirtPercentage = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#minDirtPercentage", getXMLFloat, 0.35, nil, nil)
826
827 wheel.smoothGroundRadius = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#smoothGroundRadius", getXMLFloat, Utils.getNoNil(wheel.smoothGroundRadius, math.max(0.6, wheel.width*0.75)), nil, nil)
828 wheel.versatileYRot = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#versatileYRot", getXMLBool, false, nil, nil)
829 wheel.forceVersatility = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#forceVersatility", getXMLBool, false, nil, nil)
830 wheel.supportsWheelSink = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#supportsWheelSink", getXMLBool, true, nil, nil)
831 wheel.maxWheelSink = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#maxWheelSink", getXMLFloat, math.huge, nil, nil)
832
833 wheel.hasTireTracks = ConfigurationUtil.getConfigurationValue(xmlFile, key, wheelnamei, "#hasTireTracks", getXMLBool, false, nil, nil)
834 wheel.hasParticles = ConfigurationUtil.getConfigurationValue(xmlFile, key, wheelnamei, "#hasParticles", getXMLBool, false, nil, nil)
835
836 local steeringKey = wheelnamei .. ".steering"
837 wheel.steeringNode = I3DUtil.indexToObject(self.components, ConfigurationUtil.getConfigurationValue(xmlFile, key, steeringKey, "#node", getXMLString, nil, nil, nil), self.i3dMappings)
838 wheel.steeringRotNode = I3DUtil.indexToObject(self.components, ConfigurationUtil.getConfigurationValue(xmlFile, key, steeringKey, "#rotNode", getXMLString, nil, nil, nil), self.i3dMappings)
839 wheel.steeringNodeMinTransX = ConfigurationUtil.getConfigurationValue(xmlFile, key, steeringKey, "#nodeMinTransX", getXMLFloat, nil, nil, nil)
840 wheel.steeringNodeMaxTransX = ConfigurationUtil.getConfigurationValue(xmlFile, key, steeringKey, "#nodeMaxTransX", getXMLFloat, nil, nil, nil)
841 wheel.steeringNodeMinRotY = MathUtil.degToRad(ConfigurationUtil.getConfigurationValue(xmlFile, key, steeringKey, "#nodeMinRotY", getXMLFloat, nil, nil, nil))
842 wheel.steeringNodeMaxRotY = MathUtil.degToRad(ConfigurationUtil.getConfigurationValue(xmlFile, key, steeringKey, "#nodeMaxRotY", getXMLFloat, nil, nil, nil))
843
844 local fenderKey = wheelnamei .. ".fender"
845 wheel.fenderNode = I3DUtil.indexToObject(self.components, ConfigurationUtil.getConfigurationValue(xmlFile, key, fenderKey, "#node", getXMLString, nil, nil, nil), self.i3dMappings)
846 wheel.fenderRotMax = ConfigurationUtil.getConfigurationValue(xmlFile, key, fenderKey, "#rotMax", getXMLFloat, nil, nil, nil)
847 wheel.fenderRotMin = ConfigurationUtil.getConfigurationValue(xmlFile, key, fenderKey, "#rotMin", getXMLFloat, nil, nil, nil)
848
849 local steeringAxleKey = wheelnamei .. ".steeringAxle"
850 wheel.steeringAxleScale = ConfigurationUtil.getConfigurationValue(xmlFile, key, steeringAxleKey, "#scale", getXMLFloat, 0, nil, nil)
851 wheel.steeringAxleRotMax = MathUtil.degToRad(ConfigurationUtil.getConfigurationValue(xmlFile, key, steeringAxleKey, "#rotMax", getXMLFloat, 0, nil, nil))
852 wheel.steeringAxleRotMin = MathUtil.degToRad(ConfigurationUtil.getConfigurationValue(xmlFile, key, steeringAxleKey, "#rotMin", getXMLFloat, -0, nil, nil))
853
854 wheel.rotSpeed = MathUtil.degToRad(ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#rotSpeed", getXMLFloat, nil, nil, nil))
855 wheel.rotSpeedNeg = Utils.getNoNilRad(ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#rotSpeedNeg", getXMLFloat, nil, nil, nil), nil)
856 wheel.rotMax = MathUtil.degToRad(ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#rotMax", getXMLFloat, nil, nil, nil))
857 wheel.rotMin = MathUtil.degToRad(ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#rotMin", getXMLFloat, nil, nil, nil))
858
859 wheel.rotSpeedLimit = ConfigurationUtil.getConfigurationValue(xmlFile, key, physicsKey, "#rotSpeedLimit", getXMLFloat, nil, nil, nil)
860 else
861 g_logManager:xmlWarning(self.configFileName, "Invalid repr for wheel '%s'. Needs to be a child of a collision!", key..physicsKey)
862 end
863 else
864 g_logManager:xmlWarning(self.configFileName, "Invalid repr for wheel '%s'!", key..physicsKey)
865 end
866 return true
867end

loadWheelsSteeringDataFromXML

Description
Definition
loadWheelsSteeringDataFromXML()
Code
2373function Wheels:loadWheelsSteeringDataFromXML(xmlFile, ackermannSteeringIndex)
2374 local spec = self.spec_wheels
2375
2376 local key, _ = ConfigurationUtil.getXMLConfigurationKey(xmlFile, ackermannSteeringIndex, "vehicle.wheels.ackermannSteeringConfigurations.ackermannSteering", "vehicle.ackermannSteering", "ackermann")
2377
2378 spec.steeringCenterNode = nil
2379 local rotSpeed = getXMLFloat(xmlFile, key.."#rotSpeed")
2380 local rotMax = getXMLFloat(xmlFile, key.."#rotMax")
2381
2382 local centerX
2383 local centerZ
2384 local rotCenterWheel1 = getXMLInt(xmlFile, key.."#rotCenterWheel1")
2385 if rotCenterWheel1 ~= nil and spec.wheels[rotCenterWheel1] ~= nil then
2386 local wheel = spec.wheels[rotCenterWheel1]
2387 centerX, _, centerZ = localToLocal(wheel.node, self.components[1].node, wheel.positionX, wheel.positionY, wheel.positionZ)
2388
2389 local rotCenterWheel2 = getXMLInt(xmlFile, key.."#rotCenterWheel2")
2390 if rotCenterWheel2 ~= nil and spec.wheels[rotCenterWheel2] ~= nil then
2391 local wheel2 = spec.wheels[rotCenterWheel2]
2392 local x, _, z = localToLocal(wheel2.node, self.components[1].node, wheel2.positionX, wheel2.positionY, wheel2.positionZ)
2393 centerX, centerZ = 0.5*(centerX + x), 0.5*(centerZ + z)
2394 end
2395 else
2396 local centerNode, _ = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, key.."#rotCenterNode"), self.i3dMappings)
2397 if centerNode ~= nil then
2398 centerX, _, centerZ = localToLocal(centerNode, self.components[1].node, 0,0,0)
2399 spec.steeringCenterNode = centerNode
2400 else
2401 local p = StringUtil.getVectorNFromString(getXMLString(xmlFile, key.."#rotCenter", 2))
2402 if p ~= nil then
2403 centerX = p[1]
2404 centerZ = p[2]
2405 end
2406 end
2407 end
2408 if spec.steeringCenterNode == nil then
2409 spec.steeringCenterNode = createTransformGroup("steeringCenterNode")
2410 link(self.components[1].node, spec.steeringCenterNode)
2411 if centerX ~= nil and centerZ ~= nil then
2412 setTranslation(spec.steeringCenterNode, centerX, 0, centerZ)
2413 end
2414 end
2415
2416 if rotSpeed ~= nil and rotMax ~= nil and centerX ~= nil then
2417 rotSpeed = math.abs(math.rad(rotSpeed))
2418 rotMax = math.abs(math.rad(rotMax))
2419
2420 -- find the wheel that should get the maximum steering (the one that results in the maximum turnign radius)
2421 local maxTurningRadius = 0
2422 local maxTurningRadiusWheel = 0
2423 for i, wheel in ipairs(spec.wheels) do
2424 if wheel.rotSpeed ~= 0 then
2425 local diffX, _, diffZ = localToLocal(wheel.node, spec.steeringCenterNode, wheel.positionX, wheel.positionY, wheel.positionZ)
2426 local turningRadius = math.abs(diffZ)/math.tan(rotMax) + math.abs(diffX)
2427 if turningRadius >= maxTurningRadius then
2428 maxTurningRadius = turningRadius
2429 maxTurningRadiusWheel = i
2430 end
2431 end
2432 end
2433 self.maxRotation = math.max(Utils.getNoNil(self.maxRotation, 0), rotMax)
2434 self.maxTurningRadius = maxTurningRadius
2435 self.maxTurningRadiusWheel = maxTurningRadiusWheel
2436 self.wheelSteeringDuration = rotMax / rotSpeed
2437
2438 if maxTurningRadiusWheel > 0 then
2439 for _, wheel in ipairs(spec.wheels) do
2440
2441 if wheel.rotSpeed ~= 0 then
2442 local diffX, _, diffZ = localToLocal(wheel.node, spec.steeringCenterNode, wheel.positionX, wheel.positionY, wheel.positionZ)
2443
2444 local rotMaxI = math.atan(diffZ/(maxTurningRadius-diffX))
2445 local rotMinI = -math.atan(diffZ/(maxTurningRadius+diffX))
2446
2447 local switchMaxMin = rotMinI > rotMaxI
2448 if switchMaxMin then
2449 rotMaxI, rotMinI = rotMinI, rotMaxI
2450 end
2451
2452 wheel.rotMax = rotMaxI
2453 wheel.rotMin = rotMinI
2454 wheel.rotSpeed = rotMaxI/self.wheelSteeringDuration
2455 wheel.rotSpeedNeg = -rotMinI/self.wheelSteeringDuration
2456
2457 if switchMaxMin then
2458 wheel.rotSpeed, wheel.rotSpeedNeg = -wheel.rotSpeedNeg, -wheel.rotSpeed
2459 end
2460 end
2461 end
2462 end
2463 end
2464
2465 for _, wheel in ipairs(spec.wheels) do
2466 if wheel.rotSpeed ~= 0 then
2467 -- if both speed and rot have the same sign, we can reach it with the positive time
2468 if (wheel.rotMax >= 0) == (wheel.rotSpeed >= 0) then
2469 self.maxRotTime = math.max(wheel.rotMax/wheel.rotSpeed, self.maxRotTime)
2470 end
2471 if (wheel.rotMin >= 0) == (wheel.rotSpeed >= 0) then
2472 self.maxRotTime = math.max(wheel.rotMin/wheel.rotSpeed, self.maxRotTime)
2473 end
2474
2475 -- if speed and rot have a different sign, we can reach it with the negative time
2476 local rotSpeedNeg = wheel.rotSpeedNeg
2477 if rotSpeedNeg == nil then
2478 rotSpeedNeg = wheel.rotSpeed
2479 end
2480 if (wheel.rotMax >= 0) ~= (rotSpeedNeg >= 0) then
2481 self.minRotTime = math.min(wheel.rotMax/rotSpeedNeg, self.minRotTime)
2482 end
2483 if (wheel.rotMin >= 0) ~= (rotSpeedNeg >= 0) then
2484 self.minRotTime = math.min(wheel.rotMin/rotSpeedNeg, self.minRotTime)
2485 end
2486 end
2487
2488 wheel.fenderRotMax = Utils.getNoNilRad(wheel.fenderRotMax, wheel.rotMax)
2489 wheel.fenderRotMin = Utils.getNoNilRad(wheel.fenderRotMin, wheel.rotMin)
2490 wheel.steeringNodeMaxRot = math.max(wheel.rotMax, wheel.steeringAxleRotMax)
2491 wheel.steeringNodeMinRot = math.min(wheel.rotMin, wheel.steeringAxleRotMin)
2492
2493 if wheel.rotSpeedLimit ~= nil then
2494 wheel.rotSpeedDefault = wheel.rotSpeed
2495 wheel.rotSpeedNegDefault = wheel.rotSpeedNeg
2496 wheel.currentRotSpeedAlpha = 1
2497 end
2498 end
2499end

onDelete

Description
Definition
onDelete()
Code
871function Wheels:onDelete()
872 local spec = self.spec_wheels
873
874 for _, hub in pairs(spec.hubs) do
875 delete(hub.node)
876 end
877
878 for _,wheel in pairs(spec.wheels) do
879 self:deleteVisualWheel(wheel)
880
881 if g_currentMission.tireTrackSystem ~= nil and wheel.tireTrackIndex ~= nil then
882 g_currentMission.tireTrackSystem:destroyTrack(wheel.tireTrackIndex)
883 end
884
885 if wheel.driveGroundParticleSystems ~= nil then
886 for _, ps in pairs(wheel.driveGroundParticleSystems) do
887 ParticleUtil.deleteParticleSystems(ps)
888 end
889 end
890
891 if wheel.additionalWheels ~= nil then
892 for _, additionalWheel in pairs(wheel.additionalWheels) do
893 self:deleteVisualWheel(additionalWheel)
894
895 if g_currentMission.tireTrackSystem ~= nil and additionalWheel.tireTrackIndex ~= nil then
896 g_currentMission.tireTrackSystem:destroyTrack(additionalWheel.tireTrackIndex)
897 end
898 end
899 end
900 end
901
902 if spec.wheelChocks ~= nil then
903 for _, wheelChock in pairs(spec.wheelChocks) do
904 if wheelChock.filename ~= nil then
905 g_i3DManager:releaseSharedI3DFile(wheelChock.filename, self.baseDirectory, true)
906 delete(wheelChock.node)
907 end
908 end
909 end
910
911 for _, dynamicallyLoadedWheel in pairs(spec.dynamicallyLoadedWheels) do
912 self:deleteVisualWheel(dynamicallyLoadedWheel)
913 end
914
915 if spec.surfaceSounds then
916 g_soundManager:deleteSamples(spec.surfaceSounds)
917 end
918end

onLeaveVehicle

Description
Definition
onLeaveVehicle()
Code
2660function Wheels:onLeaveVehicle()
2661 local spec = self.spec_wheels
2662 if self.isServer and self.isAddedToPhysics then
2663 for _,wheel in pairs(spec.wheels) do
2664 setWheelShapeProps(wheel.node, wheel.wheelShape, 0, self:getBrakeForce()*wheel.brakeFactor, wheel.steeringAngle, wheel.rotationDamping)
2665 end
2666 end
2667end

onLoad

Description
Definition
onLoad()
Code
141function Wheels:onLoad(savegame)
142 local spec = self.spec_wheels
143
144 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.driveGroundParticleSystems", "vehicle.wheels.wheelConfigurations.wheelConfiguration.wheels.wheel#hasParticles") --FS13 to FS15
145
146 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.wheelConfigurations.wheelConfiguration", "vehicle.wheels.wheelConfigurations.wheelConfiguration") --FS17 to FS19
147 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.rimColor", "vehicle.wheels.rimColor") --FS17 to FS19
148 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.hubColor", "vehicle.wheels.hubs.color0") --FS17 to FS19
149 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.dynamicallyLoadedWheels", "vehicle.wheels.dynamicallyLoadedWheels") --FS17 to FS19
150 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.ackermannSteeringConfigurations", "vehicle.wheels.ackermannSteeringConfigurations") --FS17 to FS19
151
152 -- wheel setup
153 local wheelConfigurationId = Utils.getNoNil(self.configurations["wheel"], 1)
154 local configKey = string.format("vehicle.wheels.wheelConfigurations.wheelConfiguration(%d)", wheelConfigurationId -1)
155 local key = configKey..".wheels"
156 if self.configurations["wheel"] ~= nil and not hasXMLProperty(self.xmlFile, key) then
157 g_logManager:xmlWarning(self.configFileName, "Invalid wheelConfigurationId '%d'. Using default wheel config instead!", self.configurations["wheel"])
158 -- reset to wheel config 1 to ensure wheels are present
159 wheelConfigurationId = 1
160 key = string.format("vehicle.wheels.wheelConfigurations.wheelConfiguration(%d)", 0) .. ".wheels"
161 end
162 ObjectChangeUtil.updateObjectChanges(self.xmlFile, "vehicle.wheels.wheelConfigurations.wheelConfiguration", wheelConfigurationId , self.components, self)
163
164 -- load configuration independent settings
165 local rimColorStr = getXMLString(self.xmlFile, "vehicle.wheels.rimColor")
166 if rimColorStr ~= nil then
167 spec.rimColor = ConfigurationUtil.getColorFromString(rimColorStr)
168 elseif getXMLBool(self.xmlFile, "vehicle.wheels.rimColor#useBaseColor") then
169 spec.rimColor = ConfigurationUtil.getColorByConfigId(self, "baseColor", self.configurations["baseColor"]) or ConfigurationUtil.getColorByConfigId(self, "baseMaterial", self.configurations["baseMaterial"])
170 end
171
172 if spec.rimColor ~= nil then
173 -- overwrite material from color string with nil unless explicitly defined in material attribute
174 spec.rimColor[4] = getXMLInt(self.xmlFile, "vehicle.wheels.rimColor#material")
175 end
176
177 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.wheels.wheel", "vehicle.wheels.wheelConfigurations.wheelConfiguration.wheels.wheel") --FS17 to FS19
178
179 -- load hubs to hubs/repr nodes
180 spec.hubs = {}
181 self:loadHubs()
182
183 self.maxRotTime = 0
184 self.minRotTime = 0
185 self.rotatedTimeInterpolator = InterpolatorValue:new(0)
186
187 self.autoRotateBackSpeed = ConfigurationUtil.getConfigurationValue(self.xmlFile, key, "", "#autoRotateBackSpeed", getXMLFloat, 1.0, nil, nil)
188 self.speedDependentRotateBack = ConfigurationUtil.getConfigurationValue(self.xmlFile, key, "", "#speedDependentRotateBack", getXMLBool, true, nil, nil)
189 self.differentialIndex = ConfigurationUtil.getConfigurationValue(self.xmlFile, key, "", "#differentialIndex", getXMLInt, nil, nil, nil) -- needed by Drivable
190 spec.ackermannSteeringIndex = ConfigurationUtil.getConfigurationValue(self.xmlFile, key, "", "#ackermannSteeringIndex", getXMLInt, nil, nil, nil)
191
192 --load surface sounds
193 if Utils.getNoNil(getXMLBool(self.xmlFile, key.."#hasSurfaceSounds"), true) then
194 local surfaceSoundLinkNodeStr = ConfigurationUtil.getConfigurationValue(self.xmlFile, key, "", "#surfaceSoundLinkNode", getXMLString, "0>", nil, nil)
195 local surfaceSoundLinkNode = I3DUtil.indexToObject(self.components, surfaceSoundLinkNodeStr, self.i3dMappings)
196 spec.surfaceSounds = {}
197 spec.surfaceIdToSound = {}
198 spec.surfaceNameToSound = {}
199 spec.currentSurfaceSound = nil
200 for _, surfaceSound in pairs(g_currentMission.surfaceSounds) do
201 if surfaceSound.type == "wheel" and surfaceSound.sample ~= nil then
202 local sample = g_soundManager:cloneSample(surfaceSound.sample, surfaceSoundLinkNode, self)
203 sample.sampleName = surfaceSound.name
204
205 table.insert(spec.surfaceSounds, sample)
206 spec.surfaceIdToSound[surfaceSound.materialId] = sample
207 spec.surfaceNameToSound[surfaceSound.name] = sample
208 end
209 end
210 end
211
212 spec.wheelSmoothAccumulation = 0
213
214 spec.wheelCreationTimer = 0
215
216 spec.wheels = {}
217 spec.wheelChocks = {}
218
219 -- load wheels
220 local i = 0
221 while true do
222 local wheelnamei = string.format(".wheel(%d)", i)
223 if not hasXMLProperty(self.xmlFile, key..wheelnamei) then
224 break
225 end
226
227 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.wheels.wheel#repr", "vehicle.wheels.wheelConfigurations.wheelConfiguration.wheels.wheel.physics#repr") --FS17 to FS19
228 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.wheelConfigurations.wheelConfiguration.wheels.wheel#repr", "vehicle.wheels.wheelConfigurations.wheelConfiguration.wheels.wheel.physics#repr") --FS17 to FS19
229 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.wheels.wheelConfigurations.wheelConfiguration.wheels.wheel#repr", "vehicle.wheels.wheelConfigurations.wheelConfiguration.wheels.wheel.physics#repr") --FS17 to FS19
230 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, "vehicle.wheels.wheelConfigurations.wheelConfiguration.wheels.wheel#configIndex", "vehicle.wheels.wheelConfigurations.wheelConfiguration.wheels.wheel#configId") --FS17 to FS19
231
232 local reprStr = ConfigurationUtil.getConfigurationValue(self.xmlFile, key, wheelnamei, ".physics#repr", getXMLString, nil, nil, nil)
233 if reprStr ~= nil then
234 local wheel = {}
235 wheel.repr = I3DUtil.indexToObject(self.components, reprStr, self.i3dMappings)
236 if wheel.repr ~= nil then
237 wheel.xmlIndex = i
238
239 self:loadDynamicWheelDataFromXML(self.xmlFile, key, wheelnamei, wheel)
240
241 self:finalizeWheel(wheel)
242
243 table.insert(spec.wheels, wheel)
244 else
245 g_logManager:xmlWarning(self.configFileName, "Invalid wheel repr '%s'!", reprStr)
246 end
247 else
248 g_logManager:xmlWarning(self.configFileName, "No repr node given for wheel '%s'!", wheelnamei)
249 end
250 i = i+1
251 end
252
253 -- load non physical wheels
254 spec.dynamicallyLoadedWheels = {}
255 local i=0
256 while true do
257 local baseName = string.format("vehicle.wheels.dynamicallyLoadedWheels.dynamicallyLoadedWheel(%d)", i)
258 if not hasXMLProperty(self.xmlFile, baseName) then
259 break
260 end
261
262 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, self.configFileName, baseName .. "#configIndex", baseName .. "#configId") --FS17 to FS19
263
264 local dynamicallyLoadedWheel = {}
265 if self:loadNonPhysicalWheelFromXML(dynamicallyLoadedWheel, self.xmlFile, baseName) then
266 table.insert(spec.dynamicallyLoadedWheels, dynamicallyLoadedWheel)
267 end
268 i = i + 1
269 end
270
271 spec.networkTimeInterpolator = InterpolationTime:new(1.2)
272
273 -- find opposite wheel
274 local numWheels = table.getn(spec.wheels)
275 for iWheel=1,numWheels do
276 local wheel1 = spec.wheels[iWheel]
277 if wheel1.oppositeWheelIndex == nil then
278 for jWheel=1,numWheels do
279 if iWheel ~= jWheel then
280 local wheel2 = spec.wheels[jWheel]
281 if math.abs(wheel1.positionX + wheel2.positionX) < 0.1 and math.abs(wheel1.positionZ - wheel2.positionZ) < 0.1 and math.abs(wheel1.positionY - wheel2.positionY) < 0.1 then
282 wheel1.oppositeWheelIndex = jWheel
283 wheel2.oppositeWheelIndex = iWheel
284 break
285 end
286 end
287 end
288 end
289 end
290
291 --
292 self:loadWheelsSteeringDataFromXML(self.xmlFile, spec.ackermannSteeringIndex)
293
294
295 SpecializationUtil.raiseEvent(self, "onFinishedWheelLoading", self.xmlFile, key)
296
297 spec.brakePedal = 0
298 spec.forceIsActiveTime = 3000
299 spec.forceIsActiveTimer = 0
300 spec.dirtyFlag = self:getNextDirtyFlag()
301end

onLoadFinished

Description
Definition
onLoadFinished()
Code
305function Wheels:onLoadFinished(savegame)
306 self:updateWheelChocksPosition(nil, true)
307end

onPostDetach

Description
Definition
onPostDetach()
Code
2677function Wheels:onPostDetach()
2678 self:updateWheelChocksPosition(false, true)
2679end

onPostUpdate

Description
Definition
onPostUpdate()
Code
1166function Wheels:onPostUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
1167 if self.isServer then
1168 local spec = self.spec_wheels
1169 for _,wheel in ipairs(spec.wheels) do
1170 if wheel.isPositionDirty then
1171 self:updateWheelBase(wheel)
1172 wheel.isPositionDirty = false
1173 end
1174 if wheel.isFrictionDirty then
1175 self:updateWheelTireFriction(wheel)
1176 wheel.isFrictionDirty = false
1177 end
1178 end
1179 end
1180end

onPreAttach

Description
Definition
onPreAttach()
Code
2671function Wheels:onPreAttach()
2672 self:updateWheelChocksPosition(true, false)
2673end

onReadStream

Description
Definition
onReadStream()
Code
922function Wheels:onReadStream(streamId, connection)
923 if connection.isServer then
924 local spec = self.spec_wheels
925 spec.networkTimeInterpolator:reset()
926 for i=1, table.getn(spec.wheels) do
927 local wheel = spec.wheels[i]
928 wheel.netInfo.x = streamReadFloat32(streamId)
929 wheel.netInfo.y = streamReadFloat32(streamId)
930 wheel.netInfo.z = streamReadFloat32(streamId)
931 wheel.netInfo.xDrive = streamReadFloat32(streamId)
932 wheel.netInfo.suspensionLength = streamReadFloat32(streamId)
933 wheel.contact = streamReadUIntN(streamId, 2)
934
935 if wheel.versatileYRot then
936 local yRot = streamReadUIntN(streamId, 9)
937 wheel.steeringAngle = yRot / 511 * math.pi*2
938 end
939
940 wheel.networkInterpolators.position:setPosition(wheel.netInfo.x, wheel.netInfo.y, wheel.netInfo.z)
941 wheel.networkInterpolators.xDrive:setAngle(wheel.netInfo.xDrive)
942 wheel.networkInterpolators.suspensionLength:setValue(wheel.netInfo.suspensionLength)
943 end
944
945 self.rotatedTimeInterpolator:setValue(0)
946 end
947end

onReadUpdateStream

Description
Definition
onReadUpdateStream()
Code
973function Wheels:onReadUpdateStream(streamId, timestamp, connection)
974 if connection.isServer then
975 local hasUpdate = streamReadBool(streamId)
976 if hasUpdate then
977 local spec = self.spec_wheels
978
979 spec.networkTimeInterpolator:startNewPhaseNetwork()
980
981 for i=1, table.getn(spec.wheels) do
982 local wheel = spec.wheels[i]
983 if wheel.isSynchronized then
984
985 local xDrive = streamReadUIntN(streamId, 9)
986 xDrive = xDrive / 511 * math.pi*2
987 wheel.networkInterpolators.xDrive:setTargetAngle(xDrive)
988
989 local y = streamReadUIntN(streamId, 8)
990 y = y / 255 * wheel.netInfo.sync.yRange + wheel.netInfo.sync.yMin
991 wheel.networkInterpolators.position:setTargetPosition(wheel.netInfo.x, y, wheel.netInfo.z)
992
993 local suspLength = streamReadUIntN(streamId, 7)
994 wheel.networkInterpolators.suspensionLength:setTargetValue(suspLength/100)
995
996 if wheel.tireTrackIndex ~= nil then
997 wheel.contact = streamReadUIntN(streamId, 2)
998 end
999
1000 if wheel.versatileYRot then
1001 local yRot = streamReadUIntN(streamId, 9)
1002 wheel.steeringAngle = yRot / 511 * math.pi*2
1003 end
1004
1005 wheel.lastTerrainValue = streamReadUIntN(streamId, 3)
1006 end
1007 end
1008
1009 if self.maxRotTime ~= 0 and self.minRotTime ~= 0 then
1010 local rotatedTimeRange = math.max(self.maxRotTime - self.minRotTime, 0.001)
1011 local rotatedTime = streamReadUIntN(streamId, 8)
1012 -- set to 0 due to inaccuracy
1013 if math.abs(self.rotatedTime) < 0.001 then
1014 self.rotatedTime = 0
1015 end
1016
1017 local rotatedTimeTarget = rotatedTime / 255 * rotatedTimeRange + self.minRotTime
1018 self.rotatedTimeInterpolator:setTargetValue(rotatedTimeTarget)
1019 end
1020 end
1021 end
1022end

onUpdate

Description
Called on update
Definition
onUpdate(float dt, boolean isActiveForInput, boolean isSelected)
Arguments
floatdttime since last call in ms
booleanisActiveForInputtrue if vehicle is active for input
booleanisSelectedtrue if vehicle is selected
Code
1068function Wheels:onUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
1069 local spec = self.spec_wheels
1070
1071 if self.isServer then
1072 if spec.wheelCreationTimer > 0 then
1073 spec.wheelCreationTimer = spec.wheelCreationTimer - 1
1074 if spec.wheelCreationTimer == 0 then
1075 for _,wheel in pairs(spec.wheels) do
1076 wheel.wheelShapeCreated = true
1077 end
1078 end
1079 end
1080 end
1081
1082 -- interpolation of wheel properties
1083 if not self.isServer and self.isClient then
1084 spec.networkTimeInterpolator:update(dt)
1085 local interpolationAlpha = spec.networkTimeInterpolator:getAlpha()
1086
1087 self.rotatedTime = self.rotatedTimeInterpolator:getInterpolatedValue(interpolationAlpha)
1088
1089 for i=1, table.getn(spec.wheels) do
1090 local wheel = spec.wheels[i]
1091 wheel.netInfo.x, wheel.netInfo.y, wheel.netInfo.z = wheel.networkInterpolators.position:getInterpolatedValues(interpolationAlpha)
1092 wheel.netInfo.xDrive = wheel.networkInterpolators.xDrive:getInterpolatedValue(interpolationAlpha)
1093 wheel.netInfo.suspensionLength = wheel.networkInterpolators.suspensionLength:getInterpolatedValue(interpolationAlpha)
1094
1095 if wheel.driveGroundParticleSystems ~= nil then
1096 for _, typedPs in pairs(wheel.driveGroundParticleSystems) do
1097 for _, ps in ipairs(typedPs) do
1098 setTranslation(ps.emitterShape, wheel.netInfo.x + ps.offsets[1], wheel.netInfo.y + ps.offsets[2], wheel.netInfo.z + ps.offsets[3])
1099 end
1100 end
1101 end
1102 end
1103
1104 if spec.networkTimeInterpolator:isInterpolating() then
1105 self:raiseActive()
1106 end
1107 end
1108
1109 if self.firstTimeRun then
1110
1111 for _,wheel in pairs(spec.wheels) do
1112 if self.isActive then
1113 self:updateWheelContact(wheel)
1114 self:updateWheelSink(wheel, dt)
1115 self:updateWheelFriction(wheel, dt)
1116 self:updateWheelTireTracks(wheel)
1117 self:updateWheelDensityMapHeight(wheel, dt)
1118 self:updateWheelDestruction(wheel, dt)
1119
1120 WheelsUtil.updateWheelPhysics(self, wheel, spec.brakePedal, dt)
1121 end
1122 local changed = WheelsUtil.updateWheelGraphics(self, wheel, dt)
1123 if wheel.updateWheelChock and changed then
1124 for _, wheelChock in ipairs(wheel.wheelChocks) do
1125 self:updateWheelChockPosition(wheelChock, false)
1126 end
1127 end
1128 end
1129
1130 if self:getAreSurfaceSoundsActive() then
1131 -- update surface sounds
1132 if spec.surfaceSounds ~= nil then
1133 local currentSound = self:getCurrentSurfaceSound()
1134 if currentSound ~= spec.currentSurfaceSound then
1135 if spec.currentSurfaceSound ~= nil then
1136 g_soundManager:stopSample(spec.currentSurfaceSound)
1137 end
1138 if currentSound ~= nil then
1139 g_soundManager:playSample(currentSound)
1140 end
1141
1142 spec.currentSurfaceSound = currentSound
1143 else
1144 if not g_soundManager:getIsSamplePlaying(spec.currentSurfaceSound) then
1145 g_soundManager:playSample(currentSound)
1146 end
1147 end
1148 end
1149 else
1150 if spec.currentSurfaceSound ~= nil then
1151 g_soundManager:stopSample(spec.currentSurfaceSound)
1152 end
1153 end
1154 end
1155
1156 if #spec.wheels > 0 then
1157 if self.isServer then
1158 self:raiseDirtyFlags(spec.dirtyFlag)
1159 end
1160 end
1161end

onUpdateEnd

Description
Called after last update tick
Definition
onUpdateEnd(float dt, boolean isActive, boolean isActiveForInput, boolean isSelected)
Arguments
floatdttime since last call in ms
booleanisActivetrue if vehicle is active
booleanisActiveForInputtrue if vehicle is active for input
booleanisSelectedtrue if vehicle is selected
Code
1307function Wheels:onUpdateEnd(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
1308 if self.isClient then
1309 local spec = self.spec_wheels
1310 for _, wheel in pairs(spec.wheels) do
1311 if wheel.driveGroundParticleSystems ~= nil then
1312 for _, typedPs in pairs(wheel.driveGroundParticleSystems) do
1313 for _, ps in ipairs(typedPs) do
1314 ParticleUtil.setEmittingState(ps, false)
1315 end
1316 end
1317 end
1318 end
1319
1320 if spec.currentSurfaceSound ~= nil then
1321 g_soundManager:stopSample(spec.currentSurfaceSound)
1322 spec.currentSurfaceSound = nil
1323 end
1324 end
1325end

onUpdateTick

Description
Called on update tick
Definition
onUpdateTick(float dt, boolean isActive, boolean isActiveForInput, boolean isSelected)
Arguments
floatdttime since last call in ms
booleanisActivetrue if vehicle is active
booleanisActiveForInputtrue if vehicle is active for input
booleanisSelectedtrue if vehicle is selected
Code
1188function Wheels:onUpdateTick(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
1189 local spec = self.spec_wheels
1190
1191 for _, wheel in pairs(spec.wheels) do
1192 if wheel.rotSpeedLimit ~= nil then
1193 local dir = -1
1194 if self:getLastSpeed() <= wheel.rotSpeedLimit then
1195 dir = 1
1196 end
1197
1198 wheel.currentRotSpeedAlpha = MathUtil.clamp(wheel.currentRotSpeedAlpha + dir*(dt/1000), 0, 1)
1199 wheel.rotSpeed = wheel.rotSpeedDefault * wheel.currentRotSpeedAlpha
1200 wheel.rotSpeedNeg = wheel.rotSpeedNegDefault * wheel.currentRotSpeedAlpha
1201 end
1202 end
1203
1204 if self.isClient then
1205 local speed = self:getLastSpeed()
1206 local groundWetness = g_currentMission.environment.weather:getGroundWetness()
1207 local groundIsWet = groundWetness > 0.2
1208 for _,wheel in pairs(spec.wheels) do
1209 if wheel.driveGroundParticleSystems ~= nil then
1210 local states = wheel.driveGroundParticleStates
1211 local enableSoilPS = false
1212 if wheel.lastTerrainValue > 0 and wheel.lastTerrainValue < 5 then
1213 enableSoilPS = (speed > 1) -- and wheel.sink > 0
1214 end
1215 local sizeScale = 2 * wheel.width * wheel.radiusOriginal
1216
1217 states.driving_dry = enableSoilPS
1218 states.driving_wet = enableSoilPS and groundIsWet
1219 states.driving_dust = not groundIsWet
1220
1221 for psName, state in pairs(states) do
1222 local typedPs = wheel.driveGroundParticleSystems[psName]
1223
1224 for _, ps in ipairs(typedPs) do
1225 if state then
1226 if self.movingDirection < 0 then
1227 setRotation(ps.emitterShape, 0, math.pi+wheel.steeringAngle, 0)
1228 else
1229 setRotation(ps.emitterShape, 0, wheel.steeringAngle, 0)
1230 end
1231
1232 local scale
1233 if psName ~= "driving_dust" then
1234 local wheelSpeed = MathUtil.rpmToMps(wheel.netInfo.xDriveSpeed / (2*math.pi) * 60, wheel.radius)
1235 local wheelSlip = math.pow(wheelSpeed/self.lastSpeedReal, 2.5)
1236 scale = self:getDriveGroundParticleSystemsScale(ps, wheelSpeed) * wheelSlip
1237 else
1238 scale = self:getDriveGroundParticleSystemsScale(ps, self.lastSpeedReal)
1239 end
1240
1241 if ps.isTintable then
1242 -- interpolate between different ground colors to avoid unrealisitic particle color changes
1243 if ps.lastColor == nil then
1244 ps.lastColor = {ps.wheel.lastColor[1],ps.wheel.lastColor[2],ps.wheel.lastColor[3]}
1245 ps.targetColor = {ps.wheel.lastColor[1],ps.wheel.lastColor[2],ps.wheel.lastColor[3]}
1246 ps.currentColor = {ps.wheel.lastColor[1],ps.wheel.lastColor[2],ps.wheel.lastColor[3]}
1247 ps.alpha = 1
1248 end
1249
1250 if ps.alpha ~= 1 then
1251 ps.alpha = math.min(ps.alpha + dt/1000, 1)
1252 ps.currentColor = {MathUtil.vector3ArrayLerp(ps.lastColor, ps.targetColor, ps.alpha)}
1253 if ps.alpha == 1 then
1254 ps.lastColor[1] = ps.currentColor[1]
1255 ps.lastColor[2] = ps.currentColor[2]
1256 ps.lastColor[3] = ps.currentColor[3]
1257 end
1258 end
1259
1260 if ps.alpha == 1 and ps.wheel.lastColor[1] ~= ps.targetColor[1] and ps.wheel.lastColor[2] ~= ps.targetColor[2] and ps.wheel.lastColor[3] ~= ps.targetColor[3] then
1261 ps.alpha = 0
1262 ps.targetColor[1] = ps.wheel.lastColor[1]
1263 ps.targetColor[2] = ps.wheel.lastColor[2]
1264 ps.targetColor[3] = ps.wheel.lastColor[3]
1265 end
1266 end
1267
1268 if scale > 0 then
1269 ParticleUtil.setEmittingState(ps, true)
1270 if ps.isTintable then
1271 setShaderParameter(ps.shape, "psColor", ps.currentColor[1], ps.currentColor[2], ps.currentColor[3], 1, false)
1272 end
1273 else
1274 ParticleUtil.setEmittingState(ps, false)
1275 end
1276
1277 -- emit count
1278 local maxSpeed = (50 / 3.6)
1279 local circum = wheel.radiusOriginal
1280 local maxWheelRpm = maxSpeed / circum
1281 local wheelRotFactor = Utils.getNoNil(wheel.netInfo.xDriveSpeed, 0) / maxWheelRpm
1282 local emitScale = scale * wheelRotFactor * sizeScale
1283 ParticleUtil.setEmitCountScale(ps, MathUtil.clamp(emitScale, ps.minScale, ps.maxScale))
1284
1285 -- speeds
1286 local speedFactor = 1.0
1287 ParticleUtil.setParticleSystemSpeed(ps, ps.particleSpeed * speedFactor)
1288 ParticleUtil.setParticleSystemSpeedRandom(ps, ps.particleRandomSpeed * speedFactor)
1289 else
1290 ParticleUtil.setEmittingState(ps, false)
1291 end
1292 end
1293
1294 states[psName] = false
1295 end
1296 end
1297 end
1298 end
1299end

onWriteStream

Description
Definition
onWriteStream()
Code
951function Wheels:onWriteStream(streamId, connection)
952 if not connection.isServer then
953 local spec = self.spec_wheels
954 for i=1, table.getn(spec.wheels) do
955 local wheel = spec.wheels[i]
956 streamWriteFloat32(streamId, wheel.netInfo.x)
957 streamWriteFloat32(streamId, wheel.netInfo.y)
958 streamWriteFloat32(streamId, wheel.netInfo.z)
959 streamWriteFloat32(streamId, wheel.netInfo.xDrive)
960 streamWriteFloat32(streamId, wheel.netInfo.suspensionLength)
961 streamWriteUIntN(streamId, wheel.contact, 2)
962
963 if wheel.versatileYRot then
964 local yRot = wheel.steeringAngle % (math.pi*2)
965 streamWriteUIntN(streamId, MathUtil.clamp(math.floor(yRot / (math.pi*2) * 511), 0, 511), 9)
966 end
967 end
968 end
969end

onWriteUpdateStream

Description
Definition
onWriteUpdateStream()
Code
1026function Wheels:onWriteUpdateStream(streamId, connection, dirtyMask)
1027 if not connection.isServer then
1028 local spec = self.spec_wheels
1029
1030 if streamWriteBool(streamId, bitAND(dirtyMask, spec.dirtyFlag) ~= 0) then
1031
1032 for i=1, table.getn(spec.wheels) do
1033 local wheel = spec.wheels[i]
1034 if wheel.isSynchronized then
1035 local xDrive = wheel.netInfo.xDrive % (math.pi*2)
1036 streamWriteUIntN(streamId, MathUtil.clamp(math.floor(xDrive / (math.pi*2) * 511), 0, 511), 9)
1037
1038 streamWriteUIntN(streamId, MathUtil.clamp(math.floor((wheel.netInfo.y - wheel.netInfo.sync.yMin) / wheel.netInfo.sync.yRange * 255), 0, 255), 8)
1039
1040 streamWriteUIntN(streamId, MathUtil.clamp(wheel.netInfo.suspensionLength*100, 0, 128), 7)
1041
1042 if wheel.tireTrackIndex ~= nil then
1043 streamWriteUIntN(streamId, wheel.contact, 2)
1044 end
1045
1046 if wheel.versatileYRot then
1047 local yRot = wheel.steeringAngle % (math.pi*2)
1048 streamWriteUIntN(streamId, MathUtil.clamp(math.floor(yRot / (math.pi*2) * 511), 0, 511), 9)
1049 end
1050
1051 streamWriteUIntN(streamId, wheel.lastTerrainValue, 3)
1052 end
1053 end
1054 if self.maxRotTime ~= 0 and self.minRotTime ~= 0 then
1055 local rotatedTimeRange = math.max(self.maxRotTime - self.minRotTime, 0.001)
1056 local rotatedTime = MathUtil.clamp(math.floor((self.rotatedTime - self.minRotTime)/rotatedTimeRange * 255), 0, 255)
1057 streamWriteUIntN(streamId, rotatedTime, 8)
1058 end
1059 end
1060 end
1061end

prerequisitesPresent

Description
Definition
prerequisitesPresent()
Code
48function Wheels.prerequisitesPresent(specializations)
49 return true
50end

registerEventListeners

Description
Definition
registerEventListeners()
Code
115function Wheels.registerEventListeners(vehicleType)
116 SpecializationUtil.registerEventListener(vehicleType, "onLoad", Wheels)
117 SpecializationUtil.registerEventListener(vehicleType, "onLoadFinished", Wheels)
118 SpecializationUtil.registerEventListener(vehicleType, "onDelete", Wheels)
119 SpecializationUtil.registerEventListener(vehicleType, "onReadStream", Wheels)
120 SpecializationUtil.registerEventListener(vehicleType, "onWriteStream", Wheels)
121 SpecializationUtil.registerEventListener(vehicleType, "onReadUpdateStream", Wheels)
122 SpecializationUtil.registerEventListener(vehicleType, "onWriteUpdateStream", Wheels)
123 SpecializationUtil.registerEventListener(vehicleType, "onUpdate", Wheels)
124 SpecializationUtil.registerEventListener(vehicleType, "onPostUpdate", Wheels)
125 SpecializationUtil.registerEventListener(vehicleType, "onUpdateTick", Wheels)
126 SpecializationUtil.registerEventListener(vehicleType, "onUpdateEnd", Wheels)
127 SpecializationUtil.registerEventListener(vehicleType, "onLeaveVehicle", Wheels)
128 SpecializationUtil.registerEventListener(vehicleType, "onPreAttach", Wheels)
129 SpecializationUtil.registerEventListener(vehicleType, "onPostDetach", Wheels)
130end

registerEvents

Description
Definition
registerEvents()
Code
54function Wheels.registerEvents(vehicleType)
55 SpecializationUtil.registerEvent(vehicleType, "onBrake")
56 SpecializationUtil.registerEvent(vehicleType, "onFinishedWheelLoading")
57end

registerFunctions

Description
Definition
registerFunctions()
Code
61function Wheels.registerFunctions(vehicleType)
62 -- update
63 SpecializationUtil.registerFunction(vehicleType, "updateWheelContact", Wheels.updateWheelContact)
64 SpecializationUtil.registerFunction(vehicleType, "updateWheelTireTracks", Wheels.updateWheelTireTracks)
65 SpecializationUtil.registerFunction(vehicleType, "updateWheelDensityMapHeight", Wheels.updateWheelDensityMapHeight)
66 SpecializationUtil.registerFunction(vehicleType, "updateWheelDestruction", Wheels.updateWheelDestruction)
67 SpecializationUtil.registerFunction(vehicleType, "getIsWheelFoliageDestructionAllowed", Wheels.getIsWheelFoliageDestructionAllowed)
68 SpecializationUtil.registerFunction(vehicleType, "updateWheelSink", Wheels.updateWheelSink)
69 SpecializationUtil.registerFunction(vehicleType, "updateWheelFriction", Wheels.updateWheelFriction)
70 SpecializationUtil.registerFunction(vehicleType, "updateWheelBase", Wheels.updateWheelBase)
71 SpecializationUtil.registerFunction(vehicleType, "updateWheelTireFriction", Wheels.updateWheelTireFriction)
72 SpecializationUtil.registerFunction(vehicleType, "setWheelPositionDirty", Wheels.setWheelPositionDirty)
73 SpecializationUtil.registerFunction(vehicleType, "setWheelTireFrictionDirty", Wheels.setWheelTireFrictionDirty)
74 SpecializationUtil.registerFunction(vehicleType, "getDriveGroundParticleSystemsScale", Wheels.getDriveGroundParticleSystemsScale)
75 SpecializationUtil.registerFunction(vehicleType, "loadDynamicWheelDataFromXML", Wheels.loadDynamicWheelDataFromXML)
76 SpecializationUtil.registerFunction(vehicleType, "loadWheelParticleSystem", Wheels.loadWheelParticleSystem)
77 SpecializationUtil.registerFunction(vehicleType, "finalizeWheel", Wheels.finalizeWheel)
78 SpecializationUtil.registerFunction(vehicleType, "finalizeConnector", Wheels.finalizeConnector)
79 SpecializationUtil.registerFunction(vehicleType, "loadHubs", Wheels.loadHubs)
80 SpecializationUtil.registerFunction(vehicleType, "loadConnectorFromXML", Wheels.loadConnectorFromXML)
81 SpecializationUtil.registerFunction(vehicleType, "loadWheelDataFromExternalXML", Wheels.loadWheelDataFromExternalXML)
82 SpecializationUtil.registerFunction(vehicleType, "loadWheelPhysicsData", Wheels.loadWheelPhysicsData)
83 SpecializationUtil.registerFunction(vehicleType, "loadWheelData", Wheels.loadWheelData)
84 SpecializationUtil.registerFunction(vehicleType, "loadHubFromXML", Wheels.loadHubFromXML)
85 SpecializationUtil.registerFunction(vehicleType, "loadWheelsSteeringDataFromXML", Wheels.loadWheelsSteeringDataFromXML)
86 SpecializationUtil.registerFunction(vehicleType, "loadNonPhysicalWheelFromXML", Wheels.loadNonPhysicalWheelFromXML)
87 SpecializationUtil.registerFunction(vehicleType, "deleteVisualWheel", Wheels.deleteVisualWheel)
88 SpecializationUtil.registerFunction(vehicleType, "getIsVersatileYRotActive", Wheels.getIsVersatileYRotActive)
89 SpecializationUtil.registerFunction(vehicleType, "getWheelFromWheelIndex", Wheels.getWheelFromWheelIndex)
90 SpecializationUtil.registerFunction(vehicleType, "getWheels", Wheels.getWheels)
91 SpecializationUtil.registerFunction(vehicleType, "getCurrentSurfaceSound", Wheels.getCurrentSurfaceSound)
92 SpecializationUtil.registerFunction(vehicleType, "getAreSurfaceSoundsActive", Wheels.getAreSurfaceSoundsActive)
93 SpecializationUtil.registerFunction(vehicleType, "destroyFruitArea", Wheels.destroyFruitArea)
94 SpecializationUtil.registerFunction(vehicleType, "brake", Wheels.brake)
95 SpecializationUtil.registerFunction(vehicleType, "getBrakeForce", Wheels.getBrakeForce)
96 SpecializationUtil.registerFunction(vehicleType, "updateWheelChocksPosition", Wheels.updateWheelChocksPosition)
97 SpecializationUtil.registerFunction(vehicleType, "updateWheelChockPosition", Wheels.updateWheelChockPosition)
98 SpecializationUtil.registerFunction(vehicleType, "updateWheelDirtAmount", Wheels.updateWheelDirtAmount)
99 SpecializationUtil.registerFunction(vehicleType, "getAllowTireTracks", Wheels.getAllowTireTracks)
100 SpecializationUtil.registerFunction(vehicleType, "getTireTrackColor", Wheels.getTireTrackColor)
101end

registerOverwrittenFunctions

Description
Definition
registerOverwrittenFunctions()
Code
105function Wheels.registerOverwrittenFunctions(vehicleType)
106 SpecializationUtil.registerOverwrittenFunction(vehicleType, "addToPhysics", Wheels.addToPhysics)
107 SpecializationUtil.registerOverwrittenFunction(vehicleType, "removeFromPhysics", Wheels.removeFromPhysics)
108 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getTotalMass", Wheels.getTotalMass)
109 SpecializationUtil.registerOverwrittenFunction(vehicleType, "validateWashableNode", Wheels.validateWashableNode)
110 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getAIVehicleDirectionNode", Wheels.getAIVehicleDirectionNode)
111end

removeFromPhysics

Description
Definition
removeFromPhysics()
Code
1359function Wheels:removeFromPhysics(superFunc)
1360 local ret = superFunc(self)
1361
1362 if self.isServer then
1363 local spec = self.spec_wheels
1364 for _, wheel in pairs(spec.wheels) do
1365 wheel.wheelShape = 0
1366 wheel.wheelShapeCreated = false
1367 end
1368 end
1369 return ret
1370end

setWheelPositionDirty

Description
Definition
setWheelPositionDirty()
Code
1455function Wheels:setWheelPositionDirty(wheel)
1456 if wheel ~= nil then
1457 wheel.isPositionDirty = true
1458 end
1459end

setWheelTireFrictionDirty

Description
Definition
setWheelTireFrictionDirty()
Code
1463function Wheels:setWheelTireFrictionDirty(wheel)
1464 if wheel ~= nil then
1465 wheel.isFrictionDirty = true
1466 end
1467end

updateWheelBase

Description
Definition
updateWheelBase()
Code
1942function Wheels:updateWheelBase(wheel)
1943 if self.isServer and self.isAddedToPhysics then
1944 local positionX, positionY, positionZ = wheel.positionX-wheel.directionX*wheel.deltaY, wheel.positionY-wheel.directionY*wheel.deltaY, wheel.positionZ-wheel.directionZ*wheel.deltaY
1945
1946 local x1, y1, z1 = localToWorld(wheel.node, wheel.positionX, wheel.positionY, wheel.positionZ)
1947 local x2, y2, z2 = localToWorld(wheel.node, positionX, positionY, positionZ)
1948 drawDebugLine(x1, y1, z1, 1, 0, 0, x2, y2, z2, 0, 1, 0)
1949
1950 local collisionMask = 255 - 4 -- all up to bit 8, except bit 2 which is set by the players kinematic object
1951 wheel.wheelShape = createWheelShape(wheel.node, positionX, positionY, positionZ, wheel.radius, wheel.suspTravel, wheel.spring, wheel.damperCompressionLowSpeed, wheel.damperCompressionHighSpeed, wheel.damperCompressionLowSpeedThreshold, wheel.damperRelaxationLowSpeed, wheel.damperRelaxationHighSpeed, wheel.damperRelaxationLowSpeedThreshold, wheel.mass, collisionMask, wheel.wheelShape)
1952
1953 local forcePointY = positionY - wheel.radius * wheel.forcePointRatio
1954 local steeringX, steeringY, steeringZ = localToLocal(getParent(wheel.repr), wheel.node, wheel.startPositionX, wheel.startPositionY+wheel.deltaY, wheel.startPositionZ)
1955 setWheelShapeForcePoint(wheel.node, wheel.wheelShape, wheel.positionX, forcePointY, positionZ)
1956 setWheelShapeSteeringCenter(wheel.node, wheel.wheelShape, steeringX, steeringY, steeringZ)
1957 setWheelShapeDirection(wheel.node, wheel.wheelShape, wheel.directionX, wheel.directionY, wheel.directionZ, wheel.axleX, wheel.axleY, wheel.axleZ)
1958 setWheelShapeWidth(wheel.node, wheel.wheelShape, wheel.wheelshapeWidth, wheel.widthOffset)
1959
1960 if wheel.driveGroundParticleSystems ~= nil then
1961 for _,typedPs in pairs(wheel.driveGroundParticleSystems) do
1962 for _, ps in ipairs(typedPs) do
1963 setTranslation(ps.emitterShape, wheel.positionX + ps.offsets[1], positionY + ps.offsets[2], wheel.positionZ + ps.offsets[3])
1964 end
1965 end
1966 end
1967 end
1968end

updateWheelChockPosition

Description
Definition
updateWheelChockPosition()
Code
2609function Wheels:updateWheelChockPosition(wheelChock, isInParkingPosition)
2610 if isInParkingPosition then
2611 if wheelChock.parkingNode ~= nil then
2612 setTranslation(wheelChock.node, 0, 0, 0)
2613 setRotation(wheelChock.node, 0, 0, 0)
2614 link(wheelChock.parkingNode, wheelChock.node)
2615 setVisibility(wheelChock.node, true)
2616 else
2617 setVisibility(wheelChock.node, false)
2618 end
2619 else
2620 setVisibility(wheelChock.node, true)
2621 local wheel = wheelChock.wheel
2622
2623 local radiusChockHeightOffset = wheel.radius - wheel.deformation - wheelChock.height
2624 local angle = math.acos(radiusChockHeightOffset / wheel.radius)
2625 local zWheelIntersection = wheel.radius * math.sin(angle)
2626 local zChockOffset = -zWheelIntersection - wheelChock.zOffset
2627
2628 link(wheel.node, wheelChock.node)
2629
2630 local _, yRot, _ = localRotationToLocal(getParent(wheel.repr), wheel.node, getRotation(wheel.repr))
2631 if wheelChock.isInverted then
2632 yRot = yRot + math.pi
2633 end
2634 setRotation(wheelChock.node, 0, yRot, 0)
2635
2636 local dirX, dirY, dirZ = localDirectionToLocal(wheelChock.node, wheel.node, 0, 0, 1)
2637 local normX, normY, normZ = localDirectionToLocal(wheelChock.node, wheel.node, 1, 0, 0)
2638
2639 local posX, posY, posZ = localToLocal(wheel.driveNode, wheel.node, 0, 0, 0)
2640 posX = posX + normX * wheelChock.offset[1] + dirX * (zChockOffset + wheelChock.offset[3])
2641 posY = posY + normY * wheelChock.offset[1] + dirY * (zChockOffset + wheelChock.offset[3]) - wheel.radius + wheel.deformation + wheelChock.offset[2]
2642 posZ = posZ + normZ * wheelChock.offset[1] + dirZ * (zChockOffset + wheelChock.offset[3])
2643
2644 setTranslation(wheelChock.node, posX, posY, posZ)
2645 end
2646
2647 if wheelChock.parkedNode ~= nil then
2648 setVisibility(wheelChock.parkedNode, isInParkingPosition)
2649 end
2650
2651 if wheelChock.linkedNode ~= nil then
2652 setVisibility(wheelChock.linkedNode, not isInParkingPosition)
2653 end
2654
2655 return true
2656end

updateWheelChocksPosition

Description
Definition
updateWheelChocksPosition()
Code
2596function Wheels:updateWheelChocksPosition(isInParkingPosition, continueUpdate)
2597 local spec = self.spec_wheels
2598 if spec.wheelChocks ~= nil then
2599 for _, wheelChock in pairs(spec.wheelChocks) do
2600 wheelChock.wheel.updateWheelChock = continueUpdate
2601 isInParkingPosition = Utils.getNoNil(isInParkingPosition, wheelChock.isParked)
2602 self:updateWheelChockPosition(wheelChock, isInParkingPosition)
2603 end
2604 end
2605end

updateWheelContact

Description
Definition
updateWheelContact()
Code
1471function Wheels:updateWheelContact(wheel)
1472 local spec = self.spec_wheels
1473
1474 -- using netinfo because of tire deformation
1475 local wx, wy, wz = wheel.netInfo.x, wheel.netInfo.y, wheel.netInfo.z
1476 wy = wy - wheel.radius
1477 wx = wx + wheel.xOffset
1478 wx, wy, wz = localToWorld(wheel.node, wx,wy,wz)
1479
1480 if self.isServer and self.isAddedToPhysics and wheel.wheelShapeCreated then
1481 --wheelSpeed = getWheelShapeAxleSpeed(wheel.node, wheel.wheelShape)
1482 local contactObject, contactSubShapeIndex = getWheelShapeContactObject(wheel.node, wheel.wheelShape)
1483 if contactObject == g_currentMission.terrainRootNode then
1484 if contactSubShapeIndex <= 0 then
1485 wheel.contact = Wheels.WHEEL_GROUND_CONTACT
1486 else
1487 wheel.contact = Wheels.WHEEL_GROUND_HEIGHT_CONTACT
1488 end
1489 elseif wheel.hasGroundContact and contactObject ~= 0 and getRigidBodyType(contactObject) == "Static" and getUserAttribute(contactObject, "noTireTracks") ~= true then
1490 wheel.contact = Wheels.WHEEL_OBJ_CONTACT
1491 else
1492 wheel.contact = Wheels.WHEEL_NO_CONTACT
1493 end
1494 end
1495
1496 if wheel.contact == Wheels.WHEEL_GROUND_CONTACT then
1497 wheel.densityBits = getDensityAtWorldPos(g_currentMission.terrainDetailId, wx, wy, wz)
1498 wheel.densityType = bitAND(bitShiftRight(wheel.densityBits, g_currentMission.terrainDetailTypeFirstChannel), 2^g_currentMission.terrainDetailTypeNumChannels - 1)
1499 else
1500 wheel.densityBits = 0
1501 wheel.densityType = 0
1502 end
1503
1504 wheel.shallowWater = wy < g_currentMission.waterY
1505end

updateWheelDensityMapHeight

Description
Definition
updateWheelDensityMapHeight()
Code
1633function Wheels:updateWheelDensityMapHeight(wheel, dt)
1634 if not self.isServer then
1635 return
1636 end
1637
1638 local spec = self.spec_wheels
1639
1640 -- smoothing of tipAny
1641 local wheelSmoothAmount = 0
1642 --if self.lastSpeedReal > 0.0002 and next(spec.wheels) ~= nil then -- start smoothing if driving faster than 0.7km/h
1643 if self.lastSpeedReal > 0.0002 then -- start smoothing if driving faster than 0.7km/h
1644 wheelSmoothAmount = spec.wheelSmoothAccumulation + math.max(self.lastMovedDistance * 1.2, 0.0003*dt) -- smooth 1.2m per meter driving or at least 0.3m/s
1645 local rounded = DensityMapHeightUtil.getRoundedHeightValue(wheelSmoothAmount)
1646 spec.wheelSmoothAccumulation = wheelSmoothAmount - rounded
1647 else
1648 spec.wheelSmoothAccumulation = 0
1649 end
1650
1651 if wheelSmoothAmount == 0 then
1652 return
1653 end
1654
1655 -- using netinfo because of tire deformation
1656 local wx, wy, wz = wheel.netInfo.x, wheel.netInfo.y, wheel.netInfo.z
1657 wy = wy - wheel.radius
1658 wx = wx + wheel.xOffset
1659 wx, wy, wz = localToWorld(wheel.node, wx,wy,wz)
1660
1661 if wheel.smoothGroundRadius > 0 then --and wheelSmoothAmount > 0 then
1662 local smoothYOffset = -0.1
1663 local heightType = DensityMapHeightUtil.getHeightTypeDescAtWorldPos(wx, wy, wz, wheel.smoothGroundRadius)
1664 if heightType ~= nil and heightType.allowsSmoothing then
1665 local terrainHeightUpdater = g_densityMapHeightManager:getTerrainDetailHeightUpdater()
1666 if terrainHeightUpdater ~= nil then
1667 local terrainHeight = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, wx, wy, wz)
1668 local physicsDeltaHeight = wy - terrainHeight
1669 local deltaHeight = (physicsDeltaHeight + heightType.collisionBaseOffset) / heightType.collisionScale
1670 deltaHeight = math.min(math.max(deltaHeight, physicsDeltaHeight+heightType.minCollisionOffset), physicsDeltaHeight+heightType.maxCollisionOffset)
1671 deltaHeight = math.max(deltaHeight + smoothYOffset, 0)
1672 local internalHeight = terrainHeight + deltaHeight
1673 smoothDensityMapHeightAtWorldPos(terrainHeightUpdater, wx, internalHeight, wz, wheelSmoothAmount, heightType.index, 0.0, wheel.smoothGroundRadius, wheel.smoothGroundRadius + 1.2)
1674 if Vehicle.debugRendering then
1675 DebugUtil.drawDebugCircle(wx,internalHeight,wz, wheel.smoothGroundRadius, 10)
1676 end
1677 end
1678 end
1679 if wheel.additionalWheels ~= nil then
1680 for _, additionalWheel in pairs(wheel.additionalWheels) do
1681 local refNode = wheel.repr
1682 local xShift,yShift,zShift = localToLocal(additionalWheel.wheelTire, refNode, additionalWheel.xOffset,0,0)
1683 local wx,wy,wz = localToWorld(refNode, xShift, yShift-additionalWheel.radius, zShift)
1684 local heightType = DensityMapHeightUtil.getHeightTypeDescAtWorldPos(wx, wy, wz, additionalWheel.smoothGroundRadius)
1685 if heightType ~= nil and heightType.allowsSmoothing then
1686 local terrainHeightUpdater = g_densityMapHeightManager:getTerrainDetailHeightUpdater()
1687 if terrainHeightUpdater ~= nil then
1688 local terrainHeight = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, wx, wy, wz)
1689 local physicsDeltaHeight = wy - terrainHeight
1690 local deltaHeight = (physicsDeltaHeight + heightType.collisionBaseOffset) / heightType.collisionScale
1691 deltaHeight = math.min(math.max(deltaHeight, physicsDeltaHeight+heightType.minCollisionOffset), physicsDeltaHeight+heightType.maxCollisionOffset)
1692 deltaHeight = math.max(deltaHeight + smoothYOffset, 0)
1693 local internalHeight = terrainHeight + deltaHeight
1694 smoothDensityMapHeightAtWorldPos(terrainHeightUpdater, wx, internalHeight, wz, wheelSmoothAmount, heightType.index, 0.0, additionalWheel.smoothGroundRadius, additionalWheel.smoothGroundRadius + 1.2)
1695 if Vehicle.debugRendering then
1696 DebugUtil.drawDebugCircle(wx,internalHeight,wz, additionalWheel.smoothGroundRadius, 10)
1697 end
1698 end
1699 end
1700 end
1701 end
1702 end
1703
1704end

updateWheelDestruction

Description
Definition
updateWheelDestruction()
Code
1708function Wheels:updateWheelDestruction(wheel, dt)
1709 if self:getIsWheelFoliageDestructionAllowed(wheel) then
1710 -- limit size of destruction
1711 local width = 0.5 * wheel.width
1712 local length = math.min(0.5, 0.5 * wheel.width)
1713 local x, _, z = localToLocal(wheel.driveNode, wheel.repr, 0, 0, 0)
1714 local x0, y0, z0 = localToWorld(wheel.repr, x + width, 0, z - length)
1715 local x1, y1, z1 = localToWorld(wheel.repr, x - width, 0, z - length)
1716 local x2, y2, z2 = localToWorld(wheel.repr, x + width, 0, z + length)
1717
1718 if g_currentMission.accessHandler:canFarmAccessLand(self:getActiveFarm(), x0, z0) then
1719 self:destroyFruitArea(x0, z0, x1, z1, x2, z2)
1720 end
1721
1722 if VehicleDebug.state == VehicleDebug.DEBUG_PHYSICS then
1723 drawDebugLine(x0, y0, z0, 1, 0, 0, x1, y1, z1, 1, 0, 0)
1724 drawDebugLine(x0, y0, z0, 1, 1, 0, x2, y2, z2, 1, 1, 0)
1725 end
1726
1727 if wheel.additionalWheels ~= nil then
1728 for _,additionalWheel in pairs(wheel.additionalWheels) do
1729 local width = 0.5 * additionalWheel.width
1730 local length = math.min(0.5, 0.5 * additionalWheel.width)
1731 local refNode = wheel.node
1732
1733 if wheel.repr ~= wheel.driveNode then
1734 refNode = wheel.repr
1735 end
1736
1737 local xShift, yShift, zShift = localToLocal(additionalWheel.wheelTire, refNode, 0, 0, 0)
1738 local x0, y0, z0 = localToWorld(refNode, xShift + width, yShift, zShift - length)
1739 local x1, y1, z1 = localToWorld(refNode, xShift - width, yShift, zShift - length)
1740 local x2, y2, z2 = localToWorld(refNode, xShift + width, yShift, zShift + length)
1741
1742 if g_farmlandManager:getIsOwnedByFarmAtWorldPosition(self:getActiveFarm(), x0, z0) then
1743 self:destroyFruitArea(x0, z0, x1, z1, x2, z2)
1744 end
1745
1746 if VehicleDebug.state == VehicleDebug.DEBUG_PHYSICS then
1747 drawDebugLine(x0, y0, z0, 1, 0, 0, x1, y1, z1, 1, 0, 0)
1748 drawDebugLine(x0, y0, z0, 1, 1, 0, x2, y2, z2, 1, 1, 0)
1749 end
1750 end
1751 end
1752 end
1753end

updateWheelDirtAmount

Description
Definition
updateWheelDirtAmount()
Code
1416function Wheels:updateWheelDirtAmount(nodeData, dt)
1417 local dirtAmount = self:updateDirtAmount(nodeData, dt)
1418
1419 local allowManipulation = true
1420 if nodeData.wheel ~= nil then
1421 if nodeData.wheel.contact == Wheels.WHEEL_NO_CONTACT then
1422 allowManipulation = false
1423 end
1424 end
1425
1426 if allowManipulation then
1427 local isOnField = false
1428 if nodeData.wheel ~= nil then
1429 if nodeData.wheel.densityType ~= 0 and nodeData.wheel.densityType ~= g_currentMission.grassValue then
1430 isOnField = true
1431 end
1432 end
1433
1434 if isOnField then
1435 dirtAmount = dirtAmount * nodeData.fieldDirtMultiplier
1436 else
1437 if self:getNodeDirtAmount(nodeData) > nodeData.minDirtPercentage then
1438 local speedFactor = self:getLastSpeed() / 20
1439 dirtAmount = dirtAmount * nodeData.streetDirtMultiplier * speedFactor
1440 end
1441 end
1442 end
1443
1444 return dirtAmount
1445end

updateWheelFriction

Description
Definition
updateWheelFriction()
Code
1923function Wheels:updateWheelFriction(wheel, dt)
1924 if self.isServer then
1925 local isOnField = wheel.densityType ~= 0
1926 local depth = wheel.lastColor[4]
1927
1928 local groundType = WheelsUtil.getGroundType(isOnField, wheel.contact ~= Wheels.WHEEL_GROUND_CONTACT, depth)
1929 local coeff = WheelsUtil.getTireFriction(wheel.tireType, groundType, g_currentMission.environment.weather:getGroundWetness())
1930
1931 if self:getLastSpeed() > 0.2 then
1932 if coeff ~= wheel.tireGroundFrictionCoeff then
1933 wheel.tireGroundFrictionCoeff = coeff
1934 self:setWheelTireFrictionDirty(wheel)
1935 end
1936 end
1937 end
1938end

updateWheelSink

Description
Definition
updateWheelSink()
Code
1785function Wheels:updateWheelSink(wheel, dt)
1786 if wheel.supportsWheelSink then
1787 if self.isServer and self.isAddedToPhysics then
1788 local spec = self.spec_wheels
1789
1790 -- map noise to an asbolute value or to a certain percentage of the wheel radius?
1791 local maxSink = 0.20
1792 local sinkTarget = 0
1793
1794 if wheel.mirroredWheel == nil then
1795 for _, mirWheel in ipairs(spec.wheels) do
1796 if mirWheel.mirroredWheel == nil and mirWheel ~= wheel then -- only the first wheel got the mirrored one
1797 local x1, y1, z1 = localToLocal(wheel.node, wheel.repr, 0, 0, 0)
1798 local x2, y2, z2 = localToLocal(wheel.node, mirWheel.repr, 0, 0, 0)
1799 local diff = math.abs(x1-(-x2)) + math.abs(y1-y2) + math.abs(z1-z2)
1800 if diff < 0.25 then
1801 wheel.mirroredWheel = mirWheel
1802 mirWheel.invMirroredWheel = wheel
1803 end
1804 end
1805 end
1806 end
1807
1808 local force = false
1809
1810 if wheel.contact ~= Wheels.WHEEL_NO_CONTACT and self:getLastSpeed() > 0.3 then
1811 wheel.avgSink = nil
1812
1813 local width = 0.25 * wheel.width
1814 local length = 0.25 * wheel.width
1815
1816 local x,_,z = localToLocal(wheel.driveNode, wheel.repr, 0,0,0)
1817 local x0,_,z0 = localToWorld(wheel.repr, x + width, 0, z - length)
1818 local x1,_,z1 = localToWorld(wheel.repr, x - width, 0, z - length)
1819 local x2,_,z2 = localToWorld(wheel.repr, x + width, 0, z + length)
1820
1821 local x,z, widthX,widthZ, heightX,heightZ = MathUtil.getXZWidthAndHeight(x0, z0, x1, z1, x2, z2)
1822 local density, area = FSDensityMapUtil.getFieldValue(x0, z0, x1, z1, x2, z2)
1823
1824 local terrainValue = 0
1825 if area > 0 then
1826 terrainValue = math.floor(density/area + 0.5)
1827 end
1828 wheel.lastTerrainValue = terrainValue
1829
1830 local noiseValue = 0
1831 if terrainValue > 0 then
1832 local xPerlin = x + 0.5*widthX + 0.5*heightX
1833 local zPerlin = z + 0.5*widthZ + 0.5*heightZ
1834 -- Round to 1cm to avoid sliding when not moving
1835 xPerlin = math.floor(xPerlin*100)*0.01
1836 zPerlin = math.floor(zPerlin*100)*0.01
1837
1838 local perlinNoise = Wheels.perlinNoiseSink
1839 local noiseSink = 0.5 * (1 + getPerlinNoise2D(xPerlin*perlinNoise.randomFrequency, zPerlin*perlinNoise.randomFrequency, perlinNoise.persistence, perlinNoise.numOctaves, perlinNoise.randomSeed))
1840
1841 perlinNoise = Wheels.perlinNoiseWobble
1842 local noiseWobble = 0.5 * (1 + getPerlinNoise2D(xPerlin*perlinNoise.randomFrequency, zPerlin*perlinNoise.randomFrequency, perlinNoise.persistence, perlinNoise.numOctaves, perlinNoise.randomSeed))
1843
1844 -- estimiate pressure on surface
1845 local gravity = 9.81
1846 local tireLoad = getWheelShapeContactForce(wheel.node, wheel.wheelShape)
1847 if tireLoad ~= nil then
1848 local nx,ny,nz = getWheelShapeContactNormal(wheel.node, wheel.wheelShape)
1849 local dx,dy,dz = localDirectionToWorld(wheel.node, 0,-1,0)
1850 tireLoad = -tireLoad*MathUtil.dotProduct(dx,dy,dz, nx,ny,nz)
1851
1852 tireLoad = tireLoad + math.max(ny*gravity, 0.0) * wheel.mass -- add gravity force of tire
1853 else
1854 tireLoad = 0
1855 end
1856 tireLoad = tireLoad / gravity
1857
1858 local loadFactor = math.min(1.0, math.max(0, tireLoad / wheel.maxLatStiffnessLoad))
1859
1860 local wetnessFactor = g_currentMission.environment.weather:getGroundWetness()
1861 noiseSink = 0.333*(2*loadFactor + wetnessFactor) * noiseSink
1862
1863 noiseValue = math.max(noiseSink, noiseWobble)
1864 end
1865
1866 maxSink = Wheels.MAX_SINK[terrainValue] or maxSink
1867
1868 -- plowing effect
1869 if terrainValue == 2 and wheel.oppositeWheelIndex ~= nil then
1870 local oppositeWheel = spec.wheels[wheel.oppositeWheelIndex]
1871 if oppositeWheel.lastTerrainValue ~= nil and oppositeWheel.lastTerrainValue ~= 2 then
1872 maxSink = maxSink * 1.3
1873 end
1874 end
1875
1876 sinkTarget = math.min(0.2*wheel.radiusOriginal, math.min(maxSink, wheel.maxWheelSink) * noiseValue)
1877 elseif self:getLastSpeed() < 0.3 then
1878 -- if we are standing still we try to synchronize the left and right wheel radius to avoid wobbeling
1879 if wheel.mirroredWheel ~= nil then
1880 if wheel.avgSink == nil then
1881 wheel.avgSink = (wheel.mirroredWheel.sinkTarget + wheel.sinkTarget) / 2
1882 end
1883 sinkTarget = wheel.avgSink
1884 force = wheel.sink ~= wheel.avgSink
1885 wheel.sinkTarget = sinkTarget
1886 elseif wheel.invMirroredWheel ~= nil then
1887 if wheel.invMirroredWheel.avgSink ~= nil then
1888 sinkTarget = wheel.invMirroredWheel.avgSink
1889 force = wheel.sink ~= wheel.invMirroredWheel.avgSink
1890 wheel.sinkTarget = sinkTarget
1891 end
1892 end
1893 end
1894
1895 if wheel.sinkTarget < sinkTarget then
1896 wheel.sinkTarget = math.min(sinkTarget, wheel.sinkTarget + (0.05 * math.min(30, math.max(0, self:getLastSpeed()-0.2)) * (dt/1000)))
1897 elseif wheel.sinkTarget > sinkTarget then
1898 wheel.sinkTarget = math.max(sinkTarget, wheel.sinkTarget - (0.05 * math.min(30, math.max(0, self:getLastSpeed()-0.2)) * (dt/1000)))
1899 end
1900
1901 if math.abs(wheel.sink - wheel.sinkTarget) > 0.001 or force then
1902 wheel.sink = wheel.sinkTarget
1903
1904 local radius = wheel.radiusOriginal - wheel.sink
1905 if radius ~= wheel.radius then
1906 wheel.radius = radius
1907 if self.isServer then
1908 self:setWheelPositionDirty(wheel)
1909
1910 local sinkFactor = (wheel.sink/maxSink) * (1 + (0.4 * g_currentMission.environment.weather:getGroundWetness()))
1911 wheel.sinkLongStiffnessFactor = (1.0 - (0.10 * sinkFactor))
1912 wheel.sinkLatStiffnessFactor = (1.0 - (0.20 * sinkFactor))
1913 self:setWheelTireFrictionDirty(wheel)
1914 end
1915 end
1916 end
1917 end
1918 end
1919end

updateWheelTireFriction

Description
Definition
updateWheelTireFriction()
Code
1972function Wheels:updateWheelTireFriction(wheel)
1973 if self.isServer and self.isAddedToPhysics then
1974 setWheelShapeTireFriction(wheel.node, wheel.wheelShape, wheel.sinkFrictionScaleFactor*wheel.maxLongStiffness, wheel.sinkLatStiffnessFactor*wheel.maxLatStiffness, wheel.maxLatStiffnessLoad, wheel.sinkFrictionScaleFactor*wheel.frictionScale*wheel.tireGroundFrictionCoeff)
1975 end
1976end

updateWheelTireTracks

Description
Definition
updateWheelTireTracks()
Code
1550function Wheels:updateWheelTireTracks(wheel)
1551
1552 -- using netinfo because of tire deformation
1553 local wx, wy, wz = wheel.netInfo.x, wheel.netInfo.y, wheel.netInfo.z
1554 wy = wy - wheel.radius
1555 wx = wx + wheel.xOffset
1556 wx, wy, wz = localToWorld(wheel.node, wx,wy,wz)
1557
1558 local r, g, b, a, t = self:getTireTrackColor(wheel, wx, wy, wz)
1559
1560 if wheel.tireTrackIndex ~= nil then
1561 if self:getAllowTireTracks() and r ~= nil then
1562 -- we are using wheel node instead of root node -> direction of wheel component could be different compared to the root component
1563 local ux,uy,uz = localDirectionToWorld(wheel.node, 0, 1, 0)
1564
1565 local tireDirection = self.movingDirection
1566 if wheel.tireIsInverted then
1567 tireDirection = tireDirection * -1
1568 end
1569
1570 -- we are using dirtAmount as alpha value -> realistic dirt fadeout
1571 g_currentMission.tireTrackSystem:addTrackPoint(wheel.tireTrackIndex, wx, wy, wz, ux, uy, uz, r, g, b, wheel.dirtAmount, a, tireDirection)
1572 if wheel.additionalWheels ~= nil then
1573 for _, additionalWheel in pairs(wheel.additionalWheels) do
1574 if additionalWheel.tireTrackIndex ~= nil then
1575 wx, wy, wz = worldToLocal(wheel.node, getWorldTranslation(additionalWheel.wheelTire))
1576 wy = wy - wheel.radius
1577 wx = wx + wheel.xOffset
1578 wx, wy, wz = localToWorld(wheel.node, wx,wy,wz)
1579 wy = math.max(wy, getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, wx, wy, wz))
1580 tireDirection = self.movingDirection
1581 if additionalWheel.tireIsInverted then
1582 tireDirection = tireDirection * -1
1583 end
1584 g_currentMission.tireTrackSystem:addTrackPoint(additionalWheel.tireTrackIndex, wx, wy, wz, ux, uy, uz, r, g, b, wheel.dirtAmount, a, tireDirection)
1585 end
1586 end
1587 end
1588 else
1589 g_currentMission.tireTrackSystem:cutTrack(wheel.tireTrackIndex)
1590 if wheel.additionalWheels ~= nil then
1591 for _, additionalWheel in pairs(wheel.additionalWheels) do
1592 if additionalWheel.tireTrackIndex ~= nil then
1593 g_currentMission.tireTrackSystem:cutTrack(additionalWheel.tireTrackIndex)
1594 end
1595 end
1596 end
1597 end
1598 end
1599end

validateWashableNode

Description
Definition
validateWashableNode()
Code
1389function Wheels:validateWashableNode(superFunc, node)
1390 local spec = self.spec_wheels
1391 for _, wheel in pairs(spec.wheels) do
1392 local wheelNode = wheel.driveNode;
1393 if wheel.linkNode ~= wheel.driveNode then
1394 wheelNode = wheel.linkNode;
1395 end
1396
1397 local wheelNodes = {}
1398 I3DUtil.getNodesByShaderParam(wheelNode, "RDT", wheelNodes);
1399
1400 if wheelNodes[node] ~= nil then
1401 return false, self.updateWheelDirtAmount, wheel, {wheel=wheel, fieldDirtMultiplier=wheel.fieldDirtMultiplier, streetDirtMultiplier=wheel.streetDirtMultiplier, minDirtPercentage=wheel.minDirtPercentage}
1402 end
1403 end
1404
1405 return superFunc(self, node)
1406end