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

英文原文: http://test.ogitor.org/tiki/AnimationBlender

动画混合 -- 实现两个动画的切换, 一个动画逐渐消逝, 另一个动画逐渐显示来实现. 主要通过动画状态的权重来实现
通过三种方式来实现两个动画的混合:
    - 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

01#ifndef __ANIMATION_BLENDER_H__
02#define __ANIMATION_BLENDER_H__
03#include <Ogre.h>
04using namespace Ogre;
05class AnimationBlender
06{
07public:
08    enum BlendingTransition
09    {
10        BlendSwitch,         // stop source and start dest
11        BlendWhileAnimating,   // cross fade, blend source animation out while blending destination animation in
12        BlendThenAnimate      // blend source to first frame of dest, when done, start dest anim
13    };
14private:
15    Entity *mEntity;
16    AnimationState *mSource;
17    AnimationState *mTarget;
18    BlendingTransition mTransition;
19    bool loop;
20    ~AnimationBlender() {}
21public
22    Real mTimeleft, mDuration;
23    bool complete;
24    void blend( const String &animation, BlendingTransition transition, Real duration, bool l=true );
25    void addTime( Real );
26    Real getProgress() { return mTimeleft/ mDuration; }
27    AnimationState *getSource() { return mSource; }
28    AnimationState *getTarget() { return mTarget; }
29    AnimationBlender( Entity *);
30    void init( const String &animation, bool l=true );
31};
32#endif

AnimationBlender.cpp

001#include "AnimationBlender.h"
002void AnimationBlender::init(const String &animation, bool l)
003{
004    // 初始化, 将所有的动画禁止, 只允许参数中的动画运行.
005    AnimationStateSet *set = mEntity->getAllAnimationStates();
006    AnimationStateIterator it = set->getAnimationStateIterator();
007    while(it.hasMoreElements())
008    {
009        AnimationState *anim = it.getNext();
010        anim->setEnabled(false);
011        anim->setWeight(0);
012        anim->setTimePosition(0);
013    }
014    mSource = mEntity->getAnimationState( animation );
015    mSource->setEnabled(true);
016    mSource->setWeight(1);
017    mTimeleft = 0;
018    mDuration = 1;
019    mTarget = 0;
020    complete = false;
021    loop = l;
022
023void AnimationBlender::blend( const String &animation, BlendingTransition transition, Real duration, bool l )
024{
025    loop = l;
026    if( transition == AnimationBlender::BlendSwitch )
027    {
028        if( mSource != 0 )
029            mSource->setEnabled(false);
030        mSource = mEntity->getAnimationState( animation );
031        mSource->setEnabled(true);
032        mSource->setWeight(1);
033        mSource->setTimePosition(0);
034        mTimeleft = 0;
035    
036    else 
037    
038        AnimationState *newTarget = mEntity->getAnimationState( animation );
039        if( mTimeleft > 0 )
040        {
041            // oops, weren't finished yet
042            if( newTarget == mTarget )
043            {
044                // nothing to do! (ignoring duration here)
045            }
046            else if( newTarget == mSource )
047            {
048                // going back to the source state, so let's switch
049                mSource = mTarget;
050                mTarget = newTarget;
051                mTimeleft = mDuration - mTimeleft; // i'm ignoring the new duration here
052            }
053            else
054            {
055                // ok, newTarget is really new, so either we simply replace the target with this one, or
056                // we make the target the new source
057                if( mTimeleft < mDuration * 0.5 )
058                {
059                    // simply replace the target with this one
060                    mTarget->setEnabled(false);
061                    mTarget->setWeight(0);
062                }
063                else
064                {
065                    // old target becomes new source
066                    mSource->setEnabled(false);
067                    mSource->setWeight(0);
068                    mSource = mTarget;
069                
070                mTarget = newTarget;
071                mTarget->setEnabled(true);
072                mTarget->setWeight( 1.0 - mTimeleft / mDuration );
073                mTarget->setTimePosition(0);
074            }
075        }
076        else
077        {
078            // assert( target == 0, "target should be 0 when not blending" )
079            // mSource->setEnabled(true);
080            // mSource->setWeight(1);
081            mTransition = transition;
082            mTimeleft = mDuration = duration;
083            mTarget = newTarget;
084            mTarget->setEnabled(true);
085            mTarget->setWeight(0);
086            mTarget->setTimePosition(0);
087        }
088    }
089}
090void AnimationBlender::addTime( Real time )
091{
092    if( mSource != 0 )
093    {
094        if( mTimeleft > 0 )
095        {
096            mTimeleft -= time;
097            if( mTimeleft < 0 )
098            {
099                // finish blending
100                mSource->setEnabled(false);
101                mSource->setWeight(0);
102                mSource = mTarget;
103                mSource->setEnabled(true);
104                mSource->setWeight(1);
105                mTarget = 0;
106            }
107            else
108            {
109                // still blending, advance weights
110                mSource->setWeight(mTimeleft / mDuration);
111                mTarget->setWeight(1.0 - mTimeleft / mDuration);
112                if(mTransition == AnimationBlender::BlendWhileAnimating)
113                    mTarget->addTime(time);
114            }
115        }
116        if (mSource->getTimePosition() >= mSource->getLength())
117        {
118            complete = true;
119        }
120        else
121        {
122            complete = false;
123        }
124        mSource->addTime(time);
125        mSource->setLoop(loop);
126    }
127}
128AnimationBlender::AnimationBlender( Entity *entity ) : mEntity(entity) 
129{
130}

AnimationBlenderDemo.h

01<P>#ifndef __ANIMATIONBLENDER_DEMO_H__
02#define __ANIMATIONBLENDER_DEMO_H__
03#include "ExampleApplication.h"
04#include "AnimationBlender.h"
05class AnimationBlenderDemoFrameListener : public ExampleFrameListener
06{
07protected:
08    Entity* mNinjaEnt;
09    AnimationBlender* mAnimationBlender;
10    bool walking, jumping;
11public:
12    AnimationBlenderDemoFrameListener(RenderWindow* mWin, Camera* mCam, Entity* ent)
13        : ExampleFrameListener(mWin, mCam, false, false), mNinjaEnt(ent)
14    {
15        mAnimationBlender = new AnimationBlender(mNinjaEnt);
16        mAnimationBlender->init("Walk", true);
17        walking = false;
18        jumping = false;
19    }
20    virtual ~AnimationBlenderDemoFrameListener()
21    {
22        if(mAnimationBlender)
23        {
24//          delete mAnimationBlender;
25        }
26    }
27    bool frameRenderingQueued(const FrameEvent& evt)
28    {
29        if (!ExampleFrameListener::frameRenderingQueued(evt))
30        {
31            return false;
32        }
33        if (!walking)
34        {
35            mAnimationBlender->blend( "Walk",AnimationBlender::BlendWhileAnimating, 0.2, true );
36            walking=true;
37        }
38        if (mKeyboard->isKeyDown( OIS::KC_SPACE ) && !jumping)
39        {
40            jumping=true;
41            mAnimationBlender->blend("Jump",AnimationBlender::BlendWhileAnimating, 0.2, false );
42        }
43        if (jumping)
44        {
45            if (mAnimationBlender->complete)
46            {
47                mAnimationBlender->blend( "Idle1",AnimationBlender::BlendWhileAnimating, 0.02, true );
48                jumping=false;
49            }
50        }
51        mAnimationBlender->addTime(evt.timeSinceLastFrame);
52        return true;
53    }
54};
55class AnimationBlenderDemoApp : public ExampleApplication
56{
57public:
58    AnimationBlenderDemoApp() {}
59protected:
60    Entity* mNinjaEnt;
61    void createScene();
62    void createFrameListener()
63    {
64        mFrameListener = new AnimationBlenderDemoFrameListener(mWindow, mCamera, mNinjaEnt);
65        mRoot->addFrameListener(mFrameListener);
66    }
67};
68#endif</P>

AnimationBlenderDemo.cpp

01<P>#include "AnimationBlenderDemo.h"
02#include <OgreStringConverter.h>
03void AnimationBlenderDemoApp::createScene()
04{
05mSceneMgr->setAmbientLight(ColourValue(1.0, 1.0, 1.0));
06mNinjaEnt = mSceneMgr->createEntity("Ninja", "ninja.mesh");
07SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode();
08node->attachObject(mNinjaEnt);
09}
10#ifdef __cplusplus
11extern "C" {
12#endif
13#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
14#define WIN32_LEAN_AND_MEAN
15#include "windows.h"
16INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
17#else
18int main(int argc, char **argv)
19#endif
20{
21AnimationBlenderDemoApp app;
22try {
23    app.go();
24} catch( Ogre::Exception& e ) {
25#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
26    MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL );
27#else
28    std::cerr << "An exception has occured: " << e.getFullDescription();
29#endif
30}
31return 0;
32}
33#ifdef __cplusplus
34}
35#endif</P>

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值