AnimationBlender Class (source)

A place for users of OGRE to discuss ideas and experiences of utilitising OGRE in their games / demos / applications.
Post Reply
User avatar
bugshake
Greenskin
Posts: 111
Joined: Sat Feb 26, 2005 1:12 pm
Location: Amsterdam
Contact:

AnimationBlender Class (source)

Post by bugshake »

Hi, I'm new to Ogre so this probably isn't they most brilliant of classes but still I figure it might be useful to some. I haven't found a general "dump your source code and sweet snippets" here topic so I'm placing it here. It's a wrapper class for animation blending in a skeleton which saves some time. It only blends between 2 animations.

"OgrePCH" is the name of my "stdafx.h" file, which contains the precompiled headers of "ExampleApplication.h".

AnimationBlender.h

Code: Select all

#include "OgrePCH.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;
	Real mTimeleft, mDuration;
	BlendingTransition mTransition;	

public:
	void blend( const String &, BlendingTransition, Real );
	void addTime( Real );
	Real getProgress() { return mTimeleft/ mDuration; }
	AnimationState *getSource() { return mSource; }
	AnimationState *getTarget() { return mTarget; }
	AnimationBlender( Entity *);
	void init( const String &animation );
};
AnimationBlender.cpp

Code: Select all

#include "OgrePCH.h"
#include "AnimationBlender.h"

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

void AnimationBlender::blend( const String &animation, BlendingTransition transition, Real duration )
{
	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 );
			}
		}
		mSource->addTime( time );
	}
}

AnimationBlender::AnimationBlender( Entity *entity ) : mEntity(entity) {}
MyFrameListener::MyFramelistener(Entity *ent)

Code: Select all

mAnimationBlender = new AnimationBlender( ent ); // initialize private member
mAnimationBlender->init("Stand"); // reset to default animation
MyFrameListener::frameStarted(const FrameEvent& evt)

Code: Select all

...
if( bJump )
{
 mAnimationBlender->blend( "Jump",  AnimationBlender::BlendThenAnimate, 0.5 );
}
else if( bWalk )
{
 mAnimationBlender->blend( "Walk",  AnimationBlender::BlendWhileAnimating, 1.0 );
}
else if( bStop )
{
mAnimationBlender->blend( "Stand", AnimationBlender::BlendThenAnimate, 1.0 );
}
...
mAnimationBlender->addTime( evt.timeSinceLastFrame * 3 );
I hoped you found this useful.
User avatar
sinbad
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 19269
Joined: Sun Oct 06, 2002 11:19 pm
Location: Guernsey, Channel Islands
x 67
Contact:

Post by sinbad »

Please can you post this sort of thing on the Wiki, that's what it's for :)
KingPrawnVindaloo
Greenskin
Posts: 100
Joined: Mon Apr 18, 2005 11:05 pm
Location: Kent, UK
Contact:

Post by KingPrawnVindaloo »

Came in very handy! Thanks!
RoundSparrow
Greenskin
Posts: 145
Joined: Wed Jan 19, 2005 4:36 am
Location: Arica, Chile

Post by RoundSparrow »

Someone interested in producing a ogre demo to show what this does?
Looking to hire people working out of their home to do C++ Ogre programming. Blender artists also wanted. Looking for long-term relationships. PM on forums or contact via AIM or email Art@Arica3D.com
User avatar
jacmoe
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 20570
Joined: Thu Jan 22, 2004 10:13 am
Location: Denmark
x 179
Contact:

Post by jacmoe »

Is this wikied yet? :)
/* Less noise. More signal. */
Ogitor Scenebuilder - powered by Ogre, presented by Qt, fueled by Passion.
OgreAddons - the Ogre code suppository.
Nazrix
Halfling
Posts: 47
Joined: Tue Oct 29, 2002 9:18 pm

wiki

Post by Nazrix »

I got it working in my project. I could probably wiki it if it's ok with bugshake.
User avatar
Mr.Bloodworth
Halfling
Posts: 91
Joined: Fri Mar 25, 2005 3:19 am

Post by Mr.Bloodworth »

Nifty i would love to see what thoes does.
KACAH
Gnoblar
Posts: 8
Joined: Thu Jul 23, 2009 2:24 pm

Re: AnimationBlender Class (source)

Post by KACAH »

Hi!

I think, I've found the bug in AnimationBlender Class.

Code: Select all

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 );
         }
      }
There must be if( mTimeleft <= 0 ), not if( mTimeleft < 0 ) in this part of code, because if after mTimeleft -= time; mTimeleft is 0, old Animation State will not change, but in main cycle it will never goes (if( mTimeleft > 0 ) ), so will remain old Animation State with last active key of new Animation State, that is incorrect.

P.S. Very sorry for my english :oops:
fratouille
Gnoblar
Posts: 1
Joined: Sun May 25, 2014 5:01 pm

Re: AnimationBlender Class (source)

Post by fratouille »

KACAH wrote:Hi!

I think, I've found the bug in AnimationBlender Class.

...
This is absolutely correct. I had the same bug and came up with the same fix as you. I wanted to post about it, but a quick forum search revealed i was 5 years late :roll:
The wiki is now updated for future generations.
Post Reply