1
1

Compare commits

...

2 Commits

Author SHA1 Message Date
0c25ee9f26 Refactor the Custom Space patch.
Instead of modifying all the get_constraint_targets and
flush_constraint_targets callbacks, introduce wrapper API
functions and handle the new reference there.

Also, it is not needed to do all the target dance to compute
the space_obj_world_matrix.
2020-11-23 23:42:05 +03:00
Henrik Dick
b5a32ee650 Add Custom Space to Constraints
Constraints can currently only use World Space, Local Space, Pose Space, Local with Parent.
This patch adds 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.

It looks like this in the UI:
{F8731291}

The motivation for this patch is the following situation.
If you have a copy location constraint in your armature you are already limited when you want to use a specific axis. It can be done, but its unneccessarily difficult (as far as I know). But the situation is close to impossible as soon as you have a root bone in your armature (which I was told you always want). I need to only copy the location along a custom axis for a lot of things. The same goes for copy rotation/scale and all the limit constraints because I need to make that axis relative to the root bone transform.

Here is a demo video:
{F8498960}
This is the file of that video:
{F8647541}

This is the file for the tests included in this patch:
{F8699677}

File intended for acceptance test:
{F8879818}

Reviewed By: #animation_rigging, sybren, Severin, angavrilov

Differential Revision: https://developer.blender.org/D7437
2020-11-23 19:37:20 +03:00
20 changed files with 458 additions and 205 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,11 +205,14 @@ 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,
const bool keep_scale);
int BKE_constraint_targets_get(struct bConstraint *con, struct ListBase *list);
void BKE_constraint_targets_flush(struct bConstraint *con, struct ListBase *list, bool no_copy);
void BKE_constraint_target_matrix_get(struct Depsgraph *depsgraph,
struct Scene *scene,
struct bConstraint *con,
@@ -221,6 +226,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_init(struct bConstraintOb *cob, struct bConstraint *con);
void BKE_constraints_solve(struct Depsgraph *depsgraph,
struct ListBase *conlist,
struct bConstraintOb *cob,

View File

@@ -992,13 +992,10 @@ void BKE_pose_channels_remove(Object *ob,
else {
/* Maybe something the bone references is being removed instead? */
for (con = pchan->constraints.first; con; con = con->next) {
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
ListBase targets = {NULL, NULL};
bConstraintTarget *ct;
if (cti && cti->get_constraint_targets) {
cti->get_constraint_targets(con, &targets);
if (BKE_constraint_targets_get(con, &targets)) {
for (ct = targets.first; ct; ct = ct->next) {
if (ct->tar == ob) {
if (ct->subtarget[0]) {
@@ -1010,9 +1007,7 @@ void BKE_pose_channels_remove(Object *ob,
}
}
if (cti->flush_constraint_targets) {
cti->flush_constraint_targets(con, &targets, 0);
}
BKE_constraint_targets_flush(con, &targets, 0);
}
}

View File

@@ -2381,22 +2381,17 @@ static void pose_proxy_sync(Object *ob, Object *from, int layer_protected)
/* constraints - set target ob pointer to own object */
for (con = pchanw.constraints.first; con; con = con->next) {
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
ListBase targets = {NULL, NULL};
bConstraintTarget *ct;
if (cti && cti->get_constraint_targets) {
cti->get_constraint_targets(con, &targets);
if (BKE_constraint_targets_get(con, &targets)) {
for (ct = targets.first; ct; ct = ct->next) {
if (ct->tar == from) {
ct->tar = ob;
}
}
if (cti->flush_constraint_targets) {
cti->flush_constraint_targets(con, &targets, 0);
}
BKE_constraint_targets_flush(con, &targets, 0);
}
}

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,11 @@ static void default_get_tarmat_full_bbone(struct Depsgraph *UNUSED(depsgraph),
} \
(void)0
static bool is_custom_space_needed(bConstraint *con)
{
return con->ownspace == CONSTRAINT_SPACE_CUSTOM || con->tarspace == CONSTRAINT_SPACE_CUSTOM;
}
/* --------- ChildOf Constraint ------------ */
static void childof_new_data(void *cdata)
@@ -1262,6 +1331,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,
@@ -2276,7 +2346,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))
{
@@ -2295,6 +2365,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,
@@ -2654,6 +2725,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,
@@ -4116,7 +4188,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);
@@ -5333,6 +5405,19 @@ static void con_unlink_refs_cb(bConstraint *UNUSED(con),
}
}
/** Helper function to invoke the id_looper callback, including custom space. */
static void con_invoke_id_looper(const bConstraintTypeInfo *cti,
bConstraint *con,
ConstraintIDFunc func,
void *userdata)
{
if (cti->id_looper) {
cti->id_looper(con, func, userdata);
}
func(con, (ID **)&con->space_object, false, userdata);
}
/**
* Free data of a specific constraint if it has any info.
* be sure to run #BIK_clear_data() when freeing an IK constraint,
@@ -5350,8 +5435,8 @@ void BKE_constraint_free_data_ex(bConstraint *con, bool do_id_user)
}
/* unlink the referenced resources it uses */
if (do_id_user && cti->id_looper) {
cti->id_looper(con, con_unlink_refs_cb, NULL);
if (do_id_user) {
con_invoke_id_looper(cti, con, con_unlink_refs_cb, NULL);
}
}
@@ -5518,9 +5603,12 @@ static bConstraint *add_new_constraint(Object *ob,
return con;
}
bool BKE_constraint_target_uses_bbone(struct bConstraint *con,
struct bConstraintTarget *UNUSED(ct))
bool BKE_constraint_target_uses_bbone(struct bConstraint *con, struct bConstraintTarget *ct)
{
if (ct->flag & CONSTRAINT_TAR_CUSTOM_SPACE) {
return false;
}
return (con->flag & CONSTRAINT_BBONE_SHAPE) || (con->type == CONSTRAINT_TYPE_ARMATURE);
}
@@ -5554,9 +5642,7 @@ void BKE_constraints_id_loop(ListBase *conlist, ConstraintIDFunc func, void *use
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
if (cti) {
if (cti->id_looper) {
cti->id_looper(con, func, userdata);
}
con_invoke_id_looper(cti, con, func, userdata);
}
}
}
@@ -5608,16 +5694,14 @@ static void constraint_copy_data_ex(bConstraint *dst,
}
/* Fix usercounts for all referenced data that need it. */
if (cti->id_looper && (flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
cti->id_looper(dst, con_fix_copied_refs_cb, NULL);
if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
con_invoke_id_looper(cti, dst, con_fix_copied_refs_cb, NULL);
}
/* for proxies we don't want to make extern */
if (do_extern) {
/* go over used ID-links for this constraint to ensure that they are valid for proxies */
if (cti->id_looper) {
cti->id_looper(dst, con_extern_cb, NULL);
}
con_invoke_id_looper(cti, dst, con_extern_cb, NULL);
}
}
}
@@ -5872,6 +5956,61 @@ bool BKE_constraints_proxylocked_owner(Object *ob, bPoseChannel *pchan)
/* -------- Target-Matrix Stuff ------- */
/** Retrieves the list of all constraint targets, including the custom space target.
* Must be followed by a call to BKE_constraint_targets_flush to free memory. */
int BKE_constraint_targets_get(struct bConstraint *con, struct ListBase *list)
{
int count = 0;
if (con && list) {
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
if (cti) {
if (cti->get_constraint_targets) {
count = cti->get_constraint_targets(con, list);
}
/* Add the custom target. */
if (is_custom_space_needed(con)) {
bConstraintTarget *ct;
SINGLETARGET_GET_TARS(con, con->space_object, con->space_subtarget, ct, list);
ct->space = CONSTRAINT_SPACE_WORLD;
ct->flag |= CONSTRAINT_TAR_CUSTOM_SPACE;
count++;
}
}
}
return count;
}
/** Copies data from the list produced by BKE_constraint_targets_get back and frees memory. */
void BKE_constraint_targets_flush(struct bConstraint *con, struct ListBase *list, bool no_copy)
{
if (con && list) {
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
if (cti) {
/* Remove the custom target. */
if (is_custom_space_needed(con)) {
bConstraintTarget *ct = (bConstraintTarget *)list->last;
BLI_assert(ct && (ct->flag & CONSTRAINT_TAR_CUSTOM_SPACE));
if (!no_copy) {
con->space_object = ct->tar;
BLI_strncpy(con->space_subtarget, ct->subtarget, sizeof(con->space_subtarget));
}
BLI_freelinkN(list, ct);
}
if (cti->flush_constraint_targets) {
cti->flush_constraint_targets(con, list, no_copy);
}
}
}
}
/* This function is a relic from the prior implementations of the constraints system, when all
* constraints either had one or no targets. It used to be called during the main constraint
* solving loop, but is now only used for the remaining cases for a few constraints.
@@ -5930,6 +6069,9 @@ void BKE_constraint_target_matrix_get(struct Depsgraph *depsgraph,
}
}
/* Initialize the custom space for use in calculating the matrices. */
BKE_constraint_custom_object_space_init(cob, con);
/* get targets - we only need the first one though (and there should only be one) */
cti->get_constraint_targets(con, &targets);
@@ -5994,6 +6136,26 @@ void BKE_constraint_targets_for_solving_get(struct Depsgraph *depsgraph,
}
}
/** Initialize the Custom Space matrix inside cob. */
void BKE_constraint_custom_object_space_init(bConstraintOb *cob, bConstraint *con)
{
if (con && con->space_object && is_custom_space_needed(con)) {
/* Basically default_get_tarmat but without the unused parameters. */
constraint_target_to_mat4(con->space_object,
con->space_subtarget,
NULL,
cob->space_obj_world_matrix,
CONSTRAINT_SPACE_WORLD,
CONSTRAINT_SPACE_WORLD,
0,
0);
return;
}
unit_m4(cob->space_obj_world_matrix);
}
/* ---------- Evaluation ----------- */
/* This function is called whenever constraints need to be evaluated. Currently, all
@@ -6042,12 +6204,15 @@ void BKE_constraints_solve(struct Depsgraph *depsgraph,
*/
enf = con->enforce;
/* Initialize the custom space for use in calculating the matrices. */
BKE_constraint_custom_object_space_init(cob, 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);
@@ -6066,7 +6231,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

@@ -2242,22 +2242,17 @@ static void copy_object_pose(Object *obn, const Object *ob, const int flag)
* BKE_library_remap stuff, but...
* the flush_constraint_targets callback am not sure about, so will delay that for now. */
for (con = chan->constraints.first; con; con = con->next) {
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
ListBase targets = {NULL, NULL};
bConstraintTarget *ct;
if (cti && cti->get_constraint_targets) {
cti->get_constraint_targets(con, &targets);
if (BKE_constraint_targets_get(con, &targets)) {
for (ct = targets.first; ct; ct = ct->next) {
if (ct->tar == ob) {
ct->tar = obn;
}
}
if (cti->flush_constraint_targets) {
cti->flush_constraint_targets(con, &targets, 0);
}
BKE_constraint_targets_flush(con, &targets, 0);
}
}
}
@@ -5337,12 +5332,10 @@ bool BKE_object_modifier_update_subframe(Depsgraph *depsgraph,
/* also update constraint targets */
for (con = ob->constraints.first; con; con = con->next) {
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
ListBase targets = {NULL, NULL};
if (cti && cti->get_constraint_targets) {
if (BKE_constraint_targets_get(con, &targets)) {
bConstraintTarget *ct;
cti->get_constraint_targets(con, &targets);
for (ct = targets.first; ct; ct = ct->next) {
if (ct->tar) {
BKE_object_modifier_update_subframe(
@@ -5350,9 +5343,7 @@ bool BKE_object_modifier_update_subframe(Depsgraph *depsgraph,
}
}
/* free temp targets */
if (cti->flush_constraint_targets) {
cti->flush_constraint_targets(con, &targets, 0);
}
BKE_constraint_targets_flush(con, &targets, 0);
}
}
}

View File

@@ -1072,6 +1072,7 @@ void DepsgraphRelationBuilder::build_constraints(ID *id,
/* Add dependencies for each constraint in turn. */
for (bConstraint *con = (bConstraint *)constraints->first; con; con = con->next) {
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
ListBase targets = {nullptr, nullptr};
/* Invalid constraint type. */
if (cti == nullptr) {
continue;
@@ -1120,9 +1121,7 @@ void DepsgraphRelationBuilder::build_constraints(ID *id,
add_relation(cache_key, constraint_op_key, cti->name);
}
}
else if (cti->get_constraint_targets) {
ListBase targets = {nullptr, nullptr};
cti->get_constraint_targets(con, &targets);
else if (BKE_constraint_targets_get(con, &targets)) {
LISTBASE_FOREACH (bConstraintTarget *, ct, &targets) {
if (ct->tar == nullptr) {
continue;
@@ -1232,9 +1231,7 @@ void DepsgraphRelationBuilder::build_constraints(ID *id,
add_relation(target_transform_key, constraint_op_key, cti->name);
}
}
if (cti->flush_constraint_targets) {
cti->flush_constraint_targets(con, &targets, true);
}
BKE_constraint_targets_flush(con, &targets, true);
}
}
}

View File

@@ -1335,16 +1335,19 @@ static void OVERLAY_relationship_lines(OVERLAY_ExtraCallBuffers *cb,
}
else {
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(curcon);
ListBase targets = {NULL, NULL};
if ((cti && cti->get_constraint_targets) && (curcon->ui_expand_flag & (1 << 0))) {
ListBase targets = {NULL, NULL};
if ((curcon->ui_expand_flag & (1 << 0)) && BKE_constraint_targets_get(curcon, &targets)) {
bConstraintTarget *ct;
cti->get_constraint_targets(curcon, &targets);
BKE_constraint_custom_object_space_init(cob, curcon);
for (ct = targets.first; ct; ct = ct->next) {
/* calculate target's matrix */
if (cti->get_target_matrix) {
if (ct->flag & CONSTRAINT_TAR_CUSTOM_SPACE) {
copy_m4_m4(ct->matrix, cob->space_obj_world_matrix);
}
else if (cti->get_target_matrix) {
cti->get_target_matrix(depsgraph, curcon, cob, ct, DEG_get_ctime(depsgraph));
}
else {
@@ -1353,9 +1356,7 @@ static void OVERLAY_relationship_lines(OVERLAY_ExtraCallBuffers *cb,
OVERLAY_extra_line_dashed(cb, ct->matrix[3], ob->obmat[3], constraint_color);
}
if (cti->flush_constraint_targets) {
cti->flush_constraint_targets(curcon, &targets, 1);
}
BKE_constraint_targets_flush(curcon, &targets, 1);
}
}
}

View File

@@ -400,13 +400,10 @@ static void updateDuplicateSubtarget(EditBone *dup_bone,
/* does this constraint have a subtarget in
* this armature?
*/
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(curcon);
ListBase targets = {NULL, NULL};
bConstraintTarget *ct;
if (cti && cti->get_constraint_targets) {
cti->get_constraint_targets(curcon, &targets);
if (BKE_constraint_targets_get(curcon, &targets)) {
for (ct = targets.first; ct; ct = ct->next) {
if ((ct->tar == ob) && (ct->subtarget[0])) {
oldtarget = get_named_editbone(editbones, ct->subtarget);
@@ -434,9 +431,7 @@ static void updateDuplicateSubtarget(EditBone *dup_bone,
}
}
if (cti->flush_constraint_targets) {
cti->flush_constraint_targets(curcon, &targets, 0);
}
BKE_constraint_targets_flush(curcon, &targets, 0);
}
}
}
@@ -453,10 +448,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_init(&cob, 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 +603,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_init(&cob, 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 +658,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_init(&cob, 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 +721,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

@@ -126,13 +126,10 @@ static void constraint_bone_name_fix(Object *ob,
bConstraintTarget *ct;
for (curcon = conlist->first; curcon; curcon = curcon->next) {
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(curcon);
ListBase targets = {NULL, NULL};
/* constraint targets */
if (cti && cti->get_constraint_targets) {
cti->get_constraint_targets(curcon, &targets);
if (BKE_constraint_targets_get(curcon, &targets)) {
for (ct = targets.first; ct; ct = ct->next) {
if (ct->tar == ob) {
if (STREQ(ct->subtarget, oldname)) {
@@ -141,9 +138,7 @@ static void constraint_bone_name_fix(Object *ob,
}
}
if (cti->flush_constraint_targets) {
cti->flush_constraint_targets(curcon, &targets, 0);
}
BKE_constraint_targets_flush(curcon, &targets, 0);
}
/* action constraints */

View File

@@ -84,14 +84,11 @@ static void joined_armature_fix_links_constraints(Main *bmain,
bool changed = false;
for (con = lb->first; con; con = con->next) {
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
ListBase targets = {NULL, NULL};
bConstraintTarget *ct;
/* constraint targets */
if (cti && cti->get_constraint_targets) {
cti->get_constraint_targets(con, &targets);
if (BKE_constraint_targets_get(con, &targets)) {
for (ct = targets.first; ct; ct = ct->next) {
if (ct->tar == srcArm) {
if (ct->subtarget[0] == '\0') {
@@ -106,9 +103,7 @@ static void joined_armature_fix_links_constraints(Main *bmain,
}
}
if (cti->flush_constraint_targets) {
cti->flush_constraint_targets(con, &targets, 0);
}
BKE_constraint_targets_flush(con, &targets, 0);
}
/* action constraint? (pose constraints only) */
@@ -467,14 +462,11 @@ static void separated_armature_fix_links(Main *bmain, Object *origArm, Object *n
if (ob->type == OB_ARMATURE) {
for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
for (con = pchan->constraints.first; con; con = con->next) {
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
ListBase targets = {NULL, NULL};
bConstraintTarget *ct;
/* constraint targets */
if (cti && cti->get_constraint_targets) {
cti->get_constraint_targets(con, &targets);
if (BKE_constraint_targets_get(con, &targets)) {
for (ct = targets.first; ct; ct = ct->next) {
/* Any targets which point to original armature
* are redirected to the new one only if:
@@ -495,9 +487,7 @@ static void separated_armature_fix_links(Main *bmain, Object *origArm, Object *n
}
}
if (cti->flush_constraint_targets) {
cti->flush_constraint_targets(con, &targets, 0);
}
BKE_constraint_targets_flush(con, &targets, 0);
}
}
}
@@ -506,14 +496,11 @@ static void separated_armature_fix_links(Main *bmain, Object *origArm, Object *n
/* fix object-level constraints */
if (ob != origArm) {
for (con = ob->constraints.first; con; con = con->next) {
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
ListBase targets = {NULL, NULL};
bConstraintTarget *ct;
/* constraint targets */
if (cti && cti->get_constraint_targets) {
cti->get_constraint_targets(con, &targets);
if (BKE_constraint_targets_get(con, &targets)) {
for (ct = targets.first; ct; ct = ct->next) {
/* any targets which point to original armature are redirected to the new one only if:
* - the target isn't origArm/newArm itself
@@ -533,9 +520,7 @@ static void separated_armature_fix_links(Main *bmain, Object *origArm, Object *n
}
}
if (cti->flush_constraint_targets) {
cti->flush_constraint_targets(con, &targets, 0);
}
BKE_constraint_targets_flush(con, &targets, 0);
}
}
}

View File

@@ -673,13 +673,10 @@ static int pose_select_constraint_target_exec(bContext *C, wmOperator *UNUSED(op
CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones) {
if (pchan->bone->flag & BONE_SELECTED) {
for (con = pchan->constraints.first; con; con = con->next) {
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
ListBase targets = {NULL, NULL};
bConstraintTarget *ct;
if (cti && cti->get_constraint_targets) {
cti->get_constraint_targets(con, &targets);
if (BKE_constraint_targets_get(con, &targets)) {
for (ct = targets.first; ct; ct = ct->next) {
Object *ob = ct->tar;
@@ -695,9 +692,7 @@ static int pose_select_constraint_target_exec(bContext *C, wmOperator *UNUSED(op
}
}
if (cti->flush_constraint_targets) {
cti->flush_constraint_targets(con, &targets, 1);
}
BKE_constraint_targets_flush(con, &targets, 1);
}
}
}

View File

@@ -271,13 +271,11 @@ static void set_constraint_nth_target(bConstraint *con,
const char subtarget[],
int index)
{
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
ListBase targets = {NULL, NULL};
bConstraintTarget *ct;
int num_targets, i;
if (cti && cti->get_constraint_targets) {
cti->get_constraint_targets(con, &targets);
if (BKE_constraint_targets_get(con, &targets)) {
num_targets = BLI_listbase_count(&targets);
if (index < 0) {
@@ -300,9 +298,7 @@ static void set_constraint_nth_target(bConstraint *con,
}
}
if (cti->flush_constraint_targets) {
cti->flush_constraint_targets(con, &targets, 0);
}
BKE_constraint_targets_flush(con, &targets, 0);
}
}
@@ -315,7 +311,6 @@ static void set_constraint_nth_target(bConstraint *con,
static void test_constraint(
Main *bmain, Object *owner, bPoseChannel *pchan, bConstraint *con, int type)
{
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
ListBase targets = {NULL, NULL};
bConstraintTarget *ct;
bool check_targets = true;
@@ -494,14 +489,7 @@ static void test_constraint(
}
/* Check targets for constraints */
if (check_targets && cti && cti->get_constraint_targets) {
cti->get_constraint_targets(con, &targets);
/* constraints with empty target list that actually require targets */
if (!targets.first && ELEM(con->type, CONSTRAINT_TYPE_ARMATURE)) {
con->flag |= CONSTRAINT_DISABLE;
}
if (check_targets && BKE_constraint_targets_get(con, &targets)) {
/* disable and clear constraints targets that are incorrect */
for (ct = targets.first; ct; ct = ct->next) {
/* general validity checks (for those constraints that need this) */
@@ -572,8 +560,12 @@ static void test_constraint(
}
/* free any temporary targets */
if (cti->flush_constraint_targets) {
cti->flush_constraint_targets(con, &targets, 0);
BKE_constraint_targets_flush(con, &targets, 0);
}
else if (check_targets) {
/* constraints with empty target list that actually require targets */
if (ELEM(con->type, CONSTRAINT_TYPE_ARMATURE)) {
con->flag |= CONSTRAINT_DISABLE;
}
}
}

View File

@@ -239,24 +239,26 @@ bool BCAnimationSampler::is_animated_by_constraint(Object *ob,
for (con = (bConstraint *)conlist->first; con; con = con->next) {
ListBase targets = {nullptr, nullptr};
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
if (!bc_validateConstraints(con)) {
continue;
}
if (cti && cti->get_constraint_targets) {
if (BKE_constraint_targets_get(con, &targets)) {
bConstraintTarget *ct;
Object *obtar;
cti->get_constraint_targets(con, &targets);
bool found = false;
for (ct = (bConstraintTarget *)targets.first; ct; ct = ct->next) {
obtar = ct->tar;
if (obtar) {
if (animated_objects.find(obtar) != animated_objects.end()) {
return true;
found = true;
break;
}
}
}
BKE_constraint_targets_flush(con, &targets, true);
return found;
}
}
return false;

View File

@@ -206,24 +206,19 @@ void SceneExporter::writeNode(Object *ob)
/* not ideal: add the target object name as another parameter.
* No real mapping in the .dae
* Need support for multiple target objects also. */
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
ListBase targets = {nullptr, nullptr};
if (cti && cti->get_constraint_targets) {
ListBase targets = {nullptr, nullptr};
if (BKE_constraint_targets_get(con, &targets)) {
bConstraintTarget *ct;
Object *obtar;
cti->get_constraint_targets(con, &targets);
for (ct = (bConstraintTarget *)targets.first; ct; ct = ct->next) {
obtar = ct->tar;
std::string tar_id((obtar) ? id_name(obtar) : "");
colladaNode.addExtraTechniqueChildParameter("blender", con_tag, "target_id", tar_id);
}
if (cti->flush_constraint_targets) {
cti->flush_constraint_targets(con, &targets, true);
}
BKE_constraint_targets_flush(con, &targets, true);
}
con = con->next;

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. */
@@ -118,6 +123,8 @@ typedef struct bConstraintTarget {
typedef enum eConstraintTargetFlag {
/** temporary target-struct that needs to be freed after use */
CONSTRAINT_TAR_TEMP = (1 << 0),
/** temporary target for the custom space reference */
CONSTRAINT_TAR_CUSTOM_SPACE = (1 << 1),
} eConstraintTargetFlag;
/* bConstraintTarget/bConstraintOb -> type */
@@ -722,6 +729,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,
@@ -582,22 +599,17 @@ static const EnumPropertyItem *rna_Constraint_target_space_itemf(bContext *UNUSE
bool *UNUSED(r_free))
{
bConstraint *con = (bConstraint *)ptr->data;
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
ListBase targets = {NULL, NULL};
bConstraintTarget *ct;
if (cti && cti->get_constraint_targets) {
cti->get_constraint_targets(con, &targets);
if (BKE_constraint_targets_get(con, &targets)) {
for (ct = targets.first; ct; ct = ct->next) {
if (ct->tar && ct->tar->type == OB_ARMATURE) {
if (ct->tar && ct->tar->type == OB_ARMATURE && !(ct->type & CONSTRAINT_TAR_CUSTOM_SPACE)) {
break;
}
}
if (cti->flush_constraint_targets) {
cti->flush_constraint_targets(con, &targets, 1);
}
BKE_constraint_targets_flush(con, &targets, 1);
if (ct) {
return target_space_pchan_items;
@@ -3398,6 +3410,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