From 1fe21f7e8f2f12b8b0707609e132b53fcc842f9e Mon Sep 17 00:00:00 2001 From: Ton Roosendaal Date: Mon, 5 Jan 2009 15:19:31 +0000 Subject: 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! :) --- source/blender/editors/armature/Makefile | 59 + source/blender/editors/armature/SConscript | 11 + source/blender/editors/armature/armature_intern.h | 39 + source/blender/editors/armature/editarmature.c | 4971 +++++++++++++++++++++ source/blender/editors/armature/meshlaplacian.c | 1923 ++++++++ source/blender/editors/armature/meshlaplacian.h | 85 + source/blender/editors/armature/poseobject.c | 1681 +++++++ source/blender/editors/armature/reeb.h | 189 + 8 files changed, 8958 insertions(+) create mode 100644 source/blender/editors/armature/Makefile create mode 100644 source/blender/editors/armature/SConscript create mode 100644 source/blender/editors/armature/armature_intern.h create mode 100644 source/blender/editors/armature/editarmature.c create mode 100644 source/blender/editors/armature/meshlaplacian.c create mode 100644 source/blender/editors/armature/meshlaplacian.h create mode 100644 source/blender/editors/armature/poseobject.c create mode 100644 source/blender/editors/armature/reeb.h (limited to 'source/blender/editors/armature') 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 +#include +#include +#include +#include + +#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; aflag & 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; atotvert; 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 +#include + +#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; atotface; 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; atotface; a++, face++) + laplacian_triangle_area(sys, (*face)[0], (*face)[1], (*face)[2]); + + for(a=0; aareaweights) { + 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; afaces); + 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; atotvert; 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; atotvert; 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; atotface; 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; jheat.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; jheat.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; atotface; 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; atotvert; 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; atotvert; a++) + laplacian_add_vertex(sys, sys->heat.verts[a], 0); + + for(a=0, mface=me->mface; atotface; 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; atotvert; 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; atotface; 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; atotvert; a++) + vertsflipped[a] = mesh_get_x_mirror_vert(ob, a); + } + + /* compute weights per bone */ + for(j=0; jtotvert; 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; atotvert; 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; atotvert; 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; atotvert; 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; atotvert; 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; atotcagevert; 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; araytree, 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; fcagecos[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; asemibound[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; anvert; 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; asize3; 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; zsize; z++) + for(y=0; ysize; y++) + for(x=0; xsize; x++) + meshdeform_matrix_add_cell(mdb, x, y, z); + + /* solve for each cage vert */ + for(a=0; atotcagevert; a++) { + if(a != 0) { + nlBegin(NL_SYSTEM); + nlBegin(NL_MATRIX); + } + + /* fill in right hand side and solve */ + for(z=0; zsize; z++) + for(y=0; ysize; y++) + for(x=0; xsize; 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; zsize; z++) + for(y=0; ysize; y++) + for(x=0; xsize; x++) + meshdeform_matrix_add_semibound_phi(mdb, x, y, z, a); + + for(z=0; zsize; z++) + for(y=0; ysize; y++) + for(x=0; xsize; x++) + meshdeform_matrix_add_exterior_phi(mdb, x, y, z, a); + + for(b=0; bsize3; 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; btotvert; 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; bsize3; 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; bsize3; 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; agridsize-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; abindcos= (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; anext) + 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; adyngrid[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; btotinfluence; 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; aobject->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 +#include +#include + +#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_*/ -- cgit v1.2.3