Coverage Report

Created: 2025-08-03 06:54

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