[Bf-blender-cvs] [3acbe2d1e93] master: NLA: Keyframe Remap Through Upper Strips

Wayde Moss noreply at git.blender.org
Thu Apr 14 11:57:35 CEST 2022


Commit: 3acbe2d1e933db12dea4894c111de7394bc82551
Author: Wayde Moss
Date:   Thu Apr 14 11:38:36 2022 +0200
Branches: master
https://developer.blender.org/rB3acbe2d1e933db12dea4894c111de7394bc82551

NLA: Keyframe Remap Through Upper Strips

Add a new operator, "Start Tweaking Strip Actions (Full Stack)", which
allows you to insert keyframes and preserve the pose that you visually
keyed while upper strips are evaluating,

The old operator has been renamed from "Start Tweaking Strip Actions" to
"Start Tweaking Strip Actions (Lower Stack)" and remains the default for
the hotkey {key TAB}.

**Limitations, Keyframe Remapping Failure Cases**:
1. For *transitions* above the tweaked strip, keyframe remapping will
   fail for channel values that are affected by the transition. A work
   around is to tweak the active strip without evaluating the upper NLA
   stack.

   It's not supported because it's non-trivial and I couldn't figure it
   out for all transition combinations of blend modes. In the future, it
   would be nice if transitions (and metas) supported nested tracks
   instead of using the left/right strips for the transitions. That
   would allow the transitioned strips to overlap in time. It would also
   allow  N strips to be part of the (previously) left and right strips,
   or perhaps even N strips being transitioned in sequence (similar to a
   blend tree). Proper keyframe remapping through all that is currently
   beyond my mathematical ability. And even if I could figure it out,
   would it make sense to keyframe remap through a transition?

   //This case is reported to the user for failed keyframe insertions.//

2. Full replace upper strip that contains the keyed channels.

   //This case is reported to the user for failed keyframe insertions.//

3. When the same action clip occurs multiple times (colored Red to
   denote it's a linked strip) and vertically overlaps the tweaked
   strip, then the remapping will generally fail and is expected to
   fail.

   I don't plan on adding support for this case as it's also non-trivial
   and (hopefully) not a common or expected use case so it shouldn't be
   much of an issue to lack support here.

   For anyone curious on the cases that would work, it works when the
   linked strips aren't time-aligned and when we can insert a keyframe
   into the tweaked strip without modifying the current frame output of
   the other linked strips. Having all frames sampled and the strip
   non-time aligned leads to a working case. But if all key handles are
   AUTO, then it's likely to fail.

   //This case is not reported to the user for failed keyframe
   insertions.//

4. When using Quaternions and a small strip influence on the tweaked
   Combine strip. This was an existing failure case before this patch
   too but worth a mention in case it causes confusion. D10504 has an
   example file with instructions.

   //This case is not reported to the user for failed keyframe insertions. //

5. When an upper Replace strip with high influence and animator keys to
   Quaternion Combine (Replace is fine). This case is similar to (4)
   where Quaternion 180 degree rotation limitations prevent a solution.

   //This case is not reported to the user for failed keyframe insertions.//

Reviewed By: sybren, RiggingDojo

Differential Revision: https://developer.blender.org/D10504

===================================================================

M	release/scripts/presets/keyconfig/keymap_data/blender_default.py
M	release/scripts/startup/bl_ui/space_nla.py
M	source/blender/blenkernel/BKE_animsys.h
M	source/blender/blenkernel/intern/anim_sys.c
M	source/blender/blenkernel/intern/nla.c
M	source/blender/blenkernel/nla_private.h
M	source/blender/editors/animation/keyframing.c
M	source/blender/editors/space_nla/nla_edit.c
M	source/blender/makesdna/DNA_anim_types.h

===================================================================

diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
index bef89ac2b92..fc86d02b83e 100644
--- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py
+++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
@@ -2509,7 +2509,8 @@ def km_nla_generic(_params):
         *_template_space_region_type_toggle(
             sidebar_key={"type": 'N', "value": 'PRESS'},
         ),
-        ("nla.tweakmode_enter", {"type": 'TAB', "value": 'PRESS'}, None),
+        ("nla.tweakmode_enter", {"type": 'TAB', "value": 'PRESS'},
+         {"properties": [("use_upper_stack_evaluation", False)]}),
         ("nla.tweakmode_exit", {"type": 'TAB', "value": 'PRESS'}, None),
         ("nla.tweakmode_enter", {"type": 'TAB', "value": 'PRESS', "shift": True},
          {"properties": [("isolate_action", True)]}),
diff --git a/release/scripts/startup/bl_ui/space_nla.py b/release/scripts/startup/bl_ui/space_nla.py
index 961d6525a69..f0e991c768d 100644
--- a/release/scripts/startup/bl_ui/space_nla.py
+++ b/release/scripts/startup/bl_ui/space_nla.py
@@ -214,7 +214,8 @@ class NLA_MT_edit(Menu):
             layout.operator("nla.tweakmode_exit", text="Stop Tweaking Strip Actions")
         else:
             layout.operator("nla.tweakmode_enter", text="Start Editing Stashed Action").isolate_action = True
-            layout.operator("nla.tweakmode_enter", text="Start Tweaking Strip Actions")
+            layout.operator("nla.tweakmode_enter", text="Start Tweaking Strip Actions (Full Stack)").use_upper_stack_evaluation = True
+            layout.operator("nla.tweakmode_enter", text="Start Tweaking Strip Actions (Lower Stack)").use_upper_stack_evaluation = False
 
 
 class NLA_MT_add(Menu):
@@ -288,7 +289,8 @@ class NLA_MT_context_menu(Menu):
             layout.operator("nla.tweakmode_exit", text="Stop Tweaking Strip Actions")
         else:
             layout.operator("nla.tweakmode_enter", text="Start Editing Stashed Action").isolate_action = True
-            layout.operator("nla.tweakmode_enter", text="Start Tweaking Strip Actions")
+            layout.operator("nla.tweakmode_enter", text="Start Tweaking Strip Actions (Full Stack)").use_upper_stack_evaluation = True
+            layout.operator("nla.tweakmode_enter", text="Start Tweaking Strip Actions (Lower Stack)").use_upper_stack_evaluation = False
 
         layout.separator()
 
diff --git a/source/blender/blenkernel/BKE_animsys.h b/source/blender/blenkernel/BKE_animsys.h
index ded64b68f79..91ecfe09f38 100644
--- a/source/blender/blenkernel/BKE_animsys.h
+++ b/source/blender/blenkernel/BKE_animsys.h
@@ -7,6 +7,7 @@
  * \ingroup bke
  */
 
+#include "BLI_bitmap.h"
 #include "BLI_sys_types.h" /* for bool */
 
 #ifdef __cplusplus
@@ -258,16 +259,20 @@ struct NlaKeyframingContext *BKE_animsys_get_nla_keyframing_context(
  * \param count: Number of values in the array.
  * \param index: Index of the element about to be updated, or -1.
  * \param[out] r_force_all: Set to true if all channels must be inserted. May be NULL.
- * \return False if correction fails due to a division by zero,
- * or null r_force_all when all channels are required.
+ * \param[out] r_successful_remaps: Bits will be enabled for indices that are both intended to be
+ * remapped and succeeded remapping. With both, it allows caller to check successfully remapped
+ * indices without having to explicitly check whether the index was intended to be remapped.
  */
-bool BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context,
+void BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context,
                                            struct PointerRNA *prop_ptr,
                                            struct PropertyRNA *prop,
                                            float *values,
                                            int count,
                                            int index,
-                                           bool *r_force_all);
+                                           const struct AnimationEvalContext *anim_eval_context,
+                                           bool *r_force_all,
+                                           BLI_bitmap *r_successful_remaps);
+
 /**
  * Free all cached contexts from the list.
  */
diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c
index 8a5bf2b81dd..54fee079947 100644
--- a/source/blender/blenkernel/intern/anim_sys.c
+++ b/source/blender/blenkernel/intern/anim_sys.c
@@ -1531,6 +1531,192 @@ static NlaEvalChannel *nlaevalchan_verify(PointerRNA *ptr, NlaEvalData *nlaeval,
 
 /* ---------------------- */
 
+/** \returns true if a solution exists and the output was 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: {
+      /* Check for division by zero. */
+      const float denominator = (influence * strip_value + (1.0f - influence));
+      if (IS_EQF(denominator, 0.0f)) {
+        /* For 0/0, any r_lower_value is a solution. We'll just choose 1.
+         *
+         * Any r_lower_value is a solution. In this case, ideally we would insert redundant
+         * keyframes, those that don't change the fcurve's shape. Otherwise, we're likely messing
+         * up interpolation for the animator, requiring further cleanup on their part.
+         */
+        if (IS_EQF(blended_value, 0.0f)) {
+          /* When denominator==0:
+           *
+           *          denominator = (inf * strip_value + (1.0f - inf))
+           *                    0 =  inf * strip_value + (1-inf)
+           *   -inf * strip_value =  1 - inf
+           *         -strip_value = (1 - inf) / inf
+           *          strip_value = (inf - 1) / inf
+           *          strip_value = 1 - (1/inf)
+           *
+           * For blending, nla_blend_value(), this results in:
+           *
+           *        blended_value =  inf * (lower_value * strip_value)   + (1 - inf) * lower_value;
+           *                      =  inf * (lower_value * (1 - (1/inf))) + ...
+           *                      =  inf * (1 - (1/inf)) * lower_value   + ...
+           *                      =  (inf - (inf/inf))   * lower_value   + ...
+           *                      = -(inf - 1) * lower_value             + (1 - inf) * lower_value;
+           *        blended_value = 0
+           *
+           * Effectively, blended_value will equal 0 no matter what lower_value is. Put another
+           * way, when (blended_value==0 and denominator==0), then lower_value can be any value and
+           * blending will give us back blended_value=0. We have infinite solutions for this case.
+           */
+          *r_lower_value = 1;
+          return true;
+        }
+        /* No solution for 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))
+       *       lower_value = blended_value / denominator
+       */
+      *r_lower_value = blended_value / denominator;
+      return true;
+    }
+    case NLASTRIP_MODE_COMBINE:
+      BLI_assert_msg(0, "Use nla_combine_get_inverted_lower_value()");
+      return false;
+
+    case NLASTRIP_MODE_REPLACE:
+
+      /* 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;
+  }
+
+  BLI_assert_msg(0, "invalid blend mode");
+  return false;
+}
+
+/** \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.
+         *
+         * Any r_lower_value is a solution. In this case, ideally we would insert redundant
+         * keyframes, those that don't change the fcurve's shape. Otherwise, we're likely messing
+         * up interpolation for the animator, requiring further cleanup on their part.
+         */
+        if (IS_EQF(blended_value, 0.0f)) {
+          /*

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list