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 ------------------------------------------

#ifndef __ANIMATION_BLENDER_H__
#define __ANIMATION_BLENDER_H__
#include <Ogre.h>
using namespace Ogre;
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 );
};
#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;
}
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)
{
}

------------------------------------------------------------AnimationBlenderDemo.h---------------------

#ifndef __ANIMATIONBLENDER_DEMO_H__
#define __ANIMATIONBLENDER_DEMO_H__
#include "ExampleApplication.h"
#include "AnimationBlender.h"
class AnimationBlenderDemoFrameListener : public ExampleFrameListener
{
protected:
 Entity* mNinjaEnt;
 AnimationBlender* mAnimationBlender;
 bool walking, 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;
  }
 }
 bool frameRenderingQueued(const FrameEvent& 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);
  return true;
 }
};
class AnimationBlenderDemoApp : public ExampleApplication
{
public:
 AnimationBlenderDemoApp() {}
protected:
 Entity* mNinjaEnt;
 void createScene();
 void createFrameListener()
 {
  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 __cplusplus
extern "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 )
#else
 int 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
  }
  return 0;
 }
#ifdef __cplusplus
}
#endif

-----------------------------------------------------------------------------------------------------------------------------------

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

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/summericeyl/archive/2010/05/02/5550226.aspx

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值