一、基本框架
先看一下OGRE动画的基本框架:
http://blog.csdn.net/leonwei/article/details/5819248
二、动画控制
OGRE的基本动画控制是很简单的,设置一个动画的操作是这样:
// Set idle animation mAnimationState = 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); ... }
调用工厂方法:
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");
上文来自:http://www.cnblogs.com/gameprogram/articles/2259127.html