History: AnimationBlender
Source of version: 7 (current)
Copy to clipboard
{maketoc}
!Introduction
To __blend__ is to fade between two states. In the animation process it allows to create a transition between two animations. With this sample code, you can blend animations in 3 ways:
- __BlendSwitch__: just switch to the second/destination animation (stop the first/source and start the second).
- __BlendWhileAnimating__: cross-fade, blend source animation out while blending destination animation in.
- __BlendThenAnimate__: pick source's current frame and blend it with destination's first frame.
Here is a ((Mogre)) port: ((MOGRE AnimationBlender))
!Source Code
!!AnimationBlender.h
{CODE(wrap="1", colors="c++")} 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 );
};
{CODE}
!!AnimationBlender.cpp
{CODE(wrap="1", colors="c++")} 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)
{
}
{CODE}
!AnimationBlender.cpp for Ogre 1.2 (minor changes)
{CODE(wrap="1", colors="c++")} 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)
{
}
{CODE}
!Utilisation
To use the functions, you can do something like this:
If you want your character to walk for instance you could do this:
{CODE(wrap="1", colors="c++")} 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;
}
}
{CODE}
!Transition Bug
{QUOTE()}!!Bug in Animation Blender
I have noticed that the AnimationBlender dies/freezes (the animation not the application) when the blend function is called between the transition time of the last blend.
I am specifically talking about "BlendWhileAnimating" state here.
Work around posted here:
[http://www.ogre3d.org/forums/viewtopic.php?f=2&t=53911&p=366582#p366582]{QUOTE}
---
Alias: (alias(Talk:AnimationBlender))