Copy Rotation & Transform: add Euler order override options.
authorAlexander Gavrilov <angavrilov@gmail.com>
Sun, 1 Sep 2019 10:19:11 +0000 (13:19 +0300)
committerAlexander Gavrilov <angavrilov@gmail.com>
Sun, 1 Sep 2019 11:13:23 +0000 (14:13 +0300)
For reasons similar to drivers, it should be possible to set an
explicit Euler rotation order in constraints that use Euler angles.

The Transform constraint in a way approaches drivers in its use,
in that it effectively alters channels using values of other
channels after applying a fixed form mathematical expression.

For this reason, instead of just specifying the euler order for
its inputs, it uses the same enum as driver variables. However
Quaternion components are converted to a weighted pseudo-angle
representation as the rest of the constraint UI expects angles.

release/scripts/startup/bl_ui/properties_constraint.py
source/blender/blenkernel/BKE_fcurve.h
source/blender/blenkernel/intern/constraint.c
source/blender/blenkernel/intern/fcurve.c
source/blender/makesdna/DNA_constraint_types.h
source/blender/makesrna/RNA_enum_types.h
source/blender/makesrna/intern/rna_constraint.c
source/blender/makesrna/intern/rna_fcurve.c

index d8eb0db62fce2a9074acbab23c959be3b5f8b2f4..0ba9fd0630ca616c75ee74fe2f5a9180eb26a946 100644 (file)
@@ -370,6 +370,8 @@ class ConstraintButtonsPanel:
     def COPY_ROTATION(self, _context, layout, con):
         self.target_template(layout, con)
 
+        layout.prop(con, "euler_order", text="Order")
+
         split = layout.split()
 
         col = split.column()
@@ -685,6 +687,9 @@ class ConstraintButtonsPanel:
         col.row().label(text="Source:")
         col.row().prop(con, "map_from", expand=True)
 
+        if con.map_from == 'ROTATION':
+            layout.prop(con, "from_rotation_mode", text="Mode")
+
         split = layout.split()
         ext = "" if con.map_from == 'LOCATION' else "_rot" if con.map_from == 'ROTATION' else "_scale"
 
@@ -727,6 +732,9 @@ class ConstraintButtonsPanel:
         col.label(text="Destination:")
         col.row().prop(con, "map_to", expand=True)
 
+        if con.map_to == 'ROTATION':
+            layout.prop(con, "to_euler_order", text="Order")
+
         split = layout.split()
         ext = "" if con.map_to == 'LOCATION' else "_rot" if con.map_to == 'ROTATION' else "_scale"
 
index 4c1a115eb232064a05dc5c35901acca16f1f6388..5be9a35b1689ecbd01791b84bf9285544de722c5 100644 (file)
@@ -89,6 +89,9 @@ struct ChannelDriver *fcurve_copy_driver(const struct ChannelDriver *driver);
 
 void driver_variables_copy(struct ListBase *dst_list, const struct ListBase *src_list);
 
+void BKE_driver_target_matrix_to_rot_channels(
+    float mat[4][4], int auto_order, int rotation_mode, int channel, bool angles, float r_buf[4]);
+
 void driver_free_variable(struct ListBase *variables, struct DriverVar *dvar);
 void driver_free_variable_ex(struct ChannelDriver *driver, struct DriverVar *dvar);
 
index 1a3c562941e167e99afc50d7d8fd09f8220257c3..ccacb85651c2ba78a2c02cfc083e126ece786324 100644 (file)
@@ -62,6 +62,7 @@
 #include "BKE_deform.h"
 #include "BKE_displist.h"
 #include "BKE_editmesh.h"
+#include "BKE_fcurve.h"
 #include "BKE_global.h"
 #include "BKE_idprop.h"
 #include "BKE_library.h"
@@ -1808,18 +1809,25 @@ static void rotlike_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar
     copy_v3_v3(loc, cob->matrix[3]);
     mat4_to_size(size, cob->matrix);
 
+    /* Select the Euler rotation order, defaulting to the owner. */
+    short rot_order = cob->rotOrder;
+
+    if (data->euler_order != CONSTRAINT_EULER_AUTO) {
+      rot_order = data->euler_order;
+    }
+
     /* To allow compatible rotations, must get both rotations in the order of the owner... */
-    mat4_to_eulO(obeul, cob->rotOrder, cob->matrix);
+    mat4_to_eulO(obeul, rot_order, cob->matrix);
     /* We must get compatible eulers from the beginning because
      * some of them can be modified below (see bug T21875). */
-    mat4_to_compatible_eulO(eul, obeul, cob->rotOrder, ct->matrix);
+    mat4_to_compatible_eulO(eul, obeul, rot_order, ct->matrix);
 
     if ((data->flag & ROTLIKE_X) == 0) {
       eul[0] = obeul[0];
     }
     else {
       if (data->flag & ROTLIKE_OFFSET) {
-        rotate_eulO(eul, cob->rotOrder, 'X', obeul[0]);
+        rotate_eulO(eul, rot_order, 'X', obeul[0]);
       }
 
       if (data->flag & ROTLIKE_X_INVERT) {
@@ -1832,7 +1840,7 @@ static void rotlike_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar
     }
     else {
       if (data->flag & ROTLIKE_OFFSET) {
-        rotate_eulO(eul, cob->rotOrder, 'Y', obeul[1]);
+        rotate_eulO(eul, rot_order, 'Y', obeul[1]);
       }
 
       if (data->flag & ROTLIKE_Y_INVERT) {
@@ -1845,7 +1853,7 @@ static void rotlike_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar
     }
     else {
       if (data->flag & ROTLIKE_OFFSET) {
-        rotate_eulO(eul, cob->rotOrder, 'Z', obeul[2]);
+        rotate_eulO(eul, rot_order, 'Z', obeul[2]);
       }
 
       if (data->flag & ROTLIKE_Z_INVERT) {
@@ -1856,7 +1864,7 @@ static void rotlike_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar
     /* Good to make eulers compatible again,
      * since we don't know how much they were changed above. */
     compatible_eul(eul, obeul);
-    loc_eulO_size_to_mat4(cob->matrix, loc, eul, size, cob->rotOrder);
+    loc_eulO_size_to_mat4(cob->matrix, loc, eul, size, rot_order);
   }
 }
 
@@ -3719,7 +3727,8 @@ static void transform_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *t
   if (VALID_CONS_TARGET(ct)) {
     float *from_min, *from_max, *to_min, *to_max;
     float loc[3], eul[3], size[3];
-    float dvec[3], sval[3];
+    float dbuf[4], sval[3];
+    float *const dvec = dbuf + 1;
     int i;
 
     /* obtain target effect */
@@ -3738,7 +3747,8 @@ static void transform_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *t
         from_max = data->from_max_scale;
         break;
       case TRANS_ROTATION:
-        mat4_to_eulO(dvec, cob->rotOrder, ct->matrix);
+        BKE_driver_target_matrix_to_rot_channels(
+            ct->matrix, cob->rotOrder, data->from_rotation_mode, -1, true, dbuf);
         from_min = data->from_min_rot;
         from_max = data->from_max_rot;
         break;
@@ -3750,9 +3760,16 @@ static void transform_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *t
         break;
     }
 
+    /* Select the output Euler rotation order, defaulting to the owner. */
+    short rot_order = cob->rotOrder;
+
+    if (data->to == TRANS_ROTATION && data->to_euler_order != CONSTRAINT_EULER_AUTO) {
+      rot_order = data->to_euler_order;
+    }
+
     /* extract components of owner's matrix */
     copy_v3_v3(loc, cob->matrix[3]);
-    mat4_to_eulO(eul, cob->rotOrder, cob->matrix);
+    mat4_to_eulO(eul, rot_order, cob->matrix);
     mat4_to_size(size, cob->matrix);
 
     /* determine where in range current transforms lie */
@@ -3811,7 +3828,7 @@ static void transform_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *t
     }
 
     /* apply to matrix */
-    loc_eulO_size_to_mat4(cob->matrix, loc, eul, size, cob->rotOrder);
+    loc_eulO_size_to_mat4(cob->matrix, loc, eul, size, rot_order);
   }
 }
 
index ef53c926daa9acc4a86d71fc0e25e0bb65f4f394..c4da2d2efc92b2c423a0a2ea17fa8cd38ddc9338 100644 (file)
@@ -1732,35 +1732,21 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar)
      *         - only an option for "transform space", if quality is really bad with a)
      */
     float quat[4];
-    float *const eul = quat + 1;
     int channel;
 
     if (dtar->transChan == DTAR_TRANSCHAN_ROTW) {
       channel = 0;
-      quat[0] = 0.0f;
     }
     else {
       channel = 1 + dtar->transChan - DTAR_TRANSCHAN_ROTX;
       BLI_assert(channel < 4);
     }
 
-    if (dtar->rotation_mode == DTAR_ROTMODE_AUTO) {
-      mat4_to_eulO(eul, rot_order, mat);
+    BKE_driver_target_matrix_to_rot_channels(
+        mat, rot_order, dtar->rotation_mode, channel, false, quat);
 
-      if (use_eulers) {
-        compatible_eul(eul, oldEul);
-      }
-    }
-    else if (dtar->rotation_mode >= DTAR_ROTMODE_EULER_MIN &&
-             dtar->rotation_mode <= DTAR_ROTMODE_EULER_MAX) {
-      mat4_to_eulO(eul, dtar->rotation_mode, mat);
-    }
-    else if (dtar->rotation_mode == DTAR_ROTMODE_QUATERNION) {
-      mat4_to_quat(quat, mat);
-    }
-    else {
-      BLI_assert(false);
-      zero_v3(eul);
+    if (use_eulers && dtar->rotation_mode == DTAR_ROTMODE_AUTO) {
+      compatible_eul(quat + 1, oldEul);
     }
 
     return quat[channel];
@@ -1771,6 +1757,52 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar)
   }
 }
 
+/* Convert a quaternion to pseudo-angles representing the weighted amount of rotation. */
+static void quaternion_to_angles(float quat[4], int channel)
+{
+  if (channel < 0) {
+    quat[0] = 2.0f * saacosf(quat[0]);
+
+    for (int i = 1; i < 4; i++) {
+      quat[i] = 2.0f * saasinf(quat[i]);
+    }
+  }
+  else if (channel == 0) {
+    quat[0] = 2.0f * saacosf(quat[0]);
+  }
+  else {
+    quat[channel] = 2.0f * saasinf(quat[channel]);
+  }
+}
+
+/* Compute channel values for a rotational Transform Channel driver variable. */
+void BKE_driver_target_matrix_to_rot_channels(
+    float mat[4][4], int auto_order, int rotation_mode, int channel, bool angles, float r_buf[4])
+{
+  float *const quat = r_buf;
+  float *const eul = r_buf + 1;
+
+  zero_v4(r_buf);
+
+  if (rotation_mode == DTAR_ROTMODE_AUTO) {
+    mat4_to_eulO(eul, auto_order, mat);
+  }
+  else if (rotation_mode >= DTAR_ROTMODE_EULER_MIN && rotation_mode <= DTAR_ROTMODE_EULER_MAX) {
+    mat4_to_eulO(eul, rotation_mode, mat);
+  }
+  else if (rotation_mode == DTAR_ROTMODE_QUATERNION) {
+    mat4_to_quat(quat, mat);
+
+    /* For Transformation constraint convenience, convert to pseudo-angles. */
+    if (angles) {
+      quaternion_to_angles(quat, channel);
+    }
+  }
+  else {
+    BLI_assert(false);
+  }
+}
+
 /* ......... */
 
 /* Table of Driver Variable Type Info Data */
index 17ce279f1b763d603a41cb32f0e3732d9be06754..42d58cb34d01ff4d4a41b194a9537719e7221a65 100644 (file)
@@ -271,7 +271,8 @@ typedef struct bTrackToConstraint {
 typedef struct bRotateLikeConstraint {
   struct Object *tar;
   int flag;
-  int reserved1;
+  char euler_order;
+  char _pad[3];
   /** MAX_ID_NAME-2. */
   char subtarget[64];
 } bRotateLikeConstraint;
@@ -441,6 +442,13 @@ typedef struct bTransformConstraint {
   /** Extrapolate motion? if 0, confine to ranges. */
   char expo;
 
+  /** Input rotation type - uses the same values as driver targets. */
+  char from_rotation_mode;
+  /** Output euler order override. */
+  char to_euler_order;
+
+  char _pad[6];
+
   /** From_min/max defines range of target transform. */
   float from_min[3];
   /** To map on to to_min/max range. */
@@ -715,6 +723,20 @@ typedef enum eConstraintChannel_Flags {
   CONSTRAINT_CHANNEL_PROTECTED = (1 << 1),
 } eConstraintChannel_Flags;
 
+/* Common enum for constraints that support override. */
+typedef enum eConstraint_EulerOrder {
+  /** Automatic euler mode. */
+  CONSTRAINT_EULER_AUTO = 0,
+
+  /** Explicit euler rotation modes - must sync with BLI_math_rotation.h defines. */
+  CONSTRAINT_EULER_XYZ = 1,
+  CONSTRAINT_EULER_XZY,
+  CONSTRAINT_EULER_YXZ,
+  CONSTRAINT_EULER_YZX,
+  CONSTRAINT_EULER_ZXY,
+  CONSTRAINT_EULER_ZYX,
+} eConstraint_EulerOrder;
+
 /* -------------------------------------- */
 
 /* bRotateLikeConstraint.flag */
index 02aaef44a1f4963a0610eca0b561104aa728578d..b3e1f22f413db74eb5b1f0eed7cc81ef07173f51 100644 (file)
@@ -91,6 +91,7 @@ extern const EnumPropertyItem rna_enum_beztriple_keyframe_type_items[];
 extern const EnumPropertyItem rna_enum_beztriple_interpolation_mode_items[];
 extern const EnumPropertyItem rna_enum_beztriple_interpolation_easing_items[];
 extern const EnumPropertyItem rna_enum_keyframe_handle_type_items[];
+extern const EnumPropertyItem rna_enum_driver_target_rotation_mode_items[];
 
 extern const EnumPropertyItem rna_enum_keyblock_type_items[];
 
index 539e2a6629643381208430397285c7369e99562d..05154dcc20e4e6cdbc741545efd2c7482a57ebc5 100644 (file)
@@ -256,6 +256,17 @@ static const EnumPropertyItem track_axis_items[] = {
     {0, NULL, 0, NULL, NULL},
 };
 
+static const EnumPropertyItem euler_order_items[] = {
+    {CONSTRAINT_EULER_AUTO, "AUTO", 0, "Default", "Euler using the default rotation order"},
+    {CONSTRAINT_EULER_XYZ, "XYZ", 0, "XYZ Euler", "Euler using the XYZ rotation order"},
+    {CONSTRAINT_EULER_XZY, "XZY", 0, "XZY Euler", "Euler using the XZY rotation order"},
+    {CONSTRAINT_EULER_YXZ, "YXZ", 0, "YXZ Euler", "Euler using the YXZ rotation order"},
+    {CONSTRAINT_EULER_YZX, "YZX", 0, "YZX Euler", "Euler using the YZX rotation order"},
+    {CONSTRAINT_EULER_ZXY, "ZXY", 0, "ZXY Euler", "Euler using the ZXY rotation order"},
+    {CONSTRAINT_EULER_ZYX, "ZYX", 0, "ZYX Euler", "Euler using the ZYX rotation order"},
+    {0, NULL, 0, NULL, NULL},
+};
+
 #ifdef RNA_RUNTIME
 
 static const EnumPropertyItem space_object_items[] = {
@@ -1324,6 +1335,12 @@ static void rna_def_constraint_rotate_like(BlenderRNA *brna)
   RNA_def_property_ui_text(prop, "Invert Z", "Invert the Z rotation");
   RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
 
+  prop = RNA_def_property(srna, "euler_order", PROP_ENUM, PROP_NONE);
+  RNA_def_property_enum_sdna(prop, NULL, "euler_order");
+  RNA_def_property_enum_items(prop, euler_order_items);
+  RNA_def_property_ui_text(prop, "Euler Order", "Explicitly specify the euler rotation order");
+  RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
+
   prop = RNA_def_property(srna, "use_offset", PROP_BOOLEAN, PROP_NONE);
   RNA_def_property_boolean_sdna(prop, NULL, "flag", ROTLIKE_OFFSET);
   RNA_def_property_ui_text(prop, "Offset", "Add original rotation into copied rotation");
@@ -1885,6 +1902,18 @@ static void rna_def_constraint_transform(BlenderRNA *brna)
   RNA_def_property_ui_text(prop, "Extrapolate Motion", "Extrapolate ranges");
   RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
 
+  prop = RNA_def_property(srna, "from_rotation_mode", PROP_ENUM, PROP_NONE);
+  RNA_def_property_enum_sdna(prop, NULL, "from_rotation_mode");
+  RNA_def_property_enum_items(prop, rna_enum_driver_target_rotation_mode_items);
+  RNA_def_property_ui_text(prop, "From Mode", "Specify the type of rotation channels to use");
+  RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
+
+  prop = RNA_def_property(srna, "to_euler_order", PROP_ENUM, PROP_NONE);
+  RNA_def_property_enum_sdna(prop, NULL, "to_euler_order");
+  RNA_def_property_enum_items(prop, euler_order_items);
+  RNA_def_property_ui_text(prop, "To Order", "Explicitly specify the output euler rotation order");
+  RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
+
   /* Loc */
   prop = RNA_def_property(srna, "from_min_x", PROP_FLOAT, PROP_DISTANCE);
   RNA_def_property_float_sdna(prop, NULL, "from_min[0]");
index 684c786b1b9021c2cf55488e965738c0930e7ac2..b6b759882b38e8d490695d7d8094d9f8994e5437 100644 (file)
@@ -132,6 +132,18 @@ const EnumPropertyItem rna_enum_beztriple_interpolation_easing_items[] = {
     {0, NULL, 0, NULL, NULL},
 };
 
+const EnumPropertyItem rna_enum_driver_target_rotation_mode_items[] = {
+    {DTAR_ROTMODE_AUTO, "AUTO", 0, "Auto Euler", "Euler using the rotation order of the target"},
+    {DTAR_ROTMODE_EULER_XYZ, "XYZ", 0, "XYZ Euler", "Euler using the XYZ rotation order"},
+    {DTAR_ROTMODE_EULER_XZY, "XZY", 0, "XZY Euler", "Euler using the XZY rotation order"},
+    {DTAR_ROTMODE_EULER_YXZ, "YXZ", 0, "YXZ Euler", "Euler using the YXZ rotation order"},
+    {DTAR_ROTMODE_EULER_YZX, "YZX", 0, "YZX Euler", "Euler using the YZX rotation order"},
+    {DTAR_ROTMODE_EULER_ZXY, "ZXY", 0, "ZXY Euler", "Euler using the ZXY rotation order"},
+    {DTAR_ROTMODE_EULER_ZYX, "ZYX", 0, "ZYX Euler", "Euler using the ZYX rotation order"},
+    {DTAR_ROTMODE_QUATERNION, "QUATERNION", 0, "Quaternion", "Quaternion rotation"},
+    {0, NULL, 0, NULL, NULL},
+};
+
 #ifdef RNA_RUNTIME
 
 #  include "WM_api.h"
@@ -1702,18 +1714,6 @@ static void rna_def_drivertarget(BlenderRNA *brna)
       {0, NULL, 0, NULL, NULL},
   };
 
-  static const EnumPropertyItem prop_rotation_mode_items[] = {
-      {DTAR_ROTMODE_AUTO, "AUTO", 0, "Auto Euler", "Euler using the rotation order of the target"},
-      {DTAR_ROTMODE_EULER_XYZ, "XYZ", 0, "XYZ Euler", "Euler using the XYZ rotation order"},
-      {DTAR_ROTMODE_EULER_XZY, "XZY", 0, "XZY Euler", "Euler using the XZY rotation order"},
-      {DTAR_ROTMODE_EULER_YXZ, "YXZ", 0, "YXZ Euler", "Euler using the YXZ rotation order"},
-      {DTAR_ROTMODE_EULER_YZX, "YZX", 0, "YZX Euler", "Euler using the YZX rotation order"},
-      {DTAR_ROTMODE_EULER_ZXY, "ZXY", 0, "ZXY Euler", "Euler using the ZXY rotation order"},
-      {DTAR_ROTMODE_EULER_ZYX, "ZYX", 0, "ZYX Euler", "Euler using the ZYX rotation order"},
-      {DTAR_ROTMODE_QUATERNION, "QUATERNION", 0, "Quaternion", "Quaternion rotation"},
-      {0, NULL, 0, NULL, NULL},
-  };
-
   srna = RNA_def_struct(brna, "DriverTarget", NULL);
   RNA_def_struct_ui_text(srna, "Driver Target", "Source of input values for driver variables");
 
@@ -1764,7 +1764,7 @@ static void rna_def_drivertarget(BlenderRNA *brna)
 
   prop = RNA_def_property(srna, "rotation_mode", PROP_ENUM, PROP_NONE);
   RNA_def_property_enum_sdna(prop, NULL, "rotation_mode");
-  RNA_def_property_enum_items(prop, prop_rotation_mode_items);
+  RNA_def_property_enum_items(prop, rna_enum_driver_target_rotation_mode_items);
   RNA_def_property_ui_text(prop, "Rotation Mode", "Mode for calculating rotation channel values");
   RNA_def_property_update(prop, 0, "rna_DriverTarget_update_data");