Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSybren A. Stüvel <sybren@blender.org>2020-02-27 12:24:11 +0300
committerSybren A. Stüvel <sybren@blender.org>2020-02-27 12:37:59 +0300
commit10162d68e38514aa8741cf9efd9e0f5deac32085 (patch)
treed78cfe4761c1fef9ef7af434eed0551158c6ff55
parent4952fb1669fb7d254fc26a527b6dc2a44ac2c496 (diff)
Constraints: replace 'Set Inverse' operator with an eval-time update
This fixes {T70269}. Before this commit there was complicated code to try and compute the correct parent inverse matrix for the 'Child Of' and 'Object Solver' constraints outside the constraint evaluation. This was done mostly correctly, but did have some issues. The Set Inverse operator now defers this computation to be performed during constraint evaluation by just setting a flag. If the constraint is disabled, and thus tagging it for update in the depsgraph is not enough to trigger immediate evaluation, evaluation is forced by temporarily enabling it. This fix changes the way how the inverse matrix works when some of the channels of the constraint are disabled. Before this commit, the channel flags were used to filter both the parent and the inverse matrix. This meant that it was impossible to make an inverse matrix that would actually fully neutralize the effect of the constraint. Now only the parent matrix is filtered, while inverse is applied fully. As a result, pressing the 'Set Inverse' matrix produces the same transformation as disabling the constraint. This is also reflected in the changed values in the 'Child Of' unit test. This change is not backward compatible, but it should be OK because the old way was effectively unusable, so it is unlikely anybody relied on it. The change in matrix for the Object Solver constraint is due to a different method of computing it, which caused a slightly different floating point error that was slightly bigger than allowed by the test, so I updated the matrix values there as well. This patch was original written by @angavrilov and subsequently updated by me. Differential Revision: https://developer.blender.org/D6091
-rw-r--r--source/blender/blenkernel/intern/constraint.c128
-rw-r--r--source/blender/editors/object/object_constraint.c136
-rw-r--r--source/blender/makesdna/DNA_constraint_types.h4
-rw-r--r--source/blender/makesrna/intern/rna_constraint.c13
-rw-r--r--tests/python/bl_constraints.py35
5 files changed, 135 insertions, 181 deletions
diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c
index 8ba746e3493..26dea11624b 100644
--- a/source/blender/blenkernel/intern/constraint.c
+++ b/source/blender/blenkernel/intern/constraint.c
@@ -860,94 +860,88 @@ static void childof_evaluate(bConstraint *con, bConstraintOb *cob, ListBase *tar
}
float parmat[4][4];
-
- /* simple matrix parenting */
- if (data->flag == CHILDOF_ALL) {
-
- /* multiply target (parent matrix) by offset (parent inverse) to get
- * the effect of the parent that will be exerted on the owner
- */
- mul_m4_m4m4(parmat, ct->matrix, data->invmat);
-
- /* now multiply the parent matrix by the owner matrix to get the
- * the effect of this constraint (i.e. owner is 'parented' to parent)
- */
- mul_m4_m4m4(cob->matrix, parmat, cob->matrix);
+ /* Simple matrix parenting. */
+ if ((data->flag & CHILDOF_ALL) == CHILDOF_ALL) {
+ copy_m4_m4(parmat, ct->matrix);
}
+ /* Filter the parent matrix by channel. */
else {
- float invmat[4][4], tempmat[4][4];
float loc[3], eul[3], size[3];
- float loco[3], eulo[3], sizo[3];
-
- /* get offset (parent-inverse) matrix */
- copy_m4_m4(invmat, data->invmat);
/* extract components of both matrices */
copy_v3_v3(loc, ct->matrix[3]);
mat4_to_eulO(eul, ct->rotOrder, ct->matrix);
mat4_to_size(size, ct->matrix);
- copy_v3_v3(loco, invmat[3]);
- mat4_to_eulO(eulo, cob->rotOrder, invmat);
- mat4_to_size(sizo, invmat);
-
/* disable channels not enabled */
if (!(data->flag & CHILDOF_LOCX)) {
- loc[0] = loco[0] = 0.0f;
+ loc[0] = 0.0f;
}
if (!(data->flag & CHILDOF_LOCY)) {
- loc[1] = loco[1] = 0.0f;
+ loc[1] = 0.0f;
}
if (!(data->flag & CHILDOF_LOCZ)) {
- loc[2] = loco[2] = 0.0f;
+ loc[2] = 0.0f;
}
if (!(data->flag & CHILDOF_ROTX)) {
- eul[0] = eulo[0] = 0.0f;
+ eul[0] = 0.0f;
}
if (!(data->flag & CHILDOF_ROTY)) {
- eul[1] = eulo[1] = 0.0f;
+ eul[1] = 0.0f;
}
if (!(data->flag & CHILDOF_ROTZ)) {
- eul[2] = eulo[2] = 0.0f;
+ eul[2] = 0.0f;
}
if (!(data->flag & CHILDOF_SIZEX)) {
- size[0] = sizo[0] = 1.0f;
+ size[0] = 1.0f;
}
if (!(data->flag & CHILDOF_SIZEY)) {
- size[1] = sizo[1] = 1.0f;
+ size[1] = 1.0f;
}
if (!(data->flag & CHILDOF_SIZEZ)) {
- size[2] = sizo[2] = 1.0f;
+ size[2] = 1.0f;
}
/* make new target mat and offset mat */
- loc_eulO_size_to_mat4(ct->matrix, loc, eul, size, ct->rotOrder);
- loc_eulO_size_to_mat4(invmat, loco, eulo, sizo, cob->rotOrder);
+ loc_eulO_size_to_mat4(parmat, loc, eul, size, ct->rotOrder);
+ }
- /* multiply target (parent matrix) by offset (parent inverse) to get
- * the effect of the parent that will be exerted on the owner
- */
- mul_m4_m4m4(parmat, ct->matrix, invmat);
+ /* Compute the inverse matrix if requested. */
+ if (data->flag & CHILDOF_SET_INVERSE) {
+ invert_m4_m4(data->invmat, parmat);
- /* now multiply the parent matrix by the owner matrix to get the
- * the effect of this constraint (i.e. owner is 'parented' to parent)
- */
- copy_m4_m4(tempmat, cob->matrix);
- mul_m4_m4m4(cob->matrix, parmat, tempmat);
+ data->flag &= ~CHILDOF_SET_INVERSE;
- /* without this, changes to scale and rotation can change location
- * of a parentless bone or a disconnected bone. Even though its set
- * to zero above. */
- if (!(data->flag & CHILDOF_LOCX)) {
- cob->matrix[3][0] = tempmat[3][0];
- }
- if (!(data->flag & CHILDOF_LOCY)) {
- cob->matrix[3][1] = tempmat[3][1];
- }
- if (!(data->flag & CHILDOF_LOCZ)) {
- cob->matrix[3][2] = tempmat[3][2];
+ /* Write the computed matrix back to the master copy if in COW evaluation. */
+ bConstraint *orig_con = constraint_find_original_for_update(cob, con);
+
+ if (orig_con != NULL) {
+ bChildOfConstraint *orig_data = orig_con->data;
+
+ copy_m4_m4(orig_data->invmat, data->invmat);
+ orig_data->flag &= ~CHILDOF_SET_INVERSE;
}
}
+
+ /* Multiply together the target (parent) matrix, parent inverse,
+ * and the owner transform matrixto get the effect of this constraint
+ * (i.e. owner is 'parented' to parent). */
+ float orig_cob_matrix[4][4];
+ copy_m4_m4(orig_cob_matrix, cob->matrix);
+ mul_m4_series(cob->matrix, parmat, data->invmat, orig_cob_matrix);
+
+ /* Without this, changes to scale and rotation can change location
+ * of a parentless bone or a disconnected bone. Even though its set
+ * to zero above. */
+ if (!(data->flag & CHILDOF_LOCX)) {
+ cob->matrix[3][0] = orig_cob_matrix[3][0];
+ }
+ if (!(data->flag & CHILDOF_LOCY)) {
+ cob->matrix[3][1] = orig_cob_matrix[3][1];
+ }
+ if (!(data->flag & CHILDOF_LOCZ)) {
+ cob->matrix[3][2] = orig_cob_matrix[3][2];
+ }
}
/* XXX note, con->flag should be CONSTRAINT_SPACEONCE for bone-childof, patched in readfile.c */
@@ -4891,23 +4885,35 @@ static void objectsolver_evaluate(bConstraint *con, bConstraintOb *cob, ListBase
return;
}
- float mat[4][4], obmat[4][4], imat[4][4], cammat[4][4], camimat[4][4], parmat[4][4];
+ float mat[4][4], obmat[4][4], imat[4][4], parmat[4][4];
float ctime = DEG_get_ctime(depsgraph);
float framenr = BKE_movieclip_remap_scene_to_clip_frame(clip, ctime);
- BKE_object_where_is_calc_mat4(camob, cammat);
-
BKE_tracking_camera_get_reconstructed_interpolate(tracking, object, framenr, mat);
- invert_m4_m4(camimat, cammat);
- mul_m4_m4m4(parmat, cammat, data->invmat);
+ invert_m4_m4(imat, mat);
+ mul_m4_m4m4(parmat, camob->obmat, imat);
- copy_m4_m4(cammat, camob->obmat);
copy_m4_m4(obmat, cob->matrix);
- invert_m4_m4(imat, mat);
+ /* Recalculate the inverse matrix if requested. */
+ if (data->flag & OBJECTSOLVER_SET_INVERSE) {
+ invert_m4_m4(data->invmat, parmat);
+
+ data->flag &= ~OBJECTSOLVER_SET_INVERSE;
+
+ /* Write the computed matrix back to the master copy if in COW evaluation. */
+ bConstraint *orig_con = constraint_find_original_for_update(cob, con);
+
+ if (orig_con != NULL) {
+ bObjectSolverConstraint *orig_data = orig_con->data;
+
+ copy_m4_m4(orig_data->invmat, data->invmat);
+ orig_data->flag &= ~OBJECTSOLVER_SET_INVERSE;
+ }
+ }
- mul_m4_series(cob->matrix, cammat, imat, camimat, parmat, obmat);
+ mul_m4_series(cob->matrix, parmat, data->invmat, obmat);
}
static bConstraintTypeInfo CTI_OBJECTSOLVER = {
diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c
index 906a9e44870..bfff36bb83a 100644
--- a/source/blender/editors/object/object_constraint.c
+++ b/source/blender/editors/object/object_constraint.c
@@ -871,118 +871,31 @@ void CONSTRAINT_OT_limitdistance_reset(wmOperatorType *ot)
/* ------------- Child-Of Constraint ------------------ */
-static void child_get_inverse_matrix_owner_bone(
- Depsgraph *depsgraph, wmOperator *op, Scene *scene, Object *ob, float invmat[4][4])
+/* Force evaluation so that the 'set inverse' flag is handled.
+ * No-op when the constraint is enabled, as in such cases the evaluation will happen anyway.
+ */
+static void force_evaluation_if_constraint_disabled(bContext *C, Object *ob, bConstraint *con)
{
- /* For bone owner we want to do this in evaluated domain.
- * BKE_pose_where_is / BKE_pose_where_is_bone relies on (re)evaluating parts of the scene
- * and copying new evaluated stuff back to original.
- */
- Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob);
- bConstraint *con_eval = edit_constraint_property_get(op, ob_eval, CONSTRAINT_TYPE_CHILDOF);
-
- /* nullify inverse matrix first */
- unit_m4(invmat);
-
- bPoseChannel *pchan_eval = BKE_pose_channel_active(ob_eval);
-
- /* try to find a pose channel - assume that this is the constraint owner */
- /* TODO: get from context instead? */
- if (ob_eval && ob_eval->pose && pchan_eval) {
- bConstraint *con_last;
-
- /* calculate/set inverse matrix:
- * We just calculate all transform-stack eval up to but not including this constraint.
- * This is because inverse should just inverse correct for just the constraint's influence
- * when it gets applied; that is, at the time of application, we don't know anything about
- * what follows.
- */
- float imat[4][4], tmat[4][4];
- float pmat[4][4];
-
- /* make sure we passed the correct constraint */
- BLI_assert(BLI_findindex(&pchan_eval->constraints, con_eval) != -1);
-
- /* 1. calculate posemat where inverse doesn't exist yet (inverse was cleared above),
- * to use as baseline ("pmat") to derive delta from. This extra calc saves users
- * from having pressing "Clear Inverse" first
- */
- BKE_pose_where_is(depsgraph, scene, ob_eval);
- copy_m4_m4(pmat, pchan_eval->pose_mat);
-
- /* 2. knock out constraints starting from this one */
- con_last = pchan_eval->constraints.last;
- pchan_eval->constraints.last = con_eval->prev;
-
- if (con_eval->prev) {
- /* new end must not point to this one, else this chain cutting is useless */
- con_eval->prev->next = NULL;
- }
- else {
- /* constraint was first */
- pchan_eval->constraints.first = NULL;
- }
-
- /* 3. solve pose without disabled constraints */
- BKE_pose_where_is(depsgraph, scene, ob_eval);
-
- /* 4. determine effect of constraint by removing the newly calculated
- * pchan->pose_mat from the original pchan->pose_mat, thus determining
- * the effect of the constraint
- */
- invert_m4_m4(imat, pchan_eval->pose_mat);
- mul_m4_m4m4(tmat, pmat, imat);
- invert_m4_m4(invmat, tmat);
-
- /* 5. restore constraints */
- pchan_eval->constraints.last = con_last;
-
- if (con_eval->prev) {
- /* hook up prev to this one again */
- con_eval->prev->next = con_eval;
- }
- else {
- /* set as first again */
- pchan_eval->constraints.first = con_eval;
- }
-
- /* 6. recalculate pose with new inv-mat applied */
- /* this one is unnecessary? (DEG seems to update correctly without)
- + if we leave this in, we have to click "Set Inverse" twice to see updates...
- BKE_pose_where_is(depsgraph, scene, ob_eval); */
+ if ((con->flag & (CONSTRAINT_DISABLE | CONSTRAINT_OFF)) == 0) {
+ return;
}
-}
-
-static void child_get_inverse_matrix_owner_object(
- Depsgraph *depsgraph, Scene *scene, Object *ob, bConstraint *con, float invmat[4][4])
-{
- /* nullify inverse matrix first */
- unit_m4(invmat);
-
- if (ob) {
- Object workob;
-
- /* make sure we passed the correct constraint */
- BLI_assert(BLI_findindex(&ob->constraints, con) != -1);
- UNUSED_VARS_NDEBUG(con);
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Scene *scene = DEG_get_evaluated_scene(depsgraph);
- /* use BKE_object_workob_calc_parent to find inverse - just like for normal parenting */
- BKE_object_workob_calc_parent(depsgraph, scene, ob, &workob);
- invert_m4_m4(invmat, workob.obmat);
- }
+ short flag_backup = con->flag;
+ con->flag &= ~(CONSTRAINT_DISABLE | CONSTRAINT_OFF);
+ BKE_object_eval_constraints(depsgraph, scene, ob);
+ con->flag = flag_backup;
}
/* ChildOf Constraint - set inverse callback */
static int childof_set_inverse_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
- Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- Scene *scene = CTX_data_scene(C);
Object *ob = ED_object_active_context(C);
bConstraint *con = edit_constraint_property_get(op, ob, CONSTRAINT_TYPE_CHILDOF);
bChildOfConstraint *data = (con) ? (bChildOfConstraint *)con->data : NULL;
- const int owner = RNA_enum_get(op->ptr, "owner");
/* despite 3 layers of checks, we may still not be able to find a constraint */
if (data == NULL) {
@@ -991,12 +904,11 @@ static int childof_set_inverse_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- if (owner == EDIT_CONSTRAINT_OWNER_OBJECT) {
- child_get_inverse_matrix_owner_object(depsgraph, scene, ob, con, data->invmat);
- }
- else if (owner == EDIT_CONSTRAINT_OWNER_BONE) {
- child_get_inverse_matrix_owner_bone(depsgraph, op, scene, ob, data->invmat);
- }
+ /* Set a flag to request recalculation on next update. */
+ data->flag |= CHILDOF_SET_INVERSE;
+
+ /* Force constraint to run, it will perform the recalculation. */
+ force_evaluation_if_constraint_disabled(C, ob, con);
ED_object_constraint_update(bmain, ob);
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob);
@@ -1231,12 +1143,9 @@ void CONSTRAINT_OT_followpath_path_animate(wmOperatorType *ot)
static int objectsolver_set_inverse_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
- Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- Scene *scene = CTX_data_scene(C);
Object *ob = ED_object_active_context(C);
bConstraint *con = edit_constraint_property_get(op, ob, CONSTRAINT_TYPE_OBJECTSOLVER);
bObjectSolverConstraint *data = (con) ? (bObjectSolverConstraint *)con->data : NULL;
- const int owner = RNA_enum_get(op->ptr, "owner");
/* despite 3 layers of checks, we may still not be able to find a constraint */
if (data == NULL) {
@@ -1246,12 +1155,11 @@ static int objectsolver_set_inverse_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- if (owner == EDIT_CONSTRAINT_OWNER_OBJECT) {
- child_get_inverse_matrix_owner_object(depsgraph, scene, ob, con, data->invmat);
- }
- else if (owner == EDIT_CONSTRAINT_OWNER_BONE) {
- child_get_inverse_matrix_owner_bone(depsgraph, op, scene, ob, data->invmat);
- }
+ /* Set a flag to request recalculation on next update. */
+ data->flag |= OBJECTSOLVER_SET_INVERSE;
+
+ /* Force constraint to run, it will perform the recalculation. */
+ force_evaluation_if_constraint_disabled(C, ob, con);
ED_object_constraint_update(bmain, ob);
WM_event_add_notifier(C, NC_OBJECT | ND_CONSTRAINT, ob);
diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h
index 6caa0ac5a74..670a6c46187 100644
--- a/source/blender/makesdna/DNA_constraint_types.h
+++ b/source/blender/makesdna/DNA_constraint_types.h
@@ -1096,6 +1096,8 @@ typedef enum eChildOf_Flags {
CHILDOF_SIZEY = (1 << 7),
CHILDOF_SIZEZ = (1 << 8),
CHILDOF_ALL = 511,
+ /* Temporary flag used by the Set Inverse operator. */
+ CHILDOF_SET_INVERSE = (1 << 9),
} eChildOf_Flags;
/* Pivot Constraint */
@@ -1147,6 +1149,8 @@ typedef enum eCameraSolver_Flags {
/* ObjectSolver Constraint -> flag */
typedef enum eObjectSolver_Flags {
OBJECTSOLVER_ACTIVECLIP = (1 << 0),
+ /* Temporary flag used by the Set Inverse operator. */
+ OBJECTSOLVER_SET_INVERSE = (1 << 1),
} eObjectSolver_Flags;
/* ObjectSolver Constraint -> flag */
diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c
index 8e57de9baeb..64e6fc9059c 100644
--- a/source/blender/makesrna/intern/rna_constraint.c
+++ b/source/blender/makesrna/intern/rna_constraint.c
@@ -973,11 +973,18 @@ static void rna_def_constraint_childof(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Scale Z", "Use Z Scale of Parent");
RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
+ prop = RNA_def_property(srna, "set_inverse_pending", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", CHILDOF_SET_INVERSE);
+ RNA_def_property_ui_text(
+ prop, "Set Inverse Pending", "Set to true to request recalculation of the inverse matrix");
+ RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
+
prop = RNA_def_property(srna, "inverse_matrix", PROP_FLOAT, PROP_MATRIX);
RNA_def_property_float_sdna(prop, NULL, "invmat");
RNA_def_property_multi_array(prop, 2, rna_matrix_dimsize_4x4);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_ui_text(prop, "Inverse Matrix", "Transformation matrix to apply before");
+ RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
}
static void rna_def_constraint_python(BlenderRNA *brna)
@@ -3152,6 +3159,12 @@ static void rna_def_constraint_object_solver(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Active Clip", "Use active clip defined in scene");
RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
+ prop = RNA_def_property(srna, "set_inverse_pending", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", OBJECTSOLVER_SET_INVERSE);
+ RNA_def_property_ui_text(
+ prop, "Set Inverse Pending", "Set to true to request recalculation of the inverse matrix");
+ RNA_def_property_update(prop, NC_OBJECT | ND_CONSTRAINT, "rna_Constraint_update");
+
/* object */
prop = RNA_def_property(srna, "object", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, NULL, "object");
diff --git a/tests/python/bl_constraints.py b/tests/python/bl_constraints.py
index 42f81bddce6..13a431541bc 100644
--- a/tests/python/bl_constraints.py
+++ b/tests/python/bl_constraints.py
@@ -156,9 +156,9 @@ class ChildOfTest(AbstractConstraintTests):
context = self.constraint_context('Child Of', owner_name='Child Of.object.owner')
bpy.ops.constraint.childof_set_inverse(context, constraint='Child Of')
self.matrix_test('Child Of.object.owner', Matrix((
- (0.9228900671005249, 0.23250490427017212, -0.035540513694286346, 0.10000000149011612),
- (-0.011224273592233658, 0.9838480949401855, 0.24731633067131042, 0.21246682107448578),
- (0.0383986234664917, -0.3163823187351227, 0.9553266167640686, 0.27248233556747437),
+ (0.9992386102676392, 0.019843991845846176, -0.03359176218509674, 0.10000000149011612),
+ (-0.017441775649785995, 0.997369647026062, 0.0703534483909607, 0.2000001221895218),
+ (0.034899499267339706, -0.06971398741006851, 0.996956467628479, 0.3000001311302185),
(0.0, 0.0, 0.0, 1.0),
)))
@@ -188,6 +188,29 @@ class ChildOfTest(AbstractConstraintTests):
bpy.ops.constraint.childof_clear_inverse(context, constraint='Child Of')
self.matrix_test('Child Of.armature.owner', initial_matrix)
+ def test_vertexgroup_simple_parent(self):
+ """Child Of: simple evaluation of vertex group parent."""
+ initial_matrix = Matrix((
+ (-0.8076590895652771, 0.397272527217865, 0.4357309341430664, 1.188504934310913),
+ (-0.4534659683704376, -0.8908230066299438, -0.028334975242614746, 1.7851561307907104),
+ (0.3769024908542633, -0.22047416865825653, 0.8996308445930481, 3.4457669258117676),
+ (0.0, 0.0, 0.0, 1.0),
+ ))
+ self.matrix_test('Child Of.vertexgroup.owner', initial_matrix)
+
+ context = self.constraint_context('Child Of', owner_name='Child Of.vertexgroup.owner')
+ bpy.ops.constraint.childof_set_inverse(context, constraint='Child Of')
+
+ self.matrix_test('Child Of.vertexgroup.owner', Matrix((
+ (0.9992386102676392, 0.019843988120555878, -0.03359176218509674, 0.10000000149011612),
+ (-0.017441775649785995, 0.997369647026062, 0.0703534483909607, 0.20000000298023224),
+ (0.03489949554204941, -0.06971397995948792, 0.9969563484191895, 0.30000001192092896),
+ (0.0, 0.0, 0.0, 1.0),
+ )))
+
+ bpy.ops.constraint.childof_clear_inverse(context, constraint='Child Of')
+ self.matrix_test('Child Of.vertexgroup.owner', initial_matrix)
+
class ObjectSolverTest(AbstractConstraintTests):
layer_collection = 'Object Solver'
@@ -205,9 +228,9 @@ class ObjectSolverTest(AbstractConstraintTests):
context = self.constraint_context('Object Solver')
bpy.ops.constraint.objectsolver_set_inverse(context, constraint='Object Solver')
self.matrix_test('Object Solver.owner', Matrix((
- (0.9992386102676392, 0.019843988120555878, -0.03359176218509674, 0.10000000149011612),
- (-0.017441775649785995, 0.997369647026062, 0.0703534483909607, 0.20000000298023224),
- (0.03489949554204941, -0.06971397995948792, 0.9969563484191895, 0.30000001192092896),
+ (0.9992387294769287, 0.019843989983201027, -0.03359176591038704, 0.10000025480985641),
+ (-0.017441747710108757, 0.9973697662353516, 0.07035345584154129, 0.1999993920326233),
+ (0.034899502992630005, -0.06971398741006851, 0.996956467628479, 0.29999980330467224),
(0.0, 0.0, 0.0, 1.0),
)))