Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Poirier <theeth@yahoo.com>2007-11-07 01:29:20 +0300
committerMartin Poirier <theeth@yahoo.com>2007-11-07 01:29:20 +0300
commit0de103c1cdf1e4c40cfad4e233a42a6d1165953d (patch)
treea57039fa592cf0f5af3738a9624d0b0ccbf20ec0 /source/blender/src/editarmature.c
Filling in branch from trunk
Diffstat (limited to 'source/blender/src/editarmature.c')
-rw-r--r--source/blender/src/editarmature.c3144
1 files changed, 3144 insertions, 0 deletions
diff --git a/source/blender/src/editarmature.c b/source/blender/src/editarmature.c
new file mode 100644
index 00000000000..b5675ef9c09
--- /dev/null
+++ b/source/blender/src/editarmature.c
@@ -0,0 +1,3144 @@
+/**
+ * $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 <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "MEM_guardedalloc.h"
+
+#include "BMF_Api.h"
+
+#include "DNA_action_types.h"
+#include "DNA_armature_types.h"
+#include "DNA_constraint_types.h"
+#include "DNA_ID.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_nla_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_space_types.h"
+#include "DNA_userdef_types.h"
+#include "DNA_view3d_types.h"
+#include "DNA_modifier_types.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_arithb.h"
+#include "BLI_editVert.h"
+
+#include "BKE_action.h"
+#include "BKE_armature.h"
+#include "BKE_constraint.h"
+#include "BKE_deform.h"
+#include "BKE_depsgraph.h"
+#include "BKE_DerivedMesh.h"
+#include "BKE_global.h"
+#include "BKE_main.h"
+#include "BKE_object.h"
+#include "BKE_subsurf.h"
+#include "BKE_utildefines.h"
+#include "BKE_modifier.h"
+
+#include "BIF_editaction.h"
+#include "BIF_editmode_undo.h"
+#include "BIF_editdeform.h"
+#include "BIF_editarmature.h"
+#include "BIF_editconstraint.h"
+#include "BIF_gl.h"
+#include "BIF_graphics.h"
+#include "BIF_interface.h"
+#include "BIF_meshlaplacian.h"
+#include "BIF_meshtools.h"
+#include "BIF_poseobject.h"
+#include "BIF_mywindow.h"
+#include "BIF_resources.h"
+#include "BIF_screen.h"
+#include "BIF_space.h"
+#include "BIF_toolbox.h"
+#include "BIF_transform.h"
+
+#include "BDR_editobject.h"
+#include "BDR_drawobject.h"
+
+#include "BSE_edit.h"
+#include "BSE_view.h"
+#include "BSE_trans_types.h"
+
+#include "PIL_time.h"
+
+#include "mydevice.h"
+#include "blendef.h"
+#include "nla.h"
+
+extern float center[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 **************** */
+
+/* converts Bones to EditBone list, used for tools as well */
+void make_boneList(ListBase* list, ListBase *bones, EditBone *parent)
+{
+ EditBone *eBone;
+ Bone *curBone;
+ float delta[3];
+ float premat[3][3];
+ float postmat[3][3];
+ float imat[3][3];
+ float difmat[3][3];
+
+ 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;
+ BLI_strncpy (eBone->name, curBone->name, 32);
+ eBone->flag = curBone->flag;
+
+ /* fix selection flags */
+ if(eBone->flag & BONE_SELECTED) {
+ eBone->flag |= BONE_TIPSEL;
+ if(eBone->parent && (eBone->flag & BONE_CONNECTED))
+ eBone->parent->flag |= BONE_TIPSEL;
+ else
+ eBone->flag |= BONE_ROOTSEL;
+ }
+ else
+ eBone->flag &= ~BONE_ROOTSEL;
+
+ VECCOPY(eBone->head, curBone->arm_head);
+ VECCOPY(eBone->tail, curBone->arm_tail);
+
+ eBone->roll= 0.0;
+
+ /* roll fixing */
+ VecSubf (delta, eBone->tail, eBone->head);
+ vec_roll_to_mat3(delta, 0.0, postmat);
+
+ Mat3CpyMat4(premat, curBone->arm_mat);
+
+ Mat3Inv(imat, postmat);
+ Mat3MulMat3(difmat, imat, premat);
+
+ eBone->roll = atan2(difmat[2][0], difmat[2][2]);
+
+ /* rest of stuff copy */
+ eBone->length= curBone->length;
+ eBone->dist= curBone->dist;
+ eBone->weight= curBone->weight;
+ eBone->xwidth= curBone->xwidth;
+ eBone->zwidth= curBone->zwidth;
+ eBone->ease1= curBone->ease1;
+ eBone->ease2= curBone->ease2;
+ eBone->rad_head= curBone->rad_head;
+ eBone->rad_tail= curBone->rad_tail;
+ eBone->segments = curBone->segments;
+ eBone->layer = curBone->layer;
+
+ BLI_addtail (list, eBone);
+
+ /* Add children if necessary */
+ if (curBone->childbase.first)
+ make_boneList (list, &curBone->childbase, eBone);
+ }
+}
+
+/* nasty stuff for converting roll in editbones into bones */
+/* also sets restposition in armature (arm_mat) */
+static void fix_bonelist_roll (ListBase *bonelist, ListBase *editbonelist)
+{
+ Bone *curBone;
+ EditBone *ebone;
+ float premat[3][3];
+ float postmat[3][3];
+ float difmat[3][3];
+ float imat[3][3];
+ float delta[3];
+
+ for (curBone=bonelist->first; curBone; curBone=curBone->next) {
+ /* sets local matrix and arm_mat (restpos) */
+ where_is_armature_bone(curBone, curBone->parent);
+
+ /* Find the associated editbone */
+ for (ebone = editbonelist->first; ebone; ebone=ebone->next)
+ if ((Bone*)ebone->temp == curBone)
+ break;
+
+ if (ebone) {
+ /* Get the ebone premat */
+ VecSubf (delta, ebone->tail, ebone->head);
+ vec_roll_to_mat3(delta, ebone->roll, premat);
+
+ /* Get the bone postmat */
+ Mat3CpyMat4(postmat, curBone->arm_mat);
+
+ Mat3Inv(imat, premat);
+ Mat3MulMat3(difmat, imat, postmat);
+#if 0
+ printf ("Bone %s\n", curBone->name);
+ printmatrix4 ("premat", premat);
+ printmatrix4 ("postmat", postmat);
+ printmatrix4 ("difmat", difmat);
+ printf ("Roll = %f\n", (-atan2(difmat[2][0], difmat[2][2]) * (180.0/M_PI)));
+#endif
+ curBone->roll = -atan2(difmat[2][0], difmat[2][2]);
+
+ /* and set restposition again */
+ where_is_armature_bone(curBone, curBone->parent);
+ }
+ fix_bonelist_roll (&curBone->childbase, editbonelist);
+ }
+}
+
+/* converts the editbones back to the armature */
+void editbones_to_armature (ListBase *list, Object *ob)
+{
+ bArmature *arm;
+ EditBone *eBone, *neBone;
+ Bone *newBone;
+ Object *obt;
+
+ arm = get_armature(ob);
+ if (!list) return;
+ if (!arm) return;
+
+ /* armature bones */
+ free_bones(arm);
+
+ /* remove zero sized bones, this gives instable restposes */
+ for (eBone=list->first; eBone; eBone= neBone) {
+ float len= VecLenf(eBone->head, eBone->tail);
+ neBone= eBone->next;
+ if(len <= FLT_EPSILON) {
+ EditBone *fBone;
+
+ /* Find any bones that refer to this bone */
+ for (fBone=list->first; fBone; fBone= fBone->next){
+ if (fBone->parent==eBone)
+ fBone->parent= eBone->parent;
+ }
+ printf("Warning; removed zero sized bone: %s\n", eBone->name);
+ BLI_freelinkN (list, eBone);
+ }
+ }
+
+ /* 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 */
+
+ BLI_strncpy (newBone->name, eBone->name, 32);
+ memcpy (newBone->head, eBone->head, sizeof(float)*3);
+ memcpy (newBone->tail, eBone->tail, sizeof(float)*3);
+ newBone->flag= eBone->flag;
+ if(eBone->flag & BONE_ACTIVE) newBone->flag |= BONE_SELECTED; /* important, editbones can be active with only 1 point selected */
+ newBone->roll = 0.0f;
+
+ newBone->weight = eBone->weight;
+ newBone->dist = eBone->dist;
+
+ newBone->xwidth = eBone->xwidth;
+ newBone->zwidth = eBone->zwidth;
+ newBone->ease1= eBone->ease1;
+ newBone->ease2= eBone->ease2;
+ newBone->rad_head= eBone->rad_head;
+ newBone->rad_tail= eBone->rad_tail;
+ newBone->segments= eBone->segments;
+ newBone->layer = eBone->layer;
+
+ }
+
+ /* 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[3][3];
+ float M_parentRest[3][3];
+ float iM_parentRest[3][3];
+ float delta[3];
+
+ /* Get the parent's matrix (rotation only) */
+ VecSubf (delta, eBone->parent->tail, eBone->parent->head);
+ vec_roll_to_mat3(delta, eBone->parent->roll, M_parentRest);
+
+ /* Get this bone's matrix (rotation only) */
+ VecSubf (delta, eBone->tail, eBone->head);
+ vec_roll_to_mat3(delta, eBone->roll, M_boneRest);
+
+ /* Invert the parent matrix */
+ Mat3Inv(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);
+
+ Mat3MulVecfl(iM_parentRest, newBone->head);
+ Mat3MulVecfl(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 */
+ /* also builds restposition again (like where_is_armature) */
+ fix_bonelist_roll (&arm->bonebase, list);
+
+ /* so all users of this armature should get rebuilt */
+ for(obt= G.main->object.first; obt; obt= obt->id.next) {
+ if(obt->data==arm)
+ armature_rebuild_pose(obt, arm);
+ }
+
+ DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA);
+}
+
+
+
+void apply_rot_armature (Object *ob, float mat[3][3])
+{
+ ListBase list;
+ EditBone *ebone;
+ bArmature *arm;
+ float scale = Mat3ToScalef(mat); /* store the scale of the matrix here to use on envelopes */
+ 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){
+ Mat3MulVecfl(mat, ebone->head);
+ Mat3MulVecfl(mat, ebone->tail);
+
+ ebone->rad_head *= scale;
+ ebone->rad_tail *= scale;
+ ebone->dist *= scale;
+ }
+
+ /* Turn the list into an armature */
+ editbones_to_armature(&list, ob);
+
+ /* Free the editbones */
+ if (list.first){
+ BLI_freelistN (&list);
+ }
+}
+
+/* 0 == do center, 1 == center new, 2 == center cursor */
+void docenter_armature (Object *ob, int centermode)
+{
+ ListBase list;
+ EditBone *ebone;
+ bArmature *arm;
+ float cent[3] = {0.0f, 0.0f, 0.0f};
+ float min[3], max[3];
+ float omat[3][3];
+
+ arm = get_armature(ob);
+ if (!arm) return;
+
+ /* Put the armature into editmode */
+ list.first= list.last = NULL;
+ make_boneList(&list, &arm->bonebase, NULL);
+
+ /* Find the centerpoint */
+ if (centermode == 2) {
+ VECCOPY(cent, give_cursor());
+ Mat4Invert(ob->imat, ob->obmat);
+ Mat4MulVecfl(ob->imat, cent);
+ }
+ else {
+ INIT_MINMAX(min, max);
+
+ for (ebone= list.first; ebone; ebone=ebone->next) {
+ DO_MINMAX(ebone->head, min, max);
+ DO_MINMAX(ebone->tail, min, max);
+ }
+
+ cent[0]= (min[0]+max[0])/2.0f;
+ cent[1]= (min[1]+max[1])/2.0f;
+ cent[2]= (min[2]+max[2])/2.0f;
+ }
+
+ /* Do the adjustments */
+ for (ebone= list.first; ebone; ebone=ebone->next){
+ VecSubf(ebone->head, ebone->head, cent);
+ VecSubf(ebone->tail, ebone->tail, cent);
+ }
+
+ /* Turn the list into an armature */
+ editbones_to_armature(&list, ob);
+
+ /* Free the editbones */
+ if (list.first){
+ BLI_freelistN(&list);
+ }
+
+ /* Adjust object location for new centerpoint */
+ if(centermode && G.obedit==0) {
+ Mat3CpyMat4(omat, ob->obmat);
+
+ Mat3MulVecfl(omat, cent);
+ ob->loc[0]+= cent[0];
+ ob->loc[1]+= cent[1];
+ ob->loc[2]+= cent[2];
+ }
+}
+
+/* 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) {
+ bConstraintTypeInfo *cti= constraint_get_typeinfo(con);
+ ListBase targets = {NULL, NULL};
+ bConstraintTarget *ct;
+
+ /* constraint targets */
+ if (cti && cti->get_constraint_targets) {
+ cti->get_constraint_targets(con, &targets);
+
+ for (ct= targets.first; ct; ct= ct->next) {
+ if (ct->tar == srcArm) {
+ if (strcmp(ct->subtarget, "")==0) {
+ ct->tar = tarArm;
+ }
+ else if (strcmp(ct->subtarget, pchan->name)==0) {
+ ct->tar = tarArm;
+ strcpy(ct->subtarget, curbone->name);
+ }
+ }
+ }
+
+ if (cti->flush_constraint_targets)
+ cti->flush_constraint_targets(con, &targets, 0);
+ }
+
+ /* 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) {
+ bConstraintTypeInfo *cti= constraint_get_typeinfo(con);
+ ListBase targets = {NULL, NULL};
+ bConstraintTarget *ct;
+
+ /* constraint targets */
+ if (cti && cti->get_constraint_targets) {
+ cti->get_constraint_targets(con, &targets);
+
+ for (ct= targets.first; ct; ct= ct->next) {
+ if (ct->tar == srcArm) {
+ if (strcmp(ct->subtarget, "")==0) {
+ ct->tar = tarArm;
+ }
+ else if (strcmp(ct->subtarget, pchan->name)==0) {
+ ct->tar = tarArm;
+ strcpy(ct->subtarget, curbone->name);
+ }
+ }
+ }
+
+ if (cti->flush_constraint_targets)
+ cti->flush_constraint_targets(con, &targets, 0);
+ }
+ }
+ }
+
+ /* 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;
+ 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*/
+ /* if(G.obedit) return; */ /* Alredy checked in join_menu() */
+
+ ob= OBACT;
+ if(ob->type!=OB_ARMATURE) return 0;
+ if (object_data_is_libdata(ob)) {
+ error_libdata();
+ return 0;
+ }
+ arm= get_armature(ob);
+
+ /* 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 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 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 */
+ {
+ float premat[4][4];
+ float postmat[4][4];
+ float difmat[4][4];
+ float imat[4][4];
+ float temp[3][3];
+ float delta[3];
+
+ /* Get the premat */
+ VecSubf (delta, curbone->tail, curbone->head);
+ vec_roll_to_mat3(delta, curbone->roll, temp);
+
+ Mat4MulMat34 (premat, temp, mat);
+
+ Mat4MulVecfl(mat, curbone->head);
+ Mat4MulVecfl(mat, curbone->tail);
+
+ /* Get the postmat */
+ 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);
+
+ 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(&ebbase, curbone);
+
+ BLI_remlink(&opose->chanbase, pchan);
+ BLI_addtail(&pose->chanbase, pchan);
+ }
+
+ free_and_unlink_base(base);
+ }
+ }
+ }
+
+ DAG_scene_sort(G.scene); // because we removed object(s)
+
+ editbones_to_armature(&ebbase, ob);
+ if (ebbase.first) BLI_freelistN(&ebbase);
+
+ allqueue(REDRAWVIEW3D, 0);
+ allqueue(REDRAWOOPS, 0);
+ return 1;
+}
+
+/* **************** END tools on Editmode Armature **************** */
+/* **************** PoseMode & EditMode *************************** */
+
+/* only for opengl selection indices */
+Bone *get_indexed_bone (Object *ob, int index)
+{
+ bPoseChannel *pchan;
+ int a= 0;
+
+ if(ob->pose==NULL) return NULL;
+ index>>=16; // bone selection codes use left 2 bytes
+
+ for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next, a++) {
+ if(a==index) return pchan->bone;
+ }
+ return NULL;
+}
+
+/* See if there are any selected bones in this buffer */
+static void *get_bone_from_selectbuffer(Base *base, unsigned int *buffer, short hits, short findunsel)
+{
+ Object *ob= base->object;
+ Bone *bone;
+ EditBone *ebone;
+ void *firstunSel=NULL, *firstSel=NULL, *data;
+ unsigned int hitresult;
+ short i, takeNext=0, sel;
+
+ for (i=0; i< hits; i++){
+ hitresult = buffer[3+(i*4)];
+
+ if (!(hitresult & BONESEL_NOSEL)) { // -1
+ if(hitresult & BONESEL_ANY) { // to avoid including objects in selection
+
+ hitresult &= ~(BONESEL_ANY);
+ /* Determine what the current bone is */
+ if (G.obedit==NULL || base->object!=G.obedit) {
+ /* no singular posemode, so check for correct object */
+ if(base->selcol == (hitresult & 0xFFFF)) {
+ bone = get_indexed_bone(ob, hitresult);
+
+ if (findunsel)
+ sel = (bone->flag & BONE_SELECTED);
+ else
+ sel = !(bone->flag & BONE_SELECTED);
+
+ data = bone;
+ }
+ else {
+ data= NULL;
+ sel= 0;
+ }
+ }
+ else{
+ ebone = BLI_findlink(&G.edbo, hitresult);
+ if (findunsel)
+ sel = (ebone->flag & BONE_SELECTED);
+ else
+ sel = !(ebone->flag & BONE_SELECTED);
+
+ data = ebone;
+ }
+
+ if(data) {
+ if (sel) {
+ if(!firstSel) firstSel= data;
+ takeNext=1;
+ }
+ else {
+ if (!firstunSel)
+ firstunSel=data;
+ if (takeNext)
+ return data;
+ }
+ }
+ }
+ }
+ }
+
+ if (firstunSel)
+ return firstunSel;
+ else
+ return firstSel;
+}
+
+/* used by posemode as well editmode */
+static void *get_nearest_bone (short findunsel)
+{
+ unsigned int buffer[MAXPICKBUF];
+ short hits;
+
+ persp(PERSP_VIEW);
+
+ glInitNames();
+ hits= view3d_opengl_select(buffer, MAXPICKBUF, 0, 0, 0, 0);
+
+ if (hits>0)
+ return get_bone_from_selectbuffer(BASACT, buffer, hits, findunsel);
+
+ return NULL;
+}
+
+/* used by posemode and editmode */
+void select_bone_parent (void)
+{
+ Object *ob;
+ bArmature *arm;
+
+ /* get data */
+ if (G.obedit)
+ ob= G.obedit;
+ else if (OBACT)
+ ob= OBACT;
+ else
+ return;
+ arm= (bArmature *)ob->data;
+
+ /* determine which mode armature is in */
+ if ((!G.obedit) && (ob->flag & OB_POSEMODE)) {
+ /* deal with pose channels */
+ /* channels are sorted on dependency, so the loop below won't result in a flood-select */
+ bPoseChannel *pchan=NULL;
+
+ for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
+ /* check if bone in original selection */
+ if (pchan->bone->flag & BONE_SELECTED) {
+ bPoseChannel *chanpar= pchan->parent;
+
+ /* check if any parent */
+ if ((chanpar) && ((chanpar->bone->flag & BONE_SELECTED)==0)) {
+ chanpar->bone->flag |= BONE_SELECTED;
+ select_actionchannel_by_name (ob->action, pchan->name, 1);
+ }
+ }
+ }
+ }
+ else if (G.obedit) {
+ /* deal with editbones */
+ EditBone *curbone, *parbone, *parpar;
+
+ /* prevent floods */
+ for (curbone= G.edbo.first; curbone; curbone= curbone->next)
+ curbone->temp= NULL;
+
+ for (curbone= G.edbo.first; curbone; curbone= curbone->next) {
+ /* check if bone selected */
+ if ((curbone->flag & BONE_SELECTED) && curbone->temp==NULL) {
+ parbone= curbone->parent;
+
+ /* check if any parent */
+ if ((parbone) && ((parbone->flag & BONE_SELECTED)==0)) {
+ /* select the parent bone */
+ parbone->flag |= (BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL);
+
+ /* check if parent has parent */
+ parpar= parbone->parent;
+
+ if ((parpar) && (parbone->flag & BONE_CONNECTED)) {
+ parpar->flag |= BONE_TIPSEL;
+ }
+ /* tag this bone to not flood selection */
+ parbone->temp= parbone;
+ }
+ }
+ }
+
+ /* to be sure... */
+ for (curbone= G.edbo.first; curbone; curbone= curbone->next)
+ curbone->temp= NULL;
+
+ }
+
+ /* undo + redraw pushes */
+ countall(); // flushes selection!
+
+ allqueue (REDRAWVIEW3D, 0);
+ allqueue (REDRAWBUTSEDIT, 0);
+ allqueue(REDRAWBUTSOBJECT, 0);
+ allqueue(REDRAWOOPS, 0);
+
+ BIF_undo_push("Select Parent");
+}
+
+
+/* **************** END PoseMode & EditMode *************************** */
+/* **************** Posemode stuff ********************** */
+
+
+static void selectconnected_posebonechildren (Object *ob, Bone *bone)
+{
+ Bone *curBone;
+
+ if (!(bone->flag & BONE_CONNECTED))
+ return;
+
+ select_actionchannel_by_name (ob->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 (ob, curBone);
+ }
+}
+
+/* within active object context */
+void selectconnected_posearmature(void)
+{
+ Bone *bone, *curBone, *next;
+ Object *ob= OBACT;
+
+ if(!ob || !ob->pose) return;
+
+ 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 (ob->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_CONNECTED)
+ next=curBone->parent;
+ else
+ next=NULL;
+ }
+
+ /* Select children */
+ for (curBone=bone->childbase.first; curBone; curBone=next){
+ selectconnected_posebonechildren (ob, curBone);
+ }
+
+ countall(); // flushes selection!
+
+ allqueue (REDRAWVIEW3D, 0);
+ allqueue(REDRAWBUTSEDIT, 0);
+ allqueue(REDRAWBUTSOBJECT, 0);
+ allqueue (REDRAWACTION, 0);
+ allqueue(REDRAWOOPS, 0);
+ BIF_undo_push("Select connected");
+
+}
+
+/* **************** END Posemode stuff ********************** */
+/* **************** EditMode stuff ********************** */
+
+/* called in space.c */
+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_CONNECTED)
+ 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_CONNECTED){
+ 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(); // flushes selection!
+
+ allqueue (REDRAWVIEW3D, 0);
+ allqueue (REDRAWBUTSEDIT, 0);
+ allqueue(REDRAWBUTSOBJECT, 0);
+ allqueue(REDRAWOOPS, 0);
+
+ BIF_undo_push("Select connected");
+
+}
+
+/* does bones and points */
+/* note that BONE ROOT only gets drawn for root bones (or without IK) */
+static EditBone * get_nearest_editbonepoint (int findunsel, int *selmask)
+{
+ EditBone *ebone;
+ unsigned int buffer[MAXPICKBUF];
+ unsigned int hitresult, besthitresult=BONESEL_NOSEL;
+ int i, mindep= 4;
+ short hits, mval[2];
+
+ persp(PERSP_VIEW);
+
+ glInitNames();
+
+ getmouseco_areawin(mval);
+ hits= view3d_opengl_select(buffer, MAXPICKBUF, mval[0]-5, mval[1]-5, mval[0]+5, mval[1]+5);
+ if(hits==0)
+ hits= view3d_opengl_select(buffer, MAXPICKBUF, mval[0]-12, mval[1]-12, mval[0]+12, mval[1]+12);
+
+ /* See if there are any selected bones in this group */
+ if (hits>0) {
+
+ if(hits==1) {
+ if (!(buffer[3] & BONESEL_NOSEL))
+ besthitresult= buffer[3];
+ }
+ else {
+ for (i=0; i< hits; i++) {
+ hitresult= buffer[3+(i*4)];
+ if (!(hitresult & BONESEL_NOSEL)) {
+ int dep;
+
+ ebone = BLI_findlink(&G.edbo, hitresult & ~BONESEL_ANY);
+
+ /* clicks on bone points get advantage */
+ if( hitresult & (BONESEL_ROOT|BONESEL_TIP)) {
+ /* but also the unselected one */
+ if(findunsel) {
+ if( (hitresult & BONESEL_ROOT) && (ebone->flag & BONE_ROOTSEL)==0)
+ dep= 1;
+ else if( (hitresult & BONESEL_TIP) && (ebone->flag & BONE_TIPSEL)==0)
+ dep= 1;
+ else
+ dep= 2;
+ }
+ else dep= 2;
+ }
+ else {
+ /* bone found */
+ if(findunsel) {
+ if((ebone->flag & BONE_SELECTED)==0)
+ dep= 2;
+ else
+ dep= 3;
+ }
+ else dep= 3;
+ }
+ if(dep < mindep) {
+ mindep= dep;
+ besthitresult= hitresult;
+ }
+ }
+ }
+ }
+
+ if (!(besthitresult & BONESEL_NOSEL)) {
+
+ ebone= BLI_findlink(&G.edbo, besthitresult & ~BONESEL_ANY);
+
+ *selmask = 0;
+ if (besthitresult & BONESEL_ROOT)
+ *selmask |= BONE_ROOTSEL;
+ if (besthitresult & BONESEL_TIP)
+ *selmask |= BONE_TIPSEL;
+ if (besthitresult & BONESEL_BONE)
+ *selmask |= BONE_SELECTED;
+ return ebone;
+ }
+ }
+ *selmask = 0;
+ return NULL;
+}
+
+static void delete_bone(EditBone* exBone)
+{
+ EditBone *curBone;
+
+ /* 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_CONNECTED;
+ }
+ }
+
+ BLI_freelinkN (&G.edbo,exBone);
+}
+
+/* only editmode! */
+void delete_armature(void)
+{
+ bArmature *arm= G.obedit->data;
+ EditBone *curBone, *next;
+ bConstraint *con;
+
+ TEST_EDITARMATURE;
+ if (okee("Erase selected bone(s)")==0) return;
+
+ /* First erase any associated pose channel */
+ if (G.obedit->pose) {
+ bPoseChannel *chan, *next;
+ for (chan=G.obedit->pose->chanbase.first; chan; chan=next) {
+ next= chan->next;
+ curBone = editbone_name_exists (&G.edbo, chan->name);
+
+ if (curBone && (curBone->flag & BONE_SELECTED) && (arm->layer & curBone->layer)) {
+ free_constraints(&chan->constraints);
+ BLI_freelinkN (&G.obedit->pose->chanbase, chan);
+ }
+ else {
+ for (con= chan->constraints.first; con; con= con->next) {
+ bConstraintTypeInfo *cti= constraint_get_typeinfo(con);
+ ListBase targets = {NULL, NULL};
+ bConstraintTarget *ct;
+
+ if (cti && cti->get_constraint_targets) {
+ cti->get_constraint_targets(con, &targets);
+
+ for (ct= targets.first; ct; ct= ct->next) {
+ if (ct->tar == G.obedit) {
+ if (ct->subtarget[0]) {
+ curBone = editbone_name_exists(&G.edbo, ct->subtarget);
+ if (curBone && (curBone->flag & BONE_SELECTED) && (arm->layer & curBone->layer)) {
+ con->flag |= CONSTRAINT_DISABLE;
+ ct->subtarget[0]= 0;
+ }
+ }
+ }
+ }
+
+ if (cti->flush_constraint_targets)
+ cti->flush_constraint_targets(con, &targets, 0);
+ }
+ }
+ }
+ }
+ }
+
+
+ for (curBone=G.edbo.first;curBone;curBone=next) {
+ next=curBone->next;
+ if (arm->layer & curBone->layer)
+ if (curBone->flag & BONE_SELECTED)
+ delete_bone(curBone);
+ }
+
+
+ allqueue(REDRAWVIEW3D, 0);
+ allqueue(REDRAWBUTSEDIT, 0);
+ allqueue(REDRAWBUTSOBJECT, 0);
+ allqueue(REDRAWOOPS, 0);
+ countall(); // flushes selection!
+
+ BIF_undo_push("Delete bone(s)");
+}
+
+/* context: editmode armature */
+void mouse_armature(void)
+{
+ EditBone *nearBone = NULL, *ebone;
+ int selmask;
+
+ nearBone= get_nearest_editbonepoint(1, &selmask);
+ if (nearBone) {
+
+ if (!(G.qual & LR_SHIFTKEY)) {
+ deselectall_armature(0, 0);
+ }
+
+ /* by definition the non-root connected bones have no root point drawn,
+ so a root selection needs to be delivered to the parent tip,
+ countall() (bad location) flushes these flags */
+
+ if(selmask & BONE_SELECTED) {
+ if(nearBone->parent && (nearBone->flag & BONE_CONNECTED)) {
+ /* click in a chain */
+ if(G.qual & LR_SHIFTKEY) {
+ /* hold shift inverts this bone's selection */
+ if(nearBone->flag & BONE_SELECTED) {
+ /* deselect this bone */
+ nearBone->flag &= ~(BONE_TIPSEL|BONE_SELECTED);
+ /* only deselect parent tip if it is not selected */
+ if(!(nearBone->parent->flag & BONE_SELECTED))
+ nearBone->parent->flag &= ~BONE_TIPSEL;
+ }
+ else {
+ /* select this bone */
+ nearBone->flag |= BONE_TIPSEL;
+ nearBone->parent->flag |= BONE_TIPSEL;
+ }
+ }
+ else {
+ /* select this bone */
+ nearBone->flag |= BONE_TIPSEL;
+ nearBone->parent->flag |= BONE_TIPSEL;
+ }
+ }
+ else {
+ if(G.qual & LR_SHIFTKEY) {
+ /* hold shift inverts this bone's selection */
+ if(nearBone->flag & BONE_SELECTED)
+ nearBone->flag &= ~(BONE_TIPSEL|BONE_ROOTSEL);
+ else
+ nearBone->flag |= (BONE_TIPSEL|BONE_ROOTSEL);
+ }
+ else nearBone->flag |= (BONE_TIPSEL|BONE_ROOTSEL);
+ }
+ }
+ else {
+ if ((G.qual & LR_SHIFTKEY) && (nearBone->flag & selmask))
+ nearBone->flag &= ~selmask;
+ else
+ nearBone->flag |= selmask;
+ }
+
+ countall(); // flushes selection!
+
+ if(nearBone) {
+ /* then now check for active status */
+ for (ebone=G.edbo.first;ebone;ebone=ebone->next) ebone->flag &= ~BONE_ACTIVE;
+ if(nearBone->flag & BONE_SELECTED) nearBone->flag |= BONE_ACTIVE;
+ }
+
+ allqueue(REDRAWVIEW3D, 0);
+ allqueue(REDRAWBUTSEDIT, 0);
+ allqueue(REDRAWBUTSOBJECT, 0);
+ allqueue(REDRAWOOPS, 0);
+ }
+
+ rightmouse_transform();
+}
+
+void free_editArmature(void)
+{
+
+ /* Clear the editbones list */
+ if (G.edbo.first){
+ BLI_freelistN (&G.edbo);
+ }
+}
+
+void remake_editArmature(void)
+{
+ if(okee("Reload original data")==0) return;
+
+ make_editArmature();
+ allqueue(REDRAWVIEW3D, 0);
+ allqueue(REDRAWOOPS, 0);
+ allqueue(REDRAWBUTSHEAD, 0);
+ allqueue(REDRAWBUTSEDIT, 0);
+ allqueue(REDRAWBUTSOBJECT, 0);
+
+// BIF_undo_push("Delete bone");
+
+}
+
+/* Put object in EditMode */
+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);
+}
+
+/* put EditMode back in Object */
+void load_editArmature(void)
+{
+ bArmature *arm;
+
+ arm= get_armature(G.obedit);
+ if (!arm) return;
+
+ editbones_to_armature(&G.edbo, G.obedit);
+}
+
+/* toggle==0: deselect
+ toggle==1: swap
+ toggle==2: only active tag
+*/
+void deselectall_armature(int toggle, int doundo)
+{
+ bArmature *arm= G.obedit->data;
+ EditBone *eBone;
+ int sel=1;
+
+ if(toggle==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(arm->layer & eBone->layer) {
+ if (eBone->flag & (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL)){
+ sel=0;
+ break;
+ }
+// }
+ }
+ }
+ else sel= toggle;
+
+ /* Set the flags */
+ for (eBone=G.edbo.first;eBone;eBone=eBone->next){
+ if (sel==1) {
+ if(arm->layer & eBone->layer && (eBone->flag & BONE_HIDDEN_A)==0) {
+ eBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+ if(eBone->parent)
+ eBone->parent->flag |= (BONE_TIPSEL);
+ }
+ }
+ else if (sel==2)
+ eBone->flag &= ~(BONE_ACTIVE);
+ else
+ eBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL | BONE_ACTIVE);
+ }
+
+ allqueue(REDRAWVIEW3D, 0);
+ allqueue(REDRAWBUTSEDIT, 0);
+ allqueue(REDRAWBUTSOBJECT, 0);
+ allqueue(REDRAWOOPS, 0);
+
+ countall(); // flushes selection!
+ if (doundo) {
+ if (sel==1) BIF_undo_push("Select All");
+ else BIF_undo_push("Deselect All");
+ }
+}
+
+/* Sets the roll value of selected bones, depending on the mode
+ * mode == 0: their z-axes point upwards
+ * mode == 1: their z-axes point towards 3d-cursor
+ */
+void auto_align_armature(short mode)
+{
+ bArmature *arm= G.obedit->data;
+ EditBone *ebone;
+ float delta[3];
+ float curmat[3][3];
+ float *cursor= give_cursor();
+
+ for (ebone = G.edbo.first; ebone; ebone=ebone->next) {
+ if(arm->layer & ebone->layer) {
+ if (ebone->flag & BONE_SELECTED) {
+ /* specific method used to calculate roll depends on mode */
+ if (mode == 1) {
+ /* Z-Axis point towards cursor */
+ float mat[4][4], tmat[4][4], imat[4][4];
+ float rmat[4][4], rot[3];
+ float vec[3];
+
+ /* find the current bone matrix as a 4x4 matrix (in Armature Space) */
+ VecSubf(delta, ebone->tail, ebone->head);
+ vec_roll_to_mat3(delta, ebone->roll, curmat);
+ Mat4CpyMat3(mat, curmat);
+ VECCOPY(mat[3], ebone->head);
+
+ /* multiply bone-matrix by object matrix (so that bone-matrix is in WorldSpace) */
+ Mat4MulMat4(tmat, mat, G.obedit->obmat);
+ Mat4Invert(imat, tmat);
+
+ /* find position of cursor relative to bone */
+ VecMat4MulVecfl(vec, imat, cursor);
+
+ /* check that cursor is in usable position */
+ if ((IS_EQ(vec[0], 0)==0) && (IS_EQ(vec[2], 0)==0)) {
+ /* Compute a rotation matrix around y */
+ rot[1] = atan2(vec[0], vec[2]);
+ rot[0] = rot[2] = 0.0f;
+ EulToMat4(rot, rmat);
+
+ /* Multiply the bone matrix by rotation matrix. This should be new bone-matrix */
+ Mat4MulMat4(tmat, rmat, mat);
+ Mat3CpyMat4(curmat, tmat);
+
+ /* Now convert from new bone-matrix, back to a roll value (in radians) */
+ mat3_to_vec_roll(curmat, delta, &ebone->roll);
+ }
+ }
+ else {
+ /* Z-Axis Point Up */
+ float xaxis[3]={1.0, 0.0, 0.0}, yaxis[3], zaxis[3]={0.0, 0.0, 1.0};
+ float targetmat[3][3], imat[3][3], diffmat[3][3];
+
+ /* Find the current bone matrix */
+ VecSubf(delta, ebone->tail, ebone->head);
+ vec_roll_to_mat3(delta, 0.0, curmat);
+
+ /* Make new matrix based on y axis & z-up */
+ VECCOPY (yaxis, curmat[1]);
+
+ Mat3One(targetmat);
+ VECCOPY (targetmat[0], xaxis);
+ VECCOPY (targetmat[1], yaxis);
+ VECCOPY (targetmat[2], zaxis);
+ Mat3Ortho(targetmat);
+
+ /* Find the difference between the two matrices */
+ Mat3Inv(imat, targetmat);
+ Mat3MulMat3(diffmat, imat, curmat);
+
+ ebone->roll = atan2(diffmat[2][0], diffmat[2][2]);
+ }
+ }
+ }
+ }
+}
+
+/* **************** undo for armatures ************** */
+
+static void undoBones_to_editBones(void *lbv)
+{
+ ListBase *lb= lbv;
+ EditBone *ebo, *newebo;
+
+ BLI_freelistN(&G.edbo);
+
+ /* copy */
+ for(ebo= lb->first; ebo; ebo= ebo->next) {
+ newebo= MEM_dupallocN(ebo);
+ ebo->temp= newebo;
+ BLI_addtail(&G.edbo, newebo);
+ }
+
+ /* set pointers */
+ for(newebo= G.edbo.first; newebo; newebo= newebo->next) {
+ if(newebo->parent) newebo->parent= newebo->parent->temp;
+ }
+ /* be sure they dont hang ever */
+ for(newebo= G.edbo.first; newebo; newebo= newebo->next) {
+ newebo->temp= NULL;
+ }
+}
+
+static void *editBones_to_undoBones(void)
+{
+ ListBase *lb;
+ EditBone *ebo, *newebo;
+
+ lb= MEM_callocN(sizeof(ListBase), "listbase undo");
+
+ /* copy */
+ for(ebo= G.edbo.first; ebo; ebo= ebo->next) {
+ newebo= MEM_dupallocN(ebo);
+ ebo->temp= newebo;
+ BLI_addtail(lb, newebo);
+ }
+
+ /* set pointers */
+ for(newebo= lb->first; newebo; newebo= newebo->next) {
+ if(newebo->parent) newebo->parent= newebo->parent->temp;
+ }
+
+ return lb;
+}
+
+static void free_undoBones(void *lbv)
+{
+ ListBase *lb= lbv;
+
+ BLI_freelistN(lb);
+ MEM_freeN(lb);
+}
+
+/* and this is all the undo system needs to know */
+void undo_push_armature(char *name)
+{
+ undo_editmode_push(name, free_undoBones, undoBones_to_editBones, editBones_to_undoBones);
+}
+
+
+
+/* **************** END EditMode stuff ********************** */
+/* *************** Adding stuff in editmode *************** */
+
+/* default bone add, returns it selected, but without tail set */
+static EditBone *add_editbone(char *name)
+{
+ bArmature *arm= G.obedit->data;
+
+ EditBone *bone= MEM_callocN(sizeof(EditBone), "eBone");
+
+ BLI_strncpy (bone->name, name, 32);
+ unique_editbone_name(&G.edbo, bone->name);
+
+ BLI_addtail(&G.edbo, bone);
+
+ bone->flag |= BONE_TIPSEL;
+ bone->weight= 1.0F;
+ bone->dist= 0.25F;
+ bone->xwidth= 0.1;
+ bone->zwidth= 0.1;
+ bone->ease1= 1.0;
+ bone->ease2= 1.0;
+ bone->rad_head= 0.10;
+ bone->rad_tail= 0.05;
+ bone->segments= 1;
+ bone->layer= arm->layer;
+
+ return bone;
+}
+
+static void add_primitive_bone(Object *ob, short newob)
+{
+ float obmat[3][3], curs[3], viewmat[3][3], totmat[3][3], imat[3][3];
+ EditBone *bone;
+
+ VECCOPY (curs, give_cursor());
+
+ /* Get inverse point for head and orientation for tail */
+ Mat4Invert(G.obedit->imat, G.obedit->obmat);
+ Mat4MulVecfl(G.obedit->imat, curs);
+
+ if ( !(newob) || U.flag & USER_ADD_VIEWALIGNED) Mat3CpyMat4(obmat, G.vd->viewmat);
+ else Mat3One(obmat);
+
+ Mat3CpyMat4(viewmat, G.obedit->obmat);
+ Mat3MulMat3(totmat, obmat, viewmat);
+ Mat3Inv(imat, totmat);
+
+ deselectall_armature(0, 0);
+
+ /* Create a bone */
+ bone= add_editbone("Bone");
+
+ VECCOPY(bone->head, curs);
+
+ if ( !(newob) || U.flag & USER_ADD_VIEWALIGNED)
+ VecAddf(bone->tail, bone->head, imat[1]); // bone with unit length 1
+ else
+ VecAddf(bone->tail, bone->head, imat[2]); // bone with unit length 1, pointing up Z
+
+}
+
+void add_primitiveArmature(int type)
+{
+ short newob=0;
+
+ 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;
+
+ G.f &= ~(G_VERTEXPAINT+G_TEXTUREPAINT+G_WEIGHTPAINT);
+ setcursor_space(SPACE_VIEW3D, CURSOR_STD);
+
+ 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);
+ newob=1;
+ }
+
+ /* no primitive support yet */
+ add_primitive_bone(G.obedit, newob);
+
+ countall(); // flushes selection!
+
+ if ( (newob) && !(U.flag & USER_ADD_EDITMODE)) {
+ exit_editmode(2);
+ }
+
+ allqueue(REDRAWALL, 0);
+ BIF_undo_push("Add primitive");
+}
+
+/* the ctrl-click method */
+void addvert_armature(void)
+{
+ bArmature *arm= G.obedit->data;
+ EditBone *ebone, *newbone, *flipbone;
+ float *curs, mat[3][3],imat[3][3];
+ int a, to_root= 0;
+
+ TEST_EDITARMATURE;
+
+ /* find the active or selected bone */
+ for (ebone = G.edbo.first; ebone; ebone=ebone->next)
+ if(arm->layer & ebone->layer)
+ if(ebone->flag & (BONE_ACTIVE|BONE_TIPSEL)) break;
+
+ if(ebone==NULL) {
+ for (ebone = G.edbo.first; ebone; ebone=ebone->next)
+ if(arm->layer & ebone->layer)
+ if(ebone->flag & (BONE_ACTIVE|BONE_ROOTSEL)) break;
+
+ if(ebone==NULL)
+ return;
+ to_root= 1;
+ }
+
+ deselectall_armature(0, 0);
+
+ /* we re-use code for mirror editing... */
+ flipbone= NULL;
+ if(arm->flag & ARM_MIRROR_EDIT)
+ flipbone= armature_bone_get_mirrored(ebone);
+
+ for(a=0; a<2; a++) {
+ if(a==1) {
+ if(flipbone==NULL)
+ break;
+ else {
+ SWAP(EditBone *, flipbone, ebone);
+ }
+ }
+
+ newbone= add_editbone(ebone->name);
+ newbone->flag |= BONE_ACTIVE;
+
+ if(to_root) {
+ VECCOPY(newbone->head, ebone->head);
+ newbone->rad_head= ebone->rad_tail;
+ newbone->parent= ebone->parent;
+ }
+ else {
+ VECCOPY(newbone->head, ebone->tail);
+ newbone->rad_head= ebone->rad_tail;
+ newbone->parent= ebone;
+ newbone->flag |= BONE_CONNECTED;
+ }
+
+ curs= give_cursor();
+ VECCOPY(newbone->tail, curs);
+ VecSubf(newbone->tail, newbone->tail, G.obedit->obmat[3]);
+
+ if(a==1)
+ newbone->tail[0]= -newbone->tail[0];
+
+ Mat3CpyMat4(mat, G.obedit->obmat);
+ Mat3Inv(imat, mat);
+ Mat3MulVecfl(imat, newbone->tail);
+
+ newbone->length= VecLenf(newbone->head, newbone->tail);
+ newbone->rad_tail= newbone->length*0.05f;
+ newbone->dist= newbone->length*0.25f;
+
+ }
+
+
+ countall();
+
+ BIF_undo_push("Add Bone");
+ allqueue(REDRAWVIEW3D, 0);
+
+ while(get_mbut()&R_MOUSE);
+}
+
+
+static EditBone *get_named_editbone(char *name)
+{
+ EditBone *eBone;
+
+ if (name)
+ for (eBone=G.edbo.first; eBone; eBone=eBone->next){
+ if (!strcmp (name, eBone->name))
+ return eBone;
+ }
+
+ return NULL;
+}
+
+static void update_dup_subtarget(EditBone *dupBone)
+{
+ /* If an edit bone has been duplicated, lets
+ * update it's constraints if the subtarget
+ * they point to has also been duplicated
+ */
+ EditBone *oldtarget, *newtarget;
+ bPoseChannel *chan;
+ bConstraint *curcon;
+ ListBase *conlist;
+
+
+ if ( (chan = verify_pose_channel(OBACT->pose, dupBone->name)) ) {
+ if ( (conlist = &chan->constraints) ) {
+ for (curcon = conlist->first; curcon; curcon=curcon->next) {
+ /* does this constraint have a subtarget in
+ * this armature?
+ */
+ bConstraintTypeInfo *cti= constraint_get_typeinfo(curcon);
+ ListBase targets = {NULL, NULL};
+ bConstraintTarget *ct;
+
+ if (cti && cti->get_constraint_targets) {
+ cti->get_constraint_targets(curcon, &targets);
+
+ for (ct= targets.first; ct; ct= ct->next) {
+ if ((ct->tar == G.obedit) && (ct->subtarget[0])) {
+ oldtarget = get_named_editbone(ct->subtarget);
+ if (oldtarget) {
+ /* was the subtarget bone duplicated too? If
+ * so, update the constraint to point at the
+ * duplicate of the old subtarget.
+ */
+ if (oldtarget->flag & BONE_SELECTED){
+ newtarget = (EditBone *) oldtarget->temp;
+ strcpy(ct->subtarget, newtarget->name);
+ }
+ }
+ }
+ }
+
+ if (cti->flush_constraint_targets)
+ cti->flush_constraint_targets(curcon, &targets, 0);
+ }
+ }
+ }
+ }
+}
+
+
+void adduplicate_armature(void)
+{
+ bArmature *arm= G.obedit->data;
+ EditBone *eBone = NULL;
+ EditBone *curBone;
+ EditBone *firstDup=NULL; /* The beginning of the duplicated bones in the edbo list */
+
+ countall(); // flushes selection!
+
+ /* Find the selected bones and duplicate them as needed */
+ for (curBone=G.edbo.first; curBone && curBone!=firstDup; curBone=curBone->next){
+ if(arm->layer & curBone->layer) {
+ 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));
+
+ curBone->temp = eBone;
+ eBone->temp = curBone;
+
+ unique_editbone_name (&G.edbo, eBone->name);
+ BLI_addtail (&G.edbo, eBone);
+ if (!firstDup)
+ firstDup=eBone;
+
+ /* Lets duplicate the list of constraints that the
+ * current bone has.
+ */
+ if (OBACT->pose) {
+ bPoseChannel *chanold, *channew;
+ ListBase *listold, *listnew;
+
+ chanold = verify_pose_channel (OBACT->pose, curBone->name);
+ if (chanold) {
+ listold = &chanold->constraints;
+ if (listold) {
+ /* WARNING: this creates a new posechannel, but there will not be an attached bone
+ * yet as the new bones created here are still 'EditBones' not 'Bones'.
+ */
+ channew =
+ verify_pose_channel(OBACT->pose, eBone->name);
+ if (channew) {
+ /* copy transform locks */
+ channew->protectflag = chanold->protectflag;
+
+ /* ik (dof) settings */
+ channew->ikflag = chanold->ikflag;
+ VECCOPY(channew->limitmin, chanold->limitmin);
+ VECCOPY(channew->limitmax, chanold->limitmax);
+ VECCOPY(channew->stiffness, chanold->stiffness);
+ channew->ikstretch= chanold->ikstretch;
+
+ /* constraints */
+ listnew = &channew->constraints;
+ copy_constraints (listnew, listold);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* Run though the list and fix the pointers */
+ for (curBone=G.edbo.first; curBone && curBone!=firstDup; curBone=curBone->next){
+ if(arm->layer & curBone->layer) {
+ 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_CONNECTED;
+ }
+
+ /* Lets try to fix any constraint subtargets that might
+ have been duplicated */
+ update_dup_subtarget(eBone);
+ }
+ }
+ }
+
+ /* Deselect the old bones and select the new ones */
+
+ for (curBone=G.edbo.first; curBone && curBone!=firstDup; curBone=curBone->next){
+ if(arm->layer & curBone->layer)
+ curBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL | BONE_ACTIVE);
+ }
+
+ BIF_TransformSetUndo("Add Duplicate");
+ initTransform(TFM_TRANSLATION, CTX_NO_PET);
+ Transform();
+
+ allqueue(REDRAWBUTSEDIT, 0);
+ allqueue(REDRAWBUTSOBJECT, 0);
+ allqueue(REDRAWOOPS, 0);
+}
+
+
+
+/* *************** END Adding stuff in editmode *************** */
+/* *************** Tools in editmode *********** */
+
+
+void hide_selected_armature_bones(void)
+{
+ bArmature *arm= G.obedit->data;
+ EditBone *ebone;
+
+ for (ebone = G.edbo.first; ebone; ebone=ebone->next){
+ if(arm->layer & ebone->layer) {
+ if(ebone->flag & (BONE_SELECTED)) {
+ ebone->flag &= ~(BONE_TIPSEL|BONE_SELECTED|BONE_ROOTSEL|BONE_ACTIVE);
+ ebone->flag |= BONE_HIDDEN_A;
+ }
+ }
+ }
+ countall();
+ allqueue(REDRAWVIEW3D, 0);
+ allqueue(REDRAWBUTSEDIT, 0);
+ BIF_undo_push("Hide Bones");
+}
+
+void hide_unselected_armature_bones(void)
+{
+ EditBone *ebone;
+
+ for (ebone = G.edbo.first; ebone; ebone=ebone->next){
+ bArmature *arm= G.obedit->data;
+ if(arm->layer & ebone->layer) {
+ if(ebone->flag & (BONE_TIPSEL|BONE_SELECTED|BONE_ROOTSEL));
+ else {
+ ebone->flag &= ~BONE_ACTIVE;
+ ebone->flag |= BONE_HIDDEN_A;
+ }
+ }
+ }
+ countall();
+ allqueue(REDRAWVIEW3D, 0);
+ allqueue(REDRAWBUTSEDIT, 0);
+ BIF_undo_push("Hide Unselected Bones");
+}
+
+void show_all_armature_bones(void)
+{
+ EditBone *ebone;
+
+ for (ebone = G.edbo.first; ebone; ebone=ebone->next){
+ bArmature *arm= G.obedit->data;
+ if(arm->layer & ebone->layer) {
+ if(ebone->flag & BONE_HIDDEN_A) {
+ ebone->flag |= (BONE_TIPSEL|BONE_SELECTED|BONE_ROOTSEL);
+ ebone->flag &= ~BONE_HIDDEN_A;
+ }
+ }
+ }
+ countall();
+ allqueue(REDRAWVIEW3D, 0);
+ allqueue(REDRAWBUTSEDIT, 0);
+ BIF_undo_push("Reveal Bones");
+}
+
+void make_bone_parent(void)
+{
+ bArmature *arm= G.obedit->data;
+ EditBone *actbone, *ebone, *selbone;
+ short allchildbones= 0, foundselbone= 0;
+ float offset[3];
+ short val;
+
+ /* find active bone to parent to */
+ for (actbone = G.edbo.first; actbone; actbone=actbone->next) {
+ if (arm->layer & actbone->layer) {
+ if (actbone->flag & BONE_ACTIVE)
+ break;
+ }
+ }
+ if (actbone == NULL) {
+ error("Needs an active bone");
+ return;
+ }
+
+ /* find selected bones */
+ for (ebone = G.edbo.first; ebone; ebone=ebone->next) {
+ if (arm->layer & ebone->layer) {
+ if ((ebone->flag & BONE_SELECTED) && (ebone != actbone)) {
+ foundselbone++;
+ if (ebone->parent != actbone) allchildbones= 1;
+ }
+ }
+ }
+ /* abort if no selected bones, and active bone doesn't have a parent to work with instead */
+ if (foundselbone==0 && actbone->parent==NULL) {
+ error("Need selected bone(s)");
+ return;
+ }
+
+ /* 'Keep Offset' option is only displayed if it's likely to be useful */
+ if (allchildbones)
+ val= pupmenu("Make Parent%t|Connected%x1|Keep Offset%x2");
+ else
+ val= pupmenu("Make Parent%t|Connected%x1");
+
+ if (val < 1) return;
+
+ if (foundselbone==0 && actbone->parent) {
+ /* When only the active bone is selected, and it has a parent,
+ * connect it to the parent, as that is the only possible outcome.
+ */
+ actbone->flag |= BONE_CONNECTED;
+ VECCOPY(actbone->head, actbone->parent->tail);
+ actbone->rad_head= actbone->parent->rad_tail;
+ }
+ else {
+ /* loop through all editbones, parenting all selected bones to the active bone */
+ for (selbone = G.edbo.first; selbone; selbone=selbone->next) {
+ if (arm->layer & selbone->layer) {
+ if ((selbone->flag & BONE_SELECTED) && (selbone!=actbone)) {
+ /* if selbone had a parent we clear parent tip */
+ if (selbone->parent && (selbone->flag & BONE_CONNECTED))
+ selbone->parent->flag &= ~(BONE_TIPSEL);
+
+ /* make actbone the parent of selbone */
+ selbone->parent= actbone;
+
+ /* in actbone tree we cannot have a loop */
+ for (ebone= actbone->parent; ebone; ebone= ebone->parent) {
+ if (ebone->parent==selbone) {
+ ebone->parent= NULL;
+ ebone->flag &= ~BONE_CONNECTED;
+ }
+ }
+
+ if (val == 1) {
+ /* Connected: Child bones will be moved to the parent tip */
+ selbone->flag |= BONE_CONNECTED;
+ VecSubf(offset, actbone->tail, selbone->head);
+
+ VECCOPY(selbone->head, actbone->tail);
+ selbone->rad_head= actbone->rad_tail;
+
+ VecAddf(selbone->tail, selbone->tail, offset);
+
+ /* offset for all its children */
+ for (ebone = G.edbo.first; ebone; ebone=ebone->next) {
+ EditBone *par;
+
+ for (par= ebone->parent; par; par= par->parent) {
+ if (par==selbone) {
+ VecAddf(ebone->head, ebone->head, offset);
+ VecAddf(ebone->tail, ebone->tail, offset);
+ break;
+ }
+ }
+ }
+ }
+ else {
+ /* Offset: Child bones will retain their distance from the parent tip */
+ selbone->flag &= ~BONE_CONNECTED;
+ }
+ }
+
+ }
+ }
+ }
+
+ countall(); /* checks selection */
+ allqueue(REDRAWVIEW3D, 0);
+ allqueue(REDRAWBUTSEDIT, 0);
+ allqueue(REDRAWOOPS, 0);
+ BIF_undo_push("Make Parent");
+
+ return;
+}
+
+void clear_bone_parent(void)
+{
+ bArmature *arm= G.obedit->data;
+ EditBone *ebone;
+ short val;
+
+ val= pupmenu("Clear Parent%t|Clear Parent%x1|Disconnect Bone%x2");
+
+ if(val<1) return;
+ for (ebone = G.edbo.first; ebone; ebone=ebone->next) {
+ if(arm->layer & ebone->layer) {
+ if(ebone->flag & BONE_SELECTED) {
+ if(ebone->parent) {
+ /* for nice selection */
+ ebone->parent->flag &= ~(BONE_TIPSEL);
+
+ if(val==1) ebone->parent= NULL;
+ ebone->flag &= ~BONE_CONNECTED;
+ }
+ }
+ }
+ }
+ countall(); // checks selection
+ allqueue(REDRAWVIEW3D, 0);
+ allqueue(REDRAWBUTSEDIT, 0);
+ allqueue(REDRAWOOPS, 0);
+ BIF_undo_push("Clear Parent");
+}
+
+
+static EditBone *editbone_name_exists (ListBase *ebones, char *name)
+{
+ EditBone *eBone;
+
+ if (ebones == NULL) ebones = &G.edbo;
+
+ for (eBone=ebones->first; eBone; eBone=eBone->next){
+ if (!strcmp (name, eBone->name))
+ return eBone;
+ }
+ return NULL;
+}
+
+/* note: there's a unique_bone_name() too! */
+void unique_editbone_name (ListBase *ebones, char *name)
+{
+ char tempname[64];
+ int number;
+ char *dot;
+
+
+ if (editbone_name_exists(ebones, name)) {
+
+ /* Strip off the suffix, if it's a number */
+ number= strlen(name);
+ if(number && isdigit(name[number-1])) {
+ dot= strrchr(name, '.'); // last occurrance
+ if (dot)
+ *dot=0;
+ }
+
+ for (number = 1; number <=999; number++){
+ sprintf (tempname, "%s.%03d", name, number);
+ if (!editbone_name_exists(ebones, tempname)){
+ BLI_strncpy (name, tempname, 32);
+ return;
+ }
+ }
+ }
+}
+
+/* context; editmode armature */
+/* if forked && mirror-edit: makes two bones with flipped names */
+void extrude_armature(int forked)
+{
+ bArmature *arm= G.obedit->data;
+ EditBone *newbone, *ebone, *flipbone, *first=NULL;
+ int a, totbone= 0, do_extrude;
+
+ TEST_EDITARMATURE;
+
+ /* since we allow root extrude too, we have to make sure selection is OK */
+ for (ebone = G.edbo.first; ebone; ebone=ebone->next){
+ if(arm->layer & ebone->layer) {
+ if(ebone->flag & BONE_ROOTSEL) {
+ if(ebone->parent && (ebone->flag & BONE_CONNECTED)) {
+ if(ebone->parent->flag & BONE_TIPSEL)
+ ebone->flag &= ~BONE_ROOTSEL;
+ }
+ }
+ }
+ }
+
+ /* Duplicate the necessary bones */
+ for (ebone = G.edbo.first; ((ebone) && (ebone!=first)); ebone=ebone->next){
+ if(arm->layer & ebone->layer) {
+
+ /* we extrude per definition the tip */
+ do_extrude= 0;
+ if (ebone->flag & (BONE_TIPSEL|BONE_SELECTED))
+ do_extrude= 1;
+ else if(ebone->flag & BONE_ROOTSEL) {
+ /* but, a bone with parent deselected we do the root... */
+ if(ebone->parent && (ebone->parent->flag & BONE_TIPSEL));
+ else do_extrude= 2;
+ }
+
+ if (do_extrude) {
+
+ /* we re-use code for mirror editing... */
+ flipbone= NULL;
+ if(arm->flag & ARM_MIRROR_EDIT) {
+ flipbone= armature_bone_get_mirrored(ebone);
+ if (flipbone) {
+ forked= 0; // we extrude 2 different bones
+ if(flipbone->flag & (BONE_TIPSEL|BONE_ROOTSEL|BONE_SELECTED))
+ /* don't want this bone to be selected... */
+ flipbone->flag &= ~(BONE_TIPSEL|BONE_SELECTED|BONE_ROOTSEL|BONE_ACTIVE);
+ }
+ if(flipbone==NULL && forked)
+ flipbone= ebone;
+ }
+
+ for(a=0; a<2; a++) {
+ if(a==1) {
+ if(flipbone==NULL)
+ break;
+ else {
+ SWAP(EditBone *, flipbone, ebone);
+ }
+ }
+
+ totbone++;
+ newbone = MEM_callocN(sizeof(EditBone), "extrudebone");
+
+ if(do_extrude==1) {
+ VECCOPY (newbone->head, ebone->tail);
+ VECCOPY (newbone->tail, newbone->head);
+ newbone->parent = ebone;
+
+ newbone->flag = ebone->flag & BONE_TIPSEL; // copies it, in case mirrored bone
+ }
+ else {
+ VECCOPY(newbone->head, ebone->head);
+ VECCOPY(newbone->tail, ebone->head);
+ newbone->parent= ebone->parent;
+
+ newbone->flag= BONE_TIPSEL;
+ }
+
+ newbone->weight= ebone->weight;
+ newbone->dist= ebone->dist;
+ newbone->xwidth= ebone->xwidth;
+ newbone->zwidth= ebone->zwidth;
+ newbone->ease1= ebone->ease1;
+ newbone->ease2= ebone->ease2;
+ newbone->rad_head= ebone->rad_tail; // dont copy entire bone...
+ newbone->rad_tail= ebone->rad_tail;
+ newbone->segments= 1;
+ newbone->layer= ebone->layer;
+
+ if(newbone->parent) newbone->flag |= BONE_CONNECTED;
+
+ BLI_strncpy (newbone->name, ebone->name, 32);
+
+ if(flipbone && forked) { // only set if mirror edit
+ if(strlen(newbone->name)<30) {
+ if(a==0) strcat(newbone->name, "_L");
+ else strcat(newbone->name, "_R");
+ }
+ }
+ unique_editbone_name(&G.edbo, newbone->name);
+
+ /* Add the new bone to the list */
+ BLI_addtail(&G.edbo, newbone);
+ if (!first)
+ first = newbone;
+
+ /* restore ebone if we were flipping */
+ if(a==1 && flipbone)
+ SWAP(EditBone *, flipbone, ebone);
+
+ }
+ }
+
+ /* Deselect the old bone */
+ ebone->flag &= ~(BONE_TIPSEL|BONE_SELECTED|BONE_ROOTSEL|BONE_ACTIVE);
+ }
+ }
+ /* if only one bone, make this one active */
+ if(totbone==1 && first) first->flag |= BONE_ACTIVE;
+
+ /* Transform the endpoints */
+ countall(); // flushes selection!
+ BIF_TransformSetUndo("Extrude");
+ initTransform(TFM_TRANSLATION, CTX_NO_PET);
+ Transform();
+
+ allqueue(REDRAWBUTSEDIT, 0);
+ allqueue(REDRAWBUTSOBJECT, 0);
+ allqueue(REDRAWOOPS, 0);
+}
+
+/* context; editmode armature */
+void subdivide_armature(int numcuts)
+{
+ bArmature *arm= G.obedit->data;
+ EditBone *ebone, *newbone, *tbone, *mbone;
+ int a, i;
+
+ if(numcuts < 1) return;
+
+ for (mbone = G.edbo.last; mbone; mbone= mbone->prev) {
+ if(arm->layer & mbone->layer) {
+ if(mbone->flag & BONE_SELECTED) {
+ for(i=numcuts+1; i>1; i--) {
+ /* compute cut ratio first */
+ float cutratio= 1/(float)i;
+ float cutratioI= 1-cutratio;
+
+ /* take care of mirrored stuff */
+ for(a=0; a<2; a++) {
+ float val1[3];
+ float val2[3];
+ float val3[3];
+
+ /* try to find mirrored bone on a != 0 */
+ if(a) {
+ if(arm->flag & ARM_MIRROR_EDIT)
+ ebone= armature_bone_get_mirrored(mbone);
+ else ebone= NULL;
+ }
+ else
+ ebone= mbone;
+
+ if(ebone) {
+ newbone= MEM_mallocN(sizeof(EditBone), "ebone subdiv");
+ *newbone = *ebone;
+ BLI_addtail(&G.edbo, newbone);
+
+ /* calculate location of newbone->head */
+ VECCOPY(val1, ebone->head);
+ VECCOPY(val2, ebone->tail);
+ VECCOPY(val3, newbone->head);
+
+ val3[0]= val1[0]*cutratio+val2[0]*cutratioI;
+ val3[1]= val1[1]*cutratio+val2[1]*cutratioI;
+ val3[2]= val1[2]*cutratio+val2[2]*cutratioI;
+
+ VECCOPY(newbone->head, val3);
+ VECCOPY(newbone->tail, ebone->tail);
+ VECCOPY(ebone->tail, newbone->head);
+
+ newbone->rad_head= 0.5*(ebone->rad_head+ebone->rad_tail);
+ ebone->rad_tail= newbone->rad_head;
+
+ newbone->flag |= BONE_CONNECTED;
+
+ unique_editbone_name (&G.edbo, newbone->name);
+
+ /* correct parent bones */
+ for (tbone = G.edbo.first; tbone; tbone=tbone->next){
+ if(tbone->parent==ebone)
+ tbone->parent= newbone;
+ }
+ newbone->parent= ebone;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if(numcuts==1) BIF_undo_push("Subdivide");
+ else BIF_undo_push("Subdivide multi");
+}
+
+/* ***************** Pose tools ********************* */
+
+void clear_armature(Object *ob, char mode)
+{
+ bPoseChannel *pchan;
+ bArmature *arm;
+
+ arm=get_armature(ob);
+
+ if (!arm)
+ return;
+
+ for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
+ if(pchan->bone && (pchan->bone->flag & BONE_SELECTED)) {
+ if(arm->layer & pchan->bone->layer) {
+ switch (mode) {
+ case 'r':
+ pchan->quat[1]=pchan->quat[2]=pchan->quat[3]=0.0F; pchan->quat[0]=1.0F;
+ break;
+ case 'g':
+ pchan->loc[0]=pchan->loc[1]=pchan->loc[2]=0.0F;
+ break;
+ case 's':
+ pchan->size[0]=pchan->size[1]=pchan->size[2]=1.0F;
+ break;
+
+ }
+
+ /* the current values from IPO's may not be zero, so tag as unkeyed */
+ pchan->bone->flag |= BONE_UNKEYED;
+ }
+ }
+ }
+
+ DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA);
+ /* no update for this object, this will execute the action again */
+ /* is weak... like for ipo editing which uses ctime now... */
+ where_is_pose (ob);
+ ob->recalc= 0;
+}
+
+/* helper for function below */
+static int clear_active_flag(Object *ob, Bone *bone, void *data)
+{
+ bone->flag &= ~BONE_ACTIVE;
+ return 0;
+}
+
+
+/* called from editview.c, for mode-less pose selection */
+int do_pose_selectbuffer(Base *base, unsigned int *buffer, short hits)
+{
+ Object *ob= base->object;
+ Bone *nearBone;
+
+ if (!ob || !ob->pose) return 0;
+
+ nearBone= get_bone_from_selectbuffer(base, buffer, hits, 1);
+
+ if (nearBone) {
+ /* since we do unified select, we don't shift+select a bone if the armature object was not active yet */
+ if (!(G.qual & LR_SHIFTKEY) || base!=BASACT){
+ deselectall_posearmature(ob, 0, 0);
+ nearBone->flag |= (BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL|BONE_ACTIVE);
+ select_actionchannel_by_name(ob->action, nearBone->name, 1);
+ }
+ else {
+ if (nearBone->flag & BONE_SELECTED) {
+ /* if not active, we make it active */
+ if((nearBone->flag & BONE_ACTIVE)==0) {
+ bArmature *arm= ob->data;
+ bone_looper(ob, arm->bonebase.first, NULL, clear_active_flag);
+
+ nearBone->flag |= BONE_ACTIVE;
+ }
+ else {
+ nearBone->flag &= ~(BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL|BONE_ACTIVE);
+ select_actionchannel_by_name(ob->action, nearBone->name, 0);
+ }
+ }
+ else{
+ bArmature *arm= ob->data;
+ bone_looper(ob, arm->bonebase.first, NULL, clear_active_flag);
+
+ nearBone->flag |= (BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL|BONE_ACTIVE);
+ select_actionchannel_by_name(ob->action, nearBone->name, 1);
+ }
+ }
+
+ /* in weightpaint we select the associated vertex group too */
+ if(G.f & G_WEIGHTPAINT) {
+ if(nearBone->flag & BONE_ACTIVE) {
+ vertexgroup_select_by_name(OBACT, nearBone->name);
+ DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA);
+ }
+ }
+
+ allqueue(REDRAWVIEW3D, 0);
+ allqueue(REDRAWACTION, 0);
+ allqueue(REDRAWIPO, 0); /* To force action/constraint ipo update */
+ allqueue(REDRAWBUTSEDIT, 0);
+ allqueue(REDRAWBUTSOBJECT, 0);
+ allqueue(REDRAWOOPS, 0);
+ }
+
+ return nearBone!=NULL;
+
+}
+
+/* test==0: deselect all
+ test==1: swap select
+ test==2: only clear active tag
+*/
+void deselectall_posearmature (Object *ob, int test, int doundo)
+{
+ bArmature *arm;
+ bPoseChannel *pchan;
+ int selectmode= 0;
+
+ /* we call this from outliner too, but with OBACT set OK */
+ if(!ob || !ob->pose) return;
+ arm= get_armature(ob);
+
+ /* Determine if we're selecting or deselecting */
+ if (test==1) {
+ for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next)
+ if(pchan->bone->layer & arm->layer && !(pchan->bone->flag & BONE_HIDDEN_P))
+ if(pchan->bone->flag & BONE_SELECTED)
+ break;
+
+ if (pchan==NULL)
+ selectmode= 1;
+ }
+ else if(test==2)
+ selectmode= 2;
+
+ /* Set the flags accordingly */
+ for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
+ if(pchan->bone->layer & arm->layer && !(pchan->bone->flag & BONE_HIDDEN_P)) {
+ if(selectmode==0) pchan->bone->flag &= ~(BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL|BONE_ACTIVE);
+ else if(selectmode==1) pchan->bone->flag |= BONE_SELECTED;
+ else pchan->bone->flag &= ~BONE_ACTIVE;
+ }
+ }
+
+ /* action editor */
+ deselect_actionchannels(ob->action, 0); /* deselects for sure */
+ if(selectmode==1)
+ deselect_actionchannels(ob->action, 1); /* swaps */
+
+ allqueue(REDRAWBUTSEDIT, 0);
+ allqueue(REDRAWBUTSOBJECT, 0);
+ allqueue(REDRAWVIEW3D, 0);
+ allqueue(REDRAWOOPS, 0);
+ allqueue(REDRAWACTION, 0);
+
+ countall();
+
+ if (doundo) {
+ if (selectmode==1) BIF_undo_push("Select All");
+ else BIF_undo_push("Deselect All");
+ }
+}
+
+
+int bone_looper(Object *ob, Bone *bone, void *data,
+ int (*bone_func)(Object *, Bone *, void *))
+{
+
+ /* We want to apply the function bone_func to every bone
+ * in an armature -- feed bone_looper the first bone and
+ * a pointer to the bone_func and watch it go!. The int count
+ * can be useful for counting bones with a certain property
+ * (e.g. skinnable)
+ */
+ int count = 0;
+
+ if (bone) {
+
+ /* only do bone_func if the bone is non null
+ */
+ count += bone_func(ob, bone, data);
+
+ /* try to execute bone_func for the first child
+ */
+ count += bone_looper(ob, bone->childbase.first, data,
+ bone_func);
+
+ /* try to execute bone_func for the next bone at this
+ * depth of the recursion.
+ */
+ count += bone_looper(ob, bone->next, data, bone_func);
+ }
+
+ return count;
+}
+
+
+static int bone_skinnable(Object *ob, Bone *bone, void *data)
+{
+ /* Bones that are deforming
+ * are regarded to be "skinnable" and are eligible for
+ * auto-skinning.
+ *
+ * This function performs 2 functions:
+ *
+ * a) It returns 1 if the bone is skinnable.
+ * If we loop over all bones with this
+ * function, we can count the number of
+ * skinnable bones.
+ * b) If the pointer data is non null,
+ * it is treated like a handle to a
+ * bone pointer -- the bone pointer
+ * is set to point at this bone, and
+ * the pointer the handle points to
+ * is incremented to point to the
+ * next member of an array of pointers
+ * to bones. This way we can loop using
+ * this function to construct an array of
+ * pointers to bones that point to all
+ * skinnable bones.
+ */
+ Bone ***hbone;
+
+ if(!(G.f & G_WEIGHTPAINT) || !(bone->flag & BONE_HIDDEN_P)) {
+ if (!(bone->flag & BONE_NO_DEFORM)) {
+ if (data != NULL) {
+ hbone = (Bone ***) data;
+ **hbone = bone;
+ ++*hbone;
+ }
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int add_defgroup_unique_bone(Object *ob, Bone *bone, void *data)
+{
+ /* This group creates a vertex group to ob that has the
+ * same name as bone (provided the bone is skinnable).
+ * If such a vertex group aleady exist the routine exits.
+ */
+ if (!(bone->flag & BONE_NO_DEFORM)) {
+ if (!get_named_vertexgroup(ob,bone->name)) {
+ add_defgroup_name(ob, bone->name);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int dgroup_skinnable(Object *ob, Bone *bone, void *data)
+{
+ /* Bones that are deforming
+ * are regarded to be "skinnable" and are eligible for
+ * auto-skinning.
+ *
+ * This function performs 2 functions:
+ *
+ * a) If the bone is skinnable, it creates
+ * a vertex group for ob that has
+ * the name of the skinnable bone
+ * (if one doesn't exist already).
+ * b) If the pointer data is non null,
+ * it is treated like a handle to a
+ * bDeformGroup pointer -- the
+ * bDeformGroup pointer is set to point
+ * to the deform group with the bone's
+ * name, and the pointer the handle
+ * points to is incremented to point to the
+ * next member of an array of pointers
+ * to bDeformGroups. This way we can loop using
+ * this function to construct an array of
+ * pointers to bDeformGroups, all with names
+ * of skinnable bones.
+ */
+ bDeformGroup ***hgroup, *defgroup;
+
+ if(!(G.f & G_WEIGHTPAINT) || !(bone->flag & BONE_HIDDEN_P)) {
+ if (!(bone->flag & BONE_NO_DEFORM)) {
+ if ( !(defgroup = get_named_vertexgroup(ob, bone->name)) ) {
+ defgroup = add_defgroup_name(ob, bone->name);
+ }
+
+ if (data != NULL) {
+ hgroup = (bDeformGroup ***) data;
+ **hgroup = defgroup;
+ ++*hgroup;
+ }
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void add_vgroups__mapFunc(void *userData, int index, float *co, float *no_f, short *no_s)
+{
+ /* DerivedMesh mapFunc for getting final coords in weight paint mode */
+
+ float (*verts)[3] = userData;
+ VECCOPY(verts[index], co);
+}
+
+static void envelope_bone_weighting(Object *ob, Mesh *mesh, float (*verts)[3], int numbones, Bone **bonelist, bDeformGroup **dgrouplist, bDeformGroup **dgroupflip, float (*root)[3], float (*tip)[3], int *selected, float scale)
+{
+ /* Create vertex group weights from envelopes */
+
+ Bone *bone;
+ bDeformGroup *dgroup;
+ float distance;
+ int i, iflip, j;
+
+ /* for each vertex in the mesh */
+ for (i=0; i < mesh->totvert; i++) {
+ iflip = (dgroupflip)? mesh_get_x_mirror_vert(ob, i): 0;
+
+ /* for each skinnable bone */
+ for (j=0; j < numbones; ++j) {
+ if(!selected[j])
+ continue;
+
+ bone = bonelist[j];
+ dgroup = dgrouplist[j];
+
+ /* store the distance-factor from the vertex to the bone */
+ distance = distfactor_to_bone (verts[i], root[j], tip[j],
+ bone->rad_head * scale, bone->rad_tail * scale, bone->dist * scale);
+
+ /* add the vert to the deform group if weight!=0.0 */
+ if (distance!=0.0)
+ add_vert_to_defgroup (ob, dgroup, i, distance, WEIGHT_REPLACE);
+ else
+ remove_vert_defgroup (ob, dgroup, i);
+
+ /* do same for mirror */
+ if (dgroupflip && dgroupflip[j] && iflip >= 0) {
+ if (distance!=0.0)
+ add_vert_to_defgroup (ob, dgroupflip[j], iflip, distance,
+ WEIGHT_REPLACE);
+ else
+ remove_vert_defgroup (ob, dgroupflip[j], iflip);
+ }
+ }
+ }
+}
+
+void add_verts_to_dgroups(Object *ob, Object *par, int heat, int mirror)
+{
+ /* This functions implements the automatic computation of vertex group
+ * weights, either through envelopes or using a heat equilibrium.
+ *
+ * This function can be called both when parenting a mesh to an armature,
+ * or in weightpaint + posemode. In the latter case selection is taken
+ * into account and vertex weights can be mirrored.
+ *
+ * The mesh vertex positions used are either the final deformed coords
+ * from the derivedmesh in weightpaint mode, the final subsurf coords
+ * when parenting, or simply the original mesh coords.
+ */
+
+ bArmature *arm;
+ Bone **bonelist, **bonehandle, *bone;
+ bDeformGroup **dgrouplist, **dgroupflip, **dgrouphandle;
+ bDeformGroup *dgroup, *curdg;
+ Mesh *mesh;
+ float (*root)[3], (*tip)[3], (*verts)[3];
+ int *selected;
+ int numbones, vertsfilled = 0, i, j;
+ int wpmode = (G.f & G_WEIGHTPAINT);
+
+ /* If the parent object is not an armature exit */
+ arm = get_armature(par);
+ if (!arm)
+ return;
+
+ /* count the number of skinnable bones */
+ numbones = bone_looper(ob, arm->bonebase.first, NULL, bone_skinnable);
+
+ if (numbones == 0)
+ return;
+
+ /* create an array of pointer to bones that are skinnable
+ * and fill it with all of the skinnable bones */
+ bonelist = MEM_callocN(numbones*sizeof(Bone *), "bonelist");
+ bonehandle = bonelist;
+ bone_looper(ob, arm->bonebase.first, &bonehandle, bone_skinnable);
+
+ /* create an array of pointers to the deform groups that
+ * coorespond to the skinnable bones (creating them
+ * as necessary. */
+ dgrouplist = MEM_callocN(numbones*sizeof(bDeformGroup *), "dgrouplist");
+ dgroupflip = MEM_callocN(numbones*sizeof(bDeformGroup *), "dgroupflip");
+
+ dgrouphandle = dgrouplist;
+ bone_looper(ob, arm->bonebase.first, &dgrouphandle, dgroup_skinnable);
+
+ /* create an array of root and tip positions transformed into
+ * global coords */
+ root = MEM_callocN(numbones*sizeof(float)*3, "root");
+ tip = MEM_callocN(numbones*sizeof(float)*3, "tip");
+ selected = MEM_callocN(numbones*sizeof(int), "selected");
+
+ for (j=0; j < numbones; ++j) {
+ bone = bonelist[j];
+ dgroup = dgrouplist[j];
+
+ /* compute root and tip */
+ VECCOPY(root[j], bone->arm_head);
+ Mat4MulVecfl(par->obmat, root[j]);
+
+ VECCOPY(tip[j], bone->arm_tail);
+ Mat4MulVecfl(par->obmat, tip[j]);
+
+ /* set selected */
+ if(wpmode) {
+ if ((arm->layer & bone->layer) && (bone->flag & BONE_SELECTED))
+ selected[j] = 1;
+ }
+ else
+ selected[j] = 1;
+
+ /* find flipped group */
+ if(mirror) {
+ char name[32];
+
+ BLI_strncpy(name, dgroup->name, 32);
+ // 0 = don't strip off number extensions
+ bone_flip_name(name, 0);
+
+ for (curdg = ob->defbase.first; curdg; curdg=curdg->next)
+ if (!strcmp(curdg->name, name))
+ break;
+
+ dgroupflip[j] = curdg;
+ }
+ }
+
+ /* create verts */
+ mesh = (Mesh*)ob->data;
+ verts = MEM_callocN(mesh->totvert*sizeof(*verts), "closestboneverts");
+
+ if (wpmode) {
+ /* if in weight paint mode, use final verts from derivedmesh */
+ DerivedMesh *dm = mesh_get_derived_final(ob, CD_MASK_BAREMESH);
+
+ if(dm->foreachMappedVert) {
+ dm->foreachMappedVert(dm, add_vgroups__mapFunc, (void*)verts);
+ vertsfilled = 1;
+ }
+
+ dm->release(dm);
+ }
+ else if (modifiers_findByType(ob, eModifierType_Subsurf)) {
+ /* is subsurf on? Lets use the verts on the limit surface then.
+ * = same amount of vertices as mesh, but vertices moved to the
+ * subsurfed position, like for 'optimal'. */
+ subsurf_calculate_limit_positions(mesh, verts);
+ vertsfilled = 1;
+ }
+
+ /* transform verts to global space */
+ for (i=0; i < mesh->totvert; i++) {
+ if (!vertsfilled)
+ VECCOPY(verts[i], mesh->mvert[i].co)
+ Mat4MulVecfl(ob->obmat, verts[i]);
+ }
+
+ /* compute the weights based on gathered vertices and bones */
+ if (heat)
+ heat_bone_weighting(ob, mesh, verts, numbones, dgrouplist, dgroupflip,
+ root, tip, selected);
+ else
+ envelope_bone_weighting(ob, mesh, verts, numbones, bonelist, dgrouplist,
+ dgroupflip, root, tip, selected, Mat4ToScalef(par->obmat));
+
+ /* free the memory allocated */
+ MEM_freeN(bonelist);
+ MEM_freeN(dgrouplist);
+ MEM_freeN(dgroupflip);
+ MEM_freeN(root);
+ MEM_freeN(tip);
+ MEM_freeN(selected);
+ MEM_freeN(verts);
+}
+
+void create_vgroups_from_armature(Object *ob, Object *par)
+{
+ /* Lets try to create some vertex groups
+ * based on the bones of the parent armature.
+ */
+
+ bArmature *arm;
+ short mode;
+
+ /* If the parent object is not an armature exit */
+ arm = get_armature(par);
+ if (!arm)
+ return;
+
+ /* Prompt the user on whether/how they want the vertex groups
+ * added to the child mesh */
+ mode= pupmenu("Create Vertex Groups? %t|"
+ "Don't Create Groups %x1|"
+ "Name Groups %x2|"
+ "Create From Envelopes %x3|"
+ "Create From Bone Heat %x4|");
+ switch (mode){
+ case 2:
+ /* Traverse the bone list, trying to create empty vertex
+ * groups cooresponding to the bone.
+ */
+ bone_looper(ob, arm->bonebase.first, NULL,
+ add_defgroup_unique_bone);
+ if (ob->type == OB_MESH)
+ create_dverts(ob->data);
+
+ break;
+
+ case 3:
+ case 4:
+ /* Traverse the bone list, trying to create vertex groups
+ * that are populated with the vertices for which the
+ * bone is closest.
+ */
+ add_verts_to_dgroups(ob, par, (mode == 4), 0);
+ break;
+
+ }
+}
+
+static int hide_selected_pose_bone(Object *ob, Bone *bone, void *ptr)
+{
+ bArmature *arm= ob->data;
+
+ if(arm->layer & bone->layer) {
+ if (bone->flag & BONE_SELECTED) {
+ bone->flag |= BONE_HIDDEN_P;
+ bone->flag &= ~BONE_SELECTED;
+ }
+ }
+ return 0;
+}
+
+/* active object is armature */
+void hide_selected_pose_bones(void)
+{
+ bArmature *arm= OBACT->data;
+
+ if (!arm)
+ return;
+
+ bone_looper(OBACT, arm->bonebase.first, NULL,
+ hide_selected_pose_bone);
+
+ allqueue(REDRAWVIEW3D, 0);
+ allqueue(REDRAWBUTSEDIT, 0);
+ allqueue(REDRAWACTION, 0);
+ BIF_undo_push("Hide Bones");
+}
+
+static int hide_unselected_pose_bone(Object *ob, Bone *bone, void *ptr)
+{
+ bArmature *arm= ob->data;
+
+ if(arm->layer & bone->layer) {
+ if (~bone->flag & BONE_SELECTED) {
+ bone->flag |= BONE_HIDDEN_P;
+ }
+ }
+ return 0;
+}
+
+/* active object is armature */
+void hide_unselected_pose_bones(void)
+{
+ bArmature *arm;
+
+ arm=get_armature (OBACT);
+
+ if (!arm)
+ return;
+
+ bone_looper(OBACT, arm->bonebase.first, NULL,
+ hide_unselected_pose_bone);
+
+ allqueue(REDRAWVIEW3D, 0);
+ allqueue(REDRAWBUTSEDIT, 0);
+ BIF_undo_push("Hide Unselected Bone");
+}
+
+static int show_pose_bone(Object *ob, Bone *bone, void *ptr)
+{
+ bArmature *arm= ob->data;
+
+ if(arm->layer & bone->layer) {
+ if (bone->flag & BONE_HIDDEN_P) {
+ bone->flag &= ~BONE_HIDDEN_P;
+ bone->flag |= BONE_SELECTED;
+ }
+ }
+
+ return 0;
+}
+
+/* active object is armature in posemode */
+void show_all_pose_bones(void)
+{
+ bArmature *arm;
+
+ arm=get_armature (OBACT);
+
+ if (!arm)
+ return;
+
+ bone_looper(OBACT, arm->bonebase.first, NULL,
+ show_pose_bone);
+
+ allqueue(REDRAWVIEW3D, 0);
+ allqueue(REDRAWBUTSEDIT, 0);
+ BIF_undo_push("Reveal Bones");
+}
+
+
+/* ************* RENAMING DISASTERS ************ */
+
+/* note: there's a unique_editbone_name() too! */
+void unique_bone_name (bArmature *arm, char *name)
+{
+ char tempname[64];
+ int number;
+ char *dot;
+
+ if (get_named_bone(arm, name)) {
+
+ /* Strip off the suffix, if it's a number */
+ number= strlen(name);
+ if(number && isdigit(name[number-1])) {
+ dot= strrchr(name, '.'); // last occurrance
+ if (dot)
+ *dot=0;
+ }
+
+ for (number = 1; number <=999; number++){
+ sprintf (tempname, "%s.%03d", name, number);
+ if (!get_named_bone(arm, tempname)){
+ BLI_strncpy (name, tempname, 32);
+ return;
+ }
+ }
+ }
+}
+
+#define MAXBONENAME 32
+/* helper call for below */
+static void constraint_bone_name_fix(Object *ob, ListBase *conlist, char *oldname, char *newname)
+{
+ bConstraint *curcon;
+ bConstraintTarget *ct;
+
+ for (curcon = conlist->first; curcon; curcon=curcon->next) {
+ bConstraintTypeInfo *cti= constraint_get_typeinfo(curcon);
+ ListBase targets = {NULL, NULL};
+
+ if (cti && cti->get_constraint_targets) {
+ cti->get_constraint_targets(curcon, &targets);
+
+ for (ct= targets.first; ct; ct= ct->next) {
+ if (ct->tar == ob) {
+ if (!strcmp(ct->subtarget, oldname) )
+ BLI_strncpy(ct->subtarget, newname, MAXBONENAME);
+ }
+ }
+
+ if (cti->flush_constraint_targets)
+ cti->flush_constraint_targets(curcon, &targets, 0);
+ }
+ }
+}
+
+/* called by UI for renaming a bone */
+/* warning: make sure the original bone was not renamed yet! */
+/* seems messy, but thats what you get with not using pointers but channel names :) */
+void armature_bone_rename(bArmature *arm, char *oldnamep, char *newnamep)
+{
+ Object *ob;
+ char newname[MAXBONENAME];
+ char oldname[MAXBONENAME];
+
+ /* names better differ! */
+ if(strncmp(oldnamep, newnamep, MAXBONENAME)) {
+
+ /* we alter newname string... so make copy */
+ BLI_strncpy(newname, newnamep, MAXBONENAME);
+ /* we use oldname for search... so make copy */
+ BLI_strncpy(oldname, oldnamep, MAXBONENAME);
+
+ /* now check if we're in editmode, we need to find the unique name */
+ if(G.obedit && G.obedit->data==arm) {
+ EditBone *eBone;
+
+ eBone= editbone_name_exists(&G.edbo, oldname);
+ if(eBone) {
+ unique_editbone_name (&G.edbo, newname);
+ BLI_strncpy(eBone->name, newname, MAXBONENAME);
+ }
+ else return;
+ }
+ else {
+ Bone *bone= get_named_bone (arm, oldname);
+
+ if(bone) {
+ unique_bone_name (arm, newname);
+ BLI_strncpy(bone->name, newname, MAXBONENAME);
+ }
+ else return;
+ }
+
+ /* do entire dbase */
+ for(ob= G.main->object.first; ob; ob= ob->id.next) {
+ /* we have the object using the armature */
+ if(arm==ob->data) {
+ Object *cob;
+ bAction *act;
+ bActionChannel *achan;
+ bActionStrip *strip;
+
+ /* Rename action channel if necessary */
+ act = ob->action;
+ if (act && !act->id.lib) {
+ /* Find the appropriate channel */
+ achan= get_action_channel(act, oldname);
+ if(achan) BLI_strncpy(achan->name, newname, MAXBONENAME);
+ }
+
+ /* Rename the pose channel, if it exists */
+ if (ob->pose) {
+ bPoseChannel *pchan = get_pose_channel(ob->pose, oldname);
+ if (pchan) {
+ BLI_strncpy (pchan->name, newname, MAXBONENAME);
+ }
+ }
+
+ /* check all nla-strips too */
+ for (strip= ob->nlastrips.first; strip; strip= strip->next) {
+ /* Rename action channel if necessary */
+ act = strip->act;
+ if (act && !act->id.lib) {
+ /* Find the appropriate channel */
+ achan= get_action_channel(act, oldname);
+ if(achan) BLI_strncpy(achan->name, newname, MAXBONENAME);
+ }
+ }
+
+ /* Update any object constraints to use the new bone name */
+ for(cob= G.main->object.first; cob; cob= cob->id.next) {
+ if(cob->constraints.first)
+ constraint_bone_name_fix(ob, &cob->constraints, oldname, newname);
+ if (cob->pose) {
+ bPoseChannel *pchan;
+ for (pchan = cob->pose->chanbase.first; pchan; pchan=pchan->next) {
+ constraint_bone_name_fix(ob, &pchan->constraints, oldname, newname);
+ }
+ }
+ }
+ }
+
+ /* See if an object is parented to this armature */
+ if (ob->parent && (ob->parent->data == arm)) {
+ if(ob->partype==PARBONE) {
+ /* bone name in object */
+ if (!strcmp(ob->parsubstr, oldname))
+ BLI_strncpy(ob->parsubstr, newname, MAXBONENAME);
+ }
+ }
+
+ if(modifiers_usesArmature(ob, arm)) {
+ bDeformGroup *dg;
+ /* bone name in defgroup */
+ for (dg=ob->defbase.first; dg; dg=dg->next) {
+ if(!strcmp(dg->name, oldname))
+ BLI_strncpy(dg->name, newname, MAXBONENAME);
+ }
+ }
+ }
+ }
+}
+
+/* context editmode object */
+void armature_flip_names(void)
+{
+ bArmature *arm= G.obedit->data;
+ EditBone *ebone;
+ char newname[32];
+
+ for (ebone = G.edbo.first; ebone; ebone=ebone->next) {
+ if(arm->layer & ebone->layer) {
+ if(ebone->flag & BONE_SELECTED) {
+ BLI_strncpy(newname, ebone->name, sizeof(newname));
+ bone_flip_name(newname, 1); // 1 = do strip off number extensions
+ armature_bone_rename(G.obedit->data, ebone->name, newname);
+ }
+ }
+ }
+
+ allqueue(REDRAWVIEW3D, 0);
+ allqueue(REDRAWBUTSEDIT, 0);
+ allqueue(REDRAWBUTSOBJECT, 0);
+ allqueue (REDRAWACTION, 0);
+ allqueue(REDRAWOOPS, 0);
+ BIF_undo_push("Flip names");
+
+}
+
+/* context; editmode armature */
+EditBone *armature_bone_get_mirrored(EditBone *ebo)
+{
+ EditBone *eboflip= NULL;
+ char name[32];
+
+ BLI_strncpy(name, ebo->name, sizeof(name));
+ bone_flip_name(name, 0); // 0 = don't strip off number extensions
+
+ for (eboflip=G.edbo.first; eboflip; eboflip=eboflip->next)
+ if(ebo!=eboflip)
+ if (!strcmp (name, eboflip->name)) break;
+
+ return eboflip;
+}
+
+/* if editbone (partial) selected, copy data */
+/* context; editmode armature, with mirror editing enabled */
+void transform_armature_mirror_update(void)
+{
+ EditBone *ebo, *eboflip;
+
+ for (ebo=G.edbo.first; ebo; ebo=ebo->next) {
+ /* no layer check, correct mirror is more important */
+ if(ebo->flag & (BONE_TIPSEL|BONE_ROOTSEL)) {
+
+ eboflip= armature_bone_get_mirrored(ebo);
+
+ if(eboflip) {
+ /* we assume X-axis flipping for now */
+ if(ebo->flag & BONE_TIPSEL) {
+ eboflip->tail[0]= -ebo->tail[0];
+ eboflip->tail[1]= ebo->tail[1];
+ eboflip->tail[2]= ebo->tail[2];
+ eboflip->rad_tail= ebo->rad_tail;
+ }
+ if(ebo->flag & BONE_ROOTSEL) {
+ eboflip->head[0]= -ebo->head[0];
+ eboflip->head[1]= ebo->head[1];
+ eboflip->head[2]= ebo->head[2];
+ eboflip->rad_head= ebo->rad_head;
+ }
+ if(ebo->flag & BONE_SELECTED) {
+ eboflip->dist= ebo->dist;
+ eboflip->roll= -ebo->roll;
+ eboflip->xwidth= ebo->xwidth;
+ eboflip->zwidth= ebo->zwidth;
+ }
+ }
+ }
+ }
+}
+
+