Copy Transforms: add a mixing mode option.
authorAlexander Gavrilov <angavrilov@gmail.com>
Wed, 4 Sep 2019 11:59:18 +0000 (14:59 +0300)
committerAlexander Gavrilov <angavrilov@gmail.com>
Fri, 6 Sep 2019 04:58:13 +0000 (07:58 +0300)
Allow combining location, rotation and scale at the same time,
using one constraint. The mixing modes are based on matrix
multiplication, but handle scale in a way that avoids creating
shear.

Reviewers: brecht

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

release/scripts/startup/bl_ui/properties_constraint.py
source/blender/blenkernel/intern/constraint.c
source/blender/editors/transform/transform_convert.c
source/blender/makesdna/DNA_constraint_types.h
source/blender/makesrna/intern/rna_constraint.c

index ac1ebedeba47a8a05f659fd16705c4adb68b7cb9..df53fccf9ea1f8e58d9510685df0b32a349e5055 100644 (file)
@@ -459,6 +459,8 @@ class ConstraintButtonsPanel:
     def COPY_TRANSFORMS(self, _context, layout, con):
         self.target_template(layout, con)
 
+        layout.prop(con, "mix_mode", text="Mix")
+
         self.space_template(layout, con)
 
     # def SCRIPT(self, context, layout, con):
index 803aae76422ae0912f08b8164b1c115236a2d8fd..317973cc0c1c528adf65af62d4c1b2fbae8d1b12 100644 (file)
@@ -2090,13 +2090,43 @@ static void translike_flush_tars(bConstraint *con, ListBase *list, bool no_copy)
   }
 }
 
-static void translike_evaluate(bConstraint *UNUSED(con), bConstraintOb *cob, ListBase *targets)
+static void translike_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *targets)
 {
+  bTransLikeConstraint *data = con->data;
   bConstraintTarget *ct = targets->first;
 
   if (VALID_CONS_TARGET(ct)) {
-    /* just copy the entire transform matrix of the target */
-    copy_m4_m4(cob->matrix, ct->matrix);
+    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;
+
+        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;
+
+        default:
+          BLI_assert(false);
+      }
+
+      loc_rot_size_to_mat4(cob->matrix, new_loc, new_rot, new_size);
+    }
   }
 }
 
index 14eee2318cde6af7af9a5bb7d9d728680f7d82f2..cbb291414bfd648e464095b790351b60e7d7f999 100644 (file)
@@ -1335,6 +1335,15 @@ bool constraints_list_needinv(TransInfo *t, ListBase *list)
             return true;
           }
         }
+        else if (con->type == CONSTRAINT_TYPE_TRANSLIKE) {
+          /* Copy Transforms constraint only does this in the Before mode. */
+          bTransLikeConstraint *data = (bTransLikeConstraint *)con->data;
+
+          if (ELEM(data->mix_mode, TRANSLIKE_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]
index a7f900ddc9b79f6ee2275c1f0485a1e7ff401f43..d123cf21db95b21f9f46715da7741b9f97e29373 100644 (file)
@@ -307,6 +307,8 @@ typedef struct bSameVolumeConstraint {
 /* Copy Transform Constraint */
 typedef struct bTransLikeConstraint {
   struct Object *tar;
+  char mix_mode;
+  char _pad[7];
   /** MAX_ID_NAME-2. */
   char subtarget[64];
 } bTransLikeConstraint;
@@ -790,6 +792,16 @@ typedef enum eCopyScale_Flags {
   SIZELIKE_UNIFORM = (1 << 5),
 } eCopyScale_Flags;
 
+/* bTransLikeConstraint.mix_mode */
+typedef enum eCopyTransforms_MixMode {
+  /* Replace rotation channel values. */
+  TRANSLIKE_MIX_REPLACE = 0,
+  /* Multiply the copied transformation on the left, with anti-shear scale handling. */
+  TRANSLIKE_MIX_BEFORE,
+  /* Multiply the copied transformation on the right, with anti-shear scale handling. */
+  TRANSLIKE_MIX_AFTER,
+} eCopyTransforms_MixMode;
+
 /* bTransformConstraint.to/from */
 typedef enum eTransform_ToFrom {
   TRANS_LOCATION = 0,
index 79e38717569fd085f4577b90c3b2e1d1f4215011..658addece8a0a4870aef6b3819f0ca98a7338f12 100644 (file)
@@ -1515,6 +1515,28 @@ static void rna_def_constraint_same_volume(BlenderRNA *brna)
 static void rna_def_constraint_transform_like(BlenderRNA *brna)
 {
   StructRNA *srna;
+  PropertyRNA *prop;
+
+  static const EnumPropertyItem mix_mode_items[] = {
+      {TRANSLIKE_MIX_REPLACE,
+       "REPLACE",
+       0,
+       "Replace",
+       "Replace the original transformation with copied"},
+      {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"},
+      {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"},
+      {0, NULL, 0, NULL, NULL},
+  };
 
   srna = RNA_def_struct(brna, "CopyTransformsConstraint", "Constraint");
   RNA_def_struct_ui_text(
@@ -1527,6 +1549,13 @@ static void rna_def_constraint_transform_like(BlenderRNA *brna)
   RNA_def_struct_ui_icon(srna, ICON_CON_TRANSLIKE);
 
   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_ui_text(
+      prop, "Mix Mode", "Specify how the copied and existing transformations are combined");
+  RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
 }
 
 static void rna_def_constraint_minmax(BlenderRNA *brna)