Copyright (c) 2008-2015 GIANTS Software GmbH, Confidential, All Rights Reserved.
This document is to be published solely by ls-mods.de
1 | -- Copyright (C) GIANTS Software GmbH, Confidential, All Rights Reserved. |
2 | |
3 | PhysicsObject= {}; |
4 | PhysicsObject_mt = Class(PhysicsObject, Object); |
5 | |
6 | InitStaticObjectClass(PhysicsObject, "PhysicsObject", ObjectIds.OBJECT_PHYSICS_OBJECT); |
7 | |
8 | function PhysicsObject:new(isServer, isClient, customMt) |
9 | local mt = customMt; |
10 | if mt == nil then |
11 | mt = PhysicsObject_mt; |
12 | end; |
13 | |
14 | local self = Object:new(isServer, isClient, mt); |
15 | self.nodeId = 0; |
16 | if not self.isServer then |
17 | self.currentPosition = {}; |
18 | self.targetPosition = {}; |
19 | self.isPositionDirty = false; |
20 | self.interpolationTime = 2; |
21 | self.interpolationDuration = 30; |
22 | end |
23 | self.forcedClipDistance = 30; |
24 | |
25 | self.physicsObjectDirtyFlag = self:getNextDirtyFlag(); |
26 | return self; |
27 | end; |
28 | |
29 | function PhysicsObject:delete() |
30 | g_currentMission:removeNodeObject(self.nodeId); |
31 | delete(self.nodeId); |
32 | self.nodeId = 0; |
33 | PhysicsObject:superClass().delete(self); |
34 | end; |
35 | |
36 | function PhysicsObject:getAllowsAutoDelete() |
37 | return true; |
38 | end |
39 | |
40 | function PhysicsObject:loadOnCreate(nodeId) |
41 | self:setNodeId(nodeId); |
42 | if not self.isServer then |
43 | self:onGhostRemove(); |
44 | end; |
45 | end; |
46 | |
47 | function PhysicsObject:setNodeId(nodeId) |
48 | self.nodeId = nodeId; |
49 | setRigidBodyType(self.nodeId, self:getDefaultRigidBodyType()); |
50 | addToPhysics(self.nodeId); |
51 | local x,y,z=getTranslation(self.nodeId) |
52 | local x_rot,y_rot,z_rot=getRotation(self.nodeId); |
53 | self.sendPosX, self.sendPosY, self.sendPosZ = x,y,z; |
54 | self.sendRotX, self.sendRotY, self.sendRotZ = x_rot,y_rot,z_rot; |
55 | |
56 | if not self.isServer then |
57 | local currentPosition = self.currentPosition; |
58 | local targetPosition = self.targetPosition; |
59 | currentPosition.x,currentPosition.y,currentPosition.z = x,y,z; |
60 | targetPosition.x,targetPosition.y,targetPosition.z = x,y,z; |
61 | currentPosition.x_rot,currentPosition.y_rot,currentPosition.z_rot,currentPosition.w_rot = mathEulerToQuaternion(x_rot,y_rot,z_rot); |
62 | targetPosition.x_rot,targetPosition.y_rot,targetPosition.z_rot,targetPosition.w_rot = currentPosition.x_rot,currentPosition.y_rot,currentPosition.z_rot,currentPosition.w_rot; |
63 | end |
64 | |
65 | self:addChildsToNodeObject(self.nodeId); |
66 | end; |
67 | |
68 | function PhysicsObject:readStream(streamId, connection) |
69 | assert(self.nodeId ~= 0); |
70 | if connection:getIsServer() then |
71 | local paramsXZ = g_currentMission.vehicleXZPosCompressionParams; |
72 | local paramsY = g_currentMission.vehicleYPosCompressionParams; |
73 | local x = Utils.readCompressedWorldPosition(streamId, paramsXZ); |
74 | local y = Utils.readCompressedWorldPosition(streamId, paramsY); |
75 | local z = Utils.readCompressedWorldPosition(streamId, paramsXZ); |
76 | local x_rot=Utils.readCompressedAngle(streamId); |
77 | local y_rot=Utils.readCompressedAngle(streamId); |
78 | local z_rot=Utils.readCompressedAngle(streamId); |
79 | |
80 | local x_rot,y_rot,z_rot,w_rot = mathEulerToQuaternion(x_rot,y_rot,z_rot); |
81 | |
82 | self:setPose(x,y,z, x_rot,y_rot,z_rot,w_rot); |
83 | end; |
84 | end; |
85 | |
86 | function PhysicsObject:writeStream(streamId, connection) |
87 | if not connection:getIsServer() then |
88 | local x,y,z = getTranslation(self.nodeId) |
89 | local x_rot,y_rot,z_rot = getRotation(self.nodeId); |
90 | local paramsXZ = g_currentMission.vehicleXZPosCompressionParams; |
91 | local paramsY = g_currentMission.vehicleYPosCompressionParams; |
92 | Utils.writeCompressedWorldPosition(streamId, x, paramsXZ); |
93 | Utils.writeCompressedWorldPosition(streamId, y, paramsY); |
94 | Utils.writeCompressedWorldPosition(streamId, z, paramsXZ); |
95 | Utils.writeCompressedAngle(streamId, x_rot); |
96 | Utils.writeCompressedAngle(streamId, y_rot); |
97 | Utils.writeCompressedAngle(streamId, z_rot); |
98 | end; |
99 | end; |
100 | |
101 | function PhysicsObject:readUpdateStream(streamId, timestamp, connection) |
102 | if connection:getIsServer() then |
103 | if streamReadBool(streamId) then |
104 | local targetPosition = self.targetPosition; |
105 | local currentPosition = self.currentPosition; |
106 | |
107 | local deltaTime = g_client.tickDuration; |
108 | if self.lastPhysicsNetworkTime ~= nil then |
109 | deltaTime = math.min(g_packetPhysicsNetworkTime - self.lastPhysicsNetworkTime, 3*g_client.tickDuration); |
110 | end |
111 | self.lastPhysicsNetworkTime = g_packetPhysicsNetworkTime; |
112 | |
113 | -- interpTimeLeft is the time we would've continued interpolating. This should always be about g_clientInterpDelay. |
114 | -- If we extrapolated, reset to the full delay |
115 | local interpTimeLeft = g_clientInterpDelay; |
116 | if self.interpolationTime < 1 then |
117 | interpTimeLeft = (1-self.interpolationTime) * self.interpolationDuration; |
118 | interpTimeLeft = interpTimeLeft * 0.95 + g_clientInterpDelay * 0.05; -- slighly blend towards the expected delay |
119 | interpTimeLeft = math.min(interpTimeLeft, 3*g_clientInterpDelay); -- clamp to some reasonable maximum |
120 | end |
121 | self.interpolationDuration = interpTimeLeft + deltaTime; |
122 | self.interpolationTime = 0; |
123 | |
124 | self.lastPositionX = currentPosition.x; |
125 | self.lastPositionY = currentPosition.y; |
126 | self.lastPositionZ = currentPosition.z; |
127 | self.lastRotationX = currentPosition.x_rot; |
128 | self.lastRotationY = currentPosition.y_rot; |
129 | self.lastRotationZ = currentPosition.z_rot; |
130 | self.lastRotationW = currentPosition.w_rot; |
131 | |
132 | local paramsXZ = g_currentMission.vehicleXZPosCompressionParams; |
133 | local paramsY = g_currentMission.vehicleYPosCompressionParams; |
134 | targetPosition.x = Utils.readCompressedWorldPosition(streamId, paramsXZ); |
135 | targetPosition.y = Utils.readCompressedWorldPosition(streamId, paramsY); |
136 | targetPosition.z = Utils.readCompressedWorldPosition(streamId, paramsXZ); |
137 | local x_rot=Utils.readCompressedAngle(streamId); |
138 | local y_rot=Utils.readCompressedAngle(streamId); |
139 | local z_rot=Utils.readCompressedAngle(streamId); |
140 | |
141 | targetPosition.x_rot,targetPosition.y_rot,targetPosition.z_rot,targetPosition.w_rot = mathEulerToQuaternion(x_rot,y_rot,z_rot); |
142 | self.isPositionDirty = true; |
143 | |
144 | local dx,dy,dz = targetPosition.x-currentPosition.x, targetPosition.y-currentPosition.y, targetPosition.z-currentPosition.z; |
145 | if dx*dx + dy*dy + dz*dz > 25 then -- snap, if more than 5m distance |
146 | --print("snapping, diff: "..math.sqrt(dx*dx + dy*dy + dz*dz )); |
147 | self:setPose(targetPosition.x,targetPosition.y,targetPosition.z, targetPosition.x_rot,targetPosition.y_rot,targetPosition.z_rot,targetPosition.w_rot); |
148 | end; |
149 | end; |
150 | end; |
151 | end; |
152 | |
153 | function PhysicsObject:writeUpdateStream(streamId, connection, dirtyMask) |
154 | if not connection:getIsServer() then |
155 | if streamWriteBool(streamId, bitAND(dirtyMask, self.physicsObjectDirtyFlag) ~= 0) then |
156 | local paramsXZ = g_currentMission.vehicleXZPosCompressionParams; |
157 | local paramsY = g_currentMission.vehicleYPosCompressionParams; |
158 | Utils.writeCompressedWorldPosition(streamId, self.sendPosX, paramsXZ); |
159 | Utils.writeCompressedWorldPosition(streamId, self.sendPosY, paramsY); |
160 | Utils.writeCompressedWorldPosition(streamId, self.sendPosZ, paramsXZ); |
161 | |
162 | Utils.writeCompressedAngle(streamId, self.sendRotX); |
163 | Utils.writeCompressedAngle(streamId, self.sendRotY); |
164 | Utils.writeCompressedAngle(streamId, self.sendRotZ); |
165 | end; |
166 | end; |
167 | end; |
168 | |
169 | function PhysicsObject:update(dt) |
170 | if not self.isServer then |
171 | if self.isPositionDirty then |
172 | local maxIntrpAlpha = 1.05; |
173 | local intrpAlpha = self.interpolationTime + g_physicsDtUnclamped / self.interpolationDuration; |
174 | --local intrpAlpha = math.max((g_physicsNetworkTime-g_clientInterpDelay - self.lastPoseTime) / (self.targetPoseTime - self.lastPoseTime), 0); |
175 | --self.curPoseTime = g_physicsNetworkTime-g_clientInterpDelay; |
176 | if intrpAlpha >= maxIntrpAlpha then |
177 | intrpAlpha = maxIntrpAlpha; |
178 | self.isPositionDirty = false; |
179 | end; |
180 | self.interpolationTime = intrpAlpha; |
181 | |
182 | local curPos = self.currentPosition; |
183 | local targetPos = self.targetPosition; |
184 | |
185 | curPos.x = self.lastPositionX + (targetPos.x-self.lastPositionX)*intrpAlpha; |
186 | curPos.y = self.lastPositionY + (targetPos.y-self.lastPositionY)*intrpAlpha; |
187 | curPos.z = self.lastPositionZ + (targetPos.z-self.lastPositionZ)*intrpAlpha; |
188 | curPos.x_rot, curPos.y_rot, curPos.z_rot, curPos.w_rot = Utils.nlerpQuaternionShortestPath( |
189 | self.lastRotationX, self.lastRotationY, self.lastRotationZ, self.lastRotationW, targetPos.x_rot, targetPos.y_rot, targetPos.z_rot, targetPos.w_rot, intrpAlpha); |
190 | |
191 | setTranslation(self.nodeId, curPos.x,curPos.y,curPos.z); |
192 | setQuaternion(self.nodeId, curPos.x_rot, curPos.y_rot, curPos.z_rot, curPos.w_rot); |
193 | end |
194 | end; |
195 | end; |
196 | |
197 | -- NB: called directly by some subclasses (e.g. MountableObject) that do more things if hasMoved is true |
198 | function PhysicsObject:updateMove() |
199 | local x,y,z=getTranslation(self.nodeId) |
200 | local x_rot,y_rot,z_rot=getRotation(self.nodeId); |
201 | local hasMoved = math.abs(self.sendPosX-x)>0.005 or math.abs(self.sendPosY-y)>0.005 or math.abs(self.sendPosZ-z)>0.005 or |
202 | math.abs(self.sendRotX-x_rot)>0.02 or math.abs(self.sendRotY-y_rot)>0.02 or math.abs(self.sendRotZ-z_rot)>0.02; |
203 | |
204 | if hasMoved then |
205 | self:raiseDirtyFlags(self.physicsObjectDirtyFlag); |
206 | self.sendPosX, self.sendPosY, self.sendPosZ = x,y,z; |
207 | self.sendRotX, self.sendRotY, self.sendRotZ = x_rot,y_rot,z_rot; |
208 | end; |
209 | |
210 | return hasMoved; |
211 | end; |
212 | |
213 | function PhysicsObject:updateTick(dt) |
214 | if self.isServer then |
215 | self:updateMove(); |
216 | end; |
217 | end; |
218 | |
219 | function PhysicsObject:needsUpdate() |
220 | if g_currentMission.missionDynamicInfo.isMultiplayer and not self.isServer then |
221 | -- multiplayer game needs updates on the client |
222 | return true; |
223 | end; |
224 | if self.update ~= PhysicsObject.update then |
225 | -- subclass redefines update, so we must call it (subclass can redefine needsUpdate to override this) |
226 | return true; |
227 | end; |
228 | -- optimize update away, we're just a PhysicsObject in a single-player game |
229 | return false; |
230 | end; |
231 | |
232 | function PhysicsObject:needsUpdateTick() |
233 | if g_currentMission.missionDynamicInfo.isMultiplayer and self.isServer then |
234 | -- multiplayer game needs updateTicks on the server |
235 | return true; |
236 | end; |
237 | if self.updateTick ~= PhysicsObject.updateTick then |
238 | -- subclass redefines updateTick, so we must call it (subclass can redefine needsUpdateTick to override this) |
239 | return true; |
240 | end; |
241 | -- optimize updateTick away, we're just a PhysicsObject in a single-player game |
242 | return false; |
243 | end; |
244 | |
245 | function PhysicsObject:testScope(x,y,z, coeff) |
246 | local x1, y1, z1 = getWorldTranslation(self.nodeId); |
247 | local dist = (x1-x)*(x1-x) + (y1-y)*(y1-y) + (z1-z)*(z1-z); |
248 | local clipDist = math.min(getClipDistance(self.nodeId)*coeff, self.forcedClipDistance); |
249 | if dist < clipDist*clipDist then |
250 | return true; |
251 | else |
252 | return false; |
253 | end; |
254 | end; |
255 | |
256 | function PhysicsObject:getUpdatePriority(skipCount, x, y, z, coeff, connection) |
257 | local x1, y1, z1 = getWorldTranslation(self.nodeId); |
258 | local dist = math.sqrt((x1-x)*(x1-x) + (y1-y)*(y1-y) + (z1-z)*(z1-z)); |
259 | local clipDist = math.min(getClipDistance(self.nodeId)*coeff, self.forcedClipDistance); |
260 | return (1-dist/clipDist)* 0.8 + 0.5*skipCount * 0.2; |
261 | end; |
262 | |
263 | function PhysicsObject:onGhostRemove() |
264 | setVisibility(self.nodeId, false); |
265 | removeFromPhysics(self.nodeId); |
266 | end; |
267 | |
268 | function PhysicsObject:onGhostAdd() |
269 | setVisibility(self.nodeId, true); |
270 | addToPhysics(self.nodeId); |
271 | end; |
272 | |
273 | function PhysicsObject:setPose(x,y,z, x_rot,y_rot,z_rot,w_rot) |
274 | if not self.isServer then |
275 | local curPos = self.currentPosition; |
276 | local targetPos = self.targetPosition; |
277 | curPos.x, targetPos.x = x, x; |
278 | curPos.y, targetPos.y = y, y; |
279 | curPos.z, targetPos.z = z, z; |
280 | curPos.x_rot, targetPos.x_rot = x_rot, x_rot; |
281 | curPos.y_rot, targetPos.y_rot = y_rot, y_rot; |
282 | curPos.z_rot, targetPos.z_rot = z_rot, z_rot; |
283 | curPos.w_rot, targetPos.w_rot = w_rot, w_rot; |
284 | self.isPositionDirty = false; |
285 | self.interpolationTime = 2; |
286 | end |
287 | |
288 | setTranslation(self.nodeId, x,y,z); |
289 | setQuaternion(self.nodeId, x_rot, y_rot, z_rot, w_rot); |
290 | end |
291 | |
292 | function PhysicsObject:getDefaultRigidBodyType() |
293 | if self.isServer then |
294 | return "Dynamic"; |
295 | else |
296 | return "Kinematic"; |
297 | end |
298 | end |
299 | |
300 | function PhysicsObject:addChildsToNodeObject(nodeId) |
301 | for i=0,getNumOfChildren(nodeId)-1 do |
302 | self:addChildsToNodeObject(getChildAt(nodeId, i)); |
303 | end |
304 | local rigidBodyType = getRigidBodyType(nodeId); |
305 | if rigidBodyType ~= "NoRigidBody" then |
306 | g_currentMission:addNodeObject(nodeId, self); |
307 | end |
308 | end
|
Copyright (c) 2008-2015 GIANTS Software GmbH, Confidential, All Rights Reserved.
This document is to be published solely by ls-mods.de