Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWayde Moss <wbmoss_dev@yahoo.com>2021-02-17 23:58:58 +0300
committerWayde Moss <wbmoss_dev@yahoo.com>2021-02-22 23:33:02 +0300
commit2d0117ff45945e286e833dd2332efedb999da86d (patch)
treef8ff40139c2db49a2dc873e891625184c8c6a546
parent0cc91272e94782f4cda033d65fbac1656de301c2 (diff)
NLA: Keyframe Remap Through Upper Stripstemp_D10504_nla_keyframe_remap_upper_strips
Differential Revision: https://developer.blender.org/D10504
-rw-r--r--source/blender/blenkernel/BKE_animsys.h3
-rw-r--r--source/blender/blenkernel/intern/anim_sys.c645
-rw-r--r--source/blender/blenkernel/nla_private.h50
-rw-r--r--source/blender/editors/animation/keyframing.c17
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. */