Ogre实现不同动画之间的混合

来源:互联网 发布:什么是4g网络 编辑:程序博客网 时间:2024/05/21 09:22

动画混合 -- 实现两个动画的切换, 一个动画逐渐消逝, 另一个动画逐渐显示来实现. 主要通过动画状态的权重来实现
通过三种方式来实现两个动画的混合:
    - BlendSwitch - 直接切换至目标动画
    - BlendWhileAnimating - 混合的过程中目标动画也更新帧, 实现动画
    - BlendThenAnimate - 用源动画的当前帧混合目标动画的第一个帧

源码理解, 主要代码位于下面两个函数
AnimationBlender::blend函数 根据传入的参数设置新转换的源动画和目标动画
AnimationBlender::add函数则更新源动画和目标动画(如存在)的状态和权重

AnimationBlender::blend函数
1. 如果动画转换类型为 AnimationBlender::BlendSwitch
    直接将源动画转换成目标动画
2. 如果不是这个转换类型 AnimationBlender::BlendSwitch
    如果上次的动画转换还未完成
        如果新的目标动画等于上次转换的源动画
            则反向转换
        如果新的目标动画不等于上次转换的源动画和目标动画
            根据上次转换的剩余时间设置是显示上次转换的源动画还是目标动画, 并将其设置为新转换的源动画
            显示新转换的目标动画, 并设置其权重
    假设上次的动画转换已经完成了转换
        设置转换时间, 转换类型, 目标动画和其权重

AnimationBlender::addTime 函数
1. 如有动画在运行
    如果存在转换
        更新剩余时间
            如剩余时间小于0
                禁止源动画
                将目标动画设置为源动画, 继续运行
            如剩余时间不小于0
                更新源动画和目标动画的权重
                如转换类型为AnimationBlender::BlendWhileAnimating
                    更新目标动画
    判断动画是否完成
        更新源动画

完整代码和Demo代码

AnimationBlender.h

 
#ifndef __ANIMATION_BLENDER_H__#define __ANIMATION_BLENDER_H__#include <Ogre.h>usingnamespace Ogre;classAnimationBlender{public:    enumBlendingTransition    {        BlendSwitch,        // stop source and start dest        BlendWhileAnimating,  // cross fade, blend source animation out while blending destination animation in        BlendThenAnimate     // blend source to first frame of dest, when done, start dest anim    };private:    Entity *mEntity;    AnimationState *mSource;    AnimationState *mTarget;    BlendingTransition mTransition;    boolloop;    ~AnimationBlender() {}public:    Real mTimeleft, mDuration;    boolcomplete;    voidblend( constString &animation, BlendingTransition transition, Real duration,bool l=true);    voidaddTime( Real );    Real getProgress() {return mTimeleft/ mDuration; }    AnimationState *getSource() {return mSource; }    AnimationState *getTarget() {return mTarget; }    AnimationBlender( Entity *);    voidinit( const String &animation, bool l=true );};#endif

AnimationBlender.cpp

#include "AnimationBlender.h"void AnimationBlender::init(const String &animation, bool l){    // 初始化, 将所有的动画禁止, 只允许参数中的动画运行.    AnimationStateSet *set = mEntity->getAllAnimationStates();    AnimationStateIterator it = set->getAnimationStateIterator();    while(it.hasMoreElements())    {        AnimationState *anim = it.getNext();        anim->setEnabled(false);        anim->setWeight(0);        anim->setTimePosition(0);    }    mSource = mEntity->getAnimationState( animation );    mSource->setEnabled(true);    mSource->setWeight(1);    mTimeleft = 0;    mDuration = 1;    mTarget = 0;    complete =false;    loop = l;} voidAnimationBlender::blend( constString &animation, BlendingTransition transition, Real duration,bool l ){    loop = l;    if( transition == AnimationBlender::BlendSwitch )    {        if( mSource != 0 )            mSource->setEnabled(false);        mSource = mEntity->getAnimationState( animation );        mSource->setEnabled(true);        mSource->setWeight(1);        mSource->setTimePosition(0);        mTimeleft = 0;    }    else    {        AnimationState *newTarget = mEntity->getAnimationState( animation );        if( mTimeleft > 0 )        {            // oops, weren't finished yet            if( newTarget == mTarget )            {                // nothing to do! (ignoring duration here)            }            else if( newTarget == mSource )            {                // going back to the source state, so let's switch                mSource = mTarget;                mTarget = newTarget;                mTimeleft = mDuration - mTimeleft; // i'm ignoring the new duration here            }            else            {                // ok, newTarget is really new, so either we simply replace the target with this one, or                // we make the target the new source                if( mTimeleft < mDuration * 0.5 )                {                   // simply replace the target with this one                   mTarget->setEnabled(false);                   mTarget->setWeight(0);                }                else                {                   // old target becomes new source                   mSource->setEnabled(false);                   mSource->setWeight(0);                   mSource = mTarget;                }                 mTarget = newTarget;                mTarget->setEnabled(true);                mTarget->setWeight( 1.0 - mTimeleft / mDuration );                mTarget->setTimePosition(0);            }        }        else        {            // assert( target == 0, "target should be 0 when not blending" )            // mSource->setEnabled(true);            // mSource->setWeight(1);            mTransition = transition;            mTimeleft = mDuration = duration;            mTarget = newTarget;            mTarget->setEnabled(true);            mTarget->setWeight(0);            mTarget->setTimePosition(0);        }    }}voidAnimationBlender::addTime( Real time){    if( mSource != 0 )    {        if( mTimeleft > 0 )        {            mTimeleft -= time;            if( mTimeleft < 0 )            {                // finish blending               mSource->setEnabled(false);               mSource->setWeight(0);               mSource = mTarget;               mSource->setEnabled(true);               mSource->setWeight(1);               mTarget = 0;            }            else            {               // still blending, advance weights               mSource->setWeight(mTimeleft / mDuration);               mTarget->setWeight(1.0 - mTimeleft / mDuration);               if(mTransition == AnimationBlender::BlendWhileAnimating)                   mTarget->addTime(time);            }        }        if (mSource->getTimePosition() >= mSource->getLength())        {            complete = true;        }        else        {            complete = false;        }        mSource->addTime(time);        mSource->setLoop(loop);    }}AnimationBlender::AnimationBlender( Entity *entity ) : mEntity(entity){}

AnimationBlenderDemo.h

#ifndef __ANIMATIONBLENDER_DEMO_H__#define __ANIMATIONBLENDER_DEMO_H__#include "ExampleApplication.h"#include "AnimationBlender.h"classAnimationBlenderDemoFrameListener : publicExampleFrameListener{protected:    Entity* mNinjaEnt;    AnimationBlender* mAnimationBlender;    boolwalking, jumping;public:    AnimationBlenderDemoFrameListener(RenderWindow* mWin, Camera* mCam, Entity* ent)        : ExampleFrameListener(mWin, mCam,false, false), mNinjaEnt(ent)    {        mAnimationBlender =new AnimationBlender(mNinjaEnt);        mAnimationBlender->init("Walk",true);        walking =false;        jumping =false;    }    virtual~AnimationBlenderDemoFrameListener()    {        if(mAnimationBlender)        {//          delete mAnimationBlender;        }    }    boolframeRenderingQueued(constFrameEvent& evt)    {        if(!ExampleFrameListener::frameRenderingQueued(evt))        {            return false;        }        if(!walking)        {            mAnimationBlender->blend( "Walk",AnimationBlender::BlendWhileAnimating, 0.2,true );            walking=true;        }        if(mKeyboard->isKeyDown( OIS::KC_SPACE ) && !jumping)        {            jumping=true;            mAnimationBlender->blend("Jump",AnimationBlender::BlendWhileAnimating, 0.2,false );        }        if(jumping)        {            if (mAnimationBlender->complete)            {                mAnimationBlender->blend( "Idle1",AnimationBlender::BlendWhileAnimating, 0.02,true );                jumping=false;            }        }        mAnimationBlender->addTime(evt.timeSinceLastFrame);        returntrue;    }};classAnimationBlenderDemoApp : publicExampleApplication{public:    AnimationBlenderDemoApp() {}protected:    Entity* mNinjaEnt;    voidcreateScene();    voidcreateFrameListener()    {        mFrameListener =new AnimationBlenderDemoFrameListener(mWindow, mCamera, mNinjaEnt);        mRoot->addFrameListener(mFrameListener);    }};#endif

AnimationBlenderDemo.cpp

#include "AnimationBlenderDemo.h"#include <OgreStringConverter.h>void AnimationBlenderDemoApp::createScene(){mSceneMgr->setAmbientLight(ColourValue(1.0, 1.0, 1.0));mNinjaEnt = mSceneMgr->createEntity("Ninja","ninja.mesh");SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode();node->attachObject(mNinjaEnt);}#ifdef __cplusplusextern"C" {#endif#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32#define WIN32_LEAN_AND_MEAN#include "windows.h"INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE,LPSTR strCmdLine,INT )#elseint main(int argc,char **argv)#endif{AnimationBlenderDemoApp app;try{    app.go();} catch( Ogre::Exception& e ) {#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32    MessageBox( NULL, e.getFullDescription().c_str(),"An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL );#else    std::cerr <<"An exception has occured: " << e.getFullDescription();#endif}return0;}#ifdef __cplusplus}#endif

不过由于Ninja.mesh的动画时间太小, 很难看出混合效果,  以上代码适合Ogre1.6.5

注:转载源地址