1/*
2Open Asset Import Library (assimp)
3----------------------------------------------------------------------
4
5Copyright (c) 2006-2017, assimp team
6
7All rights reserved.
8
9Redistribution and use of this software in source and binary forms,
10with or without modification, are permitted provided that the
11following conditions are met:
12
13* Redistributions of source code must retain the above
14 copyright notice, this list of conditions and the
15 following disclaimer.
16
17* Redistributions in binary form must reproduce the above
18 copyright notice, this list of conditions and the
19 following disclaimer in the documentation and/or other
20 materials provided with the distribution.
21
22* Neither the name of the assimp team, nor the names of its
23 contributors may be used to endorse or promote products
24 derived from this software without specific prior
25 written permission of the assimp team.
26
27THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
39----------------------------------------------------------------------
40*/
41
42/** @file LWOAnimation.cpp
43 * @brief LWOAnimationResolver utility class
44 *
45 * It's a very generic implementation of LightWave's system of
46 * componentwise-animated stuff. The one and only fully free
47 * implementation of LightWave envelopes of which I know.
48*/
49
50
51#if (!defined ASSIMP_BUILD_NO_LWO_IMPORTER) && (!defined ASSIMP_BUILD_NO_LWS_IMPORTER)
52
53#include <functional>
54
55// internal headers
56#include "LWOFileData.h"
57#include <assimp/anim.h>
58
59using namespace Assimp;
60using namespace Assimp::LWO;
61
62// ------------------------------------------------------------------------------------------------
63// Construct an animation resolver from a given list of envelopes
64AnimResolver::AnimResolver(std::list<Envelope>& _envelopes,double tick)
65 : envelopes (_envelopes)
66 , sample_rate (0.)
67 , envl_x(), envl_y(), envl_z()
68 , end_x(), end_y(), end_z()
69 , flags()
70 , sample_delta()
71{
72 trans_x = trans_y = trans_z = NULL;
73 rotat_x = rotat_y = rotat_z = NULL;
74 scale_x = scale_y = scale_z = NULL;
75
76 first = last = 150392.;
77
78 // find transformation envelopes
79 for (std::list<LWO::Envelope>::iterator it = envelopes.begin(); it != envelopes.end(); ++it) {
80
81 (*it).old_first = 0;
82 (*it).old_last = (*it).keys.size()-1;
83
84 if ((*it).keys.empty()) continue;
85 switch ((*it).type) {
86
87 // translation
88 case LWO::EnvelopeType_Position_X:
89 trans_x = &*it;break;
90 case LWO::EnvelopeType_Position_Y:
91 trans_y = &*it;break;
92 case LWO::EnvelopeType_Position_Z:
93 trans_z = &*it;break;
94
95 // rotation
96 case LWO::EnvelopeType_Rotation_Heading:
97 rotat_x = &*it;break;
98 case LWO::EnvelopeType_Rotation_Pitch:
99 rotat_y = &*it;break;
100 case LWO::EnvelopeType_Rotation_Bank:
101 rotat_z = &*it;break;
102
103 // scaling
104 case LWO::EnvelopeType_Scaling_X:
105 scale_x = &*it;break;
106 case LWO::EnvelopeType_Scaling_Y:
107 scale_y = &*it;break;
108 case LWO::EnvelopeType_Scaling_Z:
109 scale_z = &*it;break;
110 default:
111 continue;
112 };
113
114 // convert from seconds to ticks
115 for (std::vector<LWO::Key>::iterator d = (*it).keys.begin(); d != (*it).keys.end(); ++d)
116 (*d).time *= tick;
117
118 // set default animation range (minimum and maximum time value for which we have a keyframe)
119 first = std::min(first, (*it).keys.front().time );
120 last = std::max(last, (*it).keys.back().time );
121 }
122
123 // deferred setup of animation range to increase performance.
124 // typically the application will want to specify its own.
125 need_to_setup = true;
126}
127
128// ------------------------------------------------------------------------------------------------
129// Reset all envelopes to their original contents
130void AnimResolver::ClearAnimRangeSetup()
131{
132 for (std::list<LWO::Envelope>::iterator it = envelopes.begin(); it != envelopes.end(); ++it) {
133
134 (*it).keys.erase((*it).keys.begin(),(*it).keys.begin()+(*it).old_first);
135 (*it).keys.erase((*it).keys.begin()+(*it).old_last+1,(*it).keys.end());
136 }
137}
138
139// ------------------------------------------------------------------------------------------------
140// Insert additional keys to match LWO's pre& post behaviours.
141void AnimResolver::UpdateAnimRangeSetup()
142{
143 // XXX doesn't work yet (hangs if more than one envelope channels needs to be interpolated)
144
145 for (std::list<LWO::Envelope>::iterator it = envelopes.begin(); it != envelopes.end(); ++it) {
146 if ((*it).keys.empty()) continue;
147
148 const double my_first = (*it).keys.front().time;
149 const double my_last = (*it).keys.back().time;
150
151 const double delta = my_last-my_first;
152 const size_t old_size = (*it).keys.size();
153
154 const float value_delta = (*it).keys.back().value - (*it).keys.front().value;
155
156 // NOTE: We won't handle reset, linear and constant here.
157 // See DoInterpolation() for their implementation.
158
159 // process pre behaviour
160 switch ((*it).pre) {
161 case LWO::PrePostBehaviour_OffsetRepeat:
162 case LWO::PrePostBehaviour_Repeat:
163 case LWO::PrePostBehaviour_Oscillate:
164 {
165 const double start_time = delta - std::fmod(my_first-first,delta);
166 std::vector<LWO::Key>::iterator n = std::find_if((*it).keys.begin(),(*it).keys.end(),
167 [start_time](double t) { return start_time > t; }),m;
168
169 size_t ofs = 0;
170 if (n != (*it).keys.end()) {
171 // copy from here - don't use iterators, insert() would invalidate them
172 ofs = (*it).keys.end()-n;
173 (*it).keys.insert((*it).keys.begin(),ofs,LWO::Key());
174
175 std::copy((*it).keys.end()-ofs,(*it).keys.end(),(*it).keys.begin());
176 }
177
178 // do full copies. again, no iterators
179 const unsigned int num = (unsigned int)((my_first-first) / delta);
180 (*it).keys.resize((*it).keys.size() + num*old_size);
181
182 n = (*it).keys.begin()+ofs;
183 bool reverse = false;
184 for (unsigned int i = 0; i < num; ++i) {
185 m = n+old_size*(i+1);
186 std::copy(n,n+old_size,m);
187
188 if ((*it).pre == LWO::PrePostBehaviour_Oscillate && (reverse = !reverse))
189 std::reverse(m,m+old_size-1);
190 }
191
192 // update time values
193 n = (*it).keys.end() - (old_size+1);
194 double cur_minus = delta;
195 unsigned int tt = 1;
196 for (const double tmp = delta*(num+1);cur_minus <= tmp;cur_minus += delta,++tt) {
197 m = (delta == tmp ? (*it).keys.begin() : n - (old_size+1));
198 for (;m != n; --n) {
199 (*n).time -= cur_minus;
200
201 // offset repeat? add delta offset to key value
202 if ((*it).pre == LWO::PrePostBehaviour_OffsetRepeat) {
203 (*n).value += tt * value_delta;
204 }
205 }
206 }
207 break;
208 }
209 default:
210 // silence compiler warning
211 break;
212 }
213
214 // process post behaviour
215 switch ((*it).post) {
216
217 case LWO::PrePostBehaviour_OffsetRepeat:
218 case LWO::PrePostBehaviour_Repeat:
219 case LWO::PrePostBehaviour_Oscillate:
220
221 break;
222
223 default:
224 // silence compiler warning
225 break;
226 }
227 }
228}
229
230// ------------------------------------------------------------------------------------------------
231// Extract bind pose matrix
232void AnimResolver::ExtractBindPose(aiMatrix4x4& out)
233{
234 // If we have no envelopes, return identity
235 if (envelopes.empty()) {
236 out = aiMatrix4x4();
237 return;
238 }
239 aiVector3D angles, scaling(1.f,1.f,1.f), translation;
240
241 if (trans_x) translation.x = trans_x->keys[0].value;
242 if (trans_y) translation.y = trans_y->keys[0].value;
243 if (trans_z) translation.z = trans_z->keys[0].value;
244
245 if (rotat_x) angles.x = rotat_x->keys[0].value;
246 if (rotat_y) angles.y = rotat_y->keys[0].value;
247 if (rotat_z) angles.z = rotat_z->keys[0].value;
248
249 if (scale_x) scaling.x = scale_x->keys[0].value;
250 if (scale_y) scaling.y = scale_y->keys[0].value;
251 if (scale_z) scaling.z = scale_z->keys[0].value;
252
253 // build the final matrix
254 aiMatrix4x4 s,rx,ry,rz,t;
255 aiMatrix4x4::RotationZ(angles.z, rz);
256 aiMatrix4x4::RotationX(angles.y, rx);
257 aiMatrix4x4::RotationY(angles.x, ry);
258 aiMatrix4x4::Translation(translation,t);
259 aiMatrix4x4::Scaling(scaling,s);
260 out = t*ry*rx*rz*s;
261}
262
263// ------------------------------------------------------------------------------------------------
264// Do a single interpolation on a channel
265void AnimResolver::DoInterpolation(std::vector<LWO::Key>::const_iterator cur,
266 LWO::Envelope* envl,double time, float& fill)
267{
268 if (envl->keys.size() == 1) {
269 fill = envl->keys[0].value;
270 return;
271 }
272
273 // check whether we're at the beginning of the animation track
274 if (cur == envl->keys.begin()) {
275
276 // ok ... this depends on pre behaviour now
277 // we don't need to handle repeat&offset repeat&oszillate here, see UpdateAnimRangeSetup()
278 switch (envl->pre)
279 {
280 case LWO::PrePostBehaviour_Linear:
281 DoInterpolation2(cur,cur+1,time,fill);
282 return;
283
284 case LWO::PrePostBehaviour_Reset:
285 fill = 0.f;
286 return;
287
288 default : //case LWO::PrePostBehaviour_Constant:
289 fill = (*cur).value;
290 return;
291 }
292 }
293 // check whether we're at the end of the animation track
294 else if (cur == envl->keys.end()-1 && time > envl->keys.rbegin()->time) {
295 // ok ... this depends on post behaviour now
296 switch (envl->post)
297 {
298 case LWO::PrePostBehaviour_Linear:
299 DoInterpolation2(cur,cur-1,time,fill);
300 return;
301
302 case LWO::PrePostBehaviour_Reset:
303 fill = 0.f;
304 return;
305
306 default : //case LWO::PrePostBehaviour_Constant:
307 fill = (*cur).value;
308 return;
309 }
310 }
311
312 // Otherwise do a simple interpolation
313 DoInterpolation2(cur-1,cur,time,fill);
314}
315
316// ------------------------------------------------------------------------------------------------
317// Almost the same, except we won't handle pre/post conditions here
318void AnimResolver::DoInterpolation2(std::vector<LWO::Key>::const_iterator beg,
319 std::vector<LWO::Key>::const_iterator end,double time, float& fill)
320{
321 switch ((*end).inter) {
322
323 case LWO::IT_STEP:
324 // no interpolation at all - take the value of the last key
325 fill = (*beg).value;
326 return;
327 default:
328
329 // silence compiler warning
330 break;
331 }
332 // linear interpolation - default
333 double duration = (*end).time - (*beg).time;
334 if (duration > 0.0) {
335 fill = (*beg).value + ((*end).value - (*beg).value)*(float)(((time - (*beg).time) / duration));
336 } else {
337 fill = (*beg).value;
338 }
339}
340
341// ------------------------------------------------------------------------------------------------
342// Subsample animation track by given key values
343void AnimResolver::SubsampleAnimTrack(std::vector<aiVectorKey>& /*out*/,
344 double /*time*/ ,double /*sample_delta*/ )
345{
346 //ai_assert(out.empty() && sample_delta);
347
348 //const double time_start = out.back().mTime;
349// for ()
350}
351
352// ------------------------------------------------------------------------------------------------
353// Track interpolation
354void AnimResolver::InterpolateTrack(std::vector<aiVectorKey>& out,aiVectorKey& fill,double time)
355{
356 // subsample animation track?
357 if (flags & AI_LWO_ANIM_FLAG_SAMPLE_ANIMS) {
358 SubsampleAnimTrack(out,time, sample_delta);
359 }
360
361 fill.mTime = time;
362
363 // get x
364 if ((*cur_x).time == time) {
365 fill.mValue.x = (*cur_x).value;
366
367 if (cur_x != envl_x->keys.end()-1) /* increment x */
368 ++cur_x;
369 else end_x = true;
370 }
371 else DoInterpolation(cur_x,envl_x,time,(float&)fill.mValue.x);
372
373 // get y
374 if ((*cur_y).time == time) {
375 fill.mValue.y = (*cur_y).value;
376
377 if (cur_y != envl_y->keys.end()-1) /* increment y */
378 ++cur_y;
379 else end_y = true;
380 }
381 else DoInterpolation(cur_y,envl_y,time,(float&)fill.mValue.y);
382
383 // get z
384 if ((*cur_z).time == time) {
385 fill.mValue.z = (*cur_z).value;
386
387 if (cur_z != envl_z->keys.end()-1) /* increment z */
388 ++cur_z;
389 else end_x = true;
390 }
391 else DoInterpolation(cur_z,envl_z,time,(float&)fill.mValue.z);
392}
393
394// ------------------------------------------------------------------------------------------------
395// Build linearly subsampled keys from three single envelopes, one for each component (x,y,z)
396void AnimResolver::GetKeys(std::vector<aiVectorKey>& out,
397 LWO::Envelope* _envl_x,
398 LWO::Envelope* _envl_y,
399 LWO::Envelope* _envl_z,
400 unsigned int _flags)
401{
402 envl_x = _envl_x;
403 envl_y = _envl_y;
404 envl_z = _envl_z;
405 flags = _flags;
406
407 // generate default channels if none are given
408 LWO::Envelope def_x, def_y, def_z;
409 LWO::Key key_dummy;
410 key_dummy.time = 0.f;
411 if ((envl_x && envl_x->type == LWO::EnvelopeType_Scaling_X) ||
412 (envl_y && envl_y->type == LWO::EnvelopeType_Scaling_Y) ||
413 (envl_z && envl_z->type == LWO::EnvelopeType_Scaling_Z)) {
414 key_dummy.value = 1.f;
415 }
416 else key_dummy.value = 0.f;
417
418 if (!envl_x) {
419 envl_x = &def_x;
420 envl_x->keys.push_back(key_dummy);
421 }
422 if (!envl_y) {
423 envl_y = &def_y;
424 envl_y->keys.push_back(key_dummy);
425 }
426 if (!envl_z) {
427 envl_z = &def_z;
428 envl_z->keys.push_back(key_dummy);
429 }
430
431 // guess how many keys we'll get
432 size_t reserve;
433 double sr = 1.;
434 if (flags & AI_LWO_ANIM_FLAG_SAMPLE_ANIMS) {
435 if (!sample_rate)
436 sr = 100.f;
437 else sr = sample_rate;
438 sample_delta = 1.f / sr;
439
440 reserve = (size_t)(
441 std::max( envl_x->keys.rbegin()->time,
442 std::max( envl_y->keys.rbegin()->time, envl_z->keys.rbegin()->time )) * sr);
443 }
444 else reserve = std::max(envl_x->keys.size(),std::max(envl_x->keys.size(),envl_z->keys.size()));
445 out.reserve(reserve+(reserve>>1));
446
447 // Iterate through all three arrays at once - it's tricky, but
448 // rather interesting to implement.
449 cur_x = envl_x->keys.begin();
450 cur_y = envl_y->keys.begin();
451 cur_z = envl_z->keys.begin();
452
453 end_x = end_y = end_z = false;
454 while (1) {
455
456 aiVectorKey fill;
457
458 if ((*cur_x).time == (*cur_y).time && (*cur_x).time == (*cur_z).time ) {
459
460 // we have a keyframe for all of them defined .. this means
461 // we don't need to interpolate here.
462 fill.mTime = (*cur_x).time;
463
464 fill.mValue.x = (*cur_x).value;
465 fill.mValue.y = (*cur_y).value;
466 fill.mValue.z = (*cur_z).value;
467
468 // subsample animation track
469 if (flags & AI_LWO_ANIM_FLAG_SAMPLE_ANIMS) {
470 //SubsampleAnimTrack(out,cur_x, cur_y, cur_z, d, sample_delta);
471 }
472 }
473
474 // Find key with lowest time value
475 else if ((*cur_x).time <= (*cur_y).time && !end_x) {
476
477 if ((*cur_z).time <= (*cur_x).time && !end_z) {
478 InterpolateTrack(out,fill,(*cur_z).time);
479 }
480 else {
481 InterpolateTrack(out,fill,(*cur_x).time);
482 }
483 }
484 else if ((*cur_z).time <= (*cur_y).time && !end_y) {
485 InterpolateTrack(out,fill,(*cur_y).time);
486 }
487 else if (!end_y) {
488 // welcome on the server, y
489 InterpolateTrack(out,fill,(*cur_y).time);
490 }
491 else {
492 // we have reached the end of at least 2 channels,
493 // only one is remaining. Extrapolate the 2.
494 if (end_y) {
495 InterpolateTrack(out,fill,(end_x ? (*cur_z) : (*cur_x)).time);
496 }
497 else if (end_x) {
498 InterpolateTrack(out,fill,(end_z ? (*cur_y) : (*cur_z)).time);
499 }
500 else { // if (end_z)
501 InterpolateTrack(out,fill,(end_y ? (*cur_x) : (*cur_y)).time);
502 }
503 }
504 double lasttime = fill.mTime;
505 out.push_back(fill);
506
507 if (lasttime >= (*cur_x).time) {
508 if (cur_x != envl_x->keys.end()-1)
509 ++cur_x;
510 else end_x = true;
511 }
512 if (lasttime >= (*cur_y).time) {
513 if (cur_y != envl_y->keys.end()-1)
514 ++cur_y;
515 else end_y = true;
516 }
517 if (lasttime >= (*cur_z).time) {
518 if (cur_z != envl_z->keys.end()-1)
519 ++cur_z;
520 else end_z = true;
521 }
522
523 if( end_x && end_y && end_z ) /* finished? */
524 break;
525 }
526
527 if (flags & AI_LWO_ANIM_FLAG_START_AT_ZERO) {
528 for (std::vector<aiVectorKey>::iterator it = out.begin(); it != out.end(); ++it)
529 (*it).mTime -= first;
530 }
531}
532
533// ------------------------------------------------------------------------------------------------
534// Extract animation channel
535void AnimResolver::ExtractAnimChannel(aiNodeAnim** out, unsigned int flags /*= 0*/)
536{
537 *out = NULL;
538
539
540 //FIXME: crashes if more than one component is animated at different timings, to be resolved.
541
542 // If we have no envelopes, return NULL
543 if (envelopes.empty()) {
544 return;
545 }
546
547 // We won't spawn an animation channel if we don't have at least one envelope with more than one keyframe defined.
548 const bool trans = ((trans_x && trans_x->keys.size() > 1) || (trans_y && trans_y->keys.size() > 1) || (trans_z && trans_z->keys.size() > 1));
549 const bool rotat = ((rotat_x && rotat_x->keys.size() > 1) || (rotat_y && rotat_y->keys.size() > 1) || (rotat_z && rotat_z->keys.size() > 1));
550 const bool scale = ((scale_x && scale_x->keys.size() > 1) || (scale_y && scale_y->keys.size() > 1) || (scale_z && scale_z->keys.size() > 1));
551 if (!trans && !rotat && !scale)
552 return;
553
554 // Allocate the output animation
555 aiNodeAnim* anim = *out = new aiNodeAnim();
556
557 // Setup default animation setup if necessary
558 if (need_to_setup) {
559 UpdateAnimRangeSetup();
560 need_to_setup = false;
561 }
562
563 // copy translation keys
564 if (trans) {
565 std::vector<aiVectorKey> keys;
566 GetKeys(keys,trans_x,trans_y,trans_z,flags);
567
568 anim->mPositionKeys = new aiVectorKey[ anim->mNumPositionKeys = static_cast<unsigned int>(keys.size()) ];
569 std::copy(keys.begin(),keys.end(),anim->mPositionKeys);
570 }
571
572 // copy rotation keys
573 if (rotat) {
574 std::vector<aiVectorKey> keys;
575 GetKeys(keys,rotat_x,rotat_y,rotat_z,flags);
576
577 anim->mRotationKeys = new aiQuatKey[ anim->mNumRotationKeys = static_cast<unsigned int>(keys.size()) ];
578
579 // convert heading, pitch, bank to quaternion
580 // mValue.x=Heading=Rot(Y), mValue.y=Pitch=Rot(X), mValue.z=Bank=Rot(Z)
581 // Lightwave's rotation order is ZXY
582 aiVector3D X(1.0,0.0,0.0);
583 aiVector3D Y(0.0,1.0,0.0);
584 aiVector3D Z(0.0,0.0,1.0);
585 for (unsigned int i = 0; i < anim->mNumRotationKeys; ++i) {
586 aiQuatKey& qk = anim->mRotationKeys[i];
587 qk.mTime = keys[i].mTime;
588 qk.mValue = aiQuaternion(Y,keys[i].mValue.x)*aiQuaternion(X,keys[i].mValue.y)*aiQuaternion(Z,keys[i].mValue.z);
589 }
590 }
591
592 // copy scaling keys
593 if (scale) {
594 std::vector<aiVectorKey> keys;
595 GetKeys(keys,scale_x,scale_y,scale_z,flags);
596
597 anim->mScalingKeys = new aiVectorKey[ anim->mNumScalingKeys = static_cast<unsigned int>(keys.size()) ];
598 std::copy(keys.begin(),keys.end(),anim->mScalingKeys);
599 }
600}
601
602
603#endif // no lwo or no lws
604