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




  • 相关阅读:
    【转】 Docker(部署常见应用):Docker安装Alibaba Nacos教程(单机-集群)
    【转】 SpringCloudAlibaba--10——sentinel
    【转】 SpringCloudAlibaba--09——sentinel
    【转】 SpringCloudAlibaba--08——sentinel
    【转】 SpringCloudAlibaba--07——gateWay
    【转】 SpringCloudAlibaba--06——geteway
    1045
    1045 access denied for user 'root'@'localhost' using password yes的解决方法
    在PHP中使用CURL
    zendstudio快捷键复制行Ctrl+Alt+向下无效的解决方法
  • 原文地址:https://www.cnblogs.com/gameprogram/p/2259127.html
Copyright © 2020-2023  润新知