Nla Refactor: Split animsys_evaluate_nla()

No intended functional changes.

Refactors animsys_evaluate_nla() into 2 versions:
animsys_evaluate_nla_for_keyframing(), animsys_evaluate_nla_for_flush()
to make it clear what data is being calculated and why.

Dummy strip creation has been refactored to two separate functions,
animsys_create_tweak_strip() and animsys_create_action_track_strip().
Both are evaluated differently from other strips and eachother. There's
no need to interweave them. A future patch D8296, generally requires
both strips.

___

XXX anim_sys.c) nlatrack_find_tweaked() is a temporary work around.
If anyone has any insight into this problem, help is appreciated.

Reviewed by: sybren

Differential Revision: http://developer.blender.org/D9696
This commit is contained in:
Wayde Moss 2021-01-14 18:25:01 -05:00
parent b75552ebbb
commit 09709a7e64
Notes: blender-bot 2023-02-14 02:43:21 +01:00
Referenced by commit abfb9dbf59, Fix: Disabled NLA Interferes with Action Eval
4 changed files with 318 additions and 154 deletions

View File

@ -212,8 +212,7 @@ struct NlaKeyframingContext *BKE_animsys_get_nla_keyframing_context(
struct ListBase *cache,
struct PointerRNA *ptr,
struct AnimData *adt,
const struct AnimationEvalContext *anim_eval_context,
const bool flush_to_original);
const struct AnimationEvalContext *anim_eval_context);
bool BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context,
struct PointerRNA *prop_ptr,
struct PropertyRNA *prop,

View File

@ -979,6 +979,19 @@ NlaEvalStrip *nlastrips_ctime_get_strip(ListBase *list,
return nes;
}
static NlaEvalStrip *nlastrips_ctime_get_strip_single(
ListBase *dst_list,
NlaStrip *single_strip,
const AnimationEvalContext *anim_eval_context,
const bool flush_to_original)
{
ListBase single_tracks_list;
single_tracks_list.first = single_tracks_list.last = single_strip;
return nlastrips_ctime_get_strip(
dst_list, &single_tracks_list, -1, anim_eval_context, flush_to_original);
}
/* ---------------------- */
/* Initialize a valid mask, allocating memory if necessary. */
@ -2212,177 +2225,226 @@ static void animsys_evaluate_nla_domain(PointerRNA *ptr, NlaEvalData *channels,
/* ---------------------- */
/** Tweaked strip is evaluated differently from other strips. Adjacent strips are ignored
* and includes a workaround for when user is not editing in place. */
static void animsys_create_tweak_strip(const AnimData *adt,
const bool keyframing_to_strip,
NlaStrip *r_tweak_strip)
{
/* Copy active strip so we can modify how it evaluates without affecting user data. */
memcpy(r_tweak_strip, adt->actstrip, sizeof(NlaStrip));
r_tweak_strip->next = r_tweak_strip->prev = NULL;
/* If tweaked strip is syncing action length, then evaluate using action length. */
if (r_tweak_strip->flag & NLASTRIP_FLAG_SYNC_LENGTH) {
BKE_nlastrip_recalculate_bounds_sync_action(r_tweak_strip);
}
/* Strips with a user-defined time curve don't get properly remapped for editing
* at the moment, so mapping them just for display may be confusing. */
const bool is_inplace_tweak = !(adt->flag & ADT_NLA_EDIT_NOMAP) &&
!(adt->actstrip->flag & NLASTRIP_FLAG_USR_TIME);
if (!is_inplace_tweak) {
/* Use Hold due to no proper remapping yet (the note above). */
r_tweak_strip->extendmode = NLASTRIP_EXTEND_HOLD;
/* Disable range. */
r_tweak_strip->flag |= NLASTRIP_FLAG_NO_TIME_MAP;
}
/** Controls whether able to keyframe outside range of tweaked strip. */
if (keyframing_to_strip) {
r_tweak_strip->extendmode = (is_inplace_tweak &&
!(r_tweak_strip->flag & NLASTRIP_FLAG_SYNC_LENGTH)) ?
NLASTRIP_EXTEND_NOTHING :
NLASTRIP_EXTEND_HOLD;
}
}
/** Action track and strip are associated with the non-pushed action. */
static void animsys_create_action_track_strip(const AnimData *adt,
const bool keyframing_to_strip,
NlaStrip *r_action_strip)
{
memset(r_action_strip, 0, sizeof(NlaStrip));
bAction *action = adt->action;
if ((adt->flag & ADT_NLA_EDIT_ON)) {
action = adt->tmpact;
}
/* Set settings of dummy NLA strip from AnimData settings. */
r_action_strip->act = action;
/* Action range is calculated taking F-Modifiers into account
* (which making new strips doesn't do due to the troublesome nature of that). */
calc_action_range(r_action_strip->act, &r_action_strip->actstart, &r_action_strip->actend, 1);
r_action_strip->start = r_action_strip->actstart;
r_action_strip->end = (IS_EQF(r_action_strip->actstart, r_action_strip->actend)) ?
(r_action_strip->actstart + 1.0f) :
(r_action_strip->actend);
r_action_strip->blendmode = adt->act_blendmode;
r_action_strip->extendmode = adt->act_extendmode;
r_action_strip->influence = adt->act_influence;
/* NOTE: must set this, or else the default setting overrides,
* and this setting doesn't work. */
r_action_strip->flag |= NLASTRIP_FLAG_USR_INFLUENCE;
/* Unless extendmode is Nothing (might be useful for flattening NLA evaluation), disable range.
* Extendmode Nothing and Hold will behave as normal. Hold Forward will behave just like Hold.
*/
if (r_action_strip->extendmode != NLASTRIP_EXTEND_NOTHING) {
r_action_strip->flag |= NLASTRIP_FLAG_NO_TIME_MAP;
}
const bool tweaking = (adt->flag & ADT_NLA_EDIT_ON) != 0;
const bool soloing = (adt->flag & ADT_NLA_SOLO_TRACK) != 0;
const bool actionstrip_evaluated = r_action_strip->act && !soloing && !tweaking;
if (!actionstrip_evaluated) {
r_action_strip->flag |= NLASTRIP_FLAG_MUTED;
}
/** If we're keyframing, then we must allow keyframing outside fcurve bounds. */
if (keyframing_to_strip) {
r_action_strip->extendmode = NLASTRIP_EXTEND_HOLD;
}
}
static bool is_nlatrack_evaluatable(const AnimData *adt, const NlaTrack *nlt)
{
/* Skip disabled tracks unless it contains the tweaked strip. */
const bool contains_tweak_strip = (adt->flag & ADT_NLA_EDIT_ON) &&
(nlt->index == adt->act_track->index);
if ((nlt->flag & NLATRACK_DISABLED) && !contains_tweak_strip) {
return false;
}
/* Solo and muting are mutually exclusive. */
if (adt->flag & ADT_NLA_SOLO_TRACK) {
/* Skip if there is a solo track, but this isn't it. */
if ((nlt->flag & NLATRACK_SOLO) == 0) {
return false;
}
}
else {
/* Skip track if muted. */
if (nlt->flag & NLATRACK_MUTED) {
return false;
}
}
return true;
}
/** Check for special case of non-pushed action being evaluated with no NLA influence (off and no
* strips evaluated) nor NLA interference (ensure NLA not soloing). */
static bool is_action_track_evaluated_without_nla(const AnimData *adt,
const bool any_strip_evaluated)
{
if (adt->action == NULL) {
return false;
}
if (any_strip_evaluated) {
return false;
}
/** NLA settings interference. */
if ((adt->flag & (ADT_NLA_SOLO_TRACK | ADT_NLA_EDIT_ON)) == 0) {
return false;
}
/** Allow action track to evaluate as if there isn't any NLA data. */
return true;
}
/** XXX Wayde Moss: BKE_nlatrack_find_tweaked() exists within nla.c, but it doesn't appear to
* work as expected. From animsys_evaluate_nla_for_flush(), it returns NULL in tweak mode. I'm not
* sure why. Preferably, it would be as simple as checking for (adt->act_Track == nlt) but that
* doesn't work either, neither does comparing indices.
*
* This function is a temporary work around. The first disabled track is always the tweaked track.
*/
static NlaTrack *nlatrack_find_tweaked(const AnimData *adt)
{
NlaTrack *nlt;
if (adt == NULL) {
return NULL;
}
/* Since the track itself gets disabled, we want the first disabled. */
for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next) {
if (nlt->flag & NLATRACK_DISABLED) {
return nlt;
}
}
return NULL;
}
/**
* NLA Evaluation function - values are calculated and stored in temporary "NlaEvalChannels"
*
* \param[out] echannels: Evaluation channels with calculated values
* \param[out] r_context: If not NULL,
* data about the currently edited strip is stored here and excluded from value calculation.
* \return false if NLA evaluation isn't actually applicable.
*/
static bool animsys_evaluate_nla(NlaEvalData *echannels,
PointerRNA *ptr,
AnimData *adt,
const AnimationEvalContext *anim_eval_context,
const bool flush_to_original,
NlaKeyframingContext *r_context)
static bool animsys_evaluate_nla_for_flush(NlaEvalData *echannels,
PointerRNA *ptr,
const AnimData *adt,
const AnimationEvalContext *anim_eval_context,
const bool flush_to_original)
{
NlaTrack *nlt;
short track_index = 0;
bool has_strips = false;
ListBase estrips = {NULL, NULL};
NlaEvalStrip *nes;
NlaStrip dummy_strip_buf;
/* dummy strip for active action */
NlaStrip *dummy_strip = r_context ? &r_context->strip : &dummy_strip_buf;
NlaStrip tweak_strip;
memset(dummy_strip, 0, sizeof(*dummy_strip));
NlaTrack *tweaked_track = nlatrack_find_tweaked(adt);
/* 1. get the stack of strips to evaluate at current time (influence calculated here) */
/* Get the stack of strips to evaluate at current time (influence calculated here). */
for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next, track_index++) {
/* stop here if tweaking is on and this strip is the tweaking track
* (it will be the first one that's 'disabled')... */
if ((adt->flag & ADT_NLA_EDIT_ON) && (nlt->flag & NLATRACK_DISABLED)) {
break;
if (!is_nlatrack_evaluatable(adt, nlt)) {
continue;
}
/* solo and muting are mutually exclusive... */
if (adt->flag & ADT_NLA_SOLO_TRACK) {
/* skip if there is a solo track, but this isn't it */
if ((nlt->flag & NLATRACK_SOLO) == 0) {
continue;
}
/* else - mute doesn't matter */
}
else {
/* no solo tracks - skip track if muted */
if (nlt->flag & NLATRACK_MUTED) {
continue;
}
}
/* if this track has strips (but maybe they won't be suitable), set has_strips
* - used for mainly for still allowing normal action evaluation...
*/
if (nlt->strips.first) {
has_strips = true;
}
/* otherwise, get strip to evaluate for this channel */
nes = nlastrips_ctime_get_strip(
&estrips, &nlt->strips, track_index, anim_eval_context, flush_to_original);
/** Append strip to evaluate for this track. */
if (nlt == tweaked_track) {
/** Tweaked strip is evaluated differently. */
animsys_create_tweak_strip(adt, false, &tweak_strip);
nes = nlastrips_ctime_get_strip_single(
&estrips, &tweak_strip, anim_eval_context, flush_to_original);
}
else {
nes = nlastrips_ctime_get_strip(
&estrips, &nlt->strips, track_index, anim_eval_context, flush_to_original);
}
if (nes) {
nes->track = nlt;
}
}
/* add 'active' Action (may be tweaking track) as last strip to evaluate in NLA stack
* - only do this if we're not exclusively evaluating the 'solo' NLA-track
* - however, if the 'solo' track houses the current 'tweaking' strip,
* then we should allow this to play, otherwise nothing happens
*/
if ((adt->action) && ((adt->flag & ADT_NLA_SOLO_TRACK) == 0 || (adt->flag & ADT_NLA_EDIT_ON))) {
/* if there are strips, evaluate action as per NLA rules */
if ((has_strips) || (adt->actstrip)) {
/* make dummy NLA strip, and add that to the stack */
ListBase dummy_trackslist;
dummy_trackslist.first = dummy_trackslist.last = dummy_strip;
/* Strips with a user-defined time curve don't get properly remapped for editing
* at the moment, so mapping them just for display may be confusing. */
bool is_inplace_tweak = (nlt) && !(adt->flag & ADT_NLA_EDIT_NOMAP) &&
!(adt->actstrip->flag & NLASTRIP_FLAG_USR_TIME);
if (is_inplace_tweak) {
/* edit active action in-place according to its active strip, so copy the data */
memcpy(dummy_strip, adt->actstrip, sizeof(NlaStrip));
/* Prevents nla eval from considering active strip's adj strips.
* For user, this means entering tweak mode on a strip ignores evaluating adjacent strips
* in the same track. */
dummy_strip->next = dummy_strip->prev = NULL;
/* If tweaked strip is syncing action length, then evaluate using action length. */
if (dummy_strip->flag & NLASTRIP_FLAG_SYNC_LENGTH) {
BKE_nlastrip_recalculate_bounds_sync_action(dummy_strip);
}
}
else {
/* set settings of dummy NLA strip from AnimData settings */
dummy_strip->act = adt->action;
/* action range is calculated taking F-Modifiers into account
* (which making new strips doesn't do due to the troublesome nature of that) */
calc_action_range(dummy_strip->act, &dummy_strip->actstart, &dummy_strip->actend, 1);
dummy_strip->start = dummy_strip->actstart;
dummy_strip->end = (IS_EQF(dummy_strip->actstart, dummy_strip->actend)) ?
(dummy_strip->actstart + 1.0f) :
(dummy_strip->actend);
/* Always use the blend mode of the strip in tweak mode, even if not in-place. */
if (nlt && adt->actstrip) {
dummy_strip->blendmode = adt->actstrip->blendmode;
dummy_strip->extendmode = NLASTRIP_EXTEND_HOLD;
}
else {
dummy_strip->blendmode = adt->act_blendmode;
dummy_strip->extendmode = adt->act_extendmode;
}
/* Unless extend-mode is Nothing (might be useful for flattening NLA evaluation),
* disable range. */
if (dummy_strip->extendmode != NLASTRIP_EXTEND_NOTHING) {
dummy_strip->flag |= NLASTRIP_FLAG_NO_TIME_MAP;
}
dummy_strip->influence = adt->act_influence;
/* NOTE: must set this, or else the default setting overrides,
* and this setting doesn't work. */
dummy_strip->flag |= NLASTRIP_FLAG_USR_INFLUENCE;
}
/* add this to our list of evaluation strips */
if (r_context == NULL) {
nlastrips_ctime_get_strip(
&estrips, &dummy_trackslist, -1, anim_eval_context, flush_to_original);
}
/* If computing the context for keyframing, store data there instead of the list. */
else {
/* The extend mode here effectively controls
* whether it is possible to key-frame beyond the ends.*/
dummy_strip->extendmode = (is_inplace_tweak &&
!(dummy_strip->flag & NLASTRIP_FLAG_SYNC_LENGTH)) ?
NLASTRIP_EXTEND_NOTHING :
NLASTRIP_EXTEND_HOLD;
r_context->eval_strip = nes = nlastrips_ctime_get_strip(
NULL, &dummy_trackslist, -1, anim_eval_context, flush_to_original);
/* These setting combinations require no data from strips below, so exit immediately. */
if ((nes == NULL) ||
(dummy_strip->blendmode == NLASTRIP_MODE_REPLACE && dummy_strip->influence == 1.0f)) {
BLI_freelistN(&estrips);
return true;
}
}
}
else {
/* special case - evaluate as if there isn't any NLA data */
BLI_freelistN(&estrips);
return false;
}
if (is_action_track_evaluated_without_nla(adt, has_strips)) {
BLI_freelistN(&estrips);
return false;
}
/* only continue if there are strips to evaluate */
if (BLI_listbase_is_empty(&estrips)) {
return true;
}
NlaStrip action_strip = {0};
animsys_create_action_track_strip(adt, false, &action_strip);
nlastrips_ctime_get_strip_single(&estrips, &action_strip, anim_eval_context, flush_to_original);
/* 2. for each strip, evaluate then accumulate on top of existing channels,
* but don't set values yet. */
/* Per strip, evaluate and accumulate on top of existing channels. */
for (nes = estrips.first; nes; nes = nes->next) {
nlastrip_evaluate(ptr,
echannels,
@ -2393,11 +2455,116 @@ static bool animsys_evaluate_nla(NlaEvalData *echannels,
flush_to_original);
}
/* 3. free temporary evaluation data that's not used elsewhere */
/* Free temporary evaluation data that's not used elsewhere. */
BLI_freelistN(&estrips);
return true;
}
/** Lower blended values are calculated and accumulated into r_context->lower_eval_data. */
static void animsys_evaluate_nla_for_keyframing(PointerRNA *ptr,
const AnimData *adt,
const AnimationEvalContext *anim_eval_context,
NlaKeyframingContext *r_context)
{
if (!r_context) {
return;
}
/* Early out. If NLA track is soloing and tweaked action isn't it, then don't allow keyframe
* insertion. */
if (adt->flag & ADT_NLA_SOLO_TRACK) {
if (!(adt->act_track && (adt->act_track->flag & NLATRACK_SOLO))) {
r_context->eval_strip = NULL;
return;
}
}
NlaTrack *nlt;
short track_index = 0;
bool has_strips = false;
ListBase lower_estrips = {NULL, NULL};
NlaEvalStrip *nes;
NlaTrack *tweaked_track = nlatrack_find_tweaked(adt);
/* Get the lower stack of strips to evaluate at current time (influence calculated here). */
for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next, track_index++) {
if (!is_nlatrack_evaluatable(adt, nlt)) {
continue;
}
/* Tweaked strip effect should not be stored in any snapshot. */
if (nlt == tweaked_track) {
break;
}
if (nlt->strips.first) {
has_strips = true;
}
/* Get strip to evaluate for this channel. */
nes = nlastrips_ctime_get_strip(
&lower_estrips, &nlt->strips, track_index, anim_eval_context, false);
if (nes) {
nes->track = nlt;
}
}
/** Note: Although we early out, we can still keyframe to the non-pushed action since the
* keyframe remap function detects (r_context->strip.act == NULL) and will keyframe without
* remapping.
*/
if (is_action_track_evaluated_without_nla(adt, has_strips)) {
BLI_freelistN(&lower_estrips);
return;
}
/* Write r_context->eval_strip. */
if (adt->flag & ADT_NLA_EDIT_ON) {
NlaStrip *tweak_strip = &r_context->strip;
animsys_create_tweak_strip(adt, true, tweak_strip);
r_context->eval_strip = nlastrips_ctime_get_strip_single(
NULL, tweak_strip, anim_eval_context, false);
}
else {
NlaStrip *action_strip = &r_context->strip;
animsys_create_action_track_strip(adt, true, action_strip);
r_context->eval_strip = nlastrips_ctime_get_strip_single(
NULL, action_strip, anim_eval_context, false);
}
/* If NULL, then keyframing will fail. No need to do any more processing. */
if (!r_context->eval_strip) {
BLI_freelistN(&lower_estrips);
return;
}
/* If tweak strip is full REPLACE, then lower strips not needed. */
if (r_context->strip.blendmode == NLASTRIP_MODE_REPLACE &&
IS_EQF(r_context->strip.influence, 1.0f)) {
BLI_freelistN(&lower_estrips);
return;
}
/* For each strip, evaluate then accumulate on top of existing channels. */
for (nes = lower_estrips.first; nes; nes = nes->next) {
nlastrip_evaluate(ptr,
&r_context->lower_eval_data,
NULL,
nes,
&r_context->lower_eval_data.eval_snapshot,
anim_eval_context,
false);
}
/* Free temporary evaluation data that's not used elsewhere. */
BLI_freelistN(&lower_estrips);
}
/* NLA Evaluation function (mostly for use through do_animdata)
* - All channels that will be affected are not cleared anymore. Instead, we just evaluate into
* some temp channels, where values can be accumulated in one go.
@ -2412,7 +2579,7 @@ static void animsys_calculate_nla(PointerRNA *ptr,
nlaeval_init(&echannels);
/* evaluate the NLA stack, obtaining a set of values to flush */
if (animsys_evaluate_nla(&echannels, ptr, adt, anim_eval_context, flush_to_original, NULL)) {
if (animsys_evaluate_nla_for_flush(&echannels, ptr, adt, anim_eval_context, flush_to_original)) {
/* reset any channels touched by currently inactive actions to default value */
animsys_evaluate_nla_domain(ptr, &echannels, adt);
@ -2448,8 +2615,7 @@ NlaKeyframingContext *BKE_animsys_get_nla_keyframing_context(
struct ListBase *cache,
struct PointerRNA *ptr,
struct AnimData *adt,
const AnimationEvalContext *anim_eval_context,
const bool flush_to_original)
const AnimationEvalContext *anim_eval_context)
{
/* No remapping needed if NLA is off or no action. */
if ((adt == NULL) || (adt->action == NULL) || (adt->nla_tracks.first == NULL) ||
@ -2472,8 +2638,7 @@ NlaKeyframingContext *BKE_animsys_get_nla_keyframing_context(
ctx->adt = adt;
nlaeval_init(&ctx->lower_eval_data);
animsys_evaluate_nla(
&ctx->lower_eval_data, ptr, adt, anim_eval_context, flush_to_original, ctx);
animsys_evaluate_nla_for_keyframing(ptr, adt, anim_eval_context, ctx);
BLI_assert(ELEM(ctx->strip.act, NULL, adt->action));
BLI_addtail(cache, ctx);

View File

@ -4710,7 +4710,7 @@ static void achannel_setting_slider_cb(bContext *C, void *id_poin, void *fcu_poi
const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph,
(float)CFRA);
NlaKeyframingContext *nla_context = BKE_animsys_get_nla_keyframing_context(
&nla_cache, &id_ptr, adt, &anim_eval_context, false);
&nla_cache, &id_ptr, adt, &anim_eval_context);
/* get current frame and apply NLA-mapping to it (if applicable) */
cfra = BKE_nla_tweakedit_remap(adt, (float)CFRA, NLATIME_CONVERT_UNMAP);
@ -4766,7 +4766,7 @@ static void achannel_setting_slider_shapekey_cb(bContext *C, void *key_poin, voi
const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct(depsgraph,
(float)CFRA);
NlaKeyframingContext *nla_context = BKE_animsys_get_nla_keyframing_context(
&nla_cache, &id_ptr, key->adt, &anim_eval_context, false);
&nla_cache, &id_ptr, key->adt, &anim_eval_context);
/* get current frame and apply NLA-mapping to it (if applicable) */
const float remapped_frame = BKE_nla_tweakedit_remap(

View File

@ -1384,7 +1384,7 @@ static AnimationEvalContext nla_time_remap(const AnimationEvalContext *anim_eval
if (adt && adt->action == act) {
/* Get NLA context for value remapping. */
*r_nla_context = BKE_animsys_get_nla_keyframing_context(
nla_cache, id_ptr, adt, anim_eval_context, false);
nla_cache, id_ptr, adt, anim_eval_context);
/* Apply NLA-mapping to frame. */
const float remapped_frame = BKE_nla_tweakedit_remap(