永远也不完美的程序

不断学习,不断实践,不断的重构……

常用链接

统计

积分与排名

好友链接

最新评论

Ogre骨骼动画融合(转)

转自http://www.guibian.com/article.asp?id=50

很多商业引擎都支持骨骼动画融合这个功能。
融合可以让你的动画自然过渡到下一个动画,你可以精确控制下一个动画播放时间等信息。

Ogre其实也支持动画的融合,只是Ogre并没有给出代码级的支持。
PS:Ogre很有意思,他天然支持很多东西,但是都没有实现出来,需要你来实现。

好的,下面来通过代码看看如果进行一个简单的骨骼动画融合。
程序代码 程序代码


class AnimationBlender
{
public:
    enum BlendingTransition  //不同的混合方式
    {
        BlendSwitch,         // 直接切换到目标动画
        BlendWhileAnimating,   // 交叉淡入淡出(源动画比例缩小,同时目标动画比例增大)
        BlendThenAnimate      // 淡出源动画到目标动画第一帧,然后开始目标动画
    };

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 );
    void addTime( Real );
    Real getProgress() { return mTimeleft/ mDuration; }
    AnimationState *getSource() { return mSource; }
    AnimationState *getTarget() { return mTarget; }
    AnimationBlender( Entity *);
    void init( const String &animation );
};


实现的代码
程序代码 程序代码

void AnimationBlender::init(const String &animation)
{
    //初始化所有动作的AnimationState
    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
    mSource = mEntity->getAnimationState( animation );
    mSource->setEnabled(true);
    mSource->setWeight(1);
    mTimeleft = 0;
    mDuration = 1;
    mTarget = 0;
    complete=false;
}
void AnimationBlender::blend( const String &animation, BlendingTransition transition, Real duration, bool l )
{
    loop=l; //设置是否需要循环
    if( transition == AnimationBlender::BlendSwitch )
    {//如果混合方式为直接切换,改变mSource 即可
        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 ) //前一次的混合尚未结束
        {
            if( newTarget == mTarget )
            {
                // 新的目标就是正在混合中的目标,什么也不做
            }
            else if( newTarget == mSource )
            {
                // 新的目标是源动画,直接go back
                mSource = mTarget;
                mTarget = newTarget;
                mTimeleft = mDuration - mTimeleft;
            }
            else
            {
                // 现在newTarget是真的新的动画了
                if( mTimeleft < mDuration * 0.5 ) //上一次的混合进度还未超过一半
                {
                    // 简单替换Target就行了
                    mTarget->setEnabled(false);
                    mTarget->setWeight(0);
                }
                else //如果已经过半,旧的target成为新的source
                {

                    mSource->setEnabled(false);
                    mSource->setWeight(0);
                    mSource = mTarget;
                }
                mTarget = newTarget;
                mTarget->setEnabled(true);
                mTarget->setWeight( 1.0 - mTimeleft / mDuration );
                mTarget->setTimePosition(0);
            }
        }
        else //上次的混合已经结束,当前未处于混合状态中
        {
            mTransition = transition;
            mTimeleft = mDuration = duration;
            mTarget = newTarget;
            mTarget->setEnabled(true);
            mTarget->setWeight(0);
            mTarget->setTimePosition(0);
        }
    }
}
void AnimationBlender::addTime( Real time )
{
    if( mSource != 0 ) //若无AnimationState则不进行操作
    {
        if( mTimeleft > 0 ) //两个动画仍在混合过程中
        {
            mTimeleft -= time;
            if( mTimeleft < 0 )
            {
                // 混合完毕,切换到目标动画
                mSource->setEnabled(false);
                mSource->setWeight(0);
                mSource = mTarget;
                mSource->setEnabled(true);
                mSource->setWeight(1);
                mTarget = 0;
            }
            else
            {
                // 仍然处于混合状态中,改变两个动画的权值
                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){}



哈哈,相信你已经看出来,这段代码有很多很多的不足。比如只有骨骼动画的融合,
而且还是写死的,必须0.5。

我现在已经扩展了这些功能,包括做到了骨骼动画与定点动画的融合,变形动画的融合等等。而且可以精确控制融合的时机。你有什么好想法也欢迎与我讨论

posted on 2010-01-18 10:47 狂烂球 阅读(2167) 评论(1)  编辑 收藏 引用 所属分类: 图形编程

评论

# re: Ogre骨骼动画融合(转) 2011-06-01 09:09 ruibin

正在研究相关知识,能否将你实现的代码传一份,谢谢!!!!
邮箱:653396438@qq.com  回复  更多评论   


只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理