1
1

Compare commits

...

6 Commits

Author SHA1 Message Date
6f1e5b632e tweak: copy transforms 2020-12-02 12:13:09 +03:00
f38454bf9e Copy Transforms: implement Invert, Fix Shear and more Mix options.
This constraint can be naturally viewed as a prototype for a future
4x4 matrix math node (or subset thereof), since its basic semantics
already is matrix assignment. Thus it makes sense to add math options
to this constraint to increase flexibility in the meantime.

This patch adds support for several operations that would be useful:

- An option to fix shear in the incoming target matrix.
- An option to invert the target matrix.
- More ways to combine target and owner matrix.

Shear is known to cause issues for various mathematical operations,
so an option to remove it at key points is useful. In the future node
system this would be a separate operation, but due to the limits of
the constraint stack it has to be built in for now.

Inverting a matrix is also an operation that can be useful to have.
For some uses it may be useful to invert components separately, so
implement this by checking the Mix mode setting to avoid UI options.

Finally, add two more ways to combine the matrices (multiplied by
two due to the necessity for the Before/After choice). Now there
are three combine modes:

Full implements regular matrix multiplication as the most basic option.

Split Channels combines location, rotation and scale separately.
Looking at D7547 there is demand for such a mode in some cases,
and even with nodes it's cumbersome to rig manually every time.

Finally, Aligned emulates the 'anti-shear' Aligned Inherit Scale mode,
and basically uses Full for location, and Split for rotation/scale.

Differential Revision: https://developer.blender.org/D9469
2020-12-01 21:31:22 +03:00
b52d792f29 Limit Rotation: add an Euler Order option and orthogonalize the matrix.
Since Limit Rotation is based on Euler decomposition, it should allow
specifying the order to use for the same reasons as Copy Rotation does,
namely, if the bone uses Quaternion rotation for its animation channels,
there is no way to choose the order for the constraint.

In addition, add a call to orthogonalize the matrix before processing
for the same reasons as D8915, and an early exit in case no limits are
enabled for a bit of extra efficiency.

Since the constraint goes through Euler decomposition, it would remove
shear even before the change, but the rotation won't make much sense.

Differential Revision: https://developer.blender.org/D9626
2020-12-01 21:31:22 +03:00
9b73791d8d Constraints: add support for a new Owner Local Space for targets.
Add a new transformation space choice for bone constraints, which
represent the local transformation of the bone in the constraint
owner's local space.

The use case for this is transferring the local (i.e. excluding the
effect of parents) transformation of one bone to another one, while
ignoring the difference between their rest pose orientations.

Owner Local Space replaces the following setup:

* A `child` bone of the `target`, rotated the same as `owner` in rest pose.
* A `sibling` bone of the `target`, positioned same as `child` in rest
  pose and using Copy Transforms in World Space from `child`.
* The `owner` bone constraint uses Local Space of `sibling`.

Differential Revision: https://developer.blender.org/D9493
2020-12-01 21:31:22 +03:00
76dece2e0b 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-12-01 21:31:22 +03:00
Henrik Dick
f1190ff6c5 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-12-01 21:31:21 +03:00
23 changed files with 696 additions and 224 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()
@@ -236,8 +245,9 @@ class ConstraintButtonsPanel(Panel):
sub.prop(con, "max_z", text="Max")
row.label(icon="BLANK1")
layout.prop(con, "euler_order", text="Order")
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 +316,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 +385,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 +493,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)
@@ -495,6 +505,8 @@ class ConstraintButtonsPanel(Panel):
self.target_template(layout, con)
layout.prop(con, "fix_target_shear")
layout.prop(con, "invert")
layout.prop(con, "mix_mode", text="Mix")
self.space_template(layout, con)
@@ -1117,7 +1129,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,27 +287,45 @@ 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 (to != CONSTRAINT_SPACE_POSE) {
/* 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 (ELEM(to, CONSTRAINT_SPACE_LOCAL, CONSTRAINT_SPACE_OWNLOCAL)) {
if (pchan->bone) {
BKE_armature_mat_pose_to_bone(pchan, mat, mat);
if (to == CONSTRAINT_SPACE_OWNLOCAL) {
copy_m4_m4(diff_mat, pchan->bone->arm_mat);
if (cob && cob->pchan && cob->pchan->bone) {
invert_m4_m4(imat, cob->pchan->bone->arm_mat);
mul_m4_m4m4(diff_mat, imat, diff_mat);
}
zero_v3(diff_mat[3]);
invert_m4_m4(imat, diff_mat);
mul_m4_series(mat, diff_mat, mat, imat);
}
}
}
/* pose to local with parent */
@@ -312,20 +335,44 @@ 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 --------- */
{
case CONSTRAINT_SPACE_OWNLOCAL: {
/* local to pose - do inverse procedure that was done for pose to local */
if (pchan->bone) {
if (from == CONSTRAINT_SPACE_OWNLOCAL) {
copy_m4_m4(diff_mat, pchan->bone->arm_mat);
if (cob && cob->pchan && cob->pchan->bone) {
invert_m4_m4(imat, cob->pchan->bone->arm_mat);
mul_m4_m4m4(diff_mat, imat, diff_mat);
}
zero_v3(diff_mat[3]);
invert_m4_m4(imat, diff_mat);
mul_m4_series(mat, imat, mat, diff_mat);
}
/* we need the posespace_matrix = local_matrix + (parent_posespace_matrix + restpos) */
BKE_armature_mat_bone_to_pose(pchan, mat, mat);
}
/* use pose-space as stepping stone for other spaces */
if (ELEM(to, CONSTRAINT_SPACE_WORLD, CONSTRAINT_SPACE_PARLOCAL)) {
if (to != CONSTRAINT_SPACE_POSE) {
/* 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 +384,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 (to != CONSTRAINT_SPACE_POSE) {
/* 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 +409,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 +466,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 +660,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 +670,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 +683,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 +750,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 +793,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 +816,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 +934,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 +1357,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,
@@ -1551,10 +1647,28 @@ static void rotlimit_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *UN
float eul[3];
float size[3];
/* This constraint is based on euler rotation math, which doesn't work well with shear.
* The Y axis is chosen as the main one because constraints are most commonly used on bones.
* This also allows using the constraint to simply remove shear. */
orthogonalize_m4_stable(cob->matrix, 1, false);
/* Only do the complex processing if some limits are actually enabled. */
if (!(data->flag & (LIMIT_XROT | LIMIT_YROT | LIMIT_ZROT))) {
return;
}
/* Select the Euler rotation order, defaulting to the owner value. */
short rot_order = cob->rotOrder;
if (data->euler_order != CONSTRAINT_EULER_AUTO) {
rot_order = data->euler_order;
}
/* Decompose the matrix using the specified order. */
copy_v3_v3(loc, cob->matrix[3]);
mat4_to_size(size, cob->matrix);
mat4_to_eulO(eul, cob->rotOrder, cob->matrix);
mat4_to_eulO(eul, rot_order, cob->matrix);
/* constraint data uses radians internally */
@@ -1587,7 +1701,7 @@ static void rotlimit_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *UN
}
}
loc_eulO_size_to_mat4(cob->matrix, loc, eul, size, cob->rotOrder);
loc_eulO_size_to_mat4(cob->matrix, loc, eul, size, rot_order);
}
static bConstraintTypeInfo CTI_ROTLIMIT = {
@@ -2118,17 +2232,62 @@ static void translike_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *t
bConstraintTarget *ct = targets->first;
if (VALID_CONS_TARGET(ct)) {
float target_mat[4][4];
copy_m4_m4(target_mat, ct->matrix);
/* Fix the shear of the target matrix if enabled.
* Use Y as the axis since it's the natural default for bones. */
if (data->flag & TRANSLIKE_FIX_TARGET_SHEAR) {
orthogonalize_m4_stable(target_mat, 1, false);
}
/* Invert the transformation. */
if (data->flag & TRANSLIKE_INVERT) {
/* For modes that split channels, split during invert too. */
if (ELEM(data->mix_mode,
TRANSLIKE_MIX_BEFORE,
TRANSLIKE_MIX_AFTER,
TRANSLIKE_MIX_BEFORE_SPLIT,
TRANSLIKE_MIX_AFTER_SPLIT)) {
invert_m4_m4_split_channels(target_mat, target_mat);
}
else {
invert_m4(target_mat);
}
}
/* Finally, combine the matrices. */
switch (data->mix_mode) {
case TRANSLIKE_MIX_REPLACE:
copy_m4_m4(cob->matrix, ct->matrix);
copy_m4_m4(cob->matrix, target_mat);
break;
/* Simple matrix multiplication. */
case TRANSLIKE_MIX_BEFORE_FULL:
mul_m4_m4m4(cob->matrix, target_mat, cob->matrix);
break;
case TRANSLIKE_MIX_AFTER_FULL:
mul_m4_m4m4(cob->matrix, cob->matrix, target_mat);
break;
/* Aligned Inherit Scale emulation. */
case TRANSLIKE_MIX_BEFORE:
mul_m4_m4m4_aligned_scale(cob->matrix, ct->matrix, cob->matrix);
mul_m4_m4m4_aligned_scale(cob->matrix, target_mat, cob->matrix);
break;
case TRANSLIKE_MIX_AFTER:
mul_m4_m4m4_aligned_scale(cob->matrix, cob->matrix, ct->matrix);
mul_m4_m4m4_aligned_scale(cob->matrix, cob->matrix, target_mat);
break;
/* Fully separate handling of channels. */
case TRANSLIKE_MIX_BEFORE_SPLIT:
mul_m4_m4m4_split_channels(cob->matrix, target_mat, cob->matrix);
break;
case TRANSLIKE_MIX_AFTER_SPLIT:
mul_m4_m4m4_split_channels(cob->matrix, cob->matrix, target_mat);
break;
default:
@@ -2282,7 +2441,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 +2460,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,
@@ -2660,6 +2820,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,
@@ -4122,7 +4283,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);
@@ -5339,6 +5500,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,
@@ -5356,8 +5530,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);
}
}
@@ -5524,9 +5698,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);
}
@@ -5560,9 +5737,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);
}
}
}
@@ -5614,16 +5789,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);
}
}
}
@@ -5878,6 +6051,62 @@ 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. */
bConstraintTarget *ct = (bConstraintTarget *)list->last;
if (ct && (ct->flag & CONSTRAINT_TAR_CUSTOM_SPACE)) {
BLI_assert(is_custom_space_needed(con));
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.
@@ -5936,6 +6165,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);
@@ -6000,6 +6232,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
@@ -6048,12 +6300,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);
@@ -6072,7 +6327,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);
}
}
}
@@ -5341,12 +5336,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(
@@ -5354,9 +5347,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

@@ -211,6 +211,7 @@ void mul_transposed_mat3_m4_v3(const float M[4][4], float r[3]);
void mul_m3_v3_double(const float M[3][3], double r[3]);
void mul_m4_m4m4_aligned_scale(float R[4][4], const float A[4][4], const float B[4][4]);
void mul_m4_m4m4_split_channels(float R[4][4], const float A[4][4], const float B[4][4]);
void mul_m3_fl(float R[3][3], float f);
void mul_m4_fl(float R[4][4], float f);
@@ -229,6 +230,8 @@ bool invert_m4(float R[4][4]);
bool invert_m4_m4(float R[4][4], const float A[4][4]);
bool invert_m4_m4_fallback(float R[4][4], const float A[4][4]);
bool invert_m4_m4_split_channels(float R[4][4], const float A[4][4]);
/* double arithmetic (mixed float/double) */
void mul_m4_v4d(const float M[4][4], double r[4]);
void mul_v4d_m4v4d(double r[4], const float M[4][4], const double v[4]);

View File

@@ -1286,10 +1286,32 @@ bool invert_m4_m4(float inverse[4][4], const float mat[4][4])
#endif
}
/**
* Separately inverts location, rotation and scale of the input matrix.
*
* This produces a true inversion for mul_m4_m4m4_split_channels.
*/
bool invert_m4_m4_split_channels(float R[4][4], const float A[4][4])
{
float loc[3], rot[3][3], size[3];
mat4_to_loc_rot_size(loc, rot, size, A);
negate_v3(loc);
bool success = invert_m3(rot);
invert_v3(size);
loc_rot_size_to_mat4(R, loc, rot, size);
return success;
}
/**
* Combines transformations, handling scale separately in a manner equivalent
* to the Aligned Inherit Scale mode, in order to avoid creating shear.
* If A scale is uniform, the result is equivalent to ordinary multiplication.
*
* Note: this effectively takes output location from simple multiplication,
* and uses mul_m4_m4m4_split_channels for rotation and scale.
*/
void mul_m4_m4m4_aligned_scale(float R[4][4], const float A[4][4], const float B[4][4])
{
@@ -1307,6 +1329,25 @@ void mul_m4_m4m4_aligned_scale(float R[4][4], const float A[4][4], const float B
loc_rot_size_to_mat4(R, loc_r, rot_r, size_r);
}
/**
* Separately combines location, rotation and scale of the input matrices.
*/
void mul_m4_m4m4_split_channels(float R[4][4], const float A[4][4], const float B[4][4])
{
float loc_a[3], rot_a[3][3], size_a[3];
float loc_b[3], rot_b[3][3], size_b[3];
float loc_r[3], rot_r[3][3], size_r[3];
mat4_to_loc_rot_size(loc_a, rot_a, size_a, A);
mat4_to_loc_rot_size(loc_b, rot_b, size_b, B);
add_v3_v3v3(loc_r, loc_a, loc_b);
mul_m3_m3m3_uniq(rot_r, rot_a, rot_b);
mul_v3_v3v3(size_r, size_a, size_b);
loc_rot_size_to_mat4(R, loc_r, rot_r, size_r);
}
/****************************** Linear Algebra *******************************/
void transpose_m3(float R[3][3])

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

@@ -1334,16 +1334,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 {
@@ -1352,9 +1355,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,29 +431,28 @@ 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);
}
}
}
}
}
static void updateDuplicateActionConstraintSettings(EditBone *dup_bone,
EditBone *orig_bone,
Object *ob,
bConstraint *curcon)
static void updateDuplicateActionConstraintSettings(
EditBone *dup_bone, EditBone *orig_bone, Object *ob, bPoseChannel *pchan, bConstraint *curcon)
{
bActionConstraint *act_con = (bActionConstraint *)curcon->data;
bAction *act = (bAction *)act_con->act;
float mat[4][4];
bConstraintOb cob = {.depsgraph = NULL, .scene = NULL, .ob = ob, .pchan = pchan};
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 +601,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 +656,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 +719,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 */
@@ -827,7 +829,7 @@ static void updateDuplicateConstraintSettings(EditBone *dup_bone, EditBone *orig
for (curcon = conlist->first; curcon; curcon = curcon->next) {
switch (curcon->type) {
case CONSTRAINT_TYPE_ACTION:
updateDuplicateActionConstraintSettings(dup_bone, orig_bone, ob, curcon);
updateDuplicateActionConstraintSettings(dup_bone, orig_bone, ob, pchan, curcon);
break;
case CONSTRAINT_TYPE_KINEMATIC:
updateDuplicateKinematicConstraintSettings(curcon);

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

@@ -815,10 +815,13 @@ bool constraints_list_needinv(TransInfo *t, ListBase *list)
/* Copy Transforms constraint only does this in the Before mode. */
bTransLikeConstraint *data = (bTransLikeConstraint *)con->data;
if (ELEM(data->mix_mode, TRANSLIKE_MIX_BEFORE) &&
if (ELEM(data->mix_mode, TRANSLIKE_MIX_BEFORE, TRANSLIKE_MIX_BEFORE_FULL) &&
ELEM(t->mode, TFM_ROTATION, TFM_TRANSLATION)) {
return true;
}
if (ELEM(data->mix_mode, TRANSLIKE_MIX_BEFORE_SPLIT) && ELEM(t->mode, TFM_ROTATION)) {
return true;
}
}
else if (con->type == CONSTRAINT_TYPE_ACTION) {
/* The Action constraint only does this in the Before mode. */

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 */
@@ -311,8 +318,9 @@ typedef struct bSameVolumeConstraint {
/* Copy Transform Constraint */
typedef struct bTransLikeConstraint {
struct Object *tar;
int flag;
char mix_mode;
char _pad[7];
char _pad[3];
/** MAX_ID_NAME-2. */
char subtarget[64];
} bTransLikeConstraint;
@@ -529,6 +537,8 @@ typedef struct bRotLimitConstraint {
float zmin, zmax;
short flag;
short flag2;
char euler_order;
char _pad[3];
} bRotLimitConstraint;
/* Limit Scale Constraint */
@@ -722,6 +732,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).
@@ -731,6 +743,8 @@ typedef enum eBConstraint_SpaceTypes {
CONSTRAINT_SPACE_POSE = 2,
/** For posechannels - local with parent. */
CONSTRAINT_SPACE_PARLOCAL = 3,
/** For posechannels - local converted to the owner bone orientation. */
CONSTRAINT_SPACE_OWNLOCAL = 6,
/** For files from between 2.43-2.46 (should have been parlocal). */
CONSTRAINT_SPACE_INVALID = 4, /* do not exchange for anything! */
} eBConstraint_SpaceTypes;
@@ -801,6 +815,14 @@ typedef enum eCopyScale_Flags {
SIZELIKE_UNIFORM = (1 << 5),
} eCopyScale_Flags;
/* bTransLikeConstraint.flag */
typedef enum eCopyTransforms_Flags {
/* Invert the transformation matrix. */
TRANSLIKE_INVERT = (1 << 0),
/* Remove shear from the target matrix. */
TRANSLIKE_FIX_TARGET_SHEAR = (1 << 1),
} eCopyTransforms_Flags;
/* bTransLikeConstraint.mix_mode */
typedef enum eCopyTransforms_MixMode {
/* Replace rotation channel values. */
@@ -809,6 +831,14 @@ typedef enum eCopyTransforms_MixMode {
TRANSLIKE_MIX_BEFORE = 1,
/* Multiply the copied transformation on the right, with anti-shear scale handling. */
TRANSLIKE_MIX_AFTER = 2,
/* Multiply the copied transformation on the left, handling loc/rot/scale separately. */
TRANSLIKE_MIX_BEFORE_SPLIT = 3,
/* Multiply the copied transformation on the right, handling loc/rot/scale separately. */
TRANSLIKE_MIX_AFTER_SPLIT = 4,
/* Multiply the copied transformation on the left, using simple matrix multiplication. */
TRANSLIKE_MIX_BEFORE_FULL = 5,
/* Multiply the copied transformation on the right, using simple matrix multiplication. */
TRANSLIKE_MIX_AFTER_FULL = 6,
} eCopyTransforms_MixMode;
/* bTransformConstraint.to/from */

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,
@@ -218,6 +224,12 @@ static const EnumPropertyItem target_space_pchan_items[] = {
"Local Space",
"The transformation of the target is evaluated relative to its local "
"coordinate system"},
{CONSTRAINT_SPACE_OWNLOCAL,
"OWNER_LOCAL",
0,
"Owner Local Space",
"The local transformation of the target bone is evaluated relative to its local coordinate "
"system, as if the target and owner bones had the same orientation in their rest pose"},
{0, NULL, 0, NULL, NULL},
};
@@ -227,6 +239,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 +292,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 +605,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->flag & 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;
@@ -1607,18 +1625,49 @@ static void rna_def_constraint_transform_like(BlenderRNA *brna)
0,
"Replace",
"Replace the original transformation with copied"},
{0, "", 0, NULL, NULL},
{TRANSLIKE_MIX_BEFORE_FULL,
"BEFORE_FULL",
0,
"Before Original (Full)",
"Apply copied transformation before original, using simple matrix multiplication as if "
"the constraint target is a parent in Full Inherit Scale mode. "
"Will create shear when combining rotation and non-uniform scale"},
{TRANSLIKE_MIX_AFTER_FULL,
"AFTER_FULL",
0,
"After Original (Full)",
"Apply copied transformation after original, using simple matrix multiplication as if "
"the constraint target is a child in Full Inherit Scale mode. "
"Will create shear when combining rotation and non-uniform scale"},
{0, "", 0, NULL, NULL},
{TRANSLIKE_MIX_BEFORE,
"BEFORE",
0,
"Before Original",
"Apply copied transformation before original, as if the constraint target is a parent. "
"Scale is handled specially to avoid creating shear"},
"Before Original (Aligned)",
"Apply copied transformation before original, as if the constraint target is a parent in "
"Aligned Inherit Scale mode. This effectively uses Full for location and Split Channels "
"for rotation and scale"},
{TRANSLIKE_MIX_AFTER,
"AFTER",
0,
"After Original",
"Apply copied transformation after original, as if the constraint target is a child. "
"Scale is handled specially to avoid creating shear"},
"After Original (Aligned)",
"Apply copied transformation after original, as if the constraint target is a child in "
"Aligned Inherit Scale mode. This effectively uses Full for location and Split Channels "
"for rotation and scale"},
{0, "", 0, NULL, NULL},
{TRANSLIKE_MIX_BEFORE_SPLIT,
"BEFORE_SPLIT",
0,
"Before Original (Split Channels)",
"Apply copied transformation before original, handling location, rotation and scale "
"separately, similar to a sequence of three Copy constraints"},
{TRANSLIKE_MIX_AFTER_SPLIT,
"AFTER_SPLIT",
0,
"After Original (Split Channels)",
"Apply copied transformation after original, handling location, rotation and scale "
"separately, similar to a sequence of three Copy constraints"},
{0, NULL, 0, NULL, NULL},
};
@@ -1636,6 +1685,20 @@ static void rna_def_constraint_transform_like(BlenderRNA *brna)
RNA_define_lib_overridable(true);
prop = RNA_def_property(srna, "invert", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", TRANSLIKE_INVERT);
RNA_def_property_ui_text(prop,
"Invert",
"Invert the target transformation. In Aligned or Split Channels mix "
"modes channels are inverted separately");
RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
prop = RNA_def_property(srna, "fix_target_shear", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", TRANSLIKE_FIX_TARGET_SHEAR);
RNA_def_property_ui_text(
prop, "Fix Target Shear", "Remove shear from the target transformation");
RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
prop = RNA_def_property(srna, "mix_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "mix_mode");
RNA_def_property_enum_items(prop, mix_mode_items);
@@ -2580,6 +2643,12 @@ static void rna_def_constraint_rotation_limit(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Maximum Z", "Highest Z value to allow");
RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
prop = RNA_def_property(srna, "euler_order", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "euler_order");
RNA_def_property_enum_items(prop, euler_order_items);
RNA_def_property_ui_text(prop, "Euler Order", "Explicitly specify the euler rotation order");
RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
prop = RNA_def_property(srna, "use_transform_limit", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag2", LIMIT_TRANSFORM);
RNA_def_property_ui_text(
@@ -3398,6 +3467,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

@@ -303,6 +303,9 @@ static void rna_Object_mat_convert_space(Object *ob,
{
copy_m4_m4((float(*)[4])mat_ret, (float(*)[4])mat);
BLI_assert(!ELEM(from, CONSTRAINT_SPACE_OWNLOCAL));
BLI_assert(!ELEM(to, CONSTRAINT_SPACE_OWNLOCAL));
/* Error in case of invalid from/to values when pchan is NULL */
if (pchan == NULL) {
if (ELEM(from, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_PARLOCAL)) {
@@ -324,8 +327,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