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--release/scripts/startup/bl_ui/properties_constraint.py1
-rw-r--r--source/blender/blenkernel/intern/constraint.c36
-rw-r--r--source/blender/blenlib/BLI_math_matrix.h1
-rw-r--r--source/blender/blenlib/intern/math_matrix.c22
-rw-r--r--source/blender/editors/transform/transform_convert.c5
-rw-r--r--source/blender/makesdna/DNA_constraint_types.h17
-rw-r--r--source/blender/makesrna/intern/rna_constraint.c48
-rw-r--r--tests/python/bl_constraints.py64
8 files changed, 183 insertions, 11 deletions
diff --git a/release/scripts/startup/bl_ui/properties_constraint.py b/release/scripts/startup/bl_ui/properties_constraint.py
index a88def34767..2a0cf56534c 100644
--- a/release/scripts/startup/bl_ui/properties_constraint.py
+++ b/release/scripts/startup/bl_ui/properties_constraint.py
@@ -505,6 +505,7 @@ class ConstraintButtonsPanel:
self.target_template(layout, con)
+ layout.prop(con, "remove_target_shear")
layout.prop(con, "mix_mode", text="Mix")
self.space_template(layout, con)
diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c
index 193b6d82d03..41da728fa5b 100644
--- a/source/blender/blenkernel/intern/constraint.c
+++ b/source/blender/blenkernel/intern/constraint.c
@@ -2235,17 +2235,47 @@ static void translike_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *t
bConstraintTarget *ct = targets->first;
if (VALID_CONS_TARGET(ct)) {
+ float target_mat[4][4];
+
+ copy_m4_m4(target_mat, ct->matrix);
+
+ /* Remove the shear of the target matrix if enabled.
+ * Use Y as the axis since it's the natural default for bones. */
+ if (data->flag & TRANSLIKE_REMOVE_TARGET_SHEAR) {
+ orthogonalize_m4_stable(target_mat, 1, false);
+ }
+
+ /* Finally, combine the matrices. */
switch (data->mix_mode) {
case TRANSLIKE_MIX_REPLACE:
- copy_m4_m4(cob->matrix, ct->matrix);
+ copy_m4_m4(cob->matrix, target_mat);
+ break;
+
+ /* Simple matrix multiplication. */
+ case TRANSLIKE_MIX_BEFORE_FULL:
+ mul_m4_m4m4(cob->matrix, target_mat, cob->matrix);
+ break;
+
+ case TRANSLIKE_MIX_AFTER_FULL:
+ mul_m4_m4m4(cob->matrix, cob->matrix, target_mat);
break;
+ /* Aligned Inherit Scale emulation. */
case TRANSLIKE_MIX_BEFORE:
- mul_m4_m4m4_aligned_scale(cob->matrix, ct->matrix, cob->matrix);
+ mul_m4_m4m4_aligned_scale(cob->matrix, target_mat, cob->matrix);
break;
case TRANSLIKE_MIX_AFTER:
- mul_m4_m4m4_aligned_scale(cob->matrix, cob->matrix, ct->matrix);
+ mul_m4_m4m4_aligned_scale(cob->matrix, cob->matrix, target_mat);
+ break;
+
+ /* Fully separate handling of channels. */
+ case TRANSLIKE_MIX_BEFORE_SPLIT:
+ mul_m4_m4m4_split_channels(cob->matrix, target_mat, cob->matrix);
+ break;
+
+ case TRANSLIKE_MIX_AFTER_SPLIT:
+ mul_m4_m4m4_split_channels(cob->matrix, cob->matrix, target_mat);
break;
default:
diff --git a/source/blender/blenlib/BLI_math_matrix.h b/source/blender/blenlib/BLI_math_matrix.h
index 54df88ca541..0c3a184d302 100644
--- a/source/blender/blenlib/BLI_math_matrix.h
+++ b/source/blender/blenlib/BLI_math_matrix.h
@@ -211,6 +211,7 @@ 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_m4_m4m4_split_channels(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);
diff --git a/source/blender/blenlib/intern/math_matrix.c b/source/blender/blenlib/intern/math_matrix.c
index 5eb0125062d..88bef854213 100644
--- a/source/blender/blenlib/intern/math_matrix.c
+++ b/source/blender/blenlib/intern/math_matrix.c
@@ -1290,6 +1290,9 @@ bool invert_m4_m4(float inverse[4][4], const float mat[4][4])
* 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.
+ *
+ * Note: this effectively takes output location from simple multiplication,
+ * and uses mul_m4_m4m4_split_channels for rotation and scale.
*/
void mul_m4_m4m4_aligned_scale(float R[4][4], const float A[4][4], const float B[4][4])
{
@@ -1307,6 +1310,25 @@ void mul_m4_m4m4_aligned_scale(float R[4][4], const float A[4][4], const float B
loc_rot_size_to_mat4(R, loc_r, rot_r, size_r);
}
+/**
+ * Separately combines location, rotation and scale of the input matrices.
+ */
+void mul_m4_m4m4_split_channels(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);
+
+ add_v3_v3v3(loc_r, loc_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 R[3][3])
diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c
index 99368a40225..00fd008151d 100644
--- a/source/blender/editors/transform/transform_convert.c
+++ b/source/blender/editors/transform/transform_convert.c
@@ -849,10 +849,13 @@ bool constraints_list_needinv(TransInfo *t, ListBase *list)
/* Copy Transforms constraint only does this in the Before mode. */
bTransLikeConstraint *data = (bTransLikeConstraint *)con->data;
- if (ELEM(data->mix_mode, TRANSLIKE_MIX_BEFORE) &&
+ if (ELEM(data->mix_mode, TRANSLIKE_MIX_BEFORE, TRANSLIKE_MIX_BEFORE_FULL) &&
ELEM(t->mode, TFM_ROTATION, TFM_TRANSLATION)) {
return true;
}
+ if (ELEM(data->mix_mode, TRANSLIKE_MIX_BEFORE_SPLIT) && ELEM(t->mode, TFM_ROTATION)) {
+ return true;
+ }
}
else if (con->type == CONSTRAINT_TYPE_ACTION) {
/* The Action constraint only does this in the Before mode. */
diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h
index 1e4fd2a70f2..25f72da2f15 100644
--- a/source/blender/makesdna/DNA_constraint_types.h
+++ b/source/blender/makesdna/DNA_constraint_types.h
@@ -316,8 +316,9 @@ typedef struct bSameVolumeConstraint {
/* Copy Transform Constraint */
typedef struct bTransLikeConstraint {
struct Object *tar;
+ int flag;
char mix_mode;
- char _pad[7];
+ char _pad[3];
/** MAX_ID_NAME-2. */
char subtarget[64];
} bTransLikeConstraint;
@@ -810,6 +811,12 @@ typedef enum eCopyScale_Flags {
SIZELIKE_UNIFORM = (1 << 5),
} eCopyScale_Flags;
+/* bTransLikeConstraint.flag */
+typedef enum eCopyTransforms_Flags {
+ /* Remove shear from the target matrix. */
+ TRANSLIKE_REMOVE_TARGET_SHEAR = (1 << 0),
+} eCopyTransforms_Flags;
+
/* bTransLikeConstraint.mix_mode */
typedef enum eCopyTransforms_MixMode {
/* Replace rotation channel values. */
@@ -818,6 +825,14 @@ typedef enum eCopyTransforms_MixMode {
TRANSLIKE_MIX_BEFORE = 1,
/* Multiply the copied transformation on the right, with anti-shear scale handling. */
TRANSLIKE_MIX_AFTER = 2,
+ /* Multiply the copied transformation on the left, handling loc/rot/scale separately. */
+ TRANSLIKE_MIX_BEFORE_SPLIT = 3,
+ /* Multiply the copied transformation on the right, handling loc/rot/scale separately. */
+ TRANSLIKE_MIX_AFTER_SPLIT = 4,
+ /* Multiply the copied transformation on the left, using simple matrix multiplication. */
+ TRANSLIKE_MIX_BEFORE_FULL = 5,
+ /* Multiply the copied transformation on the right, using simple matrix multiplication. */
+ TRANSLIKE_MIX_AFTER_FULL = 6,
} eCopyTransforms_MixMode;
/* bTransformConstraint.to/from */
diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c
index d79f2c4d13f..d34885e1a68 100644
--- a/source/blender/makesrna/intern/rna_constraint.c
+++ b/source/blender/makesrna/intern/rna_constraint.c
@@ -1624,18 +1624,48 @@ static void rna_def_constraint_transform_like(BlenderRNA *brna)
0,
"Replace",
"Replace the original transformation with copied"},
+ {0, "", 0, NULL, NULL},
+ {TRANSLIKE_MIX_BEFORE_FULL,
+ "BEFORE_FULL",
+ 0,
+ "Before Original (Full)",
+ "Apply copied transformation before original, using simple matrix multiplication as if "
+ "the constraint target is a parent in Full Inherit Scale mode. "
+ "Will create shear when combining rotation and non-uniform scale"},
{TRANSLIKE_MIX_BEFORE,
"BEFORE",
0,
- "Before Original",
- "Apply copied transformation before original, as if the constraint target is a parent. "
- "Scale is handled specially to avoid creating shear"},
+ "Before Original (Aligned)",
+ "Apply copied transformation before original, as if the constraint target is a parent in "
+ "Aligned Inherit Scale mode. This effectively uses Full for location and Split Channels "
+ "for rotation and scale"},
+ {TRANSLIKE_MIX_BEFORE_SPLIT,
+ "BEFORE_SPLIT",
+ 0,
+ "Before Original (Split Channels)",
+ "Apply copied transformation before original, handling location, rotation and scale "
+ "separately, similar to a sequence of three Copy constraints"},
+ {0, "", 0, NULL, NULL},
+ {TRANSLIKE_MIX_AFTER_FULL,
+ "AFTER_FULL",
+ 0,
+ "After Original (Full)",
+ "Apply copied transformation after original, using simple matrix multiplication as if "
+ "the constraint target is a child in Full Inherit Scale mode. "
+ "Will create shear when combining rotation and non-uniform scale"},
{TRANSLIKE_MIX_AFTER,
"AFTER",
0,
- "After Original",
- "Apply copied transformation after original, as if the constraint target is a child. "
- "Scale is handled specially to avoid creating shear"},
+ "After Original (Aligned)",
+ "Apply copied transformation after original, as if the constraint target is a child in "
+ "Aligned Inherit Scale mode. This effectively uses Full for location and Split Channels "
+ "for rotation and scale"},
+ {TRANSLIKE_MIX_AFTER_SPLIT,
+ "AFTER_SPLIT",
+ 0,
+ "After Original (Split Channels)",
+ "Apply copied transformation after original, handling location, rotation and scale "
+ "separately, similar to a sequence of three Copy constraints"},
{0, NULL, 0, NULL, NULL},
};
@@ -1653,6 +1683,12 @@ static void rna_def_constraint_transform_like(BlenderRNA *brna)
RNA_define_lib_overridable(true);
+ prop = RNA_def_property(srna, "remove_target_shear", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", TRANSLIKE_REMOVE_TARGET_SHEAR);
+ RNA_def_property_ui_text(
+ prop, "Remove Target Shear", "Remove shear from the target transformation before combining");
+ RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
+
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);
diff --git a/tests/python/bl_constraints.py b/tests/python/bl_constraints.py
index 279c896c6af..a2690fa4e11 100644
--- a/tests/python/bl_constraints.py
+++ b/tests/python/bl_constraints.py
@@ -375,6 +375,70 @@ class CustomSpaceTest(AbstractConstraintTests):
)))
+class CopyTransformsTest(AbstractConstraintTests):
+ layer_collection = 'Copy Transforms'
+
+ def test_mix_mode_object(self):
+ """Copy Transforms: all mix modes for objects"""
+ constraint = bpy.data.objects["Copy Transforms.object.owner"].constraints["Copy Transforms"]
+
+ constraint.mix_mode = 'REPLACE'
+ self.matrix_test('Copy Transforms.object.owner', Matrix((
+ (-0.7818737626075745, 0.14389121532440186, 0.4845699667930603, -0.017531070858240128),
+ (-0.2741589844226837, -0.591389000415802, -1.2397242784500122, -0.08039521425962448),
+ (0.04909384995698929, -1.0109175443649292, 0.7942137122154236, 0.1584688276052475),
+ (0.0, 0.0, 0.0, 1.0)
+ )))
+
+ constraint.mix_mode = 'BEFORE_FULL'
+ self.matrix_test('Copy Transforms.object.owner', Matrix((
+ (-1.0791258811950684, -0.021011866629123688, 0.3120136260986328, 0.9082338809967041),
+ (0.2128538191318512, -0.3411901891231537, -1.7376484870910645, -0.39762523770332336),
+ (-0.03584420680999756, -1.0162957906723022, 0.8004404306411743, -0.9015425443649292),
+ (0.0, 0.0, 0.0, 1.0)
+ )))
+
+ constraint.mix_mode = 'BEFORE'
+ self.matrix_test('Copy Transforms.object.owner', Matrix((
+ (-0.9952367544174194, -0.03077685832977295, 0.05301344022154808, 0.9082338809967041),
+ (-0.013416174799203873, -0.39984768629074097, -1.8665285110473633, -0.39762523770332336),
+ (0.03660336509346962, -0.9833710193634033, 0.75728839635849, -0.9015425443649292),
+ (0.0, 0.0, 0.0, 1.0)
+ )))
+
+ constraint.mix_mode = 'BEFORE_SPLIT'
+ self.matrix_test('Copy Transforms.object.owner', Matrix((
+ (-0.9952367544174194, -0.03077685832977295, 0.05301344022154808, -1.0175310373306274),
+ (-0.013416174799203873, -0.39984768629074097, -1.8665285110473633, 0.9196047782897949),
+ (0.03660336509346962, -0.9833710193634033, 0.75728839635849, 0.1584688276052475),
+ (0.0, 0.0, 0.0, 1.0)
+ )))
+
+ constraint.mix_mode = 'AFTER_FULL'
+ self.matrix_test('Copy Transforms.object.owner', Matrix((
+ (-0.8939255475997925, -0.2866469621658325, 0.7563635110855103, -0.964445173740387),
+ (-0.09460853785276413, -0.73727947473526, -1.0267245769500732, 0.9622588753700256),
+ (0.37042146921157837, -1.1893107891082764, 1.0113294124603271, 0.21314144134521484),
+ (0.0, 0.0, 0.0, 1.0)
+ )))
+
+ constraint.mix_mode = 'AFTER'
+ self.matrix_test('Copy Transforms.object.owner', Matrix((
+ (-0.9033845067024231, -0.2048732340335846, 0.7542480826377869, -0.964445173740387),
+ (-0.1757974475622177, -0.6721230745315552, -1.5190268754959106, 0.9622588753700256),
+ (0.38079890608787537, -0.7963172793388367, 1.0880682468414307, 0.21314144134521484),
+ (0.0, 0.0, 0.0, 1.0)
+ )))
+
+ constraint.mix_mode = 'AFTER_SPLIT'
+ self.matrix_test('Copy Transforms.object.owner', Matrix((
+ (-0.9033845067024231, -0.2048732340335846, 0.7542480826377869, -1.0175310373306274),
+ (-0.1757974475622177, -0.6721230745315552, -1.5190268754959106, 0.9196047782897949),
+ (0.38079890608787537, -0.7963172793388367, 1.0880682468414307, 0.1584688276052475),
+ (0.0, 0.0, 0.0, 1.0)
+ )))
+
+
def main():
global args
import argparse