== Armature Joining Bugfixes ==

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.
This commit is contained in:
Joshua Leung 2007-01-16 09:18:01 +00:00
parent 062843cca1
commit c967679bb8
4 changed files with 136 additions and 31 deletions

@ -36,6 +36,7 @@ struct Object;
struct Base; struct Base;
struct Bone; struct Bone;
struct bArmature; struct bArmature;
struct ListBase;
typedef struct EditBone typedef struct EditBone
{ {
@ -105,7 +106,7 @@ void remake_editArmature(void);
void selectconnected_armature(void); void selectconnected_armature(void);
void selectconnected_posearmature(void); void selectconnected_posearmature(void);
void select_bone_parent(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 auto_align_armature(void);
void create_vgroups_from_armature(Object *ob, Object *par); void create_vgroups_from_armature(Object *ob, Object *par);

@ -271,7 +271,7 @@ static int BonesDict_SetItem(BPy_BonesDict *self, PyObject *key, PyObject *value
//create a new editbone //create a new editbone
editbone = MEM_callocN(sizeof(EditBone), "eBone"); editbone = MEM_callocN(sizeof(EditBone), "eBone");
BLI_strncpy(editbone->name, key_str, 32); 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->dist = ((BPy_EditBone*)value)->dist;
editbone->ease1 = ((BPy_EditBone*)value)->ease1; editbone->ease1 = ((BPy_EditBone*)value)->ease1;
editbone->ease2 = ((BPy_EditBone*)value)->ease2; editbone->ease2 = ((BPy_EditBone*)value)->ease2;

@ -782,7 +782,7 @@ static PyObject *EditBone_new(PyTypeObject *type, PyObject *args, PyObject *kwds
//otherwise this will act as a py_object //otherwise this will act as a py_object
py_editBone->editbone = NULL; py_editBone->editbone = NULL;
unique_editbone_name(name); unique_editbone_name(NULL, name);
BLI_strncpy(py_editBone->name, name, 32); BLI_strncpy(py_editBone->name, name, 32);
py_editBone->parent = NULL; py_editBone->parent = NULL;
py_editBone->weight= 1.0f; py_editBone->weight= 1.0f;

@ -107,6 +107,8 @@ extern float centre[3], centroid[3]; /* Originally defined in editobject.c */
/* Macros */ /* Macros */
#define TEST_EDITARMATURE {if(G.obedit==0) return; if( (G.vd->lay & G.obedit->lay)==0 ) return;} #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 **************** */ /* **************** 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) int join_armature(void)
{ {
Object *ob; Object *ob;
bArmature *arm;
Base *base, *nextbase; Base *base, *nextbase;
ListBase eblist; bPose *pose, *opose;
EditBone *curbone, *next; bPoseChannel *pchan, *pchann;
ListBase ebbase, eblist;
EditBone *curbone;
float mat[4][4], imat[4][4]; float mat[4][4], imat[4][4];
/* Ensure we're not in editmode and that the active object is an armature*/ /* Ensure we're not in editmode and that the active object is an armature*/
@ -439,27 +522,35 @@ int join_armature(void)
ob= OBACT; ob= OBACT;
if(ob->type!=OB_ARMATURE) return 0; 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*/ /* Get editbones of active armature to add editbones to */
ebbase.first=ebbase.last= NULL;
enter_editmode(EM_WAITCURSOR); make_boneList(&ebbase, &arm->bonebase, NULL);
pose= ob->pose;
for (base=FIRSTBASE; base; base=nextbase) { for (base=FIRSTBASE; base; base=nextbase) {
nextbase = base->next; nextbase = base->next;
if (TESTBASE(base)){ if (TESTBASE(base)){
if ((base->object->type==OB_ARMATURE) && (base->object!=ob)){ 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; eblist.first=eblist.last= NULL;
make_boneList (&eblist, &((bArmature*)base->object->data)->bonebase,NULL); make_boneList (&eblist, &((bArmature*)base->object->data)->bonebase,NULL);
/* Get Pose of current armature */
opose= base->object->pose;
/* Find the difference matrix */ /* Find the difference matrix */
Mat4Invert(imat, ob->obmat); Mat4Invert(imat, ob->obmat);
Mat4MulMat4(mat, base->object->obmat, imat); Mat4MulMat4(mat, base->object->obmat, imat);
/* Copy bones from the object to the edit armature */ /* Copy bones and posechannels from the object to the edit armature */
for (curbone=eblist.first; curbone; curbone=next){ for (pchan=opose->chanbase.first; pchan; pchan=pchann) {
next = curbone->next; pchann= pchan->next;
curbone= editbone_name_exists(&eblist, pchan->name);
unique_editbone_name (curbone->name);
/* Get new name */
unique_editbone_name (&ebbase, curbone->name);
/* Transform the bone */ /* Transform the bone */
{ {
@ -491,8 +582,19 @@ int join_armature(void)
curbone->roll -= atan2(difmat[2][0], difmat[2][2]); 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_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); free_and_unlink_base(base);
@ -502,7 +604,9 @@ int join_armature(void)
DAG_scene_sort(G.scene); // because we removed object(s) 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(REDRAWVIEW3D, 0);
allqueue(REDRAWOOPS, 0); allqueue(REDRAWOOPS, 0);
return 1; return 1;
@ -925,8 +1029,6 @@ static void delete_bone(EditBone* exBone)
BLI_freelinkN (&G.edbo,exBone); BLI_freelinkN (&G.edbo,exBone);
} }
static EditBone *editbone_name_exists (char *name); // proto for below
/* only editmode! */ /* only editmode! */
void delete_armature(void) void delete_armature(void)
{ {
@ -942,7 +1044,7 @@ void delete_armature(void)
bPoseChannel *chan, *next; bPoseChannel *chan, *next;
for (chan=G.obedit->pose->chanbase.first; chan; chan=next) { for (chan=G.obedit->pose->chanbase.first; chan; chan=next) {
next= 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)) { if (curBone && (curBone->flag & BONE_SELECTED) && (arm->layer & curBone->layer)) {
free_constraints(&chan->constraints); free_constraints(&chan->constraints);
@ -952,7 +1054,7 @@ void delete_armature(void)
for(con= chan->constraints.first; con; con= con->next) { for(con= chan->constraints.first; con; con= con->next) {
char *subtarget = get_con_subtarget_name(con, G.obedit); char *subtarget = get_con_subtarget_name(con, G.obedit);
if (subtarget) { if (subtarget) {
curBone = editbone_name_exists (subtarget); curBone = editbone_name_exists (&G.edbo, subtarget);
if (curBone && (curBone->flag & BONE_SELECTED) && (arm->layer & curBone->layer)) { if (curBone && (curBone->flag & BONE_SELECTED) && (arm->layer & curBone->layer)) {
con->flag |= CONSTRAINT_DISABLE; con->flag |= CONSTRAINT_DISABLE;
subtarget[0]= 0; subtarget[0]= 0;
@ -1266,7 +1368,7 @@ static EditBone *add_editbone(char *name)
EditBone *bone= MEM_callocN(sizeof(EditBone), "eBone"); EditBone *bone= MEM_callocN(sizeof(EditBone), "eBone");
BLI_strncpy (bone->name, name, 32); BLI_strncpy (bone->name, name, 32);
unique_editbone_name (bone->name); unique_editbone_name(&G.edbo, bone->name);
BLI_addtail(&G.edbo, bone); BLI_addtail(&G.edbo, bone);
@ -1451,7 +1553,7 @@ void adduplicate_armature(void)
curBone->temp = eBone; curBone->temp = eBone;
eBone->temp = curBone; eBone->temp = curBone;
unique_editbone_name (eBone->name); unique_editbone_name (&G.edbo, eBone->name);
BLI_addtail (&G.edbo, eBone); BLI_addtail (&G.edbo, eBone);
if (!firstDup) if (!firstDup)
firstDup=eBone; 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; 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)) if (!strcmp (name, eBone->name))
return eBone; return eBone;
} }
@ -1710,14 +1814,14 @@ static EditBone *editbone_name_exists (char *name)
} }
/* note: there's a unique_bone_name() too! */ /* 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]; char tempname[64];
int number; int number;
char *dot; char *dot;
if (editbone_name_exists(name)) { if (editbone_name_exists(ebones, name)) {
/* Strip off the suffix, if it's a number */ /* Strip off the suffix, if it's a number */
number= strlen(name); number= strlen(name);
@ -1729,7 +1833,7 @@ void unique_editbone_name (char *name)
for (number = 1; number <=999; number++){ for (number = 1; number <=999; number++){
sprintf (tempname, "%s.%03d", name, number); sprintf (tempname, "%s.%03d", name, number);
if (!editbone_name_exists(tempname)){ if (!editbone_name_exists(ebones, tempname)){
BLI_strncpy (name, tempname, 32); BLI_strncpy (name, tempname, 32);
return; return;
} }
@ -1837,7 +1941,7 @@ void extrude_armature(int forked)
else strcat(newbone->name, "_R"); else strcat(newbone->name, "_R");
} }
} }
unique_editbone_name(newbone->name); unique_editbone_name(&G.edbo, newbone->name);
/* Add the new bone to the list */ /* Add the new bone to the list */
BLI_addtail(&G.edbo, newbone); BLI_addtail(&G.edbo, newbone);
@ -1903,7 +2007,7 @@ void subdivide_armature(void)
newbone->flag |= BONE_CONNECTED; newbone->flag |= BONE_CONNECTED;
unique_editbone_name (newbone->name); unique_editbone_name (&G.edbo, newbone->name);
/* correct parent bones */ /* correct parent bones */
for (tbone = G.edbo.first; tbone; tbone=tbone->next){ 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) { if(G.obedit && G.obedit->data==arm) {
EditBone *eBone; EditBone *eBone;
eBone= editbone_name_exists(oldname); eBone= editbone_name_exists(&G.edbo, oldname);
if(eBone) { if(eBone) {
unique_editbone_name (newname); unique_editbone_name (&G.edbo, newname);
BLI_strncpy(eBone->name, newname, MAXBONENAME); BLI_strncpy(eBone->name, newname, MAXBONENAME);
} }
else return; else return;