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:
authorTon Roosendaal <ton@blender.org>2009-01-05 18:19:31 +0300
committerTon Roosendaal <ton@blender.org>2009-01-05 18:19:31 +0300
commit1fe21f7e8f2f12b8b0707609e132b53fcc842f9e (patch)
tree86e236a007aa35b0af3b2f606066a751d2d541a4 /source/blender/editors/armature
parentdf20a12728626372de8a5eb127e57cb2cca40649 (diff)
2.5
Put back Armature/Pose code, including 'heat weight'. I've added reeb.h to get things compile, but Martin will cleanup files and put back? Now where to put all vertexgroup code.... I guess mesh? Note for msvc: yep, another new dir to add! :)
Diffstat (limited to 'source/blender/editors/armature')
-rw-r--r--source/blender/editors/armature/Makefile59
-rw-r--r--source/blender/editors/armature/SConscript11
-rw-r--r--source/blender/editors/armature/armature_intern.h39
-rw-r--r--source/blender/editors/armature/editarmature.c4971
-rw-r--r--source/blender/editors/armature/meshlaplacian.c1923
-rw-r--r--source/blender/editors/armature/meshlaplacian.h85
-rw-r--r--source/blender/editors/armature/poseobject.c1681
-rw-r--r--source/blender/editors/armature/reeb.h189
8 files changed, 8958 insertions, 0 deletions
diff --git a/source/blender/editors/armature/Makefile b/source/blender/editors/armature/Makefile
new file mode 100644
index 00000000000..f8cbb5ab37e
--- /dev/null
+++ b/source/blender/editors/armature/Makefile
@@ -0,0 +1,59 @@
+#
+# $Id: Makefile 14 2002-10-13 15:57:19Z hans $
+#
+# ***** BEGIN GPL 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.
+#
+# 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) 2007 Blender Foundation
+# All rights reserved.
+#
+# The Original Code is: all of this file.
+#
+# Contributor(s): none yet.
+#
+# ***** END GPL LICENSE BLOCK *****
+#
+# Makes module object directory and bounces make to subdirectories.
+
+LIBNAME = ed_armature
+DIR = $(OCGDIR)/blender/$(LIBNAME)
+
+include nan_compile.mk
+
+CFLAGS += $(LEVEL_1_C_WARNINGS)
+
+CPPFLAGS += -I$(NAN_GLEW)/include
+CPPFLAGS += -I$(OPENGL_HEADERS)
+
+CPPFLAGS += -I$(NAN_BMFONT)/include
+CPPFLAGS += -I$(NAN_OPENNL)/include
+
+# not very neat....
+CPPFLAGS += -I../../windowmanager
+CPPFLAGS += -I../../blenloader
+CPPFLAGS += -I../../blenkernel
+CPPFLAGS += -I../../blenlib
+CPPFLAGS += -I../../makesdna
+CPPFLAGS += -I../../imbuf
+CPPFLAGS += -I../../python
+CPPFLAGS += -I../../gpu
+CPPFLAGS += -I../../makesrna
+CPPFLAGS += -I../../render/extern/include
+CPPFLAGS += -I$(NAN_GUARDEDALLOC)/include
+
+# own include
+
+CPPFLAGS += -I../include
diff --git a/source/blender/editors/armature/SConscript b/source/blender/editors/armature/SConscript
new file mode 100644
index 00000000000..6f87b787344
--- /dev/null
+++ b/source/blender/editors/armature/SConscript
@@ -0,0 +1,11 @@
+#!/usr/bin/python
+Import ('env')
+
+sources = env.Glob('*.c')
+
+incs = '../include ../../blenlib ../../blenkernel ../../makesdna ../../imbuf'
+incs += ' ../../windowmanager #/intern/guardedalloc #/extern/glew/include'
+incs += ' ../../render/extern/include #/intern/guardedalloc #intern/bmfont'
+incs += ' ../../gpu ../../makesrna #/intern/opennl/extern'
+
+env.BlenderLib ( 'bf_editors_armature', sources, Split(incs), [], libtype=['core'], priority=[40] )
diff --git a/source/blender/editors/armature/armature_intern.h b/source/blender/editors/armature/armature_intern.h
new file mode 100644
index 00000000000..ec2ce1a4ac6
--- /dev/null
+++ b/source/blender/editors/armature/armature_intern.h
@@ -0,0 +1,39 @@
+/**
+ * $Id:
+ *
+ * ***** BEGIN GPL 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.
+ *
+ * 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) 2009 Blender Foundation.
+ * All rights reserved.
+ *
+ *
+ * Contributor(s): Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+#ifndef ED_ARMATURE_INTERN_H
+#define ED_ARMATURE_INTERN_H
+
+/* internal exports only */
+
+void armature_bone_rename(Object *ob, char *oldnamep, char *newnamep);
+EditBone *armature_bone_get_mirrored(ListBase *edbo, EditBone *ebo);
+
+
+
+#endif /* ED_ARMATURE_INTERN_H */
+
diff --git a/source/blender/editors/armature/editarmature.c b/source/blender/editors/armature/editarmature.c
new file mode 100644
index 00000000000..c6797bedaeb
--- /dev/null
+++ b/source/blender/editors/armature/editarmature.c
@@ -0,0 +1,4971 @@
+/**
+ * $Id$
+ *
+ * ***** BEGIN GPL 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.
+ *
+ * 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.
+ *
+ * Contributor(s): Blender Foundation, 2002-2009 full recode.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <float.h>
+
+#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 "DNA_ipo_types.h"
+#include "DNA_curve_types.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_arithb.h"
+#include "BLI_editVert.h"
+#include "BLI_ghash.h"
+
+#include "BKE_action.h"
+#include "BKE_armature.h"
+#include "BKE_constraint.h"
+#include "BKE_context.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 "PIL_time.h"
+
+#include "BIF_gl.h"
+
+#include "ED_armature.h"
+#include "ED_mesh.h"
+#include "ED_object.h"
+#include "ED_util.h"
+#include "ED_view3d.h"
+
+#include "armature_intern.h"
+#include "meshlaplacian.h"
+#include "reeb.h"
+
+extern float center[3], centroid[3]; /* Originally defined in editobject.c */
+
+/* ************* XXX *************** */
+static int okee() {return 0;}
+static int pupmenu() {return 0;}
+static void waitcursor() {};
+static void error() {};
+static void error_libdata() {}
+static void BIF_undo_push() {}
+static void adduplicate() {}
+static void countall() {}
+static void vertexgroup_select_by_name() {}
+static void deselect_actionchannels() {}
+static void *add_defgroup_name() {return NULL;}
+static void add_vert_to_defgroup() {}
+#define WEIGHT_REPLACE 0
+static void create_dverts() {}
+static void remove_vert_defgroup() {}
+static int mesh_get_x_mirror_vert() {return 0;}
+static void select_actionchannel_by_name() {}
+/* ************* XXX *************** */
+
+/* **************** tools on Editmode Armature **************** */
+
+/* converts Bones to EditBone list, used for tools as well */
+void make_boneList(ListBase *edbo, 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(edbo, eBone);
+
+ /* Add children if necessary */
+ if (curBone->childbase.first)
+ make_boneList(edbo, &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);
+ }
+}
+
+/* put EditMode back in Object */
+void ED_armature_from_edit(Scene *scene, Object *obedit)
+{
+ bArmature *arm= obedit->data;
+ EditBone *eBone, *neBone;
+ Bone *newBone;
+ Object *obt;
+
+ /* armature bones */
+ free_bones(arm);
+
+ /* remove zero sized bones, this gives instable restposes */
+ for (eBone=arm->edbo->first; eBone; eBone= neBone) {
+ float len= VecLenf(eBone->head, eBone->tail);
+ neBone= eBone->next;
+ if (len <= 0.000001f) { /* FLT_EPSILON is too large? */
+ EditBone *fBone;
+
+ /* Find any bones that refer to this bone */
+ for (fBone=arm->edbo->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(arm->edbo, eBone);
+ }
+ }
+
+ /* Copy the bones from the editData into the armature */
+ for (eBone=arm->edbo->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=arm->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[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, arm->edbo);
+
+ /* 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(scene, obedit, OB_RECALC_DATA);
+}
+
+
+
+void apply_rot_armature (Scene *scene, Object *ob, float mat[3][3])
+{
+ EditBone *ebone;
+ bArmature *arm= ob->data;
+ float scale = Mat3ToScalef(mat); /* store the scale of the matrix here to use on envelopes */
+
+ /* Put the armature into editmode */
+ ED_armature_to_edit(ob);
+
+ /* Do the rotations */
+ for (ebone = arm->edbo->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 */
+ ED_armature_from_edit(scene, ob);
+ ED_armature_edit_free(ob);
+}
+
+/* 0 == do center, 1 == center new, 2 == center cursor */
+void docenter_armature (Scene *scene, View3D *v3d, Object *ob, int centermode)
+{
+ Object *obedit= scene->obedit; // XXX get from context
+ EditBone *ebone;
+ bArmature *arm= ob->data;
+ float cent[3] = {0.0f, 0.0f, 0.0f};
+ float min[3], max[3];
+ float omat[3][3];
+
+ /* Put the armature into editmode */
+ if(ob!=obedit)
+ ED_armature_to_edit(ob);
+
+ /* Find the centerpoint */
+ if (centermode == 2) {
+ float *fp= give_cursor(scene, v3d);
+ VECCOPY(cent, fp);
+ Mat4Invert(ob->imat, ob->obmat);
+ Mat4MulVecfl(ob->imat, cent);
+ }
+ else {
+ INIT_MINMAX(min, max);
+
+ for (ebone= arm->edbo->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= arm->edbo->first; ebone; ebone=ebone->next){
+ VecSubf(ebone->head, ebone->head, cent);
+ VecSubf(ebone->tail, ebone->tail, cent);
+ }
+
+ /* Turn the list into an armature */
+ ED_armature_from_edit(scene, ob);
+
+ /* Adjust object location for new centerpoint */
+ if(centermode && obedit==NULL) {
+ Mat3CpyMat4(omat, ob->obmat);
+
+ Mat3MulVecfl(omat, cent);
+ ob->loc[0]+= cent[0];
+ ob->loc[1]+= cent[1];
+ ob->loc[2]+= cent[2];
+ }
+ else
+ ED_armature_edit_free(ob);
+}
+
+/* helper for apply_armature_pose2bones - fixes parenting of objects that are bone-parented to armature */
+static void applyarmature_fix_boneparents (Scene *scene, Object *armob)
+{
+ Object workob, *ob;
+
+ /* go through all objects in database */
+ for (ob= G.main->object.first; ob; ob= ob->id.next) {
+ /* if parent is bone in this armature, apply corrections */
+ if ((ob->parent == armob) && (ob->partype == PARBONE)) {
+ /* apply current transform from parent (not yet destroyed),
+ * then calculate new parent inverse matrix
+ */
+ ED_object_apply_obmat(ob);
+
+ what_does_parent(scene, ob, &workob);
+ Mat4Invert(ob->parentinv, workob.obmat);
+ }
+ }
+}
+
+static EditBone *editbone_name_exists (ListBase *edbo, char *name)
+{
+ EditBone *eBone;
+
+ for (eBone=edbo->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 *edbo, char *name)
+{
+ char tempname[64];
+ int number;
+ char *dot;
+
+ if (editbone_name_exists(edbo, 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(edbo, tempname)) {
+ BLI_strncpy(name, tempname, 32);
+ return;
+ }
+ }
+ }
+}
+
+/* set the current pose as the restpose */
+void apply_armature_pose2bones(Scene *scene, Object *obedit)
+{
+ bArmature *arm= obedit->data;
+ bPose *pose;
+ bPoseChannel *pchan;
+ EditBone *curbone;
+
+ /* don't check if editmode (should be done by caller) */
+ if (object_data_is_libdata(obedit)) {
+ error_libdata();
+ return;
+ }
+
+ /* helpful warnings... */
+ // TODO: add warnings to be careful about actions, applying deforms first, etc.
+
+ /* Get editbones of active armature to alter */
+ ED_armature_to_edit(obedit);
+
+ /* get pose of active object and move it out of posemode */
+ pose= obedit->pose;
+
+ for (pchan=pose->chanbase.first; pchan; pchan=pchan->next) {
+ curbone= editbone_name_exists(arm->edbo, pchan->name);
+
+ /* simply copy the head/tail values from pchan over to curbone */
+ VECCOPY(curbone->head, pchan->pose_head);
+ VECCOPY(curbone->tail, pchan->pose_tail);
+
+ /* fix roll:
+ * 1. find auto-calculated roll value for this bone now
+ * 2. remove this from the 'visual' y-rotation
+ */
+ {
+ float premat[3][3], imat[3][3],pmat[3][3], tmat[3][3];
+ float delta[3], eul[3];
+
+ /* obtain new auto y-rotation */
+ VecSubf(delta, curbone->tail, curbone->head);
+ vec_roll_to_mat3(delta, 0.0, premat);
+ Mat3Inv(imat, premat);
+
+ /* get pchan 'visual' matrix */
+ Mat3CpyMat4(pmat, pchan->pose_mat);
+
+ /* remove auto from visual and get euler rotation */
+ Mat3MulMat3(tmat, imat, pmat);
+ Mat3ToEul(tmat, eul);
+
+ /* just use this euler-y as new roll value */
+ curbone->roll= eul[1];
+ }
+
+ /* clear transform values for pchan */
+ pchan->loc[0]= pchan->loc[1]= pchan->loc[2]= 0;
+ pchan->quat[1]= pchan->quat[2]= pchan->quat[3]= 0;
+ pchan->quat[0]= pchan->size[0]= pchan->size[1]= pchan->size[2]= 1;
+
+ /* set anim lock */
+ curbone->flag |= BONE_UNKEYED;
+ }
+
+ /* convert editbones back to bones */
+ ED_armature_from_edit(scene, obedit);
+
+ /* flush positions of posebones */
+ where_is_pose(scene, obedit);
+
+ /* fix parenting of objects which are bone-parented */
+ applyarmature_fix_boneparents(scene, obedit);
+
+ BIF_undo_push("Apply new restpose");
+}
+
+
+/* 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(Scene *scene, View3D *v3d)
+{
+ Object *ob= scene->basact->object; // XXX context
+ bArmature *arm= ob->data;
+ Base *base, *nextbase;
+ bPose *pose, *opose;
+ bPoseChannel *pchan, *pchann;
+ EditBone *curbone;
+ float mat[4][4], oimat[4][4];
+
+ /* Ensure we're not in editmode and that the active object is an armature*/
+ if (ob->type!=OB_ARMATURE) return 0;
+ if(arm->edbo) return 0;
+
+ if (object_data_is_libdata(ob)) {
+ error_libdata();
+ return 0;
+ }
+
+ /* Get editbones of active armature to add editbones to */
+ ED_armature_to_edit(ob);
+
+ /* get pose of active object and move it out of posemode */
+ pose= ob->pose;
+ ob->flag &= ~OB_POSEMODE;
+
+ for (base=FIRSTBASE; base; base=nextbase) {
+ nextbase = base->next;
+ if (TESTBASE(v3d, base)){
+ if ((base->object->type==OB_ARMATURE) && (base->object!=ob)) {
+ bArmature *curarm= base->object->data;
+
+ /* Make a list of editbones in current armature */
+ ED_armature_to_edit(base->object);
+
+ /* Get Pose of current armature */
+ opose= base->object->pose;
+ base->object->flag &= ~OB_POSEMODE;
+ BASACT->flag &= ~OB_POSEMODE;
+
+ /* Find the difference matrix */
+ Mat4Invert(oimat, ob->obmat);
+ Mat4MulMat4(mat, base->object->obmat, oimat);
+
+ /* 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(curarm->edbo, pchan->name);
+
+ /* Get new name */
+ unique_editbone_name(arm->edbo, 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(curarm->edbo, curbone);
+ BLI_addtail(arm->edbo, curbone);
+
+ BLI_remlink(&opose->chanbase, pchan);
+ BLI_addtail(&pose->chanbase, pchan);
+ }
+
+ ED_base_object_free_and_unlink(scene, base);
+ }
+ }
+ }
+
+ DAG_scene_sort(scene); // because we removed object(s)
+
+ ED_armature_from_edit(scene, ob);
+ ED_armature_edit_free(ob);
+
+ return 1;
+}
+
+/* Helper function for armature separating - link fixing */
+static void separated_armature_fix_links(Object *origArm, Object *newArm)
+{
+ Object *ob;
+ bPoseChannel *pchan, *pcha, *pchb;
+ bConstraint *con;
+ ListBase *opchans, *npchans;
+
+ /* get reference to list of bones in original and new armatures */
+ opchans= &origArm->pose->chanbase;
+ npchans= &newArm->pose->chanbase;
+
+ /* 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) {
+ for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
+ for (con= pchan->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) {
+ /* any targets which point to original armature are redirected to the new one only if:
+ * - the target isn't origArm/newArm itself
+ * - the target is one that can be found in newArm/origArm
+ */
+ if ((ct->tar == origArm) && (ct->subtarget[0] != 0)) {
+ for (pcha=npchans->first, pchb=npchans->last; pcha && pchb; pcha=pcha->next, pchb=pchb->prev) {
+ /* check if either one matches */
+ if ( (strcmp(pcha->name, ct->subtarget)==0) ||
+ (strcmp(pchb->name, ct->subtarget)==0) )
+ {
+ ct->tar= newArm;
+ break;
+ }
+
+ /* check if both ends have met (to stop checking) */
+ if (pcha == pchb) break;
+ }
+ }
+ else if ((ct->tar == newArm) && (ct->subtarget[0] != 0)) {
+ for (pcha=opchans->first, pchb=opchans->last; pcha && pchb; pcha=pcha->next, pchb=pchb->prev) {
+ /* check if either one matches */
+ if ( (strcmp(pcha->name, ct->subtarget)==0) ||
+ (strcmp(pchb->name, ct->subtarget)==0) )
+ {
+ ct->tar= origArm;
+ break;
+ }
+
+ /* check if both ends have met (to stop checking) */
+ if (pcha == pchb) break;
+ }
+ }
+ }
+
+ if (cti->flush_constraint_targets)
+ cti->flush_constraint_targets(con, &targets, 0);
+ }
+ }
+ }
+ }
+
+ /* fix object-level constraints */
+ if (ob != origArm) {
+ 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) {
+ /* any targets which point to original armature are redirected to the new one only if:
+ * - the target isn't origArm/newArm itself
+ * - the target is one that can be found in newArm/origArm
+ */
+ if ((ct->tar == origArm) && (ct->subtarget[0] != 0)) {
+ for (pcha=npchans->first, pchb=npchans->last; pcha && pchb; pcha=pcha->next, pchb=pchb->prev) {
+ /* check if either one matches */
+ if ( (strcmp(pcha->name, ct->subtarget)==0) ||
+ (strcmp(pchb->name, ct->subtarget)==0) )
+ {
+ ct->tar= newArm;
+ break;
+ }
+
+ /* check if both ends have met (to stop checking) */
+ if (pcha == pchb) break;
+ }
+ }
+ else if ((ct->tar == newArm) && (ct->subtarget[0] != 0)) {
+ for (pcha=opchans->first, pchb=opchans->last; pcha && pchb; pcha=pcha->next, pchb=pchb->prev) {
+ /* check if either one matches */
+ if ( (strcmp(pcha->name, ct->subtarget)==0) ||
+ (strcmp(pchb->name, ct->subtarget)==0) )
+ {
+ ct->tar= origArm;
+ break;
+ }
+
+ /* check if both ends have met (to stop checking) */
+ if (pcha == pchb) break;
+ }
+ }
+ }
+
+ 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 == origArm)) {
+ /* Is object parented to a bone of this src armature? */
+ if (ob->partype==PARBONE) {
+ /* bone name in object */
+ for (pcha=npchans->first, pchb=npchans->last; pcha && pchb; pcha=pcha->next, pchb=pchb->prev) {
+ /* check if either one matches */
+ if ( (strcmp(pcha->name, ob->parsubstr)==0) ||
+ (strcmp(pchb->name, ob->parsubstr)==0) )
+ {
+ ob->parent= newArm;
+ break;
+ }
+
+ /* check if both ends have met (to stop checking) */
+ if (pcha == pchb) break;
+ }
+ }
+ }
+ }
+}
+
+/* Helper function for armature separating - remove certain bones from the given armature
+ * sel: remove selected bones from the armature, otherwise the unselected bones are removed
+ * (ob is not in editmode)
+ */
+static void separate_armature_bones (Scene *scene, Object *ob, short sel)
+{
+ bArmature *arm= (bArmature *)ob->data;
+ bPoseChannel *pchan, *pchann;
+ EditBone *curbone;
+
+ /* make local set of editbones to manipulate here */
+ ED_armature_to_edit(ob);
+
+ /* go through pose-channels, checking if a bone should be removed */
+ for (pchan=ob->pose->chanbase.first; pchan; pchan=pchann) {
+ pchann= pchan->next;
+ curbone= editbone_name_exists(arm->edbo, pchan->name);
+
+ /* check if bone needs to be removed */
+ if ( (sel && (curbone->flag & BONE_SELECTED)) ||
+ (!sel && !(curbone->flag & BONE_SELECTED)) )
+ {
+ EditBone *ebo;
+ bPoseChannel *pchn;
+
+ /* clear the bone->parent var of any bone that had this as its parent */
+ for (ebo= arm->edbo->first; ebo; ebo= ebo->next) {
+ if (ebo->parent == curbone) {
+ ebo->parent= NULL;
+ ebo->temp= NULL; /* this is needed to prevent random crashes with in ED_armature_from_edit */
+ ebo->flag &= ~BONE_CONNECTED;
+ }
+ }
+
+ /* clear the pchan->parent var of any pchan that had this as its parent */
+ for (pchn= ob->pose->chanbase.first; pchn; pchn=pchn->next) {
+ if (pchn->parent == pchan)
+ pchn->parent= NULL;
+ }
+
+ /* free any of the extra-data this pchan might have */
+ if (pchan->path) MEM_freeN(pchan->path);
+ free_constraints(&pchan->constraints);
+
+ /* get rid of unneeded bone */
+ BLI_freelinkN(arm->edbo, curbone);
+ BLI_freelinkN(&ob->pose->chanbase, pchan);
+ }
+ }
+
+ /* exit editmode (recalculates pchans too) */
+ ED_armature_from_edit(scene, ob);
+ ED_armature_edit_free(ob);
+}
+
+/* separate selected bones into their armature */
+void separate_armature (Scene *scene, View3D *v3d)
+{
+ Object *obedit= scene->obedit; // XXX get from context
+ Object *oldob, *newob;
+ Base *base, *oldbase, *newbase;
+ bArmature *arm;
+
+ if ( okee("Separate")==0 ) return;
+
+ waitcursor(1);
+
+ arm= obedit->data;
+
+ /* we are going to do this as follows (unlike every other instance of separate):
+ * 1. exit editmode +posemode for active armature/base. Take note of what this is.
+ * 2. duplicate base - BASACT is the new one now
+ * 3. for each of the two armatures, enter editmode -> remove appropriate bones -> exit editmode + recalc
+ * 4. fix constraint links
+ * 5. make original armature active and enter editmode
+ */
+
+ /* 1) only edit-base selected */
+ base= FIRSTBASE;
+ for (base= FIRSTBASE; base; base= base->next) {
+ if (base->lay & v3d->lay) {
+ if (base->object==obedit) base->flag |= 1;
+ else base->flag &= ~1;
+ }
+ }
+
+ /* 1) store starting settings and exit editmode */
+ oldob= obedit;
+ oldbase= BASACT;
+ oldob->flag &= ~OB_POSEMODE;
+ oldbase->flag &= ~OB_POSEMODE;
+
+ ED_armature_from_edit(scene, obedit);
+ ED_armature_edit_free(obedit);
+
+ /* 2) duplicate base */
+ adduplicate(1, USER_DUP_ARM); /* no transform and zero so do get a linked dupli */
+
+ newbase= BASACT; /* basact is set in adduplicate() */
+ newob= newbase->object;
+ newbase->flag &= ~SELECT;
+
+
+ /* 3) remove bones that shouldn't still be around on both armatures */
+ separate_armature_bones(scene, oldob, 1);
+ separate_armature_bones(scene, newob, 0);
+
+
+ /* 4) fix links before depsgraph flushes */ // err... or after?
+ separated_armature_fix_links(oldob, newob);
+
+ DAG_object_flush_update(scene, oldob, OB_RECALC_DATA); /* this is the original one */
+ DAG_object_flush_update(scene, newob, OB_RECALC_DATA); /* this is the separated one */
+
+
+ /* 5) restore original conditions */
+ obedit= oldob;
+ BASACT= oldbase;
+ BASACT->flag |= SELECT;
+
+ ED_armature_to_edit(obedit);
+
+ /* recalc/redraw + cleanup */
+ waitcursor(0);
+
+ countall(); // flush!
+
+ BIF_undo_push("Separate Armature");
+}
+
+/* **************** 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 */
+/* only bones from base are checked on */
+static void *get_bone_from_selectbuffer(Scene *scene, Base *base, unsigned int *buffer, short hits, short findunsel)
+{
+ Object *obedit= scene->obedit; // XXX get from context
+ 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 (obedit==NULL || base->object!=obedit) {
+ /* no singular posemode, so check for correct object */
+ if(base->selcol == (hitresult & 0xFFFF)) {
+ bone = get_indexed_bone(base->object, hitresult);
+
+ if (findunsel)
+ sel = (bone->flag & BONE_SELECTED);
+ else
+ sel = !(bone->flag & BONE_SELECTED);
+
+ data = bone;
+ }
+ else {
+ data= NULL;
+ sel= 0;
+ }
+ }
+ else{
+ bArmature *arm= obedit->data;
+
+ ebone = BLI_findlink(arm->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 */
+/* only checks scene->basact! */
+static void *get_nearest_bone (Scene *scene, short findunsel)
+{
+ ViewContext vc;
+ rcti rect;
+ unsigned int buffer[MAXPICKBUF];
+ short hits;
+
+ memset(&vc, 0, sizeof(ViewContext));
+ vc.scene= scene;
+ vc.obedit= scene->obedit;
+ // XXX fill in further!
+
+ // rect.xmin= ... mouseco!
+
+ glInitNames();
+ hits= view3d_opengl_select(&vc, buffer, MAXPICKBUF, &rect);
+
+ if (hits>0)
+ return get_bone_from_selectbuffer(scene, scene->basact, buffer, hits, findunsel);
+
+ return NULL;
+}
+
+/* helper for setflag_sel_bone() */
+static void bone_setflag (int *bone, int flag, short mode)
+{
+ if (bone && flag) {
+ /* exception for inverse flags */
+ if (flag == BONE_NO_DEFORM) {
+ if (mode == 2)
+ *bone |= flag;
+ else if (mode == 1)
+ *bone &= ~flag;
+ else
+ *bone ^= flag;
+
+ }
+ else {
+ if (mode == 2)
+ *bone &= ~flag;
+ else if (mode == 1)
+ *bone |= flag;
+ else
+ *bone ^= flag;
+ }
+ }
+}
+
+/* Get the first available child of an editbone */
+static EditBone *editbone_get_child(bArmature *arm, EditBone *pabone, short use_visibility)
+{
+ EditBone *curbone, *chbone=NULL;
+
+ for (curbone= arm->edbo->first; curbone; curbone= curbone->next) {
+ if (curbone->parent == pabone) {
+ if (use_visibility) {
+ if ((arm->layer & curbone->layer) && !(pabone->flag & BONE_HIDDEN_A))
+ chbone = curbone;
+ }
+ else
+ chbone = curbone;
+ }
+ }
+
+ return chbone;
+}
+
+void armature_select_hierarchy(Scene *scene, short direction, short add_to_sel)
+{
+ Object *obedit= scene->obedit; // XXX get from context
+ Object *ob;
+ bArmature *arm;
+ EditBone *curbone, *pabone, *chbone;
+
+ if (!obedit) return;
+ else ob= obedit;
+ arm= (bArmature *)ob->data;
+
+ for (curbone= arm->edbo->first; curbone; curbone= curbone->next) {
+ if (EBONE_VISIBLE(arm, curbone)) {
+ if (curbone->flag & (BONE_ACTIVE)) {
+ if (direction == BONE_SELECT_PARENT) {
+ if (curbone->parent == NULL) continue;
+ else pabone = curbone->parent;
+
+ if (EBONE_VISIBLE(arm, pabone)) {
+ pabone->flag |= (BONE_ACTIVE|BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL);
+ if (pabone->parent) pabone->parent->flag |= BONE_TIPSEL;
+
+ if (!add_to_sel) curbone->flag &= ~(BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL);
+ curbone->flag &= ~BONE_ACTIVE;
+ break;
+ }
+
+ }
+ else { // BONE_SELECT_CHILD
+ chbone = editbone_get_child(arm, curbone, 1);
+ if (chbone == NULL) continue;
+
+ if (EBONE_VISIBLE(arm, chbone)) {
+ chbone->flag |= (BONE_ACTIVE|BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL);
+
+ if (!add_to_sel) {
+ curbone->flag &= ~(BONE_SELECTED|BONE_ROOTSEL);
+ if (curbone->parent) curbone->parent->flag &= ~BONE_TIPSEL;
+ }
+ curbone->flag &= ~BONE_ACTIVE;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ countall(); // flushes selection!
+
+ if (direction==BONE_SELECT_PARENT)
+ BIF_undo_push("Select edit bone parent");
+ if (direction==BONE_SELECT_CHILD)
+ BIF_undo_push("Select edit bone child");
+}
+
+/* used by posemode and editmode */
+void setflag_armature (Scene *scene, short mode)
+{
+ Object *obedit= scene->obedit; // XXX get from context
+ Object *ob;
+ bArmature *arm;
+ int flag;
+
+ /* get data */
+ if (obedit)
+ ob= obedit;
+ else if (OBACT)
+ ob= OBACT;
+ else
+ return;
+ arm= (bArmature *)ob->data;
+
+ /* get flag to set (sync these with the ones used in eBone_Flag */
+ if (mode == 2)
+ flag= pupmenu("Disable Setting%t|Draw Wire%x1|Deform%x2|Mult VG%x3|Hinge%x4|No Scale%x5|Locked%x6");
+ else if (mode == 1)
+ flag= pupmenu("Enable Setting%t|Draw Wire%x1|Deform%x2|Mult VG%x3|Hinge%x4|No Scale%x5|Locked%x6");
+ else
+ flag= pupmenu("Toggle Setting%t|Draw Wire%x1|Deform%x2|Mult VG%x3|Hinge%x4|No Scale%x5|Locked%x6");
+ switch (flag) {
+ case 1: flag = BONE_DRAWWIRE; break;
+ case 2: flag = BONE_NO_DEFORM; break;
+ case 3: flag = BONE_MULT_VG_ENV; break;
+ case 4: flag = BONE_HINGE; break;
+ case 5: flag = BONE_NO_SCALE; break;
+ case 6: flag = BONE_EDITMODE_LOCKED; break;
+ default: return;
+ }
+
+ /* determine which mode armature is in */
+ if ((!obedit) && (ob->flag & OB_POSEMODE)) {
+ /* deal with pose channels */
+ bPoseChannel *pchan;
+
+ /* set setting */
+ for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
+ if ((pchan->bone) && (arm->layer & pchan->bone->layer)) {
+ if (pchan->bone->flag & BONE_SELECTED) {
+ bone_setflag(&pchan->bone->flag, flag, mode);
+ }
+ }
+ }
+ }
+ else if (obedit) {
+ /* deal with editbones */
+ EditBone *curbone;
+
+ /* set setting */
+ for (curbone= arm->edbo->first; curbone; curbone= curbone->next) {
+ if (arm->layer & curbone->layer) {
+ if (curbone->flag & BONE_SELECTED) {
+ bone_setflag(&curbone->flag, flag, mode);
+ }
+ }
+ }
+ }
+
+ BIF_undo_push("Change Bone Setting");
+}
+
+/* **************** END PoseMode & EditMode *************************** */
+/* **************** Posemode stuff ********************** */
+
+
+static void selectconnected_posebonechildren (Object *ob, Bone *bone)
+{
+ Bone *curBone;
+ int shift= 0; // XXX
+
+ if (!(bone->flag & BONE_CONNECTED))
+ return;
+
+ select_actionchannel_by_name (ob->action, bone->name, !(shift));
+
+ if (shift)
+ 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(Scene *scene)
+{
+ Bone *bone, *curBone, *next;
+ Object *ob= OBACT;
+ int shift= 0; // XXX
+
+ if(!ob || !ob->pose) return;
+
+ if (shift)
+ bone= get_nearest_bone(scene, 0);
+ else
+ bone = get_nearest_bone(scene, 1);
+
+ if (!bone)
+ return;
+
+ /* Select parents */
+ for (curBone=bone; curBone; curBone=next){
+ select_actionchannel_by_name (ob->action, curBone->name, !(shift));
+ if (shift)
+ 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!
+
+ BIF_undo_push("Select connected");
+
+}
+
+/* **************** END Posemode stuff ********************** */
+/* **************** EditMode stuff ********************** */
+
+/* called in space.c */
+void selectconnected_armature(Scene *scene, View3D *v3d, Object *obedit)
+{
+ bArmature *arm= obedit->data;
+ EditBone *bone, *curBone, *next;
+ int shift= 0; // XXX
+
+ if (shift)
+ bone= get_nearest_bone(scene, 0);
+ else
+ bone= get_nearest_bone(scene, 1);
+
+ if (!bone)
+ return;
+
+ /* Select parents */
+ for (curBone=bone; curBone; curBone=next){
+ if (shift){
+ 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=arm->edbo->first; curBone; curBone=next){
+ next = curBone->next;
+ if (curBone->parent == bone){
+ if (curBone->flag & BONE_CONNECTED){
+ if (shift)
+ 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!
+
+ 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 (Scene *scene, Object *obedit, ListBase *edbo, int findunsel, int *selmask)
+{
+ ViewContext vc;
+ EditBone *ebone;
+ rcti rect;
+ unsigned int buffer[MAXPICKBUF];
+ unsigned int hitresult, besthitresult=BONESEL_NOSEL;
+ int i, mindep= 4;
+ short hits;
+
+ memset(&vc, 0, sizeof(ViewContext));
+ vc.scene= scene;
+ vc.obedit= scene->obedit;
+
+ glInitNames();
+
+// getmouseco_areawin(mval);
+ // fill in rect! +- 5
+
+ hits= view3d_opengl_select(&vc, buffer, MAXPICKBUF, &rect);
+ if(hits==0)
+ // rect +- 12
+ hits= view3d_opengl_select(&vc, buffer, MAXPICKBUF, &rect);
+
+ /* 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(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(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(ListBase *edbo, EditBone* exBone)
+{
+ EditBone *curBone;
+
+ /* Find any bones that refer to this bone */
+ for (curBone=edbo->first;curBone;curBone=curBone->next) {
+ if (curBone->parent==exBone) {
+ curBone->parent=exBone->parent;
+ curBone->flag &= ~BONE_CONNECTED;
+ }
+ }
+
+ BLI_freelinkN(edbo, exBone);
+}
+
+/* context: editmode armature */
+EditBone *armature_bone_get_mirrored(ListBase *edbo, 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= edbo->first; eboflip; eboflip=eboflip->next) {
+ if (ebo != eboflip) {
+ if (!strcmp (name, eboflip->name))
+ break;
+ }
+ }
+
+ return eboflip;
+}
+
+/* only editmode! */
+void delete_armature(Scene *scene)
+{
+ Object *obedit= scene->obedit; // XXX get from context
+ bArmature *arm= obedit->data;
+ EditBone *curBone, *next;
+ bConstraint *con;
+
+ if (okee("Erase selected bone(s)")==0) return;
+
+ /* Select mirrored bones */
+ if (arm->flag & ARM_MIRROR_EDIT) {
+ for (curBone=arm->edbo->first; curBone; curBone=curBone->next) {
+ if (arm->layer & curBone->layer) {
+ if (curBone->flag & BONE_SELECTED) {
+ next = armature_bone_get_mirrored(arm->edbo, curBone);
+ if (next)
+ next->flag |= BONE_SELECTED;
+ }
+ }
+ }
+ }
+
+ /* First erase any associated pose channel */
+ if (obedit->pose) {
+ bPoseChannel *chan, *next;
+ for (chan=obedit->pose->chanbase.first; chan; chan=next) {
+ next= chan->next;
+ curBone = editbone_name_exists(arm->edbo, chan->name);
+
+ if (curBone && (curBone->flag & BONE_SELECTED) && (arm->layer & curBone->layer)) {
+ free_constraints(&chan->constraints);
+ BLI_freelinkN (&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 == obedit) {
+ if (ct->subtarget[0]) {
+ curBone = editbone_name_exists(arm->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=arm->edbo->first;curBone;curBone=next) {
+ next=curBone->next;
+ if (arm->layer & curBone->layer) {
+ if (curBone->flag & BONE_SELECTED)
+ delete_bone(arm->edbo, curBone);
+ }
+ }
+
+
+ countall(); // flushes selection!
+
+ BIF_undo_push("Delete bone(s)");
+}
+
+/* toggle==0: deselect
+toggle==1: swap (based on test)
+toggle==2: only active tag
+toggle==3: swap (no test)
+*/
+void deselectall_armature(Object *obedit, int toggle, int doundo)
+{
+ bArmature *arm= 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=arm->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=arm->edbo->first;eBone;eBone=eBone->next) {
+ if (sel==3) {
+ /* invert selection of bone */
+ if ((arm->layer & eBone->layer) && (eBone->flag & BONE_HIDDEN_A)==0) {
+ eBone->flag ^= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
+ eBone->flag &= ~BONE_ACTIVE;
+ }
+ }
+ else if (sel==1) {
+ /* select bone */
+ 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) {
+ /* clear active flag */
+ eBone->flag &= ~(BONE_ACTIVE);
+ }
+ else {
+ /* deselect bone */
+ eBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL | BONE_ACTIVE);
+ }
+ }
+
+ countall(); // flushes selection!
+ if (doundo) {
+ if (sel==1) BIF_undo_push("Select All");
+ else BIF_undo_push("Deselect All");
+ }
+}
+
+
+/* context: editmode armature */
+void mouse_armature(Scene *scene, Object *obedit)
+{
+ bArmature *arm= obedit->data;
+ EditBone *nearBone = NULL, *ebone;
+ int selmask;
+ int shift= 0; // XXX
+
+ nearBone= get_nearest_editbonepoint(scene, obedit, arm->edbo, 1, &selmask);
+ if (nearBone) {
+
+ if (!(shift)) {
+ deselectall_armature(obedit, 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(shift) {
+ /* 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(shift) {
+ /* 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 ((shift) && (nearBone->flag & selmask))
+ nearBone->flag &= ~selmask;
+ else
+ nearBone->flag |= selmask;
+ }
+
+ countall(); // flushes selection!
+
+ if(nearBone) {
+ /* then now check for active status */
+ for (ebone=arm->edbo->first;ebone;ebone=ebone->next) ebone->flag &= ~BONE_ACTIVE;
+ if(nearBone->flag & BONE_SELECTED) nearBone->flag |= BONE_ACTIVE;
+ }
+
+ }
+
+// rightmouse_transform();
+}
+
+void ED_armature_edit_free(struct Object *ob)
+{
+ bArmature *arm= ob->data;
+
+ /* Clear the editbones list */
+ if (arm->edbo) {
+ if (arm->edbo->first)
+ BLI_freelistN(arm->edbo);
+ MEM_freeN(arm->edbo);
+ arm->edbo= NULL;
+ }
+}
+
+void ED_armature_edit_remake(Object *obedit)
+{
+ if(okee("Reload original data")==0) return;
+
+ ED_armature_to_edit(obedit);
+
+// BIF_undo_push("Delete bone");
+}
+
+/* Put armature in EditMode */
+void ED_armature_to_edit(Object *ob)
+{
+ bArmature *arm= ob->data;
+
+ ED_armature_edit_free(ob);
+ arm->edbo= MEM_callocN(sizeof(ListBase), "edbo armature");
+ make_boneList(arm->edbo, &arm->bonebase,NULL);
+}
+
+
+/* adjust bone roll to align Z axis with vector
+ * vec is in local space and is normalized
+ */
+float rollBoneToVector(EditBone *bone, float new_up_axis[3])
+{
+ float mat[3][3], nor[3], up_axis[3], vec[3];
+ float roll;
+
+ VecSubf(nor, bone->tail, bone->head);
+
+ vec_roll_to_mat3(nor, 0, mat);
+ VECCOPY(up_axis, mat[2]);
+
+ roll = NormalizedVecAngle2(new_up_axis, up_axis);
+
+ Crossf(vec, up_axis, new_up_axis);
+
+ if (Inpf(vec, nor) < 0)
+ {
+ roll = -roll;
+ }
+
+ return roll;
+}
+
+/* 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(Scene *scene, View3D *v3d, short mode)
+{
+ Object *obedit= scene->obedit; // XXX get from context
+ bArmature *arm= obedit->data;
+ EditBone *ebone;
+ EditBone *flipbone = NULL;
+ float delta[3];
+ float curmat[3][3];
+ float *cursor= give_cursor(scene, v3d);
+
+ for (ebone = arm->edbo->first; ebone; ebone=ebone->next) {
+ if (EBONE_VISIBLE(arm, ebone)) {
+ if (arm->flag & ARM_MIRROR_EDIT)
+ flipbone = armature_bone_get_mirrored(arm->edbo, ebone);
+
+ if ((ebone->flag & BONE_SELECTED) ||
+ (flipbone && (flipbone->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, 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 *lbuv, void *lbev)
+{
+ ListBase *lbu= lbuv;
+ ListBase *edbo= lbev;
+ EditBone *ebo, *newebo;
+
+ BLI_freelistN(edbo);
+
+ /* copy */
+ for(ebo= lbu->first; ebo; ebo= ebo->next) {
+ newebo= MEM_dupallocN(ebo);
+ ebo->temp= newebo;
+ BLI_addtail(edbo, newebo);
+ }
+
+ /* set pointers */
+ for(newebo= edbo->first; newebo; newebo= newebo->next) {
+ if(newebo->parent) newebo->parent= newebo->parent->temp;
+ }
+ /* be sure they dont hang ever */
+ for(newebo= edbo->first; newebo; newebo= newebo->next) {
+ newebo->temp= NULL;
+ }
+}
+
+static void *editBones_to_undoBones(void *lbev)
+{
+ ListBase *edbo= lbev;
+ ListBase *lb;
+ EditBone *ebo, *newebo;
+
+ lb= MEM_callocN(sizeof(ListBase), "listbase undo");
+
+ /* copy */
+ for(ebo= 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(bContext *C, char *name)
+{
+ // XXX solve getdata()
+ undo_editmode_push(C, name, NULL, free_undoBones, undoBones_to_editBones, editBones_to_undoBones, NULL);
+}
+
+
+
+/* **************** END EditMode stuff ********************** */
+/* *************** Adding stuff in editmode *************** */
+
+/* default bone add, returns it selected, but without tail set */
+static EditBone *add_editbone(Object *obedit, char *name)
+{
+ bArmature *arm= obedit->data;
+
+ EditBone *bone= MEM_callocN(sizeof(EditBone), "eBone");
+
+ BLI_strncpy(bone->name, name, 32);
+ unique_editbone_name(arm->edbo, bone->name);
+
+ BLI_addtail(arm->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(Scene *scene, View3D *v3d, short newob)
+{
+ Object *obedit= scene->obedit; // XXX get from context
+ float obmat[3][3], curs[3], viewmat[3][3], totmat[3][3], imat[3][3];
+ EditBone *bone;
+
+ VECCOPY(curs, give_cursor(scene, v3d));
+
+ /* Get inverse point for head and orientation for tail */
+ Mat4Invert(obedit->imat, obedit->obmat);
+ Mat4MulVecfl(obedit->imat, curs);
+
+ if ( !(newob) || (U.flag & USER_ADD_VIEWALIGNED) ) Mat3CpyMat4(obmat, v3d->viewmat);
+ else Mat3One(obmat);
+
+ Mat3CpyMat4(viewmat, obedit->obmat);
+ Mat3MulMat3(totmat, obmat, viewmat);
+ Mat3Inv(imat, totmat);
+
+ deselectall_armature(obedit, 0, 0);
+
+ /* Create a bone */
+ bone= add_editbone(obedit, "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(Scene *scene, View3D *v3d, int type)
+{
+ Object *obedit= scene->obedit; // XXX get from context
+ short newob=0;
+
+ if(scene->id.lib) return;
+
+ G.f &= ~(G_VERTEXPAINT+G_TEXTUREPAINT+G_WEIGHTPAINT+G_SCULPTMODE);
+// setcursor_space(SPACE_VIEW3D, CURSOR_STD);
+
+// XXX check_editmode(OB_ARMATURE);
+
+ /* If we're not the "obedit", make a new object and enter editmode */
+ if (obedit==NULL) {
+ add_object(scene, OB_ARMATURE);
+ ED_object_base_init_from_view(scene, v3d, BASACT);
+ obedit= BASACT->object;
+
+ where_is_object(scene, obedit);
+
+ ED_armature_to_edit(obedit);
+// setcursor_space(SPACE_VIEW3D, CURSOR_EDIT);
+ newob=1;
+ }
+
+ /* no primitive support yet */
+ add_primitive_bone(scene, v3d, newob);
+
+ countall(); // flushes selection!
+
+ if ((newob) && !(U.flag & USER_ADD_EDITMODE)) {
+ ED_armature_from_edit(scene, obedit);
+ ED_armature_edit_free(obedit);
+ }
+
+ BIF_undo_push("Add primitive");
+}
+
+/* the ctrl-click method */
+void addvert_armature(Scene *scene, View3D *v3d)
+{
+ Object *obedit= scene->obedit; // XXX get from context
+ bArmature *arm= obedit->data;
+ EditBone *ebone, *newbone, *flipbone;
+ float *curs, mat[3][3],imat[3][3];
+ int a, to_root= 0;
+
+ /* find the active or selected bone */
+ for (ebone = arm->edbo->first; ebone; ebone=ebone->next) {
+ if (EBONE_VISIBLE(arm, ebone)) {
+ if (ebone->flag & (BONE_ACTIVE|BONE_TIPSEL))
+ break;
+ }
+ }
+
+ if (ebone==NULL) {
+ for (ebone = arm->edbo->first; ebone; ebone=ebone->next) {
+ if (EBONE_VISIBLE(arm, ebone)) {
+ if (ebone->flag & (BONE_ACTIVE|BONE_ROOTSEL))
+ break;
+ }
+ }
+ if (ebone == NULL)
+ return;
+
+ to_root= 1;
+ }
+
+ deselectall_armature(obedit, 0, 0);
+
+ /* we re-use code for mirror editing... */
+ flipbone= NULL;
+ if (arm->flag & ARM_MIRROR_EDIT)
+ flipbone= armature_bone_get_mirrored(arm->edbo, ebone);
+
+ for (a=0; a<2; a++) {
+ if (a==1) {
+ if (flipbone==NULL)
+ break;
+ else {
+ SWAP(EditBone *, flipbone, ebone);
+ }
+ }
+
+ newbone= add_editbone(obedit, 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(scene, v3d);
+ VECCOPY(newbone->tail, curs);
+ VecSubf(newbone->tail, newbone->tail, obedit->obmat[3]);
+
+ if (a==1)
+ newbone->tail[0]= -newbone->tail[0];
+
+ Mat3CpyMat4(mat, 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");
+}
+
+/* adds an EditBone between the nominated locations (should be in the right space) */
+static EditBone *add_points_bone (Object *obedit, float head[], float tail[])
+{
+ EditBone *ebo;
+
+ ebo= add_editbone(obedit, "Bone");
+
+ VECCOPY(ebo->head, head);
+ VECCOPY(ebo->tail, tail);
+
+ return ebo;
+}
+
+
+static EditBone *get_named_editbone(ListBase *edbo, char *name)
+{
+ EditBone *eBone;
+
+ if (name) {
+ for (eBone=edbo->first; eBone; eBone=eBone->next) {
+ if (!strcmp(name, eBone->name))
+ return eBone;
+ }
+ }
+
+ return NULL;
+}
+
+static void update_dup_subtarget(Object *obedit, EditBone *dupBone)
+{
+ /* If an edit bone has been duplicated, lets
+ * update it's constraints if the subtarget
+ * they point to has also been duplicated
+ */
+ bArmature *arm = obedit->data;
+ EditBone *oldtarget, *newtarget;
+ bPoseChannel *chan;
+ bConstraint *curcon;
+ ListBase *conlist;
+
+ if ( (chan = verify_pose_channel(obedit->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 == obedit) && (ct->subtarget[0])) {
+ oldtarget = get_named_editbone(arm->edbo, 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(Scene *scene)
+{
+ Object *obedit= scene->obedit; // XXX get from context
+ bArmature *arm= obedit->data;
+ EditBone *eBone = NULL;
+ EditBone *curBone;
+ EditBone *firstDup=NULL; /* The beginning of the duplicated bones in the edbo list */
+
+ countall(); // flushes selection!
+
+ /* Select mirrored bones */
+ if (arm->flag & ARM_MIRROR_EDIT) {
+ for (curBone=arm->edbo->first; curBone; curBone=curBone->next) {
+ if (EBONE_VISIBLE(arm, curBone)) {
+ if (curBone->flag & BONE_SELECTED) {
+ eBone = armature_bone_get_mirrored(arm->edbo, curBone);
+ if (eBone)
+ eBone->flag |= BONE_SELECTED;
+ }
+ }
+ }
+ }
+
+ /* Find the selected bones and duplicate them as needed */
+ for (curBone=arm->edbo->first; curBone && curBone!=firstDup; curBone=curBone->next) {
+ if (EBONE_VISIBLE(arm, curBone)) {
+ 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(arm->edbo, eBone->name);
+ BLI_addtail(arm->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;
+
+ /* copy bone group */
+ channew->agrp_index= chanold->agrp_index;
+
+ /* 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);
+
+ /* custom shape */
+ channew->custom= chanold->custom;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* Run though the list and fix the pointers */
+ for (curBone=arm->edbo->first; curBone && curBone!=firstDup; curBone=curBone->next) {
+ if (EBONE_VISIBLE(arm, curBone)) {
+ 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(obedit, eBone);
+ }
+ }
+ }
+
+ /* Deselect the old bones and select the new ones */
+
+ for (curBone=arm->edbo->first; curBone && curBone!=firstDup; curBone=curBone->next) {
+ if (EBONE_VISIBLE(arm, curBone))
+ curBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL | BONE_ACTIVE);
+ }
+
+// XXX BIF_TransformSetUndo("Add Duplicate");
+// initTransform(TFM_TRANSLATION, CTX_NO_PET);
+// Transform();
+}
+
+
+
+/* *************** END Adding stuff in editmode *************** */
+/* ************** Add/Remove stuff in editmode **************** */
+
+/* temporary data-structure for merge/fill bones */
+typedef struct EditBonePoint {
+ struct EditBonePoint *next, *prev;
+
+ EditBone *head_owner; /* EditBone which uses this point as a 'head' point */
+ EditBone *tail_owner; /* EditBone which uses this point as a 'tail' point */
+
+ float vec[3]; /* the actual location of the point in local/EditMode space */
+} EditBonePoint;
+
+/* find chain-tips (i.e. bones without children) */
+static void chains_find_tips (ListBase *edbo, ListBase *list)
+{
+ EditBone *curBone, *ebo;
+ LinkData *ld;
+
+ /* note: this is potentially very slow ... there's got to be a better way */
+ for (curBone= edbo->first; curBone; curBone= curBone->next) {
+ short stop= 0;
+
+ /* is this bone contained within any existing chain? (skip if so) */
+ for (ld= list->first; ld; ld= ld->next) {
+ for (ebo= ld->data; ebo; ebo= ebo->parent) {
+ if (ebo == curBone) {
+ stop= 1;
+ break;
+ }
+ }
+
+ if (stop) break;
+ }
+ /* skip current bone if it is part of an existing chain */
+ if (stop) continue;
+
+ /* is any existing chain part of the chain formed by this bone? */
+ stop= 0;
+ for (ebo= curBone->parent; ebo; ebo= ebo->parent) {
+ for (ld= list->first; ld; ld= ld->next) {
+ if (ld->data == ebo) {
+ ld->data= curBone;
+ stop= 1;
+ break;
+ }
+ }
+
+ if (stop) break;
+ }
+ /* current bone has already been added to a chain? */
+ if (stop) continue;
+
+ /* add current bone to a new chain */
+ ld= MEM_callocN(sizeof(LinkData), "BoneChain");
+ ld->data= curBone;
+ BLI_addtail(list, ld);
+ }
+}
+
+
+static void fill_add_joint (EditBone *ebo, short eb_tail, ListBase *points)
+{
+ EditBonePoint *ebp;
+ float vec[3];
+ short found= 0;
+
+ if (eb_tail) {
+ VECCOPY(vec, ebo->tail);
+ }
+ else {
+ VECCOPY(vec, ebo->head);
+ }
+
+ for (ebp= points->first; ebp; ebp= ebp->next) {
+ if (VecEqual(ebp->vec, vec)) {
+ if (eb_tail) {
+ if ((ebp->head_owner) && (ebp->head_owner->parent == ebo)) {
+ /* so this bone's tail owner is this bone */
+ ebp->tail_owner= ebo;
+ found= 1;
+ break;
+ }
+ }
+ else {
+ if ((ebp->tail_owner) && (ebo->parent == ebp->tail_owner)) {
+ /* so this bone's head owner is this bone */
+ ebp->head_owner= ebo;
+ found = 1;
+ break;
+ }
+ }
+ }
+ }
+
+ /* allocate a new point if no existing point was related */
+ if (found == 0) {
+ ebp= MEM_callocN(sizeof(EditBonePoint), "EditBonePoint");
+
+ if (eb_tail) {
+ VECCOPY(ebp->vec, ebo->tail);
+ ebp->tail_owner= ebo;
+ }
+ else {
+ VECCOPY(ebp->vec, ebo->head);
+ ebp->head_owner= ebo;
+ }
+
+ BLI_addtail(points, ebp);
+ }
+}
+
+/* bone adding between selected joints */
+void fill_bones_armature(Scene *scene, View3D *v3d)
+{
+ Object *obedit= scene->obedit; // XXX get from context
+ bArmature *arm= obedit->data;
+ EditBone *ebo, *newbone=NULL;
+ ListBase points = {NULL, NULL};
+ int count;
+
+ /* loop over all bones, and only consider if visible */
+ for (ebo= arm->edbo->first; ebo; ebo= ebo->next) {
+ if (EBONE_VISIBLE(arm, ebo)) {
+ if (!(ebo->flag & BONE_CONNECTED) && (ebo->flag & BONE_ROOTSEL))
+ fill_add_joint(ebo, 0, &points);
+ if (ebo->flag & BONE_TIPSEL)
+ fill_add_joint(ebo, 1, &points);
+ }
+ }
+
+ /* the number of joints determines how we fill:
+ * 1) between joint and cursor (joint=head, cursor=tail)
+ * 2) between the two joints (order is dependent on active-bone/hierachy)
+ * 3+) error (a smarter method involving finding chains needs to be worked out
+ */
+ count= BLI_countlist(&points);
+
+ if (count == 0) {
+ error("No joints selected");
+ return;
+ }
+ else if (count == 1) {
+ EditBonePoint *ebp;
+ float *fp, curs[3];
+
+ /* Get Points - selected joint */
+ ebp= (EditBonePoint *)points.first;
+
+ /* Get points - cursor (tail) */
+ fp= give_cursor(scene, v3d);
+ VECCOPY (curs, fp);
+
+ Mat4Invert(obedit->imat, obedit->obmat);
+ Mat4MulVecfl(obedit->imat, curs);
+
+ /* Create a bone */
+ newbone= add_points_bone(obedit, ebp->vec, curs);
+ }
+ else if (count == 2) {
+ EditBonePoint *ebp, *ebp2;
+ float head[3], tail[3];
+ short headtail = 0;
+
+ /* check that the points don't belong to the same bone */
+ ebp= (EditBonePoint *)points.first;
+ ebp2= ebp->next;
+
+ if ((ebp->head_owner==ebp2->tail_owner) && (ebp->head_owner!=NULL)) {
+ error("Same bone selected...");
+ BLI_freelistN(&points);
+ return;
+ }
+ if ((ebp->tail_owner==ebp2->head_owner) && (ebp->tail_owner!=NULL)) {
+ error("Same bone selected...");
+ BLI_freelistN(&points);
+ return;
+ }
+
+ /* find which one should be the 'head' */
+ if ((ebp->head_owner && ebp2->head_owner) || (ebp->tail_owner && ebp2->tail_owner)) {
+ /* rule: whichever one is closer to 3d-cursor */
+ float curs[3], *fp= give_cursor(scene, v3d);
+ float vecA[3], vecB[3];
+ float distA, distB;
+
+ /* get cursor location */
+ VECCOPY(curs, fp);
+
+ Mat4Invert(obedit->imat, obedit->obmat);
+ Mat4MulVecfl(obedit->imat, curs);
+
+ /* get distances */
+ VecSubf(vecA, ebp->vec, curs);
+ VecSubf(vecB, ebp2->vec, curs);
+ distA= VecLength(vecA);
+ distB= VecLength(vecB);
+
+ /* compare distances - closer one therefore acts as direction for bone to go */
+ headtail= (distA < distB) ? 2 : 1;
+ }
+ else if (ebp->head_owner) {
+ headtail = 1;
+ }
+ else if (ebp2->head_owner) {
+ headtail = 2;
+ }
+
+ /* assign head/tail combinations */
+ if (headtail == 2) {
+ VECCOPY(head, ebp->vec);
+ VECCOPY(tail, ebp2->vec);
+ }
+ else if (headtail == 1) {
+ VECCOPY(head, ebp2->vec);
+ VECCOPY(tail, ebp->vec);
+ }
+
+ /* add new bone and parent it to the appropriate end */
+ if (headtail) {
+ newbone= add_points_bone(obedit, head, tail);
+
+ /* do parenting (will need to set connected flag too) */
+ if (headtail == 2) {
+ /* ebp tail or head - tail gets priority */
+ if (ebp->tail_owner)
+ newbone->parent= ebp->tail_owner;
+ else
+ newbone->parent= ebp->head_owner;
+ }
+ else {
+ /* ebp2 tail or head - tail gets priority */
+ if (ebp2->tail_owner)
+ newbone->parent= ebp2->tail_owner;
+ else
+ newbone->parent= ebp2->head_owner;
+ }
+
+ newbone->flag |= BONE_CONNECTED;
+ }
+ }
+ else {
+ // FIXME.. figure out a method for multiple bones
+ error("Too many points selected");
+ printf("Points selected: %d \n", count);
+ BLI_freelistN(&points);
+ return;
+ }
+
+ /* free points */
+ BLI_freelistN(&points);
+
+ /* undo + updates */
+ BIF_undo_push("Fill Bones");
+}
+
+/* this function merges between two bones, removes them and those in-between,
+ * and adjusts the parent relationships for those in-between
+ */
+static void bones_merge(Object *obedit, EditBone *start, EditBone *end, EditBone *endchild, ListBase *chains)
+{
+ bArmature *arm= obedit->data;
+ EditBone *ebo, *ebone, *newbone;
+ LinkData *chain;
+ float head[3], tail[3];
+
+ /* check if same bone */
+ if (start == end) {
+ printf("Error: same bone! \n");
+ printf("\tstart = %s, end = %s \n", start->name, end->name);
+ }
+
+ /* step 1: add a new bone
+ * - head = head/tail of start (default head)
+ * - tail = head/tail of end (default tail)
+ * - parent = parent of start
+ */
+ if ((start->flag & BONE_TIPSEL) && !(start->flag & (BONE_SELECTED|BONE_ACTIVE))) {
+ VECCOPY(head, start->tail);
+ }
+ else {
+ VECCOPY(head, start->head);
+ }
+ if ((end->flag & BONE_ROOTSEL) && !(end->flag & (BONE_SELECTED|BONE_ACTIVE))) {
+ VECCOPY(tail, end->head);
+ }
+ else {
+ VECCOPY(tail, end->tail);
+ }
+ newbone= add_points_bone(obedit, head, tail);
+ newbone->parent = start->parent;
+
+ /* step 2a: parent children of in-between bones to newbone */
+ for (chain= chains->first; chain; chain= chain->next) {
+ /* ick: we need to check if parent of each bone in chain is one of the bones in the */
+ for (ebo= chain->data; ebo; ebo= ebo->parent) {
+ short found= 0;
+
+ /* try to find which bone from the list to be removed, is the parent */
+ for (ebone= end; ebone; ebone= ebone->parent) {
+ if (ebo->parent == ebone) {
+ found= 1;
+ break;
+ }
+ }
+
+ /* adjust this bone's parent to newbone then */
+ if (found) {
+ ebo->parent= newbone;
+ break;
+ }
+ }
+ }
+
+ /* step 2b: parent child of end to newbone (child from this chain) */
+ if (endchild)
+ endchild->parent= newbone;
+
+ /* step 3: delete all bones between and including start and end */
+ for (ebo= end; ebo; ebo= ebone) {
+ ebone= (ebo == start) ? (NULL) : (ebo->parent);
+ BLI_freelinkN(arm->edbo, ebo);
+ }
+}
+
+/* bone merging - has a menu! */
+void merge_armature(Scene *scene)
+{
+ Object *obedit= scene->obedit; // XXX get from context
+ bArmature *arm= obedit->data;
+ short val= 0;
+
+ /* process a menu to determine how to merge */
+ // TODO: there's room for more modes of merging stuff...
+ val= pupmenu("Merge Selected Bones%t|Within Chains%x1");
+ if (val <= 0) return;
+
+ if (val == 1) {
+ /* go down chains, merging bones */
+ ListBase chains = {NULL, NULL};
+ LinkData *chain, *nchain;
+ EditBone *ebo;
+
+ /* get chains (ends on chains) */
+ chains_find_tips(arm->edbo, &chains);
+ if (chains.first == NULL) return;
+
+ /* each 'chain' is the last bone in the chain (with no children) */
+ for (chain= chains.first; chain; chain= nchain) {
+ EditBone *bstart= NULL, *bend= NULL;
+ EditBone *bchild= NULL, *child=NULL;
+
+ /* temporarily remove chain from list of chains */
+ nchain= chain->next;
+ BLI_remlink(&chains, chain);
+
+ /* only consider bones that are visible and selected */
+ for (ebo=chain->data; ebo; child=ebo, ebo=ebo->parent) {
+ /* check if visible + selected */
+ if ( EBONE_VISIBLE(arm, ebo) &&
+ ((ebo->flag & BONE_CONNECTED) || (ebo->parent==NULL)) &&
+ (ebo->flag & (BONE_SELECTED|BONE_ACTIVE)) )
+ {
+ /* set either end or start (end gets priority, unless it is already set) */
+ if (bend == NULL) {
+ bend= ebo;
+ bchild= child;
+ }
+ else
+ bstart= ebo;
+ }
+ else {
+ /* chain is broken... merge any continous segments then clear */
+ if (bstart && bend)
+ bones_merge(obedit, bstart, bend, bchild, &chains);
+
+ bstart = NULL;
+ bend = NULL;
+ bchild = NULL;
+ }
+ }
+
+ /* merge from bstart to bend if something not merged */
+ if (bstart && bend)
+ bones_merge(obedit, bstart, bend, bchild, &chains);
+
+ /* put back link */
+ BLI_insertlinkbefore(&chains, nchain, chain);
+ }
+
+ BLI_freelistN(&chains);
+ }
+
+ /* undo + updates */
+ countall();
+ BIF_undo_push("Merge Bones");
+}
+
+/* ************** END Add/Remove stuff in editmode ************ */
+/* *************** Tools in editmode *********** */
+
+
+void hide_selected_armature_bones(Scene *scene)
+{
+ Object *obedit= scene->obedit; // XXX get from context
+ bArmature *arm= obedit->data;
+ EditBone *ebone;
+
+ for (ebone = arm->edbo->first; ebone; ebone=ebone->next) {
+ if (EBONE_VISIBLE(arm, ebone)) {
+ if (ebone->flag & (BONE_SELECTED)) {
+ ebone->flag &= ~(BONE_TIPSEL|BONE_SELECTED|BONE_ROOTSEL|BONE_ACTIVE);
+ ebone->flag |= BONE_HIDDEN_A;
+ }
+ }
+ }
+ countall();
+ BIF_undo_push("Hide Bones");
+}
+
+void hide_unselected_armature_bones(Scene *scene)
+{
+ Object *obedit= scene->obedit; // XXX get from context
+ bArmature *arm= obedit->data;
+ EditBone *ebone;
+
+ for (ebone = arm->edbo->first; ebone; ebone=ebone->next) {
+ bArmature *arm= obedit->data;
+ if (EBONE_VISIBLE(arm, ebone)) {
+ if (ebone->flag & (BONE_TIPSEL|BONE_SELECTED|BONE_ROOTSEL));
+ else {
+ ebone->flag &= ~BONE_ACTIVE;
+ ebone->flag |= BONE_HIDDEN_A;
+ }
+ }
+ }
+ countall();
+ BIF_undo_push("Hide Unselected Bones");
+}
+
+void show_all_armature_bones(Scene *scene)
+{
+ Object *obedit= scene->obedit; // XXX get from context
+ bArmature *arm= obedit->data;
+ EditBone *ebone;
+
+ for (ebone = arm->edbo->first; ebone; ebone=ebone->next) {
+ 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();
+ BIF_undo_push("Reveal Bones");
+}
+
+/* check for null, before calling! */
+static void bone_connect_to_existing_parent(EditBone *bone)
+{
+ bone->flag |= BONE_CONNECTED;
+ VECCOPY(bone->head, bone->parent->tail);
+ bone->rad_head = bone->parent->rad_tail;
+}
+
+static void bone_connect_to_new_parent(ListBase *edbo, EditBone *selbone, EditBone *actbone, short mode)
+{
+ EditBone *ebone;
+ float offset[3];
+
+ 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 (mode == 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 = 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;
+ }
+}
+
+void make_bone_parent(Scene *scene)
+{
+ Object *obedit= scene->obedit; // XXX get from context
+ bArmature *arm= obedit->data;
+ EditBone *actbone, *ebone, *selbone;
+ EditBone *flipbone, *flippar;
+ short allchildbones= 0, foundselbone= 0;
+ short val;
+
+ /* find active bone to parent to */
+ for (actbone = arm->edbo->first; actbone; actbone=actbone->next) {
+ if (EBONE_VISIBLE(arm, actbone)) {
+ if (actbone->flag & BONE_ACTIVE)
+ break;
+ }
+ }
+ if (actbone == NULL) {
+ error("Needs an active bone");
+ return;
+ }
+
+ /* find selected bones */
+ for (ebone = arm->edbo->first; ebone; ebone=ebone->next) {
+ if (EBONE_VISIBLE(arm, ebone)) {
+ 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.
+ */
+ bone_connect_to_existing_parent(actbone);
+
+ if (arm->flag & ARM_MIRROR_EDIT) {
+ flipbone = armature_bone_get_mirrored(arm->edbo, actbone);
+ if (flipbone)
+ bone_connect_to_existing_parent(flipbone);
+ }
+ }
+ else {
+ /* loop through all editbones, parenting all selected bones to the active bone */
+ for (selbone = arm->edbo->first; selbone; selbone=selbone->next) {
+ if (EBONE_VISIBLE(arm, selbone)) {
+ if ((selbone->flag & BONE_SELECTED) && (selbone!=actbone)) {
+ /* parent selbone to actbone */
+ bone_connect_to_new_parent(arm->edbo, selbone, actbone, val);
+
+ if (arm->flag & ARM_MIRROR_EDIT) {
+ /* - if there's a mirrored copy of selbone, try to find a mirrored copy of actbone
+ * (i.e. selbone="child.L" and actbone="parent.L", find "child.R" and "parent.R").
+ * This is useful for arm-chains, for example parenting lower arm to upper arm
+ * - if there's no mirrored copy of actbone (i.e. actbone = "parent.C" or "parent")
+ * then just use actbone. Useful when doing upper arm to spine.
+ */
+ flipbone = armature_bone_get_mirrored(arm->edbo, selbone);
+ flippar = armature_bone_get_mirrored(arm->edbo, actbone);
+
+ if (flipbone) {
+ if (flippar)
+ bone_connect_to_new_parent(arm->edbo, flipbone, flippar, val);
+ else
+ bone_connect_to_new_parent(arm->edbo, flipbone, actbone, val);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ countall(); /* checks selection */
+ BIF_undo_push("Make Parent");
+
+ return;
+}
+
+static void editbone_clear_parent(EditBone *ebone, int mode)
+{
+ if (ebone->parent) {
+ /* for nice selection */
+ ebone->parent->flag &= ~(BONE_TIPSEL);
+ }
+
+ if (mode==1) ebone->parent= NULL;
+ ebone->flag &= ~BONE_CONNECTED;
+}
+
+void clear_bone_parent(Scene *scene)
+{
+ Object *obedit= scene->obedit; // XXX get from context
+ bArmature *arm= obedit->data;
+ EditBone *ebone;
+ EditBone *flipbone = NULL;
+ short val;
+
+ val= pupmenu("Clear Parent%t|Clear Parent%x1|Disconnect Bone%x2");
+ if (val<1) return;
+
+ for (ebone = arm->edbo->first; ebone; ebone=ebone->next) {
+ if (EBONE_VISIBLE(arm, ebone)) {
+ if (ebone->flag & BONE_SELECTED) {
+ if (arm->flag & ARM_MIRROR_EDIT)
+ flipbone = armature_bone_get_mirrored(arm->edbo, ebone);
+
+ if (flipbone)
+ editbone_clear_parent(flipbone, val);
+ editbone_clear_parent(ebone, val);
+ }
+ }
+ }
+
+ countall(); // checks selection
+ BIF_undo_push("Clear Parent");
+}
+
+
+/* context; editmode armature */
+/* if forked && mirror-edit: makes two bones with flipped names */
+void extrude_armature(Scene *scene, int forked)
+{
+ Object *obedit= scene->obedit; // XXX get from context
+ bArmature *arm= obedit->data;
+ EditBone *newbone, *ebone, *flipbone, *first=NULL;
+ int a, totbone= 0, do_extrude;
+
+ /* since we allow root extrude too, we have to make sure selection is OK */
+ for (ebone = arm->edbo->first; ebone; ebone=ebone->next) {
+ if (EBONE_VISIBLE(arm, ebone)) {
+ 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 = arm->edbo->first; ((ebone) && (ebone!=first)); ebone=ebone->next) {
+ if (EBONE_VISIBLE(arm, ebone)) {
+ /* 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(arm->edbo, 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
+
+ if (newbone->parent) newbone->flag |= BONE_CONNECTED;
+ }
+ else {
+ VECCOPY(newbone->head, ebone->head);
+ VECCOPY(newbone->tail, ebone->head);
+ newbone->parent= ebone->parent;
+
+ newbone->flag= BONE_TIPSEL;
+
+ if (newbone->parent && (ebone->flag & BONE_CONNECTED)) {
+ newbone->flag |= BONE_CONNECTED;
+ }
+ }
+
+ 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;
+
+ 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(arm->edbo, newbone->name);
+
+ /* Add the new bone to the list */
+ BLI_addtail(arm->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!
+// XXX BIF_TransformSetUndo("Extrude");
+// initTransform(TFM_TRANSLATION, CTX_NO_PET);
+// Transform();
+
+}
+
+/* context; editmode armature */
+void subdivide_armature(Scene *scene, int numcuts)
+{
+ Object *obedit= scene->obedit; // XXX get from context
+ bArmature *arm= obedit->data;
+ EditBone *ebone, *newbone, *tbone, *mbone;
+ int a, i;
+
+ if (numcuts < 1) return;
+
+ for (mbone = arm->edbo->last; mbone; mbone= mbone->prev) {
+ if (EBONE_VISIBLE(arm, mbone)) {
+ 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(arm->edbo, mbone);
+ else
+ ebone= NULL;
+ }
+ else
+ ebone= mbone;
+
+ if (ebone) {
+ newbone= MEM_mallocN(sizeof(EditBone), "ebone subdiv");
+ *newbone = *ebone;
+ BLI_addtail(arm->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 (arm->edbo, newbone->name);
+
+ /* correct parent bones */
+ for (tbone = arm->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");
+}
+
+/* switch direction of bone chains */
+void switch_direction_armature (Scene *scene)
+{
+ Object *obedit= scene->obedit; // XXX get from context
+ bArmature *arm= (obedit) ? obedit->data : NULL;
+ ListBase chains = {NULL, NULL};
+ LinkData *chain;
+
+ /* error checking paranoia */
+ if (arm == NULL)
+ return;
+
+ /* get chains of bones (ends on chains) */
+ chains_find_tips(arm->edbo, &chains);
+ if (chains.first == NULL) return;
+
+ /* loop over chains, only considering selected and visible bones */
+ for (chain= chains.first; chain; chain= chain->next) {
+ EditBone *ebo, *child=NULL, *parent=NULL;
+
+ /* loop over bones in chain */
+ for (ebo= chain->data; ebo; ebo= parent) {
+ /* parent is this bone's original parent
+ * - we store this, as the next bone that is checked is this one
+ * but the value of ebo->parent may change here...
+ */
+ parent= ebo->parent;
+
+ /* only if selected and editable */
+ if (EBONE_VISIBLE(arm, ebo) && EBONE_EDITABLE(ebo)) {
+ /* swap head and tail coordinates */
+ SWAP(float, ebo->head[0], ebo->tail[0]);
+ SWAP(float, ebo->head[1], ebo->tail[1]);
+ SWAP(float, ebo->head[2], ebo->tail[2]);
+
+ /* do parent swapping:
+ * - use 'child' as new parent
+ * - connected flag is only set if points are coincidental
+ */
+ ebo->parent= child;
+ if ((child) && VecEqual(ebo->head, child->tail))
+ ebo->flag |= BONE_CONNECTED;
+ else
+ ebo->flag &= ~BONE_CONNECTED;
+
+ /* get next bones
+ * - child will become the new parent of next bone
+ */
+ child= ebo;
+ }
+ else {
+ /* not swapping this bone, however, if its 'parent' got swapped, unparent us from it
+ * as it will be facing in opposite direction
+ */
+ if ((parent) && (EBONE_VISIBLE(arm, parent) && EBONE_EDITABLE(parent))) {
+ ebo->parent= NULL;
+ ebo->flag &= ~BONE_CONNECTED;
+ }
+
+ /* get next bones
+ * - child will become new parent of next bone (not swapping occurred,
+ * so set to NULL to prevent infinite-loop)
+ */
+ child= NULL;
+ }
+ }
+ }
+
+ /* free chains */
+ BLI_freelistN(&chains);
+
+ BIF_undo_push("Switch Direction");
+}
+
+/* editbone alignment */
+
+/* helper to fix a ebone position if its parent has moved due to alignment*/
+static void fix_connected_bone(EditBone *ebone)
+{
+ float diff[3];
+
+ if (!(ebone->parent) || !(ebone->flag & BONE_CONNECTED) || VecEqual(ebone->parent->tail, ebone->head))
+ return;
+
+ /* if the parent has moved we translate child's head and tail accordingly*/
+ VecSubf(diff, ebone->parent->tail, ebone->head);
+ VecAddf(ebone->head, ebone->head, diff);
+ VecAddf(ebone->tail, ebone->tail, diff);
+ return;
+}
+
+/* helper to recursively find chains of connected bones starting at ebone and fix their position */
+static void fix_editbone_connected_children(ListBase *edbo, EditBone *ebone)
+{
+ EditBone *selbone;
+
+ for (selbone = edbo->first; selbone; selbone=selbone->next) {
+ if ((selbone->parent) && (selbone->parent == ebone) && (selbone->flag & BONE_CONNECTED)) {
+ fix_connected_bone(selbone);
+ fix_editbone_connected_children(edbo, selbone);
+ }
+ }
+ return;
+}
+
+static void bone_align_to_bone(ListBase *edbo, EditBone *selbone, EditBone *actbone)
+{
+ float selboneaxis[3], actboneaxis[3], length;
+
+ VecSubf(actboneaxis, actbone->tail, actbone->head);
+ Normalize(actboneaxis);
+
+ VecSubf(selboneaxis, selbone->tail, selbone->head);
+ length = VecLength(selboneaxis);
+
+ VecMulf(actboneaxis, length);
+ VecAddf(selbone->tail, selbone->head, actboneaxis);
+ selbone->roll = actbone->roll;
+
+ /* if the bone being aligned has connected descendants they must be moved
+ according to their parent new position, otherwise they would be left
+ in an unconsistent state: connected but away from the parent*/
+ fix_editbone_connected_children(edbo, selbone);
+ return;
+}
+
+void align_selected_bones(Scene *scene)
+{
+ Object *obedit= scene->obedit; // XXX get from context
+ bArmature *arm= obedit->data;
+ EditBone *actbone, *ebone, *selbone;
+ EditBone *flipbone, *flippar;
+ short allchildbones= 0, foundselbone= 0;
+
+ /* find active bone to align to */
+ for (actbone = arm->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 = arm->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;
+ }
+
+ if (foundselbone==0 && actbone->parent) {
+ /* When only the active bone is selected, and it has a parent,
+ * align it to the parent, as that is the only possible outcome.
+ */
+ bone_align_to_bone(arm->edbo, actbone, actbone->parent);
+
+ if (arm->flag & ARM_MIRROR_EDIT) {
+ flipbone = armature_bone_get_mirrored(arm->edbo, actbone);
+ if (flipbone)
+ bone_align_to_bone(arm->edbo, flipbone, flipbone->parent);
+ }
+ }
+ else {
+ /* loop through all editbones, aligning all selected bones to the active bone */
+ for (selbone = arm->edbo->first; selbone; selbone=selbone->next) {
+ if (arm->layer & selbone->layer) {
+ if ((selbone->flag & BONE_SELECTED) && (selbone!=actbone)) {
+ /* align selbone to actbone */
+ bone_align_to_bone(arm->edbo, selbone, actbone);
+
+ if (arm->flag & ARM_MIRROR_EDIT) {
+ /* - if there's a mirrored copy of selbone, try to find a mirrored copy of actbone
+ * (i.e. selbone="child.L" and actbone="parent.L", find "child.R" and "parent.R").
+ * This is useful for arm-chains, for example parenting lower arm to upper arm
+ * - if there's no mirrored copy of actbone (i.e. actbone = "parent.C" or "parent")
+ * then just use actbone. Useful when doing upper arm to spine.
+ */
+ flipbone = armature_bone_get_mirrored(arm->edbo, selbone);
+ flippar = armature_bone_get_mirrored(arm->edbo, actbone);
+
+ if (flipbone) {
+ if (flippar)
+ bone_align_to_bone(arm->edbo, flipbone, flippar);
+ else
+ bone_align_to_bone(arm->edbo, flipbone, actbone);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ countall(); /* checks selection */
+ BIF_undo_push("Align bones");
+
+ return;
+}
+
+/* ***************** Pose tools ********************* */
+
+void clear_armature(Scene *scene, Object *ob, char mode)
+{
+ bPoseChannel *pchan;
+ bArmature *arm= ob->data;
+
+ /* only clear those channels that are not locked */
+ 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':
+ if (pchan->protectflag & (OB_LOCK_ROTX|OB_LOCK_ROTY|OB_LOCK_ROTZ)) {
+ float eul[3], oldeul[3], quat1[4];
+
+ QUATCOPY(quat1, pchan->quat);
+ QuatToEul(pchan->quat, oldeul);
+ eul[0]= eul[1]= eul[2]= 0.0f;
+
+ if (pchan->protectflag & OB_LOCK_ROTX)
+ eul[0]= oldeul[0];
+ if (pchan->protectflag & OB_LOCK_ROTY)
+ eul[1]= oldeul[1];
+ if (pchan->protectflag & OB_LOCK_ROTZ)
+ eul[2]= oldeul[2];
+
+ EulToQuat(eul, pchan->quat);
+ /* quaternions flip w sign to accumulate rotations correctly */
+ if ((quat1[0]<0.0f && pchan->quat[0]>0.0f) || (quat1[0]>0.0f && pchan->quat[0]<0.0f)) {
+ QuatMulf(pchan->quat, -1.0f);
+ }
+ }
+ else {
+ pchan->quat[1]=pchan->quat[2]=pchan->quat[3]=0.0F;
+ pchan->quat[0]=1.0F;
+ }
+ break;
+ case 'g':
+ if ((pchan->protectflag & OB_LOCK_LOCX)==0)
+ pchan->loc[0]= 0.0f;
+ if ((pchan->protectflag & OB_LOCK_LOCY)==0)
+ pchan->loc[1]= 0.0f;
+ if ((pchan->protectflag & OB_LOCK_LOCZ)==0)
+ pchan->loc[2]= 0.0f;
+ break;
+ case 's':
+ if ((pchan->protectflag & OB_LOCK_SCALEX)==0)
+ pchan->size[0]= 1.0f;
+ if ((pchan->protectflag & OB_LOCK_SCALEY)==0)
+ pchan->size[1]= 1.0f;
+ if ((pchan->protectflag & OB_LOCK_SCALEZ)==0)
+ 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(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 (scene, 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;
+}
+
+
+static 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;
+}
+
+/* called from editview.c, for mode-less pose selection */
+/* assumes scene obact and basact... XXX */
+int do_pose_selectbuffer(Scene *scene, Base *base, unsigned int *buffer, short hits)
+{
+ Object *ob= base->object;
+ Bone *nearBone;
+ int shift= 0; // XXX
+
+ if (!ob || !ob->pose) return 0;
+
+ nearBone= get_bone_from_selectbuffer(scene, base, buffer, hits, 1);
+
+ if (nearBone) {
+ bArmature *arm= ob->data;
+
+ /* since we do unified select, we don't shift+select a bone if the armature object was not active yet */
+ if (!(shift) || (base != scene->basact)) {
+ ED_pose_deselectall(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) {
+ 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 {
+ 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(ob, nearBone->name);
+ DAG_object_flush_update(scene, ob, OB_RECALC_DATA);
+ }
+ }
+
+ }
+
+ return nearBone!=NULL;
+}
+
+/* test==0: deselect all
+ test==1: swap select (apply to all the opposite of current situation)
+ test==2: only clear active tag
+ test==3: swap select (no test / inverse selection status of all independently)
+*/
+void ED_pose_deselectall (Object *ob, int test, int doundo)
+{
+ bArmature *arm= ob->data;
+ bPoseChannel *pchan;
+ int selectmode= 0;
+
+ /* we call this from outliner too */
+ if (ELEM(NULL, ob, ob->pose)) return;
+
+ /* 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 (test==3) {
+ pchan->bone->flag ^= (BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL);
+ pchan->bone->flag &= ~BONE_ACTIVE;
+ }
+ else {
+ 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 */
+ if (test == 3) {
+ deselect_actionchannels(ob->action, 2); /* inverts selection */
+ }
+ else {
+ deselect_actionchannels(ob->action, 0); /* deselects for sure */
+ if (selectmode == 1)
+ deselect_actionchannels(ob->action, 1); /* swaps */
+ }
+
+ countall();
+
+ if (doundo) {
+ if (selectmode==1) BIF_undo_push("Select All");
+ else BIF_undo_push("Deselect All");
+ }
+}
+
+static int bone_skinnable(Object *ob, Bone *bone, void *datap)
+{
+ /* 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;
+ int a, segments;
+ struct { Object *armob; void *list; int heat; } *data = datap;
+
+ if(!(G.f & G_WEIGHTPAINT) || !(bone->flag & BONE_HIDDEN_P)) {
+ if (!(bone->flag & BONE_NO_DEFORM)) {
+ if (data->heat && data->armob->pose && get_pose_channel(data->armob->pose, bone->name))
+ segments = bone->segments;
+ else
+ segments = 1;
+
+ if (data->list != NULL) {
+ hbone = (Bone ***) &data->list;
+
+ for (a=0; a<segments; a++) {
+ **hbone = bone;
+ ++*hbone;
+ }
+ }
+ return segments;
+ }
+ }
+ 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 *datap)
+{
+ /* 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;
+ int a, segments;
+ struct { Object *armob; void *list; int heat; } *data= datap;
+
+ if (!(G.f & G_WEIGHTPAINT) || !(bone->flag & BONE_HIDDEN_P)) {
+ if (!(bone->flag & BONE_NO_DEFORM)) {
+ if (data->heat && data->armob->pose && get_pose_channel(data->armob->pose, bone->name))
+ segments = bone->segments;
+ else
+ segments = 1;
+
+ if (!(defgroup = get_named_vertexgroup(ob, bone->name)))
+ defgroup = add_defgroup_name(ob, bone->name);
+
+ if (data->list != NULL) {
+ hgroup = (bDeformGroup ***) &data->list;
+
+ for (a=0; a<segments; a++) {
+ **hgroup = defgroup;
+ ++*hgroup;
+ }
+ }
+ return segments;
+ }
+ }
+ 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(Scene *scene, 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= ob->data;
+ Bone **bonelist, *bone;
+ bDeformGroup **dgrouplist, **dgroupflip;
+ bDeformGroup *dgroup, *curdg;
+ bPoseChannel *pchan;
+ Mesh *mesh;
+ Mat4 *bbone = NULL;
+ float (*root)[3], (*tip)[3], (*verts)[3];
+ int *selected;
+ int numbones, vertsfilled = 0, i, j, segments = 0;
+ int wpmode = (G.f & G_WEIGHTPAINT);
+ struct { Object *armob; void *list; int heat; } looper_data;
+
+ looper_data.armob = par;
+ looper_data.heat= heat;
+ looper_data.list= NULL;
+
+ /* count the number of skinnable bones */
+ numbones = bone_looper(ob, arm->bonebase.first, &looper_data, 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");
+ looper_data.list= bonelist;
+ bone_looper(ob, arm->bonebase.first, &looper_data, 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");
+
+ looper_data.list= dgrouplist;
+ bone_looper(ob, arm->bonebase.first, &looper_data, 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];
+
+ /* handle bbone */
+ if (heat) {
+ if (segments == 0) {
+ segments = 1;
+ bbone = NULL;
+
+ if ((par->pose) && (pchan=get_pose_channel(par->pose, bone->name))) {
+ if (bone->segments > 1) {
+ segments = bone->segments;
+ bbone = b_bone_spline_setup(pchan, 1);
+ }
+ }
+ }
+
+ segments--;
+ }
+
+ /* compute root and tip */
+ if (bbone) {
+ VECCOPY(root[j], bbone[segments].mat[3]);
+ Mat4MulVecfl(bone->arm_mat, root[j]);
+ if ((segments+1) < bone->segments) {
+ VECCOPY(tip[j], bbone[segments+1].mat[3])
+ Mat4MulVecfl(bone->arm_mat, tip[j]);
+ }
+ else
+ VECCOPY(tip[j], bone->arm_tail)
+ }
+ else {
+ VECCOPY(root[j], bone->arm_head);
+ VECCOPY(tip[j], bone->arm_tail);
+ }
+
+ Mat4MulVecfl(par->obmat, root[j]);
+ 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(scene, 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(Scene *scene, Object *ob, Object *par)
+{
+ /* Lets try to create some vertex groups
+ * based on the bones of the parent armature.
+ */
+ bArmature *arm= ob->data;
+ short mode;
+
+ /* 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(scene, 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|BONE_ACTIVE);
+ }
+ }
+ return 0;
+}
+
+/* active object is armature */
+void hide_selected_pose_bones(Object *ob)
+{
+ bArmature *arm= ob->data;
+
+ if (!arm)
+ return;
+
+ bone_looper(ob, arm->bonebase.first, NULL,
+ hide_selected_pose_bone);
+
+ 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) {
+ // hrm... typo here?
+ if (~bone->flag & BONE_SELECTED) {
+ bone->flag |= BONE_HIDDEN_P;
+ bone->flag &= ~BONE_ACTIVE;
+ }
+ }
+ return 0;
+}
+
+/* active object is armature */
+void hide_unselected_pose_bones(Object *ob)
+{
+ bArmature *arm= ob->data;
+
+ bone_looper(ob, arm->bonebase.first, NULL,
+ hide_unselected_pose_bone);
+
+ 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(Object *ob)
+{
+ bArmature *arm= ob->data;
+
+ bone_looper(ob, arm->bonebase.first, NULL,
+ show_pose_bone);
+
+ 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 armature_bone_rename */
+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(Object *ob, char *oldnamep, char *newnamep)
+{
+ bArmature *arm= ob->data;
+ Ipo *ipo;
+ 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 (arm->edbo) {
+ EditBone *eBone;
+
+ eBone= editbone_name_exists(arm->edbo, oldname);
+ if (eBone) {
+ unique_editbone_name(arm->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 - objects */
+ 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);
+ }
+ }
+ }
+
+ /* do entire db - ipo's for the drivers */
+ for (ipo= G.main->ipo.first; ipo; ipo= ipo->id.next) {
+ IpoCurve *icu;
+
+ /* check each curve's driver */
+ for (icu= ipo->curve.first; icu; icu= icu->next) {
+ IpoDriver *icd= icu->driver;
+
+ if ((icd) && (icd->ob)) {
+ ob= icd->ob;
+
+ if (icu->driver->type == IPO_DRIVER_TYPE_NORMAL) {
+ if (!strcmp(oldname, icd->name))
+ BLI_strncpy(icd->name, newname, MAXBONENAME);
+ }
+ else {
+ /* TODO: pydrivers need to be treated differently */
+ }
+ }
+ }
+ }
+ }
+}
+
+/* context editmode object */
+void armature_flip_names(Scene *scene)
+{
+ Object *obedit= scene->obedit; // XXX get from context
+ bArmature *arm= obedit->data;
+ EditBone *ebone;
+ char newname[32];
+
+ for (ebone = arm->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(obedit, ebone->name, newname);
+ }
+ }
+ }
+
+ BIF_undo_push("Flip names");
+}
+
+/* context: edtimode armature */
+void armature_autoside_names(Scene *scene, short axis)
+{
+ Object *obedit= scene->obedit; // XXX get from context
+ bArmature *arm= obedit->data;
+ EditBone *ebone;
+ char newname[32];
+
+ for (ebone = arm->edbo->first; ebone; ebone=ebone->next) {
+ if (arm->layer & ebone->layer) {
+ if (ebone->flag & BONE_SELECTED) {
+ BLI_strncpy(newname, ebone->name, sizeof(newname));
+ bone_autoside_name(newname, 1, axis, ebone->head[axis], ebone->tail[axis]);
+ armature_bone_rename(obedit, ebone->name, newname);
+ }
+ }
+ }
+
+ BIF_undo_push("Auto-side name");
+}
+
+/* if editbone (partial) selected, copy data */
+/* context; editmode armature, with mirror editing enabled */
+void transform_armature_mirror_update(Object *obedit)
+{
+ bArmature *arm= obedit->data;
+ EditBone *ebo, *eboflip;
+
+ for (ebo= arm->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(arm->edbo, ebo);
+
+ if (eboflip) {
+ /* we assume X-axis flipping for now */
+ if (ebo->flag & BONE_TIPSEL) {
+ EditBone *children;
+
+ eboflip->tail[0]= -ebo->tail[0];
+ eboflip->tail[1]= ebo->tail[1];
+ eboflip->tail[2]= ebo->tail[2];
+ eboflip->rad_tail= ebo->rad_tail;
+
+ /* Also move connected children, in case children's name aren't mirrored properly */
+ for (children=arm->edbo->first; children; children=children->next) {
+ if (children->parent == eboflip && children->flag & BONE_CONNECTED) {
+ VECCOPY(children->head, eboflip->tail);
+ children->rad_head = 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;
+
+ /* Also move connected parent, in case parent's name isn't mirrored properly */
+ if (eboflip->parent && eboflip->flag & BONE_CONNECTED)
+ {
+ EditBone *parent = eboflip->parent;
+ VECCOPY(parent->tail, eboflip->head);
+ parent->rad_tail = ebo->rad_head;
+ }
+ }
+ if (ebo->flag & BONE_SELECTED) {
+ eboflip->dist= ebo->dist;
+ eboflip->roll= -ebo->roll;
+ eboflip->xwidth= ebo->xwidth;
+ eboflip->zwidth= ebo->zwidth;
+ }
+ }
+ }
+ }
+}
+
+
+/*****************************************************************************************************/
+/*************************************** SKELETON GENERATOR ******************************************/
+/*****************************************************************************************************/
+
+
+
+/**************************************** SUBDIVISION ALGOS ******************************************/
+
+EditBone * subdivideByAngle(Scene *scene, Object *obedit, ReebArc *arc, ReebNode *head, ReebNode *tail)
+{
+ bArmature *arm= obedit->data;
+ EditBone *lastBone = NULL;
+
+ if (scene->toolsettings->skgen_options & SKGEN_CUT_ANGLE)
+ {
+ ReebArcIterator iter;
+ EmbedBucket *current = NULL;
+ EmbedBucket *previous = NULL;
+ EditBone *child = NULL;
+ EditBone *parent = NULL;
+ EditBone *root = NULL;
+ float angleLimit = (float)cos(scene->toolsettings->skgen_angle_limit * M_PI / 180.0f);
+
+ parent = add_editbone(obedit, "Bone");
+ parent->flag |= BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL;
+ VECCOPY(parent->head, head->p);
+
+ root = parent;
+
+ for (initArcIterator(&iter, arc, head), previous = nextBucket(&iter), current = nextBucket(&iter);
+ current;
+ previous = current, current = nextBucket(&iter))
+ {
+ float vec1[3], vec2[3];
+ float len1, len2;
+
+ VecSubf(vec1, previous->p, parent->head);
+ VecSubf(vec2, current->p, previous->p);
+
+ len1 = Normalize(vec1);
+ len2 = Normalize(vec2);
+
+ if (len1 > 0.0f && len2 > 0.0f && Inpf(vec1, vec2) < angleLimit)
+ {
+ VECCOPY(parent->tail, previous->p);
+
+ child = add_editbone(obedit, "Bone");
+ VECCOPY(child->head, parent->tail);
+ child->parent = parent;
+ child->flag |= BONE_CONNECTED|BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL;
+
+ parent = child; /* new child is next parent */
+ }
+ }
+ VECCOPY(parent->tail, tail->p);
+
+ /* If the bone wasn't subdivided, delete it and return NULL
+ * to let subsequent subdivision methods do their thing.
+ * */
+ if (parent == root)
+ {
+ delete_bone(arm->edbo, parent);
+ parent = NULL;
+ }
+
+ lastBone = parent; /* set last bone in the chain */
+ }
+
+ return lastBone;
+}
+
+float calcVariance(ReebArc *arc, int start, int end, float v0[3], float n[3])
+{
+ int len = 2 + abs(end - start);
+
+ if (len > 2)
+ {
+ ReebArcIterator iter;
+ EmbedBucket *bucket = NULL;
+ float avg_t = 0.0f;
+ float s_t = 0.0f;
+ float s_xyz = 0.0f;
+
+ /* First pass, calculate average */
+ for (initArcIterator2(&iter, arc, start, end), bucket = nextBucket(&iter);
+ bucket;
+ bucket = nextBucket(&iter))
+ {
+ float v[3];
+
+ VecSubf(v, bucket->p, v0);
+ avg_t += Inpf(v, n);
+ }
+
+ avg_t /= Inpf(n, n);
+ avg_t += 1.0f; /* adding start (0) and end (1) values */
+ avg_t /= len;
+
+ /* Second pass, calculate s_xyz and s_t */
+ for (initArcIterator2(&iter, arc, start, end), bucket = nextBucket(&iter);
+ bucket;
+ bucket = nextBucket(&iter))
+ {
+ float v[3], d[3];
+ float dt;
+
+ VecSubf(v, bucket->p, v0);
+ Projf(d, v, n);
+ VecSubf(v, v, d);
+
+ dt = VecLength(d) - avg_t;
+
+ s_t += dt * dt;
+ s_xyz += Inpf(v, v);
+ }
+
+ /* adding start(0) and end(1) values to s_t */
+ s_t += (avg_t * avg_t) + (1 - avg_t) * (1 - avg_t);
+
+ return s_xyz / s_t;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+float calcDistance(ReebArc *arc, int start, int end, float head[3], float tail[3])
+{
+ ReebArcIterator iter;
+ EmbedBucket *bucket = NULL;
+ float max_dist = 0;
+
+ /* calculate maximum distance */
+ for (initArcIterator2(&iter, arc, start, end), bucket = nextBucket(&iter);
+ bucket;
+ bucket = nextBucket(&iter))
+ {
+ float v1[3], v2[3], c[3];
+ float dist;
+
+ VecSubf(v1, head, tail);
+ VecSubf(v2, bucket->p, tail);
+
+ Crossf(c, v1, v2);
+
+ dist = Inpf(c, c) / Inpf(v1, v1);
+
+ max_dist = dist > max_dist ? dist : max_dist;
+ }
+
+
+ return max_dist;
+}
+
+EditBone * subdivideByCorrelation(Scene *scene, Object *obedit, ReebArc *arc, ReebNode *head, ReebNode *tail)
+{
+ ReebArcIterator iter;
+ float n[3];
+ float ADAPTIVE_THRESHOLD = scene->toolsettings->skgen_correlation_limit;
+ EditBone *lastBone = NULL;
+
+ /* init iterator to get start and end from head */
+ initArcIterator(&iter, arc, head);
+
+ /* Calculate overall */
+ VecSubf(n, arc->buckets[iter.end].p, head->p);
+
+ if (scene->toolsettings->skgen_options & SKGEN_CUT_CORRELATION)
+ {
+ EmbedBucket *bucket = NULL;
+ EmbedBucket *previous = NULL;
+ EditBone *child = NULL;
+ EditBone *parent = NULL;
+ float normal[3] = {0, 0, 0};
+ float avg_normal[3];
+ int total = 0;
+ int boneStart = iter.start;
+
+ parent = add_editbone(obedit, "Bone");
+ parent->flag = BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL;
+ VECCOPY(parent->head, head->p);
+
+ for (previous = nextBucket(&iter), bucket = nextBucket(&iter);
+ bucket;
+ previous = bucket, bucket = nextBucket(&iter))
+ {
+ float btail[3];
+ float value = 0;
+
+ if (scene->toolsettings->skgen_options & SKGEN_STICK_TO_EMBEDDING)
+ {
+ VECCOPY(btail, bucket->p);
+ }
+ else
+ {
+ float length;
+
+ /* Calculate normal */
+ VecSubf(n, bucket->p, parent->head);
+ length = Normalize(n);
+
+ total += 1;
+ VecAddf(normal, normal, n);
+ VECCOPY(avg_normal, normal);
+ VecMulf(avg_normal, 1.0f / total);
+
+ VECCOPY(btail, avg_normal);
+ VecMulf(btail, length);
+ VecAddf(btail, btail, parent->head);
+ }
+
+ if (scene->toolsettings->skgen_options & SKGEN_ADAPTIVE_DISTANCE)
+ {
+ value = calcDistance(arc, boneStart, iter.index, parent->head, btail);
+ }
+ else
+ {
+ float n[3];
+
+ VecSubf(n, btail, parent->head);
+ value = calcVariance(arc, boneStart, iter.index, parent->head, n);
+ }
+
+ if (value > ADAPTIVE_THRESHOLD)
+ {
+ VECCOPY(parent->tail, btail);
+
+ child = add_editbone(obedit, "Bone");
+ VECCOPY(child->head, parent->tail);
+ child->parent = parent;
+ child->flag |= BONE_CONNECTED|BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL;
+
+ parent = child; // new child is next parent
+ boneStart = iter.index; // start from end
+
+ normal[0] = normal[1] = normal[2] = 0;
+ total = 0;
+ }
+ }
+
+ VECCOPY(parent->tail, tail->p);
+
+ lastBone = parent; /* set last bone in the chain */
+ }
+
+ return lastBone;
+}
+
+float arcLengthRatio(ReebArc *arc)
+{
+ float arcLength = 0.0f;
+ float embedLength = 0.0f;
+ int i;
+
+ arcLength = VecLenf(arc->head->p, arc->tail->p);
+
+ if (arc->bcount > 0)
+ {
+ /* Add the embedding */
+ for ( i = 1; i < arc->bcount; i++)
+ {
+ embedLength += VecLenf(arc->buckets[i - 1].p, arc->buckets[i].p);
+ }
+ /* Add head and tail -> embedding vectors */
+ embedLength += VecLenf(arc->head->p, arc->buckets[0].p);
+ embedLength += VecLenf(arc->tail->p, arc->buckets[arc->bcount - 1].p);
+ }
+ else
+ {
+ embedLength = arcLength;
+ }
+
+ return embedLength / arcLength;
+}
+
+EditBone * subdivideByLength(Scene *scene, Object *obedit, ReebArc *arc, ReebNode *head, ReebNode *tail)
+{
+ EditBone *lastBone = NULL;
+
+ if ((scene->toolsettings->skgen_options & SKGEN_CUT_LENGTH) &&
+ arcLengthRatio(arc) >= scene->toolsettings->skgen_length_ratio)
+ {
+ ReebArcIterator iter;
+ EmbedBucket *bucket = NULL;
+ EmbedBucket *previous = NULL;
+ EditBone *child = NULL;
+ EditBone *parent = NULL;
+ float lengthLimit = scene->toolsettings->skgen_length_limit;
+ int same = 0;
+
+ parent = add_editbone(obedit, "Bone");
+ parent->flag |= BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL;
+ VECCOPY(parent->head, head->p);
+
+ initArcIterator(&iter, arc, head);
+
+ bucket = nextBucket(&iter);
+
+ while (bucket != NULL)
+ {
+ float *vec0 = NULL;
+ float *vec1 = bucket->p;
+
+ /* first bucket. Previous is head */
+ if (previous == NULL)
+ {
+ vec0 = head->p;
+ }
+ /* Previous is a valid bucket */
+ else
+ {
+ vec0 = previous->p;
+ }
+
+ /* If lengthLimit hits the current segment */
+ if (VecLenf(vec1, parent->head) > lengthLimit)
+ {
+ if (same == 0)
+ {
+ float dv[3], off[3];
+ float a, b, c, f;
+
+ /* Solve quadratic distance equation */
+ VecSubf(dv, vec1, vec0);
+ a = Inpf(dv, dv);
+
+ VecSubf(off, vec0, parent->head);
+ b = 2 * Inpf(dv, off);
+
+ c = Inpf(off, off) - (lengthLimit * lengthLimit);
+
+ f = (-b + (float)sqrt(b * b - 4 * a * c)) / (2 * a);
+
+ //printf("a %f, b %f, c %f, f %f\n", a, b, c, f);
+
+ if (isnan(f) == 0 && f < 1.0f)
+ {
+ VECCOPY(parent->tail, dv);
+ VecMulf(parent->tail, f);
+ VecAddf(parent->tail, parent->tail, vec0);
+ }
+ else
+ {
+ VECCOPY(parent->tail, vec1);
+ }
+ }
+ else
+ {
+ float dv[3];
+
+ VecSubf(dv, vec1, vec0);
+ Normalize(dv);
+
+ VECCOPY(parent->tail, dv);
+ VecMulf(parent->tail, lengthLimit);
+ VecAddf(parent->tail, parent->tail, parent->head);
+ }
+
+ child = add_editbone(obedit, "Bone");
+ VECCOPY(child->head, parent->tail);
+ child->parent = parent;
+ child->flag |= BONE_CONNECTED|BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL;
+
+ parent = child; // new child is next parent
+
+ same = 1; // mark as same
+ }
+ else
+ {
+ previous = bucket;
+ bucket = nextBucket(&iter);
+ same = 0; // Reset same
+ }
+ }
+ VECCOPY(parent->tail, tail->p);
+
+ lastBone = parent; /* set last bone in the chain */
+ }
+
+ return lastBone;
+}
+
+/***************************************** MAIN ALGORITHM ********************************************/
+
+void generateSkeletonFromReebGraph(Scene *scene, ReebGraph *rg)
+{
+ Object *obedit= scene->obedit; // XXX get from context
+ GHash *arcBoneMap = NULL;
+ ReebArc *arc = NULL;
+ ReebNode *node = NULL;
+ Object *src = NULL;
+ Object *dst = NULL;
+
+ src = scene->basact->object;
+
+ if (obedit != NULL)
+ {
+ ED_armature_from_edit(scene, obedit);
+ ED_armature_edit_free(obedit);
+ }
+
+ dst = add_object(scene, OB_ARMATURE);
+ ED_object_base_init_from_view(scene, NULL, scene->basact);
+ obedit= scene->basact->object;
+
+ /* Copy orientation from source */
+ VECCOPY(dst->loc, src->obmat[3]);
+ Mat4ToEul(src->obmat, dst->rot);
+ Mat4ToSize(src->obmat, dst->size);
+
+ where_is_object(scene, obedit);
+
+ ED_armature_to_edit(obedit);
+
+ arcBoneMap = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp);
+
+ BLI_markdownSymmetry((BGraph*)rg, rg->nodes.first, scene->toolsettings->skgen_symmetry_limit);
+
+ for (arc = rg->arcs.first; arc; arc = arc->next)
+ {
+ EditBone *lastBone = NULL;
+ ReebNode *head, *tail;
+ int i;
+
+ /* Find out the direction of the arc through simple heuristics (in order of priority) :
+ *
+ * 1- Arcs on primary symmetry axis (symmetry == 1) point up (head: high weight -> tail: low weight)
+ * 2- Arcs starting on a primary axis point away from it (head: node on primary axis)
+ * 3- Arcs point down (head: low weight -> tail: high weight)
+ *
+ * Finally, the arc direction is stored in its flag: 1 (low -> high), -1 (high -> low)
+ */
+
+ /* if arc is a symmetry axis, internal bones go up the tree */
+ if (arc->symmetry_level == 1 && arc->tail->degree != 1)
+ {
+ head = arc->tail;
+ tail = arc->head;
+
+ arc->flag = -1; /* mark arc direction */
+ }
+ /* Bones point AWAY from the symmetry axis */
+ else if (arc->head->symmetry_level == 1)
+ {
+ head = arc->head;
+ tail = arc->tail;
+
+ arc->flag = 1; /* mark arc direction */
+ }
+ else if (arc->tail->symmetry_level == 1)
+ {
+ head = arc->tail;
+ tail = arc->head;
+
+ arc->flag = -1; /* mark arc direction */
+ }
+ /* otherwise, always go from low weight to high weight */
+ else
+ {
+ head = arc->head;
+ tail = arc->tail;
+
+ arc->flag = 1; /* mark arc direction */
+ }
+
+ /* Loop over subdivision methods */
+ for (i = 0; lastBone == NULL && i < SKGEN_SUB_TOTAL; i++)
+ {
+ switch(scene->toolsettings->skgen_subdivisions[i])
+ {
+ case SKGEN_SUB_LENGTH:
+ lastBone = subdivideByLength(scene, obedit, arc, head, tail);
+ break;
+ case SKGEN_SUB_ANGLE:
+ lastBone = subdivideByAngle(scene, obedit, arc, head, tail);
+ break;
+ case SKGEN_SUB_CORRELATION:
+ lastBone = subdivideByCorrelation(scene, obedit, arc, head, tail);
+ break;
+ }
+ }
+
+ if (lastBone == NULL)
+ {
+ EditBone *bone;
+ bone = add_editbone(obedit, "Bone");
+ bone->flag |= BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL;
+
+ VECCOPY(bone->head, head->p);
+ VECCOPY(bone->tail, tail->p);
+
+ /* set first and last bone, since there's only one */
+ lastBone = bone;
+ }
+
+ BLI_ghash_insert(arcBoneMap, arc, lastBone);
+ }
+
+ /* Second pass, setup parent relationship between arcs */
+ for (node = rg->nodes.first; node; node = node->next)
+ {
+ ReebArc *incomingArc = NULL;
+ int i;
+
+ for (i = 0; i < node->degree; i++)
+ {
+ arc = (ReebArc*)node->arcs[i];
+
+ /* if arc is incoming into the node */
+ if ((arc->head == node && arc->flag == -1) || (arc->tail == node && arc->flag == 1))
+ {
+ if (incomingArc == NULL)
+ {
+ incomingArc = arc;
+ /* loop further to make sure there's only one incoming arc */
+ }
+ else
+ {
+ /* skip this node if more than one incomingArc */
+ incomingArc = NULL;
+ break; /* No need to look further, we are skipping already */
+ }
+ }
+ }
+
+ if (incomingArc != NULL)
+ {
+ EditBone *parentBone = BLI_ghash_lookup(arcBoneMap, incomingArc);
+
+ /* Look for outgoing arcs and parent their bones */
+ for (i = 0; i < node->degree; i++)
+ {
+ arc = node->arcs[i];
+
+ /* if arc is outgoing from the node */
+ if ((arc->head == node && arc->flag == 1) || (arc->tail == node && arc->flag == -1))
+ {
+ EditBone *childBone = BLI_ghash_lookup(arcBoneMap, arc);
+
+ /* find the root bone */
+ while(childBone->parent != NULL)
+ {
+ childBone = childBone->parent;
+ }
+
+ childBone->parent = parentBone;
+ childBone->flag |= BONE_CONNECTED;
+ }
+ }
+ }
+ }
+
+ BLI_ghash_free(arcBoneMap, NULL, NULL);
+
+ BIF_undo_push("Generate Skeleton");
+}
+
+void generateSkeleton(Scene *scene)
+{
+ ReebGraph *reebg;
+
+// setcursor_space(SPACE_VIEW3D, CURSOR_WAIT);
+
+ reebg = BIF_ReebGraphFromEditMesh();
+
+ generateSkeletonFromReebGraph(scene, reebg);
+
+ REEB_freeGraph(reebg);
+
+ //setcursor_space(SPACE_VIEW3D, CURSOR_EDIT);
+}
diff --git a/source/blender/editors/armature/meshlaplacian.c b/source/blender/editors/armature/meshlaplacian.c
new file mode 100644
index 00000000000..1db891c86fa
--- /dev/null
+++ b/source/blender/editors/armature/meshlaplacian.c
@@ -0,0 +1,1923 @@
+/**
+ * $Id$
+ *
+ * ***** BEGIN GPL 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.
+ *
+ * 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 LICENSE BLOCK *****
+ * meshlaplacian.c: Algorithms using the mesh laplacian.
+ */
+
+#include <math.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_listBase.h"
+#include "DNA_object_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_scene_types.h"
+
+#include "BLI_arithb.h"
+#include "BLI_edgehash.h"
+#include "BLI_memarena.h"
+
+#include "BKE_DerivedMesh.h"
+#include "BKE_utildefines.h"
+
+#ifdef RIGID_DEFORM
+#include "BLI_editVert.h"
+#include "BLI_polardecomp.h"
+#endif
+
+#include "RE_raytrace.h"
+
+#include "ONL_opennl.h"
+
+#include "BLO_sys_types.h" // for intptr_t support
+
+#include "meshlaplacian.h"
+
+
+/* ************* XXX *************** */
+static void remove_vert_defgroup() {}
+static int mesh_get_x_mirror_vert() {return 0;}
+static void waitcursor() {}
+static void progress_bar() {}
+static void start_progress_bar() {}
+static void end_progress_bar() {}
+static float get_vert_defgroup() {return NULL;}
+static void add_vert_to_defgroup() {}
+#define WEIGHT_REPLACE 0
+#define WEIGHT_ADD 0
+static void error() {}
+/* ************* XXX *************** */
+
+
+/************************** Laplacian System *****************************/
+
+struct LaplacianSystem {
+ NLContext context; /* opennl context */
+
+ int totvert, totface;
+
+ float **verts; /* vertex coordinates */
+ float *varea; /* vertex weights for laplacian computation */
+ char *vpinned; /* vertex pinning */
+ int (*faces)[3]; /* face vertex indices */
+ float (*fweights)[3]; /* cotangent weights per face */
+
+ int areaweights; /* use area in cotangent weights? */
+ int storeweights; /* store cotangent weights in fweights */
+ int nlbegun; /* nlBegin(NL_SYSTEM/NL_MATRIX) done */
+
+ EdgeHash *edgehash; /* edge hash for construction */
+
+ struct HeatWeighting {
+ Mesh *mesh;
+ float (*verts)[3]; /* vertex coordinates */
+ float (*vnors)[3]; /* vertex normals */
+
+ float (*root)[3]; /* bone root */
+ float (*tip)[3]; /* bone tip */
+ int numbones;
+
+ float *H; /* diagonal H matrix */
+ float *p; /* values from all p vectors */
+ float *mindist; /* minimum distance to a bone for all vertices */
+
+ RayTree *raytree; /* ray tracing acceleration structure */
+ MFace **vface; /* a face that the vertex belongs to */
+ } heat;
+
+#ifdef RIGID_DEFORM
+ struct RigidDeformation {
+ EditMesh *mesh;
+
+ float (*R)[3][3];
+ float (*rhs)[3];
+ float (*origco)[3];
+ int thrownerror;
+ } rigid;
+#endif
+};
+
+/* Laplacian matrix construction */
+
+/* Computation of these weights for the laplacian is based on:
+ "Discrete Differential-Geometry Operators for Triangulated 2-Manifolds",
+ Meyer et al, 2002. Section 3.5, formula (8).
+
+ We do it a bit different by going over faces instead of going over each
+ vertex and adjacent faces, since we don't store this adjacency. Also, the
+ formulas are tweaked a bit to work for non-manifold meshes. */
+
+static void laplacian_increase_edge_count(EdgeHash *edgehash, int v1, int v2)
+{
+ void **p = BLI_edgehash_lookup_p(edgehash, v1, v2);
+
+ if(p)
+ *p = (void*)((intptr_t)*p + (intptr_t)1);
+ else
+ BLI_edgehash_insert(edgehash, v1, v2, (void*)(intptr_t)1);
+}
+
+static int laplacian_edge_count(EdgeHash *edgehash, int v1, int v2)
+{
+ return (int)(intptr_t)BLI_edgehash_lookup(edgehash, v1, v2);
+}
+
+static float cotan_weight(float *v1, float *v2, float *v3)
+{
+ float a[3], b[3], c[3], clen;
+
+ VecSubf(a, v2, v1);
+ VecSubf(b, v3, v1);
+ Crossf(c, a, b);
+
+ clen = VecLength(c);
+
+ if (clen == 0.0f)
+ return 0.0f;
+
+ return Inpf(a, b)/clen;
+}
+
+static void laplacian_triangle_area(LaplacianSystem *sys, int i1, int i2, int i3)
+{
+ float t1, t2, t3, len1, len2, len3, area;
+ float *varea= sys->varea, *v1, *v2, *v3;
+ int obtuse = 0;
+
+ v1= sys->verts[i1];
+ v2= sys->verts[i2];
+ v3= sys->verts[i3];
+
+ t1= cotan_weight(v1, v2, v3);
+ t2= cotan_weight(v2, v3, v1);
+ t3= cotan_weight(v3, v1, v2);
+
+ if(VecAngle3(v2, v1, v3) > 90) obtuse= 1;
+ else if(VecAngle3(v1, v2, v3) > 90) obtuse= 2;
+ else if(VecAngle3(v1, v3, v2) > 90) obtuse= 3;
+
+ if (obtuse > 0) {
+ area= AreaT3Dfl(v1, v2, v3);
+
+ varea[i1] += (obtuse == 1)? area: area*0.5;
+ varea[i2] += (obtuse == 2)? area: area*0.5;
+ varea[i3] += (obtuse == 3)? area: area*0.5;
+ }
+ else {
+ len1= VecLenf(v2, v3);
+ len2= VecLenf(v1, v3);
+ len3= VecLenf(v1, v2);
+
+ t1 *= len1*len1;
+ t2 *= len2*len2;
+ t3 *= len3*len3;
+
+ varea[i1] += (t2 + t3)*0.25f;
+ varea[i2] += (t1 + t3)*0.25f;
+ varea[i3] += (t1 + t2)*0.25f;
+ }
+}
+
+static void laplacian_triangle_weights(LaplacianSystem *sys, int f, int i1, int i2, int i3)
+{
+ float t1, t2, t3;
+ float *varea= sys->varea, *v1, *v2, *v3;
+
+ v1= sys->verts[i1];
+ v2= sys->verts[i2];
+ v3= sys->verts[i3];
+
+ /* instead of *0.5 we divided by the number of faces of the edge, it still
+ needs to be verified that this is indeed the correct thing to do! */
+ t1= cotan_weight(v1, v2, v3)/laplacian_edge_count(sys->edgehash, i2, i3);
+ t2= cotan_weight(v2, v3, v1)/laplacian_edge_count(sys->edgehash, i3, i1);
+ t3= cotan_weight(v3, v1, v2)/laplacian_edge_count(sys->edgehash, i1, i2);
+
+ nlMatrixAdd(i1, i1, (t2+t3)*varea[i1]);
+ nlMatrixAdd(i2, i2, (t1+t3)*varea[i2]);
+ nlMatrixAdd(i3, i3, (t1+t2)*varea[i3]);
+
+ nlMatrixAdd(i1, i2, -t3*varea[i1]);
+ nlMatrixAdd(i2, i1, -t3*varea[i2]);
+
+ nlMatrixAdd(i2, i3, -t1*varea[i2]);
+ nlMatrixAdd(i3, i2, -t1*varea[i3]);
+
+ nlMatrixAdd(i3, i1, -t2*varea[i3]);
+ nlMatrixAdd(i1, i3, -t2*varea[i1]);
+
+ if(sys->storeweights) {
+ sys->fweights[f][0]= t1*varea[i1];
+ sys->fweights[f][1]= t2*varea[i2];
+ sys->fweights[f][2]= t3*varea[i3];
+ }
+}
+
+LaplacianSystem *laplacian_system_construct_begin(int totvert, int totface, int lsq)
+{
+ LaplacianSystem *sys;
+
+ sys= MEM_callocN(sizeof(LaplacianSystem), "LaplacianSystem");
+
+ sys->verts= MEM_callocN(sizeof(float*)*totvert, "LaplacianSystemVerts");
+ sys->vpinned= MEM_callocN(sizeof(char)*totvert, "LaplacianSystemVpinned");
+ sys->faces= MEM_callocN(sizeof(int)*3*totface, "LaplacianSystemFaces");
+
+ sys->totvert= 0;
+ sys->totface= 0;
+
+ sys->areaweights= 1;
+ sys->storeweights= 0;
+
+ /* create opennl context */
+ nlNewContext();
+ nlSolverParameteri(NL_NB_VARIABLES, totvert);
+ if(lsq)
+ nlSolverParameteri(NL_LEAST_SQUARES, NL_TRUE);
+
+ sys->context= nlGetCurrent();
+
+ return sys;
+}
+
+void laplacian_add_vertex(LaplacianSystem *sys, float *co, int pinned)
+{
+ sys->verts[sys->totvert]= co;
+ sys->vpinned[sys->totvert]= pinned;
+ sys->totvert++;
+}
+
+void laplacian_add_triangle(LaplacianSystem *sys, int v1, int v2, int v3)
+{
+ sys->faces[sys->totface][0]= v1;
+ sys->faces[sys->totface][1]= v2;
+ sys->faces[sys->totface][2]= v3;
+ sys->totface++;
+}
+
+void laplacian_system_construct_end(LaplacianSystem *sys)
+{
+ int (*face)[3];
+ int a, totvert=sys->totvert, totface=sys->totface;
+
+ laplacian_begin_solve(sys, 0);
+
+ sys->varea= MEM_callocN(sizeof(float)*totvert, "LaplacianSystemVarea");
+
+ sys->edgehash= BLI_edgehash_new();
+ for(a=0, face=sys->faces; a<sys->totface; a++, face++) {
+ laplacian_increase_edge_count(sys->edgehash, (*face)[0], (*face)[1]);
+ laplacian_increase_edge_count(sys->edgehash, (*face)[1], (*face)[2]);
+ laplacian_increase_edge_count(sys->edgehash, (*face)[2], (*face)[0]);
+ }
+
+ if(sys->areaweights)
+ for(a=0, face=sys->faces; a<sys->totface; a++, face++)
+ laplacian_triangle_area(sys, (*face)[0], (*face)[1], (*face)[2]);
+
+ for(a=0; a<totvert; a++) {
+ if(sys->areaweights) {
+ if(sys->varea[a] != 0.0f)
+ sys->varea[a]= 0.5f/sys->varea[a];
+ }
+ else
+ sys->varea[a]= 1.0f;
+
+ /* for heat weighting */
+ if(sys->heat.H)
+ nlMatrixAdd(a, a, sys->heat.H[a]);
+ }
+
+ if(sys->storeweights)
+ sys->fweights= MEM_callocN(sizeof(float)*3*totface, "LaplacianFWeight");
+
+ for(a=0, face=sys->faces; a<totface; a++, face++)
+ laplacian_triangle_weights(sys, a, (*face)[0], (*face)[1], (*face)[2]);
+
+ MEM_freeN(sys->faces);
+ sys->faces= NULL;
+
+ if(sys->varea) {
+ MEM_freeN(sys->varea);
+ sys->varea= NULL;
+ }
+
+ BLI_edgehash_free(sys->edgehash, NULL);
+ sys->edgehash= NULL;
+}
+
+void laplacian_system_delete(LaplacianSystem *sys)
+{
+ if(sys->verts) MEM_freeN(sys->verts);
+ if(sys->varea) MEM_freeN(sys->varea);
+ if(sys->vpinned) MEM_freeN(sys->vpinned);
+ if(sys->faces) MEM_freeN(sys->faces);
+ if(sys->fweights) MEM_freeN(sys->fweights);
+
+ nlDeleteContext(sys->context);
+ MEM_freeN(sys);
+}
+
+void laplacian_begin_solve(LaplacianSystem *sys, int index)
+{
+ int a;
+
+ if (!sys->nlbegun) {
+ nlBegin(NL_SYSTEM);
+
+ if(index >= 0) {
+ for(a=0; a<sys->totvert; a++) {
+ if(sys->vpinned[a]) {
+ nlSetVariable(0, a, sys->verts[a][index]);
+ nlLockVariable(a);
+ }
+ }
+ }
+
+ nlBegin(NL_MATRIX);
+ sys->nlbegun = 1;
+ }
+}
+
+void laplacian_add_right_hand_side(LaplacianSystem *sys, int v, float value)
+{
+ nlRightHandSideAdd(0, v, value);
+}
+
+int laplacian_system_solve(LaplacianSystem *sys)
+{
+ nlEnd(NL_MATRIX);
+ nlEnd(NL_SYSTEM);
+ sys->nlbegun = 0;
+
+ //nlPrintMatrix();
+
+ return nlSolveAdvanced(NULL, NL_TRUE);
+}
+
+float laplacian_system_get_solution(int v)
+{
+ return nlGetVariable(0, v);
+}
+
+/************************* Heat Bone Weighting ******************************/
+/* From "Automatic Rigging and Animation of 3D Characters"
+ Ilya Baran and Jovan Popovic, SIGGRAPH 2007 */
+
+#define C_WEIGHT 1.0f
+#define WEIGHT_LIMIT_START 0.05f
+#define WEIGHT_LIMIT_END 0.025f
+#define DISTANCE_EPSILON 1e-4f
+
+/* Raytracing for vertex to bone visibility */
+
+static LaplacianSystem *HeatSys = NULL;
+
+static void heat_ray_coords_func(RayFace *face, float **v1, float **v2, float **v3, float **v4)
+{
+ MFace *mface= (MFace*)face;
+ float (*verts)[3]= HeatSys->heat.verts;
+
+ *v1= verts[mface->v1];
+ *v2= verts[mface->v2];
+ *v3= verts[mface->v3];
+ *v4= (mface->v4)? verts[mface->v4]: NULL;
+}
+
+static int heat_ray_check_func(Isect *is, int ob, RayFace *face)
+{
+ float *v1, *v2, *v3, *v4, nor[3];
+
+ /* don't intersect if the ray faces along the face normal */
+ heat_ray_coords_func(face, &v1, &v2, &v3, &v4);
+
+ if(v4) CalcNormFloat4(v1, v2, v3, v4, nor);
+ else CalcNormFloat(v1, v2, v3, nor);
+
+ return (INPR(nor, is->vec) < 0);
+}
+
+static void heat_ray_tree_create(LaplacianSystem *sys)
+{
+ Mesh *me = sys->heat.mesh;
+ RayTree *tree;
+ MFace *mface;
+ float min[3], max[3];
+ int a;
+
+ /* create a raytrace tree from the mesh */
+ INIT_MINMAX(min, max);
+
+ for(a=0; a<me->totvert; a++)
+ DO_MINMAX(sys->heat.verts[a], min, max);
+
+ tree= RE_ray_tree_create(64, me->totface, min, max,
+ heat_ray_coords_func, heat_ray_check_func, NULL, NULL);
+
+ sys->heat.vface= MEM_callocN(sizeof(MFace*)*me->totvert, "HeatVFaces");
+
+ HeatSys= sys;
+
+ for(a=0, mface=me->mface; a<me->totface; a++, mface++) {
+ RE_ray_tree_add_face(tree, 0, mface);
+
+ sys->heat.vface[mface->v1]= mface;
+ sys->heat.vface[mface->v2]= mface;
+ sys->heat.vface[mface->v3]= mface;
+ if(mface->v4) sys->heat.vface[mface->v4]= mface;
+ }
+
+ HeatSys= NULL;
+
+ RE_ray_tree_done(tree);
+
+ sys->heat.raytree= tree;
+}
+
+static int heat_ray_bone_visible(LaplacianSystem *sys, int vertex, int bone)
+{
+ Isect isec;
+ MFace *mface;
+ float dir[3];
+ int visible;
+
+ mface= sys->heat.vface[vertex];
+ if(!mface)
+ return 1;
+
+ /* setup isec */
+ memset(&isec, 0, sizeof(isec));
+ isec.mode= RE_RAY_SHADOW;
+ isec.lay= -1;
+ isec.face_last= NULL;
+ isec.faceorig= mface;
+
+ VECCOPY(isec.start, sys->heat.verts[vertex]);
+ PclosestVL3Dfl(isec.end, isec.start,
+ sys->heat.root[bone], sys->heat.tip[bone]);
+
+ /* add an extra offset to the start position to avoid self intersection */
+ VECSUB(dir, isec.end, isec.start);
+ Normalize(dir);
+ VecMulf(dir, 1e-5);
+ VecAddf(isec.start, isec.start, dir);
+
+ HeatSys= sys;
+ visible= !RE_ray_tree_intersect(sys->heat.raytree, &isec);
+ HeatSys= NULL;
+
+ return visible;
+}
+
+static float heat_bone_distance(LaplacianSystem *sys, int vertex, int bone)
+{
+ float closest[3], d[3], dist, cosine;
+
+ /* compute euclidian distance */
+ PclosestVL3Dfl(closest, sys->heat.verts[vertex],
+ sys->heat.root[bone], sys->heat.tip[bone]);
+
+ VecSubf(d, sys->heat.verts[vertex], closest);
+ dist= Normalize(d);
+
+ /* if the vertex normal does not point along the bone, increase distance */
+ cosine= INPR(d, sys->heat.vnors[vertex]);
+
+ return dist/(0.5f*(cosine + 1.001f));
+}
+
+static int heat_bone_closest(LaplacianSystem *sys, int vertex, int bone)
+{
+ float dist;
+
+ dist= heat_bone_distance(sys, vertex, bone);
+
+ if(dist <= sys->heat.mindist[vertex]*(1.0f + DISTANCE_EPSILON))
+ if(heat_ray_bone_visible(sys, vertex, bone))
+ return 1;
+
+ return 0;
+}
+
+static void heat_set_H(LaplacianSystem *sys, int vertex)
+{
+ float dist, mindist, h;
+ int j, numclosest = 0;
+
+ mindist= 1e10;
+
+ /* compute minimum distance */
+ for(j=0; j<sys->heat.numbones; j++) {
+ dist= heat_bone_distance(sys, vertex, j);
+
+ if(dist < mindist)
+ mindist= dist;
+ }
+
+ sys->heat.mindist[vertex]= mindist;
+
+ /* count number of bones with approximately this minimum distance */
+ for(j=0; j<sys->heat.numbones; j++)
+ if(heat_bone_closest(sys, vertex, j))
+ numclosest++;
+
+ sys->heat.p[vertex]= (numclosest > 0)? 1.0f/numclosest: 0.0f;
+
+ /* compute H entry */
+ if(numclosest > 0) {
+ if(mindist > 1e-5)
+ h= numclosest*C_WEIGHT/(mindist*mindist);
+ else
+ h= 1e10f;
+ }
+ else
+ h= 0.0f;
+
+ sys->heat.H[vertex]= h;
+}
+
+void heat_calc_vnormals(LaplacianSystem *sys)
+{
+ float fnor[3];
+ int a, v1, v2, v3, (*face)[3];
+
+ sys->heat.vnors= MEM_callocN(sizeof(float)*3*sys->totvert, "HeatVNors");
+
+ for(a=0, face=sys->faces; a<sys->totface; a++, face++) {
+ v1= (*face)[0];
+ v2= (*face)[1];
+ v3= (*face)[2];
+
+ CalcNormFloat(sys->verts[v1], sys->verts[v2], sys->verts[v3], fnor);
+
+ VecAddf(sys->heat.vnors[v1], sys->heat.vnors[v1], fnor);
+ VecAddf(sys->heat.vnors[v2], sys->heat.vnors[v2], fnor);
+ VecAddf(sys->heat.vnors[v3], sys->heat.vnors[v3], fnor);
+ }
+
+ for(a=0; a<sys->totvert; a++)
+ Normalize(sys->heat.vnors[a]);
+}
+
+static void heat_laplacian_create(LaplacianSystem *sys)
+{
+ Mesh *me = sys->heat.mesh;
+ MFace *mface;
+ int a;
+
+ /* heat specific definitions */
+ sys->heat.mindist= MEM_callocN(sizeof(float)*me->totvert, "HeatMinDist");
+ sys->heat.H= MEM_callocN(sizeof(float)*me->totvert, "HeatH");
+ sys->heat.p= MEM_callocN(sizeof(float)*me->totvert, "HeatP");
+
+ /* add verts and faces to laplacian */
+ for(a=0; a<me->totvert; a++)
+ laplacian_add_vertex(sys, sys->heat.verts[a], 0);
+
+ for(a=0, mface=me->mface; a<me->totface; a++, mface++) {
+ laplacian_add_triangle(sys, mface->v1, mface->v2, mface->v3);
+ if(mface->v4)
+ laplacian_add_triangle(sys, mface->v1, mface->v3, mface->v4);
+ }
+
+ /* for distance computation in set_H */
+ heat_calc_vnormals(sys);
+
+ for(a=0; a<me->totvert; a++)
+ heat_set_H(sys, a);
+}
+
+static float heat_limit_weight(float weight)
+{
+ float t;
+
+ if(weight < WEIGHT_LIMIT_END) {
+ return 0.0f;
+ }
+ else if(weight < WEIGHT_LIMIT_START) {
+ t= (weight - WEIGHT_LIMIT_END)/(WEIGHT_LIMIT_START - WEIGHT_LIMIT_END);
+ return t*WEIGHT_LIMIT_START;
+ }
+ else
+ return weight;
+}
+
+void heat_bone_weighting(Object *ob, Mesh *me, float (*verts)[3], int numbones, bDeformGroup **dgrouplist, bDeformGroup **dgroupflip, float (*root)[3], float (*tip)[3], int *selected)
+{
+ LaplacianSystem *sys;
+ MFace *mface;
+ float solution, weight;
+ int *vertsflipped = NULL;
+ int a, totface, j, bbone, firstsegment, lastsegment, thrownerror = 0;
+
+ /* count triangles */
+ for(totface=0, a=0, mface=me->mface; a<me->totface; a++, mface++) {
+ totface++;
+ if(mface->v4) totface++;
+ }
+
+ /* create laplacian */
+ sys = laplacian_system_construct_begin(me->totvert, totface, 1);
+
+ sys->heat.mesh= me;
+ sys->heat.verts= verts;
+ sys->heat.root= root;
+ sys->heat.tip= tip;
+ sys->heat.numbones= numbones;
+
+ heat_ray_tree_create(sys);
+ heat_laplacian_create(sys);
+
+ laplacian_system_construct_end(sys);
+
+ if(dgroupflip) {
+ vertsflipped = MEM_callocN(sizeof(int)*me->totvert, "vertsflipped");
+ for(a=0; a<me->totvert; a++)
+ vertsflipped[a] = mesh_get_x_mirror_vert(ob, a);
+ }
+
+ /* compute weights per bone */
+ for(j=0; j<numbones; j++) {
+ if(!selected[j])
+ continue;
+
+ firstsegment= (j == 0 || dgrouplist[j-1] != dgrouplist[j]);
+ lastsegment= (j == numbones-1 || dgrouplist[j] != dgrouplist[j+1]);
+ bbone= !(firstsegment && lastsegment);
+
+ /* clear weights */
+ if(bbone && firstsegment) {
+ for(a=0; a<me->totvert; a++) {
+ remove_vert_defgroup(ob, dgrouplist[j], a);
+ if(vertsflipped && dgroupflip[j] && vertsflipped[a] >= 0)
+ remove_vert_defgroup(ob, dgroupflip[j], vertsflipped[a]);
+ }
+ }
+
+ /* fill right hand side */
+ laplacian_begin_solve(sys, -1);
+
+ for(a=0; a<me->totvert; a++)
+ if(heat_bone_closest(sys, a, j))
+ laplacian_add_right_hand_side(sys, a,
+ sys->heat.H[a]*sys->heat.p[a]);
+
+ /* solve */
+ if(laplacian_system_solve(sys)) {
+ /* load solution into vertex groups */
+ for(a=0; a<me->totvert; a++) {
+ solution= laplacian_system_get_solution(a);
+
+ if(bbone) {
+ if(solution > 0.0f)
+ add_vert_to_defgroup(ob, dgrouplist[j], a, solution,
+ WEIGHT_ADD);
+ }
+ else {
+ weight= heat_limit_weight(solution);
+ if(weight > 0.0f)
+ add_vert_to_defgroup(ob, dgrouplist[j], a, weight,
+ WEIGHT_REPLACE);
+ else
+ remove_vert_defgroup(ob, dgrouplist[j], a);
+ }
+
+ /* do same for mirror */
+ if(vertsflipped && dgroupflip[j] && vertsflipped[a] >= 0) {
+ if(bbone) {
+ if(solution > 0.0f)
+ add_vert_to_defgroup(ob, dgroupflip[j], vertsflipped[a],
+ solution, WEIGHT_ADD);
+ }
+ else {
+ weight= heat_limit_weight(solution);
+ if(weight > 0.0f)
+ add_vert_to_defgroup(ob, dgroupflip[j], vertsflipped[a],
+ weight, WEIGHT_REPLACE);
+ else
+ remove_vert_defgroup(ob, dgroupflip[j], vertsflipped[a]);
+ }
+ }
+ }
+ }
+ else if(!thrownerror) {
+ error("Bone Heat Weighting:"
+ " failed to find solution for one or more bones");
+ thrownerror= 1;
+ break;
+ }
+
+ /* remove too small vertex weights */
+ if(bbone && lastsegment) {
+ for(a=0; a<me->totvert; a++) {
+ weight= get_vert_defgroup(ob, dgrouplist[j], a);
+ weight= heat_limit_weight(weight);
+ if(weight <= 0.0f)
+ remove_vert_defgroup(ob, dgrouplist[j], a);
+
+ if(vertsflipped && dgroupflip[j] && vertsflipped[a] >= 0) {
+ weight= get_vert_defgroup(ob, dgroupflip[j], vertsflipped[a]);
+ weight= heat_limit_weight(weight);
+ if(weight <= 0.0f)
+ remove_vert_defgroup(ob, dgroupflip[j], vertsflipped[a]);
+ }
+ }
+ }
+ }
+
+ /* free */
+ if(vertsflipped) MEM_freeN(vertsflipped);
+
+ RE_ray_tree_free(sys->heat.raytree);
+ MEM_freeN(sys->heat.vface);
+
+ MEM_freeN(sys->heat.mindist);
+ MEM_freeN(sys->heat.H);
+ MEM_freeN(sys->heat.p);
+ MEM_freeN(sys->heat.vnors);
+
+ laplacian_system_delete(sys);
+}
+
+#ifdef RIGID_DEFORM
+/********************** As-Rigid-As-Possible Deformation ******************/
+/* From "As-Rigid-As-Possible Surface Modeling",
+ Olga Sorkine and Marc Alexa, ESGP 2007. */
+
+/* investigate:
+ - transpose R in orthogonal
+ - flipped normals and per face adding
+ - move cancelling to transform, make origco pointer
+*/
+
+static LaplacianSystem *RigidDeformSystem = NULL;
+
+static void rigid_add_half_edge_to_R(LaplacianSystem *sys, EditVert *v1, EditVert *v2, float w)
+{
+ float e[3], e_[3];
+ int i;
+
+ VecSubf(e, sys->rigid.origco[v1->tmp.l], sys->rigid.origco[v2->tmp.l]);
+ VecSubf(e_, v1->co, v2->co);
+
+ /* formula (5) */
+ for (i=0; i<3; i++) {
+ sys->rigid.R[v1->tmp.l][i][0] += w*e[0]*e_[i];
+ sys->rigid.R[v1->tmp.l][i][1] += w*e[1]*e_[i];
+ sys->rigid.R[v1->tmp.l][i][2] += w*e[2]*e_[i];
+ }
+}
+
+static void rigid_add_edge_to_R(LaplacianSystem *sys, EditVert *v1, EditVert *v2, float w)
+{
+ rigid_add_half_edge_to_R(sys, v1, v2, w);
+ rigid_add_half_edge_to_R(sys, v2, v1, w);
+}
+
+static void rigid_orthogonalize_R(float R[][3])
+{
+ HMatrix M, Q, S;
+
+ Mat4CpyMat3(M, R);
+ polar_decomp(M, Q, S);
+ Mat3CpyMat4(R, Q);
+}
+
+static void rigid_add_half_edge_to_rhs(LaplacianSystem *sys, EditVert *v1, EditVert *v2, float w)
+{
+ /* formula (8) */
+ float Rsum[3][3], rhs[3];
+
+ if (sys->vpinned[v1->tmp.l])
+ return;
+
+ Mat3AddMat3(Rsum, sys->rigid.R[v1->tmp.l], sys->rigid.R[v2->tmp.l]);
+ Mat3Transp(Rsum);
+
+ VecSubf(rhs, sys->rigid.origco[v1->tmp.l], sys->rigid.origco[v2->tmp.l]);
+ Mat3MulVecfl(Rsum, rhs);
+ VecMulf(rhs, 0.5f);
+ VecMulf(rhs, w);
+
+ VecAddf(sys->rigid.rhs[v1->tmp.l], sys->rigid.rhs[v1->tmp.l], rhs);
+}
+
+static void rigid_add_edge_to_rhs(LaplacianSystem *sys, EditVert *v1, EditVert *v2, float w)
+{
+ rigid_add_half_edge_to_rhs(sys, v1, v2, w);
+ rigid_add_half_edge_to_rhs(sys, v2, v1, w);
+}
+
+void rigid_deform_iteration()
+{
+ LaplacianSystem *sys= RigidDeformSystem;
+ EditMesh *em;
+ EditVert *eve;
+ EditFace *efa;
+ int a, i;
+
+ if(!sys)
+ return;
+
+ nlMakeCurrent(sys->context);
+ em= sys->rigid.mesh;
+
+ /* compute R */
+ memset(sys->rigid.R, 0, sizeof(float)*3*3*sys->totvert);
+ memset(sys->rigid.rhs, 0, sizeof(float)*3*sys->totvert);
+
+ for(a=0, efa=em->faces.first; efa; efa=efa->next, a++) {
+ rigid_add_edge_to_R(sys, efa->v1, efa->v2, sys->fweights[a][2]);
+ rigid_add_edge_to_R(sys, efa->v2, efa->v3, sys->fweights[a][0]);
+ rigid_add_edge_to_R(sys, efa->v3, efa->v1, sys->fweights[a][1]);
+
+ if(efa->v4) {
+ a++;
+ rigid_add_edge_to_R(sys, efa->v1, efa->v3, sys->fweights[a][2]);
+ rigid_add_edge_to_R(sys, efa->v3, efa->v4, sys->fweights[a][0]);
+ rigid_add_edge_to_R(sys, efa->v4, efa->v1, sys->fweights[a][1]);
+ }
+ }
+
+ for(a=0, eve=em->verts.first; eve; eve=eve->next, a++) {
+ rigid_orthogonalize_R(sys->rigid.R[a]);
+ eve->tmp.l= a;
+ }
+
+ /* compute right hand sides for solving */
+ for(a=0, efa=em->faces.first; efa; efa=efa->next, a++) {
+ rigid_add_edge_to_rhs(sys, efa->v1, efa->v2, sys->fweights[a][2]);
+ rigid_add_edge_to_rhs(sys, efa->v2, efa->v3, sys->fweights[a][0]);
+ rigid_add_edge_to_rhs(sys, efa->v3, efa->v1, sys->fweights[a][1]);
+
+ if(efa->v4) {
+ a++;
+ rigid_add_edge_to_rhs(sys, efa->v1, efa->v3, sys->fweights[a][2]);
+ rigid_add_edge_to_rhs(sys, efa->v3, efa->v4, sys->fweights[a][0]);
+ rigid_add_edge_to_rhs(sys, efa->v4, efa->v1, sys->fweights[a][1]);
+ }
+ }
+
+ /* solve for positions, for X,Y and Z separately */
+ for(i=0; i<3; i++) {
+ laplacian_begin_solve(sys, i);
+
+ for(a=0; a<sys->totvert; a++)
+ if(!sys->vpinned[a])
+ laplacian_add_right_hand_side(sys, a, sys->rigid.rhs[a][i]);
+
+ if(laplacian_system_solve(sys)) {
+ for(a=0, eve=em->verts.first; eve; eve=eve->next, a++)
+ eve->co[i]= laplacian_system_get_solution(a);
+ }
+ else {
+ if(!sys->rigid.thrownerror) {
+ error("RigidDeform: failed to find solution.");
+ sys->rigid.thrownerror= 1;
+ }
+ break;
+ }
+ }
+}
+
+static void rigid_laplacian_create(LaplacianSystem *sys)
+{
+ EditMesh *em = sys->rigid.mesh;
+ EditVert *eve;
+ EditFace *efa;
+ int a;
+
+ /* add verts and faces to laplacian */
+ for(a=0, eve=em->verts.first; eve; eve=eve->next, a++) {
+ laplacian_add_vertex(sys, eve->co, eve->pinned);
+ eve->tmp.l= a;
+ }
+
+ for(efa=em->faces.first; efa; efa=efa->next) {
+ laplacian_add_triangle(sys,
+ efa->v1->tmp.l, efa->v2->tmp.l, efa->v3->tmp.l);
+ if(efa->v4)
+ laplacian_add_triangle(sys,
+ efa->v1->tmp.l, efa->v3->tmp.l, efa->v4->tmp.l);
+ }
+}
+
+void rigid_deform_begin(EditMesh *em)
+{
+ LaplacianSystem *sys;
+ EditVert *eve;
+ EditFace *efa;
+ int a, totvert, totface;
+
+ /* count vertices, triangles */
+ for(totvert=0, eve=em->verts.first; eve; eve=eve->next)
+ totvert++;
+
+ for(totface=0, efa=em->faces.first; efa; efa=efa->next) {
+ totface++;
+ if(efa->v4) totface++;
+ }
+
+ /* create laplacian */
+ sys = laplacian_system_construct_begin(totvert, totface, 0);
+
+ sys->rigid.mesh= em;
+ sys->rigid.R = MEM_callocN(sizeof(float)*3*3*totvert, "RigidDeformR");
+ sys->rigid.rhs = MEM_callocN(sizeof(float)*3*totvert, "RigidDeformRHS");
+ sys->rigid.origco = MEM_callocN(sizeof(float)*3*totvert, "RigidDeformCo");
+
+ for(a=0, eve=em->verts.first; eve; eve=eve->next, a++)
+ VecCopyf(sys->rigid.origco[a], eve->co);
+
+ sys->areaweights= 0;
+ sys->storeweights= 1;
+
+ rigid_laplacian_create(sys);
+
+ laplacian_system_construct_end(sys);
+
+ RigidDeformSystem = sys;
+}
+
+void rigid_deform_end(int cancel)
+{
+ LaplacianSystem *sys = RigidDeformSystem;
+
+ if(sys) {
+ EditMesh *em = sys->rigid.mesh;
+ EditVert *eve;
+ int a;
+
+ if(cancel)
+ for(a=0, eve=em->verts.first; eve; eve=eve->next, a++)
+ if(!eve->pinned)
+ VecCopyf(eve->co, sys->rigid.origco[a]);
+
+ if(sys->rigid.R) MEM_freeN(sys->rigid.R);
+ if(sys->rigid.rhs) MEM_freeN(sys->rigid.rhs);
+ if(sys->rigid.origco) MEM_freeN(sys->rigid.origco);
+
+ /* free */
+ laplacian_system_delete(sys);
+ }
+
+ RigidDeformSystem = NULL;
+}
+#endif
+
+/************************** Harmonic Coordinates ****************************/
+/* From "Harmonic Coordinates for Character Articulation",
+ Pushkar Joshi, Mark Meyer, Tony DeRose, Brian Green and Tom Sanocki,
+ SIGGRAPH 2007. */
+
+#define EPSILON 0.0001f
+
+#define MESHDEFORM_TAG_UNTYPED 0
+#define MESHDEFORM_TAG_BOUNDARY 1
+#define MESHDEFORM_TAG_INTERIOR 2
+#define MESHDEFORM_TAG_EXTERIOR 3
+
+#define MESHDEFORM_LEN_THRESHOLD 1e-6
+
+#define MESHDEFORM_MIN_INFLUENCE 0.0005
+
+static int MESHDEFORM_OFFSET[7][3] =
+ {{0,0,0}, {1,0,0}, {-1,0,0}, {0,1,0}, {0,-1,0}, {0,0,1}, {0,0,-1}};
+
+typedef struct MDefBoundIsect {
+ float co[3], uvw[4];
+ int nvert, v[4], facing;
+ float len;
+} MDefBoundIsect;
+
+typedef struct MDefBindInfluence {
+ struct MDefBindInfluence *next;
+ float weight;
+ int vertex;
+} MDefBindInfluence;
+
+typedef struct MeshDeformBind {
+ /* grid dimensions */
+ float min[3], max[3];
+ float width[3], halfwidth[3];
+ int size, size3;
+
+ /* meshes */
+ DerivedMesh *cagedm;
+ float (*cagecos)[3];
+ float (*vertexcos)[3];
+ int totvert, totcagevert;
+
+ /* grids */
+ MemArena *memarena;
+ MDefBoundIsect *(*boundisect)[6];
+ int *semibound;
+ int *tag;
+ float *phi, *totalphi;
+
+ /* mesh stuff */
+ int *inside;
+ float *weights;
+ MDefBindInfluence **dyngrid;
+ float cagemat[4][4];
+
+ /* direct solver */
+ int *varidx;
+
+ /* raytrace */
+ RayTree *raytree;
+} MeshDeformBind;
+
+/* ray intersection */
+
+/* our own triangle intersection, so we can fully control the epsilons and
+ * prevent corner case from going wrong*/
+static int meshdeform_tri_intersect(float orig[3], float end[3], float vert0[3],
+ float vert1[3], float vert2[3], float *isectco, float *uvw)
+{
+ float edge1[3], edge2[3], tvec[3], pvec[3], qvec[3];
+ float det,inv_det, u, v, dir[3], isectdir[3];
+
+ VECSUB(dir, end, orig);
+
+ /* find vectors for two edges sharing vert0 */
+ VECSUB(edge1, vert1, vert0);
+ VECSUB(edge2, vert2, vert0);
+
+ /* begin calculating determinant - also used to calculate U parameter */
+ Crossf(pvec, dir, edge2);
+
+ /* if determinant is near zero, ray lies in plane of triangle */
+ det = INPR(edge1, pvec);
+
+ if (det == 0.0f)
+ return 0;
+ inv_det = 1.0f / det;
+
+ /* calculate distance from vert0 to ray origin */
+ VECSUB(tvec, orig, vert0);
+
+ /* calculate U parameter and test bounds */
+ u = INPR(tvec, pvec) * inv_det;
+ if (u < -EPSILON || u > 1.0f+EPSILON)
+ return 0;
+
+ /* prepare to test V parameter */
+ Crossf(qvec, tvec, edge1);
+
+ /* calculate V parameter and test bounds */
+ v = INPR(dir, qvec) * inv_det;
+ if (v < -EPSILON || u + v > 1.0f+EPSILON)
+ return 0;
+
+ isectco[0]= (1.0f - u - v)*vert0[0] + u*vert1[0] + v*vert2[0];
+ isectco[1]= (1.0f - u - v)*vert0[1] + u*vert1[1] + v*vert2[1];
+ isectco[2]= (1.0f - u - v)*vert0[2] + u*vert1[2] + v*vert2[2];
+
+ uvw[0]= 1.0 - u - v;
+ uvw[1]= u;
+ uvw[2]= v;
+
+ /* check if it is within the length of the line segment */
+ VECSUB(isectdir, isectco, orig);
+
+ if(INPR(dir, isectdir) < -EPSILON)
+ return 0;
+
+ if(INPR(dir, dir) + EPSILON < INPR(isectdir, isectdir))
+ return 0;
+
+ return 1;
+}
+
+/* blender's raytracer is not use now, even though it is much faster. it can
+ * give problems with rays falling through, so we use our own intersection
+ * function above with tweaked epsilons */
+
+#if 0
+static MeshDeformBind *MESHDEFORM_BIND = NULL;
+
+static void meshdeform_ray_coords_func(RayFace *face, float **v1, float **v2, float **v3, float **v4)
+{
+ MFace *mface= (MFace*)face;
+ float (*cagecos)[3]= MESHDEFORM_BIND->cagecos;
+
+ *v1= cagecos[mface->v1];
+ *v2= cagecos[mface->v2];
+ *v3= cagecos[mface->v3];
+ *v4= (mface->v4)? cagecos[mface->v4]: NULL;
+}
+
+static int meshdeform_ray_check_func(Isect *is, RayFace *face)
+{
+ return 1;
+}
+
+static void meshdeform_ray_tree_create(MeshDeformBind *mdb)
+{
+ MFace *mface;
+ float min[3], max[3];
+ int a, totface;
+
+ /* create a raytrace tree from the mesh */
+ INIT_MINMAX(min, max);
+
+ for(a=0; a<mdb->totcagevert; a++)
+ DO_MINMAX(mdb->cagecos[a], min, max)
+
+ MESHDEFORM_BIND= mdb;
+
+ mface= mdb->cagedm->getFaceArray(mdb->cagedm);
+ totface= mdb->cagedm->getNumFaces(mdb->cagedm);
+
+ mdb->raytree= RE_ray_tree_create(64, totface, min, max,
+ meshdeform_ray_coords_func, meshdeform_ray_check_func);
+
+ for(a=0; a<totface; a++, mface++)
+ RE_ray_tree_add_face(mdb->raytree, mface);
+
+ RE_ray_tree_done(mdb->raytree);
+}
+
+static void meshdeform_ray_tree_free(MeshDeformBind *mdb)
+{
+ MESHDEFORM_BIND= NULL;
+ RE_ray_tree_free(mdb->raytree);
+}
+#endif
+
+static int meshdeform_intersect(MeshDeformBind *mdb, Isect *isec)
+{
+ MFace *mface;
+ float face[4][3], co[3], uvw[3], len, nor[3];
+ int f, hit, is= 0, totface;
+
+ isec->labda= 1e10;
+
+ mface= mdb->cagedm->getFaceArray(mdb->cagedm);
+ totface= mdb->cagedm->getNumFaces(mdb->cagedm);
+
+ for(f=0; f<totface; f++, mface++) {
+ VECCOPY(face[0], mdb->cagecos[mface->v1]);
+ VECCOPY(face[1], mdb->cagecos[mface->v2]);
+ VECCOPY(face[2], mdb->cagecos[mface->v3]);
+
+ if(mface->v4) {
+ VECCOPY(face[3], mdb->cagecos[mface->v4]);
+ hit= meshdeform_tri_intersect(isec->start, isec->end, face[0], face[1], face[2], co, uvw);
+
+ if(hit) {
+ CalcNormFloat(face[0], face[1], face[2], nor);
+ }
+ else {
+ hit= meshdeform_tri_intersect(isec->start, isec->end, face[0], face[2], face[3], co, uvw);
+ CalcNormFloat(face[0], face[2], face[3], nor);
+ }
+ }
+ else {
+ hit= meshdeform_tri_intersect(isec->start, isec->end, face[0], face[1], face[2], co, uvw);
+ CalcNormFloat(face[0], face[1], face[2], nor);
+ }
+
+ if(hit) {
+ len= VecLenf(isec->start, co)/VecLenf(isec->start, isec->end);
+ if(len < isec->labda) {
+ isec->labda= len;
+ isec->face= mface;
+ isec->isect= (INPR(isec->vec, nor) <= 0.0f);
+ is= 1;
+ }
+ }
+ }
+
+ return is;
+}
+
+static MDefBoundIsect *meshdeform_ray_tree_intersect(MeshDeformBind *mdb, float *co1, float *co2)
+{
+ MDefBoundIsect *isect;
+ Isect isec;
+ float (*cagecos)[3];
+ MFace *mface;
+ float vert[4][3], len;
+ static float epsilon[3]= {0, 0, 0}; //1e-4, 1e-4, 1e-4};
+
+ /* setup isec */
+ memset(&isec, 0, sizeof(isec));
+ isec.mode= RE_RAY_MIRROR; /* we want the closest intersection */
+ isec.lay= -1;
+ isec.face_last= NULL;
+ isec.faceorig= NULL;
+ isec.labda= 1e10f;
+
+ VECADD(isec.start, co1, epsilon);
+ VECADD(isec.end, co2, epsilon);
+ VECSUB(isec.vec, isec.end, isec.start);
+
+#if 0
+ /*if(RE_ray_tree_intersect(mdb->raytree, &isec)) {*/
+#endif
+
+ if(meshdeform_intersect(mdb, &isec)) {
+ len= isec.labda;
+ mface= isec.face;
+
+ /* create MDefBoundIsect */
+ isect= BLI_memarena_alloc(mdb->memarena, sizeof(*isect));
+
+ /* compute intersection coordinate */
+ isect->co[0]= co1[0] + isec.vec[0]*len;
+ isect->co[1]= co1[1] + isec.vec[1]*len;
+ isect->co[2]= co1[2] + isec.vec[2]*len;
+
+ isect->len= VecLenf(co1, isect->co);
+ if(isect->len < MESHDEFORM_LEN_THRESHOLD)
+ isect->len= MESHDEFORM_LEN_THRESHOLD;
+
+ isect->v[0]= mface->v1;
+ isect->v[1]= mface->v2;
+ isect->v[2]= mface->v3;
+ isect->v[3]= mface->v4;
+ isect->nvert= (mface->v4)? 4: 3;
+
+ isect->facing= isec.isect;
+
+ /* compute mean value coordinates for interpolation */
+ cagecos= mdb->cagecos;
+ VECCOPY(vert[0], cagecos[mface->v1]);
+ VECCOPY(vert[1], cagecos[mface->v2]);
+ VECCOPY(vert[2], cagecos[mface->v3]);
+ if(mface->v4) VECCOPY(vert[3], cagecos[mface->v4]);
+ MeanValueWeights(vert, isect->nvert, isect->co, isect->uvw);
+
+ return isect;
+ }
+
+ return NULL;
+}
+
+static int meshdeform_inside_cage(MeshDeformBind *mdb, float *co)
+{
+ MDefBoundIsect *isect;
+ float outside[3], start[3], dir[3];
+ int i, counter;
+
+ for(i=1; i<=6; i++) {
+ counter = 0;
+
+ outside[0] = co[0] + (mdb->max[0] - mdb->min[0] + 1.0f)*MESHDEFORM_OFFSET[i][0];
+ outside[1] = co[1] + (mdb->max[1] - mdb->min[1] + 1.0f)*MESHDEFORM_OFFSET[i][1];
+ outside[2] = co[2] + (mdb->max[2] - mdb->min[2] + 1.0f)*MESHDEFORM_OFFSET[i][2];
+
+ VECCOPY(start, co);
+ VECSUB(dir, outside, start);
+ Normalize(dir);
+
+ isect = meshdeform_ray_tree_intersect(mdb, start, outside);
+ if(isect && !isect->facing)
+ return 1;
+ }
+
+ return 0;
+}
+
+/* solving */
+
+static int meshdeform_index(MeshDeformBind *mdb, int x, int y, int z, int n)
+{
+ int size= mdb->size;
+
+ x += MESHDEFORM_OFFSET[n][0];
+ y += MESHDEFORM_OFFSET[n][1];
+ z += MESHDEFORM_OFFSET[n][2];
+
+ if(x < 0 || x >= mdb->size)
+ return -1;
+ if(y < 0 || y >= mdb->size)
+ return -1;
+ if(z < 0 || z >= mdb->size)
+ return -1;
+
+ return x + y*size + z*size*size;
+}
+
+static void meshdeform_cell_center(MeshDeformBind *mdb, int x, int y, int z, int n, float *center)
+{
+ x += MESHDEFORM_OFFSET[n][0];
+ y += MESHDEFORM_OFFSET[n][1];
+ z += MESHDEFORM_OFFSET[n][2];
+
+ center[0]= mdb->min[0] + x*mdb->width[0] + mdb->halfwidth[0];
+ center[1]= mdb->min[1] + y*mdb->width[1] + mdb->halfwidth[1];
+ center[2]= mdb->min[2] + z*mdb->width[2] + mdb->halfwidth[2];
+}
+
+static void meshdeform_add_intersections(MeshDeformBind *mdb, int x, int y, int z)
+{
+ MDefBoundIsect *isect;
+ float center[3], ncenter[3];
+ int i, a;
+
+ a= meshdeform_index(mdb, x, y, z, 0);
+ meshdeform_cell_center(mdb, x, y, z, 0, center);
+
+ /* check each outgoing edge for intersection */
+ for(i=1; i<=6; i++) {
+ if(meshdeform_index(mdb, x, y, z, i) == -1)
+ continue;
+
+ meshdeform_cell_center(mdb, x, y, z, i, ncenter);
+
+ isect= meshdeform_ray_tree_intersect(mdb, center, ncenter);
+ if(isect) {
+ mdb->boundisect[a][i-1]= isect;
+ mdb->tag[a]= MESHDEFORM_TAG_BOUNDARY;
+ }
+ }
+}
+
+static void meshdeform_bind_floodfill(MeshDeformBind *mdb)
+{
+ int *stack, *tag= mdb->tag;
+ int a, b, i, xyz[3], stacksize, size= mdb->size;
+
+ stack= MEM_callocN(sizeof(int)*mdb->size3, "MeshDeformBindStack");
+
+ /* we know lower left corner is EXTERIOR because of padding */
+ tag[0]= MESHDEFORM_TAG_EXTERIOR;
+ stack[0]= 0;
+ stacksize= 1;
+
+ /* floodfill exterior tag */
+ while(stacksize > 0) {
+ a= stack[--stacksize];
+
+ xyz[2]= a/(size*size);
+ xyz[1]= (a - xyz[2]*size*size)/size;
+ xyz[0]= a - xyz[1]*size - xyz[2]*size*size;
+
+ for(i=1; i<=6; i++) {
+ b= meshdeform_index(mdb, xyz[0], xyz[1], xyz[2], i);
+
+ if(b != -1) {
+ if(tag[b] == MESHDEFORM_TAG_UNTYPED ||
+ (tag[b] == MESHDEFORM_TAG_BOUNDARY && !mdb->boundisect[a][i-1])) {
+ tag[b]= MESHDEFORM_TAG_EXTERIOR;
+ stack[stacksize++]= b;
+ }
+ }
+ }
+ }
+
+ /* other cells are interior */
+ for(a=0; a<size*size*size; a++)
+ if(tag[a]==MESHDEFORM_TAG_UNTYPED)
+ tag[a]= MESHDEFORM_TAG_INTERIOR;
+
+#if 0
+ {
+ int tb, ti, te, ts;
+ tb= ti= te= ts= 0;
+ for(a=0; a<size*size*size; a++)
+ if(tag[a]==MESHDEFORM_TAG_BOUNDARY)
+ tb++;
+ else if(tag[a]==MESHDEFORM_TAG_INTERIOR)
+ ti++;
+ else if(tag[a]==MESHDEFORM_TAG_EXTERIOR) {
+ te++;
+
+ if(mdb->semibound[a])
+ ts++;
+ }
+
+ printf("interior %d exterior %d boundary %d semi-boundary %d\n", ti, te, tb, ts);
+ }
+#endif
+
+ MEM_freeN(stack);
+}
+
+static float meshdeform_boundary_phi(MeshDeformBind *mdb, MDefBoundIsect *isect, int cagevert)
+{
+ int a;
+
+ for(a=0; a<isect->nvert; a++)
+ if(isect->v[a] == cagevert)
+ return isect->uvw[a];
+
+ return 0.0f;
+}
+
+static float meshdeform_interp_w(MeshDeformBind *mdb, float *gridvec, float *vec, int cagevert)
+{
+ float dvec[3], ivec[3], wx, wy, wz, result=0.0f;
+ float weight, totweight= 0.0f;
+ int i, a, x, y, z;
+
+ for(i=0; i<3; i++) {
+ ivec[i]= (int)gridvec[i];
+ dvec[i]= gridvec[i] - ivec[i];
+ }
+
+ for(i=0; i<8; i++) {
+ if(i & 1) { x= ivec[0]+1; wx= dvec[0]; }
+ else { x= ivec[0]; wx= 1.0f-dvec[0]; }
+
+ if(i & 2) { y= ivec[1]+1; wy= dvec[1]; }
+ else { y= ivec[1]; wy= 1.0f-dvec[1]; }
+
+ if(i & 4) { z= ivec[2]+1; wz= dvec[2]; }
+ else { z= ivec[2]; wz= 1.0f-dvec[2]; }
+
+ CLAMP(x, 0, mdb->size-1);
+ CLAMP(y, 0, mdb->size-1);
+ CLAMP(z, 0, mdb->size-1);
+
+ a= meshdeform_index(mdb, x, y, z, 0);
+ weight= wx*wy*wz;
+ result += weight*mdb->phi[a];
+ totweight += weight;
+ }
+
+ if(totweight > 0.0f)
+ result /= totweight;
+
+ return result;
+}
+
+static void meshdeform_check_semibound(MeshDeformBind *mdb, int x, int y, int z)
+{
+ int i, a;
+
+ a= meshdeform_index(mdb, x, y, z, 0);
+ if(mdb->tag[a] != MESHDEFORM_TAG_EXTERIOR)
+ return;
+
+ for(i=1; i<=6; i++)
+ if(mdb->boundisect[a][i-1])
+ mdb->semibound[a]= 1;
+}
+
+static float meshdeform_boundary_total_weight(MeshDeformBind *mdb, int x, int y, int z)
+{
+ float weight, totweight= 0.0f;
+ int i, a;
+
+ a= meshdeform_index(mdb, x, y, z, 0);
+
+ /* count weight for neighbour cells */
+ for(i=1; i<=6; i++) {
+ if(meshdeform_index(mdb, x, y, z, i) == -1)
+ continue;
+
+ if(mdb->boundisect[a][i-1])
+ weight= 1.0f/mdb->boundisect[a][i-1]->len;
+ else if(!mdb->semibound[a])
+ weight= 1.0f/mdb->width[0];
+ else
+ weight= 0.0f;
+
+ totweight += weight;
+ }
+
+ return totweight;
+}
+
+static void meshdeform_matrix_add_cell(MeshDeformBind *mdb, int x, int y, int z)
+{
+ MDefBoundIsect *isect;
+ float weight, totweight;
+ int i, a, acenter;
+
+ acenter= meshdeform_index(mdb, x, y, z, 0);
+ if(mdb->tag[acenter] == MESHDEFORM_TAG_EXTERIOR)
+ return;
+
+ nlMatrixAdd(mdb->varidx[acenter], mdb->varidx[acenter], 1.0f);
+
+ totweight= meshdeform_boundary_total_weight(mdb, x, y, z);
+ for(i=1; i<=6; i++) {
+ a= meshdeform_index(mdb, x, y, z, i);
+ if(a == -1 || mdb->tag[a] == MESHDEFORM_TAG_EXTERIOR)
+ continue;
+
+ isect= mdb->boundisect[acenter][i-1];
+ if (!isect) {
+ weight= (1.0f/mdb->width[0])/totweight;
+ nlMatrixAdd(mdb->varidx[acenter], mdb->varidx[a], -weight);
+ }
+ }
+}
+
+static void meshdeform_matrix_add_rhs(MeshDeformBind *mdb, int x, int y, int z, int cagevert)
+{
+ MDefBoundIsect *isect;
+ float rhs, weight, totweight;
+ int i, a, acenter;
+
+ acenter= meshdeform_index(mdb, x, y, z, 0);
+ if(mdb->tag[acenter] == MESHDEFORM_TAG_EXTERIOR)
+ return;
+
+ totweight= meshdeform_boundary_total_weight(mdb, x, y, z);
+ for(i=1; i<=6; i++) {
+ a= meshdeform_index(mdb, x, y, z, i);
+ if(a == -1)
+ continue;
+
+ isect= mdb->boundisect[acenter][i-1];
+
+ if (isect) {
+ weight= (1.0f/isect->len)/totweight;
+ rhs= weight*meshdeform_boundary_phi(mdb, isect, cagevert);
+ nlRightHandSideAdd(0, mdb->varidx[acenter], rhs);
+ }
+ }
+}
+
+static void meshdeform_matrix_add_semibound_phi(MeshDeformBind *mdb, int x, int y, int z, int cagevert)
+{
+ MDefBoundIsect *isect;
+ float rhs, weight, totweight;
+ int i, a;
+
+ a= meshdeform_index(mdb, x, y, z, 0);
+ if(!mdb->semibound[a])
+ return;
+
+ mdb->phi[a]= 0.0f;
+
+ totweight= meshdeform_boundary_total_weight(mdb, x, y, z);
+ for(i=1; i<=6; i++) {
+ isect= mdb->boundisect[a][i-1];
+
+ if (isect) {
+ weight= (1.0f/isect->len)/totweight;
+ rhs= weight*meshdeform_boundary_phi(mdb, isect, cagevert);
+ mdb->phi[a] += rhs;
+ }
+ }
+}
+
+static void meshdeform_matrix_add_exterior_phi(MeshDeformBind *mdb, int x, int y, int z, int cagevert)
+{
+ float phi, totweight;
+ int i, a, acenter;
+
+ acenter= meshdeform_index(mdb, x, y, z, 0);
+ if(mdb->tag[acenter] != MESHDEFORM_TAG_EXTERIOR || mdb->semibound[acenter])
+ return;
+
+ phi= 0.0f;
+ totweight= 0.0f;
+ for(i=1; i<=6; i++) {
+ a= meshdeform_index(mdb, x, y, z, i);
+
+ if(a != -1 && mdb->semibound[a]) {
+ phi += mdb->phi[a];
+ totweight += 1.0f;
+ }
+ }
+
+ if(totweight != 0.0f)
+ mdb->phi[acenter]= phi/totweight;
+}
+
+static void meshdeform_matrix_solve(MeshDeformBind *mdb)
+{
+ NLContext *context;
+ float vec[3], gridvec[3];
+ int a, b, x, y, z, totvar;
+ char message[1024];
+
+ /* setup variable indices */
+ mdb->varidx= MEM_callocN(sizeof(int)*mdb->size3, "MeshDeformDSvaridx");
+ for(a=0, totvar=0; a<mdb->size3; a++)
+ mdb->varidx[a]= (mdb->tag[a] == MESHDEFORM_TAG_EXTERIOR)? -1: totvar++;
+
+ if(totvar == 0) {
+ MEM_freeN(mdb->varidx);
+ return;
+ }
+
+ progress_bar(0, "Starting mesh deform solve");
+
+ /* setup opennl solver */
+ nlNewContext();
+ context= nlGetCurrent();
+
+ nlSolverParameteri(NL_NB_VARIABLES, totvar);
+ nlSolverParameteri(NL_NB_ROWS, totvar);
+ nlSolverParameteri(NL_NB_RIGHT_HAND_SIDES, 1);
+
+ nlBegin(NL_SYSTEM);
+ nlBegin(NL_MATRIX);
+
+ /* build matrix */
+ for(z=0; z<mdb->size; z++)
+ for(y=0; y<mdb->size; y++)
+ for(x=0; x<mdb->size; x++)
+ meshdeform_matrix_add_cell(mdb, x, y, z);
+
+ /* solve for each cage vert */
+ for(a=0; a<mdb->totcagevert; a++) {
+ if(a != 0) {
+ nlBegin(NL_SYSTEM);
+ nlBegin(NL_MATRIX);
+ }
+
+ /* fill in right hand side and solve */
+ for(z=0; z<mdb->size; z++)
+ for(y=0; y<mdb->size; y++)
+ for(x=0; x<mdb->size; x++)
+ meshdeform_matrix_add_rhs(mdb, x, y, z, a);
+
+ nlEnd(NL_MATRIX);
+ nlEnd(NL_SYSTEM);
+
+#if 0
+ nlPrintMatrix();
+#endif
+
+ if(nlSolveAdvanced(NULL, NL_TRUE)) {
+ for(z=0; z<mdb->size; z++)
+ for(y=0; y<mdb->size; y++)
+ for(x=0; x<mdb->size; x++)
+ meshdeform_matrix_add_semibound_phi(mdb, x, y, z, a);
+
+ for(z=0; z<mdb->size; z++)
+ for(y=0; y<mdb->size; y++)
+ for(x=0; x<mdb->size; x++)
+ meshdeform_matrix_add_exterior_phi(mdb, x, y, z, a);
+
+ for(b=0; b<mdb->size3; b++) {
+ if(mdb->tag[b] != MESHDEFORM_TAG_EXTERIOR)
+ mdb->phi[b]= nlGetVariable(0, mdb->varidx[b]);
+ mdb->totalphi[b] += mdb->phi[b];
+ }
+
+ if(mdb->weights) {
+ /* static bind : compute weights for each vertex */
+ for(b=0; b<mdb->totvert; b++) {
+ if(mdb->inside[b]) {
+ VECCOPY(vec, mdb->vertexcos[b]);
+ Mat4MulVecfl(mdb->cagemat, vec);
+ gridvec[0]= (vec[0] - mdb->min[0] - mdb->halfwidth[0])/mdb->width[0];
+ gridvec[1]= (vec[1] - mdb->min[1] - mdb->halfwidth[1])/mdb->width[1];
+ gridvec[2]= (vec[2] - mdb->min[2] - mdb->halfwidth[2])/mdb->width[2];
+
+ mdb->weights[b*mdb->totcagevert + a]= meshdeform_interp_w(mdb, gridvec, vec, a);
+ }
+ }
+ }
+ else {
+ MDefBindInfluence *inf;
+
+ /* dynamic bind */
+ for(b=0; b<mdb->size3; b++) {
+ if(mdb->phi[b] >= MESHDEFORM_MIN_INFLUENCE) {
+ inf= BLI_memarena_alloc(mdb->memarena, sizeof(*inf));
+ inf->vertex= a;
+ inf->weight= mdb->phi[b];
+ inf->next= mdb->dyngrid[b];
+ mdb->dyngrid[b]= inf;
+ }
+ }
+ }
+ }
+ else {
+ error("Mesh Deform: failed to find solution.");
+ break;
+ }
+
+ sprintf(message, "Mesh deform solve %d / %d |||", a+1, mdb->totcagevert);
+ progress_bar((float)(a+1)/(float)(mdb->totcagevert), message);
+ }
+
+#if 0
+ /* sanity check */
+ for(b=0; b<mdb->size3; b++)
+ if(mdb->tag[b] != MESHDEFORM_TAG_EXTERIOR)
+ if(fabs(mdb->totalphi[b] - 1.0f) > 1e-4)
+ printf("totalphi deficiency [%s|%d] %d: %.10f\n",
+ (mdb->tag[b] == MESHDEFORM_TAG_INTERIOR)? "interior": "boundary", mdb->semibound[b], mdb->varidx[b], mdb->totalphi[b]);
+#endif
+
+ /* free */
+ MEM_freeN(mdb->varidx);
+
+ nlDeleteContext(context);
+}
+
+void harmonic_coordinates_bind(Scene *scene, MeshDeformModifierData *mmd, float (*vertexcos)[3], int totvert, float cagemat[][4])
+{
+ MeshDeformBind mdb;
+ MDefBindInfluence *inf;
+ MDefInfluence *mdinf;
+ MDefCell *cell;
+ MVert *mvert;
+ float center[3], vec[3], maxwidth, totweight;
+ int a, b, x, y, z, totinside, offset;
+
+ waitcursor(1);
+ start_progress_bar();
+
+ memset(&mdb, 0, sizeof(MeshDeformBind));
+
+ /* get mesh and cage mesh */
+ mdb.vertexcos= vertexcos;
+ mdb.totvert= totvert;
+
+ mdb.cagedm= mesh_create_derived_no_deform(scene, mmd->object, NULL, CD_MASK_BAREMESH);
+ mdb.totcagevert= mdb.cagedm->getNumVerts(mdb.cagedm);
+ mdb.cagecos= MEM_callocN(sizeof(*mdb.cagecos)*mdb.totcagevert, "MeshDeformBindCos");
+ Mat4CpyMat4(mdb.cagemat, cagemat);
+
+ mvert= mdb.cagedm->getVertArray(mdb.cagedm);
+ for(a=0; a<mdb.totcagevert; a++)
+ VECCOPY(mdb.cagecos[a], mvert[a].co)
+
+ /* compute bounding box of the cage mesh */
+ INIT_MINMAX(mdb.min, mdb.max);
+
+ for(a=0; a<mdb.totcagevert; a++)
+ DO_MINMAX(mdb.cagecos[a], mdb.min, mdb.max);
+
+ /* allocate memory */
+ mdb.size= (2<<(mmd->gridsize-1)) + 2;
+ mdb.size3= mdb.size*mdb.size*mdb.size;
+ mdb.tag= MEM_callocN(sizeof(int)*mdb.size3, "MeshDeformBindTag");
+ mdb.phi= MEM_callocN(sizeof(float)*mdb.size3, "MeshDeformBindPhi");
+ mdb.totalphi= MEM_callocN(sizeof(float)*mdb.size3, "MeshDeformBindTotalPhi");
+ mdb.boundisect= MEM_callocN(sizeof(*mdb.boundisect)*mdb.size3, "MDefBoundIsect");
+ mdb.semibound= MEM_callocN(sizeof(int)*mdb.size3, "MDefSemiBound");
+
+ mdb.inside= MEM_callocN(sizeof(int)*mdb.totvert, "MDefInside");
+
+ if(mmd->flag & MOD_MDEF_DYNAMIC_BIND)
+ mdb.dyngrid= MEM_callocN(sizeof(MDefBindInfluence*)*mdb.size3, "MDefDynGrid");
+ else
+ mdb.weights= MEM_callocN(sizeof(float)*mdb.totvert*mdb.totcagevert, "MDefWeights");
+
+ mdb.memarena= BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE);
+ BLI_memarena_use_calloc(mdb.memarena);
+
+ /* make bounding box equal size in all directions, add padding, and compute
+ * width of the cells */
+ maxwidth = -1.0f;
+ for(a=0; a<3; a++)
+ if(mdb.max[a]-mdb.min[a] > maxwidth)
+ maxwidth= mdb.max[a]-mdb.min[a];
+
+ for(a=0; a<3; a++) {
+ center[a]= (mdb.min[a]+mdb.max[a])*0.5f;
+ mdb.min[a]= center[a] - maxwidth*0.5f;
+ mdb.max[a]= center[a] + maxwidth*0.5f;
+
+ mdb.width[a]= (mdb.max[a]-mdb.min[a])/(mdb.size-4);
+ mdb.min[a] -= 2.1f*mdb.width[a];
+ mdb.max[a] += 2.1f*mdb.width[a];
+
+ mdb.width[a]= (mdb.max[a]-mdb.min[a])/mdb.size;
+ mdb.halfwidth[a]= mdb.width[a]*0.5f;
+ }
+
+ progress_bar(0, "Setting up mesh deform system");
+
+#if 0
+ /* create ray tree */
+ meshdeform_ray_tree_create(&mdb);
+#endif
+
+ totinside= 0;
+ for(a=0; a<mdb.totvert; a++) {
+ VECCOPY(vec, mdb.vertexcos[a]);
+ Mat4MulVecfl(mdb.cagemat, vec);
+ mdb.inside[a]= meshdeform_inside_cage(&mdb, vec);
+ if(mdb.inside[a])
+ totinside++;
+ }
+
+ /* free temporary MDefBoundIsects */
+ BLI_memarena_free(mdb.memarena);
+ mdb.memarena= BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE);
+
+ /* start with all cells untyped */
+ for(a=0; a<mdb.size3; a++)
+ mdb.tag[a]= MESHDEFORM_TAG_UNTYPED;
+
+ /* detect intersections and tag boundary cells */
+ for(z=0; z<mdb.size; z++)
+ for(y=0; y<mdb.size; y++)
+ for(x=0; x<mdb.size; x++)
+ meshdeform_add_intersections(&mdb, x, y, z);
+
+#if 0
+ /* free ray tree */
+ meshdeform_ray_tree_free(&mdb);
+#endif
+
+ /* compute exterior and interior tags */
+ meshdeform_bind_floodfill(&mdb);
+
+ for(z=0; z<mdb.size; z++)
+ for(y=0; y<mdb.size; y++)
+ for(x=0; x<mdb.size; x++)
+ meshdeform_check_semibound(&mdb, x, y, z);
+
+ /* solve */
+ meshdeform_matrix_solve(&mdb);
+
+ /* assign results */
+ mmd->bindcos= (float*)mdb.cagecos;
+ mmd->totvert= mdb.totvert;
+ mmd->totcagevert= mdb.totcagevert;
+ Mat4CpyMat4(mmd->bindmat, mmd->object->obmat);
+
+ if(mmd->flag & MOD_MDEF_DYNAMIC_BIND) {
+ mmd->totinfluence= 0;
+ for(a=0; a<mdb.size3; a++)
+ for(inf=mdb.dyngrid[a]; inf; inf=inf->next)
+ mmd->totinfluence++;
+
+ /* convert MDefBindInfluences to smaller MDefInfluences */
+ mmd->dyngrid= MEM_callocN(sizeof(MDefCell)*mdb.size3, "MDefDynGrid");
+ mmd->dyninfluences= MEM_callocN(sizeof(MDefInfluence)*mmd->totinfluence, "MDefInfluence");
+ offset= 0;
+ for(a=0; a<mdb.size3; a++) {
+ cell= &mmd->dyngrid[a];
+ cell->offset= offset;
+
+ totweight= 0.0f;
+ mdinf= mmd->dyninfluences + cell->offset;
+ for(inf=mdb.dyngrid[a]; inf; inf=inf->next, mdinf++) {
+ mdinf->weight= inf->weight;
+ mdinf->vertex= inf->vertex;
+ totweight += mdinf->weight;
+ cell->totinfluence++;
+ }
+
+ if(totweight > 0.0f) {
+ mdinf= mmd->dyninfluences + cell->offset;
+ for(b=0; b<cell->totinfluence; b++, mdinf++)
+ mdinf->weight /= totweight;
+ }
+
+ offset += cell->totinfluence;
+ }
+
+ mmd->dynverts= mdb.inside;
+ mmd->dyngridsize= mdb.size;
+ VECCOPY(mmd->dyncellmin, mdb.min);
+ mmd->dyncellwidth= mdb.width[0];
+ MEM_freeN(mdb.dyngrid);
+ }
+ else {
+ mmd->bindweights= mdb.weights;
+ MEM_freeN(mdb.inside);
+ }
+
+ /* transform bindcos to world space */
+ for(a=0; a<mdb.totcagevert; a++)
+ Mat4MulVecfl(mmd->object->obmat, mmd->bindcos+a*3);
+
+ /* free */
+ mdb.cagedm->release(mdb.cagedm);
+ MEM_freeN(mdb.tag);
+ MEM_freeN(mdb.phi);
+ MEM_freeN(mdb.totalphi);
+ MEM_freeN(mdb.boundisect);
+ MEM_freeN(mdb.semibound);
+ BLI_memarena_free(mdb.memarena);
+
+ end_progress_bar();
+ waitcursor(0);
+}
+
diff --git a/source/blender/editors/armature/meshlaplacian.h b/source/blender/editors/armature/meshlaplacian.h
new file mode 100644
index 00000000000..00c0aefaec7
--- /dev/null
+++ b/source/blender/editors/armature/meshlaplacian.h
@@ -0,0 +1,85 @@
+/**
+ * $Id$
+ *
+ * ***** BEGIN GPL 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.
+ *
+ * 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 LICENSE BLOCK *****
+ * BIF_meshlaplacian.h: Algorithms using the mesh laplacian.
+ */
+
+#ifndef BIF_MESHLAPLACIAN_H
+#define BIF_MESHLAPLACIAN_H
+
+//#define RIGID_DEFORM
+
+struct Scene;
+struct Object;
+struct Mesh;
+struct bDeformGroup;
+struct MeshDeformModifierData;
+
+#ifdef RIGID_DEFORM
+struct EditMesh;
+#endif
+
+/* Laplacian System */
+
+struct LaplacianSystem;
+typedef struct LaplacianSystem LaplacianSystem;
+
+LaplacianSystem *laplacian_construct_begin(int totvert, int totface, int lsq);
+
+void laplacian_add_vertex(LaplacianSystem *sys, float *co, int pinned);
+void laplacian_add_triangle(LaplacianSystem *sys, int v1, int v2, int v3);
+
+void laplacian_construct_end(LaplacianSystem *sys);
+void laplacian_delete(LaplacianSystem *sys);
+
+void laplacian_begin_solve(LaplacianSystem *sys, int index);
+void laplacian_add_right_hand_side(LaplacianSystem *sys, int v, float value);
+int laplacian_system_solve(LaplacianSystem *sys);
+float laplacian_system_get_solution(int v);
+
+/* Heat Weighting */
+
+void heat_bone_weighting(struct Object *ob, struct Mesh *me, float (*verts)[3],
+ int numbones, struct bDeformGroup **dgrouplist,
+ struct bDeformGroup **dgroupflip, float (*root)[3], float (*tip)[3],
+ int *selected);
+
+#ifdef RIGID_DEFORM
+/* As-Rigid-As-Possible Deformation */
+
+void rigid_deform_begin(struct EditMesh *em);
+void rigid_deform_iteration(void);
+void rigid_deform_end(int cancel);
+#endif
+
+/* Harmonic Coordinates */
+
+void harmonic_coordinates_bind(struct Scene *scene, struct MeshDeformModifierData *mmd,
+ float (*vertexcos)[3], int totvert, float cagemat[][4]);
+
+#endif
+
diff --git a/source/blender/editors/armature/poseobject.c b/source/blender/editors/armature/poseobject.c
new file mode 100644
index 00000000000..14e571219ff
--- /dev/null
+++ b/source/blender/editors/armature/poseobject.c
@@ -0,0 +1,1681 @@
+/**
+ * $Id$
+ *
+ * ***** BEGIN GPL 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.
+ *
+ * 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.
+ *
+ * Contributor(s): Ton Roosendaal, Blender Foundation '05, full recode.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ * support for animation modes - Reevan McKay
+ */
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_arithb.h"
+#include "BLI_blenlib.h"
+#include "BLI_dynstr.h"
+
+#include "DNA_action_types.h"
+#include "DNA_armature_types.h"
+#include "DNA_constraint_types.h"
+#include "DNA_curve_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_screen_types.h"
+#include "DNA_view3d_types.h"
+#include "DNA_userdef_types.h"
+
+#include "BKE_action.h"
+#include "BKE_armature.h"
+#include "BKE_blender.h"
+#include "BKE_constraint.h"
+#include "BKE_deform.h"
+#include "BKE_depsgraph.h"
+#include "BKE_displist.h"
+#include "BKE_global.h"
+#include "BKE_modifier.h"
+#include "BKE_object.h"
+#include "BKE_utildefines.h"
+#include "BKE_ipo.h"
+
+#include "BIF_transform.h" /* for autokey TFM_TRANSLATION, etc */
+#include "BIF_gl.h"
+
+#include "ED_armature.h"
+#include "ED_keyframing.h"
+#include "ED_object.h"
+#include "ED_view3d.h"
+
+#include "armature_intern.h"
+
+/* ************* XXX *************** */
+static int movetolayer_short_buts() {return 1;}
+static int okee() {return 0;}
+static int pupmenu() {return 0;}
+static void waitcursor() {};
+static void error() {};
+static void BIF_undo_push() {}
+static void countall() {}
+static void add_constraint() {}
+static void vertexgroup_select_by_name() {}
+static int screen_view3d_layers() {return 0;}
+static void select_actionchannel_by_name() {}
+static int autokeyframe_cfra_can_key() {return 0;}
+static void autokeyframe_pose_cb_func() {}
+/* ************* XXX *************** */
+
+
+void enter_posemode(Scene *scene)
+{
+ Object *obedit= scene->obedit; // XXX context
+ Base *base;
+ Object *ob;
+
+ if(scene->id.lib) return;
+ base= BASACT;
+ if(base==NULL) return;
+
+ ob= base->object;
+
+ if (ob->id.lib){
+ error ("Can't pose libdata");
+ return;
+ }
+
+ switch (ob->type){
+ case OB_ARMATURE:
+
+ ob->flag |= OB_POSEMODE;
+ base->flag= ob->flag;
+
+ break;
+ default:
+ return;
+ }
+
+ if (obedit) {
+ ED_armature_from_edit(scene, obedit);
+ ED_armature_edit_free(obedit);
+ }
+ G.f &= ~(G_VERTEXPAINT | G_TEXTUREPAINT | G_WEIGHTPAINT | G_SCULPTMODE);
+}
+
+void set_pose_keys (Object *ob)
+{
+ bArmature *arm= ob->data;
+ bPoseChannel *chan;
+
+ if (ob->pose){
+ for (chan=ob->pose->chanbase.first; chan; chan=chan->next){
+ Bone *bone= chan->bone;
+ if(bone && (bone->flag & BONE_SELECTED) && (arm->layer & bone->layer)) {
+ chan->flag |= POSE_KEY;
+ }
+ else {
+ chan->flag &= ~POSE_KEY;
+ }
+ }
+ }
+}
+
+
+void exit_posemode(Scene *scene)
+{
+ Object *ob= OBACT;
+ Base *base= BASACT;
+
+ if(ob==NULL) return;
+
+ ob->flag &= ~OB_POSEMODE;
+ base->flag= ob->flag;
+
+ countall();
+
+}
+
+/* called by buttons to find a bone to display/edit values for */
+bPoseChannel *get_active_posechannel (Object *ob)
+{
+ bArmature *arm= ob->data;
+ bPoseChannel *pchan;
+
+ if ELEM(NULL, ob, ob->pose)
+ return NULL;
+
+ /* find active */
+ for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
+ if(pchan->bone && (pchan->bone->flag & BONE_ACTIVE) && (pchan->bone->layer & arm->layer))
+ return pchan;
+ }
+
+ return NULL;
+}
+
+/* if a selected or active bone is protected, throw error (oonly if warn==1) and return 1 */
+/* only_selected==1 : the active bone is allowed to be protected */
+static short pose_has_protected_selected(Object *ob, short only_selected, short warn)
+{
+ /* check protection */
+ if (ob->proxy) {
+ bPoseChannel *pchan;
+ bArmature *arm= ob->data;
+
+ for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
+ if (pchan->bone && (pchan->bone->layer & arm->layer)) {
+ if (pchan->bone->layer & arm->layer_protected) {
+ if (only_selected && (pchan->bone->flag & BONE_ACTIVE));
+ else if (pchan->bone->flag & (BONE_ACTIVE|BONE_SELECTED))
+ break;
+ }
+ }
+ }
+ if (pchan) {
+ if (warn) error("Cannot change Proxy protected bones");
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* only for real IK, not for auto-IK */
+int pose_channel_in_IK_chain(Object *ob, bPoseChannel *pchan)
+{
+ bConstraint *con;
+ Bone *bone;
+
+ /* No need to check if constraint is active (has influence),
+ * since all constraints with CONSTRAINT_IK_AUTO are active */
+ for(con= pchan->constraints.first; con; con= con->next) {
+ if(con->type==CONSTRAINT_TYPE_KINEMATIC) {
+ bKinematicConstraint *data= con->data;
+ if((data->flag & CONSTRAINT_IK_AUTO)==0)
+ return 1;
+ }
+ }
+ for(bone= pchan->bone->childbase.first; bone; bone= bone->next) {
+ pchan= get_pose_channel(ob->pose, bone->name);
+ if(pchan && pose_channel_in_IK_chain(ob, pchan))
+ return 1;
+ }
+ return 0;
+}
+
+/* ********************************************** */
+
+/* For the object with pose/action: create path curves for selected bones
+ * This recalculates the WHOLE path within the pchan->pathsf and pchan->pathef range
+ */
+void pose_calculate_path(Scene *scene, Object *ob)
+{
+ bArmature *arm;
+ bPoseChannel *pchan;
+ Base *base;
+ float *fp;
+ int cfra;
+ int sfra, efra;
+
+ if (ob==NULL || ob->pose==NULL)
+ return;
+ arm= ob->data;
+
+ /* version patch for older files here (do_versions patch too complicated) */
+ if ((arm->pathsf == 0) || (arm->pathef == 0)) {
+ arm->pathsf = SFRA;
+ arm->pathef = EFRA;
+ }
+ if (arm->pathsize == 0) {
+ arm->pathsize = 1;
+ }
+
+ /* set frame values */
+ cfra= CFRA;
+ sfra = arm->pathsf;
+ efra = arm->pathef;
+ if (efra <= sfra) {
+ error("Can't calculate paths when pathlen <= 0");
+ return;
+ }
+
+ waitcursor(1);
+
+ /* hack: for unsaved files, set OB_RECALC so that paths can get calculated */
+ if ((ob->recalc & OB_RECALC)==0) {
+ ob->recalc |= OB_RECALC;
+ DAG_object_update_flags(scene, ob, screen_view3d_layers());
+ }
+ else
+ DAG_object_update_flags(scene, ob, screen_view3d_layers());
+
+
+ /* malloc the path blocks */
+ for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
+ if ((pchan->bone) && (pchan->bone->flag & BONE_SELECTED)) {
+ if (arm->layer & pchan->bone->layer) {
+ pchan->pathlen= efra-sfra+1;
+ pchan->pathsf= sfra;
+ pchan->pathef= efra+1;
+ if (pchan->path)
+ MEM_freeN(pchan->path);
+ pchan->path= MEM_callocN(3*pchan->pathlen*sizeof(float), "pchan path");
+ }
+ }
+ }
+
+ for (CFRA=sfra; CFRA<=efra; CFRA++) {
+ /* do all updates */
+ for (base= FIRSTBASE; base; base= base->next) {
+ if (base->object->recalc) {
+ int temp= base->object->recalc;
+ object_handle_update(scene, base->object);
+ base->object->recalc= temp;
+ }
+ }
+
+ for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
+ if ((pchan->bone) && (pchan->bone->flag & BONE_SELECTED)) {
+ if (arm->layer & pchan->bone->layer) {
+ if (pchan->path) {
+ fp= pchan->path+3*(CFRA-sfra);
+
+ if (arm->pathflag & ARM_PATH_HEADS) {
+ VECCOPY(fp, pchan->pose_head);
+ }
+ else {
+ VECCOPY(fp, pchan->pose_tail);
+ }
+
+ Mat4MulVecfl(ob->obmat, fp);
+ }
+ }
+ }
+ }
+ }
+
+ waitcursor(0);
+
+ CFRA= cfra;
+}
+
+/* For the object with pose/action: update paths for those that have got them
+ * This should selectively update paths that exist...
+ */
+void pose_recalculate_paths(Scene *scene, Object *ob)
+{
+ bArmature *arm;
+ bPoseChannel *pchan;
+ Base *base;
+ float *fp;
+ int cfra;
+ int sfra, efra;
+
+ if (ob==NULL || ob->pose==NULL)
+ return;
+ arm= ob->data;
+
+ /* set frame values */
+ cfra = CFRA;
+ sfra = efra = cfra;
+ for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
+ if ((pchan->bone) && (arm->layer & pchan->bone->layer)) {
+ if (pchan->path) {
+ /* if the pathsf and pathef aren't initialised, abort! */
+ if (ELEM(0, pchan->pathsf, pchan->pathef))
+ return;
+
+ /* try to increase area to do (only as much as needed) */
+ sfra= MIN2(sfra, pchan->pathsf);
+ efra= MAX2(efra, pchan->pathef);
+ }
+ }
+ }
+ if (efra <= sfra) return;
+
+ waitcursor(1);
+
+ /* hack: for unsaved files, set OB_RECALC so that paths can get calculated */
+ if ((ob->recalc & OB_RECALC)==0) {
+ ob->recalc |= OB_RECALC;
+ DAG_object_update_flags(scene, ob, screen_view3d_layers());
+ }
+ else
+ DAG_object_update_flags(scene, ob, screen_view3d_layers());
+
+ for (CFRA=sfra; CFRA<=efra; CFRA++) {
+ /* do all updates */
+ for (base= FIRSTBASE; base; base= base->next) {
+ if (base->object->recalc) {
+ int temp= base->object->recalc;
+ object_handle_update(scene, base->object);
+ base->object->recalc= temp;
+ }
+ }
+
+ for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
+ if ((pchan->bone) && (arm->layer & pchan->bone->layer)) {
+ if (pchan->path) {
+ /* only update if:
+ * - in range of this pchan's existing path
+ * - ... insert evil filtering/optimising conditions here...
+ */
+ if (IN_RANGE(CFRA, pchan->pathsf, pchan->pathef)) {
+ fp= pchan->path+3*(CFRA-sfra);
+
+ if (arm->pathflag & ARM_PATH_HEADS) {
+ VECCOPY(fp, pchan->pose_head);
+ }
+ else {
+ VECCOPY(fp, pchan->pose_tail);
+ }
+
+ Mat4MulVecfl(ob->obmat, fp);
+ }
+ }
+ }
+ }
+ }
+
+ waitcursor(0);
+
+ CFRA= cfra;
+ ob->pose->flag &= ~POSE_RECALCPATHS;
+}
+
+/* for the object with pose/action: clear path curves for selected bones only */
+void pose_clear_paths(Object *ob)
+{
+ bPoseChannel *pchan;
+
+ if (ob==NULL || ob->pose==NULL)
+ return;
+
+ /* free the path blocks */
+ for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
+ if ((pchan->bone) && (pchan->bone->flag & BONE_SELECTED)) {
+ if (pchan->path) {
+ MEM_freeN(pchan->path);
+ pchan->path= NULL;
+ }
+ }
+ }
+
+}
+
+
+
+void pose_select_constraint_target(Scene *scene)
+{
+ Object *obedit= scene->obedit; // XXX context
+ Object *ob= OBACT;
+ bArmature *arm= ob->data;
+ bPoseChannel *pchan;
+ bConstraint *con;
+
+ /* paranoia checks */
+ if (!ob && !ob->pose) return;
+ if (ob==obedit || (ob->flag & OB_POSEMODE)==0) return;
+
+ for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
+ if (arm->layer & pchan->bone->layer) {
+ if (pchan->bone->flag & (BONE_ACTIVE|BONE_SELECTED)) {
+ for (con= pchan->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 == ob) && (ct->subtarget[0])) {
+ bPoseChannel *pchanc= get_pose_channel(ob->pose, ct->subtarget);
+ if(pchanc)
+ pchanc->bone->flag |= BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL;
+ }
+ }
+
+ if (cti->flush_constraint_targets)
+ cti->flush_constraint_targets(con, &targets, 1);
+ }
+ }
+ }
+ }
+ }
+
+ BIF_undo_push("Select constraint target");
+
+}
+
+void pose_select_hierarchy(Scene *scene, short direction, short add_to_sel)
+{
+ Object *obedit= scene->obedit; // XXX context
+ Object *ob= OBACT;
+ bArmature *arm= ob->data;
+ bPoseChannel *pchan;
+ Bone *curbone, *pabone, *chbone;
+
+ /* paranoia checks */
+ if (!ob && !ob->pose) return;
+ if (ob==obedit || (ob->flag & OB_POSEMODE)==0) return;
+
+ for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
+ curbone= pchan->bone;
+
+ if (arm->layer & curbone->layer) {
+ if (curbone->flag & (BONE_ACTIVE)) {
+ if (direction == BONE_SELECT_PARENT) {
+
+ if (pchan->parent == NULL) continue;
+ else pabone= pchan->parent->bone;
+
+ if ((arm->layer & pabone->layer) && !(pabone->flag & BONE_HIDDEN_P)) {
+
+ if (!add_to_sel) curbone->flag &= ~BONE_SELECTED;
+ curbone->flag &= ~BONE_ACTIVE;
+ pabone->flag |= (BONE_ACTIVE|BONE_SELECTED);
+
+ select_actionchannel_by_name (ob->action, pchan->name, 0);
+ select_actionchannel_by_name (ob->action, pchan->parent->name, 1);
+ break;
+ }
+ } else { // BONE_SELECT_CHILD
+
+ if (pchan->child == NULL) continue;
+ else chbone = pchan->child->bone;
+
+ if ((arm->layer & chbone->layer) && !(chbone->flag & BONE_HIDDEN_P)) {
+
+ if (!add_to_sel) curbone->flag &= ~BONE_SELECTED;
+ curbone->flag &= ~BONE_ACTIVE;
+ chbone->flag |= (BONE_ACTIVE|BONE_SELECTED);
+
+ select_actionchannel_by_name (ob->action, pchan->name, 0);
+ select_actionchannel_by_name (ob->action, pchan->child->name, 1);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (direction==BONE_SELECT_PARENT)
+ BIF_undo_push("Select pose bone parent");
+ if (direction==BONE_SELECT_CHILD)
+ BIF_undo_push("Select pose bone child");
+}
+
+
+void pose_add_IK(Scene *scene)
+{
+ Object *obedit= scene->obedit; // XXX context
+ Object *ob= OBACT;
+
+ /* paranoia checks */
+ if(!ob && !ob->pose) return;
+ if(ob==obedit || (ob->flag & OB_POSEMODE)==0) return;
+
+ add_constraint(1); /* 1 means only IK */
+}
+
+/* context: all selected channels */
+void pose_clear_IK(Scene *scene)
+{
+ Object *obedit= scene->obedit; // XXX context
+ Object *ob= OBACT;
+ bArmature *arm= ob->data;
+ bPoseChannel *pchan;
+ bConstraint *con;
+ bConstraint *next;
+
+ /* paranoia checks */
+ if(!ob && !ob->pose) return;
+ if(ob==obedit || (ob->flag & OB_POSEMODE)==0) return;
+
+ if(pose_has_protected_selected(ob, 0, 1))
+ return;
+
+ if(okee("Remove IK constraint(s)")==0) return;
+
+ for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
+ if(arm->layer & pchan->bone->layer) {
+ if(pchan->bone->flag & (BONE_ACTIVE|BONE_SELECTED)) {
+
+ for(con= pchan->constraints.first; con; con= next) {
+ next= con->next;
+ if(con->type==CONSTRAINT_TYPE_KINEMATIC) {
+ BLI_remlink(&pchan->constraints, con);
+ free_constraint_data(con);
+ MEM_freeN(con);
+ }
+ }
+ pchan->constflag &= ~(PCHAN_HAS_IK|PCHAN_HAS_TARGET);
+ }
+ }
+ }
+
+ DAG_object_flush_update(scene, ob, OB_RECALC_DATA); // and all its relations
+
+ BIF_undo_push("Remove IK constraint(s)");
+}
+
+void pose_clear_constraints(Scene *scene)
+{
+ Object *obedit= scene->obedit; // XXX context
+ Object *ob= OBACT;
+ bArmature *arm= ob->data;
+ bPoseChannel *pchan;
+
+ /* paranoia checks */
+ if(!ob && !ob->pose) return;
+ if(ob==obedit || (ob->flag & OB_POSEMODE)==0) return;
+
+ if(pose_has_protected_selected(ob, 0, 1))
+ return;
+
+ if(okee("Remove Constraints")==0) return;
+
+ /* find active */
+ for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
+ if(arm->layer & pchan->bone->layer) {
+ if(pchan->bone->flag & (BONE_ACTIVE|BONE_SELECTED)) {
+ free_constraints(&pchan->constraints);
+ pchan->constflag= 0;
+ }
+ }
+ }
+
+ DAG_object_flush_update(scene, ob, OB_RECALC_DATA); // and all its relations
+
+ BIF_undo_push("Remove Constraint(s)");
+
+}
+
+
+void pose_copy_menu(Scene *scene)
+{
+ Object *obedit= scene->obedit; // XXX context
+ Object *ob= OBACT;
+ bArmature *arm= ob->data;
+ bPoseChannel *pchan, *pchanact;
+ short nr=0;
+ int i=0;
+
+ /* paranoia checks */
+ if (ELEM(NULL, ob, ob->pose)) return;
+ if ((ob==obedit) || (ob->flag & OB_POSEMODE)==0) return;
+
+ /* find active */
+ for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
+ if (pchan->bone->flag & BONE_ACTIVE)
+ break;
+ }
+
+ if (pchan==NULL) return;
+ pchanact= pchan;
+
+ /* if proxy-protected bones selected, some things (such as locks + displays) shouldn't be changable,
+ * but for constraints (just add local constraints)
+ */
+ if (pose_has_protected_selected(ob, 1, 0)) {
+ i= BLI_countlist(&(pchanact->constraints)); /* if there are 24 or less, allow for the user to select constraints */
+ if (i < 25)
+ nr= pupmenu("Copy Pose Attributes %t|Local Location%x1|Local Rotation%x2|Local Size%x3|%l|Visual Location %x9|Visual Rotation%x10|Visual Size%x11|%l|Constraints (All)%x4|Constraints...%x5");
+ else
+ nr= pupmenu("Copy Pose Attributes %t|Local Location%x1|Local Rotation%x2|Local Size%x3|%l|Visual Location %x9|Visual Rotation%x10|Visual Size%x11|%l|Constraints (All)%x4");
+ }
+ else {
+ i= BLI_countlist(&(pchanact->constraints)); /* if there are 24 or less, allow for the user to select constraints */
+ if (i < 25)
+ nr= pupmenu("Copy Pose Attributes %t|Local Location%x1|Local Rotation%x2|Local Size%x3|%l|Visual Location %x9|Visual Rotation%x10|Visual Size%x11|%l|Constraints (All)%x4|Constraints...%x5|%l|Transform Locks%x6|IK Limits%x7|Bone Shape%x8");
+ else
+ nr= pupmenu("Copy Pose Attributes %t|Local Location%x1|Local Rotation%x2|Local Size%x3|%l|Visual Location %x9|Visual Rotation%x10|Visual Size%x11|%l|Constraints (All)%x4|%l|Transform Locks%x6|IK Limits%x7|Bone Shape%x8");
+ }
+
+ if (nr <= 0)
+ return;
+
+ if (nr != 5) {
+ for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
+ if ( (arm->layer & pchan->bone->layer) &&
+ (pchan->bone->flag & BONE_SELECTED) &&
+ (pchan != pchanact) )
+ {
+ switch (nr) {
+ case 1: /* Local Location */
+ VECCOPY(pchan->loc, pchanact->loc);
+ break;
+ case 2: /* Local Rotation */
+ QUATCOPY(pchan->quat, pchanact->quat);
+ break;
+ case 3: /* Local Size */
+ VECCOPY(pchan->size, pchanact->size);
+ break;
+ case 4: /* All Constraints */
+ {
+ ListBase tmp_constraints = {NULL, NULL};
+
+ /* copy constraints to tmpbase and apply 'local' tags before
+ * appending to list of constraints for this channel
+ */
+ copy_constraints(&tmp_constraints, &pchanact->constraints);
+ if ((ob->proxy) && (pchan->bone->layer & arm->layer_protected)) {
+ bConstraint *con;
+
+ /* add proxy-local tags */
+ for (con= tmp_constraints.first; con; con= con->next)
+ con->flag |= CONSTRAINT_PROXY_LOCAL;
+ }
+ addlisttolist(&pchan->constraints, &tmp_constraints);
+
+ /* update flags (need to add here, not just copy) */
+ pchan->constflag |= pchanact->constflag;
+
+ if (ob->pose)
+ ob->pose->flag |= POSE_RECALC;
+ }
+ break;
+ case 6: /* Transform Locks */
+ pchan->protectflag = pchanact->protectflag;
+ break;
+ case 7: /* IK (DOF) settings */
+ {
+ pchan->ikflag = pchanact->ikflag;
+ VECCOPY(pchan->limitmin, pchanact->limitmin);
+ VECCOPY(pchan->limitmax, pchanact->limitmax);
+ VECCOPY(pchan->stiffness, pchanact->stiffness);
+ pchan->ikstretch= pchanact->ikstretch;
+ }
+ break;
+ case 8: /* Custom Bone Shape */
+ pchan->custom = pchanact->custom;
+ break;
+ case 9: /* Visual Location */
+ armature_loc_pose_to_bone(pchan, pchanact->pose_mat[3], pchan->loc);
+ break;
+ case 10: /* Visual Rotation */
+ {
+ float delta_mat[4][4], quat[4];
+
+ armature_mat_pose_to_bone(pchan, pchanact->pose_mat, delta_mat);
+ Mat4ToQuat(delta_mat, quat);
+ QUATCOPY(pchan->quat, quat);
+ }
+ break;
+ case 11: /* Visual Size */
+ {
+ float delta_mat[4][4], size[4];
+
+ armature_mat_pose_to_bone(pchan, pchanact->pose_mat, delta_mat);
+ Mat4ToSize(delta_mat, size);
+ VECCOPY(pchan->size, size);
+ }
+ }
+ }
+ }
+ }
+ else { /* constraints, optional (note: max we can have is 24 constraints) */
+ bConstraint *con, *con_back;
+ int const_toggle[24];
+ ListBase const_copy = {NULL, NULL};
+
+ BLI_duplicatelist(&const_copy, &(pchanact->constraints));
+
+ /* build the puplist of constraints */
+ for (con = pchanact->constraints.first, i=0; con; con=con->next, i++){
+ const_toggle[i]= 1;
+// add_numbut(i, TOG|INT, con->name, 0, 0, &(const_toggle[i]), "");
+ }
+
+// if (!do_clever_numbuts("Select Constraints", i, REDRAW)) {
+// BLI_freelistN(&const_copy);
+// return;
+// }
+
+ /* now build a new listbase from the options selected */
+ for (i=0, con=const_copy.first; con; i++) {
+ /* if not selected, free/remove it from the list */
+ if (!const_toggle[i]) {
+ con_back= con->next;
+ BLI_freelinkN(&const_copy, con);
+ con= con_back;
+ }
+ else
+ con= con->next;
+ }
+
+ /* Copy the temo listbase to the selected posebones */
+ for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
+ if ( (arm->layer & pchan->bone->layer) &&
+ (pchan->bone->flag & BONE_SELECTED) &&
+ (pchan!=pchanact) )
+ {
+ ListBase tmp_constraints = {NULL, NULL};
+
+ /* copy constraints to tmpbase and apply 'local' tags before
+ * appending to list of constraints for this channel
+ */
+ copy_constraints(&tmp_constraints, &const_copy);
+ if ((ob->proxy) && (pchan->bone->layer & arm->layer_protected)) {
+ bConstraint *con;
+
+ /* add proxy-local tags */
+ for (con= tmp_constraints.first; con; con= con->next)
+ con->flag |= CONSTRAINT_PROXY_LOCAL;
+ }
+ addlisttolist(&pchan->constraints, &tmp_constraints);
+
+ /* update flags (need to add here, not just copy) */
+ pchan->constflag |= pchanact->constflag;
+ }
+ }
+ BLI_freelistN(&const_copy);
+ update_pose_constraint_flags(ob->pose); /* we could work out the flags but its simpler to do this */
+
+ if (ob->pose)
+ ob->pose->flag |= POSE_RECALC;
+ }
+
+ DAG_object_flush_update(scene, ob, OB_RECALC_DATA); // and all its relations
+
+ BIF_undo_push("Copy Pose Attributes");
+
+}
+
+/* ******************** copy/paste pose ********************** */
+
+static bPose *g_posebuf=NULL;
+
+void free_posebuf(void)
+{
+ if (g_posebuf) {
+ // was copied without constraints
+ BLI_freelistN (&g_posebuf->chanbase);
+ MEM_freeN (g_posebuf);
+ }
+ g_posebuf=NULL;
+}
+
+void copy_posebuf (Scene *scene)
+{
+ Object *ob= OBACT;
+
+ if (!ob || !ob->pose){
+ error ("No Pose");
+ return;
+ }
+
+ free_posebuf();
+
+ set_pose_keys(ob); // sets chan->flag to POSE_KEY if bone selected
+ copy_pose(&g_posebuf, ob->pose, 0);
+
+}
+
+void paste_posebuf (Scene *scene, int flip)
+{
+ Object *ob= OBACT;
+ bPoseChannel *chan, *pchan;
+ float eul[4];
+ char name[32];
+
+ if (!ob || !ob->pose)
+ return;
+
+ if (!g_posebuf){
+ error ("Copy buffer is empty");
+ return;
+ }
+
+ /*
+ // disabled until protected bones in proxies follow the rules everywhere else!
+ if(pose_has_protected_selected(ob, 1, 1))
+ return;
+ */
+
+ /* Safely merge all of the channels in this pose into
+ any existing pose */
+ for (chan=g_posebuf->chanbase.first; chan; chan=chan->next) {
+ if (chan->flag & POSE_KEY) {
+ BLI_strncpy(name, chan->name, sizeof(name));
+ if (flip)
+ bone_flip_name (name, 0); // 0 = don't strip off number extensions
+
+ /* only copy when channel exists, poses are not meant to add random channels to anymore */
+ pchan= get_pose_channel(ob->pose, name);
+
+ if (pchan) {
+ /* only loc rot size */
+ /* only copies transform info for the pose */
+ VECCOPY(pchan->loc, chan->loc);
+ VECCOPY(pchan->size, chan->size);
+ QUATCOPY(pchan->quat, chan->quat);
+ pchan->flag= chan->flag;
+
+ if (flip) {
+ pchan->loc[0]*= -1;
+
+ QuatToEul(pchan->quat, eul);
+ eul[1]*= -1;
+ eul[2]*= -1;
+ EulToQuat(eul, pchan->quat);
+ }
+
+ if (autokeyframe_cfra_can_key(ob)) {
+ ID *id= &ob->id;
+
+ /* Set keys on pose */
+ if (chan->flag & POSE_ROT) {
+ insertkey(id, ID_PO, pchan->name, NULL, AC_QUAT_X, 0);
+ insertkey(id, ID_PO, pchan->name, NULL, AC_QUAT_Y, 0);
+ insertkey(id, ID_PO, pchan->name, NULL, AC_QUAT_Z, 0);
+ insertkey(id, ID_PO, pchan->name, NULL, AC_QUAT_W, 0);
+ }
+ if (chan->flag & POSE_SIZE) {
+ insertkey(id, ID_PO, pchan->name, NULL, AC_SIZE_X, 0);
+ insertkey(id, ID_PO, pchan->name, NULL, AC_SIZE_Y, 0);
+ insertkey(id, ID_PO, pchan->name, NULL, AC_SIZE_Z, 0);
+ }
+ if (chan->flag & POSE_LOC) {
+ insertkey(id, ID_PO, pchan->name, NULL, AC_LOC_X, 0);
+ insertkey(id, ID_PO, pchan->name, NULL, AC_LOC_Y, 0);
+ insertkey(id, ID_PO, pchan->name, NULL, AC_LOC_Z, 0);
+ }
+
+ /* clear any unkeyed tags */
+ if (chan->bone)
+ chan->bone->flag &= ~BONE_UNKEYED;
+ }
+ else {
+ /* add unkeyed tags */
+ if (chan->bone)
+ chan->bone->flag |= BONE_UNKEYED;
+ }
+ }
+ }
+ }
+
+ /* Update event for pose and deformation children */
+ DAG_object_flush_update(scene, ob, OB_RECALC_DATA);
+
+ if (IS_AUTOKEY_ON) {
+// XXX remake_action_ipos(ob->action);
+ }
+ else {
+ /* need to trick depgraph, action is not allowed to execute on pose */
+ where_is_pose(scene, ob);
+ ob->recalc= 0;
+ }
+
+ BIF_undo_push("Paste Action Pose");
+}
+
+/* ********************************************** */
+
+/* context weightpaint and deformer in posemode */
+void pose_adds_vgroups(Scene *scene, Object *meshobj, int heatweights)
+{
+// XXX extern VPaint Gwp; /* from vpaint */
+ Object *poseobj= modifiers_isDeformedByArmature(meshobj);
+
+ if(poseobj==NULL || (poseobj->flag & OB_POSEMODE)==0) {
+ error("The active object must have a deforming armature in pose mode");
+ return;
+ }
+
+// XXX add_verts_to_dgroups(meshobj, poseobj, heatweights, (Gwp.flag & VP_MIRROR_X));
+
+ if(heatweights)
+ BIF_undo_push("Apply Bone Heat Weights to Vertex Groups");
+ else
+ BIF_undo_push("Apply Bone Envelopes to Vertex Groups");
+
+
+ // and all its relations
+ DAG_object_flush_update(scene, meshobj, OB_RECALC_DATA);
+}
+
+/* ********************************************** */
+
+/* adds a new pose-group */
+void pose_add_posegroup (Scene *scene)
+{
+ Object *ob= OBACT;
+ bPose *pose= (ob) ? ob->pose : NULL;
+ bActionGroup *grp;
+
+ if (ELEM(NULL, ob, ob->pose))
+ return;
+
+ grp= MEM_callocN(sizeof(bActionGroup), "PoseGroup");
+ strcpy(grp->name, "Group");
+ BLI_addtail(&pose->agroups, grp);
+ BLI_uniquename(&pose->agroups, grp, "Group", offsetof(bActionGroup, name), 32);
+
+ pose->active_group= BLI_countlist(&pose->agroups);
+
+ BIF_undo_push("Add Bone Group");
+
+}
+
+/* Remove the active bone-group */
+void pose_remove_posegroup (Scene *scene)
+{
+ Object *ob= OBACT;
+ bPose *pose= (ob) ? ob->pose : NULL;
+ bActionGroup *grp = NULL;
+ bPoseChannel *pchan;
+
+ /* sanity checks */
+ if (ELEM(NULL, ob, pose))
+ return;
+ if (pose->active_group <= 0)
+ return;
+
+ /* get group to remove */
+ grp= BLI_findlink(&pose->agroups, pose->active_group-1);
+ if (grp) {
+ /* adjust group references (the trouble of using indices!):
+ * - firstly, make sure nothing references it
+ * - also, make sure that those after this item get corrected
+ */
+ for (pchan= pose->chanbase.first; pchan; pchan= pchan->next) {
+ if (pchan->agrp_index == pose->active_group)
+ pchan->agrp_index= 0;
+ else if (pchan->agrp_index > pose->active_group)
+ pchan->agrp_index--;
+ }
+
+ /* now, remove it from the pose */
+ BLI_freelinkN(&pose->agroups, grp);
+ pose->active_group= 0;
+
+ BIF_undo_push("Remove Bone Group");
+ }
+
+}
+
+char *build_posegroups_menustr (bPose *pose, short for_pupmenu)
+{
+ DynStr *pupds= BLI_dynstr_new();
+ bActionGroup *grp;
+ char *str;
+ char buf[16];
+ int i;
+
+ /* add title first (and the "none" entry) */
+ BLI_dynstr_append(pupds, "Bone Group%t|");
+ if (for_pupmenu)
+ BLI_dynstr_append(pupds, "Add New%x0|");
+ else
+ BLI_dynstr_append(pupds, "BG: [None]%x0|");
+
+ /* loop through groups, adding them */
+ for (grp= pose->agroups.first, i=1; grp; grp=grp->next, i++) {
+ if (for_pupmenu == 0)
+ BLI_dynstr_append(pupds, "BG: ");
+ BLI_dynstr_append(pupds, grp->name);
+
+ sprintf(buf, "%%x%d", i);
+ BLI_dynstr_append(pupds, buf);
+
+ if (grp->next)
+ BLI_dynstr_append(pupds, "|");
+ }
+
+ /* convert to normal MEM_malloc'd string */
+ str= BLI_dynstr_get_cstring(pupds);
+ BLI_dynstr_free(pupds);
+
+ return str;
+}
+
+/* Assign selected pchans to the bone group that the user selects */
+void pose_assign_to_posegroup (Scene *scene, short active)
+{
+ Object *ob= OBACT;
+ bArmature *arm= (ob) ? ob->data : NULL;
+ bPose *pose= (ob) ? ob->pose : NULL;
+ bPoseChannel *pchan;
+ char *menustr;
+ int nr;
+ short done= 0;
+
+ /* sanity checks */
+ if (ELEM3(NULL, ob, pose, arm))
+ return;
+
+ /* get group to affect */
+ if ((active==0) || (pose->active_group <= 0)) {
+ menustr= build_posegroups_menustr(pose, 1);
+ nr= 0; // XXX pupmenu_col(menustr, 20);
+ MEM_freeN(menustr);
+
+ if (nr < 0)
+ return;
+ else if (nr == 0) {
+ /* add new - note: this does an undo push and sets active group */
+ pose_add_posegroup(scene);
+ }
+ else
+ pose->active_group= nr;
+ }
+
+ /* add selected bones to group then */
+ for (pchan= pose->chanbase.first; pchan; pchan= pchan->next) {
+ if ((pchan->bone->flag & BONE_SELECTED) && (pchan->bone->layer & arm->layer)) {
+ pchan->agrp_index= pose->active_group;
+ done= 1;
+ }
+ }
+
+ if (done)
+ BIF_undo_push("Add Bones To Group");
+
+}
+
+/* Remove selected pchans from their bone groups */
+void pose_remove_from_posegroups (Scene *scene)
+{
+ Object *ob= OBACT;
+ bArmature *arm= (ob) ? ob->data : NULL;
+ bPose *pose= (ob) ? ob->pose : NULL;
+ bPoseChannel *pchan;
+ short done= 0;
+
+ /* sanity checks */
+ if (ELEM3(NULL, ob, pose, arm))
+ return;
+
+ /* remove selected bones from their groups */
+ for (pchan= pose->chanbase.first; pchan; pchan= pchan->next) {
+ if ((pchan->bone->flag & BONE_SELECTED) && (pchan->bone->layer & arm->layer)) {
+ if (pchan->agrp_index) {
+ pchan->agrp_index= 0;
+ done= 1;
+ }
+ }
+ }
+
+ if (done)
+ BIF_undo_push("Remove Bones From Groups");
+
+}
+
+/* Ctrl-G in 3D-View while in PoseMode */
+void pgroup_operation_with_menu (Scene *scene)
+{
+ Object *ob= OBACT;
+ bArmature *arm= (ob) ? ob->data : NULL;
+ bPose *pose= (ob) ? ob->pose : NULL;
+ bPoseChannel *pchan= NULL;
+ int mode;
+
+ /* sanity checks */
+ if (ELEM3(NULL, ob, pose, arm))
+ return;
+
+ /* check that something is selected */
+ for (pchan= pose->chanbase.first; pchan; pchan= pchan->next) {
+ if ((pchan->bone->flag & BONE_SELECTED) && (pchan->bone->layer & arm->layer))
+ break;
+ }
+ if (pchan == NULL)
+ return;
+
+ /* get mode of action */
+ if (pchan)
+ mode= pupmenu("Bone Groups%t|Add Selected to Active Group%x1|Add Selected to Group%x2|%|Remove Selected From Groups%x3|Remove Active Group%x4");
+ else
+ mode= pupmenu("Bone Groups%t|Add New Group%x5|Remove Active Group%x4");
+
+ /* handle mode */
+ switch (mode) {
+ case 1:
+ pose_assign_to_posegroup(scene, 1);
+ break;
+ case 2:
+ pose_assign_to_posegroup(scene, 0);
+ break;
+ case 5:
+ pose_add_posegroup(scene);
+ break;
+ case 3:
+ pose_remove_from_posegroups(scene);
+ break;
+ case 4:
+ pose_remove_posegroup(scene);
+ break;
+ }
+}
+
+/* ********************************************** */
+
+static short pose_select_same_group (Object *ob)
+{
+ bPose *pose= (ob)? ob->pose : NULL;
+ bArmature *arm= (ob)? ob->data : NULL;
+ bPoseChannel *pchan, *chan;
+ short changed= 0;
+
+ if (ELEM3(NULL, ob, pose, arm))
+ return 0;
+
+ /* loop in loop... bad and slow! */
+ for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
+ if (arm->layer & pchan->bone->layer) {
+ if (pchan->bone->flag & (BONE_ACTIVE|BONE_SELECTED)) {
+
+ /* only if group matches (and is not selected or current bone) */
+ for (chan= ob->pose->chanbase.first; chan; chan= chan->next) {
+ if (arm->layer & chan->bone->layer) {
+ if (pchan->agrp_index == chan->agrp_index) {
+ chan->bone->flag |= BONE_SELECTED;
+ changed= 1;
+ }
+ }
+ }
+
+ }
+ }
+ }
+
+ return changed;
+}
+
+static short pose_select_same_layer (Object *ob)
+{
+ bPose *pose= (ob)? ob->pose : NULL;
+ bArmature *arm= (ob)? ob->data : NULL;
+ bPoseChannel *pchan;
+ short layers= 0, changed= 0;
+
+ if (ELEM3(NULL, ob, pose, arm))
+ return 0;
+
+ /* figure out what bones are selected */
+ for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
+ if (arm->layer & pchan->bone->layer) {
+ if (pchan->bone->flag & (BONE_ACTIVE|BONE_SELECTED)) {
+ layers |= pchan->bone->layer;
+ }
+ }
+ }
+ if (layers == 0)
+ return 0;
+
+ /* select bones that are on same layers as layers flag */
+ for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
+ if (arm->layer & pchan->bone->layer) {
+ if (layers & pchan->bone->layer) {
+ pchan->bone->flag |= BONE_SELECTED;
+ changed= 1;
+ }
+ }
+ }
+
+ return changed;
+}
+
+
+void pose_select_grouped (Scene *scene, short nr)
+{
+ short changed = 0;
+
+ if (nr == 1) changed= pose_select_same_group(OBACT);
+ else if (nr == 2) changed= pose_select_same_layer(OBACT);
+
+ if (changed) {
+ countall();
+ BIF_undo_push("Select Grouped");
+ }
+}
+
+/* Shift-G in 3D-View while in PoseMode */
+void pose_select_grouped_menu (Scene *scene)
+{
+ short nr;
+
+ /* here we go */
+ nr= pupmenu("Select Grouped%t|In Same Group%x1|In Same Layer%x2");
+ pose_select_grouped(scene, nr);
+}
+
+/* ********************************************** */
+
+/* context active object */
+void pose_flip_names(Scene *scene)
+{
+ Object *obedit= scene->obedit; // XXX context
+ Object *ob= OBACT;
+ bArmature *arm= ob->data;
+ bPoseChannel *pchan;
+ char newname[32];
+
+ /* paranoia checks */
+ if(!ob && !ob->pose) return;
+ if(ob==obedit || (ob->flag & OB_POSEMODE)==0) return;
+
+ if(pose_has_protected_selected(ob, 0, 1))
+ return;
+
+ for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
+ if(arm->layer & pchan->bone->layer) {
+ if(pchan->bone->flag & (BONE_ACTIVE|BONE_SELECTED)) {
+ BLI_strncpy(newname, pchan->name, sizeof(newname));
+ bone_flip_name(newname, 1); // 1 = do strip off number extensions
+ armature_bone_rename(ob, pchan->name, newname);
+ }
+ }
+ }
+
+ BIF_undo_push("Flip names");
+}
+
+/* context active object */
+void pose_autoside_names(Scene *scene, short axis)
+{
+ Object *obedit= scene->obedit; // XXX context
+ Object *ob= OBACT;
+ bArmature *arm= ob->data;
+ bPoseChannel *pchan;
+ char newname[32];
+
+ /* paranoia checks */
+ if (ELEM(NULL, ob, ob->pose)) return;
+ if (ob==obedit || (ob->flag & OB_POSEMODE)==0) return;
+
+ if (pose_has_protected_selected(ob, 0, 1))
+ return;
+
+ for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
+ if(arm->layer & pchan->bone->layer) {
+ if(pchan->bone->flag & (BONE_ACTIVE|BONE_SELECTED)) {
+ BLI_strncpy(newname, pchan->name, sizeof(newname));
+ bone_autoside_name(newname, 1, axis, pchan->bone->head[axis], pchan->bone->tail[axis]);
+ armature_bone_rename(ob, pchan->name, newname);
+ }
+ }
+ }
+
+ BIF_undo_push("Flip names");
+}
+
+/* context active object, or weightpainted object with armature in posemode */
+void pose_activate_flipped_bone(Scene *scene)
+{
+ Object *ob= OBACT;
+ bArmature *arm= ob->data;
+
+ if(ob==NULL) return;
+
+ if(G.f & G_WEIGHTPAINT) {
+ ob= modifiers_isDeformedByArmature(ob);
+ }
+ if(ob && (ob->flag & OB_POSEMODE)) {
+ bPoseChannel *pchan, *pchanf;
+
+ for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
+ if(arm->layer & pchan->bone->layer) {
+ if(pchan->bone->flag & BONE_ACTIVE)
+ break;
+ }
+ }
+ if(pchan) {
+ char name[32];
+
+ BLI_strncpy(name, pchan->name, 32);
+ bone_flip_name(name, 1); // 0 = do not strip off number extensions
+
+ pchanf= get_pose_channel(ob->pose, name);
+ if(pchanf && pchanf!=pchan) {
+ pchan->bone->flag &= ~(BONE_SELECTED|BONE_ACTIVE);
+ pchanf->bone->flag |= (BONE_SELECTED|BONE_ACTIVE);
+
+ /* in weightpaint we select the associated vertex group too */
+ if(G.f & G_WEIGHTPAINT) {
+ vertexgroup_select_by_name(OBACT, name);
+ DAG_object_flush_update(scene, OBACT, OB_RECALC_DATA);
+ }
+
+ select_actionchannel_by_name(ob->action, name, 1);
+
+ }
+ }
+ }
+}
+
+/* This function pops up the move-to-layer popup widgets when the user
+ * presses either SHIFT-MKEY or MKEY in PoseMode OR EditMode (for Armatures)
+ */
+void pose_movetolayer(Scene *scene)
+{
+ Object *obedit= scene->obedit; // XXX context
+ Object *ob= OBACT;
+ bArmature *arm;
+ short lay= 0;
+ short shift= 0; // XXX
+
+ if (ob==NULL) return;
+ arm= ob->data;
+
+ if (shift) {
+ /* armature layers */
+ lay= arm->layer;
+ if ( movetolayer_short_buts(&lay, "Armature Layers")==0 ) return;
+ if (lay==0) return;
+ arm->layer= lay;
+ if(ob->pose)
+ ob->pose->proxy_layer= lay;
+
+ }
+ else if (obedit) {
+ /* the check for editbone layer moving needs to occur before posemode one to work */
+ EditBone *ebo;
+ EditBone *flipBone;
+
+ for (ebo= arm->edbo->first; ebo; ebo= ebo->next) {
+ if (arm->layer & ebo->layer) {
+ if (ebo->flag & BONE_SELECTED)
+ lay |= ebo->layer;
+ }
+ }
+ if (lay==0) return;
+
+ if ( movetolayer_short_buts(&lay, "Bone Layers")==0 ) return;
+ if (lay==0) return;
+
+ for (ebo= arm->edbo->first; ebo; ebo= ebo->next) {
+ if (arm->layer & ebo->layer) {
+ if (ebo->flag & BONE_SELECTED) {
+ ebo->layer= lay;
+ if (arm->flag & ARM_MIRROR_EDIT) {
+ flipBone = armature_bone_get_mirrored(arm->edbo, ebo);
+ if (flipBone)
+ flipBone->layer = lay;
+ }
+ }
+ }
+ }
+
+ BIF_undo_push("Move Bone Layer");
+ }
+ else if (ob->flag & OB_POSEMODE) {
+ /* pose-channel layers */
+ bPoseChannel *pchan;
+
+ if (pose_has_protected_selected(ob, 0, 1))
+ return;
+
+ for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
+ if (arm->layer & pchan->bone->layer) {
+ if (pchan->bone->flag & BONE_SELECTED)
+ lay |= pchan->bone->layer;
+ }
+ }
+ if (lay==0) return;
+
+ if ( movetolayer_short_buts(&lay, "Bone Layers")==0 ) return;
+ if (lay==0) return;
+
+ for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
+ if (arm->layer & pchan->bone->layer) {
+ if (pchan->bone->flag & BONE_SELECTED)
+ pchan->bone->layer= lay;
+ }
+ }
+
+ BIF_undo_push("Move Bone Layer");
+ }
+}
+
+
+/* for use with pose_relax only */
+static int pose_relax_icu(struct IpoCurve *icu, float framef, float *val, float *frame_prev, float *frame_next)
+{
+ if (!icu) {
+ return 0;
+ }
+ else {
+ BezTriple *bezt = icu->bezt;
+
+ BezTriple *bezt_prev=NULL, *bezt_next=NULL;
+ float w1, w2, wtot;
+ int i;
+
+ for (i=0; i < icu->totvert; i++, bezt++) {
+ if (bezt->vec[1][0] < framef - 0.5) {
+ bezt_prev = bezt;
+ } else {
+ break;
+ }
+ }
+
+ if (bezt_prev==NULL) return 0;
+
+ /* advance to the next, dont need to advance i */
+ bezt = bezt_prev+1;
+
+ for (; i < icu->totvert; i++, bezt++) {
+ if (bezt->vec[1][0] > framef + 0.5) {
+ bezt_next = bezt;
+ break;
+ }
+ }
+
+ if (bezt_next==NULL) return 0;
+
+ if (val) {
+ w1 = framef - bezt_prev->vec[1][0];
+ w2 = bezt_next->vec[1][0] - framef;
+ wtot = w1 + w2;
+ w1=w1/wtot;
+ w2=w2/wtot;
+#if 0
+ val = (bezt_prev->vec[1][1] * w2) + (bezt_next->vec[1][1] * w1);
+#else
+ /* apply the value with a hard coded 6th */
+ *val = (((bezt_prev->vec[1][1] * w2) + (bezt_next->vec[1][1] * w1)) + (*val * 5.0f)) / 6.0f;
+#endif
+ }
+
+ if (frame_prev) *frame_prev = bezt_prev->vec[1][0];
+ if (frame_next) *frame_next = bezt_next->vec[1][0];
+
+ return 1;
+ }
+}
+
+void pose_relax(Scene *scene)
+{
+ Object *ob = OBACT;
+ bPose *pose;
+ bAction *act;
+ bArmature *arm;
+
+ IpoCurve *icu_w, *icu_x, *icu_y, *icu_z;
+
+ bPoseChannel *pchan;
+ bActionChannel *achan;
+ float framef = F_CFRA;
+ float frame_prev, frame_next;
+ float quat_prev[4], quat_next[4], quat_interp[4], quat_orig[4];
+
+ int do_scale = 0;
+ int do_loc = 0;
+ int do_quat = 0;
+ int flag = 0;
+ int do_x, do_y, do_z;
+
+ if (!ob) return;
+
+ pose = ob->pose;
+ act = ob->action;
+ arm = (bArmature *)ob->data;
+
+ if (!pose || !act || !arm) return;
+
+ for (pchan=pose->chanbase.first; pchan; pchan= pchan->next) {
+
+ pchan->bone->flag &= ~BONE_TRANSFORM;
+
+ if (pchan->bone->layer & arm->layer) {
+ if (pchan->bone->flag & BONE_SELECTED) {
+ /* do we have an ipo curve? */
+ achan= get_action_channel(act, pchan->name);
+
+ if (achan && achan->ipo) {
+ /*calc_ipo(achan->ipo, ctime);*/
+
+ do_x = pose_relax_icu(find_ipocurve(achan->ipo, AC_LOC_X), framef, &pchan->loc[0], NULL, NULL);
+ do_y = pose_relax_icu(find_ipocurve(achan->ipo, AC_LOC_Y), framef, &pchan->loc[1], NULL, NULL);
+ do_z = pose_relax_icu(find_ipocurve(achan->ipo, AC_LOC_Z), framef, &pchan->loc[2], NULL, NULL);
+ do_loc += do_x + do_y + do_z;
+
+ do_x = pose_relax_icu(find_ipocurve(achan->ipo, AC_SIZE_X), framef, &pchan->size[0], NULL, NULL);
+ do_y = pose_relax_icu(find_ipocurve(achan->ipo, AC_SIZE_Y), framef, &pchan->size[1], NULL, NULL);
+ do_z = pose_relax_icu(find_ipocurve(achan->ipo, AC_SIZE_Z), framef, &pchan->size[2], NULL, NULL);
+ do_scale += do_x + do_y + do_z;
+
+ if( ((icu_w = find_ipocurve(achan->ipo, AC_QUAT_W))) &&
+ ((icu_x = find_ipocurve(achan->ipo, AC_QUAT_X))) &&
+ ((icu_y = find_ipocurve(achan->ipo, AC_QUAT_Y))) &&
+ ((icu_z = find_ipocurve(achan->ipo, AC_QUAT_Z))) )
+ {
+ /* use the quatw keyframe as a basis for others */
+ if (pose_relax_icu(icu_w, framef, NULL, &frame_prev, &frame_next)) {
+ /* get 2 quats */
+ quat_prev[0] = eval_icu(icu_w, frame_prev);
+ quat_prev[1] = eval_icu(icu_x, frame_prev);
+ quat_prev[2] = eval_icu(icu_y, frame_prev);
+ quat_prev[3] = eval_icu(icu_z, frame_prev);
+
+ quat_next[0] = eval_icu(icu_w, frame_next);
+ quat_next[1] = eval_icu(icu_x, frame_next);
+ quat_next[2] = eval_icu(icu_y, frame_next);
+ quat_next[3] = eval_icu(icu_z, frame_next);
+
+#if 0
+ /* apply the setting, completely smooth */
+ QuatInterpol(pchan->quat, quat_prev, quat_next, (framef-frame_prev) / (frame_next-frame_prev) );
+#else
+ /* tricky interpolation */
+ QuatInterpol(quat_interp, quat_prev, quat_next, (framef-frame_prev) / (frame_next-frame_prev) );
+ QUATCOPY(quat_orig, pchan->quat);
+ QuatInterpol(pchan->quat, quat_orig, quat_interp, 1.0f/6.0f);
+ /* done */
+#endif
+ do_quat++;
+ }
+ }
+
+ /* apply BONE_TRANSFORM tag so that autokeying will pick it up */
+ pchan->bone->flag |= BONE_TRANSFORM;
+ }
+ }
+ }
+ }
+
+ ob->pose->flag |= (POSE_LOCKED|POSE_DO_UNLOCK);
+
+ /* do auto-keying */
+ if (do_loc) flag |= TFM_TRANSLATION;
+ if (do_scale) flag |= TFM_RESIZE;
+ if (do_quat) flag |= TFM_ROTATION;
+ autokeyframe_pose_cb_func(ob, flag, 0);
+
+ /* clear BONE_TRANSFORM flags */
+ for (pchan=pose->chanbase.first; pchan; pchan= pchan->next)
+ pchan->bone->flag &= ~ BONE_TRANSFORM;
+
+ /* do depsgraph flush */
+ DAG_object_flush_update(scene, ob, OB_RECALC_DATA);
+ BIF_undo_push("Relax Pose");
+}
+
+/* for use in insertkey, ensure rotation goes other way around */
+void pose_flipquats(Scene *scene)
+{
+ Object *ob = OBACT;
+ bArmature *arm= ob->data;
+ bPoseChannel *pchan;
+
+ if(ob->pose==NULL)
+ return;
+
+ /* find sel bones */
+ for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
+ if(pchan->bone && (pchan->bone->flag & BONE_SELECTED) && (pchan->bone->layer & arm->layer)) {
+ /* quaternions have 720 degree range */
+ pchan->quat[0]= -pchan->quat[0];
+ pchan->quat[1]= -pchan->quat[1];
+ pchan->quat[2]= -pchan->quat[2];
+ pchan->quat[3]= -pchan->quat[3];
+ }
+ }
+
+ /* do autokey */
+ autokeyframe_pose_cb_func(ob, TFM_ROTATION, 0);
+}
+
+/* context: active channel */
+void pose_special_editmenu(Scene *scene)
+{
+#if 0
+ Object *obedit= scene->obedit; // XXX context
+ Object *ob= OBACT;
+ short nr;
+
+ /* paranoia checks */
+ if(!ob && !ob->pose) return;
+ if(ob==obedit || (ob->flag & OB_POSEMODE)==0) return;
+
+ nr= pupmenu("Specials%t|Select Constraint Target%x1|Flip Left-Right Names%x2|Calculate Paths%x3|Clear Paths%x4|Clear User Transform %x5|Relax Pose %x6|%l|AutoName Left-Right%x7|AutoName Front-Back%x8|AutoName Top-Bottom%x9");
+ if(nr==1) {
+ pose_select_constraint_target(scene);
+ }
+ else if(nr==2) {
+ pose_flip_names();
+ }
+ else if(nr==3) {
+ pose_calculate_path(ob);
+ }
+ else if(nr==4) {
+ pose_clear_paths(ob);
+ }
+ else if(nr==5) {
+ rest_pose(ob->pose);
+ DAG_object_flush_update(scene, ob, OB_RECALC_DATA);
+ BIF_undo_push("Clear User Transform Pose");
+ }
+ else if(nr==6) {
+ pose_relax();
+ }
+ else if(ELEM3(nr, 7, 8, 9)) {
+ pose_autoside_names(nr-7);
+ }
+#endif
+}
+
diff --git a/source/blender/editors/armature/reeb.h b/source/blender/editors/armature/reeb.h
new file mode 100644
index 00000000000..c4c062196fc
--- /dev/null
+++ b/source/blender/editors/armature/reeb.h
@@ -0,0 +1,189 @@
+/**
+ * $Id:
+ *
+ * ***** BEGIN GPL 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.
+ *
+ * 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.
+ *
+ * Contributor(s): Martin Poirier
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef REEB_H_
+#define REEB_H_
+
+//#define WITH_BF_REEB
+
+#include "DNA_listBase.h"
+
+#include "BLI_graph.h"
+
+struct GHash;
+struct EdgeHash;
+struct ReebArc;
+struct ReebEdge;
+struct ReebNode;
+
+typedef struct ReebGraph {
+ ListBase arcs;
+ ListBase nodes;
+
+ float length;
+
+ FreeArc free_arc;
+ FreeNode free_node;
+ RadialSymmetry radial_symmetry;
+ AxialSymmetry axial_symmetry;
+ /*********************************/
+
+ int resolution;
+ int totnodes;
+ struct EdgeHash *emap;
+ int multi_level;
+ struct ReebGraph *link_up; /* for multi resolution filtering, points to higher levels */
+} ReebGraph;
+
+typedef struct EmbedBucket {
+ float val;
+ int nv;
+ float p[3];
+} EmbedBucket;
+
+typedef struct ReebNode {
+ void *next, *prev;
+ float p[3];
+ int flag;
+
+ int degree;
+ struct ReebArc **arcs;
+
+ int subgraph_index;
+
+ int symmetry_level;
+ int symmetry_flag;
+ float symmetry_axis[3];
+ /*********************************/
+
+ int index;
+ float weight;
+ int multi_level;
+ struct ReebNode *link_down; /* for multi resolution filtering, points to lower levels, if present */
+ struct ReebNode *link_up;
+} ReebNode;
+
+typedef struct ReebEdge {
+ struct ReebEdge *next, *prev;
+ struct ReebArc *arc;
+ struct ReebNode *v1, *v2;
+ struct ReebEdge *nextEdge;
+ int flag;
+} ReebEdge;
+
+typedef struct ReebArc {
+ void *next, *prev;
+ struct ReebNode *head, *tail;
+ int flag;
+
+ float length;
+
+ int symmetry_level;
+ int symmetry_group;
+ int symmetry_flag;
+ /*********************************/
+
+ ListBase edges;
+ int bcount;
+ struct EmbedBucket *buckets;
+
+ struct GHash *faces;
+ float angle;
+ struct ReebArc *link_up; /* for multi resolution filtering, points to higher levels */
+} ReebArc;
+
+typedef struct ReebArcIterator {
+ struct ReebArc *arc;
+ int index;
+ int start;
+ int end;
+ int stride;
+ int length;
+} ReebArcIterator;
+
+struct EditMesh;
+struct EdgeIndex;
+
+int weightToHarmonic(struct EditMesh *em, struct EdgeIndex *indexed_edges);
+int weightFromDistance(struct EditMesh *em, struct EdgeIndex *indexed_edges);
+int weightFromLoc(struct EditMesh *me, int axis);
+void weightToVCol(struct EditMesh *em, int index);
+void arcToVCol(struct ReebGraph *rg, struct EditMesh *em, int index);
+void angleToVCol(struct EditMesh *em, int index);
+void renormalizeWeight(struct EditMesh *em, float newmax);
+
+ReebGraph * generateReebGraph(struct EditMesh *me, int subdivisions);
+ReebGraph * newReebGraph();
+
+void initArcIterator(struct ReebArcIterator *iter, struct ReebArc *arc, struct ReebNode *head);
+void initArcIterator2(struct ReebArcIterator *iter, struct ReebArc *arc, int start, int end);
+void initArcIteratorStart(struct ReebArcIterator *iter, struct ReebArc *arc, struct ReebNode *head, int start);
+struct EmbedBucket * nextBucket(struct ReebArcIterator *iter);
+struct EmbedBucket * nextNBucket(ReebArcIterator *iter, int n);
+struct EmbedBucket * peekBucket(ReebArcIterator *iter, int n);
+struct EmbedBucket * currentBucket(struct ReebArcIterator *iter);
+struct EmbedBucket * previousBucket(struct ReebArcIterator *iter);
+int iteratorStopped(struct ReebArcIterator *iter);
+
+/* Filtering */
+void filterNullReebGraph(ReebGraph *rg);
+int filterSmartReebGraph(ReebGraph *rg, float threshold);
+int filterExternalReebGraph(ReebGraph *rg, float threshold);
+int filterInternalReebGraph(ReebGraph *rg, float threshold);
+
+/* Post-Build processing */
+void repositionNodes(ReebGraph *rg);
+void postprocessGraph(ReebGraph *rg, char mode);
+void removeNormalNodes(ReebGraph *rg);
+
+void sortNodes(ReebGraph *rg);
+void sortArcs(ReebGraph *rg);
+
+/*------------ Sanity check ------------*/
+void verifyBuckets(ReebGraph *rg);
+void verifyFaces(ReebGraph *rg);
+
+/*********************** PUBLIC *********************************/
+
+#define REEB_MAX_MULTI_LEVEL 10
+
+ReebGraph *BIF_ReebGraphFromEditMesh(void);
+ReebGraph *BIF_ReebGraphMultiFromEditMesh(void);
+void BIF_flagMultiArcs(ReebGraph *rg, int flag);
+
+void BIF_GlobalReebGraphFromEditMesh(void);
+void BIF_GlobalReebFree(void);
+
+ReebNode *BIF_otherNodeFromIndex(ReebArc *arc, ReebNode *node);
+ReebNode *BIF_NodeFromIndex(ReebArc *arc, ReebNode *node);
+ReebNode *BIF_lowestLevelNode(ReebNode *node);
+
+ReebGraph *BIF_graphForMultiNode(ReebGraph *rg, ReebNode *node);
+
+void REEB_freeGraph(ReebGraph *rg);
+void REEB_exportGraph(ReebGraph *rg, int count);
+void REEB_draw();
+
+
+#endif /*REEB_H_*/