Skeletal Animation

The skeletal animation system provided by HOOPS Luminate gives access to multiple interfaces that allow to animate a character in real-time. It gives the ability to build complex animation trees and dynamically animate any part of a skeleton.

The base interface of all the skeletal animation objects is RED::ISkeletalAnimationController. This class has many purposes:

  • associating the animation to external skinned meshes

  • providing functions to blend and fade the animations

  • filtering the animated bones of the skeleton

  • defining root motion policies

According to the parameter set, the animations will be evaluated at each application frame by calling the RED::ISkeletalAnimationController::Update method. This function updates the animation handled by the controller and then move the skinned meshes skeletons according to it.

The skeletal animation system exposes two main objects to the user. Both of them implement the RED::ISkeletalAnimationController interface:

  • The skeletal animation clip controller brings the animation clip capabilities to the skeletal API

  • The skeletal animation blender allows to build animation blend trees to mix various animations

More details about these two objects can be found later in this chapter.

../../../_images/bk_skeletal_animation01.png

Skeletal animation system objects and interfaces

Bone Filtering

The RED::ISkeletalAnimationController interface provides functions to filter the bones. Sometimes, you don’t want to animate the full skeleton. The bone filtering option allows to select the bone hierarchy to animate via one function: RED::ISkeletalAnimationController::SetBoneFilter.

The function lets the user choose the bone or bone hierarchy he wants to filter in or filter out.

Filtering Skeleton Bones

In this task, we want to split the animation of a humanoid skeleton between two controllers: one for the upper body and one for the lower body.

Below is the skeleton we want to animate:

../../../_images/tk_filtering_skeleton_bones01.png

The skeleton and bone indices

For each controller, we will use the RED::ISkeletalAnimationController::SetBoneFilter to filter-in and filter-out the desired bones.

// RED::Object* lowerController and upperController are skeletal animation clip controllers controlling the same skeleton.
RED::ISkeletalAnimationController* ilowerController = lowerController->As< RED::ISkeletalAnimationController >();
RED::ISkeletalAnimationController* iupperController = upperController->As< RED::ISkeletalAnimationController >();

// By default, bones are unfiltered. Just filter-out the upper body.
RC_TEST( ilowerController->SetBoneFilter( 1, true, true ) );

// Filter-out all the skeleton then filter-in the upper body.
RC_TEST( iupperController->SetBoneFilter( 0, true, true ) );
RC_TEST( iupperController->SetBoneFilter( 1, false, false ) );

In the lower body controller, we have excluded the bone hierarchy from bone 1.

In the upper body controller, we first excluded all the bones then included the bone hierarchy from bone 1.

For upper body, we could also have excluded bone 0 only, then leg bones and their hierarchies:

// The lower body filtering could be done also:
RC_TEST( iupperController->SetBoneFilter( 0, true, false ) );
RC_TEST( iupperController->SetBoneFilter( 8, true, true ) );
RC_TEST( iupperController->SetBoneFilter( 11, true, true ) );

The Skeletal Animation Clip Controller

The skeletal animation clip controller is the link between the animation clip controller seen here: ref bk_animation_basic and the skeletal system. Because it implements the RED::IAnimationClipController interface, it has all the clip-related functions like play, pause, stop, etc.. Added to this are the skeletal system options: associated mesh, bone filtering, root motion, etc..

User can access to each functionalities by requesting one or the other of the interfaces: RED::ISkeletalAnimationController or RED::IAnimationClipController.

The skeletal animation clip controller is created by the factory with the RED::Factory::CreateSkeletalAnimationClipController function and deleted with RED::Factory::DeleteInstance.

Creating an Animation Clip Controller

An animation clip controller allows to play an animation clip. It is created thanks to the RED::Factory.

// Create an animation clip controller to control a RED::AnimationClip.
RED_RC rc;
RED::Object* animController = RED::Factory::CreateAnimationClipController( *resmgr, clip, rc );
RC_TEST( rc );
RED::IAnimationClipController* ianimController = animController->As< RED::IAnimationClipController >();

The controller will allow to play the animation clip by calling its update function at each frame.

The destruction of the animation clip controller can be done like this:

// Delete the animation clip controller.
RC_TEST( RED::Factory::DeleteInstance( animController, iresmgr->GetState() ) );

The Skeletal Animation Blender

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. More details are available here: ref bk_skeletal_animation_blender.

Root Motion

Some animations move the skeleton in space. If the animation have to loop, the skinned mesh will jump from its last position to its first position. This is a problem if we want it to move in a continuous way in the scene.

The root motion policies are here to solve it. It exists several options to move the root bone of an animated skeleton in the scene. They can be configured thanks to the RED::ISkeletalAnimationController::SetRootMotionPolicy function.

Root Bone Components

Each of the skeleton root bone transformation components (RED::ROOT_MOTION_COMPONENT) can be configured individually: rotation and translations:

  • RED::RMC_ROTATION

  • RED::RMC_POSITION_X

  • RED::RMC_POSITION_Y

  • RED::RMC_POSITION_Z

Splitting them allows for example to let the animation handles its rotation correctly and extract the translations to move the skeleton in the world.

Root Motion Policies

The policies (RED::ROOT_MOTION_POLICY) are defined like this:

The RED::RMP_DEFAULT policy is the default one: the root bone is applied like any other bone leading to the issue we described previously.

The RED::RMP_CUMULATIVE policy calculates the delta transform between each frame and cumulates it in the root bone transform. By setting this policy, we fix the issue of the position reset because translation is no more absolute but relative to the last frame.

The RED::RMP_ZERO policy sets the root bone components to zero, meaning we suppress the rotation and/or position components from the root bone.

Finally, the RED::RMP_DELTA policy applies only the delta transform between each frame to the root bone. Alone, this option is not very useful. It must be associated to the last extraction parameter.

Extraction Parameter

Sometimes, it can be preferable not to move the skeleton root bone but another mesh parent shape. For instance because other objects are attached to the mesh. The ‘extract’ parameter of the RED::ISkeletalAnimationController::SetRootMotionPolicy function allows to do that.

If a component is defined as ‘extract’, its value will not be set to the skeleton root bone directly. Instead the user will be able to retrieve it via the RED::ISkeletalAnimationController::GetRootMotionMatrix function. It will then be free to apply it on the HOOPS Luminate object he wants. This is where the RED::RMP_DELTA policy become useful.

Defining Root Motion Policies for a Walking Character

In this task, let’s take an example of root motion policies for a biped character walking on a ground:

  • For RED::RMC_ROTATION: the rotation is handled by the animation and stays absolute. We set RED::RMP_DEFAULT with no extraction.

  • For RED::RMC_POSITION_X and RED::RMC_POSITION_Y: the translations on the X and Y axes are handled by the animation but we want a relative motion. We set RED::RMP_DELTA with extraction and apply the delta transform ourselves on the parent shape.

  • For RED::RMC_POSITION_Z: the character should follow the ground level, the translation on the Z axis is handled externally. We set RED::RMP_ZERO.

// RED::Object* animController is a skeletal animation clip controller.
RED::ISkeletalAnimationController* iskeletalController = animController->As< RED::ISkeletalAnimationController >();

// Rotation is handled by the animation.
RC_TEST( iskeletalController->SetRootMotionPolicy( RED::RMC_ROTATION, RED::RMP_DEFAULT, false ) );

// Translations on X and Y axes are handled by the animation by applied on an external shape.
RC_TEST( iskeletalController->SetRootMotionPolicy( RED::RMC_POSITION_X, RED::RMP_DELTA, true ) );
RC_TEST( iskeletalController->SetRootMotionPolicy( RED::RMC_POSITION_Y, RED::RMP_DELTA, true ) );

// Translation on Z axis is handled externally.
RC_TEST( iskeletalController->SetRootMotionPolicy( RED::RMC_POSITION_Z, RED::RMP_ZERO, false ) );