Ogre骨骼动画

一、基本框架

先看一下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, 
            &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);
...
}
复制代码


调用工厂方法:

复制代码
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");

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值