Attempted bugfix to improve behaviour of inverse calculations for ChildOf
authorJoshua Leung <aligorith@gmail.com>
Tue, 22 Nov 2011 11:51:12 +0000 (11:51 +0000)
committerJoshua Leung <aligorith@gmail.com>
Tue, 22 Nov 2011 11:51:12 +0000 (11:51 +0000)
Constraint (bones case only for now). See [#29100] for some problematic test
cases

* It is no longer necessary to have to press "Clear Inverse" before doing "Set
Inverse" to get a reliable result. The calculation now calculates the pose with
the inverse cleared as it's "baseline" first now, which has the same result as
clearing the inverse manually first.

* Also, this commit trials a new method for computing inverses when there is
more than one constraint on the bone. Previously it just temporarily muted the
offending constraint, but kept all following constraints active, thus
potentially including their results in the inverse calculation. However, since
the inverse should only really care about what the stack looked like up to and
including when the constraint is applied, all constraints starting from this one
onwards are now disabled when calculating the inverse.  PLEASE TEST!!! In the
few tests I've done, the effects appear minimal, but this may be quite an issue
in the wild

source/blender/editors/object/object_constraint.c

index 3c84b2df1d2b61730880cf5d4610e3342f0b428a..40d52e0b2618e32a7777534186121c566a5d976a 100644 (file)
@@ -691,6 +691,7 @@ static int childof_set_inverse_exec (bContext *C, wmOperator *op)
        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;
+       bConstraint *lastcon = NULL;
        bPoseChannel *pchan= NULL;
        
        /* despite 3 layers of checks, we may still not be able to find a constraint */
@@ -703,27 +704,45 @@ static int childof_set_inverse_exec (bContext *C, wmOperator *op)
        /* nullify inverse matrix first */
        unit_m4(data->invmat);
        
-       /* try to find a pose channel */
+       /* try to find a pose channel - assume that this is the constraint owner */
        // TODO: get from context instead?
        if (ob && ob->pose)
                pchan= get_active_posechannel(ob);
        
-       /* calculate/set inverse matrix */
+       /* 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.
+        */
        if (pchan) {
-               float pmat[4][4], cinf;
                float imat[4][4], tmat[4][4];
+               float pmat[4][4];
                
-               /* make copy of pchan's original pose-mat (for use later) */
+               /* 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
+                */
+               where_is_pose(scene, ob);
                copy_m4_m4(pmat, pchan->pose_mat);
                
-               /* disable constraint for pose to be solved without it */
-               cinf= con->enforce;
-               con->enforce= 0.0f;
+               /* 2. knock out constraints starting from this one */
+               lastcon = pchan->constraints.last;
+               pchan->constraints.last = con->prev;
+               
+               if (con->prev) {
+                       /* new end must not point to this one, else this chain cutting is useless */
+                       con->prev->next = NULL;
+               }
+               else {
+                       /* constraint was first */
+                       pchan->constraints.first = NULL;
+               }
                
-               /* solve pose without constraint */
+               /* 3. solve pose without disabled constraints */
                where_is_pose(scene, ob);
                
-               /* determine effect of constraint by removing the newly calculated 
+               /* 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
                 */
@@ -731,8 +750,19 @@ static int childof_set_inverse_exec (bContext *C, wmOperator *op)
                mul_m4_m4m4(tmat, imat, pmat);
                invert_m4_m4(data->invmat, tmat);
                
-               /* recalculate pose with new inv-mat */
-               con->enforce= cinf;
+               /* 5. restore constraints */
+               pchan->constraints.last = lastcon;
+               
+               if (con->prev) {
+                       /* hook up prev to this one again */
+                       con->prev->next = con;
+               }
+               else {
+                       /* set as first again */
+                       pchan->constraints.first = con;
+               }
+               
+               /* 6. recalculate pose with new inv-mat applied */
                where_is_pose(scene, ob);
        }
        else if (ob) {