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.
Link to the Skinned Mesh
Because the goal of the skeletal animation system is to animate a skeleton, it needs a skeleton to animate. In HOOPS Luminate, the skeleton definition is contained in a RED::IMeshShape
object (details about skinned mesh shapes can be read in the doc Skinned Mesh Shapes).
The RED::ISkeletalAnimationController::AddSkinnedMesh
associates the skinned mesh we want to animate to the animation controller. Each skeletal animation controller must be linked to at least one skinned mesh.
Note
Because a controller animates a single skeleton, all the skinned meshes associated to it must have the same skeleton.
The RED::ISkeletalAnimationController::SetIsAppliedToSkeleton
option allows to specify if the result of the animation evaluation must be applied to the associated skinned meshes or not. If not, the user can query the result for each bone with the RED::ISkeletalAnimationController::GetBoneTransform
function.
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:
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 setRED::RMP_DEFAULT
with no extraction.For
RED::RMC_POSITION_X
andRED::RMC_POSITION_Y:
the translations on the X and Y axes are handled by the animation but we want a relative motion. We setRED::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 setRED::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 ) );