原文網址:http://www.cnblogs.com/gameprogram/archive/2011/11/22/2259127.html Ogre骨骼動畫
一、基本架構
先看一下OGRE動畫的基本架構:
http://blog.csdn.net/leonwei/article/details/5819248
二、動畫控制
OGRE的基本動畫控制是很簡單的,設定一個動畫的操作是這樣:
// Set idle animationmAnimationState = ent->getAnimationState( "Idle" ); mAnimationState->setLoop( true );mAnimationState->setEnabled( true );
複製代碼
(上面這段代碼來自Intermediate Tutorial1 – Ogre Wiki)從一個Entity對象中得到AnimationState指標,然後設定一些屬性,在每幀需要調用(FrameListener中):
mAnimationState->addTime( evt.timeSinceLastFrame );
傳入每幀的時間,這樣動畫就開始動起來了。
三、動畫資源載入
1.Skeleton載入
可以通過SkeletonManager來載入骨骼檔案。
SkeletonPtr skel = SkeletonManager::getSingleton().load("jaiqua.skeleton", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);複製代碼
這個load是SkeletonManager所繼承的ResourceManager的虛函數load。
ResourcePtr ResourceManager::load(const String& name, const String& group, bool isManual, ManualResourceLoader* loader, const NameValuePairList* loadParams) { ResourcePtr r = createOrRetrieve(name,group,isManual,loader,loadParams).first; // ensure loaded r->load(); return r; }複製代碼
裡邊建立一個Resource,這邊就是Skeleton了,並調用他的load函數。
這個load函數也是繼承來的,它是繼承於Resource類的,Ogre裡的資源都是繼承這個類的,如Material,Mesh等。
void Resource::load(bool background){... if (old==LOADSTATE_UNLOADED) prepareImpl(); preLoadImpl(); old = LOADSTATE_PREPARED; if (mGroup == ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME) { // Derive resource group changeGroupOwnership( ResourceGroupManager::getSingleton() .findGroupContainingResource(mName)); } loadImpl(); postLoadImpl();...}複製代碼
這裡邊的主要載入資源邏輯就是在loadImpl(),這是個純虛函數,具體由子類來實現,我們現在讀取的是skeleton,因此就是使用skeleton類中的loadImpl().
void Skeleton::loadImpl(void){ SkeletonSerializer serializer; LogManager::getSingleton().stream() << "Skeleton: Loading " << mName; DataStreamPtr stream = ResourceGroupManager::getSingleton().openResource( mName, mGroup, true, this); serializer.importSkeleton(stream, this); // Load any linked skeletons LinkedSkeletonAnimSourceList::iterator i; for (i = mLinkedSkeletonAnimSourceList.begin(); i != mLinkedSkeletonAnimSourceList.end(); ++i) { i->pSkeleton = SkeletonManager::getSingleton().load( i->skeletonName, mGroup); }}複製代碼
此函數主要進行了兩件事:1、用SkeletonSerializer來解析檔案資料流,儲存到當前的skeleton執行個體中;2、載入所有相關連的skeleton(在.skeleton檔案的<animationlinks>標籤中定義)。
動畫資訊儲存在Skeleton的“AnimationList mAnimationsList”成員變數中,可以根據動畫的名字來擷取相應的動畫資訊
SkeletonPtr skel = SkeletonManager::getSingleton().load("Jaiqua.skeleton", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);Animation* anim = skel->getAnimation("Sneak");複製代碼
這邊說一下Skeleton和SkeletonInstance的區別,SkeletonInstance繼承了Skeleton,內部儲存了一個指向Skeleton的指標。可以開啟SkeletonInstance的標頭檔,可以看到一段簡短的英文描述。
/** A SkeletonInstance is a single instance of a Skeleton used by a world object. @remarks The difference between a Skeleton and a SkeletonInstance is that the Skeleton is the 'master' version much like Mesh is a 'master' version of Entity. Many SkeletonInstance objects can be based on a single Skeleton, and are copies of it when created. Any changes made to this are not reflected in the master copy. The exception is animations; these are shared on the Skeleton itself and may not be modified here. */
複製代碼
簡單翻譯如下:
一個SkeletonInstance類是一個單一的Skeleton的一個執行個體,Skeleton是一個宿主,就像一個Mesh可以有多個Entity一樣。一個Skeleton可以有許多個SkeletonInstance,每個SkeletonInstance拷貝了一份Skeleton資訊,對SkeletonInstance裡的一些資訊的更改不會影響到Skeleton裡的資訊,但是對動畫的修改就會影響到Skeleton裡的動畫,因為動畫資訊是共用的。
這邊最典型的好處就是可以動態為骨骼添加一些東西,如武器,裝備等。
2.Mesh載入
skeleton單獨載入進來了,那Mesh怎麼知道哪個skeleton是他的呢?如果用XML轉換器將.mesh檔案轉為XML,開啟可以看到在檔案末尾有 <skeletonlink name="jaiqua.skeleton" />
所以他會在載入Mesh的時候載入進來。
現在來看Mesh的載入步驟。。這個載入真是能把人繞暈掉。。
先從調用的地方開始看:
Entity* ent = mSceneMgr->createEntity("jaiQua", "jaiqua.mesh");
跳轉到:
Entity* SceneManager::createEntity( const String& entityName, const String& meshName ){ // delegate to factory implementation NameValuePairList params; params["mesh"] = meshName; return static_cast<Entity*>( createMovableObject(entityName, EntityFactory::FACTORY_TYPE_NAME, ¶ms));}複製代碼
代理給工產方法:
MovableObject* SceneManager::createMovableObject(const String& name, const String& typeName, const NameValuePairList* params){... MovableObjectFactory* factory = Root::getSingleton().getMovableObjectFactory(typeName); MovableObject* newObj = factory->createInstance(name, this, params);...}複製代碼
調用Factory 方法:
MovableObject* MovableObjectFactory::createInstance( const String& name, SceneManager* manager, const NameValuePairList* params){ MovableObject* m = createInstanceImpl(name, params); m->_notifyCreator(this); m->_notifyManager(manager); return m;}複製代碼
createInstanceImpl()是一個純虛函數,由不同的子類各自實現,這邊就是調用Entity的方法了。
MovableObject* EntityFactory::createInstanceImpl( const String& name, const NameValuePairList* params){ // must have mesh parameter MeshPtr pMesh; if (params != 0) { NameValuePairList::const_iterator ni = params->find("mesh"); if (ni != params->end()) { // Get mesh (load if required) pMesh = MeshManager::getSingleton().load( ni->second, // autodetect group location ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME ); } } if (pMesh.isNull()) { OGRE_EXCEPT(Exception::ERR_INVALIDPARAMS, "'mesh' parameter required when constructing an Entity.", "EntityFactory::createInstance"); } return OGRE_NEW Entity(name, pMesh);}複製代碼
這邊也開始調用load函數了,這才開始進入mesh資源的載入。
載入的過程跟skeleton類似,因此只挑重點的說一下:
經過若干次跳轉後。。調用到MeshSerializerImpl::importMesh
void MeshSerializerImpl::importMesh(DataStreamPtr& stream, Mesh* pMesh, MeshSerializerListener *listener){ // Determine endianness (must be the first thing we do!) determineEndianness(stream); // Check header readFileHeader(stream); unsigned short streamID; while(!stream->eof()) { streamID = readChunk(stream); switch (streamID) { case M_MESH: readMesh(stream, pMesh, listener); break; } }}複製代碼
跟蹤下去:
MeshSerializerImpl::readMesh()――case M_MESH_SKELETON_LINK:=》MeshSerializerImpl::readSkeletonLink();=》Mesh::setSkeletonName();=》mSkeleton = SkeletonManager::getSingleton().load(skelName, mGroup);――得到骨骼指標
mSkeleton為Mesh裡儲存的骨骼的指標。
這樣就從Entity中可以得到對應的骨骼了。
SkeletonInstance* skelIns = ent->getSkeleton();
3.動畫集合
Animation類的對象就是“An animation sequence”,各種類型的動畫序列都由這個類來管理。它管理了三種類型的Track list,分別是:NodeAnimationTrack、NumericAnimationTrack、VertexAnimationTrack。
通俗的講就是一個動畫是由它的各個部分的動畫軌跡組成的。比如人的動畫是由他的頭和四肢的軌跡組成的。
AnimationState是一個Animation的狀態管理類,可以在裡邊設定動畫的權重,設定動畫是否開啟,是否迴圈等狀態。
AnimationStateSet就是所有的AnimationState的集合。
在Entity的建構函式中,如果Mesh含有骨骼動畫或者頂點動畫,則會new一個AnimationStateSet對象,並調用“mesh->_initAnimationState(mAnimationState);”。
void Entity::_initialise(bool forceReinitialise){...// Is mesh skeletally animated? if (mMesh->hasSkeleton() && !mMesh->getSkeleton().isNull()) { mSkeletonInstance = OGRE_NEW SkeletonInstance(mMesh->getSkeleton()); mSkeletonInstance->load(); }// Initialise the AnimationState, if Mesh has animation if (hasSkeleton()) { mFrameBonesLastUpdated = OGRE_NEW_T(unsigned long, MEMCATEGORY_ANIMATION)(std::numeric_limits<unsigned long>::max()); mNumBoneMatrices = mSkeletonInstance->getNumBones(); mBoneMatrices = static_cast<Matrix4*>(OGRE_MALLOC_SIMD(sizeof(Matrix4) * mNumBoneMatrices, MEMCATEGORY_ANIMATION)); } if (hasSkeleton() || hasVertexAnimation()) { mAnimationState = OGRE_NEW AnimationStateSet(); mMesh->_initAnimationState(mAnimationState); prepareTempBlendBuffers(); }...}複製代碼
可以看到Entity中用的是SkeletonInstance,而不是直接用mesh的Skeleton。
4.動畫的更新
主要運算就在“void Entity::updateAnimation(void)”函數中。
=》 Entity::cacheBoneMatrices
=》 Skeleton::setAnimationState
此函數先是調用“Skeleton::reset”,然後針對每個enabled animation state,找到其對應的Animation,然後調用Animation::apply()來計算每個Bone的狀態。
幾個小技巧:
1.複製動畫
Animation* copyAnim = skel->createAnimation("OldSneak", anim->getLength());
*copyAnim = *(anim->clone("OldSneak"));
ent->refreshAvailableAnimationState();
記得要refresh才能用,不然找不到。
2.添加動畫:
SkeletonInstance* skelIns = ent->getSkeleton();
skelIns->addLinkedSkeletonAnimationSource("XX.skeleton");