NLA: Extract nlasnapshot_blend()
Refactor //nlastrip_evaluate_actionclip()// and //nlaeval_blend_value()// into //nlasnapshot_blend()//, //nlastrip_evaluate_actionclip()//, //nlasnapshot_from_action()//. **Motivations**: * {T83615} Requires reading all pose bone fcurves before being able to apply pre-blend transforms. The function //nlasnapshot_from_action()// achieves this. This effectively removed the need to specially handle Quaternion blend queuing so that code has been removed. * {D8296} Adds support for keyframe remapping through an upper stack of strips. Instead of introducing a variant of the form: //nlastrip_evaluate_actionclip_inverted_get_lower()//, //nlastrip_evaluate_actionclip()// will later be extended to take an `evaluation_mode` as input to avoid duplicating the recursion functions related to //nlastrip_evaluate()//. * //nlasnapshot_blend()// will eventually have variants of //nlasnapshot_blend_get_inverted_lower_snapshot()// and //nlasnapshot_blend_get_inverted_upper_snapshot()// which are all independent of NlaStrips and NlaTracks, further simplifying the blending implementation. Ideally, //nlastrip_evaluate()// would get renamed to //nlasnapshot_blend_strip()// but that'll be a later patch to avoid unnecessary patches slowing the review of more important patches. No User-side Functional changes Reviewed By: sybren, #animation_rigging Differential Revision: https://developer.blender.org/D10220
This commit is contained in:
parent
264af1519e
commit
40b7929cc0
Notes:
blender-bot
2023-02-13 19:39:59 +01:00
Referenced by commitc48360c255
, Fix: NLA Blends Non-Animated Upper Channel Values Referenced by commit5bc9ddd98b
, Fix T85380: NLA Evaluation Missing Null Check Referenced by commit0352546cdd
, NLA: Fix nlasnapshot_blend() Misplaced Null Check Referenced by issue #85380, Crash on opening a specific file
|
@ -1173,10 +1173,6 @@ static void nlaeval_snapshot_free_data(NlaEvalSnapshot *snapshot)
|
|||
static void nlaevalchan_free_data(NlaEvalChannel *nec)
|
||||
{
|
||||
nlavalidmask_free(&nec->domain);
|
||||
|
||||
if (nec->blend_snapshot != NULL) {
|
||||
nlaevalchan_snapshot_free(nec->blend_snapshot);
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize a full NLA evaluation state structure. */
|
||||
|
@ -1661,92 +1657,6 @@ static bool nla_combine_quaternion_get_inverted_strip_values(const float lower_v
|
|||
return true;
|
||||
}
|
||||
|
||||
/* Data about the current blend mode. */
|
||||
typedef struct NlaBlendData {
|
||||
NlaEvalSnapshot *snapshot;
|
||||
int mode;
|
||||
float influence;
|
||||
|
||||
NlaEvalChannel *blend_queue;
|
||||
} NlaBlendData;
|
||||
|
||||
/* Queue the channel for deferred blending. */
|
||||
static NlaEvalChannelSnapshot *nlaevalchan_queue_blend(NlaBlendData *blend, NlaEvalChannel *nec)
|
||||
{
|
||||
if (!nec->in_blend) {
|
||||
if (nec->blend_snapshot == NULL) {
|
||||
nec->blend_snapshot = nlaevalchan_snapshot_new(nec);
|
||||
}
|
||||
|
||||
nec->in_blend = true;
|
||||
nlaevalchan_snapshot_copy(nec->blend_snapshot, &nec->base_snapshot);
|
||||
|
||||
nec->next_blend = blend->blend_queue;
|
||||
blend->blend_queue = nec;
|
||||
}
|
||||
|
||||
return nec->blend_snapshot;
|
||||
}
|
||||
|
||||
/* Accumulate (i.e. blend) the given value on to the channel it affects. */
|
||||
static bool nlaeval_blend_value(NlaBlendData *blend,
|
||||
NlaEvalChannel *nec,
|
||||
int array_index,
|
||||
float value)
|
||||
{
|
||||
if (nec == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!nlaevalchan_validate_index_ex(nec, array_index)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NlaEvalChannelSnapshot *nec_snapshot = nlaeval_snapshot_ensure_channel(blend->snapshot, nec);
|
||||
float *p_value = &nec_snapshot->values[array_index];
|
||||
|
||||
if (blend->mode == NLASTRIP_MODE_COMBINE) {
|
||||
/* Quaternion blending is deferred until all sub-channel values are known. */
|
||||
if (nec->mix_mode == NEC_MIX_QUATERNION) {
|
||||
NlaEvalChannelSnapshot *blend_snapshot = nlaevalchan_queue_blend(blend, nec);
|
||||
|
||||
blend_snapshot->values[array_index] = value;
|
||||
}
|
||||
else {
|
||||
float base_value = nec->base_snapshot.values[array_index];
|
||||
|
||||
*p_value = nla_combine_value(nec->mix_mode, base_value, *p_value, value, blend->influence);
|
||||
}
|
||||
}
|
||||
else {
|
||||
*p_value = nla_blend_value(blend->mode, *p_value, value, blend->influence);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Finish deferred quaternion blending. */
|
||||
static void nlaeval_blend_flush(NlaBlendData *blend)
|
||||
{
|
||||
NlaEvalChannel *nec;
|
||||
|
||||
while ((nec = blend->blend_queue)) {
|
||||
blend->blend_queue = nec->next_blend;
|
||||
nec->in_blend = false;
|
||||
|
||||
NlaEvalChannelSnapshot *nec_snapshot = nlaeval_snapshot_ensure_channel(blend->snapshot, nec);
|
||||
NlaEvalChannelSnapshot *blend_snapshot = nec->blend_snapshot;
|
||||
|
||||
if (nec->mix_mode == NEC_MIX_QUATERNION) {
|
||||
nla_combine_quaternion(
|
||||
nec_snapshot->values, blend_snapshot->values, blend->influence, nec_snapshot->values);
|
||||
}
|
||||
else {
|
||||
BLI_assert(!"mix quaternion");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Blend the specified snapshots into the target, and free the input snapshots. */
|
||||
static void nlaeval_snapshot_mix_and_free(NlaEvalData *nlaeval,
|
||||
NlaEvalSnapshot *out,
|
||||
|
@ -1857,6 +1767,45 @@ static void nlaeval_fmodifiers_split_stacks(ListBase *list1, ListBase *list2)
|
|||
|
||||
/* ---------------------- */
|
||||
|
||||
/** Fills \a r_snapshot with the \a action's evaluated fcurve values with modifiers applied. */
|
||||
static void nlasnapshot_from_action(PointerRNA *ptr,
|
||||
NlaEvalData *channels,
|
||||
ListBase *modifiers,
|
||||
bAction *action,
|
||||
const float evaltime,
|
||||
NlaEvalSnapshot *r_snapshot)
|
||||
{
|
||||
FCurve *fcu;
|
||||
|
||||
action_idcode_patch_check(ptr->owner_id, action);
|
||||
|
||||
/* Evaluate modifiers which modify time to evaluate the base curves at. */
|
||||
FModifiersStackStorage storage;
|
||||
storage.modifier_count = BLI_listbase_count(modifiers);
|
||||
storage.size_per_modifier = evaluate_fmodifiers_storage_size_per_modifier(modifiers);
|
||||
storage.buffer = alloca(storage.modifier_count * storage.size_per_modifier);
|
||||
|
||||
const float modified_evaltime = evaluate_time_fmodifiers(
|
||||
&storage, modifiers, NULL, 0.0f, evaltime);
|
||||
|
||||
for (fcu = action->curves.first; fcu; fcu = fcu->next) {
|
||||
if (!is_fcurve_evaluatable(fcu)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NlaEvalChannel *nec = nlaevalchan_verify(ptr, channels, fcu->rna_path);
|
||||
|
||||
NlaEvalChannelSnapshot *necs = nlaeval_snapshot_ensure_channel(r_snapshot, nec);
|
||||
if (!nlaevalchan_validate_index_ex(nec, fcu->array_index)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
float value = evaluate_fcurve(fcu, modified_evaltime);
|
||||
evaluate_value_fmodifiers(&storage, modifiers, fcu, &value, evaltime);
|
||||
necs->values[fcu->array_index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/* evaluate action-clip strip */
|
||||
static void nlastrip_evaluate_actionclip(PointerRNA *ptr,
|
||||
NlaEvalData *channels,
|
||||
|
@ -1864,10 +1813,8 @@ static void nlastrip_evaluate_actionclip(PointerRNA *ptr,
|
|||
NlaEvalStrip *nes,
|
||||
NlaEvalSnapshot *snapshot)
|
||||
{
|
||||
ListBase tmp_modifiers = {NULL, NULL};
|
||||
|
||||
NlaStrip *strip = nes->strip;
|
||||
FCurve *fcu;
|
||||
float evaltime;
|
||||
|
||||
/* sanity checks for action */
|
||||
if (strip == NULL) {
|
||||
|
@ -1879,54 +1826,20 @@ static void nlastrip_evaluate_actionclip(PointerRNA *ptr,
|
|||
return;
|
||||
}
|
||||
|
||||
action_idcode_patch_check(ptr->owner_id, strip->act);
|
||||
ListBase tmp_modifiers = {NULL, NULL};
|
||||
|
||||
/* join this strip's modifiers to the parent's modifiers (own modifiers first) */
|
||||
nlaeval_fmodifiers_join_stacks(&tmp_modifiers, &strip->modifiers, modifiers);
|
||||
|
||||
/* evaluate strip's modifiers which modify time to evaluate the base curves at */
|
||||
FModifiersStackStorage storage;
|
||||
storage.modifier_count = BLI_listbase_count(&tmp_modifiers);
|
||||
storage.size_per_modifier = evaluate_fmodifiers_storage_size_per_modifier(&tmp_modifiers);
|
||||
storage.buffer = alloca(storage.modifier_count * storage.size_per_modifier);
|
||||
NlaEvalSnapshot strip_snapshot;
|
||||
nlaeval_snapshot_init(&strip_snapshot, channels, NULL);
|
||||
|
||||
evaltime = evaluate_time_fmodifiers(&storage, &tmp_modifiers, NULL, 0.0f, strip->strip_time);
|
||||
nlasnapshot_from_action(
|
||||
ptr, channels, &tmp_modifiers, strip->act, strip->strip_time, &strip_snapshot);
|
||||
nlasnapshot_blend(
|
||||
channels, snapshot, &strip_snapshot, strip->blendmode, strip->influence, snapshot);
|
||||
|
||||
NlaBlendData blend = {
|
||||
.snapshot = snapshot,
|
||||
.mode = strip->blendmode,
|
||||
.influence = strip->influence,
|
||||
};
|
||||
|
||||
/* Evaluate all the F-Curves in the action,
|
||||
* saving the relevant pointers to data that will need to be used. */
|
||||
for (fcu = strip->act->curves.first; fcu; fcu = fcu->next) {
|
||||
|
||||
if (!is_fcurve_evaluatable(fcu)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* evaluate the F-Curve's value for the time given in the strip
|
||||
* NOTE: we use the modified time here, since strip's F-Curve Modifiers
|
||||
* are applied on top of this.
|
||||
*/
|
||||
float value = evaluate_fcurve(fcu, evaltime);
|
||||
|
||||
/* apply strip's F-Curve Modifiers on this value
|
||||
* NOTE: we apply the strip's original evaluation time not the modified one
|
||||
* (as per standard F-Curve eval)
|
||||
*/
|
||||
evaluate_value_fmodifiers(&storage, &tmp_modifiers, fcu, &value, strip->strip_time);
|
||||
|
||||
/* Get an NLA evaluation channel to work with,
|
||||
* and accumulate the evaluated value with the value(s)
|
||||
* stored in this channel if it has been used already. */
|
||||
NlaEvalChannel *nec = nlaevalchan_verify(ptr, channels, fcu->rna_path);
|
||||
|
||||
nlaeval_blend_value(&blend, nec, fcu->array_index, value);
|
||||
}
|
||||
|
||||
nlaeval_blend_flush(&blend);
|
||||
nlaeval_snapshot_free_data(&strip_snapshot);
|
||||
|
||||
/* unlink this strip's modifiers from the parent's modifiers again */
|
||||
nlaeval_fmodifiers_split_stacks(&strip->modifiers, modifiers);
|
||||
|
@ -2597,6 +2510,67 @@ static void animsys_calculate_nla(PointerRNA *ptr,
|
|||
|
||||
/* ---------------------- */
|
||||
|
||||
/** Blends the \a lower_snapshot with the \a upper_snapshot into \a r_blended_snapshot according
|
||||
* to the given \a upper_blendmode and \a upper_influence. */
|
||||
void nlasnapshot_blend(NlaEvalData *eval_data,
|
||||
NlaEvalSnapshot *lower_snapshot,
|
||||
NlaEvalSnapshot *upper_snapshot,
|
||||
const short upper_blendmode,
|
||||
const float upper_influence,
|
||||
NlaEvalSnapshot *r_blended_snapshot)
|
||||
{
|
||||
nlaeval_snapshot_ensure_size(r_blended_snapshot, eval_data->num_channels);
|
||||
|
||||
const bool zero_upper_influence = IS_EQF(upper_influence, 0.0f);
|
||||
|
||||
LISTBASE_FOREACH (NlaEvalChannel *, nec, &eval_data->channels) {
|
||||
const int length = nec->base_snapshot.length;
|
||||
|
||||
NlaEvalChannelSnapshot *upper_necs = nlaeval_snapshot_get(upper_snapshot, nec->index);
|
||||
NlaEvalChannelSnapshot *lower_necs = nlaeval_snapshot_get(lower_snapshot, nec->index);
|
||||
if (upper_necs == NULL && lower_necs == NULL) {
|
||||
continue;
|
||||
}
|
||||
|
||||
NlaEvalChannelSnapshot *result_necs = nlaeval_snapshot_ensure_channel(r_blended_snapshot, nec);
|
||||
|
||||
if (upper_necs == NULL || zero_upper_influence) {
|
||||
memcpy(result_necs->values, lower_necs->values, length * sizeof(float));
|
||||
continue;
|
||||
}
|
||||
|
||||
/** Blend with lower_snapshot's base or default. */
|
||||
if (lower_necs == NULL) {
|
||||
lower_necs = nlaeval_snapshot_find_channel(lower_snapshot->base, nec);
|
||||
}
|
||||
|
||||
if (upper_blendmode == NLASTRIP_MODE_COMBINE) {
|
||||
const int mix_mode = nec->mix_mode;
|
||||
if (mix_mode == NEC_MIX_QUATERNION) {
|
||||
nla_combine_quaternion(
|
||||
lower_necs->values, upper_necs->values, upper_influence, result_necs->values);
|
||||
}
|
||||
else {
|
||||
for (int j = 0; j < length; j++) {
|
||||
result_necs->values[j] = nla_combine_value(mix_mode,
|
||||
nec->base_snapshot.values[j],
|
||||
lower_necs->values[j],
|
||||
upper_necs->values[j],
|
||||
upper_influence);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (int j = 0; j < length; j++) {
|
||||
result_necs->values[j] = nla_blend_value(
|
||||
upper_blendmode, lower_necs->values[j], upper_necs->values[j], upper_influence);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------------- */
|
||||
|
||||
/**
|
||||
* Prepare data necessary to compute correct keyframe values for NLA strips
|
||||
* with non-Replace mode or influence different from 1.
|
||||
|
|
|
@ -106,12 +106,8 @@ typedef struct NlaEvalChannel {
|
|||
|
||||
int index;
|
||||
bool is_array;
|
||||
bool in_blend;
|
||||
char mix_mode;
|
||||
|
||||
struct NlaEvalChannel *next_blend;
|
||||
NlaEvalChannelSnapshot *blend_snapshot;
|
||||
|
||||
/* Associated with the RNA property's value(s), marks which elements are affected by NLA. */
|
||||
NlaValidMask domain;
|
||||
|
||||
|
@ -186,6 +182,13 @@ void nladata_flush_channels(PointerRNA *ptr,
|
|||
NlaEvalSnapshot *snapshot,
|
||||
const bool flush_to_original);
|
||||
|
||||
void nlasnapshot_blend(NlaEvalData *eval_data,
|
||||
NlaEvalSnapshot *lower_snapshot,
|
||||
NlaEvalSnapshot *upper_snapshot,
|
||||
const short upper_blendmode,
|
||||
const float upper_influence,
|
||||
NlaEvalSnapshot *r_blended_snapshot);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue