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