/* ----------------------------------------------------------------------------- This source file is part of OGRE (Object-oriented Graphics Rendering Engine) For the latest info, see http://www.ogre3d.org/ Copyright (c) 2000-2009 Torus Knot Software Ltd Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ----------------------------------------------------------------------------- */ #include "OgreStableHeaders.h" #include "OgreAnimation.h" #include "OgreKeyFrame.h" #include "OgreException.h" #include "OgreSkeleton.h" #include "OgreBone.h" #include "OgreEntity.h" #include "OgreSubEntity.h" #include "OgreMesh.h" #include "OgreSubMesh.h" #include "OgreStringConverter.h" namespace Ogre { Animation::InterpolationMode Animation::msDefaultInterpolationMode = Animation::IM_LINEAR; Animation::RotationInterpolationMode Animation::msDefaultRotationInterpolationMode = Animation::RIM_LINEAR; //--------------------------------------------------------------------- Animation::Animation(const String& name, Real length) : mName(name) , mLength(length) , mInterpolationMode(msDefaultInterpolationMode) , mRotationInterpolationMode(msDefaultRotationInterpolationMode) , mKeyFrameTimesDirty(false) { } //--------------------------------------------------------------------- Animation::~Animation() { destroyAllTracks(); } //--------------------------------------------------------------------- Real Animation::getLength(void) const { return mLength; } //--------------------------------------------------------------------- void Animation::setLength(Real len) { mLength = len; } //--------------------------------------------------------------------- NodeAnimationTrack* Animation::createNodeTrack(unsigned short handle) { if (hasNodeTrack(handle)) { OGRE_EXCEPT(Exception::ERR_DUPLICATE_ITEM, "Node track with the specified handle " + StringConverter::toString(handle) + " already exists", "Animation::createNodeTrack"); } NodeAnimationTrack* ret = OGRE_NEW NodeAnimationTrack(this, handle); mNodeTrackList[handle] = ret; return ret; } //--------------------------------------------------------------------- NodeAnimationTrack* Animation::createNodeTrack(unsigned short handle, Node* node) { NodeAnimationTrack* ret = createNodeTrack(handle); ret->setAssociatedNode(node); return ret; } //--------------------------------------------------------------------- unsigned short Animation::getNumNodeTracks(void) const { return (unsigned short)mNodeTrackList.size(); } //--------------------------------------------------------------------- bool Animation::hasNodeTrack(unsigned short handle) const { return (mNodeTrackList.find(handle) != mNodeTrackList.end()); } //--------------------------------------------------------------------- NodeAnimationTrack* Animation::getNodeTrack(unsigned short handle) const { NodeTrackList::const_iterator i = mNodeTrackList.find(handle); if (i == mNodeTrackList.end()) { OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "Cannot find node track with the specified handle " + StringConverter::toString(handle), "Animation::getNodeTrack"); } return i->second; } //--------------------------------------------------------------------- void Animation::destroyNodeTrack(unsigned short handle) { NodeTrackList::iterator i = mNodeTrackList.find(handle); if (i != mNodeTrackList.end()) { OGRE_DELETE i->second; mNodeTrackList.erase(i); _keyFrameListChanged(); } } //--------------------------------------------------------------------- void Animation::destroyAllNodeTracks(void) { NodeTrackList::iterator i; for (i = mNodeTrackList.begin(); i != mNodeTrackList.end(); ++i) { OGRE_DELETE i->second; } mNodeTrackList.clear(); _keyFrameListChanged(); } //--------------------------------------------------------------------- NumericAnimationTrack* Animation::createNumericTrack(unsigned short handle) { if (hasNumericTrack(handle)) { OGRE_EXCEPT(Exception::ERR_DUPLICATE_ITEM, "Numeric track with the specified handle " + StringConverter::toString(handle) + " already exists", "Animation::createNumericTrack"); } NumericAnimationTrack* ret = OGRE_NEW NumericAnimationTrack(this, handle); mNumericTrackList[handle] = ret; return ret; } //--------------------------------------------------------------------- NumericAnimationTrack* Animation::createNumericTrack(unsigned short handle, const AnimableValuePtr& anim) { NumericAnimationTrack* ret = createNumericTrack(handle); ret->setAssociatedAnimable(anim); return ret; } //--------------------------------------------------------------------- unsigned short Animation::getNumNumericTracks(void) const { return (unsigned short)mNumericTrackList.size(); } //--------------------------------------------------------------------- bool Animation::hasNumericTrack(unsigned short handle) const { return (mNumericTrackList.find(handle) != mNumericTrackList.end()); } //--------------------------------------------------------------------- NumericAnimationTrack* Animation::getNumericTrack(unsigned short handle) const { NumericTrackList::const_iterator i = mNumericTrackList.find(handle); if (i == mNumericTrackList.end()) { OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "Cannot find numeric track with the specified handle " + StringConverter::toString(handle), "Animation::getNumericTrack"); } return i->second; } //--------------------------------------------------------------------- void Animation::destroyNumericTrack(unsigned short handle) { NumericTrackList::iterator i = mNumericTrackList.find(handle); if (i != mNumericTrackList.end()) { OGRE_DELETE i->second; mNumericTrackList.erase(i); _keyFrameListChanged(); } } //--------------------------------------------------------------------- void Animation::destroyAllNumericTracks(void) { NumericTrackList::iterator i; for (i = mNumericTrackList.begin(); i != mNumericTrackList.end(); ++i) { OGRE_DELETE i->second; } mNumericTrackList.clear(); _keyFrameListChanged(); } //--------------------------------------------------------------------- VertexAnimationTrack* Animation::createVertexTrack(unsigned short handle, VertexAnimationType animType) { if (hasVertexTrack(handle)) { OGRE_EXCEPT(Exception::ERR_DUPLICATE_ITEM, "Vertex track with the specified handle " + StringConverter::toString(handle) + " already exists", "Animation::createVertexTrack"); } VertexAnimationTrack* ret = OGRE_NEW VertexAnimationTrack(this, handle, animType); mVertexTrackList[handle] = ret; return ret; } //--------------------------------------------------------------------- VertexAnimationTrack* Animation::createVertexTrack(unsigned short handle, VertexData* data, VertexAnimationType animType) { VertexAnimationTrack* ret = createVertexTrack(handle, animType); ret->setAssociatedVertexData(data); return ret; } //--------------------------------------------------------------------- unsigned short Animation::getNumVertexTracks(void) const { return (unsigned short)mVertexTrackList.size(); } //--------------------------------------------------------------------- bool Animation::hasVertexTrack(unsigned short handle) const { return (mVertexTrackList.find(handle) != mVertexTrackList.end()); } //--------------------------------------------------------------------- VertexAnimationTrack* Animation::getVertexTrack(unsigned short handle) const { VertexTrackList::const_iterator i = mVertexTrackList.find(handle); if (i == mVertexTrackList.end()) { OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, "Cannot find vertex track with the specified handle " + StringConverter::toString(handle), "Animation::getVertexTrack"); } return i->second; } //--------------------------------------------------------------------- void Animation::destroyVertexTrack(unsigned short handle) { VertexTrackList::iterator i = mVertexTrackList.find(handle); if (i != mVertexTrackList.end()) { OGRE_DELETE i->second; mVertexTrackList.erase(i); _keyFrameListChanged(); } } //--------------------------------------------------------------------- void Animation::destroyAllVertexTracks(void) { VertexTrackList::iterator i; for (i = mVertexTrackList.begin(); i != mVertexTrackList.end(); ++i) { OGRE_DELETE i->second; } mVertexTrackList.clear(); _keyFrameListChanged(); } //--------------------------------------------------------------------- void Animation::destroyAllTracks(void) { destroyAllNodeTracks(); destroyAllNumericTracks(); destroyAllVertexTracks(); } //--------------------------------------------------------------------- const String& Animation::getName(void) const { return mName; } //--------------------------------------------------------------------- void Animation::apply(Real timePos, Real weight, Real scale) { // Calculate time index for fast keyframe search TimeIndex timeIndex = _getTimeIndex(timePos); NodeTrackList::iterator i; for (i = mNodeTrackList.begin(); i != mNodeTrackList.end(); ++i) { i->second->apply(timeIndex, weight, scale); } NumericTrackList::iterator j; for (j = mNumericTrackList.begin(); j != mNumericTrackList.end(); ++j) { j->second->apply(timeIndex, weight, scale); } VertexTrackList::iterator k; for (k = mVertexTrackList.begin(); k != mVertexTrackList.end(); ++k) { k->second->apply(timeIndex, weight, scale); } } //--------------------------------------------------------------------- void Animation::applyToNode(Node* node, Real timePos, Real weight, Real scale) { // Calculate time index for fast keyframe search TimeIndex timeIndex = _getTimeIndex(timePos); NodeTrackList::iterator i; for (i = mNodeTrackList.begin(); i != mNodeTrackList.end(); ++i) { i->second->applyToNode(node, timeIndex, weight, scale); } } //--------------------------------------------------------------------- void Animation::apply(Skeleton* skel, Real timePos, Real weight, Real scale) { // Calculate time index for fast keyframe search TimeIndex timeIndex = _getTimeIndex(timePos); NodeTrackList::iterator i; for (i = mNodeTrackList.begin(); i != mNodeTrackList.end(); ++i) { // get bone to apply to Bone* b = skel->getBone(i->first); i->second->applyToNode(b, timeIndex, weight, scale); } } //--------------------------------------------------------------------- void Animation::apply(Skeleton* skel, Real timePos, float weight, const AnimationState::BoneBlendMask* blendMask, Real scale) { // Calculate time index for fast keyframe search TimeIndex timeIndex = _getTimeIndex(timePos); NodeTrackList::iterator i; for (i = mNodeTrackList.begin(); i != mNodeTrackList.end(); ++i) { // get bone to apply to Bone* b = skel->getBone(i->first); i->second->applyToNode(b, timeIndex, (*blendMask)[b->getHandle()] * weight, scale); } } //--------------------------------------------------------------------- void Animation::apply(Entity* entity, Real timePos, Real weight, bool software, bool hardware) { // Calculate time index for fast keyframe search TimeIndex timeIndex = _getTimeIndex(timePos); VertexTrackList::iterator i; for (i = mVertexTrackList.begin(); i != mVertexTrackList.end(); ++i) { unsigned short handle = i->first; VertexAnimationTrack* track = i->second; VertexData* swVertexData; VertexData* hwVertexData; VertexData* origVertexData; bool firstAnim = false; if (handle == 0) { // shared vertex data firstAnim = !entity->_getBuffersMarkedForAnimation(); swVertexData = entity->_getSoftwareVertexAnimVertexData(); hwVertexData = entity->_getHardwareVertexAnimVertexData(); origVertexData = entity->getMesh()->sharedVertexData; entity->_markBuffersUsedForAnimation(); } else { // sub entity vertex data (-1) SubEntity* s = entity->getSubEntity(handle - 1); // Skip this track if subentity is not visible if (!s->isVisible()) continue; firstAnim = !s->_getBuffersMarkedForAnimation(); swVertexData = s->_getSoftwareVertexAnimVertexData(); hwVertexData = s->_getHardwareVertexAnimVertexData(); origVertexData = s->getSubMesh()->vertexData; s->_markBuffersUsedForAnimation(); } // Apply to both hardware and software, if requested if (software) { if (firstAnim && track->getAnimationType() == VAT_POSE) { // First time through for a piece of pose animated vertex data // We need to copy the original position values to the temp accumulator const VertexElement* origelem = origVertexData->vertexDeclaration->findElementBySemantic(VES_POSITION); const VertexElement* destelem = swVertexData->vertexDeclaration->findElementBySemantic(VES_POSITION); HardwareVertexBufferSharedPtr origBuffer = origVertexData->vertexBufferBinding->getBuffer(origelem->getSource()); HardwareVertexBufferSharedPtr destBuffer = swVertexData->vertexBufferBinding->getBuffer(destelem->getSource()); destBuffer->copyData(*origBuffer.get(), 0, 0, destBuffer->getSizeInBytes(), true); } track->setTargetMode(VertexAnimationTrack::TM_SOFTWARE); track->applyToVertexData(swVertexData, timeIndex, weight, &(entity->getMesh()->getPoseList())); } if (hardware) { track->setTargetMode(VertexAnimationTrack::TM_HARDWARE); track->applyToVertexData(hwVertexData, timeIndex, weight, &(entity->getMesh()->getPoseList())); } } } //--------------------------------------------------------------------- void Animation::applyToAnimable(const AnimableValuePtr& anim, Real timePos, Real weight, Real scale) { // Calculate time index for fast keyframe search TimeIndex timeIndex = _getTimeIndex(timePos); NumericTrackList::iterator j; for (j = mNumericTrackList.begin(); j != mNumericTrackList.end(); ++j) { j->second->applyToAnimable(anim, weight, scale); } } //--------------------------------------------------------------------- void Animation::applyToVertexData(VertexData* data, Real timePos, Real weight) { // Calculate time index for fast keyframe search TimeIndex timeIndex = _getTimeIndex(timePos); VertexTrackList::iterator k; for (k = mVertexTrackList.begin(); k != mVertexTrackList.end(); ++k) { k->second->applyToVertexData(data, timeIndex, weight); } } //--------------------------------------------------------------------- void Animation::setInterpolationMode(InterpolationMode im) { mInterpolationMode = im; } //--------------------------------------------------------------------- Animation::InterpolationMode Animation::getInterpolationMode(void) const { return mInterpolationMode; } //--------------------------------------------------------------------- void Animation::setDefaultInterpolationMode(InterpolationMode im) { msDefaultInterpolationMode = im; } //--------------------------------------------------------------------- Animation::InterpolationMode Animation::getDefaultInterpolationMode(void) { return msDefaultInterpolationMode; } //--------------------------------------------------------------------- const Animation::NodeTrackList& Animation::_getNodeTrackList(void) const { return mNodeTrackList; } //--------------------------------------------------------------------- const Animation::NumericTrackList& Animation::_getNumericTrackList(void) const { return mNumericTrackList; } //--------------------------------------------------------------------- const Animation::VertexTrackList& Animation::_getVertexTrackList(void) const { return mVertexTrackList; } //--------------------------------------------------------------------- void Animation::setRotationInterpolationMode(RotationInterpolationMode im) { mRotationInterpolationMode = im; } //--------------------------------------------------------------------- Animation::RotationInterpolationMode Animation::getRotationInterpolationMode(void) const { return mRotationInterpolationMode; } //--------------------------------------------------------------------- void Animation::setDefaultRotationInterpolationMode(RotationInterpolationMode im) { msDefaultRotationInterpolationMode = im; } //--------------------------------------------------------------------- Animation::RotationInterpolationMode Animation::getDefaultRotationInterpolationMode(void) { return msDefaultRotationInterpolationMode; } //--------------------------------------------------------------------- void Animation::optimise(bool discardIdentityNodeTracks) { optimiseNodeTracks(discardIdentityNodeTracks); optimiseVertexTracks(); } //----------------------------------------------------------------------- void Animation::_collectIdentityNodeTracks(TrackHandleList& tracks) const { NodeTrackList::const_iterator i, iend; iend = mNodeTrackList.end(); for (i = mNodeTrackList.begin(); i != iend; ++i) { const NodeAnimationTrack* track = i->second; if (track->hasNonZeroKeyFrames()) { tracks.erase(i->first); } } } //----------------------------------------------------------------------- void Animation::_destroyNodeTracks(const TrackHandleList& tracks) { TrackHandleList::const_iterator t, tend; tend = tracks.end(); for (t = tracks.begin(); t != tend; ++t) { destroyNodeTrack(*t); } } //----------------------------------------------------------------------- void Animation::optimiseNodeTracks(bool discardIdentityTracks) { // Iterate over the node tracks and identify those with no useful keyframes list::type tracksToDestroy; NodeTrackList::iterator i; for (i = mNodeTrackList.begin(); i != mNodeTrackList.end(); ++i) { NodeAnimationTrack* track = i->second; if (discardIdentityTracks && !track->hasNonZeroKeyFrames()) { // mark the entire track for destruction tracksToDestroy.push_back(i->first); } else { track->optimise(); } } // Now destroy the tracks we marked for death for(list::type::iterator h = tracksToDestroy.begin(); h != tracksToDestroy.end(); ++h) { destroyNodeTrack(*h); } } //----------------------------------------------------------------------- void Animation::optimiseVertexTracks(void) { // Iterate over the node tracks and identify those with no useful keyframes list::type tracksToDestroy; VertexTrackList::iterator i; for (i = mVertexTrackList.begin(); i != mVertexTrackList.end(); ++i) { VertexAnimationTrack* track = i->second; if (!track->hasNonZeroKeyFrames()) { // mark the entire track for destruction tracksToDestroy.push_back(i->first); } else { track->optimise(); } } // Now destroy the tracks we marked for death for(list::type::iterator h = tracksToDestroy.begin(); h != tracksToDestroy.end(); ++h) { destroyVertexTrack(*h); } } //----------------------------------------------------------------------- Animation* Animation::clone(const String& newName) const { Animation* newAnim = OGRE_NEW Animation(newName, mLength); newAnim->mInterpolationMode = mInterpolationMode; newAnim->mRotationInterpolationMode = mRotationInterpolationMode; // Clone all tracks for (NodeTrackList::const_iterator i = mNodeTrackList.begin(); i != mNodeTrackList.end(); ++i) { i->second->_clone(newAnim); } for (NumericTrackList::const_iterator i = mNumericTrackList.begin(); i != mNumericTrackList.end(); ++i) { i->second->_clone(newAnim); } for (VertexTrackList::const_iterator i = mVertexTrackList.begin(); i != mVertexTrackList.end(); ++i) { i->second->_clone(newAnim); } newAnim->_keyFrameListChanged(); return newAnim; } //----------------------------------------------------------------------- TimeIndex Animation::_getTimeIndex(Real timePos) const { // Uncomment following statement for work as previous //return timePos; // Build keyframe time list on demand if (mKeyFrameTimesDirty) { buildKeyFrameTimeList(); } // Wrap time Real totalAnimationLength = mLength; while (timePos > totalAnimationLength && totalAnimationLength > 0.0f) { timePos -= totalAnimationLength; } // Search for global index KeyFrameTimeList::iterator it = std::lower_bound(mKeyFrameTimes.begin(), mKeyFrameTimes.end(), timePos); return TimeIndex(timePos, std::distance(mKeyFrameTimes.begin(), it)); } //----------------------------------------------------------------------- void Animation::buildKeyFrameTimeList(void) const { NodeTrackList::const_iterator i; NumericTrackList::const_iterator j; VertexTrackList::const_iterator k; // Clear old keyframe times mKeyFrameTimes.clear(); // Collect all keyframe times from each track for (i = mNodeTrackList.begin(); i != mNodeTrackList.end(); ++i) { i->second->_collectKeyFrameTimes(mKeyFrameTimes); } for (j = mNumericTrackList.begin(); j != mNumericTrackList.end(); ++j) { j->second->_collectKeyFrameTimes(mKeyFrameTimes); } for (k = mVertexTrackList.begin(); k != mVertexTrackList.end(); ++k) { k->second->_collectKeyFrameTimes(mKeyFrameTimes); } // Build global index to local index map for each track for (i = mNodeTrackList.begin(); i != mNodeTrackList.end(); ++i) { i->second->_buildKeyFrameIndexMap(mKeyFrameTimes); } for (j = mNumericTrackList.begin(); j != mNumericTrackList.end(); ++j) { j->second->_buildKeyFrameIndexMap(mKeyFrameTimes); } for (k = mVertexTrackList.begin(); k != mVertexTrackList.end(); ++k) { k->second->_buildKeyFrameIndexMap(mKeyFrameTimes); } // Reset dirty flag mKeyFrameTimesDirty = false; } }