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