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