/src/ogre/OgreMain/src/OgreAnimationTrack.cpp
Line | Count | Source |
1 | | /* |
2 | | ----------------------------------------------------------------------------- |
3 | | This source file is part of OGRE |
4 | | (Object-oriented Graphics Rendering Engine) |
5 | | For the latest info, see http://www.ogre3d.org/ |
6 | | |
7 | | Copyright (c) 2000-2014 Torus Knot Software Ltd |
8 | | |
9 | | Permission is hereby granted, free of charge, to any person obtaining a copy |
10 | | of this software and associated documentation files (the "Software"), to deal |
11 | | in the Software without restriction, including without limitation the rights |
12 | | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
13 | | copies of the Software, and to permit persons to whom the Software is |
14 | | furnished to do so, subject to the following conditions: |
15 | | |
16 | | The above copyright notice and this permission notice shall be included in |
17 | | all copies or substantial portions of the Software. |
18 | | |
19 | | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
20 | | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
21 | | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
22 | | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
23 | | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
24 | | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
25 | | THE SOFTWARE. |
26 | | ----------------------------------------------------------------------------- |
27 | | */ |
28 | | #include "OgreStableHeaders.h" |
29 | | #include "OgreAnimationTrack.h" |
30 | | #include "OgreAnimation.h" |
31 | | #include "OgreKeyFrame.h" |
32 | | |
33 | | namespace Ogre { |
34 | | |
35 | | namespace { |
36 | | // Locally key frame search helper |
37 | | struct KeyFrameTimeLess |
38 | | { |
39 | | bool operator() (const KeyFrame* kf, const KeyFrame* kf2) const |
40 | 0 | { |
41 | 0 | return kf->getTime() < kf2->getTime(); |
42 | 0 | } |
43 | | }; |
44 | | } |
45 | | //--------------------------------------------------------------------- |
46 | | //--------------------------------------------------------------------- |
47 | | AnimationTrack::AnimationTrack(Animation* parent, unsigned short handle) : |
48 | 0 | mParent(parent), mListener(0), mHandle(handle) |
49 | 0 | { |
50 | 0 | } |
51 | | //--------------------------------------------------------------------- |
52 | | AnimationTrack::~AnimationTrack() |
53 | 0 | { |
54 | 0 | removeAllKeyFrames(); |
55 | 0 | } |
56 | | //--------------------------------------------------------------------- |
57 | | float AnimationTrack::getKeyFramesAtTime(const TimeIndex& timeIndex, KeyFrame** keyFrame1, KeyFrame** keyFrame2, |
58 | | unsigned short* firstKeyIndex) const |
59 | 0 | { |
60 | | // Parametric time |
61 | | // t1 = time of previous keyframe |
62 | | // t2 = time of next keyframe |
63 | 0 | Real t1, t2; |
64 | |
|
65 | 0 | Real timePos = timeIndex.getTimePos(); |
66 | | |
67 | | // Find first keyframe after or on current time |
68 | 0 | KeyFrameList::const_iterator i; |
69 | 0 | if (timeIndex.hasKeyIndex()) |
70 | 0 | { |
71 | | // Global keyframe index available, map to local keyframe index directly. |
72 | 0 | assert(timeIndex.getKeyIndex() < mKeyFrameIndexMap.size()); |
73 | 0 | i = mKeyFrames.begin() + mKeyFrameIndexMap[timeIndex.getKeyIndex()]; |
74 | | #if OGRE_DEBUG_MODE |
75 | | KeyFrame timeKey(NULL, timePos); |
76 | | if (i != std::lower_bound(mKeyFrames.begin(), mKeyFrames.end() - 1, &timeKey, KeyFrameTimeLess())) |
77 | | { |
78 | | OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "Optimised key frame search failed"); |
79 | | } |
80 | | #endif |
81 | 0 | } |
82 | 0 | else |
83 | 0 | { |
84 | | // Wrap time |
85 | 0 | Real totalAnimationLength = mParent->getLength(); |
86 | 0 | OgreAssertDbg(totalAnimationLength > 0.0f, "Invalid animation length!"); |
87 | |
|
88 | 0 | if( timePos > totalAnimationLength && totalAnimationLength > 0.0f ) |
89 | 0 | timePos = std::fmod( timePos, totalAnimationLength ); |
90 | | |
91 | | // No global keyframe index, need to search with local keyframes. |
92 | 0 | KeyFrame timeKey(NULL, timePos); |
93 | 0 | i = std::lower_bound(mKeyFrames.begin(), mKeyFrames.end() - 1, &timeKey, KeyFrameTimeLess()); |
94 | 0 | } |
95 | |
|
96 | 0 | OgreAssertDbg(i != mKeyFrames.end(), "time should have been wrapped before this"); |
97 | |
|
98 | 0 | *keyFrame2 = *i; |
99 | 0 | t2 = (*keyFrame2)->getTime(); |
100 | | |
101 | | // Find last keyframe before or on current time |
102 | 0 | if (i != mKeyFrames.begin() && timePos < (*i)->getTime()) |
103 | 0 | { |
104 | 0 | --i; |
105 | 0 | } |
106 | | |
107 | | // Fill index of the first key |
108 | 0 | if (firstKeyIndex) |
109 | 0 | { |
110 | 0 | *firstKeyIndex = static_cast<unsigned short>(std::distance(mKeyFrames.begin(), i)); |
111 | 0 | } |
112 | |
|
113 | 0 | *keyFrame1 = *i; |
114 | |
|
115 | 0 | t1 = (*keyFrame1)->getTime(); |
116 | |
|
117 | 0 | if (t1 == t2) |
118 | 0 | { |
119 | | // Same KeyFrame (only one) |
120 | 0 | return 0.0f; |
121 | 0 | } |
122 | 0 | else |
123 | 0 | { |
124 | 0 | return (timePos - t1) / (t2 - t1); |
125 | 0 | } |
126 | 0 | } |
127 | | //--------------------------------------------------------------------- |
128 | | KeyFrame* AnimationTrack::createKeyFrame(Real timePos) |
129 | 0 | { |
130 | 0 | KeyFrame* kf = createKeyFrameImpl(timePos); |
131 | | |
132 | | // Insert just before upper bound |
133 | 0 | KeyFrameList::iterator i = |
134 | 0 | std::upper_bound(mKeyFrames.begin(), mKeyFrames.end(), kf, KeyFrameTimeLess()); |
135 | 0 | mKeyFrames.insert(i, kf); |
136 | |
|
137 | 0 | _keyFrameDataChanged(); |
138 | 0 | mParent->_keyFrameListChanged(); |
139 | |
|
140 | 0 | return kf; |
141 | |
|
142 | 0 | } |
143 | | //--------------------------------------------------------------------- |
144 | | void AnimationTrack::removeKeyFrame(unsigned short index) |
145 | 0 | { |
146 | | // If you hit this assert, then the keyframe index is out of bounds |
147 | 0 | assert( index < (ushort)mKeyFrames.size() ); |
148 | |
|
149 | 0 | KeyFrameList::iterator i = mKeyFrames.begin(); |
150 | |
|
151 | 0 | i += index; |
152 | |
|
153 | 0 | OGRE_DELETE *i; |
154 | |
|
155 | 0 | mKeyFrames.erase(i); |
156 | |
|
157 | 0 | _keyFrameDataChanged(); |
158 | 0 | mParent->_keyFrameListChanged(); |
159 | | |
160 | |
|
161 | 0 | } |
162 | | //--------------------------------------------------------------------- |
163 | | void AnimationTrack::removeAllKeyFrames(void) |
164 | 0 | { |
165 | 0 | for (auto *f : mKeyFrames) |
166 | 0 | OGRE_DELETE f; |
167 | |
|
168 | 0 | _keyFrameDataChanged(); |
169 | 0 | mParent->_keyFrameListChanged(); |
170 | |
|
171 | 0 | mKeyFrames.clear(); |
172 | |
|
173 | 0 | } |
174 | | //--------------------------------------------------------------------- |
175 | | void AnimationTrack::_collectKeyFrameTimes(std::vector<Real>& keyFrameTimes) |
176 | 0 | { |
177 | 0 | for (auto k : mKeyFrames) |
178 | 0 | { |
179 | 0 | Real timePos = k->getTime(); |
180 | |
|
181 | 0 | std::vector<Real>::iterator it = |
182 | 0 | std::lower_bound(keyFrameTimes.begin(), keyFrameTimes.end(), timePos); |
183 | 0 | if (it == keyFrameTimes.end() || *it != timePos) |
184 | 0 | { |
185 | 0 | keyFrameTimes.insert(it, timePos); |
186 | 0 | } |
187 | 0 | } |
188 | 0 | } |
189 | | //--------------------------------------------------------------------- |
190 | | void AnimationTrack::_buildKeyFrameIndexMap(const std::vector<Real>& keyFrameTimes) |
191 | 0 | { |
192 | | // Pre-allocate memory |
193 | 0 | mKeyFrameIndexMap.resize(keyFrameTimes.size()); |
194 | |
|
195 | 0 | int i = 0, j = 0; |
196 | |
|
197 | 0 | while (j < int(keyFrameTimes.size())) |
198 | 0 | { |
199 | 0 | mKeyFrameIndexMap[j] = static_cast<ushort>(i); |
200 | 0 | while (i < (int(mKeyFrames.size()) - 1) && mKeyFrames[i]->getTime() <= keyFrameTimes[j]) |
201 | 0 | { |
202 | 0 | ++i; |
203 | 0 | } |
204 | 0 | ++j; |
205 | 0 | } |
206 | 0 | } |
207 | | //-------------------------------------------------------------------------- |
208 | | void AnimationTrack::_applyBaseKeyFrame(const KeyFrame*) |
209 | 0 | { |
210 | |
|
211 | 0 | } |
212 | | //--------------------------------------------------------------------- |
213 | | void AnimationTrack::populateClone(AnimationTrack* clone) const |
214 | 0 | { |
215 | 0 | for (auto k : mKeyFrames) |
216 | 0 | { |
217 | 0 | KeyFrame* clonekf = k->_clone(clone); |
218 | 0 | clone->mKeyFrames.push_back(clonekf); |
219 | 0 | } |
220 | 0 | } |
221 | | //--------------------------------------------------------------------- |
222 | | //--------------------------------------------------------------------- |
223 | | // Numeric specialisations |
224 | | //--------------------------------------------------------------------- |
225 | | NumericAnimationTrack::NumericAnimationTrack(Animation* parent, |
226 | | unsigned short handle, const AnimableValuePtr& target) |
227 | 0 | :AnimationTrack(parent, handle), mTargetAnim(target) |
228 | 0 | { |
229 | 0 | } |
230 | | //--------------------------------------------------------------------- |
231 | | KeyFrame* NumericAnimationTrack::createKeyFrameImpl(Real time) |
232 | 0 | { |
233 | 0 | return OGRE_NEW NumericKeyFrame(this, time); |
234 | 0 | } |
235 | | |
236 | | static Any lerpAny(const Any& v0, const Any& v1, Real t, AnimableValue::ValueType type) |
237 | 0 | { |
238 | 0 | switch(type) |
239 | 0 | { |
240 | 0 | default: |
241 | 0 | case AnimableValue::INT: |
242 | 0 | return Math::lerp(any_cast<int>(v0), any_cast<int>(v1), t); |
243 | 0 | case AnimableValue::REAL: |
244 | 0 | return Math::lerp(any_cast<Real>(v0), any_cast<Real>(v1), t); |
245 | 0 | case AnimableValue::VECTOR2: |
246 | 0 | return Math::lerp(any_cast<Vector2>(v0), any_cast<Vector2>(v1), t); |
247 | 0 | case AnimableValue::VECTOR3: |
248 | 0 | return Math::lerp(any_cast<Vector3>(v0), any_cast<Vector3>(v1), t); |
249 | 0 | case AnimableValue::VECTOR4: |
250 | 0 | return Math::lerp(any_cast<Vector4>(v0), any_cast<Vector4>(v1), t); |
251 | 0 | case AnimableValue::QUATERNION: |
252 | 0 | return Math::lerp(any_cast<Quaternion>(v0), any_cast<Quaternion>(v1), t); |
253 | 0 | case AnimableValue::COLOUR: |
254 | 0 | return Math::lerp(any_cast<ColourValue>(v0), any_cast<ColourValue>(v1), t); |
255 | 0 | case AnimableValue::RADIAN: |
256 | 0 | return Math::lerp(any_cast<Radian>(v0), any_cast<Radian>(v1), t); |
257 | 0 | } |
258 | 0 | } |
259 | | |
260 | | //--------------------------------------------------------------------- |
261 | | void NumericAnimationTrack::getInterpolatedKeyFrame(const TimeIndex& timeIndex, |
262 | | KeyFrame* kf) const |
263 | 0 | { |
264 | 0 | if (mListener) |
265 | 0 | { |
266 | 0 | if (mListener->getInterpolatedKeyFrame(this, timeIndex, kf)) |
267 | 0 | return; |
268 | 0 | } |
269 | | |
270 | 0 | NumericKeyFrame* kret = static_cast<NumericKeyFrame*>(kf); |
271 | | |
272 | | // Keyframe pointers |
273 | 0 | KeyFrame *kBase1, *kBase2; |
274 | 0 | NumericKeyFrame *k1, *k2; |
275 | 0 | unsigned short firstKeyIndex; |
276 | |
|
277 | 0 | Real t = this->getKeyFramesAtTime(timeIndex, &kBase1, &kBase2, &firstKeyIndex); |
278 | 0 | k1 = static_cast<NumericKeyFrame*>(kBase1); |
279 | 0 | k2 = static_cast<NumericKeyFrame*>(kBase2); |
280 | |
|
281 | 0 | if (t == 0.0) |
282 | 0 | { |
283 | | // Just use k1 |
284 | 0 | kret->setValue(k1->getValue()); |
285 | 0 | } |
286 | 0 | else |
287 | 0 | { |
288 | | // Interpolate by t |
289 | 0 | kret->setValue(lerpAny(k1->getValue(), k2->getValue(), t, mTargetAnim->getType())); |
290 | 0 | } |
291 | 0 | } |
292 | | //--------------------------------------------------------------------- |
293 | | void NumericAnimationTrack::apply(const TimeIndex& timeIndex, Real weight, Real scale) |
294 | 0 | { |
295 | 0 | applyToAnimable(mTargetAnim, timeIndex, weight, scale); |
296 | 0 | } |
297 | | //--------------------------------------------------------------------- |
298 | | static Any scaleAny(const Any& v, Real s, AnimableValue::ValueType type) |
299 | 0 | { |
300 | 0 | switch(type) |
301 | 0 | { |
302 | 0 | default: |
303 | 0 | case AnimableValue::INT: |
304 | 0 | return any_cast<int>(v) * s; |
305 | 0 | case AnimableValue::REAL: |
306 | 0 | return any_cast<Real>(v) * s; |
307 | 0 | case AnimableValue::VECTOR2: |
308 | 0 | return any_cast<Vector2>(v) * s; |
309 | 0 | case AnimableValue::VECTOR3: |
310 | 0 | return any_cast<Vector3>(v) * s; |
311 | 0 | case AnimableValue::VECTOR4: |
312 | 0 | return any_cast<Vector4>(v) * s; |
313 | 0 | case AnimableValue::QUATERNION: |
314 | 0 | return any_cast<Quaternion>(v) * s; |
315 | 0 | case AnimableValue::COLOUR: |
316 | 0 | return any_cast<ColourValue>(v) * s; |
317 | 0 | case AnimableValue::RADIAN: |
318 | 0 | return any_cast<Radian>(v) * s; |
319 | 0 | } |
320 | 0 | } |
321 | | void NumericAnimationTrack::applyToAnimable(const AnimableValuePtr& anim, const TimeIndex& timeIndex, |
322 | | Real weight, Real scale) |
323 | 0 | { |
324 | | // Nothing to do if no keyframes or zero weight, scale |
325 | 0 | if (mKeyFrames.empty() || !weight || !scale) |
326 | 0 | return; |
327 | | |
328 | 0 | NumericKeyFrame kf(0, timeIndex.getTimePos()); |
329 | 0 | getInterpolatedKeyFrame(timeIndex, &kf); |
330 | | // add to existing. Weights are not relative, but treated as |
331 | | // absolute multipliers for the animation |
332 | 0 | anim->applyDeltaValue(scaleAny(kf.getValue(), weight * scale, mTargetAnim->getType())); |
333 | |
|
334 | 0 | } |
335 | | //-------------------------------------------------------------------------- |
336 | | NumericKeyFrame* NumericAnimationTrack::createNumericKeyFrame(Real timePos) |
337 | 0 | { |
338 | 0 | return static_cast<NumericKeyFrame*>(createKeyFrame(timePos)); |
339 | 0 | } |
340 | | //-------------------------------------------------------------------------- |
341 | | NumericKeyFrame* NumericAnimationTrack::getNumericKeyFrame(unsigned short index) const |
342 | 0 | { |
343 | 0 | return static_cast<NumericKeyFrame*>(getKeyFrame(index)); |
344 | 0 | } |
345 | | //--------------------------------------------------------------------- |
346 | | NumericAnimationTrack* NumericAnimationTrack::_clone(Animation* newParent) const |
347 | 0 | { |
348 | 0 | NumericAnimationTrack* newTrack = newParent->createNumericTrack(mHandle, mTargetAnim); |
349 | 0 | populateClone(newTrack); |
350 | 0 | return newTrack; |
351 | 0 | } |
352 | | //--------------------------------------------------------------------- |
353 | | //--------------------------------------------------------------------- |
354 | | // Node specialisations |
355 | | //--------------------------------------------------------------------- |
356 | | NodeAnimationTrack::NodeAnimationTrack(Animation* parent, unsigned short handle) |
357 | 0 | : NodeAnimationTrack(parent, handle, 0) |
358 | 0 | { |
359 | 0 | } |
360 | | //--------------------------------------------------------------------- |
361 | | NodeAnimationTrack::NodeAnimationTrack(Animation* parent, unsigned short handle, Node* targetNode) |
362 | 0 | : AnimationTrack(parent, handle), mSplineBuildNeeded(false), mUseShortestRotationPath(true), |
363 | 0 | mTargetNode(targetNode), mSplines(0) |
364 | | |
365 | 0 | { |
366 | 0 | } |
367 | | //--------------------------------------------------------------------- |
368 | | NodeAnimationTrack::~NodeAnimationTrack() |
369 | 0 | { |
370 | 0 | OGRE_DELETE_T(mSplines, Splines, MEMCATEGORY_ANIMATION); |
371 | 0 | } |
372 | | //--------------------------------------------------------------------- |
373 | | void NodeAnimationTrack::getInterpolatedKeyFrame(const TimeIndex& timeIndex, KeyFrame* kf) const |
374 | 0 | { |
375 | 0 | if (mListener) |
376 | 0 | { |
377 | 0 | if (mListener->getInterpolatedKeyFrame(this, timeIndex, kf)) |
378 | 0 | return; |
379 | 0 | } |
380 | | |
381 | 0 | TransformKeyFrame* kret = static_cast<TransformKeyFrame*>(kf); |
382 | | |
383 | | // Keyframe pointers |
384 | 0 | KeyFrame *kBase1, *kBase2; |
385 | 0 | TransformKeyFrame *k1, *k2; |
386 | 0 | unsigned short firstKeyIndex; |
387 | |
|
388 | 0 | Real t = this->getKeyFramesAtTime(timeIndex, &kBase1, &kBase2, &firstKeyIndex); |
389 | 0 | k1 = static_cast<TransformKeyFrame*>(kBase1); |
390 | 0 | k2 = static_cast<TransformKeyFrame*>(kBase2); |
391 | |
|
392 | 0 | if (t == 0.0) |
393 | 0 | { |
394 | | // Just use k1 |
395 | 0 | kret->setRotation(k1->getRotation()); |
396 | 0 | kret->setTranslate(k1->getTranslate()); |
397 | 0 | kret->setScale(k1->getScale()); |
398 | 0 | } |
399 | 0 | else |
400 | 0 | { |
401 | | // Interpolate by t |
402 | 0 | Animation::InterpolationMode im = mParent->getInterpolationMode(); |
403 | 0 | Animation::RotationInterpolationMode rim = |
404 | 0 | mParent->getRotationInterpolationMode(); |
405 | 0 | Vector3 base; |
406 | 0 | switch(im) |
407 | 0 | { |
408 | 0 | case Animation::IM_LINEAR: |
409 | | // Interpolate linearly |
410 | | // Rotation |
411 | | // Interpolate to nearest rotation if mUseShortestRotationPath set |
412 | 0 | if (rim == Animation::RIM_LINEAR) |
413 | 0 | { |
414 | 0 | kret->setRotation( Quaternion::nlerp(t, k1->getRotation(), |
415 | 0 | k2->getRotation(), mUseShortestRotationPath) ); |
416 | 0 | } |
417 | 0 | else //if (rim == Animation::RIM_SPHERICAL) |
418 | 0 | { |
419 | 0 | kret->setRotation( Quaternion::Slerp(t, k1->getRotation(), |
420 | 0 | k2->getRotation(), mUseShortestRotationPath) ); |
421 | 0 | } |
422 | | |
423 | | // Translation |
424 | 0 | base = k1->getTranslate(); |
425 | 0 | kret->setTranslate( base + ((k2->getTranslate() - base) * t) ); |
426 | | |
427 | | // Scale |
428 | 0 | base = k1->getScale(); |
429 | 0 | kret->setScale( base + ((k2->getScale() - base) * t) ); |
430 | 0 | break; |
431 | | |
432 | 0 | case Animation::IM_SPLINE: |
433 | | // Spline interpolation |
434 | | |
435 | | // Build splines if required |
436 | 0 | if (mSplineBuildNeeded) |
437 | 0 | { |
438 | 0 | buildInterpolationSplines(); |
439 | 0 | } |
440 | | |
441 | | // Rotation, take mUseShortestRotationPath into account |
442 | 0 | kret->setRotation( mSplines->rotationSpline.interpolate(firstKeyIndex, t, |
443 | 0 | mUseShortestRotationPath) ); |
444 | | |
445 | | // Translation |
446 | 0 | kret->setTranslate( mSplines->positionSpline.interpolate(firstKeyIndex, t) ); |
447 | | |
448 | | // Scale |
449 | 0 | kret->setScale( mSplines->scaleSpline.interpolate(firstKeyIndex, t) ); |
450 | |
|
451 | 0 | break; |
452 | 0 | } |
453 | |
|
454 | 0 | } |
455 | 0 | } |
456 | | //--------------------------------------------------------------------- |
457 | | void NodeAnimationTrack::apply(const TimeIndex& timeIndex, Real weight, Real scale) |
458 | 0 | { |
459 | 0 | applyToNode(mTargetNode, timeIndex, weight, scale); |
460 | |
|
461 | 0 | } |
462 | | //--------------------------------------------------------------------- |
463 | | Node* NodeAnimationTrack::getAssociatedNode(void) const |
464 | 0 | { |
465 | 0 | return mTargetNode; |
466 | 0 | } |
467 | | //--------------------------------------------------------------------- |
468 | | void NodeAnimationTrack::setAssociatedNode(Node* node) |
469 | 0 | { |
470 | 0 | mTargetNode = node; |
471 | 0 | } |
472 | | //--------------------------------------------------------------------- |
473 | | void NodeAnimationTrack::applyToNode(Node* node, const TimeIndex& timeIndex, Real weight, |
474 | | Real scl) |
475 | 0 | { |
476 | | // Nothing to do if no keyframes or zero weight or no node |
477 | 0 | if (mKeyFrames.empty() || !weight || !node) |
478 | 0 | return; |
479 | | |
480 | 0 | TransformKeyFrame kf(0, timeIndex.getTimePos()); |
481 | 0 | getInterpolatedKeyFrame(timeIndex, &kf); |
482 | | |
483 | | // add to existing. Weights are not relative, but treated as absolute multipliers for the animation |
484 | 0 | Vector3 translate = kf.getTranslate() * weight * scl; |
485 | 0 | node->translate(translate); |
486 | | |
487 | | // interpolate between no-rotation and full rotation, to point 'weight', so 0 = no rotate, 1 = full |
488 | 0 | Quaternion rotate; |
489 | 0 | Animation::RotationInterpolationMode rim = |
490 | 0 | mParent->getRotationInterpolationMode(); |
491 | 0 | if (rim == Animation::RIM_LINEAR) |
492 | 0 | { |
493 | 0 | rotate = Quaternion::nlerp(weight, Quaternion::IDENTITY, kf.getRotation(), mUseShortestRotationPath); |
494 | 0 | } |
495 | 0 | else //if (rim == Animation::RIM_SPHERICAL) |
496 | 0 | { |
497 | 0 | rotate = Quaternion::Slerp(weight, Quaternion::IDENTITY, kf.getRotation(), mUseShortestRotationPath); |
498 | 0 | } |
499 | 0 | node->rotate(rotate); |
500 | |
|
501 | 0 | Vector3 scale = kf.getScale(); |
502 | | // Not sure how to modify scale for cumulative anims... leave it alone |
503 | | //scale = ((Vector3::UNIT_SCALE - kf.getScale()) * weight) + Vector3::UNIT_SCALE; |
504 | 0 | if (scale != Vector3::UNIT_SCALE) |
505 | 0 | { |
506 | 0 | if (scl != 1.0f) |
507 | 0 | scale = Vector3::UNIT_SCALE + (scale - Vector3::UNIT_SCALE) * scl; |
508 | 0 | else if (weight != 1.0f) |
509 | 0 | scale = Vector3::UNIT_SCALE + (scale - Vector3::UNIT_SCALE) * weight; |
510 | 0 | } |
511 | 0 | node->scale(scale); |
512 | |
|
513 | 0 | } |
514 | | //--------------------------------------------------------------------- |
515 | | void NodeAnimationTrack::buildInterpolationSplines(void) const |
516 | 0 | { |
517 | | // Allocate splines if not exists |
518 | 0 | if (!mSplines) |
519 | 0 | { |
520 | 0 | mSplines = OGRE_NEW_T(Splines, MEMCATEGORY_ANIMATION); |
521 | 0 | } |
522 | | |
523 | | // Cache to register for optimisation |
524 | 0 | Splines* splines = mSplines; |
525 | | |
526 | | // Don't calc automatically, do it on request at the end |
527 | 0 | splines->positionSpline.setAutoCalculate(false); |
528 | 0 | splines->rotationSpline.setAutoCalculate(false); |
529 | 0 | splines->scaleSpline.setAutoCalculate(false); |
530 | |
|
531 | 0 | splines->positionSpline.clear(); |
532 | 0 | splines->rotationSpline.clear(); |
533 | 0 | splines->scaleSpline.clear(); |
534 | |
|
535 | 0 | for (auto *f : mKeyFrames) |
536 | 0 | { |
537 | 0 | TransformKeyFrame* kf = static_cast<TransformKeyFrame*>(f); |
538 | 0 | splines->positionSpline.addPoint(kf->getTranslate()); |
539 | 0 | splines->rotationSpline.addPoint(kf->getRotation()); |
540 | 0 | splines->scaleSpline.addPoint(kf->getScale()); |
541 | 0 | } |
542 | |
|
543 | 0 | splines->positionSpline.recalcTangents(); |
544 | 0 | splines->rotationSpline.recalcTangents(); |
545 | 0 | splines->scaleSpline.recalcTangents(); |
546 | | |
547 | |
|
548 | 0 | mSplineBuildNeeded = false; |
549 | 0 | } |
550 | | |
551 | | //--------------------------------------------------------------------- |
552 | | void NodeAnimationTrack::setUseShortestRotationPath(bool useShortestPath) |
553 | 0 | { |
554 | 0 | mUseShortestRotationPath = useShortestPath ; |
555 | 0 | } |
556 | | |
557 | | //--------------------------------------------------------------------- |
558 | | bool NodeAnimationTrack::getUseShortestRotationPath() const |
559 | 0 | { |
560 | 0 | return mUseShortestRotationPath ; |
561 | 0 | } |
562 | | //--------------------------------------------------------------------- |
563 | | void NodeAnimationTrack::_keyFrameDataChanged(void) const |
564 | 0 | { |
565 | 0 | mSplineBuildNeeded = true; |
566 | 0 | } |
567 | | //--------------------------------------------------------------------- |
568 | | bool NodeAnimationTrack::hasNonZeroKeyFrames(void) const |
569 | 0 | { |
570 | 0 | for (auto *k : mKeyFrames) |
571 | 0 | { |
572 | | // look for keyframes which have any component which is non-zero |
573 | | // Since exporters can be a little inaccurate sometimes we use a |
574 | | // tolerance value rather than looking for nothing |
575 | 0 | TransformKeyFrame* kf = static_cast<TransformKeyFrame*>(k); |
576 | 0 | Vector3 trans = kf->getTranslate(); |
577 | 0 | Vector3 scale = kf->getScale(); |
578 | 0 | Vector3 axis; |
579 | 0 | Radian angle; |
580 | 0 | kf->getRotation().ToAngleAxis(angle, axis); |
581 | 0 | Real tolerance = 1e-3f; |
582 | 0 | if (!trans.positionEquals(Vector3::ZERO, tolerance) || |
583 | 0 | !scale.positionEquals(Vector3::UNIT_SCALE, tolerance) || |
584 | 0 | !Math::RealEqual(angle.valueRadians(), 0.0f, tolerance)) |
585 | 0 | { |
586 | 0 | return true; |
587 | 0 | } |
588 | 0 | } |
589 | | |
590 | 0 | return false; |
591 | 0 | } |
592 | | //--------------------------------------------------------------------- |
593 | | void NodeAnimationTrack::optimise(void) |
594 | 0 | { |
595 | | // Eliminate duplicate keyframes from 2nd to penultimate keyframe |
596 | | // NB only eliminate middle keys from sequences of 5+ identical keyframes |
597 | | // since we need to preserve the boundary keys in place, and we need |
598 | | // 2 at each end to preserve tangents for spline interpolation |
599 | 0 | Vector3 lasttrans = Vector3::ZERO; |
600 | 0 | Vector3 lastscale = Vector3::ZERO; |
601 | 0 | Quaternion lastorientation; |
602 | 0 | Radian quatTolerance(1e-3f); |
603 | 0 | std::list<unsigned short> removeList; |
604 | 0 | unsigned short k = 0; |
605 | 0 | ushort dupKfCount = 0; |
606 | 0 | for (auto *f : mKeyFrames) |
607 | 0 | { |
608 | 0 | auto kf = static_cast<TransformKeyFrame*>(f); |
609 | 0 | Vector3 newtrans = kf->getTranslate(); |
610 | 0 | Vector3 newscale = kf->getScale(); |
611 | 0 | Quaternion neworientation = kf->getRotation(); |
612 | | // Ignore first keyframe; now include the last keyframe as we eliminate |
613 | | // only k-2 in a group of 5 to ensure we only eliminate middle keys |
614 | 0 | if (k && newtrans.positionEquals(lasttrans) && |
615 | 0 | newscale.positionEquals(lastscale) && |
616 | 0 | neworientation.equals(lastorientation, quatTolerance)) |
617 | 0 | { |
618 | 0 | ++dupKfCount; |
619 | | |
620 | | // 4 indicates this is the 5th duplicate keyframe |
621 | 0 | if (dupKfCount == 4) |
622 | 0 | { |
623 | | // remove the 'middle' keyframe |
624 | 0 | removeList.push_back(k-2); |
625 | 0 | --dupKfCount; |
626 | 0 | } |
627 | 0 | } |
628 | 0 | else |
629 | 0 | { |
630 | | // reset |
631 | 0 | dupKfCount = 0; |
632 | 0 | lasttrans = newtrans; |
633 | 0 | lastscale = newscale; |
634 | 0 | lastorientation = neworientation; |
635 | 0 | } |
636 | 0 | ++k; |
637 | 0 | } |
638 | | |
639 | | // Now remove keyframes, in reverse order to avoid index revocation |
640 | 0 | std::list<unsigned short>::reverse_iterator r = removeList.rbegin(); |
641 | 0 | for (; r!= removeList.rend(); ++r) |
642 | 0 | { |
643 | 0 | removeKeyFrame(*r); |
644 | 0 | } |
645 | 0 | } |
646 | | //-------------------------------------------------------------------------- |
647 | | KeyFrame* NodeAnimationTrack::createKeyFrameImpl(Real time) |
648 | 0 | { |
649 | 0 | return OGRE_NEW TransformKeyFrame(this, time); |
650 | 0 | } |
651 | | //-------------------------------------------------------------------------- |
652 | | TransformKeyFrame* NodeAnimationTrack::createNodeKeyFrame(Real timePos) |
653 | 0 | { |
654 | 0 | return static_cast<TransformKeyFrame*>(createKeyFrame(timePos)); |
655 | 0 | } |
656 | | //-------------------------------------------------------------------------- |
657 | | TransformKeyFrame* NodeAnimationTrack::getNodeKeyFrame(unsigned short index) const |
658 | 0 | { |
659 | 0 | return static_cast<TransformKeyFrame*>(getKeyFrame(index)); |
660 | 0 | } |
661 | | //--------------------------------------------------------------------- |
662 | | NodeAnimationTrack* NodeAnimationTrack::_clone(Animation* newParent) const |
663 | 0 | { |
664 | 0 | NodeAnimationTrack* newTrack = |
665 | 0 | newParent->createNodeTrack(mHandle, mTargetNode); |
666 | 0 | newTrack->mUseShortestRotationPath = mUseShortestRotationPath; |
667 | 0 | populateClone(newTrack); |
668 | 0 | return newTrack; |
669 | 0 | } |
670 | | //-------------------------------------------------------------------------- |
671 | | void NodeAnimationTrack::_applyBaseKeyFrame(const KeyFrame* b) |
672 | 0 | { |
673 | 0 | const TransformKeyFrame* base = static_cast<const TransformKeyFrame*>(b); |
674 | | |
675 | 0 | for (auto& k : mKeyFrames) |
676 | 0 | { |
677 | 0 | TransformKeyFrame* kf = static_cast<TransformKeyFrame*>(k); |
678 | 0 | kf->setTranslate(kf->getTranslate() - base->getTranslate()); |
679 | 0 | kf->setRotation(base->getRotation().Inverse() * kf->getRotation()); |
680 | 0 | kf->setScale(kf->getScale() * (Vector3::UNIT_SCALE / base->getScale())); |
681 | 0 | } |
682 | | |
683 | 0 | } |
684 | | //-------------------------------------------------------------------------- |
685 | | VertexAnimationTrack::VertexAnimationTrack(Animation* parent, |
686 | | unsigned short handle, VertexAnimationType animType) |
687 | 0 | : AnimationTrack(parent, handle) |
688 | 0 | , mAnimationType(animType) |
689 | 0 | { |
690 | 0 | } |
691 | | //-------------------------------------------------------------------------- |
692 | | VertexAnimationTrack::VertexAnimationTrack(Animation* parent, unsigned short handle, |
693 | | VertexAnimationType animType, VertexData* targetData, TargetMode target) |
694 | 0 | : AnimationTrack(parent, handle) |
695 | 0 | , mAnimationType(animType) |
696 | 0 | , mTargetMode(target) |
697 | 0 | , mTargetVertexData(targetData) |
698 | 0 | { |
699 | 0 | } |
700 | | //-------------------------------------------------------------------------- |
701 | | VertexMorphKeyFrame* VertexAnimationTrack::createVertexMorphKeyFrame(Real timePos) |
702 | 0 | { |
703 | 0 | OgreAssert(mAnimationType == VAT_MORPH, "Type mismatch"); |
704 | 0 | return static_cast<VertexMorphKeyFrame*>(createKeyFrame(timePos)); |
705 | 0 | } |
706 | | //-------------------------------------------------------------------------- |
707 | | VertexPoseKeyFrame* VertexAnimationTrack::createVertexPoseKeyFrame(Real timePos) |
708 | 0 | { |
709 | 0 | OgreAssert(mAnimationType == VAT_POSE, "Type mismatch"); |
710 | 0 | return static_cast<VertexPoseKeyFrame*>(createKeyFrame(timePos)); |
711 | 0 | } |
712 | | //-------------------------------------------------------------------------- |
713 | | void VertexAnimationTrack::getInterpolatedKeyFrame(const TimeIndex& timeIndex, KeyFrame* kf) const |
714 | 0 | { |
715 | | // Only relevant for pose animation |
716 | 0 | if (mAnimationType == VAT_POSE) |
717 | 0 | { |
718 | | // Get keyframes |
719 | 0 | KeyFrame *kf1, *kf2; |
720 | 0 | Real t = getKeyFramesAtTime(timeIndex, &kf1, &kf2); |
721 | | |
722 | 0 | VertexPoseKeyFrame* vkfOut = static_cast<VertexPoseKeyFrame*>(kf); |
723 | 0 | VertexPoseKeyFrame* vkf1 = static_cast<VertexPoseKeyFrame*>(kf1); |
724 | 0 | VertexPoseKeyFrame* vkf2 = static_cast<VertexPoseKeyFrame*>(kf2); |
725 | | |
726 | | // For each pose reference in key 1, we need to locate the entry in |
727 | | // key 2 and interpolate the influence |
728 | 0 | const VertexPoseKeyFrame::PoseRefList& poseList1 = vkf1->getPoseReferences(); |
729 | 0 | const VertexPoseKeyFrame::PoseRefList& poseList2 = vkf2->getPoseReferences(); |
730 | 0 | for (auto& p1 : poseList1) |
731 | 0 | { |
732 | 0 | Real startInfluence = p1.influence; |
733 | 0 | Real endInfluence = 0; |
734 | | // Search for entry in keyframe 2 list (if not there, will be 0) |
735 | 0 | for (auto& p2 : poseList2) |
736 | 0 | { |
737 | 0 | if (p1.poseIndex == p2.poseIndex) |
738 | 0 | { |
739 | 0 | endInfluence = p2.influence; |
740 | 0 | break; |
741 | 0 | } |
742 | 0 | } |
743 | | // Interpolate influence |
744 | 0 | Real influence = startInfluence + t*(endInfluence - startInfluence); |
745 | | |
746 | 0 | vkfOut->addPoseReference(p1.poseIndex, influence); |
747 | | |
748 | | |
749 | 0 | } |
750 | | // Now deal with any poses in key 2 which are not in key 1 |
751 | 0 | for (auto& p2 : poseList2) |
752 | 0 | { |
753 | 0 | bool found = false; |
754 | 0 | for (auto& p1 : poseList1) |
755 | 0 | { |
756 | 0 | if (p1.poseIndex == p2.poseIndex) |
757 | 0 | { |
758 | 0 | found = true; |
759 | 0 | break; |
760 | 0 | } |
761 | 0 | } |
762 | 0 | if (!found) |
763 | 0 | { |
764 | | // Need to apply this pose too, scaled from 0 start |
765 | 0 | Real influence = t * p2.influence; |
766 | | |
767 | 0 | vkfOut->addPoseReference(p2.poseIndex, influence); |
768 | |
|
769 | 0 | } |
770 | 0 | } // key 2 iteration |
771 | | |
772 | 0 | } |
773 | 0 | } |
774 | | //-------------------------------------------------------------------------- |
775 | | bool VertexAnimationTrack::getVertexAnimationIncludesNormals() const |
776 | 0 | { |
777 | 0 | if (mAnimationType == VAT_NONE) |
778 | 0 | return false; |
779 | | |
780 | 0 | if (mAnimationType == VAT_MORPH) |
781 | 0 | { |
782 | 0 | bool normals = false; |
783 | 0 | for (KeyFrameList::const_iterator i = mKeyFrames.begin(); i != mKeyFrames.end(); ++i) |
784 | 0 | { |
785 | 0 | VertexMorphKeyFrame* kf = static_cast<VertexMorphKeyFrame*>(*i); |
786 | 0 | bool thisnorm = kf->getVertexBuffer()->getVertexSize() > 12; |
787 | 0 | if (i == mKeyFrames.begin()) |
788 | 0 | normals = thisnorm; |
789 | 0 | else |
790 | | // Only support normals if ALL keyframes include them |
791 | 0 | normals = normals && thisnorm; |
792 | |
|
793 | 0 | } |
794 | 0 | return normals; |
795 | 0 | } |
796 | 0 | else |
797 | 0 | { |
798 | | // needs to derive from Mesh::PoseList, can't tell here |
799 | 0 | return false; |
800 | 0 | } |
801 | 0 | } |
802 | | //-------------------------------------------------------------------------- |
803 | | void VertexAnimationTrack::apply(const TimeIndex& timeIndex, Real weight, Real scale) |
804 | 0 | { |
805 | 0 | applyToVertexData(mTargetVertexData, timeIndex, weight); |
806 | 0 | } |
807 | | //-------------------------------------------------------------------------- |
808 | | void VertexAnimationTrack::applyToVertexData(VertexData* data, |
809 | | const TimeIndex& timeIndex, float weight, const PoseList* poseList) |
810 | 0 | { |
811 | | // Nothing to do if no keyframes or no vertex data |
812 | 0 | if (mKeyFrames.empty() || !data) |
813 | 0 | return; |
814 | | |
815 | | // Get keyframes |
816 | 0 | KeyFrame *kf1, *kf2; |
817 | 0 | float t = getKeyFramesAtTime(timeIndex, &kf1, &kf2); |
818 | |
|
819 | 0 | if (mAnimationType == VAT_MORPH) |
820 | 0 | { |
821 | 0 | VertexMorphKeyFrame* vkf1 = static_cast<VertexMorphKeyFrame*>(kf1); |
822 | 0 | VertexMorphKeyFrame* vkf2 = static_cast<VertexMorphKeyFrame*>(kf2); |
823 | |
|
824 | 0 | if (mTargetMode == TM_HARDWARE) |
825 | 0 | { |
826 | | // If target mode is hardware, need to bind our 2 keyframe buffers, |
827 | | // one to main pos, one to morph target texcoord |
828 | 0 | assert(!data->hwAnimationDataList.empty() && |
829 | 0 | "Haven't set up hardware vertex animation elements!"); |
830 | | |
831 | | // no use for TempBlendedBufferInfo here btw |
832 | | // NB we assume that position buffer is unshared, except for normals |
833 | | // VertexDeclaration::getAutoOrganisedDeclaration should see to that |
834 | 0 | const VertexElement* posElem = |
835 | 0 | data->vertexDeclaration->findElementBySemantic(VES_POSITION); |
836 | | // Set keyframe1 data as original position |
837 | 0 | data->vertexBufferBinding->setBinding( |
838 | 0 | posElem->getSource(), vkf1->getVertexBuffer()); |
839 | | // Set keyframe2 data as derived |
840 | 0 | data->vertexBufferBinding->setBinding( |
841 | 0 | data->hwAnimationDataList[0].targetBufferIndex, |
842 | 0 | vkf2->getVertexBuffer()); |
843 | | // save T for use later |
844 | 0 | data->hwAnimationDataList[0].parametric = t; |
845 | |
|
846 | 0 | } |
847 | 0 | else |
848 | 0 | { |
849 | | // If target mode is software, need to software interpolate each vertex |
850 | 0 | Mesh::softwareVertexMorph( |
851 | 0 | t, vkf1->getVertexBuffer(), vkf2->getVertexBuffer(), data); |
852 | 0 | } |
853 | 0 | } |
854 | 0 | else |
855 | 0 | { |
856 | | // Pose |
857 | 0 | OgreAssert(poseList, "Pose list required for pose animation"); |
858 | 0 | VertexPoseKeyFrame* vkf1 = static_cast<VertexPoseKeyFrame*>(kf1); |
859 | 0 | VertexPoseKeyFrame* vkf2 = static_cast<VertexPoseKeyFrame*>(kf2); |
860 | | // For each pose reference in key 1, we need to locate the entry in |
861 | | // key 2 and interpolate the influence |
862 | 0 | const VertexPoseKeyFrame::PoseRefList& poseList1 = vkf1->getPoseReferences(); |
863 | 0 | const VertexPoseKeyFrame::PoseRefList& poseList2 = vkf2->getPoseReferences(); |
864 | 0 | for (auto& p1 : poseList1) |
865 | 0 | { |
866 | 0 | float startInfluence = p1.influence; |
867 | 0 | float endInfluence = 0; |
868 | | // Search for entry in keyframe 2 list (if not there, will be 0) |
869 | 0 | for (auto& p2 : poseList2) |
870 | 0 | { |
871 | 0 | if (p1.poseIndex == p2.poseIndex) |
872 | 0 | { |
873 | 0 | endInfluence = p2.influence; |
874 | 0 | break; |
875 | 0 | } |
876 | 0 | } |
877 | | // Interpolate influence |
878 | 0 | float influence = startInfluence + t*(endInfluence - startInfluence); |
879 | | // Scale by animation weight |
880 | 0 | influence = weight * influence; |
881 | | // Get pose |
882 | 0 | assert (p1.poseIndex < poseList->size()); |
883 | 0 | Pose* pose = (*poseList)[p1.poseIndex]; |
884 | | // apply |
885 | 0 | applyPoseToVertexData(pose, data, influence); |
886 | 0 | } |
887 | | // Now deal with any poses in key 2 which are not in key 1 |
888 | 0 | for (auto& p2 : poseList2) |
889 | 0 | { |
890 | 0 | bool found = false; |
891 | 0 | for (auto& p1 : poseList1) |
892 | 0 | { |
893 | 0 | if (p1.poseIndex == p2.poseIndex) |
894 | 0 | { |
895 | 0 | found = true; |
896 | 0 | break; |
897 | 0 | } |
898 | 0 | } |
899 | 0 | if (!found) |
900 | 0 | { |
901 | | // Need to apply this pose too, scaled from 0 start |
902 | 0 | float influence = t * p2.influence; |
903 | | // Scale by animation weight |
904 | 0 | influence = weight * influence; |
905 | | // Get pose |
906 | 0 | assert (p2.poseIndex < poseList->size()); |
907 | 0 | const Pose* pose = (*poseList)[p2.poseIndex]; |
908 | | // apply |
909 | 0 | applyPoseToVertexData(pose, data, influence); |
910 | 0 | } |
911 | 0 | } // key 2 iteration |
912 | 0 | } // morph or pose animation |
913 | 0 | } |
914 | | //----------------------------------------------------------------------------- |
915 | | void VertexAnimationTrack::applyPoseToVertexData(const Pose* pose, |
916 | | VertexData* data, float influence) |
917 | 0 | { |
918 | 0 | if (mTargetMode == TM_HARDWARE) |
919 | 0 | { |
920 | | // Hardware |
921 | | // If target mode is hardware, need to bind our pose buffer |
922 | | // to a target texcoord |
923 | 0 | assert(!data->hwAnimationDataList.empty() && |
924 | 0 | "Haven't set up hardware vertex animation elements!"); |
925 | | // no use for TempBlendedBufferInfo here btw |
926 | | // Set pose target as required |
927 | 0 | size_t hwIndex = data->hwAnimDataItemsUsed++; |
928 | | // If we try to use too many poses, ignore extras |
929 | 0 | if (hwIndex < data->hwAnimationDataList.size()) |
930 | 0 | { |
931 | 0 | VertexData::HardwareAnimationData& animData = data->hwAnimationDataList[hwIndex]; |
932 | 0 | data->vertexBufferBinding->setBinding( |
933 | 0 | animData.targetBufferIndex, |
934 | 0 | pose->_getHardwareVertexBuffer(data)); |
935 | | // save final influence in parametric |
936 | 0 | animData.parametric = influence; |
937 | |
|
938 | 0 | } |
939 | 0 | } |
940 | 0 | else |
941 | 0 | { |
942 | | // Software |
943 | 0 | Mesh::softwareVertexPoseBlend(influence, pose->getVertexOffsets(), pose->getNormals(), data); |
944 | 0 | } |
945 | |
|
946 | 0 | } |
947 | | //-------------------------------------------------------------------------- |
948 | | VertexMorphKeyFrame* VertexAnimationTrack::getVertexMorphKeyFrame(unsigned short index) const |
949 | 0 | { |
950 | 0 | OgreAssert(mAnimationType == VAT_MORPH, "Type mismatch"); |
951 | 0 | return static_cast<VertexMorphKeyFrame*>(getKeyFrame(index)); |
952 | 0 | } |
953 | | //-------------------------------------------------------------------------- |
954 | | VertexPoseKeyFrame* VertexAnimationTrack::getVertexPoseKeyFrame(unsigned short index) const |
955 | 0 | { |
956 | 0 | OgreAssert(mAnimationType == VAT_POSE, "Type mismatch"); |
957 | 0 | return static_cast<VertexPoseKeyFrame*>(getKeyFrame(index)); |
958 | 0 | } |
959 | | //-------------------------------------------------------------------------- |
960 | | KeyFrame* VertexAnimationTrack::createKeyFrameImpl(Real time) |
961 | 0 | { |
962 | 0 | switch(mAnimationType) |
963 | 0 | { |
964 | 0 | default: |
965 | 0 | case VAT_MORPH: |
966 | 0 | return OGRE_NEW VertexMorphKeyFrame(this, time); |
967 | 0 | case VAT_POSE: |
968 | 0 | return OGRE_NEW VertexPoseKeyFrame(this, time); |
969 | 0 | }; |
970 | |
|
971 | 0 | } |
972 | | //--------------------------------------------------------------------- |
973 | | bool VertexAnimationTrack::hasNonZeroKeyFrames(void) const |
974 | 0 | { |
975 | 0 | if (mAnimationType == VAT_MORPH) |
976 | 0 | { |
977 | 0 | return !mKeyFrames.empty(); |
978 | 0 | } |
979 | 0 | else |
980 | 0 | { |
981 | 0 | for (auto kf : mKeyFrames) |
982 | 0 | { |
983 | | // look for keyframes which have a pose influence which is non-zero |
984 | 0 | auto& poses = static_cast<const VertexPoseKeyFrame *>(kf)->getPoseReferences(); |
985 | 0 | for (auto& poseIt : poses) |
986 | 0 | { |
987 | 0 | if (poseIt.influence > 0.0f) |
988 | 0 | return true; |
989 | 0 | } |
990 | 0 | } |
991 | | |
992 | 0 | return false; |
993 | 0 | } |
994 | 0 | } |
995 | | //--------------------------------------------------------------------- |
996 | | void VertexAnimationTrack::optimise(void) |
997 | 0 | { |
998 | | // TODO - remove sequences of duplicate pose references? |
999 | |
|
1000 | 0 | } |
1001 | | //--------------------------------------------------------------------- |
1002 | | VertexAnimationTrack* VertexAnimationTrack::_clone(Animation* newParent) const |
1003 | 0 | { |
1004 | 0 | VertexAnimationTrack* newTrack = |
1005 | 0 | newParent->createVertexTrack(mHandle, mAnimationType); |
1006 | 0 | newTrack->mTargetMode = mTargetMode; |
1007 | 0 | populateClone(newTrack); |
1008 | 0 | return newTrack; |
1009 | 0 | } |
1010 | | //-------------------------------------------------------------------------- |
1011 | | void VertexAnimationTrack::_applyBaseKeyFrame(const KeyFrame* b) |
1012 | 0 | { |
1013 | 0 | const VertexPoseKeyFrame* base = static_cast<const VertexPoseKeyFrame*>(b); |
1014 | | |
1015 | 0 | for (auto& k : mKeyFrames) |
1016 | 0 | { |
1017 | 0 | VertexPoseKeyFrame* kf = static_cast<VertexPoseKeyFrame*>(k); |
1018 | | |
1019 | 0 | kf->_applyBaseKeyFrame(base); |
1020 | 0 | } |
1021 | 0 | } |
1022 | | } |