24 | function FillVolume:load(savegame) |
25 | |
26 | self.getDischargeInfos = FillVolume.getDischargeInfos; |
27 | |
28 | self.setUnitFillLevel = Utils.overwrittenFunction(self.setUnitFillLevel, FillVolume.setUnitFillLevel); |
29 | |
30 | self.alsoUseFillVolumeLoadInfoForDischarge = Utils.getNoNil(getXMLBool(self.xmlFile, "vehicle.alsoUseFillVolumeLoadInfoForDischarge"), false); |
31 | |
32 | if self.isClient then |
33 | self.fillVolumes = {}; |
34 | self.fillVolumeDeformers = {}; |
35 | local i = 0; |
36 | while true do |
37 | local key = string.format("vehicle.fillVolumes.volumes.volume(%d)", i); |
38 | if not hasXMLProperty(self.xmlFile, key) then |
39 | break; |
40 | end |
41 | |
42 | local fillVolume = {}; |
43 | |
44 | fillVolume.baseNode = Utils.indexToObject(self.components, getXMLString(self.xmlFile, key.."#index")); |
45 | fillVolume.allSidePlanes = Utils.getNoNil(getXMLBool(self.xmlFile, key.."#allSidePlanes"), true); |
46 | |
47 | local defaultFillType = getXMLString(self.xmlFile, key.."#defaultFillType"); |
48 | if defaultFillType ~= nil then |
49 | |
50 | local fillType = FillUtil.fillTypeNameToInt[defaultFillType]; |
51 | if fillType ~= nil then |
52 | fillVolume.defaultFillType = fillType; |
53 | else |
54 | print("Warning: Invalid defaultFillType '"..tostring(defaultFillType).."' in '"..self.configFileName.."'"); |
55 | end |
56 | end |
57 | |
58 | fillVolume.maxDelta = Utils.getNoNil(getXMLFloat(self.xmlFile, key.."#maxDelta"), 1.0); |
59 | fillVolume.maxSurfaceAngle = math.rad( Utils.getNoNil(getXMLFloat(self.xmlFile, key.."#maxAllowedHeapAngle"), 35) ); |
60 | |
61 | local maxPhysicalSurfaceAngle = math.rad(35); |
62 | fillVolume.maxSubDivEdgeLength = Utils.getNoNil(getXMLFloat(self.xmlFile, key.."#maxSubDivEdgeLength"), 0.9); |
63 | |
64 | local fillUnitIndex = Utils.getNoNil(getXMLInt(self.xmlFile, key.."#fillUnitIndex"), i+1); |
65 | if self.fillUnits == nil or self.fillUnits[fillUnitIndex] == nil then |
66 | print("Warning: '"..self.configFileName.. "' could not determine capacity for fillVolume!"); |
67 | end |
68 | local capacity = self.fillUnits[fillUnitIndex].capacity; |
69 | |
70 | fillVolume.volume = createFillPlaneShape(fillVolume.baseNode, "fillPlane", capacity, fillVolume.maxDelta, fillVolume.maxSurfaceAngle, maxPhysicalSurfaceAngle, fillVolume.maxSubDivEdgeLength, fillVolume.allSidePlanes); |
71 | setVisibility(fillVolume.volume, false); |
72 | |
73 | fillVolume.deformers = {}; |
74 | if fillVolume.volume ~= nil then |
75 | local j = 0; |
76 | while true do |
77 | local node = Utils.indexToObject(self.components, getXMLString(self.xmlFile, key..".deformNode("..j..")#index")); |
78 | if node == nil then |
79 | break; |
80 | end |
81 | local initPos = { localToLocal(node, fillVolume.baseNode, 0,0,0) }; |
82 | local polyline = findPolyline(fillVolume.volume, initPos[1],initPos[3]); |
83 | self.fillVolumeDeformers[node] = {node=node, initPos=initPos, posX=initPos[1], posZ=initPos[3], polyline=polyline, volume=fillVolume.volume, baseNode=fillVolume.baseNode}; |
84 | j = j + 1; |
85 | end |
86 | end |
87 | |
88 | fillVolume.scrollSpeedDischarge = { Utils.getVectorFromString(Utils.getNoNil(getXMLString(self.xmlFile, key.."#scrollSpeedDischarge"), "0 0 0")) }; |
89 | fillVolume.scrollSpeedLoad = { Utils.getVectorFromString(Utils.getNoNil(getXMLString(self.xmlFile, key.."#scrollSpeedLoad"), "0 0 0")) }; |
90 | for i=1,3 do |
91 | fillVolume.scrollSpeedDischarge[i] = fillVolume.scrollSpeedDischarge[i] / 1000; |
92 | fillVolume.scrollSpeedLoad[i] = fillVolume.scrollSpeedLoad[i] / 1000; |
93 | end |
94 | fillVolume.uvPosition = {0, 0, 0}; |
95 | |
96 | if fillVolume.volume ~= nil and fillVolume.volume ~= 0 then |
97 | link(fillVolume.baseNode, fillVolume.volume); |
98 | table.insert(self.fillVolumes, fillVolume); |
99 | end |
100 | |
101 | i = i + 1; |
102 | end |
103 | |
104 | self.fillVolumeHeights = {}; |
105 | self.fillVolumeHeightRefNodeToFillVolumeHeight = {}; |
106 | local i=0; |
107 | while true do |
108 | local key = string.format("vehicle.fillVolumes.heights.height(%d)", i); |
109 | if not hasXMLProperty(self.xmlFile, key) then |
110 | break; |
111 | end |
112 | local volumeHeight = {}; |
113 | volumeHeight.fillVolumeIndex = getXMLInt(self.xmlFile, key.."#fillVolumeIndex"); |
114 | volumeHeight.volumeHeightIsDirty = false; |
115 | |
116 | volumeHeight.refNodes = {}; |
117 | local j=0; |
118 | while true do |
119 | local refNode = Utils.indexToObject(self.components, getXMLString(self.xmlFile, string.format("%s.refNode(%d)#index", key, j))); |
120 | if refNode == nil then |
121 | break; |
122 | end |
123 | table.insert(volumeHeight.refNodes, {refNode=refNode}); |
124 | self.fillVolumeHeightRefNodeToFillVolumeHeight[refNode] = volumeHeight; |
125 | j=j+1; |
126 | end |
127 | |
128 | volumeHeight.nodes = {}; |
129 | local j=0; |
130 | while true do |
131 | local node = Utils.indexToObject(self.components, getXMLString(self.xmlFile, string.format("%s.node(%d)#index", key, j))); |
132 | if node == nil then |
133 | break; |
134 | end |
135 | if node ~= nil then |
136 | local baseScale = { Utils.getVectorFromString(Utils.getNoNil(getXMLString(self.xmlFile, string.format("%s.node(%d)#baseScale", key, j)), "1 1 1")) }; |
137 | local scaleAxis = { Utils.getVectorFromString(Utils.getNoNil(getXMLString(self.xmlFile, string.format("%s.node(%d)#scaleAxis", key, j)), "0 0 0")) }; |
138 | local scaleMax = { Utils.getVectorFromString(Utils.getNoNil(getXMLString(self.xmlFile, string.format("%s.node(%d)#scaleMax", key, j)), "0 0 0")) }; |
139 | local basePosition = { getTranslation(node) }; |
140 | local transAxis = { Utils.getVectorFromString(Utils.getNoNil(getXMLString(self.xmlFile, string.format("%s.node(%d)#transAxis", key, j)), "0 0 0")) }; |
141 | local transMax = { Utils.getVectorFromString(Utils.getNoNil(getXMLString(self.xmlFile, string.format("%s.node(%d)#transMax", key, j)), "0 0 0")) }; |
142 | local orientateToWorldY = Utils.getNoNil(getXMLBool(self.xmlFile, string.format("%s.node(%d)#orientateToWorldY", key, j)), false); |
143 | table.insert(volumeHeight.nodes, {node=node, baseScale=baseScale, scaleAxis=scaleAxis, scaleMax=scaleMax, basePosition=basePosition, transAxis=transAxis, transMax=transMax, orientateToWorldY=orientateToWorldY}); |
144 | end |
145 | j=j+1; |
146 | end |
147 | |
148 | table.insert(self.fillVolumeHeights, volumeHeight); |
149 | i=i+1; |
150 | end |
151 | |
152 | self.fillVolumeLoadInfos = {}; |
153 | self.fillVolumeLoadInfos.name = "loadInfo"; |
154 | |
155 | self.fillVolumeUnloadInfos = {}; |
156 | self.fillVolumeUnloadInfos.name = "unloadInfo"; |
157 | |
158 | self.fillVolumeDischargeInfos = {}; |
159 | self.fillVolumeDischargeInfos.name = "dischargeInfo"; |
160 | |
161 | for _,tbl in pairs( {self.fillVolumeLoadInfos, self.fillVolumeUnloadInfos, self.fillVolumeDischargeInfos} ) do |
162 | local i=0; |
163 | while true do |
164 | if not hasXMLProperty(self.xmlFile, string.format("vehicle.fillVolumes."..tbl.name.."s."..tbl.name.."(%d)", i)) then |
165 | break; |
166 | end |
167 | local entry = {}; |
168 | entry.fillVolumeIndex = Utils.getNoNil(getXMLInt(self.xmlFile, string.format("vehicle.fillVolumes."..tbl.name.."s."..tbl.name.."(%d)#fillVolumeIndex", 1)), 1); |
169 | entry.nodes = {}; |
170 | local j=0; |
171 | while true do |
172 | local key = string.format("vehicle.fillVolumes."..tbl.name.."s."..tbl.name.."(%d).node(%d)", i, j); |
173 | if not hasXMLProperty(self.xmlFile, key) then |
174 | break; |
175 | end |
176 | |
177 | local nodeEntry = {}; |
178 | nodeEntry.node = Utils.indexToObject(self.components, getXMLString(self.xmlFile, key .. "#index")); |
179 | nodeEntry.width = Utils.getNoNil(getXMLFloat(self.xmlFile, key .. "#width"), 1.0); |
180 | nodeEntry.length = Utils.getNoNil(getXMLFloat(self.xmlFile, key .. "#length"), 1.0); |
181 | |
182 | nodeEntry.fillVolumeHeightIndex = getXMLInt(self.xmlFile, key .. "#fillVolumeHeightIndex"); |
183 | nodeEntry.priority = Utils.getNoNil(getXMLInt(self.xmlFile, key .. "#priority"), 1); |
184 | nodeEntry.minHeight = getXMLFloat(self.xmlFile, key .. "#minHeight"); |
185 | nodeEntry.maxHeight = getXMLFloat(self.xmlFile, key .. "#maxHeight"); |
186 | nodeEntry.minFillLevelPercentage = getXMLFloat(self.xmlFile, key .. "#minFillLevelPercentage"); |
187 | nodeEntry.maxFillLevelPercentage = getXMLFloat(self.xmlFile, key .. "#maxFillLevelPercentage"); |
188 | |
189 | nodeEntry.heightForTranslation = getXMLFloat(self.xmlFile, key .. "#heightForTranslation"); |
190 | nodeEntry.translationStart = Utils.getVectorNFromString(getXMLString(self.xmlFile, key .. "#translationStart"), 3); |
191 | nodeEntry.translationEnd = Utils.getVectorNFromString(getXMLString(self.xmlFile, key .. "#translationEnd"), 3); |
192 | nodeEntry.translationAlpha = 0; |
193 | |
194 | table.insert(entry.nodes, nodeEntry); |
195 | |
196 | j=j+1; |
197 | end |
198 | table.sort(entry.nodes, function(a, b) return a.priority > b.priority end); |
199 | table.insert(tbl, entry); |
200 | i=i+1; |
201 | end |
202 | end |
203 | |
204 | end |
205 | |
206 | self.fillVolumeDirtyFlag = self:getNextDirtyFlag(); |
207 | end |
248 | function FillVolume:update(dt) |
249 | if self.isClient then |
250 | for _,deformer in pairs(self.fillVolumeDeformers) do |
251 | if deformer.deformerIsDirty and deformer.polyline ~= nil and deformer.polyline ~= -1 then |
252 | deformer.deformerIsDirty = false; |
253 | local posX, _, posZ = localToLocal(deformer.node, deformer.baseNode, 0,0,0); |
254 | if math.abs(posX - deformer.posX) > 0.0001 or math.abs(posZ - deformer.posZ) > 0.0001 then |
255 | deformer.lastPosX = posX; |
256 | deformer.lastPosZ = posZ; |
257 | local dx = posX - deformer.initPos[1]; |
258 | local dz = posZ - deformer.initPos[3]; |
259 | setPolylineTranslation(deformer.volume, deformer.polyline, dx,dz); |
260 | end |
261 | end |
262 | end |
263 | |
264 | for _,fillVolumeHeight in pairs(self.fillVolumeHeights) do |
265 | if fillVolumeHeight.volumeHeightIsDirty == true and self.fillVolumes[fillVolumeHeight.fillVolumeIndex] ~= nil then |
266 | fillVolumeHeight.volumeHeightIsDirty = false; |
267 | |
268 | local baseNode = self.fillVolumes[fillVolumeHeight.fillVolumeIndex].baseNode; |
269 | local volumeNode = self.fillVolumes[fillVolumeHeight.fillVolumeIndex].volume; |
270 | |
271 | if baseNode ~= nil and volumeNode ~= nil then |
272 | |
273 | local minHeight = math.huge; |
274 | local maxHeight = -math.huge; |
275 | local maxHeightWorld = -math.huge; |
276 | for _,refNode in pairs(fillVolumeHeight.refNodes) do |
277 | local x,_,z = localToLocal(refNode.refNode, baseNode, 0,0,0); |
278 | local height = getFillPlaneHeightAtLocalPos(volumeNode, x,z); |
279 | minHeight = math.min(minHeight, height); |
280 | maxHeight = math.max(maxHeight, height); |
281 | local _,yw,_ = localToWorld(baseNode, x,height,z); |
282 | maxHeightWorld = math.max(maxHeightWorld, yw); |
283 | end |
284 | fillVolumeHeight.currentMinHeight = minHeight; |
285 | fillVolumeHeight.currentMaxHeight = maxHeight; |
286 | fillVolumeHeight.currentMaxHeightWorld = maxHeightWorld; |
287 | |
288 | for _,node in pairs(fillVolumeHeight.nodes) do |
289 | local sx = node.scaleAxis[1]*minHeight; |
290 | local sy = node.scaleAxis[2]*minHeight; |
291 | local sz = node.scaleAxis[3]*minHeight; |
292 | if node.scaleMax[1] > 0 then |
293 | sx = math.min(node.scaleMax[1], sx); |
294 | end |
295 | if node.scaleMax[2] > 0 then |
296 | sy = math.min(node.scaleMax[2], sy); |
297 | end |
298 | if node.scaleMax[3] > 0 then |
299 | sz = math.min(node.scaleMax[3], sz); |
300 | end |
301 | local tx = node.transAxis[1]*minHeight; |
302 | local ty = node.transAxis[2]*minHeight; |
303 | local tz = node.transAxis[3]*minHeight; |
304 | if node.transMax[1] > 0 then |
305 | tx = math.min(node.transMax[1], tx); |
306 | end |
307 | if node.transMax[2] > 0 then |
308 | ty = math.min(node.transMax[2], ty); |
309 | end |
310 | if node.transMax[3] > 0 then |
311 | tz = math.min(node.transMax[3], tz); |
312 | end |
313 | |
314 | setScale(node.node, node.baseScale[1]+sx, node.baseScale[2]+sy, node.baseScale[3]+sz); |
315 | setTranslation(node.node, node.basePosition[1]+tx, node.basePosition[2]+ty, node.basePosition[3]+tz); |
316 | |
317 | if node.orientateToWorldY then |
318 | local _,dy,_ = localDirectionToWorld(getParent(node.node), 0,1,0); |
319 | local alpha = math.acos(dy); |
320 | setRotation(node.node, alpha,0,0); |
321 | end |
322 | end |
323 | end |
324 | end |
325 | end |
326 | end |
327 | end |
354 | function FillVolume:setUnitFillLevel(superFunc, fillUnitIndex, fillLevel, fillType, force, fillInfo) |
355 | local fillUnit = self.fillUnits[fillUnitIndex]; |
356 | if fillUnit == nil then |
357 | return; |
358 | end |
359 | |
360 | local lastFillType = fillUnit.currentFillType; |
361 | local oldFillLevel = fillUnit.fillLevel; |
362 | superFunc(self, fillUnitIndex, fillLevel, fillType, force, fillInfo); |
363 | local delta = fillUnit.fillLevel - oldFillLevel; |
364 | |
365 | if self.isClient then |
366 | |
367 | local fillVolume = self.fillVolumes[fillUnit.fillVolumeIndex]; |
368 | if fillVolume == nil then |
369 | return; |
370 | end |
371 | |
372 | if fillType ~= fillUnit.currentFillType then |
373 | local maxPhysicalSurfaceAngle; |
374 | if FillUtil.fillTypeIntToName[fillType] ~= nil then |
375 | maxPhysicalSurfaceAngle = FillUtil.fillTypeIntToName[fillType].maxPhysicalSurfaceAngle; |
376 | end |
377 | if maxPhysicalSurfaceAngle ~= nil then |
378 | if fillVolume.volume ~= nil then |
379 | setFillPlaneMaxPhysicalSurfaceAngle(fillVolume.volume, maxPhysicalSurfaceAngle); |
380 | end |
381 | end |
382 | end |
383 | |
384 | setVisibility(fillVolume.volume, fillUnit.fillLevel > 0); |
385 | |
386 | local material = nil; |
387 | if fillUnit.currentFillType ~= FillUtil.FILLTYPE_UNKNOWN and fillUnit.currentFillType ~= lastFillType then |
388 | local usedFillType = fillUnit.currentFillType; |
389 | if fillUnit.forcedFillPlaneType ~= nil then |
390 | usedFillType = fillUnit.forcedFillPlaneType; |
391 | end |
392 | material = MaterialUtil.getMaterial(usedFillType, "fillplane", 1); |
393 | end |
394 | |
395 | if fillUnit.currentFillType ~= FillUtil.FILLTYPE_UNKNOWN and fillUnit.currentFillType ~= lastFillType then |
396 | if material == nil and fillVolume.defaultFillType ~= nil then |
397 | material = MaterialUtil.getMaterial(fillVolume.defaultFillType, "fillplane", 1); |
398 | end |
399 | if material ~= nil then |
400 | setMaterial(fillVolume.volume, material, 0); |
401 | end |
402 | end |
403 | |
404 | if fillInfo ~= nil and fillInfo.nodes ~= nil then |
405 | |
406 | local availableFillNodes = {}; |
407 | local neededPriority = fillInfo.nodes[1].priority; |
408 | |
409 | while table.getn(availableFillNodes) == 0 and neededPriority >= 1 do |
410 | |
411 | for _,node in pairs(fillInfo.nodes) do |
412 | if node.priority >= neededPriority then |
413 | local doInsert = true; |
414 | |
415 | if node.minHeight ~= nil or node.maxHeight ~= nil and fillInfo.fillVolumeIndex ~= nil and self.fillVolumes[fillInfo.fillVolumeIndex].baseNode ~= nil and self.fillVolumes[fillInfo.fillVolumeIndex].volume ~= nil then |
416 | |
417 | local baseNode = self.fillVolumes[fillInfo.fillVolumeIndex].baseNode; |
418 | local volumeNode = self.fillVolumes[fillInfo.fillVolumeIndex].volume; |
419 | |
420 | local height = -math.huge; |
421 | if node.fillVolumeHeightIndex ~= nil and self.fillVolumeHeights[node.fillVolumeHeightIndex] ~= nil then |
422 | for _,refNode in pairs(self.fillVolumeHeights[node.fillVolumeHeightIndex].refNodes) do |
423 | local x,_,z = localToLocal(refNode.refNode, baseNode, 0,0,0); |
424 | height = math.max(height, getFillPlaneHeightAtLocalPos(volumeNode, x,z)); |
425 | end |
426 | else |
427 | local x,_,z = localToLocal(node.node, baseNode, 0,0,0); |
428 | height = math.max(height, getFillPlaneHeightAtLocalPos(volumeNode, x,z)); |
429 | end |
430 | |
431 | if node.minHeight ~= nil and height < node.minHeight then |
432 | doInsert = false; |
433 | end |
434 | if node.maxHeight ~= nil and height > node.maxHeight then |
435 | doInsert = false; |
436 | end |
437 | |
438 | if node.heightForTranslation ~= nil then |
439 | if height > node.heightForTranslation then |
440 | node.translationAlpha = node.translationAlpha + 0.01; |
441 | local x,y,z = Utils.vector3ArrayLerp(node.translationStart, node.translationEnd, node.translationAlpha); |
442 | setTranslation(node.node, x,y,z); |
443 | else |
444 | node.translationAlpha = node.translationAlpha - 0.01; |
445 | end |
446 | node.translationAlpha = Utils.clamp(node.translationAlpha, 0, 1); |
447 | end |
448 | |
449 | end |
450 | |
451 | if node.minFillLevelPercentage ~= nil or node.maxFillLevelPercentage ~= nil then |
452 | local percentage = self:getUnitFillLevel(fillUnitIndex) / self:getUnitCapacity(fillUnitIndex); |
453 | |
454 | if node.minFillLevelPercentage ~= nil and percentage < node.minFillLevelPercentage then |
455 | doInsert = false; |
456 | end |
457 | if node.maxFillLevelPercentage ~= nil and percentage > node.maxFillLevelPercentage then |
458 | doInsert = false; |
459 | end |
460 | end |
461 | |
462 | if doInsert then |
463 | table.insert(availableFillNodes, node); |
464 | end |
465 | end |
466 | end |
467 | if table.getn(availableFillNodes) > 0 then |
468 | break; |
469 | end |
470 | neededPriority = neededPriority - 1; |
471 | end |
472 | |
473 | local numFillNodes = table.getn(availableFillNodes); |
474 | for i=1,numFillNodes do |
475 | local node = availableFillNodes[i]; |
476 | |
477 | local x0,y0,z0 = getWorldTranslation(node.node); |
478 | local d1x,d1y,d1z = localDirectionToWorld(node.node, node.width,0,0); |
479 | local d2x,d2y,d2z = localDirectionToWorld(node.node, 0,0,node.length); |
480 | |
481 | if Vehicle.debugRendering then |
482 | drawDebugLine( x0,y0,z0, 1,0,0, x0+d1x, y0+d1y, z0+d1z, 1,0,0 ); |
483 | drawDebugLine( x0,y0,z0, 0,0,1, x0+d2x, y0+d2y, z0+d2z, 0,0,1 ); |
484 | drawDebugPoint( x0,y0,z0, 1,1,1,1 ); |
485 | drawDebugPoint( x0+d1x, y0+d1y, z0+d1z, 1,0,0,1 ); |
486 | drawDebugPoint( x0+d2x, y0+d2y, z0+d2z, 0,0,1,1 ); |
487 | end |
488 | x0 = x0 - (d1x + d2x) / 2; |
489 | y0 = y0 - (d1y + d2y) / 2; |
490 | z0 = z0 - (d1z + d2z) / 2; |
491 | fillPlaneAdd(fillVolume.volume, delta/numFillNodes, x0,y0,z0, d1x,d1y,d1z, d2x,d2y,d2z); |
492 | end |
493 | else |
494 | local x,y,z = localToWorld(fillVolume.volume, 0,100,0); |
495 | local d1x,d1y,d1z = localDirectionToWorld(fillVolume.volume, 5,0,0); |
496 | local d2x,d2y,d2z = localDirectionToWorld(fillVolume.volume, 0,0,5); |
497 | x = x - (d1x+d2x)/2; |
498 | y = y - (d1y+d2y)/2; |
499 | z = z - (d1z+d2z)/2; |
500 | fillPlaneAdd(fillVolume.volume, delta, x,y,z, d1x,d1y,d1z, d2x,d2y,d2z); |
501 | end |
502 | |
503 | end |
504 | |
505 | |
506 | end |