Animation & Rigging: Non-Linear Animation System¶
Todo: Brief intro to the NLA
NLA Stack Evaluation¶
Root function: (anim_sys.cc) animsys_calculate_nla()
NLA: rewrite evaluation channel data structures (D4120)
For a given frame, there are several strips that may be evaluated and blended. We begin with an empty NlaEvalSnapshot. Strips are then blended from the bottom-most track to the top-most track, storing results within an NlaEvalChannelSnapshot. If the channel snapshot did not exist already, then its allocated and filled with default values based on the underlying channel's RNA property type. If already existing, then we overwrite relevant NlaEvalChannelSnapshot values with the blended result. In the end the NlaEvalSnapshot contains the fully blended NLA stack. A domain() pass afterwards adds default channels for those affected by the NLA at some point but were not currently evaluated. Effectively, the domain() pass forces such channels to evaluate to default. Channels that are never touched by the NLA evaluation remain untouched.
There are two special cases for whether a strip is evaluated and blended, described below.
Special Case: Action Track¶
Root function: (anim_sys.cc) nonstrip_action_fill_strip_data() (refactor)
- Treats
Hold_Forward
extrapolation the same asHold
, which causes confusion: NLA strip unexpectedly auto-switching from Hold to Hold Forward (T66946) - FModifiers frame range restrictions are taken into account for determining the evaluation bounds.
- Unlike normal strips, where the action sampling time is clamped to the bounds of the strip, the Action Track's sample time is unclamped. This allows the extrapolation settings of curves to be used.
- If there are no other strips, then the action evaluates as if there is no NLA system. The Action Track's properties (extrapolation, blending, influence) are ignored.
Special Case: Tweaked Strip¶
Root function: (anim_sys.cc) animsys_append_tweaked_strip() (refactor)
Animated Time
: Currently there is no proper UI support when the tweaked strip has animated strip time. Thus we evaluate it as if it's not animated with anExtrapolation
ofHold
. The evaluation time is also independent of the strip's start frame (anim_sys.cc) nlastrip_evaluate_controls() and unclamped.Synced Action Length
: If active, then the tweaked strip will evaluate according to the actions bounds instead of the strip's bounds. (NLA: Evaluate Tweak Strip Within Synced Action Bounds (D7533))- No other strips in the same track will evaluate.
Blending Strips¶
Related functions: (anim_sys.cc) nla_blend_value(), nla_combine_value(), nla_combine_quaternion()
We blend lower NLA stack snapshot result (lower_value) with the next strip's evaluated value (fcurve_value) accordingly:
- Replace:
blended_value = lower_value * (1.0f - influence) + (fcurve_value * influence);
- Combine: Depends on the underlying type:
case default:
case NEC_MIX_AXIS_ANGLE:
blended_value = lower_value + (fcurve_value - base_value) * influence;
case Proportional Properties:
blended_value = lower_value * powf(fcurve_value / base_value, influence);
case Quaternion:
blended_value = lower_values @ fcurve_values^(influence)
- Add:
/* Simply add the scaled value on to the stack. */
blended_value = lower_value + (fcurve_value * influence);
- Subtract:
/* Simply subtract the scaled value from the stack. */
blended_value = lower_value - (fcurve_value * influence);
- Multiply:
/* Multiply the scaled value with the stack. */
blended_value = influence* (lower_value * fcurve_value) + (1 - influence) * lower_value;
Keyframing¶
Keyframing to Action Track¶
Root function: (anim_sys.cc) nonstrip_action_fill_strip_data() (refactor)
- Ignores Extrapolation property to allow keyframing anywhere.
Keyframing to Tweaked Strip¶
Root function: (anim_sys.cc) animsys_append_tweaked_strip() (refactor)
- If strip bounds is synced to action bounds, then we ignore the NlaStrip's Extrapolation property to allow keyframing anywhere. (D7533)
- If strip has animated strip time, then we allow keyframing anywhere.
Keyframe Remapping¶
Root function: (anim_sys.cc) BKE_animsys_nla_remap_keyframe_values()
NLA: insert keyframes correctly for strips with non-Replace mode (D3927)
NLA: Feature: NLA: Evaluate Whole NLA Stack in Tweak Mode (D8296)
When the user keyframes through the viewport, they expect their pose to be preserved. What you see is what you get. The general implementation follows. We view the result of the NLA system as:
To find tweaked strip's values, we effectively have to apply each function's inverse sequentially. We do this for each blended strip above the tweaked strip, getting blend_result_after_tweak:
where we also know the value of the lower stack result. Thus we apply
the inverse of
We currently only properly support keyframe remapping when the tweaked strip's underlying action occurs once in the current frame. Keyframing through some transitions are problematic (Quaternion Combine strip to other Quaternion non-Combine strips). Until Feature: NLA: Evaluate Whole NLA Stack in Tweak Mode (D8296), tweak mode would only evaluate from the first strip up to the tweaked strip and exclude the strips above it. So before, we would only have the second equation.