LUADOC - Farming Simulator 22

VehicleCamera

Description
Camera for vehicles
Functions

actionEventLookLeftRight

Description
Definition
actionEventLookLeftRight()
Code
736function VehicleCamera:actionEventLookLeftRight(actionName, inputValue, callbackState, isAnalog, isMouse)
737 if isMouse then
738 inputValue = inputValue * 0.001 * 16.666
739 else
740 inputValue = inputValue * 0.001 * g_currentDt
741 end
742 self.lastInputValues.leftRight = self.lastInputValues.leftRight + inputValue
743end

actionEventLookUpDown

Description
Definition
actionEventLookUpDown()
Code
699function VehicleCamera:actionEventLookUpDown(actionName, inputValue, callbackState, isAnalog, isMouse)
700 if isMouse then
701 inputValue = inputValue * 0.001 * 16.666
702 else
703 inputValue = inputValue * 0.001 * g_currentDt
704 end
705 self.lastInputValues.upDown = self.lastInputValues.upDown + inputValue
706end

consoleCommandLODDebug

Description
Definition
consoleCommandLODDebug()
Code
659function VehicleCamera:consoleCommandLODDebug()
660 if not self.lodDebugMode then
661 self.transMaxOrig = self.transMax
662 self.transMax = 350
663 self.lodDebugMode = true
664 self.loadDebugZoom = self.zoom
665 else
666 self.lodDebugMode = false
667 self.transMax = self.transMaxOrig
668 self.zoomTarget = self.zoomDefault
669 self.zoom = self.zoomDefault
670 setFovY(self.cameraNode, self.fovY)
671 end
672end

delete

Description
Deleting vehicle camera
Definition
delete()
Code
328function VehicleCamera:delete()
329 self:onDeactivate()
330
331 if self.cameraNode ~= nil and self.positionSmoothingParameter > 0 then
332 delete(self.cameraNode)
333 self.cameraNode = nil
334 end
335
336 g_messageCenter:unsubscribe(MessageType.SETTING_CHANGED[GameSettings.SETTING.ACTIVE_SUSPENSION_CAMERA], self)
337 g_messageCenter:unsubscribe(MessageType.SETTING_CHANGED[GameSettings.SETTING.CAMERA_CHECK_COLLISION], self)
338end

getCollisionDistance

Description
Get distance to collision
Definition
getCollisionDistance()
Return Values
booleanhasCollisionhas collision
floatcollisionDistancedistance to collision
floatnormalXnormal x
floatnormalYnormal y
floatnormalZnormal z
floatnormalDotDirnormal dot direction
Code
876function VehicleCamera:getCollisionDistance()
877 if not self.isCollisionEnabled then
878 return false, nil, nil, nil, nil, nil
879 end
880
881 local raycastMask = VehicleCamera.raycastMask
882
883 local targetCamX, targetCamY, targetCamZ = localToWorld(self.rotateNode, self.transDirX*self.zoomTarget, self.transDirY*self.zoomTarget, self.transDirZ*self.zoomTarget)
884
885 local hasCollision = false
886 local collisionDistance = -1
887 local normalX,normalY,normalZ
888 local normalDotDir
889 for _, raycastNode in ipairs(self.raycastNodes) do
890
891 hasCollision = false
892
893 local nodeX, nodeY, nodeZ = getWorldTranslation(raycastNode)
894 local dirX, dirY, dirZ = targetCamX-nodeX, targetCamY-nodeY, targetCamZ-nodeZ
895 local dirLength = MathUtil.vector3Length(dirX, dirY, dirZ)
896 dirX = dirX / dirLength
897 dirY = dirY / dirLength
898 dirZ = dirZ / dirLength
899
900 local startX = nodeX
901 local startY = nodeY
902 local startZ = nodeZ
903 local currentDistance = 0
904 local minDistance = self.transMin
905
906 while true do
907 if (dirLength-currentDistance) <= 0 then
908 break
909 end
910 self.raycastDistance = 0
911 raycastClosest(startX, startY, startZ, dirX, dirY, dirZ, "raycastCallback", dirLength-currentDistance, self, raycastMask, true)
912
913 if self.raycastDistance ~= 0 then
914 currentDistance = currentDistance + self.raycastDistance+0.001
915 local ndotd = MathUtil.dotProduct(self.normalX, self.normalY, self.normalZ, dirX, dirY, dirZ)
916
917 local isAttachedVehicle = false
918 local ignoreObject = false
919 local object = g_currentMission:getNodeObject(self.raycastTransformId)
920 if object ~= nil then
921 local vehicles = self.vehicle:getChildVehicles()
922 for i=1, #vehicles do
923 local vehicle = vehicles[i]
924
925 if object ~= vehicle then
926 local attached1 = object.getIsAttachedTo ~= nil and object:getIsAttachedTo(vehicle)
927 local attached2 = vehicle.getIsAttachedTo ~= nil and vehicle:getIsAttachedTo(object)
928 isAttachedVehicle = attached1 or attached2
929
930 local mountObject = object.dynamicMountObject or object.tensionMountObject or object.mountObject
931 if mountObject ~= nil and (mountObject == vehicle or mountObject.rootVehicle == vehicle) then
932 isAttachedVehicle = true
933 end
934 end
935
936 if isAttachedVehicle then
937 break
938 end
939 end
940 end
941
942 -- ignore cut trees that are loaded to a vehicle
943 if getHasClassId(self.raycastTransformId, ClassIds.SHAPE) and getSplitType(self.raycastTransformId) ~= 0 then
944 ignoreObject = true
945 end
946
947 if getHasTrigger(self.raycastTransformId) then
948 ignoreObject = true
949 end
950
951 if isAttachedVehicle or object == self.vehicle or ignoreObject then --isAttachedNode or isDynamicallyMounted then
952 if ndotd > 0 then
953 minDistance = math.max(minDistance, currentDistance)
954 end
955 else
956 hasCollision = true
957 -- we take the distance from the rotate node
958 if raycastNode == self.rotateNode then
959 normalX,normalY,normalZ = self.normalX, self.normalY, self.normalZ
960
961 -- for static buildings we allow less than min. distance
962 -- for all other objects we limit by min. camera translation (e.g. if you load a dynamic object onto a pickup truck)
963 if getRigidBodyType(self.raycastTransformId) == RigidBodyType.STATIC then
964 collisionDistance = currentDistance
965 else
966 collisionDistance = math.max(self.transMin, currentDistance)
967 end
968
969 normalDotDir = ndotd
970 end
971 break
972 end
973 startX = nodeX+dirX*currentDistance
974 startY = nodeY+dirY*currentDistance
975 startZ = nodeZ+dirZ*currentDistance
976 else
977 break
978 end
979 end
980 if not hasCollision then
981 break
982 end
983 end
984
985 return hasCollision, collisionDistance, normalX,normalY,normalZ, normalDotDir
986end

getTiltDirectionOffset

Description
Set separate camera pose
Definition
getTiltDirectionOffset()
Code
858function VehicleCamera:getTiltDirectionOffset()
859 if not self.isInside and g_gameSettings:getValue(GameSettings.SETTING.CAMERA_TILTING) and getHasTouchpad() then
860 local dx, dy, dz = getGravityDirection()
861 local tiltOffset = MathUtil.getHorizontalRotationFromDeviceGravity(dx, dy, dz)
862 return tiltOffset
863 end
864
865 return 0
866end

loadFromXML

Description
Load vehicle camera from xml file
Definition
loadFromXML(integer xmlFile, string key)
Arguments
integerxmlFileid of xml object
stringkeykey
Return Values
booleansuccesssuccess
Code
66function VehicleCamera:loadFromXML(xmlFile, key, savegame, cameraIndex)
67 XMLUtil.checkDeprecatedXMLElements(xmlFile, self.vehicle.configFileName, key .. "#index", "#node") -- FS17 to FS19
68
69 self.cameraNode = xmlFile:getValue(key .. "#node", nil, self.vehicle.components, self.vehicle.i3dMappings)
70 if self.cameraNode == nil or not getHasClassId(self.cameraNode, ClassIds.CAMERA) then
71 Logging.xmlWarning(xmlFile, "Invalid camera node for camera '%s'. Must be a camera type!", key)
72 return false
73 end
74
75 self.fovY = calculateFovY(self.cameraNode)
76 setFovY(self.cameraNode, self.fovY)
77
78 self.isRotatable = xmlFile:getValue(key .. "#rotatable", false)
79 self.limit = xmlFile:getValue(key .. "#limit", false)
80 if self.limit then
81 self.rotMinX = xmlFile:getValue(key .. "#rotMinX")
82 self.rotMaxX = xmlFile:getValue(key .. "#rotMaxX")
83
84 self.transMin = xmlFile:getValue(key .. "#transMin")
85 self.transMax = xmlFile:getValue(key .. "#transMax")
86
87 if self.transMax ~= nil then
88 self.transMax = math.max(self.transMin, self.transMax * Platform.gameplay.maxCameraZoomFactor)
89 end
90
91 if self.rotMinX == nil or self.rotMaxX == nil or self.transMin == nil or self.transMax == nil then
92 Logging.xmlWarning(xmlFile, "Missing 'rotMinX', 'rotMaxX', 'transMin' or 'transMax' for camera '%s'", key)
93 return false
94 end
95 end
96
97 self.isInside = xmlFile:getValue(key .. "#isInside", false)
98 self.allowHeadTracking = xmlFile:getValue(key .. "#allowHeadTracking", self.isInside)
99
100 self.shadowFocusBoxNode = xmlFile:getValue(key .. "#shadowFocusBox", nil, self.vehicle.components, self.vehicle.i3dMappings)
101 if self.shadowFocusBoxNode ~= nil and not getHasClassId(self.shadowFocusBoxNode, ClassIds.SHAPE) then
102 Logging.xmlWarning(xmlFile, "Invalid camera shadow focus box '%s'. Must be a shape and cpu mesh", getName(self.shadowFocusBoxNode))
103 self.shadowFocusBoxNode = nil
104 end
105
106 if self.isInside and self.shadowFocusBoxNode == nil then
107 Logging.xmlDevWarning(xmlFile, "Missing shadow focus box for indoor camera '%s'", key)
108 end
109
110 self.useOutdoorSounds = xmlFile:getValue(key .. "#useOutdoorSounds", not self.isInside)
111
112 if self.isRotatable then
113 self.rotateNode = xmlFile:getValue(key .. "#rotateNode", nil, self.vehicle.components, self.vehicle.i3dMappings)
114 self.hasExtraRotationNode = self.rotateNode ~= nil
115 end
116
117 local rotation = xmlFile:getValue(key.."#rotation", nil, true)
118 if rotation ~= nil then
119 local rotationNode = self.cameraNode
120 if self.rotateNode ~= nil then
121 rotationNode = self.rotateNode
122 end
123 setRotation(rotationNode, unpack(rotation))
124 end
125 local translation = xmlFile:getValue(key.."#translation", nil, true)
126 if translation ~= nil then
127 setTranslation(self.cameraNode, unpack(translation))
128 end
129
130 self.allowTranslation = (self.rotateNode ~= nil and self.rotateNode ~= self.cameraNode)
131
132 self.useMirror = xmlFile:getValue(key .. "#useMirror", false)
133 self.useWorldXZRotation = xmlFile:getValue(key .. "#useWorldXZRotation") -- overrides the ingame setting
134 self.resetCameraOnVehicleSwitch = xmlFile:getValue(key .. "#resetCameraOnVehicleSwitch") -- overrides the ingame setting
135 self.suspensionNodeIndex = xmlFile:getValue(key .. "#suspensionNodeIndex")
136
137 if (not Platform.gameplay.useWorldCameraInside and self.isInside) or
138 (not Platform.gameplay.useWorldCameraOutside and not self.isInside) then
139 self.useWorldXZRotation = false
140 end
141
142 self.positionSmoothingParameter = 0
143 self.lookAtSmoothingParameter = 0
144 local useDefaultPositionSmoothing = xmlFile:getValue(key .. "#useDefaultPositionSmoothing", true)
145 if useDefaultPositionSmoothing then
146 if self.isInside then
147 self.positionSmoothingParameter = 0.128 -- 0.095
148 self.lookAtSmoothingParameter = 0.176 -- 0.12
149 else
150 self.positionSmoothingParameter = 0.016
151 self.lookAtSmoothingParameter = 0.022
152 end
153 end
154 self.positionSmoothingParameter = xmlFile:getValue(key .. "#positionSmoothingParameter", self.positionSmoothingParameter)
155 self.lookAtSmoothingParameter = xmlFile:getValue(key .. "#lookAtSmoothingParameter", self.lookAtSmoothingParameter)
156
157 local useHeadTracking = g_gameSettings:getValue("isHeadTrackingEnabled") and isHeadTrackingAvailable() and self.allowHeadTracking
158 if useHeadTracking then
159 self.positionSmoothingParameter = 0
160 self.lookAtSmoothingParameter = 0
161 end
162
163 self.cameraPositionNode = self.cameraNode
164 if self.positionSmoothingParameter > 0 then
165 -- create a node which indicates the target position of the camera
166 self.cameraPositionNode = createTransformGroup("cameraPositionNode")
167 local camIndex = getChildIndex(self.cameraNode)
168 link(getParent(self.cameraNode), self.cameraPositionNode, camIndex)
169 local x,y,z = getTranslation(self.cameraNode)
170 local rx,ry,rz = getRotation(self.cameraNode)
171 setTranslation(self.cameraPositionNode, x, y, z)
172 setRotation(self.cameraPositionNode, rx, ry, rz)
173
174 unlink(self.cameraNode)
175 end
176 self.rotYSteeringRotSpeed = xmlFile:getValue(key .. "#rotYSteeringRotSpeed", 0)
177
178 if self.rotateNode == nil or self.rotateNode == self.cameraNode then
179 self.rotateNode = self.cameraPositionNode
180 end
181
182 if useHeadTracking then
183 local dx,_,dz = localDirectionToLocal(self.cameraPositionNode, getParent(self.cameraPositionNode), 0, 0, 1)
184 local tx,ty,tz = localToLocal(self.cameraPositionNode, getParent(self.cameraPositionNode), 0, 0, 0)
185 self.headTrackingNode = createTransformGroup("headTrackingNode")
186 link(getParent(self.cameraPositionNode), self.headTrackingNode)
187 setTranslation(self.headTrackingNode, tx, ty, tz)
188 if math.abs(dx)+math.abs(dz) > 0.0001 then
189 setDirection(self.headTrackingNode, dx, 0, dz, 0, 1, 0)
190 else
191 setRotation(self.headTrackingNode, 0, 0, 0)
192 end
193 end
194
195 self.origRotX, self.origRotY, self.origRotZ = getRotation(self.rotateNode)
196 self.rotX = self.origRotX
197 self.rotY = self.origRotY
198 self.rotZ = self.origRotZ
199
200 self.origTransX, self.origTransY, self.origTransZ = getTranslation(self.cameraPositionNode)
201 self.transX = self.origTransX
202 self.transY = self.origTransY
203 self.transZ = self.origTransZ
204
205 local transLength = MathUtil.vector3Length(self.origTransX, self.origTransY, self.origTransZ) + 0.00001 -- prevent devision by zero
206 self.zoom = transLength
207 self.zoomTarget = transLength
208 self.zoomDefault = transLength
209 self.zoomLimitedTarget = -1
210
211 local trans1OverLength = 1.0/transLength
212 self.transDirX = trans1OverLength*self.origTransX
213 self.transDirY = trans1OverLength*self.origTransY
214 self.transDirZ = trans1OverLength*self.origTransZ
215 if self.allowTranslation then
216 if transLength <= 0.01 then
217 Logging.xmlWarning(xmlFile, "Invalid camera translation for camera '%s'. Distance needs to be bigger than 0.01", key)
218 end
219 end
220
221 table.insert(self.raycastNodes, self.rotateNode)
222 local i=0
223 while true do
224 local raycastKey = key..string.format(".raycastNode(%d)", i)
225 if not xmlFile:hasProperty(raycastKey) then
226 break
227 end
228
229 XMLUtil.checkDeprecatedXMLElements(xmlFile, self.vehicle.configFileName, raycastKey .. "#index", raycastKey .. "#node") --FS17 to FS19
230
231 local node = xmlFile:getValue(raycastKey .. "#node", nil, self.vehicle.components, self.vehicle.i3dMappings)
232 if node ~= nil then
233 table.insert(self.raycastNodes, node)
234 end
235
236 i = i + 1
237 end
238
239 local sx, sy, sz = getScale(self.cameraNode)
240 if sx ~= 1 or sy ~= 1 or sz ~= 1 then
241 Logging.xmlWarning(xmlFile, "Vehicle camera with scale found for camera '%s'. Resetting to scale 1", key)
242 setScale(self.cameraNode, 1, 1, 1)
243 end
244
245 self.headTrackingPositionOffset = {0, 0, 0}
246 self.headTrackingRotationOffset = {0, 0, 0}
247
248 self.changeObjects = {}
249 ObjectChangeUtil.loadObjectChangeFromXML(xmlFile, key, self.changeObjects, self.vehicle.components, self.vehicle)
250 ObjectChangeUtil.setObjectChanges(self.changeObjects, false, self.vehicle, self.vehicle.setMovingToolDirty)
251
252
253 if not g_gameSettings:getValue("resetCamera") then
254 if savegame ~= nil and not savegame.resetVehicles then
255 local cameraKey = string.format(savegame.key..".enterable.camera(%d)", cameraIndex)
256 if savegame.xmlFile:hasProperty(cameraKey) then
257 self.rotX, self.rotY, self.rotZ = savegame.xmlFile:getValue(cameraKey.."#rotation", {self.rotX, self.rotY, self.rotZ})
258 if self.allowTranslation then
259 self.transX, self.transY, self.transZ = savegame.xmlFile:getValue(cameraKey.."#translation", {self.transX, self.transY, self.transZ})
260
261 self.zoom = savegame.xmlFile:getValue(cameraKey.."#zoom", self.zoom)
262 self.zoomTarget = self.zoom
263 end
264
265 setTranslation(self.cameraPositionNode, self.transX, self.transY, self.transZ)
266 setRotation(self.rotateNode, self.rotX, self.rotY, self.rotZ)
267
268 if g_currentMission.isReloadingVehicles then
269 local fovY = savegame.xmlFile:getValue(cameraKey.."#fovY")
270 if fovY ~= nil then
271 setFovY(self.cameraNode, fovY)
272 end
273 end
274
275--#debug local lodDebugActive = savegame.xmlFile:getValue(cameraKey.."#lodDebugActive", false)
276--#debug if lodDebugActive then
277--#debug self:consoleCommandLODDebug()
278--#debug self.loadDebugZoom = savegame.xmlFile:getValue(cameraKey.."#lodDebugZoom", self.zoom)
279--#debug end
280 end
281 end
282 end
283
284 return true
285end

new

Description
Creating vehicle camera
Definition
new(boolean isServer, boolean isClient, table customMt)
Arguments
booleanisServeris server
booleanisClientis client
tablecustomMtcustom metatable
Return Values
tableselfInstance of object
Code
23function VehicleCamera.new(vehicle, customMt)
24 local self = setmetatable({}, customMt or VehicleCamera_mt)
25
26 self.vehicle = vehicle
27 self.isActivated = false
28
29 self.limitRotXDelta = 0
30
31 self.raycastDistance = 0
32 self.normalX = 0
33 self.normalY = 0
34 self.normalZ = 0
35
36 self.raycastNodes = {}
37 self.disableCollisionTime = -1
38
39 self.lookAtPosition = {0,0,0}
40 self.lookAtLastTargetPosition = {0,0,0}
41 self.position = {0,0,0}
42 self.lastTargetPosition = {0,0,0}
43 self.upVector = {0,0,0}
44 self.lastUpVector = {0,0,0}
45
46 self.lastInputValues = {}
47 self.lastInputValues.upDown = 0
48 self.lastInputValues.leftRight = 0
49
50 self.isCollisionEnabled = true
51 if g_modIsLoaded["FS22_disableVehicleCameraCollision"] then
52 self.isCollisionEnabled = g_gameSettings:getValue("cameraCheckCollision")
53 g_messageCenter:subscribe(MessageType.SETTING_CHANGED[GameSettings.SETTING.CAMERA_CHECK_COLLISION], self.onCameraCollisionDetectionSettingChanged, self)
54 end
55
56 g_messageCenter:subscribe(MessageType.SETTING_CHANGED[GameSettings.SETTING.ACTIVE_SUSPENSION_CAMERA], self.onActiveCameraSuspensionSettingChanged, self)
57
58 return self
59end

onActivate

Description
Called on activate
Definition
onActivate()
Code
591function VehicleCamera:onActivate()
592 if self.cameraNode == nil then
593 return
594 end
595
596 self:onActiveCameraSuspensionSettingChanged(g_gameSettings:getValue("activeSuspensionCamera"))
597
598 self.isActivated = true
599 if (self.resetCameraOnVehicleSwitch == nil and g_gameSettings:getValue("resetCamera")) or self.resetCameraOnVehicleSwitch then
600 self:resetCamera()
601 end
602 setCamera(self.cameraNode)
603 if self.shadowFocusBoxNode then
604 setShadowFocusBox(self.shadowFocusBoxNode)
605 end
606
607 if self.positionSmoothingParameter > 0 then
608 local xlook,ylook,zlook = getWorldTranslation(self.rotateNode)
609 self.lookAtPosition[1] = xlook
610 self.lookAtPosition[2] = ylook
611 self.lookAtPosition[3] = zlook
612 self.lookAtLastTargetPosition[1] = xlook
613 self.lookAtLastTargetPosition[2] = ylook
614 self.lookAtLastTargetPosition[3] = zlook
615 local x,y,z = getWorldTranslation(self.cameraPositionNode)
616 self.position[1] = x
617 self.position[2] = y
618 self.position[3] = z
619 self.lastTargetPosition[1] = x
620 self.lastTargetPosition[2] = y
621 self.lastTargetPosition[3] = z
622 local upx, upy, upz = localDirectionToWorld(self.rotateNode, self:getTiltDirectionOffset(), 1, 0)
623 self.upVector[1] = upx
624 self.upVector[2] = upy
625 self.upVector[3] = upz
626 self.lastUpVector[1] = upx
627 self.lastUpVector[2] = upy
628 self.lastUpVector[3] = upz
629
630 local rx,ry,rz = getWorldRotation(self.rotateNode)
631
632 setRotation(self.cameraNode, rx,ry,rz)
633 setTranslation(self.cameraNode, x,y,z)
634 end
635
636 self.lastInputValues = {}
637 self.lastInputValues.upDown = 0
638 self.lastInputValues.leftRight = 0
639
640 -- activate action event callbacks
641 local _, actionEventId1 = g_inputBinding:registerActionEvent(InputAction.AXIS_LOOK_UPDOWN_VEHICLE, self, VehicleCamera.actionEventLookUpDown, false, false, true, true, nil)
642 local _, actionEventId2 = g_inputBinding:registerActionEvent(InputAction.AXIS_LOOK_LEFTRIGHT_VEHICLE, self, VehicleCamera.actionEventLookLeftRight, false, false, true, true, nil)
643 g_inputBinding:setActionEventTextVisibility(actionEventId1, false)
644 g_inputBinding:setActionEventTextVisibility(actionEventId2, false)
645
646 ObjectChangeUtil.setObjectChanges(self.changeObjects, true, self.vehicle, self.vehicle.setMovingToolDirty)
647
648 if g_touchHandler ~= nil then
649 self.touchListenerPinch = g_touchHandler:registerGestureListener(TouchHandler.GESTURE_PINCH, VehicleCamera.touchEventZoomInOut, self)
650 self.touchListenerY = g_touchHandler:registerGestureListener(TouchHandler.GESTURE_AXIS_Y, VehicleCamera.touchEventLookUpDown, self)
651 self.touchListenerX = g_touchHandler:registerGestureListener(TouchHandler.GESTURE_AXIS_X, VehicleCamera.touchEventLookLeftRight, self)
652 end
653
654 --#debug addConsoleCommand("gsVehicleDebugLOD", "Enables vehicle LOD debug", "consoleCommandLODDebug", self)
655end

onActiveCameraSuspensionSettingChanged

Description
Called when camera suspension setting has changed
Definition
onActiveCameraSuspensionSettingChanged(bool newState)
Arguments
boolnewStatenew setting state
Code
991function VehicleCamera:onActiveCameraSuspensionSettingChanged(newState)
992 if self.suspensionNode ~= nil then
993 if self.lastActiveCameraSuspensionSetting ~= newState then
994 if newState then
995 link(self.cameraSuspensionParentNode, self.cameraPositionNode)
996 else
997 link(self.cameraBaseParentNode, self.cameraPositionNode)
998 end
999
1000 self.lastActiveCameraSuspensionSetting = newState
1001 end
1002 end
1003end

onCameraCollisionDetectionSettingChanged

Description
Called when camera collision detection setting has changed
Definition
onCameraCollisionDetectionSettingChanged(bool newState)
Arguments
boolnewStatenew setting state
Code
1008function VehicleCamera:onCameraCollisionDetectionSettingChanged(newState)
1009 self.isCollisionEnabled = newState
1010end

onDeactivate

Description
Called on deactivate
Definition
onDeactivate()
Code
676function VehicleCamera:onDeactivate()
677 self.isActivated = false
678 setShadowFocusBox(0)
679
680 -- remove action event callbacks
681 g_inputBinding:removeActionEventsByTarget(self)
682
683 ObjectChangeUtil.setObjectChanges(self.changeObjects, false, self.vehicle, self.vehicle.setMovingToolDirty)
684
685 if g_touchHandler ~= nil then
686 g_touchHandler:removeGestureListener(self.touchListenerPinch)
687 g_touchHandler:removeGestureListener(self.touchListenerY)
688 g_touchHandler:removeGestureListener(self.touchListenerX)
689 end
690
691--#debug removeConsoleCommand("gsVehicleDebugLOD")
692--#debug if self.lodDebugMode then
693--#debug self:consoleCommandLODDebug()
694--#debug end
695end

onPostLoad

Description
Called after loading
Definition
onPostLoad(table savegame)
Arguments
tablesavegamesavegame data
Code
290function VehicleCamera:onPostLoad(savegame)
291 self.suspensionNode = nil
292 if self.suspensionNodeIndex ~= nil and self.vehicle.getSuspensionNodeFromIndex ~= nil then
293 self.suspensionNode = self.vehicle:getSuspensionNodeFromIndex(self.suspensionNodeIndex)
294 end
295 if self.suspensionNode ~= nil then
296 if self.suspensionNode.node ~= nil then
297 self.cameraSuspensionParentNode = createTransformGroup("cameraSuspensionParentNode")
298 link(self.suspensionNode.node, self.cameraSuspensionParentNode)
299 setWorldTranslation(self.cameraSuspensionParentNode, getWorldTranslation(getParent(self.cameraPositionNode)))
300 setWorldQuaternion(self.cameraSuspensionParentNode, getWorldQuaternion(getParent(self.cameraPositionNode)))
301
302 self.cameraBaseParentNode = getParent(self.cameraPositionNode)
303
304 self.lastActiveCameraSuspensionSetting = false
305 else
306 Logging.warning("Vehicle Camera '%s' with invalid suspensionIndex '%s' found. CharacterTorso suspensions are not allowed.", getName(self.cameraNode), self.suspensionNodeIndex)
307 self.suspensionNode = nil
308 end
309 end
310end

raycastCallback

Description
Raycast callback
Definition
raycastCallback(integer transformId, float x, float y, float z, float distance, float nx, float ny, float nz)
Arguments
integertransformIdid raycasted object
floatxx raycast position
floatyy raycast position
floatzz raycast position
floatdistancedistance to raycast position
floatnxnormal x
floatnynormal y
floatnznormal z
Code
372function VehicleCamera:raycastCallback(transformId, x, y, z, distance, nx, ny, nz)
373 self.raycastDistance = distance
374 self.normalX = nx
375 self.normalY = ny
376 self.normalZ = nz
377 self.raycastTransformId = transformId
378end

registerCameraSavegameXMLPaths

Description
Definition
registerCameraSavegameXMLPaths()
Code
1050function VehicleCamera.registerCameraSavegameXMLPaths(schema, basePath)
1051 schema:register(XMLValueType.VECTOR_ROT, basePath .. "#rotation", "Camera rotation")
1052 schema:register(XMLValueType.VECTOR_TRANS, basePath .. "#translation", "Camera translation")
1053 schema:register(XMLValueType.FLOAT, basePath .. "#zoom", "Camera zoom")
1054 schema:register(XMLValueType.FLOAT, basePath .. "#fovY", "Custom Field of View Y")
1055 schema:register(XMLValueType.BOOL, basePath .. "#lodDebugActive", "LOD Debug Mode Active")
1056 schema:register(XMLValueType.FLOAT, basePath .. "#lodDebugZoom", "LOD Debug Mode Zoom Ref")
1057end

registerCameraXMLPaths

Description
Definition
registerCameraXMLPaths()
Code
1014function VehicleCamera.registerCameraXMLPaths(schema, basePath)
1015 schema:register(XMLValueType.NODE_INDEX, basePath .. "#node", "Camera node")
1016 schema:register(XMLValueType.BOOL, basePath .. "#rotatable", "Camera is rotatable", false)
1017 schema:register(XMLValueType.BOOL, basePath .. "#limit", "Has limits", false)
1018 schema:register(XMLValueType.FLOAT, basePath .. "#rotMinX", "Min. X rotation")
1019 schema:register(XMLValueType.FLOAT, basePath .. "#rotMaxX", "Max. X rotation")
1020 schema:register(XMLValueType.FLOAT, basePath .. "#transMin", "Min. Z translation")
1021 schema:register(XMLValueType.FLOAT, basePath .. "#transMax", "Max. Z translation")
1022
1023 schema:register(XMLValueType.BOOL, basePath .. "#isInside", "Is camera inside. Used for camera smoothing and fallback/default value for 'useOutdoorSounds'", false)
1024 schema:register(XMLValueType.BOOL, basePath .. "#allowHeadTracking", "Allow head tracking", "isInside value")
1025 schema:register(XMLValueType.NODE_INDEX, basePath .. "#shadowFocusBox", "Shadow focus box")
1026
1027 schema:register(XMLValueType.BOOL, basePath .. "#useOutdoorSounds", "Use outdoor sounds", "false for 'isInside' cameras, otherwise true")
1028 schema:register(XMLValueType.NODE_INDEX, basePath .. "#rotateNode", "Rotate node")
1029 schema:register(XMLValueType.VECTOR_ROT, basePath .. "#rotation", "Camera rotation")
1030 schema:register(XMLValueType.VECTOR_TRANS, basePath .. "#translation", "Camera translation")
1031
1032 schema:register(XMLValueType.BOOL, basePath .. "#useMirror", "Use mirrors", false)
1033 schema:register(XMLValueType.BOOL, basePath .. "#useWorldXZRotation", "Use world XZ rotation")
1034 schema:register(XMLValueType.BOOL, basePath .. "#resetCameraOnVehicleSwitch", "Reset camera on vehicle switch")
1035 schema:register(XMLValueType.INT, basePath .. "#suspensionNodeIndex", "Index of seat suspension node")
1036 schema:register(XMLValueType.BOOL, basePath .. "#useDefaultPositionSmoothing", "Use default position smoothing parameters", true)
1037
1038 schema:register(XMLValueType.FLOAT, basePath .. "#positionSmoothingParameter", "Position smoothing parameter", "0.128 for indoor / 0.016 for outside")
1039 schema:register(XMLValueType.FLOAT, basePath .. "#lookAtSmoothingParameter", "Look at smoothing parameter", "0.176 for indoor / 0.022 for outside")
1040
1041 schema:register(XMLValueType.ANGLE, basePath .. "#rotYSteeringRotSpeed", "Rot Y steering rotation speed", 0)
1042
1043 schema:register(XMLValueType.NODE_INDEX, basePath .. ".raycastNode(?)#node", "Raycast node")
1044
1045 ObjectChangeUtil.registerObjectChangeXMLPaths(schema, basePath)
1046end

resetCamera

Description
Reset camera to original pose
Definition
resetCamera()
Code
748function VehicleCamera:resetCamera()
749 self.rotX = self.origRotX
750 self.rotY = self.origRotY
751 self.rotZ = self.origRotZ
752
753 self.transX = self.origTransX
754 self.transY = self.origTransY
755 self.transZ = self.origTransZ
756
757 local transLength = MathUtil.vector3Length(self.origTransX, self.origTransY, self.origTransZ)
758 self.zoom = transLength
759 self.zoomTarget = transLength
760 self.zoomLimitedTarget = -1
761
762 self:updateRotateNodeRotation()
763 setTranslation(self.cameraPositionNode, self.transX, self.transY, self.transZ)
764
765 if self.positionSmoothingParameter > 0 then
766 local xlook,ylook,zlook = getWorldTranslation(self.rotateNode)
767 self.lookAtPosition[1] = xlook
768 self.lookAtPosition[2] = ylook
769 self.lookAtPosition[3] = zlook
770 local x,y,z = getWorldTranslation(self.cameraPositionNode)
771 self.position[1] = x
772 self.position[2] = y
773 self.position[3] = z
774
775 self:setSeparateCameraPose()
776 end
777end

saveToXMLFile

Description
Definition
saveToXMLFile()
Code
314function VehicleCamera:saveToXMLFile(xmlFile, key, usedModNames)
315 xmlFile:setValue(key .. "#rotation", self.rotX, self.rotY, self.rotZ)
316 xmlFile:setValue(key .. "#translation", self.transX, self.transY, self.transZ)
317 xmlFile:setValue(key .. "#zoom", self.zoom)
318 xmlFile:setValue(key .. "#fovY", getFovY(self.cameraNode))
319
320--#debug if self.lodDebugMode then
321--#debug xmlFile:setValue(key .. "#lodDebugActive", true)
322--#debug xmlFile:setValue(key .. "#lodDebugZoom", self.loadDebugZoom)
323--#debug end
324end

setSeparateCameraPose

Description
Set separate camera pose
Definition
setSeparateCameraPose()
Code
818function VehicleCamera:setSeparateCameraPose()
819 if self.rotateNode ~= self.cameraPositionNode then
820 local dx = self.position[1] - self.lookAtPosition[1]
821 local dy = self.position[2] - self.lookAtPosition[2]
822 local dz = self.position[3] - self.lookAtPosition[3]
823
824 local upx, upy, upz = unpack(self.upVector)
825 if upx == 0 and upy == 0 and upz == 0 then
826 upy = 1
827 end
828
829 if math.abs(dx) < 0.001 and math.abs(dz) < 0.001 then
830 upx = 0.1
831 end
832
833 setDirection(self.cameraNode, dx,dy,dz, upx,upy,upz)
834 else
835 local dx, dy, dz = localDirectionToWorld(self.rotateNode, 0, 0, 1)
836 local upx, upy, upz = localDirectionToWorld(self.rotateNode, self:getTiltDirectionOffset(), 1, 0)
837 setDirection(self.cameraNode, dx, dy, dz, upx, upy, upz)
838 end
839 setTranslation(self.cameraNode, self.position[1],self.position[2],self.position[3])
840
841--#debug if self.lodDebugMode then
842--#debug local _, _ , curZoom = localToLocal(self.cameraNode, self.rotateNode, 0, 0, 0)
843--#debug local l = math.atan(self.fovY) * self.loadDebugZoom
844--#debug local mouseButtonLast, mouseButtonStateLast = g_inputBinding:getMouseButtonState()
845--#debug if mouseButtonStateLast and mouseButtonLast == Input.MOUSE_BUTTON_MIDDLE then
846--#debug setFovY(self.cameraNode, self.fovY)
847--#debug else
848--#debug setFovY(self.cameraNode, math.tan(l / math.max(curZoom, l)))
849--#debug end
850--#debug setTextAlignment(RenderText.ALIGN_CENTER)
851--#debug renderText(0.5, 0.1, 0.04, string.format("Distance: %d", self.zoom))
852--#debug setTextAlignment(RenderText.ALIGN_LEFT)
853--#debug end
854end

touchEventLookLeftRight

Description
Definition
touchEventLookLeftRight()
Code
727function VehicleCamera:touchEventLookLeftRight(value)
728 if self.isActivated then
729 local factor = (g_screenWidth / g_screenHeight) * 150
730 VehicleCamera.actionEventLookLeftRight(self, nil, value * factor, nil, nil, false)
731 end
732end

touchEventLookUpDown

Description
Definition
touchEventLookUpDown()
Code
710function VehicleCamera:touchEventLookUpDown(value)
711 if self.isActivated then
712 local factor = (g_screenHeight / g_screenWidth) * -150
713 VehicleCamera.actionEventLookUpDown(self, nil, value * factor, nil, nil, false)
714 end
715end

touchEventZoomInOut

Description
Definition
touchEventZoomInOut()
Code
719function VehicleCamera:touchEventZoomInOut(value)
720 if self.isActivated then
721 self:zoomSmoothly(value * 15)
722 end
723end

update

Description
Update
Definition
update(float dt)
Arguments
floatdttime since last call in ms
Code
383function VehicleCamera:update(dt)
384 local target = self.zoomTarget
385 if self.zoomLimitedTarget >= 0 then
386 target = math.min(self.zoomLimitedTarget, self.zoomTarget)
387 end
388 self.zoom = target + ( math.pow(0.99579, dt) * (self.zoom - target) )
389
390 --
391 if self.lastInputValues.upDown ~= 0 then
392 local value = self.lastInputValues.upDown * g_gameSettings:getValue(GameSettings.SETTING.CAMERA_SENSITIVITY)
393 self.lastInputValues.upDown = 0
394 value = g_gameSettings:getValue("invertYLook") and -value or value
395
396 if self.isRotatable then
397 if self.isActivated and not g_gui:getIsGuiVisible() then
398 if self.limitRotXDelta > 0.001 then
399 self.rotX = math.min(self.rotX - value, self.rotX)
400 elseif self.limitRotXDelta < -0.001 then
401 self.rotX = math.max(self.rotX - value, self.rotX)
402 else
403 self.rotX = self.rotX - value
404 end
405
406 if self.limit then
407 self.rotX = math.min(self.rotMaxX, math.max(self.rotMinX, self.rotX))
408 end
409 end
410 end
411 end
412
413 if self.lastInputValues.leftRight ~= 0 then
414 local value = self.lastInputValues.leftRight * g_gameSettings:getValue(GameSettings.SETTING.CAMERA_SENSITIVITY)
415 self.lastInputValues.leftRight = 0
416
417 if self.isRotatable then
418 if self.isActivated and not g_gui:getIsGuiVisible() then
419 self.rotY = self.rotY - value
420 end
421 end
422 end
423
424 --
425 if g_gameSettings:getValue("isHeadTrackingEnabled") and isHeadTrackingAvailable() and self.allowHeadTracking and self.headTrackingNode ~= nil then
426 local tx,ty,tz = getHeadTrackingTranslation()
427 local pitch,yaw,roll = getHeadTrackingRotation()
428 if pitch ~= nil then
429 local camParent = getParent(self.cameraNode)
430 local ctx,cty,ctz
431 local crx,cry,crz
432 if camParent ~= 0 then
433 ctx, cty, ctz = localToLocal(self.headTrackingNode, camParent, tx, ty, tz)
434 crx, cry, crz = localRotationToLocal(self.headTrackingNode, camParent, pitch,yaw,roll)
435 else
436 ctx, cty, ctz = localToWorld(self.headTrackingNode, tx, ty, tz)
437 crx, cry, crz = localRotationToWorld(self.headTrackingNode, pitch,yaw,roll)
438 end
439
440 setRotation(self.cameraNode, crx, cry, crz)
441 setTranslation(self.cameraNode, ctx, cty, ctz)
442 end
443 else
444 self:updateRotateNodeRotation()
445
446 if self.limit then
447 -- adjust rotation to avoid clipping with terrain
448 if self.isRotatable and ((self.useWorldXZRotation == nil and g_gameSettings:getValue("useWorldCamera")) or self.useWorldXZRotation) then
449 local numIterations = 4
450 for _=1, numIterations do
451 local transX, transY, transZ = self.transDirX*self.zoom, self.transDirY*self.zoom, self.transDirZ*self.zoom
452 local x,y,z = localToWorld(getParent(self.cameraPositionNode), transX, transY, transZ)
453
454 local terrainHeight = DensityMapHeightUtil.getHeightAtWorldPos(x,0,z)
455
456 local minHeight = terrainHeight + 0.9
457 if y < minHeight then
458 local h = math.sin(self.rotX)*self.zoom
459 local h2 = h-(minHeight-y)
460 self.rotX = math.asin(MathUtil.clamp(h2/self.zoom, -1, 1))
461 self:updateRotateNodeRotation()
462 else
463 break
464 end
465 end
466 end
467
468 -- adjust zoom to avoid collision with objects
469 if self.allowTranslation then
470
471 self.limitRotXDelta = 0
472 local hasCollision, collisionDistance, nx,ny,nz, normalDotDir = self:getCollisionDistance()
473 if hasCollision then
474 local distOffset = 0.1
475 if normalDotDir ~= nil then
476 local absNormalDotDir = math.abs(normalDotDir)
477 distOffset = MathUtil.lerp(1.2, 0.1, absNormalDotDir*absNormalDotDir*(3-2*absNormalDotDir))
478 end
479 collisionDistance = math.max(collisionDistance-distOffset, 0.01)
480 self.disableCollisionTime = g_currentMission.time+400
481 self.zoomLimitedTarget = collisionDistance
482 if collisionDistance < self.zoom then
483 self.zoom = collisionDistance
484 end
485 if self.isRotatable and nx ~= nil and collisionDistance < self.transMin then
486 local _,lny,_ = worldDirectionToLocal(self.rotateNode, nx,ny,nz)
487 if lny > 0.5 then
488 self.limitRotXDelta = 1
489 elseif lny < -0.5 then
490 self.limitRotXDelta = -1
491 end
492 end
493 else
494 if self.disableCollisionTime <= g_currentMission.time then
495 self.zoomLimitedTarget = -1
496 end
497 end
498 end
499
500 end
501 self.transX, self.transY, self.transZ = self.transDirX*self.zoom, self.transDirY*self.zoom, self.transDirZ*self.zoom
502 setTranslation(self.cameraPositionNode, self.transX, self.transY, self.transZ)
503
504 if self.positionSmoothingParameter > 0 then
505
506 local interpDt = g_physicsDt
507
508 if self.vehicle.spec_rideable ~= nil then
509 interpDt = self.vehicle.spec_rideable.interpolationDt
510 end
511
512 if g_server == nil then
513 -- on clients, we interpolate the vehicles with dt, thus we need to use the same for camera interpolation
514 interpDt = dt
515 end
516 if interpDt > 0 then
517 local xlook,ylook,zlook = getWorldTranslation(self.rotateNode)
518 local lookAtPos = self.lookAtPosition
519 local lookAtLastPos = self.lookAtLastTargetPosition
520 lookAtPos[1],lookAtPos[2],lookAtPos[3] = self:getSmoothed(self.lookAtSmoothingParameter, lookAtPos[1],lookAtPos[2],lookAtPos[3], xlook,ylook,zlook, lookAtLastPos[1],lookAtLastPos[2],lookAtLastPos[3], interpDt)
521 lookAtLastPos[1],lookAtLastPos[2],lookAtLastPos[3] = xlook,ylook,zlook
522
523 local x,y,z = getWorldTranslation(self.cameraPositionNode)
524 local pos = self.position
525 local lastPos = self.lastTargetPosition
526 pos[1],pos[2],pos[3] = self:getSmoothed(self.positionSmoothingParameter, pos[1],pos[2],pos[3], x,y,z, lastPos[1],lastPos[2],lastPos[3], interpDt)
527 lastPos[1],lastPos[2],lastPos[3] = x,y,z
528
529 local upx, upy, upz = localDirectionToWorld(self.rotateNode, self:getTiltDirectionOffset(), 1, 0)
530 local up = self.upVector
531 local lastUp = self.lastUpVector
532 up[1],up[2],up[3] = self:getSmoothed(self.positionSmoothingParameter, up[1],up[2],up[3], upx, upy, upz, lastUp[1],lastUp[2],lastUp[3], interpDt)
533 lastUp[1],lastUp[2],lastUp[3] = upx, upy, upz
534
535 self:setSeparateCameraPose()
536 end
537 end
538
539 end
540
541end

updateRotateNodeRotation

Description
Update rotation node rotation
Definition
updateRotateNodeRotation()
Code
781function VehicleCamera:updateRotateNodeRotation()
782 local rotY = self.rotY
783 if self.rotYSteeringRotSpeed ~= nil and self.rotYSteeringRotSpeed ~= 0 and self.vehicle.spec_articulatedAxis ~= nil and self.vehicle.spec_articulatedAxis.interpolatedRotatedTime ~= nil then
784 rotY = rotY + self.vehicle.spec_articulatedAxis.interpolatedRotatedTime*self.rotYSteeringRotSpeed
785 end
786
787 if (self.useWorldXZRotation == nil and g_gameSettings:getValue("useWorldCamera")) or self.useWorldXZRotation then
788 local upx,upy,upz = 0,1,0
789
790 local dx,_,dz = localDirectionToWorld(getParent(self.rotateNode), 0,0,1)
791 local invLen = 1/math.sqrt(dx*dx + dz*dz)
792 dx = dx*invLen
793 dz = dz*invLen
794
795
796
797 local newDx = math.cos(self.rotX) * (math.cos(rotY)*dx + math.sin(rotY)*dz)
798 local newDy = -math.sin(self.rotX)
799 local newDz = math.cos(self.rotX) * (-math.sin(rotY)*dx + math.cos(rotY)*dz)
800
801
802 newDx,newDy,newDz = worldDirectionToLocal(getParent(self.rotateNode), newDx,newDy,newDz)
803 upx,upy,upz = worldDirectionToLocal(getParent(self.rotateNode), upx,upy,upz)
804
805 -- worst case check
806 if math.abs(MathUtil.dotProduct(newDx,newDy,newDz, upx,upy,upz)) > ( 0.99 * MathUtil.vector3Length(newDx,newDy,newDz) * MathUtil.vector3Length(upx,upy,upz) ) then
807 setRotation(self.rotateNode, self.rotX, rotY, self.rotZ)
808 else
809 setDirection(self.rotateNode, newDx,newDy,newDz, upx,upy,upz)
810 end
811 else
812 setRotation(self.rotateNode, self.rotX, rotY, self.rotZ)
813 end
814end

zoomSmoothly

Description
Zoom camera smoothly
Definition
zoomSmoothly(float offset)
Arguments
floatoffsetoffset
Code
343function VehicleCamera:zoomSmoothly(offset)
344 --self.zoomTarget = self.zoomTarget + offset
345 --if self.limit then
346 -- self.zoomTarget = math.min(self.transMax, math.max(self.transMin, self.zoomTarget))
347 --end
348 --self.zoomTarget = math.max(self.zoomTarget, 0.01)
349
350--#debug if self.lodDebugMode then
351--#debug offset = offset * 10
352--#debug end
353
354 local zoomTarget = self.zoomTarget
355 if self.transMin ~= nil and self.transMax ~= nil and self.transMin ~= self.transMax then
356 zoomTarget = math.min(self.transMax, math.max(self.transMin, self.zoomTarget + offset))
357 end
358 self.zoomTarget = zoomTarget
359
360end