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:
-rw-r--r--source/blender/blenkernel/intern/anim_sys.c246
-rw-r--r--source/blender/blenkernel/nla_private.h13
-rw-r--r--source/blender/makesdna/DNA_anim_types.h3
-rw-r--r--source/blender/makesrna/intern/rna_nla.c6
4 files changed, 261 insertions, 7 deletions
diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c
index 8928ecbb5e2..2a11fba8eab 100644
--- a/source/blender/blenkernel/intern/anim_sys.c
+++ b/source/blender/blenkernel/intern/anim_sys.c
@@ -44,6 +44,8 @@
#include "BLI_dynstr.h"
#include "BLI_listbase.h"
#include "BLI_string_utils.h"
+#include "BLI_math_rotation.h"
+#include "BLI_math_vector.h"
#include "BLT_translation.h"
@@ -2191,6 +2193,10 @@ static void nlaeval_snapshot_free_data(NlaEvalSnapshot *snapshot)
static void nlaevalchan_free_data(NlaEvalChannel *nec)
{
nlavalidmask_free(&nec->valid);
+
+ if (nec->blend_snapshot != NULL) {
+ nlaevalchan_snapshot_free(nec->blend_snapshot);
+ }
}
/* Initialize a full NLA evaluation state structure. */
@@ -2243,6 +2249,17 @@ static void nlaevalchan_get_default_values(NlaEvalChannel *nec, float *r_values)
PropertyRNA *prop = nec->key.prop;
int length = nec->base_snapshot.length;
+ /* Use unit quaternion for quaternion properties. */
+ if (nec->mix_mode == NEC_MIX_QUATERNION) {
+ unit_qt(r_values);
+ return;
+ }
+ /* Use all zero for Axis-Angle properties. */
+ if (nec->mix_mode == NEC_MIX_AXIS_ANGLE) {
+ zero_v4(r_values);
+ return;
+ }
+
/* NOTE: while this doesn't work for all RNA properties as default values aren't in fact
* set properly for most of them, at least the common ones (which also happen to get used
* in NLA strips a lot, e.g. scale) are set correctly.
@@ -2296,6 +2313,33 @@ static void nlaevalchan_get_default_values(NlaEvalChannel *nec, float *r_values)
*r_values = 0.0f;
}
}
+
+ /* Ensure multiplicative properties aren't reset to 0. */
+ if (nec->mix_mode == NEC_MIX_MULTIPLY) {
+ for (int i = 0; i < length; i++) {
+ if (r_values[i] == 0.0f) {
+ r_values[i] = 1.0f;
+ }
+ }
+ }
+}
+
+static char nlaevalchan_detect_mix_mode(NlaEvalChannelKey *key, int length)
+{
+ PropertySubType subtype = RNA_property_subtype(key->prop);
+
+ if (subtype == PROP_QUATERNION && length == 4) {
+ return NEC_MIX_QUATERNION;
+ }
+ else if (subtype == PROP_AXISANGLE && length == 4) {
+ return NEC_MIX_AXIS_ANGLE;
+ }
+ else if (RNA_property_flag(key->prop) & PROP_PROPORTIONAL) {
+ return NEC_MIX_MULTIPLY;
+ }
+ else {
+ return NEC_MIX_ADD;
+ }
}
/* Verify that an appropriate NlaEvalChannel for this property exists. */
@@ -2324,6 +2368,8 @@ static NlaEvalChannel *nlaevalchan_verify_key(NlaEvalData *nlaeval, const char *
nec->index = nlaeval->num_channels++;
nec->is_array = is_array;
+ nec->mix_mode = nlaevalchan_detect_mix_mode(key, length);
+
nlavalidmask_init(&nec->valid, length);
nec->base_snapshot.channel = nec;
@@ -2409,6 +2455,10 @@ static float nla_blend_value(int blendmode, float old_value, float value, float
*/
return inf * (old_value * value) + (1 - inf) * old_value;
+ case NLASTRIP_MODE_COMBINE:
+ BLI_assert(!"combine mode");
+ ATTR_FALLTHROUGH;
+
case NLASTRIP_MODE_REPLACE:
default: /* TODO: do we really want to blend by default? it seems more uses might prefer add... */
/* do linear interpolation
@@ -2419,6 +2469,33 @@ static float nla_blend_value(int blendmode, float old_value, float value, float
}
}
+/* accumulate the old and new values of a channel according to mode and influence */
+static float nla_combine_value(int mix_mode, float base_value, float old_value, float value, float inf)
+{
+ /* optimisation: no need to try applying if there is no influence */
+ if (IS_EQF(inf, 0.0f)) {
+ return old_value;
+ }
+
+ /* perform blending */
+ switch (mix_mode) {
+ case NEC_MIX_ADD:
+ case NEC_MIX_AXIS_ANGLE:
+ return old_value + (value - base_value) * inf;
+
+ case NEC_MIX_MULTIPLY:
+ if (base_value == 0.0f) {
+ base_value = 1.0f;
+ }
+ return old_value * powf(value / base_value, inf);
+
+ case NEC_MIX_QUATERNION:
+ default:
+ BLI_assert(!"invalid mix mode");
+ return old_value;
+ }
+}
+
/* compute the value that would blend to the desired target value using nla_blend_value */
static bool nla_invert_blend_value(int blend_mode, float old_value, float target_value, float influence, float *r_value)
{
@@ -2446,6 +2523,10 @@ static bool nla_invert_blend_value(int blend_mode, float old_value, float target
return true;
}
+ case NLASTRIP_MODE_COMBINE:
+ BLI_assert(!"combine mode");
+ ATTR_FALLTHROUGH;
+
case NLASTRIP_MODE_REPLACE:
default:
*r_value = (target_value - old_value) / influence + old_value;
@@ -2453,13 +2534,92 @@ static bool nla_invert_blend_value(int blend_mode, float old_value, float target
}
}
+/* compute the value that would blend to the desired target value using nla_combine_value */
+static bool nla_invert_combine_value(int mix_mode, float base_value, float old_value, float target_value, float influence, float *r_value)
+{
+ switch (mix_mode) {
+ case NEC_MIX_ADD:
+ case NEC_MIX_AXIS_ANGLE:
+ *r_value = base_value + (target_value - old_value) / influence;
+ return true;
+
+ case NEC_MIX_MULTIPLY:
+ if (base_value == 0.0f) {
+ base_value = 1.0f;
+ }
+ if (old_value == 0.0f) {
+ /* Resolve 0/0 to 1. */
+ if (target_value == 0.0f) {
+ *r_value = base_value;
+ return true;
+ }
+ /* Division by zero. */
+ return false;
+ }
+ else {
+ *r_value = base_value * powf(target_value / old_value, 1.0f / influence);
+ return true;
+ }
+
+ case NEC_MIX_QUATERNION:
+ default:
+ BLI_assert(!"invalid mix mode");
+ return false;
+ }
+}
+
+/* accumulate quaternion channels for Combine mode according to influence */
+static void nla_combine_quaternion(const float old_values[4], const float values[4], float influence, float result[4])
+{
+ float tmp_old[4], tmp_new[4];
+
+ normalize_qt_qt(tmp_old, old_values);
+ normalize_qt_qt(tmp_new, values);
+
+ pow_qt_fl_normalized(tmp_new, influence);
+ mul_qt_qtqt(result, tmp_old, tmp_new);
+}
+
+/* invert accumulation of quaternion channels for Combine mode according to influence */
+static void nla_invert_combine_quaternion(const float old_values[4], const float values[4], float influence, float result[4])
+{
+ float tmp_old[4], tmp_new[4];
+
+ normalize_qt_qt(tmp_old, old_values);
+ normalize_qt_qt(tmp_new, values);
+ invert_qt_normalized(tmp_old);
+
+ mul_qt_qtqt(result, tmp_old, tmp_new);
+ pow_qt_fl_normalized(result, 1.0f / influence);
+}
+
/* Data about the current blend mode. */
typedef struct NlaBlendData {
NlaEvalSnapshot *snapshot;
int mode;
float influence;
+
+ NlaEvalChannel *blend_queue;
} NlaBlendData;
+/* Queue the channel for deferred blending. */
+static NlaEvalChannelSnapshot *nlaevalchan_queue_blend(NlaBlendData *blend, NlaEvalChannel *nec)
+{
+ if (!nec->in_blend) {
+ if (nec->blend_snapshot == NULL) {
+ nec->blend_snapshot = nlaevalchan_snapshot_new(nec);
+ }
+
+ nec->in_blend = true;
+ nlaevalchan_snapshot_copy(nec->blend_snapshot, &nec->base_snapshot);
+
+ nec->next_blend = blend->blend_queue;
+ blend->blend_queue = nec;
+ }
+
+ return nec->blend_snapshot;
+}
+
/* Accumulate (i.e. blend) the given value on to the channel it affects. */
static bool nlaeval_blend_value(NlaBlendData *blend, NlaEvalChannel *nec, int array_index, float value)
{
@@ -2479,15 +2639,58 @@ static bool nlaeval_blend_value(NlaBlendData *blend, NlaEvalChannel *nec, int ar
return false;
}
- BLI_BITMAP_ENABLE(nec->valid.ptr, index);
+ if (nec->mix_mode == NEC_MIX_QUATERNION) {
+ /* For quaternion properties, always output all sub-channels. */
+ BLI_bitmap_set_all(nec->valid.ptr, true, 4);
+ }
+ else {
+ BLI_BITMAP_ENABLE(nec->valid.ptr, index);
+ }
NlaEvalChannelSnapshot *nec_snapshot = nlaeval_snapshot_ensure_channel(blend->snapshot, nec);
+ float *p_value = &nec_snapshot->values[index];
- nec_snapshot->values[index] = nla_blend_value(blend->mode, nec_snapshot->values[index], value, blend->influence);
+ if (blend->mode == NLASTRIP_MODE_COMBINE) {
+ /* Quaternion blending is deferred until all sub-channel values are known. */
+ if (nec->mix_mode == NEC_MIX_QUATERNION) {
+ NlaEvalChannelSnapshot *blend_snapshot = nlaevalchan_queue_blend(blend, nec);
+
+ blend_snapshot->values[index] = value;
+ }
+ else {
+ float base_value = nec->base_snapshot.values[index];
+
+ *p_value = nla_combine_value(nec->mix_mode, base_value, *p_value, value, blend->influence);
+ }
+ }
+ else {
+ *p_value = nla_blend_value(blend->mode, *p_value, value, blend->influence);
+ }
return true;
}
+/* Finish deferred quaternion blending. */
+static void nlaeval_blend_flush(NlaBlendData *blend)
+{
+ NlaEvalChannel *nec;
+
+ while ((nec = blend->blend_queue)) {
+ blend->blend_queue = nec->next_blend;
+ nec->in_blend = false;
+
+ NlaEvalChannelSnapshot *nec_snapshot = nlaeval_snapshot_ensure_channel(blend->snapshot, nec);
+ NlaEvalChannelSnapshot *blend_snapshot = nec->blend_snapshot;
+
+ if (nec->mix_mode == NEC_MIX_QUATERNION) {
+ nla_combine_quaternion(nec_snapshot->values, blend_snapshot->values, blend->influence, nec_snapshot->values);
+ }
+ else {
+ BLI_assert(!"mix quaternion");
+ }
+ }
+}
+
/* Blend the specified snapshots into the target, and free the input snapshots. */
static void nlaeval_snapshot_mix_and_free(NlaEvalData *nlaeval, NlaEvalSnapshot *out, NlaEvalSnapshot *in1, NlaEvalSnapshot *in2, float alpha)
{
@@ -2651,6 +2854,8 @@ static void nlastrip_evaluate_actionclip(PointerRNA *ptr, NlaEvalData *channels,
nlaeval_blend_value(&blend, nec, fcu->array_index, value);
}
+ nlaeval_blend_flush(&blend);
+
/* free temporary storage */
evaluate_fmodifiers_storage_free(storage);
@@ -2835,6 +3040,12 @@ static void nla_eval_domain_action(PointerRNA *ptr, NlaEvalData *channels, bActi
NlaEvalChannel *nec = nlaevalchan_verify(ptr, channels, fcu->rna_path);
if (nec != NULL) {
+ /* For quaternion properties, enable all sub-channels. */
+ if (nec->mix_mode == NEC_MIX_QUATERNION) {
+ BLI_bitmap_set_all(nec->valid.ptr, true, 4);
+ continue;
+ }
+
int idx = nlaevalchan_validate_index(nec, fcu->array_index);
if (idx >= 0) {
@@ -3160,11 +3371,36 @@ bool BKE_animsys_nla_remap_keyframe_values(struct NlaKeyframingContext *context,
float *old_values = nec_snapshot->values;
- for (int i = 0; i < count; i++) {
- if (ELEM(index, i, -1)) {
- if (!nla_invert_blend_value(blend_mode, old_values[i], values[i], influence, &values[i])) {
+ if (blend_mode == NLASTRIP_MODE_COMBINE) {
+ /* Quaternion combine handles all sub-channels as a unit. */
+ if (nec->mix_mode == NEC_MIX_QUATERNION) {
+ if (r_force_all == NULL) {
return false;
}
+
+ *r_force_all = true;
+
+ nla_invert_combine_quaternion(old_values, values, influence, values);
+ }
+ else {
+ float *base_values = nec->base_snapshot.values;
+
+ for (int i = 0; i < count; i++) {
+ if (ELEM(index, i, -1)) {
+ if (!nla_invert_combine_value(nec->mix_mode, base_values[i], old_values[i], values[i], influence, &values[i])) {
+ return false;
+ }
+ }
+ }
+ }
+ }
+ else {
+ for (int i = 0; i < count; i++) {
+ if (ELEM(index, i, -1)) {
+ if (!nla_invert_blend_value(blend_mode, old_values[i], values[i], influence, &values[i])) {
+ return false;
+ }
+ }
}
}
diff --git a/source/blender/blenkernel/nla_private.h b/source/blender/blenkernel/nla_private.h
index d995eb5dd39..310ff1e2dbf 100644
--- a/source/blender/blenkernel/nla_private.h
+++ b/source/blender/blenkernel/nla_private.h
@@ -92,6 +92,14 @@ typedef struct NlaEvalChannelSnapshot {
/* Memory over-allocated to provide space for values. */
} NlaEvalChannelSnapshot;
+/* NlaEvalChannel->mix_mode */
+enum eNlaEvalChannel_MixMode {
+ NEC_MIX_ADD,
+ NEC_MIX_MULTIPLY,
+ NEC_MIX_QUATERNION,
+ NEC_MIX_AXIS_ANGLE,
+};
+
/* Temp channel for accumulating data from NLA for a single property.
* Handles array properties as a unit to allow intelligent blending. */
typedef struct NlaEvalChannel {
@@ -104,6 +112,11 @@ typedef struct NlaEvalChannel {
int index;
bool is_array;
+ bool in_blend;
+ char mix_mode;
+
+ struct NlaEvalChannel *next_blend;
+ NlaEvalChannelSnapshot *blend_snapshot;
/* Mask of array items controlled by NLA. */
NlaValidMask valid;
diff --git a/source/blender/makesdna/DNA_anim_types.h b/source/blender/makesdna/DNA_anim_types.h
index 15203812b2d..4df9c51609c 100644
--- a/source/blender/makesdna/DNA_anim_types.h
+++ b/source/blender/makesdna/DNA_anim_types.h
@@ -718,7 +718,8 @@ typedef enum eNlaStrip_Blend_Mode {
NLASTRIP_MODE_REPLACE = 0,
NLASTRIP_MODE_ADD,
NLASTRIP_MODE_SUBTRACT,
- NLASTRIP_MODE_MULTIPLY
+ NLASTRIP_MODE_MULTIPLY,
+ NLASTRIP_MODE_COMBINE,
} eNlaStrip_Blend_Mode;
/* NLA Strip Extrpolation Mode */
diff --git a/source/blender/makesrna/intern/rna_nla.c b/source/blender/makesrna/intern/rna_nla.c
index 7b5b2f13eef..ceedb5078a2 100644
--- a/source/blender/makesrna/intern/rna_nla.c
+++ b/source/blender/makesrna/intern/rna_nla.c
@@ -488,7 +488,11 @@ static void rna_NlaTrack_solo_set(PointerRNA *ptr, bool value)
/* enum defines exported for rna_animation.c */
const EnumPropertyItem rna_enum_nla_mode_blend_items[] = {
{NLASTRIP_MODE_REPLACE, "REPLACE", 0, "Replace",
- "Result strip replaces the accumulated results by amount specified by influence"},
+ "The strip values replace the accumulated results by amount specified by influence"},
+ {NLASTRIP_MODE_COMBINE, "COMBINE", 0, "Combine",
+ "The strip values are combined with accumulated results by appropriately using addition, "
+ "multiplication, or quaternion math, based on channel type"},
+ {0, "", 0, NULL, NULL},
{NLASTRIP_MODE_ADD, "ADD", 0, "Add", "Weighted result of strip is added to the accumulated results"},
{NLASTRIP_MODE_SUBTRACT, "SUBTRACT", 0, "Subtract",
"Weighted result of strip is removed from the accumulated results"},