Copyright (c) 2008-2015 GIANTS Software GmbH, Confidential, All Rights Reserved.
This document is to be published solely by ls-mods.de
1 | -- |
2 | -- VehicleCamera |
3 | -- Camera for vehicles |
4 | -- |
5 | -- @author Stefan Geiger |
6 | -- @date 09/08/07 |
7 | -- |
8 | -- Copyright (C) GIANTS Software GmbH, Confidential, All Rights Reserved. |
9 | |
10 | VehicleCamera = {}; |
11 | VehicleCamera.doCameraSmoothing = false; |
12 | |
13 | local VehicleCamera_mt = Class(VehicleCamera); |
14 | |
15 | function VehicleCamera:new(vehicle, customMt) |
16 | |
17 | local instance = {}; |
18 | if customMt ~= nil then |
19 | setmetatable(instance, customMt); |
20 | else |
21 | setmetatable(instance, VehicleCamera_mt); |
22 | end; |
23 | |
24 | |
25 | instance.vehicle = vehicle; |
26 | instance.isActivated = false; |
27 | |
28 | --instance.smoothZoom = false; |
29 | --instance.smoothZoomOffset = 0; |
30 | |
31 | |
32 | --instance.posXLast = -1; |
33 | --instance.posYLast = -1; |
34 | --instance.posXDelta = 0; |
35 | --instance.posYDelta = 0; |
36 | instance.smoothUpdateDt = 0; |
37 | |
38 | instance.limitRotXDelta = 0; |
39 | |
40 | instance.raycastDistance = 0; |
41 | instance.normalX = 0; |
42 | instance.normalY = 0; |
43 | instance.normalZ = 0; |
44 | |
45 | instance.raycastNodes = {}; |
46 | instance.disableCollisionTime = -1; |
47 | |
48 | instance.targetLookAtPosition = {0,0,0}; |
49 | instance.targetPosition = {0,0,0}; |
50 | instance.lastCamX, instance.lastCamY, instance.lastCamZ = 0,0,0; |
51 | instance.lastCamLookX, instance.lastCamLookY, instance.lastCamLookZ = 0,0,0; |
52 | instance.separatedFromVehicleUpdateDt = 0; |
53 | |
54 | |
55 | |
56 | --clearAdsOcclusionRegionGroup(AdSystemHelper.GROUP_VEHICLECAMERA); -- is registered only once in AdSystemHelper:init() |
57 | --addAdsOcclusionRegion( |
58 | -- AdSystemHelper.GROUP_VEHICLECAMERA, |
59 | -- 0.0, 0.0, |
60 | -- 1.0, 1.0); |
61 | --setActiveStateAdsOcclusionRegionGroup(AdSystemHelper.GROUP_VEHICLECAMERA, false); |
62 | |
63 | |
64 | return instance; |
65 | end; |
66 | |
67 | function VehicleCamera:loadFromXML(xmlFile, key) |
68 | --cameraNode, isRotatable, rotateNode, limit, rotMinX, rotMaxX, transMin, transMax, isInside, |
69 | |
70 | local camIndexStr = getXMLString(xmlFile, key .. "#index"); |
71 | self.cameraNode = Utils.indexToObject(self.vehicle.components, camIndexStr); |
72 | if self.cameraNode == nil or not getHasClassId(self.cameraNode, ClassIds.CAMERA) then |
73 | print("Error loading camera "..key..": invalid camera node"); |
74 | return false; |
75 | end; |
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 | print("Error loading camera "..key..": missing limit values"); |
87 | return false; |
88 | end; |
89 | end; |
90 | self.isInside = Utils.getNoNil(getXMLBool(xmlFile, key .. "#isInside"), false); |
91 | if self.isRotatable then |
92 | self.rotateNode = Utils.indexToObject(self.vehicle.components, getXMLString(xmlFile, key .. "#rotateNode")); |
93 | end; |
94 | self.allowTranslation = (self.rotateNode ~= nil and self.rotateNode ~= self.cameraNode); |
95 | |
96 | self.useMirror = Utils.getNoNil(getXMLBool(xmlFile, key .. "#useMirror"), false); |
97 | |
98 | self.useWorldXZRotation = Utils.getNoNil(getXMLBool(xmlFile, key .. "#useWorldXZRotation"), false); |
99 | |
100 | self.positionSmoothingParameter = 0; |
101 | self.lookAtSmoothingParameter = 0; |
102 | self.positionSmoothingResolution = 0.5; |
103 | if Utils.getNoNil(getXMLBool(xmlFile, key .. "#useDefaultPositionSmoothing"), self.useWorldXZRotation and self.allowTranslation and (not self.limit or math.abs(self.transMax - self.transMin) > 0.01)) then |
104 | if self.isInside then |
105 | self.positionSmoothingParameter = 0.095; |
106 | self.lookAtSmoothingParameter = 0.12; |
107 | else |
108 | self.positionSmoothingParameter = 0.016; |
109 | self.lookAtSmoothingParameter = 0.022; |
110 | end |
111 | end |
112 | self.positionSmoothingParameter = Utils.getNoNil(getXMLFloat(xmlFile, key .. "#positionSmoothingParameter"), self.positionSmoothingParameter); |
113 | self.lookAtSmoothingParameter = Utils.getNoNil(getXMLFloat(xmlFile, key .. "#lookAtSmoothingParameter"), self.lookAtSmoothingParameter); |
114 | |
115 | self.positionSmoothingParameter = 1-math.pow(1-self.positionSmoothingParameter, self.positionSmoothingResolution); |
116 | self.lookAtSmoothingParameter = 1-math.pow(1-self.lookAtSmoothingParameter, self.positionSmoothingResolution); |
117 | |
118 | self.cameraPositionNode = self.cameraNode; |
119 | if self.positionSmoothingParameter > 0 then |
120 | -- create a node which indicates the target position of the camera |
121 | self.cameraPositionNode = createTransformGroup("cameraPositionNode"); |
122 | local camIndex = getChildIndex(self.cameraNode); |
123 | link(getParent(self.cameraNode), self.cameraPositionNode, camIndex); |
124 | local x,y,z = getTranslation(self.cameraNode); |
125 | local rx,ry,rz = getRotation(self.cameraNode); |
126 | setTranslation(self.cameraPositionNode, x,y,z); |
127 | setRotation(self.cameraPositionNode, rx,ry,rz); |
128 | |
129 | unlink(self.cameraNode); |
130 | end; |
131 | self.rotYSteeringRotSpeed = math.rad(Utils.getNoNil(getXMLFloat(xmlFile, key .. "#rotYSteeringRotSpeed"), 0)); |
132 | |
133 | if self.rotateNode == nil or self.rotateNode == self.cameraNode then |
134 | self.rotateNode = self.cameraPositionNode; |
135 | end; |
136 | --[[if VehicleCamera.doCameraSmoothing then |
137 | link(getParent(self.rotateNode), self.cameraNode); |
138 | end;]] |
139 | |
140 | self.origRotX, self.origRotY, self.origRotZ = getRotation(self.rotateNode); |
141 | self.rotX = self.origRotX; |
142 | self.rotY = self.origRotY; |
143 | self.rotZ = self.origRotZ; |
144 | |
145 | self.origTransX, self.origTransY, self.origTransZ = getTranslation(self.cameraPositionNode); |
146 | self.transX = self.origTransX; |
147 | self.transY = self.origTransY; |
148 | self.transZ = self.origTransZ; |
149 | |
150 | local transLength = Utils.vector3Length(self.origTransX, self.origTransY, self.origTransZ); |
151 | self.zoom = transLength |
152 | self.zoomTarget = transLength; |
153 | self.zoomLimitedTarget = -1; |
154 | |
155 | local trans1OverLength = 1.0/transLength; |
156 | self.transDirX = trans1OverLength*self.origTransX; |
157 | self.transDirY = trans1OverLength*self.origTransY; |
158 | self.transDirZ = trans1OverLength*self.origTransZ; |
159 | if self.allowTranslation then |
160 | if transLength <= 0.01 then |
161 | print("Warning: Invalid camera translation in "..key..". Distance needs to be bigger than 0.01"); |
162 | end |
163 | end |
164 | |
165 | table.insert(self.raycastNodes, self.rotateNode); |
166 | local i=0; |
167 | while true do |
168 | local raycastKey = key..string.format(".raycastNode(%d)", i); |
169 | if not hasXMLProperty(xmlFile, raycastKey) then |
170 | break; |
171 | end; |
172 | |
173 | local node = Utils.indexToObject(self.vehicle.components, getXMLString(xmlFile, raycastKey .. "#index")); |
174 | if node ~= nil then |
175 | table.insert(self.raycastNodes, node); |
176 | end; |
177 | |
178 | i=i+1; |
179 | end; |
180 | |
181 | local sx,sy,sz = getScale(self.cameraNode); |
182 | if sx ~= 1 or sy ~= 1 or sz ~= 1 then |
183 | print("Warning: Vehicle camera with scale found in "..self.vehicle.configFileName.." ("..key.."). Resetting to scale 1"); |
184 | setScale(self.cameraNode, 1,1,1); |
185 | end |
186 | |
187 | return true; |
188 | end; |
189 | |
190 | function VehicleCamera:delete() |
191 | --delete(self.cameraNode); |
192 | if self.positionSmoothingParameter > 0 then |
193 | delete(self.cameraNode); |
194 | end; |
195 | end; |
196 | |
197 | function VehicleCamera:addZoom(offset) |
198 | self.zoomTarget = self.zoomTarget+offset; |
199 | if self.limit then |
200 | self.zoomTarget = math.min(self.transMax, math.max(self.transMin, self.zoomTarget)); |
201 | end; |
202 | self.zoomTarget = math.max(self.zoomTarget, 0.01); |
203 | self.zoom = self.zoomTarget; |
204 | --self.transX = self.transX+self.transDirX*offset; |
205 | --self.transY = self.transY+self.transDirY*offset; |
206 | --self.transZ = self.transZ+self.transDirZ*offset; |
207 | end; |
208 | |
209 | function VehicleCamera:zoomSmoothly(offset) |
210 | self.zoomTarget = self.zoomTarget+offset; |
211 | if self.limit then |
212 | self.zoomTarget = math.min(self.transMax, math.max(self.transMin, self.zoomTarget)); |
213 | end; |
214 | self.zoomTarget = math.max(self.zoomTarget, 0.01); |
215 | --self.smoothZoom = true; |
216 | --self.smoothZoomOffset = self.smoothZoomOffset + offset; |
217 | end; |
218 | |
219 | function VehicleCamera:mouseEvent(posX, posY, isDown, isUp, button) |
220 | if self.isActivated then |
221 | --[[if self.posXLast == -1 or self.posYLast == -1 then |
222 | self.posXLast = posX; |
223 | self.posYLast = posY; |
224 | end; |
225 | |
226 | self.posXDelta = posX - self.posXLast; |
227 | self.posYDelta = self.posYLast - posY;]] |
228 | |
229 | local translateMode = Input.isMouseButtonPressed(Input.MOUSE_BUTTON_LEFT) or Input.isMouseButtonPressed(Input.MOUSE_BUTTON_RIGHT) or Input.isMouseButtonPressed(Input.MOUSE_BUTTON_MIDDLE); |
230 | if self.isRotatable and not translateMode then -- only rotate if neither left or right mouse button is pressed |
231 | if self.limitRotXDelta > 0.001 then |
232 | self.rotX = math.min(self.rotX + InputBinding.mouseMovementY, self.rotX); |
233 | elseif self.limitRotXDelta < -0.001 then |
234 | self.rotX = math.max(self.rotX + InputBinding.mouseMovementY, self.rotX); |
235 | else |
236 | self.rotX = self.rotX + InputBinding.mouseMovementY; |
237 | end; |
238 | self.rotY = self.rotY - InputBinding.mouseMovementX; -- - (posX-self.posXLast); |
239 | end; |
240 | end; |
241 | end; |
242 | |
243 | function VehicleCamera:keyEvent(unicode, sym, modifier, isDown) |
244 | end; |
245 | |
246 | function VehicleCamera:raycastCallback(transformId, x, y, z, distance, nx, ny, nz) |
247 | self.raycastDistance = distance; |
248 | self.normalX = nx; |
249 | self.normalY = ny; |
250 | self.normalZ = nz; |
251 | self.raycastTransformId = transformId; |
252 | end; |
253 | |
254 | function VehicleCamera:update(dt) |
255 | self.smoothUpdateDt = self.smoothUpdateDt + dt; |
256 | |
257 | local smoothDt = 25; -- update with constant 40fps |
258 | while self.smoothUpdateDt > smoothDt do |
259 | local target = self.zoomTarget; |
260 | if self.zoomLimitedTarget >= 0 then |
261 | target = math.min(self.zoomLimitedTarget, self.zoomTarget); |
262 | end; |
263 | self.zoom = 0.9*self.zoom + 0.1*target; |
264 | --[[if self.smoothZoom then |
265 | self.transX = self.transX + self.transDirX * self.smoothZoomOffset; |
266 | self.transY = self.transY + self.transDirY * self.smoothZoomOffset; |
267 | self.transZ = self.transZ + self.transDirZ * self.smoothZoomOffset; |
268 | |
269 | self.smoothZoomOffset = self.smoothZoomOffset / 1.5; |
270 | |
271 | if math.abs(self.smoothZoomOffset) < 0.005 then |
272 | self.smoothZoomOffset = 0; |
273 | self.smoothZoom = false; |
274 | end; |
275 | end;]] |
276 | self.smoothUpdateDt = self.smoothUpdateDt-smoothDt; |
277 | end; |
278 | if self.isActivated then |
279 | if self.isRotatable then |
280 | |
281 | local rotSpeed = 0.001*dt; |
282 | local inputW = InputBinding.getDigitalInputAxis(InputBinding.AXIS_LOOK_UPDOWN_VEHICLE); |
283 | local inputZ = InputBinding.getDigitalInputAxis(InputBinding.AXIS_LOOK_LEFTRIGHT_VEHICLE); |
284 | if InputBinding.isAxisZero(inputW) then |
285 | inputW = InputBinding.getAnalogInputAxis(InputBinding.AXIS_LOOK_UPDOWN_VEHICLE); |
286 | end; |
287 | if InputBinding.isAxisZero(inputZ) then |
288 | inputZ = InputBinding.getAnalogInputAxis(InputBinding.AXIS_LOOK_LEFTRIGHT_VEHICLE); |
289 | end; |
290 | if self.limitRotXDelta > 0.001 then |
291 | self.rotX = math.min(self.rotX - rotSpeed*inputW, self.rotX); |
292 | elseif self.limitRotXDelta < -0.001 then |
293 | self.rotX = math.max(self.rotX - rotSpeed*inputW, self.rotX); |
294 | else |
295 | self.rotX = self.rotX - rotSpeed*inputW; |
296 | end; |
297 | --self.rotX = self.rotX - rotSpeed*inputW; |
298 | self.rotY = self.rotY - rotSpeed*inputZ; |
299 | end; |
300 | end; |
301 | |
302 | |
303 | local useScaledTrans = false; |
304 | local scaledTransDist = 0; |
305 | if self.limit then |
306 | self.rotX = math.min(self.rotMaxX, math.max(self.rotMinX, self.rotX)); |
307 | end; |
308 | |
309 | self:updateRotateNodeRotation(); |
310 | |
311 | |
312 | if self.limit then |
313 | -- adjust rotation to avoid clipping with terrain |
314 | if self.isRotatable and self.useWorldXZRotation then |
315 | |
316 | local numIterations = 4; |
317 | for i=1, numIterations do |
318 | local transX, transY, transZ = self.transDirX*self.zoom, self.transDirY*self.zoom, self.transDirZ*self.zoom; |
319 | local x,y,z = localToWorld(getParent(self.cameraPositionNode), transX, transY, transZ); |
320 | |
321 | local terrainHeight = getTerrainHeightAtWorldPos(g_currentMission.terrainRootNode, x, 0, z); |
322 | |
323 | local minHeight = terrainHeight + 0.9; |
324 | if y < minHeight then |
325 | local h = math.sin(self.rotX)*self.zoom; |
326 | local h2 = h-(minHeight-y); |
327 | self.rotX = math.asin(Utils.clamp(h2/self.zoom, -1, 1)); |
328 | self:updateRotateNodeRotation(); |
329 | else |
330 | break; |
331 | end; |
332 | end; |
333 | end; |
334 | |
335 | -- adjust zoom to avoid collision with objects |
336 | if self.allowTranslation then |
337 | |
338 | self.limitRotXDelta = 0; |
339 | |
340 | --local len = Utils.vector3Length(self.transX, self.transY, self.transZ); |
341 | --len = math.min(self.transMax, math.max(self.transMin, len)); |
342 | --self.transX, self.transY, self.transZ = self.transDirX*len, self.transDirY*len, self.transDirZ*len; |
343 | local hasCollision, collisionDistance, nx,ny,nz, normalDotDir = self:getCollisionDistance(); |
344 | if hasCollision then |
345 | local distOffset = 0.1; |
346 | if normalDotDir ~= nil then |
347 | local absNormalDotDir = math.abs(normalDotDir) |
348 | distOffset = Utils.lerp(1.2, 0.1, absNormalDotDir*absNormalDotDir*(3-2*absNormalDotDir)); |
349 | end |
350 | collisionDistance = math.max(collisionDistance-distOffset, 0.01); |
351 | self.disableCollisionTime = g_currentMission.time+400; |
352 | self.zoomLimitedTarget = collisionDistance; |
353 | if collisionDistance < self.zoom then |
354 | self.zoom = collisionDistance; |
355 | end; |
356 | if self.isRotatable and nx ~= nil and collisionDistance < self.transMin then |
357 | local lnx,lny,lnz = worldDirectionToLocal(self.rotateNode, nx,ny,nz); |
358 | --print("normal: "..nx.." "..ny.. " "..nz.. " local: "..lnx .. " "..lny.." "..lnz); |
359 | if lny > 0.5 then |
360 | self.limitRotXDelta = 1; |
361 | --self.rotX = self.rotX - dt*0.0001; |
362 | elseif lny < -0.5 then |
363 | self.limitRotXDelta = -1; |
364 | --self.rotX = self.rotX + dt*0.0001; |
365 | end; |
366 | end; |
367 | else |
368 | if self.disableCollisionTime <= g_currentMission.time then |
369 | self.zoomLimitedTarget = -1; |
370 | end; |
371 | end; |
372 | end; |
373 | |
374 | end; |
375 | self.transX, self.transY, self.transZ = self.transDirX*self.zoom, self.transDirY*self.zoom, self.transDirZ*self.zoom; |
376 | setTranslation(self.cameraPositionNode, self.transX, self.transY, self.transZ); |
377 | |
378 | --[[if self.useWorldXRotation then |
379 | setRotation(self.rotateNode, 0, self.rotY, self.rotZ); |
380 | local dx,dy,dz = localDirectionToWorld(self.rotateNode, 0,0,1); |
381 | local s = math.cos(self.rotX)/math.sqrt(dx*dx + dz*dz); -- normalize and scale to new length |
382 | dx = dx*s; |
383 | dz = dz*s; |
384 | dy = -math.sin(self.rotX); |
385 | |
386 | local sx,sy,sz = worldDirectionToLocal(getParent(self.rotateNode), localDirectionToWorld(self.rotateNode, 1,0,0)); |
387 | local dx,dy,dz = worldDirectionToLocal(getParent(self.rotateNode), dx,dy,dz); |
388 | local upx,upy,upz = Utils.crossProduct(dx,dy,dz, sx,sy,sz); |
389 | |
390 | setDirection(self.rotateNode, dx,dy,dz, upx,upy,upz); |
391 | else |
392 | setRotation(self.rotateNode, self.rotX, self.rotY, self.rotZ); |
393 | end;]] |
394 | |
395 | if self.positionSmoothingParameter > 0 then |
396 | |
397 | local interpDt = g_physicsDt; |
398 | if g_server == nil then |
399 | -- on clients, we interpolate the vehicles with g_physicsDtUnclamped, thus we need to use the same for camera interpolation |
400 | interpDt = g_physicsDtUnclamped; |
401 | end |
402 | |
403 | local xlook,ylook,zlook = getWorldTranslation(self.rotateNode); |
404 | -- 1-math.pow(1-0.02, resolution) |
405 | self.targetLookAtPosition[1], self.targetLookAtPosition[2], self.targetLookAtPosition[3] = |
406 | self:getSmoothed(self.lookAtSmoothingParameter, self.positionSmoothingResolution, self.targetLookAtPosition[1], self.targetLookAtPosition[2], self.targetLookAtPosition[3], |
407 | self.lastCamLookX, self.lastCamLookY, self.lastCamLookZ, xlook,ylook,zlook, interpDt); |
408 | self.lastCamLookX, self.lastCamLookY, self.lastCamLookZ = xlook,ylook,zlook; |
409 | |
410 | local x,y,z = getWorldTranslation(self.cameraPositionNode); |
411 | -- 1-math.pow(1-0.014, resolution) |
412 | self.targetPosition[1], self.targetPosition[2], self.targetPosition[3] = |
413 | self:getSmoothed(self.positionSmoothingParameter, self.positionSmoothingResolution, self.targetPosition[1], self.targetPosition[2], self.targetPosition[3], |
414 | self.lastCamX, self.lastCamY, self.lastCamZ, x,y,z, interpDt) |
415 | --print(string.format("dt: %1.2f pdt %1.2f",dt,interpDt)); |
416 | self.lastCamX, self.lastCamY, self.lastCamZ = x,y,z; |
417 | |
418 | self:setSeparateCameraPose(); |
419 | end; |
420 | |
421 | --[[if self.isActivated then |
422 | wrapMousePosition(0.5, 0.5); |
423 | self.posXLast = 0.5; |
424 | self.posYLast = 0.5; |
425 | end;]] |
426 | end; |
427 | |
428 | -- if alpha is known for resultion 1, it can be adjusted to true resolution with (alpha = 1-math.pow(1-alpha, resolution)) |
429 | function VehicleCamera:getSmoothed(alpha, resolution, curX,curY,curZ,startX,startY,startZ,endX,endY,endZ, dt) |
430 | dt = math.modf(dt, resolution); |
431 | local scale = resolution/dt; |
432 | local dx,dy,dz = (endX-startX)*scale, (endY-startY)*scale, (endZ-startZ)*scale; |
433 | local x,y,z = startX, startY, startZ; |
434 | for i=resolution,dt,resolution do |
435 | x,y,z = x + dx, y + dy, z + dz; |
436 | curX = curX + (x-curX)*alpha; |
437 | curY = curY + (y-curY)*alpha; |
438 | curZ = curZ + (z-curZ)*alpha; |
439 | end |
440 | |
441 | return curX,curY,curZ; |
442 | end |
443 | |
444 | function VehicleCamera:onActivate() |
445 | |
446 | self.isActivated = true; |
447 | self:resetCamera(); |
448 | setCamera(self.cameraNode); |
449 | |
450 | |
451 | if self.positionSmoothingParameter > 0 then |
452 | local xlook,ylook,zlook = getWorldTranslation(self.rotateNode); |
453 | self.targetLookAtPosition[1] = xlook; |
454 | self.targetLookAtPosition[2] = ylook; |
455 | self.targetLookAtPosition[3] = zlook; |
456 | local x,y,z = getWorldTranslation(self.cameraPositionNode); |
457 | self.targetPosition[1] = x; |
458 | self.targetPosition[2] = y; |
459 | self.targetPosition[3] = z; |
460 | |
461 | self.lastCamLookX, self.lastCamLookY, self.lastCamLookZ = xlook, ylook, zlook; |
462 | self.lastCamX, self.lastCamY, self.lastCamZ = x,y,z; |
463 | |
464 | local rx,ry,rz = getWorldRotation(self.rotateNode); |
465 | |
466 | --link(getRootNode(), self.cameraNode); |
467 | setRotation(self.cameraNode, rx,ry,rz); |
468 | --setDirection(self.cameraNode, x-xlook, y-ylook, z-zlook, 0, 1, 0); |
469 | setTranslation(self.cameraNode, x,y,z); |
470 | end; |
471 | |
472 | --[[if (self.isInside) then |
473 | setActiveStateAdsOcclusionRegionGroup(AdSystemHelper.GROUP_VEHICLECAMERA, true); |
474 | end;]] |
475 | end; |
476 | |
477 | function VehicleCamera:onDeactivate() |
478 | self:resetCamera(); |
479 | self.isActivated = false; |
480 | |
481 | --[[if (self.isInside) then |
482 | setActiveStateAdsOcclusionRegionGroup(AdSystemHelper.GROUP_VEHICLECAMERA, false); |
483 | end;]] |
484 | end; |
485 | |
486 | function VehicleCamera:resetCamera() |
487 | self.rotX = self.origRotX; |
488 | self.rotY = self.origRotY; |
489 | self.rotZ = self.origRotZ; |
490 | --self.posXLast = -1; |
491 | --self.posYLast = -1; |
492 | |
493 | self.transX = self.origTransX; |
494 | self.transY = self.origTransY; |
495 | self.transZ = self.origTransZ; |
496 | |
497 | local transLength = Utils.vector3Length(self.origTransX, self.origTransY, self.origTransZ); |
498 | self.zoom = transLength |
499 | self.zoomTarget = transLength; |
500 | self.zoomLimitedTarget = -1; |
501 | |
502 | --setRotation(self.rotateNode, self.rotX, self.rotY, self.rotZ); |
503 | self:updateRotateNodeRotation(); |
504 | setTranslation(self.cameraPositionNode, self.transX, self.transY, self.transZ); |
505 | |
506 | if self.positionSmoothingParameter > 0 then |
507 | local xlook,ylook,zlook = getWorldTranslation(self.rotateNode); |
508 | self.targetLookAtPosition[1] = xlook; |
509 | self.targetLookAtPosition[2] = ylook; |
510 | self.targetLookAtPosition[3] = zlook; |
511 | local x,y,z = getWorldTranslation(self.cameraPositionNode); |
512 | self.targetPosition[1] = x; |
513 | self.targetPosition[2] = y; |
514 | self.targetPosition[3] = z; |
515 | |
516 | self.lastCamLookX, self.lastCamLookY, self.lastCamLookZ = xlook, ylook, zlook; |
517 | self.lastCamX, self.lastCamY, self.lastCamZ = x,y,z; |
518 | |
519 | self:setSeparateCameraPose(); |
520 | end; |
521 | end; |
522 | |
523 | function VehicleCamera:updateRotateNodeRotation() |
524 | local rotY = self.rotY; |
525 | if self.rotYSteeringRotSpeed ~= 0 and self.vehicle.rotatedTime ~= nil then |
526 | rotY = rotY + self.vehicle.rotatedTime*self.rotYSteeringRotSpeed; |
527 | end |
528 | |
529 | if self.useWorldXZRotation then |
530 | local upx,upy,upz = 0,1,0; |
531 | |
532 | local dx,dy,dz = localDirectionToWorld(getParent(self.rotateNode), 0,0,1); |
533 | local invLen = 1/math.sqrt(dx*dx + dz*dz); |
534 | dx = dx*invLen; |
535 | dz = dz*invLen; |
536 | |
537 | |
538 | |
539 | local newDx = math.cos(self.rotX) * (math.cos(rotY)*dx + math.sin(rotY)*dz); |
540 | local newDy = -math.sin(self.rotX); |
541 | local newDz = math.cos(self.rotX) * (-math.sin(rotY)*dx + math.cos(rotY)*dz); |
542 | |
543 | |
544 | newDx,newDy,newDz = worldDirectionToLocal(getParent(self.rotateNode), newDx,newDy,newDz); |
545 | upx,upy,upz = worldDirectionToLocal(getParent(self.rotateNode), upx,upy,upz); |
546 | |
547 | setDirection(self.rotateNode, newDx,newDy,newDz, upx,upy,upz); |
548 | else |
549 | setRotation(self.rotateNode, self.rotX, rotY, self.rotZ); |
550 | end; |
551 | end; |
552 | |
553 | function VehicleCamera:setSeparateCameraPose() |
554 | if self.rotateNode ~= self.cameraPositionNode then |
555 | local dx = self.targetPosition[1] - self.targetLookAtPosition[1]; |
556 | local dy = self.targetPosition[2] - self.targetLookAtPosition[2]; |
557 | local dz = self.targetPosition[3] - self.targetLookAtPosition[3]; |
558 | |
559 | local upx,upy,upz = 0,1,0; |
560 | if math.abs(dx) < 0.001 and math.abs(dz) < 0.001 then |
561 | upx = 0.1; |
562 | --upx,upy,upz = localDirectionToWorld(self.cameraNode, 0,1,0); |
563 | end; |
564 | |
565 | --local dx,dy,dz = worldDirectionToLocal(getParent(self.cameraNode), self.targetPosition[1]-self.targetLookAtPosition[1], self.targetPosition[2]-self.targetLookAtPosition[2], self.targetPosition[3]-self.targetLookAtPosition[3]); |
566 | --local upx,upy,upz = worldDirectionToLocal(getParent(self.cameraNode), 0, 1, 0); |
567 | --local x,y,z = worldToLocal(getParent(self.cameraNode), self.targetPosition[1],self.targetPosition[2],self.targetPosition[3]); |
568 | |
569 | setDirection(self.cameraNode, dx,dy,dz, upx,upy,upz); |
570 | else |
571 | local rx,ry,rz = getWorldRotation(self.rotateNode); |
572 | setRotation(self.cameraNode, rx,ry,rz); |
573 | end |
574 | setTranslation(self.cameraNode, self.targetPosition[1],self.targetPosition[2],self.targetPosition[3]); |
575 | end; |
576 | |
577 | function VehicleCamera:getCollisionDistance() |
578 | |
579 | local raycastMask = 32+64+128+256+4096; |
580 | |
581 | local targetCamX, targetCamY, targetCamZ = localToWorld(self.rotateNode, self.transDirX*self.zoomTarget, self.transDirY*self.zoomTarget, self.transDirZ*self.zoomTarget); |
582 | |
583 | local hasCollision = false; |
584 | local collisionDistance = -1; |
585 | local normalX,normalY,normalZ; |
586 | local normalDotDir; |
587 | for _, raycastNode in ipairs(self.raycastNodes) do |
588 | |
589 | hasCollision = false; |
590 | |
591 | local nodeX, nodeY, nodeZ = getWorldTranslation(raycastNode); |
592 | local dirX, dirY, dirZ = targetCamX-nodeX, targetCamY-nodeY, targetCamZ-nodeZ; |
593 | local dirLength = Utils.vector3Length(dirX, dirY, dirZ); |
594 | dirX = dirX / dirLength; |
595 | dirY = dirY / dirLength; |
596 | dirZ = dirZ / dirLength; |
597 | |
598 | local startX = nodeX; |
599 | local startY = nodeY; |
600 | local startZ = nodeZ; |
601 | --local outsideObject = 0; |
602 | local currentDistance = 0; |
603 | local minDistance = self.transMin; |
604 | --local worldTransDirX, worldTransDirY, worldTransDirZ = localDirectionToWorld(self.rotateNode, self.transDirX, self.transDirY, self.transDirZ); |
605 | while (true) do |
606 | --local worldStartX, worldStartY, worldStartZ = localToWorld(self.rotateNode, startX, startY, startZ); |
607 | if (dirLength-currentDistance) <= 0 then |
608 | break; |
609 | end; |
610 | self.raycastDistance = 0; |
611 | raycastClosest(startX, startY, startZ, dirX, dirY, dirZ, "raycastCallback", dirLength-currentDistance, self, raycastMask, true); |
612 | |
613 | if self.raycastDistance ~= 0 then |
614 | currentDistance = currentDistance + self.raycastDistance+0.001; |
615 | local ndotd = Utils.dotProduct(self.normalX, self.normalY, self.normalZ, dirX, dirY, dirZ); |
616 | if self.vehicle:getIsAttachedVehicleNode(self.raycastTransformId) or self.vehicle:getIsDynamicallyMountedNode(self.raycastTransformId) then |
617 | if ndotd > 0 then |
618 | --outsideObject = outsideObject +1; |
619 | minDistance = math.max(minDistance, currentDistance); |
620 | end; |
621 | else |
622 | hasCollision = true; |
623 | -- we take the distance from the rotate node |
624 | if raycastNode == self.rotateNode then |
625 | normalX,normalY,normalZ = self.normalX, self.normalY, self.normalZ; |
626 | collisionDistance = math.max(self.transMin, currentDistance); |
627 | normalDotDir = ndotd |
628 | end; |
629 | break; |
630 | end; |
631 | startX = nodeX+dirX*currentDistance; |
632 | startY = nodeY+dirY*currentDistance; |
633 | startZ = nodeZ+dirZ*currentDistance; |
634 | else |
635 | break; |
636 | end; |
637 | end; |
638 | if not hasCollision then |
639 | break; |
640 | end; |
641 | end; |
642 | return hasCollision, collisionDistance, normalX,normalY,normalZ, normalDotDir; |
643 | end;
|
Copyright (c) 2008-2015 GIANTS Software GmbH, Confidential, All Rights Reserved.
This document is to be published solely by ls-mods.de