/** * $Id$ * * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. The Blender * Foundation also sells licenses for use in proprietary software under * the Blender License. See http://www.blender.org/BL/ for information * about this. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. * All rights reserved. * * The Original Code is: all of this file. * * Contributor(s): none yet. * * ***** END GPL/BL DUAL LICENSE BLOCK ***** * editarmature.c: Interface for creating and posing armature objects */ #include #include #include #ifdef WIN32 #include "BLI_winstuff.h" #endif #include "MEM_guardedalloc.h" #include "BMF_Api.h" #include "BLI_blenlib.h" #include "BLI_arithb.h" #include "BLI_editVert.h" #include "DNA_action_types.h" #include "DNA_armature_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "DNA_space_types.h" #include "DNA_view3d_types.h" #include "BKE_utildefines.h" #include "BKE_action.h" #include "BKE_armature.h" #include "BKE_constraint.h" #include "BKE_global.h" #include "BKE_object.h" #include "BIF_gl.h" #include "BIF_graphics.h" #include "BIF_interface.h" #include "BIF_resources.h" #include "BIF_screen.h" #include "BIF_space.h" #include "BIF_toolbox.h" #include "BIF_editarmature.h" #include "BIF_poseobject.h" #include "BIF_mywindow.h" #include "BDR_editobject.h" #include "BDR_drawobject.h" #include "BSE_edit.h" #include "BSE_view.h" #include "BSE_trans_types.h" #include "BSE_editaction.h" #include "mydevice.h" #include "interface.h" #include "blendef.h" #include "nla.h" /* >>>>> FIXME: ARG! Colours should be defined in a header somewhere! */ /* Note, these came from drawobject.c They really should be in a nice header file somewhere */ #define B_YELLOW 0x77FFFF #define B_PURPLE 0xFF70FF #define B_CYAN 0xFFFF00 #define B_AQUA 0xFFBB55 /* 0xFF8833*/ extern int tottrans; /* Originally defined in editobject.c */ extern struct TransOb *transmain; /* Originally defined in editobject.c */ 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;} /* Local Function Prototypes */ static void editbones_to_armature (ListBase *bones, Object *ob); static int editbone_to_parnr (EditBone *bone); static void validate_editbonebutton(EditBone *bone); static void fix_bonelist_roll (ListBase *bonelist, ListBase *editbonelist); static int select_bonechildren_by_name (struct Bone *bone, char *name, int select); static void build_bonestring (char *string, struct EditBone *bone); static void draw_boneverti (float x, float y, float z, float size, int flag); static void draw_bone (int armflag, int boneflag, unsigned int id, char *name, float length); static void draw_bonechildren (struct Bone *bone, int flag, unsigned int *index); static void add_bone_input (struct Object *ob); static void make_boneList(struct ListBase* list, struct ListBase *bones, struct EditBone *parent); static void make_bone_menu_children (struct Bone *bone, char *str, int *index); static void delete_bone(struct EditBone* exBone); static void clear_armature_children (struct Bone *bone, struct bPose *pose, char mode); static void parnr_to_editbone(EditBone *bone); static int count_bones (struct bArmature *arm, int flagmask, int allbones); static int count_bonechildren (struct Bone *bone, int incount, int flagmask, int allbones); static int add_trans_bonechildren (struct Object* ob, struct Bone* bone, struct TransOb* buffer, int index, char mode); static void deselect_bonechildren (struct Bone *bone, int mode); static void selectconnected_posebonechildren (struct Bone *bone); static int editbone_name_exists (char* name); static void unique_editbone_name (char* name); static void *get_nearest_bone (int findunsel); static EditBone * get_nearest_editbonepoint (int findunsel, int *selmask); static void attach_bone_to_parent(EditBone *bone); static Bone *get_first_selected_bonechildren (Bone *bone); /* Functions */ void apply_rot_armature (Object *ob, float mat[3][3]){ ListBase list; EditBone *ebone; bArmature *arm; arm = get_armature(ob); if (!arm) return; /* Put the armature into editmode */ list.first= list.last = NULL; make_boneList(&list, &arm->bonebase, NULL); /* Do the rotations */ for (ebone = list.first; ebone; ebone=ebone->next){ { /* Fixme: This is essentially duplicated from join_armature */ float premat[4][4]; float postmat[4][4]; float difmat[4][4]; float imat[4][4]; float temp[4][4]; float delta[3]; float rmat[4][4]; Mat4CpyMat3 (rmat, mat); /* Get the premat */ VecSubf (delta, ebone->tail, ebone->head); make_boneMatrixvr(temp, delta, ebone->roll); Mat4MulMat4 (premat, temp, rmat); Mat4MulVecfl(rmat, ebone->head); Mat4MulVecfl(rmat, ebone->tail); /* Get the postmat */ VecSubf (delta, ebone->tail, ebone->head); make_boneMatrixvr(postmat, delta, ebone->roll); /* Find the roll */ Mat4Invert (imat, premat); Mat4MulMat4 (difmat, postmat, imat); #if 0 printmatrix4 ("Difmat", difmat); #endif ebone->roll -=atan(difmat[2][0]/difmat[2][2]); if (difmat[0][0]<0) ebone->roll +=M_PI; } } /* Turn the list into an armature */ editbones_to_armature(&list, ob); /* Free the editbones */ if (list.first){ BLI_freelistN (&list); } } static Bone *get_first_selected_bonechildren (Bone *bone) { Bone *curbone, *result; if (bone->flag & BONE_SELECTED) return bone; for (curbone = bone->childbase.first; curbone; curbone=curbone->next){ result = get_first_selected_bonechildren(curbone); if (result) return result; }; return NULL; } Bone *get_first_selected_bone (void) { Bone *curbone, *result; bArmature *arm; arm = get_armature(OBACT); if (!arm) return NULL; for (curbone = arm->bonebase.first; curbone; curbone=curbone->next){ result = get_first_selected_bonechildren(curbone); if (result) return result; } return NULL; } void clever_numbuts_posearmature (void) { bArmature *arm; Bone *bone; bPoseChannel *chan; arm = get_armature(OBACT); if (!arm) return; bone = get_first_selected_bone(); if (!bone) return; add_numbut(0, NUM|FLO, "Loc X:", -G.vd->far, G.vd->far, bone->loc, 0); add_numbut(1, NUM|FLO, "Loc Y:", -G.vd->far, G.vd->far, bone->loc+1, 0); add_numbut(2, NUM|FLO, "Loc Z:", -G.vd->far, G.vd->far, bone->loc+2, 0); add_numbut(3, NUM|FLO, "Quat X:", -G.vd->far, G.vd->far, bone->quat, 0); add_numbut(4, NUM|FLO, "Quat Y:", -G.vd->far, G.vd->far, bone->quat+1, 0); add_numbut(5, NUM|FLO, "Quat Z:", -G.vd->far, G.vd->far, bone->quat+2, 0); add_numbut(6, NUM|FLO, "Quat W:", -G.vd->far, G.vd->far, bone->quat+3, 0); add_numbut(7, NUM|FLO, "Size X:", -G.vd->far, G.vd->far, bone->size, 0); add_numbut(8, NUM|FLO, "Size Y:", -G.vd->far, G.vd->far, bone->size+1, 0); add_numbut(9, NUM|FLO, "Size Z:", -G.vd->far, G.vd->far, bone->size+2, 0); do_clever_numbuts("Active Bone", 10, REDRAW); /* This is similar to code in special_trans_update */ if (!G.obpose->pose) G.obpose->pose= MEM_callocN(sizeof(bPose), "pose"); chan = MEM_callocN (sizeof (bPoseChannel), "transPoseChannel"); chan->flag |= POSE_LOC|POSE_ROT|POSE_SIZE; memcpy (chan->loc, bone->loc, sizeof (chan->loc)); memcpy (chan->quat, bone->quat, sizeof (chan->quat)); memcpy (chan->size, bone->size, sizeof (chan->size)); strcpy (chan->name, bone->name); set_pose_channel (G.obpose->pose, chan); } void clever_numbuts_armature (void) { EditBone *ebone, *child; ebone= G.edbo.first; for (ebone = G.edbo.first; ebone; ebone=ebone->next){ if (ebone->flag & BONE_SELECTED) break; } if (!ebone) return; add_numbut(0, NUM|FLO, "Root X:", -G.vd->far, G.vd->far, ebone->head, 0); add_numbut(1, NUM|FLO, "Root Y:", -G.vd->far, G.vd->far, ebone->head+1, 0); add_numbut(2, NUM|FLO, "Root Z:", -G.vd->far, G.vd->far, ebone->head+2, 0); add_numbut(3, NUM|FLO, "Tip X:", -G.vd->far, G.vd->far, ebone->tail, 0); add_numbut(4, NUM|FLO, "Tip Y:", -G.vd->far, G.vd->far, ebone->tail+1, 0); add_numbut(5, NUM|FLO, "Tip Z:", -G.vd->far, G.vd->far, ebone->tail+2, 0); /* Convert roll to degrees */ ebone->roll *= (180.0F/M_PI); add_numbut(6, NUM|FLO, "Roll:", -G.vd->far, G.vd->far, &ebone->roll, 0); do_clever_numbuts("Active Bone", 7, REDRAW); /* Convert roll to radians */ ebone->roll /= (180.0F/M_PI); // Update our parent if (ebone->parent && ebone->flag & BONE_IK_TOPARENT){ VECCOPY (ebone->parent->tail, ebone->head); } // Update our children if necessary for (child = G.edbo.first; child; child=child->next){ if (child->parent == ebone && child->flag & BONE_IK_TOPARENT){ VECCOPY (child->head, ebone->tail); } } } void select_bone_by_name (bArmature *arm, char *name, int select) { Bone *bone; if (!arm) return; for (bone=arm->bonebase.first; bone; bone=bone->next) if (select_bonechildren_by_name (bone, name, select)) break; } static int select_bonechildren_by_name (Bone *bone, char *name, int select) { Bone *curBone; if (!strcmp (bone->name, name)){ if (select) bone->flag |= BONE_SELECTED; else bone->flag &= ~BONE_SELECTED; return 1; } for (curBone=bone->childbase.first; curBone; curBone=curBone->next){ if (select_bonechildren_by_name (curBone, name, select)) return 1; } return 0; } void selectconnected_armature(void) { EditBone *bone, *curBone, *next; if (G.qual & LR_SHIFTKEY) bone= get_nearest_bone(0); else bone= get_nearest_bone(1); if (!bone) return; /* Select parents */ for (curBone=bone; curBone; curBone=next){ if (G.qual & LR_SHIFTKEY){ curBone->flag &= ~(BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL); } else{ curBone->flag |= (BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL); } if (curBone->flag & BONE_IK_TOPARENT) next=curBone->parent; else next=NULL; } /* Select children */ while (bone){ for (curBone=G.edbo.first; curBone; curBone=next){ next = curBone->next; if (curBone->parent == bone){ if (curBone->flag & BONE_IK_TOPARENT){ if (G.qual & LR_SHIFTKEY) curBone->flag &= ~(BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL); else curBone->flag |= (BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL); bone=curBone; break; } else{ bone=NULL; break; } } } if (!curBone) bone=NULL; } countall(); allqueue (REDRAWVIEW3D, 0); } void selectconnected_posearmature(void) { Bone *bone, *curBone, *next; if (G.qual & LR_SHIFTKEY) bone= get_nearest_bone(0); else bone = get_nearest_bone(1); if (!bone) return; /* Select parents */ for (curBone=bone; curBone; curBone=next){ select_actionchannel_by_name (G.obpose->action, curBone->name, !(G.qual & LR_SHIFTKEY)); if (G.qual & LR_SHIFTKEY) curBone->flag &= ~BONE_SELECTED; else curBone->flag |= BONE_SELECTED; if (curBone->flag & BONE_IK_TOPARENT) next=curBone->parent; else next=NULL; } /* Select children */ for (curBone=bone->childbase.first; curBone; curBone=next){ selectconnected_posebonechildren (curBone); } countall(); allqueue (REDRAWVIEW3D, 0); allqueue (REDRAWACTION, 0); } static void selectconnected_posebonechildren (Bone *bone) { Bone *curBone; if (!(bone->flag & BONE_IK_TOPARENT)) return; select_actionchannel_by_name (G.obpose->action, bone->name, !(G.qual & LR_SHIFTKEY)); if (G.qual & LR_SHIFTKEY) bone->flag &= ~BONE_SELECTED; else bone->flag |= BONE_SELECTED; for (curBone=bone->childbase.first; curBone; curBone=curBone->next){ selectconnected_posebonechildren (curBone); } } char *make_bone_menu (bArmature *arm) { char *menustr=NULL; Bone *curBone; int size; int index=0; // Count the bones size = (count_bones (arm, 0xFFFFFFFF, 1)*48) + 256; menustr = MEM_callocN(size, "bonemenu"); sprintf (menustr, "Select Bone%%t"); for (curBone=arm->bonebase.first; curBone; curBone=curBone->next){ make_bone_menu_children (curBone, menustr, &index); } return menustr; } static void make_bone_menu_children (Bone *bone, char *str, int *index) { Bone *curBone; sprintf (str, "%s|%s%%x%d", str, bone->name, *index); (*index) ++; for (curBone=bone->childbase.first; curBone; curBone=curBone->next) make_bone_menu_children (curBone, str, index); } void free_editArmature(void) { /* Clear the editbones list */ if (G.edbo.first){ BLI_freelistN (&G.edbo); } } static EditBone * get_nearest_editbonepoint (int findunsel, int *selmask){ EditBone *ebone; GLuint buffer[MAXPICKBUF]; short hits; int i, takeNext=0; int sel; unsigned int hitresult, hitbone, firstunSel=-1; glInitNames(); hits=selectprojektie(buffer, 0, 0, 0, 0); /* See if there are any selected bones in this group */ if (hits){ for (i=0; i< hits; i++){ hitresult = buffer[3+(i*4)]; if (!(hitresult&BONESEL_NOSEL)){ /* Determine which points are selected */ hitbone = hitresult & ~(BONESEL_ROOT|BONESEL_TIP); /* Determine what the current bone is */ ebone = BLI_findlink(&G.edbo, hitbone); /* See if it is selected */ sel = 0; if ((hitresult & (BONESEL_TIP|BONESEL_ROOT)) == (BONESEL_TIP|BONESEL_ROOT)) sel = (ebone->flag & (BONE_TIPSEL| BONE_ROOTSEL)) == (BONE_TIPSEL|BONE_ROOTSEL) ? 1 : 0; else if (hitresult & BONESEL_TIP) sel |= ebone->flag & BONE_TIPSEL; else if (hitresult & BONESEL_ROOT) sel |= ebone->flag & BONE_ROOTSEL; if (!findunsel) sel = !sel; if (sel) takeNext=1; else{ if (firstunSel == -1) firstunSel = hitresult; if (takeNext){ *selmask =0; if (hitresult & BONESEL_ROOT) *selmask |= BONE_ROOTSEL; if (hitresult & BONESEL_TIP) *selmask |= BONE_TIPSEL; return ebone; } } } } if (firstunSel != -1){ *selmask = 0; if (firstunSel & BONESEL_ROOT) *selmask |= BONE_ROOTSEL; if (firstunSel & BONESEL_TIP) *selmask |= BONE_TIPSEL; return BLI_findlink(&G.edbo, firstunSel & ~(BONESEL_ROOT|BONESEL_TIP)); } #if 1 else{ *selmask = 0; if (buffer[3] & BONESEL_ROOT) *selmask |= BONE_ROOTSEL; if (buffer[3] & BONESEL_TIP) *selmask |= BONE_TIPSEL; #if 1 return BLI_findlink(&G.edbo, buffer[3] & ~(BONESEL_ROOT|BONESEL_TIP)); #else return NULL; #endif } #endif } *selmask = 0; return NULL; } static void * get_nearest_bone (int findunsel){ void *firstunSel=NULL, *data; GLuint buffer[MAXPICKBUF]; short hits; int i, takeNext=0; int sel; unsigned int hitresult; Bone *bone; EditBone *ebone; glInitNames(); hits=selectprojektie(buffer, 0, 0, 0, 0); /* See if there are any selected bones in this group */ if (hits){ for (i=0; i< hits; i++){ hitresult = buffer[3+(i*4)]; if (!(hitresult&BONESEL_NOSEL)){ /* Determine which points are selected */ hitresult &= ~(BONESEL_ROOT|BONESEL_TIP); /* Determine what the current bone is */ if (!G.obedit){ bone = get_indexed_bone(OBACT->data, hitresult); if (findunsel) sel = (bone->flag & BONE_SELECTED); else sel = !(bone->flag & BONE_SELECTED); data = bone; } else{ ebone = BLI_findlink(&G.edbo, hitresult); if (findunsel) sel = (ebone->flag & BONE_SELECTED); else sel = !(ebone->flag & BONE_SELECTED); data = ebone; } if (sel) takeNext=1; else{ if (!firstunSel) firstunSel=data; if (takeNext) return data; } } } if (firstunSel) return firstunSel; #if 1 else{ #if 1 if (G.obedit) return BLI_findlink(&G.edbo, buffer[3] & ~(BONESEL_ROOT|BONESEL_TIP)); else return get_indexed_bone(OBACT->data, buffer[3] & ~(BONESEL_ROOT|BONESEL_TIP)); #else return NULL; #endif } #endif } return NULL; } void delete_armature(void) { EditBone *curBone, *next; TEST_EDITARMATURE; if(okee("Erase selected")==0) return; for (curBone=G.edbo.first;curBone;curBone=next){ next=curBone->next; if (curBone->flag&BONE_SELECTED) delete_bone(curBone); } allqueue(REDRAWVIEW3D, 0); allqueue(REDRAWBUTSEDIT, 0); allqueue(REDRAWBUTSCONSTRAINT, 0); countall(); } static void delete_bone(EditBone* exBone) { EditBone *curBone; bPoseChannel *chan; /* Find any bones that refer to this bone */ for (curBone=G.edbo.first;curBone;curBone=curBone->next){ if (curBone->parent==exBone){ curBone->parent=exBone->parent; curBone->flag &= ~BONE_IK_TOPARENT; } } /* Erase any associated pose channel */ if (G.obedit->pose){ for (chan=G.obedit->pose->chanbase.first; chan; chan=chan->next){ if (!strcmp (chan->name, exBone->name)){ free_constraints(&chan->constraints); BLI_freelinkN (&G.obedit->pose->chanbase, chan); break; } } } allqueue(REDRAWBUTSCONSTRAINT, 0); allqueue(REDRAWBUTSEDIT, 0); BLI_freelinkN (&G.edbo,exBone); } void remake_editArmature(void) { if(okee("Reload Original data")==0) return; make_editArmature(); allqueue(REDRAWVIEW3D, 0); allqueue(REDRAWBUTSHEAD, 0); allqueue(REDRAWBUTSCONSTRAINT, 0); allqueue(REDRAWBUTSEDIT, 0); } void mouse_armature(void) { EditBone* nearBone = NULL; int selmask; nearBone=get_nearest_editbonepoint(1, &selmask); if (nearBone){ if ((G.qual & LR_SHIFTKEY) && (nearBone->flag & selmask)) nearBone->flag &= ~selmask; else nearBone->flag |= selmask; if (!(G.qual & LR_SHIFTKEY)){ deselectall_armature(); nearBone->flag |= selmask; } allqueue(REDRAWVIEW3D, 0); allqueue(REDRAWBUTSEDIT, 0); allqueue(REDRAWBUTSCONSTRAINT, 0); }; countall(); rightmouse_transform(); } void make_editArmature(void) { bArmature *arm; if (G.obedit==0) return; free_editArmature(); arm= get_armature(G.obedit); if (!arm) return; make_boneList (&G.edbo, &arm->bonebase,NULL); } static void editbones_to_armature (ListBase *list, Object *ob) { bArmature *arm; EditBone *eBone; Bone *newBone; arm = get_armature(ob); if (!list) return; if (!arm) return; free_bones(arm); /* Copy the bones from the editData into the armature*/ for (eBone=list->first;eBone;eBone=eBone->next){ newBone=MEM_callocN (sizeof(Bone), "bone"); eBone->temp= newBone; /* Associate the real Bones with the EditBones */ strcpy (newBone->name, eBone->name); memcpy (newBone->head, eBone->head, sizeof(float)*3); memcpy (newBone->tail, eBone->tail, sizeof(float)*3); newBone->flag=eBone->flag &~(BONE_SELECTED|BONE_HILIGHTED); // newBone->roll=eBone->roll; newBone->roll = 0.0F; /* >>>>> FIXME: This isn't a very good system: a lot of pointless copying involved. To be investigated once things are working better. */ newBone->weight = eBone->weight; newBone->dist = eBone->dist; memcpy (newBone->loc, eBone->loc, sizeof(eBone->loc)); memcpy (newBone->dloc, eBone->dloc, sizeof(eBone->dloc)); /* memcpy (newBone->orig, eBone->orig, sizeof(eBone->orig));*/ memcpy (newBone->size, eBone->size, sizeof(eBone->size)); memcpy (newBone->dsize, eBone->dsize, sizeof(eBone->dsize)); memcpy (newBone->quat, eBone->quat, sizeof(eBone->quat)); memcpy (newBone->dquat, eBone->dquat, sizeof(eBone->dquat)); memcpy (newBone->obmat, eBone->obmat, sizeof(eBone->obmat)); } /* Fix parenting in a separate pass to ensure ebone->bone connections are valid at this point */ for (eBone=list->first;eBone;eBone=eBone->next){ newBone= (Bone*) eBone->temp; if (eBone->parent){ newBone->parent=(Bone*) eBone->parent->temp; BLI_addtail (&newBone->parent->childbase,newBone); { float M_boneRest[4][4]; float M_parentRest[4][4]; float iM_parentRest[4][4]; float delta[3]; /* Get the parent's global matrix (rotation only)*/ VecSubf (delta, eBone->parent->tail, eBone->parent->head); make_boneMatrixvr(M_parentRest, delta, eBone->parent->roll); /* Get this bone's global matrix (rotation only)*/ VecSubf (delta, eBone->tail, eBone->head); make_boneMatrixvr(M_boneRest, delta, eBone->roll); /* Invert the parent matrix */ Mat4Invert (iM_parentRest, M_parentRest); /* Get the new head and tail */ VecSubf (newBone->head, eBone->head, eBone->parent->tail); VecSubf (newBone->tail, eBone->tail, eBone->parent->tail); Mat4MulVecfl(iM_parentRest, newBone->head); Mat4MulVecfl(iM_parentRest, newBone->tail); } } /* ...otherwise add this bone to the armature's bonebase */ else BLI_addtail (&arm->bonebase,newBone); } /* Make a pass through the new armature to fix rolling */ fix_bonelist_roll (&arm->bonebase, list); /* Get rid of pose channels that may have belonged to deleted bones */ collect_pose_garbage(ob); /* Ensure all bones have channels */ apply_pose_armature(arm, ob->pose, 0); /* Calculate and cache the inverse restposition matrices (needed for deformation)*/ precalc_bonelist_irestmats(&arm->bonebase); } void load_editArmature(void) { bArmature *arm; arm=get_armature(G.obedit); if (!arm) return; #if 1 editbones_to_armature(&G.edbo, G.obedit); #else free_bones(arm); /* Copy the bones from the editData into the armature*/ for (eBone=G.edbo.first;eBone;eBone=eBone->next){ newBone=MEM_callocN (sizeof(Bone), "bone"); eBone->temp= newBone; /* Associate the real Bones with the EditBones */ strcpy (newBone->name, eBone->name); memcpy (newBone->head, eBone->head, sizeof(float)*3); memcpy (newBone->tail, eBone->tail, sizeof(float)*3); newBone->flag=eBone->flag &~(BONE_SELECTED|BONE_HILIGHTED); // newBone->roll=eBone->roll; newBone->roll = 0.0F; /* >>>>> FIXME: This isn't a very good system: a lot of pointless copying involved. To be investigated once things are working better. */ newBone->weight = eBone->weight; newBone->dist = eBone->dist; memcpy (newBone->loc, eBone->loc, sizeof(eBone->loc)); memcpy (newBone->dloc, eBone->dloc, sizeof(eBone->dloc)); /* memcpy (newBone->orig, eBone->orig, sizeof(eBone->orig));*/ memcpy (newBone->size, eBone->size, sizeof(eBone->size)); memcpy (newBone->dsize, eBone->dsize, sizeof(eBone->dsize)); memcpy (newBone->quat, eBone->quat, sizeof(eBone->quat)); memcpy (newBone->dquat, eBone->dquat, sizeof(eBone->dquat)); memcpy (newBone->obmat, eBone->obmat, sizeof(eBone->obmat)); } /* Fix parenting in a separate pass to ensure ebone->bone connections are valid at this point */ for (eBone=G.edbo.first;eBone;eBone=eBone->next){ newBone= (Bone*) eBone->temp; if (eBone->parent){ newBone->parent=(Bone*) eBone->parent->temp; BLI_addtail (&newBone->parent->childbase,newBone); { float M_boneRest[4][4]; float M_parentRest[4][4]; float M_relativeBone[4][4]; float iM_parentRest[4][4]; float delta[3]; /* Get the parent's global matrix (rotation only)*/ VecSubf (delta, eBone->parent->tail, eBone->parent->head); make_boneMatrixvr(M_parentRest, delta, eBone->parent->roll); /* Get this bone's global matrix (rotation only)*/ VecSubf (delta, eBone->tail, eBone->head); make_boneMatrixvr(M_boneRest, delta, eBone->roll); /* Invert the parent matrix */ Mat4Invert (iM_parentRest, M_parentRest); /* Get the new head and tail */ VecSubf (newBone->head, eBone->head, eBone->parent->tail); VecSubf (newBone->tail, eBone->tail, eBone->parent->tail); Mat4MulVecfl(iM_parentRest, newBone->head); Mat4MulVecfl(iM_parentRest, newBone->tail); } } /* ...otherwise add this bone to the armature's bonebase */ else BLI_addtail (&arm->bonebase,newBone); } /* Make a pass through the new armature to fix rolling */ fix_bonelist_roll (&arm->bonebase, &G.edbo); /* Get rid of pose channels that may have belonged to deleted bones */ collect_pose_garbage(G.obedit); #endif } static void fix_bonelist_roll (ListBase *bonelist, ListBase *editbonelist) { Bone *curBone; EditBone *ebone; for (curBone=bonelist->first; curBone; curBone=curBone->next){ /* Fix this bone's roll */ #if 1 { float premat[4][4]; float postmat[4][4]; float difmat[4][4]; float imat[4][4]; float delta[3]; /* Find the associated editbone */ for (ebone = editbonelist->first; ebone; ebone=ebone->next) if ((Bone*)ebone->temp == curBone) break; if (ebone){ /* Get the premat */ VecSubf (delta, ebone->tail, ebone->head); make_boneMatrixvr(premat, delta, ebone->roll); /* Get the postmat */ get_objectspace_bone_matrix(curBone, postmat, 1, 0); postmat[3][0]=postmat[3][1]=postmat[3][2]=0.0F; #if 1 Mat4Invert (imat, premat); Mat4MulMat4 (difmat, postmat, imat); #else Mat4Invert (imat, postmat); Mat4MulMat4 (difmat, premat, imat); #endif #if 0 printf ("Bone %s\n", curBone->name); printmatrix4 ("premat", premat); printmatrix4 ("postmat", postmat); printmatrix4 ("difmat", difmat); printf ("Roll = %f\n", (-atan(difmat[2][0]/difmat[2][2]) * (180.0/M_PI))); #endif curBone->roll = -atan(difmat[2][0]/difmat[2][2]); if (difmat[0][0]<0) curBone->roll +=M_PI; } } #endif fix_bonelist_roll (&curBone->childbase, editbonelist); } } void make_bone_parent(void) { /* error ("Bone Parenting not yet implemented"); */ return; } static void make_boneList(ListBase* list, ListBase *bones, EditBone *parent) { EditBone *eBone; Bone *curBone; for (curBone=bones->first; curBone; curBone=curBone->next){ eBone=MEM_callocN(sizeof(EditBone),"make_editbone"); /* Copy relevant data from bone to eBone */ eBone->parent=parent; strcpy (eBone->name, curBone->name); eBone->flag = curBone->flag & ~(BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL); /* Print bone matrix before and after */ get_bone_root_pos(curBone, eBone->head, 0); get_bone_tip_pos(curBone, eBone->tail, 0); // eBone->roll=curBone->roll; eBone->roll=0; #if 1 { float delta[3]; float premat[4][4]; float postmat[4][4]; float imat[4][4]; float difmat[4][4]; VecSubf (delta, eBone->tail, eBone->head); make_boneMatrixvr(postmat, delta, eBone->roll); get_objectspace_bone_matrix(curBone, premat, 1, 0); #if 0 Mat4Invert (imat, premat); Mat4MulMat4 (difmat, postmat, imat); #else Mat4Invert (imat, postmat); Mat4MulMat4 (difmat, premat, imat); #endif #if 0 printf ("Bone %s\n", curBone->name); printmatrix4 ("diffmat", difmat); printf ("Roll : atan of %f / %f = %f\n", difmat[2][0], difmat[2][2], (float)atan(difmat[2][0]/difmat[2][2])*(180.0F/M_PI)); #endif eBone->roll = atan(difmat[2][0]/difmat[2][2]); if (difmat[0][0]<0) eBone->roll +=M_PI; } #endif eBone->dist= curBone->dist; eBone->weight= curBone->weight; memcpy (eBone->loc, curBone->loc, sizeof(curBone->loc)); memcpy (eBone->dloc, curBone->dloc, sizeof(curBone->dloc)); /* memcpy (eBone->orig, curBone->orig, sizeof(curBone->orig));*/ memcpy (eBone->size, curBone->size, sizeof(curBone->size)); memcpy (eBone->dsize, curBone->dsize, sizeof(curBone->dsize)); memcpy (eBone->quat, curBone->quat, sizeof(curBone->quat)); memcpy (eBone->dquat, curBone->dquat, sizeof(curBone->dquat)); memcpy (eBone->obmat, curBone->obmat, sizeof(curBone->obmat)); BLI_addtail (list, eBone); /* Add children if necessary */ if (curBone->childbase.first) make_boneList (list, &curBone->childbase, eBone); } } #if 0 static EditVert* add_armatureVert (float *loc) { EditVert* vert=NULL; vert = MEM_callocN (sizeof (EditVert), "armaturevert"); if (vert){ VECCOPY (vert->co, loc); BLI_addtail (&G.edve,vert); } return vert; } static EditVert* get_armatureVert (float *loc) { EditVert* vert; for (vert=G.edve.first;vert;vert=vert->next){ if ((vert->co[0]==loc[0])&&(vert->co[1]==loc[1])&&(vert->co[2]==loc[2])){ return (vert); } } return add_armatureVert (loc); } #endif void draw_armature(Object *ob) { bArmature *arm; Bone *bone; EditBone *eBone; unsigned int index; float delta[3],offset[3]; float bmat[4][4]; float length; if (ob==NULL) return; arm= ob->data; if (arm==NULL) return; if (!(ob->lay & G.vd->lay)) return; /* If we're in editmode, draw the Global edit data */ if(ob==G.obedit || (G.obedit && ob->data==G.obedit->data)) { cpack (0x000000); arm->flag |= ARM_EDITMODE; for (eBone=G.edbo.first, index=0; eBone; eBone=eBone->next, index++){ if (ob==G.obedit){ // if ((eBone->flag&(BONE_TIPSEL | BONE_ROOTSEL))==(BONE_TIPSEL|BONE_ROOTSEL)) // cpack (B_YELLOW); // else cpack (B_PURPLE); } else cpack (0x000000); glPushMatrix(); /* Compose the parent transforms (i.e. their translations) */ VECCOPY (offset,eBone->head); glTranslatef (offset[0],offset[1],offset[2]); delta[0]=eBone->tail[0]-eBone->head[0]; delta[1]=eBone->tail[1]-eBone->head[1]; delta[2]=eBone->tail[2]-eBone->head[2]; length = sqrt (delta[0]*delta[0] + delta[1]*delta[1] +delta[2]*delta[2]); make_boneMatrixvr(bmat, delta, eBone->roll); glMultMatrixf (bmat); draw_bone (arm->flag, eBone->flag, index, eBone->name, length); glPopMatrix(); if (eBone->parent){ glLoadName (-1); setlinestyle(3); glBegin(GL_LINES); glVertex3fv(eBone->parent->tail); glVertex3fv(eBone->head); glEnd(); setlinestyle(0); } }; arm->flag &= ~ARM_EDITMODE; cpack (B_YELLOW); } else{ /* Draw hierarchical bone list (non-edit data) */ /* Ensure we're using the mose recent pose */ if (!ob->pose) ob->pose=MEM_callocN (sizeof(bPose), "pose"); #if 1 /* Activate if there are problems with action lag */ apply_pose_armature(arm, ob->pose, 0); where_is_armature (ob); #endif if (G.obpose == ob){ arm->flag |= ARM_POSEMODE; #if 0 /* Depreciated interactive ik goal drawing */ if (arm->chainbase.first){ glPushMatrix(); glTranslatef(((PoseChain*)arm->chainbase.first)->goal[0], ((PoseChain*)arm->chainbase.first)->goal[1], ((PoseChain*)arm->chainbase.first)->goal[2]); drawaxes(1.0); glPopMatrix(); } #endif } index = 0; for (bone=arm->bonebase.first; bone; bone=bone->next) { glPushMatrix(); draw_bonechildren(bone, arm->flag, &index); glPopMatrix(); if (arm->flag & ARM_POSEMODE) cpack (B_CYAN); } arm->flag &= ~ARM_POSEMODE; } } static void draw_boneverti (float x, float y, float z, float size, int flag) { GLUquadricObj *qobj; size*=0.05; /* Ultimately dots should be drawn in screenspace For now we'll just draw them any old way. */ glPushMatrix(); glTranslatef(x, y, z); qobj = gluNewQuadric(); gluQuadricDrawStyle(qobj, GLU_SILHOUETTE); gluPartialDisk( qobj, 0, 1.0F*size, 32, 1, 360.0, 360.0); glRotatef (90, 0, 1, 0); gluPartialDisk( qobj, 0, 1.0F*size, 32, 1, 360.0, 360.0); glRotatef (90, 1, 0, 0); gluPartialDisk( qobj, 0, 1.0F*size, 32, 1, 360.0, 360.0); gluDeleteQuadric(qobj); glPopMatrix(); } static void draw_bonechildren (Bone *bone, int flag, unsigned int *index) { Bone *cbone; float delta[3]; float length; float M_objectspacemat[4][4]; if (bone==NULL) return; if (flag & ARM_POSEMODE){ if (bone->flag & BONE_SELECTED) cpack (B_CYAN); else cpack (B_AQUA); } // Draw a line from our root to the parent's tip if (bone->parent && !(bone->flag & (BONE_IK_TOPARENT|BONE_HIDDEN)) ){ float childMat[4][4]; float boneMat[4][4]; float tip[3], root[3]; get_objectspace_bone_matrix(bone->parent, boneMat, 0, 1); get_objectspace_bone_matrix(bone, childMat, 1, 1); VECCOPY (tip, boneMat[3]); VECCOPY (root, childMat[3]); if (flag & ARM_POSEMODE) glLoadName (-1); setlinestyle(3); glBegin(GL_LINES); glVertex3fv(tip); glVertex3fv(root); glEnd(); setlinestyle(0); } /* Draw this bone in objectspace */ delta[0]=bone->tail[0]-bone->head[0]; delta[1]=bone->tail[1]-bone->head[1]; delta[2]=bone->tail[2]-bone->head[2]; length = sqrt (delta[0]*delta[0] + delta[1]*delta[1] + delta[2]*delta[2] ); /* Incorporates offset, rest rotation, user rotation and parent coordinates*/ get_objectspace_bone_matrix(bone, M_objectspacemat, 1, 1); if (!(bone->flag & BONE_HIDDEN)){ glPushMatrix(); glMultMatrixf(M_objectspacemat); if (flag & ARM_POSEMODE) draw_bone(flag, (bone->parent && bone->parent->flag & BONE_HIDDEN) ? (bone->flag & ~BONE_IK_TOPARENT) : (bone->flag), *index, bone->name, length); else draw_bone(flag, (bone->parent && bone->parent->flag & BONE_HIDDEN) ? (bone->flag & ~BONE_IK_TOPARENT) : (bone->flag), -1, bone->name, length); glPopMatrix(); } (*index)++; /* Draw the children */ for (cbone= bone->childbase.first; cbone; cbone=cbone->next){ draw_bonechildren (cbone, flag, index); } } static void draw_bone (int armflag, int boneflag, unsigned int id, char *name, float length) { float vec[2]; float bulge; float pointsize; /* FIXME: This routine should probably draw the bones in screenspace using the projected start & end points of the transformed bone */ pointsize = length; if (pointsize<0.1) pointsize=0.1; /* Draw a 3d octahedral bone */ bulge=length*0.1; if (id!=-1) glLoadName((GLuint) id ); glPushMatrix(); /* Draw root point if we have no IK parent */ if (!(boneflag & BONE_IK_TOPARENT)){ if (id != -1) glLoadName (id | BONESEL_ROOT); if (armflag & ARM_EDITMODE){ if (boneflag & BONE_ROOTSEL) cpack (B_YELLOW); else cpack (B_PURPLE); } draw_boneverti (0, 0, 0, pointsize, 0); } /* Draw tip point (for selection only )*/ if (id != -1) glLoadName (id | BONESEL_TIP); if (armflag & ARM_EDITMODE){ if (boneflag & BONE_TIPSEL) cpack (B_YELLOW); else cpack (B_PURPLE); } draw_boneverti (0, length, 0, pointsize, 0); if (id != -1){ if (armflag & ARM_POSEMODE) glLoadName((GLuint) id); else{ #if 1 /* Bones not selectable in editmode */ glLoadName((GLuint) -1); #else glLoadName ((GLuint) id|BONESEL_TIP|BONESEL_ROOT); #endif } } if (armflag & ARM_EDITMODE){ if (boneflag & BONE_SELECTED) cpack (B_YELLOW); else cpack (B_PURPLE); } /* Draw additional axes */ if (armflag & ARM_DRAWAXES){ drawaxes(length*0.25F); } /* Section 1*/ glBegin(GL_LINE_STRIP); vec[0]= vec[1]= 0; glVertex2fv(vec); vec[0]= -bulge; vec[1]= bulge; glVertex2fv(vec); vec[0]= 0; vec[1]= length; glVertex2fv(vec); vec[0]= bulge; vec[1]= bulge; glVertex2fv(vec); vec[0]= 0; vec[1]= 0; glVertex2fv(vec); glEnd(); /* Section 2*/ glRotatef(90,0,1,0); glBegin(GL_LINE_STRIP); vec[0]= vec[1]= 0; glVertex2fv(vec); vec[0]= -bulge; vec[1]= bulge; glVertex2fv(vec); vec[0]= 0; vec[1]= length; glVertex2fv(vec); vec[0]= bulge; vec[1]= bulge; glVertex2fv(vec); vec[0]= 0; vec[1]= 0; glVertex2fv(vec); glEnd(); /* Square*/ glTranslatef (0,bulge,0); glRotatef(45,0,1,0); glRotatef(90,1,0,0); glBegin(GL_LINE_STRIP); vec[0]= -bulge*.707;vec[1]=-bulge*.707; glVertex2fv(vec); vec[0]= bulge*.707;vec[1]= -bulge*.707; glVertex2fv(vec); vec[0]= bulge*.707;vec[1]= bulge*.707; glVertex2fv(vec); vec[0]= -bulge*.707;vec[1]= bulge*.707; glVertex2fv(vec); vec[0]= vec[1]= -bulge*.707; glVertex2fv(vec); glEnd(); glPopMatrix(); /* Draw the bone name */ if (armflag & ARM_DRAWNAMES){ glRasterPos3f(0, length/2.0, 0); BMF_DrawString(G.font, " "); BMF_DrawString(G.font, name); } } void add_primitiveArmature(int type) { if(G.scene->id.lib) return; /* this function also comes from an info window */ if ELEM(curarea->spacetype, SPACE_VIEW3D, SPACE_INFO); else return; if(G.vd==NULL) return; check_editmode(OB_ARMATURE); /* If we're not the "obedit", make a new object and enter editmode */ if(G.obedit==NULL) { add_object(OB_ARMATURE); base_init_from_view3d(BASACT, G.vd); G.obedit= BASACT->object; where_is_object(G.obedit); make_editArmature(); setcursor_space(SPACE_VIEW3D, CURSOR_EDIT); } switch (type){ default: add_bone_input (G.obedit); break; }; countall(); allqueue(REDRAWVIEW3D, 0); allqueue(REDRAWBUTSEDIT, 0); allqueue(REDRAWBUTSHEAD, 0); allqueue(REDRAWBUTSCONSTRAINT, 0); allqueue(REDRAWNLA, 0); } static void add_bone_input (Object *ob) /* FUNCTION: Creates a bone chain under user input. As the user clicks to accept each bone, a new bone is started as the child and IK child of the previous bone. Pressing ESC cancels the current bone and leaves bone adding mode. */ { float *cursLoc, cent[3], dx, dy; float mat[3][3], curs[3], cmat[3][3], imat[3][3], rmat[4][4], itmat[4][4]; short xo, yo, mval[2], afbreek=0; short val; float restmat[4][4], tempVec[4]; EditBone *bone; EditBone *parent; unsigned short event; float angle, scale; float length, lengths; float newEnd[4]; int addbones=1; float dvec[3], dvecp[3]; cursLoc= give_cursor(); VECCOPY (curs,cursLoc); while (addbones>0){ afbreek=0; /* Create an inverse matrix */ Mat3CpyMat4(mat, G.obedit->obmat); VECCOPY(cent, curs); cent[0]-= G.obedit->obmat[3][0]; cent[1]-= G.obedit->obmat[3][1]; cent[2]-= G.obedit->obmat[3][2]; Mat3CpyMat4(imat, G.vd->viewmat); Mat3MulVecfl(imat, cent); Mat3MulMat3(cmat, imat, mat); Mat3Inv(imat,cmat); /* Create a temporary bone */ bone= MEM_callocN(sizeof(EditBone), "eBone"); /* If we're the child of something, set that up now */ if (addbones>1){ parent=G.edbo.last; bone->parent=parent; bone->flag|=BONE_IK_TOPARENT; } strcpy (bone->name,"Bone"); unique_editbone_name (bone->name); BLI_addtail(&G.edbo, bone); bone->flag |= BONE_QUATROT; bone->flag |= (BONE_SELECTED); deselectall_armature(); bone->flag |= BONE_SELECTED|BONE_HILIGHTED|BONE_TIPSEL|BONE_ROOTSEL; bone->weight= 1.0F; bone->dist= 1.0F; /* Project cursor center to screenspace. */ getmouseco_areawin(mval); xo= mval[0]; yo= mval[1]; window_to_3d(dvecp, xo, yo); while (1) { getmouseco_areawin(mval); window_to_3d(dvec, mval[0], mval[1]); scale=1000; dx= ((float)mval[0]-(float)xo)*scale; dy= ((float)mval[1]-(float)yo)*scale; /* Calc bone length*/ lengths= sqrt((dx*dx)+(dy*dy)); length = sqrt(((dvec[0]-dvecp[0])*(dvec[0]-dvecp[0]))+((dvec[1]-dvecp[1])*(dvec[1]-dvecp[1]))+((dvec[2]-dvecp[2])*(dvec[2]-dvecp[2]))); /* Find rotation around screen normal */ if (lengths>0.0F) { angle= acos(dy/lengths); if (dx<0.0F) angle*= -1.0F; } else angle= 0.0F; /* FIXME: Is there a blender-defined way of making rot and trans matrices? */ rmat[0][0]= cos (angle); rmat[0][1]= -sin (angle); rmat[0][2]= 0.0F; rmat[0][3]= 0.0F; rmat[1][0]= sin (angle); rmat[1][1]= cos (angle); rmat[1][2]= 0.0F; rmat[1][3]= 0.0F; rmat[2][0]= 0.0F; rmat[2][1]= 0.0F; rmat[2][2]= 1.0F; rmat[2][3]= 0.0F; rmat[3][0]= cent[0]; rmat[3][1]= cent[1]; rmat[3][2]= cent[2]; rmat[3][3]= 1.0F; /* Rotate object's inversemat by the bone's rotation to get the coordinate space of the bone */ Mat4CpyMat3 (itmat, imat); Mat4MulMat4 (restmat, rmat, itmat); /* Find the bone head */ tempVec[0]=0; tempVec[1]=0.0F; tempVec[2]=0.0F; tempVec[3]=1.0F; Mat4MulVec4fl (restmat, tempVec); VECCOPY (bone->head, tempVec); /* Find the bone tail */ tempVec[0]=0; tempVec[1]=length; tempVec[2]=0.0F; tempVec[3]=1.0F; Mat4MulVec4fl (restmat, tempVec); VECCOPY (bone->tail, tempVec); /* IF we're a child of something, add the parents' translates */ /* Offset of child is new cursor*/ VECCOPY (newEnd,bone->tail); newEnd[3]=1; /* Set the bone's transformations */ Mat4One (bone->obmat); bone->size[0]=bone->size[1]=bone->size[2]=1.0F; force_draw(); while(qtest()) { event= extern_qread(&val); if(val) { switch(event) { case ESCKEY: case RIGHTMOUSE: BLI_freelinkN (&G.edbo,bone); afbreek=1; addbones=0; break; case LEFTMOUSE: case MIDDLEMOUSE: case SPACEKEY: case RETKEY: afbreek= 1; Mat4MulVec4fl (G.obedit->obmat,newEnd); curs[0]=newEnd[0]; curs[1]=newEnd[1]; curs[2]=newEnd[2]; addbones++; break; } /* End of case*/ } /* End of if (val)*/ if(afbreek) break; } /* Endd of Qtest loop */ if(afbreek) break; }/* End of positioning loop (while)*/ } /* End of bone adding loop*/ countall(); } static void validate_editbonebutton_cb(void *bonev, void *arg2_unused) { EditBone *curBone= bonev; validate_editbonebutton(curBone); } static void parnr_to_editbone_cb(void *bonev, void *arg2_unused) { EditBone *curBone= bonev; parnr_to_editbone(curBone); } static void attach_bone_to_parent_cb(void *bonev, void *arg2_unused) { EditBone *curBone= bonev; attach_bone_to_parent(curBone); } void armaturebuts(void) { bArmature *arm=NULL; Object *ob=NULL; uiBlock *block=NULL; char str[64]; int bx=148, by=100; EditBone *curBone; uiBut *but; char *boneString=NULL; int index; ob= OBACT; if (ob==NULL) return; sprintf(str, "editbuttonswin %d", curarea->win); block= uiNewBlock (&curarea->uiblocks, str, UI_EMBOSSX, UI_HELV, curarea->win); arm= ob->data; if (arm==NULL) return; uiBlockSetCol(block, BUTGREEN); uiDefButI(block, TOG|BIT|ARM_RESTPOSBIT,REDRAWVIEW3D, "Rest Pos", bx,by,97,20, &arm->flag, 0, 0, 0, 0, "Disable all animation for this object"); uiDefButI(block, TOG|BIT|ARM_DRAWAXESBIT,REDRAWVIEW3D, "Draw Axes", bx,by-46,97,20, &arm->flag, 0, 0, 0, 0, "Draw bone axes"); uiDefButI(block, TOG|BIT|ARM_DRAWNAMESBIT,REDRAWVIEW3D, "Draw Names", bx,by-69,97,20, &arm->flag, 0, 0, 0, 0, "Draw bone names"); uiBlockSetCol(block, BUTGREY); /* Draw the bone name block */ bx+=400; by=200; if (G.obedit==ob){ uiDefBut(block, LABEL, 0, "Selected Bones", bx,by,128,18, 0, 0, 0, 0, 0, ""); by-=20; for (curBone=G.edbo.first, index=0; curBone; curBone=curBone->next, index++){ if (curBone->flag & (BONE_SELECTED)){ /* Hide in posemode flag */ uiBlockSetCol(block, BUTGREEN); uiDefButI(block, TOG|BIT|BONE_HIDDENBIT, REDRAWVIEW3D, "Hide", bx-50,by,48,18, &curBone->flag, 0, 0, 0, 0, "Toggles display of this bone in posemode"); /* Bone naming button */ uiBlockSetCol(block, BUTGREY); strcpy (curBone->oldname, curBone->name); but=uiDefBut(block, TEX, REDRAWVIEW3D, "BO:", bx,by,97,18, &curBone->name, 0, 24, 0, 0, "Change the bone name"); uiButSetFunc(but, validate_editbonebutton_cb, curBone, NULL); uiDefBut(block, LABEL, 0, "child of", bx+106,by,100,18, NULL, 0.0, 0.0, 0.0, 0.0, ""); boneString = malloc((BLI_countlist(&G.edbo) * 64)+64); build_bonestring (boneString, curBone); curBone->parNr = editbone_to_parnr(curBone->parent); but = uiDefButI(block, MENU,REDRAWVIEW3D, boneString, bx+164,by,97,18, &curBone->parNr, 0.0, 0.0, 0.0, 0.0, "Parent"); uiButSetFunc(but, parnr_to_editbone_cb, curBone, NULL); free(boneString); /* IK to parent flag */ if (curBone->parent){ uiBlockSetCol(block, BUTGREEN); but=uiDefButI(block, TOG|BIT|BONE_IK_TOPARENTBIT, REDRAWVIEW3D, "IK", bx+275,by,32,18, &curBone->flag, 0.0, 0.0, 0.0, 0.0, "IK link to parent"); uiButSetFunc(but, attach_bone_to_parent_cb, curBone, NULL); } /* Dist and weight buttons */ uiBlockSetCol(block, BUTGREY); uiDefButF(block, NUM,REDRAWVIEW3D, "Dist:", bx+320, by, 110, 18, &curBone->dist, 0.0, 1000.0, 10.0, 0.0, "Bone deformation distance"); uiDefButF(block, NUM,REDRAWVIEW3D, "Weight:", bx+438, by, 110, 18, &curBone->weight, 0.0F, 1000.0F, 10.0F, 0.0F, "Bone deformation weight"); by-=19; } } } uiDrawBlock (block); } static int editbone_to_parnr (EditBone *bone) { EditBone *ebone; int index; for (ebone=G.edbo.first, index=0; ebone; ebone=ebone->next, index++){ if (ebone==bone) return index; } return -1; } static void parnr_to_editbone(EditBone *bone) { if (bone->parNr == -1){ bone->parent = NULL; bone->flag &= ~BONE_IK_TOPARENT; } else{ bone->parent = BLI_findlink(&G.edbo, bone->parNr); attach_bone_to_parent(bone); } } static void attach_bone_to_parent(EditBone *bone) { EditBone *curbone; if (bone->flag & BONE_IK_TOPARENT) { /* See if there are any other bones that refer to the same parent and disconnect them */ for (curbone = G.edbo.first; curbone; curbone=curbone->next){ if (curbone!=bone){ if (curbone->parent && (curbone->parent == bone->parent) && (curbone->flag & BONE_IK_TOPARENT)) curbone->flag &= ~BONE_IK_TOPARENT; } } /* Attach this bone to its parent */ VECCOPY(bone->head, bone->parent->tail); } } static void build_bonestring (char *string, EditBone *bone){ EditBone *curBone; EditBone *pBone; int skip=0; int index; sprintf (string, "Parent%%t| %%x%d", -1); /* That space is there for a reason */ for (curBone = G.edbo.first, index=0; curBone; curBone=curBone->next, index++){ /* Make sure this is a valid child */ if (curBone != bone){ skip=0; for (pBone=curBone->parent; pBone; pBone=pBone->parent){ if (pBone==bone){ skip=1; break; } } if (skip) continue; sprintf (string, "%s|%s%%x%d", string, curBone->name, index); } } } static void validate_editbonebutton(EditBone *eBone){ EditBone *prev; bAction *act; bActionChannel *chan; Base *base; /* Separate the bone from the G.edbo */ prev=eBone->prev; BLI_remlink (&G.edbo, eBone); /* Validate the name */ unique_editbone_name (eBone->name); /* Re-insert the bone */ if (prev) BLI_insertlink(&G.edbo, prev, eBone); else BLI_addhead (&G.edbo, eBone); /* Rename channel if necessary */ if (G.obedit) act = G.obedit->action; if (act && !act->id.lib){ // Find the appropriate channel for (chan = act->chanbase.first; chan; chan=chan->next){ if (!strcmp (chan->name, eBone->oldname)){ strcpy (chan->name, eBone->name); } } allqueue(REDRAWACTION, 0); } /* Update the parenting info of any users */ /* Yes, I know this is the worst thing you have ever seen. */ for (base = G.scene->base.first; base; base=base->next){ Object *ob = base->object; /* See if an object is parented to this armature */ if (ob->parent && ob->partype==PARBONE && (ob->parent->type==OB_ARMATURE) && (ob->parent->data == G.obedit->data)){ if (!strcmp(ob->parsubstr, eBone->oldname)) strcpy(ob->parsubstr, eBone->name); } } exit_editmode(0); /* To ensure new names make it to the edit armature */ } void deselectall_armature(void) /* Actually, it toggles selection, deselecting everything if anything is selected */ { EditBone *eBone; int sel=1; /* Determine if there are any selected bones And therefore whether we are selecting or deselecting */ for (eBone=G.edbo.first;eBone;eBone=eBone->next){ if (eBone->flag & (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL)){ sel=0; break; }; }; /* Set the flags */ for (eBone=G.edbo.first;eBone;eBone=eBone->next){ if (sel) eBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); else eBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); }; allqueue(REDRAWVIEW3D, 0); allqueue(REDRAWBUTSEDIT, 0); allqueue(REDRAWBUTSHEAD, 0); allqueue(REDRAWBUTSCONSTRAINT, 0); countall(); } void join_armature(void) { Object *ob; Base *base, *nextbase; ListBase eblist; EditBone *curbone, *next; float mat[4][4], imat[4][4]; /* Ensure we're not in editmode and that the active object is an armature*/ if(G.obedit) return; ob= OBACT; if(ob->type!=OB_ARMATURE) return; /* Make sure the user wants to continue*/ if(okee("Join selected Armatures")==0) return; /* Put the active armature into editmode and join the bones from the other one*/ #if 1 enter_editmode(); #else baselist.first=baselist.last=0; make_boneList(&baselist, &((bArmature*)ob->data)->bonebase, NULL); #endif 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 */ eblist.first=eblist.last=0; make_boneList (&eblist, &((bArmature*)base->object->data)->bonebase,NULL); /* 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; /* Blank out tranformation data */ curbone->loc[0]=curbone->loc[1]=curbone->loc[2]=0.0F; curbone->size[0]=curbone->size[1]=curbone->size[2]=1.0F; curbone->quat[0]=curbone->quat[1]=curbone->quat[2]=curbone->quat[3]=0.0F; unique_editbone_name (curbone->name); /* Transform the bone */ { float premat[4][4]; float postmat[4][4]; float difmat[4][4]; float imat[4][4]; float temp[4][4]; float delta[3]; /* Get the premat */ VecSubf (delta, curbone->tail, curbone->head); make_boneMatrixvr(temp, delta, curbone->roll); Mat4MulMat4 (premat, temp, mat); Mat4MulVecfl(mat, curbone->head); Mat4MulVecfl(mat, curbone->tail); /* Get the postmat */ VecSubf (delta, curbone->tail, curbone->head); make_boneMatrixvr(postmat, delta, curbone->roll); /* Find the roll */ Mat4Invert (imat, premat); Mat4MulMat4 (difmat, postmat, imat); curbone->roll -=atan(difmat[2][0]/difmat[2][2]); if (difmat[0][0]<0) curbone->roll +=M_PI; } #if 1 BLI_remlink(&eblist, curbone); BLI_addtail(&G.edbo, curbone); #else BLI_remlink(&eblist, curbone); BLI_addtail(&baselist, curbone); #endif } free_and_unlink_base(base); } } } #if 1 exit_editmode(1); #else editbones_to_armature(&baselist, ob); if (baselist.first){ BLI_freelistN (&baselist); } #endif allqueue(REDRAWVIEW3D, 0); } static int editbone_name_exists (char *name){ EditBone *eBone; for (eBone=G.edbo.first; eBone; eBone=eBone->next){ if (!strcmp (name, eBone->name)) return 1; } return 0; } static void unique_editbone_name (char *name){ char tempname[64]; int number; char *dot; if (editbone_name_exists(name)){ /* Strip off the suffix */ dot=strchr(name, '.'); if (dot) *dot=0; for (number = 1; number <=999; number++){ sprintf (tempname, "%s.%03d", name, number); if (!editbone_name_exists(tempname)){ strcpy (name, tempname); return; } } } } void extrude_armature(void) { EditBone *newbone, *curbone, *first=NULL, *partest; TEST_EDITARMATURE; if(okee("Extrude Bone Segments")==0) return; /* Duplicate the necessary bones */ for (curbone = G.edbo.first; ((curbone) && (curbone!=first)); curbone=curbone->next){ if (curbone->flag & (BONE_TIPSEL|BONE_SELECTED)){ newbone = MEM_callocN(sizeof(EditBone), "extrudebone"); VECCOPY (newbone->head, curbone->tail); VECCOPY (newbone->tail, newbone->head); newbone->parent = curbone; newbone->flag = BONE_TIPSEL; newbone->flag |= BONE_QUATROT; newbone->weight= curbone->weight; newbone->dist= curbone->dist; Mat4One(newbone->obmat); /* See if there are any ik children of the parent */ for (partest = G.edbo.first; partest; partest=partest->next){ if ((partest->parent == curbone) && (partest->flag & BONE_IK_TOPARENT)) break; } if (!partest) newbone->flag |= BONE_IK_TOPARENT; strcpy (newbone->name, curbone->name); unique_editbone_name(newbone->name); /* Add the new bone to the list */ BLI_addtail(&G.edbo, newbone); if (!first) first = newbone; } /* Deselect the old bone */ curbone->flag &= ~(BONE_TIPSEL|BONE_SELECTED|BONE_ROOTSEL); } /* Transform the endpoints */ countall(); transform('g'); allqueue(REDRAWBUTSEDIT, 0); allqueue(REDRAWBUTSCONSTRAINT, 0); } void addvert_armature(void) { /* I haven't decided if it will be possible to add bones in this way. For the moment, we'll use Extrude, or explicit parenting. */ } void adduplicate_armature(void) { EditBone *eBone = NULL; EditBone *curBone; EditBone *firstDup=NULL; /* The beginning of the duplicated bones in the edbo list */ countall(); /* Find the selected bones and duplicate them as needed */ for (curBone=G.edbo.first; curBone && curBone!=firstDup; curBone=curBone->next){ if (curBone->flag & BONE_SELECTED){ eBone=MEM_callocN(sizeof(EditBone), "addup_editbone"); eBone->flag |= BONE_SELECTED; /* Copy data from old bone to new bone */ memcpy (eBone, curBone, sizeof(EditBone)); /* Blank out tranformation data */ eBone->loc[0]=eBone->loc[1]=eBone->loc[2]=0.0F; eBone->size[0]=eBone->size[1]=eBone->size[2]=1.0F; eBone->quat[0]=eBone->quat[1]=eBone->quat[2]=eBone->quat[3]=0.0F; curBone->temp = eBone; eBone->temp = curBone; unique_editbone_name (eBone->name); BLI_addtail (&G.edbo, eBone); if (!firstDup) firstDup=eBone; } } if (eBone){ /* Fix the head and tail */ if (eBone->parent && !eBone->parent->flag & BONE_SELECTED){ VecSubf (eBone->tail, eBone->tail, eBone->head); VecSubf (eBone->head, eBone->head, eBone->head); } } /* Run though the list and fix the pointers */ for (curBone=G.edbo.first; curBone && curBone!=firstDup; curBone=curBone->next){ if (curBone->flag & BONE_SELECTED){ eBone=(EditBone*) curBone->temp; /* If this bone has no parent, Set the duplicate->parent to NULL */ if (!curBone->parent){ eBone->parent = NULL; } /* If this bone has a parent that IS selected, Set the duplicate->parent to the curBone->parent->duplicate */ else if (curBone->parent->flag & BONE_SELECTED){ eBone->parent=(EditBone*) curBone->parent->temp; } /* If this bone has a parent that IS not selected, Set the duplicate->parent to the curBone->parent */ else { eBone->parent=(EditBone*) curBone->parent; eBone->flag &= ~BONE_IK_TOPARENT; } } } /* Deselect the old bones and select the new ones */ for (curBone=G.edbo.first; curBone && curBone!=firstDup; curBone=curBone->next){ curBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); } transform('g'); allqueue(REDRAWBUTSEDIT, 0); allqueue(REDRAWBUTSCONSTRAINT, 0); } /* * * POSING FUNCTIONS: Maybe move these to a separate file at some point * * */ void clear_armature(Object *ob, char mode){ Bone *curBone; bArmature *arm; arm=get_armature(ob); if (!arm) return; for (curBone=arm->bonebase.first; curBone; curBone=curBone->next){ clear_armature_children (curBone, ob->pose, mode); } where_is_armature (ob); } static void clear_armature_children (Bone *bone, bPose *pose, char mode){ Bone *curBone; bPoseChannel *chan; if (!bone) return; verify_pose_channel (pose, bone->name); chan=get_pose_channel (pose, bone->name); if (!chan) return; if (bone->flag & BONE_SELECTED){ switch (mode){ case 'r': chan->quat[1]=chan->quat[2]=chan->quat[3]=0.0F; chan->quat[0]=1.0F; break; case 'g': chan->loc[0]=chan->loc[1]=chan->loc[2]=0.0F; break; case 's': chan->size[0]=chan->size[1]=chan->size[2]=1.0F; break; } } for (curBone=bone->childbase.first; curBone; curBone=curBone->next){ clear_armature_children (curBone, pose, mode); } } void mousepose_armature(void) /* Handles right-clicking for selection of bones in armature pose modes. */ { Bone *nearBone; if (!G.obpose) return; nearBone = get_nearest_bone(1); if (nearBone){ if (!(G.qual & LR_SHIFTKEY)){ deselectall_posearmature(0); nearBone->flag|=BONE_SELECTED; select_actionchannel_by_name(G.obpose->action, nearBone->name, 1); } else { if (nearBone->flag & BONE_SELECTED){ nearBone->flag &= ~BONE_SELECTED; select_actionchannel_by_name(G.obpose->action, nearBone->name, 0); } else{ nearBone->flag |= BONE_SELECTED; select_actionchannel_by_name(G.obpose->action, nearBone->name, 1); } }; } allqueue(REDRAWVIEW3D, 0); allqueue(REDRAWACTION, 0); allqueue(REDRAWIPO, 0); /* To force action ipo update */ allqueue(REDRAWBUTSCONSTRAINT, 0); // countall(); rightmouse_transform(); } void make_trans_bones (char mode) /* Used in pose mode */ { bArmature *arm; Bone *curBone; int count=0; transmain=NULL; arm=get_armature (G.obpose); if (!arm) return; if (arm->flag & ARM_RESTPOS){ notice ("Transformation not possible while Rest Position is enabled"); return; } if (!(G.obpose->lay & G.vd->lay)) return; centroid[0]=centroid[1]=centroid[2]=0; apply_pose_armature(arm, G.obpose->pose, 0); where_is_armature (G.obpose); /* Allocate memory for the transformation record */ tottrans= count_bones (arm, BONE_SELECTED, 0); if (!tottrans) return; transmain= MEM_callocN(tottrans*sizeof(TransOb), "bonetransmain"); for (curBone=arm->bonebase.first; curBone; curBone=curBone->next){ count = add_trans_bonechildren (G.obpose, curBone, transmain, count, mode); } tottrans=count; if (tottrans){ centroid[0]/=tottrans; centroid[1]/=tottrans; centroid[2]/=tottrans; Mat4MulVecfl (G.obpose->obmat, centroid); } else{ MEM_freeN (transmain); } return; } static int count_bones (bArmature *arm, int flagmask, int allbones) { int count=0; Bone *curBone; if (!arm) return 0; for (curBone=arm->bonebase.first; curBone; curBone=curBone->next){ count = count_bonechildren (curBone, count, flagmask, allbones); } return count; } static int count_bonechildren (Bone *bone, int incount, int flagmask, int allbones){ Bone *curBone; if (!bone) return incount; if (bone->flag & flagmask || flagmask == 0xFFFFFFFF){ incount++; if (!allbones) return incount; } for (curBone=bone->childbase.first; curBone; curBone=curBone->next){ incount=count_bonechildren (curBone, incount, flagmask, allbones); } return incount; } static int add_trans_bonechildren (Object* ob, Bone* bone, TransOb* buffer, int index, char mode) { Bone *curBone; TransOb *curOb; float parmat[4][4], tempmat[4][4]; float tempobmat[4][4]; float vec[3]; if (!bone) return index; /* We don't let IK children get "grabbed" */ if (bone->flag & BONE_SELECTED){ if (!((mode=='g' || mode=='G') && (bone->flag & BONE_IK_TOPARENT))){ get_bone_root_pos (bone, vec, 1); VecAddf (centroid, centroid, vec); curOb=&buffer[index]; curOb->ob = ob; curOb->rot=NULL; curOb->quat= bone->quat; curOb->size= bone->size; curOb->loc = bone->loc; curOb->data = bone; // FIXME: Dangerous memcpy (curOb->oldquat, bone->quat, sizeof (bone->quat)); memcpy (curOb->oldsize, bone->size, sizeof (bone->size)); memcpy (curOb->oldloc, bone->loc, sizeof (bone->loc)); #if 0 if (bone->parent) get_objectspace_bone_matrix(bone->parent, tempmat, 1, 1); else Mat4One (tempmat); #else /* Get the matrix of this bone minus the usertransform */ Mat4CpyMat4 (tempobmat, bone->obmat); Mat4One (bone->obmat); get_objectspace_bone_matrix(bone, tempmat, 1, 1); Mat4CpyMat4 (bone->obmat, tempobmat); #endif #if 1 Mat4MulMat4 (parmat, tempmat, ob->obmat); /* Original */ /* Get world transform */ get_objectspace_bone_matrix(bone, tempmat, 1, 1); if (ob->parent){ where_is_object(ob->parent); Mat4MulSerie (tempobmat, ob->parent->obmat, ob->obmat, tempmat, NULL, NULL, NULL, NULL, NULL); } else Mat4MulSerie (tempobmat, ob->obmat, tempmat, NULL, NULL, NULL, NULL, NULL, NULL); Mat3CpyMat4 (curOb->axismat, tempobmat); Mat3Ortho(curOb->axismat); #else Mat4MulMat4 (parmat, ob->obmat, tempmat); #endif Mat3CpyMat4 (curOb->parmat, parmat); Mat3Inv (curOb->parinv, curOb->parmat); Mat3CpyMat4 (curOb->obmat, bone->obmat); Mat3Inv (curOb->obinv, curOb->obmat); index++; return index; } } /* Recursively search */ for (curBone = bone->childbase.first; curBone; curBone=curBone->next){ index=add_trans_bonechildren (ob, curBone, buffer, index, mode); } return index; } static void deselect_bonechildren (Bone *bone, int mode) { Bone *curBone; if (!bone) return; if (mode==0) bone->flag &= ~BONE_SELECTED; else if (!(bone->flag & BONE_HIDDEN)) bone->flag |= BONE_SELECTED; select_actionchannel_by_name(G.obpose->action, bone->name, mode); for (curBone=bone->childbase.first; curBone; curBone=curBone->next){ deselect_bonechildren(curBone, mode); } } void deselectall_posearmature (int test){ int selectmode = 0; Bone* curBone; /* Determine if we're selecting or deselecting */ if (test){ if (!count_bones (get_armature(G.obpose), BONE_SELECTED, 0)) selectmode = 1; } /* Set the flags accordingly */ for (curBone=get_armature(G.obpose)->bonebase.first; curBone; curBone=curBone->next) deselect_bonechildren (curBone, selectmode); allqueue(REDRAWVIEW3D, 0); allqueue(REDRAWACTION, 0); } void auto_align_armature(void) /* Sets the roll value of selected bones so that their zaxes point upwards */ { EditBone *ebone; float xaxis[3]={1.0, 0.0, 0.0}, yaxis[3], zaxis[3]={0.0, 0.0, 1.0}, ytest[3]={0.0, -1.0, 0.0}; float targetmat[4][4], imat[4][4]; float curmat[4][4], diffmat[4][4]; float delta[3]; for (ebone = G.edbo.first; ebone; ebone=ebone->next){ if (ebone->flag & BONE_SELECTED){ /* Find the current bone matrix */ VecSubf(delta, ebone->tail, ebone->head); make_boneMatrixvr (curmat, delta, 0.0); /* Make new matrix based on y axis & z-up */ VECCOPY (yaxis, curmat[1]); Mat4One(targetmat); VECCOPY (targetmat[0], xaxis); VECCOPY (targetmat[1], yaxis); VECCOPY (targetmat[2], zaxis); Mat4Ortho(targetmat); /* Find the difference between the two matrices */ Mat4Invert (imat, targetmat); Mat4MulMat4(diffmat, curmat, imat); ebone->roll = atan(diffmat[2][0]/diffmat[2][2]); } } }