diff options
-rw-r--r-- | source/blender/blenkernel/intern/constraint.c | 128 | ||||
-rw-r--r-- | source/blender/editors/object/object_constraint.c | 136 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_constraint_types.h | 4 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_constraint.c | 13 | ||||
-rw-r--r-- | tests/python/bl_constraints.py | 35 |
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), ))) |