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
Notes: blender-bot 2024-04-11 14:26:06 +02:00
Referenced by issue #97523, In some Constraints, only one Custom Space selection box appears when there should be multiple
9 changed files with 397 additions and 94 deletions

View File

@ -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")

View File

@ -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,

View File

@ -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);
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, mat, CONSTRAINT_SPACE_POSE, to, keep_scale);
/* 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

View File

@ -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. */

View File

@ -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 */

View File

@ -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).

View File

@ -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);

View File

@ -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,

View File

@ -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