50 | function AnimatedObject:load(rootNode, xmlFile, key, xmlFilename, i3dMappings) |
51 | self.xmlFilename = xmlFilename |
52 | |
53 | local modName, baseDirectory = Utils.getModNameAndBaseDirectory(xmlFilename) |
54 | self.baseDirectory = baseDirectory |
55 | self.customEnvironment = modName |
56 | |
57 | self.nodeId = rootNode |
58 | if type(rootNode) == "table" then |
59 | self.nodeId = rootNode[1].node |
60 | end |
61 | |
62 | self.samples = {} |
63 | |
64 | local success = true |
65 | self.saveId = xmlFile:getValue(key.."#saveId") |
66 | if self.saveId == nil then |
67 | self.saveId = "AnimatedObject_"..getName(self.nodeId) |
68 | end |
69 | |
70 | local animKey = key .. ".animation" |
71 | |
72 | self.animation = {} |
73 | self.animation.parts = {} |
74 | self.animation.shaderAnims = {} |
75 | self.animation.duration = xmlFile:getValue(animKey.."#duration") |
76 | self.animation.time = 0 |
77 | self.animation.direction = 0 |
78 | self.animation.maxTime = 0 |
79 | |
80 | xmlFile:iterate(animKey .. ".part", function(_, partKey) |
81 | local node = xmlFile:getValue(partKey.."#node", nil, rootNode, i3dMappings) |
82 | if node ~= nil then |
83 | local part = {} |
84 | part.node = node |
85 | part.frames = {} |
86 | |
87 | local hasFrames = false |
88 | xmlFile:iterate(partKey .. ".keyFrame", function(_, frameKey) |
89 | local keyframe = {self:loadFrameValues(xmlFile, frameKey, node)} |
90 | keyframe.time = xmlFile:getValue(frameKey.."#time") |
91 | self.animation.maxTime = math.max(keyframe.time, self.animation.maxTime) |
92 | |
93 | table.insert(part.frames, keyframe) |
94 | hasFrames = true |
95 | end) |
96 | |
97 | if hasFrames then |
98 | table.insert(self.animation.parts, part) |
99 | end |
100 | end |
101 | end) |
102 | |
103 | xmlFile:iterate(animKey .. ".shader", function(_, shaderKey) |
104 | local node = xmlFile:getValue(shaderKey.."#node", nil, rootNode, i3dMappings) |
105 | if node ~= nil then |
106 | local parameterName = xmlFile:getValue(shaderKey.."#parameterName") |
107 | if parameterName ~= nil and getHasShaderParameter(node, parameterName) then |
108 | local shader = {} |
109 | |
110 | shader.node = node |
111 | shader.parameterName = parameterName |
112 | shader.frames = {} |
113 | |
114 | local hasFrames = false |
115 | xmlFile:iterate(shaderKey .. ".keyFrame", function(_, frameKey) |
116 | local keyTime = xmlFile:getValue(frameKey.."#time") |
117 | local shaderX, shaderY, shaderZ, shaderW = getShaderParameter(node, parameterName) |
118 | |
119 | local shaderValuesStr = xmlFile:getValue(frameKey.."#values", nil) |
120 | if shaderValuesStr ~= nil then |
121 | local splits = string.split(shaderValuesStr, " ") |
122 | local values = {} |
123 | values[1] = splits[1] and tonumber(splits[1]) or shaderX |
124 | values[2] = splits[2] and tonumber(splits[2]) or shaderY |
125 | values[3] = splits[3] and tonumber(splits[3]) or shaderZ |
126 | values[4] = splits[4] and tonumber(splits[4]) or shaderW |
127 | |
128 | local keyframe = values |
129 | keyframe.time = keyTime |
130 | table.insert(shader.frames, keyframe) |
131 | hasFrames = true |
132 | end |
133 | end) |
134 | |
135 | if hasFrames then |
136 | table.insert(self.animation.shaderAnims, shader) |
137 | end |
138 | end |
139 | end |
140 | end) |
141 | |
142 | for _, part in ipairs(self.animation.parts) do |
143 | part.animCurve = AnimCurve.new(linearInterpolatorN) |
144 | |
145 | for _, frame in ipairs(part.frames) do |
146 | if self.animation.duration == nil then |
147 | frame.time = frame.time / self.animation.maxTime |
148 | end |
149 | |
150 | part.animCurve:addKeyframe(frame) |
151 | end |
152 | end |
153 | |
154 | for _, shader in ipairs(self.animation.shaderAnims) do |
155 | shader.animCurve = AnimCurve.new(linearInterpolatorN) |
156 | |
157 | for _, frame in ipairs(shader.frames) do |
158 | if self.animation.duration == nil then |
159 | frame.time = frame.time / self.animation.maxTime |
160 | end |
161 | |
162 | shader.animCurve:addKeyframe(frame) |
163 | end |
164 | end |
165 | |
166 | local clipRootNode = xmlFile:getValue(animKey .. ".clip#rootNode", nil, rootNode, i3dMappings) |
167 | local clipName = xmlFile:getValue(animKey .. ".clip#name") |
168 | |
169 | if clipRootNode ~= nil and clipName ~= nil then |
170 | local clipFilename = xmlFile:getValue(animKey .. ".clip#filename") |
171 | |
172 | self.animation.clipRootNode = clipRootNode |
173 | self.animation.clipName = clipName |
174 | self.animation.clipTrack = 0 |
175 | |
176 | if clipFilename ~= nil then |
177 | clipFilename = Utils.getFilename(clipFilename, self.baseDirectory) |
178 | self.animation.sharedLoadRequestId = g_i3DManager:loadSharedI3DFileAsync(clipFilename, false, false, self.onSharedAnimationFileLoaded, self, nil) |
179 | self.animation.clipFilename = clipFilename |
180 | else |
181 | self:applyAnimation() |
182 | end |
183 | end |
184 | |
185 | if self.animation.duration == nil then |
186 | self.animation.duration = self.animation.maxTime |
187 | end |
188 | self.animation.duration = self.animation.duration * 1000 |
189 | |
190 | |
191 | local initialTime = xmlFile:getValue(animKey.."#initialTime", 0)*1000 |
192 | self:setAnimTime(initialTime / self.animation.duration, true) |
193 | |
194 | local startTime = xmlFile:getValue(key..".openingHours#startTime") |
195 | local endTime = xmlFile:getValue(key..".openingHours#endTime") |
196 | if startTime ~= nil and endTime ~= nil then |
197 | local disableIfClosed = xmlFile:getValue(key..".openingHours#disableIfClosed", false) |
198 | local closedText = xmlFile:getValue(key..".openingHours#closedText", nil, self.customEnvironment) |
199 | self.openingHours = {startTime=startTime, endTime=endTime, disableIfClosed=disableIfClosed, closedText=closedText} |
200 | g_messageCenter:subscribe(MessageType.HOUR_CHANGED, self.hourChanged, self) |
201 | end |
202 | |
203 | self.isEnabled = true |
204 | |
205 | |
206 | local triggerId = xmlFile:getValue(key..".controls#triggerNode", nil, rootNode, i3dMappings) |
207 | if triggerId ~= nil then |
208 | self.triggerNode = triggerId |
209 | |
210 | addTrigger(self.triggerNode, "triggerCallback", self) |
211 | for i=0, getNumOfChildren(self.triggerNode)-1 do |
212 | addTrigger(getChildAt(self.triggerNode, i), "triggerCallback", self) |
213 | end |
214 | |
215 | local posAction = xmlFile:getValue(key..".controls#posAction") |
216 | if posAction ~= nil then |
217 | if InputAction[posAction] then |
218 | self.controls.posAction = posAction |
219 | |
220 | local posText = xmlFile:getValue(key..".controls#posText") |
221 | if posText ~= nil then |
222 | if g_i18n:hasText(posText, self.customEnvironment) then |
223 | posText = g_i18n:getText(posText, self.customEnvironment) |
224 | end |
225 | self.controls.posActionText = posText |
226 | end |
227 | |
228 | local negText = xmlFile:getValue(key..".controls#negText") |
229 | if negText ~= nil then |
230 | if g_i18n:hasText(negText, self.customEnvironment) then |
231 | negText = g_i18n:getText(negText, self.customEnvironment) |
232 | end |
233 | self.controls.negActionText = negText |
234 | end |
235 | |
236 | local negAction = xmlFile:getValue(key..".controls#negAction") |
237 | if negAction ~= nil then |
238 | if InputAction[negAction] then |
239 | self.controls.negAction = negAction |
240 | else |
241 | print("Warning: Negative direction action '"..negAction.."' not defined!") |
242 | end |
243 | end |
244 | else |
245 | print("Warning: Positive direction action '"..posAction.."' not defined!") |
246 | end |
247 | end |
248 | end |
249 | |
250 | if g_client ~= nil then |
251 | local soundsKey = key .. ".sounds" |
252 | self.samplesMoving = g_soundManager:loadSamplesFromXML(xmlFile, soundsKey, "moving", self.baseDirectory, rootNode, 1, AudioGroup.ENVIRONMENT, i3dMappings, nil) |
253 | self.samplePosEnd = g_soundManager:loadSampleFromXML(xmlFile, soundsKey, "posEnd", self.baseDirectory, rootNode, 1, AudioGroup.ENVIRONMENT, i3dMappings, nil) |
254 | self.sampleNegEnd = g_soundManager:loadSampleFromXML(xmlFile, soundsKey, "negEnd", self.baseDirectory, rootNode, 1, AudioGroup.ENVIRONMENT, i3dMappings, nil) |
255 | end |
256 | |
257 | self.animatedObjectDirtyFlag = self:getNextDirtyFlag() |
258 | |
259 | return success |
260 | end |
654 | function AnimatedObject.registerXMLPaths(schema, basePath) |
655 | schema:setXMLSharedRegistration("AnimatedObject", basePath) |
656 | |
657 | basePath = basePath .. ".animatedObject(?)" |
658 | schema:register(XMLValueType.STRING, basePath .. "#saveId", "Save identifier", "AnimatedObject_[nodeName]") |
659 | schema:register(XMLValueType.FLOAT, basePath .. ".animation#duration", "Animation duration (sec.)", 3) |
660 | schema:register(XMLValueType.NODE_INDEX, basePath .. ".animation.part(?)#node", "Part node") |
661 | schema:register(XMLValueType.FLOAT, basePath .. ".animation.part(?).keyFrame(?)#time", "Key time") |
662 | schema:register(XMLValueType.VECTOR_ROT, basePath .. ".animation.part(?).keyFrame(?)#rotation", "Key rotation", "values read from i3d node") |
663 | schema:register(XMLValueType.VECTOR_TRANS, basePath .. ".animation.part(?).keyFrame(?)#translation", "Key translation", "values read from i3d node") |
664 | schema:register(XMLValueType.VECTOR_SCALE, basePath .. ".animation.part(?).keyFrame(?)#scale", "Key scale", "values read from i3d node") |
665 | schema:register(XMLValueType.BOOL, basePath .. ".animation.part(?).keyFrame(?)#visibility", "Key visibility", true) |
666 | |
667 | schema:register(XMLValueType.NODE_INDEX, basePath .. ".animation.shader(?)#node", "Shader node") |
668 | schema:register(XMLValueType.STRING, basePath .. ".animation.shader(?)#parameterName", "Shader parameter name") |
669 | schema:register(XMLValueType.FLOAT, basePath .. ".animation.shader(?).keyFrame(?)#time", "Key time") |
670 | schema:register(XMLValueType.STRING, basePath .. ".animation.shader(?).keyFrame(?)#values", "Key shader parameter values. Use '-' to force using existing shader parameter value") |
671 | |
672 | schema:register(XMLValueType.NODE_INDEX, basePath .. ".animation.clip#rootNode", "I3d animation rootnode") |
673 | schema:register(XMLValueType.STRING, basePath .. ".animation.clip#name", "I3d animation clipName") |
674 | schema:register(XMLValueType.STRING, basePath .. ".animation.clip#filename", "I3d animation external animation") |
675 | |
676 | schema:register(XMLValueType.FLOAT, basePath .. ".animation#initialTime", "Animation time after loading", 0) |
677 | schema:register(XMLValueType.FLOAT, basePath .. ".openingHours#startTime", "Start day time") |
678 | schema:register(XMLValueType.FLOAT, basePath .. ".openingHours#endTime", "End day time") |
679 | schema:register(XMLValueType.BOOL, basePath .. ".openingHours#disableIfClosed", "Disabled if closed") |
680 | schema:register(XMLValueType.L10N_STRING, basePath .. ".openingHours#closedText", "Closed text") |
681 | |
682 | schema:register(XMLValueType.NODE_INDEX, basePath .. ".controls#triggerNode", "Player trigger node") |
683 | |
684 | schema:register(XMLValueType.STRING, basePath .. ".controls#posAction", "Positive direction action event name") |
685 | schema:register(XMLValueType.STRING, basePath .. ".controls#posText", "Positive direction text") |
686 | schema:register(XMLValueType.STRING, basePath .. ".controls#negText", "Negative direction text") |
687 | schema:register(XMLValueType.STRING, basePath .. ".controls#negAction", "Negative direction action event name") |
688 | |
689 | SoundManager.registerSampleXMLPaths(schema, basePath .. ".sounds", "moving(?)") |
690 | SoundManager.registerSampleXMLPaths(schema, basePath .. ".sounds", "posEnd") |
691 | SoundManager.registerSampleXMLPaths(schema, basePath .. ".sounds", "negEnd") |
692 | |
693 | schema:setXMLSharedRegistration() |
694 | end |
471 | function AnimatedObject:update(dt) |
472 | AnimatedObject:superClass().update(self, dt) |
473 | |
474 | |
475 | local finishedAnimation = false |
476 | |
477 | -- former updateTick() |
478 | if self.isServer then |
479 | if self.animation.direction ~= 0 then |
480 | local newAnimTime = MathUtil.clamp(self.animation.time + (self.animation.direction*dt)/self.animation.duration, 0, 1) |
481 | |
482 | self:setAnimTime(newAnimTime) |
483 | if newAnimTime == 0 or newAnimTime == 1 then |
484 | self.animation.direction = 0 |
485 | finishedAnimation = true |
486 | end |
487 | end |
488 | |
489 | if self.animation.time ~= self.animation.timeSend then |
490 | self.animation.timeSend = self.animation.time |
491 | self:raiseDirtyFlags(self.animatedObjectDirtyFlag) |
492 | end |
493 | else |
494 | self.networkTimeInterpolator:update(dt) |
495 | local interpolationAlpha = self.networkTimeInterpolator:getAlpha() |
496 | local animTime = self.networkAnimTimeInterpolator:getInterpolatedValue(interpolationAlpha) |
497 | local newAnimTime = self:setAnimTime(animTime) |
498 | |
499 | if self.animation.direction ~= 0 then |
500 | if self.animation.direction > 0 then |
501 | if newAnimTime == 1 then |
502 | self.animation.direction = 0 |
503 | finishedAnimation = true |
504 | end |
505 | else |
506 | if newAnimTime == 0 then |
507 | self.animation.direction = 0 |
508 | finishedAnimation = true |
509 | end |
510 | end |
511 | end |
512 | |
513 | if self.networkTimeInterpolator:isInterpolating() then |
514 | self:raiseActive() |
515 | end |
516 | end |
517 | |
518 | if self.samplesMoving ~= nil then |
519 | if self.isMoving and self.animation.direction ~= 0 then |
520 | if not self.samplesMovingArePlaying then |
521 | g_soundManager:playSamples(self.samplesMoving) |
522 | self.samplesMovingArePlaying = true |
523 | end |
524 | else |
525 | if self.samplesMovingArePlaying then |
526 | g_soundManager:stopSamples(self.samplesMoving) |
527 | self.samplesMovingArePlaying = false |
528 | end |
529 | end |
530 | end |
531 | |
532 | if finishedAnimation and self.animation.direction == 0 then |
533 | if self.samplePosEnd ~= nil and self.animation.time == 1 then |
534 | g_soundManager:playSample(self.samplePosEnd) |
535 | elseif self.sampleNegEnd ~= nil and self.animation.time == 0 then |
536 | g_soundManager:playSample(self.sampleNegEnd) |
537 | end |
538 | end |
539 | |
540 | self.isMoving = false |
541 | |
542 | if self.animation.direction ~= 0 then |
543 | self:raiseActive() |
544 | end |
545 | end |