== Armature Joining Bugfixes ==
authorJoshua Leung <aligorith@gmail.com>
Tue, 16 Jan 2007 09:18:01 +0000 (09:18 +0000)
committerJoshua Leung <aligorith@gmail.com>
Tue, 16 Jan 2007 09:18:01 +0000 (09:18 +0000)
Since 2.40 (and a few pre-releases around then), armature joining has not
worked correctly. Constraints and other attributes of bones in posemode
(IK DOF limits, transform locks, custom shapes, etc.) were not preserved
on the armature(s) that were joined onto the last selected armature. This
was a serious production problem, as it meant that you could not easily
add pre-made rig segments and merge them with the rest of your rigs without
having to redo all the constraints. After a few attempts, I've finally
managed to fix this.

All constraints and parenting relationships get name corrections for the post-
merge armatures. Action channels in actions don't really get any corrections
yet unless the action is being used by an Action Constraint.

Python-API people: beware, I may have broken something in this commit.

source/blender/include/BIF_editarmature.h
source/blender/python/api2_2x/Armature.c
source/blender/python/api2_2x/Bone.c
source/blender/src/editarmature.c

index 9cdde5ffcb3d3ccc420db1e6e14306e22f120cd6..a48ca3bbe829fa1f01d84396e645be70c079ad6d 100644 (file)
@@ -36,6 +36,7 @@ struct Object;
 struct Base;
 struct Bone;
 struct bArmature;
+struct ListBase;
 
 typedef struct EditBone
 {
@@ -105,7 +106,7 @@ void        remake_editArmature(void);
 void   selectconnected_armature(void);
 void   selectconnected_posearmature(void);
 void   select_bone_parent(void);
-void    unique_editbone_name (char* name);
+void    unique_editbone_name (struct ListBase *ebones, char* name);
 
 void   auto_align_armature(void);
 void   create_vgroups_from_armature(Object *ob, Object *par);
index 2ccf0b7b77441ee9ebf9b0dec88882fc7f07adad..4aab0515587583edd46ee8e006776854c88c8956 100644 (file)
@@ -271,7 +271,7 @@ static int BonesDict_SetItem(BPy_BonesDict *self, PyObject *key, PyObject *value
                        //create a new editbone
                        editbone = MEM_callocN(sizeof(EditBone), "eBone");
                        BLI_strncpy(editbone->name, key_str, 32);
-                       unique_editbone_name(editbone->name);
+                       unique_editbone_name(NULL, editbone->name);
                        editbone->dist = ((BPy_EditBone*)value)->dist;
                        editbone->ease1 = ((BPy_EditBone*)value)->ease1;
                        editbone->ease2 = ((BPy_EditBone*)value)->ease2;
index e00ccd004771eca4d9ee2bef6e825fc236f570e6..0d4c0983a67049bbc32f0b8344a629af763a3b92 100644 (file)
@@ -782,7 +782,7 @@ static PyObject *EditBone_new(PyTypeObject *type, PyObject *args, PyObject *kwds
        //otherwise this will act as a py_object
        py_editBone->editbone = NULL;
 
-       unique_editbone_name(name);
+       unique_editbone_name(NULL, name);
        BLI_strncpy(py_editBone->name, name, 32);
        py_editBone->parent = NULL;
        py_editBone->weight= 1.0f;
index cc3d19295fac943797e39d08caa8b61cf9dba443..8f8b035348970f198745ffd3a1fd4a59cd090535 100644 (file)
@@ -107,6 +107,8 @@ extern      float centre[3], centroid[3];   /* Originally defined in editobject.c */
 /*     Macros  */
 #define TEST_EDITARMATURE {if(G.obedit==0) return; if( (G.vd->lay & G.obedit->lay)==0 ) return;}
 
+/* prototypes for later */
+static EditBone *editbone_name_exists (ListBase *ebones, char *name);  // proto for below
 
 /* **************** tools on Editmode Armature **************** */
 
@@ -426,12 +428,93 @@ void docentre_armature (Object *ob, int centremode)
        }
 }
 
+/* Helper function for armature joining - link fixing */
+static void joined_armature_fix_links(Object *tarArm, Object *srcArm, bPoseChannel *pchan, EditBone *curbone)
+{
+       Object *ob;
+       bPose *pose;
+       bPoseChannel *pchant;
+       bConstraint *con;
+       
+       /* let's go through all objects in database */
+       for (ob= G.main->object.first; ob; ob= ob->id.next) {
+               /* do some object-type specific things */
+               if (ob->type == OB_ARMATURE) {
+                       pose= ob->pose;
+                       for (pchant= pose->chanbase.first; pchant; pchant= pchant->next) {
+                               for (con= pchant->constraints.first; con; con= con->next) {
+                                       Object *conOb;
+                                       char *subtarget;
+                                       
+                                       /* constraint targets */
+                                       conOb= get_constraint_target(con, &subtarget);
+                                       if (conOb == srcArm) {
+                                               if (strcmp(subtarget, "")==0)
+                                                       set_constraint_target(con, tarArm, "");
+                                               else if (strcmp(pchan->name, subtarget)==0)
+                                                       set_constraint_target(con, tarArm, curbone->name);
+                                       }
+                                       
+                                       /* action constraint? */
+                                       if (con->type == CONSTRAINT_TYPE_ACTION) {
+                                               bActionConstraint *data= con->data;
+                                               bAction *act;
+                                               bActionChannel *achan;
+                                               
+                                               if (data->act) {
+                                                       act= data->act;
+                                                       
+                                                       for (achan= act->chanbase.first; achan; achan= achan->next) {
+                                                               if (strcmp(achan->name, pchan->name)==0)
+                                                                       BLI_strncpy(achan->name, curbone->name, 32);
+                                                       }
+                                               }
+                                       }
+                                       
+                               }
+                       }
+               }
+                       
+               /* fix object-level constraints */
+               if (ob != srcArm) {
+                       for (con= ob->constraints.first; con; con= con->next) {
+                               Object *conOb;
+                               char *subtarget;
+                               
+                               conOb= get_constraint_target(con, &subtarget);
+                               if (conOb == srcArm) {
+                                       if (strcmp(subtarget, "")==0)
+                                               set_constraint_target(con, tarArm, "");
+                                       else if (strcmp(pchan->name, subtarget)==0)
+                                               set_constraint_target(con, tarArm, curbone->name);
+                               }
+                       }
+               }
+               
+               /* See if an object is parented to this armature */
+               if (ob->parent && (ob->parent == srcArm)) {
+                       /* Is object parented to a bone of this src armature? */
+                       if (ob->partype==PARBONE) {
+                               /* bone name in object */
+                               if (!strcmp(ob->parsubstr, pchan->name))
+                                       BLI_strncpy(ob->parsubstr, curbone->name, 32);
+                       }
+                       
+                       /* make tar armature be new parent */
+                       ob->parent = tarArm;
+               }
+       }       
+}
+
 int join_armature(void)
 {
        Object  *ob;
+       bArmature *arm;
        Base    *base, *nextbase;
-       ListBase eblist;
-       EditBone *curbone, *next;
+       bPose *pose, *opose;
+       bPoseChannel *pchan, *pchann;
+       ListBase ebbase, eblist;
+       EditBone *curbone;
        float   mat[4][4], imat[4][4];
        
        /*      Ensure we're not in editmode and that the active object is an armature*/
@@ -439,27 +522,35 @@ int join_armature(void)
        
        ob= OBACT;
        if(ob->type!=OB_ARMATURE) return 0;
+       arm= get_armature(ob);
        
-       /*      Put the active armature into editmode and join the bones from the other one*/
-
-       enter_editmode(EM_WAITCURSOR);
+       /* Get editbones of active armature to add editbones to */
+       ebbase.first=ebbase.last= NULL;
+       make_boneList(&ebbase, &arm->bonebase, NULL);
+       pose= ob->pose;
        
        for (base=FIRSTBASE; base; base=nextbase) {
                nextbase = base->next;
                if (TESTBASE(base)){
                        if ((base->object->type==OB_ARMATURE) && (base->object!=ob)){
-                               /* Make a list of editbones */
+                               /* Make a list of editbones in current armature */
                                eblist.first=eblist.last= NULL;
                                make_boneList (&eblist, &((bArmature*)base->object->data)->bonebase,NULL);
+                               
+                               /* Get Pose of current armature */
+                               opose= base->object->pose;
+                               
                                /* Find the difference matrix */
                                Mat4Invert(imat, ob->obmat);
                                Mat4MulMat4(mat, base->object->obmat, imat);
                                
-                               /* Copy bones from the object to the edit armature */
-                               for (curbone=eblist.first; curbone; curbone=next){
-                                       next = curbone->next;
-
-                                       unique_editbone_name (curbone->name);
+                               /* Copy bones and posechannels from the object to the edit armature */
+                               for (pchan=opose->chanbase.first; pchan; pchan=pchann) {
+                                       pchann= pchan->next;
+                                       curbone= editbone_name_exists(&eblist, pchan->name);
+                                       
+                                       /* Get new name */
+                                       unique_editbone_name (&ebbase, curbone->name);
                                        
                                        /* Transform the bone */
                                        {
@@ -491,8 +582,19 @@ int join_armature(void)
                                                curbone->roll -= atan2(difmat[2][0], difmat[2][2]);
                                                
                                        }
+                                       
+                                       /* Fix Constraints and Other Links to this Bone and Armature */
+                                       joined_armature_fix_links(ob, base->object, pchan, curbone);
+                                       
+                                       /* Rename pchan */
+                                       sprintf(pchan->name, curbone->name);
+                                       
+                                       /* Jump Ship! */
                                        BLI_remlink(&eblist, curbone);
-                                       BLI_addtail(&G.edbo, curbone);
+                                       BLI_addtail(&ebbase, curbone);
+                                       
+                                       BLI_remlink(&opose->chanbase, pchan);
+                                       BLI_addtail(&pose->chanbase, pchan);
                                }
                                
                                free_and_unlink_base(base);
@@ -502,7 +604,9 @@ int join_armature(void)
        
        DAG_scene_sort(G.scene);        // because we removed object(s)
        
-       exit_editmode(EM_FREEDATA|EM_WAITCURSOR);
+       editbones_to_armature(&ebbase, ob);
+       if (ebbase.first) BLI_freelistN(&ebbase);
+       
        allqueue(REDRAWVIEW3D, 0);
        allqueue(REDRAWOOPS, 0);
        return 1;
@@ -925,8 +1029,6 @@ static void delete_bone(EditBone* exBone)
        BLI_freelinkN (&G.edbo,exBone);
 }
 
-static EditBone *editbone_name_exists (char *name);    // proto for below
-
 /* only editmode! */
 void delete_armature(void)
 {
@@ -942,7 +1044,7 @@ void delete_armature(void)
                bPoseChannel *chan, *next;
                for (chan=G.obedit->pose->chanbase.first; chan; chan=next) {
                        next= chan->next;
-                       curBone = editbone_name_exists (chan->name);
+                       curBone = editbone_name_exists (&G.edbo, chan->name);
                        
                        if (curBone && (curBone->flag & BONE_SELECTED) && (arm->layer & curBone->layer)) {
                                free_constraints(&chan->constraints);
@@ -952,7 +1054,7 @@ void delete_armature(void)
                                for(con= chan->constraints.first; con; con= con->next) {
                                        char *subtarget = get_con_subtarget_name(con, G.obedit);
                                        if (subtarget) {
-                                               curBone = editbone_name_exists (subtarget);
+                                               curBone = editbone_name_exists (&G.edbo, subtarget);
                                                if (curBone && (curBone->flag & BONE_SELECTED) && (arm->layer & curBone->layer)) {
                                                        con->flag |= CONSTRAINT_DISABLE;
                                                        subtarget[0]= 0;
@@ -1266,7 +1368,7 @@ static EditBone *add_editbone(char *name)
        EditBone *bone= MEM_callocN(sizeof(EditBone), "eBone");
        
        BLI_strncpy (bone->name, name, 32);
-       unique_editbone_name (bone->name);
+       unique_editbone_name(&G.edbo, bone->name);
        
        BLI_addtail(&G.edbo, bone);
        
@@ -1451,7 +1553,7 @@ void adduplicate_armature(void)
                                curBone->temp = eBone;
                                eBone->temp = curBone;
                                
-                               unique_editbone_name (eBone->name);
+                               unique_editbone_name (&G.edbo, eBone->name);
                                BLI_addtail (&G.edbo, eBone);
                                if (!firstDup)
                                        firstDup=eBone;
@@ -1698,11 +1800,13 @@ void clear_bone_parent(void)
 }
        
 
-static EditBone *editbone_name_exists (char *name)
+static EditBone *editbone_name_exists (ListBase *ebones, char *name)
 {
        EditBone        *eBone;
        
-       for (eBone=G.edbo.first; eBone; eBone=eBone->next){
+       if (ebones == NULL) ebones = &G.edbo;
+       
+       for (eBone=ebones->first; eBone; eBone=eBone->next){
                if (!strcmp (name, eBone->name))
                        return eBone;
        }
@@ -1710,14 +1814,14 @@ static EditBone *editbone_name_exists (char *name)
 }
 
 /* note: there's a unique_bone_name() too! */
-void unique_editbone_name (char *name)
+void unique_editbone_name (ListBase *ebones, char *name)
 {
        char            tempname[64];
        int                     number;
        char            *dot;
        
        
-       if (editbone_name_exists(name)) {
+       if (editbone_name_exists(ebones, name)) {
                
                /*      Strip off the suffix, if it's a number */
                number= strlen(name);
@@ -1729,7 +1833,7 @@ void unique_editbone_name (char *name)
                
                for (number = 1; number <=999; number++){
                        sprintf (tempname, "%s.%03d", name, number);
-                       if (!editbone_name_exists(tempname)){
+                       if (!editbone_name_exists(ebones, tempname)){
                                BLI_strncpy (name, tempname, 32);
                                return;
                        }
@@ -1837,7 +1941,7 @@ void extrude_armature(int forked)
                                                        else strcat(newbone->name, "_R");
                                                }
                                        }
-                                       unique_editbone_name(newbone->name);
+                                       unique_editbone_name(&G.edbo, newbone->name);
                                        
                                        /* Add the new bone to the list */
                                        BLI_addtail(&G.edbo, newbone);
@@ -1903,7 +2007,7 @@ void subdivide_armature(void)
 
                                                newbone->flag |= BONE_CONNECTED;
                                                
-                                               unique_editbone_name (newbone->name);
+                                               unique_editbone_name (&G.edbo, newbone->name);
                                                
                                                /* correct parent bones */
                                                for (tbone = G.edbo.first; tbone; tbone=tbone->next){
@@ -2535,9 +2639,9 @@ void armature_bone_rename(bArmature *arm, char *oldnamep, char *newnamep)
                if(G.obedit && G.obedit->data==arm) {
                        EditBone        *eBone;
 
-                       eBone= editbone_name_exists(oldname);
+                       eBone= editbone_name_exists(&G.edbo, oldname);
                        if(eBone) {
-                               unique_editbone_name (newname);
+                               unique_editbone_name (&G.edbo, newname);
                                BLI_strncpy(eBone->name, newname, MAXBONENAME);
                        }
                        else return;