diff options
Diffstat (limited to 'source/blender/blenkernel')
-rw-r--r-- | source/blender/blenkernel/BKE_animsys.h | 3 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/anim_sys.c | 453 |
2 files changed, 310 insertions, 146 deletions
diff --git a/source/blender/blenkernel/BKE_animsys.h b/source/blender/blenkernel/BKE_animsys.h index 8d904bd6019..2fce4bfc5b8 100644 --- a/source/blender/blenkernel/BKE_animsys.h +++ b/source/blender/blenkernel/BKE_animsys.h @@ -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, diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index 20956d6eb18..c8a4a125693 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -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,190 +2225,344 @@ 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 */ + if (nlt->strips.first) { + has_strips = true; + } + + /** 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 { - /* no solo tracks - skip track if muted */ - if (nlt->flag & NLATRACK_MUTED) { - continue; - } + nes = nlastrips_ctime_get_strip( + &estrips, &nlt->strips, track_index, anim_eval_context, flush_to_original); + } + if (nes) { + nes->track = nlt; + } + } + + if (is_action_track_evaluated_without_nla(adt, has_strips)) { + BLI_freelistN(&estrips); + return false; + } + + 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); + + /* Per strip, evaluate and accumulate on top of existing channels. */ + for (nes = estrips.first; nes; nes = nes->next) { + nlastrip_evaluate(ptr, + echannels, + NULL, + nes, + &echannels->eval_snapshot, + anim_eval_context, + flush_to_original); + } + + /* 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 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 */ + /* Get strip to evaluate for this channel. */ nes = nlastrips_ctime_get_strip( - &estrips, &nlt->strips, track_index, anim_eval_context, flush_to_original); + &lower_estrips, &nlt->strips, track_index, anim_eval_context, false); 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 + /** 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 ((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; - } + if (is_action_track_evaluated_without_nla(adt, has_strips)) { + BLI_freelistN(&lower_estrips); + return; + } - /* 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; - } + /* Write r_context->eval_strip. */ + if (adt->flag & ADT_NLA_EDIT_ON) { - dummy_strip->influence = adt->act_influence; + 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 { - /* NOTE: must set this, or else the default setting overrides, - * and this setting doesn't work. */ - dummy_strip->flag |= NLASTRIP_FLAG_USR_INFLUENCE; - } + 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); + } - /* 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 NULL, then keyframing will fail. No need to do any more processing. */ + if (!r_context->eval_strip) { + BLI_freelistN(&lower_estrips); + return; } - /* only continue if there are strips to evaluate */ - if (BLI_listbase_is_empty(&estrips)) { - return true; + /* 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; } - /* 2. for each strip, evaluate then accumulate on top of existing channels, - * but don't set values yet. */ - for (nes = estrips.first; nes; nes = nes->next) { + /* For each strip, evaluate then accumulate on top of existing channels. */ + for (nes = lower_estrips.first; nes; nes = nes->next) { nlastrip_evaluate(ptr, - echannels, + &r_context->lower_eval_data, NULL, nes, - &echannels->eval_snapshot, + &r_context->lower_eval_data.eval_snapshot, anim_eval_context, - flush_to_original); + false); } - /* 3. free temporary evaluation data that's not used elsewhere */ - BLI_freelistN(&estrips); - return true; + /* Free temporary evaluation data that's not used elsewhere. */ + BLI_freelistN(&lower_estrips); } /* NLA Evaluation function (mostly for use through do_animdata) @@ -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); |