Commit: d3caa9ec2dcecffac68deff3d1f728421bb92170
Author: Wayde Moss
Date:   Wed Oct 21 00:18:51 2020 -0400
Branches: arcpatch-D8867_Nla_Merge_Strips
https://developer.blender.org/rBd3caa9ec2dcecffac68deff3d1f728421bb92170

Feature: NLA: Evaluate Whole NLA Stack in Tweak Mode

Feature: NLA: show and evaluate whole NLA stack while in tweak mode:

**Note for reviewers**
This patch is relative to {D9247}. Apply that patch first, then apply this one 
afterward.

For reviewers, the two core functions changed are in anim_sys.c 
(BKE_animsys_nla_remap_keyframe_values and animsys_calculate_nla). I've 
separated the old animsys_evaluate_nla() into for_flush and for_keyframing 
variations to make the intent clear.

I should add that the nlastrip_evaluate() and recursive strip evaluate calls 
have been duplicated for inverting. They can be refactored but I've decided 
against it. I didn't want to refactor and add a new feature in a single patch. 
Keeping those calls nearly the same should make them easier to understand 
relative to the old implementation. I suppose I could've refactored first then 
made the patch, but without the duplication it could've been difficult to see 
the motivation for refactoring [...]

**Question**: I also noticed a UI-based confusion introduced by patch. If the 
animator has a nonpushed action then it has influence even while in tweak mode 
which is expected. However, the bounds of that influence is currently not drawn 
in any way while in tweak mode. It can be a surprise and source of confusion 
when the non-pushed action begins to influence the animation result. I should 
talk to the UI team about how it should be drawn. Or maybe it should be a 
separate patch so the UI ca [...]

**Problem/Solution**
This feature solves the problem of not being able to see and consider the final 
animation result while tweaking a strip. Before, as a user keyframes a strip, 
the upper strips would be disabled. Now, the user can optionally view the upper 
strip affects while keyframing. The strips above it are accounted for such that 
the final NLA result matches what the user visually keyframed. The feature pros 
and cons itself sounds self explanatory but let me know if I should explain 
more in depth.

**Implementation**
The core implementation is that each upper strip needs to be inverted 
separately, solving for the blend output of the lower stack. That goes on until 
we have solved for the blend output of the tweaked strip and lower stack. Then 
we use the old existing invert math to get the fcurve value of the tweak strip. 
The lower stack can be inverted as a group instead of separately because we're 
solving for the tweak strip's value, not the lower blended value, and it 
doesn't include the tweaked strip.

**Changes to old behavior (Evaluating without upper stack)**
Evaluating upper stack is optional and, for now, set as default (Tab). The 
RClick context menu and header menu exposes the option to evaluate without the 
upper stack (Ctrl+Tab).

Tweaked strip no longer uses animdata->act_influence since that's used by the 
non-pushed action.

**Bugfixes Included Relative to Public Version **
This includes bugfixes such as evaluating meta strips correctly when next to a 
transition {D8287}.

**Limitations Introduced by Patch**
Keyframing through a quaternion transition of (Combine<->Replace/Add/Sub/Mul) 
is not handled properly and will fail to insert a keyframe. I'm still working 
out the math, at least for the case of (Combine<->Replace). Other quaternion 
transition cases should be handled properly (Combine<->Combine) and 
(Add/Sub/Mul/Replace <-> Add/Sub/Mul/Replace). All non-quaternion transition 
cases should be handled properly. This limitation only applies when such 
transitions appear above the tweaked strip [...]

**Example Files**
Location Focused:
 {F8802904}
Quaternion Focused:
 {F8802907}
Scale Focused:
 {F8802906}

Each file has multiple objects in it that have simple NLA setups. It's just 
meant to save a little bit of time. Autokeying is on and keying sets match the 
file focus. Per check, just unhide one object at a time, enter tweak mode on 
the lowest strip, then try to keyframe. In most cases, your overall NLA results 
should be preserved when refreshing the keyframe(changing to different frame 
then back to the keyframed one). General failure cases include not being able 
to keyframe through full ( [...]

For location, it's easy to verify by using auto keying and snapping Suzanne to 
cursor. Refresh the frame and her origin should match the cursor. Generally, I 
just eyeball things and use the grid.

No specific Euler file since NLA evaluation is the same as location values. The 
Scale file only includes Combine strip examples since other blends modes 
evaluate the same as any other property, like location.

**Existing Limitations of NLA Not Solved By Patch**
If the tweaked strip's action evaluates multiple times in the same frame, then 
it's not solved for correctly. To support this is nontrivial. We have to allow 
keyframing at multiple frames and account for the different flags of each strip 
that uses the same action. The lower stack would have to be inverted separately 
just like the upper stack. Each strip may also sample the action using a 
different action range too.. Transitions between the linked action.. It's 
complicated. I expect it to  [...]
{F8883083}

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

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

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
M       source/blender/makesrna/intern/rna_animation.c

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

diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py 
b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
index b27cdbec308..da85004664b 100644
--- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py
+++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py
@@ -2162,6 +2162,9 @@ def km_nla_generic(_params):
          {"properties": [("isolate_action", True)]}),
         ("nla.tweakmode_exit", {"type": 'TAB', "value": 'PRESS', "shift": 
True},
          {"properties": [("isolate_action", True)]}),
+        ("nla.tweakmode_enter", {"type": 'TAB', "value": 'PRESS', "ctrl": 
True},
+         {"properties": [("use_upper_stack_evaluation", False)]}),
+        ("nla.tweakmode_exit", {"type": 'TAB', "value": 'PRESS', "ctrl": 
True},None),
         ("anim.channels_find", {"type": 'F', "value": 'PRESS', "ctrl": True}, 
None),
     ])
 
diff --git a/release/scripts/startup/bl_ui/space_nla.py 
b/release/scripts/startup/bl_ui/space_nla.py
index 4ecc4e7fdd9..44bea586dbc 100644
--- a/release/scripts/startup/bl_ui/space_nla.py
+++ b/release/scripts/startup/bl_ui/space_nla.py
@@ -198,6 +198,7 @@ class NLA_MT_edit(Menu):
         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 (Exclude Upper Stack)").use_upper_stack_evaluation = False
 
 
 class NLA_MT_add(Menu):
@@ -260,6 +261,7 @@ class NLA_MT_context_menu(Menu):
         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 (Exclude Upper 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 e812d04c7d1..ec0e42c0998 100644
--- a/source/blender/blenkernel/BKE_animsys.h
+++ b/source/blender/blenkernel/BKE_animsys.h
@@ -217,8 +217,10 @@ struct NlaKeyframingContext 
*BKE_animsys_get_nla_keyframing_context(
     const struct AnimationEvalContext *anim_eval_context,
     const bool flush_to_original);
 bool BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext 
*context,
+                                           const AnimationEvalContext 
*anim_eval_context,
                                            struct PointerRNA *prop_ptr,
                                            struct PropertyRNA *prop,
+                                           char rna_path[],
                                            float *values,
                                            int count,
                                            int index,
diff --git a/source/blender/blenkernel/intern/anim_sys.c 
b/source/blender/blenkernel/intern/anim_sys.c
index 5e42ef5d100..f5cbfdeb635 100644
--- a/source/blender/blenkernel/intern/anim_sys.c
+++ b/source/blender/blenkernel/intern/anim_sys.c
@@ -1043,6 +1043,8 @@ static NlaEvalChannelSnapshot 
*nlaevalchan_snapshot_new(NlaEvalChannel *nec)
 
   nec_snapshot->channel = nec;
   nec_snapshot->length = length;
+  nlavalidmask_init(&nec_snapshot->invertible, length);
+  nlavalidmask_init(&nec_snapshot->raw_value_sampled, length);
 
   return nec_snapshot;
 }
@@ -1052,6 +1054,8 @@ static void 
nlaevalchan_snapshot_free(NlaEvalChannelSnapshot *nec_snapshot)
 {
   BLI_assert(!nec_snapshot->is_base);
 
+  nlavalidmask_free(&nec_snapshot->invertible);
+  nlavalidmask_free(&nec_snapshot->raw_value_sampled);
   MEM_freeN(nec_snapshot);
 }
 
@@ -1385,6 +1389,23 @@ static NlaEvalChannel 
*nlaevalchan_verify_key(NlaEvalData *nlaeval,
   return nec;
 }
 
+/** Unlike nlaevalchan_verify(), this will not create a channel if it does not 
exist. */
+static bool nlaevalchan_try_get(NlaEvalData *nlaeval, const char *path, 
NlaEvalChannel **r_nec)
+{
+  if (path == NULL) {
+    return false;
+  }
+
+  /* Lookup the path in the path based hash. */
+  NlaEvalChannel *p_path_nec = (NlaEvalChannel 
*)BLI_ghash_lookup(nlaeval->path_hash,
+                                                                  (void 
*)path);
+
+  if (p_path_nec) {
+    *r_nec = p_path_nec;
+  }
+  return p_path_nec != NULL;
+}
+
 /* Verify that an appropriate NlaEvalChannel for this path exists. */
 static NlaEvalChannel *nlaevalchan_verify(PointerRNA *ptr, NlaEvalData 
*nlaeval, const char *path)
 {
@@ -1656,6 +1677,127 @@ static bool 
nla_combine_quaternion_invert_get_fcurve_values(const float lower_va
   return true;
 }
 
+/** \returns true if solution exists and output written to. */
+static bool nla_blend_value_invert_get_lower_value(const int blendmode,
+                                                   const float fcurve_value,
+                                                   const float blended_value,
+                                                   const float influence,
+                                                   float *r_lower_value)
+{
+  switch (blendmode) {
+    case NLASTRIP_MODE_ADD:
+      /* Simply subtract the scaled value on to the stack. */
+      *r_lower_value = blended_value - (fcurve_value * influence);
+      return true;
+
+    case NLASTRIP_MODE_SUBTRACT:
+      /* Simply add the scaled value from the stack. */
+      *r_lower_value = blended_value + (fcurve_value * influence);
+      return true;
+
+    case NLASTRIP_MODE_MULTIPLY:
+
+      /** Division by zero. */
+      if (IS_EQF(-fcurve_value * influence, 1.0f - influence)) {
+        /** Resolve 0/0 to 1. */
+        if (IS_EQF(0.0f, blended_value)) {
+          *r_lower_value = 1;
+          return true;
+        }
+        /** Division by zero. */
+        return false;
+      }
+      /* Math:
+       *     blended_value = inf * (lower_value * fcurve_value) + (1 - inf) * 
lower_value
+       *                   = lower_value * (inf * fcurve_value + (1-inf))
+       *         lower_value = blended_value / (inf * fcurve_value + (1-inf))
+       */
+      *r_lower_value = blended_value / (influence * fcurve_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(1.0f, influence)) {
+        return false;
+      }
+
+      /** Math:
+       *
+       *  blended_value = lower_value * (1.0f - inf) + (fcurve_value * inf)
+       *  blended_value - (fcurve_value * inf) = lower_value * (1.0f - inf)
+       *  blended_value - (fcurve_value * inf) / (1.0f - inf) = lower_value
+       *
+       *  lower_value = blended_value - (fcurve_value * inf) / (1.0f - inf)
+       */
+      *r_lower_value = (blended_value - (fcurve_value * influence)) / (1.0f - 
influence);
+      return true;
+  }
+}
+
+/** \returns true if solution exists and output written to. */
+static bool nla_combine_value_invert_get_lower_value(const int mix_mode,
+                                                     float base_value,
+                                                     const float fcurve_value,
+                                                     const float blended_value,
+                                                     const float inf,
+                                                     float *r_lower_value)
+{
+  /* Perform blending. */
+  switch (mix_mode) {
+    case NEC_MIX_ADD:
+    case NEC_MIX_AXIS_ANGLE:
+      *r_lower_value = blended_value - (fcurve_value - base_value) * inf;
+      return true;
+    case NEC_MIX_MULTIPLY:
+      /** Division by zero. */
+      if (IS_EQF(0.0f, fcurve_value)) {
+        /** Resolve 0/0 to 1. */
+        if (IS_EQF(0.0f, blended_value)) {
+          *r_lower_value = 1.0f;
+          return true;
+        }
+        return false;
+      }
+
+      if (IS_EQF(0.0f, base_value)) {
+        base_value = 1.0f;
+      }
+
+      *r_lower_value = blended_value / powf(fcurve_value / base_value, inf);
+      return true;
+
+    default:
+      BLI_assert(!"invalid mix mode");
+      return false;
+  }
+}
+
+static void nla_combine_quaternion_invert_get_lower_values(const float 
fcurve_values[4],
+                                                           const float 
blended_values[4],
+                                                           const float 
influence,
+                                                           float 
r_lower_value[4])
+{
+  /* blended_value = lower_values @ fcurve_values^infl
+   * blended_value @ inv(fcurve_values^inf) = lower_values
+   *
+   * Returns: lower_values = blended_value @ inv(fcurve_values^inf) */
+
+  float tmp_fcurve[4], tmp_blended[4];
+
+  normalize_qt_qt(tmp_fcurve, fcurve_values);
+  normalize_qt_qt(tmp_blended, blended_values);
+
+  pow_qt_fl_normalized(tmp_fcurve, influence);
+  invert_qt_normalized(tmp_fcurve);
+
+  mul_qt_qtqt(r_lower_value, tmp_blended, tmp_fcurve);
+}
+
 /* Data about the current blend mode. */
 typedef struct NlaBlendData {
   NlaEvalSnapshot *snapshot;
@@ -1720,6 +1862,46 @@ static bool nlaeval_blend_value(NlaBlendData *blend,
   return true;
 }
 
+/* Storing lower values within snapshot's necs->values if invertible. Marks 
non-invertible channels
+ * and defers quaternion combine inversion. */
+static void nlaeval_blend_value_invert_get_lower_value(NlaBlendData *blend,
+                                                       NlaEvalChannelSnapshot 
*necs,
+                                                       const int array_index,
+                                                       const float 
fcurve_value)
+{
+  NlaEvalChannel *nec = necs->channel;
+  if (!nlaevalchan_validate_index_ex(nec, array_index)) {
+    /** Note: no need to disable bits. If index invalid, then the fcurve 
wouldn't contribute
+     * anyways. */
+    return;
+  }
+
+  float *const p_value = &necs->values[array_index];
+
+  if (blend->mode == NLASTRIP_MODE_COMBINE) {
+    /* Quaternion blending is deferred until all sub-channel values are known. 
*/
+

@@ Diff output truncated at 10240 characters. @@

_______________________________________________
Bf-blender-cvs mailing list
[email protected]
https://lists.blender.org/mailman/listinfo/bf-blender-cvs

Reply via email to