Radium Engine  1.5.20
Loading...
Searching...
No Matches
Animation pipeline

Animation in Radium

The Radium Engine provides a set of classes dedicated to animation, which can be found in Core/Animation.

In Radium, the animation of any data can be done through the Ra::Core::Animation::KeyFramedValue template. It stores the data keyframes, i.e. time-value couples, which are interpolated when the value for a given time is requested.

Using KeyFramedValue to animate data

There are two ways data can be animated using Ra::Core::Animation::KeyFramedValue:

  • When the data is directly specified as a Ra::Core::Animation::KeyFramedValue. In this case, particular values at specific times can be specified to create the animation keyframes, and then used when requested:
    #include <Core/Animation/KeyFramedValue.hpp>
    #include <Core/Animation/KeyFramedValueInterpolators.hpp>
    struct MyStruct {
    MyStruct() :
    m_nonAnimatedData { 2_ra }, // initialize the non animated data as 2
    m_animatedData { 1_ra, 0_ra } // creating the animated data with value 0 at time 1
    {
    m_animatedData.insertKeyFrame( 3_ra, 2_ra ); // adding a keyframe with value 2 at time 3
    }
    Scalar fetch( Scalar time ) {
    // fetch the interpolated value for the given time
    Scalar v_t = m_animatedData.at( time, Ra::Core::Animation::linearInterpolate<Scalar> );
    // use it
    return m_nonAnimatedData * v_t;
    }
    Scalar m_nonAnimatedData;
    };
    MyStruct a;
    std::cout << a.fetch( 0_ra ) << std::endl; // prints: 0
    std::cout << a.fetch( 2_ra ) << std::endl; // prints: 2
    std::cout << a.fetch( 4_ra ) << std::endl; // prints: 4
  • When the data is not specified as a Ra::Core::Animation::KeyFramedValue but one wants to animate it. In this case, one must use the Ra::Core::Animation::KeyFramedValueController to animate the data:
    #include <Core/Animation/KeyFramedValueController.hpp>
    struct MyStructAnimator {
    MyStructAnimator( MyStruct& s ) {
    // create the keyframes for the data
    auto frames = new Ra::Core::Animation::KeyFramedValue<Scalar> { 0_ra, 0_ra };
    frames->insertKeyFrame( 4_ra, 4_ra );
    // create the controller
    m_controller.m_value = frames;
    m_controller.m_updater = [frames, &s]( const Scalar& t ) {
    // fetch the interpolated value for the given time
    auto v_t = frames->at( t, Ra::Core::Animation::linearInterpolate<Scalar> );
    // update the data
    s.m_nonAnimatedData = v_t;
    };
    }
    void update( Scalar time ) {
    m_controller.updateKeyFrame( time ); // uses the keyframes to update the data.
    }
    };
    MyStruct a; // a.m_nonAnimatedData = 2 in ctor
    MyStructAnimator b( a );
    std::cout << a.fetch( 0_ra ) << std::endl; // prints: 0
    std::cout << a.fetch( 2_ra ) << std::endl; // prints: 2
    std::cout << a.fetch( 4_ra ) << std::endl; // prints: 4
    b.update( 1_ra ); // now: a.m_nonAnimatedData = 1
    std::cout << a.fetch( 0_ra ) << std::endl; // prints: 0
    std::cout << a.fetch( 2_ra ) << std::endl; // prints: 1
    std::cout << a.fetch( 4_ra ) << std::endl; // prints: 2
    b.update( 2_ra ); // now: a.m_nonAnimatedData = 2
    std::cout << a.fetch( 0_ra ) << std::endl; // prints: 0
    std::cout << a.fetch( 2_ra ) << std::endl; // prints: 2
    std::cout << a.fetch( 4_ra ) << std::endl; // prints: 4
    b.update( 4_ra ); // now: a.m_nonAnimatedData = 4
    std::cout << a.fetch( 0_ra ) << std::endl; // prints: 0
    std::cout << a.fetch( 2_ra ) << std::endl; // prints: 4
    std::cout << a.fetch( 4_ra ) << std::endl; // prints: 8

Character Animation

Character Animation Basics

In order to animate a digital character, one may choose from two main animation techniques:

  • skeleton-based animation, in which the character's mesh is bound to an animation skeleton composed of joints, sometimes called bones.
  • cage-based animation, in which the character's mesh is bound to a set of points defining an englobing deformation cage.

In both cases, the character's mesh is bound to the handles (skeleton joints or cage points) through a set of skinning weights. During the animation, the handles transformations are first updated with respect to the animation pose, then the mesh is deformed by combining the handles transformations through the skinnning weights.

Character Animation in Radium

The Radium Engine provides the basic classes for character animation.

Starting from a Ra::Core::Animation::HandleArray, which represents a deformation metaphor, such as Ra::Core::Animation::Cage or Ra::Core::Animation::Skeleton, one must associate influence weights to each vertex of a mesh within a Ra::Core::Animation::WeightMatrix (rows representing vertices, columns handles). These influence weights, or skinning weights, are then used by a skinning algorithm to deform the associated mesh vertices w.r.t. the handles deformation defined by the animation pose. The mesh is also linked to each handle through a bindMatrix that expresses the transformation from the mesh's local frame to the handle's local frame.

In the case of character animation, an animation is a set of Ra::Core::Animation::KeyFramedValue<Ra::Core::Transform>, one for each joint, which are interpolated by default as follows:

  • the translation and scale channels are linearly interpolated;
  • the rotation channel is linearly interpolated in quaternion space.

Animations are stored in local space, i.e. each handle transformation is expressed in the handle's frame (transformation from bone's parent for skeleton bones, position in cage's frame for cage points).

Hence, in order to deform the character's mesh, one must first use the handle's bindMatrix to express the mesh vertices position into the handles' local space before combining the handles local transformations to deform the mesh vertices.

Note: As of now, cage-based character animation is not implemented in the Radium Engine.

Skeleton-Based Character Animation in Radium

The Radium Engine provides a system specific to the skeleton-based animation of a character, the Ra::Engine::Scene::SkeletonBasedAnimationSystem. This system is responsible for transmitting calls to/from animation-related processes to 2 specificRa::Engine::Scene::Component:

The Radium Engine provides 4 skinning methods: Linear-Blend Skinning, Dual-Quaternion Skinning and Center-Of-Rotation Skinning.

The Radium Engine also provides a user interface for Skeleton-based character animation parameters: the Ra::Gui::SkeletonBasedAnimationUI, which allows the user to edit animations, running parameters, skeleton display, skinning parameters and skinning weights display.

Importing skeleton-based character animation data into Radium

In order to import animation related data into Radium, the default loader would be the Ra::IO::AssimpLoader, which deals with several standard animation formats (fbx, collada, ...). In case one need to develop his own loader because using a file format not managed by the Assimp library, the Radium Engine provides a set of classes in Core/Asset defining data wrappers filled by loaders (see the specific documentation – which does not exist for now). For now, Ra::Core::Asset::FileLoaderInterfaces in the Radium Engine produce only one Ra::Core::Asset::FileData per loaded file.

Regarding character animation data, there are a few things to do in order to ensure correct import by the animation and skinning plugins: