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
This commit is contained in:
Henrik Dick 2020-12-03 10:42:29 +01:00 committed by Sybren A. Stüvel
parent 899dcc5f60
commit a6c4e39876
9 changed files with 397 additions and 94 deletions

@ -85,14 +85,23 @@ class ConstraintButtonsPanel(Panel):
row.operator("constraint.disable_keep_transform", text="", icon='CANCEL') row.operator("constraint.disable_keep_transform", text="", icon='CANCEL')
@staticmethod @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: if target or owner:
if separator:
layout.separator() layout.separator()
if target: if target:
layout.prop(con, "target_space", text="Target") layout.prop(con, "target_space", text="Target")
if owner: if owner:
layout.prop(con, "owner_space", text="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 @staticmethod
def target_template(layout, con, subtargets=True): def target_template(layout, con, subtargets=True):
col = layout.column() col = layout.column()
@ -237,7 +246,7 @@ class ConstraintButtonsPanel(Panel):
row.label(icon="BLANK1") row.label(icon="BLANK1")
layout.prop(con, "use_transform_limit") 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) self.draw_influence(layout, con)
@ -306,7 +315,7 @@ class ConstraintButtonsPanel(Panel):
row.prop_decorator(con, "max_z") row.prop_decorator(con, "max_z")
layout.prop(con, "use_transform_limit") 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) self.draw_influence(layout, con)
@ -375,7 +384,7 @@ class ConstraintButtonsPanel(Panel):
row.prop_decorator(con, "max_z") row.prop_decorator(con, "max_z")
layout.prop(con, "use_transform_limit") 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) self.draw_influence(layout, con)
@ -483,7 +492,7 @@ class ConstraintButtonsPanel(Panel):
layout.prop(con, "volume") layout.prop(con, "volume")
layout.prop(con, "owner_space") self.space_template(layout, con, target=False, owner=True)
self.draw_influence(layout, con) self.draw_influence(layout, con)
@ -1117,7 +1126,7 @@ class ConstraintButtonsSubPanel(Panel):
col = layout.column() col = layout.column()
col.active = not con.use_eval_time col.active = not con.use_eval_time
col.prop(con, "transform_channel", text="Channel") 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 = col.column(align=True)
sub.prop(con, "min", text="Range Min") sub.prop(con, "min", text="Range Min")

@ -56,6 +56,8 @@ typedef struct bConstraintOb {
float matrix[4][4]; float matrix[4][4];
/** original matrix (before constraint solving) */ /** original matrix (before constraint solving) */
float startmat[4][4]; float startmat[4][4];
/** space matrix for custom object space */
float space_obj_world_matrix[4][4];
/** type of owner */ /** type of owner */
short type; short type;
@ -203,6 +205,7 @@ void BKE_constraints_clear_evalob(struct bConstraintOb *cob);
void BKE_constraint_mat_convertspace(struct Object *ob, void BKE_constraint_mat_convertspace(struct Object *ob,
struct bPoseChannel *pchan, struct bPoseChannel *pchan,
struct bConstraintOb *cob,
float mat[4][4], float mat[4][4],
short from, short from,
short to, short to,
@ -221,6 +224,7 @@ void BKE_constraint_targets_for_solving_get(struct Depsgraph *depsgraph,
struct bConstraintOb *ob, struct bConstraintOb *ob,
struct ListBase *targets, struct ListBase *targets,
float ctime); float ctime);
void BKE_constraint_custom_object_space_get(float r_mat[4][4], struct bConstraint *con);
void BKE_constraints_solve(struct Depsgraph *depsgraph, void BKE_constraints_solve(struct Depsgraph *depsgraph,
struct ListBase *conlist, struct ListBase *conlist,
struct bConstraintOb *cob, struct bConstraintOb *cob,

@ -261,8 +261,13 @@ void BKE_constraints_clear_evalob(bConstraintOb *cob)
* of a matrix from one space to another for constraint evaluation. * of a matrix from one space to another for constraint evaluation.
* For now, this is only implemented for Objects and PoseChannels. * For now, this is only implemented for Objects and PoseChannels.
*/ */
void BKE_constraint_mat_convertspace( void BKE_constraint_mat_convertspace(Object *ob,
Object *ob, bPoseChannel *pchan, float mat[4][4], short from, short to, const bool keep_scale) bPoseChannel *pchan,
bConstraintOb *cob,
float mat[4][4],
short from,
short to,
const bool keep_scale)
{ {
float diff_mat[4][4]; float diff_mat[4][4];
float imat[4][4]; float imat[4][4];
@ -282,25 +287,30 @@ void BKE_constraint_mat_convertspace(
switch (from) { switch (from) {
case CONSTRAINT_SPACE_WORLD: /* ---------- FROM WORLDSPACE ---------- */ case CONSTRAINT_SPACE_WORLD: /* ---------- FROM WORLDSPACE ---------- */
{ {
/* world to pose */ 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); invert_m4_m4(imat, ob->obmat);
mul_m4_m4m4(mat, imat, mat); mul_m4_m4m4(mat, imat, mat);
/* use pose-space as stepping stone for other spaces... */ /* Use pose-space as stepping stone for other spaces. */
if (ELEM(to, CONSTRAINT_SPACE_LOCAL, CONSTRAINT_SPACE_PARLOCAL)) { if (ELEM(to, CONSTRAINT_SPACE_LOCAL, CONSTRAINT_SPACE_PARLOCAL)) {
/* call self with slightly different values */ /* 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; break;
} }
case CONSTRAINT_SPACE_POSE: /* ---------- FROM POSESPACE ---------- */ case CONSTRAINT_SPACE_POSE: /* ---------- FROM POSESPACE ---------- */
{ {
/* pose to world */
if (to == CONSTRAINT_SPACE_WORLD) {
mul_m4_m4m4(mat, ob->obmat, mat);
}
/* pose to local */ /* pose to local */
else if (to == CONSTRAINT_SPACE_LOCAL) { if (to == CONSTRAINT_SPACE_LOCAL) {
if (pchan->bone) { if (pchan->bone) {
BKE_armature_mat_pose_to_bone(pchan, mat, mat); BKE_armature_mat_pose_to_bone(pchan, mat, mat);
} }
@ -312,6 +322,16 @@ void BKE_constraint_mat_convertspace(
mul_m4_m4m4(mat, imat, mat); 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; break;
} }
case CONSTRAINT_SPACE_LOCAL: /* ------------ FROM LOCALSPACE --------- */ case CONSTRAINT_SPACE_LOCAL: /* ------------ FROM LOCALSPACE --------- */
@ -323,9 +343,10 @@ void BKE_constraint_mat_convertspace(
} }
/* use pose-space as stepping stone for other spaces */ /* 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 */ /* 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; break;
} }
@ -337,9 +358,24 @@ void BKE_constraint_mat_convertspace(
} }
/* use pose-space as stepping stone for other spaces */ /* 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 */ /* 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; break;
} }
@ -347,10 +383,11 @@ void BKE_constraint_mat_convertspace(
} }
else { else {
/* objects */ /* objects */
if (from == CONSTRAINT_SPACE_WORLD && to == CONSTRAINT_SPACE_LOCAL) { if (from == CONSTRAINT_SPACE_WORLD) {
/* check if object has a parent */ if (to == CONSTRAINT_SPACE_LOCAL) {
/* Check if object has a parent. */
if (ob->parent) { if (ob->parent) {
/* 'subtract' parent's effects from owner */ /* 'subtract' parent's effects from owner. */
mul_m4_m4m4(diff_mat, ob->parent->obmat, ob->parentinv); mul_m4_m4m4(diff_mat, ob->parent->obmat, ob->parentinv);
invert_m4_m4_safe(imat, diff_mat); invert_m4_m4_safe(imat, diff_mat);
mul_m4_m4m4(mat, imat, mat); mul_m4_m4m4(mat, imat, mat);
@ -359,9 +396,8 @@ void BKE_constraint_mat_convertspace(
/* Local space in this case will have to be defined as local to the owner's /* 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. * 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 /* XXX This is actually an ugly hack, local space of a parent-less object *is* the same
* global space! * as global space! Think what we want actually here is some kind of 'Final Space', i.e
* 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, * . once transformations are applied - users are often confused about this too,
* this is not consistent with bones * this is not consistent with bones
* local space either... Meh :| * local space either... Meh :|
@ -377,7 +413,14 @@ void BKE_constraint_mat_convertspace(
mul_m4_m4m4(mat, imat, mat); mul_m4_m4m4(mat, imat, mat);
} }
} }
else if (from == CONSTRAINT_SPACE_LOCAL && to == CONSTRAINT_SPACE_WORLD) { 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) {
/* check that object has a parent - otherwise this won't work */ /* check that object has a parent - otherwise this won't work */
if (ob->parent) { if (ob->parent) {
/* 'add' parent's effect back to owner */ /* 'add' parent's effect back to owner */
@ -397,6 +440,24 @@ void BKE_constraint_mat_convertspace(
mul_m4_m4m4(mat, diff_mat, mat); 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 */ /* The cases where the target can be object data have not been implemented */
static void constraint_target_to_mat4(Object *ob, static void constraint_target_to_mat4(Object *ob,
const char *substring, const char *substring,
bConstraintOb *cob,
float mat[4][4], float mat[4][4],
short from, short from,
short to, short to,
@ -582,7 +644,7 @@ static void constraint_target_to_mat4(Object *ob,
/* Case OBJECT */ /* Case OBJECT */
if (substring[0] == '\0') { if (substring[0] == '\0') {
copy_m4_m4(mat, ob->obmat); 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 */ /* Case VERTEXGROUP */
/* Current method just takes the average location of all the points in the /* 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) { else if (ob->type == OB_MESH) {
contarget_get_mesh_mat(ob, substring, mat); 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) { else if (ob->type == OB_LATTICE) {
contarget_get_lattice_mat(ob, substring, mat); 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 */ /* Case BONE */
else { else {
@ -662,7 +724,7 @@ static void constraint_target_to_mat4(Object *ob,
} }
/* convert matrix space as required */ /* 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), static void default_get_tarmat(struct Depsgraph *UNUSED(depsgraph),
bConstraint *con, bConstraint *con,
bConstraintOb *UNUSED(cob), bConstraintOb *cob,
bConstraintTarget *ct, bConstraintTarget *ct,
float UNUSED(ctime)) float UNUSED(ctime))
{ {
if (VALID_CONS_TARGET(ct)) { if (VALID_CONS_TARGET(ct)) {
constraint_target_to_mat4(ct->tar, constraint_target_to_mat4(ct->tar,
ct->subtarget, ct->subtarget,
cob,
ct->matrix, ct->matrix,
CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_WORLD,
ct->space, 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), static void default_get_tarmat_full_bbone(struct Depsgraph *UNUSED(depsgraph),
bConstraint *con, bConstraint *con,
bConstraintOb *UNUSED(cob), bConstraintOb *cob,
bConstraintTarget *ct, bConstraintTarget *ct,
float UNUSED(ctime)) float UNUSED(ctime))
{ {
if (VALID_CONS_TARGET(ct)) { if (VALID_CONS_TARGET(ct)) {
constraint_target_to_mat4(ct->tar, constraint_target_to_mat4(ct->tar,
ct->subtarget, ct->subtarget,
cob,
ct->matrix, ct->matrix,
CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_WORLD,
ct->space, ct->space,
@ -844,6 +908,32 @@ static void default_get_tarmat_full_bbone(struct Depsgraph *UNUSED(depsgraph),
} \ } \
(void)0 (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 ------------ */ /* --------- ChildOf Constraint ------------ */
static void childof_new_data(void *cdata) static void childof_new_data(void *cdata)
@ -1030,6 +1120,8 @@ static void trackto_id_looper(bConstraint *con, ConstraintIDFunc func, void *use
/* target only */ /* target only */
func(con, (ID **)&data->tar, false, userdata); func(con, (ID **)&data->tar, false, userdata);
custom_space_id_looper(con, func, userdata);
} }
static int trackto_get_tars(bConstraint *con, ListBase *list) 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 */ /* standard target-getting macro for single-target constraints */
SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list); SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list);
return 1; return 1 + get_space_tar(con, list);
} }
return 0; 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 */ /* the following macro is used for all standard single-target constraints */
SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, no_copy); 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)) { if (VALID_CONS_TARGET(ct)) {
constraint_target_to_mat4(ct->tar, constraint_target_to_mat4(ct->tar,
ct->subtarget, ct->subtarget,
cob,
ct->matrix, ct->matrix,
CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_WORLD,
ct->space, ct->space,
@ -1533,11 +1627,11 @@ static bConstraintTypeInfo CTI_LOCLIMIT = {
"Limit Location", /* name */ "Limit Location", /* name */
"bLocLimitConstraint", /* struct name */ "bLocLimitConstraint", /* struct name */
NULL, /* free data */ NULL, /* free data */
NULL, /* id looper */ custom_space_id_looper, /* id looper */
NULL, /* copy data */ NULL, /* copy data */
NULL, /* new data */ NULL, /* new data */
NULL, /* get constraint targets */ get_space_tar, /* get constraint targets */
NULL, /* flush constraint targets */ flush_space_tar, /* flush constraint targets */
NULL, /* get target matrix */ NULL, /* get target matrix */
loclimit_evaluate, /* evaluate */ loclimit_evaluate, /* evaluate */
}; };
@ -1596,11 +1690,11 @@ static bConstraintTypeInfo CTI_ROTLIMIT = {
"Limit Rotation", /* name */ "Limit Rotation", /* name */
"bRotLimitConstraint", /* struct name */ "bRotLimitConstraint", /* struct name */
NULL, /* free data */ NULL, /* free data */
NULL, /* id looper */ custom_space_id_looper, /* id looper */
NULL, /* copy data */ NULL, /* copy data */
NULL, /* new data */ NULL, /* new data */
NULL, /* get constraint targets */ get_space_tar, /* get constraint targets */
NULL, /* flush constraint targets */ flush_space_tar, /* flush constraint targets */
NULL, /* get target matrix */ NULL, /* get target matrix */
rotlimit_evaluate, /* evaluate */ rotlimit_evaluate, /* evaluate */
}; };
@ -1663,11 +1757,11 @@ static bConstraintTypeInfo CTI_SIZELIMIT = {
"Limit Scale", /* name */ "Limit Scale", /* name */
"bSizeLimitConstraint", /* struct name */ "bSizeLimitConstraint", /* struct name */
NULL, /* free data */ NULL, /* free data */
NULL, /* id looper */ custom_space_id_looper, /* id looper */
NULL, /* copy data */ NULL, /* copy data */
NULL, /* new data */ NULL, /* new data */
NULL, /* get constraint targets */ get_space_tar, /* get constraint targets */
NULL, /* flush constraint targets */ flush_space_tar, /* flush constraint targets */
NULL, /* get target matrix */ NULL, /* get target matrix */
sizelimit_evaluate, /* evaluate */ sizelimit_evaluate, /* evaluate */
}; };
@ -1687,6 +1781,8 @@ static void loclike_id_looper(bConstraint *con, ConstraintIDFunc func, void *use
/* target only */ /* target only */
func(con, (ID **)&data->tar, false, userdata); func(con, (ID **)&data->tar, false, userdata);
custom_space_id_looper(con, func, userdata);
} }
static int loclike_get_tars(bConstraint *con, ListBase *list) 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 */ /* standard target-getting macro for single-target constraints */
SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list); SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list);
return 1; return 1 + get_space_tar(con, list);
} }
return 0; 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 */ /* the following macro is used for all standard single-target constraints */
SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, no_copy); 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 */ /* target only */
func(con, (ID **)&data->tar, false, userdata); func(con, (ID **)&data->tar, false, userdata);
custom_space_id_looper(con, func, userdata);
} }
static int rotlike_get_tars(bConstraint *con, ListBase *list) 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 */ /* standard target-getting macro for single-target constraints */
SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list); SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list);
return 1; return 1 + get_space_tar(con, list);
} }
return 0; 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 */ /* the following macro is used for all standard single-target constraints */
SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, no_copy); 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 */ /* target only */
func(con, (ID **)&data->tar, false, userdata); func(con, (ID **)&data->tar, false, userdata);
custom_space_id_looper(con, func, userdata);
} }
static int sizelike_get_tars(bConstraint *con, ListBase *list) 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 */ /* standard target-getting macro for single-target constraints */
SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list); SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list);
return 1; return 1 + get_space_tar(con, list);
} }
return 0; 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 */ /* the following macro is used for all standard single-target constraints */
SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, no_copy); 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 */ /* target only */
func(con, (ID **)&data->tar, false, userdata); func(con, (ID **)&data->tar, false, userdata);
custom_space_id_looper(con, func, userdata);
} }
static int translike_get_tars(bConstraint *con, ListBase *list) 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 */ /* standard target-getting macro for single-target constraints */
SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list); SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list);
return 1; return 1 + get_space_tar(con, list);
} }
return 0; 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 */ /* the following macro is used for all standard single-target constraints */
SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, no_copy); 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 */ "Maintain Volume", /* name */
"bSameVolumeConstraint", /* struct name */ "bSameVolumeConstraint", /* struct name */
NULL, /* free data */ NULL, /* free data */
NULL, /* id looper */ custom_space_id_looper, /* id looper */
NULL, /* copy data */ NULL, /* copy data */
samevolume_new_data, /* new data */ samevolume_new_data, /* new data */
NULL, /* get constraint targets */ get_space_tar, /* get constraint targets */
NULL, /* flush constraint targets */ flush_space_tar, /* flush constraint targets */
NULL, /* get target matrix */ NULL, /* get target matrix */
samevolume_evaluate, /* evaluate */ 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) */ /* Whether this approach is maintained remains to be seen (aligorith) */
static void pycon_get_tarmat(struct Depsgraph *UNUSED(depsgraph), static void pycon_get_tarmat(struct Depsgraph *UNUSED(depsgraph),
bConstraint *con, bConstraint *con,
bConstraintOb *UNUSED(cob), bConstraintOb *cob,
bConstraintTarget *ct, bConstraintTarget *ct,
float UNUSED(ctime)) float UNUSED(ctime))
{ {
@ -2301,6 +2407,7 @@ static void pycon_get_tarmat(struct Depsgraph *UNUSED(depsgraph),
*/ */
constraint_target_to_mat4(ct->tar, constraint_target_to_mat4(ct->tar,
ct->subtarget, ct->subtarget,
cob,
ct->matrix, ct->matrix,
CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_WORLD,
ct->space, ct->space,
@ -2608,6 +2715,8 @@ static void actcon_id_looper(bConstraint *con, ConstraintIDFunc func, void *user
/* action */ /* action */
func(con, (ID **)&data->act, true, userdata); func(con, (ID **)&data->act, true, userdata);
custom_space_id_looper(con, func, userdata);
} }
static int actcon_get_tars(bConstraint *con, ListBase *list) 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 */ /* standard target-getting macro for single-target constraints */
SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list); SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list);
return 1; return 1 + get_space_tar(con, list);
} }
return 0; 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 */ /* the following macro is used for all standard single-target constraints */
SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, no_copy); 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 */ /* get the transform matrix of the target */
constraint_target_to_mat4(ct->tar, constraint_target_to_mat4(ct->tar,
ct->subtarget, ct->subtarget,
cob,
tempmat, tempmat,
CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_WORLD,
ct->space, ct->space,
@ -3117,6 +3228,8 @@ static void distlimit_id_looper(bConstraint *con, ConstraintIDFunc func, void *u
/* target only */ /* target only */
func(con, (ID **)&data->tar, false, userdata); func(con, (ID **)&data->tar, false, userdata);
custom_space_id_looper(con, func, userdata);
} }
static int distlimit_get_tars(bConstraint *con, ListBase *list) 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 */ /* standard target-getting macro for single-target constraints */
SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list); SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list);
return 1; return 1 + get_space_tar(con, list);
} }
return 0; 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 */ /* the following macro is used for all standard single-target constraints */
SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, no_copy); 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 */ /* target only */
func(con, (ID **)&data->tar, false, userdata); func(con, (ID **)&data->tar, false, userdata);
custom_space_id_looper(con, func, userdata);
} }
static int minmax_get_tars(bConstraint *con, ListBase *list) 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 */ /* standard target-getting macro for single-target constraints */
SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list); SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list);
return 1; return 1 + get_space_tar(con, list);
} }
return 0; 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 */ /* the following macro is used for all standard single-target constraints */
SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, no_copy); 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 */ /* target only */
func(con, (ID **)&data->tar, false, userdata); func(con, (ID **)&data->tar, false, userdata);
custom_space_id_looper(con, func, userdata);
} }
static int transform_get_tars(bConstraint *con, ListBase *list) 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 */ /* standard target-getting macro for single-target constraints */
SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list); SINGLETARGET_GET_TARS(con, data->tar, data->subtarget, ct, list);
return 1; return 1 + get_space_tar(con, list);
} }
return 0; 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 */ /* the following macro is used for all standard single-target constraints */
SINGLETARGET_FLUSH_TARS(con, data->tar, data->subtarget, ct, list, no_copy); 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. */ * See T42447. */
unit_m4(mat); unit_m4(mat);
BKE_constraint_mat_convertspace( 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); invert_m4(mat);
mul_mat3_m4_v3(mat, no); 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 ----------- */ /* ---------- Evaluation ----------- */
/* This function is called whenever constraints need to be evaluated. Currently, all /* 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; 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 */ /* make copy of world-space matrix pre-constraint for use with blending later */
copy_m4_m4(oldmat, cob->matrix); copy_m4_m4(oldmat, cob->matrix);
/* move owner matrix into right space */ /* move owner matrix into right space */
BKE_constraint_mat_convertspace( 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 */ /* prepare targets for constraint solving */
BKE_constraint_targets_for_solving_get(depsgraph, con, cob, &targets, ctime); 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 */ /* move owner back into world-space for next constraint/other business */
if ((con->flag & CONSTRAINT_SPACEONCE) == 0) { if ((con->flag & CONSTRAINT_SPACEONCE) == 0) {
BKE_constraint_mat_convertspace( 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 /* Interpolate the enforcement, to blend result of constraint into final owner transform

@ -411,7 +411,7 @@ static float dvar_eval_locDiff(ChannelDriver *driver, DriverVar *dvar)
/* Extract transform just like how the constraints do it! */ /* Extract transform just like how the constraints do it! */
copy_m4_m4(mat, pchan->pose_mat); copy_m4_m4(mat, pchan->pose_mat);
BKE_constraint_mat_convertspace( 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. */ /* ... and from that, we get our transform. */
copy_v3_v3(tmp_loc, mat[3]); 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! */ /* Extract transform just like how the constraints do it! */
copy_m4_m4(mat, ob->obmat); copy_m4_m4(mat, ob->obmat);
BKE_constraint_mat_convertspace( 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. */ /* ... and from that, we get our transform. */
copy_v3_v3(tmp_loc, mat[3]); 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! */ /* Just like how the constraints do it! */
copy_m4_m4(mat, pchan->pose_mat); copy_m4_m4(mat, pchan->pose_mat);
BKE_constraint_mat_convertspace( 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 { else {
/* Specially calculate local matrix, since chan_mat is not valid /* 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! */ /* Just like how the constraints do it! */
copy_m4_m4(mat, ob->obmat); copy_m4_m4(mat, ob->obmat);
BKE_constraint_mat_convertspace( 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 { else {
/* Transforms to matrix. */ /* Transforms to matrix. */

@ -453,10 +453,13 @@ static void updateDuplicateActionConstraintSettings(EditBone *dup_bone,
float mat[4][4]; 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); unit_m4(mat);
bPoseChannel *target_pchan = BKE_pose_channel_find_name(ob->pose, act_con->subtarget); bPoseChannel *target_pchan = BKE_pose_channel_find_name(ob->pose, act_con->subtarget);
BKE_constraint_mat_convertspace( 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; float max_axis_val = 0;
int max_axis = 0; int max_axis = 0;
@ -605,8 +608,11 @@ static void updateDuplicateLocRotConstraintSettings(Object *ob,
unit_m4(local_mat); 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( 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) { if (curcon->type == CONSTRAINT_TYPE_ROTLIMIT) {
/* Zero out any location translation */ /* 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]; 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); unit_m4(own_mat);
BKE_constraint_mat_convertspace( 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### */ /* ###Source map mirroring### */
float old_min, old_max; 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); bPoseChannel *target_pchan = BKE_pose_channel_find_name(ob->pose, trans->subtarget);
unit_m4(target_mat); unit_m4(target_mat);
BKE_constraint_mat_convertspace( 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); invert_m4_m4(imat, target_mat);
/* convert values into local object space */ /* convert values into local object space */

@ -61,12 +61,17 @@ typedef struct bConstraint {
/** Space that target should be evaluated in (only used if 1 target). */ /** Space that target should be evaluated in (only used if 1 target). */
char tarspace; char tarspace;
/** Constraint name, MAX_NAME. */
char name[64];
/* An "expand" bit for each of the constraint's (sub)panels (uiPanelDataExpansion). */ /* An "expand" bit for each of the constraint's (sub)panels (uiPanelDataExpansion). */
short ui_expand_flag; 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). */ /** Amount of influence exherted by constraint (0.0-1.0). */
float enforce; float enforce;
/** Point along subtarget bone where the actual target is. 0=head (default for all), 1=tail. */ /** 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 { typedef enum eBConstraint_SpaceTypes {
/** Default for all - worldspace. */ /** Default for all - worldspace. */
CONSTRAINT_SPACE_WORLD = 0, CONSTRAINT_SPACE_WORLD = 0,
/** For all - custom space. */
CONSTRAINT_SPACE_CUSTOM = 5,
/** /**
* For objects (relative to parent/without parent influence), * For objects (relative to parent/without parent influence),
* for bones (along normals of bone, without parent/rest-positions). * for bones (along normals of bone, without parent/rest-positions).

@ -200,6 +200,12 @@ static const EnumPropertyItem target_space_pchan_items[] = {
"World Space", "World Space",
"The transformation of the target is evaluated relative to the world " "The transformation of the target is evaluated relative to the world "
"coordinate system"}, "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, {CONSTRAINT_SPACE_POSE,
"POSE", "POSE",
0, 0,
@ -227,6 +233,11 @@ static const EnumPropertyItem owner_space_pchan_items[] = {
0, 0,
"World Space", "World Space",
"The constraint is applied relative to the world coordinate system"}, "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, {CONSTRAINT_SPACE_POSE,
"POSE", "POSE",
0, 0,
@ -275,6 +286,12 @@ static const EnumPropertyItem space_object_items[] = {
0, 0,
"World Space", "World Space",
"The transformation of the target is evaluated relative to the world coordinate system"}, "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, {CONSTRAINT_SPACE_LOCAL,
"LOCAL", "LOCAL",
0, 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_ui_text(prop, "Target Space", "Space that target is evaluated in");
RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update"); 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 */ /* flags */
prop = RNA_def_property(srna, "mute", PROP_BOOLEAN, PROP_NONE); prop = RNA_def_property(srna, "mute", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", CONSTRAINT_OFF); RNA_def_property_boolean_sdna(prop, NULL, "flag", CONSTRAINT_OFF);

@ -324,8 +324,27 @@ static void rna_Object_mat_convert_space(Object *ob,
return; 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, static void rna_Object_calc_matrix_camera(Object *ob,

@ -301,6 +301,80 @@ class ObjectSolverTest(AbstractConstraintTests):
self.matrix_test('Object Solver.owner', initial_matrix) 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(): def main():
global args global args
import argparse import argparse