LUADOC - Farming Simulator 22

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

addTireTrackNode

Description
Definition
addTireTrackNode()
Code
3346function Wheels:addTireTrackNode(wheel, isAdditionalTrack, parent, linkNode, tireTrackAtlasIndex, width, radius, xOffset, inverted, activeFunc)
3347 local spec = self.spec_wheels
3348
3349 local tireTrackNode = {}
3350 tireTrackNode.wheel = wheel
3351 tireTrackNode.isAdditionalTrack = isAdditionalTrack
3352 tireTrackNode.parent = parent
3353 tireTrackNode.linkNode = linkNode
3354 tireTrackNode.tireTrackAtlasIndex = tireTrackAtlasIndex
3355 tireTrackNode.width = width
3356 tireTrackNode.radius = radius
3357 tireTrackNode.xOffset = xOffset
3358 tireTrackNode.inverted = inverted
3359 tireTrackNode.activeFunc = activeFunc
3360
3361 if self.tireTrackSystem ~= nil then
3362 tireTrackNode.tireTrackIndex = self.tireTrackSystem:createTrack(width, tireTrackAtlasIndex)
3363
3364 if tireTrackNode.tireTrackIndex ~= nil then
3365 table.insert(spec.tireTrackNodes, tireTrackNode)
3366 return #spec.tireTrackNodes
3367 end
3368 end
3369end

addToPhysics

Description
Definition
addToPhysics()
Code
2891function Wheels:addToPhysics(superFunc)
2892 if not superFunc(self) then
2893 return false
2894 end
2895
2896 local spec = self.spec_wheels
2897
2898 for _, wheel in pairs(spec.wheels) do
2899 wheel.xDriveOffset = wheel.netInfo.xDrive
2900 wheel.updateWheel = false
2901 self:updateWheelBase(wheel)
2902 self:updateWheelTireFriction(wheel)
2903 end
2904 if self.isServer then
2905 local brakeForce = self:getBrakeForce()
2906 for _,wheel in pairs(spec.wheels) do
2907 setWheelShapeProps(wheel.node, wheel.wheelShape, 0, brakeForce*wheel.brakeFactor, wheel.steeringAngle, wheel.rotationDamping)
2908 setWheelShapeAutoHoldBrakeForce(wheel.node, wheel.wheelShape, brakeForce*wheel.autoHoldBrakeFactor)
2909 end
2910
2911 self:brake(brakeForce)
2912
2913 spec.wheelCreationTimer = 2
2914 end
2915
2916 return true
2917end

brake

Description
Definition
brake()
Code
3881function Wheels:brake(brakePedal)
3882 local spec = self.spec_wheels
3883 if brakePedal ~= spec.brakePedal then
3884 spec.brakePedal = brakePedal
3885
3886 for _,wheel in pairs(spec.wheels) do
3887 WheelsUtil.updateWheelPhysics(self, wheel, spec.brakePedal, 0)
3888 end
3889
3890 SpecializationUtil.raiseEvent(self, "onBrake", spec.brakePedal)
3891 end
3892end

createConfigSaveIdMapping

Description
Create wheel config save if mappings
Definition
createConfigSaveIdMapping(table xmlFile)
Arguments
tablexmlFilexml file object
Return Values
tableconfigurationSaveIdToIndexsave id to config index mapping
tableconfigurationIndexToBaseConfigconfig index to base config mapping
Code
1329function Wheels.createConfigSaveIdMapping(xmlFile)
1330 local configurationSaveIdToIndex = {}
1331 local configurationIndexToBaseConfig = {}
1332
1333 xmlFile:iterate("vehicle.wheels.wheelConfigurations.wheelConfiguration", function(index, key)
1334 XMLUtil.checkDeprecatedXMLElements(xmlFile, key .. ".wheels.foliageBendingModifier", key .. ".foliageBendingModifier") --FS19 to FS22
1335
1336 local saveId = xmlFile:getValue(key .. "#saveId", index)
1337 configurationSaveIdToIndex[saveId] = index
1338
1339 local baseConfigId = xmlFile:getValue(key .. ".wheels#baseConfig")
1340
1341 if saveId == baseConfigId then
1342 Logging.xmlError(xmlFile, "Wheel configuration %s references itself as baseConfig! Ignoring this reference", key)
1343 else
1344 configurationIndexToBaseConfig[index] = baseConfigId
1345 end
1346 end)
1347
1348 return configurationSaveIdToIndex, configurationIndexToBaseConfig
1349end

destroyFruitArea

Description
Definition
destroyFruitArea()
Code
3848function Wheels:destroyFruitArea(x0,z0, x1,z1, x2,z2)
3849 FSDensityMapUtil.updateWheelDestructionArea(x0,z0, x1,z1, x2,z2)
3850end

destroySnowArea

Description
Definition
destroySnowArea()
Code
3854function Wheels:destroySnowArea(x0,z0, x1,z1, x2,z2)
3855 local spec = self.spec_wheels
3856
3857 local worldSnowHeight = spec.snowSystem.height
3858 local curHeight = spec.snowSystem:getSnowHeightAtArea(x0,z0, x1,z1, x2,z2)
3859
3860 -- Reduce only when it is fresh snow
3861 local reduceSnow = MathUtil.equalEpsilon(worldSnowHeight, curHeight, 0.005)
3862 -- Detect unmelted heaps
3863 local isOnSnowHeap = worldSnowHeight < 0.005 and curHeight > 1
3864
3865 -- Melt only if height is substantial
3866 if curHeight > SnowSystem.MIN_LAYER_HEIGHT and (reduceSnow or isOnSnowHeap) then
3867 local sink = 0.7 * worldSnowHeight
3868 if isOnSnowHeap then
3869 sink = 0.1 * curHeight
3870 end
3871
3872 local sinkLayers = math.floor(math.min(sink, curHeight) / SnowSystem.MIN_LAYER_HEIGHT)
3873 if sinkLayers > 0 then
3874 spec.snowSystem:removeSnow(x0,z0, x1,z1, x2,z2, sinkLayers)
3875 end
3876 end
3877end

finalizeWheel

Description
load i3d files for wheel, perform checks
Definition
finalizeWheel()
Code
2478function Wheels:finalizeWheel(wheel, parentWheel)
2479 local spec = self.spec_wheels
2480 if parentWheel == nil and wheel.repr ~= nil then
2481 wheel.startPositionX, wheel.startPositionY, wheel.startPositionZ = getTranslation(wheel.repr)
2482 wheel.driveNodeStartPosX, wheel.driveNodeStartPosY, wheel.driveNodeStartPosZ = getTranslation(wheel.driveNode)
2483 wheel.dirtAmount = 0
2484 wheel.xDriveOffset = 0
2485 wheel.lastXDrive = 0
2486 wheel.lastColor = {0,0,0,0}
2487 wheel.lastTerrainAttribute = 0
2488 wheel.contact = Wheels.WHEEL_NO_CONTACT
2489 wheel.hasSnowContact = false
2490 wheel.snowScale = 0
2491 wheel.lastSnowScale = 0
2492 wheel.steeringAngle = 0
2493 wheel.lastSteeringAngle = 0
2494 wheel.lastMovement = 0
2495 wheel.hasGroundContact = false
2496 wheel.hasHandbrake = true
2497 wheel.lastContactObjectAllowsTireTracks = true
2498 wheel.densityBits = 0
2499 wheel.densityType = 0
2500
2501 local vehicleNode = self.vehicleNodes[wheel.node]
2502 if vehicleNode ~= nil and vehicleNode.component ~= nil and vehicleNode.component.motorized == nil then
2503 vehicleNode.component.motorized = true
2504 end
2505
2506 if wheel.useReprDirection then
2507 wheel.directionX, wheel.directionY, wheel.directionZ = localDirectionToLocal(wheel.repr, wheel.node, 0,-1,0)
2508 wheel.axleX, wheel.axleY, wheel.axleZ = localDirectionToLocal(wheel.repr, wheel.node, 1,0,0)
2509 elseif wheel.useDriveNodeDirection then
2510 wheel.directionX, wheel.directionY, wheel.directionZ = localDirectionToLocal(wheel.driveNodeDirectionNode, wheel.node, 0,-1,0)
2511 wheel.axleX, wheel.axleY, wheel.axleZ = localDirectionToLocal(wheel.driveNodeDirectionNode, wheel.node, 1,0,0)
2512 else
2513 wheel.directionX, wheel.directionY, wheel.directionZ = 0,-1,0
2514 wheel.axleX, wheel.axleY, wheel.axleZ = 1,0,0
2515 end
2516 wheel.steeringCenterOffsetX, wheel.steeringCenterOffsetY, wheel.steeringCenterOffsetZ = 0,0,0
2517 if wheel.repr ~= wheel.driveNode then
2518 wheel.steeringCenterOffsetX, wheel.steeringCenterOffsetY, wheel.steeringCenterOffsetZ = localToLocal(wheel.driveNode, wheel.repr, 0, 0, 0)
2519 wheel.steeringCenterOffsetX = -wheel.steeringCenterOffsetX
2520 wheel.steeringCenterOffsetY = -wheel.steeringCenterOffsetY
2521 wheel.steeringCenterOffsetZ = -wheel.steeringCenterOffsetZ
2522 end
2523
2524 wheel.syncContactState = false
2525
2526 if wheel.hasTireTracks then
2527 wheel.tireTrackNodeIndex = self:addTireTrackNode(wheel, false, wheel.node, wheel.repr, wheel.tireTrackAtlasIndex, wheel.width, wheel.radius, wheel.xOffset, wheel.tireIsInverted)
2528 wheel.syncContactState = true
2529 end
2530
2531 wheel.maxLatStiffness = wheel.maxLatStiffness*wheel.restLoad
2532 wheel.maxLatStiffnessLoad = wheel.maxLatStiffnessLoad*wheel.restLoad
2533
2534 wheel.mass = wheel.mass + wheel.additionalMass
2535
2536 wheel.lastTerrainValue = 0
2537 wheel.sink = 0
2538 wheel.sinkTarget = 0
2539 wheel.radiusOriginal = wheel.radius
2540 wheel.sinkFrictionScaleFactor = 1
2541 wheel.sinkLongStiffnessFactor = 1
2542 wheel.sinkLatStiffnessFactor = 1
2543
2544 local positionY = wheel.positionY+wheel.deltaY
2545 wheel.netInfo = {}
2546 wheel.netInfo.xDrive = 0
2547 wheel.netInfo.xDriveSpeed = 0
2548 wheel.netInfo.x = wheel.positionX
2549 wheel.netInfo.y = positionY
2550 wheel.netInfo.z = wheel.positionZ
2551 wheel.netInfo.suspensionLength = wheel.suspTravel*0.5
2552
2553 -- The suspension elongates by 20% of the specified susp travel
2554 wheel.netInfo.sync = {yMin = -5, yRange = 10}
2555 wheel.netInfo.yMin = positionY-1.2*wheel.suspTravel
2556
2557 local width = 0.5 * wheel.width
2558 local length = math.min(0.5, 0.5 * wheel.width)
2559 local x, _, z = localToLocal(wheel.driveNode, wheel.repr, 0, 0, 0)
2560 wheel.destructionStartNode = createTransformGroup("destructionStartNode")
2561 wheel.destructionWidthNode = createTransformGroup("destructionWidthNode")
2562 wheel.destructionHeightNode = createTransformGroup("destructionHeightNode")
2563 link(wheel.repr, wheel.destructionStartNode)
2564 link(wheel.repr, wheel.destructionWidthNode)
2565 link(wheel.repr, wheel.destructionHeightNode)
2566 setTranslation(wheel.destructionStartNode, x + width, 0, z - length)
2567 setTranslation(wheel.destructionWidthNode, x - width, 0, z - length)
2568 setTranslation(wheel.destructionHeightNode, x + width, 0, z + length)
2569
2570 --
2571 self:updateWheelBase(wheel)
2572 self:updateWheelTireFriction(wheel)
2573
2574 wheel.networkInterpolators = {}
2575 wheel.networkInterpolators.xDrive = InterpolatorAngle.new(wheel.netInfo.xDrive)
2576 wheel.networkInterpolators.position = InterpolatorPosition.new(wheel.netInfo.x, wheel.netInfo.y, wheel.netInfo.z)
2577 wheel.networkInterpolators.suspensionLength = InterpolatorValue.new(wheel.netInfo.suspensionLength)
2578 end
2579
2580 if parentWheel ~= nil then
2581 wheel.linkNode = createTransformGroup("linkNode")
2582 link(parentWheel.driveNode, wheel.linkNode)
2583 end
2584
2585 if wheel.tireFilename ~= nil then
2586 local filename = Utils.getFilename(wheel.tireFilename, self.baseDirectory)
2587 wheel.tireFilename = nil
2588 local args = {wheel=wheel,
2589 parentWheel=parentWheel,
2590 linkNode=wheel.linkNode,
2591 name="wheelTire",
2592 filename=filename,
2593 fileIdentifier="tireFilename",
2594 index=wheel.tireNodeStr,
2595 offset=0,
2596 widthAndDiam=nil,
2597 scale=nil
2598 }
2599 local sharedLoadRequestId = self:loadSubSharedI3DFile(filename, false, false, self.onWheelPartI3DLoaded, self, args)
2600 table.insert(spec.sharedLoadRequestIds, sharedLoadRequestId)
2601 end
2602 if wheel.outerRimFilename ~= nil then
2603 local filename = Utils.getFilename(wheel.outerRimFilename, self.baseDirectory)
2604 wheel.outerRimFilename = nil
2605 local args = {wheel=wheel,
2606 parentWheel=parentWheel,
2607 linkNode=wheel.linkNode,
2608 name="wheelOuterRim",
2609 filename=filename,
2610 fileIdentifier="outerRimFilename",
2611 index=wheel.outerRimNodeStr,
2612 offset=0,
2613 widthAndDiam=wheel.outerRimWidthAndDiam,
2614 scale=wheel.outerRimScale
2615 }
2616 local sharedLoadRequestId = self:loadSubSharedI3DFile(filename, false, false, self.onWheelPartI3DLoaded, self, args)
2617 table.insert(spec.sharedLoadRequestIds, sharedLoadRequestId)
2618 end
2619 if wheel.innerRimFilename ~= nil then
2620 local filename = Utils.getFilename(wheel.innerRimFilename, self.baseDirectory)
2621 wheel.innerRimFilename = nil
2622 local args = {wheel=wheel,
2623 parentWheel=parentWheel,
2624 linkNode=wheel.linkNode,
2625 name="wheelInnerRim",
2626 filename=filename,
2627 fileIdentifier="innerRimFilename",
2628 index=wheel.innerRimNodeStr,
2629 offset=wheel.innerRimOffset,
2630 widthAndDiam=wheel.innerRimWidthAndDiam,
2631 scale=wheel.innerRimScale
2632 }
2633 local sharedLoadRequestId = self:loadSubSharedI3DFile(filename, false, false, self.onWheelPartI3DLoaded, self, args)
2634 table.insert(spec.sharedLoadRequestIds, sharedLoadRequestId)
2635 end
2636 if wheel.additionalFilename ~= nil then
2637 local filename = Utils.getFilename(wheel.additionalFilename, self.baseDirectory)
2638 wheel.additionalFilename = nil
2639 local args = {wheel=wheel,
2640 parentWheel=parentWheel,
2641 linkNode=wheel.linkNode,
2642 name="wheelAdditional",
2643 filename=filename,
2644 fileIdentifier="additionalFilename",
2645 index=wheel.additionalNodeStr,
2646 offset=wheel.additionalOffset,
2647 widthAndDiam=wheel.additionalWidthAndDiam,
2648 scale=wheel.additionalScale
2649 }
2650 local sharedLoadRequestId = self:loadSubSharedI3DFile(filename, false, false, self.onWheelPartI3DLoaded, self, args)
2651 table.insert(spec.sharedLoadRequestIds, sharedLoadRequestId)
2652 end
2653
2654 if wheel.additionalWheels ~= nil then
2655 local outmostWheelWidth = 0
2656 local totalWheelShapeOffset = 0
2657 local offsetDir = 1
2658 for _, additionalWheel in pairs(wheel.additionalWheels) do
2659 self:finalizeWheel(additionalWheel, wheel)
2660
2661 local baseWheelWidth = MathUtil.mToInch(wheel.width)
2662 local dualWheelWidth = MathUtil.mToInch(additionalWheel.width)
2663 local diameter = 0
2664 local wheelOffset = MathUtil.mToInch(additionalWheel.offset)
2665
2666 if wheel.outerRimWidthAndDiam ~= nil then
2667 baseWheelWidth = wheel.outerRimWidthAndDiam[1]
2668 diameter = wheel.outerRimWidthAndDiam[2]
2669 end
2670 if additionalWheel.outerRimWidthAndDiam ~= nil then
2671 dualWheelWidth = additionalWheel.outerRimWidthAndDiam[1]
2672 end
2673
2674 if wheelOffset < 0 then
2675 offsetDir = additionalWheel.isLeft and -1 or 1
2676 else
2677 offsetDir = additionalWheel.isLeft and 1 or -1
2678 end
2679
2680 local totalOffset = 0
2681 totalOffset = totalOffset + (additionalWheel.isLeft and 1 or -1) * MathUtil.inchToM(0.5*baseWheelWidth + wheelOffset + 0.5*dualWheelWidth )
2682
2683 -- get wheel with furthest offset
2684 if math.abs(totalOffset) > math.abs(totalWheelShapeOffset) then
2685 totalWheelShapeOffset = math.abs(totalOffset)
2686 outmostWheelWidth = additionalWheel.width
2687 end
2688
2689 if additionalWheel.connector ~= nil then
2690 local filename = Utils.getFilename(additionalWheel.connector.filename, self.baseDirectory)
2691 additionalWheel.connector.filename = nil
2692
2693 local arguments = {
2694 wheel = wheel,
2695 connector = additionalWheel.connector,
2696 diameter = diameter,
2697 baseWheelWidth = baseWheelWidth,
2698 wheelDistance = wheelOffset,
2699 offsetDir = offsetDir,
2700 dualWheelWidth = dualWheelWidth,
2701 filename = filename
2702 }
2703 local sharedLoadRequestId = self:loadSubSharedI3DFile(filename, false, false, self.onAdditionalWheelConnectorI3DLoaded, self, arguments)
2704 table.insert(spec.sharedLoadRequestIds, sharedLoadRequestId)
2705 end
2706
2707 local x,y,z = getTranslation(additionalWheel.linkNode)
2708 setTranslation(additionalWheel.linkNode, x+totalOffset,y,z)
2709
2710 if additionalWheel.hasTireTracks then
2711 additionalWheel.tireTrackNodeIndex = self:addTireTrackNode(wheel, true, wheel.node, additionalWheel.linkNode, additionalWheel.tireTrackAtlasIndex, additionalWheel.width, wheel.radius, wheel.xOffset, wheel.tireIsInverted)
2712 end
2713
2714 if additionalWheel.driveGroundParticleSystems ~= nil then
2715 for name, particleSystems in pairs(additionalWheel.driveGroundParticleSystems) do
2716 for i=1, #particleSystems do
2717 local ps = particleSystems[i]
2718 ps.offsets[1] = ps.offsets[1] + totalOffset
2719 local wx, wy, wz = worldToLocal(wheel.node, getWorldTranslation(wheel.driveNode))
2720 setTranslation(ps.emitterShape, wx + ps.offsets[1], wy + ps.offsets[2], wz + ps.offsets[3])
2721 table.insert(wheel.driveGroundParticleSystems[name], ps)
2722 end
2723 end
2724 end
2725 end
2726
2727 wheel.widthOffset = wheel.widthOffset + offsetDir * (totalWheelShapeOffset/2)
2728 wheel.wheelShapeWidthTotalOffset = totalWheelShapeOffset
2729 wheel.wheelShapeWidthTmp = wheel.width/2 + outmostWheelWidth/2
2730 end
2731end

forceUpdateWheelPhysics

Description
Definition
forceUpdateWheelPhysics()
Code
3316function Wheels:forceUpdateWheelPhysics(dt)
3317 local spec = self.spec_wheels
3318 for i=1, #spec.wheels do
3319 WheelsUtil.updateWheelPhysics(self, spec.wheels[i], spec.brakePedal, dt)
3320 end
3321end

getAIDirectionNode

Description
Definition
getAIDirectionNode()
Code
3114function Wheels:getAIDirectionNode(superFunc)
3115 return self.spec_wheels.steeringCenterNode or superFunc(self)
3116end

getAIRootNode

Description
Definition
getAIRootNode()
Code
3120function Wheels:getAIRootNode(superFunc)
3121 return self.spec_wheels.steeringCenterNode or superFunc(self)
3122end

getAllowTireTracks

Description
Definition
getAllowTireTracks()
Code
3199function Wheels:getAllowTireTracks()
3200 return self.currentUpdateDistance < Wheels.MAX_TIRE_TRACK_CREATION_DISTANCE and self.spec_wheels.tyreTracksSegmentsCoeff > 0
3201end

getAreSurfaceSoundsActive

Description
Definition
getAreSurfaceSoundsActive()
Code
3454function Wheels:getAreSurfaceSoundsActive()
3455 return self.isActiveForLocalSound
3456end

getBrakeForce

Description
Definition
getBrakeForce()
Code
3896function Wheels:getBrakeForce()
3897 return 0
3898end

getBrands

Description
Definition
getBrands()
Code
4196function Wheels.getBrands(items)
4197 local brands = {}
4198 local addedBrands = {}
4199 for _, item in ipairs(items) do
4200 if item.wheelBrandName ~= nil and addedBrands[item.wheelBrandName] == nil then
4201 table.insert(brands, {title=item.wheelBrandName, icon=item.wheelBrandIconFilename})
4202 addedBrands[item.wheelBrandName] = true
4203 end
4204 end
4205
4206 return brands
4207end

getComponentMass

Description
Returns total mass of vehicle (optional including attached vehicles)
Definition
getComponentMass(boolean onlyGivenVehicle)
Arguments
booleanonlyGivenVehicleuse only the given vehicle, if false or nil it includes all attachables
Return Values
floattotalMasstotal mass
Code
2938function Wheels:getComponentMass(superFunc, component)
2939 local mass = superFunc(self, component)
2940
2941 local spec = self.spec_wheels
2942 for _, wheel in pairs(spec.wheels) do
2943 if wheel.node == component.node then
2944 mass = mass + wheel.mass
2945 end
2946 end
2947
2948 return mass
2949end

getConfigurationValue

Description
Create wheel config save if mappings
Definition
getConfigurationValue(table configurationSaveIdToIndex, table configurationIndexToBaseConfig, table xmlFile, string configId, string configurationKey, string key, any_type attributes)
Arguments
tableconfigurationSaveIdToIndexsave id to config index mapping
tableconfigurationIndexToBaseConfigconfig index to base config mapping
tablexmlFilexml file object
stringconfigIdconfig identifier
stringconfigurationKeyconfig key
stringkeyattribute key
any_typeattributesadditional attributes
Return Values
any_typevaluevalue
Code
1361function Wheels.getConfigurationValue(configurationSaveIdToIndex, configurationIndexToBaseConfig, xmlFile, configId, configurationKey, key, ...)
1362 local baseConfigurationIndex
1363 local baseConfigurationName = configurationIndexToBaseConfig[configId]
1364 if baseConfigurationName ~= nil then
1365 baseConfigurationIndex = configurationSaveIdToIndex[baseConfigurationName]
1366 end
1367
1368 local value = xmlFile:getString(configurationKey .. key)
1369 if value ~= nil or baseConfigurationIndex == nil then
1370 if value == "-" then
1371 return nil
1372 end
1373
1374 return xmlFile:getValue(configurationKey .. key, ...)
1375 else
1376 return Wheels.getConfigurationValue(configurationSaveIdToIndex, configurationIndexToBaseConfig, xmlFile, baseConfigurationIndex, string.format("vehicle.wheels.wheelConfigurations.wheelConfiguration(%d)", baseConfigurationIndex - 1), key, ...)
1377 end
1378end

getCurrentSurfaceSound

Description
Definition
getCurrentSurfaceSound()
Code
3417function Wheels:getCurrentSurfaceSound()
3418 local spec = self.spec_wheels
3419
3420 for i, wheel in ipairs(spec.wheels) do
3421 if wheel.hasTireTracks or i == #spec.wheels then
3422 if wheel.contact == Wheels.WHEEL_GROUND_CONTACT then
3423 local isOnField = wheel.densityType ~= 0
3424 local shallowWater = wheel.shallowWater
3425 if isOnField then
3426 return spec.surfaceNameToSound["field"]
3427 elseif shallowWater then
3428 return spec.surfaceNameToSound["shallowWater"]
3429 else
3430 local lastTerrainAttribute = wheel.lastTerrainAttribute
3431 if not wheel.hasTireTracks then
3432 local wx, wy, wz = getWorldTranslation(wheel.driveNode)
3433 local _, _, _, _, t = getTerrainAttributesAtWorldPos(g_currentMission.terrainRootNode, wx, wy, wz, true, true, true, true, false)
3434 lastTerrainAttribute = t
3435 end
3436
3437 return spec.surfaceIdToSound[lastTerrainAttribute]
3438 end
3439 elseif wheel.contact == Wheels.WHEEL_GROUND_HEIGHT_CONTACT then
3440 if wheel.hasSnowContact then
3441 return spec.surfaceNameToSound["snow"]
3442 end
3443 elseif wheel.contact == Wheels.WHEEL_OBJ_CONTACT then
3444 return spec.surfaceNameToSound["asphalt"]
3445 elseif wheel.contact ~= Wheels.WHEEL_NO_CONTACT then
3446 break
3447 end
3448 end
3449 end
3450end

getDriveGroundParticleSystemsScale

Description
Definition
getDriveGroundParticleSystemsScale()
Code
3778function Wheels:getDriveGroundParticleSystemsScale(particleSystem, speed)
3779 local wheel = particleSystem.wheel
3780 if wheel ~= nil then
3781 if particleSystem.onlyActiveOnGroundContact and (wheel.contact ~= Wheels.WHEEL_GROUND_CONTACT and not wheel.hasSnowContact) then
3782 return 0
3783 end
3784 if not Wheels.GROUND_PARTICLES[wheel.lastTerrainAttribute] and not wheel.hasSnowContact then
3785 return 0
3786 end
3787 local grassValue = g_currentMission.fieldGroundSystem:getFieldGroundValue(FieldGroundType.GRASS)
3788 if wheel.densityType == grassValue then
3789 return 0
3790 end
3791 end
3792
3793 local minSpeed = particleSystem.minSpeed
3794 local direction = particleSystem.direction
3795 if speed > minSpeed and (direction == 0 or (direction > 0) == (self.movingDirection > 0)) then
3796 local maxSpeed = particleSystem.maxSpeed
3797 local alpha = math.min((speed - minSpeed) / (maxSpeed - minSpeed), 1)
3798 local scale = MathUtil.lerp(particleSystem.minScale, particleSystem.maxScale, alpha)
3799 return scale
3800 end
3801 return 0
3802end

getIsVersatileYRotActive

Description
Definition
getIsVersatileYRotActive()
Code
3806function Wheels:getIsVersatileYRotActive(wheel)
3807 return true
3808end

getIsWheelFoliageDestructionAllowed

Description
Definition
getIsWheelFoliageDestructionAllowed()
Code
3595function Wheels:getIsWheelFoliageDestructionAllowed(wheel)
3596 if not g_currentMission.missionInfo.fruitDestruction then
3597 return false
3598 end
3599
3600 if self:getIsAIActive() then
3601 return false
3602 end
3603
3604 if wheel.contact ~= Wheels.WHEEL_GROUND_CONTACT then
3605 return false
3606 end
3607
3608 if wheel.isCareWheel then
3609 return false
3610 end
3611
3612 if self.getBlockFoliageDestruction ~= nil then
3613 if self:getBlockFoliageDestruction() then
3614 return false
3615 end
3616 end
3617
3618 return true
3619end

getSpecValueWheels

Description
Definition
getSpecValueWheels()
Code
4306function Wheels.getSpecValueWheels(storeItem, realItem)
4307 if realItem == nil then
4308 return nil
4309 end
4310
4311 return table.concat(Wheels.getTireNames(realItem), " / ")
4312end

getSteeringRotTimeByCurvature

Description
Definition
getSteeringRotTimeByCurvature()
Code
3993function Wheels:getSteeringRotTimeByCurvature(curvature)
3994 local targetRotTime = 0
3995 if curvature ~= 0 then
3996 local spec = self.spec_wheels
3997
3998 targetRotTime = math.huge
3999 if curvature > 0 then
4000 targetRotTime = -math.huge
4001 end
4002 for i, wheel in ipairs(spec.wheels) do
4003 if wheel.rotSpeed ~= 0 then
4004 local diffX, _, diffZ = localToLocal(wheel.node, spec.steeringCenterNode, wheel.positionX, wheel.positionY, wheel.positionZ)
4005 local targetRot = math.atan((diffZ * math.abs(curvature)) / ((1 - math.abs(curvature) * math.abs(diffX))))
4006 local wheelRotTime = targetRot / wheel.rotSpeed
4007 if curvature > 0 then
4008 wheelRotTime = -wheelRotTime
4009 targetRotTime = math.max(targetRotTime, wheelRotTime)
4010 else
4011 targetRotTime = math.min(targetRotTime, wheelRotTime)
4012 end
4013
4014 end
4015 end
4016
4017 targetRotTime = targetRotTime * -1
4018
4019-- log(string.format("Curvature %.4f , MaxCurvature %.4f, CalcTurnRadius %.3fm -> targetRot %.3f deg -> targetRotTime %.4f", curvature, maxCurvature, currentTurnRadius, math.deg(targetRot), targetRotTime))
4020 end
4021
4022
4023 return targetRotTime
4024end

getSupportsMountKinematic

Description
Definition
getSupportsMountKinematic()
Code
3126function Wheels:getSupportsMountKinematic(superFunc)
3127 return #self.spec_wheels.wheels == 0 and superFunc(self)
3128end

getTireNames

Description
Returns indexed list without duplicates of tire names used on the vehicle
Definition
getTireNames()
Code
4136function Wheels.getTireNames(instance)
4137 local spec = instance.spec_wheels
4138 local tireNames = {}
4139 if spec ~= nil then
4140 for i=1, #spec.wheels do
4141 local wheel = spec.wheels[i]
4142 if wheel.name ~= nil then
4143 tireNames[wheel.name] = true
4144 end
4145 end
4146 for i=1, #spec.dynamicallyLoadedWheels do
4147 local wheel = spec.dynamicallyLoadedWheels[i]
4148 if wheel.name ~= nil then
4149 tireNames[wheel.name] = true
4150 end
4151 end
4152 end
4153 return table.toList(tireNames)
4154end

getTireTrackColor

Description
Definition
getTireTrackColor()
Code
3272function Wheels:getTireTrackColor(wheel, wx, wy, wz, groundWetness)
3273 local r, g, b, a, t = nil, nil, nil, 0, nil
3274
3275 if wheel.contact == Wheels.WHEEL_GROUND_CONTACT then
3276 local isOnField = wheel.densityType ~= 0
3277 local dirtAmount = 1
3278
3279 if isOnField then
3280 local spec = self.spec_wheels
3281 r, g, b, a = spec.fieldGroundSystem:getFieldGroundTyreTrackColor(wheel.densityBits)
3282 t = 1.0
3283
3284 if wheel.densityType == spec.tireTrackGroundGrassValue then
3285 dirtAmount = 0.7
3286 elseif wheel.densityType == spec.tireTrackGroundGrassCutValue then
3287 dirtAmount = 0.6
3288 end
3289 else
3290 r, g, b, a, t = getTerrainAttributesAtWorldPos(g_currentMission.terrainRootNode, wx, wy, wz, true, true, true, true, false)
3291 dirtAmount = 0.5
3292 end
3293
3294 wheel.dirtAmount = dirtAmount
3295 wheel.lastColor[1] = r
3296 wheel.lastColor[2] = g
3297 wheel.lastColor[3] = b
3298 wheel.lastColor[4] = a
3299 wheel.lastTerrainAttribute = t
3300 elseif wheel.contact == Wheels.WHEEL_OBJ_CONTACT and wheel.lastContactObjectAllowsTireTracks then
3301 if wheel.dirtAmount > 0 then
3302 local maxTrackLength = 30 * (1 + groundWetness)
3303 local speedFactor = math.min(self:getLastSpeed(), 20) / 20
3304 maxTrackLength = maxTrackLength * (2 - speedFactor)
3305 wheel.dirtAmount = math.max(wheel.dirtAmount - self.lastMovedDistance/maxTrackLength, 0)
3306 r, g, b = wheel.lastColor[1], wheel.lastColor[2], wheel.lastColor[3]
3307 a = 0 -- no depth to tyre tracks on road etc.
3308 end
3309 end
3310
3311 return r, g, b, a, t
3312end

getTurningRadiusByRotTime

Description
Definition
getTurningRadiusByRotTime()
Code
4028function Wheels:getTurningRadiusByRotTime(rotTime)
4029 local spec = self.spec_wheels
4030
4031 local maxTurningRadius = math.huge
4032 if spec.steeringCenterNode ~= nil then
4033 for i, wheel in ipairs(spec.wheels) do
4034 if wheel.rotSpeed ~= 0 then
4035 local rotSpeed = math.abs(wheel.rotSpeed)
4036 local wheelRot = math.abs(rotTime * rotSpeed)
4037
4038 local diffX, _, diffZ = localToLocal(wheel.node, spec.steeringCenterNode, wheel.positionX, wheel.positionY, wheel.positionZ)
4039 local turningRadius = math.abs(diffZ)/math.tan(wheelRot) + math.abs(diffX)
4040 if turningRadius < maxTurningRadius then
4041 maxTurningRadius = turningRadius
4042 end
4043 end
4044 end
4045 end
4046
4047 return maxTurningRadius
4048end

getVehicleWorldDirection

Description
Returns the world space direction of the vehicle
Definition
getVehicleWorldDirection()
Return Values
floatxx
floatyy
floatzz
Code
2998function Wheels:getVehicleWorldDirection(superFunc)
2999 local avgDirX, avgDirY, avgDirZ, _ = 0, 0, 0
3000 local centerZ = 0
3001 local contactedWheels = 0
3002 local spec = self.spec_wheels
3003 for i=1, #spec.wheels do
3004 local wheel = spec.wheels[i]
3005 if wheel.hasGroundContact then
3006 local _, _, z = localToLocal(wheel.node, self.components[1].node, wheel.netInfo.x, wheel.netInfo.y, wheel.netInfo.z)
3007 centerZ = centerZ + z
3008
3009 local dx, dy, dz = localDirectionToWorld(wheel.node, wheel.directionZ, wheel.directionX, -wheel.directionY)
3010 avgDirX, avgDirY, avgDirZ = avgDirX + dx, avgDirY + dy, avgDirZ + dz
3011
3012 contactedWheels = contactedWheels + 1
3013 end
3014 end
3015
3016 if contactedWheels > 0 then
3017 avgDirX, _, avgDirZ = avgDirX / contactedWheels, avgDirY / contactedWheels, avgDirZ / contactedWheels
3018 end
3019
3020 if contactedWheels > 2 then
3021 centerZ = centerZ / contactedWheels
3022
3023 local frontCenterX, frontCenterY, frontCenterZ, frontWheelsCount = 0, 0, 0, 0
3024 local backCenterX, backCenterY, backCenterZ, backWheelsCount = 0, 0, 0, 0
3025
3026 for i=1, #spec.wheels do
3027 local wheel = spec.wheels[i]
3028 if wheel.hasGroundContact then
3029 local x, y, z = localToLocal(wheel.node, self.components[1].node, wheel.netInfo.x + wheel.directionX * wheel.radius, wheel.netInfo.y + wheel.directionY * wheel.radius, wheel.netInfo.z + wheel.directionZ * wheel.radius)
3030
3031 if z > centerZ + 0.25 then
3032 frontCenterX, frontCenterY, frontCenterZ, frontWheelsCount = frontCenterX + x, frontCenterY + y, frontCenterZ + z, frontWheelsCount + 1
3033 elseif z < centerZ - 0.25 then
3034 backCenterX, backCenterY, backCenterZ, backWheelsCount = backCenterX + x, backCenterY + y, backCenterZ + z, backWheelsCount + 1
3035 end
3036 end
3037 end
3038
3039 if frontWheelsCount > 0 and backWheelsCount > 0 then
3040 frontCenterX, frontCenterY, frontCenterZ = frontCenterX / frontWheelsCount, frontCenterY / frontWheelsCount, frontCenterZ / frontWheelsCount
3041 backCenterX, backCenterY, backCenterZ = backCenterX / backWheelsCount, backCenterY / backWheelsCount, backCenterZ / backWheelsCount
3042
3043 frontCenterX, frontCenterY, frontCenterZ = localToWorld(self.components[1].node, frontCenterX, frontCenterY, frontCenterZ)
3044 backCenterX, backCenterY, backCenterZ = localToWorld(self.components[1].node, backCenterX, backCenterY, backCenterZ)
3045
3046 if VehicleDebug.state == VehicleDebug.DEBUG_TRANSMISSION then
3047 DebugUtil.drawDebugGizmoAtWorldPos(frontCenterX, frontCenterY, frontCenterZ, 1, 0, 0, 0, 1, 0, "frontWheels", false)
3048 DebugUtil.drawDebugGizmoAtWorldPos(backCenterX, backCenterY, backCenterZ, 1, 0, 0, 0, 1, 0, "backWheels", false)
3049 end
3050
3051 local dx, dy, dz, _ = frontCenterX - backCenterX, frontCenterY - backCenterY, frontCenterZ - backCenterZ
3052 _, avgDirY, _ = MathUtil.vector3Normalize(dx, dy, dz)
3053 else
3054 return superFunc(self)
3055 end
3056 else
3057 return 0, 0, 0
3058 end
3059
3060 return MathUtil.vector3Normalize(avgDirX, avgDirY, avgDirZ)
3061end

getVehicleWorldXRot

Description
Returns the world space x rotation in rad
Definition
getVehicleWorldXRot()
Return Values
floatrotationrotation
Code
2954function Wheels:getVehicleWorldXRot(superFunc)
2955 local slopeAngle = 0
2956 local minWheelZ = math.huge
2957 local minWheelZHeight = 0
2958 local maxWheelZ = -math.huge
2959 local maxWheelZHeight = 0
2960
2961 local spec = self.spec_wheels
2962 for i=1, #spec.wheels do
2963 local wheel = spec.wheels[i]
2964 if wheel.hasGroundContact then
2965 local _, _, z = localToLocal(wheel.node, self.components[1].node, 0, 0, wheel.netInfo.z)
2966 local _, wheelY, _ = localToWorld(wheel.node, wheel.netInfo.x, wheel.netInfo.y - wheel.radius, wheel.netInfo.z)
2967 if z < minWheelZ then
2968 minWheelZ = z
2969 minWheelZHeight = wheelY
2970 end
2971 if z > maxWheelZ then
2972 maxWheelZ = z
2973 maxWheelZHeight = wheelY
2974 end
2975 end
2976 end
2977
2978 if minWheelZ ~= math.huge then
2979 local y = maxWheelZHeight - minWheelZHeight
2980 local l = maxWheelZ - minWheelZ
2981 if l < 0.25 and superFunc ~= nil then
2982 return superFunc(self)
2983 end
2984 slopeAngle = math.pi * 0.5 - math.atan(l/(y))
2985 if slopeAngle > math.pi * 0.5 then
2986 slopeAngle = slopeAngle - math.pi
2987 end
2988 end
2989
2990 return slopeAngle
2991end

getWheelByWheelNode

Description
Definition
getWheelByWheelNode()
Code
3818function Wheels:getWheelByWheelNode(wheelNode)
3819 local spec = self.spec_wheels
3820
3821 if type(wheelNode) == "string" then
3822 local mapping = self.i3dMappings[wheelNode]
3823 if mapping ~= nil then
3824 wheelNode = mapping.nodeId
3825 end
3826 end
3827
3828 for i=1, #spec.wheels do
3829 local wheel = spec.wheels[i]
3830 if wheel.repr == wheelNode
3831 or wheel.driveNode == wheelNode
3832 or wheel.linkNode == wheelNode then
3833 return wheel
3834 end
3835 end
3836
3837 return nil
3838end

getWheelConfigurationValue

Description
Load value from xml. First from the given key and if not found try to load from the baseConfig
Definition
getWheelConfigurationValue(table xmlFile, string configId, string configurationKey, string key, any_type attributes)
Arguments
tablexmlFilexml file object
stringconfigIdconfig identifier
stringconfigurationKeyconfig key
stringkeyattribute key
any_typeattributesadditional attributes
Return Values
any_typevaluevalue
Code
1319function Wheels:getWheelConfigurationValue(xmlFile, configId, configurationKey, key, ...)
1320 local spec = self.spec_wheels
1321 return Wheels.getConfigurationValue(spec.configurationSaveIdToIndex, spec.configurationIndexToBaseConfig, xmlFile, configId, configurationKey, key, ...)
1322end

getWheelFromWheelIndex

Description
Definition
getWheelFromWheelIndex()
Code
3812function Wheels:getWheelFromWheelIndex(wheelIndex)
3813 return self.spec_wheels.wheels[wheelIndex]
3814end

getWheelMassFromExternalFile

Description
Definition
getWheelMassFromExternalFile()
Code
4224function Wheels.getWheelMassFromExternalFile(filename, wheelConfigId)
4225 local mass = 0
4226 local wheelXMLFile = XMLFile.load("specWeightWheelXml", filename, Wheels.xmlSchema)
4227 if wheelXMLFile ~= nil then
4228 local wheelMass = wheelXMLFile:getValue("wheel.default.physics#mass", 0.1)
4229 local additionalMass = wheelXMLFile:getValue("wheel.default.additional#mass", 0)
4230
4231 if wheelConfigId ~= nil then
4232 wheelXMLFile:iterate("wheel.configurations.configuration", function(_, configKey)
4233 if wheelXMLFile:getValue(configKey .. "#id") == wheelConfigId then
4234 wheelMass = wheelXMLFile:getValue(configKey .. ".physics#mass", wheelMass)
4235 additionalMass = wheelXMLFile:getValue(configKey .. ".additional#mass", additionalMass)
4236 return false
4237 end
4238 end)
4239 end
4240
4241 mass = wheelMass + additionalMass
4242
4243 wheelXMLFile:delete()
4244 end
4245
4246 return mass
4247end

getWheels

Description
Definition
getWheels()
Code
3842function Wheels:getWheels()
3843 return self.spec_wheels.wheels
3844end

getWheelsByBrand

Description
Definition
getWheelsByBrand()
Code
4211function Wheels.getWheelsByBrand(items, brand)
4212 local wheels = {}
4213 for _, item in ipairs(items) do
4214 if item.wheelBrandName == brand.title then
4215 table.insert(wheels, item)
4216 end
4217 end
4218
4219 return wheels
4220end

initSpecialization

Description
Definition
initSpecialization()
Code
187function Wheels.initSpecialization()
188 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)
189 g_configurationManager:addConfigurationType("rimColor", g_i18n:getText("configuration_rimColor"), nil, nil, ConfigurationUtil.getConfigColorSingleItemLoad, ConfigurationUtil.getConfigColorPostLoad, ConfigurationUtil.SELECTOR_COLOR)
190
191 g_storeManager:addSpecType("wheels", "shopListAttributeIconWheels", Wheels.loadSpecValueWheels, Wheels.getSpecValueWheels, "vehicle")
192
193 g_storeManager:addVRamUsageFunction(Wheels.getVRamUsageFromXML)
194
195 local schema = Vehicle.xmlSchema
196 schema:setXMLSpecializationType("Wheels")
197
198 ConfigurationUtil.registerColorConfigurationXMLPaths(schema, "rimColor")
199 ObjectChangeUtil.registerObjectChangeXMLPaths(schema, "vehicle.wheels.wheelConfigurations.wheelConfiguration(?)")
200 schema:register(XMLValueType.STRING, "vehicle.wheels.wheelConfigurations.wheelConfiguration(?)#brand", "Wheel brand")
201
202 local configKey = Wheels.WHEELS_XML_PATH
203 schema:register(XMLValueType.FLOAT, configKey .. "#autoRotateBackSpeed", "Auto rotate back speed", 1)
204 schema:register(XMLValueType.BOOL, configKey .. "#speedDependentRotateBack", "Speed dependent auto rotate back speed", true)
205 schema:register(XMLValueType.INT, configKey .. "#differentialIndex", "Differential index")
206 schema:register(XMLValueType.INT, configKey .. "#ackermannSteeringIndex", "Ackermann steering index")
207 schema:register(XMLValueType.STRING, configKey .. "#baseConfig", "Base for this configuration")
208
209 schema:register(XMLValueType.BOOL, configKey .. "#hasSurfaceSounds", "Has surface sounds", true)
210 schema:register(XMLValueType.STRING, configKey .. "#surfaceSoundTireType", "Tire type that is used for surface sounds", "Tire type of first wheel")
211 schema:register(XMLValueType.NODE_INDEX, configKey .. "#surfaceSoundLinkNode", "Surface sound link node", "Root component")
212
213 Wheels.registerWheelXMLPaths(schema, configKey .. ".wheel(?)")
214
215 schema:register(XMLValueType.COLOR, "vehicle.wheels.rimColor", "Rim color")
216 schema:register(XMLValueType.BOOL, "vehicle.wheels.rimColor#useBaseColor", "Use base vehicle color", false)
217 schema:register(XMLValueType.INT, "vehicle.wheels.rimColor#material", "Material id")
218
219 for i = 0, 7 do
220 schema:register(XMLValueType.COLOR, "vehicle.wheels.hubs.color" .. i, "Color")
221 schema:register(XMLValueType.INT, "vehicle.wheels.hubs.color" .. i .. "#material", "Material id")
222 schema:register(XMLValueType.BOOL, "vehicle.wheels.hubs.color" .. i .. "#useBaseColor", "Use base color", false)
223 schema:register(XMLValueType.BOOL, "vehicle.wheels.hubs.color" .. i .. "#useRimColor", "Use rim color", false)
224 end
225
226 schema:register(XMLValueType.NODE_INDEX, "vehicle.wheels.hubs.hub(?)#linkNode", "Link node")
227 schema:register(XMLValueType.STRING, "vehicle.wheels.hubs.hub(?)#filename", "Filename")
228 schema:register(XMLValueType.BOOL, "vehicle.wheels.hubs.hub(?)#isLeft", "Is left side", false)
229
230 for i = 0, 7 do
231 schema:register(XMLValueType.STRING, "vehicle.wheels.hubs.hub(?).color" .. i, "Color")
232 schema:register(XMLValueType.INT, "vehicle.wheels.hubs.hub(?).color" .. i .. "#material", "Material id")
233 end
234
235 schema:register(XMLValueType.FLOAT, "vehicle.wheels.hubs.hub(?)#offset", "X axis offset")
236 schema:register(XMLValueType.VECTOR_SCALE, "vehicle.wheels.hubs.hub(?)#scale", "Hub scale")
237
238 schema:addDelayedRegistrationFunc("AnimatedVehicle:part", function(cSchema, cKey)
239 cSchema:register(XMLValueType.INT, cKey .. "#wheelIndex", "Wheel index [1..n]")
240 cSchema:register(XMLValueType.ANGLE, cKey .. "#startSteeringAngle", "Start steering angle")
241 cSchema:register(XMLValueType.ANGLE, cKey .. "#endSteeringAngle", "End steering angle")
242 cSchema:register(XMLValueType.FLOAT, cKey .. "#startBrakeFactor", "Start brake force factor")
243 cSchema:register(XMLValueType.FLOAT, cKey .. "#endBrakeFactor", "End brake force factor")
244 end)
245
246 local dynaWheelKey = "vehicle.wheels.dynamicallyLoadedWheels.dynamicallyLoadedWheel(?)"
247 schema:register(XMLValueType.NODE_INDEX, dynaWheelKey .. "#linkNode", "Link node")
248 schema:register(XMLValueType.STRING, dynaWheelKey .. "#filename", "Path to wheel xml file")
249 schema:register(XMLValueType.STRING, dynaWheelKey .. "#configId", "Wheel config id", "default")
250 schema:register(XMLValueType.BOOL, dynaWheelKey .. "#isLeft", "Is left", true)
251 schema:register(XMLValueType.BOOL, dynaWheelKey .. "#isInverted", "Tire profile inverted", false)
252 schema:register(XMLValueType.ANGLE, dynaWheelKey .. "#xRotOffset", "X rotation offset", 0)
253 schema:register(XMLValueType.COLOR, dynaWheelKey .. "#color", "Rim color")
254 schema:register(XMLValueType.COLOR, dynaWheelKey .. "#additionalColor", "Color of additional part")
255
256 Wheels.registerAckermannSteeringXMLPaths(schema, "vehicle.wheels.ackermannSteeringConfigurations.ackermannSteering(?)")
257
258 schema:setXMLSpecializationType()
259
260 Wheels.xmlSchema = XMLSchema.new("wheel")
261 Wheels.xmlSchema:register(XMLValueType.STRING, "wheel.brand", "Wheel tire brand", "LIZARD")
262 Wheels.xmlSchema:register(XMLValueType.STRING, "wheel.name", "Wheel tire name", "Tire")
263 Wheels.registerWheelSharedDataXMLPaths(Wheels.xmlSchema, "wheel.default")
264 Wheels.registerWheelSharedDataXMLPaths(Wheels.xmlSchema, "wheel.configurations.configuration(?)")
265 Wheels.xmlSchema:register(XMLValueType.STRING, "wheel.configurations.configuration(?)#id", "Configuration Id")
266
267 Wheels.xmlSchemaHub = XMLSchema.new("wheelHub")
268 local hubSchema = Wheels.xmlSchemaHub
269 hubSchema:register(XMLValueType.STRING, "hub.filename", "I3D filename")
270 hubSchema:register(XMLValueType.STRING, "hub.nodes#left", "Index of left node in hub i3d file")
271 hubSchema:register(XMLValueType.STRING, "hub.nodes#right", "Index of right node in hub i3d file")
272 hubSchema:register(XMLValueType.COLOR, "hub.color0", "Color 0")
273 hubSchema:register(XMLValueType.COLOR, "hub.color1", "Color 1")
274 hubSchema:register(XMLValueType.COLOR, "hub.color2", "Color 2")
275 hubSchema:register(XMLValueType.COLOR, "hub.color3", "Color 3")
276 hubSchema:register(XMLValueType.COLOR, "hub.color4", "Color 4")
277 hubSchema:register(XMLValueType.COLOR, "hub.color5", "Color 5")
278 hubSchema:register(XMLValueType.COLOR, "hub.color6", "Color 6")
279 hubSchema:register(XMLValueType.COLOR, "hub.color7", "Color 7")
280
281 Wheels.xmlSchemaConnector = XMLSchema.new("wheelConnector")
282 local connectorSchema = Wheels.xmlSchemaConnector
283 connectorSchema:register(XMLValueType.STRING, "connector.file#name", "I3D filename")
284 connectorSchema:register(XMLValueType.STRING, "connector.file#leftNode", "Index of left node in connector i3d file")
285 connectorSchema:register(XMLValueType.STRING, "connector.file#rightNode", "Index of right node in connector i3d file")
286
287 local schemaSavegame = Vehicle.xmlSchemaSavegame
288 schemaSavegame:register(XMLValueType.INT, "vehicles.vehicle(?).wheels#lastWheelConfiguration", "Last selected wheel configuration", 1)
289end

loadAckermannSteeringFromXML

Description
Definition
loadAckermannSteeringFromXML()
Code
2272function Wheels:loadAckermannSteeringFromXML(xmlFile, ackermannSteeringIndex)
2273 local spec = self.spec_wheels
2274
2275 local key, _ = ConfigurationUtil.getXMLConfigurationKey(xmlFile, ackermannSteeringIndex, "vehicle.wheels.ackermannSteeringConfigurations.ackermannSteering", nil, "ackermann")
2276
2277 spec.steeringCenterNode = nil
2278
2279 if key ~= nil then
2280 local rotSpeed = xmlFile:getValue(key.."#rotSpeed")
2281 local rotMax = xmlFile:getValue(key.."#rotMax")
2282
2283 local centerX
2284 local centerZ
2285 local rotCenterWheel1 = xmlFile:getValue(key.."#rotCenterWheel1")
2286 if rotCenterWheel1 ~= nil and spec.wheels[rotCenterWheel1] ~= nil then
2287 local wheel = spec.wheels[rotCenterWheel1]
2288 centerX, _, centerZ = localToLocal(wheel.node, self.components[1].node, wheel.positionX, wheel.positionY, wheel.positionZ)
2289
2290 local rotCenterWheel2 = xmlFile:getValue(key.."#rotCenterWheel2")
2291 if rotCenterWheel2 ~= nil and spec.wheels[rotCenterWheel2] ~= nil then
2292 if rotCenterWheel2 == rotCenterWheel1 then
2293 Logging.xmlWarning(xmlFile, "The ackermann steering wheels are identical (both index %d). Are you sure this is correct? (%s)", rotCenterWheel1, key)
2294 end
2295
2296 local wheel2 = spec.wheels[rotCenterWheel2]
2297 local x, _, z = localToLocal(wheel2.node, self.components[1].node, wheel2.positionX, wheel2.positionY, wheel2.positionZ)
2298 centerX, centerZ = 0.5*(centerX + x), 0.5*(centerZ + z)
2299 end
2300 else
2301 local centerNode, _ = xmlFile:getValue(key.."#rotCenterNode", nil, self.components, self.i3dMappings)
2302 if centerNode ~= nil then
2303 centerX, _, centerZ = localToLocal(centerNode, self.components[1].node, 0,0,0)
2304 spec.steeringCenterNode = centerNode
2305 else
2306 local p = xmlFile:getValue(key.."#rotCenter", nil, true)
2307 if p ~= nil then
2308 centerX = p[1]
2309 centerZ = p[2]
2310 end
2311 end
2312 end
2313 if spec.steeringCenterNode == nil then
2314 spec.steeringCenterNode = createTransformGroup("steeringCenterNode")
2315 link(self.components[1].node, spec.steeringCenterNode)
2316 if centerX ~= nil and centerZ ~= nil then
2317 setTranslation(spec.steeringCenterNode, centerX, 0, centerZ)
2318 end
2319 end
2320
2321 if rotSpeed ~= nil and rotMax ~= nil and centerX ~= nil then
2322 rotSpeed = math.abs(math.rad(rotSpeed))
2323 rotMax = math.abs(math.rad(rotMax))
2324
2325 -- find the wheel that should get the maximum steering (the one that results in the maximum turnign radius)
2326 local maxTurningRadius = 0
2327 local maxTurningRadiusWheel = 0
2328 for i, wheel in ipairs(spec.wheels) do
2329 if wheel.rotSpeed ~= 0 then
2330 local diffX, _, diffZ = localToLocal(wheel.node, spec.steeringCenterNode, wheel.positionX, wheel.positionY, wheel.positionZ)
2331 local turningRadius = math.abs(diffZ)/math.tan(rotMax) + math.abs(diffX)
2332 if turningRadius >= maxTurningRadius then
2333 maxTurningRadius = turningRadius
2334 maxTurningRadiusWheel = i
2335 end
2336 end
2337 end
2338 self.maxRotation = math.max(Utils.getNoNil(self.maxRotation, 0), rotMax)
2339 self.maxTurningRadius = maxTurningRadius
2340 self.maxTurningRadiusWheel = maxTurningRadiusWheel
2341 self.wheelSteeringDuration = rotMax / rotSpeed
2342
2343 if maxTurningRadiusWheel > 0 then
2344 for _, wheel in ipairs(spec.wheels) do
2345
2346 if wheel.rotSpeed ~= 0 then
2347 local diffX, _, diffZ = localToLocal(wheel.node, spec.steeringCenterNode, wheel.positionX, wheel.positionY, wheel.positionZ)
2348
2349 local rotMaxI = math.atan(diffZ/(maxTurningRadius-diffX))
2350 local rotMinI = -math.atan(diffZ/(maxTurningRadius+diffX))
2351
2352 local switchMaxMin = rotMinI > rotMaxI
2353 if switchMaxMin then
2354 rotMaxI, rotMinI = rotMinI, rotMaxI
2355 end
2356
2357 wheel.rotMax = rotMaxI
2358 wheel.rotMin = rotMinI
2359 wheel.rotSpeed = rotMaxI/self.wheelSteeringDuration
2360 wheel.rotSpeedNeg = -rotMinI/self.wheelSteeringDuration
2361
2362 if wheel.steeringAxleScale ~= 0 then
2363 if switchMaxMin then
2364 wheel.steeringAxleScale = -wheel.steeringAxleScale
2365 end
2366
2367 wheel.steeringAxleRotMax = rotMaxI
2368 wheel.steeringAxleRotMin = rotMinI
2369 end
2370
2371 if wheel.invertRotLimit then
2372 switchMaxMin = not switchMaxMin
2373 end
2374
2375 if switchMaxMin then
2376 wheel.rotSpeed, wheel.rotSpeedNeg = -wheel.rotSpeedNeg, -wheel.rotSpeed
2377 end
2378 end
2379 end
2380 end
2381 end
2382 end
2383
2384 for _, wheel in ipairs(spec.wheels) do
2385 if wheel.rotSpeed ~= 0 then
2386 -- if both speed and rot have the same sign, we can reach it with the positive time
2387 if (wheel.rotMax >= 0) == (wheel.rotSpeed >= 0) then
2388 self.maxRotTime = math.max(wheel.rotMax/wheel.rotSpeed, self.maxRotTime)
2389 end
2390 if (wheel.rotMin >= 0) == (wheel.rotSpeed >= 0) then
2391 self.maxRotTime = math.max(wheel.rotMin/wheel.rotSpeed, self.maxRotTime)
2392 end
2393
2394 -- if speed and rot have a different sign, we can reach it with the negative time
2395 local rotSpeedNeg = wheel.rotSpeedNeg
2396 if rotSpeedNeg == nil then
2397 rotSpeedNeg = wheel.rotSpeed
2398 end
2399 if (wheel.rotMax >= 0) ~= (rotSpeedNeg >= 0) then
2400 self.minRotTime = math.min(wheel.rotMax/rotSpeedNeg, self.minRotTime)
2401 end
2402 if (wheel.rotMin >= 0) ~= (rotSpeedNeg >= 0) then
2403 self.minRotTime = math.min(wheel.rotMin/rotSpeedNeg, self.minRotTime)
2404 end
2405 end
2406
2407 for i=1, #wheel.fenders do
2408 local fender = wheel.fenders[i]
2409
2410 fender.rotMax = fender.rotMax or wheel.rotMax
2411 fender.rotMin = fender.rotMin or wheel.rotMin
2412 end
2413
2414 wheel.steeringNodeMaxRot = math.max(wheel.rotMax, wheel.steeringAxleRotMax)
2415 wheel.steeringNodeMinRot = math.min(wheel.rotMin, wheel.steeringAxleRotMin)
2416
2417 if wheel.rotSpeedLimit ~= nil then
2418 wheel.rotSpeedDefault = wheel.rotSpeed
2419 wheel.rotSpeedNegDefault = wheel.rotSpeedNeg
2420 wheel.currentRotSpeedAlpha = 1
2421 end
2422 end
2423end

loadAdditionalWheelConnectorFromXML

Description
Definition
loadAdditionalWheelConnectorFromXML()
Code
1875function Wheels:loadAdditionalWheelConnectorFromXML(wheel, additionalWheel, xmlFile, configKey, wheelKey)
1876 local spec = self.spec_wheels
1877
1878 local connectorFilename = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey..".connector#filename")
1879 if connectorFilename ~= nil and connectorFilename ~= "" then
1880 XMLUtil.checkDeprecatedXMLElements(xmlFile, configKey..wheelKey..".connector#index", configKey..wheelKey..".connector#node")
1881
1882 local connector = {}
1883
1884 if connectorFilename:endsWith(".xml") then
1885 local xmlFilename = Utils.getFilename(connectorFilename, self.baseDirectory)
1886 local connectorXmlFile = XMLFile.load("connectorXml", xmlFilename, Wheels.xmlSchemaConnector)
1887
1888 if connectorXmlFile ~= nil then
1889 local nodeKey = "leftNode"
1890 if not wheel.isLeft then
1891 nodeKey = "rightNode"
1892 end
1893 connector.filename = connectorXmlFile:getValue("connector.file#name")
1894 connector.nodeStr = connectorXmlFile:getValue("connector.file#"..nodeKey)
1895
1896 connectorXmlFile:delete()
1897 else
1898 Logging.xmlError(xmlFile, "Unable to load connector xml file '%s'!", connectorFilename)
1899 end
1900 else
1901 connector.filename = connectorFilename
1902 connector.nodeStr = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey..".connector#node")
1903 end
1904
1905 if connector.filename ~= nil and connector.filename ~= "" then
1906 connector.useWidthAndDiam = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey..".connector#useWidthAndDiam", false)
1907 connector.usePosAndScale = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey..".connector#usePosAndScale", false)
1908
1909 connector.diameter = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey..".connector#diameter")
1910 connector.additionalOffset = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey..".connector#offset", 0)
1911 connector.width = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey..".connector#width")
1912 connector.startPos = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey..".connector#startPos")
1913 connector.endPos = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey..".connector#endPos")
1914 connector.scale = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey..".connector#uniformScale")
1915 connector.color = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey..".connector#color", nil, true)
1916 if connector.color == nil then
1917 connector.color = ConfigurationUtil.getColorByConfigId(self, "rimColor", self.configurations["rimColor"]) or wheel.color or spec.rimColor
1918 else
1919 connector.color[4] = nil
1920 end
1921
1922 additionalWheel.connector = connector
1923 end
1924 end
1925end

loadAdditionalWheelsFromXML

Description
Loads additional wheels from xml key
Definition
loadAdditionalWheelsFromXML(table xmlFile, string configKey, string wheelKey, table wheel)
Arguments
tablexmlFilexml file object
stringconfigKeyconfigKey
stringwheelKeywheelKey
tablewheelwheel
Return Values
booleansuccesssuccess
Code
1819function Wheels:loadAdditionalWheelsFromXML(xmlFile, configKey, wheelKey, wheel)
1820 local additionalWheels = {}
1821
1822 local i = 0
1823 while true do
1824 local additionalWheelKey = string.format(wheelKey..".additionalWheel(%d)", i)
1825 if not xmlFile:hasProperty(configKey .. additionalWheelKey) then
1826 break
1827 end
1828
1829 local xmlFilename = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, additionalWheelKey.."#filename")
1830 if xmlFilename ~= nil and xmlFilename ~= "" then
1831 XMLUtil.checkDeprecatedXMLElements(xmlFile, configKey .. additionalWheelKey.."#configIndex", configKey .. additionalWheelKey.."#configId")
1832 XMLUtil.checkDeprecatedXMLElements(xmlFile, configKey .. additionalWheelKey.."#addRaycast", nil)
1833
1834 local additionalWheel = {}
1835 additionalWheel.node = wheel.node
1836 additionalWheel.key = configKey .. additionalWheelKey
1837 additionalWheel.singleKey = additionalWheelKey
1838 additionalWheel.linkNode = wheel.linkNode
1839
1840 local wheelConfigId = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, additionalWheelKey.."#configId", "default")
1841 additionalWheel.isLeft = Utils.getNoNil(self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, additionalWheelKey.."#isLeft", wheel.isLeft), false)
1842 additionalWheel.xRotOffset = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, additionalWheelKey.."#xRotOffset", 0)
1843 additionalWheel.color = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, additionalWheelKey.."#color", nil, true) or wheel.color
1844
1845 if self:loadWheelDataFromExternalXML(additionalWheel, xmlFilename, wheelConfigId, false) then
1846 additionalWheel.hasParticles = Utils.getNoNil(self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, additionalWheelKey.."#hasParticles", wheel.hasParticles), false)
1847 additionalWheel.hasTireTracks = Utils.getNoNil(self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, additionalWheelKey.."#hasTireTracks", wheel.hasTireTracks), false)
1848
1849 additionalWheel.offset = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, additionalWheelKey.."#offset", 0)
1850
1851 self:loadWheelVisualData(xmlFile, configKey, additionalWheelKey, additionalWheel, wheel.configIndex)
1852
1853 self:loadAdditionalWheelConnectorFromXML(wheel, additionalWheel, xmlFile, configKey, additionalWheelKey)
1854
1855 table.insert(additionalWheels, additionalWheel)
1856
1857 wheel.mass = wheel.mass + additionalWheel.mass + additionalWheel.additionalMass
1858 wheel.maxLatStiffness = wheel.maxLatStiffness + additionalWheel.maxLatStiffness
1859 wheel.maxLongStiffness = wheel.maxLongStiffness + additionalWheel.maxLongStiffness
1860 end
1861 end
1862
1863 i = i + 1
1864 end
1865
1866 if #additionalWheels > 0 then
1867 wheel.additionalWheels = additionalWheels
1868 end
1869
1870 return true
1871end

loadBrandName

Description
Definition
loadBrandName()
Code
4158function Wheels.loadBrandName(xmlFile, key, baseDir, customEnvironment, isMod, configItem)
4159 local name = xmlFile:getValue(key.."#brand")
4160 configItem.wheelBrandKey = key
4161 if name ~= nil then
4162 local brandDesc = g_brandManager:getBrandByName(name)
4163 if brandDesc ~= nil then
4164 configItem.wheelBrandName = brandDesc.title
4165 configItem.wheelBrandIconFilename = brandDesc.image
4166
4167 table.insert(configItem.nameCompareParams, "wheelBrandName")
4168 else
4169 Logging.xmlWarning(xmlFile, "Wheel brand '%s' is not defined for '%s'!", name, key)
4170 end
4171 end
4172end

loadedBrandNames

Description
Definition
loadedBrandNames()
Code
4176function Wheels.loadedBrandNames(xmlFile, baseKey, baseDir, customEnvironment, isMod, configurationItems, storeItem)
4177 local hasWheelBrands = false
4178 for _, item in ipairs(configurationItems) do
4179 if item.wheelBrandName ~= nil then
4180 hasWheelBrands = true
4181 break
4182 end
4183 end
4184
4185 if hasWheelBrands then
4186 for _, item in ipairs(configurationItems) do
4187 if item.wheelBrandName == nil then
4188 Logging.xmlWarning(xmlFile, "Wheel brand missing for wheel configuration '%s'!", item.wheelBrandKey)
4189 end
4190 end
4191 end
4192end

loadHubFromXML

Description
Definition
loadHubFromXML()
Code
2156function Wheels:loadHubFromXML(xmlFile, key)
2157 local spec = self.spec_wheels
2158 local linkNode = xmlFile:getValue(key .. "#linkNode", nil, self.components, self.i3dMappings)
2159 if linkNode == nil then
2160 Logging.xmlError(xmlFile, "Missing link node for hub '%s'", key)
2161 return
2162 end
2163
2164 local hub = {}
2165 hub.linkNode = linkNode
2166 hub.isLeft = xmlFile:getValue(key .. "#isLeft")
2167
2168 local hubXmlFilename = xmlFile:getValue(key .. "#filename")
2169 hub.xmlFilename = Utils.getFilename(hubXmlFilename, self.baseDirectory)
2170 local xmlFileHub = XMLFile.load("wheelHubXml", hub.xmlFilename, Wheels.xmlSchemaHub)
2171 if xmlFileHub ~= nil then
2172 local i3dFilename = xmlFileHub:getValue("hub.filename")
2173 if i3dFilename == nil then
2174 Logging.xmlError(xmlFileHub, "Unable to retrieve hub i3d filename!")
2175 return
2176 end
2177 hub.i3dFilename = Utils.getFilename(i3dFilename, self.baseDirectory)
2178
2179 hub.colors = {}
2180 for j = 0, 7 do
2181 -- save supported colorMat shaders to be able to check for invalid usage
2182 hub.colors[j] = xmlFileHub:getValue(string.format("hub.color%d", j), nil, true)
2183 end
2184
2185 hub.nodeStr = xmlFileHub:getValue("hub.nodes#" .. (hub.isLeft and "left" or "right"))
2186
2187 local arguments = {
2188 hub = hub,
2189 linkNode = linkNode,
2190 xmlFile = xmlFile,
2191 key = key
2192 }
2193
2194 local sharedLoadRequestId = self:loadSubSharedI3DFile(hub.i3dFilename, false, false, self.onWheelHubI3DLoaded, self, arguments)
2195 table.insert(spec.sharedLoadRequestIds, sharedLoadRequestId)
2196
2197 xmlFileHub:delete()
2198 end
2199
2200 return true
2201end

loadHubsFromXML

Description
Definition
loadHubsFromXML()
Code
2119function Wheels:loadHubsFromXML()
2120 local spec = self.spec_wheels
2121
2122 spec.hubsColors = {}
2123
2124 -- load hubs colors (global)
2125 for j = 0, 7 do
2126 local hubsColorsKey = string.format("vehicle.wheels.hubs.color%d", j)
2127 local color = self.xmlFile:getValue(hubsColorsKey, nil, true)
2128 local material = self.xmlFile:getValue(hubsColorsKey.."#material")
2129 if color ~= nil then
2130 spec.hubsColors[j] = color
2131 spec.hubsColors[j][4] = material
2132 elseif self.xmlFile:getValue(hubsColorsKey .. "#useBaseColor") then
2133 spec.hubsColors[j] = ConfigurationUtil.getColorByConfigId(self, "baseColor", self.configurations["baseColor"]) or ConfigurationUtil.getColorByConfigId(self, "baseMaterial", self.configurations["baseMaterial"])
2134 elseif self.xmlFile:getValue(hubsColorsKey .. "#useRimColor") then
2135 spec.hubsColors[j] = Utils.getNoNil(ConfigurationUtil.getColorByConfigId(self, "rimColor", self.configurations["rimColor"]), spec.rimColor)
2136 end
2137
2138 local overwrittenHubColor = spec.overwrittenWheelColors[string.format("_hubColor%d", j)]
2139 if #overwrittenHubColor > 0 then
2140 spec.hubsColors[j] = spec.hubsColors[j] or {0, 0, 0, 0}
2141 spec.hubsColors[j][1], spec.hubsColors[j][2], spec.hubsColors[j][3] = overwrittenHubColor[1], overwrittenHubColor[2], overwrittenHubColor[3]
2142 if #overwrittenHubColor == 4 then
2143 spec.hubsColors[j][4] = overwrittenHubColor[4]
2144 end
2145 end
2146 end
2147
2148 spec.hubs = {}
2149 self.xmlFile:iterate("vehicle.wheels.hubs.hub", function (_, key)
2150 self:loadHubFromXML(self.xmlFile, key)
2151 end)
2152end

loadNonPhysicalWheelFromXML

Description
Definition
loadNonPhysicalWheelFromXML()
Code
2427function Wheels:loadNonPhysicalWheelFromXML(dynamicallyLoadedWheel, xmlFile, key)
2428
2429 dynamicallyLoadedWheel.linkNode = xmlFile:getValue(key .. "#linkNode", self.components[1].node, self.components, self.i3dMappings)
2430
2431 local wheelXmlFilename = xmlFile:getValue(key.."#filename")
2432 if wheelXmlFilename ~= nil and wheelXmlFilename ~= "" then
2433 local wheelConfigId = xmlFile:getValue(key.."#configId", "default")
2434 dynamicallyLoadedWheel.isLeft = xmlFile:getValue(key.."#isLeft", true)
2435 dynamicallyLoadedWheel.tireIsInverted = xmlFile:getValue(key.."#isInverted", false)
2436 dynamicallyLoadedWheel.xRotOffset = xmlFile:getValue(key.."#xRotOffset", 0)
2437 dynamicallyLoadedWheel.color = xmlFile:getValue(key.."#color", nil, true)
2438 dynamicallyLoadedWheel.additionalColor = xmlFile:getValue(key.."#additionalColor", nil, true)
2439 self:loadWheelDataFromExternalXML(dynamicallyLoadedWheel, wheelXmlFilename, wheelConfigId)
2440
2441 self:finalizeWheel(dynamicallyLoadedWheel)
2442
2443 return true
2444 end
2445
2446 return false
2447end

loadSpecValueWheels

Description
Definition
loadSpecValueWheels()
Code
4299function Wheels.loadSpecValueWheels(xmlFile, customEnvironment, baseDir)
4300 -- No data to load as this spec is only for existing items
4301 return nil
4302end

loadSpecValueWheelWeight

Description
Definition
loadSpecValueWheelWeight()
Code
4251function Wheels.loadSpecValueWheelWeight(xmlFile, customEnvironment, baseDir)
4252 local configurationSaveIdToIndex, configurationIndexToBaseConfig = Wheels.createConfigSaveIdMapping(xmlFile)
4253
4254 -- find default wheel config, either index 0 or the one where #isDefault attribute is set
4255 local defaultConfigIndex = 0
4256 xmlFile:iterate("vehicle.wheels.wheelConfigurations.wheelConfiguration", function(configIndex, wheelConfigKey)
4257 if xmlFile:getValue(wheelConfigKey .. "#isDefault") then
4258 defaultConfigIndex = configIndex
4259 return false
4260 end
4261 end)
4262
4263 local defaultConfigKey = string.format("vehicle.wheels.wheelConfigurations.wheelConfiguration(%d)", defaultConfigIndex)
4264 local configMass = 0
4265
4266 xmlFile:iterate(defaultConfigKey .. ".wheels.wheel", function(index, key)
4267 local mass = Wheels.getConfigurationValue(configurationSaveIdToIndex, configurationIndexToBaseConfig, xmlFile, defaultConfigIndex, defaultConfigKey, string.format(".wheels.wheel(%d).physics#mass", index - 1))
4268
4269 if mass ~= nil then
4270 configMass = configMass + mass
4271 else
4272 local filename = Wheels.getConfigurationValue(configurationSaveIdToIndex, configurationIndexToBaseConfig, xmlFile, defaultConfigIndex, defaultConfigKey, string.format(".wheels.wheel(%d)#filename", index - 1))
4273 if filename ~= nil then
4274 local wheelConfigId = Wheels.getConfigurationValue(configurationSaveIdToIndex, configurationIndexToBaseConfig, xmlFile, defaultConfigIndex, defaultConfigKey, string.format(".wheels.wheel(%d)#configId", index - 1))
4275 filename = Utils.getFilename(filename, baseDir)
4276
4277 configMass = configMass + Wheels.getWheelMassFromExternalFile(filename, wheelConfigId)
4278 else
4279 configMass = configMass + 0.1 -- default mass
4280 end
4281
4282 xmlFile:iterate(defaultConfigKey .. string.format(".wheels.wheel(%d).additionalWheel", index - 1), function(additionalIndex, _)
4283 local additionalFilename = Wheels.getConfigurationValue(configurationSaveIdToIndex, configurationIndexToBaseConfig, xmlFile, defaultConfigIndex, defaultConfigKey, string.format(".wheels.wheel(%d).additionalWheel(%d)#filename", index - 1, additionalIndex - 1))
4284 if additionalFilename ~= nil then
4285 local wheelConfigId = Wheels.getConfigurationValue(configurationSaveIdToIndex, configurationIndexToBaseConfig, xmlFile, defaultConfigIndex, defaultConfigKey, string.format(".wheels.wheel(%d).additionalWheel(%d)#configId", index - 1, additionalIndex - 1))
4286 additionalFilename = Utils.getFilename(additionalFilename, baseDir)
4287
4288 configMass = configMass + Wheels.getWheelMassFromExternalFile(additionalFilename, wheelConfigId)
4289 end
4290 end)
4291 end
4292 end)
4293
4294 return configMass
4295end

loadWheelBaseData

Description
Loads wheel basics
Definition
loadWheelBaseData(table xmlFile, string configKey, string wheelKey, table wheel)
Arguments
tablexmlFilexml file object
stringconfigKeyconfigKey
stringwheelKeywheelKey
tablewheelwheel
Return Values
booleansuccesssuccess
Code
1437function Wheels:loadWheelBaseData(xmlFile, configKey, wheelKey, wheel)
1438 wheel.repr = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#repr", nil, self.components, self.i3dMappings)
1439 if wheel.repr == nil then
1440 Logging.xmlWarning(xmlFile, "Failed to load wheel! Missing repr node for wheel '%s'", configKey .. wheelKey)
1441 return false
1442 end
1443
1444 wheel.color = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. "#color", nil, true)
1445 wheel.material = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. "#material")
1446 wheel.additionalColor = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. "#additionalColor", nil, true)
1447 wheel.additionalMaterial = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. "#additionalMaterial")
1448
1449 wheel.isLeft = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. "#isLeft", true)
1450 wheel.hasTireTracks = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. "#hasTireTracks", false)
1451 wheel.hasParticles = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. "#hasParticles", false)
1452
1453 local filename = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. "#filename")
1454 if filename ~= nil and filename ~= "" then
1455 local wheelConfigId = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. "#configId", "default")
1456 wheel.xRotOffset = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. "#xRotOffset", 0)
1457 self:loadWheelDataFromExternalXML(wheel, filename, wheelConfigId, true)
1458 end
1459
1460 return true
1461end

loadWheelChocksFromXML

Description
Loads wheel chocks from xml file
Definition
loadWheelChocksFromXML(table xmlFile, string configKey, string wheelKey, table wheel)
Arguments
tablexmlFilexml file object
stringconfigKeyconfigKey
stringwheelKeywheelKey
tablewheelwheel
Return Values
booleansuccesssuccess
Code
1934function Wheels:loadWheelChocksFromXML(xmlFile, configKey, wheelKey, wheel)
1935 local spec = self.spec_wheels
1936 wheel.wheelChocks = {}
1937
1938 local i = 0
1939 while true do
1940 local chockKey = string.format(wheelKey .. ".wheelChock(%d)", i)
1941 if not xmlFile:hasProperty(configKey .. chockKey) then
1942 break
1943 end
1944
1945 local filename = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, chockKey.."#filename", "$data/shared/assets/wheelChocks/wheelChock01.i3d")
1946 filename = Utils.getFilename(filename, self.baseDirectory)
1947 local arguments = {
1948 wheel = wheel,
1949 filename = filename,
1950 xmlFile = xmlFile,
1951 configKey = configKey,
1952 chockKey = chockKey
1953 }
1954 local sharedLoadRequestId = self:loadSubSharedI3DFile(filename, false, false, self.onWheelChockI3DLoaded, self, arguments)
1955 table.insert(spec.sharedLoadRequestIds, sharedLoadRequestId)
1956
1957 i = i + 1
1958 end
1959
1960 return true
1961end

loadWheelDataFromExternalXML

Description
loads external wheel xml for specified config and writes values into table
Definition
loadWheelDataFromExternalXML()
Code
1465function Wheels:loadWheelDataFromExternalXML(wheel, xmlFilename, wheelConfigId)
1466 xmlFilename = Utils.getFilename(xmlFilename, self.baseDirectory)
1467 local xmlFile = XMLFile.load("wheelXml", xmlFilename, Wheels.xmlSchema)
1468
1469 if xmlFile ~= nil then
1470 -- load default values
1471 local wheelKey = "wheel"
1472 wheel.brand = g_brandManager:getBrandByName(xmlFile:getValue(wheelKey .. ".brand"))
1473 wheel.name = xmlFile:getValue(wheelKey .. ".name")
1474 self:loadWheelSharedData(xmlFile, wheelKey, ".default", wheel, true)
1475
1476 -- load config specific values and overwrite existing ones
1477 if wheelConfigId ~= nil and wheelConfigId ~= "" and wheelConfigId ~= "default" then
1478 local i = 0
1479 local wheelConfigFound = false
1480 while true do
1481 local configKey = string.format(".configurations.configuration(%d)", i)
1482 if not xmlFile:hasProperty(wheelKey .. configKey) then
1483 break
1484 end
1485 if xmlFile:getValue(wheelKey .. configKey.."#id") == wheelConfigId then
1486 wheelConfigFound = true
1487 self:loadWheelSharedData(xmlFile, wheelKey, configKey, wheel, true)
1488 break
1489 end
1490 i = i + 1
1491 end
1492
1493 if not wheelConfigFound then
1494 Logging.xmlError(xmlFile, "WheelConfigId '%s' not found!", wheelConfigId)
1495 return false
1496 end
1497 end
1498
1499 xmlFile:delete()
1500 else
1501 return false
1502 end
1503
1504 return true
1505end

loadWheelFromXML

Description
Loads a physical wheel from vehicle xml file
Definition
loadWheelFromXML(table xmlFile, string key, table wheel)
Arguments
tablexmlFilexml file object
stringkeykey
tablewheelwheel
Return Values
booleansuccesssuccess
Code
1386function Wheels:loadWheelFromXML(xmlFile, configKey, wheelKey, wheel)
1387 XMLUtil.checkDeprecatedXMLElements(xmlFile, string.format("vehicle.wheels.wheel(%d)#hasTyreTracks", wheel.xmlIndex), string.format("vehicle.wheels.wheelConfigurations.wheelConfiguration.wheels.wheel(%d)#hasTireTracks", wheel.xmlIndex))
1388 XMLUtil.checkDeprecatedXMLElements(xmlFile, string.format("vehicle.wheels.wheel(%d)#tyreTrackAtlasIndex", wheel.xmlIndex), string.format("vehicle.wheels.wheelConfigurations.wheelConfiguration.wheels.wheel(%d)#tireTrackAtlasIndex", wheel.xmlIndex))
1389 XMLUtil.checkDeprecatedXMLElements(xmlFile, string.format("vehicle.wheels.wheel(%d)#configIndex", wheel.xmlIndex), string.format("vehicle.wheels.wheelConfigurations.wheelConfiguration.wheels.wheel(%d)#configId", wheel.xmlIndex))
1390
1391 if not self:loadWheelBaseData(xmlFile, configKey, wheelKey, wheel) then
1392 return false
1393 end
1394
1395 if not self:loadWheelSharedData(xmlFile, configKey, wheelKey, wheel) then
1396 return false
1397 end
1398
1399 if wheel.mass == nil then
1400 Logging.xmlWarning(xmlFile, "Missing 'mass' for wheel '%s'. Using default '0.1'!", configKey .. wheelKey)
1401 wheel.mass = 0.1
1402 end
1403
1404 if not self:loadWheelPhysicsData(xmlFile, configKey, wheelKey, wheel) then
1405 return false
1406 end
1407
1408 if not self:loadWheelSteeringData(xmlFile, configKey, wheelKey, wheel) then
1409 return false
1410 end
1411
1412 if not self:loadAdditionalWheelsFromXML(xmlFile, configKey, wheelKey, wheel) then
1413 return false
1414 end
1415
1416 if not self:loadWheelChocksFromXML(xmlFile, configKey, wheelKey, wheel) then
1417 return false
1418 end
1419
1420 if wheel.hasParticles then
1421 self:loadWheelParticleSystem(xmlFile, configKey, wheelKey, wheel)
1422 end
1423
1424 wheel.wheelShape = 0
1425 wheel.wheelShapeCreated = false
1426
1427 return true
1428end

loadWheelParticleSystem

Description
Loads wheel particle system from xml
Definition
loadWheelParticleSystem(table xmlFile, string configKey, string wheelKey, table wheel)
Arguments
tablexmlFilexml file object
stringconfigKeyconfigKey
stringwheelKeywheelKey
tablewheelwheel
Return Values
booleansuccesssuccess
Code
2028function Wheels:loadWheelParticleSystem(xmlFile, configKey, wheelKey, wheel)
2029 local spec = self.spec_wheels
2030 wheel.driveGroundParticleSystems = {}
2031 wheel.driveGroundParticleStates = {wheel_dust=false, wheel_dry=false, wheel_wet=false, wheel_snow=false}
2032
2033 local i3dFilename = Utils.getFilename(Wheels.PARTICLE_SYSTEM_PATH, self.baseDirectory)
2034 for name, _ in pairs(wheel.driveGroundParticleStates) do
2035 local sourceParticleSystem = g_particleSystemManager:getParticleSystem(name)
2036 if sourceParticleSystem ~= nil then
2037 local args = {
2038 xmlFile = xmlFile,
2039 configKey = configKey,
2040 wheelKey = wheelKey,
2041 wheel = wheel,
2042 wheelData = wheel,
2043 sourceParticleSystem = sourceParticleSystem,
2044 name = name,
2045 i3dFilename = i3dFilename
2046 }
2047
2048 local sharedLoadRequestId = self:loadSubSharedI3DFile(i3dFilename, false, false, self.onWheelParticleSystemI3DLoaded, self, args)
2049 table.insert(spec.sharedLoadRequestIds, sharedLoadRequestId)
2050
2051 if wheel.additionalWheels ~= nil then
2052 for _, additionalWheel in ipairs(wheel.additionalWheels) do
2053 if additionalWheel.hasParticles then
2054 if additionalWheel.driveGroundParticleSystems == nil then
2055 additionalWheel.driveGroundParticleSystems = {}
2056 end
2057
2058 local argsAdditional = {
2059 xmlFile = xmlFile,
2060 configKey = configKey,
2061 wheelKey = additionalWheel.singleKey,
2062 wheel = wheel,
2063 wheelData = additionalWheel,
2064 sourceParticleSystem = sourceParticleSystem,
2065 name = name,
2066 i3dFilename = i3dFilename
2067 }
2068
2069 local sharedLoadRequestIdAdditional = self:loadSubSharedI3DFile(i3dFilename, false, false, self.onWheelParticleSystemI3DLoaded, self, argsAdditional)
2070 table.insert(spec.sharedLoadRequestIds, sharedLoadRequestIdAdditional)
2071 end
2072 end
2073 end
2074 end
2075 end
2076end

loadWheelPhysicsData

Description
Loads physical data of wheel from vehicle xml
Definition
loadWheelPhysicsData(table xmlFile, string configKey, string wheelKey, table wheel)
Arguments
tablexmlFilexml file object
stringconfigKeyconfigKey
stringwheelKeywheelKey
tablewheelwheel
Return Values
booleansuccesssuccess
Code
1616function Wheels:loadWheelPhysicsData(xmlFile, configKey, wheelKey, wheel)
1617 wheel.node = self:getParentComponent(wheel.repr)
1618 if wheel.node ~= 0 then
1619 wheel.driveNode = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#driveNode", nil, self.components, self.i3dMappings)
1620
1621 if wheel.driveNode == wheel.repr then
1622 Logging.xmlWarning(xmlFile, "repr and driveNode may not be equal for '%s'. Using default driveNode instead!", wheelKey)
1623 wheel.driveNode = nil
1624 end
1625
1626 wheel.linkNode = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#linkNode", nil, self.components, self.i3dMappings)
1627
1628 if wheel.driveNode == nil then
1629 -- create a new repr and use repr as drivenode
1630 local newRepr = createTransformGroup("wheelReprNode")
1631 local reprIndex = getChildIndex(wheel.repr)
1632 link(getParent(wheel.repr), newRepr, reprIndex)
1633 setTranslation(newRepr, getTranslation(wheel.repr))
1634 setRotation(newRepr, getRotation(wheel.repr))
1635 setScale(newRepr, getScale(wheel.repr))
1636 wheel.driveNode = wheel.repr
1637
1638 link(newRepr, wheel.driveNode)
1639 setTranslation(wheel.driveNode, 0, 0, 0)
1640 setRotation(wheel.driveNode, 0, 0, 0)
1641 setScale(wheel.driveNode, 1, 1, 1)
1642 wheel.repr = newRepr
1643 end
1644
1645 if wheel.driveNode ~= nil then
1646 local driveNodeDirectionNode = createTransformGroup("driveNodeDirectionNode")
1647 link(getParent(wheel.repr), driveNodeDirectionNode)
1648 setWorldTranslation(driveNodeDirectionNode, getWorldTranslation(wheel.driveNode))
1649 setWorldRotation(driveNodeDirectionNode, getWorldRotation(wheel.driveNode))
1650 wheel.driveNodeDirectionNode = driveNodeDirectionNode
1651
1652 local defaultX, defaultY, defaultZ = getRotation(wheel.driveNode)
1653 if math.abs(defaultX) > 0.0001 or math.abs(defaultY) > 0.0001 or math.abs(defaultZ) > 0.0001 then
1654 Logging.xmlWarning(xmlFile, "Rotation of driveNode '%s' is not 0/0/0 in the i3d file (%.1f/%.1f/%.1f). '%s'", getName(wheel.driveNode), math.deg(defaultX), math.deg(defaultY), math.deg(defaultZ), wheelKey)
1655 end
1656 end
1657
1658 if wheel.linkNode == nil then
1659 wheel.linkNode = wheel.driveNode
1660 end
1661
1662 wheel.yOffset = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#yOffset", 0.0)
1663 if wheel.yOffset ~= 0 then
1664 -- move drivenode in y direction. Use convert yOffset from driveNode local space to driveNodeParent local space to translate according to directions
1665 setTranslation(wheel.driveNode, localToLocal(wheel.driveNode, getParent(wheel.driveNode), 0, wheel.yOffset, 0))
1666 end
1667
1668 wheel.showSteeringAngle = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#showSteeringAngle", true)
1669 wheel.suspTravel = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#suspTravel", 0.01)
1670 local initialCompression = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#initialCompression")
1671 if initialCompression ~= nil then
1672 wheel.deltaY = (1-initialCompression*0.01)*wheel.suspTravel
1673 else
1674 wheel.deltaY = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#deltaY", 0.0)
1675 end
1676 wheel.spring = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#spring", 0)*Vehicle.SPRING_SCALE
1677
1678 wheel.torque = 0
1679 wheel.brakeFactor = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#brakeFactor", 1)
1680 wheel.autoHoldBrakeFactor = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#autoHoldBrakeFactor", wheel.brakeFactor)
1681
1682 wheel.damperCompressionLowSpeed = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#damperCompressionLowSpeed")
1683 wheel.damperRelaxationLowSpeed = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#damperRelaxationLowSpeed")
1684 if wheel.damperRelaxationLowSpeed == nil then
1685 wheel.damperRelaxationLowSpeed = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#damper", wheel.damperCompressionLowSpeed or 0)
1686 end
1687 -- by default, the high speed relaxation damper is set to 90% of the low speed relaxation damper
1688 wheel.damperRelaxationHighSpeed = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#damperRelaxationHighSpeed", wheel.damperRelaxationLowSpeed * 0.7)
1689
1690 -- by default, we set the low speed compression damper to 90% of the low speed relaxation damper
1691 if wheel.damperCompressionLowSpeed == nil then
1692 wheel.damperCompressionLowSpeed = wheel.damperRelaxationLowSpeed * 0.9
1693 end
1694 -- by default, the high speed compression damper is set to 20% of the low speed compression damper
1695 wheel.damperCompressionHighSpeed = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#damperCompressionHighSpeed", wheel.damperCompressionLowSpeed * 0.2)
1696 wheel.damperCompressionLowSpeedThreshold = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#damperCompressionLowSpeedThreshold", 0.1016) -- default 4 inch / s
1697 wheel.damperRelaxationLowSpeedThreshold = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#damperRelaxationLowSpeedThreshold", 0.1524) -- default 6 inch / s
1698
1699 wheel.forcePointRatio = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#forcePointRatio", 0)
1700 wheel.driveMode = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#driveMode", 0)
1701 wheel.xOffset = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#xOffset", 0)
1702
1703
1704 wheel.transRatio = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#transRatio", 0.0)
1705
1706 wheel.isSynchronized = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#isSynchronized", true)
1707 wheel.tipOcclusionAreaGroupId = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#tipOcclusionAreaGroupId")
1708
1709 wheel.positionX, wheel.positionY, wheel.positionZ = localToLocal(wheel.driveNode, wheel.node, 0, 0, 0)
1710 wheel.useReprDirection = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#useReprDirection", false)
1711 wheel.useDriveNodeDirection = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#useDriveNodeDirection", false)
1712
1713
1714 wheel.mass = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#mass", wheel.mass)
1715 wheel.radius = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#radius", wheel.radius or 0.5)
1716 wheel.width = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#width", wheel.width or 0.6)
1717 wheel.wheelShapeWidth = wheel.width
1718 wheel.widthOffset = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#widthOffset", 0)
1719 wheel.restLoad = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#restLoad", wheel.restLoad or 1.0) -- [t]
1720 wheel.maxLongStiffness = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#maxLongStiffness", wheel.maxLongStiffness or 30.0) -- [t / rad]
1721 wheel.maxLatStiffness = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#maxLatStiffness", wheel.maxLatStiffness or 40.0) -- xml is ratio to restLoad [1/rad], final value is [t / rad]
1722 wheel.maxLatStiffnessLoad = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#maxLatStiffnessLoad", wheel.maxLatStiffnessLoad or 2) -- xml is ratio to restLoad, final value is [t]
1723 wheel.frictionScale = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#frictionScale", wheel.frictionScale or 1.0)
1724 wheel.rotationDamping = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#rotationDamping", wheel.mass * 0.035)
1725 wheel.tireGroundFrictionCoeff = 1.0 -- This will be changed dynamically based on the tire-ground pair
1726
1727 local tireTypeName = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#tireType", "mud")
1728 wheel.tireType = WheelsUtil.getTireType(tireTypeName)
1729 if wheel.tireType == nil then
1730 Logging.xmlWarning(xmlFile, "Failed to find tire type '%s'. Defaulting to 'mud'!", tireTypeName)
1731 wheel.tireType = WheelsUtil.getTireType("mud")
1732 end
1733
1734 local maxWheelSinkDefault = 0.5
1735 if wheel.tireType == WheelsUtil.getTireType("crawler") then
1736 -- crawlers barely sink + avoid clipping of terrain detail with belt shape
1737 maxWheelSinkDefault = 0.01
1738 end
1739
1740 wheel.fieldDirtMultiplier = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#fieldDirtMultiplier", 75)
1741 wheel.streetDirtMultiplier = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#streetDirtMultiplier", -150)
1742 wheel.minDirtPercentage = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#minDirtPercentage", 0.35)
1743 wheel.maxDirtOffset = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#maxDirtOffset", 0.5)
1744 wheel.dirtColorChangeSpeed = 1 / (self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#dirtColorChangeSpeed", 20) * 1000)
1745
1746 wheel.smoothGroundRadius = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#smoothGroundRadius", wheel.smoothGroundRadius or math.max(0.6, wheel.width*0.75))
1747 wheel.versatileYRot = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#versatileYRot", false)
1748 wheel.forceVersatility = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#forceVersatility", false)
1749 wheel.supportsWheelSink = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#supportsWheelSink", true)
1750 wheel.maxWheelSink = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#maxWheelSink", maxWheelSinkDefault)
1751
1752 wheel.rotSpeed = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#rotSpeed", 0)
1753 wheel.rotSpeedNeg = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#rotSpeedNeg", 0)
1754 wheel.rotMax = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#rotMax", 0)
1755 wheel.rotMin = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#rotMin", 0)
1756
1757 wheel.invertRotLimit = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#invertRotLimit", false)
1758 wheel.rotSpeedLimit = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey .. ".physics#rotSpeedLimit")
1759 else
1760 Logging.xmlWarning(xmlFile, "Invalid repr for wheel '%s'. Needs to be a child of a collision!", configKey .. wheelKey)
1761 return false
1762 end
1763
1764 return true
1765end

loadWheelsFromXML

Description
Definition
loadWheelsFromXML()
Code
2451function Wheels:loadWheelsFromXML(xmlFile, key, wheelConfigurationId)
2452 local spec = self.spec_wheels
2453
2454 local i = 0
2455 while true do
2456 local wheelKey = string.format(".wheels.wheel(%d)", i)
2457 if not xmlFile:hasProperty(key .. wheelKey) then
2458 break
2459 end
2460
2461 local wheel = {}
2462 wheel.xmlIndex = i
2463 wheel.updateIndex = (i % 4) + 1
2464 wheel.configIndex = wheelConfigurationId
2465 if self:loadWheelFromXML(xmlFile, key, wheelKey, wheel) then
2466 self:finalizeWheel(wheel)
2467 spec.maxUpdateIndex = math.max(spec.maxUpdateIndex, wheel.updateIndex)
2468
2469 table.insert(spec.wheels, wheel)
2470 end
2471
2472 i = i + 1
2473 end
2474end

loadWheelSharedData

Description
Loads wheel data from vehicle xml OR wheel xml file
Definition
loadWheelSharedData(table xmlFile, string configKey, string wheelKey, table wheel)
Arguments
tablexmlFilexml file object
stringconfigKeyconfigKey
stringwheelKeywheelKey
tablewheelwheel
Return Values
booleansuccesssuccess
Code
1514function Wheels:loadWheelSharedData(xmlFile, configKey, wheelKey, wheel, skipConfigurations)
1515 local configIndex = wheel.configIndex
1516 if skipConfigurations == true then
1517 configIndex = -1
1518 end
1519
1520 local key = "nodeLeft"
1521 if not wheel.isLeft then
1522 key = "nodeRight"
1523 end
1524
1525 wheel.radius = self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey..".physics#radius", wheel.radius)
1526 if wheel.radius == nil then
1527 Logging.xmlWarning(xmlFile, "No radius defined for wheel '%s'! Using default value of 0.5!", configKey .. wheelKey..".physics#radius")
1528 wheel.radius = 0.5
1529 end
1530 wheel.width = self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey..".physics#width", wheel.width)
1531 if wheel.width == nil then
1532 Logging.xmlWarning(xmlFile, "No width defined for wheel '%s'! Using default value of 0.5!", configKey .. wheelKey..".physics#width")
1533 wheel.width = 0.5
1534 end
1535 wheel.mass = self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey..".physics#mass", wheel.mass or 0.1)
1536
1537 local tireTypeName = self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey..".physics#tireType")
1538 if tireTypeName ~= nil then
1539 local tireType = WheelsUtil.getTireType(tireTypeName)
1540 if tireType ~= nil then
1541 wheel.tireType = tireType
1542 else
1543 Logging.xmlWarning(xmlFile, "Tire type '%s' not defined!", tireTypeName)
1544 end
1545 end
1546
1547 wheel.frictionScale = self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey .. ".physics#frictionScale", wheel.frictionScale)
1548 wheel.maxLongStiffness = self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey .. ".physics#maxLongStiffness", wheel.maxLongStiffness) -- [t / rad]
1549 wheel.maxLatStiffness = self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey .. ".physics#maxLatStiffness", wheel.maxLatStiffness) -- xml is ratio to restLoad [1/rad], final value is [t / rad]
1550 wheel.maxLatStiffnessLoad = self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey .. ".physics#maxLatStiffnessLoad", wheel.maxLatStiffnessLoad) -- xml is ratio to restLoad, final value is [t]
1551
1552 wheel.tireTrackAtlasIndex = self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey .. ".tire#tireTrackAtlasIndex", wheel.tireTrackAtlasIndex or 0)
1553 wheel.widthOffset = self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey .. ".tire#widthOffset", wheel.widthOffset or 0.0)
1554 wheel.xOffset = self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey .. ".tire#xOffset", wheel.xOffset or 0)
1555 wheel.maxDeformation = self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey .. ".tire#maxDeformation", wheel.maxDeformation or 0)
1556 wheel.initialDeformation = self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey .. ".tire#initialDeformation", wheel.initialDeformation or math.min(0.04, wheel.maxDeformation * 0.6))
1557 wheel.sideDeformOffset = self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey .. ".tire#sideDeformOffset", wheel.sideDeformOffset or 1.0)
1558
1559 wheel.deformation = 0
1560 wheel.isCareWheel = Utils.getNoNil(self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey .. ".tire#isCareWheel", wheel.isCareWheel), true)
1561 wheel.smoothGroundRadius = self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey .. ".physics#smoothGroundRadius", math.max(0.6, wheel.width*0.75))
1562
1563 self:loadWheelVisualData(xmlFile, configKey, wheelKey, wheel, configIndex)
1564
1565 return true
1566end

loadWheelSteeringData

Description
Loads wheel basics
Definition
loadWheelSteeringData(table xmlFile, string configKey, string wheelKey, table wheel)
Arguments
tablexmlFilexml file object
stringconfigKeyconfigKey
stringwheelKeywheelKey
tablewheelwheel
Return Values
booleansuccesssuccess
Code
1774function Wheels:loadWheelSteeringData(xmlFile, configKey, wheelKey, wheel)
1775 XMLUtil.checkDeprecatedXMLElements(xmlFile, configKey..wheelKey.."#steeringNode", string.format("vehicle.wheels.wheelConfigurations.wheelConfiguration.wheels.wheel(%d).steering#node", wheel.xmlIndex)) -- FS17 to FS19
1776
1777 local steeringKey = wheelKey .. ".steering"
1778 wheel.steeringNode = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, steeringKey .. "#node", nil, self.components, self.i3dMappings)
1779 wheel.steeringRotNode = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, steeringKey .. "#rotNode", nil, self.components, self.i3dMappings)
1780 wheel.steeringNodeMinTransX = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, steeringKey .. "#nodeMinTransX")
1781 wheel.steeringNodeMaxTransX = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, steeringKey .. "#nodeMaxTransX")
1782 wheel.steeringNodeMinRotY = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, steeringKey .. "#nodeMinRotY")
1783 wheel.steeringNodeMaxRotY = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, steeringKey .. "#nodeMaxRotY")
1784
1785 wheel.fenders = {}
1786 local i = 0
1787 while true do
1788 local singleKey = string.format("%s.fender(%d)", wheelKey, i)
1789 local node = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, singleKey .. "#node", nil, self.components, self.i3dMappings)
1790 if node == nil then
1791 break
1792 end
1793
1794 local entry = {}
1795 entry.node = node
1796 entry.rotMax = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, singleKey .. "#rotMax")
1797 entry.rotMin = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, singleKey .. "#rotMin")
1798
1799 table.insert(wheel.fenders, entry)
1800
1801 i = i + 1
1802 end
1803
1804 local steeringAxleKey = wheelKey .. ".steeringAxle"
1805 wheel.steeringAxleScale = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, steeringAxleKey .. "#scale", 0)
1806 wheel.steeringAxleRotMax = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, steeringAxleKey .. "#rotMax", 0)
1807 wheel.steeringAxleRotMin = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, steeringAxleKey .. "#rotMin", -0)
1808
1809 return true
1810end

loadWheelVisualData

Description
Loads visual wheel data from xml file
Definition
loadWheelVisualData(table xmlFile, string configKey, string wheelKey, table wheel)
Arguments
tablexmlFilexml file object
stringconfigKeyconfigKey
stringwheelKeywheelKey
tablewheelwheel
Return Values
booleansuccesssuccess
Code
1575function Wheels:loadWheelVisualData(xmlFile, configKey, wheelKey, wheel, configIndex)
1576 local key = "nodeLeft"
1577 if not wheel.isLeft then
1578 key = "nodeRight"
1579 end
1580
1581 wheel.tireFilename = self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey .. ".tire#filename", wheel.tireFilename)
1582 wheel.tireIsInverted = self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey .. ".tire#isInverted", wheel.tireIsInverted)
1583 wheel.tireNodeStr = self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey .. ".tire#node")
1584 or self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey .. ".tire#"..key, wheel.tireNodeStr)
1585
1586 wheel.outerRimFilename = self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey .. ".outerRim#filename", wheel.outerRimFilename)
1587 wheel.outerRimNodeStr = self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey .. ".outerRim#node", nil)
1588 or self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey .. ".outerRim#"..key, wheel.outerRimNodeStr)
1589 or "0|0"
1590 wheel.outerRimWidthAndDiam = self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey .. ".outerRim#widthAndDiam", wheel.outerRimWidthAndDiam, true)
1591 wheel.outerRimScale = self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey .. ".outerRim#scale", wheel.outerRimScale, true)
1592
1593 wheel.innerRimFilename = self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey .. ".innerRim#filename", wheel.innerRimFilename)
1594 wheel.innerRimNodeStr = self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey .. ".innerRim#node", nil)
1595 or self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey .. ".innerRim#"..key, wheel.innerRimNodeStr)
1596 wheel.innerRimWidthAndDiam = self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey .. ".innerRim#widthAndDiam", wheel.innerRimWidthAndDiam, true)
1597 wheel.innerRimOffset = self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey .. ".innerRim#offset", wheel.innerRimOffset) or 0
1598 wheel.innerRimScale = self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey .. ".innerRim#scale", wheel.innerRimScale, true)
1599
1600 wheel.additionalFilename = self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey .. ".additional#filename", wheel.additionalFilename)
1601 wheel.additionalNodeStr = self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey .. ".additional#node", nil)
1602 or self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey .. ".additional#"..key, wheel.additionalNodeStr)
1603 wheel.additionalOffset = self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey .. ".additional#offset", wheel.additionalOffset) or 0
1604 wheel.additionalScale = self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey .. ".additional#scale", wheel.additionalScale, true)
1605 wheel.additionalMass = self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey .. ".additional#mass", wheel.additionalMass) or 0
1606 wheel.additionalWidthAndDiam = self:getWheelConfigurationValue(xmlFile, configIndex, configKey, wheelKey .. ".additional#widthAndDiam", wheel.additionalWidthAndDiam, true)
1607end

onAdditionalWheelConnectorI3DLoaded

Description
Called when wheel connector i3d was loaded
Definition
onAdditionalWheelConnectorI3DLoaded(integer i3dNode, table args)
Arguments
integeri3dNodei3dNode of wheel chock
tableargsarguments
Code
2842function Wheels:onAdditionalWheelConnectorI3DLoaded(i3dNode, failedReason, args)
2843 if i3dNode ~= 0 then
2844 local wheel = args.wheel
2845 local connector = args.connector
2846 local diameter = args.diameter
2847 local baseWheelWidth = args.baseWheelWidth
2848 local wheelDistance = args.wheelDistance
2849 local offsetDir = args.offsetDir
2850 local dualWheelWidth = args.dualWheelWidth
2851 local filename = args.filename
2852
2853 local node = I3DUtil.indexToObject(i3dNode, connector.nodeStr, self.i3dMappings)
2854 if node ~= nil then
2855 connector.node = node
2856 connector.linkNode = wheel.wheelTire
2857 connector.filename = filename
2858
2859 link(wheel.driveNode, connector.node)
2860
2861 if not connector.useWidthAndDiam then
2862 if getHasShaderParameter(connector.node, "connectorPos") then
2863 I3DUtil.setShaderParameterRec(connector.node, "connectorPos", 0, baseWheelWidth, wheelDistance, dualWheelWidth, false)
2864 end
2865
2866 local x,_,z,w = I3DUtil.getShaderParameterRec(connector.node, "widthAndDiam")
2867 I3DUtil.setShaderParameterRec(connector.node, "widthAndDiam", x, diameter, z, w, false)
2868 else
2869 local connectorOffset = offsetDir*(((0.5*baseWheelWidth + 0.5*wheelDistance) * 0.0254) + connector.additionalOffset) -- in meters
2870 local connectorDiameter = connector.diameter or diameter
2871 setTranslation(connector.node, connectorOffset, 0, 0)
2872 I3DUtil.setShaderParameterRec(connector.node, "widthAndDiam", connector.width, connectorDiameter, 0, 0, false)
2873 end
2874 if connector.usePosAndScale and getHasShaderParameter(connector.node, "connectorPosAndScale") then
2875 local _,_,_,w = I3DUtil.getShaderParameterRec(connector.node, "connectorPosAndScale")
2876 I3DUtil.setShaderParameterRec(connector.node, "connectorPosAndScale", connector.startPos, connector.endPos, connector.scale, w, false)
2877 end
2878 if connector.color ~= nil and getHasShaderParameter(connector.node, "colorMat0") then
2879 local r, g, b, mat1 = unpack(connector.color)
2880 local _, _, _, mat2 = I3DUtil.getShaderParameterRec(connector.node, "colorMat0")
2881 I3DUtil.setShaderParameterRec(connector.node, "colorMat0", r, g, b, mat1 or mat2, false)
2882 end
2883 end
2884
2885 delete(i3dNode)
2886 end
2887end

onDelete

Description
Definition
onDelete()
Code
791function Wheels:onDelete()
792 local spec = self.spec_wheels
793
794 if spec.sharedLoadRequestIds ~= nil then
795 for _, sharedLoadRequestId in pairs(spec.sharedLoadRequestIds) do
796 g_i3DManager:releaseSharedI3DFile(sharedLoadRequestId)
797 end
798 end
799
800 if spec.hubs ~= nil then
801 for _, hub in pairs(spec.hubs) do
802 delete(hub.node)
803 end
804 end
805
806 if spec.wheels ~= nil then
807 for _,wheel in pairs(spec.wheels) do
808
809 if wheel.driveGroundParticleSystems ~= nil then
810 for _, ps in pairs(wheel.driveGroundParticleSystems) do
811 ParticleUtil.deleteParticleSystems(ps)
812 end
813 end
814
815 if wheel.additionalWheels ~= nil then
816 for _, additionalWheel in pairs(wheel.additionalWheels) do
817 if additionalWheel.driveGroundParticleSystems ~= nil then
818 for _, ps in pairs(additionalWheel.driveGroundParticleSystems) do
819 ParticleUtil.deleteParticleSystems(ps)
820 end
821 end
822 end
823 end
824 end
825 end
826
827 if spec.tireTrackNodes ~= nil then
828 for i=1, #spec.tireTrackNodes do
829 local tireTrackNode = spec.tireTrackNodes[i]
830 self.tireTrackSystem:destroyTrack(tireTrackNode.tireTrackIndex)
831 end
832 end
833
834 if spec.wheelChocks ~= nil then
835 for _, wheelChock in pairs(spec.wheelChocks) do
836 if wheelChock.node ~= nil then
837 delete(wheelChock.node)
838 wheelChock.node = nil
839 end
840 end
841 end
842
843 g_soundManager:deleteSamples(spec.surfaceSounds)
844
845 spec.snowSystem = nil
846 spec.fieldGroundSystem = nil
847end

onLeaveVehicle

Description
Definition
onLeaveVehicle()
Code
3970function Wheels:onLeaveVehicle()
3971 local spec = self.spec_wheels
3972 if self.isServer and self.isAddedToPhysics then
3973 for _,wheel in pairs(spec.wheels) do
3974 setWheelShapeProps(wheel.node, wheel.wheelShape, 0, self:getBrakeForce()*wheel.brakeFactor, wheel.steeringAngle, wheel.rotationDamping)
3975 end
3976 end
3977end

onLoad

Description
Definition
onLoad()
Code
542function Wheels:onLoad(savegame)
543 local spec = self.spec_wheels
544
545 spec.sharedLoadRequestIds = {}
546
547 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.driveGroundParticleSystems", "vehicle.wheels.wheelConfigurations.wheelConfiguration.wheels.wheel#hasParticles") --FS13 to FS15
548
549 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.wheelConfigurations.wheelConfiguration", "vehicle.wheels.wheelConfigurations.wheelConfiguration") --FS17 to FS19
550 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.rimColor", "vehicle.wheels.rimColor") --FS17 to FS19
551 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.hubColor", "vehicle.wheels.hubs.color0") --FS17 to FS19
552 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.dynamicallyLoadedWheels", "vehicle.wheels.dynamicallyLoadedWheels") --FS17 to FS19
553 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.ackermannSteeringConfigurations", "vehicle.wheels.ackermannSteeringConfigurations") --FS17 to FS19
554 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.wheels.wheel", "vehicle.wheels.wheelConfigurations.wheelConfiguration.wheels.wheel") --FS17 to FS19
555 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.wheels.wheel#repr", "vehicle.wheels.wheelConfigurations.wheelConfiguration.wheels.wheel.physics#repr") --FS17 to FS19
556 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.wheelConfigurations.wheelConfiguration.wheels.wheel#repr", "vehicle.wheels.wheelConfigurations.wheelConfiguration.wheels.wheel.physics#repr") --FS17 to FS19
557 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.wheels.wheelConfigurations.wheelConfiguration.wheels.wheel#repr", "vehicle.wheels.wheelConfigurations.wheelConfiguration.wheels.wheel.physics#repr") --FS17 to FS19
558 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.wheels.wheelConfigurations.wheelConfiguration.wheels.wheel#configIndex", "vehicle.wheels.wheelConfigurations.wheelConfiguration.wheels.wheel#configId") --FS17 to FS19
559
560 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, "vehicle.ackermannSteering", "vehicle.wheels.ackermannSteeringConfigurations.ackermannSteering") --FS19 to FS21
561
562 spec.configurationSaveIdToIndex, spec.configurationIndexToBaseConfig = Wheels.createConfigSaveIdMapping(self.xmlFile)
563 spec.lastWheelConfigIndex = self.configurations["wheel"]
564
565 -- wheel setup
566 local wheelConfigurationId = self.configurations["wheel"] or 1
567 local configKey = string.format("vehicle.wheels.wheelConfigurations.wheelConfiguration(%d)", wheelConfigurationId - 1)
568 local wheelsKey = configKey .. ".wheels"
569 if self.configurations["wheel"] ~= nil and not self.xmlFile:hasProperty(wheelsKey) then
570 Logging.xmlWarning(self.xmlFile, "Invalid wheelConfigurationId '%d'. Using default wheel config instead!", self.configurations["wheel"])
571
572 -- reset to wheel config 1 to ensure wheels are present
573 wheelConfigurationId = 1
574 configKey = "vehicle.wheels.wheelConfigurations.wheelConfiguration(0)"
575 wheelsKey = configKey .. ".wheels"
576 end
577 ObjectChangeUtil.updateObjectChanges(self.xmlFile, "vehicle.wheels.wheelConfigurations.wheelConfiguration", wheelConfigurationId , self.components, self)
578
579 -- load configuration independent settings
580 spec.rimColor = self.xmlFile:getValue("vehicle.wheels.rimColor", nil, true)
581 if spec.rimColor == nil then
582 if self.xmlFile:getValue("vehicle.wheels.rimColor#useBaseColor") then
583 spec.rimColor = ConfigurationUtil.getColorByConfigId(self, "baseColor", self.configurations["baseColor"]) or ConfigurationUtil.getColorByConfigId(self, "baseMaterial", self.configurations["baseMaterial"])
584 end
585 end
586
587 if spec.rimColor ~= nil then
588 -- overwrite material from color string with nil unless explicitly defined in material attribute
589 spec.rimColor[4] = self.xmlFile:getValue("vehicle.wheels.rimColor#material")
590 end
591
592 -- load overwritten rim and hub colors from material definitions in all configurations
593 spec.overwrittenWheelColors = {}
594 spec.overwrittenWheelColors["_rimColor"] = {}
595 for i=1, 8 do
596 spec.overwrittenWheelColors[string.format("_hubColor%d", i - 1)] = {}
597 end
598 ConfigurationUtil.getOverwrittenMaterialColors(self, self.xmlFile, spec.overwrittenWheelColors)
599
600 local overwrittenRimColor = spec.overwrittenWheelColors["_rimColor"]
601 if #overwrittenRimColor > 0 then
602 if spec.rimColor == nil then
603 spec.rimColor = overwrittenRimColor
604 else
605 spec.rimColor[1], spec.rimColor[2], spec.rimColor[3] = overwrittenRimColor[1], overwrittenRimColor[2], overwrittenRimColor[3]
606 if #overwrittenRimColor == 4 then
607 spec.rimColor[4] = overwrittenRimColor[4]
608 end
609 end
610 end
611
612 -- load hubs to hubs/repr nodes
613 self:loadHubsFromXML()
614
615 self.maxRotTime = 0
616 self.minRotTime = 0
617 self.rotatedTimeInterpolator = InterpolatorValue.new(0)
618
619 self.autoRotateBackSpeed = self:getWheelConfigurationValue(self.xmlFile, wheelConfigurationId, configKey, ".wheels#autoRotateBackSpeed", 1.0)
620 self.speedDependentRotateBack = self:getWheelConfigurationValue(self.xmlFile, wheelConfigurationId, configKey, ".wheels#speedDependentRotateBack", true)
621 self.differentialIndex = self:getWheelConfigurationValue(self.xmlFile, wheelConfigurationId, configKey, ".wheels#differentialIndex") -- needed by Drivable
622 spec.ackermannSteeringIndex = self:getWheelConfigurationValue(self.xmlFile, wheelConfigurationId, configKey, ".wheels#ackermannSteeringIndex")
623
624 spec.wheelSmoothAccumulation = 0
625
626 spec.wheelCreationTimer = 0
627 spec.currentUpdateIndex = 1
628 spec.maxUpdateIndex = 1
629
630 spec.wheels = {}
631 spec.wheelsByNode = {}
632 spec.wheelChocks = {}
633
634 spec.tireTrackNodes = {}
635
636 -- load wheels
637 self:loadWheelsFromXML(self.xmlFile, configKey, wheelConfigurationId)
638
639 --load surface sounds
640 if self.xmlFile:getValue(wheelsKey.."#hasSurfaceSounds", true) then
641 local surfaceSoundLinkNode = self.xmlFile:getValue(wheelsKey .. "#surfaceSoundLinkNode", self.components[1].node, self.components, self.i3dMappings)
642
643 local tireTypeName = ""
644 if #spec.wheels > 0 and spec.wheels[1].tireType ~= nil then
645 tireTypeName = WheelsUtil.getTireTypeName(spec.wheels[1].tireType)
646 end
647 tireTypeName = self.xmlFile:getValue(wheelsKey.."#surfaceSoundTireType", tireTypeName)
648
649 spec.surfaceSounds = {}
650 spec.surfaceIdToSound = {}
651 spec.surfaceNameToSound = {}
652 spec.currentSurfaceSound = nil
653
654 local function addSurfaceSound(surfaceSound)
655 local sample = g_soundManager:cloneSample(surfaceSound.sample, surfaceSoundLinkNode, self)
656 sample.sampleName = surfaceSound.name
657
658 table.insert(spec.surfaceSounds, sample)
659 spec.surfaceIdToSound[surfaceSound.materialId] = sample
660 spec.surfaceNameToSound[surfaceSound.name] = sample
661 end
662
663 local surfaceSounds = g_currentMission.surfaceSounds
664 for j=1, #surfaceSounds do
665 local surfaceSound = surfaceSounds[j]
666 if surfaceSound.type:lower() == ("wheel_" .. tireTypeName):lower() then
667 addSurfaceSound(surfaceSound)
668 end
669 end
670
671 for j=1, #surfaceSounds do
672 local surfaceSound = surfaceSounds[j]
673 if spec.surfaceNameToSound[surfaceSound.name] == nil then
674 if surfaceSound.type == "wheel" then
675 addSurfaceSound(surfaceSound)
676 end
677 end
678 end
679 end
680
681 -- load non physical wheels
682 spec.dynamicallyLoadedWheels = {}
683 local i = 0
684 while true do
685 local baseName = string.format("vehicle.wheels.dynamicallyLoadedWheels.dynamicallyLoadedWheel(%d)", i)
686 if not self.xmlFile:hasProperty(baseName) then
687 break
688 end
689
690 XMLUtil.checkDeprecatedXMLElements(self.xmlFile, baseName .. "#configIndex", baseName .. "#configId") --FS17 to FS19
691
692 local dynamicallyLoadedWheel = {}
693 if self:loadNonPhysicalWheelFromXML(dynamicallyLoadedWheel, self.xmlFile, baseName) then
694 table.insert(spec.dynamicallyLoadedWheels, dynamicallyLoadedWheel)
695 end
696
697 i = i + 1
698 end
699
700 spec.networkTimeInterpolator = InterpolationTime.new(1.2)
701
702 -- find opposite wheel
703 local numWheels = #spec.wheels
704 for iWheel=1,numWheels do
705 local wheel1 = spec.wheels[iWheel]
706 if wheel1.oppositeWheelIndex == nil then
707 for jWheel=1,numWheels do
708 if iWheel ~= jWheel then
709 local wheel2 = spec.wheels[jWheel]
710 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
711 wheel1.oppositeWheelIndex = jWheel
712 wheel2.oppositeWheelIndex = iWheel
713 break
714 end
715 end
716 end
717 end
718 end
719
720 --
721 self:loadAckermannSteeringFromXML(self.xmlFile, spec.ackermannSteeringIndex)
722
723
724 SpecializationUtil.raiseEvent(self, "onFinishedWheelLoading", self.xmlFile, wheelsKey)
725
726 spec.wheelSinkActive = Platform.gameplay.wheelSink
727 spec.wheelDensityHeightSmoothActive = Platform.gameplay.wheelDensityHeightSmooth
728 spec.wheelVisualPressureActive = Platform.gameplay.wheelVisualPressure
729
730 spec.tyreTracksSegmentsCoeff = getTyreTracksSegmentsCoeff()
731
732 spec.snowSystem = g_currentMission.snowSystem
733 spec.fieldGroundSystem = g_currentMission.fieldGroundSystem
734 spec.tireTrackGroundGrassValue = spec.fieldGroundSystem:getFieldGroundValue(FieldGroundType.GRASS)
735 spec.tireTrackGroundGrassCutValue = spec.fieldGroundSystem:getFieldGroundValue(FieldGroundType.GRASS_CUT)
736
737 spec.brakePedal = 0
738 spec.forceIsActiveTime = 3000
739 spec.forceIsActiveTimer = 0
740 spec.dirtyFlag = self:getNextDirtyFlag()
741
742 g_messageCenter:subscribe(MessageType.SNOW_HEIGHT_CHANGED, self.onWheelSnowHeightChanged, self)
743end

onLoadFinished

Description
Definition
onLoadFinished()
Code
747function Wheels:onLoadFinished(savegame)
748 self:updateWheelChocksPosition(nil, true)
749
750 -- add wheel masses to default vehicle mass
751 if self.isServer then
752 local spec = self.spec_wheels
753 for _, wheel in pairs(spec.wheels) do
754 self.defaultMass = self.defaultMass + wheel.mass
755
756 if wheel.wheelTire ~= nil and wheel.wheelShapeWidthTmp ~= nil then
757 local wheelX, _, _ = getTranslation(wheel.wheelTire)
758
759 local additionalWheelX = wheelX + wheel.wheelShapeWidthTotalOffset
760 wheel.wheelShapeWidth = wheel.wheelShapeWidthTmp + math.abs(wheelX-additionalWheelX)
761 wheel.wheelShapeWidthTmp = nil
762 setWheelShapeWidth(wheel.node, wheel.wheelShape, wheel.wheelShapeWidth, wheel.widthOffset)
763 end
764 end
765
766 if savegame ~= nil and not savegame.resetVehicles then
767 local lastWheelConfiguration = savegame.xmlFile:getValue(savegame.key ..".wheels#lastWheelConfiguration", 1)
768 if lastWheelConfiguration ~= self.configurations["wheel"] then
769 for _, wheel in pairs(spec.wheels) do
770 local washableNode = self:getWashableNodeByCustomIndex(wheel)
771 if washableNode ~= nil then
772 self:setNodeDirtAmount(washableNode, 0, true)
773 end
774 end
775
776 SpecializationUtil.raiseEvent(self, "onWheelConfigurationChanged", lastWheelConfiguration, self.configurations["wheel"])
777 end
778 end
779 end
780end

onPostAttachImplement

Description
Definition
onPostAttachImplement()
Code
4129function Wheels:onPostAttachImplement(object, inputJointDescIndex, jointDescIndex)
4130 -- raise onBrake event again, so the brake force of the new implement is updated, even if the attacherVehicle has no brake pedal change
4131 SpecializationUtil.raiseEvent(self, "onBrake", self.spec_wheels.brakePedal)
4132end

onPostDetach

Description
Definition
onPostDetach()
Code
3987function Wheels:onPostDetach()
3988 self:updateWheelChocksPosition(false, true)
3989end

onPostUpdate

Description
Definition
onPostUpdate()
Code
1146function Wheels:onPostUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
1147 if self.isServer then
1148 local spec = self.spec_wheels
1149 for _,wheel in ipairs(spec.wheels) do
1150 if wheel.isPositionDirty then
1151 self:updateWheelBase(wheel)
1152 wheel.isPositionDirty = false
1153 end
1154 if wheel.isFrictionDirty then
1155 self:updateWheelTireFriction(wheel)
1156 wheel.isFrictionDirty = false
1157 end
1158 end
1159 end
1160end

onPreAttach

Description
Definition
onPreAttach()
Code
3981function Wheels:onPreAttach()
3982 self:updateWheelChocksPosition(true, false)
3983end

onReadStream

Description
Definition
onReadStream()
Code
851function Wheels:onReadStream(streamId, connection)
852 if connection.isServer then
853 local spec = self.spec_wheels
854 spec.networkTimeInterpolator:reset()
855 for i=1, #spec.wheels do
856 local wheel = spec.wheels[i]
857 if wheel.isSynchronized then
858 self:readWheelDataFromStream(wheel, streamId, true)
859 end
860 end
861
862 self.rotatedTimeInterpolator:setValue(0)
863 end
864end

onReadUpdateStream

Description
Definition
onReadUpdateStream()
Code
882function Wheels:onReadUpdateStream(streamId, timestamp, connection)
883 if connection.isServer then
884 local hasUpdate = streamReadBool(streamId)
885 if hasUpdate then
886 local spec = self.spec_wheels
887 spec.networkTimeInterpolator:startNewPhaseNetwork()
888
889 for i=1, #spec.wheels do
890 local wheel = spec.wheels[i]
891 if wheel.isSynchronized then
892 self:readWheelDataFromStream(wheel, streamId, false)
893 end
894 end
895
896 if self.maxRotTime ~= 0 and self.minRotTime ~= 0 then
897 local rotatedTimeRange = math.max(self.maxRotTime - self.minRotTime, 0.001)
898 local rotatedTime = streamReadUIntN(streamId, 8)
899 -- set to 0 due to inaccuracy
900 if math.abs(self.rotatedTime) < 0.001 then
901 self.rotatedTime = 0
902 end
903
904 local rotatedTimeTarget = rotatedTime / 255 * rotatedTimeRange + self.minRotTime
905 self.rotatedTimeInterpolator:setTargetValue(rotatedTimeTarget)
906 end
907 end
908 end
909end

onRegisterAnimationValueTypes

Description
Called on pre load to register animation value types
Definition
onRegisterAnimationValueTypes()
Code
4052function Wheels:onRegisterAnimationValueTypes()
4053 self:registerAnimationValueType("steeringAngle", "startSteeringAngle", "endSteeringAngle", false, AnimationValueFloat,
4054 function(value, xmlFile, xmlKey)
4055 value.wheelIndex = xmlFile:getValue(xmlKey .. "#wheelIndex")
4056
4057 if value.wheelIndex ~= nil then
4058 value:setWarningInformation("wheelIndex: " .. value.wheelIndex)
4059 value:addCompareParameters("wheelIndex")
4060
4061 return true
4062 end
4063
4064 return false
4065 end,
4066
4067 function(value)
4068 if value.wheelIndex ~= nil then
4069 if value.wheel == nil then
4070 value.wheel = self:getWheelFromWheelIndex(value.wheelIndex)
4071 if value.wheel == nil then
4072 Logging.xmlWarning(self.xmlFile, "Unknown wheel index '%s' for animation part.", value.wheelIndex)
4073 value.wheelIndex = nil
4074 return 0
4075 end
4076 end
4077
4078 return value.wheel.steeringAngle
4079 end
4080
4081 return 0
4082 end,
4083
4084 function(value, steeringAngle)
4085 if value.wheel ~= nil then
4086 value.wheel.steeringAngle = steeringAngle
4087 end
4088 end)
4089
4090 self:registerAnimationValueType("brakeFactor", "startBrakeFactor", "endBrakeFactor", false, AnimationValueFloat,
4091 function(value, xmlFile, xmlKey)
4092 value.wheelIndex = xmlFile:getValue(xmlKey .. "#wheelIndex")
4093
4094 if value.wheelIndex ~= nil then
4095 value:setWarningInformation("wheelIndex: " .. value.wheelIndex)
4096 value:addCompareParameters("wheelIndex")
4097
4098 return true
4099 end
4100
4101 return false
4102 end,
4103
4104 function(value)
4105 if value.wheel == nil then
4106 local wheel = value.vehicle:getWheelFromWheelIndex(value.wheelIndex)
4107 if wheel == nil then
4108 Logging.xmlWarning(self.xmlFile, "Unknown wheel index '%s' for animation part.", value.wheelIndex)
4109 value.startValue = nil
4110 return 0
4111 end
4112
4113 value.wheel = wheel
4114 end
4115
4116 return value.wheel.brakeFactor
4117 end,
4118
4119 function(value, brakeFactor)
4120 if value.wheel ~= nil then
4121 value.wheel.brakeFactor = brakeFactor
4122 WheelsUtil.updateWheelPhysics(self, value.wheel, self.spec_wheels.brakePedal, 16.66)
4123 end
4124 end)
4125end

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
1004function Wheels:onUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
1005 local spec = self.spec_wheels
1006
1007 if self.isServer then
1008 if spec.wheelCreationTimer > 0 then
1009 spec.wheelCreationTimer = spec.wheelCreationTimer - 1
1010 if spec.wheelCreationTimer == 0 then
1011 for _,wheel in pairs(spec.wheels) do
1012 wheel.wheelShapeCreated = true
1013 end
1014 end
1015 end
1016 end
1017
1018 -- interpolation of wheel properties
1019 if not self.isServer and self.isClient then
1020 spec.networkTimeInterpolator:update(dt)
1021 local interpolationAlpha = spec.networkTimeInterpolator:getAlpha()
1022
1023 self.rotatedTime = self.rotatedTimeInterpolator:getInterpolatedValue(interpolationAlpha)
1024
1025 for i=1, table.getn(spec.wheels) do
1026 local wheel = spec.wheels[i]
1027 wheel.netInfo.x, wheel.netInfo.y, wheel.netInfo.z = wheel.networkInterpolators.position:getInterpolatedValues(interpolationAlpha)
1028 wheel.netInfo.xDrive = wheel.networkInterpolators.xDrive:getInterpolatedValue(interpolationAlpha)
1029 wheel.netInfo.suspensionLength = wheel.networkInterpolators.suspensionLength:getInterpolatedValue(interpolationAlpha)
1030
1031 if wheel.driveGroundParticleSystems ~= nil then
1032 for _, typedPs in pairs(wheel.driveGroundParticleSystems) do
1033 for _, ps in ipairs(typedPs) do
1034 setTranslation(ps.emitterShape, wheel.netInfo.x + ps.offsets[1], wheel.netInfo.y + ps.offsets[2], wheel.netInfo.z + ps.offsets[3])
1035 end
1036 end
1037 end
1038 end
1039
1040 if spec.networkTimeInterpolator:isInterpolating() then
1041 self:raiseActive()
1042 end
1043 end
1044
1045 local numWheels = #spec.wheels
1046 if self.finishedFirstUpdate then
1047 local groundWetness = g_currentMission.environment.weather:getGroundWetness()
1048
1049 for i=1, numWheels do
1050 local wheel = spec.wheels[i]
1051
1052 if self.isActive then
1053 if spec.currentUpdateIndex == wheel.updateIndex then
1054 self:updateWheelContact(wheel)
1055 end
1056
1057 if spec.wheelSinkActive then
1058 self:updateWheelSink(wheel, dt, groundWetness)
1059 end
1060 self:updateWheelFriction(wheel, dt, groundWetness)
1061 if spec.wheelDensityHeightSmoothActive then
1062 self:updateWheelDensityMapHeight(wheel, dt)
1063 end
1064
1065 WheelsUtil.updateWheelPhysics(self, wheel, spec.brakePedal, dt)
1066 end
1067
1068 if self.isServer and self.isAddedToPhysics then
1069 WheelsUtil.updateWheelNetInfo(self, wheel)
1070 end
1071
1072 if self.currentUpdateDistance < Wheels.VISUAL_WHEEL_UPDATE_DISTANCE then
1073 local changed = WheelsUtil.updateWheelGraphics(self, wheel, dt)
1074 if wheel.updateWheelChock and changed then
1075 for j=1, #wheel.wheelChocks do
1076 self:updateWheelChockPosition(wheel.wheelChocks[j], false)
1077 end
1078 end
1079 end
1080 end
1081
1082 spec.currentUpdateIndex = spec.currentUpdateIndex + 1
1083 if spec.currentUpdateIndex > spec.maxUpdateIndex then
1084 spec.currentUpdateIndex = 1
1085 end
1086
1087 if self.isActive then
1088 local numTireTrackNodes = #spec.tireTrackNodes
1089 if numTireTrackNodes > 0 then
1090 local allowTireTracks = self:getAllowTireTracks()
1091 for i=1, numTireTrackNodes do
1092 local tireTrackNode = spec.tireTrackNodes[i]
1093 self:updateTireTrackNode(tireTrackNode, allowTireTracks, groundWetness)
1094 end
1095 end
1096
1097 if numWheels > 0 then
1098 if g_currentMission.missionInfo.fruitDestruction
1099 and not self:getIsAIActive()
1100 and (self.getBlockFoliageDestruction == nil or not self:getBlockFoliageDestruction()) then
1101
1102 for i=1, numWheels do
1103 local wheel = spec.wheels[i]
1104 self:updateWheelDestruction(wheel, dt)
1105 end
1106 end
1107 end
1108 end
1109
1110 if self:getAreSurfaceSoundsActive() then
1111 -- update surface sounds
1112 if spec.surfaceSounds ~= nil then
1113 local currentSound = self:getCurrentSurfaceSound()
1114 if currentSound ~= spec.currentSurfaceSound then
1115 if spec.currentSurfaceSound ~= nil then
1116 g_soundManager:stopSample(spec.currentSurfaceSound)
1117 end
1118 if currentSound ~= nil then
1119 g_soundManager:playSample(currentSound)
1120 end
1121
1122 spec.currentSurfaceSound = currentSound
1123 else
1124 if not g_soundManager:getIsSamplePlaying(currentSound) then
1125 g_soundManager:playSample(currentSound)
1126 end
1127 end
1128 end
1129 else
1130 if spec.currentSurfaceSound ~= nil then
1131 g_soundManager:stopSample(spec.currentSurfaceSound)
1132 end
1133 end
1134 end
1135
1136 if numWheels > 0 then
1137 if self.isServer then
1138 self:raiseDirtyFlags(spec.dirtyFlag)
1139 end
1140 end
1141end

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
1291function Wheels:onUpdateEnd(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
1292 if self.isClient then
1293 local spec = self.spec_wheels
1294 for _, wheel in pairs(spec.wheels) do
1295 if wheel.driveGroundParticleSystems ~= nil then
1296 for _, typedPs in pairs(wheel.driveGroundParticleSystems) do
1297 for _, ps in ipairs(typedPs) do
1298 ParticleUtil.setEmittingState(ps, false)
1299 end
1300 end
1301 end
1302 end
1303
1304 if spec.currentSurfaceSound ~= nil then
1305 g_soundManager:stopSample(spec.currentSurfaceSound)
1306 spec.currentSurfaceSound = nil
1307 end
1308 end
1309end

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
1168function Wheels:onUpdateTick(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
1169 local spec = self.spec_wheels
1170
1171 for _, wheel in pairs(spec.wheels) do
1172 if wheel.rotSpeedLimit ~= nil then
1173 local dir = -1
1174 if self:getLastSpeed() <= wheel.rotSpeedLimit then
1175 dir = 1
1176 end
1177
1178 wheel.currentRotSpeedAlpha = MathUtil.clamp(wheel.currentRotSpeedAlpha + dir*(dt/1000), 0, 1)
1179 wheel.rotSpeed = wheel.rotSpeedDefault * wheel.currentRotSpeedAlpha
1180 wheel.rotSpeedNeg = wheel.rotSpeedNegDefault * wheel.currentRotSpeedAlpha
1181 end
1182 end
1183
1184 if self.isClient then
1185 local speed = self:getLastSpeed()
1186 local groundWetness = g_currentMission.environment.weather:getGroundWetness()
1187 local groundIsWet = groundWetness > 0.2
1188
1189 for _,wheel in pairs(spec.wheels) do
1190 if wheel.driveGroundParticleSystems ~= nil then
1191 local states = wheel.driveGroundParticleStates
1192 local enableSoilPS = false
1193 if wheel.lastTerrainValue > 0 and wheel.lastTerrainValue < 9 then
1194 enableSoilPS = (speed > 1) -- and wheel.sink > 0
1195 end
1196
1197 local sizeScale = 2 * wheel.width * wheel.radiusOriginal
1198
1199 states.wheel_dry = not wheel.hasSnowContact and enableSoilPS
1200 states.wheel_wet = not wheel.hasSnowContact and enableSoilPS and groundIsWet
1201 states.wheel_dust = not wheel.hasSnowContact and not groundIsWet
1202 states.wheel_snow = wheel.hasSnowContact
1203
1204 for psName, state in pairs(states) do
1205 local typedPs = wheel.driveGroundParticleSystems[psName]
1206 if typedPs ~= nil then
1207 for _, ps in ipairs(typedPs) do
1208 if state then
1209 if self.movingDirection < 0 then
1210 setRotation(ps.emitterShape, 0, math.pi+wheel.steeringAngle, 0)
1211 else
1212 setRotation(ps.emitterShape, 0, wheel.steeringAngle, 0)
1213 end
1214
1215 local scale
1216 if psName ~= "wheel_dust" then
1217 local wheelSpeed = MathUtil.rpmToMps(wheel.netInfo.xDriveSpeed / (2*math.pi) * 60, wheel.radius)
1218 local wheelSlip = math.pow(wheelSpeed/self.lastSpeedReal, 2.5)
1219 scale = self:getDriveGroundParticleSystemsScale(ps, wheelSpeed) * wheelSlip
1220 else
1221 scale = self:getDriveGroundParticleSystemsScale(ps, self.lastSpeedReal)
1222 end
1223
1224 if ps.isTintable then
1225 -- interpolate between different ground colors to avoid unrealisitic particle color changes
1226 if ps.lastColor == nil then
1227 ps.lastColor = {ps.wheel.lastColor[1],ps.wheel.lastColor[2],ps.wheel.lastColor[3]}
1228 ps.targetColor = {ps.wheel.lastColor[1],ps.wheel.lastColor[2],ps.wheel.lastColor[3]}
1229 ps.currentColor = {ps.wheel.lastColor[1],ps.wheel.lastColor[2],ps.wheel.lastColor[3]}
1230 ps.alpha = 1
1231 end
1232
1233 if ps.alpha ~= 1 then
1234 ps.alpha = math.min(ps.alpha + dt/1000, 1)
1235 ps.currentColor = {MathUtil.vector3ArrayLerp(ps.lastColor, ps.targetColor, ps.alpha)}
1236 if ps.alpha == 1 then
1237 ps.lastColor[1] = ps.currentColor[1]
1238 ps.lastColor[2] = ps.currentColor[2]
1239 ps.lastColor[3] = ps.currentColor[3]
1240 end
1241 end
1242
1243 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
1244 ps.alpha = 0
1245 ps.targetColor[1] = ps.wheel.lastColor[1]
1246 ps.targetColor[2] = ps.wheel.lastColor[2]
1247 ps.targetColor[3] = ps.wheel.lastColor[3]
1248 end
1249 end
1250
1251 if scale > 0 then
1252 ParticleUtil.setEmittingState(ps, true)
1253 if ps.isTintable then
1254 I3DUtil.setShaderParameterRec(ps.shape, "colorAlpha", ps.currentColor[1], ps.currentColor[2], ps.currentColor[3], 1, false)
1255 end
1256 else
1257 ParticleUtil.setEmittingState(ps, false)
1258 end
1259
1260 -- emit count
1261 local maxSpeed = (50 / 3.6)
1262 local circum = wheel.radiusOriginal
1263 local maxWheelRpm = maxSpeed / circum
1264 local wheelRotFactor = Utils.getNoNil(wheel.netInfo.xDriveSpeed, 0) / maxWheelRpm
1265 local emitScale = scale * wheelRotFactor * sizeScale
1266 ParticleUtil.setEmitCountScale(ps, MathUtil.clamp(emitScale, ps.minScale, ps.maxScale))
1267
1268 -- speeds
1269 local speedFactor = 1.0
1270 ParticleUtil.setParticleSystemSpeed(ps, ps.particleSpeed * speedFactor)
1271 ParticleUtil.setParticleSystemSpeedRandom(ps, ps.particleRandomSpeed * speedFactor)
1272 else
1273 ParticleUtil.setEmittingState(ps, false)
1274 end
1275 end
1276 end
1277
1278 states[psName] = false
1279 end
1280 end
1281 end
1282 end
1283end

onWheelChockI3DLoaded

Description
Called when wheel chock i3d was loaded
Definition
onWheelChockI3DLoaded(integer i3dNode, table args)
Arguments
integeri3dNodei3dNode of wheel chock
tableargsarguments
Code
1967function Wheels:onWheelChockI3DLoaded(i3dNode, failedReason, args)
1968 local wheel = args.wheel
1969 local filename = args.filename
1970 local xmlFile = args.xmlFile
1971 local configKey = args.configKey
1972 local chockKey = args.chockKey
1973
1974 if i3dNode ~= 0 then
1975 local _
1976 local chockNode = getChildAt(i3dNode, 0)
1977 local posRefNode = I3DUtil.indexToObject(chockNode, getUserAttribute(chockNode, "posRefNode"), self.i3dMappings)
1978 if posRefNode ~= nil then
1979 local chock = {}
1980 chock.wheel = wheel
1981 chock.node = chockNode
1982 chock.filename = filename
1983 chock.scale = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, chockKey.."#scale", "1 1 1", true)
1984 setScale(chock.node, unpack(chock.scale))
1985
1986 chock.parkingNode = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, chockKey.."#parkingNode", nil, self.components, self.i3dMappings)
1987 chock.isInverted = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, chockKey.."#isInverted", false)
1988 chock.isParked = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, chockKey.."#isParked", false)
1989 _, chock.height, chock.zOffset = localToLocal(posRefNode, chock.node, 0, 0, 0)
1990 chock.height = chock.height / chock.scale[2]
1991 chock.zOffset = chock.zOffset / chock.scale[3]
1992
1993 chock.offset = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, chockKey.."#offset", "0 0 0", true)
1994
1995 chock.parkedNode = I3DUtil.indexToObject(chockNode, getUserAttribute(chockNode, "parkedNode"), self.i3dMappings)
1996 chock.linkedNode = I3DUtil.indexToObject(chockNode, getUserAttribute(chockNode, "linkedNode"), self.i3dMappings)
1997
1998 local color = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, chockKey.."#color", nil, true)
1999 if color ~= nil then
2000 local _, _, _, defaultMaterial = getShaderParameter(chockNode, "colorMat0")
2001 color[4] = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, chockKey.."#material", defaultMaterial)
2002 I3DUtil.setShaderParameterRec(chockNode, "colorMat0", color[1], color[2], color[3], color[4])
2003 end
2004
2005 chock.isInParkingPosition = false
2006
2007 self:updateWheelChockPosition(chock, chock.isParked)
2008
2009 wheel.updateWheelChock = false
2010
2011 table.insert(wheel.wheelChocks, chock)
2012 table.insert(self.spec_wheels.wheelChocks, chock)
2013 else
2014 Logging.xmlWarning(xmlFile, "Missing 'posRefNode'-userattribute for wheel-chock '%s'!", chockKey)
2015 end
2016
2017 delete(i3dNode)
2018 end
2019end

onWheelHubI3DLoaded

Description
Called when wheel hub i3d was loaded
Definition
onWheelHubI3DLoaded(integer i3dNode, table args)
Arguments
integeri3dNodei3dNode of wheel chock
tableargsarguments
Code
2207function Wheels:onWheelHubI3DLoaded(i3dNode, failedReason, args)
2208 local spec = self.spec_wheels
2209 local hub = args.hub
2210 local linkNode = args.linkNode
2211 local xmlFile = args.xmlFile
2212 local key = args.key
2213
2214 if i3dNode ~= 0 then
2215 hub.node = I3DUtil.indexToObject(i3dNode, hub.nodeStr, self.i3dMappings)
2216
2217 if hub.node ~= nil then
2218 link(linkNode, hub.node)
2219 delete(i3dNode)
2220 else
2221 Logging.xmlError(xmlFile, "Could not find hub node '%s' in '%s'", hub.nodeStr, hub.xmlFilename)
2222 return
2223 end
2224
2225 for j = 0, 7 do
2226 -- only use global hub color if hub color wasn't reset explicitly (== nil)
2227 local color = XMLUtil.getXMLOverwrittenValue(xmlFile, key, string.format(".color%d", j), "", "global")
2228 local material = XMLUtil.getXMLOverwrittenValue(xmlFile, key, string.format(".color%d#material", j), "")
2229 if color == "global" then
2230 color = spec.hubsColors[j]
2231 else
2232 color = ConfigurationUtil.getColorFromString(color)
2233 if color ~= nil then
2234 color[4] = material
2235 end
2236 end
2237
2238 color = color or hub.colors[j]
2239 if color ~= nil then
2240 local r, g, b, mat = unpack(color)
2241 local _
2242 if mat == nil then
2243 _, _, _, mat = I3DUtil.getShaderParameterRec(hub.node, string.format("colorMat%d", j))
2244 end
2245 I3DUtil.setShaderParameterRec(hub.node, string.format("colorMat%d", j), r, g, b, mat, false)
2246 end
2247 end
2248
2249 local offset = xmlFile:getValue(key .. "#offset")
2250 if offset ~= nil then
2251 if not hub.isLeft then
2252 offset = offset * -1
2253 end
2254 setTranslation(hub.node, offset, 0, 0)
2255 end
2256
2257 local scale = xmlFile:getValue(key .. "#scale", nil, true)
2258 if scale ~= nil then
2259 setScale(hub.node, scale[1], scale[2], scale[3])
2260 end
2261
2262 table.insert(spec.hubs, hub)
2263 else
2264 if not (self.isDeleting or self.isDeleted) then
2265 Logging.xmlError(xmlFile, "Unable to load hub '%s'", hub.xmlFilename)
2266 end
2267 end
2268end

onWheelPartI3DLoaded

Description
Called when wheel part i3d was loaded
Definition
onWheelPartI3DLoaded(integer i3dNode, table args)
Arguments
integeri3dNodei3dNode of wheel chock
tableargsarguments
Code
2737function Wheels:onWheelPartI3DLoaded(i3dNode, failedReason, args)
2738 local spec = self.spec_wheels
2739 local wheel = args.wheel
2740 local parentWheel = args.parentWheel
2741 local linkNode = args.linkNode
2742 local name = args.name
2743 local filename = args.filename
2744 local index = args.index
2745 local offset = args.offset
2746 local widthAndDiam = args.widthAndDiam
2747 local scale = args.scale
2748 local fileIdentifier = args.fileIdentifier
2749
2750 if i3dNode ~= 0 then
2751 wheel[fileIdentifier] = filename
2752 wheel[name] = I3DUtil.indexToObject(i3dNode, index)
2753 if wheel[name] ~= nil then
2754 link(linkNode, wheel[name])
2755 delete(i3dNode)
2756
2757 if offset ~= 0 then
2758 local dir = 1
2759 if not wheel.isLeft then
2760 dir = -1
2761 end
2762 setTranslation(wheel[name], offset*dir, 0, 0)
2763 end
2764 if scale ~= nil then
2765 setScale(wheel[name], scale[1], scale[2], scale[3])
2766 end
2767 if widthAndDiam ~= nil then
2768 if getHasShaderParameter(wheel[name], "widthAndDiam") then
2769 I3DUtil.setShaderParameterRec(wheel[name], "widthAndDiam", widthAndDiam[1], widthAndDiam[2], 0, 0, false)
2770 else
2771 -- convert width and diam to scale (mesh is normalized to 1 meter)
2772 local scaleX = MathUtil.inchToM(widthAndDiam[1])
2773 local scaleZY = MathUtil.inchToM(widthAndDiam[2])
2774 setScale(wheel[name], scaleX, scaleZY, scaleZY)
2775 end
2776 end
2777
2778 local rimConfigColor = ConfigurationUtil.getColorByConfigId(self, "rimColor", self.configurations["rimColor"])
2779 local rimColor = wheel.color or rimConfigColor or spec.rimColor
2780
2781 if name == "wheelTire" then
2782 local zRot = 0
2783 if wheel.tireIsInverted or (parentWheel ~= nil and parentWheel.tireIsInverted) then
2784 zRot = math.pi
2785 end
2786 setRotation(wheel.wheelTire, wheel.xRotOffset, 0, zRot)
2787
2788 local x, y, z, _ = I3DUtil.getShaderParameterRec(wheel.wheelTire, "morphPosition")
2789 I3DUtil.setShaderParameterRec(wheel.wheelTire, "morphPosition", x, y, z, 0, false)
2790 I3DUtil.setShaderParameterRec(wheel.wheelTire, "prevMorphPosition", x, y, z, 0, false)
2791 elseif name == "wheelOuterRim" or name == "wheelInnerRim" then
2792 if rimColor ~= nil then
2793 local r, g, b, mat, _ = unpack(rimColor)
2794 mat = wheel.material or mat
2795 if wheel.wheelOuterRim ~= nil then
2796 -- never use material from config color since it is always '1'
2797 if mat == nil then
2798 _, _, _, mat = I3DUtil.getShaderParameterRec(wheel.wheelOuterRim, "colorMat0")
2799 end
2800 I3DUtil.setShaderParameterRec(wheel.wheelOuterRim, "colorMat0", r, g, b, mat, false)
2801 end
2802 if wheel.wheelInnerRim ~= nil then
2803 -- never use material from config color since it is always '1'
2804 if mat == nil then
2805 _, _, _, mat = I3DUtil.getShaderParameterRec(wheel.wheelInnerRim, "colorMat0")
2806 end
2807 I3DUtil.setShaderParameterRec(wheel.wheelInnerRim, "colorMat0", r, g, b, mat, false)
2808 end
2809 end
2810
2811 if wheel.wheelInnerRim ~= nil then
2812 for i=1, 7 do
2813 local color = spec.hubsColors[i]
2814 if color ~= nil then
2815 I3DUtil.setShaderParameterRec(wheel.wheelInnerRim, string.format("colorMat%d", i), color[1], color[2], color[3], color[4], false)
2816 end
2817 end
2818 end
2819 elseif name == "wheelAdditional" then
2820 local additionalColor = Utils.getNoNil(wheel.additionalColor or (parentWheel ~= nil and parentWheel.additionalColor or nil), rimColor)
2821 if wheel.wheelAdditional ~= nil and additionalColor ~= nil then
2822 local r,g,b,_ = unpack(additionalColor)
2823 local _,_,_,w = I3DUtil.getShaderParameterRec(wheel.wheelAdditional, "colorMat0")
2824 w = (wheel.additionalMaterial or (parentWheel ~= nil and parentWheel.additionalMaterial or nil)) or w
2825 I3DUtil.setShaderParameterRec(wheel.wheelAdditional, "colorMat0", r, g, b, w, false)
2826 end
2827 end
2828 else
2829 Logging.xmlWarning(self.xmlFile, "Failed to load node '%s' for file '%s'", index, filename)
2830 end
2831 else
2832 if not (self.isDeleted or self.isDeleting) then
2833 Logging.xmlWarning(self.xmlFile, "Failed to load file '%s' wheel part '%s'", filename, name)
2834 end
2835 end
2836end

onWheelParticleSystemI3DLoaded

Description
Called when wheel particle i3d was loaded
Definition
onWheelParticleSystemI3DLoaded(integer i3dNode, table args)
Arguments
integeri3dNodei3dNode of wheel chock
tableargsarguments
Code
2082function Wheels:onWheelParticleSystemI3DLoaded(i3dNode, failedReason, args)
2083 if i3dNode ~= 0 then
2084 local xmlFile = args.xmlFile
2085 local configKey = args.configKey
2086 local wheelKey = args.wheelKey
2087 local wheel = args.wheel
2088 local wheelData = args.wheelData
2089
2090 local emitterShape = getChildAt(i3dNode, 0)
2091 link(wheel.node, emitterShape)
2092 delete(i3dNode)
2093
2094 local particleSystem = ParticleUtil.copyParticleSystem(xmlFile, nil, args.sourceParticleSystem, emitterShape)
2095 particleSystem.i3dFilename = args.i3dFilename
2096 particleSystem.particleSpeed = ParticleUtil.getParticleSystemSpeed(particleSystem)
2097 particleSystem.particleRandomSpeed = ParticleUtil.getParticleSystemSpeedRandom(particleSystem)
2098
2099 particleSystem.isTintable = Utils.getNoNil(getUserAttribute(particleSystem.shape, "tintable"), true)
2100 particleSystem.offsets = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey..".wheelParticleSystem#psOffset", "0 0 0", true)
2101 local wx, wy, wz = worldToLocal(wheel.node, getWorldTranslation(wheel.driveNode))
2102 setTranslation(particleSystem.emitterShape, wx + particleSystem.offsets[1], wy + particleSystem.offsets[2], wz + particleSystem.offsets[3])
2103 setScale(particleSystem.emitterShape, wheelData.width, wheelData.radius*2, wheelData.radius*2)
2104 particleSystem.wheel = wheel
2105 particleSystem.rootNode = particleSystem.emitterShape
2106 particleSystem.minSpeed = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey..".wheelParticleSystem#minSpeed", 3)/3600
2107 particleSystem.maxSpeed = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey..".wheelParticleSystem#maxSpeed", 20)/3600
2108 particleSystem.minScale = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey..".wheelParticleSystem#minScale", 0.1)
2109 particleSystem.maxScale = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey..".wheelParticleSystem#maxScale", 1)
2110 particleSystem.direction = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey..".wheelParticleSystem#direction", 0)
2111 particleSystem.onlyActiveOnGroundContact = self:getWheelConfigurationValue(xmlFile, wheel.configIndex, configKey, wheelKey..".wheelParticleSystem#onlyActiveOnGroundContact", true)
2112
2113 wheelData.driveGroundParticleSystems[args.name] = {particleSystem}
2114 end
2115end

onWheelSnowHeightChanged

Description
Definition
onWheelSnowHeightChanged()
Code
3325function Wheels:onWheelSnowHeightChanged(heightPct, heightAbs)
3326 if heightPct <= 0 then
3327 local spec = self.spec_wheels
3328 local changedSnowScale = false
3329 for i=1, #spec.wheels do
3330 if spec.wheels[i].snowScale > 0 then
3331 spec.wheels[i].snowScale = 0
3332 spec.wheels[i].forceWheelDirtUpdate = true
3333 changedSnowScale = true
3334 end
3335 end
3336
3337 -- raise active to update dirt amount
3338 if changedSnowScale then
3339 self:raiseActive()
3340 end
3341 end
3342end

onWriteStream

Description
Definition
onWriteStream()
Code
868function Wheels:onWriteStream(streamId, connection)
869 if not connection.isServer then
870 local spec = self.spec_wheels
871 for i=1, #spec.wheels do
872 local wheel = spec.wheels[i]
873 if wheel.isSynchronized then
874 self:writeWheelDataToStream(wheel, streamId)
875 end
876 end
877 end
878end

onWriteUpdateStream

Description
Definition
onWriteUpdateStream()
Code
913function Wheels:onWriteUpdateStream(streamId, connection, dirtyMask)
914 if not connection.isServer then
915 local spec = self.spec_wheels
916
917 if streamWriteBool(streamId, bitAND(dirtyMask, spec.dirtyFlag) ~= 0) then
918 for i=1, #spec.wheels do
919 local wheel = spec.wheels[i]
920 if wheel.isSynchronized then
921 self:writeWheelDataToStream(wheel, streamId)
922 end
923 end
924
925 if self.maxRotTime ~= 0 and self.minRotTime ~= 0 then
926 local rotatedTimeRange = math.max(self.maxRotTime - self.minRotTime, 0.001)
927 local rotatedTime = MathUtil.clamp(math.floor((self.rotatedTime - self.minRotTime)/rotatedTimeRange * 255), 0, 255)
928 streamWriteUIntN(streamId, rotatedTime, 8)
929 end
930 end
931 end
932end

prerequisitesPresent

Description
Definition
prerequisitesPresent()
Code
71function Wheels.prerequisitesPresent(specializations)
72 return true
73end

readWheelDataFromStream

Description
Definition
readWheelDataFromStream()
Code
936function Wheels:readWheelDataFromStream(wheel, streamId, updateInterpolation)
937 local xDrive = streamReadUIntN(streamId, 9)
938 xDrive = xDrive / 511 * math.pi*2
939 if updateInterpolation then
940 wheel.netInfo.xDrive = xDrive
941 wheel.networkInterpolators.xDrive:setAngle(xDrive)
942 else
943 wheel.networkInterpolators.xDrive:setTargetAngle(xDrive)
944 end
945
946 local y = streamReadUIntN(streamId, 8)
947 y = y / 255 * wheel.netInfo.sync.yRange + wheel.netInfo.sync.yMin
948 if updateInterpolation then
949 wheel.netInfo.y = y
950 wheel.networkInterpolators.position:setPosition(wheel.netInfo.x, y, wheel.netInfo.z)
951 else
952 wheel.networkInterpolators.position:setTargetPosition(wheel.netInfo.x, y, wheel.netInfo.z)
953 end
954
955 local suspLength = streamReadUIntN(streamId, 7)
956 if updateInterpolation then
957 wheel.netInfo.suspensionLength = suspLength/100
958 wheel.networkInterpolators.suspensionLength:setValue(suspLength/100)
959 else
960 wheel.networkInterpolators.suspensionLength:setTargetValue(suspLength/100)
961 end
962
963 if wheel.syncContactState then
964 wheel.contact = streamReadUIntN(streamId, 2)
965 wheel.lastContactObjectAllowsTireTracks = streamReadBool(streamId)
966 end
967
968 if wheel.versatileYRot then
969 local yRot = streamReadUIntN(streamId, 9)
970 wheel.steeringAngle = yRot / 511 * math.pi*2
971 end
972
973 wheel.lastTerrainValue = streamReadUIntN(streamId, 3)
974end

registerAckermannSteeringXMLPaths

Description
Definition
registerAckermannSteeringXMLPaths()
Code
531function Wheels.registerAckermannSteeringXMLPaths(schema, key)
532 schema:register(XMLValueType.FLOAT, key .. "#rotSpeed", "Rotation speed")
533 schema:register(XMLValueType.FLOAT, key .. "#rotMax", "Max. rotation")
534 schema:register(XMLValueType.INT, key .. "#rotCenterWheel1", "Rotation center wheel 1")
535 schema:register(XMLValueType.INT, key .. "#rotCenterWheel2", "Rotation center wheel 2")
536 schema:register(XMLValueType.NODE_INDEX, key .. "#rotCenterNode", "Rotation center node (Used if rotCenterWheelX not given)")
537 schema:register(XMLValueType.VECTOR_2, key .. "#rotCenter", "Center position (from root component) (Used if rotCenterWheelX not given)")
538end

registerConnectorXMLPaths

Description
Definition
registerConnectorXMLPaths()
Code
488function Wheels.registerConnectorXMLPaths(schema, key)
489 schema:register(XMLValueType.STRING, key .. "#filename", "Path to connector i3d or xml file")
490 schema:register(XMLValueType.STRING, key .. "#node", "Node in connector i3d file if i3d file is linked instead of xml")
491
492 schema:register(XMLValueType.BOOL, key .. "#useWidthAndDiam", "Use width and diameter from connector definition", false)
493 schema:register(XMLValueType.BOOL, key .. "#usePosAndScale", "Use position and scale from connector definition", false)
494
495 schema:register(XMLValueType.FLOAT, key .. "#diameter", "Diameter for shader")
496 schema:register(XMLValueType.FLOAT, key .. "#offset", "Additional connector X offset", 0)
497 schema:register(XMLValueType.FLOAT, key .. "#width", "Width for shader")
498 schema:register(XMLValueType.FLOAT, key .. "#startPos", "Start pos for shader")
499 schema:register(XMLValueType.FLOAT, key .. "#endPos", "End pos for shader")
500 schema:register(XMLValueType.FLOAT, key .. "#uniformScale", "Uniform scale for shader")
501 schema:register(XMLValueType.COLOR, key .. "#color", "Connector color")
502end

registerEventListeners

Description
Definition
registerEventListeners()
Code
166function Wheels.registerEventListeners(vehicleType)
167 SpecializationUtil.registerEventListener(vehicleType, "onLoad", Wheels)
168 SpecializationUtil.registerEventListener(vehicleType, "onLoadFinished", Wheels)
169 SpecializationUtil.registerEventListener(vehicleType, "onDelete", Wheels)
170 SpecializationUtil.registerEventListener(vehicleType, "onReadStream", Wheels)
171 SpecializationUtil.registerEventListener(vehicleType, "onWriteStream", Wheels)
172 SpecializationUtil.registerEventListener(vehicleType, "onReadUpdateStream", Wheels)
173 SpecializationUtil.registerEventListener(vehicleType, "onWriteUpdateStream", Wheels)
174 SpecializationUtil.registerEventListener(vehicleType, "onUpdate", Wheels)
175 SpecializationUtil.registerEventListener(vehicleType, "onPostUpdate", Wheels)
176 SpecializationUtil.registerEventListener(vehicleType, "onUpdateTick", Wheels)
177 SpecializationUtil.registerEventListener(vehicleType, "onUpdateEnd", Wheels)
178 SpecializationUtil.registerEventListener(vehicleType, "onLeaveVehicle", Wheels)
179 SpecializationUtil.registerEventListener(vehicleType, "onPreAttach", Wheels)
180 SpecializationUtil.registerEventListener(vehicleType, "onPostDetach", Wheels)
181 SpecializationUtil.registerEventListener(vehicleType, "onRegisterAnimationValueTypes", Wheels)
182 SpecializationUtil.registerEventListener(vehicleType, "onPostAttachImplement", Wheels)
183end

registerEvents

Description
Definition
registerEvents()
Code
77function Wheels.registerEvents(vehicleType)
78 SpecializationUtil.registerEvent(vehicleType, "onBrake")
79 SpecializationUtil.registerEvent(vehicleType, "onFinishedWheelLoading")
80 SpecializationUtil.registerEvent(vehicleType, "onWheelConfigurationChanged")
81end

registerFunctions

Description
Definition
registerFunctions()
Code
85function Wheels.registerFunctions(vehicleType)
86 SpecializationUtil.registerFunction(vehicleType, "getSteeringRotTimeByCurvature", Wheels.getSteeringRotTimeByCurvature)
87 SpecializationUtil.registerFunction(vehicleType, "getTurningRadiusByRotTime", Wheels.getTurningRadiusByRotTime)
88 SpecializationUtil.registerFunction(vehicleType, "getWheelConfigurationValue", Wheels.getWheelConfigurationValue)
89 SpecializationUtil.registerFunction(vehicleType, "loadWheelFromXML", Wheels.loadWheelFromXML)
90 SpecializationUtil.registerFunction(vehicleType, "loadWheelBaseData", Wheels.loadWheelBaseData)
91 SpecializationUtil.registerFunction(vehicleType, "loadWheelDataFromExternalXML", Wheels.loadWheelDataFromExternalXML)
92 SpecializationUtil.registerFunction(vehicleType, "loadWheelSharedData", Wheels.loadWheelSharedData)
93 SpecializationUtil.registerFunction(vehicleType, "loadWheelVisualData", Wheels.loadWheelVisualData)
94 SpecializationUtil.registerFunction(vehicleType, "loadWheelPhysicsData", Wheels.loadWheelPhysicsData)
95 SpecializationUtil.registerFunction(vehicleType, "loadWheelSteeringData", Wheels.loadWheelSteeringData)
96 SpecializationUtil.registerFunction(vehicleType, "loadAdditionalWheelsFromXML", Wheels.loadAdditionalWheelsFromXML)
97 SpecializationUtil.registerFunction(vehicleType, "loadAdditionalWheelConnectorFromXML", Wheels.loadAdditionalWheelConnectorFromXML)
98 SpecializationUtil.registerFunction(vehicleType, "loadWheelChocksFromXML", Wheels.loadWheelChocksFromXML)
99 SpecializationUtil.registerFunction(vehicleType, "onWheelChockI3DLoaded", Wheels.onWheelChockI3DLoaded)
100 SpecializationUtil.registerFunction(vehicleType, "loadWheelParticleSystem", Wheels.loadWheelParticleSystem)
101 SpecializationUtil.registerFunction(vehicleType, "onWheelParticleSystemI3DLoaded", Wheels.onWheelParticleSystemI3DLoaded)
102
103 SpecializationUtil.registerFunction(vehicleType, "loadHubsFromXML", Wheels.loadHubsFromXML)
104 SpecializationUtil.registerFunction(vehicleType, "loadHubFromXML", Wheels.loadHubFromXML)
105 SpecializationUtil.registerFunction(vehicleType, "onWheelHubI3DLoaded", Wheels.onWheelHubI3DLoaded)
106
107 SpecializationUtil.registerFunction(vehicleType, "loadAckermannSteeringFromXML", Wheels.loadAckermannSteeringFromXML)
108 SpecializationUtil.registerFunction(vehicleType, "loadNonPhysicalWheelFromXML", Wheels.loadNonPhysicalWheelFromXML)
109
110 SpecializationUtil.registerFunction(vehicleType, "loadWheelsFromXML", Wheels.loadWheelsFromXML)
111
112 SpecializationUtil.registerFunction(vehicleType, "finalizeWheel", Wheels.finalizeWheel)
113 SpecializationUtil.registerFunction(vehicleType, "onWheelPartI3DLoaded", Wheels.onWheelPartI3DLoaded)
114 SpecializationUtil.registerFunction(vehicleType, "onAdditionalWheelConnectorI3DLoaded", Wheels.onAdditionalWheelConnectorI3DLoaded)
115
116 SpecializationUtil.registerFunction(vehicleType, "readWheelDataFromStream", Wheels.readWheelDataFromStream)
117 SpecializationUtil.registerFunction(vehicleType, "writeWheelDataToStream", Wheels.writeWheelDataToStream)
118 SpecializationUtil.registerFunction(vehicleType, "updateWheelContact", Wheels.updateWheelContact)
119 SpecializationUtil.registerFunction(vehicleType, "addTireTrackNode", Wheels.addTireTrackNode)
120 SpecializationUtil.registerFunction(vehicleType, "updateTireTrackNode", Wheels.updateTireTrackNode)
121 SpecializationUtil.registerFunction(vehicleType, "updateWheelDensityMapHeight", Wheels.updateWheelDensityMapHeight)
122 SpecializationUtil.registerFunction(vehicleType, "updateWheelDestruction", Wheels.updateWheelDestruction)
123 SpecializationUtil.registerFunction(vehicleType, "getIsWheelFoliageDestructionAllowed", Wheels.getIsWheelFoliageDestructionAllowed)
124 SpecializationUtil.registerFunction(vehicleType, "updateWheelSink", Wheels.updateWheelSink)
125 SpecializationUtil.registerFunction(vehicleType, "updateWheelFriction", Wheels.updateWheelFriction)
126 SpecializationUtil.registerFunction(vehicleType, "updateWheelBase", Wheels.updateWheelBase)
127 SpecializationUtil.registerFunction(vehicleType, "updateWheelTireFriction", Wheels.updateWheelTireFriction)
128 SpecializationUtil.registerFunction(vehicleType, "setWheelPositionDirty", Wheels.setWheelPositionDirty)
129 SpecializationUtil.registerFunction(vehicleType, "setWheelTireFrictionDirty", Wheels.setWheelTireFrictionDirty)
130 SpecializationUtil.registerFunction(vehicleType, "getDriveGroundParticleSystemsScale", Wheels.getDriveGroundParticleSystemsScale)
131 SpecializationUtil.registerFunction(vehicleType, "getIsVersatileYRotActive", Wheels.getIsVersatileYRotActive)
132 SpecializationUtil.registerFunction(vehicleType, "getWheelFromWheelIndex", Wheels.getWheelFromWheelIndex)
133 SpecializationUtil.registerFunction(vehicleType, "getWheelByWheelNode", Wheels.getWheelByWheelNode)
134 SpecializationUtil.registerFunction(vehicleType, "getWheels", Wheels.getWheels)
135 SpecializationUtil.registerFunction(vehicleType, "getCurrentSurfaceSound", Wheels.getCurrentSurfaceSound)
136 SpecializationUtil.registerFunction(vehicleType, "getAreSurfaceSoundsActive", Wheels.getAreSurfaceSoundsActive)
137 SpecializationUtil.registerFunction(vehicleType, "destroyFruitArea", Wheels.destroyFruitArea)
138 SpecializationUtil.registerFunction(vehicleType, "destroySnowArea", Wheels.destroySnowArea)
139 SpecializationUtil.registerFunction(vehicleType, "brake", Wheels.brake)
140 SpecializationUtil.registerFunction(vehicleType, "getBrakeForce", Wheels.getBrakeForce)
141 SpecializationUtil.registerFunction(vehicleType, "updateWheelChocksPosition", Wheels.updateWheelChocksPosition)
142 SpecializationUtil.registerFunction(vehicleType, "updateWheelChockPosition", Wheels.updateWheelChockPosition)
143 SpecializationUtil.registerFunction(vehicleType, "updateWheelDirtAmount", Wheels.updateWheelDirtAmount)
144 SpecializationUtil.registerFunction(vehicleType, "getAllowTireTracks", Wheels.getAllowTireTracks)
145 SpecializationUtil.registerFunction(vehicleType, "getTireTrackColor", Wheels.getTireTrackColor)
146 SpecializationUtil.registerFunction(vehicleType, "forceUpdateWheelPhysics", Wheels.forceUpdateWheelPhysics)
147 SpecializationUtil.registerFunction(vehicleType, "onWheelSnowHeightChanged", Wheels.onWheelSnowHeightChanged)
148end

registerOverwrittenFunctions

Description
Definition
registerOverwrittenFunctions()
Code
152function Wheels.registerOverwrittenFunctions(vehicleType)
153 SpecializationUtil.registerOverwrittenFunction(vehicleType, "addToPhysics", Wheels.addToPhysics)
154 SpecializationUtil.registerOverwrittenFunction(vehicleType, "removeFromPhysics", Wheels.removeFromPhysics)
155 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getComponentMass", Wheels.getComponentMass)
156 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getVehicleWorldXRot", Wheels.getVehicleWorldXRot)
157 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getVehicleWorldDirection", Wheels.getVehicleWorldDirection)
158 SpecializationUtil.registerOverwrittenFunction(vehicleType, "validateWashableNode", Wheels.validateWashableNode)
159 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getAIDirectionNode", Wheels.getAIDirectionNode)
160 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getAIRootNode", Wheels.getAIRootNode)
161 SpecializationUtil.registerOverwrittenFunction(vehicleType, "getSupportsMountKinematic", Wheels.getSupportsMountKinematic)
162end

registerWheelAdditionalWheelsXMLPaths

Description
Definition
registerWheelAdditionalWheelsXMLPaths()
Code
471function Wheels.registerWheelAdditionalWheelsXMLPaths(schema, key)
472 schema:register(XMLValueType.STRING, key .. "#filename", "Filename")
473 schema:register(XMLValueType.STRING, key .. "#configId", "Config id", "default")
474 schema:register(XMLValueType.BOOL, key .. "#isLeft", "Is left wheel", false)
475 schema:register(XMLValueType.ANGLE, key .. "#xRotOffset", "X Rotation offset", 0)
476 schema:register(XMLValueType.COLOR, key .. "#color", "Color")
477 schema:register(XMLValueType.BOOL, key .. "#hasParticles", "Has particles", false)
478 schema:register(XMLValueType.BOOL, key .. "#hasTireTracks", "Has tire tracks", false)
479 schema:register(XMLValueType.FLOAT, key .. "#offset", "X Offset", 0)
480
481 Wheels.registerConnectorXMLPaths(schema, key .. ".connector")
482 Wheels.registerWheelParticleSystemXMLPaths(schema, key .. ".wheelParticleSystem")
483 Wheels.registerWheelVisualDataXMLPaths(schema, key)
484end

registerWheelBaseDataXMLPaths

Description
Definition
registerWheelBaseDataXMLPaths()
Code
305function Wheels.registerWheelBaseDataXMLPaths(schema, key)
306 schema:register(XMLValueType.NODE_INDEX, key .. ".physics#repr", "Wheel repr node")
307
308 schema:register(XMLValueType.COLOR, key .. "#color", "Wheel color")
309 schema:register(XMLValueType.INT, key .. "#material", "Wheel material id")
310 schema:register(XMLValueType.COLOR, key .. "#additionalColor", "Additional wheel color")
311 schema:register(XMLValueType.INT, key .. "#additionalMaterial", "Additional wheel material id")
312
313 schema:register(XMLValueType.BOOL, key .. "#isLeft", "Is left", true)
314 schema:register(XMLValueType.BOOL, key .. "#hasTireTracks", "Has tire tracks", false)
315 schema:register(XMLValueType.BOOL, key .. "#hasParticles", "Has particles", false)
316
317 schema:register(XMLValueType.STRING, key .. "#filename", "Filename")
318 schema:register(XMLValueType.STRING, key .. "#configId", "Wheel config id", "default")
319 schema:register(XMLValueType.ANGLE, key .. "#xRotOffset", "X Rotation offset", 0)
320end

registerWheelChockXMLPaths

Description
Definition
registerWheelChockXMLPaths()
Code
506function Wheels.registerWheelChockXMLPaths(schema, key)
507 schema:register(XMLValueType.STRING, key .. "#filename", "Path to wheel chock i3d", "$data/shared/assets/wheelChocks/wheelChock01.i3d")
508 schema:register(XMLValueType.VECTOR_SCALE, key .. "#scale", "Scale", "1 1 1")
509 schema:register(XMLValueType.NODE_INDEX, key .. "#parkingNode", "Parking node")
510 schema:register(XMLValueType.BOOL, key .. "#isInverted", "Is inverted (In front or back of the wheel)", false)
511 schema:register(XMLValueType.BOOL, key .. "#isParked", "Default is parked", false)
512 schema:register(XMLValueType.VECTOR_TRANS, key .. "#offset", "Translation offset", "0 0 0")
513 schema:register(XMLValueType.COLOR, key .. "#color", "Color")
514 schema:register(XMLValueType.INT, key .. "#material", "Material")
515end

registerWheelParticleSystemXMLPaths

Description
Definition
registerWheelParticleSystemXMLPaths()
Code
519function Wheels.registerWheelParticleSystemXMLPaths(schema, key)
520 schema:register(XMLValueType.VECTOR_TRANS, key .. "#psOffset", "Translation offset", "0 0 0")
521 schema:register(XMLValueType.FLOAT, key .. "#minSpeed", "Min. speed for activation", 3)
522 schema:register(XMLValueType.FLOAT, key .. "#maxSpeed", "Max. speed for activation", 20)
523 schema:register(XMLValueType.FLOAT, key .. "#minScale", "Min. scale", 0.1)
524 schema:register(XMLValueType.FLOAT, key .. "#maxScale", "Max. scale", 1)
525 schema:register(XMLValueType.INT, key .. "#direction", "Moving direction for activation", 0)
526 schema:register(XMLValueType.BOOL, key .. "#onlyActiveOnGroundContact", "Only active while wheel has ground contact", true)
527end

registerWheelPhysicsDataXMLPaths

Description
Definition
registerWheelPhysicsDataXMLPaths()
Code
384function Wheels.registerWheelPhysicsDataXMLPaths(schema, key)
385 schema:register(XMLValueType.NODE_INDEX, key .. ".physics#driveNode", "Drive node")
386 schema:register(XMLValueType.NODE_INDEX, key .. ".physics#linkNode", "Link node")
387 schema:register(XMLValueType.FLOAT, key .. ".physics#yOffset", "Y offset", 0)
388 schema:register(XMLValueType.BOOL, key .. ".physics#showSteeringAngle", "Show steering angle", true)
389 schema:register(XMLValueType.FLOAT, key .. ".physics#suspTravel", "Suspension travel", 0.01)
390 schema:register(XMLValueType.FLOAT, key .. ".physics#initialCompression", "Initial compression value")
391 schema:register(XMLValueType.FLOAT, key .. ".physics#deltaY", "Delta Y", 0)
392 schema:register(XMLValueType.FLOAT, key .. ".physics#spring", "Spring", 0)
393 schema:register(XMLValueType.FLOAT, key .. ".physics#brakeFactor", "Brake factor", 1)
394 schema:register(XMLValueType.FLOAT, key .. ".physics#autoHoldBrakeFactor", "Auto hold brake factor", "brakeFactor")
395
396 schema:register(XMLValueType.FLOAT, key .. ".physics#damper", "Damper", 0)
397 schema:register(XMLValueType.FLOAT, key .. ".physics#damperCompressionLowSpeed", "Damper compression on low speeds")
398 schema:register(XMLValueType.FLOAT, key .. ".physics#damperCompressionHighSpeed", "Damper compression on high speeds")
399 schema:register(XMLValueType.FLOAT, key .. ".physics#damperCompressionLowSpeedThreshold", "Damper compression on low speeds threshold", 0.1016)
400 schema:register(XMLValueType.FLOAT, key .. ".physics#damperRelaxationLowSpeed", "Damper relaxation on low speeds")
401 schema:register(XMLValueType.FLOAT, key .. ".physics#damperRelaxationHighSpeed", "Damper relaxation on high speeds")
402 schema:register(XMLValueType.FLOAT, key .. ".physics#damperRelaxationLowSpeedThreshold", "Damper relaxation on low speeds threshold", 0.1524)
403
404 schema:register(XMLValueType.FLOAT, key .. ".physics#forcePointRatio", "Force point ratio", 0)
405 schema:register(XMLValueType.INT, key .. ".physics#driveMode", "Drive mode", 0)
406 schema:register(XMLValueType.FLOAT, key .. ".physics#xOffset", "X axis offset", 0)
407 schema:register(XMLValueType.FLOAT, key .. ".physics#transRatio", "Suspension translation ratio between repr and drive node", 0)
408
409 schema:register(XMLValueType.BOOL, key .. ".physics#isSynchronized", "Wheel is synchronized in multiplayer", true)
410 schema:register(XMLValueType.INT, key .. ".physics#tipOcclusionAreaGroupId", "Tip occlusion area group id")
411
412 schema:register(XMLValueType.BOOL, key .. ".physics#useReprDirection", "Use repr direction instead of component direction", false)
413 schema:register(XMLValueType.BOOL, key .. ".physics#useDriveNodeDirection", "Use drive node direction instead of component direction", false)
414
415 schema:register(XMLValueType.FLOAT, key .. ".physics#mass", "Wheel mass (to.)")
416 schema:register(XMLValueType.FLOAT, key .. ".physics#radius", "Wheel radius", 0.5)
417 schema:register(XMLValueType.FLOAT, key .. ".physics#width", "Wheel width", 0.6)
418
419 schema:register(XMLValueType.FLOAT, key .. ".physics#widthOffset", "Wheel width offset", 0)
420 schema:register(XMLValueType.FLOAT, key .. ".physics#restLoad", "Wheel load while resting", 1.0)
421 schema:register(XMLValueType.FLOAT, key .. ".physics#maxLongStiffness", "Max. longitude stiffness")
422 schema:register(XMLValueType.FLOAT, key .. ".physics#maxLatStiffness", "Max. latitude stiffness")
423 schema:register(XMLValueType.FLOAT, key .. ".physics#maxLatStiffnessLoad", "Max. latitude stiffness load")
424 schema:register(XMLValueType.FLOAT, key .. ".physics#frictionScale", "Wheel friction scale", 1.0)
425 schema:register(XMLValueType.FLOAT, key .. ".physics#rotationDamping", "Rotation damping ", "mass * 0.035")
426 schema:register(XMLValueType.STRING, key .. ".physics#tireType", "Tire type (mud, offRoad, street, crawler)")
427
428 schema:register(XMLValueType.FLOAT, key .. ".physics#fieldDirtMultiplier", "Field dirt multiplier", 75)
429 schema:register(XMLValueType.FLOAT, key .. ".physics#streetDirtMultiplier", "Street dirt multiplier", -150)
430 schema:register(XMLValueType.FLOAT, key .. ".physics#minDirtPercentage", "Min. dirt scale while cleaning on street drive", 0.35)
431 schema:register(XMLValueType.FLOAT, key .. ".physics#maxDirtOffset", "Max. dirt amount offset to global dirt node", 0.5)
432 schema:register(XMLValueType.FLOAT, key .. ".physics#dirtColorChangeSpeed", "Defines speed to change the dirt color (sec)", 20)
433
434 schema:register(XMLValueType.FLOAT, key .. ".physics#smoothGroundRadius", "Smooth ground radius", "width * 0.75")
435
436 schema:register(XMLValueType.BOOL, key .. ".physics#versatileYRot", "Do versatile Y rotation", false)
437 schema:register(XMLValueType.BOOL, key .. ".physics#forceVersatility", "Force versatility, also if no ground contact", false)
438 schema:register(XMLValueType.BOOL, key .. ".physics#supportsWheelSink", "Supports wheel sink in field", true)
439 schema:register(XMLValueType.FLOAT, key .. ".physics#maxWheelSink", "Max. wheel sink in fields", 0.5)
440
441 schema:register(XMLValueType.ANGLE, key .. ".physics#rotSpeed", "Rotation speed")
442 schema:register(XMLValueType.ANGLE, key .. ".physics#rotSpeedNeg", "Rotation speed in negative direction")
443 schema:register(XMLValueType.ANGLE, key .. ".physics#rotMax", "Max. rotation")
444 schema:register(XMLValueType.ANGLE, key .. ".physics#rotMin", "Min. rotation")
445
446 schema:register(XMLValueType.BOOL, key .. ".physics#invertRotLimit", "Invert the rotation limits")
447 schema:register(XMLValueType.FLOAT, key .. ".physics#rotSpeedLimit", "Rotation speed limit")
448end

registerWheelSharedDataXMLPaths

Description
Definition
registerWheelSharedDataXMLPaths()
Code
324function Wheels.registerWheelSharedDataXMLPaths(schema, key)
325 schema:register(XMLValueType.FLOAT, key .. ".physics#radius", "Wheel radius")
326 schema:register(XMLValueType.FLOAT, key .. ".physics#width", "Wheel width")
327 schema:register(XMLValueType.FLOAT, key .. ".physics#mass", "Wheel mass (to.)", 0.1)
328 schema:register(XMLValueType.STRING, key .. ".physics#tireType", "Tire type (mud, offRoad, street, crawler)")
329
330 schema:register(XMLValueType.FLOAT, key .. ".physics#frictionScale", "Friction scale")
331 schema:register(XMLValueType.FLOAT, key .. ".physics#maxLongStiffness", "Max. longitude stiffness")
332 schema:register(XMLValueType.FLOAT, key .. ".physics#maxLatStiffness", "Max. latitude stiffness")
333 schema:register(XMLValueType.FLOAT, key .. ".physics#maxLatStiffnessLoad", "Max. latitude stiffness load")
334
335 schema:register(XMLValueType.FLOAT, key .. ".tire#tireTrackAtlasIndex", "Tire track atlas index", 0)
336 schema:register(XMLValueType.FLOAT, key .. ".tire#widthOffset", "Width offset", 0)
337 schema:register(XMLValueType.FLOAT, key .. ".tire#xOffset", "X offset", 0)
338 schema:register(XMLValueType.FLOAT, key .. ".tire#maxDeformation", "Max. deformation", 0)
339 schema:register(XMLValueType.FLOAT, key .. ".tire#initialDeformation", "Tire deformation at initial compression value", "min. 0.04 and max. 60% of the deformation")
340 schema:register(XMLValueType.FLOAT, key .. ".tire#sideDeformOffset", "Offset from lowerst point in center to lowerest point on the side in percentage (0.95: Radius on the side is 5% smaller than in the center)", 1.0)
341
342 schema:register(XMLValueType.BOOL, key .. ".tire#isCareWheel", "Is care wheel")
343 schema:register(XMLValueType.FLOAT, key .. ".physics#smoothGroundRadius", "Smooth ground radius", "width * 0.75")
344
345 Wheels.registerWheelVisualDataXMLPaths(schema, key)
346end

registerWheelSteeringDataXMLPaths

Description
Definition
registerWheelSteeringDataXMLPaths()
Code
452function Wheels.registerWheelSteeringDataXMLPaths(schema, key)
453 schema:register(XMLValueType.NODE_INDEX, key .. ".steering#node", "Steering node")
454 schema:register(XMLValueType.NODE_INDEX, key .. ".steering#rotNode", "Steering rot node")
455 schema:register(XMLValueType.FLOAT, key .. ".steering#nodeMinTransX", "Min. X translation")
456 schema:register(XMLValueType.FLOAT, key .. ".steering#nodeMaxTransX", "Max. X translation")
457 schema:register(XMLValueType.ANGLE, key .. ".steering#nodeMinRotY", "Min. Y rotation")
458 schema:register(XMLValueType.ANGLE, key .. ".steering#nodeMaxRotY", "Max. Y rotation")
459
460 schema:register(XMLValueType.NODE_INDEX, key .. ".fender(?)#node", "Fender node")
461 schema:register(XMLValueType.ANGLE, key .. ".fender(?)#rotMax", "Max. rotation")
462 schema:register(XMLValueType.ANGLE, key .. ".fender(?)#rotMin", "Min. rotation")
463
464 schema:register(XMLValueType.FLOAT, key .. ".steeringAxle#scale", "Steering axle scale")
465 schema:register(XMLValueType.ANGLE, key .. ".steeringAxle#rotMax", "Max. rotation")
466 schema:register(XMLValueType.ANGLE, key .. ".steeringAxle#rotMin", "Min. rotation")
467end

registerWheelVisualDataXMLPaths

Description
Definition
registerWheelVisualDataXMLPaths()
Code
350function Wheels.registerWheelVisualDataXMLPaths(schema, key)
351 schema:register(XMLValueType.STRING, key .. ".tire#filename", "Path to tire i3d file")
352 schema:register(XMLValueType.BOOL, key .. ".tire#isInverted", "Tire profile is inverted")
353 schema:register(XMLValueType.STRING, key .. ".tire#node", "Node Index inside tire i3d")
354 schema:register(XMLValueType.STRING, key .. ".tire#nodeLeft", "Left node index inside tire i3d")
355 schema:register(XMLValueType.STRING, key .. ".tire#nodeRight", "Right node index inside tire i3d")
356
357 schema:register(XMLValueType.STRING, key .. ".outerRim#filename", "Path to outer rim i3d file")
358 schema:register(XMLValueType.STRING, key .. ".outerRim#node", "Outer rim node index in i3d file", "0|0")
359 schema:register(XMLValueType.STRING, key .. ".outerRim#nodeLeft", "Outer rim node left index in i3d file", "0|0")
360 schema:register(XMLValueType.STRING, key .. ".outerRim#nodeRight", "Outer rim node right index in i3d file", "0|0")
361 schema:register(XMLValueType.VECTOR_2, key .. ".outerRim#widthAndDiam", "Width and diameter")
362 schema:register(XMLValueType.VECTOR_SCALE, key .. ".outerRim#scale", "Outer rim scale")
363
364 schema:register(XMLValueType.STRING, key .. ".innerRim#filename", "Path to inner rim i3d file")
365 schema:register(XMLValueType.STRING, key .. ".innerRim#node", "Inner rim node index in i3d file", "0|0")
366 schema:register(XMLValueType.STRING, key .. ".innerRim#nodeLeft", "Inner rim node left index in i3d file")
367 schema:register(XMLValueType.STRING, key .. ".innerRim#nodeRight", "Inner rim node right index in i3d file")
368 schema:register(XMLValueType.VECTOR_2, key .. ".innerRim#widthAndDiam", "Width and diameter")
369 schema:register(XMLValueType.FLOAT, key .. ".innerRim#offset", "Inner rim offset", 0)
370 schema:register(XMLValueType.VECTOR_SCALE, key .. ".innerRim#scale", "Inner rim scale")
371
372 schema:register(XMLValueType.STRING, key .. ".additional#filename", "Path to additional i3d")
373 schema:register(XMLValueType.STRING, key .. ".additional#node", "Additional node index in i3d file")
374 schema:register(XMLValueType.STRING, key .. ".additional#nodeLeft", "Additional node left index in i3d file")
375 schema:register(XMLValueType.STRING, key .. ".additional#nodeRight", "Additional node right index in i3d file")
376 schema:register(XMLValueType.FLOAT, key .. ".additional#offset", "Additional node offset", 0)
377 schema:register(XMLValueType.VECTOR_SCALE, key .. ".additional#scale", "Additional node scale")
378 schema:register(XMLValueType.FLOAT, key .. ".additional#mass", "Additional mass (to.)")
379 schema:register(XMLValueType.VECTOR_2, key .. ".additional#widthAndDiam", "Width and diameter")
380end

registerWheelXMLPaths

Description
Definition
registerWheelXMLPaths()
Code
293function Wheels.registerWheelXMLPaths(schema, key)
294 Wheels.registerWheelBaseDataXMLPaths(schema, key)
295 Wheels.registerWheelSharedDataXMLPaths(schema, key)
296 Wheels.registerWheelPhysicsDataXMLPaths(schema, key)
297 Wheels.registerWheelSteeringDataXMLPaths(schema, key)
298 Wheels.registerWheelAdditionalWheelsXMLPaths(schema, key .. ".additionalWheel(?)")
299 Wheels.registerWheelChockXMLPaths(schema, key .. ".wheelChock(?)")
300 Wheels.registerWheelParticleSystemXMLPaths(schema, key .. ".wheelParticleSystem")
301end

removeFromPhysics

Description
Definition
removeFromPhysics()
Code
2921function Wheels:removeFromPhysics(superFunc)
2922 local ret = superFunc(self)
2923
2924 if self.isServer then
2925 local spec = self.spec_wheels
2926 for _, wheel in pairs(spec.wheels) do
2927 wheel.wheelShape = 0
2928 wheel.wheelShapeCreated = false
2929 end
2930 end
2931 return ret
2932end

saveToXMLFile

Description
Definition
saveToXMLFile()
Code
784function Wheels:saveToXMLFile(xmlFile, key, usedModNames)
785 local spec = self.spec_wheels
786 xmlFile:setValue(key .. "#lastWheelConfiguration", spec.lastWheelConfigIndex or 1)
787end

setWheelPositionDirty

Description
Definition
setWheelPositionDirty()
Code
3205function Wheels:setWheelPositionDirty(wheel)
3206 if wheel ~= nil then
3207 wheel.isPositionDirty = true
3208 end
3209end

setWheelTireFrictionDirty

Description
Definition
setWheelTireFrictionDirty()
Code
3213function Wheels:setWheelTireFrictionDirty(wheel)
3214 if wheel ~= nil then
3215 wheel.isFrictionDirty = true
3216 end
3217end

updateTireTrackNode

Description
Definition
updateTireTrackNode()
Code
3373function Wheels:updateTireTrackNode(tireTrackNode, allowTireTracks, groundWetness)
3374 local wheel = tireTrackNode.wheel
3375
3376 if not allowTireTracks then
3377 self.tireTrackSystem:cutTrack(tireTrackNode.tireTrackIndex)
3378 return
3379 end
3380
3381 if tireTrackNode.activeFunc ~= nil and not tireTrackNode.activeFunc() then
3382 self.tireTrackSystem:cutTrack(tireTrackNode.tireTrackIndex)
3383 return
3384 end
3385
3386 local wx, wy, wz
3387 if not tireTrackNode.isAdditionalTrack then
3388 local netInfo = wheel.netInfo
3389 wx, wy, wz = netInfo.x, netInfo.y, netInfo.z
3390 else
3391 wx, wy, wz = worldToLocal(tireTrackNode.parent, getWorldTranslation(tireTrackNode.linkNode))
3392 end
3393
3394 wy = wy - tireTrackNode.radius
3395 wx = wx + tireTrackNode.xOffset
3396 wx, wy, wz = localToWorld(tireTrackNode.parent, wx, wy, wz)
3397 wy = math.max(wy, getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, wx, wy, wz))
3398
3399 local r, g, b, a, _ = self:getTireTrackColor(wheel, wx, wy, wz, groundWetness)
3400 if r ~= nil then
3401 -- we are using wheel shape direction to be independent from component(s) direction
3402 local ux, uy, uz = localDirectionToWorld(wheel.node, -wheel.directionX, -wheel.directionY, -wheel.directionZ)
3403
3404 local tireDirection = self.movingDirection
3405 if tireTrackNode.inverted then
3406 tireDirection = tireDirection * -1
3407 end
3408
3409 self.tireTrackSystem:addTrackPoint(tireTrackNode.tireTrackIndex, wx, wy, wz, ux, uy, uz, r, g, b, wheel.dirtAmount, a, tireDirection)
3410 else
3411 self.tireTrackSystem:cutTrack(tireTrackNode.tireTrackIndex)
3412 end
3413end

updateWheelBase

Description
Definition
updateWheelBase()
Code
3738function Wheels:updateWheelBase(wheel)
3739 if self.isServer and self.isAddedToPhysics then
3740 local positionX, positionY, positionZ = wheel.positionX-wheel.directionX*wheel.deltaY, wheel.positionY-wheel.directionY*wheel.deltaY, wheel.positionZ-wheel.directionZ*wheel.deltaY
3741
3742 --#debug if VehicleDebug.state == VehicleDebug.DEBUG_ATTRIBUTES then
3743 --#debug local x1, y1, z1 = localToWorld(wheel.node, wheel.positionX, wheel.positionY, wheel.positionZ)
3744 --#debug local x2, y2, z2 = localToWorld(wheel.node, positionX, positionY, positionZ)
3745 --#debug drawDebugLine(x1, y1, z1, 1, 0, 0, x2, y2, z2, 0, 1, 0, false)
3746 --#debug end
3747
3748 local collisionMask = 255 - 4 -- all up to bit 8, except bit 2 which is set by the players kinematic object
3749 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)
3750
3751 local forcePointY = positionY - wheel.radius * wheel.forcePointRatio
3752 local steeringX, steeringY, steeringZ = localToLocal(getParent(wheel.repr), wheel.node, wheel.startPositionX, wheel.startPositionY+wheel.deltaY, wheel.startPositionZ)
3753 setWheelShapeForcePoint(wheel.node, wheel.wheelShape, wheel.positionX, forcePointY, positionZ)
3754 setWheelShapeSteeringCenter(wheel.node, wheel.wheelShape, steeringX, steeringY, steeringZ)
3755 setWheelShapeDirection(wheel.node, wheel.wheelShape, wheel.directionX, wheel.directionY, wheel.directionZ, wheel.axleX, wheel.axleY, wheel.axleZ)
3756 setWheelShapeWidth(wheel.node, wheel.wheelShape, wheel.wheelShapeWidth, wheel.widthOffset)
3757
3758 if wheel.driveGroundParticleSystems ~= nil then
3759 for _,typedPs in pairs(wheel.driveGroundParticleSystems) do
3760 for _, ps in ipairs(typedPs) do
3761 setTranslation(ps.emitterShape, wheel.positionX + ps.offsets[1], positionY + ps.offsets[2], wheel.positionZ + ps.offsets[3])
3762 end
3763 end
3764 end
3765 end
3766end

updateWheelChockPosition

Description
Definition
updateWheelChockPosition()
Code
3913function Wheels:updateWheelChockPosition(wheelChock, isInParkingPosition)
3914 if isInParkingPosition == nil then
3915 isInParkingPosition = wheelChock.isInParkingPosition
3916 end
3917
3918 wheelChock.isInParkingPosition = isInParkingPosition
3919
3920 if isInParkingPosition then
3921 if wheelChock.parkingNode ~= nil then
3922 setTranslation(wheelChock.node, 0, 0, 0)
3923 setRotation(wheelChock.node, 0, 0, 0)
3924 link(wheelChock.parkingNode, wheelChock.node)
3925 setVisibility(wheelChock.node, true)
3926 else
3927 setVisibility(wheelChock.node, false)
3928 end
3929 else
3930 setVisibility(wheelChock.node, true)
3931 local wheel = wheelChock.wheel
3932
3933 local radiusChockHeightOffset = wheel.radius - wheel.deformation - wheelChock.height
3934 local angle = math.acos(radiusChockHeightOffset / wheel.radius)
3935 local zWheelIntersection = wheel.radius * math.sin(angle)
3936 local zChockOffset = -zWheelIntersection - wheelChock.zOffset
3937
3938 link(wheel.node, wheelChock.node)
3939
3940 local _, yRot, _ = localRotationToLocal(getParent(wheel.repr), wheel.node, getRotation(wheel.repr))
3941 if wheelChock.isInverted then
3942 yRot = yRot + math.pi
3943 end
3944 setRotation(wheelChock.node, 0, yRot, 0)
3945
3946 local dirX, dirY, dirZ = localDirectionToLocal(wheelChock.node, wheel.node, 0, 0, 1)
3947 local normX, normY, normZ = localDirectionToLocal(wheelChock.node, wheel.node, 1, 0, 0)
3948
3949 local posX, posY, posZ = localToLocal(wheel.driveNode, wheel.node, 0, 0, 0)
3950 posX = posX + normX * wheelChock.offset[1] + dirX * (zChockOffset + wheelChock.offset[3])
3951 posY = posY + normY * wheelChock.offset[1] + dirY * (zChockOffset + wheelChock.offset[3]) - wheel.radius + wheel.deformation + wheelChock.offset[2]
3952 posZ = posZ + normZ * wheelChock.offset[1] + dirZ * (zChockOffset + wheelChock.offset[3])
3953
3954 setTranslation(wheelChock.node, posX, posY, posZ)
3955 end
3956
3957 if wheelChock.parkedNode ~= nil then
3958 setVisibility(wheelChock.parkedNode, isInParkingPosition)
3959 end
3960
3961 if wheelChock.linkedNode ~= nil then
3962 setVisibility(wheelChock.linkedNode, not isInParkingPosition)
3963 end
3964
3965 return true
3966end

updateWheelChocksPosition

Description
Definition
updateWheelChocksPosition()
Code
3902function Wheels:updateWheelChocksPosition(isInParkingPosition, continueUpdate)
3903 local spec = self.spec_wheels
3904 for _, wheelChock in pairs(spec.wheelChocks) do
3905 wheelChock.wheel.updateWheelChock = continueUpdate
3906 isInParkingPosition = Utils.getNoNil(isInParkingPosition, wheelChock.isParked)
3907 self:updateWheelChockPosition(wheelChock, isInParkingPosition)
3908 end
3909end

updateWheelContact

Description
Definition
updateWheelContact()
Code
3221function Wheels:updateWheelContact(wheel)
3222 -- using netinfo because of tire deformation
3223 local wx, wy, wz = wheel.netInfo.x, wheel.netInfo.y, wheel.netInfo.z
3224 wy = wy - wheel.radius
3225 wx = wx + wheel.xOffset
3226 wx, wy, wz = localToWorld(wheel.node, wx, wy, wz)
3227
3228 local mission = g_currentMission
3229 if self.isServer and self.isAddedToPhysics and wheel.wheelShapeCreated then
3230 wheel.hasGroundContact = getWheelShapeContactPoint(wheel.node, wheel.wheelShape) ~= nil
3231
3232 --wheelSpeed = getWheelShapeAxleSpeed(wheel.node, wheel.wheelShape)
3233 local contactObject, contactSubShapeIndex = getWheelShapeContactObject(wheel.node, wheel.wheelShape)
3234 if contactObject == mission.terrainRootNode then
3235 if contactSubShapeIndex <= 0 then
3236 wheel.contact = Wheels.WHEEL_GROUND_CONTACT
3237 else
3238 wheel.contact = Wheels.WHEEL_GROUND_HEIGHT_CONTACT
3239 end
3240 elseif wheel.hasGroundContact and contactObject ~= 0 then
3241 wheel.contact = Wheels.WHEEL_OBJ_CONTACT
3242
3243 wheel.lastContactObjectAllowsTireTracks = getRigidBodyType(contactObject) == RigidBodyType.STATIC and getUserAttribute(contactObject, "noTireTracks") ~= true
3244 else
3245 wheel.contact = Wheels.WHEEL_NO_CONTACT
3246 end
3247 end
3248
3249 if wheel.contact == Wheels.WHEEL_GROUND_CONTACT then
3250 local groundTypeMapId, groundTypeFirstChannel, groundTypeNumChannels = mission.fieldGroundSystem:getDensityMapData(FieldDensityMap.GROUND_TYPE)
3251 wheel.densityBits = getDensityAtWorldPos(groundTypeMapId, wx, wy, wz)
3252 wheel.densityType = bitAND(bitShiftRight(wheel.densityBits, groundTypeFirstChannel), 2^groundTypeNumChannels - 1)
3253 else
3254 wheel.densityBits = 0
3255 wheel.densityType = 0
3256 end
3257
3258 if wheel.contact ~= Wheels.WHEEL_NO_CONTACT then
3259 local densityHeightBits = getDensityAtWorldPos(mission.terrainDetailHeightId, wx, wy, wz)
3260 local numChannels = g_densityMapHeightManager.heightTypeNumChannels
3261 local heightType = bitAND(densityHeightBits, 2^numChannels - 1)
3262 wheel.hasSnowContact = heightType == self.spec_wheels.snowSystem.snowHeightTypeIndex
3263 else
3264 wheel.hasSnowContact = false
3265 end
3266
3267 wheel.shallowWater = wy < self.waterY
3268end

updateWheelDensityMapHeight

Description
Definition
updateWheelDensityMapHeight()
Code
3460function Wheels:updateWheelDensityMapHeight(wheel, dt)
3461 if not self.isServer then
3462 return
3463 end
3464
3465 local spec = self.spec_wheels
3466
3467 -- smoothing of tipAny
3468 local wheelSmoothAmount = 0
3469 --if self.lastSpeedReal > 0.0002 and next(spec.wheels) ~= nil then -- start smoothing if driving faster than 0.7km/h
3470 if self.lastSpeedReal > 0.0002 then -- start smoothing if driving faster than 0.7km/h
3471 wheelSmoothAmount = spec.wheelSmoothAccumulation + math.max(self.lastMovedDistance * 1.2, 0.0003*dt) -- smooth 1.2m per meter driving or at least 0.3m/s
3472 local rounded = DensityMapHeightUtil.getRoundedHeightValue(wheelSmoothAmount)
3473 spec.wheelSmoothAccumulation = wheelSmoothAmount - rounded
3474 else
3475 spec.wheelSmoothAccumulation = 0
3476 end
3477
3478 if wheelSmoothAmount == 0 then
3479 return
3480 end
3481
3482 -- using netinfo because of tire deformation
3483 local wx, wy, wz = wheel.netInfo.x, wheel.netInfo.y, wheel.netInfo.z
3484 wy = wy - wheel.radius
3485 wx = wx + wheel.xOffset
3486 wx, wy, wz = localToWorld(wheel.node, wx,wy,wz)
3487
3488 if wheel.smoothGroundRadius > 0 then --and wheelSmoothAmount > 0 then
3489 local smoothYOffset = -0.1
3490 local heightType = DensityMapHeightUtil.getHeightTypeDescAtWorldPos(wx, wy, wz, wheel.smoothGroundRadius)
3491 if heightType ~= nil and heightType.allowsSmoothing then
3492 local terrainHeightUpdater = g_densityMapHeightManager:getTerrainDetailHeightUpdater()
3493 if terrainHeightUpdater ~= nil then
3494 local terrainHeight = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, wx, wy, wz)
3495 local physicsDeltaHeight = wy - terrainHeight
3496 local deltaHeight = (physicsDeltaHeight + heightType.collisionBaseOffset) / heightType.collisionScale
3497 deltaHeight = math.min(math.max(deltaHeight, physicsDeltaHeight+heightType.minCollisionOffset), physicsDeltaHeight+heightType.maxCollisionOffset)
3498 deltaHeight = math.max(deltaHeight + smoothYOffset, 0)
3499 local internalHeight = terrainHeight + deltaHeight
3500 smoothDensityMapHeightAtWorldPos(terrainHeightUpdater, wx, internalHeight, wz, wheelSmoothAmount, heightType.index, 0.0, wheel.smoothGroundRadius, wheel.smoothGroundRadius + 1.2)
3501 if VehicleDebug.state == VehicleDebug.DEBUG_ATTRIBUTES then
3502 DebugUtil.drawDebugCircle(wx,internalHeight,wz, wheel.smoothGroundRadius, 10)
3503 end
3504 end
3505 end
3506 if wheel.additionalWheels ~= nil then
3507 for _, additionalWheel in pairs(wheel.additionalWheels) do
3508 local refNode = wheel.repr
3509 local xShift,yShift,zShift = localToLocal(additionalWheel.wheelTire, refNode, additionalWheel.xOffset,0,0)
3510 wx,wy,wz = localToWorld(refNode, xShift, yShift-additionalWheel.radius, zShift)
3511 heightType = DensityMapHeightUtil.getHeightTypeDescAtWorldPos(wx, wy, wz, additionalWheel.smoothGroundRadius)
3512 if heightType ~= nil and heightType.allowsSmoothing then
3513 local terrainHeightUpdater = g_densityMapHeightManager:getTerrainDetailHeightUpdater()
3514 if terrainHeightUpdater ~= nil then
3515 local terrainHeight = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, wx, wy, wz)
3516 local physicsDeltaHeight = wy - terrainHeight
3517 local deltaHeight = (physicsDeltaHeight + heightType.collisionBaseOffset) / heightType.collisionScale
3518 deltaHeight = math.min(math.max(deltaHeight, physicsDeltaHeight+heightType.minCollisionOffset), physicsDeltaHeight+heightType.maxCollisionOffset)
3519 deltaHeight = math.max(deltaHeight + smoothYOffset, 0)
3520 local internalHeight = terrainHeight + deltaHeight
3521 smoothDensityMapHeightAtWorldPos(terrainHeightUpdater, wx, internalHeight, wz, wheelSmoothAmount, heightType.index, 0.0, additionalWheel.smoothGroundRadius, additionalWheel.smoothGroundRadius + 1.2)
3522 if VehicleDebug.state == VehicleDebug.DEBUG_ATTRIBUTES then
3523 DebugUtil.drawDebugCircle(wx,internalHeight,wz, additionalWheel.smoothGroundRadius, 10)
3524 end
3525 end
3526 end
3527 end
3528 end
3529 end
3530
3531end

updateWheelDestruction

Description
Definition
updateWheelDestruction()
Code
3535function Wheels:updateWheelDestruction(wheel, dt)
3536 local doFruitDestruction = self:getIsWheelFoliageDestructionAllowed(wheel)
3537 local doSnowDestruction = wheel.contact ~= Wheels.WHEEL_NO_CONTACT and wheel.hasSnowContact
3538 if doFruitDestruction or doSnowDestruction then
3539 if doFruitDestruction then
3540 local x0, _, z0 = getWorldTranslation(wheel.destructionStartNode)
3541 if g_currentMission.accessHandler:canFarmAccessLand(self:getActiveFarm(), x0, z0) then
3542 local x1, _, z1 = getWorldTranslation(wheel.destructionWidthNode)
3543 local x2, _, z2 = getWorldTranslation(wheel.destructionHeightNode)
3544
3545 self:destroyFruitArea(x0, z0, x1, z1, x2, z2)
3546 end
3547 end
3548
3549 if doSnowDestruction then
3550 -- remove the snow in front of the wheel to avoid drop of the wheel since the collision below changes
3551 local snowOffset = wheel.radius * 0.75 * self.movingDirection
3552 local x3, _, z3 = localToWorld(wheel.destructionStartNode, 0, 0, snowOffset)
3553 local x4, _, z4 = localToWorld(wheel.destructionWidthNode, 0, 0, snowOffset)
3554 local x5, _, z5 = localToWorld(wheel.destructionHeightNode, 0, 0, snowOffset)
3555 self:destroySnowArea(x3, z3, x4, z4, x5, z5)
3556 end
3557
3558 if wheel.additionalWheels ~= nil then
3559 for _,additionalWheel in pairs(wheel.additionalWheels) do
3560 local width = 0.5 * additionalWheel.width
3561 local length = math.min(0.5, 0.5 * additionalWheel.width)
3562 local refNode = wheel.node
3563
3564 if wheel.repr ~= wheel.driveNode then
3565 refNode = wheel.repr
3566 end
3567
3568 local xShift, yShift, zShift = localToLocal(additionalWheel.wheelTire, refNode, 0, 0, 0)
3569
3570 if doFruitDestruction then
3571 local x0, _, z0 = localToWorld(refNode, xShift + width, yShift, zShift - length)
3572 if g_farmlandManager:getIsOwnedByFarmAtWorldPosition(self:getActiveFarm(), x0, z0) then
3573 local x1, _, z1 = localToWorld(refNode, xShift - width, yShift, zShift - length)
3574 local x2, _, z2 = localToWorld(refNode, xShift + width, yShift, zShift + length)
3575
3576 self:destroyFruitArea(x0, z0, x1, z1, x2, z2)
3577 end
3578 end
3579
3580 if doSnowDestruction then
3581 local snowOffset = wheel.radius * 0.75 * self.movingDirection
3582 local x3, _, z3 = localToWorld(refNode, xShift + width, yShift, zShift - length + snowOffset)
3583 local x4, _, z4 = localToWorld(refNode, xShift - width, yShift, zShift - length + snowOffset)
3584 local x5, _, z5 = localToWorld(refNode, xShift + width, yShift, zShift + length + snowOffset)
3585
3586 self:destroySnowArea(x3, z3, x4, z4, x5, z5)
3587 end
3588 end
3589 end
3590 end
3591end

updateWheelDirtAmount

Description
Definition
updateWheelDirtAmount()
Code
3132function Wheels:updateWheelDirtAmount(nodeData, dt, allowsWashingByRain, rainScale, timeSinceLastRain, temperature)
3133 local dirtAmount = self:updateDirtAmount(nodeData, dt, allowsWashingByRain, rainScale, timeSinceLastRain, temperature)
3134
3135 local allowManipulation = true
3136 if nodeData.wheel ~= nil then
3137 if nodeData.wheel.contact == Wheels.WHEEL_NO_CONTACT and nodeData.wheel.forceWheelDirtUpdate ~= true then
3138 allowManipulation = false
3139 end
3140 end
3141
3142 if allowManipulation then
3143 local spec = self.spec_wheels
3144
3145 local isOnField = nodeData.wheel.hasSnowContact
3146 if nodeData.wheel ~= nil then
3147 if nodeData.wheel.densityType ~= 0 and nodeData.wheel.densityType ~= spec.tireTrackGroundGrassValue and nodeData.wheel.densityType ~= spec.tireTrackGroundGrassCutValue then
3148 isOnField = true
3149 end
3150 end
3151
3152 local lastSpeed = self.lastSpeed * 3600
3153
3154 if isOnField then
3155 dirtAmount = dirtAmount * nodeData.fieldDirtMultiplier
3156 else
3157 if nodeData.dirtAmount > nodeData.minDirtPercentage then
3158 local speedFactor = lastSpeed / 20
3159 dirtAmount = dirtAmount * nodeData.streetDirtMultiplier * speedFactor
3160 end
3161 end
3162
3163 local globalValue = self.spec_washable.washableNodes[1].dirtAmount
3164 local minDirtOffset = nodeData.maxDirtOffset * (math.pow(1-globalValue, 2) * 0.75 + 0.25)
3165 local maxDirtOffset = nodeData.maxDirtOffset * (math.pow(1-globalValue, 2) * 0.95 + 0.05)
3166 if globalValue - nodeData.dirtAmount > minDirtOffset then
3167 if dirtAmount < 0 then
3168 dirtAmount = 0
3169 end
3170 elseif globalValue - nodeData.dirtAmount < -maxDirtOffset then
3171 if dirtAmount > 0 then
3172 dirtAmount = 0
3173 end
3174 end
3175
3176 -- change dirt scale of wheels slowly to snow color when having snow contact
3177 -- changing back to normal color takes longer
3178 local factor = (nodeData.wheel.hasSnowContact and (temperature or 0) < 1) and 1 or -0.25
3179 local speedFactor = math.min(lastSpeed / 5, 2)
3180 local lastSnowScale = nodeData.wheel.snowScale
3181 nodeData.wheel.snowScale = math.min(math.max(lastSnowScale + factor * dt * nodeData.dirtColorChangeSpeed * speedFactor, 0), 1)
3182
3183 if nodeData.wheel.snowScale ~= nodeData.wheel.lastSnowScale then
3184 local defaultColor, snowColor = g_currentMission.environment:getDirtColors()
3185 local r, g, b = MathUtil.vector3ArrayLerp(defaultColor, snowColor, nodeData.wheel.snowScale)
3186 self:setNodeDirtColor(nodeData, r, g, b)
3187
3188 nodeData.wheel.lastSnowScale = nodeData.wheel.snowScale
3189 end
3190
3191 nodeData.wheel.forceWheelDirtUpdate = false
3192 end
3193
3194 return dirtAmount
3195end

updateWheelFriction

Description
Definition
updateWheelFriction()
Code
3714function Wheels:updateWheelFriction(wheel, dt, groundWetness)
3715 if self.isServer then
3716 local isOnField = wheel.densityType ~= 0
3717 local depth = wheel.lastColor[4]
3718
3719 local snowScale = 0
3720 if wheel.hasSnowContact then
3721 groundWetness = 0
3722 snowScale = 1
3723 end
3724
3725 local groundType = WheelsUtil.getGroundType(isOnField, wheel.contact ~= Wheels.WHEEL_GROUND_CONTACT, depth)
3726 local coeff = WheelsUtil.getTireFriction(wheel.tireType, groundType, groundWetness, snowScale)
3727 if self:getLastSpeed() > 0.2 then
3728 if coeff ~= wheel.tireGroundFrictionCoeff then
3729 wheel.tireGroundFrictionCoeff = coeff
3730 self:setWheelTireFrictionDirty(wheel)
3731 end
3732 end
3733 end
3734end

updateWheelSink

Description
Definition
updateWheelSink()
Code
3623function Wheels:updateWheelSink(wheel, dt, groundWetness)
3624 if wheel.supportsWheelSink then
3625 if self.isServer and self.isAddedToPhysics then
3626 local spec = self.spec_wheels
3627
3628 local maxSink = wheel.maxWheelSink
3629 local sinkTarget = wheel.sinkTarget
3630 local lastSpeed = self:getLastSpeed()
3631 local interpolationFactor = 1
3632
3633 if wheel.contact ~= Wheels.WHEEL_NO_CONTACT and lastSpeed > 0.3 then
3634 local x, _, z = getWorldTranslation(wheel.repr)
3635
3636 local noiseValue = 0
3637 if wheel.densityType > 0 then
3638 -- Round to 1cm to avoid sliding when not moving
3639 local xPerlin = math.floor(x*100)*0.01
3640 local zPerlin = math.floor(z*100)*0.01
3641
3642 local perlinNoise = Wheels.perlinNoiseSink
3643 local noiseSink = 0.5 * (1 + getPerlinNoise2D(xPerlin*perlinNoise.randomFrequency, zPerlin*perlinNoise.randomFrequency, perlinNoise.persistence, perlinNoise.numOctaves, perlinNoise.randomSeed))
3644
3645 perlinNoise = Wheels.perlinNoiseWobble
3646 local noiseWobble = 0.5 * (1 + getPerlinNoise2D(xPerlin*perlinNoise.randomFrequency, zPerlin*perlinNoise.randomFrequency, perlinNoise.persistence, perlinNoise.numOctaves, perlinNoise.randomSeed))
3647
3648 -- estimiate pressure on surface
3649 local gravity = 9.81
3650 local tireLoad = getWheelShapeContactForce(wheel.node, wheel.wheelShape)
3651 if tireLoad ~= nil then
3652 local nx,ny,nz = getWheelShapeContactNormal(wheel.node, wheel.wheelShape)
3653 local dx,dy,dz = localDirectionToWorld(wheel.node, 0,-1,0)
3654 tireLoad = -tireLoad*MathUtil.dotProduct(dx,dy,dz, nx,ny,nz)
3655
3656 tireLoad = tireLoad + math.max(ny*gravity, 0.0) * wheel.mass -- add gravity force of tire
3657 else
3658 tireLoad = 0
3659 end
3660 tireLoad = tireLoad / gravity
3661
3662 local loadFactor = math.min(1.0, math.max(0, tireLoad / wheel.maxLatStiffnessLoad))
3663
3664 noiseSink = 0.333*(2*loadFactor + groundWetness) * noiseSink
3665
3666 noiseValue = math.max(noiseSink, noiseWobble)
3667 end
3668
3669 maxSink = Wheels.MAX_SINK[wheel.densityType] or maxSink
3670
3671 -- plowing effect
3672 if wheel.densityType == FieldGroundType.PLOWED and wheel.oppositeWheelIndex ~= nil then
3673 local oppositeWheel = spec.wheels[wheel.oppositeWheelIndex]
3674 if oppositeWheel.densityType ~= nil and oppositeWheel.densityType ~= FieldGroundType.PLOWED then
3675 maxSink = maxSink * 1.3
3676 end
3677 end
3678
3679 sinkTarget = math.min(0.2*wheel.radiusOriginal, math.min(maxSink, wheel.maxWheelSink) * noiseValue)
3680 elseif wheel.contact == Wheels.WHEEL_NO_CONTACT then
3681 sinkTarget = 0
3682 lastSpeed = 10
3683 interpolationFactor = 0.075 -- smoother interpolation back to normal radius in case we directly sink again after having ground contact -> this avoid jittering
3684 end
3685
3686 if wheel.sinkTarget < sinkTarget then
3687 wheel.sinkTarget = math.min(sinkTarget, wheel.sinkTarget + (0.05 * math.min(30, math.max(0, lastSpeed-0.2)) * (dt/1000) * interpolationFactor))
3688 elseif wheel.sinkTarget > sinkTarget then
3689 wheel.sinkTarget = math.max(sinkTarget, wheel.sinkTarget - (0.05 * math.min(30, math.max(0, lastSpeed-0.2)) * (dt/1000) * interpolationFactor))
3690 end
3691
3692 if math.abs(wheel.sink - wheel.sinkTarget) > 0.001 then
3693 wheel.sink = wheel.sinkTarget
3694
3695 local radius = wheel.radiusOriginal - wheel.sink
3696 if radius ~= wheel.radius then
3697 wheel.radius = radius
3698 if self.isServer then
3699 self:setWheelPositionDirty(wheel)
3700
3701 local sinkFactor = (wheel.sink/maxSink) * (1 + (0.4 * groundWetness))
3702 wheel.sinkLongStiffnessFactor = (1.0 - (0.10 * sinkFactor))
3703 wheel.sinkLatStiffnessFactor = (1.0 - (0.20 * sinkFactor))
3704 self:setWheelTireFrictionDirty(wheel)
3705 end
3706 end
3707 end
3708 end
3709 end
3710end

updateWheelTireFriction

Description
Definition
updateWheelTireFriction()
Code
3770function Wheels:updateWheelTireFriction(wheel)
3771 if self.isServer and self.isAddedToPhysics then
3772 setWheelShapeTireFriction(wheel.node, wheel.wheelShape, wheel.sinkFrictionScaleFactor*wheel.maxLongStiffness, wheel.sinkLatStiffnessFactor*wheel.maxLatStiffness, wheel.maxLatStiffnessLoad, wheel.sinkFrictionScaleFactor*wheel.frictionScale*wheel.tireGroundFrictionCoeff)
3773 end
3774end

validateWashableNode

Description
Definition
validateWashableNode()
Code
3065function Wheels:validateWashableNode(superFunc, node)
3066 -- start checking the wheel nodes only if all wheel parts are loaded
3067 if self.loadingStep >= Vehicle.LOAD_STEP_FINISHED then
3068 local spec = self.spec_wheels
3069 for i=1, #spec.wheels do
3070 local wheel = spec.wheels[i]
3071 local wheelNode = wheel.driveNode
3072 if wheel.linkNode ~= wheel.driveNode then
3073 wheelNode = wheel.linkNode
3074 end
3075
3076 if wheel.wheelDirtNodes == nil then
3077 wheel.wheelDirtNodes = {}
3078 I3DUtil.getNodesByShaderParam(wheelNode, "RDT", wheel.wheelDirtNodes)
3079 end
3080
3081 if wheel.wheelDirtNodes[node] ~= nil then
3082 local nodeData = {}
3083 nodeData.wheel = wheel
3084 nodeData.fieldDirtMultiplier = wheel.fieldDirtMultiplier
3085 nodeData.streetDirtMultiplier = wheel.streetDirtMultiplier
3086 nodeData.minDirtPercentage = wheel.minDirtPercentage
3087 nodeData.maxDirtOffset = wheel.maxDirtOffset
3088 nodeData.dirtColorChangeSpeed = wheel.dirtColorChangeSpeed
3089 nodeData.isSnowNode = true
3090
3091 nodeData.loadFromSavegameFunc = function(xmlFile, key)
3092 nodeData.wheel.snowScale = xmlFile:getValue(key.."#snowScale", 0)
3093 nodeData.wheel.lastSnowScale = nodeData.wheel.snowScale
3094
3095 local defaultColor, snowColor = g_currentMission.environment:getDirtColors()
3096 local r, g, b = MathUtil.vector3ArrayLerp(defaultColor, snowColor, nodeData.wheel.snowScale)
3097 local washableNode = self:getWashableNodeByCustomIndex(wheel)
3098 self:setNodeDirtColor(washableNode, r, g, b, true)
3099 end
3100 nodeData.saveToSavegameFunc = function(xmlFile, key)
3101 xmlFile:setValue(key.."#snowScale", nodeData.wheel.snowScale)
3102 end
3103
3104 return false, self.updateWheelDirtAmount, wheel, nodeData
3105 end
3106 end
3107 end
3108
3109 return superFunc(self, node)
3110end

writeWheelDataToStream

Description
Definition
writeWheelDataToStream()
Code
978function Wheels:writeWheelDataToStream(wheel, streamId)
979 local xDrive = wheel.netInfo.xDrive % (math.pi*2)
980 streamWriteUIntN(streamId, MathUtil.clamp(math.floor(xDrive / (math.pi*2) * 511), 0, 511), 9)
981
982 streamWriteUIntN(streamId, MathUtil.clamp(math.floor((wheel.netInfo.y - wheel.netInfo.sync.yMin) / wheel.netInfo.sync.yRange * 255), 0, 255), 8)
983
984 streamWriteUIntN(streamId, MathUtil.clamp(wheel.netInfo.suspensionLength*100, 0, 128), 7)
985
986 if wheel.syncContactState then
987 streamWriteUIntN(streamId, wheel.contact, 2)
988 streamWriteBool(streamId, wheel.lastContactObjectAllowsTireTracks)
989 end
990
991 if wheel.versatileYRot then
992 local yRot = wheel.steeringAngle % (math.pi*2)
993 streamWriteUIntN(streamId, MathUtil.clamp(math.floor(yRot / (math.pi*2) * 511), 0, 511), 9)
994 end
995
996 streamWriteUIntN(streamId, wheel.lastTerrainValue, 3)
997end