From 2d0117ff45945e286e833dd2332efedb999da86d Mon Sep 17 00:00:00 2001 From: Wayde Moss Date: Wed, 17 Feb 2021 15:58:58 -0500 Subject: NLA: Keyframe Remap Through Upper Strips Differential Revision: https://developer.blender.org/D10504 --- source/blender/blenkernel/BKE_animsys.h | 3 +- source/blender/blenkernel/intern/anim_sys.c | 645 +++++++++++++++++++++++--- source/blender/blenkernel/nla_private.h | 50 +- source/blender/editors/animation/keyframing.c | 17 +- 4 files changed, 637 insertions(+), 78 deletions(-) diff --git a/source/blender/blenkernel/BKE_animsys.h b/source/blender/blenkernel/BKE_animsys.h index 2fce4bfc5b8..3b40ec24a70 100644 --- a/source/blender/blenkernel/BKE_animsys.h +++ b/source/blender/blenkernel/BKE_animsys.h @@ -219,7 +219,8 @@ bool BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context, float *values, int count, int index, - bool *r_force_all); + bool *r_force_all, + const struct AnimationEvalContext *anim_eval_context); void BKE_animsys_free_nla_keyframing_context_cache(struct ListBase *cache); /* ************************************* */ diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index bdbffc2d733..5bfa5c1f828 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -1441,6 +1441,141 @@ static NlaEvalChannel *nlaevalchan_verify(PointerRNA *ptr, NlaEvalData *nlaeval, /* ---------------------- */ +/** \returns true if solution exists and output written to. */ +static bool nla_blend_get_inverted_lower_value(const int blendmode, + const float strip_value, + const float blended_value, + const float influence, + float *r_lower_value) +{ + if (IS_EQF(influence, 0.0f)) { + *r_lower_value = blended_value; + return true; + } + + switch (blendmode) { + case NLASTRIP_MODE_ADD: + /* Simply subtract the scaled value on to the stack. */ + *r_lower_value = blended_value - (strip_value * influence); + return true; + + case NLASTRIP_MODE_SUBTRACT: + /* Simply add the scaled value from the stack. */ + *r_lower_value = blended_value + (strip_value * influence); + return true; + + case NLASTRIP_MODE_MULTIPLY: + + /** Division by zero. */ + if (IS_EQF(-strip_value * influence, 1.0f - influence)) { + /** Resolve 0/0 to 1. */ + if (IS_EQF(blended_value, 0.0f)) { + *r_lower_value = 1; + return true; + } + /** Division by zero. */ + return false; + } + /* Math: + * blended_value = inf * (lower_value * strip_value) + (1 - inf) * lower_value + * = lower_value * (inf * strip_value + (1-inf)) + * lower_value = blended_value / (inf * strip_value + (1-inf)) + */ + *r_lower_value = blended_value / (influence * strip_value + (1.0f - influence)); + return true; + + case NLASTRIP_MODE_COMBINE: + BLI_assert(!"combine mode"); + return false; + + default: + + /** No solution if lower strip has 0 influence. */ + if (IS_EQF(influence, 1.0f)) { + return false; + } + + /** Math: + * + * blended_value = lower_value * (1.0f - inf) + (strip_value * inf) + * blended_value - (strip_value * inf) = lower_value * (1.0f - inf) + * blended_value - (strip_value * inf) / (1.0f - inf) = lower_value + * + * lower_value = blended_value - (strip_value * inf) / (1.0f - inf) + */ + *r_lower_value = (blended_value - (strip_value * influence)) / (1.0f - influence); + return true; + } +} + +/** \returns true if solution exists and output written to. */ +static bool nla_combine_get_inverted_lower_value(const int mix_mode, + float base_value, + const float strip_value, + const float blended_value, + const float influence, + float *r_lower_value) +{ + if (IS_EQF(influence, 0.0f)) { + *r_lower_value = blended_value; + return true; + } + + /* Perform blending. */ + switch (mix_mode) { + case NEC_MIX_ADD: + case NEC_MIX_AXIS_ANGLE: + *r_lower_value = blended_value - (strip_value - base_value) * influence; + return true; + case NEC_MIX_MULTIPLY: + /** Division by zero. */ + if (IS_EQF(strip_value, 0.0f)) { + /** Resolve 0/0 to 1. */ + if (IS_EQF(blended_value, 0.0f)) { + *r_lower_value = 1.0f; + return true; + } + return false; + } + + if (IS_EQF(base_value, 0.0f)) { + base_value = 1.0f; + } + + *r_lower_value = blended_value / powf(strip_value / base_value, influence); + return true; + + default: + BLI_assert(!"invalid mix mode"); + return false; + } +} + +static void nla_combine_quaternion_get_inverted_lower_values(const float strip_values[4], + const float blended_values[4], + const float influence, + float r_lower_value[4]) +{ + if (IS_EQF(influence, 0.0f)) { + normalize_qt_qt(r_lower_value, blended_values); + return; + } + + /* blended_value = lower_values @ strip_values^infl + * blended_value @ inv(strip_values^inf) = lower_values + * + * Returns: lower_values = blended_value @ inv(strip_values^inf) */ + float tmp_strip_values[4], tmp_blended[4]; + + normalize_qt_qt(tmp_strip_values, strip_values); + normalize_qt_qt(tmp_blended, blended_values); + + pow_qt_fl_normalized(tmp_strip_values, influence); + invert_qt_normalized(tmp_strip_values); + + mul_qt_qtqt(r_lower_value, tmp_blended, tmp_strip_values); +} + /* Blend the lower nla stack value and upper strip value of a channel according to mode and * influence. */ static float nla_blend_value(const int blendmode, @@ -1775,7 +1910,8 @@ static void nlasnapshot_from_action(PointerRNA *ptr, } /* evaluate action-clip strip */ -static void nlastrip_evaluate_actionclip(PointerRNA *ptr, +static void nlastrip_evaluate_actionclip(const int evaluation_mode, + PointerRNA *ptr, NlaEvalData *channels, ListBase *modifiers, NlaEvalStrip *nes, @@ -1799,22 +1935,49 @@ static void nlastrip_evaluate_actionclip(PointerRNA *ptr, /* join this strip's modifiers to the parent's modifiers (own modifiers first) */ nlaeval_fmodifiers_join_stacks(&tmp_modifiers, &strip->modifiers, modifiers); - NlaEvalSnapshot strip_snapshot; - nlaeval_snapshot_init(&strip_snapshot, channels, NULL); + switch (evaluation_mode) { + case STRIP_EVAL_BLEND: { - 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); + NlaEvalSnapshot strip_snapshot; + nlaeval_snapshot_init(&strip_snapshot, channels, NULL); - nlaeval_snapshot_free_data(&strip_snapshot); + 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); + + nlaeval_snapshot_free_data(&strip_snapshot); + + break; + } + case STRIP_EVAL_BLEND_GET_INVERTED_LOWER_SNAPSHOT: { + + NlaEvalSnapshot strip_snapshot; + nlaeval_snapshot_init(&strip_snapshot, channels, NULL); + + nlasnapshot_from_action( + ptr, channels, &tmp_modifiers, strip->act, strip->strip_time, &strip_snapshot); + nlasnapshot_blend_get_inverted_lower_snapshot( + channels, snapshot, &strip_snapshot, strip->blendmode, strip->influence, snapshot); + + nlaeval_snapshot_free_data(&strip_snapshot); + + break; + } + case STRIP_EVAL_NOBLEND: { + nlasnapshot_from_action( + ptr, channels, &tmp_modifiers, strip->act, strip->strip_time, snapshot); + break; + } + } /* unlink this strip's modifiers from the parent's modifiers again */ nlaeval_fmodifiers_split_stacks(&strip->modifiers, modifiers); } /* evaluate transition strip */ -static void nlastrip_evaluate_transition(PointerRNA *ptr, +static void nlastrip_evaluate_transition(const int evaluation_mode, + PointerRNA *ptr, NlaEvalData *channels, ListBase *modifiers, NlaEvalStrip *nes, @@ -1846,49 +2009,128 @@ static void nlastrip_evaluate_transition(PointerRNA *ptr, s2 = nes->strip->next; } - /* prepare template for 'evaluation strip' - * - based on the transition strip's evaluation strip data - * - strip_mode is NES_TIME_TRANSITION_* based on which endpoint - * - strip_time is the 'normalized' (i.e. in-strip) time for evaluation, - * which doubles up as an additional weighting factor for the strip influences - * which allows us to appear to be 'interpolating' between the two extremes - */ - tmp_nes = *nes; - - /* evaluate these strips into a temp-buffer (tmp_channels) */ - /* FIXME: modifier evaluation here needs some work... */ - /* first strip */ - tmp_nes.strip_mode = NES_TIME_TRANSITION_START; - tmp_nes.strip = s1; - tmp_nes.strip_time = s1->strip_time; - nlaeval_snapshot_init(&snapshot1, channels, snapshot); - nlastrip_evaluate( - ptr, channels, &tmp_modifiers, &tmp_nes, &snapshot1, anim_eval_context, flush_to_original); + switch (evaluation_mode) { + case STRIP_EVAL_BLEND: { - /* second strip */ - tmp_nes.strip_mode = NES_TIME_TRANSITION_END; - tmp_nes.strip = s2; - tmp_nes.strip_time = s2->strip_time; - nlaeval_snapshot_init(&snapshot2, channels, snapshot); - nlastrip_evaluate( - ptr, channels, &tmp_modifiers, &tmp_nes, &snapshot2, anim_eval_context, flush_to_original); + /* prepare template for 'evaluation strip' + * - based on the transition strip's evaluation strip data + * - strip_mode is NES_TIME_TRANSITION_* based on which endpoint + * - strip_time is the 'normalized' (i.e. in-strip) time for evaluation, + * which doubles up as an additional weighting factor for the strip influences + * which allows us to appear to be 'interpolating' between the two extremes + */ + tmp_nes = *nes; + + /* evaluate these strips into a temp-buffer (tmp_channels) */ + /* FIXME: modifier evaluation here needs some work... */ + /* first strip */ + tmp_nes.strip_mode = NES_TIME_TRANSITION_START; + tmp_nes.strip = s1; + tmp_nes.strip_time = s1->strip_time; + nlaeval_snapshot_init(&snapshot1, channels, snapshot); + nlasnapshot_blend_strip(ptr, + channels, + &tmp_modifiers, + &tmp_nes, + &snapshot1, + anim_eval_context, + flush_to_original); + + /* second strip */ + tmp_nes.strip_mode = NES_TIME_TRANSITION_END; + tmp_nes.strip = s2; + tmp_nes.strip_time = s2->strip_time; + nlaeval_snapshot_init(&snapshot2, channels, snapshot); + nlasnapshot_blend_strip(ptr, + channels, + &tmp_modifiers, + &tmp_nes, + &snapshot2, + anim_eval_context, + flush_to_original); + + /** Replace \a snapshot2 NULL channels with base or default values so all channels blend. */ + nlasnapshot_ensure_channels(channels, &snapshot2); + /** Mark all \a snapshot2 channel's values to blend. */ + nlasnapshot_enable_all_blend_domain(&snapshot2); + nlasnapshot_blend( + channels, &snapshot1, &snapshot2, NLASTRIP_MODE_REPLACE, nes->strip_time, snapshot); + + nlaeval_snapshot_free_data(&snapshot1); + nlaeval_snapshot_free_data(&snapshot2); - /** Replace \a snapshot2 NULL channels with base or default values so all channels blend. */ - nlasnapshot_ensure_channels(channels, &snapshot2); - /** Mark all \a snapshot2 channel's values to blend. */ - nlasnapshot_enable_all_blend_domain(&snapshot2); - nlasnapshot_blend( - channels, &snapshot1, &snapshot2, NLASTRIP_MODE_REPLACE, nes->strip_time, snapshot); + break; + } + case STRIP_EVAL_BLEND_GET_INVERTED_LOWER_SNAPSHOT: { + /* No support for remapping values through a transition. Mark all channel values affected by + * transition as non-remappable. */ + tmp_nes = *nes; + + /** Process first strip. */ + tmp_nes.strip_mode = NES_TIME_TRANSITION_START; + tmp_nes.strip = s1; + tmp_nes.strip_time = s1->strip_time; + nlaeval_snapshot_init(&snapshot1, channels, snapshot); + nlasnapshot_blend_strip_no_blend( + ptr, channels, &tmp_modifiers, &tmp_nes, &snapshot1, anim_eval_context); + + /** Remove channel values affected by transition from the remap domain. */ + LISTBASE_FOREACH (NlaEvalChannel *, nec, &channels->channels) { + NlaEvalChannelSnapshot *necs = nlaeval_snapshot_get(&snapshot1, nec->index); + if (necs == NULL) { + continue; + } + NlaEvalChannelSnapshot *output_necs = nlaeval_snapshot_ensure_channel(snapshot, nec); + for (int i = 0; i < necs->length; i++) { + if (BLI_BITMAP_TEST_BOOL(necs->blend_domain.ptr, i)) { + BLI_BITMAP_DISABLE(output_necs->remap_domain.ptr, i); + } + } + } - nlaeval_snapshot_free_data(&snapshot1); - nlaeval_snapshot_free_data(&snapshot2); + nlaeval_snapshot_free_data(&snapshot1); + + /** Process second strip. */ + tmp_nes.strip_mode = NES_TIME_TRANSITION_END; + tmp_nes.strip = s2; + tmp_nes.strip_time = s2->strip_time; + nlaeval_snapshot_init(&snapshot2, channels, snapshot); + nlasnapshot_blend_strip_no_blend( + ptr, channels, &tmp_modifiers, &tmp_nes, &snapshot2, anim_eval_context); + + /** Remove channel values affected by transition from the remap domain. */ + LISTBASE_FOREACH (NlaEvalChannel *, nec, &channels->channels) { + NlaEvalChannelSnapshot *necs = nlaeval_snapshot_get(&snapshot2, nec->index); + if (necs == NULL) { + continue; + } + NlaEvalChannelSnapshot *output_necs = nlaeval_snapshot_ensure_channel(snapshot, nec); + for (int i = 0; i < necs->length; i++) { + if (BLI_BITMAP_TEST_BOOL(necs->blend_domain.ptr, i)) { + BLI_BITMAP_DISABLE(output_necs->remap_domain.ptr, i); + } + } + } + + nlaeval_snapshot_free_data(&snapshot2); + + break; + } + case STRIP_EVAL_NOBLEND: { + BLI_assert( + "This case shouldn't occur. Transitions assumed to not reference other " + "transitions. "); + break; + } + } /* unlink this strip's modifiers from the parent's modifiers again */ nlaeval_fmodifiers_split_stacks(&nes->strip->modifiers, modifiers); } /* evaluate meta-strip */ -static void nlastrip_evaluate_meta(PointerRNA *ptr, +static void nlastrip_evaluate_meta(const int evaluation_mode, + PointerRNA *ptr, NlaEvalData *channels, ListBase *modifiers, NlaEvalStrip *nes, @@ -1918,12 +2160,31 @@ static void nlastrip_evaluate_meta(PointerRNA *ptr, evaltime); tmp_nes = nlastrips_ctime_get_strip(NULL, &strip->strips, -1, &child_context, flush_to_original); + /* Assert currently supported modes. If new mode added, then assertion marks potentially missed + * area. + * + * Note: In the future if support is ever added to metastrips to support nested tracks, then + * STRIP_EVAL_BLEND and STRIP_EVAL_BLEND_GET_INVERTED_LOWER_SNAPSHOT cases are no longer + * equivalent. The output of nlastrips_ctime_get_strip() may return a list of strips. The only + * case difference should be the evaluation order. + */ + BLI_assert(ELEM(evaluation_mode, + STRIP_EVAL_BLEND, + STRIP_EVAL_BLEND_GET_INVERTED_LOWER_SNAPSHOT, + STRIP_EVAL_NOBLEND)); + /* directly evaluate child strip into accumulation buffer... * - there's no need to use a temporary buffer (as it causes issues [T40082]) */ if (tmp_nes) { - nlastrip_evaluate( - ptr, channels, &tmp_modifiers, tmp_nes, snapshot, &child_context, flush_to_original); + nlastrip_evaluate(evaluation_mode, + ptr, + channels, + &tmp_modifiers, + tmp_nes, + snapshot, + &child_context, + flush_to_original); /* free temp eval-strip */ MEM_freeN(tmp_nes); @@ -1934,7 +2195,8 @@ static void nlastrip_evaluate_meta(PointerRNA *ptr, } /* evaluates the given evaluation strip */ -void nlastrip_evaluate(PointerRNA *ptr, +void nlastrip_evaluate(const int evaluation_mode, + PointerRNA *ptr, NlaEvalData *channels, ListBase *modifiers, NlaEvalStrip *nes, @@ -1959,15 +2221,27 @@ void nlastrip_evaluate(PointerRNA *ptr, /* actions to take depend on the type of strip */ switch (strip->type) { case NLASTRIP_TYPE_CLIP: /* action-clip */ - nlastrip_evaluate_actionclip(ptr, channels, modifiers, nes, snapshot); + nlastrip_evaluate_actionclip(evaluation_mode, ptr, channels, modifiers, nes, snapshot); break; case NLASTRIP_TYPE_TRANSITION: /* transition */ - nlastrip_evaluate_transition( - ptr, channels, modifiers, nes, snapshot, anim_eval_context, flush_to_original); + nlastrip_evaluate_transition(evaluation_mode, + ptr, + channels, + modifiers, + nes, + snapshot, + anim_eval_context, + flush_to_original); break; case NLASTRIP_TYPE_META: /* meta */ - nlastrip_evaluate_meta( - ptr, channels, modifiers, nes, snapshot, anim_eval_context, flush_to_original); + nlastrip_evaluate_meta(evaluation_mode, + ptr, + channels, + modifiers, + nes, + snapshot, + anim_eval_context, + flush_to_original); break; default: /* do nothing */ @@ -1978,6 +2252,53 @@ void nlastrip_evaluate(PointerRNA *ptr, strip->flag &= ~NLASTRIP_FLAG_EDIT_TOUCHED; } +void nlasnapshot_blend_strip(PointerRNA *ptr, + NlaEvalData *channels, + ListBase *modifiers, + NlaEvalStrip *nes, + NlaEvalSnapshot *snapshot, + const struct AnimationEvalContext *anim_eval_context, + const bool flush_to_original) +{ + nlastrip_evaluate(STRIP_EVAL_BLEND, + ptr, + channels, + modifiers, + nes, + snapshot, + anim_eval_context, + flush_to_original); +} + +void nlasnapshot_blend_strip_get_inverted_lower_snapshot( + PointerRNA *ptr, + NlaEvalData *channels, + ListBase *modifiers, + NlaEvalStrip *nes, + NlaEvalSnapshot *snapshot, + const struct AnimationEvalContext *anim_eval_context) +{ + nlastrip_evaluate(STRIP_EVAL_BLEND_GET_INVERTED_LOWER_SNAPSHOT, + ptr, + channels, + modifiers, + nes, + snapshot, + anim_eval_context, + false); +} + +void nlasnapshot_blend_strip_no_blend(PointerRNA *ptr, + NlaEvalData *channels, + ListBase *modifiers, + NlaEvalStrip *nes, + NlaEvalSnapshot *snapshot, + const struct AnimationEvalContext *anim_eval_context) +{ + nlastrip_evaluate( + STRIP_EVAL_NOBLEND, ptr, channels, modifiers, nes, snapshot, anim_eval_context, false); +} + /* write the accumulated settings to */ void nladata_flush_channels(PointerRNA *ptr, NlaEvalData *channels, @@ -2336,13 +2657,13 @@ static bool animsys_evaluate_nla_for_flush(NlaEvalData *echannels, /* 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); + nlasnapshot_blend_strip(ptr, + echannels, + NULL, + nes, + &echannels->eval_snapshot, + anim_eval_context, + flush_to_original); } /* Free temporary evaluation data that's not used elsewhere. */ @@ -2373,6 +2694,7 @@ static void animsys_evaluate_nla_for_keyframing(PointerRNA *ptr, short track_index = 0; bool has_strips = false; + ListBase *upper_estrips = &r_context->upper_estrips; ListBase lower_estrips = {NULL, NULL}; NlaEvalStrip *nes; @@ -2402,6 +2724,30 @@ static void animsys_evaluate_nla_for_keyframing(PointerRNA *ptr, } } + /* Get the upper stack of strips to evaluate at current time (influence calculated here). */ + /* Var nlt exists only if tweak strip exists. */ + if (nlt) { + + /* Skip tweaked strip. */ + nlt = nlt->next; + track_index++; + + for (; nlt; nlt = nlt->next, track_index++) { + + if (!is_nlatrack_evaluatable(adt, nlt)) { + continue; + } + + if (nlt->strips.first) { + has_strips = true; + } + + /* Get strip to evaluate for this channel. */ + nes = nlastrips_ctime_get_strip( + upper_estrips, &nlt->strips, track_index, anim_eval_context, false); + } + } + /** 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. @@ -2413,6 +2759,10 @@ static void animsys_evaluate_nla_for_keyframing(PointerRNA *ptr, /* Write r_context->eval_strip. */ if (adt->flag & ADT_NLA_EDIT_ON) { + /* Append action_track_strip to upper estrips. */ + NlaStrip *action_strip = &r_context->action_track_strip; + animsys_create_action_track_strip(adt, false, action_strip); + nlastrips_ctime_get_strip_single(upper_estrips, action_strip, anim_eval_context, false); NlaStrip *tweak_strip = &r_context->strip; animsys_create_tweak_strip(adt, true, tweak_strip); @@ -2442,13 +2792,13 @@ static void animsys_evaluate_nla_for_keyframing(PointerRNA *ptr, /* 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); + nlasnapshot_blend_strip(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. */ @@ -2698,6 +3048,143 @@ void nlasnapshot_blend_get_inverted_upper_snapshot(NlaEvalData *eval_data, } } +/** Using \a blended_snapshot and \a upper_snapshot, we can solve for the \a r_lower_snapshot. + * + * Only channels that exist within \a blended_snapshot are processed. + * Only blended values within the \a remap_domain are processed. + * + * Writes to \a r_upper_snapshot NlaEvalChannelSnapshot->remap_domain to match remapping success. + * + * Assumes caller marked upper values that are in the \a blend_domain. This determines whether the + * blended value came directly from the lower snapshot or a result of blending. + **/ +void nlasnapshot_blend_get_inverted_lower_snapshot(NlaEvalData *eval_data, + NlaEvalSnapshot *blended_snapshot, + NlaEvalSnapshot *upper_snapshot, + const short upper_blendmode, + const float upper_influence, + NlaEvalSnapshot *r_lower_snapshot) +{ + nlaeval_snapshot_ensure_size(r_lower_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 *blended_necs = nlaeval_snapshot_get(blended_snapshot, nec->index); + if (blended_necs == NULL) { + /** We assume the caller only wants a subset of channels to be inverted, those that exist + * within \a blended_snapshot. */ + continue; + } + + NlaEvalChannelSnapshot *result_necs = nlaeval_snapshot_ensure_channel(r_lower_snapshot, nec); + + NlaEvalChannelSnapshot *upper_necs = nlaeval_snapshot_get(upper_snapshot, nec->index); + if (upper_necs == NULL || zero_upper_influence) { + for (int j = 0; j < length; j++) { + result_necs->values[j] = blended_necs->values[j]; + + const bool in_remap_domain = BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, j); + BLI_BITMAP_SET(result_necs->remap_domain.ptr, j, in_remap_domain); + } + continue; + } + + if (upper_blendmode == NLASTRIP_MODE_COMBINE) { + const int mix_mode = nec->mix_mode; + + if (mix_mode == NEC_MIX_QUATERNION) { + if (!BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, 0)) { + BLI_bitmap_set_all(result_necs->remap_domain.ptr, false, 4); + continue; + } + + /* If blended values are not a result of blending the lower snapshot with the upper + * snapshot, then they should be equal. + * + * Just directly copy blended_necs to result_necs. + */ + if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, 0)) { + memcpy(result_necs->values, blended_necs->values, 4 * sizeof(float)); + BLI_bitmap_set_all(result_necs->remap_domain.ptr, true, 4); + continue; + } + + nla_combine_quaternion_get_inverted_lower_values( + upper_necs->values, blended_necs->values, upper_influence, result_necs->values); + + BLI_bitmap_set_all(result_necs->remap_domain.ptr, true, 4); + continue; + } + else { + BLI_assert(ELEM(mix_mode, NEC_MIX_ADD, NEC_MIX_AXIS_ANGLE, NEC_MIX_MULTIPLY)); + + for (int j = 0; j < length; j++) { + if (!BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, j)) { + BLI_BITMAP_DISABLE(result_necs->remap_domain.ptr, j); + continue; + } + + /* If blended values are not a result of blending the lower snapshot with the upper + * snapshot, then they should be equal. + * + * Just directly copy blended_necs to result_necs. + */ + if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, j)) { + result_necs->values[j] = blended_necs->values[j]; + BLI_BITMAP_ENABLE(result_necs->remap_domain.ptr, j); + continue; + } + + const bool success = nla_combine_get_inverted_lower_value(mix_mode, + nec->base_snapshot.values[j], + upper_necs->values[j], + blended_necs->values[j], + upper_influence, + &result_necs->values[j]); + + BLI_BITMAP_SET(result_necs->remap_domain.ptr, j, success); + } + } + } + else { + BLI_assert(ELEM(upper_blendmode, + NLASTRIP_MODE_ADD, + NLASTRIP_MODE_SUBTRACT, + NLASTRIP_MODE_MULTIPLY, + NLASTRIP_MODE_REPLACE)); + + for (int j = 0; j < length; j++) { + if (!BLI_BITMAP_TEST_BOOL(blended_necs->remap_domain.ptr, j)) { + BLI_BITMAP_DISABLE(result_necs->remap_domain.ptr, j); + continue; + } + + /* If blended values are not a result of blending the lower snapshot with the upper + * snapshot, then they should be equal. + * + * Just directly copy blended_necs to result_necs. + */ + if (!BLI_BITMAP_TEST_BOOL(upper_necs->blend_domain.ptr, j)) { + result_necs->values[j] = blended_necs->values[j]; + BLI_BITMAP_ENABLE(result_necs->remap_domain.ptr, j); + continue; + } + + const bool success = nla_blend_get_inverted_lower_value(upper_blendmode, + upper_necs->values[j], + blended_necs->values[j], + upper_influence, + &result_necs->values[j]); + + BLI_BITMAP_SET(result_necs->remap_domain.ptr, j, success); + } + } + } +} + /* ---------------------- */ /** @@ -2721,9 +3208,11 @@ NlaKeyframingContext *BKE_animsys_get_nla_keyframing_context( return NULL; } - /* No remapping if editing an ordinary Replace action with full influence. */ + /* No remapping if editing an ordinary Replace action with full influence and upper tracks not + * evaluated. */ if (!(adt->flag & ADT_NLA_EDIT_ON) && - (adt->act_blendmode == NLASTRIP_MODE_REPLACE && adt->act_influence == 1.0f)) { + (adt->act_blendmode == NLASTRIP_MODE_REPLACE && adt->act_influence == 1.0f) && + (adt->flag & ADT_NLA_EVAL_UPPER_TRACKS) == 0) { return NULL; } @@ -2763,7 +3252,8 @@ bool BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context, float *values, int count, int index, - bool *r_force_all) + bool *r_force_all, + const struct AnimationEvalContext *anim_eval_context) { if (r_force_all != NULL) { *r_force_all = false; @@ -2779,11 +3269,13 @@ bool BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context, return false; } - /* Full influence Replace strips also require no correction. */ + /* Full influence Replace strips also require no correction if there are no upper tracks + * evaluating. */ int blend_mode = context->strip.blendmode; float influence = context->strip.influence; - if (blend_mode == NLASTRIP_MODE_REPLACE && influence == 1.0f) { + if (blend_mode == NLASTRIP_MODE_REPLACE && influence == 1.0f && + BLI_listbase_is_empty(&context->upper_estrips)) { return true; } @@ -2816,6 +3308,18 @@ bool BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context, BLI_bitmap_set_all(blended_necs->remap_domain.ptr, true, count); } + /** Need to send id_ptr instead of prop_ptr so fcurve RNA paths resolve properly. */ + PointerRNA id_ptr; + RNA_id_pointer_create(prop_ptr->owner_id, &id_ptr); + + /** Per iteration, remove effect of upper strip which gives output of nla stack below it. */ + LISTBASE_FOREACH_BACKWARD (NlaEvalStrip *, nes, &context->upper_estrips) { + /** This will disable blended_necs->remap_domain bits if an upper strip is not invertible + * (full replace, multiply zero, or transition). Then there is no remap solution. */ + nlasnapshot_blend_strip_get_inverted_lower_snapshot( + &id_ptr, eval_data, NULL, nes, &blended_snapshot, anim_eval_context); + } + /** Remove lower NLA stack effects. */ nlasnapshot_blend_get_inverted_upper_snapshot(eval_data, &context->lower_eval_data.eval_snapshot, @@ -2861,6 +3365,7 @@ void BKE_animsys_free_nla_keyframing_context_cache(struct ListBase *cache) { LISTBASE_FOREACH (NlaKeyframingContext *, ctx, cache) { MEM_SAFE_FREE(ctx->eval_strip); + BLI_freelistN(&ctx->upper_estrips); nlaeval_free(&ctx->lower_eval_data); } diff --git a/source/blender/blenkernel/nla_private.h b/source/blender/blenkernel/nla_private.h index b5d431a0a0d..47a029ad948 100644 --- a/source/blender/blenkernel/nla_private.h +++ b/source/blender/blenkernel/nla_private.h @@ -158,7 +158,11 @@ typedef struct NlaKeyframingContext { /* Data of the currently edited strip (copy, or fake strip for the main action). */ NlaStrip strip; NlaEvalStrip *eval_strip; + /** Storage for the action track as a strip. */ + NlaStrip action_track_strip; + /* Strips above tweaked strip. */ + ListBase upper_estrips; /* Evaluated NLA stack below the tweak strip. */ NlaEvalData lower_eval_data; } NlaKeyframingContext; @@ -177,13 +181,27 @@ NlaEvalStrip *nlastrips_ctime_get_strip(ListBase *list, short index, const struct AnimationEvalContext *anim_eval_context, const bool flush_to_original); -void nlastrip_evaluate(PointerRNA *ptr, + +enum eNlaStripEvaluate_Mode { + /** Blend strip with lower stack. */ + STRIP_EVAL_BLEND, + /** Given upper strip, solve for lower stack. */ + STRIP_EVAL_BLEND_GET_INVERTED_LOWER_SNAPSHOT, + /** Store strip fcurve values in snapshot. + * Currently only used for transitions to distinguish fcurve sampled values from existing + * default or lower stack values. The values of interest are in the blend_domain. */ + STRIP_EVAL_NOBLEND, +}; + +void nlastrip_evaluate(const int evaluation_mode, + PointerRNA *ptr, NlaEvalData *channels, ListBase *modifiers, NlaEvalStrip *nes, NlaEvalSnapshot *snapshot, const struct AnimationEvalContext *anim_eval_context, const bool flush_to_original); + void nladata_flush_channels(PointerRNA *ptr, NlaEvalData *channels, NlaEvalSnapshot *snapshot, @@ -209,6 +227,36 @@ void nlasnapshot_blend_get_inverted_upper_snapshot(NlaEvalData *eval_data, const float upper_influence, NlaEvalSnapshot *r_upper_snapshot); +void nlasnapshot_blend_get_inverted_lower_snapshot(NlaEvalData *eval_data, + NlaEvalSnapshot *blended_snapshot, + NlaEvalSnapshot *upper_snapshot, + const short upper_blendmode, + const float upper_influence, + NlaEvalSnapshot *r_lower_snapshot); + +void nlasnapshot_blend_strip(PointerRNA *ptr, + NlaEvalData *channels, + ListBase *modifiers, + NlaEvalStrip *nes, + NlaEvalSnapshot *snapshot, + const struct AnimationEvalContext *anim_eval_context, + const bool flush_to_original); + +void nlasnapshot_blend_strip_get_inverted_lower_snapshot( + PointerRNA *ptr, + NlaEvalData *eval_data, + ListBase *modifiers, + NlaEvalStrip *nes, + NlaEvalSnapshot *snapshot, + const struct AnimationEvalContext *anim_eval_context); + +void nlasnapshot_blend_strip_no_blend(PointerRNA *ptr, + NlaEvalData *channels, + ListBase *modifiers, + NlaEvalStrip *nes, + NlaEvalSnapshot *snapshot, + const struct AnimationEvalContext *anim_eval_context); + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index 4cc0413be5b..10df3804cbd 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -1122,7 +1122,8 @@ static float *get_keyframe_values(ReportList *reports, float *buffer, int buffer_size, int *r_count, - bool *r_force_all) + bool *r_force_all, + const struct AnimationEvalContext *anim_eval_context) { float *values; @@ -1140,9 +1141,11 @@ static float *get_keyframe_values(ReportList *reports, /* adjust the value for NLA factors */ if (!BKE_animsys_nla_remap_keyframe_values( - nla_context, &ptr, prop, values, *r_count, index, r_force_all)) { - BKE_report( - reports, RPT_ERROR, "Could not insert keyframe due to zero NLA influence or base value"); + nla_context, &ptr, prop, values, *r_count, index, r_force_all, anim_eval_context)) { + BKE_report(reports, + RPT_ERROR, + "Could not insert keyframe due to zero NLA influence, base value, or value " + "remapping failed"); if (values != buffer) { MEM_freeN(values); @@ -1306,7 +1309,8 @@ bool insert_keyframe_direct(ReportList *reports, value_buffer, RNA_MAX_ARRAY_LENGTH, &value_count, - NULL); + NULL, + anim_eval_context); if (values == NULL) { /* This happens if NLA rejects this insertion. */ @@ -1480,7 +1484,8 @@ int insert_keyframe(Main *bmain, value_buffer, RNA_MAX_ARRAY_LENGTH, &value_count, - &force_all); + &force_all, + anim_eval_context); if (values != NULL) { /* Key the entire array. */ -- cgit v1.2.3