diff options
-rw-r--r-- | release/scripts/startup/bl_ui/properties_constraint.py | 2 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/constraint.c | 66 | ||||
-rw-r--r-- | source/blender/blenlib/BLI_math_matrix.h | 2 | ||||
-rw-r--r-- | source/blender/blenlib/intern/math_matrix.c | 21 | ||||
-rw-r--r-- | source/blender/editors/transform/transform_convert.c | 9 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_constraint_types.h | 12 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_constraint.c | 50 |
7 files changed, 128 insertions, 34 deletions
diff --git a/release/scripts/startup/bl_ui/properties_constraint.py b/release/scripts/startup/bl_ui/properties_constraint.py index b75d67b5350..3fc54ff6d12 100644 --- a/release/scripts/startup/bl_ui/properties_constraint.py +++ b/release/scripts/startup/bl_ui/properties_constraint.py @@ -492,6 +492,8 @@ class ConstraintButtonsPanel: col.prop(con, "frame_start", text="Start") col.prop(con, "frame_end", text="End") + layout.prop(con, "mix_mode", text="Mix") + def LOCKED_TRACK(self, _context, layout, con): self.target_template(layout, con) diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index c397fbcf115..a17a09297c5 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -2062,36 +2062,21 @@ static void translike_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *t bConstraintTarget *ct = targets->first; if (VALID_CONS_TARGET(ct)) { - if (data->mix_mode == TRANSLIKE_MIX_REPLACE) { - /* just copy the entire transform matrix of the target */ - copy_m4_m4(cob->matrix, ct->matrix); - } - else { - float old_loc[3], old_rot[3][3], old_size[3]; - float new_loc[3], new_rot[3][3], new_size[3]; - - /* Separate matrices so they can be combined in a way that avoids shear. */ - mat4_to_loc_rot_size(old_loc, old_rot, old_size, cob->matrix); - mat4_to_loc_rot_size(new_loc, new_rot, new_size, ct->matrix); - - switch (data->mix_mode) { - case TRANSLIKE_MIX_BEFORE: - mul_v3_m4v3(new_loc, ct->matrix, old_loc); - mul_m3_m3m3(new_rot, new_rot, old_rot); - mul_v3_v3(new_size, old_size); - break; + switch (data->mix_mode) { + case TRANSLIKE_MIX_REPLACE: + copy_m4_m4(cob->matrix, ct->matrix); + break; - case TRANSLIKE_MIX_AFTER: - mul_v3_m4v3(new_loc, cob->matrix, new_loc); - mul_m3_m3m3(new_rot, old_rot, new_rot); - mul_v3_v3(new_size, old_size); - break; + case TRANSLIKE_MIX_BEFORE: + mul_m4_m4m4_aligned_scale(cob->matrix, ct->matrix, cob->matrix); + break; - default: - BLI_assert(false); - } + case TRANSLIKE_MIX_AFTER: + mul_m4_m4m4_aligned_scale(cob->matrix, cob->matrix, ct->matrix); + break; - loc_rot_size_to_mat4(cob->matrix, new_loc, new_rot, new_size); + default: + BLI_assert(!"Unknown Copy Transforms mix mode"); } } } @@ -2555,6 +2540,9 @@ static void actcon_new_data(void *cdata) /* set type to 20 (Loc X), as 0 is Rot X for backwards compatibility */ data->type = 20; + + /* Set the mix mode to After Original with anti-shear scale handling. */ + data->mix_mode = ACTCON_MIX_AFTER; } static void actcon_id_looper(bConstraint *con, ConstraintIDFunc func, void *userdata) @@ -2695,18 +2683,28 @@ static void actcon_get_tarmat(struct Depsgraph *UNUSED(depsgraph), } } -static void actcon_evaluate(bConstraint *UNUSED(con), bConstraintOb *cob, ListBase *targets) +static void actcon_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *targets) { + bActionConstraint *data = con->data; bConstraintTarget *ct = targets->first; if (VALID_CONS_TARGET(ct)) { - float temp[4][4]; + switch (data->mix_mode) { + case ACTCON_MIX_BEFORE: + mul_m4_m4m4_aligned_scale(cob->matrix, ct->matrix, cob->matrix); + break; - /* Nice and simple... we just need to multiply the matrices, as the get_target_matrix - * function has already taken care of everything else. - */ - copy_m4_m4(temp, cob->matrix); - mul_m4_m4m4(cob->matrix, temp, ct->matrix); + case ACTCON_MIX_AFTER: + mul_m4_m4m4_aligned_scale(cob->matrix, cob->matrix, ct->matrix); + break; + + case ACTCON_MIX_AFTER_FULL: + mul_m4_m4m4(cob->matrix, cob->matrix, ct->matrix); + break; + + default: + BLI_assert(!"Unknown Action mix mode"); + } } } diff --git a/source/blender/blenlib/BLI_math_matrix.h b/source/blender/blenlib/BLI_math_matrix.h index 814b13fa47f..ac0f5f44c74 100644 --- a/source/blender/blenlib/BLI_math_matrix.h +++ b/source/blender/blenlib/BLI_math_matrix.h @@ -205,6 +205,8 @@ void mul_transposed_m3_v3(const float M[3][3], float r[3]); void mul_transposed_mat3_m4_v3(const float M[4][4], float r[3]); void mul_m3_v3_double(const float M[3][3], double r[3]); +void mul_m4_m4m4_aligned_scale(float R[4][4], const float A[4][4], const float B[4][4]); + void mul_m3_fl(float R[3][3], float f); void mul_m4_fl(float R[4][4], float f); void mul_mat3_m4_fl(float R[4][4], float f); diff --git a/source/blender/blenlib/intern/math_matrix.c b/source/blender/blenlib/intern/math_matrix.c index 5118d8a698e..4d8a2f72eca 100644 --- a/source/blender/blenlib/intern/math_matrix.c +++ b/source/blender/blenlib/intern/math_matrix.c @@ -1235,6 +1235,27 @@ bool invert_m4_m4(float inverse[4][4], const float mat[4][4]) #endif } +/** + * Combines transformations, handling scale separately in a manner equivalent + * to the Aligned Inherit Scale mode, in order to avoid creating shear. + * If A scale is uniform, the result is equivalent to ordinary multiplication. + */ +void mul_m4_m4m4_aligned_scale(float R[4][4], const float A[4][4], const float B[4][4]) +{ + float loc_a[3], rot_a[3][3], size_a[3]; + float loc_b[3], rot_b[3][3], size_b[3]; + float loc_r[3], rot_r[3][3], size_r[3]; + + mat4_to_loc_rot_size(loc_a, rot_a, size_a, A); + mat4_to_loc_rot_size(loc_b, rot_b, size_b, B); + + mul_v3_m4v3(loc_r, A, loc_b); + mul_m3_m3m3_uniq(rot_r, rot_a, rot_b); + mul_v3_v3v3(size_r, size_a, size_b); + + loc_rot_size_to_mat4(R, loc_r, rot_r, size_r); +} + /****************************** Linear Algebra *******************************/ void transpose_m3(float mat[3][3]) diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index db8f36883f8..2001d42a5eb 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -1350,6 +1350,15 @@ bool constraints_list_needinv(TransInfo *t, ListBase *list) return true; } } + else if (con->type == CONSTRAINT_TYPE_ACTION) { + /* The Action constraint only does this in the Before mode. */ + bActionConstraint *data = (bActionConstraint *)con->data; + + if (ELEM(data->mix_mode, ACTCON_MIX_BEFORE) && + ELEM(t->mode, TFM_ROTATION, TFM_TRANSLATION)) { + return true; + } + } else if (con->type == CONSTRAINT_TYPE_TRANSFORM) { /* Transform constraint needs it for rotation at least (r.57309), * but doing so when translating may also mess things up [#36203] diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h index 0baa11c3059..f816041010b 100644 --- a/source/blender/makesdna/DNA_constraint_types.h +++ b/source/blender/makesdna/DNA_constraint_types.h @@ -336,6 +336,8 @@ typedef struct bActionConstraint { float min; float max; int flag; + char mix_mode; + char _pad[7]; struct bAction *act; /** MAX_ID_NAME-2. */ char subtarget[64]; @@ -865,6 +867,16 @@ typedef enum eActionConstraint_Flags { ACTCON_BONE_USE_OBJECT_ACTION = (1 << 0), } eActionConstraint_Flags; +/* bActionConstraint.mix_mode */ +typedef enum eActionConstraint_MixMode { + /* Multiply the action transformation on the right. */ + ACTCON_MIX_AFTER_FULL = 0, + /* Multiply the action transformation on the right, with anti-shear scale handling. */ + ACTCON_MIX_AFTER, + /* Multiply the action transformation on the left, with anti-shear scale handling. */ + ACTCON_MIX_BEFORE, +} eActionConstraint_MixMode; + /* Locked-Axis Values (Locked Track) */ typedef enum eLockAxis_Modes { LOCK_X = 0, diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c index 6a10074810d..8e57de9baeb 100644 --- a/source/blender/makesrna/intern/rna_constraint.c +++ b/source/blender/makesrna/intern/rna_constraint.c @@ -633,6 +633,23 @@ static void rna_ArmatureConstraint_target_clear(ID *id, bConstraint *con, Main * ED_object_constraint_dependency_tag_update(bmain, (Object *)id, con); } +static void rna_ActionConstraint_mix_mode_set(PointerRNA *ptr, int value) +{ + bConstraint *con = (bConstraint *)ptr->data; + bActionConstraint *acon = (bActionConstraint *)con->data; + + acon->mix_mode = value; + + /* The After mode can be computed in world space for efficiency + * and backward compatibility, while Before requires Local. */ + if (ELEM(value, ACTCON_MIX_AFTER, ACTCON_MIX_AFTER_FULL)) { + con->ownspace = CONSTRAINT_SPACE_WORLD; + } + else { + con->ownspace = CONSTRAINT_SPACE_LOCAL; + } +} + static void rna_ActionConstraint_minmax_range( PointerRNA *ptr, float *min, float *max, float *UNUSED(softmin), float *UNUSED(softmax)) { @@ -1618,6 +1635,29 @@ static void rna_def_constraint_action(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem mix_mode_items[] = { + {ACTCON_MIX_BEFORE, + "BEFORE", + 0, + "Before Original", + "Apply the action channels before the original transformation, " + "as if applied to an imaginary parent with Aligned Inherit Scale"}, + {ACTCON_MIX_AFTER, + "AFTER", + 0, + "After Original", + "Apply the action channels after the original transformation, " + "as if applied to an imaginary child with Aligned Inherit Scale"}, + {ACTCON_MIX_AFTER_FULL, + "AFTER_FULL", + 0, + "After Original (Full Scale)", + "Apply the action channels after the original transformation, as if " + "applied to an imaginary child with Full Inherit Scale. This mode " + "can create shear and is provided only for backward compatibility"}, + {0, NULL, 0, NULL, NULL}, + }; + srna = RNA_def_struct(brna, "ActionConstraint", "Constraint"); RNA_def_struct_ui_text( srna, "Action Constraint", "Map an action to the transform axes of a bone"); @@ -1626,6 +1666,16 @@ static void rna_def_constraint_action(BlenderRNA *brna) rna_def_constraint_target_common(srna); + prop = RNA_def_property(srna, "mix_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "mix_mode"); + RNA_def_property_enum_items(prop, mix_mode_items); + RNA_def_property_enum_funcs(prop, NULL, "rna_ActionConstraint_mix_mode_set", NULL); + RNA_def_property_ui_text( + prop, + "Mix Mode", + "Specify how existing transformations and the action channels are combined"); + RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); + prop = RNA_def_property(srna, "transform_channel", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "type"); RNA_def_property_enum_items(prop, transform_channel_items); |