天天看點

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

view source print ?

01

#ifndef __ANIMATION_BLENDER_H__

02

#define __ANIMATION_BLENDER_H__

03

#include <Ogre.h>

04

using

namespace

Ogre;

05

class

AnimationBlender

06

{

07

public

:

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

};

14

private

:

15

Entity *mEntity;

16

AnimationState *mSource;

17

AnimationState *mTarget;

18

BlendingTransition mTransition;

19

bool

loop;

20

~AnimationBlender() {}

21

public

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

view source print ?

001

#include "AnimationBlender.h"

002

void

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

023

void

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

}

090

void

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

}

128

AnimationBlender::AnimationBlender( Entity *entity ) : mEntity(entity) 

129

{

130

}

AnimationBlenderDemo.h

view source print ?

01

<P>#ifndef __ANIMATIONBLENDER_DEMO_H__

02

#define __ANIMATIONBLENDER_DEMO_H__

03

#include "ExampleApplication.h"

04

#include "AnimationBlender.h"

05

class

AnimationBlenderDemoFrameListener :

public

ExampleFrameListener

06

{

07

protected

:

08

Entity* mNinjaEnt;

09

AnimationBlender* mAnimationBlender;

10

bool

walking, jumping;

11

public

:

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

};

55

class

AnimationBlenderDemoApp :

public

ExampleApplication

56

{

57

public

:

58

AnimationBlenderDemoApp() {}

59

protected

:

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

view source print ?

01

<P>#include

"AnimationBlenderDemo.h"

02

#include <OgreStringConverter.h>

03

void

AnimationBlenderDemoApp::createScene()

04

{

05

mSceneMgr->setAmbientLight(ColourValue(1.0, 1.0, 1.0));

06

mNinjaEnt = mSceneMgr->createEntity(

"Ninja"

,

"ninja.mesh"

);

07

SceneNode* node = mSceneMgr->getRootSceneNode()->createChildSceneNode();

08

node->attachObject(mNinjaEnt);

09

}

10

#ifdef __cplusplus

11

extern

"C"

{

12

#endif

13

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

14

#define WIN32_LEAN_AND_MEAN

15

#include "windows.h"

16

INT

WINAPI WinMain(

HINSTANCE

hInst,

HINSTANCE

,

LPSTR

strCmdLine,

INT

)

17

#else

18

int

main(

int

argc,

char

**argv)

19

#endif

20

{

21

AnimationBlenderDemoApp app;

22

try

{

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

}

31

return

0;

32

}

33

#ifdef __cplusplus

34

}

35

#endif</P>

不過由于Ninja.mesh的動畫時間太小, 很難看出混合效果,  以上代碼适合Ogre1.6.5