/* ----------------------------------------------------------------------------- This source file is part of OGRE (Object-oriented Graphics Rendering Engine) For the latest info, see http://www.ogre3d.org/ Copyright (c) 2000-2006 Torus Knot Software Ltd Also see acknowledgements in Readme.html This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA, or go to http://www.gnu.org/copyleft/lesser.txt. You may alternatively use this source under the terms of a specific version of the OGRE Unrestricted License provided you have obtained such a license from Torus Knot Software Ltd. ----------------------------------------------------------------------------- */ #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; } //--------------------------------------------------------------------- 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 = 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()) { delete i->second; mNodeTrackList.erase(i); _keyFrameListChanged(); } } //--------------------------------------------------------------------- void Animation::destroyAllNodeTracks(void) { NodeTrackList::iterator i; for (i = mNodeTrackList.begin(); i != mNodeTrackList.end(); ++i) { 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 = 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()) { delete i->second; mNumericTrackList.erase(i); _keyFrameListChanged(); } } //--------------------------------------------------------------------- void Animation::destroyAllNumericTracks(void) { NumericTrackList::iterator i; for (i = mNumericTrackList.begin(); i != mNumericTrackList.end(); ++i) { 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 = 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()) { delete i->second; mVertexTrackList.erase(i); _keyFrameListChanged(); } } //--------------------------------------------------------------------- void Animation::destroyAllVertexTracks(void) { VertexTrackList::iterator i; for (i = mVertexTrackList.begin(); i != mVertexTrackList.end(); ++i) { 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::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(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::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 std::list 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(std::list::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 std::list 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(std::list::iterator h = tracksToDestroy.begin(); h != tracksToDestroy.end(); ++h) { destroyVertexTrack(*h); } } //----------------------------------------------------------------------- Animation* Animation::clone(const String& newName) const { Animation* newAnim = new Animation(newName, mLength); newAnim->mInterpolationMode = mInterpolationMode; newAnim->mRotationInterpolationMode = mRotationInterpolationMode; // Clone all tracks for (NodeTrackList::const_iterator i = mNodeTrackList.begin(); i != mNodeTrackList.end(); ++i) { newAnim->mNodeTrackList[i->second->getHandle()] = i->second->_clone(newAnim); } for (NumericTrackList::const_iterator i = mNumericTrackList.begin(); i != mNumericTrackList.end(); ++i) { newAnim->mNumericTrackList[i->second->getHandle()] = i->second->_clone(newAnim); } for (VertexTrackList::const_iterator i = mVertexTrackList.begin(); i != mVertexTrackList.end(); ++i) { newAnim->mVertexTrackList[i->second->getHandle()] = 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; } }