Copyright (c) 2008-2015 GIANTS Software GmbH, Confidential, All Rights Reserved.
This document is to be published solely by ls-mods.de
1 | -- |
2 | -- Cylindered |
3 | -- This is the specialization for vehicles which have movable parts |
4 | -- |
5 | -- @author Stefan Geiger |
6 | -- @date 18/08/09 |
7 | -- |
8 | -- Copyright (C) GIANTS Software GmbH, Confidential, All Rights Reserved. |
9 | |
10 | Cylindered = {}; |
11 | |
12 | g_cylinderedFix = true; |
13 | |
14 | function Cylindered.prerequisitesPresent(specializations) |
15 | --return SpecializationUtil.hasSpecialization(Steerable, specializations); |
16 | return true; |
17 | end; |
18 | |
19 | function Cylindered:load(xmlFile) |
20 | |
21 | self.setMovingToolDirty = SpecializationUtil.callSpecializationsFunction("setMovingToolDirty"); |
22 | self.updateCylinderedInitial = SpecializationUtil.callSpecializationsFunction("updateCylinderedInitial"); |
23 | self.isDetachAllowed = Utils.overwrittenFunction(self.isDetachAllowed, Cylindered.isDetachAllowed); |
24 | |
25 | if self.isClient then |
26 | self.sampleCylinderedHydraulic = Utils.loadSample(xmlFile, {}, "vehicle.cylinderedHydraulicSound", nil, self.baseDirectory); |
27 | end; |
28 | |
29 | self.activeDirtyMovingParts = {}; |
30 | |
31 | local referenceNodes = {}; |
32 | self.nodesToMovingParts = {}; |
33 | self.movingParts = {}; |
34 | self.detachLockNodes = nil; |
35 | local i=0; |
36 | while true do |
37 | local baseName = string.format("vehicle.movingParts.movingPart(%d)", i); |
38 | if not hasXMLProperty(xmlFile, baseName) then |
39 | break; |
40 | end; |
41 | |
42 | local node = Utils.indexToObject(self.components, getXMLString(xmlFile, baseName.."#index")); |
43 | local referenceFrame = Utils.indexToObject(self.components, getXMLString(xmlFile, baseName.."#referenceFrame")); |
44 | if node ~= nil and referenceFrame ~= nil then |
45 | local entry = {}; |
46 | entry.referencePoint = Utils.indexToObject(self.components, getXMLString(xmlFile, baseName.."#referencePoint")); |
47 | entry.node = node; |
48 | entry.referenceFrame = referenceFrame; |
49 | entry.invertZ = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#invertZ"), false); |
50 | entry.scaleZ = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#scaleZ"), false); |
51 | entry.limitedAxis = getXMLInt(xmlFile, baseName.."#limitedAxis"); |
52 | local isActiveDirty = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#isActiveDirty"), false); |
53 | entry.playSound = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#playSound"), not isActiveDirty) |
54 | |
55 | entry.moveToReferenceFrame = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#moveToReferenceFrame"), false); |
56 | if entry.moveToReferenceFrame then |
57 | local x,y,z = worldToLocal(referenceFrame, getWorldTranslation(node)); |
58 | entry.referenceFrameOffset = {x,y,z}; |
59 | end |
60 | |
61 | entry.doDirectionAlignment = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#doDirectionAlignment"), true); |
62 | |
63 | local minRot = getXMLFloat(xmlFile, baseName.."#minRot"); |
64 | local maxRot = getXMLFloat(xmlFile, baseName.."#maxRot"); |
65 | if minRot ~= nil and maxRot ~= nil then |
66 | if entry.limitedAxis ~= nil then |
67 | entry.minRot = Utils.getValidLimit(math.rad(minRot)); |
68 | entry.maxRot = Utils.getValidLimit(math.rad(maxRot)); |
69 | else |
70 | print("Warning: minRot/maxRot requires the use of limitedAxis in '"..self.configFileName.."'"); |
71 | end; |
72 | end; |
73 | entry.alignToWorldY = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#alignToWorldY"), false); |
74 | |
75 | if entry.referencePoint ~= nil then |
76 | local localReferencePoint = Utils.indexToObject(self.components, getXMLString(xmlFile, baseName.."#localReferencePoint")); |
77 | local refX, refY, refZ = worldToLocal(node, getWorldTranslation(entry.referencePoint)); |
78 | if localReferencePoint ~= nil then |
79 | local x,y,z = worldToLocal(node, getWorldTranslation(localReferencePoint)); |
80 | |
81 | entry.referenceDistance = Utils.vector3Length(refX-x, refY-y, refZ-z); |
82 | entry.localReferencePoint = {x, y, z}; |
83 | |
84 | local side = y*(refZ-z) - z*(refY-y); |
85 | entry.localReferenceAngleSide = side; |
86 | entry.localReferencePointNode = localReferencePoint; |
87 | entry.updateLocalReferenceDistance = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#updateLocalReferenceDistance"), false); |
88 | else |
89 | entry.referenceDistance = 0; |
90 | entry.localReferencePoint = {refX, refY, refZ}; |
91 | end; |
92 | entry.useLocalOffset = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#useLocalOffset"), false); |
93 | |
94 | entry.localReferenceDistance = Utils.vector2Length(entry.localReferencePoint[2], entry.localReferencePoint[3]); |
95 | |
96 | Cylindered.loadTranslatingParts(self, xmlFile, baseName, entry); |
97 | |
98 | end |
99 | entry.isDirty = false; |
100 | |
101 | |
102 | if referenceNodes[node] == nil then |
103 | referenceNodes[node] = {}; |
104 | end; |
105 | table.insert(referenceNodes[node], entry); |
106 | |
107 | Cylindered.loadDependentParts(self, xmlFile, baseName, entry); |
108 | |
109 | Cylindered.loadComponentJoints(self, xmlFile, baseName, entry); |
110 | Cylindered.loadAttacherJoints(self, xmlFile, baseName, entry); |
111 | |
112 | Cylindered.loadCopyLocalDirectionParts(self, xmlFile, baseName, entry); |
113 | |
114 | table.insert(self.movingParts, entry); |
115 | |
116 | if isActiveDirty then |
117 | table.insert(self.activeDirtyMovingParts, entry); |
118 | end |
119 | |
120 | self.nodesToMovingParts[node] = entry; |
121 | end; |
122 | i = i+1; |
123 | end; |
124 | |
125 | self.isActiveDirtyTimeOffset = Utils.getNoNil(getXMLFloat(xmlFile, "vehicle.movingParts#isActiveDirtyTimeOffset"), 0) * 1000; |
126 | self.isActiveDirtyTime = g_currentMission.time; |
127 | |
128 | -- find dependencies |
129 | for _, part in pairs(self.movingParts) do |
130 | part.dependentParts = {}; |
131 | for _, ref in pairs(part.dependentPartNodes) do |
132 | if referenceNodes[ref] ~= nil then |
133 | for _, p in pairs(referenceNodes[ref]) do |
134 | part.dependentParts[p] = p; |
135 | end; |
136 | end; |
137 | end; |
138 | end; |
139 | |
140 | |
141 | function hasDependentPart(w1, w2) |
142 | if w1.dependentParts[w2] ~= nil then |
143 | return true; |
144 | else |
145 | for _, v in pairs(w1.dependentParts) do |
146 | if hasDependentPart(v, w2) then |
147 | return true; |
148 | end; |
149 | end; |
150 | end; |
151 | return false; |
152 | end; |
153 | |
154 | -- sort moving parts by dependencies |
155 | function movingPartsSort(w1,w2) |
156 | if hasDependentPart(w1, w2) then |
157 | return true; |
158 | end |
159 | if not hasDependentPart(w2, w1) then |
160 | return w1.node < w2.node; |
161 | end |
162 | return false; |
163 | end |
164 | |
165 | table.sort(self.movingParts, movingPartsSort); |
166 | |
167 | self.nodesToMovingTools = {}; |
168 | self.movingTools = {}; |
169 | local i=0; |
170 | while true do |
171 | local baseName = string.format("vehicle.movingTools.movingTool(%d)", i); |
172 | if not hasXMLProperty(xmlFile, baseName) then |
173 | break; |
174 | end; |
175 | local node = Utils.indexToObject(self.components, getXMLString(xmlFile, baseName.."#index")); |
176 | if node ~= nil then |
177 | local entry = {}; |
178 | entry.node = node; |
179 | |
180 | entry.externalRotSpeed = 0; |
181 | entry.externalTransSpeed = 0; |
182 | -- rotation |
183 | local rotSpeed = getXMLFloat(xmlFile, baseName.."#rotSpeed"); |
184 | if rotSpeed ~= nil then |
185 | entry.rotSpeed = math.rad(rotSpeed)/1000; |
186 | end; |
187 | local rotAcceleration = getXMLFloat(xmlFile, baseName.."#rotAcceleration"); |
188 | if rotAcceleration ~= nil then |
189 | entry.rotAcceleration = math.rad(rotAcceleration)/(1000*1000); |
190 | end; |
191 | entry.lastRotSpeed = 0; |
192 | local rotMax = getXMLFloat(xmlFile, baseName.."#rotMax"); |
193 | if rotMax ~= nil then |
194 | entry.rotMax = math.rad(rotMax); |
195 | end; |
196 | local rotMin = getXMLFloat(xmlFile, baseName.."#rotMin"); |
197 | if rotMin ~= nil then |
198 | entry.rotMin = math.rad(rotMin); |
199 | end; |
200 | entry.syncMaxRotLimits = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#syncMaxRotLimits"), false); |
201 | entry.syncMinRotLimits = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#syncMinRotLimits"), false); |
202 | entry.rotSendNumBits = Utils.getNoNil(getXMLInt(xmlFile, baseName.."#rotSendNumBits"), 8); |
203 | local attachRotMax = getXMLFloat(xmlFile, baseName.."#attachRotMax"); |
204 | if attachRotMax ~= nil then |
205 | entry.attachRotMax = math.rad(attachRotMax); |
206 | end; |
207 | local attachRotMin = getXMLFloat(xmlFile, baseName.."#attachRotMin"); |
208 | if attachRotMin ~= nil then |
209 | entry.attachRotMin = math.rad(attachRotMin); |
210 | end; |
211 | |
212 | -- translation |
213 | local transSpeed = getXMLFloat(xmlFile, baseName.."#transSpeed"); |
214 | if transSpeed ~= nil then |
215 | entry.transSpeed = transSpeed/1000; |
216 | end; |
217 | local transAcceleration = getXMLFloat(xmlFile, baseName.."#transAcceleration"); |
218 | if transAcceleration ~= nil then |
219 | entry.transAcceleration = transAcceleration/(1000*1000); |
220 | end; |
221 | entry.lastTransSpeed = 0; |
222 | entry.transMax = Utils.getNoNil(getXMLFloat(xmlFile, baseName.."#transMax"), 1); |
223 | entry.transMin = Utils.getNoNil(getXMLFloat(xmlFile, baseName.."#transMin"), 0); |
224 | entry.transSendNumBits = Utils.getNoNil(getXMLInt(xmlFile, baseName.."#transSendNumBits"), 8); |
225 | entry.attachTransMax = getXMLFloat(xmlFile, baseName.."#attachTransMax"); |
226 | entry.attachTransMin = getXMLFloat(xmlFile, baseName.."#attachTransMin"); |
227 | |
228 | entry.axis = getXMLString(xmlFile, baseName.."#axis"); |
229 | if entry.axis ~= nil then |
230 | entry.axisActionIndex = InputBinding[entry.axis]; |
231 | |
232 | if entry.axisActionIndex ~= nil then |
233 | self:addConflictCheckedInput(entry.axisActionIndex); |
234 | end |
235 | end |
236 | entry.invertAxis = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#invertAxis"), false); |
237 | entry.invertMouseAxis = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#invertMouseAxis"), false); |
238 | entry.speedFactor = Utils.getNoNil(getXMLFloat(xmlFile, baseName.."#speedFactor"), 1.0); |
239 | entry.isDirty = false; |
240 | entry.rotationAxis = Utils.getNoNil(getXMLInt(xmlFile, baseName.."#rotationAxis"), 1); |
241 | entry.translationAxis = Utils.getNoNil(getXMLInt(xmlFile, baseName.."#translationAxis"), 3); |
242 | |
243 | entry.foldMinLimit = Utils.getNoNil(getXMLFloat(xmlFile, baseName .. "#foldMinLimit"), 0); |
244 | entry.foldMaxLimit = Utils.getNoNil(getXMLFloat(xmlFile, baseName .. "#foldMaxLimit"), 1); |
245 | |
246 | local detachingRotMaxLimit = getXMLFloat(xmlFile, baseName.."#detachingRotMaxLimit"); |
247 | local detachingRotMinLimit = getXMLFloat(xmlFile, baseName.."#detachingRotMinLimit"); |
248 | local detachingTransMaxLimit = getXMLFloat(xmlFile, baseName.."#detachingTransMaxLimit"); |
249 | local detachingTransMinLimit = getXMLFloat(xmlFile, baseName.."#detachingTransMinLimit"); |
250 | if detachingRotMaxLimit ~= nil or detachingRotMinLimit ~= nil or detachingTransMaxLimit ~= nil or detachingTransMinLimit ~= nil then |
251 | if self.detachLockNodes == nil then |
252 | self.detachLockNodes = {}; |
253 | end; |
254 | |
255 | local detachLock = {}; |
256 | if detachingRotMaxLimit ~= nil then |
257 | detachLock.detachingRotMaxLimit = math.rad(detachingRotMaxLimit); |
258 | end; |
259 | if detachingRotMinLimit ~= nil then |
260 | detachLock.detachingRotMinLimit = math.rad(detachingRotMinLimit); |
261 | end; |
262 | detachLock.detachingTransMaxLimit = detachingTransMaxLimit; |
263 | detachLock.detachingTransMaxLimit = detachingTransMaxLimit; |
264 | |
265 | self.detachLockNodes[entry] = detachLock; |
266 | end; |
267 | |
268 | |
269 | local x,y,z = getRotation(node); |
270 | entry.curRot = {x,y,z}; |
271 | local x,y,z = getTranslation(node); |
272 | entry.curTrans = {x,y,z}; |
273 | |
274 | entry.startRot = getXMLFloat(xmlFile, baseName.."#startRot"); |
275 | if entry.startRot ~= nil then |
276 | entry.startRot = math.rad(entry.startRot); |
277 | end; |
278 | entry.startTrans = getXMLFloat(xmlFile, baseName.."#startTrans"); |
279 | |
280 | |
281 | entry.isDelayed = Utils.getNoNil(getXMLBool(xmlFile, baseName.."#isDelayed"), false); |
282 | |
283 | if entry.isDelayed then |
284 | entry.history = {}; |
285 | entry.history[1] = {move=0, moveDt=0}; |
286 | entry.history[2] = {move=0, moveDt=0}; |
287 | entry.history[3] = {move=0, moveDt=0}; |
288 | end |
289 | |
290 | entry.adjustAttributesToMovingToolIndex = getXMLInt(xmlFile, baseName.."#adjustAttributesToMovingToolIndex"); |
291 | |
292 | if referenceNodes[node] == nil then |
293 | referenceNodes[node] = {}; |
294 | end; |
295 | table.insert(referenceNodes[node], entry); |
296 | |
297 | local indices = Utils.getVectorNFromString(getXMLString(xmlFile, baseName.. "#wheelIndices")); |
298 | if indices ~= nil then |
299 | local wheels = {}; |
300 | for _,wheelIndex in pairs(indices) do |
301 | local wheel = self.wheels[wheelIndex]; |
302 | if wheel ~= nil then |
303 | table.insert(wheels, wheel); |
304 | else |
305 | print("Warning: Invalid wheelIndex '"..wheelIndex.."' in '"..self.configFileName.."'"); |
306 | end; |
307 | end; |
308 | if table.getn(wheels) > 0 then |
309 | entry.wheels = wheels; |
310 | end; |
311 | end; |
312 | |
313 | Cylindered.loadDependentMovingTools(self, xmlFile, baseName, entry); |
314 | |
315 | Cylindered.loadDependentParts(self, xmlFile, baseName, entry); |
316 | |
317 | Cylindered.loadComponentJoints(self, xmlFile, baseName, entry); |
318 | Cylindered.loadAttacherJoints(self, xmlFile, baseName, entry); |
319 | |
320 | entry.isActive = true; |
321 | table.insert(self.movingTools, entry); |
322 | self.nodesToMovingTools[node] = entry; |
323 | end; |
324 | i = i+1; |
325 | end; |
326 | |
327 | for _, part in pairs(self.movingTools) do |
328 | part.dependentParts = {}; |
329 | for _, ref in pairs(part.dependentPartNodes) do |
330 | if referenceNodes[ref] ~= nil then |
331 | for _, p in pairs(referenceNodes[ref]) do |
332 | part.dependentParts[p] = p; |
333 | end; |
334 | end; |
335 | end; |
336 | for _, dependentTool in pairs(part.dependentMovingTools) do |
337 | dependentTool.movingTool = self.nodesToMovingTools[dependentTool.node]; |
338 | end; |
339 | end; |
340 | |
341 | for _, tool in pairs(self.movingTools) do |
342 | if tool.startRot ~= nil then |
343 | tool.curRot[tool.rotationAxis] = tool.startRot; |
344 | setRotation(tool.node, unpack(tool.curRot)); |
345 | end; |
346 | if tool.startTrans ~= nil then |
347 | tool.curTrans[tool.translationAxis] = tool.startTrans; |
348 | setTranslation(tool.node, unpack(tool.curTrans)); |
349 | end; |
350 | Cylindered.setDirty(self, tool); |
351 | end; |
352 | |
353 | self.cylinderedDirtyFlag = self:getNextDirtyFlag(); |
354 | |
355 | for _, tool in pairs(self.movingTools) do |
356 | if (tool.rotSpeed ~= nil or tool.transSpeed ~= nil) and tool.axisActionIndex ~= nil then |
357 | self.isSelectable = true; |
358 | break; |
359 | end |
360 | end |
361 | |
362 | end; |
363 | |
364 | function Cylindered:delete() |
365 | if self.isClient then |
366 | Utils.deleteSample(self.sampleCylinderedHydraulic); |
367 | end; |
368 | end; |
369 | |
370 | function Cylindered:readStream(streamId, connection) |
371 | for i=1, table.getn(self.movingTools) do |
372 | local tool = self.movingTools[i]; |
373 | local changed = false; |
374 | if tool.transSpeed ~= nil then |
375 | local newTrans=streamReadFloat32(streamId); |
376 | if math.abs(newTrans - tool.curTrans[tool.translationAxis]) > 0.0001 then |
377 | tool.curTrans[tool.translationAxis] = newTrans; |
378 | setTranslation(tool.node, unpack(tool.curTrans)); |
379 | changed = true; |
380 | end; |
381 | end; |
382 | if tool.rotSpeed ~= nil then |
383 | local newRot=streamReadFloat32(streamId) |
384 | if math.abs(newRot - tool.curRot[tool.rotationAxis]) > 0.0001 then |
385 | tool.curRot[tool.rotationAxis] = newRot; |
386 | setRotation(tool.node, unpack(tool.curRot)); |
387 | changed = true; |
388 | end; |
389 | end; |
390 | if changed then |
391 | Cylindered.setDirty(self, tool); |
392 | end; |
393 | end; |
394 | end; |
395 | |
396 | function Cylindered:writeStream(streamId, connection) |
397 | for i=1, table.getn(self.movingTools) do |
398 | local tool = self.movingTools[i]; |
399 | if tool.transSpeed ~= nil then |
400 | streamWriteFloat32(streamId, tool.curTrans[tool.translationAxis]); |
401 | end; |
402 | if tool.rotSpeed ~= nil then |
403 | streamWriteFloat32(streamId, tool.curRot[tool.rotationAxis]); |
404 | end; |
405 | end; |
406 | end; |
407 | |
408 | function Cylindered:readUpdateStream(streamId, timestamp, connection) |
409 | local hasUpdate = streamReadBool(streamId); |
410 | if hasUpdate then |
411 | for i=1, table.getn(self.movingTools) do |
412 | local tool = self.movingTools[i]; |
413 | if tool.axisActionIndex ~= nil then |
414 | local changed = false; |
415 | if tool.transSpeed ~= nil then |
416 | local newTrans=Utils.readCompressedRange(streamId, tool.transMin, tool.transMax, tool.transSendNumBits); |
417 | if math.abs(newTrans - tool.curTrans[tool.translationAxis]) > 0.0001 then |
418 | tool.curTrans[tool.translationAxis] = newTrans; |
419 | setTranslation(tool.node, unpack(tool.curTrans)); |
420 | changed = true; |
421 | end; |
422 | end; |
423 | if tool.rotSpeed ~= nil then |
424 | local newRot; |
425 | if tool.rotMin == nil or tool.rotMax == nil then |
426 | newRot = Utils.readCompressedAngle(streamId); |
427 | else |
428 | if tool.syncMinRotLimits then |
429 | tool.rotMin = streamReadFloat32(streamId); |
430 | end; |
431 | if tool.syncMaxRotLimits then |
432 | tool.rotMax = streamReadFloat32(streamId); |
433 | end; |
434 | newRot = Utils.readCompressedRange(streamId, tool.rotMin, tool.rotMax, tool.rotSendNumBits); |
435 | end; |
436 | if math.abs(newRot - tool.curRot[tool.rotationAxis]) > 0.0001 then |
437 | tool.curRot[tool.rotationAxis] = newRot; |
438 | setRotation(tool.node, unpack(tool.curRot)); |
439 | changed = true; |
440 | end; |
441 | end; |
442 | if changed then |
443 | Cylindered.setDirty(self, tool); |
444 | end; |
445 | end |
446 | end; |
447 | if not connection:getIsServer() then |
448 | -- we are on the server, write the data to the clients |
449 | self:raiseDirtyFlags(self.cylinderedDirtyFlag); |
450 | end; |
451 | end; |
452 | end; |
453 | |
454 | function Cylindered:writeUpdateStream(streamId, connection, dirtyMask) |
455 | if bitAND(dirtyMask, self.cylinderedDirtyFlag) ~= 0 and (connection:getIsServer() or connection ~= self:getOwner()) then |
456 | -- either we are on the client, or the target connection is not the owner |
457 | streamWriteBool(streamId, true); |
458 | for i=1, table.getn(self.movingTools) do |
459 | local tool = self.movingTools[i]; |
460 | if tool.axisActionIndex ~= nil then |
461 | if tool.transSpeed ~= nil then |
462 | Utils.writeCompressedRange(streamId, tool.curTrans[tool.translationAxis], tool.transMin, tool.transMax, tool.transSendNumBits); |
463 | end; |
464 | if tool.rotSpeed ~= nil then |
465 | local rot = tool.curRot[tool.rotationAxis]; |
466 | if tool.rotMin == nil or tool.rotMax == nil then |
467 | Utils.writeCompressedAngle(streamId, rot); |
468 | else |
469 | if tool.syncMinRotLimits then |
470 | streamWriteFloat32(streamId, tool.rotMin); |
471 | end; |
472 | if tool.syncMaxRotLimits then |
473 | streamWriteFloat32(streamId, tool.rotMax); |
474 | end; |
475 | Utils.writeCompressedRange(streamId, rot, tool.rotMin, tool.rotMax, tool.rotSendNumBits); |
476 | end; |
477 | end; |
478 | end |
479 | end; |
480 | else |
481 | streamWriteBool(streamId, false); |
482 | end; |
483 | end; |
484 | |
485 | |
486 | function Cylindered.loadDependentMovingTools(self, xmlFile, baseName, entry) |
487 | entry.dependentMovingTools = {}; |
488 | local j=0; |
489 | while true do |
490 | local refBaseName = baseName..string.format(".dependentMovingTool(%d)", j); |
491 | if not hasXMLProperty(xmlFile, refBaseName) then |
492 | break; |
493 | end; |
494 | local node = Utils.indexToObject(self.components, getXMLString(xmlFile, refBaseName.."#index")); |
495 | local rotSpeedScale = getXMLFloat(xmlFile, refBaseName.."#rotSpeedScale"); |
496 | local transSpeedScale = getXMLFloat(xmlFile, refBaseName.."#transSpeedScale"); |
497 | local minTransLimits = getXMLString(xmlFile, refBaseName.."#minTransLimits") |
498 | local maxTransLimits = getXMLString(xmlFile, refBaseName.."#maxTransLimits") |
499 | local minRotLimits = getXMLString(xmlFile, refBaseName.."#minRotLimits") |
500 | local maxRotLimits = getXMLString(xmlFile, refBaseName.."#maxRotLimits") |
501 | if node ~= nil and (rotSpeedScale ~= nil or transSpeedScale ~= nil or minTransLimits ~= nil or maxTransLimits ~= nil or minRotLimits ~= nil or maxRotLimits ~= nil) then |
502 | local dependentTool = {}; |
503 | dependentTool.node = node; |
504 | dependentTool.rotSpeedScale = rotSpeedScale; |
505 | dependentTool.transSpeedScale = transSpeedScale; |
506 | dependentTool.minTransLimits = Utils.getVectorNFromString(minTransLimits, 2); |
507 | dependentTool.maxTransLimits = Utils.getVectorNFromString(maxTransLimits, 2); |
508 | dependentTool.minRotLimits = Utils.getRadiansFromString(minRotLimits, 2); |
509 | dependentTool.maxRotLimits = Utils.getRadiansFromString(maxRotLimits, 2); |
510 | table.insert(entry.dependentMovingTools, dependentTool); |
511 | end; |
512 | |
513 | j = j+1; |
514 | end; |
515 | end; |
516 | |
517 | function Cylindered.loadDependentParts(self, xmlFile, baseName, entry) |
518 | entry.dependentPartNodes = {}; |
519 | local j=0; |
520 | while true do |
521 | local refBaseName = baseName..string.format(".dependentPart(%d)", j); |
522 | if not hasXMLProperty(xmlFile, refBaseName) then |
523 | break; |
524 | end; |
525 | local node = Utils.indexToObject(self.components, getXMLString(xmlFile, refBaseName.."#index")); |
526 | if node ~= nil then |
527 | table.insert(entry.dependentPartNodes, node); |
528 | end; |
529 | j = j+1; |
530 | end; |
531 | end; |
532 | |
533 | function Cylindered.loadComponentJoints(self, xmlFile, baseName, entry) |
534 | local indices = Utils.getVectorNFromString(getXMLString(xmlFile, baseName.. "#componentJointIndex")); |
535 | local actors = Utils.getNoNil(Utils.getVectorNFromString(getXMLString(xmlFile, baseName.. "#anchorActor")), {}); |
536 | if indices ~= nil then |
537 | local componentJoints = {}; |
538 | for i=1, table.getn(indices) do |
539 | local componentJoint = self.componentJoints[indices[i]+1]; |
540 | if componentJoint ~= nil then |
541 | local anchorActor = Utils.getNoNil(actors[i], 0); |
542 | local jointEntry = {componentJoint=componentJoint, anchorActor=anchorActor }; |
543 | |
544 | local node = self.components[componentJoint.componentIndices[((anchorActor+1)%2)+1] ].node; |
545 | jointEntry.x,jointEntry.y,jointEntry.z = worldToLocal(componentJoint.jointNode, getWorldTranslation(node)); |
546 | jointEntry.upX,jointEntry.upY,jointEntry.upZ = worldDirectionToLocal(componentJoint.jointNode, localDirectionToWorld(node, 0, 1, 0)); |
547 | jointEntry.dirX,jointEntry.dirY,jointEntry.dirZ = worldDirectionToLocal(componentJoint.jointNode, localDirectionToWorld(node, 0, 0, 1)); |
548 | |
549 | table.insert(componentJoints, jointEntry ); |
550 | end; |
551 | end; |
552 | if table.getn(componentJoints) > 0 then |
553 | entry.componentJoints = componentJoints; |
554 | end; |
555 | end; |
556 | end; |
557 | |
558 | function Cylindered.loadAttacherJoints(self, xmlFile, baseName, entry) |
559 | |
560 | local indices = Utils.getVectorNFromString(getXMLString(xmlFile, baseName.. "#attacherJointIndices")); |
561 | --local index = getXMLInt(xmlFile, baseName.. "#componentJointIndex"); |
562 | if indices ~= nil then |
563 | local attacherJoints = {}; |
564 | for i=1, table.getn(indices) do |
565 | local attacherJoint = self.attacherJoints[indices[i]+1]; |
566 | if attacherJoint ~= nil then |
567 | table.insert(attacherJoints, attacherJoint); |
568 | end; |
569 | end; |
570 | if table.getn(attacherJoints) > 0 then |
571 | entry.attacherJoints = attacherJoints; |
572 | end; |
573 | end; |
574 | entry.inputAttacherJoint = Utils.getNoNil(getXMLBool(xmlFile, baseName.. "#inputAttacherJoint"), false); |
575 | end; |
576 | |
577 | function Cylindered.loadTranslatingParts(self, xmlFile, baseName, entry) |
578 | entry.translatingParts = {}; |
579 | if entry.referencePoint ~= nil then |
580 | local j=0; |
581 | while true do |
582 | local refBaseName = baseName..string.format(".translatingPart(%d)", j); |
583 | if not hasXMLProperty(xmlFile, refBaseName) then |
584 | break; |
585 | end; |
586 | local node = Utils.indexToObject(self.components, getXMLString(xmlFile, refBaseName.."#index")); |
587 | if node ~= nil then |
588 | local transEntry = {}; |
589 | transEntry.node = node; |
590 | local x,y,z = getTranslation(node); |
591 | transEntry.startPos = {x,y,z}; |
592 | local x,y,z = worldToLocal(node, getWorldTranslation(entry.referencePoint)); |
593 | transEntry.referenceDistance = z; |
594 | transEntry.referenceDistancePoint = Utils.indexToObject(self.components, getXMLString(xmlFile, refBaseName.."#referenceDistancePoint")); |
595 | table.insert(entry.translatingParts, transEntry); |
596 | end; |
597 | j = j+1; |
598 | end; |
599 | end |
600 | end; |
601 | |
602 | function Cylindered.loadCopyLocalDirectionParts(self, xmlFile, baseName, entry) |
603 | entry.copyLocalDirectionParts = {}; |
604 | local j=0; |
605 | while true do |
606 | local refBaseName = baseName..string.format(".copyLocalDirectionPart(%d)", j); |
607 | if not hasXMLProperty(xmlFile, refBaseName) then |
608 | break; |
609 | end; |
610 | local node = Utils.indexToObject(self.components, getXMLString(xmlFile, refBaseName.."#index")); |
611 | if node ~= nil then |
612 | local copyLocalDirectionPart = {}; |
613 | copyLocalDirectionPart.node = node; |
614 | copyLocalDirectionPart.dirScale = Utils.getVectorNFromString(getXMLString(xmlFile, refBaseName.."#dirScale"), 3); |
615 | copyLocalDirectionPart.upScale = Utils.getVectorNFromString(getXMLString(xmlFile, refBaseName.."#upScale"), 3); |
616 | |
617 | Cylindered.loadComponentJoints(self, xmlFile, refBaseName, copyLocalDirectionPart); |
618 | |
619 | table.insert(entry.copyLocalDirectionParts, copyLocalDirectionPart); |
620 | end; |
621 | j = j + 1; |
622 | end; |
623 | end; |
624 | |
625 | function Cylindered:loadFromAttributesAndNodes(xmlFile, key, resetVehicles) |
626 | if not resetVehicles then |
627 | for i, tool in ipairs(self.movingTools) do |
628 | if tool.axisActionIndex ~= nil then |
629 | local toolKey = key..string.format(".movingTool%d",i); |
630 | local changed = false; |
631 | if tool.transSpeed ~= nil then |
632 | local newTrans = getXMLFloat(xmlFile, toolKey.."#translation"); |
633 | if newTrans ~= nil then |
634 | if tool.transMax ~= nil then |
635 | newTrans = math.min(newTrans, tool.transMax); |
636 | end; |
637 | if tool.transMin ~= nil then |
638 | newTrans = math.max(newTrans, tool.transMin); |
639 | end; |
640 | end |
641 | if newTrans ~= nil and math.abs(newTrans - tool.curTrans[tool.translationAxis]) > 0.0001 then |
642 | tool.curTrans[tool.translationAxis] = newTrans; |
643 | setTranslation(tool.node, unpack(tool.curTrans)); |
644 | changed = true; |
645 | end; |
646 | end; |
647 | if tool.rotSpeed ~= nil then |
648 | local newRot = getXMLFloat(xmlFile, toolKey.."#rotation"); |
649 | if newRot ~= nil then |
650 | if tool.rotMax ~= nil then |
651 | newRot = math.min(newRot, tool.rotMax); |
652 | end; |
653 | if tool.rotMin ~= nil then |
654 | newRot = math.max(newRot, tool.rotMin); |
655 | end; |
656 | end |
657 | if newRot ~= nil and math.abs(newRot - tool.curRot[tool.rotationAxis]) > 0.0001 then |
658 | tool.curRot[tool.rotationAxis] = newRot; |
659 | setRotation(tool.node, unpack(tool.curRot)); |
660 | changed = true; |
661 | end; |
662 | end; |
663 | if changed then |
664 | Cylindered.setDirty(self, tool); |
665 | end; |
666 | end |
667 | end |
668 | -- cycle again to match delayed mesh and physics tools |
669 | for i, tool in ipairs(self.movingTools) do |
670 | if tool.axisActionIndex ~= nil then |
671 | local toolKey = key..string.format(".movingTool%d",i); |
672 | if tool.adjustAttributesToMovingToolIndex ~= nil then |
673 | if self.movingTools[tool.adjustAttributesToMovingToolIndex] ~= nil then |
674 | local changed = false; |
675 | if tool.transSpeed ~= nil then |
676 | local newTrans = self.movingTools[tool.adjustAttributesToMovingToolIndex].curTrans[tool.translationAxis]; |
677 | tool.curTrans[tool.translationAxis] = newTrans; |
678 | setTranslation(tool.node, unpack(tool.curTrans)); |
679 | elseif tool.rotSpeed ~= nil then |
680 | local newRot = self.movingTools[tool.adjustAttributesToMovingToolIndex].curRot[tool.rotationAxis]; |
681 | tool.curRot[tool.rotationAxis] = newRot; |
682 | setRotation(tool.node, unpack(tool.curRot)); |
683 | end; |
684 | Cylindered.setDirty(self, tool); |
685 | end; |
686 | end; |
687 | end; |
688 | end; |
689 | |
690 | self:updateCylinderedInitial(false); |
691 | end |
692 | return BaseMission.VEHICLE_LOAD_OK; |
693 | end; |
694 | |
695 | function Cylindered:getSaveAttributesAndNodes(nodeIdent) |
696 | local attributes = ""; |
697 | local nodes = ""; |
698 | local numNodes = 0; |
699 | for i, tool in ipairs(self.movingTools) do |
700 | if tool.axisActionIndex ~= nil and (tool.transSpeed ~= nil or tool.rotSpeed ~= nil) then |
701 | if numNodes > 0 then |
702 | nodes = nodes.."\n"; |
703 | end |
704 | numNodes = numNodes + 1; |
705 | |
706 | nodes = nodes.. nodeIdent..string.format('<movingTool%d', i); |
707 | if tool.transSpeed ~= nil then |
708 | nodes = nodes.. ' translation="'..tool.curTrans[tool.translationAxis]..'"'; |
709 | end |
710 | if tool.rotSpeed ~= nil then |
711 | nodes = nodes.. ' rotation="'..tool.curRot[tool.rotationAxis]..'"'; |
712 | end |
713 | nodes = nodes..'/>'; |
714 | end |
715 | end |
716 | |
717 | return attributes, nodes; |
718 | end; |
719 | |
720 | function Cylindered:mouseEvent(posX, posY, isDown, isUp, button) |
721 | end; |
722 | |
723 | function Cylindered:keyEvent(unicode, sym, modifier, isDown) |
724 | end; |
725 | |
726 | function Cylindered:update(dt) |
727 | if self:getIsActive() then |
728 | if self.isClient and self:getIsActiveForInput(false) and not self:hasInputConflictWithSelection() then |
729 | |
730 | local foldAnimTime = self.foldAnimTime; |
731 | for i=1, table.getn(self.movingTools) do |
732 | local tool = self.movingTools[i]; |
733 | local foldActive = (foldAnimTime == nil or (foldAnimTime <= tool.foldMaxLimit and foldAnimTime >= tool.foldMinLimit)); |
734 | if tool.isActive and foldActive and (tool.rotSpeed ~= nil or tool.transSpeed ~= nil) and tool.axisActionIndex ~= nil then |
735 | local move, axisType = InputBinding.getInputAxis(tool.axisActionIndex); |
736 | if axisType == InputBinding.INPUTTYPE_MOUSE_AXIS then |
737 | if tool.invertMouseAxis then |
738 | move = -move; |
739 | end |
740 | if InputBinding.actions[tool.axisActionIndex].mouseAxis == InputBinding.MOUSE_AXIS_Y then |
741 | move = move * InputBinding.yDirection; |
742 | end; |
743 | move = move * tool.speedFactor; |
744 | else |
745 | if tool.invertAxis then |
746 | move = -move; |
747 | end |
748 | if InputBinding.actions[tool.axisActionIndex].gamepadAxis == Input.AXIS_4 then |
749 | move = move * InputBinding.yDirection; |
750 | end; |
751 | end |
752 | |
753 | local moveDt = dt; |
754 | |
755 | if tool.isDelayed then |
756 | tool.history[1] = tool.history[2]; |
757 | tool.history[2] = tool.history[3]; |
758 | tool.history[3] = {move=move, moveDt=dt}; |
759 | |
760 | move = tool.history[1].move; |
761 | moveDt = tool.history[1].moveDt; |
762 | end; |
763 | local isAxisZero = InputBinding.isAxisZero(move); |
764 | |
765 | local rotSpeed = 0; |
766 | local transSpeed = 0; |
767 | if not isAxisZero then |
768 | if tool.rotSpeed ~= nil then |
769 | rotSpeed = move*tool.rotSpeed; |
770 | if tool.rotAcceleration ~= nil and math.abs(rotSpeed - tool.lastRotSpeed) >= tool.rotAcceleration*moveDt then |
771 | if rotSpeed > tool.lastRotSpeed then |
772 | rotSpeed = tool.lastRotSpeed + tool.rotAcceleration*moveDt; |
773 | else |
774 | rotSpeed = tool.lastRotSpeed - tool.rotAcceleration*moveDt; |
775 | end; |
776 | end; |
777 | end; |
778 | if tool.transSpeed ~= nil then |
779 | transSpeed = move*tool.transSpeed; |
780 | if tool.transAcceleration ~= nil and math.abs(transSpeed - tool.lastTransSpeed) >= tool.transAcceleration*moveDt then |
781 | if transSpeed > tool.lastTransSpeed then |
782 | transSpeed = tool.lastTransSpeed + tool.transAcceleration*moveDt; |
783 | else |
784 | transSpeed = tool.lastTransSpeed - tool.transAcceleration*moveDt; |
785 | end; |
786 | end; |
787 | end; |
788 | else |
789 | -- decelerate |
790 | if tool.externalRotSpeed ~= 0 then |
791 | rotSpeed = tool.externalRotSpeed; |
792 | elseif tool.rotAcceleration ~= nil then |
793 | if tool.lastRotSpeed < 0 then |
794 | rotSpeed = math.min(tool.lastRotSpeed + tool.rotAcceleration*moveDt, 0); |
795 | else |
796 | rotSpeed = math.max(tool.lastRotSpeed - tool.rotAcceleration*moveDt, 0); |
797 | end; |
798 | end; |
799 | if tool.externalTransSpeed ~= 0 then |
800 | transSpeed = tool.externalTransSpeed; |
801 | elseif tool.transAcceleration ~= nil then |
802 | if tool.lastTransSpeed < 0 then |
803 | transSpeed = math.min(tool.lastTransSpeed + tool.transAcceleration*moveDt, 0); |
804 | else |
805 | transSpeed = math.max(tool.lastTransSpeed - tool.transAcceleration*moveDt, 0); |
806 | end; |
807 | end; |
808 | end; |
809 | |
810 | local changed = false; |
811 | if rotSpeed ~= 0 then |
812 | changed = changed or Cylindered.setToolRotation(self, tool, rotSpeed, moveDt); |
813 | else |
814 | tool.lastRotSpeed = 0; |
815 | end; |
816 | if transSpeed ~= 0 then |
817 | changed = changed or Cylindered.setToolTranslation(self, tool, transSpeed, moveDt); |
818 | else |
819 | tool.lastTransSpeed = 0; |
820 | end; |
821 | |
822 | for _, dependentTool in pairs(tool.dependentMovingTools) do |
823 | if dependentTool.rotSpeedScale ~= nil and tool.lastRotSpeed ~= 0 then |
824 | dependentTool.movingTool.externalRotSpeed = dependentTool.rotSpeedScale * tool.lastRotSpeed; |
825 | end |
826 | if dependentTool.transSpeedScale ~= nil and tool.lastTransSpeed ~= 0 then |
827 | dependentTool.movingTool.externalTransSpeed = dependentTool.transSpeedScale * tool.lastTransSpeed; |
828 | end |
829 | if dependentTool.minTransLimits ~= nil or dependentTool.maxTransLimits ~= nil then |
830 | |
831 | local state = Cylindered.getMovingToolState(self, tool); |
832 | if dependentTool.minTransLimits ~= nil then |
833 | dependentTool.movingTool.transMin = Utils.lerp(dependentTool.minTransLimits[1], dependentTool.minTransLimits[2], 1-state); |
834 | end; |
835 | if dependentTool.maxTransLimits ~= nil then |
836 | dependentTool.movingTool.transMax = Utils.lerp(dependentTool.maxTransLimits[1], dependentTool.maxTransLimits[2], 1-state); |
837 | end; |
838 | local transLimitChanged = Cylindered.setToolTranslation(self, dependentTool.movingTool, 0, 0); |
839 | if transLimitChanged then |
840 | Cylindered.setDirty(self, dependentTool.movingTool); |
841 | end; |
842 | end; |
843 | if dependentTool.minRotLimits ~= nil or dependentTool.maxRotLimits ~= nil then |
844 | |
845 | local state = Cylindered.getMovingToolState(self, tool); |
846 | if dependentTool.minRotLimits ~= nil then |
847 | dependentTool.movingTool.rotMin = Utils.lerp(dependentTool.minRotLimits[1], dependentTool.minRotLimits[2], 1-state); |
848 | end; |
849 | if dependentTool.maxRotLimits ~= nil then |
850 | dependentTool.movingTool.rotMax = Utils.lerp(dependentTool.maxRotLimits[1], dependentTool.maxRotLimits[2], 1-state); |
851 | end; |
852 | local rotLimitChanged = Cylindered.setToolRotation(self, dependentTool.movingTool, 0, 0); |
853 | if rotLimitChanged then |
854 | Cylindered.setDirty(self, dependentTool.movingTool); |
855 | end; |
856 | end; |
857 | end; |
858 | |
859 | if changed then |
860 | Cylindered.setDirty(self, tool); |
861 | self:raiseDirtyFlags(self.cylinderedDirtyFlag); |
862 | end; |
863 | end; |
864 | tool.externalRotSpeed = 0; |
865 | tool.externalTransSpeed = 0; |
866 | end; |
867 | end; |
868 | |
869 | self.isActiveDirtyTime = g_currentMission.time + self.isActiveDirtyTimeOffset; |
870 | end |
871 | |
872 | if self.isActiveDirtyTime >= g_currentMission.time then |
873 | for _, part in pairs(self.activeDirtyMovingParts) do |
874 | Cylindered.setDirty(self, part); |
875 | end |
876 | end; |
877 | |
878 | for _, tool in pairs(self.movingTools) do |
879 | if tool.isDirty then |
880 | if self.isServer then |
881 | -- update component joint |
882 | Cylindered.updateComponentJoints(self, tool, false); |
883 | end; |
884 | |
885 | tool.isDirty = false; |
886 | end; |
887 | end; |
888 | |
889 | for i, part in ipairs(self.movingParts) do |
890 | if part.isDirty then |
891 | Cylindered.updateMovingPart(self, part, false); |
892 | if self.isClient and part.playSound and not self.sampleCylinderedHydraulic.isPlaying and self:getIsActiveForSound() then |
893 | self.cylinderedHydraulicSoundPartNumber = i; |
894 | Utils.playSample(self.sampleCylinderedHydraulic, 0, 0, nil); |
895 | end; |
896 | else |
897 | if self.isClient and self.cylinderedHydraulicSoundPartNumber == i then |
898 | Utils.stopSample(self.sampleCylinderedHydraulic); |
899 | end; |
900 | end; |
901 | end; |
902 | end; |
903 | |
904 | function Cylindered:updateTick(dt) |
905 | end; |
906 | |
907 | function Cylindered:draw() |
908 | end; |
909 | |
910 | function Cylindered.setToolTranslation(self, tool, transSpeed, dt) |
911 | local newTrans = tool.curTrans[tool.translationAxis]+transSpeed*dt; |
912 | if tool.transMax ~= nil then |
913 | newTrans = math.min(newTrans, tool.transMax); |
914 | end; |
915 | if tool.transMin ~= nil then |
916 | newTrans = math.max(newTrans, tool.transMin); |
917 | end; |
918 | local diff = newTrans - tool.curTrans[tool.translationAxis]; |
919 | if dt ~= 0 then |
920 | tool.lastTransSpeed = diff/dt; |
921 | end; |
922 | if math.abs(diff) > 0.0001 then |
923 | tool.curTrans[tool.translationAxis] = newTrans; |
924 | setTranslation(tool.node, unpack(tool.curTrans)); |
925 | return true; |
926 | end; |
927 | |
928 | return false; |
929 | end; |
930 | |
931 | function Cylindered.setToolRotation(self, tool, rotSpeed, dt) |
932 | local newRot = tool.curRot[tool.rotationAxis]+rotSpeed*dt; |
933 | |
934 | if tool.rotMax ~= nil then |
935 | newRot = math.min(newRot, tool.rotMax); |
936 | end |
937 | if tool.rotMin ~= nil then |
938 | newRot = math.max(newRot, tool.rotMin); |
939 | end |
940 | local diff = newRot - tool.curRot[tool.rotationAxis]; |
941 | if dt ~= 0 then |
942 | tool.lastRotSpeed = diff/dt; |
943 | end; |
944 | if math.abs(diff) > 0.0001 then |
945 | -- wrap if not limited |
946 | if tool.rotMin == nil and tool.rotMax == nil then |
947 | if newRot > 2*math.pi then |
948 | newRot = newRot - 2*math.pi; |
949 | end; |
950 | if newRot < 0 then |
951 | newRot = newRot + 2*math.pi; |
952 | end; |
953 | end |
954 | tool.curRot[tool.rotationAxis] = newRot; |
955 | setRotation(tool.node, unpack(tool.curRot)); |
956 | return true; |
957 | end; |
958 | |
959 | return false; |
960 | end; |
961 | |
962 | function Cylindered.getMovingToolState(self, tool) |
963 | local state = 0; |
964 | if tool.rotMax ~= nil and tool.rotMin ~= nil then |
965 | state = (tool.curRot[tool.rotationAxis]-tool.rotMin) / (tool.rotMax-tool.rotMin); |
966 | else |
967 | if tool.transMax ~= nil and tool.transMin ~= nil then |
968 | state = (tool.curTrans[tool.translationAxis]-tool.transMin) / (tool.transMax-tool.transMin); |
969 | end; |
970 | end; |
971 | |
972 | return state; |
973 | end; |
974 | |
975 | function Cylindered:setMovingToolDirty(node) |
976 | local tool = self.nodesToMovingTools[node]; |
977 | if tool ~= nil then |
978 | local x,y,z = getRotation(tool.node); |
979 | tool.curRot[1] = x; |
980 | tool.curRot[2] = y; |
981 | tool.curRot[3] = z; |
982 | local x,y,z = getTranslation(tool.node); |
983 | tool.curTrans[1] = x; |
984 | tool.curTrans[2] = y; |
985 | tool.curTrans[3] = z; |
986 | Cylindered.setDirty(self, tool); |
987 | end; |
988 | end; |
989 | |
990 | function Cylindered.setDirty(self, part) |
991 | if not part.isDirty then |
992 | part.isDirty = true; |
993 | |
994 | Cylindered.updateAttacherJoints(self, part); |
995 | |
996 | if part.wheels ~= nil then |
997 | for _, wheel in pairs(part.wheels) do |
998 | wheel.positionX, wheel.positionY, wheel.positionZ = localToLocal(getParent(wheel.repr), wheel.node, wheel.startPositionX, wheel.startPositionY, wheel.startPositionZ); |
999 | self:updateWheelBase(wheel); |
1000 | end; |
1001 | end; |
1002 | |
1003 | for _, v in pairs(part.dependentParts) do |
1004 | Cylindered.setDirty(self, v); |
1005 | end; |
1006 | end; |
1007 | end; |
1008 | |
1009 | function Cylindered.updateMovingPart(self, part, placeComponents) |
1010 | |
1011 | -- the local reference point must be referenceDistance away from the referencePoint |
1012 | local refX,refY,refZ; |
1013 | local dirX, dirY, dirZ = 0,0,0; |
1014 | if part.referencePoint ~= nil then |
1015 | if part.moveToReferenceFrame then |
1016 | local x,y,z = localToLocal(part.referenceFrame, getParent(part.node), part.referenceFrameOffset[1], part.referenceFrameOffset[2], part.referenceFrameOffset[3]); |
1017 | setTranslation(part.node, x,y,z); |
1018 | end |
1019 | refX,refY,refZ = getWorldTranslation(part.referencePoint); |
1020 | if part.referenceDistance == 0 then |
1021 | if part.useLocalOffset then |
1022 | local lx, ly, lz = worldToLocal(part.node, refX, refY, refZ); |
1023 | dirX, dirY, dirZ = localDirectionToWorld(part.node, lx-part.localReferencePoint[1], ly-part.localReferencePoint[2], lz); |
1024 | else |
1025 | local x,y,z = getWorldTranslation(part.node); |
1026 | dirX, dirY, dirZ = refX - x, refY-y, refZ-z; |
1027 | end |
1028 | else |
1029 | if part.updateLocalReferenceDistance then |
1030 | local _,y,z = worldToLocal(part.node, getWorldTranslation(part.localReferencePointNode)); |
1031 | part.localReferenceDistance = Utils.vector2Length(y, z); |
1032 | end; |
1033 | if part.referenceDistancePoint ~= nil then |
1034 | local _,_,z = worldToLocal(part.node, getWorldTranslation(part.referenceDistancePoint)); |
1035 | part.referenceDistance = z; |
1036 | end; |
1037 | |
1038 | local r1 = part.localReferenceDistance; |
1039 | local r2 = part.referenceDistance; |
1040 | local lx, ly, lz = worldToLocal(part.node, refX, refY, refZ); |
1041 | --print("intersect: "..ly .. " "..lz); |
1042 | local ix, iy, i2x, i2y = Utils.getCircleCircleIntersection(0,0, r1, ly, lz, r2); |
1043 | |
1044 | if ix ~= nil then |
1045 | if i2x ~= nil then |
1046 | -- use the point which as the same angle side as the original configuration |
1047 | local side = ix*(lz-iy) - iy*(ly-ix); |
1048 | if (side < 0) ~= (part.localReferenceAngleSide < 0) then |
1049 | iy = i2y; |
1050 | ix = i2x; |
1051 | end |
1052 | end |
1053 | dirX, dirY, dirZ = localDirectionToWorld(part.node, 0, ix, iy) |
1054 | end; |
1055 | end; |
1056 | else |
1057 | if part.alignToWorldY then |
1058 | dirX, dirY, dirZ = localDirectionToWorld(getRootNode(), 0, 1, 0); |
1059 | else |
1060 | dirX, dirY, dirZ = localDirectionToWorld(part.referenceFrame, 0, 0, 1); |
1061 | end; |
1062 | if part.moveToReferenceFrame then |
1063 | local x,y,z = localToLocal(part.referenceFrame, getParent(part.node), part.referenceFrameOffset[1], part.referenceFrameOffset[2], part.referenceFrameOffset[3]); |
1064 | setTranslation(part.node, x,y,z); |
1065 | end |
1066 | end |
1067 | if (dirX ~= 0 or dirY ~= 0 or dirZ ~= 0) and part.doDirectionAlignment then |
1068 | local upX, upY, upZ = localDirectionToWorld(part.referenceFrame, 0, 1, 0); |
1069 | if part.invertZ then |
1070 | dirX = -dirX; |
1071 | dirY = -dirY; |
1072 | dirZ = -dirZ; |
1073 | end; |
1074 | |
1075 | -- just debugging to get more info for this error message: |
1076 | -- ... fabsf(Vector3::dotProduct(direction, up)) < 0.9999f*direction.calcMagnitude()*up.calcMagnitude() |
1077 | if GS_PLATFORM_TYPE == GS_PLATFORM_TYPE_PC and part.printedBadReferenceFrameMessage ~= true and g_isDevelopmentVersion then |
1078 | local parent = getParent(part.node); |
1079 | |
1080 | local dirX2,dirY2,dirZ2 = worldDirectionToLocal(parent, dirX,dirY,dirZ); |
1081 | local upX2,upY2,upZ2 = worldDirectionToLocal(parent, upX,upY,upZ); |
1082 | |
1083 | local dotProd = math.abs( (dirX2*upX2 + dirY2*upY2 + dirZ2*upZ2) ); |
1084 | |
1085 | if dotProd > 0.9999 * (Utils.vector3Length(dirX2,dirY2,dirZ2) * Utils.vector3Length(upX2,upY2,upZ2)) then |
1086 | print("Error: Node '" .. getName(part.node) .. "' has a bad reference frame! ("..self.configFileName..")"); |
1087 | part.printedBadReferenceFrameMessage = true; |
1088 | end; |
1089 | end; |
1090 | -- end debugging |
1091 | |
1092 | Utils.setWorldDirection(part.node, dirX, dirY, dirZ, upX, upY, upZ, part.limitedAxis, part.minRot, part.maxRot); |
1093 | |
1094 | if part.scaleZ and part.localReferenceDistance ~= nil then |
1095 | local len = Utils.vector3Length(dirX, dirY, dirZ); |
1096 | setScale(part.node, 1, 1, len/part.localReferenceDistance); |
1097 | end |
1098 | end |
1099 | |
1100 | if part.referencePoint ~= nil then |
1101 | local numTranslatingParts = table.getn(part.translatingParts) |
1102 | if numTranslatingParts > 0 then |
1103 | local _, _, dist = worldToLocal(part.node, refX, refY, refZ); |
1104 | for i=1,numTranslatingParts do |
1105 | local translatingPart = part.translatingParts[i]; |
1106 | local newZ = (dist - translatingPart.referenceDistance)/numTranslatingParts; |
1107 | setTranslation(translatingPart.node, translatingPart.startPos[1], translatingPart.startPos[2], newZ); |
1108 | end |
1109 | end |
1110 | end |
1111 | |
1112 | if part.copyLocalDirectionParts ~= nil then |
1113 | for _,copyLocalDirectionPart in pairs(part.copyLocalDirectionParts) do |
1114 | local dx,dy,dz = localDirectionToWorld(part.node, 0,0,1); |
1115 | dx,dy,dz = worldDirectionToLocal(getParent(part.node), dx,dy,dz); |
1116 | dx = dx * copyLocalDirectionPart.dirScale[1]; |
1117 | dy = dy * copyLocalDirectionPart.dirScale[2]; |
1118 | dz = dz * copyLocalDirectionPart.dirScale[3]; |
1119 | |
1120 | local ux,uy,uz = localDirectionToWorld(part.node, 0,1,0); |
1121 | ux,uy,uz = worldDirectionToLocal(getParent(part.node), ux,uy,uz); |
1122 | ux = ux * copyLocalDirectionPart.upScale[1]; |
1123 | uy = uy * copyLocalDirectionPart.upScale[2]; |
1124 | uz = uz * copyLocalDirectionPart.upScale[3]; |
1125 | |
1126 | setDirection(copyLocalDirectionPart.node, dx,dy,dz, ux,uy,uz); |
1127 | |
1128 | if self.isServer then |
1129 | Cylindered.updateComponentJoints(self, copyLocalDirectionPart, placeComponents); |
1130 | end; |
1131 | end; |
1132 | end; |
1133 | |
1134 | -- update component joint |
1135 | if self.isServer then |
1136 | Cylindered.updateComponentJoints(self, part, placeComponents); |
1137 | Cylindered.updateAttacherJoints(self, part); |
1138 | end |
1139 | |
1140 | part.isDirty = false; |
1141 | end; |
1142 | |
1143 | function Cylindered.updateComponentJoints(self, entry, placeComponents) |
1144 | if self.isServer then |
1145 | if entry.componentJoints ~= nil then |
1146 | for _,joint in ipairs(entry.componentJoints) do |
1147 | local componentJoint = joint.componentJoint; |
1148 | |
1149 | local jointNode = componentJoint.jointNode; |
1150 | if joint.anchorActor == 1 then |
1151 | jointNode = componentJoint.jointNodeActor1; |
1152 | end |
1153 | if placeComponents then |
1154 | |
1155 | local node = self.components[componentJoint.componentIndices[((joint.anchorActor+1)%2)+1] ].node; |
1156 | local x,y,z = localToWorld(jointNode, joint.x, joint.y, joint.z); |
1157 | local upX,upY,upZ = localDirectionToWorld(jointNode, joint.upX,joint.upY,joint.upZ); |
1158 | local dirX,dirY,dirZ = localDirectionToWorld(jointNode, joint.dirX,joint.dirY,joint.dirZ); |
1159 | Utils.setWorldTranslation(node, x,y,z); |
1160 | Utils.setWorldDirection(node, dirX,dirY,dirZ, upX,upY,upZ); |
1161 | end |
1162 | setJointFrame(componentJoint.jointIndex, joint.anchorActor, jointNode); |
1163 | end; |
1164 | end; |
1165 | end; |
1166 | end; |
1167 | |
1168 | function Cylindered.updateAttacherJoints(self, entry) |
1169 | if self.isServer then |
1170 | if entry.attacherJoints ~= nil then |
1171 | for _,joint in ipairs(entry.attacherJoints) do |
1172 | if joint.jointIndex ~= 0 then |
1173 | setJointFrame(joint.jointIndex, 0, joint.jointTransform); |
1174 | end; |
1175 | end; |
1176 | end; |
1177 | if entry.inputAttacherJoint and self.attacherVehicle ~= nil then |
1178 | local implement = self.attacherVehicle:getImplementByObject(self); |
1179 | if implement ~= nil then |
1180 | local jointDesc = self.attacherVehicle.attacherJoints[implement.jointDescIndex]; |
1181 | if jointDesc.jointIndex ~= 0 then |
1182 | setJointFrame(jointDesc.jointIndex, 1, self.attacherJoint.node); |
1183 | end |
1184 | end |
1185 | end |
1186 | end; |
1187 | end; |
1188 | |
1189 | function Cylindered:updateCylinderedInitial(placeComponents) |
1190 | if placeComponents == nil then |
1191 | placeComponents = true; |
1192 | end |
1193 | |
1194 | for _, part in pairs(self.activeDirtyMovingParts) do |
1195 | Cylindered.setDirty(self, part); |
1196 | end |
1197 | |
1198 | for _, tool in ipairs(self.movingTools) do |
1199 | if tool.isDirty then |
1200 | if self.isServer then |
1201 | Cylindered.updateComponentJoints(self, tool, placeComponents); |
1202 | end; |
1203 | tool.isDirty = false; |
1204 | end; |
1205 | end; |
1206 | |
1207 | for _, part in ipairs(self.movingParts) do |
1208 | if part.isDirty then |
1209 | Cylindered.updateMovingPart(self, part, placeComponents); |
1210 | part.isDirty = false; |
1211 | end |
1212 | end |
1213 | end |
1214 | |
1215 | function Cylindered:onAttach(attacherVehicle) |
1216 | for i, tool in ipairs(self.movingTools) do |
1217 | local changed = false; |
1218 | if tool.transSpeed ~= nil then |
1219 | local trans = tool.curTrans[tool.translationAxis]; |
1220 | |
1221 | local changedTrans = false; |
1222 | if tool.attachTransMax ~= nil and trans > tool.attachTransMax then |
1223 | trans = tool.attachTransMax; |
1224 | changedTrans = true; |
1225 | elseif tool.attachTransMin ~= nil and trans < tool.attachTransMin then |
1226 | trans = tool.attachTransMin; |
1227 | changedTrans = true; |
1228 | end; |
1229 | if changedTrans then |
1230 | tool.curTrans[tool.translationAxis] = trans; |
1231 | setTranslation(tool.node, unpack(tool.curTrans)); |
1232 | changed = true; |
1233 | end; |
1234 | end; |
1235 | if tool.rotSpeed ~= nil then |
1236 | local rot = tool.curRot[tool.rotationAxis]; |
1237 | |
1238 | local changedRot = false; |
1239 | if tool.attachRotMax ~= nil and rot > tool.attachRotMax then |
1240 | rot = tool.attachRotMax; |
1241 | changedRot = true; |
1242 | elseif tool.attachRotMin ~= nil and rot < tool.attachRotMin then |
1243 | rot = tool.attachRotMin; |
1244 | changedRot = true; |
1245 | end; |
1246 | if changedRot then |
1247 | tool.curRot[tool.rotationAxis] = rot; |
1248 | setRotation(tool.node, unpack(tool.curRot)); |
1249 | changed = true; |
1250 | end; |
1251 | end; |
1252 | if changed then |
1253 | Cylindered.setDirty(self, tool); |
1254 | end; |
1255 | end; |
1256 | end; |
1257 | |
1258 | function Cylindered:onDeactivateSounds() |
1259 | if self.isClient then |
1260 | Utils.stopSample(self.sampleCylinderedHydraulic, true); |
1261 | end; |
1262 | end; |
1263 | |
1264 | function Cylindered:isDetachAllowed(superFunc) |
1265 | if self.detachLockNodes ~= nil then |
1266 | for entry, data in pairs(self.detachLockNodes) do |
1267 | local node = entry.node; |
1268 | local rot = {getRotation(node)}; |
1269 | |
1270 | if data.detachingRotMinLimit ~= nil and rot[entry.rotationAxis] < data.detachingRotMinLimit then |
1271 | return false; |
1272 | end; |
1273 | if data.detachingRotMaxLimit ~= nil and rot[entry.rotationAxis] > data.detachingRotMaxLimit then |
1274 | return false; |
1275 | end; |
1276 | |
1277 | local trans = {getTranslation(node)}; |
1278 | if data.detachingTransMinLimit ~= nil and trans[entry.translationAxis] < data.detachingTransMinLimit then |
1279 | return false; |
1280 | end; |
1281 | if data.detachingTransMaxLimit ~= nil and trans[entry.translationAxis] > data.detachingTransMaxLimit then |
1282 | return false; |
1283 | end; |
1284 | end; |
1285 | end; |
1286 | |
1287 | if superFunc ~= nil then |
1288 | return superFunc(self); |
1289 | end |
1290 | return true; |
1291 | end;
|
Copyright (c) 2008-2015 GIANTS Software GmbH, Confidential, All Rights Reserved.
This document is to be published solely by ls-mods.de