LUADOC - Farming Simulator 22

Script v1_7_1_0

Engine v1_7_1_0

Foundation Reference

FillVolume

Description
Specialization for visual fill volumes/planes; requires FillUnit specialization
Functions

getFillVolumeIndicesByFillUnitIndex

Description
Definition
getFillVolumeIndicesByFillUnitIndex()
Code
606function FillVolume:getFillVolumeIndicesByFillUnitIndex(fillUnitIndex)
607 local spec = self.spec_fillVolume
608 local indices = {}
609 for i, fillVolume in ipairs(spec.volumes) do
610 if fillVolume.fillUnitIndex == fillUnitIndex then
611 table.insert(indices, i)
612 end
613 end
614
615 return indices
616end

getFillVolumeLoadInfo

Description
Definition
getFillVolumeLoadInfo()
Code
592function FillVolume:getFillVolumeLoadInfo(loadInfoIndex)
593 local spec = self.spec_fillVolume
594 return spec.loadInfos[loadInfoIndex]
595end

getFillVolumeUnloadInfo

Description
Definition
getFillVolumeUnloadInfo()
Code
599function FillVolume:getFillVolumeUnloadInfo(unloadInfoIndex)
600 local spec = self.spec_fillVolume
601 return spec.unloadInfos[unloadInfoIndex]
602end

getFillVolumeUVScrollSpeed

Description
Definition
getFillVolumeUVScrollSpeed()
Code
640function FillVolume:getFillVolumeUVScrollSpeed()
641 return 0, 0, 0
642end

initSpecialization

Description
Definition
initSpecialization()
Code
25function FillVolume.initSpecialization()
26 g_configurationManager:addConfigurationType("fillVolume", g_i18n:getText("configuration_fillVolume"), "fillVolume", nil, nil, nil, ConfigurationUtil.SELECTOR_MULTIOPTION)
27
28 local schema = Vehicle.xmlSchema
29 schema:setXMLSpecializationType("FillVolume")
30
31 local basePath = "vehicle.fillVolume.fillVolumeConfigurations.fillVolumeConfiguration(?)"
32
33 schema:register(XMLValueType.NODE_INDEX, basePath .. ".volumes.volume(?)#node", "Fill volume node")
34 schema:register(XMLValueType.INT, basePath .. ".volumes.volume(?)#fillUnitIndex", "Fill unit index")
35 schema:register(XMLValueType.FLOAT, basePath .. ".volumes.volume(?)#fillUnitFactor", "Fill unit factor", 1)
36 schema:register(XMLValueType.BOOL, basePath .. ".volumes.volume(?)#allSidePlanes", "All side planes", true)
37 schema:register(XMLValueType.BOOL, basePath .. ".volumes.volume(?)#retessellateTop", "Retessellate top plane for better triangulation quality", false)
38
39 schema:register(XMLValueType.STRING, basePath .. ".volumes.volume(?)#defaultFillType", "Default fill type name")
40 schema:register(XMLValueType.STRING, basePath .. ".volumes.volume(?)#forcedVolumeFillType", "Forced fill type name")
41
42 schema:register(XMLValueType.FLOAT, basePath .. ".volumes.volume(?)#maxDelta", "Max. heap size above above input surface [m]", 1.0)
43 schema:register(XMLValueType.ANGLE, basePath .. ".volumes.volume(?)#maxAllowedHeapAngle", "Max. allowed heap surface slope angle [deg]", 35)
44 schema:register(XMLValueType.FLOAT, basePath .. ".volumes.volume(?)#maxSurfaceDistanceError", "Max. allowed distance from input mesh surface to created fill plane mesh [m]", 0.05)
45 schema:register(XMLValueType.FLOAT, basePath .. ".volumes.volume(?)#maxSubDivEdgeLength", "Max. length of sub division edges [m]", 0.9)
46 schema:register(XMLValueType.FLOAT, basePath .. ".volumes.volume(?)#syncMaxSubDivEdgeLength", "Max. length of sub division edges used to sync in multiplayer [m]", 1.35)
47
48 schema:register(XMLValueType.NODE_INDEX, basePath .. ".volumes.volume(?).deformNode(?)#node", "Deformer node")
49
50 FillVolume.registerInfoNodeXMLPaths(schema, "vehicle.fillVolume.loadInfos.loadInfo(?)")
51 FillVolume.registerInfoNodeXMLPaths(schema, "vehicle.fillVolume.unloadInfos.unloadInfo(?)")
52
53 schema:register(XMLValueType.INT, "vehicle.fillVolume.heightNodes.heightNode(?)#fillVolumeIndex", "Fill volume index")
54 schema:register(XMLValueType.NODE_INDEX, "vehicle.fillVolume.heightNodes.heightNode(?).refNode(?)#node", "Reference node")
55
56 schema:register(XMLValueType.NODE_INDEX, "vehicle.fillVolume.heightNodes.heightNode(?).node(?)#node", "Height node")
57 schema:register(XMLValueType.VECTOR_SCALE, "vehicle.fillVolume.heightNodes.heightNode(?).node(?)#baseScale", "Base scale", "1 1 1")
58 schema:register(XMLValueType.VECTOR_3, "vehicle.fillVolume.heightNodes.heightNode(?).node(?)#scaleAxis", "Scale axis", "0 0 0")
59 schema:register(XMLValueType.VECTOR_SCALE, "vehicle.fillVolume.heightNodes.heightNode(?).node(?)#scaleMax", "Max. scale", "0 0 0")
60 schema:register(XMLValueType.VECTOR_3, "vehicle.fillVolume.heightNodes.heightNode(?).node(?)#transAxis", "Translation axis", "0 0 0")
61 schema:register(XMLValueType.VECTOR_TRANS, "vehicle.fillVolume.heightNodes.heightNode(?).node(?)#transMax", "Max. translation", "0 0 0")
62 schema:register(XMLValueType.FLOAT, "vehicle.fillVolume.heightNodes.heightNode(?).node(?)#minHeight", "Min. fill volume height used for heigth node", 0)
63 schema:register(XMLValueType.FLOAT, "vehicle.fillVolume.heightNodes.heightNode(?).node(?)#heightOffset", "Fill plane height offset", 0)
64 schema:register(XMLValueType.BOOL, "vehicle.fillVolume.heightNodes.heightNode(?).node(?)#orientateToWorldY", "Orientate to world Y", false)
65
66 ObjectChangeUtil.registerObjectChangeXMLPaths(schema, basePath)
67
68 schema:register(XMLValueType.INT, Cylindered.MOVING_TOOL_XML_KEY .. ".fillVolume#fillVolumeIndex", "Fill Unit index which includes the deformers", 1)
69 schema:register(XMLValueType.VECTOR_N, Cylindered.MOVING_TOOL_XML_KEY .. ".fillVolume#deformerNodeIndices", "Indices of deformer nodes to update")
70 schema:register(XMLValueType.INT, Cylindered.MOVING_PART_XML_KEY .. ".fillVolume#fillVolumeIndex", "Fill Unit index which includes the deformers", 1)
71 schema:register(XMLValueType.VECTOR_N, Cylindered.MOVING_PART_XML_KEY .. ".fillVolume#deformerNodeIndices", "Indices of deformer nodes to update")
72
73 schema:setXMLSpecializationType()
74end

loadExtraDependentParts

Description
Definition
loadExtraDependentParts()
Code
664function FillVolume:loadExtraDependentParts(superFunc, xmlFile, baseName, entry)
665 if not superFunc(self, xmlFile, baseName, entry) then
666 return false
667 end
668
669 local fillVolumeIndex = xmlFile:getValue(baseName.. ".fillVolume#fillVolumeIndex", 1)
670
671 local indices = xmlFile:getValue(baseName.. ".fillVolume#deformerNodeIndices", nil, true)
672 if indices ~= nil and #indices > 0 then
673 entry.fillVolumeIndex = fillVolumeIndex
674 entry.deformerNodes = {}
675
676 for i=1, table.getn(indices) do
677 table.insert(entry.deformerNodes, indices[i])
678 end
679 end
680
681 return true
682end

loadFillVolume

Description
Definition
loadFillVolume()
Code
387function FillVolume:loadFillVolume(xmlFile, key, entry)
388 local spec = self.spec_fillVolume
389 XMLUtil.checkDeprecatedXMLElements(xmlFile, key .. "#index", key .. "#node") -- FS17
390
391 entry.baseNode = xmlFile:getValue(key .. "#node", nil, self.components, self.i3dMappings)
392 if entry.baseNode == nil then
393 print("Warning: fillVolume '"..tostring(key).."' has an invalid 'node' in '"..self.configFileName.."'!")
394 return false
395 end
396
397 local fillUnitIndex = xmlFile:getValue(key.."#fillUnitIndex")
398 entry.fillUnitIndex = fillUnitIndex
399 if fillUnitIndex == nil then
400 print("Warning: fillVolume '"..tostring(key).."' has no 'fillUnitIndex' given in '"..self.configFileName.."'!")
401 return false
402 end
403 if not self:getFillUnitExists(fillUnitIndex) then
404 print("Warning: fillVolume '"..tostring(key).."' has an invalid 'fillUnitIndex' in '"..self.configFileName.."'!")
405 return false
406 end
407
408 entry.fillUnitFactor = xmlFile:getValue(key.."#fillUnitFactor", 1.0)
409
410 if spec.fillUnitFillVolumeMapping[fillUnitIndex] == nil then
411 spec.fillUnitFillVolumeMapping[fillUnitIndex] = {fillVolumes={}, sumFactors=0}
412 end
413 table.insert(spec.fillUnitFillVolumeMapping[fillUnitIndex].fillVolumes, entry)
414 spec.fillUnitFillVolumeMapping[fillUnitIndex].sumFactors = entry.fillUnitFactor
415
416 entry.allSidePlanes = xmlFile:getValue(key .. "#allSidePlanes", true)
417 entry.retessellateTop = xmlFile:getValue(key .. "#retessellateTop", false)
418
419 local defaultFillTypeStr = xmlFile:getValue(key .. "#defaultFillType")
420 if defaultFillTypeStr ~= nil then
421 local defaultFillTypeIndex = g_fillTypeManager:getFillTypeIndexByName(defaultFillTypeStr)
422 if defaultFillTypeIndex == nil then
423 print("Warning: Invalid defaultFillType '"..tostring(defaultFillTypeStr).."' for '"..tostring(key).."' in '"..self.configFileName.."'")
424 return false
425 else
426 entry.defaultFillType = defaultFillTypeIndex
427 end
428 else
429 entry.defaultFillType = self:getFillUnitFirstSupportedFillType(fillUnitIndex)
430 end
431
432 local forcedVolumeFillTypeStr = xmlFile:getValue(key .. "#forcedVolumeFillType")
433 if forcedVolumeFillTypeStr ~= nil then
434 local forcedVolumeFillTypeIndex = g_fillTypeManager:getFillTypeIndexByName(forcedVolumeFillTypeStr)
435 if forcedVolumeFillTypeIndex ~= nil then
436 entry.forcedVolumeFillType = forcedVolumeFillTypeIndex
437 else
438 print("Warning: Invalid forcedVolumeFillType '"..tostring(forcedVolumeFillTypeStr).."' for '"..tostring(key).."' in '"..self.configFileName.."'")
439 return false
440 end
441 end
442
443 entry.maxDelta = xmlFile:getValue(key.."#maxDelta", 1.0)
444 entry.maxSurfaceAngle = xmlFile:getValue(key.."#maxAllowedHeapAngle", 35)
445
446 entry.maxPhysicalSurfaceAngle = math.rad(35)
447 entry.maxSurfaceDistanceError = xmlFile:getValue(key.."#maxSurfaceDistanceError", 0.05)
448 entry.maxSubDivEdgeLength = xmlFile:getValue(key.."#maxSubDivEdgeLength", 0.9)
449 entry.syncMaxSubDivEdgeLength = xmlFile:getValue(key.."#syncMaxSubDivEdgeLength", 1.35)
450
451 entry.uvPosition = {0, 0, 0}
452
453 entry.deformers = {}
454 local j = 0
455 while true do
456 local deformerKey = string.format("%s.deformNode(%d)", key, j)
457 if not xmlFile:hasProperty(deformerKey) then
458 break
459 end
460
461 XMLUtil.checkDeprecatedXMLElements(xmlFile, deformerKey .. "#index", deformerKey .. "#node") -- FS17
462
463 local node = xmlFile:getValue(deformerKey .. "#node", nil, self.components, self.i3dMappings)
464 if node ~= nil then
465 local initPos = {localToLocal(node, entry.baseNode, 0,0,0)}
466 local deformer = {node = node, initPos = initPos, posX = initPos[1], posZ = initPos[3], polyline = nil, volume = entry.volume, baseNode = entry.baseNode}
467 table.insert(entry.deformers, deformer)
468 spec.fillVolumeDeformersByNode[node] = deformer
469 end
470 j = j + 1
471 end
472
473 entry.lastFillType = FillType.UNKNOWN
474
475 return true
476end

loadFillVolumeHeightNode

Description
Definition
loadFillVolumeHeightNode()
Code
526function FillVolume:loadFillVolumeHeightNode(xmlFile, key, entry)
527 entry.isDirty = false
528
529 entry.fillVolumeIndex = xmlFile:getValue(key.."#fillVolumeIndex", 1)
530
531 if self.spec_fillVolume.volumes[entry.fillVolumeIndex] == nil then
532 Logging.xmlWarning(self.xmlFile, "Invalid fillVolumeIndex '%d' for heightNode '%s'. Igoring heightNode!", entry.fillVolumeIndex, key)
533 return false
534 end
535
536 entry.refNodes = {}
537 local i = 0
538 while true do
539 local nodeKey = key .. string.format(".refNode(%d)", i)
540 if not xmlFile:hasProperty(nodeKey) then
541 break
542 end
543
544 XMLUtil.checkDeprecatedXMLElements(xmlFile, nodeKey .. "#index", nodeKey .. "#node") -- FS17 to FS19
545
546 local node = xmlFile:getValue(nodeKey.."#node", nil, self.components, self.i3dMappings)
547 if node ~= nil then
548 table.insert(entry.refNodes, {refNode = node})
549 else
550 Logging.xmlWarning(self.xmlFile, "Missing node for '%s'", nodeKey)
551 end
552
553 i = i + 1
554 end
555
556 entry.nodes = {}
557 i = 0
558 while true do
559 local nodeKey = key .. string.format(".node(%d)", i)
560 if not xmlFile:hasProperty(nodeKey) then
561 break
562 end
563
564 XMLUtil.checkDeprecatedXMLElements(xmlFile, nodeKey .. "#index", nodeKey .. "#node") -- FS17 to FS19
565
566 local node = xmlFile:getValue(nodeKey.."#node", nil, self.components, self.i3dMappings)
567 if node ~= nil then
568 local nodeEntry = {}
569 nodeEntry.node = node
570 nodeEntry.baseScale = xmlFile:getValue(nodeKey.."#baseScale", "1 1 1", true)
571 nodeEntry.scaleAxis = xmlFile:getValue(nodeKey.."#scaleAxis", "0 0 0", true)
572 nodeEntry.scaleMax = xmlFile:getValue(nodeKey.."#scaleMax", "0 0 0", true)
573 nodeEntry.basePosition = {getTranslation(node)}
574 nodeEntry.transAxis = xmlFile:getValue(nodeKey.."#transAxis", "0 0 0", true)
575 nodeEntry.transMax = xmlFile:getValue(nodeKey.."#transMax", "0 0 0", true)
576 nodeEntry.minHeight = xmlFile:getValue(nodeKey.."#minHeight", 0)
577 nodeEntry.heightOffset = xmlFile:getValue(nodeKey.."#heightOffset", 0)
578 nodeEntry.orientateToWorldY = xmlFile:getValue(nodeKey.."#orientateToWorldY", false)
579 table.insert(entry.nodes, nodeEntry)
580 else
581 Logging.xmlWarning(self.xmlFile, "Missing node for '%s'", nodeKey)
582 end
583
584 i = i + 1
585 end
586
587 return true
588end

loadFillVolumeInfo

Description
Definition
loadFillVolumeInfo()
Code
480function FillVolume:loadFillVolumeInfo(xmlFile, key, entry)
481 entry.nodes = {}
482 local i = 0
483 while true do
484 local infoKey = key .. string.format(".node(%d)", i)
485 if not xmlFile:hasProperty(infoKey) then
486 break
487 end
488
489 XMLUtil.checkDeprecatedXMLElements(xmlFile, infoKey .. "#index", infoKey .. "#node") -- FS17 to FS19
490
491 local node = xmlFile:getValue(infoKey .. "#node", nil, self.components, self.i3dMappings)
492 if node ~= nil then
493 local nodeEntry = {}
494
495 nodeEntry.node = node
496 nodeEntry.width = xmlFile:getValue(infoKey .. "#width", 1.0)
497 nodeEntry.length = xmlFile:getValue(infoKey .. "#length", 1.0)
498
499 nodeEntry.fillVolumeHeightIndex = xmlFile:getValue(infoKey .. "#fillVolumeHeightIndex")
500 nodeEntry.priority = xmlFile:getValue(infoKey .. "#priority", 1)
501 nodeEntry.minHeight = xmlFile:getValue(infoKey .. "#minHeight")
502 nodeEntry.maxHeight = xmlFile:getValue(infoKey .. "#maxHeight")
503 nodeEntry.minFillLevelPercentage = xmlFile:getValue(infoKey .. "#minFillLevelPercentage")
504 nodeEntry.maxFillLevelPercentage = xmlFile:getValue(infoKey .. "#maxFillLevelPercentage")
505
506 nodeEntry.heightForTranslation = xmlFile:getValue(infoKey .. "#heightForTranslation")
507 nodeEntry.translationStart = xmlFile:getValue(infoKey .. "#translationStart", nil, true)
508 nodeEntry.translationEnd = xmlFile:getValue(infoKey .. "#translationEnd", nil, true)
509 nodeEntry.translationAlpha = 0
510
511 table.insert(entry.nodes, nodeEntry)
512 else
513 Logging.xmlWarning(self.xmlFile, "Missing node for '%s'", infoKey)
514 end
515
516 i = i + 1
517 end
518
519 table.sort(entry.nodes, function(a, b) return a.priority > b.priority end)
520
521 return true
522end

onDelete

Description
Definition
onDelete()
Code
232function FillVolume:onDelete()
233 local spec = self.spec_fillVolume
234 if spec.volumes ~= nil then
235 for _, fillVolume in ipairs(spec.volumes) do
236 if fillVolume.volume ~= nil then
237 delete(fillVolume.volume)
238 end
239 fillVolume.volume = nil
240 end
241 end
242end

onFillUnitFillLevelChanged

Description
Definition
onFillUnitFillLevelChanged()
Code
715function FillVolume:onFillUnitFillLevelChanged(fillUnitIndex, fillLevelDelta, _, toolType, fillPositionData, appliedDelta)
716 local spec = self.spec_fillVolume
717
718 local mapping = spec.fillUnitFillVolumeMapping[fillUnitIndex]
719 if mapping == nil then
720 return
721 end
722
723 local fillLevel = self:getFillUnitFillLevel(fillUnitIndex)
724 local fillType = self:getFillUnitFillType(fillUnitIndex)
725
726 for _, volume in ipairs(mapping.fillVolumes) do
727 local baseNode = volume.baseNode
728 local volumeNode = volume.volume
729 if baseNode == nil or volumeNode == nil then
730 return
731 end
732
733 if volume.forcedFillType ~= nil then
734 fillType = volume.forcedFillType
735 end
736 if fillLevel == 0 then
737 volume.forcedFillType = nil
738 end
739
740 if fillType ~= volume.lastFillType then
741 local maxPhysicalSurfaceAngle
742 local fillTypeInfo = g_fillTypeManager:getFillTypeByIndex(fillType)
743 if fillTypeInfo ~= nil then
744 maxPhysicalSurfaceAngle = fillTypeInfo.maxPhysicalSurfaceAngle
745 end
746 if maxPhysicalSurfaceAngle ~= nil then
747 if volume.volume ~= nil then
748 setFillPlaneMaxPhysicalSurfaceAngle(volume.volume, maxPhysicalSurfaceAngle)
749 volume.maxPhysicalSurfaceAngle = maxPhysicalSurfaceAngle
750 end
751 end
752 end
753
754 setVisibility(volume.volume, fillLevel > 0)
755
756 if fillType ~= FillType.UNKNOWN and fillType ~= volume.lastFillType then
757 local textureArrayIndex = g_fillTypeManager:getTextureArrayIndexByFillTypeIndex(fillType)
758 if textureArrayIndex ~= nil then
759 setShaderParameter(volume.volume, "fillTypeId", textureArrayIndex - 1, 0, 0, 0, false)
760 end
761 end
762
763 if fillPositionData ~= nil then
764 for i=#spec.availableFillNodes, 1, -1 do
765 spec.availableFillNodes[i] = nil
766 end
767
768 if fillPositionData.nodes ~= nil then
769 local neededPriority = fillPositionData.nodes[1].priority
770
771 while #spec.availableFillNodes == 0 and neededPriority >= 1 do
772 for _,node in pairs(fillPositionData.nodes) do
773 if node.priority >= neededPriority then
774 local doInsert = true
775
776 if node.minHeight ~= nil or node.maxHeight ~= nil then
777 local height = -math.huge
778 if node.fillVolumeHeightIndex ~= nil and spec.heightNodes[node.fillVolumeHeightIndex] ~= nil then
779 for _,refNode in pairs(spec.heightNodes[node.fillVolumeHeightIndex].refNodes) do
780 local x,_,z = localToLocal(refNode.refNode, baseNode, 0,0,0)
781 height = math.max(height, getFillPlaneHeightAtLocalPos(volumeNode, x, z) - volume.heightOffset)
782 end
783 else
784 local x,_,z = localToLocal(node.node, baseNode, 0,0,0)
785 height = math.max(height, getFillPlaneHeightAtLocalPos(volumeNode, x, z) - volume.heightOffset)
786 end
787
788 if node.minHeight ~= nil and height < node.minHeight then
789 doInsert = false
790 end
791 if node.maxHeight ~= nil and height > node.maxHeight then
792 doInsert = false
793 end
794
795 if node.heightForTranslation ~= nil then
796 if height > node.heightForTranslation then
797 node.translationAlpha = node.translationAlpha + 0.01
798 local x,y,z = MathUtil.vector3ArrayLerp(node.translationStart, node.translationEnd, node.translationAlpha)
799 setTranslation(node.node, x,y,z)
800 else
801 node.translationAlpha = node.translationAlpha - 0.01
802 end
803 node.translationAlpha = MathUtil.clamp(node.translationAlpha, 0, 1)
804 end
805 end
806
807 if node.minFillLevelPercentage ~= nil or node.maxFillLevelPercentage ~= nil then
808 local percentage = fillLevel / self:getFillUnitCapacity(fillUnitIndex)
809
810 if node.minFillLevelPercentage ~= nil and percentage < node.minFillLevelPercentage then
811 doInsert = false
812 end
813 if node.maxFillLevelPercentage ~= nil and percentage > node.maxFillLevelPercentage then
814 doInsert = false
815 end
816 end
817
818 if doInsert then
819 table.insert(spec.availableFillNodes, node)
820 end
821 end
822 end
823 if #spec.availableFillNodes > 0 then
824 break
825 end
826 neededPriority = neededPriority - 1
827 end
828 else
829 table.insert(spec.availableFillNodes, fillPositionData)
830 end
831
832 local numFillNodes = #spec.availableFillNodes
833 local avgX, avgZ = 0, 0
834
835 for i=1,numFillNodes do
836 local node = spec.availableFillNodes[i]
837
838 local x0,y0,z0 = getWorldTranslation(node.node)
839 local d1x,d1y,d1z = localDirectionToWorld(node.node, node.width,0,0)
840 local d2x,d2y,d2z = localDirectionToWorld(node.node, 0,0,node.length)
841
842 if VehicleDebug.state == VehicleDebug.DEBUG then
843 drawDebugLine( x0,y0,z0, 1,0,0, x0+d1x, y0+d1y, z0+d1z, 1,0,0 )
844 drawDebugLine( x0,y0,z0, 0,0,1, x0+d2x, y0+d2y, z0+d2z, 0,0,1 )
845 drawDebugPoint( x0,y0,z0, 1,1,1,1 )
846 drawDebugPoint( x0+d1x, y0+d1y, z0+d1z, 1,0,0,1 )
847 drawDebugPoint( x0+d2x, y0+d2y, z0+d2z, 0,0,1,1 )
848 end
849 x0 = x0 - (d1x + d2x) / 2
850 y0 = y0 - (d1y + d2y) / 2
851 z0 = z0 - (d1z + d2z) / 2
852 fillPlaneAdd(volume.volume, appliedDelta/numFillNodes, x0,y0,z0, d1x,d1y,d1z, d2x,d2y,d2z)
853
854 local newX, _, newZ = localToLocal(node.node, volume.volume, 0, 0, 0)
855 avgX, avgZ = avgX+newX, avgZ+newZ
856 end
857
858 local newX = avgX / numFillNodes
859 local newZ = avgZ / numFillNodes
860 if math.abs(newX-spec.lastPositionInfoSent[1]) > FillVolume.SEND_PRECISION or math.abs(newZ-spec.lastPositionInfoSent[2]) > FillVolume.SEND_PRECISION then
861 spec.lastPositionInfoSent[1] = newX
862 spec.lastPositionInfoSent[2] = newZ
863
864 self:raiseDirtyFlags(spec.dirtyFlag)
865 end
866 else
867 -- increase size of fill info if there should not be a heap since a small fill info will still produce a heap
868 local loadSize = 0.1
869 if volume.maxPhysicalSurfaceAngle == 0 or volume.maxSurfaceAngle == 0 then
870 loadSize = 10
871 end
872
873 local x,y,z = localToWorld(volume.volume, -loadSize * 0.5, 0, -loadSize * 0.5)
874 local d1x,d1y,d1z = localDirectionToWorld(volume.volume, loadSize, 0, 0)
875 local d2x,d2y,d2z = localDirectionToWorld(volume.volume, 0, 0, loadSize)
876
877 if not self.isServer then
878 if spec.lastPositionInfo[1] ~= 0 and spec.lastPositionInfo[2] ~= 0 then
879 x, y, z = localToWorld(volume.volume, spec.lastPositionInfo[1], 0, spec.lastPositionInfo[2])
880 end
881 end
882
883 local steps = MathUtil.clamp(math.floor(appliedDelta/400), 1, 25)
884 for _=1, steps do
885 fillPlaneAdd(volume.volume, appliedDelta/steps, x,y,z, d1x,d1y,d1z, d2x,d2y,d2z)
886 end
887 end
888
889 local heightNodes = spec.fillVolumeIndexToHeightNode[volume.index]
890 if heightNodes ~= nil then
891 for _, heightNode in ipairs(heightNodes) do
892 heightNode.isDirty = true
893 end
894 end
895
896 for _,deformer in pairs(volume.deformers) do
897 deformer.isDirty = true
898 end
899
900 volume.lastFillType = fillType
901 end
902end

onLoad

Description
Definition
onLoad()
Code
128function FillVolume:onLoad(savegame)
129 local spec = self.spec_fillVolume
130
131 local fillVolumeConfigurationId = Utils.getNoNil(self.configurations["fillVolume"], 1)
132 local configKey = string.format("vehicle.fillVolume.fillVolumeConfigurations.fillVolumeConfiguration(%d).volumes", fillVolumeConfigurationId -1)
133 ObjectChangeUtil.updateObjectChanges(self.xmlFile, "vehicle.fillVolume.fillVolumeConfigurations.fillVolumeConfiguration", fillVolumeConfigurationId , self.components, self)
134
135 spec.volumes = {}
136 spec.fillVolumeDeformersByNode = {}
137 spec.fillUnitFillVolumeMapping = {}
138
139 self.xmlFile:iterate(configKey .. ".volume", function(_, key)
140 local entry = {}
141 if self:loadFillVolume(self.xmlFile, key, entry) then
142 table.insert(spec.volumes, entry)
143 entry.index = #spec.volumes
144 end
145 end)
146
147 -- create fill units
148 for _, mapping in ipairs(spec.fillUnitFillVolumeMapping) do
149 for _, fillVolume in ipairs(mapping.fillVolumes) do
150 fillVolume.fillUnitFactor = fillVolume.fillUnitFactor / mapping.sumFactors
151 end
152 end
153
154 for _, fillVolume in ipairs(spec.volumes) do
155 local capacity = self:getFillUnitCapacity(fillVolume.fillUnitIndex)
156 local fillVolumeCapacity = capacity * fillVolume.fillUnitFactor
157 fillVolume.volume = createFillPlaneShape(fillVolume.baseNode, "fillPlane", fillVolumeCapacity, fillVolume.maxDelta, fillVolume.maxSurfaceAngle, fillVolume.maxPhysicalSurfaceAngle, fillVolume.maxSurfaceDistanceError, fillVolume.maxSubDivEdgeLength, fillVolume.syncMaxSubDivEdgeLength, fillVolume.allSidePlanes, fillVolume.retessellateTop)
158 if fillVolume.volume == nil or fillVolume.volume == 0 then
159 print("Warning: fillVolume '"..tostring(getName(fillVolume.baseNode)).."' could not create actual fillVolume in '"..self.configFileName.."'! Simplifying the mesh could help")
160 fillVolume.volume = nil
161 else
162 setVisibility(fillVolume.volume, false)
163
164 for i=#fillVolume.deformers, 1, -1 do
165 local deformer = fillVolume.deformers[i]
166 deformer.polyline = findPolyline(fillVolume.volume, deformer.posX, deformer.posZ)
167 if deformer.polyline == nil and deformer.polyline ~= -1 then
168 print("Warning: Could not find 'polyline' for '"..tostring(getName(deformer.node)).."' in '"..self.configFileName.."'")
169 table.remove(fillVolume.deformers, i)
170 end
171 end
172
173 link(fillVolume.baseNode, fillVolume.volume)
174
175 local fillVolumeMaterial = g_materialManager:getBaseMaterialByName("fillPlane")
176 if fillVolumeMaterial ~= nil then
177 setMaterial(fillVolume.volume, fillVolumeMaterial, 0)
178 g_fillTypeManager:assignFillTypeTextureArrays(fillVolume.volume, true, true, true)
179 else
180 Logging.error("Failed to assign material to fill volume. Base Material 'fillPlane' not found!")
181 end
182
183 -- check offset between pivot and bottom plane of fill volume
184 fillPlaneAdd(fillVolume.volume, 1, 0, 1, 0, 11, 0, 0, 0, 0, 11)
185 fillVolume.heightOffset = getFillPlaneHeightAtLocalPos(fillVolume.volume, 0, 0)
186 fillPlaneAdd(fillVolume.volume, -1, 0, 1, 0, 11, 0, 0, 0, 0, 11)
187 end
188 end
189
190 spec.loadInfos = {}
191 self.xmlFile:iterate("vehicle.fillVolume.loadInfos.loadInfo", function(_, key)
192 local entry = {}
193 if self:loadFillVolumeInfo(self.xmlFile, key, entry) then
194 table.insert(spec.loadInfos, entry)
195 end
196 end)
197
198 spec.unloadInfos = {}
199 self.xmlFile:iterate("vehicle.fillVolume.unloadInfos.unloadInfo", function(_, key)
200 local entry = {}
201 if self:loadFillVolumeInfo(self.xmlFile, key, entry) then
202 table.insert(spec.unloadInfos, entry)
203 end
204 end)
205
206 spec.heightNodes = {}
207 spec.fillVolumeIndexToHeightNode = {}
208 self.xmlFile:iterate("vehicle.fillVolume.heightNodes.heightNode", function(_, key)
209 local entry = {}
210 if self:loadFillVolumeHeightNode(self.xmlFile, key, entry) then
211 table.insert(spec.heightNodes, entry)
212
213 if spec.fillVolumeIndexToHeightNode[entry.fillVolumeIndex] == nil then
214 spec.fillVolumeIndexToHeightNode[entry.fillVolumeIndex] = {}
215 end
216 table.insert(spec.fillVolumeIndexToHeightNode[entry.fillVolumeIndex], entry)
217 end
218 end)
219
220 spec.lastPositionInfo = {0, 0}
221 spec.lastPositionInfoSent = {0, 0}
222 spec.availableFillNodes = {}
223 spec.dirtyFlag = self:getNextDirtyFlag()
224
225 if not self.isClient or (#spec.volumes == 0 and #spec.heightNodes == 0) then
226 SpecializationUtil.removeEventListener(self, "onUpdate", FillVolume)
227 end
228end

onReadUpdateStream

Description
Definition
onReadUpdateStream()
Code
246function FillVolume:onReadUpdateStream(streamId, timestamp, connection)
247 if connection:getIsServer() then
248 local spec = self.spec_fillVolume
249
250 if streamReadBool(streamId) then
251 local x = (streamReadUIntN(streamId, FillVolume.SEND_NUM_BITS) / (math.pow(2, FillVolume.SEND_NUM_BITS) - 1) * FillVolume.SEND_MAX_SIZE) - FillVolume.SEND_MAX_SIZE * 0.5
252 local z = (streamReadUIntN(streamId, FillVolume.SEND_NUM_BITS) / (math.pow(2, FillVolume.SEND_NUM_BITS) - 1) * FillVolume.SEND_MAX_SIZE) - FillVolume.SEND_MAX_SIZE * 0.5
253
254 spec.lastPositionInfo[1] = x
255 spec.lastPositionInfo[2] = z
256 end
257 end
258end

onUpdate

Description
Definition
onUpdate()
Code
281function FillVolume:onUpdate(dt, isActiveForInput, isActiveForInputIgnoreSelection, isSelected)
282 if self.isClient then
283 local spec = self.spec_fillVolume
284
285 -- deform (fill)volume
286 for _,volume in pairs(spec.volumes) do
287 for _,deformer in ipairs(volume.deformers) do
288 if deformer.isDirty and deformer.polyline ~= nil and deformer.polyline ~= -1 then
289 deformer.isDirty = false
290
291 local posX, posY, posZ = localToLocal(deformer.node, deformer.baseNode, 0,0,0)
292 if math.abs(posX - deformer.posX) > 0.0001 or math.abs(posZ - deformer.posZ) > 0.0001 then
293 --#debug local x, y, z = localToWorld(deformer.baseNode, posX, posY, posZ)
294 --#debug drawDebugLine(x, y, z, 0, 1, 0, x, y + 4, z, 0, 1, 0, true)
295
296 deformer.lastPosX = posX
297 deformer.lastPosZ = posZ
298 local dx = posX - deformer.initPos[1]
299 local dz = posZ - deformer.initPos[3]
300 setPolylineTranslation(volume.volume, deformer.polyline, dx,dz)
301 end
302 end
303 end
304
305 local uvScrollSpeedX, uvScrollSpeedY, uvScrollSpeedZ = self:getFillVolumeUVScrollSpeed(volume.index)
306 if uvScrollSpeedX ~= 0 or uvScrollSpeedY ~= 0 or uvScrollSpeedZ ~= 0 then
307 volume.uvPosition[1] = volume.uvPosition[1] + uvScrollSpeedX * (dt/1000)
308 volume.uvPosition[2] = volume.uvPosition[2] + uvScrollSpeedY * (dt/1000)
309 volume.uvPosition[3] = volume.uvPosition[3] + uvScrollSpeedZ * (dt/1000)
310 setShaderParameter(volume.volume, "uvOffset", volume.uvPosition[1], volume.uvPosition[2], volume.uvPosition[3], 0, false)
311 end
312 end
313
314 -- update heightNodes
315 for _,heightNode in pairs(spec.heightNodes) do
316 if heightNode.isDirty then
317 heightNode.isDirty = false
318
319 local fillVolume = spec.volumes[heightNode.fillVolumeIndex]
320
321 local baseNode = fillVolume.baseNode
322 local volumeNode = fillVolume.volume
323
324 if baseNode ~= nil and volumeNode ~= nil then
325
326 local minHeight = math.huge
327 local maxHeight = -math.huge
328 local maxHeightWorld = -math.huge
329 for _, refNode in pairs(heightNode.refNodes) do
330 local x, _, z = localToLocal(refNode.refNode, baseNode, 0, 0, 0)
331 local height = getFillPlaneHeightAtLocalPos(volumeNode, x, z) - fillVolume.heightOffset
332 minHeight = math.min(minHeight, height)
333 maxHeight = math.max(maxHeight, height)
334 local _, yw, _ = localToWorld(baseNode, x, height, z)
335 maxHeightWorld = math.max(maxHeightWorld, yw)
336 end
337
338 heightNode.currentMinHeight = minHeight
339 heightNode.currentMaxHeight = maxHeight
340 heightNode.currentMaxHeightWorld = maxHeightWorld
341
342 for _,node in pairs(heightNode.nodes) do
343 local nodeHeight = math.max(minHeight + node.heightOffset, node.minHeight)
344
345 local sx = node.scaleAxis[1] * nodeHeight
346 local sy = node.scaleAxis[2] * nodeHeight
347 local sz = node.scaleAxis[3] * nodeHeight
348 if node.scaleMax[1] > 0 then
349 sx = math.min(node.scaleMax[1], sx)
350 end
351 if node.scaleMax[2] > 0 then
352 sy = math.min(node.scaleMax[2], sy)
353 end
354 if node.scaleMax[3] > 0 then
355 sz = math.min(node.scaleMax[3], sz)
356 end
357 local tx = node.transAxis[1] * nodeHeight
358 local ty = node.transAxis[2] * nodeHeight
359 local tz = node.transAxis[3] * nodeHeight
360 if node.transMax[1] > 0 then
361 tx = math.min(node.transMax[1], tx)
362 end
363 if node.transMax[2] > 0 then
364 ty = math.min(node.transMax[2], ty)
365 end
366 if node.transMax[3] > 0 then
367 tz = math.min(node.transMax[3], tz)
368 end
369
370 setScale(node.node, node.baseScale[1]+sx, node.baseScale[2]+sy, node.baseScale[3]+sz)
371 setTranslation(node.node, node.basePosition[1]+tx, node.basePosition[2]+ty, node.basePosition[3]+tz)
372
373 if node.orientateToWorldY then
374 local _,dy,_ = localDirectionToWorld(getParent(node.node), 0,1,0)
375 local alpha = math.acos(dy)
376 setRotation(node.node, alpha,0,0)
377 end
378 end
379 end
380 end
381 end
382 end
383end

onWriteUpdateStream

Description
Definition
onWriteUpdateStream()
Code
262function FillVolume:onWriteUpdateStream(streamId, connection, dirtyMask)
263 if not connection:getIsServer() then
264 local spec = self.spec_fillVolume
265
266 if streamWriteBool(streamId, bitAND(dirtyMask, spec.dirtyFlag) ~= 0) then
267 local x = (spec.lastPositionInfoSent[1] + FillVolume.SEND_MAX_SIZE * 0.5) / FillVolume.SEND_MAX_SIZE * (math.pow(2, FillVolume.SEND_NUM_BITS) - 1)
268 streamWriteUIntN(streamId, x, FillVolume.SEND_NUM_BITS)
269
270 local z = (spec.lastPositionInfoSent[2] + FillVolume.SEND_MAX_SIZE * 0.5) / FillVolume.SEND_MAX_SIZE * (math.pow(2, FillVolume.SEND_NUM_BITS) - 1)
271 streamWriteUIntN(streamId, z, FillVolume.SEND_NUM_BITS)
272
273 spec.lastPositionInfoSent[1] = (math.floor(x) / (math.pow(2, FillVolume.SEND_NUM_BITS) - 1) * FillVolume.SEND_MAX_SIZE) - FillVolume.SEND_MAX_SIZE * 0.5
274 spec.lastPositionInfoSent[2] = (math.floor(z) / (math.pow(2, FillVolume.SEND_NUM_BITS) - 1) * FillVolume.SEND_MAX_SIZE) - FillVolume.SEND_MAX_SIZE * 0.5
275 end
276 end
277end

prerequisitesPresent

Description
Definition
prerequisitesPresent()
Code
19function FillVolume.prerequisitesPresent(specializations)
20 return SpecializationUtil.hasSpecialization(FillUnit, specializations)
21end

registerEventListeners

Description
Definition
registerEventListeners()
Code
117function FillVolume.registerEventListeners(vehicleType)
118 SpecializationUtil.registerEventListener(vehicleType, "onLoad", FillVolume)
119 SpecializationUtil.registerEventListener(vehicleType, "onDelete", FillVolume)
120 SpecializationUtil.registerEventListener(vehicleType, "onReadUpdateStream", FillVolume)
121 SpecializationUtil.registerEventListener(vehicleType, "onWriteUpdateStream", FillVolume)
122 SpecializationUtil.registerEventListener(vehicleType, "onUpdate", FillVolume)
123 SpecializationUtil.registerEventListener(vehicleType, "onFillUnitFillLevelChanged", FillVolume)
124end

registerFunctions

Description
Definition
registerFunctions()
Code
95function FillVolume.registerFunctions(vehicleType)
96 SpecializationUtil.registerFunction(vehicleType, "loadFillVolume", FillVolume.loadFillVolume)
97 SpecializationUtil.registerFunction(vehicleType, "loadFillVolumeInfo", FillVolume.loadFillVolumeInfo)
98 SpecializationUtil.registerFunction(vehicleType, "loadFillVolumeHeightNode", FillVolume.loadFillVolumeHeightNode)
99 SpecializationUtil.registerFunction(vehicleType, "getFillVolumeLoadInfo", FillVolume.getFillVolumeLoadInfo)
100 SpecializationUtil.registerFunction(vehicleType, "getFillVolumeUnloadInfo", FillVolume.getFillVolumeUnloadInfo)
101 SpecializationUtil.registerFunction(vehicleType, "getFillVolumeIndicesByFillUnitIndex", FillVolume.getFillVolumeIndicesByFillUnitIndex)
102 SpecializationUtil.registerFunction(vehicleType, "setFillVolumeForcedFillTypeByFillUnitIndex", FillVolume.setFillVolumeForcedFillTypeByFillUnitIndex)
103 SpecializationUtil.registerFunction(vehicleType, "setFillVolumeForcedFillType", FillVolume.setFillVolumeForcedFillType)
104 SpecializationUtil.registerFunction(vehicleType, "getFillVolumeUVScrollSpeed", FillVolume.getFillVolumeUVScrollSpeed)
105end

registerInfoNodeXMLPaths

Description
Definition
registerInfoNodeXMLPaths()
Code
78function FillVolume.registerInfoNodeXMLPaths(schema, basePath)
79 schema:register(XMLValueType.NODE_INDEX, basePath .. ".node(?)#node", "Info node")
80 schema:register(XMLValueType.FLOAT, basePath .. ".node(?)#width", "Info width", 1.0)
81 schema:register(XMLValueType.FLOAT, basePath .. ".node(?)#length", "Info length", 1.0)
82 schema:register(XMLValueType.INT, basePath .. ".node(?)#fillVolumeHeightIndex", "Fill volume height index")
83 schema:register(XMLValueType.INT, basePath .. ".node(?)#priority", "Priority", 1)
84 schema:register(XMLValueType.FLOAT, basePath .. ".node(?)#minHeight", "Min. height")
85 schema:register(XMLValueType.FLOAT, basePath .. ".node(?)#maxHeight", "Max. height")
86 schema:register(XMLValueType.FLOAT, basePath .. ".node(?)#minFillLevelPercentage", "Min. fill level percentage")
87 schema:register(XMLValueType.FLOAT, basePath .. ".node(?)#maxFillLevelPercentage", "Min. fill level percentage")
88 schema:register(XMLValueType.FLOAT, basePath .. ".node(?)#heightForTranslation", "Min. height for translation")
89 schema:register(XMLValueType.VECTOR_TRANS, basePath .. ".node(?)#translationStart", "Translation start")
90 schema:register(XMLValueType.VECTOR_TRANS, basePath .. ".node(?)#translationEnd", "Translation end")
91end

registerOverwrittenFunctions

Description
Definition
registerOverwrittenFunctions()
Code
109function FillVolume.registerOverwrittenFunctions(vehicleType)
110 SpecializationUtil.registerOverwrittenFunction(vehicleType, "setMovingToolDirty", FillVolume.setMovingToolDirty)
111 SpecializationUtil.registerOverwrittenFunction(vehicleType, "loadExtraDependentParts", FillVolume.loadExtraDependentParts)
112 SpecializationUtil.registerOverwrittenFunction(vehicleType, "updateExtraDependentParts", FillVolume.updateExtraDependentParts)
113end

setFillVolumeForcedFillType

Description
Definition
setFillVolumeForcedFillType()
Code
631function FillVolume:setFillVolumeForcedFillType(fillVolumeIndex, forcedFillType)
632 local spec = self.spec_fillVolume
633 if spec.volumes[fillVolumeIndex] ~= nil then
634 spec.volumes[fillVolumeIndex].forcedFillType = forcedFillType
635 end
636end

setFillVolumeForcedFillTypeByFillUnitIndex

Description
Definition
setFillVolumeForcedFillTypeByFillUnitIndex()
Code
620function FillVolume:setFillVolumeForcedFillTypeByFillUnitIndex(fillUnitIndex, forcedFillType)
621 local spec = self.spec_fillVolume
622 for i, fillVolume in ipairs(spec.volumes) do
623 if fillVolume.fillUnitIndex == fillUnitIndex then
624 self:setFillVolumeForcedFillType(i, forcedFillType)
625 end
626 end
627end

setMovingToolDirty

Description
Set moving tool dirty
Definition
setMovingToolDirty(function superFunc, integer node, bool forceUpdate, float dt)
Arguments
functionsuperFuncsuperFunc
integernodenode id
boolforceUpdateforce immediate update of moving tool and dependent parts
floatdttime since last call (only if forceUpdate is set)
Code
650function FillVolume:setMovingToolDirty(superFunc, node, forceUpdate, dt)
651 superFunc(self, node, forceUpdate, dt)
652
653 local spec = self.spec_fillVolume
654 if spec.fillVolumeDeformersByNode ~= nil then
655 local deformer = spec.fillVolumeDeformersByNode[node]
656 if deformer ~= nil then
657 deformer.isDirty = true
658 end
659 end
660end

updateExtraDependentParts

Description
Definition
updateExtraDependentParts()
Code
686function FillVolume:updateExtraDependentParts(superFunc, part, dt)
687 superFunc(self, part, dt)
688
689 if part.deformerNodes ~= nil then
690 if part.fillVolumeIndex ~= nil then
691 part.fillVolume = self.spec_fillVolume.volumes[part.fillVolumeIndex]
692
693 if part.fillVolume == nil then
694 Logging.xmlWarning(self.xmlFile, "Unable to find fillVolume with index '%d' for movingPart/movingTool '%s'", part.fillVolumeIndex, getName(part.node))
695 part.deformerNodes = nil
696 end
697 part.fillVolumeIndex = nil
698 end
699
700 if part.fillVolume ~= nil then
701 for i, nodeIndex in pairs(part.deformerNodes) do
702 local deformerNode = part.fillVolume.deformers[nodeIndex]
703 if deformerNode == nil then
704 part.deformerNodes[i] = nil
705 else
706 deformerNode.isDirty = true
707 end
708 end
709 end
710 end
711end