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:
authorHenrik Dick <weasel>2020-12-03 12:42:29 +0300
committerSybren A. Stüvel <sybren@blender.org>2020-12-03 13:20:21 +0300
commita6c4e39876d8a0765290312f6d8c0175274114cd (patch)
tree98568a07fb46bb643cee07ea0ac08991f4f4db2e
parent899dcc5f60667a54d9628fcb5656f7e9db642068 (diff)
Add Custom Object Space to Constraints
Add Custom Space to the list of space conversions for constraints. Constraints can use World Space, Local Space, Pose Space, Local with Parent, and now also Custom Space with a custom object to define the evaluation space. The Custom Space option uses the Local Space of an other object/bone/vertex group. If selected on owner or target it will show a box for object selection. If an armature is selected, then it will also show a box for bone selection. If a mesh object is selected it will show the option for using the local space of a vertex group. Reviewed By: #animation_rigging, sybren, Severin, angavrilov Differential Revision: https://developer.blender.org/D7437
-rw-r--r--release/scripts/startup/bl_ui/properties_constraint.py23
-rw-r--r--source/blender/blenkernel/BKE_constraint.h4
-rw-r--r--source/blender/blenkernel/intern/constraint.c304
-rw-r--r--source/blender/blenkernel/intern/fcurve_driver.c8
-rw-r--r--source/blender/editors/armature/armature_add.c17
-rw-r--r--source/blender/makesdna/DNA_constraint_types.h13
-rw-r--r--source/blender/makesrna/intern/rna_constraint.c29
-rw-r--r--source/blender/makesrna/intern/rna_object_api.c21
-rw-r--r--tests/python/bl_constraints.py74
9 files changed, 398 insertions, 95 deletions
diff --git a/release/scripts/startup/bl_ui/properties_constraint.py b/release/scripts/startup/bl_ui/properties_constraint.py
index f46e9f9727f..71a7b056d07 100644
--- a/release/scripts/startup/bl_ui/properties_constraint.py
+++ b/release/scripts/startup/bl_ui/properties_constraint.py
@@ -85,14 +85,23 @@ class ConstraintButtonsPanel(Panel):
row.operator("constraint.disable_keep_transform", text="", icon='CANCEL')
@staticmethod
- def space_template(layout, con, target=True, owner=True):
+ def space_template(layout, con, target=True, owner=True, separator=True):
if target or owner:
- layout.separator()
+ if separator:
+ layout.separator()
if target:
layout.prop(con, "target_space", text="Target")
if owner:
layout.prop(con, "owner_space", text="Owner")
+ if con.target_space == 'CUSTOM' or con.owner_space == 'CUSTOM':
+ col = layout.column()
+ col.prop(con, "space_object")
+ if con.space_object and con.space_object.type == 'ARMATURE':
+ col.prop_search(con, "space_subtarget", con.space_object.data, "bones", text="Bone")
+ elif con.space_object and con.space_object.type in {'MESH', 'LATTICE'}:
+ col.prop_search(con, "space_subtarget", con.space_object, "vertex_groups", text="Vertex Group")
+
@staticmethod
def target_template(layout, con, subtargets=True):
col = layout.column()
@@ -237,7 +246,7 @@ class ConstraintButtonsPanel(Panel):
row.label(icon="BLANK1")
layout.prop(con, "use_transform_limit")
- layout.prop(con, "owner_space")
+ self.space_template(layout, con, target=False, owner=True)
self.draw_influence(layout, con)
@@ -306,7 +315,7 @@ class ConstraintButtonsPanel(Panel):
row.prop_decorator(con, "max_z")
layout.prop(con, "use_transform_limit")
- layout.prop(con, "owner_space")
+ self.space_template(layout, con, target=False, owner=True)
self.draw_influence(layout, con)
@@ -375,7 +384,7 @@ class ConstraintButtonsPanel(Panel):
row.prop_decorator(con, "max_z")
layout.prop(con, "use_transform_limit")
- layout.prop(con, "owner_space")
+ self.space_template(layout, con, target=False, owner=True)
self.draw_influence(layout, con)
@@ -483,7 +492,7 @@ class ConstraintButtonsPanel(Panel):
layout.prop(con, "volume")
- layout.prop(con, "owner_space")
+ self.space_template(layout, con, target=False, owner=True)
self.draw_influence(layout, con)
@@ -1117,7 +1126,7 @@ class ConstraintButtonsSubPanel(Panel):
col = layout.column()
col.active = not con.use_eval_time
col.prop(con, "transform_channel", text="Channel")
- col.prop(con, "target_space")
+ ConstraintButtonsPanel.space_template(col, con, target=True, owner=False, separator=False)
sub = col.column(align=True)
sub.prop(con, "min", text="Range Min")
diff --git a/source/blender/blenkernel/BKE_constraint.h b/source/blender/blenkernel/BKE_constraint.h
index e5c4535560d..4b9f480e091 100644
--- a/source/blender/blenkernel/BKE_constraint.h
+++ b/source/blender/blenkernel/BKE_constraint.h
@@ -56,6 +56,8 @@ typedef struct bConstraintOb {
float matrix[4][4];
/** original matrix (before constraint solving) */
float startmat[4][4];
+ /** space matrix for custom object space */
+ float space_obj_world_matrix[4][4];
/** type of owner */
short type;
@@ -203,6 +205,7 @@ void BKE_constraints_clear_evalob(struct bConstraintOb *cob);
void BKE_constraint_mat_convertspace(struct Object *ob,
struct bPoseChannel *pchan,
+ struct bConstraintOb *cob,
float mat[4][4],
short from,
short to,
@@ -221,6 +224,7 @@ void BKE_constraint_targets_for_solving_get(struct Depsgraph *depsgraph,
struct bConstraintOb *ob,
struct ListBase *targets,
float ctime);
+void BKE_constraint_custom_object_space_get(float r_mat[4][4], struct bConstraint *con);
void BKE_constraints_solve(struct Depsgraph *depsgraph,
struct ListBase *conlist,
struct bConstraintOb *cob,
diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c
index 5497065bb34..1a16f1d3c6e 100644
--- a/source/blender/blenkernel/intern/constraint.c
+++ b/source/blender/blenkernel/intern/constraint.c
@@ -261,8 +261,13 @@ void BKE_constraints_clear_evalob(bConstraintOb *cob)
* of a matrix from one space to another for constraint evaluation.
* For now, this is only implemented for Objects and PoseChannels.
*/
-void BKE_constraint_mat_convertspace(
- Object *ob, bPoseChannel *pchan, float mat[4][4], short from, short to, const bool keep_scale)
+void BKE_constraint_mat_convertspace(Object *ob,
+ bPoseChannel *pchan,
+ bConstraintOb *cob,
+ float mat[4][4],
+ short from,
+ short to,
+ const bool keep_scale)
{
float diff_mat[4][4];
float imat[4][4];
@@ -282,25 +287,30 @@ void BKE_constraint_mat_convertspace(
switch (from) {
case CONSTRAINT_SPACE_WORLD: /* ---------- FROM WORLDSPACE ---------- */
{
- /* world to pose */
- invert_m4_m4(imat, ob->obmat);
- mul_m4_m4m4(mat, imat, mat);
-
- /* use pose-space as stepping stone for other spaces... */
- if (ELEM(to, CONSTRAINT_SPACE_LOCAL, CONSTRAINT_SPACE_PARLOCAL)) {
- /* call self with slightly different values */
- BKE_constraint_mat_convertspace(ob, pchan, mat, CONSTRAINT_SPACE_POSE, to, keep_scale);
+ if (to == CONSTRAINT_SPACE_CUSTOM) {
+ /* World to custom. */
+ BLI_assert(cob);
+ invert_m4_m4(imat, cob->space_obj_world_matrix);
+ mul_m4_m4m4(mat, imat, mat);
+ }
+ else {
+ /* World to pose. */
+ invert_m4_m4(imat, ob->obmat);
+ mul_m4_m4m4(mat, imat, mat);
+
+ /* Use pose-space as stepping stone for other spaces. */
+ if (ELEM(to, CONSTRAINT_SPACE_LOCAL, CONSTRAINT_SPACE_PARLOCAL)) {
+ /* Call self with slightly different values. */
+ BKE_constraint_mat_convertspace(
+ ob, pchan, cob, mat, CONSTRAINT_SPACE_POSE, to, keep_scale);
+ }
}
break;
}
case CONSTRAINT_SPACE_POSE: /* ---------- FROM POSESPACE ---------- */
{
- /* pose to world */
- if (to == CONSTRAINT_SPACE_WORLD) {
- mul_m4_m4m4(mat, ob->obmat, mat);
- }
/* pose to local */
- else if (to == CONSTRAINT_SPACE_LOCAL) {
+ if (to == CONSTRAINT_SPACE_LOCAL) {
if (pchan->bone) {
BKE_armature_mat_pose_to_bone(pchan, mat, mat);
}
@@ -312,6 +322,16 @@ void BKE_constraint_mat_convertspace(
mul_m4_m4m4(mat, imat, mat);
}
}
+ else {
+ /* Pose to world. */
+ mul_m4_m4m4(mat, ob->obmat, mat);
+ /* Use world-space as stepping stone for other spaces. */
+ if (to != CONSTRAINT_SPACE_WORLD) {
+ /* Call self with slightly different values. */
+ BKE_constraint_mat_convertspace(
+ ob, pchan, cob, mat, CONSTRAINT_SPACE_WORLD, to, keep_scale);
+ }
+ }
break;
}
case CONSTRAINT_SPACE_LOCAL: /* ------------ FROM LOCALSPACE --------- */
@@ -323,9 +343,10 @@ void BKE_constraint_mat_convertspace(
}
/* use pose-space as stepping stone for other spaces */
- if (ELEM(to, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_PARLOCAL)) {
+ if (ELEM(to, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_PARLOCAL, CONSTRAINT_SPACE_CUSTOM)) {
/* call self with slightly different values */
- BKE_constraint_mat_convertspace(ob, pchan, mat, CONSTRAINT_SPACE_POSE, to, keep_scale);
+ BKE_constraint_mat_convertspace(
+ ob, pchan, cob, mat, CONSTRAINT_SPACE_POSE, to, keep_scale);
}
break;
}
@@ -337,9 +358,24 @@ void BKE_constraint_mat_convertspace(
}
/* use pose-space as stepping stone for other spaces */
- if (ELEM(to, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL)) {
+ if (ELEM(to, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, CONSTRAINT_SPACE_CUSTOM)) {
/* call self with slightly different values */
- BKE_constraint_mat_convertspace(ob, pchan, mat, CONSTRAINT_SPACE_POSE, to, keep_scale);
+ BKE_constraint_mat_convertspace(
+ ob, pchan, cob, mat, CONSTRAINT_SPACE_POSE, to, keep_scale);
+ }
+ break;
+ }
+ case CONSTRAINT_SPACE_CUSTOM: /* -------------- FROM CUSTOM SPACE ---------- */
+ {
+ /* Custom to world. */
+ BLI_assert(cob);
+ mul_m4_m4m4(mat, cob->space_obj_world_matrix, mat);
+
+ /* Use world-space as stepping stone for other spaces. */
+ if (to != CONSTRAINT_SPACE_WORLD) {
+ /* Call self with slightly different values. */
+ BKE_constraint_mat_convertspace(
+ ob, pchan, cob, mat, CONSTRAINT_SPACE_WORLD, to, keep_scale);
}
break;
}
@@ -347,37 +383,44 @@ void BKE_constraint_mat_convertspace(
}
else {
/* objects */
- if (from == CONSTRAINT_SPACE_WORLD && to == CONSTRAINT_SPACE_LOCAL) {
- /* check if object has a parent */
- if (ob->parent) {
- /* 'subtract' parent's effects from owner */
- mul_m4_m4m4(diff_mat, ob->parent->obmat, ob->parentinv);
- invert_m4_m4_safe(imat, diff_mat);
- mul_m4_m4m4(mat, imat, mat);
- }
- else {
- /* Local space in this case will have to be defined as local to the owner's
- * transform-property-rotated axes. So subtract this rotation component.
- */
- /* XXX This is actually an ugly hack, local space of a parent-less object *is* the same as
- * global space!
- * Think what we want actually here is some kind of 'Final Space', i.e
- * . once transformations are applied - users are often confused about this too,
- * this is not consistent with bones
- * local space either... Meh :|
- * --mont29
- */
- BKE_object_to_mat4(ob, diff_mat);
- if (!keep_scale) {
- normalize_m4(diff_mat);
+ if (from == CONSTRAINT_SPACE_WORLD) {
+ if (to == CONSTRAINT_SPACE_LOCAL) {
+ /* Check if object has a parent. */
+ if (ob->parent) {
+ /* 'subtract' parent's effects from owner. */
+ mul_m4_m4m4(diff_mat, ob->parent->obmat, ob->parentinv);
+ invert_m4_m4_safe(imat, diff_mat);
+ mul_m4_m4m4(mat, imat, mat);
}
- zero_v3(diff_mat[3]);
+ else {
+ /* Local space in this case will have to be defined as local to the owner's
+ * transform-property-rotated axes. So subtract this rotation component.
+ */
+ /* XXX This is actually an ugly hack, local space of a parent-less object *is* the same
+ * as global space! Think what we want actually here is some kind of 'Final Space', i.e
+ * . once transformations are applied - users are often confused about this too,
+ * this is not consistent with bones
+ * local space either... Meh :|
+ * --mont29
+ */
+ BKE_object_to_mat4(ob, diff_mat);
+ if (!keep_scale) {
+ normalize_m4(diff_mat);
+ }
+ zero_v3(diff_mat[3]);
- invert_m4_m4_safe(imat, diff_mat);
+ invert_m4_m4_safe(imat, diff_mat);
+ mul_m4_m4m4(mat, imat, mat);
+ }
+ }
+ else if (to == CONSTRAINT_SPACE_CUSTOM) {
+ /* 'subtract' custom objects's effects from owner. */
+ BLI_assert(cob);
+ invert_m4_m4_safe(imat, cob->space_obj_world_matrix);
mul_m4_m4m4(mat, imat, mat);
}
}
- else if (from == CONSTRAINT_SPACE_LOCAL && to == CONSTRAINT_SPACE_WORLD) {
+ else if (from == CONSTRAINT_SPACE_LOCAL) {
/* check that object has a parent - otherwise this won't work */
if (ob->parent) {
/* 'add' parent's effect back to owner */
@@ -397,6 +440,24 @@ void BKE_constraint_mat_convertspace(
mul_m4_m4m4(mat, diff_mat, mat);
}
+ if (to == CONSTRAINT_SPACE_CUSTOM) {
+ /* 'subtract' objects's effects from owner. */
+ BLI_assert(cob);
+ invert_m4_m4_safe(imat, cob->space_obj_world_matrix);
+ mul_m4_m4m4(mat, imat, mat);
+ }
+ }
+ else if (from == CONSTRAINT_SPACE_CUSTOM) {
+ /* Custom to world. */
+ BLI_assert(cob);
+ mul_m4_m4m4(mat, cob->space_obj_world_matrix, mat);
+
+ /* Use world-space as stepping stone for other spaces. */
+ if (to != CONSTRAINT_SPACE_WORLD) {
+ /* Call self with slightly different values. */
+ BKE_constraint_mat_convertspace(
+ ob, pchan, cob, mat, CONSTRAINT_SPACE_WORLD, to, keep_scale);
+ }
}
}
}
@@ -573,6 +634,7 @@ static void contarget_get_lattice_mat(Object *ob, const char *substring, float m
/* The cases where the target can be object data have not been implemented */
static void constraint_target_to_mat4(Object *ob,
const char *substring,
+ bConstraintOb *cob,
float mat[4][4],
short from,
short to,
@@ -582,7 +644,7 @@ static void constraint_target_to_mat4(Object *ob,
/* Case OBJECT */
if (substring[0] == '\0') {
copy_m4_m4(mat, ob->obmat);
- BKE_constraint_mat_convertspace(ob, NULL, mat, from, to, false);
+ BKE_constraint_mat_convertspace(ob, NULL, cob, mat, from, to, false);
}
/* Case VERTEXGROUP */
/* Current method just takes the average location of all the points in the
@@ -595,11 +657,11 @@ static void constraint_target_to_mat4(Object *ob,
*/
else if (ob->type == OB_MESH) {
contarget_get_mesh_mat(ob, substring, mat);
- BKE_constraint_mat_convertspace(ob, NULL, mat, from, to, false);
+ BKE_constraint_mat_convertspace(ob, NULL, cob, mat, from, to, false);
}
else if (ob->type == OB_LATTICE) {
contarget_get_lattice_mat(ob, substring, mat);
- BKE_constraint_mat_convertspace(ob, NULL, mat, from, to, false);
+ BKE_constraint_mat_convertspace(ob, NULL, cob, mat, from, to, false);
}
/* Case BONE */
else {
@@ -662,7 +724,7 @@ static void constraint_target_to_mat4(Object *ob,
}
/* convert matrix space as required */
- BKE_constraint_mat_convertspace(ob, pchan, mat, from, to, false);
+ BKE_constraint_mat_convertspace(ob, pchan, cob, mat, from, to, false);
}
}
@@ -705,13 +767,14 @@ static bConstraintTypeInfo CTI_CONSTRNAME = {
*/
static void default_get_tarmat(struct Depsgraph *UNUSED(depsgraph),
bConstraint *con,
- bConstraintOb *UNUSED(cob),
+ bConstraintOb *cob,
bConstraintTarget *ct,
float UNUSED(ctime))
{
if (VALID_CONS_TARGET(ct)) {
constraint_target_to_mat4(ct->tar,
ct->subtarget,
+ cob,
ct->matrix,
CONSTRAINT_SPACE_WORLD,
ct->space,
@@ -727,13 +790,14 @@ static void default_get_tarmat(struct Depsgraph *UNUSED(depsgraph),
*/
static void default_get_tarmat_full_bbone(struct Depsgraph *UNUSED(depsgraph),
bConstraint *con,
- bConstraintOb *UNUSED(cob),
+ bConstraintOb *cob,
bConstraintTarget *ct,
float UNUSED(ctime))
{
if (VALID_CONS_TARGET(ct)) {
constraint_target_to_mat4(ct->tar,
ct->subtarget,
+ cob,
ct->matrix,
CONSTRAINT_SPACE_WORLD,
ct->space,
@@ -844,6 +908,32 @@ static void default_get_tarmat_full_bbone(struct Depsgraph *UNUSED(depsgraph),
} \
(void)0
+static void custom_space_id_looper(bConstraint *con, ConstraintIDFunc func, void *userdata)
+{
+ func(con, (ID **)&con->space_object, false, userdata);
+}
+
+static int get_space_tar(bConstraint *con, ListBase *list)
+{
+ if (!con || !list ||
+ (con->ownspace != CONSTRAINT_SPACE_CUSTOM && con->tarspace != CONSTRAINT_SPACE_CUSTOM)) {
+ return 0;
+ }
+ bConstraintTarget *ct;
+ SINGLETARGET_GET_TARS(con, con->space_object, con->space_subtarget, ct, list);
+ return 1;
+}
+
+static void flush_space_tar(bConstraint *con, ListBase *list, bool no_copy)
+{
+ if (!con || !list ||
+ (con->ownspace != CONSTRAINT_SPACE_CUSTOM && con->tarspace != CONSTRAINT_SPACE_CUSTOM)) {
+ return;
+ }
+ bConstraintTarget *ct = (bConstraintTarget *)list->last;
+ SINGLETARGET_FLUSH_TARS(con, con->space_object, con->space_subtarget, ct, list, no_copy);
+}
+
/* --------- ChildOf Constraint ------------ */
static void childof_new_data(void *cdata)
@@ -1030,6 +1120,8 @@ static void trackto_id_looper(bConstraint *con, ConstraintIDFunc func, void *use
/* target only */
func(con, (ID **)&data->tar, false, userdata);
+
+ custom_space_id_looper(con, func, userdata);
}
static int trackto_get_tars(bConstraint *con, ListBase *list)
@@ -1041,7 +1133,7 @@ static int trackto_get_tars(bConstraint *con, ListBase *list)
/* standard target-getting macro for single-target constraints */
SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list);
- return 1;
+ return 1 + get_space_tar(con, list);
}
return 0;
@@ -1055,6 +1147,7 @@ static void trackto_flush_tars(bConstraint *con, ListBase *list, bool no_copy)
/* the following macro is used for all standard single-target constraints */
SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, no_copy);
+ flush_space_tar(con, list, no_copy);
}
}
@@ -1262,6 +1355,7 @@ static void kinematic_get_tarmat(struct Depsgraph *UNUSED(depsgraph),
if (VALID_CONS_TARGET(ct)) {
constraint_target_to_mat4(ct->tar,
ct->subtarget,
+ cob,
ct->matrix,
CONSTRAINT_SPACE_WORLD,
ct->space,
@@ -1533,11 +1627,11 @@ static bConstraintTypeInfo CTI_LOCLIMIT = {
"Limit Location", /* name */
"bLocLimitConstraint", /* struct name */
NULL, /* free data */
- NULL, /* id looper */
+ custom_space_id_looper, /* id looper */
NULL, /* copy data */
NULL, /* new data */
- NULL, /* get constraint targets */
- NULL, /* flush constraint targets */
+ get_space_tar, /* get constraint targets */
+ flush_space_tar, /* flush constraint targets */
NULL, /* get target matrix */
loclimit_evaluate, /* evaluate */
};
@@ -1596,11 +1690,11 @@ static bConstraintTypeInfo CTI_ROTLIMIT = {
"Limit Rotation", /* name */
"bRotLimitConstraint", /* struct name */
NULL, /* free data */
- NULL, /* id looper */
+ custom_space_id_looper, /* id looper */
NULL, /* copy data */
NULL, /* new data */
- NULL, /* get constraint targets */
- NULL, /* flush constraint targets */
+ get_space_tar, /* get constraint targets */
+ flush_space_tar, /* flush constraint targets */
NULL, /* get target matrix */
rotlimit_evaluate, /* evaluate */
};
@@ -1663,11 +1757,11 @@ static bConstraintTypeInfo CTI_SIZELIMIT = {
"Limit Scale", /* name */
"bSizeLimitConstraint", /* struct name */
NULL, /* free data */
- NULL, /* id looper */
+ custom_space_id_looper, /* id looper */
NULL, /* copy data */
NULL, /* new data */
- NULL, /* get constraint targets */
- NULL, /* flush constraint targets */
+ get_space_tar, /* get constraint targets */
+ flush_space_tar, /* flush constraint targets */
NULL, /* get target matrix */
sizelimit_evaluate, /* evaluate */
};
@@ -1687,6 +1781,8 @@ static void loclike_id_looper(bConstraint *con, ConstraintIDFunc func, void *use
/* target only */
func(con, (ID **)&data->tar, false, userdata);
+
+ custom_space_id_looper(con, func, userdata);
}
static int loclike_get_tars(bConstraint *con, ListBase *list)
@@ -1698,7 +1794,7 @@ static int loclike_get_tars(bConstraint *con, ListBase *list)
/* standard target-getting macro for single-target constraints */
SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list);
- return 1;
+ return 1 + get_space_tar(con, list);
}
return 0;
@@ -1712,6 +1808,7 @@ static void loclike_flush_tars(bConstraint *con, ListBase *list, bool no_copy)
/* the following macro is used for all standard single-target constraints */
SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, no_copy);
+ flush_space_tar(con, list, no_copy);
}
}
@@ -1784,6 +1881,8 @@ static void rotlike_id_looper(bConstraint *con, ConstraintIDFunc func, void *use
/* target only */
func(con, (ID **)&data->tar, false, userdata);
+
+ custom_space_id_looper(con, func, userdata);
}
static int rotlike_get_tars(bConstraint *con, ListBase *list)
@@ -1795,7 +1894,7 @@ static int rotlike_get_tars(bConstraint *con, ListBase *list)
/* standard target-getting macro for single-target constraints */
SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list);
- return 1;
+ return 1 + get_space_tar(con, list);
}
return 0;
@@ -1809,6 +1908,7 @@ static void rotlike_flush_tars(bConstraint *con, ListBase *list, bool no_copy)
/* the following macro is used for all standard single-target constraints */
SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, no_copy);
+ flush_space_tar(con, list, no_copy);
}
}
@@ -1962,6 +2062,8 @@ static void sizelike_id_looper(bConstraint *con, ConstraintIDFunc func, void *us
/* target only */
func(con, (ID **)&data->tar, false, userdata);
+
+ custom_space_id_looper(con, func, userdata);
}
static int sizelike_get_tars(bConstraint *con, ListBase *list)
@@ -1973,7 +2075,7 @@ static int sizelike_get_tars(bConstraint *con, ListBase *list)
/* standard target-getting macro for single-target constraints */
SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list);
- return 1;
+ return 1 + get_space_tar(con, list);
}
return 0;
@@ -1987,6 +2089,7 @@ static void sizelike_flush_tars(bConstraint *con, ListBase *list, bool no_copy)
/* the following macro is used for all standard single-target constraints */
SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, no_copy);
+ flush_space_tar(con, list, no_copy);
}
}
@@ -2084,6 +2187,8 @@ static void translike_id_looper(bConstraint *con, ConstraintIDFunc func, void *u
/* target only */
func(con, (ID **)&data->tar, false, userdata);
+
+ custom_space_id_looper(con, func, userdata);
}
static int translike_get_tars(bConstraint *con, ListBase *list)
@@ -2095,7 +2200,7 @@ static int translike_get_tars(bConstraint *con, ListBase *list)
/* standard target-getting macro for single-target constraints */
SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list);
- return 1;
+ return 1 + get_space_tar(con, list);
}
return 0;
@@ -2109,6 +2214,7 @@ static void translike_flush_tars(bConstraint *con, ListBase *list, bool no_copy)
/* the following macro is used for all standard single-target constraints */
SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, no_copy);
+ flush_space_tar(con, list, no_copy);
}
}
@@ -2212,11 +2318,11 @@ static bConstraintTypeInfo CTI_SAMEVOL = {
"Maintain Volume", /* name */
"bSameVolumeConstraint", /* struct name */
NULL, /* free data */
- NULL, /* id looper */
+ custom_space_id_looper, /* id looper */
NULL, /* copy data */
samevolume_new_data, /* new data */
- NULL, /* get constraint targets */
- NULL, /* flush constraint targets */
+ get_space_tar, /* get constraint targets */
+ flush_space_tar, /* flush constraint targets */
NULL, /* get target matrix */
samevolume_evaluate, /* evaluate */
};
@@ -2282,7 +2388,7 @@ static void pycon_id_looper(bConstraint *con, ConstraintIDFunc func, void *userd
/* Whether this approach is maintained remains to be seen (aligorith) */
static void pycon_get_tarmat(struct Depsgraph *UNUSED(depsgraph),
bConstraint *con,
- bConstraintOb *UNUSED(cob),
+ bConstraintOb *cob,
bConstraintTarget *ct,
float UNUSED(ctime))
{
@@ -2301,6 +2407,7 @@ static void pycon_get_tarmat(struct Depsgraph *UNUSED(depsgraph),
*/
constraint_target_to_mat4(ct->tar,
ct->subtarget,
+ cob,
ct->matrix,
CONSTRAINT_SPACE_WORLD,
ct->space,
@@ -2608,6 +2715,8 @@ static void actcon_id_looper(bConstraint *con, ConstraintIDFunc func, void *user
/* action */
func(con, (ID **)&data->act, true, userdata);
+
+ custom_space_id_looper(con, func, userdata);
}
static int actcon_get_tars(bConstraint *con, ListBase *list)
@@ -2619,7 +2728,7 @@ static int actcon_get_tars(bConstraint *con, ListBase *list)
/* standard target-getting macro for single-target constraints */
SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list);
- return 1;
+ return 1 + get_space_tar(con, list);
}
return 0;
@@ -2633,6 +2742,7 @@ static void actcon_flush_tars(bConstraint *con, ListBase *list, bool no_copy)
/* the following macro is used for all standard single-target constraints */
SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, no_copy);
+ flush_space_tar(con, list, no_copy);
}
}
@@ -2660,6 +2770,7 @@ static void actcon_get_tarmat(struct Depsgraph *depsgraph,
/* get the transform matrix of the target */
constraint_target_to_mat4(ct->tar,
ct->subtarget,
+ cob,
tempmat,
CONSTRAINT_SPACE_WORLD,
ct->space,
@@ -3117,6 +3228,8 @@ static void distlimit_id_looper(bConstraint *con, ConstraintIDFunc func, void *u
/* target only */
func(con, (ID **)&data->tar, false, userdata);
+
+ custom_space_id_looper(con, func, userdata);
}
static int distlimit_get_tars(bConstraint *con, ListBase *list)
@@ -3128,7 +3241,7 @@ static int distlimit_get_tars(bConstraint *con, ListBase *list)
/* standard target-getting macro for single-target constraints */
SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list);
- return 1;
+ return 1 + get_space_tar(con, list);
}
return 0;
@@ -3142,6 +3255,7 @@ static void distlimit_flush_tars(bConstraint *con, ListBase *list, bool no_copy)
/* the following macro is used for all standard single-target constraints */
SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, no_copy);
+ flush_space_tar(con, list, no_copy);
}
}
@@ -3470,6 +3584,8 @@ static void minmax_id_looper(bConstraint *con, ConstraintIDFunc func, void *user
/* target only */
func(con, (ID **)&data->tar, false, userdata);
+
+ custom_space_id_looper(con, func, userdata);
}
static int minmax_get_tars(bConstraint *con, ListBase *list)
@@ -3481,7 +3597,7 @@ static int minmax_get_tars(bConstraint *con, ListBase *list)
/* standard target-getting macro for single-target constraints */
SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list);
- return 1;
+ return 1 + get_space_tar(con, list);
}
return 0;
@@ -3495,6 +3611,7 @@ static void minmax_flush_tars(bConstraint *con, ListBase *list, bool no_copy)
/* the following macro is used for all standard single-target constraints */
SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, no_copy);
+ flush_space_tar(con, list, no_copy);
}
}
@@ -3789,6 +3906,8 @@ static void transform_id_looper(bConstraint *con, ConstraintIDFunc func, void *u
/* target only */
func(con, (ID **)&data->tar, false, userdata);
+
+ custom_space_id_looper(con, func, userdata);
}
static int transform_get_tars(bConstraint *con, ListBase *list)
@@ -3800,7 +3919,7 @@ static int transform_get_tars(bConstraint *con, ListBase *list)
/* standard target-getting macro for single-target constraints */
SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list);
- return 1;
+ return 1 + get_space_tar(con, list);
}
return 0;
@@ -3814,6 +3933,7 @@ static void transform_flush_tars(bConstraint *con, ListBase *list, bool no_copy)
/* the following macro is used for all standard single-target constraints */
SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, no_copy);
+ flush_space_tar(con, list, no_copy);
}
}
@@ -4122,7 +4242,7 @@ static void shrinkwrap_get_tarmat(struct Depsgraph *UNUSED(depsgraph),
* See T42447. */
unit_m4(mat);
BKE_constraint_mat_convertspace(
- cob->ob, cob->pchan, mat, CONSTRAINT_SPACE_LOCAL, scon->projAxisSpace, true);
+ cob->ob, cob->pchan, cob, mat, CONSTRAINT_SPACE_LOCAL, scon->projAxisSpace, true);
invert_m4(mat);
mul_mat3_m4_v3(mat, no);
@@ -6000,6 +6120,35 @@ void BKE_constraint_targets_for_solving_get(struct Depsgraph *depsgraph,
}
}
+void BKE_constraint_custom_object_space_get(float r_mat[4][4], bConstraint *con)
+{
+ if (!con ||
+ (con->ownspace != CONSTRAINT_SPACE_CUSTOM && con->tarspace != CONSTRAINT_SPACE_CUSTOM)) {
+ return;
+ }
+ bConstraintTarget *ct;
+ ListBase target = {NULL, NULL};
+ SINGLETARGET_GET_TARS(con, con->space_object, con->space_subtarget, ct, &target);
+
+ /* Basically default_get_tarmat but without the unused parameters. */
+ if (VALID_CONS_TARGET(ct)) {
+ constraint_target_to_mat4(ct->tar,
+ ct->subtarget,
+ NULL,
+ ct->matrix,
+ CONSTRAINT_SPACE_WORLD,
+ CONSTRAINT_SPACE_WORLD,
+ 0,
+ 0);
+ copy_m4_m4(r_mat, ct->matrix);
+ }
+ else {
+ unit_m4(r_mat);
+ }
+
+ SINGLETARGET_FLUSH_TARS(con, con->space_object, con->space_subtarget, ct, &target, true);
+}
+
/* ---------- Evaluation ----------- */
/* This function is called whenever constraints need to be evaluated. Currently, all
@@ -6048,12 +6197,15 @@ void BKE_constraints_solve(struct Depsgraph *depsgraph,
*/
enf = con->enforce;
+ /* Get custom space matrix. */
+ BKE_constraint_custom_object_space_get(cob->space_obj_world_matrix, con);
+
/* make copy of world-space matrix pre-constraint for use with blending later */
copy_m4_m4(oldmat, cob->matrix);
/* move owner matrix into right space */
BKE_constraint_mat_convertspace(
- cob->ob, cob->pchan, cob->matrix, CONSTRAINT_SPACE_WORLD, con->ownspace, false);
+ cob->ob, cob->pchan, cob, cob->matrix, CONSTRAINT_SPACE_WORLD, con->ownspace, false);
/* prepare targets for constraint solving */
BKE_constraint_targets_for_solving_get(depsgraph, con, cob, &targets, ctime);
@@ -6072,7 +6224,7 @@ void BKE_constraints_solve(struct Depsgraph *depsgraph,
/* move owner back into world-space for next constraint/other business */
if ((con->flag & CONSTRAINT_SPACEONCE) == 0) {
BKE_constraint_mat_convertspace(
- cob->ob, cob->pchan, cob->matrix, con->ownspace, CONSTRAINT_SPACE_WORLD, false);
+ cob->ob, cob->pchan, cob, cob->matrix, con->ownspace, CONSTRAINT_SPACE_WORLD, false);
}
/* Interpolate the enforcement, to blend result of constraint into final owner transform
diff --git a/source/blender/blenkernel/intern/fcurve_driver.c b/source/blender/blenkernel/intern/fcurve_driver.c
index b11a3cb9457..1bce9ad8e35 100644
--- a/source/blender/blenkernel/intern/fcurve_driver.c
+++ b/source/blender/blenkernel/intern/fcurve_driver.c
@@ -411,7 +411,7 @@ static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar)
/* Extract transform just like how the constraints do it! */
copy_m4_m4(mat, pchan->pose_mat);
BKE_constraint_mat_convertspace(
- ob, pchan, mat, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_LOCAL, false);
+ ob, pchan, NULL, mat, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_LOCAL, false);
/* ... and from that, we get our transform. */
copy_v3_v3(tmp_loc, mat[3]);
@@ -437,7 +437,7 @@ static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar)
/* Extract transform just like how the constraints do it! */
copy_m4_m4(mat, ob->obmat);
BKE_constraint_mat_convertspace(
- ob, NULL, mat, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, false);
+ ob, NULL, NULL, mat, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, false);
/* ... and from that, we get our transform. */
copy_v3_v3(tmp_loc, mat[3]);
@@ -514,7 +514,7 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar)
/* Just like how the constraints do it! */
copy_m4_m4(mat, pchan->pose_mat);
BKE_constraint_mat_convertspace(
- ob, pchan, mat, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_LOCAL, false);
+ ob, pchan, NULL, mat, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_LOCAL, false);
}
else {
/* Specially calculate local matrix, since chan_mat is not valid
@@ -541,7 +541,7 @@ static float dvar_eval_transChan(ChannelDriver *driver, DriverVar *dvar)
/* Just like how the constraints do it! */
copy_m4_m4(mat, ob->obmat);
BKE_constraint_mat_convertspace(
- ob, NULL, mat, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, false);
+ ob, NULL, NULL, mat, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_LOCAL, false);
}
else {
/* Transforms to matrix. */
diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c
index fde062b8454..af323bf91e4 100644
--- a/source/blender/editors/armature/armature_add.c
+++ b/source/blender/editors/armature/armature_add.c
@@ -453,10 +453,13 @@ static void updateDuplicateActionConstraintSettings(EditBone *dup_bone,
float mat[4][4];
+ bConstraintOb cob = {.depsgraph = NULL, .scene = NULL, .ob = ob, .pchan = NULL};
+ BKE_constraint_custom_object_space_get(cob.space_obj_world_matrix, curcon);
+
unit_m4(mat);
bPoseChannel *target_pchan = BKE_pose_channel_find_name(ob->pose, act_con->subtarget);
BKE_constraint_mat_convertspace(
- ob, target_pchan, mat, curcon->tarspace, CONSTRAINT_SPACE_LOCAL, false);
+ ob, target_pchan, &cob, mat, curcon->tarspace, CONSTRAINT_SPACE_LOCAL, false);
float max_axis_val = 0;
int max_axis = 0;
@@ -605,8 +608,11 @@ static void updateDuplicateLocRotConstraintSettings(Object *ob,
unit_m4(local_mat);
+ bConstraintOb cob = {.depsgraph = NULL, .scene = NULL, .ob = ob, .pchan = pchan};
+ BKE_constraint_custom_object_space_get(cob.space_obj_world_matrix, curcon);
+
BKE_constraint_mat_convertspace(
- ob, pchan, local_mat, curcon->ownspace, CONSTRAINT_SPACE_LOCAL, false);
+ ob, pchan, &cob, local_mat, curcon->ownspace, CONSTRAINT_SPACE_LOCAL, false);
if (curcon->type == CONSTRAINT_TYPE_ROTLIMIT) {
/* Zero out any location translation */
@@ -657,9 +663,12 @@ static void updateDuplicateTransformConstraintSettings(Object *ob,
float target_mat[4][4], own_mat[4][4], imat[4][4];
+ bConstraintOb cob = {.depsgraph = NULL, .scene = NULL, .ob = ob, .pchan = pchan};
+ BKE_constraint_custom_object_space_get(cob.space_obj_world_matrix, curcon);
+
unit_m4(own_mat);
BKE_constraint_mat_convertspace(
- ob, pchan, own_mat, curcon->ownspace, CONSTRAINT_SPACE_LOCAL, false);
+ ob, pchan, &cob, own_mat, curcon->ownspace, CONSTRAINT_SPACE_LOCAL, false);
/* ###Source map mirroring### */
float old_min, old_max;
@@ -717,7 +726,7 @@ static void updateDuplicateTransformConstraintSettings(Object *ob,
bPoseChannel *target_pchan = BKE_pose_channel_find_name(ob->pose, trans->subtarget);
unit_m4(target_mat);
BKE_constraint_mat_convertspace(
- ob, target_pchan, target_mat, curcon->tarspace, CONSTRAINT_SPACE_LOCAL, false);
+ ob, target_pchan, &cob, target_mat, curcon->tarspace, CONSTRAINT_SPACE_LOCAL, false);
invert_m4_m4(imat, target_mat);
/* convert values into local object space */
diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h
index 70d33d9ff94..ddc1b3bd9d7 100644
--- a/source/blender/makesdna/DNA_constraint_types.h
+++ b/source/blender/makesdna/DNA_constraint_types.h
@@ -61,12 +61,17 @@ typedef struct bConstraint {
/** Space that target should be evaluated in (only used if 1 target). */
char tarspace;
- /** Constraint name, MAX_NAME. */
- char name[64];
-
/* An "expand" bit for each of the constraint's (sub)panels (uiPanelDataExpansion). */
short ui_expand_flag;
+ /** Object to use as target for Custom Space of owner. */
+ struct Object *space_object;
+ /** Subtarget for Custom Space of owner - pchan or vgroup name, MAX_ID_NAME-2. */
+ char space_subtarget[64];
+
+ /** Constraint name, MAX_NAME. */
+ char name[64];
+
/** Amount of influence exherted by constraint (0.0-1.0). */
float enforce;
/** Point along subtarget bone where the actual target is. 0=head (default for all), 1=tail. */
@@ -722,6 +727,8 @@ typedef enum eBConstraint_Flags {
typedef enum eBConstraint_SpaceTypes {
/** Default for all - worldspace. */
CONSTRAINT_SPACE_WORLD = 0,
+ /** For all - custom space. */
+ CONSTRAINT_SPACE_CUSTOM = 5,
/**
* For objects (relative to parent/without parent influence),
* for bones (along normals of bone, without parent/rest-positions).
diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c
index 1c84be5907b..170de68a038 100644
--- a/source/blender/makesrna/intern/rna_constraint.c
+++ b/source/blender/makesrna/intern/rna_constraint.c
@@ -200,6 +200,12 @@ static const EnumPropertyItem target_space_pchan_items[] = {
"World Space",
"The transformation of the target is evaluated relative to the world "
"coordinate system"},
+ {CONSTRAINT_SPACE_CUSTOM,
+ "CUSTOM",
+ 0,
+ "Custom Space",
+ "The transformation of the target is evaluated relative to a custom object/bone/vertex "
+ "group"},
{CONSTRAINT_SPACE_POSE,
"POSE",
0,
@@ -227,6 +233,11 @@ static const EnumPropertyItem owner_space_pchan_items[] = {
0,
"World Space",
"The constraint is applied relative to the world coordinate system"},
+ {CONSTRAINT_SPACE_CUSTOM,
+ "CUSTOM",
+ 0,
+ "Custom Space",
+ "The constraint is applied in local space of a custom object/bone/vertex group"},
{CONSTRAINT_SPACE_POSE,
"POSE",
0,
@@ -275,6 +286,12 @@ static const EnumPropertyItem space_object_items[] = {
0,
"World Space",
"The transformation of the target is evaluated relative to the world coordinate system"},
+ {CONSTRAINT_SPACE_CUSTOM,
+ "CUSTOM",
+ 0,
+ "Custom Space",
+ "The transformation of the target is evaluated relative to a custom object/bone/vertex "
+ "group"},
{CONSTRAINT_SPACE_LOCAL,
"LOCAL",
0,
@@ -3398,6 +3415,18 @@ void RNA_def_constraint(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Target Space", "Space that target is evaluated in");
RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
+ prop = RNA_def_property(srna, "space_object", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "space_object");
+ RNA_def_property_ui_text(prop, "Object", "Object for Custom Space");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
+ RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_dependency_update");
+
+ prop = RNA_def_property(srna, "space_subtarget", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "space_subtarget");
+ RNA_def_property_ui_text(prop, "Sub-Target", "Armature bone, mesh or lattice vertex group, ...");
+ RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_dependency_update");
+
/* flags */
prop = RNA_def_property(srna, "mute", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", CONSTRAINT_OFF);
diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c
index a04ad28f9c3..9fb883568c9 100644
--- a/source/blender/makesrna/intern/rna_object_api.c
+++ b/source/blender/makesrna/intern/rna_object_api.c
@@ -324,8 +324,27 @@ static void rna_Object_mat_convert_space(Object *ob,
return;
}
}
+ /* These checks are extra security, they should never occur. */
+ if (from == CONSTRAINT_SPACE_CUSTOM) {
+ const char *identifier = NULL;
+ RNA_enum_identifier(space_items, from, &identifier);
+ BKE_reportf(reports,
+ RPT_ERROR,
+ "'from_space' '%s' is invalid when no custom space is given!",
+ identifier);
+ return;
+ }
+ if (to == CONSTRAINT_SPACE_CUSTOM) {
+ const char *identifier = NULL;
+ RNA_enum_identifier(space_items, to, &identifier);
+ BKE_reportf(reports,
+ RPT_ERROR,
+ "'to_space' '%s' is invalid when no custom space is given!",
+ identifier);
+ return;
+ }
- BKE_constraint_mat_convertspace(ob, pchan, (float(*)[4])mat_ret, from, to, false);
+ BKE_constraint_mat_convertspace(ob, pchan, NULL, (float(*)[4])mat_ret, from, to, false);
}
static void rna_Object_calc_matrix_camera(Object *ob,
diff --git a/tests/python/bl_constraints.py b/tests/python/bl_constraints.py
index 4deabc5f541..279c896c6af 100644
--- a/tests/python/bl_constraints.py
+++ b/tests/python/bl_constraints.py
@@ -301,6 +301,80 @@ class ObjectSolverTest(AbstractConstraintTests):
self.matrix_test('Object Solver.owner', initial_matrix)
+class CustomSpaceTest(AbstractConstraintTests):
+ layer_collection = 'Custom Space'
+
+ def test_loc_like_object(self):
+ """Custom Space: basic custom space evaluation for objects"""
+ loc_like_constraint = bpy.data.objects["Custom Space.object.owner"].constraints["Copy Location"]
+ loc_like_constraint.use_x = True
+ loc_like_constraint.use_y = True
+ loc_like_constraint.use_z = True
+ self.matrix_test('Custom Space.object.owner', Matrix((
+ (1.0, 0.0, -2.9802322387695312e-08, -0.01753106713294983),
+ (0.0, 1.0, 0.0, -0.08039519190788269),
+ (-2.9802322387695312e-08, 5.960464477539063e-08, 1.0, 0.1584688425064087),
+ (0.0, 0.0, 0.0, 1.0),
+ )))
+ loc_like_constraint.use_x = False
+ self.matrix_test('Custom Space.object.owner', Matrix((
+ (1.0, 0.0, -2.9802322387695312e-08, 0.18370598554611206),
+ (0.0, 1.0, 0.0, 0.47120195627212524),
+ (-2.9802322387695312e-08, 5.960464477539063e-08, 1.0, -0.16521614789962769),
+ (0.0, 0.0, 0.0, 1.0),
+ )))
+ loc_like_constraint.use_y = False
+ self.matrix_test('Custom Space.object.owner', Matrix((
+ (1.0, 0.0, -2.9802322387695312e-08, -0.46946945786476135),
+ (0.0, 1.0, 0.0, 0.423120379447937),
+ (-2.9802322387695312e-08, 5.960464477539063e-08, 1.0, -0.6532361507415771),
+ (0.0, 0.0, 0.0, 1.0),
+ )))
+ loc_like_constraint.use_z = False
+ loc_like_constraint.use_y = True
+ self.matrix_test('Custom Space.object.owner', Matrix((
+ (1.0, 0.0, -2.9802322387695312e-08, -0.346824586391449),
+ (0.0, 1.0, 0.0, 1.0480815172195435),
+ (-2.9802322387695312e-08, 5.960464477539063e-08, 1.0, 0.48802000284194946),
+ (0.0, 0.0, 0.0, 1.0),
+ )))
+
+ def test_loc_like_armature(self):
+ """Custom Space: basic custom space evaluation for bones"""
+ loc_like_constraint = bpy.data.objects["Custom Space.armature.owner"].pose.bones["Bone"].constraints["Copy Location"]
+ loc_like_constraint.use_x = True
+ loc_like_constraint.use_y = True
+ loc_like_constraint.use_z = True
+ self.bone_matrix_test('Custom Space.armature.owner', 'Bone', Matrix((
+ (0.4556015729904175, -0.03673229366540909, -0.8894257545471191, -0.01753103733062744),
+ (-0.45956411957740784, -0.8654094934463501, -0.19966775178909302, -0.08039522171020508),
+ (-0.762383222579956, 0.49971696734428406, -0.4111628830432892, 0.1584688425064087),
+ (0.0, 0.0, 0.0, 1.0),
+ )))
+ loc_like_constraint.use_x = False
+ self.bone_matrix_test('Custom Space.armature.owner', 'Bone', Matrix((
+ (0.4556015729904175, -0.03673229366540909, -0.8894257545471191, -0.310153603553772),
+ (-0.45956411957740784, -0.8654094934463501, -0.19966775178909302, -0.8824828863143921),
+ (-0.762383222579956, 0.49971696734428406, -0.4111628830432892, 0.629145085811615),
+ (0.0, 0.0, 0.0, 1.0),
+ )))
+ loc_like_constraint.use_y = False
+ self.bone_matrix_test('Custom Space.armature.owner', 'Bone', Matrix((
+ (0.4556015729904175, -0.03673229366540909, -0.8894257545471191, -1.0574829578399658),
+ (-0.45956411957740784, -0.8654094934463501, -0.19966775178909302, -0.937495231628418),
+ (-0.762383222579956, 0.49971696734428406, -0.4111628830432892, 0.07077804207801819),
+ (0.0, 0.0, 0.0, 1.0),
+ )))
+ loc_like_constraint.use_z = False
+ loc_like_constraint.use_y = True
+ self.bone_matrix_test('Custom Space.armature.owner', 'Bone', Matrix((
+ (0.4556015729904175, -0.03673229366540909, -0.8894257545471191, -0.25267064571380615),
+ (-0.45956411957740784, -0.8654094934463501, -0.19966775178909302, -0.9449876546859741),
+ (-0.762383222579956, 0.49971696734428406, -0.4111628830432892, 0.5583670735359192),
+ (0.0, 0.0, 0.0, 1.0),
+ )))
+
+
def main():
global args
import argparse