Coverage Report

Created: 2025-10-12 07:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ogre/OgreMain/src/OgreSkeleton.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 "OgreAnimationState.h"
30
#include "OgreSkeletonManager.h"
31
#include "OgreSkeletonSerializer.h"
32
// Just for logging
33
#include "OgreAnimationTrack.h"
34
#include "OgreKeyFrame.h"
35
36
37
namespace Ogre {
38
39
    //---------------------------------------------------------------------
40
    Skeleton::Skeleton()
41
0
        : Resource(),
42
0
        mBlendState(ANIMBLEND_AVERAGE),
43
0
        mManualBonesDirty(false)
44
0
    {
45
0
    }
46
    //---------------------------------------------------------------------
47
    Skeleton::Skeleton(ResourceManager* creator, const String& name, ResourceHandle handle,
48
        const String& group, bool isManual, ManualResourceLoader* loader) 
49
0
        : Resource(creator, name, handle, group, isManual, loader), 
50
0
        mBlendState(ANIMBLEND_AVERAGE)
51
        // set animation blending to weighted, not cumulative
52
0
    {
53
0
        if (createParamDictionary("Skeleton"))
54
0
        {
55
            // no custom params
56
0
        }
57
0
    }
58
    //---------------------------------------------------------------------
59
    Skeleton::~Skeleton()
60
0
    {
61
        // have to call this here reather than in Resource destructor
62
        // since calling virtual methods in base destructors causes crash
63
0
        unload(); 
64
0
    }
65
    //---------------------------------------------------------------------
66
    void Skeleton::prepareImpl(void)
67
0
    {
68
0
        SkeletonSerializer serializer;
69
70
0
        if (getCreator()->getVerbose())
71
0
            LogManager::getSingleton().stream() << "Skeleton: Loading " << mName;
72
73
0
        DataStreamPtr stream = ResourceGroupManager::getSingleton().openResource(mName, mGroup, this);
74
75
0
        serializer.importSkeleton(stream, this);
76
77
        // Load any linked skeletons
78
0
        for (auto& s : mLinkedSkeletonAnimSourceList)
79
0
        {
80
0
            s.pSkeleton = static_pointer_cast<Skeleton>(
81
0
                SkeletonManager::getSingleton().prepare(s.skeletonName, mGroup));
82
0
        }
83
0
    }
84
    //---------------------------------------------------------------------
85
    void Skeleton::unprepareImpl(void)
86
0
    {
87
        // destroy bones
88
0
        for (auto *b : mBoneList)
89
0
        {
90
0
            OGRE_DELETE b;
91
0
        }
92
0
        mBoneList.clear();
93
0
        mBoneListByName.clear();
94
0
        mRootBones.clear();
95
0
        mManualBones.clear();
96
0
        mManualBonesDirty = false;
97
98
        // Destroy animations
99
0
        for (auto& ai : mAnimationsList)
100
0
        {
101
0
            OGRE_DELETE ai.second;
102
0
        }
103
0
        mAnimationsList.clear();
104
105
        // Remove all linked skeletons
106
0
        mLinkedSkeletonAnimSourceList.clear();
107
0
    }
108
    //---------------------------------------------------------------------
109
    Bone* Skeleton::createBone(void)
110
0
    {
111
0
        return createBone(mBoneList.size());
112
0
    }
113
    //---------------------------------------------------------------------
114
    Bone* Skeleton::createBone(const String& name)
115
0
    {
116
0
        return createBone(name, mBoneList.size());
117
0
    }
118
    //---------------------------------------------------------------------
119
    Bone* Skeleton::createBone(unsigned short handle)
120
0
    {
121
0
        return createBone(StringUtil::format("Bone%u", handle), handle);
122
0
    }
123
    //---------------------------------------------------------------------
124
    Bone* Skeleton::createBone(const String& name, unsigned short handle)
125
0
    {
126
0
        OgreAssert(handle < OGRE_MAX_NUM_BONES, "Exceeded the maximum number of bones per skeleton");
127
        // Check handle not used
128
0
        if (handle < mBoneList.size() && mBoneList[handle] != NULL)
129
0
        {
130
0
            OGRE_EXCEPT(
131
0
                Exception::ERR_DUPLICATE_ITEM,
132
0
                "A bone with the handle " + StringConverter::toString(handle) + " already exists",
133
0
                "Skeleton::createBone" );
134
0
        }
135
        // Check name not used
136
0
        if (mBoneListByName.find(name) != mBoneListByName.end())
137
0
        {
138
0
            OGRE_EXCEPT(
139
0
                Exception::ERR_DUPLICATE_ITEM,
140
0
                "A bone with the name " + name + " already exists",
141
0
                "Skeleton::createBone" );
142
0
        }
143
0
        Bone* ret = OGRE_NEW Bone(name, handle, this);
144
0
        if (mBoneList.size() <= handle)
145
0
        {
146
0
            mBoneList.resize(handle+1);
147
0
        }
148
0
        mBoneList[handle] = ret;
149
0
        mBoneListByName[name] = ret;
150
0
        return ret;
151
0
    }
152
153
0
    const Skeleton::BoneList& Skeleton::getRootBones() const {
154
0
        if (mRootBones.empty())
155
0
        {
156
0
            deriveRootBone();
157
0
        }
158
159
0
        return mRootBones;
160
0
    }
161
162
    //---------------------------------------------------------------------
163
    void Skeleton::setAnimationState(const AnimationStateSet& animSet)
164
0
    {
165
        /* 
166
        Algorithm:
167
          1. Reset all bone positions
168
          2. Iterate per AnimationState, if enabled get Animation and call Animation::apply
169
        */
170
171
        // Reset bones
172
0
        reset();
173
174
0
        Real weightFactor = 1.0f;
175
0
        if (mBlendState == ANIMBLEND_AVERAGE)
176
0
        {
177
            // Derive total weights so we can rebalance if > 1.0f
178
0
            Real totalWeights = 0.0f;
179
0
            EnabledAnimationStateList::const_iterator animIt;
180
0
            for(animIt = animSet.getEnabledAnimationStates().begin(); animIt != animSet.getEnabledAnimationStates().end(); ++animIt)
181
0
            {
182
0
                const AnimationState* animState = *animIt;
183
                // Make sure we have an anim to match implementation
184
0
                const LinkedSkeletonAnimationSource* linked = 0;
185
0
                if (_getAnimationImpl(animState->getAnimationName(), &linked))
186
0
                {
187
0
                    totalWeights += animState->getWeight();
188
0
                }
189
0
            }
190
191
            // Allow < 1.0f, allows fade out of all anims if required 
192
0
            if (totalWeights > 1.0f)
193
0
            {
194
0
                weightFactor = 1.0f / totalWeights;
195
0
            }
196
0
        }
197
198
        // Per enabled animation state
199
0
        for(auto *animState : animSet.getEnabledAnimationStates())
200
0
        {
201
0
            const LinkedSkeletonAnimationSource* linked = 0;
202
0
            Animation* anim = _getAnimationImpl(animState->getAnimationName(), &linked);
203
            // tolerate state entries for animations we're not aware of
204
0
            if (anim)
205
0
            {
206
0
              if(animState->hasBlendMask())
207
0
              {
208
0
                anim->apply(this, animState->getTimePosition(), animState->getWeight() * weightFactor,
209
0
                  animState->getBlendMask(), linked ? linked->scale : 1.0f);
210
0
              }
211
0
              else
212
0
              {
213
0
                anim->apply(this, animState->getTimePosition(), 
214
0
                  animState->getWeight() * weightFactor, linked ? linked->scale : 1.0f);
215
0
              }
216
0
            }
217
0
        }
218
219
220
0
    }
221
    //---------------------------------------------------------------------
222
    void Skeleton::setBindingPose(void)
223
0
    {
224
        // Update the derived transforms
225
0
        _updateTransforms();
226
227
0
        for (auto *b : mBoneList)
228
0
        {            
229
0
            b->setBindingPose();
230
0
        }
231
0
    }
232
    //---------------------------------------------------------------------
233
    void Skeleton::reset(bool resetManualBones)
234
0
    {
235
0
        for (auto *b : mBoneList)
236
0
        {
237
0
            if(!b->isManuallyControlled() || resetManualBones)
238
0
                b->reset();
239
0
        }
240
0
    }
241
    //---------------------------------------------------------------------
242
    Animation* Skeleton::createAnimation(const String& name, Real length)
243
0
    {
244
        // Check name not used
245
0
        if (mAnimationsList.find(name) != mAnimationsList.end())
246
0
        {
247
0
            OGRE_EXCEPT(
248
0
                Exception::ERR_DUPLICATE_ITEM,
249
0
                "An animation with the name " + name + " already exists",
250
0
                "Skeleton::createAnimation");
251
0
        }
252
253
0
        Animation* ret = OGRE_NEW Animation(name, length);
254
0
        ret->_notifyContainer(this);
255
256
        // Add to list
257
0
        mAnimationsList[name] = ret;
258
259
0
        return ret;
260
261
0
    }
262
    //---------------------------------------------------------------------
263
    Animation* Skeleton::getAnimation(const String& name, 
264
        const LinkedSkeletonAnimationSource** linker) const
265
0
    {
266
0
        Animation* ret = _getAnimationImpl(name, linker);
267
0
        if (!ret)
268
0
        {
269
0
            OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "No animation entry found named " + name, 
270
0
                "Skeleton::getAnimation");
271
0
        }
272
273
0
        return ret;
274
0
    }
275
    //---------------------------------------------------------------------
276
    Animation* Skeleton::getAnimation(const String& name) const
277
0
    {
278
0
        return getAnimation(name, 0);
279
0
    }
280
    //---------------------------------------------------------------------
281
    bool Skeleton::hasAnimation(const String& name) const
282
0
    {
283
0
        return _getAnimationImpl(name) != 0;
284
0
    }
285
    //---------------------------------------------------------------------
286
    Animation* Skeleton::_getAnimationImpl(const String& name, 
287
        const LinkedSkeletonAnimationSource** linker) const
288
0
    {
289
0
        Animation* ret = 0;
290
0
        AnimationList::const_iterator i = mAnimationsList.find(name);
291
292
0
        if (i == mAnimationsList.end())
293
0
        {
294
0
            LinkedSkeletonAnimSourceList::const_iterator it;
295
0
            for (it = mLinkedSkeletonAnimSourceList.begin(); 
296
0
                it != mLinkedSkeletonAnimSourceList.end() && !ret; ++it)
297
0
            {
298
0
                if (it->pSkeleton)
299
0
                {
300
0
                    ret = it->pSkeleton->_getAnimationImpl(name);
301
0
                    if (ret && linker)
302
0
                    {
303
0
                        *linker = &(*it);
304
0
                    }
305
306
0
                }
307
0
            }
308
309
0
        }
310
0
        else
311
0
        {
312
0
            if (linker)
313
0
                *linker = 0;
314
0
            ret = i->second;
315
0
        }
316
317
0
        return ret;
318
319
0
    }
320
    //---------------------------------------------------------------------
321
    void Skeleton::removeAnimation(const String& name)
322
0
    {
323
0
        AnimationList::iterator i = mAnimationsList.find(name);
324
325
0
        if (i == mAnimationsList.end())
326
0
        {
327
0
            OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "No animation entry found named " + name, 
328
0
            "Skeleton::getAnimation");
329
0
        }
330
331
0
        OGRE_DELETE i->second;
332
333
0
        mAnimationsList.erase(i);
334
335
0
    }
336
    //-----------------------------------------------------------------------
337
    void Skeleton::_initAnimationState(AnimationStateSet* animSet)
338
0
    {
339
0
        animSet->removeAllAnimationStates();
340
           
341
0
        for (auto& a : mAnimationsList)
342
0
        {
343
0
            Animation* anim = a.second;
344
            // Create animation at time index 0, default params mean this has weight 1 and is disabled
345
0
            const String& animName = anim->getName();
346
0
            animSet->createAnimationState(animName, 0.0, anim->getLength());
347
0
        }
348
349
        // Also iterate over linked animation
350
0
        for (auto& li : mLinkedSkeletonAnimSourceList)
351
0
        {
352
0
            if (li.pSkeleton)
353
0
            {
354
0
                li.pSkeleton->_refreshAnimationState(animSet);
355
0
            }
356
0
        }
357
0
    }
358
    //-----------------------------------------------------------------------
359
    void Skeleton::_refreshAnimationState(AnimationStateSet* animSet)
360
0
    {
361
        // Merge in any new animations
362
0
        for (auto& a : mAnimationsList)
363
0
        {
364
0
            Animation* anim = a.second;
365
            // Create animation at time index 0, default params mean this has weight 1 and is disabled
366
0
            const String& animName = anim->getName();
367
0
            if (!animSet->hasAnimationState(animName))
368
0
            {
369
0
                animSet->createAnimationState(animName, 0.0, anim->getLength());
370
0
            }
371
0
            else
372
0
            {
373
                // Update length incase changed
374
0
                AnimationState* animState = animSet->getAnimationState(animName);
375
0
                animState->setLength(anim->getLength());
376
0
                animState->setTimePosition(std::min(anim->getLength(), animState->getTimePosition()));
377
0
            }
378
0
        }
379
        // Also iterate over linked animation
380
0
        for (auto& li : mLinkedSkeletonAnimSourceList)
381
0
        {
382
0
            if (li.pSkeleton)
383
0
            {
384
0
                li.pSkeleton->_refreshAnimationState(animSet);
385
0
            }
386
0
        }
387
0
    }
388
    //-----------------------------------------------------------------------
389
    void Skeleton::_notifyManualBonesDirty(void)
390
0
    {
391
0
        mManualBonesDirty = true;
392
0
    }
393
    //-----------------------------------------------------------------------
394
    void Skeleton::_notifyManualBoneStateChange(Bone* bone)
395
0
    {
396
0
        if (bone->isManuallyControlled())
397
0
            mManualBones.insert(bone);
398
0
        else
399
0
            mManualBones.erase(bone);
400
0
    }
401
    //-----------------------------------------------------------------------
402
    void Skeleton::_getBoneMatrices(Affine3* pMatrices)
403
0
    {
404
        // Update derived transforms
405
0
        _updateTransforms();
406
407
        /*
408
            Calculating the bone matrices
409
            -----------------------------
410
            Now that we have the derived scaling factors, orientations & positions in the
411
            Bone nodes, we have to compute the Matrix4 to apply to the vertices of a mesh.
412
            Because any modification of a vertex has to be relative to the bone, we must
413
            first reverse transform by the Bone's original derived position/orientation/scale,
414
            then transform by the new derived position/orientation/scale.
415
            Also note we combine scale as equivalent axes, no shearing.
416
        */
417
418
0
        for (auto *b : mBoneList)
419
0
        {
420
0
            b->_getOffsetTransform(*pMatrices);
421
0
            pMatrices++;
422
0
        }
423
424
0
    }
425
    //---------------------------------------------------------------------
426
    unsigned short Skeleton::getNumAnimations(void) const
427
0
    {
428
0
        return (unsigned short)mAnimationsList.size();
429
0
    }
430
    //---------------------------------------------------------------------
431
    Animation* Skeleton::getAnimation(unsigned short index) const
432
0
    {
433
        // If you hit this assert, then the index is out of bounds.
434
0
        assert( index < mAnimationsList.size() );
435
436
0
        AnimationList::const_iterator i = mAnimationsList.begin();
437
438
0
        std::advance(i, index);
439
440
0
        return i->second;
441
0
    }
442
    //---------------------------------------------------------------------
443
    Bone* Skeleton::getBone(const String& name) const
444
0
    {
445
0
        BoneListByName::const_iterator i = mBoneListByName.find(name);
446
447
0
        if (i == mBoneListByName.end())
448
0
        {
449
0
            OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "Bone named '" + name + "' not found.", 
450
0
                "Skeleton::getBone");
451
0
        }
452
453
0
        return i->second;
454
455
0
    }
456
    //---------------------------------------------------------------------
457
    bool Skeleton::hasBone(const String& name) const 
458
0
    {   
459
0
        return mBoneListByName.find(name) != mBoneListByName.end();
460
0
    }
461
    //---------------------------------------------------------------------
462
    void Skeleton::deriveRootBone(void) const
463
0
    {
464
        // Start at the first bone and work up
465
0
        OgreAssert(!mBoneList.empty(), "Cannot derive root bone as this skeleton has no bones");
466
467
0
        mRootBones.clear();
468
469
0
        for (auto *b : mBoneList)
470
0
        {
471
0
            if (b->getParent() == 0)
472
0
            {
473
                // This is a root
474
0
                mRootBones.push_back(b);
475
0
            }
476
0
        }
477
0
    }
478
    //---------------------------------------------------------------------
479
    std::ostream& operator<<(std::ostream& o, const Skeleton& s)
480
0
    {
481
0
        Quaternion q;
482
0
        Radian angle;
483
0
        Vector3 axis;
484
485
0
        o << "-= Debug output of skeleton " << s.mName << " =-" << std::endl << std::endl;
486
0
        o << "== Bones ==" << std::endl;
487
0
        o << "Number of bones: " << (unsigned int)s.mBoneList.size() << std::endl;
488
        
489
0
        for (auto *b : s.mBoneList)
490
0
        {
491
0
            o << "-- Bone " << b->getHandle() << " --" << std::endl;
492
0
            o << "Position: " << b->getPosition();
493
0
            q = b->getOrientation();
494
0
            o << "Rotation: " << q;
495
0
            q.ToAngleAxis(angle, axis);
496
0
            o << " = " << angle.valueRadians() << " radians around axis " << axis << std::endl << std::endl;
497
0
        }
498
499
0
        o << "== Animations ==" << std::endl;
500
0
        o << "Number of animations: " << (unsigned int)s.mAnimationsList.size() << std::endl;
501
502
0
        for (auto& a : s.mAnimationsList)
503
0
        {
504
0
            Animation* anim = a.second;
505
506
0
            o << "-- Animation '" << anim->getName() << "' (length " << anim->getLength() << ") --" << std::endl;
507
0
            o << "Number of tracks: " << anim->getNumNodeTracks() << std::endl;
508
509
0
            for (unsigned short ti = 0; ti < anim->getNumNodeTracks(); ++ti)
510
0
            {
511
0
                NodeAnimationTrack* track = anim->getNodeTrack(ti);
512
0
                o << "  -- AnimationTrack " << ti << " --" << std::endl;
513
0
                o << "  Affects bone: " << static_cast<Bone*>(track->getAssociatedNode())->getHandle() << std::endl;
514
0
                o << "  Number of keyframes: " << track->getNumKeyFrames() << std::endl;
515
516
0
                for (unsigned short ki = 0; ki < track->getNumKeyFrames(); ++ki)
517
0
                {
518
0
                    TransformKeyFrame* key = track->getNodeKeyFrame(ki);
519
0
                    o << "    -- KeyFrame " << ki << " --" << std::endl;
520
0
                    o << "    Time index: " << key->getTime();
521
0
                    o << "    Translation: " << key->getTranslate() << std::endl;
522
0
                    q = key->getRotation();
523
0
                    o << "    Rotation: " << q;
524
0
                    q.ToAngleAxis(angle, axis);
525
0
                    o << " = " << angle.valueRadians() << " radians around axis " << axis << std::endl;
526
0
                }
527
528
0
            }
529
0
        }
530
0
        return o;
531
0
    }
532
    //---------------------------------------------------------------------
533
    Skeleton::BoneIterator Skeleton::getRootBoneIterator(void)
534
0
    {
535
0
        if (mRootBones.empty())
536
0
        {
537
0
            deriveRootBone();
538
0
        }
539
0
        return BoneIterator(mRootBones.begin(), mRootBones.end());
540
0
    }
541
    //---------------------------------------------------------------------
542
    Skeleton::BoneIterator Skeleton::getBoneIterator(void)
543
0
    {
544
0
        return BoneIterator(mBoneList.begin(), mBoneList.end());
545
0
    }
546
    //---------------------------------------------------------------------
547
    void Skeleton::_updateTransforms(void)
548
0
    {
549
0
        for (auto *b : mRootBones)
550
0
        {
551
0
            b->_update(true, false);
552
0
        }
553
0
        mManualBonesDirty = false;
554
0
    }
555
    //---------------------------------------------------------------------
556
    void Skeleton::optimiseAllAnimations(bool preservingIdentityNodeTracks)
557
0
    {
558
0
        if (!preservingIdentityNodeTracks)
559
0
        {
560
0
            Animation::TrackHandleList tracksToDestroy;
561
562
            // Assume all node tracks are identity
563
0
            ushort numBones = getNumBones();
564
0
            for (ushort h = 0; h < numBones; ++h)
565
0
            {
566
0
                tracksToDestroy.insert(h);
567
0
            }
568
569
            // Collect identity node tracks for all animations
570
0
            for (auto& a : mAnimationsList)
571
0
            {
572
0
                a.second->_collectIdentityNodeTracks(tracksToDestroy);
573
0
            }
574
575
            // Destroy identity node tracks
576
0
            for (auto& a : mAnimationsList)
577
0
            {
578
0
                a.second->_destroyNodeTracks(tracksToDestroy);
579
0
            }
580
0
        }
581
582
0
        for (auto& a : mAnimationsList)
583
0
        {
584
            // Don't discard identity node tracks here
585
0
            a.second->optimise(false);
586
0
        }
587
0
    }
588
    //---------------------------------------------------------------------
589
    void Skeleton::addLinkedSkeletonAnimationSource(const String& skelName, 
590
        Real scale)
591
0
    {
592
        // Check not already linked
593
0
        for (auto& l : mLinkedSkeletonAnimSourceList)
594
0
        {
595
0
            if (skelName == l.skeletonName)
596
0
                return; // don't bother
597
0
        }
598
599
0
        if (isPrepared() || isLoaded())
600
0
        {
601
            // Load immediately
602
0
            SkeletonPtr skelPtr = static_pointer_cast<Skeleton>(
603
0
                SkeletonManager::getSingleton().prepare(skelName, mGroup));
604
0
            mLinkedSkeletonAnimSourceList.push_back(
605
0
                LinkedSkeletonAnimationSource(skelName, scale, skelPtr));
606
607
0
        }
608
0
        else
609
0
        {
610
            // Load later
611
0
            mLinkedSkeletonAnimSourceList.push_back(
612
0
                LinkedSkeletonAnimationSource(skelName, scale));
613
0
        }
614
615
0
    }
616
    //---------------------------------------------------------------------
617
    void Skeleton::removeAllLinkedSkeletonAnimationSources(void)
618
0
    {
619
0
        mLinkedSkeletonAnimSourceList.clear();
620
0
    }
621
    //---------------------------------------------------------------------
622
    Skeleton::LinkedSkeletonAnimSourceIterator 
623
    Skeleton::getLinkedSkeletonAnimationSourceIterator(void) const
624
0
    {
625
0
        return LinkedSkeletonAnimSourceIterator(
626
0
            mLinkedSkeletonAnimSourceList.begin(), 
627
0
            mLinkedSkeletonAnimSourceList.end());
628
0
    }
629
    //---------------------------------------------------------------------
630
    struct DeltaTransform
631
    {
632
        Vector3 translate;
633
        Quaternion rotate;
634
        Vector3 scale;
635
        bool isIdentity;
636
    };
637
    void Skeleton::_mergeSkeletonAnimations(const Skeleton* src,
638
        const BoneHandleMap& boneHandleMap,
639
        const StringVector& animations)
640
0
    {
641
0
        ushort handle;
642
643
0
        ushort numSrcBones = src->getNumBones();
644
0
        ushort numDstBones = this->getNumBones();
645
646
0
        OgreAssert(
647
0
            boneHandleMap.size() == numSrcBones,
648
0
            "Number of bones in the bone handle map must equal to number of bones in the source skeleton");
649
650
0
        bool existsMissingBone = false;
651
652
        // Check source skeleton structures compatible with ourself (that means
653
        // identically bones with identical handles, and with same hierarchy, but
654
        // not necessary to have same number of bones and bone names).
655
0
        for (handle = 0; handle < numSrcBones; ++handle)
656
0
        {
657
0
            const Bone* srcBone = src->getBone(handle);
658
0
            ushort dstHandle = boneHandleMap[handle];
659
660
            // Does it exists in target skeleton?
661
0
            if (dstHandle < numDstBones)
662
0
            {
663
0
                Bone* destBone = this->getBone(dstHandle);
664
665
                // Check both bones have identical parent, or both are root bone.
666
0
                const Bone* srcParent = static_cast<Bone*>(srcBone->getParent());
667
0
                Bone* destParent = static_cast<Bone*>(destBone->getParent());
668
0
                if ((srcParent || destParent) &&
669
0
                    (!srcParent || !destParent ||
670
0
                     boneHandleMap[srcParent->getHandle()] != destParent->getHandle()))
671
0
                {
672
0
                    OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS,
673
0
                        "Source skeleton incompatible with this skeleton: "
674
0
                        "difference hierarchy between bone '" + srcBone->getName() +
675
0
                        "' and '" + destBone->getName() + "'.",
676
0
                        "Skeleton::_mergeSkeletonAnimations");
677
0
                }
678
0
            }
679
0
            else
680
0
            {
681
0
                existsMissingBone = true;
682
0
            }
683
0
        }
684
685
        // Clone bones if need
686
0
        if (existsMissingBone)
687
0
        {
688
            // Create missing bones
689
0
            for (handle = 0; handle < numSrcBones; ++handle)
690
0
            {
691
0
                const Bone* srcBone = src->getBone(handle);
692
0
                ushort dstHandle = boneHandleMap[handle];
693
694
                // The bone is missing in target skeleton?
695
0
                if (dstHandle >= numDstBones)
696
0
                {
697
0
                    Bone* dstBone = this->createBone(srcBone->getName(), dstHandle);
698
                    // Sets initial transform
699
0
                    dstBone->setPosition(srcBone->getInitialPosition());
700
0
                    dstBone->setOrientation(srcBone->getInitialOrientation());
701
0
                    dstBone->setScale(srcBone->getInitialScale());
702
0
                    dstBone->setInitialState();
703
0
                }
704
0
            }
705
706
            // Link new bones to parent
707
0
            for (handle = 0; handle < numSrcBones; ++handle)
708
0
            {
709
0
                const Bone* srcBone = src->getBone(handle);
710
0
                ushort dstHandle = boneHandleMap[handle];
711
712
                // Is new bone?
713
0
                if (dstHandle >= numDstBones)
714
0
                {
715
0
                    const Bone* srcParent = static_cast<Bone*>(srcBone->getParent());
716
0
                    if (srcParent)
717
0
                    {
718
0
                        Bone* destParent = this->getBone(boneHandleMap[srcParent->getHandle()]);
719
0
                        Bone* dstBone = this->getBone(dstHandle);
720
0
                        destParent->addChild(dstBone);
721
0
                    }
722
0
                }
723
0
            }
724
725
            // Derive root bones in case it was changed
726
0
            this->deriveRootBone();
727
728
            // Reset binding pose for new bones
729
0
            this->reset(true);
730
0
            this->setBindingPose();
731
0
        }
732
733
        //
734
        // We need to adapt animations from source to target skeleton, but since source
735
        // and target skeleton bones bind transform might difference, so we need to alter
736
        // keyframes in source to suit to target skeleton.
737
        //
738
        // For any given animation time, formula:
739
        //
740
        //      LocalTransform = BindTransform * KeyFrame;
741
        //      DerivedTransform = ParentDerivedTransform * LocalTransform
742
        //
743
        // And all derived transforms should be keep identically after adapt to
744
        // target skeleton, Then:
745
        //
746
        //      DestDerivedTransform == SrcDerivedTransform
747
        //      DestParentDerivedTransform == SrcParentDerivedTransform
748
        // ==>
749
        //      DestLocalTransform = SrcLocalTransform
750
        // ==>
751
        //      DestBindTransform * DestKeyFrame = SrcBindTransform * SrcKeyFrame
752
        // ==>
753
        //      DestKeyFrame = inverse(DestBindTransform) * SrcBindTransform * SrcKeyFrame
754
        //
755
        // We define (inverse(DestBindTransform) * SrcBindTransform) as 'delta-transform' here.
756
        //
757
758
        // Calculate delta-transforms for all source bones.
759
0
        std::vector<DeltaTransform> deltaTransforms(numSrcBones);
760
0
        for (handle = 0; handle < numSrcBones; ++handle)
761
0
        {
762
0
            const Bone* srcBone = src->getBone(handle);
763
0
            DeltaTransform& deltaTransform = deltaTransforms[handle];
764
0
            ushort dstHandle = boneHandleMap[handle];
765
766
0
            if (dstHandle < numDstBones)
767
0
            {
768
                // Common bone, calculate delta-transform
769
770
0
                Bone* dstBone = this->getBone(dstHandle);
771
772
0
                deltaTransform.translate = srcBone->getInitialPosition() - dstBone->getInitialPosition();
773
0
                deltaTransform.rotate = dstBone->getInitialOrientation().Inverse() * srcBone->getInitialOrientation();
774
0
                deltaTransform.scale = srcBone->getInitialScale() / dstBone->getInitialScale();
775
776
                // Check whether or not delta-transform is identity
777
0
                const Real tolerance = 1e-3f;
778
0
                Vector3 axis;
779
0
                Radian angle;
780
0
                deltaTransform.rotate.ToAngleAxis(angle, axis);
781
0
                deltaTransform.isIdentity =
782
0
                    deltaTransform.translate.positionEquals(Vector3::ZERO, tolerance) &&
783
0
                    deltaTransform.scale.positionEquals(Vector3::UNIT_SCALE, tolerance) &&
784
0
                    Math::RealEqual(angle.valueRadians(), 0.0f, tolerance);
785
0
            }
786
0
            else
787
0
            {
788
                // New bone, the delta-transform is identity
789
790
0
                deltaTransform.translate = Vector3::ZERO;
791
0
                deltaTransform.rotate = Quaternion::IDENTITY;
792
0
                deltaTransform.scale = Vector3::UNIT_SCALE;
793
0
                deltaTransform.isIdentity = true;
794
0
            }
795
0
        }
796
797
        // Now copy animations
798
799
0
        ushort numAnimations;
800
0
        if (animations.empty())
801
0
            numAnimations = src->getNumAnimations();
802
0
        else
803
0
            numAnimations = static_cast<ushort>(animations.size());
804
0
        for (ushort i = 0; i < numAnimations; ++i)
805
0
        {
806
0
            const Animation* srcAnimation;
807
0
            if (animations.empty())
808
0
            {
809
                // Get animation of source skeleton by the given index
810
0
                srcAnimation = src->getAnimation(i);
811
0
            }
812
0
            else
813
0
            {
814
                // Get animation of source skeleton by the given name
815
0
                const LinkedSkeletonAnimationSource* linker;
816
0
                srcAnimation = src->_getAnimationImpl(animations[i], &linker);
817
0
                if (!srcAnimation || linker)
818
0
                {
819
0
                    OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND,
820
0
                        "No animation entry found named " + animations[i],
821
0
                        "Skeleton::_mergeSkeletonAnimations");
822
0
                }
823
0
            }
824
825
            // Create target animation
826
0
            Animation* dstAnimation = this->createAnimation(srcAnimation->getName(), srcAnimation->getLength());
827
828
            // Copy interpolation modes
829
0
            dstAnimation->setInterpolationMode(srcAnimation->getInterpolationMode());
830
0
            dstAnimation->setRotationInterpolationMode(srcAnimation->getRotationInterpolationMode());
831
832
            // Copy track for each bone
833
0
            for (handle = 0; handle < numSrcBones; ++handle)
834
0
            {
835
0
                const DeltaTransform& deltaTransform = deltaTransforms[handle];
836
0
                ushort dstHandle = boneHandleMap[handle];
837
838
0
                if (srcAnimation->hasNodeTrack(handle))
839
0
                {
840
                    // Clone track from source animation
841
842
0
                    const NodeAnimationTrack* srcTrack = srcAnimation->getNodeTrack(handle);
843
0
                    NodeAnimationTrack* dstTrack = dstAnimation->createNodeTrack(dstHandle, this->getBone(dstHandle));
844
0
                    dstTrack->setUseShortestRotationPath(srcTrack->getUseShortestRotationPath());
845
846
0
                    ushort numKeyFrames = srcTrack->getNumKeyFrames();
847
0
                    for (ushort k = 0; k < numKeyFrames; ++k)
848
0
                    {
849
0
                        const TransformKeyFrame* srcKeyFrame = srcTrack->getNodeKeyFrame(k);
850
0
                        TransformKeyFrame* dstKeyFrame = dstTrack->createNodeKeyFrame(srcKeyFrame->getTime());
851
852
                        // Adjust keyframes to match target binding pose
853
0
                        if (deltaTransform.isIdentity)
854
0
                        {
855
0
                            dstKeyFrame->setTranslate(srcKeyFrame->getTranslate());
856
0
                            dstKeyFrame->setRotation(srcKeyFrame->getRotation());
857
0
                            dstKeyFrame->setScale(srcKeyFrame->getScale());
858
0
                        }
859
0
                        else
860
0
                        {
861
0
                            dstKeyFrame->setTranslate(deltaTransform.translate + srcKeyFrame->getTranslate());
862
0
                            dstKeyFrame->setRotation(deltaTransform.rotate * srcKeyFrame->getRotation());
863
0
                            dstKeyFrame->setScale(deltaTransform.scale * srcKeyFrame->getScale());
864
0
                        }
865
0
                    }
866
0
                }
867
0
                else if (!deltaTransform.isIdentity)
868
0
                {
869
                    // Create 'static' track for this bone
870
871
0
                    NodeAnimationTrack* dstTrack = dstAnimation->createNodeTrack(dstHandle, this->getBone(dstHandle));
872
0
                    TransformKeyFrame* dstKeyFrame;
873
874
0
                    dstKeyFrame = dstTrack->createNodeKeyFrame(0);
875
0
                    dstKeyFrame->setTranslate(deltaTransform.translate);
876
0
                    dstKeyFrame->setRotation(deltaTransform.rotate);
877
0
                    dstKeyFrame->setScale(deltaTransform.scale);
878
879
0
                    dstKeyFrame = dstTrack->createNodeKeyFrame(dstAnimation->getLength());
880
0
                    dstKeyFrame->setTranslate(deltaTransform.translate);
881
0
                    dstKeyFrame->setRotation(deltaTransform.rotate);
882
0
                    dstKeyFrame->setScale(deltaTransform.scale);
883
0
                }
884
0
            }
885
0
        }
886
0
    }
887
    //---------------------------------------------------------------------
888
    void Skeleton::_buildMapBoneByHandle(const Skeleton* src,
889
        BoneHandleMap& boneHandleMap) const
890
0
    {
891
0
        ushort numSrcBones = src->getNumBones();
892
0
        boneHandleMap.resize(numSrcBones);
893
894
0
        for (ushort handle = 0; handle < numSrcBones; ++handle)
895
0
        {
896
0
            boneHandleMap[handle] = handle;
897
0
        }
898
0
    }
899
    //---------------------------------------------------------------------
900
    void Skeleton::_buildMapBoneByName(const Skeleton* src,
901
        BoneHandleMap& boneHandleMap) const
902
0
    {
903
0
        ushort numSrcBones = src->getNumBones();
904
0
        boneHandleMap.resize(numSrcBones);
905
906
0
        ushort newBoneHandle = this->getNumBones();
907
0
        for (ushort handle = 0; handle < numSrcBones; ++handle)
908
0
        {
909
0
            const Bone* srcBone = src->getBone(handle);
910
0
            BoneListByName::const_iterator i = this->mBoneListByName.find(srcBone->getName());
911
0
            if (i == mBoneListByName.end())
912
0
                boneHandleMap[handle] = newBoneHandle++;
913
0
            else
914
0
                boneHandleMap[handle] = i->second->getHandle();
915
0
        }
916
0
    }
917
918
    size_t Skeleton::calculateSize(void) const
919
0
    {
920
0
        size_t memSize = sizeof(*this);
921
0
        memSize += mBoneList.size() * sizeof(Bone);
922
0
        memSize += mRootBones.size() * sizeof(Bone);
923
0
        memSize += mBoneListByName.size() * (sizeof(String) + sizeof(Bone*));
924
0
        memSize += mAnimationsList.size() * (sizeof(String) + sizeof(Animation*));
925
0
        memSize += mManualBones.size() * sizeof(Bone*);
926
0
        memSize += mLinkedSkeletonAnimSourceList.size() * sizeof(LinkedSkeletonAnimationSource);
927
928
0
        return memSize;
929
0
    }
930
}