Separating armatures should now be more stable than it was, but is still too unstable...
authorJoshua Leung <aligorith@gmail.com>
Sun, 11 May 2008 11:53:12 +0000 (11:53 +0000)
committerJoshua Leung <aligorith@gmail.com>
Sun, 11 May 2008 11:53:12 +0000 (11:53 +0000)
source/blender/src/editarmature.c

index 99c87097bd35d4e6b767a58a5370e2d3f80ed314..14c81ef1bf59e24e4402f7fc46d43069e6a5adb1 100644 (file)
@@ -180,7 +180,7 @@ void make_boneList(ListBase *list, ListBase *bones, EditBone *parent)
                
                /*      Add children if necessary */
                if (curBone->childbase.first) 
-                       make_boneList (list, &curBone->childbase, eBone);
+                       make_boneList(list, &curBone->childbase, eBone);
        }
 }
 
@@ -600,25 +600,24 @@ int join_armature(void)
                                                float delta[3];
                                                
                                                /* Get the premat */
-                                               VecSubf (delta, curbone->tail, curbone->head);
+                                               VecSubf(delta, curbone->tail, curbone->head);
                                                vec_roll_to_mat3(delta, curbone->roll, temp);
                                                
-                                               Mat4MulMat34 (premat, temp, mat);
+                                               Mat4MulMat34(premat, temp, mat);
                                                
                                                Mat4MulVecfl(mat, curbone->head);
                                                Mat4MulVecfl(mat, curbone->tail);
                                                
                                                /* Get the postmat */
-                                               VecSubf (delta, curbone->tail, curbone->head);
+                                               VecSubf(delta, curbone->tail, curbone->head);
                                                vec_roll_to_mat3(delta, curbone->roll, temp);
                                                Mat4CpyMat3(postmat, temp);
                                                
                                                /* Find the roll */
-                                               Mat4Invert (imat, premat);
-                                               Mat4MulMat4 (difmat, postmat, imat);
+                                               Mat4Invert(imat, premat);
+                                               Mat4MulMat4(difmat, postmat, imat);
                                                
                                                curbone->roll -= atan2(difmat[2][0], difmat[2][2]);
-                                               
                                        }
                                        
                                        /* Fix Constraints and Other Links to this Bone and Armature */
@@ -651,12 +650,15 @@ int join_armature(void)
 }
 
 /* Helper function for armature separating - link fixing */
-static void separated_armature_fix_links(Object *origArm, Object *newArm, ListBase *edbo)
+static void separated_armature_fix_links(Object *origArm, Object *newArm)
 {
        Object *ob;
-       bPoseChannel *pchan;
+       bPoseChannel *pchan, *pcha, *pchb;
        bConstraint *con;
-       EditBone *ebo, *ebn;
+       ListBase *npchans;
+       
+       /* get reference to list of bones in new armature  */
+       npchans= &newArm->pose->chanbase;
        
        /* let's go through all objects in database */
        for (ob= G.main->object.first; ob; ob= ob->id.next) {
@@ -675,27 +677,21 @@ static void separated_armature_fix_links(Object *origArm, Object *newArm, ListBa
                                                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 the original armature itself
-                                                        *      - the target is one of the bones which were moved into newArm
+                                                        *      - the target is one that can be found in newArm
                                                         */
                                                        if ((ct->tar == origArm) && (ct->subtarget[0] != 0)) {
-                                                               for (ebo=edbo->first, ebn=edbo->last; ebo && ebn; ebo=ebo->next, ebn=ebn->prev) {
+                                                               for (pcha=npchans->first, pchb=npchans->last; pcha && pchb; pcha=pcha->next, pchb=pchb->prev) {
                                                                        /* check if either one matches */
-                                                                       if ( (strcmp(ebo->name, ct->subtarget)==0) ||
-                                                                                (strcmp(ebn->name, ct->subtarget)==0) )
+                                                                       if ( (strcmp(pcha->name, ct->subtarget)==0) ||
+                                                                                (strcmp(pchb->name, ct->subtarget)==0) )
                                                                        {
                                                                                ct->tar= newArm;
-                                                                               printf("arm = '%s' pchan = '%s', ebo = '%s', YES \n", ob->id.name+2, pchan->name, ebo->name);
-                                                                               printf("arm = '%s' pchan = '%s', ebn = '%s', YES \n", ob->id.name+2, pchan->name, ebn->name);
                                                                                break;
                                                                        }
-                                                                       else {
-                                                                               printf("arm = '%s' pchan = '%s', ebo = '%s', NOT\n", ob->id.name+2, pchan->name, ebo->name);
-                                                                               printf("arm = '%s' pchan = '%s', ebn = '%s', NOT \n", ob->id.name+2, pchan->name, ebn->name);
-                                                                       }
                                                                        
                                                                        /* check if both ends have met (to stop checking) */
-                                                                       if (ebo == ebn) break;
-                                                               }
+                                                                       if (pcha == pchb) break;
+                                                               }                                                               
                                                        }
                                                }
                                                
@@ -723,17 +719,17 @@ static void separated_armature_fix_links(Object *origArm, Object *newArm, ListBa
                                                 *      - the target is one of the bones which were moved into newArm
                                                 */
                                                if ((ct->tar == origArm) && (ct->subtarget[0] != 0)) {
-                                                       for (ebo=edbo->first, ebn=edbo->last; ebo && ebn; ebo=ebo->next, ebn=ebn->prev) {
+                                                       for (pcha=npchans->first, pchb=npchans->last; pcha && pchb; pcha=pcha->next, pchb=pchb->prev) {
                                                                /* check if either one matches */
-                                                               if ( (strcmp(ebo->name, ct->subtarget)==0) ||
-                                                                        (strcmp(ebn->name, ct->subtarget)==0) )
+                                                               if ( (strcmp(pcha->name, ct->subtarget)==0) ||
+                                                                        (strcmp(pchb->name, ct->subtarget)==0) )
                                                                {
                                                                        ct->tar= newArm;
                                                                        break;
                                                                }
                                                                
                                                                /* check if both ends have met (to stop checking) */
-                                                               if (ebo == ebn) break;
+                                                               if (pcha == pchb) break;
                                                        }
                                                }
                                        }
@@ -749,34 +745,86 @@ static void separated_armature_fix_links(Object *origArm, Object *newArm, ListBa
                        /* Is object parented to a bone of this src armature? */
                        if (ob->partype==PARBONE) {
                                /* bone name in object */
-                               for (ebo=edbo->first, ebn=edbo->last; ebo && ebn; ebo=ebo->next, ebn=ebn->prev) {
+                               for (pcha=npchans->first, pchb=npchans->last; pcha && pchb; pcha=pcha->next, pchb=pchb->prev) {
                                        /* check if either one matches */
-                                       if ( (strcmp(ebo->name, ob->parsubstr)==0) ||
-                                                (strcmp(ebn->name, ob->parsubstr)==0) )
+                                       if ( (strcmp(pcha->name, ob->parsubstr)==0) ||
+                                                (strcmp(pchb->name, ob->parsubstr)==0) )
                                        {
                                                ob->parent= newArm;
                                                break;
                                        }
                                        
                                        /* check if both ends have met (to stop checking) */
-                                       if (ebo == ebn) break;
+                                       if (pcha == pchb) break;
                                }
                        }
                }
        }       
 }
 
+/* Helper function for armature separating - remove certain bones from the given armature 
+ *     sel: remove selected bones from the armature, otherwise the unselected bones are removed
+ */
+static void separate_armature_bones (Object *ob, short sel) 
+{
+       ListBase edbo = {NULL, NULL};
+       bArmature *arm= (bArmature *)ob->data;
+       bPoseChannel *pchan, *pchann;
+       EditBone *curbone;
+       
+       /* make local set of editbones to manipulate here */
+       make_boneList(&edbo, &arm->bonebase, NULL);
+       
+       /* go through pose-channels, checking if a bone should be removed */
+       for (pchan=ob->pose->chanbase.first; pchan; pchan=pchann) {
+               pchann= pchan->next;
+               curbone= editbone_name_exists(&edbo, pchan->name);
+               
+               /* check if bone needs to be removed */
+               if ( (sel && (curbone->flag & BONE_SELECTED)) ||
+                        (!sel && !(curbone->flag & BONE_SELECTED)) )
+               {
+                       EditBone *ebo;
+                       bPoseChannel *pchn;
+                       
+                       /* clear the bone->parent var of any bone that had this as its parent  */
+                       for (ebo= edbo.first; ebo; ebo= ebo->next) {
+                               if (ebo->parent == curbone) {
+                                       ebo->parent= NULL;
+                                       ebo->temp= NULL; /* this is needed to prevent random crashes with in editbones_to_armature */
+                                       ebo->flag &= ~BONE_CONNECTED;
+                               }
+                       }
+                       
+                       /* clear the pchan->parent var of any pchan that had this as its parent */
+                       for (pchn= ob->pose->chanbase.first; pchn; pchn=pchn->next) {
+                               if (pchn->parent == pchan)
+                                       pchn->parent= NULL;
+                       }
+                       
+                       /* free any of the extra-data this pchan might have */
+                       if (pchan->path) MEM_freeN(pchan->path);
+                       free_constraints(&pchan->constraints);
+                       
+                       /* get rid of unneeded bone */
+                       BLI_freelinkN(&edbo, curbone);
+                       BLI_freelinkN(&ob->pose->chanbase, pchan);
+               }
+       }
+       
+       /* exit editmode (recalculates pchans too) */
+       editbones_to_armature(&edbo, ob);
+       BLI_freelistN(&edbo);
+}
+
 void separate_armature (void)
 {
-       EditBone *ebo, *ebn;
-       Object *oldob;
-       Base *base, *oldbase;
+       Object *oldob, *newob;
+       Base *base, *oldbase, *newbase;
        bArmature *arm;
-       ListBase edbo = {NULL, NULL};
        
-       // 31Mar08 - Aligorith:
-       //      this tool is currently not ready for production use, as it will still 
-       //      crash in some cases, and also constraint relinking isn't working yet 
+       // 31 Mar 08 \ 11 May 08 - Aligorith:
+       // currently, this is still too unstable to be enabled for general consumption.
        // remove the following two lines to test this tool... you have been warned!
                okee("Not implemented (WIP)");
                return;
@@ -788,77 +836,58 @@ void separate_armature (void)
        
        arm= G.obedit->data;
        
-       /* we are going to trick everything as follows:
-        * 1. duplicate base: this is the new one,  remember old pointer
-        * 2. set aside all NOT selected bones
-        * 3. load_editArmature(): this will be the new base
-        * 4. freelist and restore old armature
+       /* we are going to do this as follows (unlike every other instance of separate):
+        *      1. exit editmode for active armature/base. Take note of what this is. 
+        *      2. duplicate base - BASACT is the new one now
+        *      3. for each of the two armatures, enter editmode -> remove appropriate bones -> exit editmode + recalc
+        *      4. fix constraint links
+        *      5. make original armature active and enter editmode
         */
        
-       /* only edit-base selected */
+       /* 1) only edit-base selected */
        base= FIRSTBASE;
-       while(base) {
-               if(base->lay & G.vd->lay) {
-                       if(base->object==G.obedit) base->flag |= 1;
+       for (base= FIRSTBASE; base; base= base->next) {
+               if (base->lay & G.vd->lay) {
+                       if (base->object==G.obedit) base->flag |= 1;
                        else base->flag &= ~1;
                }
-               base= base->next;
-       }
-
-       /* set aside: everything that is not selected */
-       for (ebo= G.edbo.first; ebo; ebo= ebn) {
-               ebn= ebo->next;
-               
-               /* remove from original, and move to duplicate if not-selected */
-               if ((ebo->flag & BONE_SELECTED)==0) {
-                       EditBone *curbone;
-                       
-                       /* need to make sure children don't still refer to this only if they are selected
-                        *      - potentially slow O(n*n) situation here... 
-                        */
-                       for (curbone= G.edbo.first; curbone; curbone=curbone->next) {
-                               if ((curbone->parent == ebo) && (curbone->flag & BONE_SELECTED)) {
-                                       curbone->parent= ebo->parent;
-                                       curbone->flag &= ~BONE_CONNECTED;
-                               }
-                       }
-                       
-                       BLI_remlink(&G.edbo, ebo);
-                       BLI_addtail(&edbo, ebo); 
-               }
        }
-
+       
+       /* 1) store starting settings and exit editmode */
        oldob= G.obedit;
        oldbase= BASACT;
-
-       adduplicate(1, 0); /* no transform and zero so do get a linked dupli */
        
-       G.obedit= BASACT->object;       /* basact is set in adduplicate() */
+       load_editArmature();
+       free_editArmature();
        
-       G.obedit->data= copy_armature(arm);
-       /* because new armature is a copy: reduce user count */
-       arm->id.us--;
+       /* 2) duplicate base */
+       adduplicate(1, USER_DUP_ARM); /* no transform and zero so do get a linked dupli */
        
-       load_editArmature();
+       newbase= BASACT; /* basact is set in adduplicate() */
+       newob= newbase->object;         
+       newbase->flag &= ~SELECT;
        
-       BASACT->flag &= ~SELECT;
        
-       /* fix links before depsgraph flushes */ // err... or after?
-       printf("oldob = %p, obact = %p \n", oldob, G.obedit);
-       separated_armature_fix_links(oldob, G.obedit, &G.edbo);
+       /* 3) remove bones that shouldn't still be around on both armatures */
+       separate_armature_bones(oldob, 1);
+       separate_armature_bones(newob, 0);
        
-       if (G.edbo.first) free_editArmature();
        
-       G.edbo = edbo;
+       /* 4) fix links before depsgraph flushes */ // err... or after?
+       separated_armature_fix_links(oldob, newob);
        
-       G.obedit= 0;    /* displists behave different in edit mode */ // needed?
-       DAG_object_flush_update(G.scene, OBACT, OB_RECALC_DATA);        /* this is the separated one */
        DAG_object_flush_update(G.scene, oldob, OB_RECALC_DATA);        /* this is the original one */
+       DAG_object_flush_update(G.scene, newob, OB_RECALC_DATA);        /* this is the separated one */
+       
        
+       /* 5) restore original conditions */
        G.obedit= oldob;
        BASACT= oldbase;
        BASACT->flag |= SELECT;
        
+       make_editArmature();
+       
+       /* recalc/redraw + cleanup */
        waitcursor(0);
 
        countall();
@@ -1442,9 +1471,10 @@ void delete_armature(void)
        
        for (curBone=G.edbo.first;curBone;curBone=next) {
                next=curBone->next;
-               if (arm->layer & curBone->layer)
+               if (arm->layer & curBone->layer) {
                        if (curBone->flag & BONE_SELECTED)
                                delete_bone(curBone);
+               }
        }