/* ----------------------------------------------------------------------------- Copyright (c) 2010 Jacob 'jacmoe' Moen You may use this sample code for anything you like, it is public domain. ----------------------------------------------------------------------------- */ /* ----------------------------------------------------------------------------- Filename: AnimationSerializer.cpp Description: A serializer for keyframe animations ----------------------------------------------------------------------------- */ #include "AnimationSerializer.h" #include "OgrePrerequisites.h" #include "OgreException.h" #include "OgreAnimation.h" #include "OgreAnimationTrack.h" #include "OgreKeyFrame.h" #include "OgreString.h" #include "OgreDataStream.h" #include "OgreLogManager.h" #include "OgreResourceGroupManager.h" #include "OgreStringConverter.h" /// stream overhead = ID + size const long STREAM_OVERHEAD_SIZE = sizeof(Ogre::uint16) + sizeof(Ogre::uint32); AnimationSerializer::AnimationSerializer() { mVersion = "[AnimationSerializer.10]"; } AnimationSerializer::~AnimationSerializer() { } void AnimationSerializer::exportAnimation(const Ogre::Animation* pAnimation, const Ogre::String& filename, Endian endianMode) { // Decide on endian mode determineEndianness(endianMode); Ogre::String msg; mpfFile = fopen(filename.c_str(), "wb"); if (!mpfFile) { OGRE_EXCEPT(Ogre::Exception::ERR_CANNOT_WRITE_TO_FILE, "Unable to open file " + filename + " for writing", "AnimationSerializer::exportAnimation"); } writeFileHeader(); Ogre::LogManager::getSingleton().stream() << "Exporting animation: " << pAnimation->getName(); writeAnimation(pAnimation); Ogre::LogManager::getSingleton().logMessage("Animation exported."); fclose(mpfFile); } Ogre::Animation* AnimationSerializer::importAnimation(const Ogre::String& filename) { Ogre::DataStreamPtr stream = Ogre::ResourceGroupManager::getSingleton().openResource(filename); return importAnimation(stream); } Ogre::Animation* AnimationSerializer::importAnimation(Ogre::DataStreamPtr& stream) { // Determine endianness (must be the first thing we do!) determineEndianness(stream); // Check header readFileHeader(stream); Ogre::Animation* anim; unsigned short streamID; while(!stream->eof()) { streamID = readChunk(stream); switch (streamID) { case AC_ANIMATION: anim = readAnimation(stream); break; } } return anim; } void AnimationSerializer::writeAnimation(const Ogre::Animation* anim) { writeChunkHeader(AC_ANIMATION, calcAnimationSize(anim)); // char* name : Name of the animation writeString(anim->getName()); // float length : Length of the animation in seconds float len = anim->getLength(); writeFloats(&len, 1); // Write all tracks Ogre::Animation::NodeTrackIterator trackIt = anim->getNodeTrackIterator(); while(trackIt.hasMoreElements()) { writeAnimationTrack(trackIt.getNext()); } } void AnimationSerializer::writeAnimationTrack(const Ogre::NodeAnimationTrack* track) { static unsigned short trackId = 0; writeChunkHeader(AC_ANIMATION_TRACK, calcAnimationTrackSize(track)); writeShorts(&trackId, 1); // Write all keyframes for (unsigned short i = 0; i < track->getNumKeyFrames(); ++i) { writeKeyFrame(track->getNodeKeyFrame(i)); } trackId++; } void AnimationSerializer::writeKeyFrame(const Ogre::TransformKeyFrame* key) { writeChunkHeader(AC_ANIMATION_TRACK_KEYFRAME, calcKeyFrameSize(key)); // float time : The time position (seconds) float time = key->getTime(); writeFloats(&time, 1); // Quaternion rotate : Rotation to apply at this keyframe writeObject(key->getRotation()); // Vector3 translate : Translation to apply at this keyframe writeObject(key->getTranslate()); } Ogre::Animation* AnimationSerializer::readAnimation(Ogre::DataStreamPtr& stream) { Ogre::String name; name = readString(stream); // float length : Length of the animation in seconds float len; readFloats(stream, &len, 1); Ogre::Animation* anim = new Ogre::Animation(name, len); // Read all tracks if (!stream->eof()) { unsigned short streamID = readChunk(stream); while(streamID == AC_ANIMATION_TRACK && !stream->eof()) { readAnimationTrack(stream, anim); if (!stream->eof()) { // Get next stream streamID = readChunk(stream); } } if (!stream->eof()) { // Backpedal back to start of this stream if we've found a non-track stream->skip(-STREAM_OVERHEAD_SIZE); } } return anim; } void AnimationSerializer::readAnimationTrack(Ogre::DataStreamPtr& stream, Ogre::Animation* anim) { unsigned short trackId; readShorts(stream, &trackId, 1); // Create track Ogre::NodeAnimationTrack* pTrack = anim->createNodeTrack(trackId); // Keep looking for nested keyframes if (!stream->eof()) { unsigned short streamID = readChunk(stream); while(streamID == AC_ANIMATION_TRACK_KEYFRAME && !stream->eof()) { readKeyFrame(stream, pTrack); if (!stream->eof()) { // Get next stream streamID = readChunk(stream); } } if (!stream->eof()) { // Backpedal back to start of this stream if we've found a non-keyframe stream->skip(-STREAM_OVERHEAD_SIZE); } } } void AnimationSerializer::readKeyFrame(Ogre::DataStreamPtr& stream, Ogre::NodeAnimationTrack* track) { // float time : The time position (seconds) float time; readFloats(stream, &time, 1); Ogre::TransformKeyFrame *kf = track->createNodeKeyFrame(time); // Quaternion rotate : Rotation to apply at this keyframe Ogre::Quaternion rot; readObject(stream, rot); kf->setRotation(rot); // Vector3 translate : Translation to apply at this keyframe Ogre::Vector3 trans; readObject(stream, trans); kf->setTranslate(trans); } size_t AnimationSerializer::calcAnimationSize(const Ogre::Animation* pAnim) { size_t size = STREAM_OVERHEAD_SIZE; // Name, including terminator size += pAnim->getName().length() + 1; // length size += sizeof(float); // Nested animation tracks Ogre::Animation::NodeTrackIterator trackIt = pAnim->getNodeTrackIterator(); while(trackIt.hasMoreElements()) { size += calcAnimationTrackSize(trackIt.getNext()); } return size; } size_t AnimationSerializer::calcAnimationTrackSize(const Ogre::NodeAnimationTrack* pTrack) { size_t size = STREAM_OVERHEAD_SIZE; // unsigned short boneIndex : Index of bone to apply to size += sizeof(unsigned short); // Nested keyframes for (unsigned short i = 0; i < pTrack->getNumKeyFrames(); ++i) { size += calcKeyFrameSize(pTrack->getNodeKeyFrame(i)); } return size; } size_t AnimationSerializer::calcKeyFrameSize(const Ogre::TransformKeyFrame* pKey) { size_t size = STREAM_OVERHEAD_SIZE; // float time : The time position (seconds) size += sizeof(float); // Quaternion rotate : Rotation to apply at this keyframe size += sizeof(float) * 4; // Vector3 translate : Translation to apply at this keyframe size += sizeof(float) * 3; return size; }