/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 | | |