User animation
The user_animation
sub-compound is where you define the advanced transformation logic such as space switching, blending, IK solving, or simply transfer control transforms to joints or vice versa.
While user_setup
defines the rest state and hierarchy of rig entities, user_animation
allows you to use attributes and transforms to perform procedural logic and schedule control and joint transform updates.
You can find some nodes designed specifically for use in this section in the Rigging::Module::Animation namespace in either the Tab menu or Node Library.
When authoring this section, ensure that the edit_mode module parameter is enabled so the find_* nodes' internal lookup results are recomputed when the graph is evaluated.
Concept
In this section, you are responsible for defining the advanced transformation logic of your module. You are provided with input data, including transforms, and you are expected to use them, compute new matrices, and schedule transform updates.
At this stage in the module evaluation, control and joint transforms have already been computed. They have inherited from their respective parents and have had their operator matrices applied.
As a result, if your module does not require anything beyond pure forward kinematics, you may not need to define any logic within user_animation
.
The user_animation
layout includes:
AnimationInputs
(1): A struct containing all control and joint data, along with any custom setup or animation data. This node provides direct access to the raw data, and is not required for most workflows. In many cases, it can be deleted.
set_update_settings
(2): A node where you can connectset_transform_update_settings
nodes in order to schedule control and joint transform updates.user_animation_inputs
(3): An input node to expose parameters at the top level of the module so that you can control youruser_animation
logic from there. You can create multiple input nodes as needed.
Accessing Attributes
You can use control attributes defined in user_setup
to drive transformation logic in user_animation
.
Attribute values are passed from the host application through the module’s inputs
port as arrays and are available inside the AnimationInputs
struct, along with their corresponding attribute descriptions.
Although you can access the raw data directly, the recommended way to retrieve attribute values is by using the find_attribute
or find_all_attributes
nodes.
Connecting animation data
To start, connect the output
port from the AnimationInputs
struct to the animation_inputs
input of each find_attribute
or find_all_attributes
node you intend to use.
Alternatively, you can use the animation_inputs
port on the input node directly if you don't need to access raw data from the AnimationInputs
struct.
Specifying type and search
Start by defining the data type of the attribute you want to query. To do this, right-click on the default_and_type
port of find_attribute
and set the type under Value Types.
Then, use the find
port to specify which attribute to retrieve. This input accepts a search expression that filters attribute names. The search supports wildcards (*) and negation (!). For example:
- "
*blend*
" will match any attribute whose name contains "blend" - "
weight*
" will match any attribute whose name starts with "weight" - "
*blend* !*weight*
" will match any attribute whose name contains "blend" but not "weight" - "
weight
" will match any attribute named exactly "weight"
The first attribute matching the type and the expression is returned by find_attribute
, while find_all_attributes
returns all matches as an array.
Accessing Transforms
To access the transforms of the entities defined in your module, the recommended method is to use the find_transform
or find_all_transforms
nodes.
These nodes let you search controls and joints by name or tag, and return the transform and relative index for the matching entities.
You can use find_transform
to get a single control or joint transform, and use find_all_transforms
to retrieve an array of control or joint transforms.
The returned transform(s) is of type Core::Transform::Transform
. You can access the individual transform matrix components using the access_transform_matrices
nodes.
Connecting animation data
To start, connect the output port from the AnimationInputs
struct to the animation_inputs
input of each find_transform
or find_all_transforms
node you intend to use.
Alternatively, if you do not need access to raw data from the AnimationInputs
struct, you can connect the animation_inputs
port from the input node directly.
Specifying a Search
First, set the kind
to indicate the type of entity you want to search for. This should be either Control or Joint.
Next, set the search_method
to determine how the control or joint will be searched. You can choose to search By Name or By Tags.
When using the By Name method, the find string is interpreted as a search expression that matches control or joint names. Wildcards (*) and negation (!) are supported. Examples:
- "
foot
" will match the first transform named exactly "foot" - "
leg_*
" will match the first transform whose name starts with "leg_" - "
*hand*
" will match the first transform whose name contains "hand"
When using the By Tags method, the find string is interpreted as a comma-separated list of tags. This is useful when you want query entities without relying on specific names.
For example, specifying "root, anchor, hook
" will search for a entity that matches one or more of those tags.
- If
find_all_tags
is disabled, the first transform containing any of the listed tags will match. - If
find_all_tags
is enabled, the first transform containing all of the listed tags will match.
Updating Transforms
Once you have accessed the necessary transforms and attributes, you can use them to perform operations such as blending, constraints, or IK solving.
After computing the result matrices, use the set_transform_update_settings
node to define which controls or joints should be updated and how.
These update settings must be connected to the set_update_settings
node, which applies the updates in the order they are received.
Scheduling Transform Updates
To apply transformation results to controls or joints, use the set_transform_update_settings
node to build a set of update instructions.
If you want to update all controls or joints at once, you can leave the transforms_to_update
input empty and connect an array of matrices to the update_matrices
input, assuming the array matches the target entity count, and its ordering.
If you want to update only a subset of transforms or apply matrices out of order, in addition to the update_matrices
, you must provide the correct indices in the transforms_to_update
input. Each index corresponds to the index of an entity as returned by the find_transform
or find_all_transforms
nodes.
The way update_matrices
are applied depends on the update_method
parameter, which defines both how the matrices are interpreted and which component of the transform they will update. Available methods include From Pivot, From Operator, From Local, and From World.
When the update settings are ready, connect the output to the set_update_settings
node. Multiple update settings can be connected in sequence and will be applied in the order they are received during evaluation.
Using Custom Data
The user_animation
sub-compound also allows you to access and generate custom data using Bifrost Objects, just like in user_setup
.
On the AnimationInputs
struct, the custom
group provides access to two Bifrost objects:
setup
contains any custom setup data created in theuser_setup
sub-compound.animation
contains any custom animation data passed through the module’sinputs.animation.custom
.
You can retrieve values from these objects using the get_property
node and use the results as part of your animation logic.
To store custom animation data, use the set_property
node to set values in a Bifrost object. The object should be connected to the custom
input of the set_update_settings
node.
Custom animation data is output through the module’s outputs.animation.custom
port, making it available to downstream modules and the host application.
Example Use Case
The example below demonstrates how to access custom setup data defined in the user_setup
sub-compound, and create custom animation data from the user_animation logic.
Custom setup data is accessed from SetupInputs.custom.setup
(1) using get_property
nodes (2), in this case, this is used as layer weights and biases for a neural network solver. The solver's computed loss is stored using a set_property
node (3), then passed to custom port of the set_update_settings
node (4) so that is can be accessed outside of this module.
This is just one example. The same workflow can be used to handle any kind of custom data.