AnimationBlender - Ogre实现不同动画之间的混合
英文原文: http://test.ogitor.org/tiki/AnimationBlender
动画混合 -- 实现两个动画的切换, 一个动画逐渐消逝, 另一个动画逐渐显示来实现. 主要通过动画状态的权重来实现
通过三种方式来实现两个动画的混合:
- BlendSwitch - 直接切换至目标动画
- BlendWhileAnimating - 混合的过程中目标动画也更新帧, 实现动画
- BlendThenAnimate - 用源动画的当前帧混合目标动画的第一个帧
源码理解, 主要代码位于下面两个函数
AnimationBlender::blend函数 根据传入的参数设置新转换的源动画和目标动画
AnimationBlender::add函数则更新源动画和目标动画(如存在)的状态和权重
AnimationBlender.h
1 #ifndef __ANIMATION_BLENDER_H__
2 #define __ANIMATION_BLENDER_H__
3 #include <Ogre.h>
4 using namespace Ogre;
5
6 class AnimationBlender
7 {
8 public:
9 enum BlendingTransition
10 {
11 BlendSwitch, // stop source and start dest
12 BlendWhileAnimating, // cross fade, blend source animation out while blending destination animation in
13 BlendThenAnimate // blend source to first frame of dest, when done, start dest anim
14 };
15
16 private:
17 Entity *mEntity;
18 AnimationState *mSource;
19 AnimationState *mTarget;
20
21 BlendingTransition mTransition;
22
23 bool loop;
24
25 ~AnimationBlender() {}
26
27 public:
28 Real mTimeleft, mDuration;
29
30 bool complete;
31
32 void blend( const String &animation, BlendingTransition transition, Real duration, bool l= true );
33 void addTime( Real );
34 Real getProgress() { return mTimeleft/ mDuration; }
35 AnimationState *getSource() { return mSource; }
36 AnimationState *getTarget() { return mTarget; }
37 AnimationBlender( Entity *);
38 void init( const String &animation, bool l= true );
39 };
40
41 #endif
2 #define __ANIMATION_BLENDER_H__
3 #include <Ogre.h>
4 using namespace Ogre;
5
6 class AnimationBlender
7 {
8 public:
9 enum BlendingTransition
10 {
11 BlendSwitch, // stop source and start dest
12 BlendWhileAnimating, // cross fade, blend source animation out while blending destination animation in
13 BlendThenAnimate // blend source to first frame of dest, when done, start dest anim
14 };
15
16 private:
17 Entity *mEntity;
18 AnimationState *mSource;
19 AnimationState *mTarget;
20
21 BlendingTransition mTransition;
22
23 bool loop;
24
25 ~AnimationBlender() {}
26
27 public:
28 Real mTimeleft, mDuration;
29
30 bool complete;
31
32 void blend( const String &animation, BlendingTransition transition, Real duration, bool l= true );
33 void addTime( Real );
34 Real getProgress() { return mTimeleft/ mDuration; }
35 AnimationState *getSource() { return mSource; }
36 AnimationState *getTarget() { return mTarget; }
37 AnimationBlender( Entity *);
38 void init( const String &animation, bool l= true );
39 };
40
41 #endif
AnimationBlender.cpp
1 #include "AnimationBlender.h"
2
3 void AnimationBlender::init( const String &animation, bool l)
4 {
5 // 初始化, 将所有的动画禁止, 只允许参数中的动画运行.
6 AnimationStateSet * set = mEntity->getAllAnimationStates();
7 AnimationStateIterator it = set->getAnimationStateIterator();
8 while(it.hasMoreElements())
9 {
10 AnimationState *anim = it.getNext();
11 anim->setEnabled( false);
12 anim->setWeight(0);
13 anim->setTimePosition(0);
14 }
15 mSource = mEntity->getAnimationState( animation );
16 mSource->setEnabled( true);
17 mSource->setWeight(1);
18 mTimeleft = 0;
19 mDuration = 1;
20 mTarget = 0;
21 complete = false;
22 loop = l;
23 }
24 void AnimationBlender::blend( const String &animation, BlendingTransition transition, Real duration, bool l )
25 {
26 loop = l;
27 if( transition == AnimationBlender::BlendSwitch )
28 {
29 if( mSource != 0 )
30 mSource->setEnabled( false);
31 mSource = mEntity->getAnimationState( animation );
32 mSource->setEnabled( true);
33 mSource->setWeight(1);
34 mSource->setTimePosition(0);
35 mTimeleft = 0;
36 }
37 else
38 {
39 AnimationState *newTarget = mEntity->getAnimationState( animation );
40 if( mTimeleft > 0 )
41 {
42 // oops, weren't finished yet
43 if( newTarget == mTarget )
44 {
45 // nothing to do! (ignoring duration here)
46 }
47 else if( newTarget == mSource )
48 {
49 // going back to the source state, so let's switch
50 mSource = mTarget;
51 mTarget = newTarget;
52 mTimeleft = mDuration - mTimeleft; // i'm ignoring the new duration here
53 }
54 else
55 {
56 // ok, newTarget is really new, so either we simply replace the target with this one, or
57 // we make the target the new source
58 if( mTimeleft < mDuration * 0.5 )
59 {
60 // simply replace the target with this one
61 mTarget->setEnabled( false);
62 mTarget->setWeight(0);
63 }
64 else
65 {
66 // old target becomes new source
67 mSource->setEnabled( false);
68 mSource->setWeight(0);
69 mSource = mTarget;
70 }
71 mTarget = newTarget;
72 mTarget->setEnabled( true);
73 mTarget->setWeight( 1.0 - mTimeleft / mDuration );
74 mTarget->setTimePosition(0);
75 }
76 }
77 else
78 {
79 // assert( target == 0, "target should be 0 when not blending" )
80 // mSource->setEnabled(true);
81 // mSource->setWeight(1);
82 mTransition = transition;
83 mTimeleft = mDuration = duration;
84 mTarget = newTarget;
85 mTarget->setEnabled( true);
86 mTarget->setWeight(0);
87 mTarget->setTimePosition(0);
88 }
89 }
90 }
91 void AnimationBlender::addTime( Real time )
92 {
93 if( mSource != 0 )
94 {
95 if( mTimeleft > 0 )
96 {
97 mTimeleft -= time;
98 if( mTimeleft < 0 )
99 {
100 // finish blending
101 mSource->setEnabled( false);
102 mSource->setWeight(0);
103 mSource = mTarget;
104 mSource->setEnabled( true);
105 mSource->setWeight(1);
106 mTarget = 0;
107 }
108 else
109 {
110 // still blending, advance weights
111 mSource->setWeight(mTimeleft / mDuration);
112 mTarget->setWeight(1.0 - mTimeleft / mDuration);
113 if(mTransition == AnimationBlender::BlendWhileAnimating)
114 mTarget->addTime(time);
115 }
116 }
117 if (mSource->getTimePosition() >= mSource->getLength())
118 {
119 complete = true;
120 }
121 else
122 {
123 complete = false;
124 }
125 mSource->addTime(time);
126 mSource->setLoop(loop);
127 }
128 }
129 AnimationBlender::AnimationBlender( Entity *entity ) : mEntity(entity)
130 {
131 }
132
2
3 void AnimationBlender::init( const String &animation, bool l)
4 {
5 // 初始化, 将所有的动画禁止, 只允许参数中的动画运行.
6 AnimationStateSet * set = mEntity->getAllAnimationStates();
7 AnimationStateIterator it = set->getAnimationStateIterator();
8 while(it.hasMoreElements())
9 {
10 AnimationState *anim = it.getNext();
11 anim->setEnabled( false);
12 anim->setWeight(0);
13 anim->setTimePosition(0);
14 }
15 mSource = mEntity->getAnimationState( animation );
16 mSource->setEnabled( true);
17 mSource->setWeight(1);
18 mTimeleft = 0;
19 mDuration = 1;
20 mTarget = 0;
21 complete = false;
22 loop = l;
23 }
24 void AnimationBlender::blend( const String &animation, BlendingTransition transition, Real duration, bool l )
25 {
26 loop = l;
27 if( transition == AnimationBlender::BlendSwitch )
28 {
29 if( mSource != 0 )
30 mSource->setEnabled( false);
31 mSource = mEntity->getAnimationState( animation );
32 mSource->setEnabled( true);
33 mSource->setWeight(1);
34 mSource->setTimePosition(0);
35 mTimeleft = 0;
36 }
37 else
38 {
39 AnimationState *newTarget = mEntity->getAnimationState( animation );
40 if( mTimeleft > 0 )
41 {
42 // oops, weren't finished yet
43 if( newTarget == mTarget )
44 {
45 // nothing to do! (ignoring duration here)
46 }
47 else if( newTarget == mSource )
48 {
49 // going back to the source state, so let's switch
50 mSource = mTarget;
51 mTarget = newTarget;
52 mTimeleft = mDuration - mTimeleft; // i'm ignoring the new duration here
53 }
54 else
55 {
56 // ok, newTarget is really new, so either we simply replace the target with this one, or
57 // we make the target the new source
58 if( mTimeleft < mDuration * 0.5 )
59 {
60 // simply replace the target with this one
61 mTarget->setEnabled( false);
62 mTarget->setWeight(0);
63 }
64 else
65 {
66 // old target becomes new source
67 mSource->setEnabled( false);
68 mSource->setWeight(0);
69 mSource = mTarget;
70 }
71 mTarget = newTarget;
72 mTarget->setEnabled( true);
73 mTarget->setWeight( 1.0 - mTimeleft / mDuration );
74 mTarget->setTimePosition(0);
75 }
76 }
77 else
78 {
79 // assert( target == 0, "target should be 0 when not blending" )
80 // mSource->setEnabled(true);
81 // mSource->setWeight(1);
82 mTransition = transition;
83 mTimeleft = mDuration = duration;
84 mTarget = newTarget;
85 mTarget->setEnabled( true);
86 mTarget->setWeight(0);
87 mTarget->setTimePosition(0);
88 }
89 }
90 }
91 void AnimationBlender::addTime( Real time )
92 {
93 if( mSource != 0 )
94 {
95 if( mTimeleft > 0 )
96 {
97 mTimeleft -= time;
98 if( mTimeleft < 0 )
99 {
100 // finish blending
101 mSource->setEnabled( false);
102 mSource->setWeight(0);
103 mSource = mTarget;
104 mSource->setEnabled( true);
105 mSource->setWeight(1);
106 mTarget = 0;
107 }
108 else
109 {
110 // still blending, advance weights
111 mSource->setWeight(mTimeleft / mDuration);
112 mTarget->setWeight(1.0 - mTimeleft / mDuration);
113 if(mTransition == AnimationBlender::BlendWhileAnimating)
114 mTarget->addTime(time);
115 }
116 }
117 if (mSource->getTimePosition() >= mSource->getLength())
118 {
119 complete = true;
120 }
121 else
122 {
123 complete = false;
124 }
125 mSource->addTime(time);
126 mSource->setLoop(loop);
127 }
128 }
129 AnimationBlender::AnimationBlender( Entity *entity ) : mEntity(entity)
130 {
131 }
132
AnimationBlenderDemo.h
1 #ifndef __ANIMATIONBLENDER_DEMO_H__
2 #define __ANIMATIONBLENDER_DEMO_H__
3 #include "ExampleApplication.h"
4 #include "AnimationBlender.h"
5
6 class AnimationBlenderDemoFrameListener : public ExampleFrameListener
7 {
8 protected:
9 Entity* mNinjaEnt;
10 AnimationBlender* mAnimationBlender;
11 bool walking, jumping;
12 public:
13 AnimationBlenderDemoFrameListener(RenderWindow* mWin, Camera* mCam, Entity* ent)
14 : ExampleFrameListener(mWin, mCam, false, false), mNinjaEnt(ent)
15 {
16 mAnimationBlender = new AnimationBlender(mNinjaEnt);
17 mAnimationBlender->init("Walk", true);
18 walking = false;
19 jumping = false;
20 }
21
22 virtual ~AnimationBlenderDemoFrameListener()
23 {
24 if(mAnimationBlender)
25 {
26 // delete mAnimationBlender;
27 }
28 }
29
30 bool frameRenderingQueued( const FrameEvent& evt)
31 {
32 if (!ExampleFrameListener::frameRenderingQueued(evt))
33 {
34 return false;
35 }
36
37 if (!walking)
38 {
39 mAnimationBlender->blend( "Walk",AnimationBlender::BlendWhileAnimating, 0.2, true );
40 walking= true;
41 }
42 if (mKeyboard->isKeyDown( OIS::KC_SPACE ) && !jumping)
43 {
44 jumping= true;
45 mAnimationBlender->blend("Jump",AnimationBlender::BlendWhileAnimating, 0.2, false );
46 }
47 if (jumping)
48 {
49 if (mAnimationBlender->complete)
50 {
51 mAnimationBlender->blend( "Idle1",AnimationBlender::BlendWhileAnimating, 0.02, true );
52 jumping= false;
53 }
54 }
55 mAnimationBlender->addTime(evt.timeSinceLastFrame);
56
57 return true;
58 }
59 };
60
61 class AnimationBlenderDemoApp : public ExampleApplication
62 {
63 public:
64 AnimationBlenderDemoApp() {}
65 protected:
66 Entity* mNinjaEnt;
67 void createScene();
68 void createFrameListener()
69 {
70 mFrameListener = new AnimationBlenderDemoFrameListener(mWindow, mCamera, mNinjaEnt);
71 mRoot->addFrameListener(mFrameListener);
72 }
73 };
74
75 #endif
2 #define __ANIMATIONBLENDER_DEMO_H__
3 #include "ExampleApplication.h"
4 #include "AnimationBlender.h"
5
6 class AnimationBlenderDemoFrameListener : public ExampleFrameListener
7 {
8 protected:
9 Entity* mNinjaEnt;
10 AnimationBlender* mAnimationBlender;
11 bool walking, jumping;
12 public:
13 AnimationBlenderDemoFrameListener(RenderWindow* mWin, Camera* mCam, Entity* ent)
14 : ExampleFrameListener(mWin, mCam, false, false), mNinjaEnt(ent)
15 {
16 mAnimationBlender = new AnimationBlender(mNinjaEnt);
17 mAnimationBlender->init("Walk", true);
18 walking = false;
19 jumping = false;
20 }
21
22 virtual ~AnimationBlenderDemoFrameListener()
23 {
24 if(mAnimationBlender)
25 {
26 // delete mAnimationBlender;
27 }
28 }
29
30 bool frameRenderingQueued( const FrameEvent& evt)
31 {
32 if (!ExampleFrameListener::frameRenderingQueued(evt))
33 {
34 return false;
35 }
36
37 if (!walking)
38 {
39 mAnimationBlender->blend( "Walk",AnimationBlender::BlendWhileAnimating, 0.2, true );
40 walking= true;
41 }
42 if (mKeyboard->isKeyDown( OIS::KC_SPACE ) && !jumping)
43 {
44 jumping= true;
45 mAnimationBlender->blend("Jump",AnimationBlender::BlendWhileAnimating, 0.2, false );
46 }
47 if (jumping)
48 {
49 if (mAnimationBlender->complete)
50 {
51 mAnimationBlender->blend( "Idle1",AnimationBlender::BlendWhileAnimating, 0.02, true );
52 jumping= false;
53 }
54 }
55 mAnimationBlender->addTime(evt.timeSinceLastFrame);
56
57 return true;
58 }
59 };
60
61 class AnimationBlenderDemoApp : public ExampleApplication
62 {
63 public:
64 AnimationBlenderDemoApp() {}
65 protected:
66 Entity* mNinjaEnt;
67 void createScene();
68 void createFrameListener()
69 {
70 mFrameListener = new AnimationBlenderDemoFrameListener(mWindow, mCamera, mNinjaEnt);
71 mRoot->addFrameListener(mFrameListener);
72 }
73 };
74
75 #endif
AnimationBlenderDemo.cpp
1 #include "AnimationBlenderDemo.h"
2 #include <OgreStringConverter.h>
3
4 void AnimationBlenderDemoApp::createScene()
5 {
6 mSceneMgr->setAmbientLight(ColourValue(1.0, 1.0, 1.0));
7
8 mNinjaEnt = mSceneMgr->createEntity("Ninja", "ninja.mesh");
9 SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode();
10 node->attachObject(mNinjaEnt);
11 }
12
13 #ifdef __cplusplus
14 extern "C" {
15 #endif
16
17 #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
18 #define WIN32_LEAN_AND_MEAN
19 #include "windows.h"
20
21 INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
22 #else
23 int main( int argc, char **argv)
24 #endif
25 {
26 AnimationBlenderDemoApp app;
27 try {
28 app.go();
29 } catch( Ogre::Exception& e ) {
30 #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
31 MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL );
32 #else
33 std::cerr << "An exception has occured: " << e.getFullDescription();
34 #endif
35 }
36 return 0;
37 }
38
39 #ifdef __cplusplus
40 }
41 #endif
2 #include <OgreStringConverter.h>
3
4 void AnimationBlenderDemoApp::createScene()
5 {
6 mSceneMgr->setAmbientLight(ColourValue(1.0, 1.0, 1.0));
7
8 mNinjaEnt = mSceneMgr->createEntity("Ninja", "ninja.mesh");
9 SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode();
10 node->attachObject(mNinjaEnt);
11 }
12
13 #ifdef __cplusplus
14 extern "C" {
15 #endif
16
17 #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
18 #define WIN32_LEAN_AND_MEAN
19 #include "windows.h"
20
21 INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
22 #else
23 int main( int argc, char **argv)
24 #endif
25 {
26 AnimationBlenderDemoApp app;
27 try {
28 app.go();
29 } catch( Ogre::Exception& e ) {
30 #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
31 MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL );
32 #else
33 std::cerr << "An exception has occured: " << e.getFullDescription();
34 #endif
35 }
36 return 0;
37 }
38
39 #ifdef __cplusplus
40 }
41 #endif
http://www.cppblog.com/summericeyl/archive/2010/05/02/114175.html