Ogre 动画过度(AnimationBlender)

转自:http://blog.sina.com.cn/s/blog_45209f340100f7y6.html

 

 英文原版连接于此,这个东西是我翻译的

内容

介绍

    动作的混合就是两个动作状态的过渡,在动作创作过程中允许我们对动作进行过渡(两种动作间的柔和)。在下面的例子程序中,你有三种方式进行这种过渡。

 

- BlendSwitch: 转换到第二种动作状态(用第二个动作代码,直接替换第一个)
- BlendWhileAnimating: 交叉减淡两个动作,减淡第一个动作的同时,逐渐加入第二个动作
- BlendThenAnimate: 拿到当前动画帧用第二个动作的第一帧混合它。

 

源代码

AnimationBlender.h

class AnimationBlender
{
public:
  enum BlendingTransition
  {
     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;

  bool loop;

  ~AnimationBlender() {}

public:
  Real mTimeleft, mDuration;

  bool complete;
 
  void blend( const String &animation, BlendingTransition transition, Real duration, bool l=true );
  void addTime( Real );
  Real getProgress() { return mTimeleft/ mDuration; }
  AnimationState *getSource() { return mSource; }
  AnimationState *getTarget() { return mTarget; }
  AnimationBlender( Entity *);
  void init( const String &animation, bool l=true );
};
AnimationBlender.cpp

void AnimationBlender::init(const String &animation, bool l)
{
  AnimationStateSet *set = mEntity->getAllAnimationStates();
  AnimationStateSet::iterator it = set->begin();
  while(it != set->end())
  {
     AnimationState &anim = it->second;
     anim.setEnabled(false);
     anim.setWeight(0);
     anim.setTimePosition(0);
     ++it;
  }
  mSource = mEntity->getAnimationState( animation );
  mSource->setEnabled(true);
  mSource->setWeight(1);
  mTimeleft = 0;
  mDuration = 1;
  mTarget = 0;
  complete = false;
  loop = l;
}
void AnimationBlender::blend( const String &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);
     }
  }
}
void AnimationBlender::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)
{
}

 

 

————————————————————————————————————————————
AnimationBlender.cpp for Ogre 1.2 (minor changes)

 

 

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;
}
void AnimationBlender::blend( const String &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);
    }
 }
}
void AnimationBlender::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)
{
}
 

应用

应用这些函数你能做如下事情,让你的模型以走为例

 

if (!walking)
{
  thePlayer->mAnimationBlender->blend( "Walk",AnimationBlender::BlendWhileAnimating, 0.2, true );
  walking=true;
}

if (mInputDevice->isKeyDown( KC_SPACE ) && !jumping)
{
  jumping=true;
  thePlayer->mAnimationBlender->blend("Jump",AnimationBlender::BlendWhileAnimating, 0.2, false );
}

if (jumping)
{
  if (thePlayer->mAnimationBlender->complete)
  {
     thePlayer->mAnimationBlender->blend( "Idle1",AnimationBlender::BlendWhileAnimating, 0.02, true );
     jumping=false;
  }
}

 

 

推荐博客阅读: http://hi.baidu.com/hocker421/blog/item/0cc98a12a8cca7d8f6039e7f.html

 

另外附上我修改过的可执行代码:

#include "ExampleApplication.h"
#include "AnimationBlender.h"

AnimationBlender * blender;


class AnimBlenderFrameListener : public ExampleFrameListener
{
private:
 SceneManager* mSceneMgr;
public:
 AnimBlenderFrameListener(SceneManager *sceneMgr, RenderWindow* win, Camera* cam)
  : ExampleFrameListener(win, cam),
  mSceneMgr(sceneMgr)
 {
 }

 bool frameStarted(const FrameEvent& evt)
 {
  bool ret = ExampleFrameListener::frameStarted(evt);
  blender->addTime(evt.timeSinceLastFrame);

  return ret;

 }
 //覆盖父类的键盘输入函数
 virtual bool processUnbufferedKeyInput(const FrameEvent& evt)
 {
  if (mKeyboard->isKeyDown(OIS::KC_T))
  {
   blender->blend("Shoot",AnimationBlender::BlendWhileAnimating,1.0,false);
  }

  if (mKeyboard->isKeyDown(OIS::KC_J))
  {
   blender->blend("Walk",AnimationBlender::BlendWhileAnimating,1.0,false);
  }

  if (mKeyboard->isKeyDown(OIS::KC_I))
  {
   blender->blend("Die",AnimationBlender::BlendWhileAnimating,1.0,false);
  }
  //记得调用父类的键盘处理,以使WASD仍然可用
  return ExampleFrameListener::processUnbufferedKeyInput(evt);
 }
};


class AnimBlenderApp : public ExampleApplication
{
public:
 AnimBlenderApp()
 {}

 ~AnimBlenderApp()
 {
 }

protected:

 virtual void createCamera(void)
 {
  // Create the camera
  mCamera = mSceneMgr->createCamera("PlayerCam");

  // Position it at 500 in Z direction
  mCamera->setPosition(Vector3(0,0,180));
  // Look back along -Z
  mCamera->lookAt(Vector3(0,0,-300));
  mCamera->setNearClipDistance(5);
 }

 

 // Just override the mandatory create scene method
 virtual void createScene(void)
 {

  Entity* ninjaHead = mSceneMgr->createEntity("ninja", "robot.mesh");

  SceneNode* ninjaNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
  ninjaNode->attachObject(ninjaHead);

  // Set ambient light
  mSceneMgr->setAmbientLight(ColourValue(0.5, 0.5, 0.5));

  // Create a light
  Light* l = mSceneMgr->createLight("MainLight");
  l->setPosition(20,80,50);

  //实例化一个blender并将Entity传入
  blender=new AnimationBlender(ninjaHead);
  //设置一个初始动作
  blender->init("Die");
 }

 // Create new frame listener
 void createFrameListener(void)
 {
  mFrameListener= new AnimBlenderFrameListener(mSceneMgr, mWindow, mCamera);
  mRoot->addFrameListener(mFrameListener);
 }
};

INT WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int)
{
         AnimBlenderApp app;
   app.go();
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值