Blending Skeletal Animations Together

The skeletal animation blender is the object that allows to build complex animation blend trees. Its goal is to mix several skeletal animation controllers together to produce smooth transitions between them or to merge partial skeletal animations.

Properties of the Skeletal Animation Blender

This object implements both interfaces: RED::ISkeletalAnimationController and RED::ISkeletalAnimationBlender.

The blender contains a list of RED::ISkeletalAnimationController child objects. Because the blender also implements this interface, it can be added to the children list of other blenders allowing to build animation trees.

The RED::ISkeletalAnimationBlender interface provides functions to:

  • Manage the list of children: RED::ISkeletalAnimationBlender::AddController, RED::ISkeletalAnimationBlender::RemoveController;

  • Blend between them: RED::ISkeletalAnimationBlender::BlendTo. This function make the transition between a currently played animation and another.

The RED::ISkeletalAnimationController objects hold all the parameters needed by the blender to apply its rules:

  • Blend weight (RED::ISkeletalAnimationController::GetBlendWeight) is the importance of the animation amongst all the others (sum must be 1 inside a blender);

  • Fade parameters (RED::ISkeletalAnimationController::SetFadeParameters) are the targeted blend weight and a duration to reach this weight;

  • Group (RED::ISkeletalAnimationController::SetGroup) allows to blend animations by group.

../../../../_images/bk_skeletal_animation_blender01.png

An example of animation tree

In the previous figure, we can imagine a character animation blend tree where the animations of the group 1 are body animations like idle and walk and animations of the group 2 are head animations. The group 1 controllers have filtered-out the head bones whereas the group 2 controllers have filtered-out the body bones.

Like every RED::ISkeletalAnimationController object, the blender needs to be updated at each frame by calling its RED::ISkeletalAnimationController::Update function. One important point: it handles the update of its children, so no need for the user to call the children update functions. Only the animation tree root blender update needs to be done.

The RED::ISkeletalAnimationController::SetIsAppliedToSkeleton method specifies if the result of the animation evaluation must be applied to the associated skinned mesh or not. Generally, in a blend tree, only the root blender result must be applied to the skeleton, all the children should set this option to false.

The skeletal animation blender is created by the factory with the RED::Factory::CreateSkeletalAnimationBlender function and deleted with RED::Factory::DeleteInstance.

Creating a Skeletal Animation Blender

A skeletal animation blender is an object used to combine several skeletal animations and mix their outputs dynamically. It is created using the RED::Factory.

// Create a skeletal animation blender.
RED_RC rc;
RED::Object* animBlender = RED::Factory::CreateSkeletalAnimationBlender( *resmgr, rc );
RC_TEST( rc );

// It implements both interfaces.
RED::ISkeletalAnimationBlender* iblender = animBlender->As< RED::ISkeletalAnimationBlender >();
RED::ISkeletalAnimationController* iskeletalController = animBlender->As< RED::ISkeletalAnimationController >();

The destruction of the animation blender is done like this:

// Delete the skeletal animation blender.
RC_TEST( RED::Factory::DeleteInstance( animBlender, iresmgr->GetState() ) );

Doing Smooth Transitions between Skeletal Animations

The blender main task is to do smooth transitions between animation clips. The RED::ISkeletalAnimationBlender::BlendTo function gives the user the ability to do that.

Let’s have a blender containing two skeletal animation clip controllers: idle and walk.

At the initialization step, we want the idle to be played:

  • Idle animation blend weight is set to 1 (RED::ISkeletalAnimationController::SetBlendWeight)

  • Walk animation blend weight is set to 0

// RED::Object* idleController and walkController are skeletal animation clip controllers controlling the same skeleton.
RED::ISkeletalAnimationController* iidleController = idleController->As< RED::ISkeletalAnimationController >();
RED::ISkeletalAnimationController* iwalkController = walkController->As< RED::ISkeletalAnimationController >();

// Add the animations to the blender.
RED::ISkeletalAnimationBlender* iblender = blender->As< RED::ISkeletalAnimationBlender >();
RC_TEST( iblender->AddController( idleController ) );
RC_TEST( iblender->AddController( walkController ) );

// At initialization, idle animation is played.
iidleController->SetBlendWeight( 1.0 );
iwalkController->SetBlendWeight( 0.0 );

After calling the RED::ISkeletalAnimationController::Update method, we see that only the idle animation is applied to the mesh.

Now, if we invert the blend weights, the walk animation will be played but there will be no transitions to them.

Fortunately, the blender is here to ease the task thanks to its RED::ISkeletalAnimationBlender::BlendTo function. By calling it, the user can specify the target animation, the duration of the transition and the target blend weight. It will fade-out the currently played animations and fade-in the target animation.

// Send a message to the blender: blend to the walk animation in 0.3s.
RC_TEST( iblender->BlendTo( walkController, 0.3, 1.0 ) );

Note

The cross-fade is done on animations belonging to the same group (RED::ISkeletalAnimationController::SetGroup).

This operation is equivalent to calling the function RED::ISkeletalAnimationController::SetFadeParameters of both idle and walk animation controllers.

Merging Skeletal Animations onto Different Body Parts

Thanks to the blender, any user can split the skeleton into body parts and animate them separately. Doing it is simple: filter the bones and define groups.

Let’s have a blender containing two skeletal animation clip controllers: body and head.

At the initialization step, we want both to be played but on different bones:

  • Filter-out the body bones of the head animation (RED::ISkeletalAnimationController::SetBoneFilter)

  • Filter-out the head bones of the body animation

  • Set different groups for head and body (RED::ISkeletalAnimationController::SetGroup)

  • Set both blend weights to 1 (RED::ISkeletalAnimationController::SetBlendWeight)

// RED::Object* headController and bodyController are skeletal animation clip controllers controlling the same skeleton.
RED::ISkeletalAnimationController* iheadController = headController->As< RED::ISkeletalAnimationController >();
RED::ISkeletalAnimationController* ibodyController = bodyController->As< RED::ISkeletalAnimationController >();

// Add the animations to the blender.
RED::ISkeletalAnimationBlender* iblender = blender->As< RED::ISkeletalAnimationBlender >();
RC_TEST( iblender->AddController( headController ) );
RC_TEST( iblender->AddController( bodyController ) );

// Filter the head and body bones (head bone index is 3).
RC_TEST( iheadController->SetBoneFilter( 0, true, true ) );
RC_TEST( iheadController->SetBoneFilter( 3, false, true ) );
RC_TEST( ibodyController->SetBoneFilter( 3, true, true ) );

// Make them belong to a different group (useful for later BlendTo).
iheadController->SetGroup( 0 );
ibodyController->SetGroup( 1 );

// Both animations are played. We can do this because they animate different bones.
iheadController->SetBlendWeight( 1.0 );
ibodyController->SetBlendWeight( 1.0 );

After calling the RED::ISkeletalAnimationController::Update method, we see that both animations are played, each on its dedicated body part.

Additive Animations

The RED::ISkeletalAnimationController animations can be flagged to be additive (RED::ISkeletalAnimationController::SetAdditive).

Additive animations are not blended like other animations, they are not cross-faded. Instead, as indicated by its name, they are added to the currently played animations. They are very useful to add small variations.

The blend weight of additive animation is used to control the amount of the additive animation we want to add: 0 being not added, 1 being totally added.