Source/Animation/NLA

=Animation & Rigging: Non-Linear Animation System= Todo: Brief intro to the NLA

NLA Stack Evaluation
Root function: (anim_sys.c) 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.c) nonstrip_action_fill_strip_data (refactor)


 * Treats `Hold_Forward` extrapolation the same as `Hold`, 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.c) 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 an `Extrapolation` of `Hold`. The evaluation time is also independent of the strip's start frame (anim_sys.c) 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.c) 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:

blended_value = lower_value * (1.0f - influence) + (fcurve_value * influence); 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_valuesinfluence
 * Replace:
 * Combine: Depends on the underlying type:

/* Simply add the scaled value on to the stack. */     blended_value = lower_value + (fcurve_value * influence); /* Simply subtract the scaled value from the stack. */     blended_value = lower_value - (fcurve_value * influence);
 * Add:
 * Subtract:

/* Multiply the scaled value with the stack. */     blended_value = influence* (lower_value * fcurve_value) + (1 - influence) * lower_value;
 * Multiply:

Keyframing to Action Track
Root function: (anim_sys.c) nonstrip_action_fill_strip_data (refactor)
 * Ignores Extrapolation property to allow keyframing anywhere.

Keyframing to Tweaked Strip
Root function: (anim_sys.c) 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.c) 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: final_nla_result = fN(fN-1(...f2(f1(default_values, strip1 values), strip2 values), ...), tweaked strip's values), ...), stripN) 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 : blend_result_after_tweak = ftweak(ftweak-1(...(f1(default values, strip1 values),...), striptweak-1 values), tweak strip's values) blend_result_after_tweak = ftweak(lower stack result, tweak strip's values) where we also know the value of the lower stack result. Thus we apply the inverse of ftweak to solve for tweak strip's values. It's important to note that this value is what the fcurve should evaluate to. It does not mean it's the keyframe co value that should be inserted because fmodifiers may change the fcurve value. Currently only cyclic fmodifiers are properly remapped through (Add an option to do keyframe insertion in a cycle-aware fashion). Solving this is not the job of the NLA system.

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.