LUADOC - Farming Simulator 19

VehicleCamera

Description
Camera for vehicles
Functions

actionEventLookLeftRight

Description
Definition
actionEventLookLeftRight()
Code
570function VehicleCamera:actionEventLookLeftRight(actionName, inputValue, callbackState, isAnalog, isMouse)
571 if isMouse then
572 inputValue = inputValue * 0.001 * 16.666
573 else
574 inputValue = inputValue * 0.001 * g_currentDt
575 end
576 self.lastInputValues.leftRight = self.lastInputValues.leftRight + inputValue
577end

actionEventLookUpDown

Description
Definition
actionEventLookUpDown()
Code
559function VehicleCamera:actionEventLookUpDown(actionName, inputValue, callbackState, isAnalog, isMouse)
560 if isMouse then
561 inputValue = inputValue * 0.001 * 16.666
562 else
563 inputValue = inputValue * 0.001 * g_currentDt
564 end
565 self.lastInputValues.upDown = self.lastInputValues.upDown + inputValue
566end

delete

Description
Deleting vehicle camera
Definition
delete()
Code
250function VehicleCamera:delete()
251 if self.cameraNode ~= nil and self.positionSmoothingParameter > 0 then
252 delete(self.cameraNode)
253 self.cameraNode = nil
254 end
255 setShadowFocusBox(0)
256end

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
679function VehicleCamera:getCollisionDistance()
680 if not self.isCollisionEnabled then
681 return false, nil, nil, nil, nil, nil
682 end
683
684 local raycastMask = 32+64+128+256+4096
685
686 local targetCamX, targetCamY, targetCamZ = localToWorld(self.rotateNode, self.transDirX*self.zoomTarget, self.transDirY*self.zoomTarget, self.transDirZ*self.zoomTarget)
687
688 local hasCollision = false
689 local collisionDistance = -1
690 local normalX,normalY,normalZ
691 local normalDotDir
692 for _, raycastNode in ipairs(self.raycastNodes) do
693
694 hasCollision = false
695
696 local nodeX, nodeY, nodeZ = getWorldTranslation(raycastNode)
697 local dirX, dirY, dirZ = targetCamX-nodeX, targetCamY-nodeY, targetCamZ-nodeZ
698 local dirLength = MathUtil.vector3Length(dirX, dirY, dirZ)
699 dirX = dirX / dirLength
700 dirY = dirY / dirLength
701 dirZ = dirZ / dirLength
702
703 local startX = nodeX
704 local startY = nodeY
705 local startZ = nodeZ
706 local currentDistance = 0
707 local minDistance = self.transMin
708
709 while true do
710 if (dirLength-currentDistance) <= 0 then
711 break
712 end
713 self.raycastDistance = 0
714 raycastClosest(startX, startY, startZ, dirX, dirY, dirZ, "raycastCallback", dirLength-currentDistance, self, raycastMask, true)
715
716 if self.raycastDistance ~= 0 then
717 currentDistance = currentDistance + self.raycastDistance+0.001
718 local ndotd = MathUtil.dotProduct(self.normalX, self.normalY, self.normalZ, dirX, dirY, dirZ)
719
720 local isAttachedVehicle = false
721 local ignoreObject = false
722 local object = g_currentMission:getNodeObject(self.raycastTransformId)
723 if object ~= nil then
724 if object ~= self.vehicle then
725 local attached1 = object.getIsAttachedTo ~= nil and object:getIsAttachedTo(self.vehicle)
726 local attached2 = self.vehicle.getIsAttachedTo ~= nil and self.vehicle:getIsAttachedTo(object)
727 isAttachedVehicle = attached1 or attached2
728
729 local mountObject = object.dynamicMountObject or object.tensionMountObject
730 if mountObject ~= nil and (mountObject == self.vehicle or mountObject:getRootVehicle() == self.vehicle) then
731 isAttachedVehicle = true
732 end
733 end
734 end
735
736 -- ignore cut trees that are loaded to a vehicle
737 if getHasClassId(self.raycastTransformId, ClassIds.SHAPE) and getSplitType(self.raycastTransformId) ~= 0 then
738 ignoreObject = true
739 end
740
741 if isAttachedVehicle or object == self.vehicle or ignoreObject then --isAttachedNode or isDynamicallyMounted then
742 if ndotd > 0 then
743 minDistance = math.max(minDistance, currentDistance)
744 end
745 else
746 hasCollision = true
747 -- we take the distance from the rotate node
748 if raycastNode == self.rotateNode then
749 normalX,normalY,normalZ = self.normalX, self.normalY, self.normalZ
750 collisionDistance = math.max(self.transMin, currentDistance)
751 normalDotDir = ndotd
752 end
753 break
754 end
755 startX = nodeX+dirX*currentDistance
756 startY = nodeY+dirY*currentDistance
757 startZ = nodeZ+dirZ*currentDistance
758 else
759 break
760 end
761 end
762 if not hasCollision then
763 break
764 end
765 end
766
767 return hasCollision, collisionDistance, normalX,normalY,normalZ, normalDotDir
768end

loadFromXML

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

new

Description
Creating vehicle camera
Definition
new(boolean isServer, boolean isClient, table customMt)
Arguments
booleanisServeris server
booleanisClientis client
tablecustomMtcustom metatable
Return Values
tableinstanceInstance of object
Code
22function VehicleCamera:new(vehicle, customMt)
23
24 local instance = {}
25 if customMt ~= nil then
26 setmetatable(instance, customMt)
27 else
28 setmetatable(instance, VehicleCamera_mt)
29 end
30
31
32 instance.vehicle = vehicle
33 instance.isActivated = false
34
35 instance.limitRotXDelta = 0
36
37 instance.raycastDistance = 0
38 instance.normalX = 0
39 instance.normalY = 0
40 instance.normalZ = 0
41
42 instance.raycastNodes = {}
43 instance.disableCollisionTime = -1
44
45 instance.lookAtPosition = {0,0,0}
46 instance.lookAtLastTargetPosition = {0,0,0}
47 instance.position = {0,0,0}
48 instance.lastTargetPosition = {0,0,0}
49
50 instance.lastInputValues = {}
51 instance.lastInputValues.upDown = 0
52 instance.lastInputValues.leftRight = 0
53
54 instance.isCollisionEnabled = not g_modIsLoaded["FS19_disableVehicleCameraCollision"]
55
56 return instance
57end

onActivate

Description
Called on activate
Definition
onActivate()
Code
502function VehicleCamera:onActivate()
503
504 self.isActivated = true
505 if (self.resetCameraOnVehicleSwitch == nil and g_gameSettings:getValue("resetCamera")) or self.resetCameraOnVehicleSwitch then
506 self:resetCamera()
507 end
508 setCamera(self.cameraNode)
509 if self.shadowFocusBoxNode then
510 setShadowFocusBox(self.shadowFocusBoxNode)
511 end
512
513 if self.positionSmoothingParameter > 0 then
514 local xlook,ylook,zlook = getWorldTranslation(self.rotateNode)
515 self.lookAtPosition[1] = xlook
516 self.lookAtPosition[2] = ylook
517 self.lookAtPosition[3] = zlook
518 self.lookAtLastTargetPosition[1] = xlook
519 self.lookAtLastTargetPosition[2] = ylook
520 self.lookAtLastTargetPosition[3] = zlook
521 local x,y,z = getWorldTranslation(self.cameraPositionNode)
522 self.position[1] = x
523 self.position[2] = y
524 self.position[3] = z
525 self.lastTargetPosition[1] = x
526 self.lastTargetPosition[2] = y
527 self.lastTargetPosition[3] = z
528
529
530 local rx,ry,rz = getWorldRotation(self.rotateNode)
531
532 setRotation(self.cameraNode, rx,ry,rz)
533 setTranslation(self.cameraNode, x,y,z)
534 end
535
536 self.lastInputValues = {}
537 self.lastInputValues.upDown = 0
538 self.lastInputValues.leftRight = 0
539
540 -- activate action event callbacks
541 local _, actionEventId1 = g_inputBinding:registerActionEvent(InputAction.AXIS_LOOK_UPDOWN_VEHICLE, self, VehicleCamera.actionEventLookUpDown, false, false, true, true, nil)
542 local _, actionEventId2 = g_inputBinding:registerActionEvent(InputAction.AXIS_LOOK_LEFTRIGHT_VEHICLE, self, VehicleCamera.actionEventLookLeftRight, false, false, true, true, nil)
543 g_inputBinding:setActionEventTextVisibility(actionEventId1, false)
544 g_inputBinding:setActionEventTextVisibility(actionEventId2, false)
545end

onDeactivate

Description
Called on deactivate
Definition
onDeactivate()
Code
549function VehicleCamera:onDeactivate()
550 self.isActivated = false
551 setShadowFocusBox(0)
552
553 -- remove action event callbacks
554 g_inputBinding:removeActionEventsByTarget(self)
555end

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
286function VehicleCamera:raycastCallback(transformId, x, y, z, distance, nx, ny, nz)
287 self.raycastDistance = distance
288 self.normalX = nx
289 self.normalY = ny
290 self.normalZ = nz
291 self.raycastTransformId = transformId
292end

resetCamera

Description
Reset camera to original pose
Definition
resetCamera()
Code
582function VehicleCamera:resetCamera()
583 self.rotX = self.origRotX
584 self.rotY = self.origRotY
585 self.rotZ = self.origRotZ
586
587 self.transX = self.origTransX
588 self.transY = self.origTransY
589 self.transZ = self.origTransZ
590
591 local transLength = MathUtil.vector3Length(self.origTransX, self.origTransY, self.origTransZ)
592 self.zoom = transLength
593 self.zoomTarget = transLength
594 self.zoomLimitedTarget = -1
595
596 self:updateRotateNodeRotation()
597 setTranslation(self.cameraPositionNode, self.transX, self.transY, self.transZ)
598
599 if self.positionSmoothingParameter > 0 then
600 local xlook,ylook,zlook = getWorldTranslation(self.rotateNode)
601 self.lookAtPosition[1] = xlook
602 self.lookAtPosition[2] = ylook
603 self.lookAtPosition[3] = zlook
604 local x,y,z = getWorldTranslation(self.cameraPositionNode)
605 self.position[1] = x
606 self.position[2] = y
607 self.position[3] = z
608
609 self:setSeparateCameraPose()
610 end
611end

setSeparateCameraPose

Description
Set separate camera pose
Definition
setSeparateCameraPose()
Code
652function VehicleCamera:setSeparateCameraPose()
653 if self.rotateNode ~= self.cameraPositionNode then
654 local dx = self.position[1] - self.lookAtPosition[1]
655 local dy = self.position[2] - self.lookAtPosition[2]
656 local dz = self.position[3] - self.lookAtPosition[3]
657
658 local upx,upy,upz = 0,1,0
659 if math.abs(dx) < 0.001 and math.abs(dz) < 0.001 then
660 upx = 0.1
661 end
662
663 setDirection(self.cameraNode, dx,dy,dz, upx,upy,upz)
664 else
665 local rx,ry,rz = getWorldRotation(self.rotateNode)
666 setRotation(self.cameraNode, rx,ry,rz)
667 end
668 setTranslation(self.cameraNode, self.position[1],self.position[2],self.position[3])
669end

update

Description
Update
Definition
update(float dt)
Arguments
floatdttime since last call in ms
Code
297function VehicleCamera:update(dt)
298 local target = self.zoomTarget
299 if self.zoomLimitedTarget >= 0 then
300 target = math.min(self.zoomLimitedTarget, self.zoomTarget)
301 end
302 self.zoom = target + ( math.pow(0.99579, dt) * (self.zoom - target) )
303
304 --
305 if self.lastInputValues.upDown ~= 0 then
306 local value = self.lastInputValues.upDown * g_gameSettings:getValue(GameSettings.SETTING.CAMERA_SENSITIVITY)
307 self.lastInputValues.upDown = 0
308 value = g_gameSettings:getValue("invertYLook") and -value or value
309
310 if self.isRotatable then
311 if self.isActivated and not g_gui:getIsGuiVisible() then
312 if self.limitRotXDelta > 0.001 then
313 self.rotX = math.min(self.rotX - value, self.rotX)
314 elseif self.limitRotXDelta < -0.001 then
315 self.rotX = math.max(self.rotX - value, self.rotX)
316 else
317 self.rotX = self.rotX - value
318 end
319
320 if self.limit then
321 self.rotX = math.min(self.rotMaxX, math.max(self.rotMinX, self.rotX))
322 end
323 end
324 end
325 end
326
327 if self.lastInputValues.leftRight ~= 0 then
328 local value = self.lastInputValues.leftRight * g_gameSettings:getValue(GameSettings.SETTING.CAMERA_SENSITIVITY)
329 self.lastInputValues.leftRight = 0
330
331 if self.isRotatable then
332 if self.isActivated and not g_gui:getIsGuiVisible() then
333 self.rotY = self.rotY - value
334 end
335 end
336 end
337
338 --
339 if g_gameSettings:getValue("isHeadTrackingEnabled") and isHeadTrackingAvailable() and self.allowHeadTracking and self.headTrackingNode ~= nil then
340 local tx,ty,tz = getHeadTrackingTranslation()
341 local pitch,yaw,roll = getHeadTrackingRotation()
342 if pitch ~= nil then
343 local camParent = getParent(self.cameraNode)
344 local ctx,cty,ctz;
345 local crx,cry,crz;
346 if camParent ~= 0 then
347 ctx, cty, ctz = localToLocal(self.headTrackingNode, camParent, tx, ty, tz);
348 crx, cry, crz = localRotationToLocal(self.headTrackingNode, camParent, pitch + self.headTrackingPitchOffset,yaw,roll);
349 else
350 ctx, cty, ctz = localToWorld(self.headTrackingNode, tx, ty, tz);
351 crx, cry, crz = localRotationToWorld(self.headTrackingNode, pitch + self.headTrackingPitchOffset,yaw,roll);
352 end
353
354 setRotation(self.cameraNode, crx, cry, crz)
355 setTranslation(self.cameraNode, ctx, cty, ctz)
356 end
357 else
358
359 self:updateRotateNodeRotation()
360
361
362 if self.limit then
363 -- adjust rotation to avoid clipping with terrain
364 if self.isRotatable and ((self.useWorldXZRotation == nil and g_gameSettings:getValue("useWorldCamera")) or self.useWorldXZRotation) then
365
366 local numIterations = 4
367 for i=1, numIterations do
368 local transX, transY, transZ = self.transDirX*self.zoom, self.transDirY*self.zoom, self.transDirZ*self.zoom
369 local x,y,z = localToWorld(getParent(self.cameraPositionNode), transX, transY, transZ)
370
371 local terrainHeight = DensityMapHeightUtil.getHeightAtWorldPos(x,0,z)
372
373 local minHeight = terrainHeight + 0.9
374 if y < minHeight then
375 local h = math.sin(self.rotX)*self.zoom
376 local h2 = h-(minHeight-y)
377 self.rotX = math.asin(MathUtil.clamp(h2/self.zoom, -1, 1))
378 self:updateRotateNodeRotation()
379 else
380 break
381 end
382 end
383 end
384
385 -- adjust zoom to avoid collision with objects
386 if self.allowTranslation then
387
388 self.limitRotXDelta = 0
389 local hasCollision, collisionDistance, nx,ny,nz, normalDotDir = self:getCollisionDistance()
390 if hasCollision then
391 local distOffset = 0.1
392 if normalDotDir ~= nil then
393 local absNormalDotDir = math.abs(normalDotDir)
394 distOffset = MathUtil.lerp(1.2, 0.1, absNormalDotDir*absNormalDotDir*(3-2*absNormalDotDir))
395 end
396 collisionDistance = math.max(collisionDistance-distOffset, 0.01)
397 self.disableCollisionTime = g_currentMission.time+400
398 self.zoomLimitedTarget = collisionDistance
399 if collisionDistance < self.zoom then
400 self.zoom = collisionDistance
401 end
402 if self.isRotatable and nx ~= nil and collisionDistance < self.transMin then
403 local _,lny,_ = worldDirectionToLocal(self.rotateNode, nx,ny,nz)
404 if lny > 0.5 then
405 self.limitRotXDelta = 1
406 elseif lny < -0.5 then
407 self.limitRotXDelta = -1
408 end
409 end
410 else
411 if self.disableCollisionTime <= g_currentMission.time then
412 self.zoomLimitedTarget = -1
413 end
414 end
415 end
416
417 end
418 self.transX, self.transY, self.transZ = self.transDirX*self.zoom, self.transDirY*self.zoom, self.transDirZ*self.zoom
419 setTranslation(self.cameraPositionNode, self.transX, self.transY, self.transZ)
420
421 if self.positionSmoothingParameter > 0 then
422
423 local interpDt = g_physicsDt
424
425 if self.vehicle.spec_rideable ~= nil then
426 interpDt = self.vehicle.spec_rideable.interpolationDt
427 end
428
429 if g_server == nil then
430 -- on clients, we interpolate the vehicles with dt, thus we need to use the same for camera interpolation
431 interpDt = dt
432 end
433 if interpDt > 0 then
434 local xlook,ylook,zlook = getWorldTranslation(self.rotateNode)
435 local lookAtPos = self.lookAtPosition
436 local lookAtLastPos = self.lookAtLastTargetPosition
437 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)
438 lookAtLastPos[1],lookAtLastPos[2],lookAtLastPos[3] = xlook,ylook,zlook
439
440 local x,y,z = getWorldTranslation(self.cameraPositionNode)
441 local pos = self.position
442 local lastPos = self.lastTargetPosition
443 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)
444 lastPos[1],lastPos[2],lastPos[3] = x,y,z
445
446 self:setSeparateCameraPose()
447 end
448 end
449
450 end
451
452end

updateRotateNodeRotation

Description
Update rotation node rotation
Definition
updateRotateNodeRotation()
Code
615function VehicleCamera:updateRotateNodeRotation()
616 local rotY = self.rotY
617 if self.rotYSteeringRotSpeed ~= nil and self.rotYSteeringRotSpeed ~= 0 and self.vehicle.spec_articulatedAxis ~= nil and self.vehicle.spec_articulatedAxis.interpolatedRotatedTime ~= nil then
618 rotY = rotY + self.vehicle.spec_articulatedAxis.interpolatedRotatedTime*self.rotYSteeringRotSpeed
619 end
620
621 if (self.useWorldXZRotation == nil and g_gameSettings:getValue("useWorldCamera")) or self.useWorldXZRotation then
622 local upx,upy,upz = 0,1,0
623
624 local dx,_,dz = localDirectionToWorld(getParent(self.rotateNode), 0,0,1)
625 local invLen = 1/math.sqrt(dx*dx + dz*dz)
626 dx = dx*invLen
627 dz = dz*invLen
628
629
630
631 local newDx = math.cos(self.rotX) * (math.cos(rotY)*dx + math.sin(rotY)*dz)
632 local newDy = -math.sin(self.rotX)
633 local newDz = math.cos(self.rotX) * (-math.sin(rotY)*dx + math.cos(rotY)*dz)
634
635
636 newDx,newDy,newDz = worldDirectionToLocal(getParent(self.rotateNode), newDx,newDy,newDz)
637 upx,upy,upz = worldDirectionToLocal(getParent(self.rotateNode), upx,upy,upz)
638
639 -- worst case check
640 if math.abs(MathUtil.dotProduct(newDx,newDy,newDz, upx,upy,upz)) > ( 0.99 * MathUtil.vector3Length(newDx,newDy,newDz) * MathUtil.vector3Length(upx,upy,upz) ) then
641 setRotation(self.rotateNode, self.rotX, rotY, self.rotZ)
642 else
643 setDirection(self.rotateNode, newDx,newDy,newDz, upx,upy,upz)
644 end
645 else
646 setRotation(self.rotateNode, self.rotX, rotY, self.rotZ)
647 end
648end

zoomSmoothly

Description
Zoom camera smoothly
Definition
zoomSmoothly(float offset)
Arguments
floatoffsetoffset
Code
261function VehicleCamera:zoomSmoothly(offset)
262 --self.zoomTarget = self.zoomTarget + offset
263 --if self.limit then
264 -- self.zoomTarget = math.min(self.transMax, math.max(self.transMin, self.zoomTarget))
265 --end
266 --self.zoomTarget = math.max(self.zoomTarget, 0.01)
267
268 local zoomTarget = self.zoomTarget
269 if self.transMin ~= nil and self.transMax ~= nil and self.transMin ~= self.transMax then
270 zoomTarget = math.min(self.transMax, math.max(self.transMin, self.zoomTarget + offset))
271 end
272 self.zoomTarget = zoomTarget
273
274end