Ogre骨骼動畫

來源:互聯網
上載者:User
原文網址: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,             &params));}
複製代碼

代理給工產方法:

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");

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.