160 | function SpeedRotatingParts:loadSpeedRotatingPartFromXML(speedRotatingPart, xmlFile, key) |
161 | speedRotatingPart.repr = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, key.."#node"), self.i3dMappings) |
162 | speedRotatingPart.shaderNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, key.."#shaderNode"), self.i3dMappings) |
163 | if speedRotatingPart.shaderNode ~= nil then |
164 | speedRotatingPart.useShaderRotation = Utils.getNoNil(getXMLBool(xmlFile, key.."#useRotation"), true) |
165 | local scale = Utils.getNoNil(getXMLString(xmlFile, key.."#scrollScale"), "1 0") |
166 | speedRotatingPart.scrollScale = StringUtil.getVectorNFromString(scale, 2) |
167 | end |
168 | |
169 | if speedRotatingPart.repr == nil and speedRotatingPart.shaderNode == nil then |
170 | g_logManager:xmlWarning(self.configFileName, "Invalid speedRotationPart node '%s' in '%s'", tostring(getXMLString(xmlFile, key.."#node")), key) |
171 | return false |
172 | end |
173 | speedRotatingPart.driveNode = Utils.getNoNil(I3DUtil.indexToObject(self.components, getXMLString(xmlFile, key .. "#driveNode"), self.i3dMappings), speedRotatingPart.repr) |
174 | |
175 | local componentIndex = getXMLInt(xmlFile, key.."#refComponentIndex") |
176 | if componentIndex ~= nil and self.components[componentIndex] ~= nil then |
177 | speedRotatingPart.componentNode = self.components[componentIndex].node |
178 | else |
179 | local node = Utils.getNoNil(speedRotatingPart.driveNode, speedRotatingPart.shaderNode) |
180 | speedRotatingPart.componentNode = self:getParentComponent(node) |
181 | end |
182 | |
183 | speedRotatingPart.xDrive = 0 |
184 | local wheelIndex = getXMLInt(xmlFile, key.."#wheelIndex") |
185 | if wheelIndex ~= nil then |
186 | if self.getWheels == nil then |
187 | g_logManager:xmlWarning(self.configFileName, "wheelIndex for speedRotatingPart '%s' given, but no wheels loaded/defined", key) |
188 | else |
189 | local wheels = self:getWheels() |
190 | local wheel = wheels[wheelIndex] |
191 | if wheel == nil then |
192 | g_logManager:xmlWarning(self.configFileName, "Invalid wheel index '%s' for speedRotatingPart '%s'", tostring(wheelIndex), key) |
193 | return false |
194 | end |
195 | if not wheel.isSynchronized then |
196 | g_logManager:xmlDevWarning(self.configFileName, "referenced wheel with index '%s' for speedRotatingPart '%s' is not synchronized in multiplayer", tostring(wheelIndex), key) |
197 | end |
198 | speedRotatingPart.wheel = wheel |
199 | speedRotatingPart.lastWheelXRot = 0 |
200 | end |
201 | end |
202 | |
203 | speedRotatingPart.dirRefNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, key.."#dirRefNode"), self.i3dMappings) |
204 | speedRotatingPart.dirFrameNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, key.."#dirFrameNode"), self.i3dMappings) |
205 | speedRotatingPart.alignDirection = Utils.getNoNil(getXMLBool(xmlFile, key .. "#alignDirection"), false) |
206 | speedRotatingPart.applySteeringAngle = Utils.getNoNil(getXMLBool(xmlFile, key .. "#applySteeringAngle"), false) |
207 | speedRotatingPart.useWheelReprTranslation = Utils.getNoNil(getXMLBool(xmlFile, key .. "#useWheelReprTranslation"), true) |
208 | speedRotatingPart.updateXDrive = Utils.getNoNil(getXMLBool(xmlFile, key .. "#updateXDrive"), true) |
209 | |
210 | speedRotatingPart.versatileYRot = Utils.getNoNil(getXMLBool(xmlFile, key .. "#versatileYRot"), false) |
211 | if speedRotatingPart.versatileYRot and speedRotatingPart.repr == nil then |
212 | g_logManager:xmlWarning(self.configFileName, "Versatile speedRotationPart '%s' does not support shaderNodes", key) |
213 | return false |
214 | end |
215 | |
216 | local minYRot = getXMLFloat(xmlFile, key .. "#minYRot") |
217 | if minYRot ~= nil then |
218 | speedRotatingPart.minYRot = math.rad(minYRot) |
219 | end |
220 | local maxYRot = getXMLFloat(xmlFile, key .. "#maxYRot") |
221 | if maxYRot ~= nil then |
222 | speedRotatingPart.maxYRot = math.rad(maxYRot) |
223 | end |
224 | speedRotatingPart.steeringAngle = 0 |
225 | speedRotatingPart.steeringAngleSent = 0 |
226 | |
227 | speedRotatingPart.wheelScale = getXMLFloat(xmlFile, key .. "#wheelScale") |
228 | if speedRotatingPart.wheelScale == nil then |
229 | local baseRadius = 1.0 |
230 | local radius = 1.0 |
231 | if speedRotatingPart.wheel ~= nil then |
232 | baseRadius = speedRotatingPart.wheel.radius |
233 | radius = speedRotatingPart.wheel.radius |
234 | end |
235 | speedRotatingPart.wheelScale = baseRadius / Utils.getNoNil(getXMLFloat(xmlFile, key.."#radius"), radius) |
236 | end |
237 | |
238 | speedRotatingPart.wheelScaleBackup = speedRotatingPart.wheelScale |
239 | |
240 | speedRotatingPart.onlyActiveWhenLowered = Utils.getNoNil(getXMLBool(xmlFile, key .. "#onlyActiveWhenLowered"), false) |
241 | speedRotatingPart.stopIfNotActive = Utils.getNoNil(getXMLBool(xmlFile, key .. "#stopIfNotActive"), false) |
242 | speedRotatingPart.fadeOutTime = Utils.getNoNil(getXMLFloat(xmlFile, key .. "#fadeOutTime"), 3) * 1000 |
243 | speedRotatingPart.activationSpeed = Utils.getNoNil(getXMLFloat(xmlFile, key .. "#activationSpeed"), 1) |
244 | speedRotatingPart.speedReferenceNode = I3DUtil.indexToObject(self.components, getXMLString(xmlFile, key.."#speedReferenceNode"), self.i3dMappings) |
245 | if speedRotatingPart.speedReferenceNode ~= nil and speedRotatingPart.speedReferenceNode == speedRotatingPart.driveNode then |
246 | g_logManager:xmlWarning(self.configFileName, "Ignoring speedRotationPart '%s' because speedReferenceNode is identical with driveNode. Need to be different!", key) |
247 | return false |
248 | end |
249 | speedRotatingPart.lastSpeed = 0 |
250 | speedRotatingPart.lastDir = 1 |
251 | |
252 | return true |
253 | end |
281 | function SpeedRotatingParts:updateSpeedRotatingPart(speedRotatingPart, dt, isPartActive) |
282 | local spec = self.spec_speedRotatingParts |
283 | local speed = speedRotatingPart.lastSpeed |
284 | local dir = speedRotatingPart.lastDir |
285 | |
286 | -- use angle from the repr node since the repr node could be rotated by another spec |
287 | if speedRotatingPart.repr ~= nil then |
288 | _, speedRotatingPart.steeringAngle, _ = getRotation(speedRotatingPart.repr) |
289 | end |
290 | |
291 | if isPartActive then |
292 | if speedRotatingPart.wheel ~= nil then |
293 | |
294 | local rotDiff = speedRotatingPart.wheel.netInfo.xDrive - speedRotatingPart.lastWheelXRot |
295 | if rotDiff > math.pi then |
296 | rotDiff = rotDiff - (2*math.pi) |
297 | elseif rotDiff < -math.pi then |
298 | rotDiff = rotDiff + (2*math.pi) |
299 | end |
300 | speed = math.abs(rotDiff) |
301 | dir = MathUtil.sign(rotDiff) |
302 | speedRotatingPart.lastWheelXRot = speedRotatingPart.wheel.netInfo.xDrive |
303 | |
304 | _, speedRotatingPart.steeringAngle, _ = getRotation(speedRotatingPart.wheel.repr) |
305 | |
306 | elseif speedRotatingPart.speedReferenceNode ~= nil then |
307 | local newX, newY, newZ = getWorldTranslation(speedRotatingPart.speedReferenceNode) |
308 | if speedRotatingPart.lastPosition == nil then |
309 | speedRotatingPart.lastPosition = {newX, newY, newZ} |
310 | end |
311 | |
312 | local dx, dy, dz = worldDirectionToLocal(speedRotatingPart.speedReferenceNode, newX-speedRotatingPart.lastPosition[1], newY-speedRotatingPart.lastPosition[2], newZ-speedRotatingPart.lastPosition[3]) |
313 | speed = MathUtil.vector3Length(dx, dy, dz) |
314 | |
315 | if dz > 0.001 then |
316 | dir = 1 |
317 | elseif dz < -0.001 then |
318 | dir = -1 |
319 | else |
320 | dir = 0 |
321 | end |
322 | |
323 | speedRotatingPart.lastPosition[1], speedRotatingPart.lastPosition[2], speedRotatingPart.lastPosition[3] = newX, newY, newZ |
324 | else |
325 | speed = self.lastSpeedReal * dt |
326 | dir = self.movingDirection |
327 | end |
328 | speedRotatingPart.brakeForce = speed * dt/speedRotatingPart.fadeOutTime |
329 | else |
330 | speed = math.max(speed - speedRotatingPart.brakeForce, 0) |
331 | end |
332 | |
333 | speedRotatingPart.lastSpeed = speed |
334 | speedRotatingPart.lastDir = dir |
335 | if speedRotatingPart.updateXDrive then |
336 | speedRotatingPart.xDrive = speedRotatingPart.xDrive + speed * dir * self:getSpeedRotatingPartDirection(speedRotatingPart) * speedRotatingPart.wheelScale |
337 | end |
338 | |
339 | if speedRotatingPart.versatileYRot then |
340 | if speed > 0.0017 then -- 0.1deg threshold cause float accuracy |
341 | if self.isServer and self:getLastSpeed(true) > speedRotatingPart.activationSpeed then |
342 | local posX, posY, posZ = localToLocal(speedRotatingPart.repr, speedRotatingPart.componentNode, 0,0,0) |
343 | speedRotatingPart.steeringAngle = Utils.getVersatileRotation(speedRotatingPart.repr, speedRotatingPart.componentNode, dt, posX, posY, posZ, speedRotatingPart.steeringAngle, speedRotatingPart.minYRot, speedRotatingPart.maxYRot) |
344 | if math.abs(speedRotatingPart.steeringAngleSent-speedRotatingPart.steeringAngle) > 0.1 then |
345 | speedRotatingPart.steeringAngleSent = speedRotatingPart.steeringAngle |
346 | self:raiseDirtyFlags(spec.dirtyFlag) |
347 | end |
348 | end |
349 | end |
350 | else |
351 | if speedRotatingPart.componentNode ~= nil and speedRotatingPart.dirRefNode ~= nil and not speedRotatingPart.alignDirection then |
352 | speedRotatingPart.steeringAngle = Utils.getYRotationBetweenNodes(speedRotatingPart.componentNode, speedRotatingPart.dirRefNode) |
353 | local _,yTrans,_ = localToLocal(speedRotatingPart.driveNode, speedRotatingPart.wheel.driveNode, 0, 0, 0) |
354 | setTranslation(speedRotatingPart.driveNode, 0, yTrans, 0) |
355 | end |
356 | |
357 | if speedRotatingPart.dirRefNode ~= nil and speedRotatingPart.alignDirection then |
358 | local upX, upY, upZ = localDirectionToWorld(speedRotatingPart.dirFrameNode, 0, 1, 0) |
359 | local dirX, dirY, dirZ = localDirectionToWorld(speedRotatingPart.dirRefNode, 0, 0, 1) |
360 | I3DUtil.setWorldDirection(speedRotatingPart.repr, dirX, dirY, dirZ, upX, upY, upZ, 2) |
361 | if speedRotatingPart.wheel ~= nil and speedRotatingPart.useWheelReprTranslation then |
362 | local _,yTrans,_ = localToLocal(speedRotatingPart.wheel.driveNode, getParent(speedRotatingPart.repr), 0,0,0) |
363 | setTranslation(speedRotatingPart.repr, 0, yTrans, 0) |
364 | end |
365 | end |
366 | end |
367 | |
368 | if speedRotatingPart.driveNode ~= nil then |
369 | if speedRotatingPart.repr == speedRotatingPart.driveNode then |
370 | local steeringAngle =speedRotatingPart.steeringAngle |
371 | if not speedRotatingPart.applySteeringAngle then |
372 | steeringAngle = 0 |
373 | end |
374 | |
375 | setRotation(speedRotatingPart.repr, speedRotatingPart.xDrive, steeringAngle, 0) |
376 | else |
377 | if not speedRotatingPart.alignDirection and (speedRotatingPart.versatileYRot or speedRotatingPart.applySteeringAngle) then |
378 | setRotation(speedRotatingPart.repr, 0, speedRotatingPart.steeringAngle, 0) |
379 | end |
380 | setRotation(speedRotatingPart.driveNode, speedRotatingPart.xDrive, 0, 0) |
381 | end |
382 | end |
383 | |
384 | if speedRotatingPart.shaderNode ~= nil then |
385 | if speedRotatingPart.useShaderRotation then |
386 | setShaderParameter(speedRotatingPart.shaderNode, "offsetUV", 0, 0, speedRotatingPart.xDrive, 0, false) |
387 | else |
388 | local pos = (speedRotatingPart.xDrive % math.pi) / (2*math.pi) -- normalize rotation |
389 | setShaderParameter(speedRotatingPart.shaderNode, "offsetUV", pos*speedRotatingPart.scrollScale[1], pos*speedRotatingPart.scrollScale[2], 0, 0, false) |
390 | end |
391 | end |
392 | end |