17 | function PhysicsObject:new(isServer, isClient, customMt) |
18 | local mt = customMt; |
19 | if mt == nil then |
20 | mt = PhysicsObject_mt; |
21 | end; |
22 | |
23 | local self = Object:new(isServer, isClient, mt); |
24 | self.nodeId = 0; |
25 | if not self.isServer then |
26 | self.currentPosition = {}; |
27 | self.targetPosition = {}; |
28 | self.isPositionDirty = false; |
29 | self.interpolationTime = 2; |
30 | self.interpolationDuration = 30; |
31 | end |
32 | self.forcedClipDistance = 60; |
33 | |
34 | self.physicsObjectDirtyFlag = self:getNextDirtyFlag(); |
35 | return self; |
36 | end; |
67 | function PhysicsObject:setNodeId(nodeId) |
68 | self.nodeId = nodeId; |
69 | setRigidBodyType(self.nodeId, self:getDefaultRigidBodyType()); |
70 | addToPhysics(self.nodeId); |
71 | local x,y,z=getTranslation(self.nodeId) |
72 | local x_rot,y_rot,z_rot=getRotation(self.nodeId); |
73 | self.sendPosX, self.sendPosY, self.sendPosZ = x,y,z; |
74 | self.sendRotX, self.sendRotY, self.sendRotZ = x_rot,y_rot,z_rot; |
75 | |
76 | if not self.isServer then |
77 | local currentPosition = self.currentPosition; |
78 | local targetPosition = self.targetPosition; |
79 | currentPosition.x,currentPosition.y,currentPosition.z = x,y,z; |
80 | targetPosition.x,targetPosition.y,targetPosition.z = x,y,z; |
81 | currentPosition.x_rot,currentPosition.y_rot,currentPosition.z_rot,currentPosition.w_rot = mathEulerToQuaternion(x_rot,y_rot,z_rot); |
82 | targetPosition.x_rot,targetPosition.y_rot,targetPosition.z_rot,targetPosition.w_rot = currentPosition.x_rot,currentPosition.y_rot,currentPosition.z_rot,currentPosition.w_rot; |
83 | end |
84 | |
85 | self:addChildsToNodeObject(self.nodeId); |
86 | end; |
92 | function PhysicsObject:readStream(streamId, connection) |
93 | assert(self.nodeId ~= 0); |
94 | if connection:getIsServer() then |
95 | local paramsXZ = g_currentMission.vehicleXZPosCompressionParams; |
96 | local paramsY = g_currentMission.vehicleYPosCompressionParams; |
97 | local x = Utils.readCompressedWorldPosition(streamId, paramsXZ); |
98 | local y = Utils.readCompressedWorldPosition(streamId, paramsY); |
99 | local z = Utils.readCompressedWorldPosition(streamId, paramsXZ); |
100 | local x_rot=Utils.readCompressedAngle(streamId); |
101 | local y_rot=Utils.readCompressedAngle(streamId); |
102 | local z_rot=Utils.readCompressedAngle(streamId); |
103 | |
104 | local x_rot,y_rot,z_rot,w_rot = mathEulerToQuaternion(x_rot,y_rot,z_rot); |
105 | |
106 | self:setPose(x,y,z, x_rot,y_rot,z_rot,w_rot); |
107 | end; |
108 | end; |
114 | function PhysicsObject:writeStream(streamId, connection) |
115 | if not connection:getIsServer() then |
116 | local x,y,z = getTranslation(self.nodeId) |
117 | local x_rot,y_rot,z_rot = getRotation(self.nodeId); |
118 | local paramsXZ = g_currentMission.vehicleXZPosCompressionParams; |
119 | local paramsY = g_currentMission.vehicleYPosCompressionParams; |
120 | Utils.writeCompressedWorldPosition(streamId, x, paramsXZ); |
121 | Utils.writeCompressedWorldPosition(streamId, y, paramsY); |
122 | Utils.writeCompressedWorldPosition(streamId, z, paramsXZ); |
123 | Utils.writeCompressedAngle(streamId, x_rot); |
124 | Utils.writeCompressedAngle(streamId, y_rot); |
125 | Utils.writeCompressedAngle(streamId, z_rot); |
126 | end; |
127 | end; |
134 | function PhysicsObject:readUpdateStream(streamId, timestamp, connection) |
135 | if connection:getIsServer() then |
136 | if streamReadBool(streamId) then |
137 | local targetPosition = self.targetPosition; |
138 | local currentPosition = self.currentPosition; |
139 | |
140 | local deltaTime = g_client.tickDuration; |
141 | if self.lastPhysicsNetworkTime ~= nil then |
142 | deltaTime = math.min(g_packetPhysicsNetworkTime - self.lastPhysicsNetworkTime, 3*g_client.tickDuration); |
143 | end |
144 | self.lastPhysicsNetworkTime = g_packetPhysicsNetworkTime; |
145 | |
146 | -- interpTimeLeft is the time we would've continued interpolating. This should always be about g_clientInterpDelay. |
147 | -- If we extrapolated, reset to the full delay |
148 | local interpTimeLeft = g_clientInterpDelay; |
149 | if self.interpolationTime < 1 then |
150 | interpTimeLeft = (1-self.interpolationTime) * self.interpolationDuration; |
151 | interpTimeLeft = interpTimeLeft * 0.95 + g_clientInterpDelay * 0.05; -- slighly blend towards the expected delay |
152 | interpTimeLeft = math.min(interpTimeLeft, 3*g_clientInterpDelay); -- clamp to some reasonable maximum |
153 | end |
154 | self.interpolationDuration = interpTimeLeft + deltaTime; |
155 | self.interpolationTime = 0; |
156 | |
157 | self.lastPositionX = currentPosition.x; |
158 | self.lastPositionY = currentPosition.y; |
159 | self.lastPositionZ = currentPosition.z; |
160 | self.lastRotationX = currentPosition.x_rot; |
161 | self.lastRotationY = currentPosition.y_rot; |
162 | self.lastRotationZ = currentPosition.z_rot; |
163 | self.lastRotationW = currentPosition.w_rot; |
164 | |
165 | local paramsXZ = g_currentMission.vehicleXZPosCompressionParams; |
166 | local paramsY = g_currentMission.vehicleYPosCompressionParams; |
167 | targetPosition.x = Utils.readCompressedWorldPosition(streamId, paramsXZ); |
168 | targetPosition.y = Utils.readCompressedWorldPosition(streamId, paramsY); |
169 | targetPosition.z = Utils.readCompressedWorldPosition(streamId, paramsXZ); |
170 | local x_rot=Utils.readCompressedAngle(streamId); |
171 | local y_rot=Utils.readCompressedAngle(streamId); |
172 | local z_rot=Utils.readCompressedAngle(streamId); |
173 | |
174 | targetPosition.x_rot,targetPosition.y_rot,targetPosition.z_rot,targetPosition.w_rot = mathEulerToQuaternion(x_rot,y_rot,z_rot); |
175 | self.isPositionDirty = true; |
176 | |
177 | local dx,dy,dz = targetPosition.x-currentPosition.x, targetPosition.y-currentPosition.y, targetPosition.z-currentPosition.z; |
178 | if dx*dx + dy*dy + dz*dz > 25 then -- snap, if more than 5m distance |
179 | --print("snapping, diff: "..math.sqrt(dx*dx + dy*dy + dz*dz )); |
180 | self:setPose(targetPosition.x,targetPosition.y,targetPosition.z, targetPosition.x_rot,targetPosition.y_rot,targetPosition.z_rot,targetPosition.w_rot); |
181 | end; |
182 | end; |
183 | end; |
184 | end; |
191 | function PhysicsObject:writeUpdateStream(streamId, connection, dirtyMask) |
192 | if not connection:getIsServer() then |
193 | if streamWriteBool(streamId, bitAND(dirtyMask, self.physicsObjectDirtyFlag) ~= 0) then |
194 | local paramsXZ = g_currentMission.vehicleXZPosCompressionParams; |
195 | local paramsY = g_currentMission.vehicleYPosCompressionParams; |
196 | Utils.writeCompressedWorldPosition(streamId, self.sendPosX, paramsXZ); |
197 | Utils.writeCompressedWorldPosition(streamId, self.sendPosY, paramsY); |
198 | Utils.writeCompressedWorldPosition(streamId, self.sendPosZ, paramsXZ); |
199 | |
200 | Utils.writeCompressedAngle(streamId, self.sendRotX); |
201 | Utils.writeCompressedAngle(streamId, self.sendRotY); |
202 | Utils.writeCompressedAngle(streamId, self.sendRotZ); |
203 | end; |
204 | end; |
205 | end; |
210 | function PhysicsObject:update(dt) |
211 | if not self.isServer then |
212 | if self.isPositionDirty then |
213 | local maxIntrpAlpha = 1.05; |
214 | local intrpAlpha = self.interpolationTime + g_physicsDtUnclamped / self.interpolationDuration; |
215 | --local intrpAlpha = math.max((g_physicsNetworkTime-g_clientInterpDelay - self.lastPoseTime) / (self.targetPoseTime - self.lastPoseTime), 0); |
216 | --self.curPoseTime = g_physicsNetworkTime-g_clientInterpDelay; |
217 | if intrpAlpha >= maxIntrpAlpha then |
218 | intrpAlpha = maxIntrpAlpha; |
219 | self.isPositionDirty = false; |
220 | end; |
221 | self.interpolationTime = intrpAlpha; |
222 | |
223 | local curPos = self.currentPosition; |
224 | local targetPos = self.targetPosition; |
225 | |
226 | curPos.x = self.lastPositionX + (targetPos.x-self.lastPositionX)*intrpAlpha; |
227 | curPos.y = self.lastPositionY + (targetPos.y-self.lastPositionY)*intrpAlpha; |
228 | curPos.z = self.lastPositionZ + (targetPos.z-self.lastPositionZ)*intrpAlpha; |
229 | curPos.x_rot, curPos.y_rot, curPos.z_rot, curPos.w_rot = Utils.nlerpQuaternionShortestPath( |
230 | self.lastRotationX, self.lastRotationY, self.lastRotationZ, self.lastRotationW, targetPos.x_rot, targetPos.y_rot, targetPos.z_rot, targetPos.w_rot, intrpAlpha); |
231 | |
232 | setTranslation(self.nodeId, curPos.x,curPos.y,curPos.z); |
233 | setQuaternion(self.nodeId, curPos.x_rot, curPos.y_rot, curPos.z_rot, curPos.w_rot); |
234 | end |
235 | end; |
236 | end; |
242 | function PhysicsObject:updateMove() |
243 | local x,y,z=getTranslation(self.nodeId) |
244 | local x_rot,y_rot,z_rot=getRotation(self.nodeId); |
245 | 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 |
246 | 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; |
247 | |
248 | if hasMoved then |
249 | self:raiseDirtyFlags(self.physicsObjectDirtyFlag); |
250 | self.sendPosX, self.sendPosY, self.sendPosZ = x,y,z; |
251 | self.sendRotX, self.sendRotY, self.sendRotZ = x_rot,y_rot,z_rot; |
252 | end; |
253 | |
254 | return hasMoved; |
255 | end; |
355 | function PhysicsObject:setPose(x,y,z, x_rot,y_rot,z_rot,w_rot) |
356 | if not self.isServer then |
357 | local curPos = self.currentPosition; |
358 | local targetPos = self.targetPosition; |
359 | curPos.x, targetPos.x = x, x; |
360 | curPos.y, targetPos.y = y, y; |
361 | curPos.z, targetPos.z = z, z; |
362 | curPos.x_rot, targetPos.x_rot = x_rot, x_rot; |
363 | curPos.y_rot, targetPos.y_rot = y_rot, y_rot; |
364 | curPos.z_rot, targetPos.z_rot = z_rot, z_rot; |
365 | curPos.w_rot, targetPos.w_rot = w_rot, w_rot; |
366 | self.isPositionDirty = false; |
367 | self.interpolationTime = 2; |
368 | end |
369 | |
370 | setTranslation(self.nodeId, x,y,z); |
371 | setQuaternion(self.nodeId, x_rot, y_rot, z_rot, w_rot); |
372 | end |