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