Coverage Report

Created: 2026-08-13 06:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ogre/OgreMain/src/OgreSkeletonSerializer.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
30
#include "OgreSkeletonFileFormat.h"
31
#include "OgreSkeletonSerializer.h"
32
#include "OgreAnimation.h"
33
#include "OgreAnimationTrack.h"
34
#include "OgreKeyFrame.h"
35
36
namespace Ogre {
37
    /// stream overhead = ID + size
38
    const long SSTREAM_OVERHEAD_SIZE = sizeof(uint16) + sizeof(uint32);
39
    const uint16 HEADER_STREAM_ID_EXT = 0x1000;
40
    //---------------------------------------------------------------------
41
    SkeletonSerializer::SkeletonSerializer()
42
27.1k
    {
43
        // Version number
44
        // NB changed to include bone names in 1.1
45
27.1k
        mVersion = "[Unknown]";
46
27.1k
    }
47
48
    //---------------------------------------------------------------------
49
    void SkeletonSerializer::exportSkeleton(const Skeleton* pSkeleton, 
50
        const String& filename, SkeletonVersion ver, Endian endianMode)
51
0
    {
52
0
        DataStreamPtr stream = _openFileStream(filename, std::ios::binary | std::ios::out);
53
0
        exportSkeleton(pSkeleton, stream, ver, endianMode);
54
55
0
        stream->close();
56
0
    }
57
    //---------------------------------------------------------------------
58
    void SkeletonSerializer::exportSkeleton(const Skeleton* pSkeleton, 
59
        const DataStreamPtr& stream, SkeletonVersion ver, Endian endianMode)
60
0
    {
61
0
        setWorkingVersion(ver);
62
        // Decide on endian mode
63
0
        determineEndianness(endianMode);
64
65
0
        mStream = stream; 
66
0
        if (!stream->isWriteable())
67
0
        {
68
0
            OGRE_EXCEPT(Exception::ERR_CANNOT_WRITE_TO_FILE,
69
0
                "Unable to write to stream " + stream->getName(),
70
0
                "SkeletonSerializer::exportSkeleton");
71
0
        }
72
73
74
0
        writeFileHeader();
75
76
0
        pushInnerChunk(mStream);
77
        // Write main skeleton data
78
0
        LogManager::getSingleton().logMessage("Exporting bones..");
79
0
        writeSkeleton(pSkeleton, ver);
80
0
        LogManager::getSingleton().logMessage("Bones exported.");
81
82
        // Write all animations
83
0
        unsigned short numAnims = pSkeleton->getNumAnimations();
84
0
        LogManager::getSingleton().stream()
85
0
            << "Exporting animations, count=" << numAnims;
86
0
        for (unsigned short i = 0; i < numAnims; ++i)
87
0
        {
88
0
            Animation* pAnim = pSkeleton->getAnimation(i);
89
0
            LogManager::getSingleton().stream()
90
0
                << "Exporting animation: " << pAnim->getName();
91
0
            writeAnimation(pSkeleton, pAnim, ver);
92
0
            LogManager::getSingleton().logMessage("Animation exported.");
93
94
0
        }
95
96
        // Write links
97
0
        for(const auto& link : pSkeleton->getLinkedSkeletonAnimationSources())
98
0
        {
99
0
            writeSkeletonAnimationLink(pSkeleton, link);
100
0
        }       
101
0
        popInnerChunk(stream);
102
0
    }
103
    //---------------------------------------------------------------------
104
    void SkeletonSerializer::importSkeleton(DataStreamPtr& stream, Skeleton* pSkel)
105
998
    {
106
        // Determine endianness (must be the first thing we do!)
107
998
        determineEndianness(stream);
108
109
        // Check header
110
998
        unsigned short headerID;
111
998
        readShorts(stream, &headerID, 1);
112
998
        if (headerID != HEADER_STREAM_ID_EXT)
113
0
        {
114
0
            OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR, "File header not found",
115
0
                "SkeletonSerializer::importSkeleton");
116
0
        }
117
118
        // Read version
119
998
        String ver = readString(stream);
120
998
        if ((ver != "[Serializer_v1.10]") &&
121
941
            (ver != "[Serializer_v1.80]"))
122
112
        {
123
112
            OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR,
124
112
                "Invalid file: version incompatible, file reports " + String(ver),
125
112
                "SkeletonSerializer::importSkeleton");
126
112
        }
127
886
        mVersion = ver;
128
129
886
        pushInnerChunk(stream);
130
886
        unsigned short streamID = readChunk(stream);
131
132
4.31k
        while(!stream->eof())
133
3.64k
        {
134
3.64k
            switch (streamID)
135
3.64k
            {
136
229
            case SKELETON_BLENDMODE:
137
229
            {
138
                // Optional blend mode
139
229
                uint16 blendMode;
140
229
                readShorts(stream, &blendMode, 1);
141
229
                pSkel->setBlendMode(static_cast<SkeletonAnimationBlendMode>(blendMode));
142
229
                break;
143
0
            }
144
718
            case SKELETON_BONE:
145
718
                readBone(stream, pSkel);
146
718
                break;
147
35
            case SKELETON_BONE_PARENT:
148
35
                readBoneParent(stream, pSkel);
149
35
                break;
150
2.25k
            case SKELETON_ANIMATION:
151
2.25k
                readAnimation(stream, pSkel);
152
2.25k
                break;
153
105
            case SKELETON_ANIMATION_LINK:
154
105
                readSkeletonAnimationLink(stream, pSkel);
155
105
                break;
156
299
            default:
157
299
                break;
158
3.64k
            }
159
160
3.42k
            streamID = readChunk(stream);
161
3.42k
        }
162
        // Assume bones are stored in the binding pose
163
669
        pSkel->setBindingPose();
164
669
        popInnerChunk(stream);
165
166
669
    }
167
    
168
    //---------------------------------------------------------------------
169
    void SkeletonSerializer::setWorkingVersion(SkeletonVersion ver)
170
0
    {
171
0
        if (ver == SKELETON_VERSION_1_0)
172
0
            mVersion = "[Serializer_v1.10]";
173
0
        else mVersion = "[Serializer_v1.80]";
174
0
    }
175
    //---------------------------------------------------------------------
176
    void SkeletonSerializer::writeSkeleton(const Skeleton* pSkel, SkeletonVersion ver)
177
0
    {
178
        
179
        // Write blend mode
180
0
        if ((int)ver > (int)SKELETON_VERSION_1_0)
181
0
        {
182
0
            writeChunkHeader(SKELETON_BLENDMODE, SSTREAM_OVERHEAD_SIZE + sizeof(unsigned short));
183
0
            uint16 blendMode = static_cast<uint16>(pSkel->getBlendMode());
184
0
            writeShorts(&blendMode, 1);
185
0
        }
186
        
187
        // Write each bone
188
0
        for (Bone* pBone : pSkel->getBones())
189
0
        {
190
0
            writeBone(pSkel, pBone);
191
0
        }
192
        // Write parents
193
0
        for (Bone* pBone : pSkel->getBones())
194
0
        {
195
0
            unsigned short handle = pBone->getHandle();
196
0
            Bone* pParent = static_cast<Bone*>(pBone->getParent());
197
0
            if (pParent != NULL) 
198
0
            {
199
0
                writeBoneParent(pSkel, handle, pParent->getHandle());             
200
0
            }
201
0
        }
202
0
    }
203
    //---------------------------------------------------------------------
204
    void SkeletonSerializer::writeBone(const Skeleton* pSkel, const Bone* pBone)
205
0
    {
206
0
        writeChunkHeader(SKELETON_BONE, calcBoneSize(pSkel, pBone));
207
208
0
        unsigned short handle = pBone->getHandle();
209
        // char* name
210
0
        writeString(pBone->getName());
211
#if OGRE_SERIALIZER_VALIDATE_CHUNKSIZE
212
        // Hack to fix chunk size validation:
213
        mChunkSizeStack.back() += calcStringSize(pBone->getName());
214
#endif
215
        // unsigned short handle            : handle of the bone, should be contiguous & start at 0
216
0
        writeShorts(&handle, 1);
217
        // Vector3 position                 : position of this bone relative to parent 
218
0
        writeObject(pBone->getPosition());
219
        // Quaternion orientation           : orientation of this bone relative to parent 
220
0
        writeObject(pBone->getOrientation());
221
        // Vector3 scale                    : scale of this bone relative to parent 
222
0
        if (pBone->getScale() != Vector3::UNIT_SCALE)
223
0
        {
224
0
            writeObject(pBone->getScale());
225
0
        }
226
0
    }
227
    //---------------------------------------------------------------------
228
    void SkeletonSerializer::writeBoneParent(const Skeleton* pSkel, 
229
        unsigned short boneId, unsigned short parentId)
230
0
    {
231
0
        writeChunkHeader(SKELETON_BONE_PARENT, calcBoneParentSize(pSkel));
232
233
        // unsigned short handle             : child bone
234
0
        writeShorts(&boneId, 1);
235
        // unsigned short parentHandle   : parent bone
236
0
        writeShorts(&parentId, 1);
237
238
0
    }
239
    //---------------------------------------------------------------------
240
    void SkeletonSerializer::writeAnimation(const Skeleton* pSkel, 
241
        const Animation* anim, SkeletonVersion ver)
242
0
    {
243
0
        writeChunkHeader(SKELETON_ANIMATION, calcAnimationSize(pSkel, anim, ver));
244
245
        // char* name                       : Name of the animation
246
0
        writeString(anim->getName());
247
        // float length                      : Length of the animation in seconds
248
0
        float len = anim->getLength();
249
0
        writeFloats(&len, 1);
250
0
        pushInnerChunk(mStream);
251
0
        {
252
0
        if ((int)ver > (int)SKELETON_VERSION_1_0)
253
0
        {
254
0
            if (anim->getUseBaseKeyFrame())
255
0
            {
256
0
                size_t size = SSTREAM_OVERHEAD_SIZE;
257
                // char* baseAnimationName (including terminator)
258
0
                    size += calcStringSize(anim->getBaseKeyFrameAnimationName());
259
                // float baseKeyFrameTime
260
0
                size += sizeof(float);
261
                
262
0
                writeChunkHeader(SKELETON_ANIMATION_BASEINFO, size);
263
                
264
                // char* baseAnimationName (blank for self)
265
0
                writeString(anim->getBaseKeyFrameAnimationName());
266
                
267
                // float baseKeyFrameTime
268
0
                float t = (float)anim->getBaseKeyFrameTime();
269
0
                writeFloats(&t, 1);
270
0
            }
271
0
        }
272
273
        // Write all tracks
274
0
        for (const auto& it : anim->_getNodeTrackList())
275
0
        {
276
0
            writeAnimationTrack(pSkel, it.second);
277
0
        }
278
0
        }
279
0
        popInnerChunk(mStream);
280
281
0
    }
282
    //---------------------------------------------------------------------
283
    void SkeletonSerializer::writeAnimationTrack(const Skeleton* pSkel, 
284
        const NodeAnimationTrack* track)
285
0
    {
286
0
        writeChunkHeader(SKELETON_ANIMATION_TRACK, calcAnimationTrackSize(pSkel, track));
287
288
        // unsigned short boneIndex     : Index of bone to apply to
289
0
        Bone* bone = static_cast<Bone*>(track->getAssociatedNode());
290
0
        unsigned short boneid = bone->getHandle();
291
0
        writeShorts(&boneid, 1);
292
0
        pushInnerChunk(mStream);
293
        // Write all keyframes
294
0
        for (unsigned short i = 0; i < track->getNumKeyFrames(); ++i)
295
0
        {
296
0
            writeKeyFrame(pSkel, track->getNodeKeyFrame(i));
297
0
        }
298
0
        popInnerChunk(mStream);
299
0
    }
300
    //---------------------------------------------------------------------
301
    void SkeletonSerializer::writeKeyFrame(const Skeleton* pSkel, 
302
        const TransformKeyFrame* key)
303
0
    {
304
305
0
        writeChunkHeader(SKELETON_ANIMATION_TRACK_KEYFRAME, calcKeyFrameSize(pSkel, key));
306
307
        // float time                    : The time position (seconds)
308
0
        float time = key->getTime();
309
0
        writeFloats(&time, 1);
310
        // Quaternion rotate            : Rotation to apply at this keyframe
311
0
        writeObject(key->getRotation());
312
        // Vector3 translate            : Translation to apply at this keyframe
313
0
        writeObject(key->getTranslate());
314
        // Vector3 scale                : Scale to apply at this keyframe
315
0
        if (key->getScale() != Vector3::UNIT_SCALE)
316
0
        {
317
0
            writeObject(key->getScale());
318
0
        }
319
0
    }
320
    //---------------------------------------------------------------------
321
    size_t SkeletonSerializer::calcBoneSize(const Skeleton* pSkel, 
322
        const Bone* pBone)
323
0
    {
324
0
        size_t size = calcBoneSizeWithoutScale(pSkel, pBone);
325
326
        // scale
327
0
        if (pBone->getScale() != Vector3::UNIT_SCALE)
328
0
        {
329
0
            size += sizeof(float) * 3;
330
0
        }
331
332
0
        return size;
333
0
    }
334
    //---------------------------------------------------------------------
335
    size_t SkeletonSerializer::calcBoneSizeWithoutScale(const Skeleton* pSkel, 
336
        const Bone* pBone)
337
701
    {
338
701
        size_t size = SSTREAM_OVERHEAD_SIZE;
339
340
        // TODO: Add this for next skeleton format!
341
        // Currently it is broken, because to determine that we have scale, it will compare chunk size.
342
        //size += calcStringSize(pBone->getName());
343
344
        // handle
345
701
        size += sizeof(unsigned short);
346
347
        // position
348
701
        size += sizeof(float) * 3;
349
350
        // orientation
351
701
        size += sizeof(float) * 4;
352
353
701
        return size;
354
701
    }
355
    //---------------------------------------------------------------------
356
    size_t SkeletonSerializer::calcBoneParentSize(const Skeleton* pSkel)
357
0
    {
358
0
        size_t size = SSTREAM_OVERHEAD_SIZE;
359
360
        // handle
361
0
        size += sizeof(unsigned short);
362
363
        // parent handle
364
0
        size += sizeof(unsigned short);
365
366
0
        return size;
367
0
    }
368
    //---------------------------------------------------------------------
369
    size_t SkeletonSerializer::calcAnimationSize(const Skeleton* pSkel, const Animation* pAnim, SkeletonVersion ver)
370
0
    {
371
0
        size_t size = SSTREAM_OVERHEAD_SIZE;
372
373
        // Name, including terminator
374
0
        size += calcStringSize(pAnim->getName());
375
        // length
376
0
        size += sizeof(float);
377
378
0
        if ((int)ver > (int)SKELETON_VERSION_1_0)
379
0
        {
380
0
            if (pAnim->getUseBaseKeyFrame())
381
0
            {
382
0
                size += SSTREAM_OVERHEAD_SIZE;
383
                // char* baseAnimationName (including terminator)
384
0
                size += calcStringSize(pAnim->getBaseKeyFrameAnimationName());
385
                // float baseKeyFrameTime
386
0
                size += sizeof(float);
387
0
            }
388
0
        }
389
390
        // Nested animation tracks
391
0
        for (const auto& it : pAnim->_getNodeTrackList())
392
0
        {
393
0
            size += calcAnimationTrackSize(pSkel, it.second);
394
0
        }
395
396
0
        return size;
397
0
    }
398
    //---------------------------------------------------------------------
399
    size_t SkeletonSerializer::calcAnimationTrackSize(const Skeleton* pSkel, 
400
        const NodeAnimationTrack* pTrack)
401
0
    {
402
0
        size_t size = SSTREAM_OVERHEAD_SIZE;
403
404
        // unsigned short boneIndex     : Index of bone to apply to
405
0
        size += sizeof(unsigned short);
406
407
        // Nested keyframes
408
0
        for (unsigned short i = 0; i < pTrack->getNumKeyFrames(); ++i)
409
0
        {
410
0
            size += calcKeyFrameSize(pSkel, pTrack->getNodeKeyFrame(i));
411
0
        }
412
413
0
        return size;
414
0
    }
415
    //---------------------------------------------------------------------
416
    size_t SkeletonSerializer::calcKeyFrameSize(const Skeleton* pSkel, 
417
        const TransformKeyFrame* pKey)
418
0
    {
419
0
        size_t size = calcKeyFrameSizeWithoutScale(pSkel, pKey);
420
421
        // Vector3 scale                : Scale to apply at this keyframe
422
0
        if (pKey->getScale() != Vector3::UNIT_SCALE)
423
0
        {
424
0
            size += sizeof(float) * 3;
425
0
        }
426
427
0
        return size;
428
0
    }
429
    //---------------------------------------------------------------------
430
    size_t SkeletonSerializer::calcKeyFrameSizeWithoutScale(const Skeleton* pSkel, 
431
        const TransformKeyFrame* pKey)
432
915
    {
433
915
        size_t size = SSTREAM_OVERHEAD_SIZE;
434
435
        // float time                    : The time position (seconds)
436
915
        size += sizeof(float);
437
        // Quaternion rotate            : Rotation to apply at this keyframe
438
915
        size += sizeof(float) * 4;
439
        // Vector3 translate            : Translation to apply at this keyframe
440
915
        size += sizeof(float) * 3;
441
442
915
        return size;
443
915
    }
444
    //---------------------------------------------------------------------
445
    void SkeletonSerializer::readBone(DataStreamPtr& stream, Skeleton* pSkel)
446
718
    {
447
        // char* name
448
718
        String name = readString(stream);
449
        // unsigned short handle            : handle of the bone, should be contiguous & start at 0
450
718
        unsigned short handle;
451
718
        readShorts(stream, &handle, 1);
452
453
718
        if (handle > pSkel->getNumBones())
454
718
            OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Bone handles must be contiguous");
455
456
        // Create new bone
457
709
        Bone* pBone = pSkel->createBone(name, handle);
458
459
        // Vector3 position                 : position of this bone relative to parent 
460
709
        Vector3 pos;
461
709
        readObject(stream, pos);
462
709
        pBone->setPosition(pos);
463
        // Quaternion orientation           : orientation of this bone relative to parent 
464
709
        Quaternion q;
465
709
        readObject(stream, q);
466
709
        pBone->setOrientation(q);
467
468
#if OGRE_SERIALIZER_VALIDATE_CHUNKSIZE
469
        // Hack to fix chunk size validation:
470
        mChunkSizeStack.back() += calcStringSize(name);
471
#endif
472
        // TODO: don't depend on mCurrentstreamLen in next skeleton format!
473
        // Currently we use wrong chunk sizes, but we can't fix it, because we depend on mCurrentstreamLen
474
        // Do we have scale?
475
709
        if (mCurrentstreamLen > calcBoneSizeWithoutScale(pSkel, pBone))
476
23
        {
477
23
            Vector3 scale;
478
23
            readObject(stream, scale);
479
23
            pBone->setScale(scale);
480
23
        }
481
        
482
709
    }
483
    //---------------------------------------------------------------------
484
    void SkeletonSerializer::readBoneParent(DataStreamPtr& stream, Skeleton* pSkel)
485
35
    {
486
        // All bones have been created by this point
487
35
        Bone *child, *parent;
488
35
        unsigned short childHandle, parentHandle;
489
490
        // unsigned short handle             : child bone
491
35
        readShorts(stream, &childHandle, 1);
492
        // unsigned short parentHandle   : parent bone
493
35
        readShorts(stream, &parentHandle, 1);
494
495
35
        auto numBones = pSkel->getBones().size();
496
35
        if (parentHandle >= numBones || childHandle >= numBones)
497
35
            OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Bone handle out of range");
498
499
14
        if (childHandle == parentHandle)
500
14
            OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "A bone cannot be parent of itself");
501
502
        // Find bones
503
12
        parent = pSkel->getBone(parentHandle);
504
12
        child = pSkel->getBone(childHandle);
505
506
        // attach
507
12
        parent->addChild(child);
508
509
12
    }
510
    //---------------------------------------------------------------------
511
    void SkeletonSerializer::readAnimation(DataStreamPtr& stream, Skeleton* pSkel)
512
2.25k
    {
513
        // char* name                       : Name of the animation
514
2.25k
        String name;
515
2.25k
        name = readString(stream);
516
        // float length                      : Length of the animation in seconds
517
2.25k
        float len;
518
2.25k
        readFloats(stream, &len, 1);
519
520
2.25k
        Animation *pAnim = pSkel->createAnimation(name, len);
521
        // Read all tracks
522
2.25k
        if (!stream->eof())
523
2.08k
        {
524
2.08k
            pushInnerChunk(stream);
525
2.08k
            unsigned short streamID = readChunk(stream);
526
            // Optional base info is possible
527
2.08k
            if (streamID == SKELETON_ANIMATION_BASEINFO)
528
122
            {
529
                // char baseAnimationName
530
122
                String baseAnimName = readString(stream);
531
                // float baseKeyFrameTime
532
122
                float baseKeyTime;
533
122
                readFloats(stream, &baseKeyTime, 1);
534
                
535
122
                pAnim->setUseBaseKeyFrame(true, baseKeyTime, baseAnimName);
536
                
537
122
                if (!stream->eof())
538
115
                {
539
                    // Get next stream
540
115
                    streamID = readChunk(stream);
541
115
                }
542
122
            }
543
            
544
2.27k
            while(streamID == SKELETON_ANIMATION_TRACK && !stream->eof())
545
189
            {
546
189
                readAnimationTrack(stream, pAnim, pSkel);
547
548
189
                if (!stream->eof())
549
127
                {
550
                    // Get next stream
551
127
                    streamID = readChunk(stream);
552
127
                }
553
189
            }
554
2.08k
            if (!stream->eof())
555
1.93k
            {
556
                // Backpedal back to start of this stream if we've found a non-track
557
1.93k
                backpedalChunkHeader(stream);
558
1.93k
            }
559
2.08k
            popInnerChunk(stream);
560
2.08k
        }
561
2.25k
    }
562
    //---------------------------------------------------------------------
563
    void SkeletonSerializer::readAnimationTrack(DataStreamPtr& stream, Animation* anim, 
564
        Skeleton* pSkel)
565
189
    {
566
        // unsigned short boneIndex     : Index of bone to apply to
567
189
        unsigned short boneHandle;
568
189
        readShorts(stream, &boneHandle, 1);
569
570
        // Find bone
571
189
        if (boneHandle >= pSkel->getBones().size())
572
189
            OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "Animation track boneHandle out of range");
573
185
        Bone* targetBone = pSkel->getBone(boneHandle);
574
575
        // Create track
576
185
        NodeAnimationTrack* pTrack = anim->createNodeTrack(boneHandle, targetBone);
577
578
        // Keep looking for nested keyframes
579
185
        if (!stream->eof())
580
179
        {
581
179
            pushInnerChunk(stream);
582
179
            unsigned short streamID = readChunk(stream);
583
1.09k
            while(streamID == SKELETON_ANIMATION_TRACK_KEYFRAME && !stream->eof())
584
915
            {
585
915
                readKeyFrame(stream, pTrack, pSkel);
586
587
915
                if (!stream->eof())
588
896
                {
589
                    // Get next stream
590
896
                    streamID = readChunk(stream);
591
896
                }
592
915
            }
593
179
            if (!stream->eof())
594
127
            {
595
                // Backpedal back to start of this stream if we've found a non-keyframe
596
127
                backpedalChunkHeader(stream);
597
127
            }
598
179
            popInnerChunk(stream);
599
179
        }
600
601
602
185
    }
603
    //---------------------------------------------------------------------
604
    void SkeletonSerializer::readKeyFrame(DataStreamPtr& stream, NodeAnimationTrack* track, 
605
        Skeleton* pSkel)
606
915
    {
607
        // float time                    : The time position (seconds)
608
915
        float time;
609
915
        readFloats(stream, &time, 1);
610
611
915
        TransformKeyFrame *kf = track->createNodeKeyFrame(time);
612
613
        // Quaternion rotate            : Rotation to apply at this keyframe
614
915
        Quaternion rot;
615
915
        readObject(stream, rot);
616
915
        kf->setRotation(rot);
617
        // Vector3 translate            : Translation to apply at this keyframe
618
915
        Vector3 trans;
619
915
        readObject(stream, trans);
620
915
        kf->setTranslate(trans);
621
        // Do we have scale?
622
915
        if (mCurrentstreamLen > calcKeyFrameSizeWithoutScale(pSkel, kf))
623
206
        {
624
206
            Vector3 scale;
625
206
            readObject(stream, scale);
626
206
            kf->setScale(scale);
627
206
        }
628
915
    }
629
    //---------------------------------------------------------------------
630
    void SkeletonSerializer::writeSkeletonAnimationLink(const Skeleton* pSkel, 
631
        const LinkedSkeletonAnimationSource& link)
632
0
    {
633
0
        writeChunkHeader(SKELETON_ANIMATION_LINK, 
634
0
            calcSkeletonAnimationLinkSize(pSkel, link));
635
636
        // char* skeletonName
637
0
        writeString(link.skeletonName);
638
        // float scale
639
0
        writeFloats(&(link.scale), 1);
640
641
0
    }
642
    //---------------------------------------------------------------------
643
    size_t SkeletonSerializer::calcSkeletonAnimationLinkSize(const Skeleton* pSkel, 
644
        const LinkedSkeletonAnimationSource& link)
645
0
    {
646
0
        size_t size = SSTREAM_OVERHEAD_SIZE;
647
648
        // char* skeletonName
649
0
        size += link.skeletonName.length() + 1;
650
        // float scale
651
0
        size += sizeof(float);
652
653
0
        return size;
654
655
0
    }
656
    //---------------------------------------------------------------------
657
    void SkeletonSerializer::readSkeletonAnimationLink(DataStreamPtr& stream, 
658
        Skeleton* pSkel)
659
105
    {
660
        // char* skeletonName
661
105
        String skelName = readString(stream);
662
        // float scale
663
105
        float scale;
664
105
        readFloats(stream, &scale, 1);
665
666
105
        pSkel->addLinkedSkeletonAnimationSource(skelName, scale);
667
668
105
    }
669
670
671
672
}
673
674