4887 lines
122 KiB
C
4887 lines
122 KiB
C
/**
|
|
* $Id$
|
|
*
|
|
* ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version. The Blender
|
|
* Foundation also sells licenses for use in proprietary software under
|
|
* the Blender License. See http://www.blender.org/BL/ for information
|
|
* about this.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
|
|
* All rights reserved.
|
|
*
|
|
* The Original Code is: all of this file.
|
|
*
|
|
* Contributor(s): none yet.
|
|
*
|
|
* ***** END GPL/BL DUAL LICENSE BLOCK *****
|
|
* editarmature.c: Interface for creating and posing armature objects
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BMF_Api.h"
|
|
|
|
#include "DNA_action_types.h"
|
|
#include "DNA_armature_types.h"
|
|
#include "DNA_constraint_types.h"
|
|
#include "DNA_ID.h"
|
|
#include "DNA_mesh_types.h"
|
|
#include "DNA_meshdata_types.h"
|
|
#include "DNA_nla_types.h"
|
|
#include "DNA_object_types.h"
|
|
#include "DNA_scene_types.h"
|
|
#include "DNA_screen_types.h"
|
|
#include "DNA_space_types.h"
|
|
#include "DNA_userdef_types.h"
|
|
#include "DNA_view3d_types.h"
|
|
#include "DNA_modifier_types.h"
|
|
|
|
#include "BLI_blenlib.h"
|
|
#include "BLI_arithb.h"
|
|
#include "BLI_editVert.h"
|
|
#include "BLI_ghash.h"
|
|
|
|
#include "BKE_action.h"
|
|
#include "BKE_armature.h"
|
|
#include "BKE_constraint.h"
|
|
#include "BKE_deform.h"
|
|
#include "BKE_depsgraph.h"
|
|
#include "BKE_DerivedMesh.h"
|
|
#include "BKE_global.h"
|
|
#include "BKE_main.h"
|
|
#include "BKE_object.h"
|
|
#include "BKE_subsurf.h"
|
|
#include "BKE_utildefines.h"
|
|
#include "BKE_modifier.h"
|
|
|
|
#include "BIF_editaction.h"
|
|
#include "BIF_editmode_undo.h"
|
|
#include "BIF_editdeform.h"
|
|
#include "BIF_editarmature.h"
|
|
#include "BIF_editconstraint.h"
|
|
#include "BIF_gl.h"
|
|
#include "BIF_graphics.h"
|
|
#include "BIF_interface.h"
|
|
#include "BIF_meshlaplacian.h"
|
|
#include "BIF_meshtools.h"
|
|
#include "BIF_poseobject.h"
|
|
#include "BIF_mywindow.h"
|
|
#include "BIF_resources.h"
|
|
#include "BIF_screen.h"
|
|
#include "BIF_space.h"
|
|
#include "BIF_toolbox.h"
|
|
#include "BIF_transform.h"
|
|
|
|
#include "BDR_editobject.h"
|
|
#include "BDR_drawobject.h"
|
|
|
|
#include "BSE_edit.h"
|
|
#include "BSE_view.h"
|
|
#include "BSE_trans_types.h"
|
|
|
|
#include "PIL_time.h"
|
|
|
|
#include "reeb.h" // FIX ME
|
|
|
|
#include "mydevice.h"
|
|
#include "blendef.h"
|
|
#include "nla.h"
|
|
|
|
extern float center[3], centroid[3]; /* Originally defined in editobject.c */
|
|
|
|
/* Macros */
|
|
#define TEST_EDITARMATURE {if(G.obedit==0) return; if( (G.vd->lay & G.obedit->lay)==0 ) return;}
|
|
|
|
/* prototypes for later */
|
|
static EditBone *editbone_name_exists (ListBase *ebones, char *name); // proto for below
|
|
|
|
/* **************** tools on Editmode Armature **************** */
|
|
|
|
/* converts Bones to EditBone list, used for tools as well */
|
|
void make_boneList(ListBase* list, ListBase *bones, EditBone *parent)
|
|
{
|
|
EditBone *eBone;
|
|
Bone *curBone;
|
|
float delta[3];
|
|
float premat[3][3];
|
|
float postmat[3][3];
|
|
float imat[3][3];
|
|
float difmat[3][3];
|
|
|
|
for (curBone=bones->first; curBone; curBone=curBone->next){
|
|
eBone= MEM_callocN(sizeof(EditBone), "make_editbone");
|
|
|
|
/* Copy relevant data from bone to eBone */
|
|
eBone->parent=parent;
|
|
BLI_strncpy (eBone->name, curBone->name, 32);
|
|
eBone->flag = curBone->flag;
|
|
|
|
/* fix selection flags */
|
|
if(eBone->flag & BONE_SELECTED) {
|
|
eBone->flag |= BONE_TIPSEL;
|
|
if(eBone->parent && (eBone->flag & BONE_CONNECTED))
|
|
eBone->parent->flag |= BONE_TIPSEL;
|
|
else
|
|
eBone->flag |= BONE_ROOTSEL;
|
|
}
|
|
else
|
|
eBone->flag &= ~BONE_ROOTSEL;
|
|
|
|
VECCOPY(eBone->head, curBone->arm_head);
|
|
VECCOPY(eBone->tail, curBone->arm_tail);
|
|
|
|
eBone->roll= 0.0;
|
|
|
|
/* roll fixing */
|
|
VecSubf (delta, eBone->tail, eBone->head);
|
|
vec_roll_to_mat3(delta, 0.0, postmat);
|
|
|
|
Mat3CpyMat4(premat, curBone->arm_mat);
|
|
|
|
Mat3Inv(imat, postmat);
|
|
Mat3MulMat3(difmat, imat, premat);
|
|
|
|
eBone->roll = atan2(difmat[2][0], difmat[2][2]);
|
|
|
|
/* rest of stuff copy */
|
|
eBone->length= curBone->length;
|
|
eBone->dist= curBone->dist;
|
|
eBone->weight= curBone->weight;
|
|
eBone->xwidth= curBone->xwidth;
|
|
eBone->zwidth= curBone->zwidth;
|
|
eBone->ease1= curBone->ease1;
|
|
eBone->ease2= curBone->ease2;
|
|
eBone->rad_head= curBone->rad_head;
|
|
eBone->rad_tail= curBone->rad_tail;
|
|
eBone->segments = curBone->segments;
|
|
eBone->layer = curBone->layer;
|
|
|
|
BLI_addtail (list, eBone);
|
|
|
|
/* Add children if necessary */
|
|
if (curBone->childbase.first)
|
|
make_boneList (list, &curBone->childbase, eBone);
|
|
}
|
|
}
|
|
|
|
/* nasty stuff for converting roll in editbones into bones */
|
|
/* also sets restposition in armature (arm_mat) */
|
|
static void fix_bonelist_roll (ListBase *bonelist, ListBase *editbonelist)
|
|
{
|
|
Bone *curBone;
|
|
EditBone *ebone;
|
|
float premat[3][3];
|
|
float postmat[3][3];
|
|
float difmat[3][3];
|
|
float imat[3][3];
|
|
float delta[3];
|
|
|
|
for (curBone=bonelist->first; curBone; curBone=curBone->next) {
|
|
/* sets local matrix and arm_mat (restpos) */
|
|
where_is_armature_bone(curBone, curBone->parent);
|
|
|
|
/* Find the associated editbone */
|
|
for (ebone = editbonelist->first; ebone; ebone=ebone->next)
|
|
if ((Bone*)ebone->temp == curBone)
|
|
break;
|
|
|
|
if (ebone) {
|
|
/* Get the ebone premat */
|
|
VecSubf (delta, ebone->tail, ebone->head);
|
|
vec_roll_to_mat3(delta, ebone->roll, premat);
|
|
|
|
/* Get the bone postmat */
|
|
Mat3CpyMat4(postmat, curBone->arm_mat);
|
|
|
|
Mat3Inv(imat, premat);
|
|
Mat3MulMat3(difmat, imat, postmat);
|
|
#if 0
|
|
printf ("Bone %s\n", curBone->name);
|
|
printmatrix4 ("premat", premat);
|
|
printmatrix4 ("postmat", postmat);
|
|
printmatrix4 ("difmat", difmat);
|
|
printf ("Roll = %f\n", (-atan2(difmat[2][0], difmat[2][2]) * (180.0/M_PI)));
|
|
#endif
|
|
curBone->roll = -atan2(difmat[2][0], difmat[2][2]);
|
|
|
|
/* and set restposition again */
|
|
where_is_armature_bone(curBone, curBone->parent);
|
|
}
|
|
fix_bonelist_roll (&curBone->childbase, editbonelist);
|
|
}
|
|
}
|
|
|
|
/* converts the editbones back to the armature */
|
|
void editbones_to_armature (ListBase *list, Object *ob)
|
|
{
|
|
bArmature *arm;
|
|
EditBone *eBone, *neBone;
|
|
Bone *newBone;
|
|
Object *obt;
|
|
|
|
arm = get_armature(ob);
|
|
if (!list) return;
|
|
if (!arm) return;
|
|
|
|
/* armature bones */
|
|
free_bones(arm);
|
|
|
|
/* remove zero sized bones, this gives instable restposes */
|
|
for (eBone=list->first; eBone; eBone= neBone) {
|
|
float len= VecLenf(eBone->head, eBone->tail);
|
|
neBone= eBone->next;
|
|
if (len <= FLT_EPSILON) {
|
|
EditBone *fBone;
|
|
|
|
/* Find any bones that refer to this bone */
|
|
for (fBone=list->first; fBone; fBone= fBone->next){
|
|
if (fBone->parent==eBone)
|
|
fBone->parent= eBone->parent;
|
|
}
|
|
printf("Warning: removed zero sized bone: %s\n", eBone->name);
|
|
BLI_freelinkN (list, eBone);
|
|
}
|
|
}
|
|
|
|
/* Copy the bones from the editData into the armature */
|
|
for (eBone=list->first; eBone; eBone=eBone->next) {
|
|
newBone= MEM_callocN (sizeof(Bone), "bone");
|
|
eBone->temp= newBone; /* Associate the real Bones with the EditBones */
|
|
|
|
BLI_strncpy (newBone->name, eBone->name, 32);
|
|
memcpy (newBone->head, eBone->head, sizeof(float)*3);
|
|
memcpy (newBone->tail, eBone->tail, sizeof(float)*3);
|
|
newBone->flag= eBone->flag;
|
|
if (eBone->flag & BONE_ACTIVE)
|
|
newBone->flag |= BONE_SELECTED; /* important, editbones can be active with only 1 point selected */
|
|
newBone->roll = 0.0f;
|
|
|
|
newBone->weight = eBone->weight;
|
|
newBone->dist = eBone->dist;
|
|
|
|
newBone->xwidth = eBone->xwidth;
|
|
newBone->zwidth = eBone->zwidth;
|
|
newBone->ease1= eBone->ease1;
|
|
newBone->ease2= eBone->ease2;
|
|
newBone->rad_head= eBone->rad_head;
|
|
newBone->rad_tail= eBone->rad_tail;
|
|
newBone->segments= eBone->segments;
|
|
newBone->layer = eBone->layer;
|
|
}
|
|
|
|
/* Fix parenting in a separate pass to ensure ebone->bone connections
|
|
are valid at this point */
|
|
for (eBone=list->first;eBone;eBone=eBone->next) {
|
|
newBone= (Bone *)eBone->temp;
|
|
if (eBone->parent) {
|
|
newBone->parent=(Bone *)eBone->parent->temp;
|
|
BLI_addtail(&newBone->parent->childbase,newBone);
|
|
|
|
{
|
|
float M_boneRest[3][3];
|
|
float M_parentRest[3][3];
|
|
float iM_parentRest[3][3];
|
|
float delta[3];
|
|
|
|
/* Get the parent's matrix (rotation only) */
|
|
VecSubf (delta, eBone->parent->tail, eBone->parent->head);
|
|
vec_roll_to_mat3(delta, eBone->parent->roll, M_parentRest);
|
|
|
|
/* Get this bone's matrix (rotation only) */
|
|
VecSubf (delta, eBone->tail, eBone->head);
|
|
vec_roll_to_mat3(delta, eBone->roll, M_boneRest);
|
|
|
|
/* Invert the parent matrix */
|
|
Mat3Inv(iM_parentRest, M_parentRest);
|
|
|
|
/* Get the new head and tail */
|
|
VecSubf (newBone->head, eBone->head, eBone->parent->tail);
|
|
VecSubf (newBone->tail, eBone->tail, eBone->parent->tail);
|
|
|
|
Mat3MulVecfl(iM_parentRest, newBone->head);
|
|
Mat3MulVecfl(iM_parentRest, newBone->tail);
|
|
}
|
|
}
|
|
/* ...otherwise add this bone to the armature's bonebase */
|
|
else
|
|
BLI_addtail(&arm->bonebase,newBone);
|
|
}
|
|
|
|
/* Make a pass through the new armature to fix rolling */
|
|
/* also builds restposition again (like where_is_armature) */
|
|
fix_bonelist_roll (&arm->bonebase, list);
|
|
|
|
/* so all users of this armature should get rebuilt */
|
|
for (obt= G.main->object.first; obt; obt= obt->id.next) {
|
|
if(obt->data==arm)
|
|
armature_rebuild_pose(obt, arm);
|
|
}
|
|
|
|
DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA);
|
|
}
|
|
|
|
|
|
|
|
void apply_rot_armature (Object *ob, float mat[3][3])
|
|
{
|
|
ListBase list;
|
|
EditBone *ebone;
|
|
bArmature *arm;
|
|
float scale = Mat3ToScalef(mat); /* store the scale of the matrix here to use on envelopes */
|
|
arm = get_armature(ob);
|
|
|
|
if (!arm)
|
|
return;
|
|
|
|
/* Put the armature into editmode */
|
|
list.first= list.last = NULL;
|
|
make_boneList(&list, &arm->bonebase, NULL);
|
|
|
|
/* Do the rotations */
|
|
for (ebone = list.first; ebone; ebone=ebone->next){
|
|
Mat3MulVecfl(mat, ebone->head);
|
|
Mat3MulVecfl(mat, ebone->tail);
|
|
|
|
ebone->rad_head *= scale;
|
|
ebone->rad_tail *= scale;
|
|
ebone->dist *= scale;
|
|
}
|
|
|
|
/* Turn the list into an armature */
|
|
editbones_to_armature(&list, ob);
|
|
|
|
/* Free the editbones */
|
|
if (list.first){
|
|
BLI_freelistN (&list);
|
|
}
|
|
}
|
|
|
|
/* 0 == do center, 1 == center new, 2 == center cursor */
|
|
void docenter_armature (Object *ob, int centermode)
|
|
{
|
|
ListBase list;
|
|
EditBone *ebone;
|
|
bArmature *arm;
|
|
float cent[3] = {0.0f, 0.0f, 0.0f};
|
|
float min[3], max[3];
|
|
float omat[3][3];
|
|
|
|
arm = get_armature(ob);
|
|
if (!arm) return;
|
|
|
|
/* Put the armature into editmode */
|
|
list.first= list.last = NULL;
|
|
make_boneList(&list, &arm->bonebase, NULL);
|
|
|
|
/* Find the centerpoint */
|
|
if (centermode == 2) {
|
|
VECCOPY(cent, give_cursor());
|
|
Mat4Invert(ob->imat, ob->obmat);
|
|
Mat4MulVecfl(ob->imat, cent);
|
|
}
|
|
else {
|
|
INIT_MINMAX(min, max);
|
|
|
|
for (ebone= list.first; ebone; ebone=ebone->next) {
|
|
DO_MINMAX(ebone->head, min, max);
|
|
DO_MINMAX(ebone->tail, min, max);
|
|
}
|
|
|
|
cent[0]= (min[0]+max[0])/2.0f;
|
|
cent[1]= (min[1]+max[1])/2.0f;
|
|
cent[2]= (min[2]+max[2])/2.0f;
|
|
}
|
|
|
|
/* Do the adjustments */
|
|
for (ebone= list.first; ebone; ebone=ebone->next){
|
|
VecSubf(ebone->head, ebone->head, cent);
|
|
VecSubf(ebone->tail, ebone->tail, cent);
|
|
}
|
|
|
|
/* Turn the list into an armature */
|
|
editbones_to_armature(&list, ob);
|
|
|
|
/* Free the editbones */
|
|
if (list.first){
|
|
BLI_freelistN(&list);
|
|
}
|
|
|
|
/* Adjust object location for new centerpoint */
|
|
if(centermode && G.obedit==0) {
|
|
Mat3CpyMat4(omat, ob->obmat);
|
|
|
|
Mat3MulVecfl(omat, cent);
|
|
ob->loc[0]+= cent[0];
|
|
ob->loc[1]+= cent[1];
|
|
ob->loc[2]+= cent[2];
|
|
}
|
|
}
|
|
|
|
/* Helper function for armature joining - link fixing */
|
|
static void joined_armature_fix_links(Object *tarArm, Object *srcArm, bPoseChannel *pchan, EditBone *curbone)
|
|
{
|
|
Object *ob;
|
|
bPose *pose;
|
|
bPoseChannel *pchant;
|
|
bConstraint *con;
|
|
|
|
/* let's go through all objects in database */
|
|
for (ob= G.main->object.first; ob; ob= ob->id.next) {
|
|
/* do some object-type specific things */
|
|
if (ob->type == OB_ARMATURE) {
|
|
pose= ob->pose;
|
|
for (pchant= pose->chanbase.first; pchant; pchant= pchant->next) {
|
|
for (con= pchant->constraints.first; con; con= con->next) {
|
|
bConstraintTypeInfo *cti= constraint_get_typeinfo(con);
|
|
ListBase targets = {NULL, NULL};
|
|
bConstraintTarget *ct;
|
|
|
|
/* constraint targets */
|
|
if (cti && cti->get_constraint_targets) {
|
|
cti->get_constraint_targets(con, &targets);
|
|
|
|
for (ct= targets.first; ct; ct= ct->next) {
|
|
if (ct->tar == srcArm) {
|
|
if (strcmp(ct->subtarget, "")==0) {
|
|
ct->tar = tarArm;
|
|
}
|
|
else if (strcmp(ct->subtarget, pchan->name)==0) {
|
|
ct->tar = tarArm;
|
|
strcpy(ct->subtarget, curbone->name);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (cti->flush_constraint_targets)
|
|
cti->flush_constraint_targets(con, &targets, 0);
|
|
}
|
|
|
|
/* action constraint? */
|
|
if (con->type == CONSTRAINT_TYPE_ACTION) {
|
|
bActionConstraint *data= con->data;
|
|
bAction *act;
|
|
bActionChannel *achan;
|
|
|
|
if (data->act) {
|
|
act= data->act;
|
|
|
|
for (achan= act->chanbase.first; achan; achan= achan->next) {
|
|
if (strcmp(achan->name, pchan->name)==0)
|
|
BLI_strncpy(achan->name, curbone->name, 32);
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
/* fix object-level constraints */
|
|
if (ob != srcArm) {
|
|
for (con= ob->constraints.first; con; con= con->next) {
|
|
bConstraintTypeInfo *cti= constraint_get_typeinfo(con);
|
|
ListBase targets = {NULL, NULL};
|
|
bConstraintTarget *ct;
|
|
|
|
/* constraint targets */
|
|
if (cti && cti->get_constraint_targets) {
|
|
cti->get_constraint_targets(con, &targets);
|
|
|
|
for (ct= targets.first; ct; ct= ct->next) {
|
|
if (ct->tar == srcArm) {
|
|
if (strcmp(ct->subtarget, "")==0) {
|
|
ct->tar = tarArm;
|
|
}
|
|
else if (strcmp(ct->subtarget, pchan->name)==0) {
|
|
ct->tar = tarArm;
|
|
strcpy(ct->subtarget, curbone->name);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (cti->flush_constraint_targets)
|
|
cti->flush_constraint_targets(con, &targets, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* See if an object is parented to this armature */
|
|
if (ob->parent && (ob->parent == srcArm)) {
|
|
/* Is object parented to a bone of this src armature? */
|
|
if (ob->partype==PARBONE) {
|
|
/* bone name in object */
|
|
if (!strcmp(ob->parsubstr, pchan->name))
|
|
BLI_strncpy(ob->parsubstr, curbone->name, 32);
|
|
}
|
|
|
|
/* make tar armature be new parent */
|
|
ob->parent = tarArm;
|
|
}
|
|
}
|
|
}
|
|
|
|
int join_armature(void)
|
|
{
|
|
Object *ob;
|
|
bArmature *arm;
|
|
Base *base, *nextbase;
|
|
bPose *pose, *opose;
|
|
bPoseChannel *pchan, *pchann;
|
|
ListBase ebbase, eblist;
|
|
EditBone *curbone;
|
|
float mat[4][4], imat[4][4];
|
|
|
|
/* Ensure we're not in editmode and that the active object is an armature*/
|
|
/* if(G.obedit) return; */ /* Alredy checked in join_menu() */
|
|
|
|
ob= OBACT;
|
|
if(ob->type!=OB_ARMATURE) return 0;
|
|
if (object_data_is_libdata(ob)) {
|
|
error_libdata();
|
|
return 0;
|
|
}
|
|
arm= get_armature(ob);
|
|
|
|
/* Get editbones of active armature to add editbones to */
|
|
ebbase.first=ebbase.last= NULL;
|
|
make_boneList(&ebbase, &arm->bonebase, NULL);
|
|
pose= ob->pose;
|
|
|
|
for (base=FIRSTBASE; base; base=nextbase) {
|
|
nextbase = base->next;
|
|
if (TESTBASE(base)){
|
|
if ((base->object->type==OB_ARMATURE) && (base->object!=ob)){
|
|
/* Make a list of editbones in current armature */
|
|
eblist.first=eblist.last= NULL;
|
|
make_boneList (&eblist, &((bArmature*)base->object->data)->bonebase,NULL);
|
|
|
|
/* Get Pose of current armature */
|
|
opose= base->object->pose;
|
|
|
|
/* Find the difference matrix */
|
|
Mat4Invert(imat, ob->obmat);
|
|
Mat4MulMat4(mat, base->object->obmat, imat);
|
|
|
|
/* Copy bones and posechannels from the object to the edit armature */
|
|
for (pchan=opose->chanbase.first; pchan; pchan=pchann) {
|
|
pchann= pchan->next;
|
|
curbone= editbone_name_exists(&eblist, pchan->name);
|
|
|
|
/* Get new name */
|
|
unique_editbone_name (&ebbase, curbone->name);
|
|
|
|
/* Transform the bone */
|
|
{
|
|
float premat[4][4];
|
|
float postmat[4][4];
|
|
float difmat[4][4];
|
|
float imat[4][4];
|
|
float temp[3][3];
|
|
float delta[3];
|
|
|
|
/* Get the premat */
|
|
VecSubf (delta, curbone->tail, curbone->head);
|
|
vec_roll_to_mat3(delta, curbone->roll, temp);
|
|
|
|
Mat4MulMat34 (premat, temp, mat);
|
|
|
|
Mat4MulVecfl(mat, curbone->head);
|
|
Mat4MulVecfl(mat, curbone->tail);
|
|
|
|
/* Get the postmat */
|
|
VecSubf (delta, curbone->tail, curbone->head);
|
|
vec_roll_to_mat3(delta, curbone->roll, temp);
|
|
Mat4CpyMat3(postmat, temp);
|
|
|
|
/* Find the roll */
|
|
Mat4Invert (imat, premat);
|
|
Mat4MulMat4 (difmat, postmat, imat);
|
|
|
|
curbone->roll -= atan2(difmat[2][0], difmat[2][2]);
|
|
|
|
}
|
|
|
|
/* Fix Constraints and Other Links to this Bone and Armature */
|
|
joined_armature_fix_links(ob, base->object, pchan, curbone);
|
|
|
|
/* Rename pchan */
|
|
sprintf(pchan->name, curbone->name);
|
|
|
|
/* Jump Ship! */
|
|
BLI_remlink(&eblist, curbone);
|
|
BLI_addtail(&ebbase, curbone);
|
|
|
|
BLI_remlink(&opose->chanbase, pchan);
|
|
BLI_addtail(&pose->chanbase, pchan);
|
|
}
|
|
|
|
free_and_unlink_base(base);
|
|
}
|
|
}
|
|
}
|
|
|
|
DAG_scene_sort(G.scene); // because we removed object(s)
|
|
|
|
editbones_to_armature(&ebbase, ob);
|
|
if (ebbase.first) BLI_freelistN(&ebbase);
|
|
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
allqueue(REDRAWOOPS, 0);
|
|
return 1;
|
|
}
|
|
|
|
/* **************** END tools on Editmode Armature **************** */
|
|
/* **************** PoseMode & EditMode *************************** */
|
|
|
|
/* only for opengl selection indices */
|
|
Bone *get_indexed_bone (Object *ob, int index)
|
|
{
|
|
bPoseChannel *pchan;
|
|
int a= 0;
|
|
|
|
if(ob->pose==NULL) return NULL;
|
|
index>>=16; // bone selection codes use left 2 bytes
|
|
|
|
for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next, a++) {
|
|
if(a==index) return pchan->bone;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* See if there are any selected bones in this buffer */
|
|
static void *get_bone_from_selectbuffer(Base *base, unsigned int *buffer, short hits, short findunsel)
|
|
{
|
|
Object *ob= base->object;
|
|
Bone *bone;
|
|
EditBone *ebone;
|
|
void *firstunSel=NULL, *firstSel=NULL, *data;
|
|
unsigned int hitresult;
|
|
short i, takeNext=0, sel;
|
|
|
|
for (i=0; i< hits; i++){
|
|
hitresult = buffer[3+(i*4)];
|
|
|
|
if (!(hitresult & BONESEL_NOSEL)) { // -1
|
|
if(hitresult & BONESEL_ANY) { // to avoid including objects in selection
|
|
|
|
hitresult &= ~(BONESEL_ANY);
|
|
/* Determine what the current bone is */
|
|
if (G.obedit==NULL || base->object!=G.obedit) {
|
|
/* no singular posemode, so check for correct object */
|
|
if(base->selcol == (hitresult & 0xFFFF)) {
|
|
bone = get_indexed_bone(ob, hitresult);
|
|
|
|
if (findunsel)
|
|
sel = (bone->flag & BONE_SELECTED);
|
|
else
|
|
sel = !(bone->flag & BONE_SELECTED);
|
|
|
|
data = bone;
|
|
}
|
|
else {
|
|
data= NULL;
|
|
sel= 0;
|
|
}
|
|
}
|
|
else{
|
|
ebone = BLI_findlink(&G.edbo, hitresult);
|
|
if (findunsel)
|
|
sel = (ebone->flag & BONE_SELECTED);
|
|
else
|
|
sel = !(ebone->flag & BONE_SELECTED);
|
|
|
|
data = ebone;
|
|
}
|
|
|
|
if(data) {
|
|
if (sel) {
|
|
if(!firstSel) firstSel= data;
|
|
takeNext=1;
|
|
}
|
|
else {
|
|
if (!firstunSel)
|
|
firstunSel=data;
|
|
if (takeNext)
|
|
return data;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (firstunSel)
|
|
return firstunSel;
|
|
else
|
|
return firstSel;
|
|
}
|
|
|
|
/* used by posemode as well editmode */
|
|
static void *get_nearest_bone (short findunsel)
|
|
{
|
|
unsigned int buffer[MAXPICKBUF];
|
|
short hits;
|
|
|
|
persp(PERSP_VIEW);
|
|
|
|
glInitNames();
|
|
hits= view3d_opengl_select(buffer, MAXPICKBUF, 0, 0, 0, 0);
|
|
|
|
if (hits>0)
|
|
return get_bone_from_selectbuffer(BASACT, buffer, hits, findunsel);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* used by posemode and editmode */
|
|
void select_bone_parent (void)
|
|
{
|
|
Object *ob;
|
|
bArmature *arm;
|
|
|
|
/* get data */
|
|
if (G.obedit)
|
|
ob= G.obedit;
|
|
else if (OBACT)
|
|
ob= OBACT;
|
|
else
|
|
return;
|
|
arm= (bArmature *)ob->data;
|
|
|
|
/* determine which mode armature is in */
|
|
if ((!G.obedit) && (ob->flag & OB_POSEMODE)) {
|
|
/* deal with pose channels */
|
|
/* channels are sorted on dependency, so the loop below won't result in a flood-select */
|
|
bPoseChannel *pchan=NULL;
|
|
|
|
for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
|
|
/* check if bone in original selection */
|
|
if (pchan->bone->flag & BONE_SELECTED) {
|
|
bPoseChannel *chanpar= pchan->parent;
|
|
|
|
/* check if any parent */
|
|
if ((chanpar) && ((chanpar->bone->flag & BONE_SELECTED)==0)) {
|
|
chanpar->bone->flag |= BONE_SELECTED;
|
|
select_actionchannel_by_name (ob->action, pchan->name, 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (G.obedit) {
|
|
/* deal with editbones */
|
|
EditBone *curbone, *parbone, *parpar;
|
|
|
|
/* prevent floods */
|
|
for (curbone= G.edbo.first; curbone; curbone= curbone->next)
|
|
curbone->temp= NULL;
|
|
|
|
for (curbone= G.edbo.first; curbone; curbone= curbone->next) {
|
|
/* check if bone selected */
|
|
if ((curbone->flag & BONE_SELECTED) && curbone->temp==NULL) {
|
|
parbone= curbone->parent;
|
|
|
|
/* check if any parent */
|
|
if ((parbone) && ((parbone->flag & BONE_SELECTED)==0)) {
|
|
/* select the parent bone */
|
|
parbone->flag |= (BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL);
|
|
|
|
/* check if parent has parent */
|
|
parpar= parbone->parent;
|
|
|
|
if ((parpar) && (parbone->flag & BONE_CONNECTED)) {
|
|
parpar->flag |= BONE_TIPSEL;
|
|
}
|
|
/* tag this bone to not flood selection */
|
|
parbone->temp= parbone;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* to be sure... */
|
|
for (curbone= G.edbo.first; curbone; curbone= curbone->next)
|
|
curbone->temp= NULL;
|
|
|
|
}
|
|
|
|
/* undo + redraw pushes */
|
|
countall(); // flushes selection!
|
|
|
|
allqueue (REDRAWVIEW3D, 0);
|
|
allqueue (REDRAWBUTSEDIT, 0);
|
|
allqueue(REDRAWBUTSOBJECT, 0);
|
|
allqueue(REDRAWOOPS, 0);
|
|
|
|
BIF_undo_push("Select Parent");
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* used by posemode and editmode */
|
|
void setflag_armature (short mode)
|
|
{
|
|
Object *ob;
|
|
bArmature *arm;
|
|
int flag;
|
|
|
|
/* get data */
|
|
if (G.obedit)
|
|
ob= G.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");
|
|
else if (mode == 1)
|
|
flag= pupmenu("Enable Setting%t|Draw Wire%x1|Deform%x2|Mult VG%x3|Hinge%x4|No Scale%x5");
|
|
else
|
|
flag= pupmenu("Toggle Setting%t|Draw Wire%x1|Deform%x2|Mult VG%x3|Hinge%x4|No Scale%x5");
|
|
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;
|
|
default: return;
|
|
}
|
|
|
|
/* determine which mode armature is in */
|
|
if ((!G.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 (G.obedit) {
|
|
/* deal with editbones */
|
|
EditBone *curbone;
|
|
|
|
/* set setting */
|
|
for (curbone= G.edbo.first; curbone; curbone= curbone->next) {
|
|
if (arm->layer & curbone->layer) {
|
|
if (curbone->flag & BONE_SELECTED) {
|
|
bone_setflag(&curbone->flag, flag, mode);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
allqueue(REDRAWBUTSEDIT, 0);
|
|
allqueue(REDRAWBUTSOBJECT, 0);
|
|
allqueue(REDRAWOOPS, 0);
|
|
|
|
BIF_undo_push("Change Bone Setting");
|
|
}
|
|
|
|
/* **************** END PoseMode & EditMode *************************** */
|
|
/* **************** Posemode stuff ********************** */
|
|
|
|
|
|
static void selectconnected_posebonechildren (Object *ob, Bone *bone)
|
|
{
|
|
Bone *curBone;
|
|
|
|
if (!(bone->flag & BONE_CONNECTED))
|
|
return;
|
|
|
|
select_actionchannel_by_name (ob->action, bone->name, !(G.qual & LR_SHIFTKEY));
|
|
|
|
if (G.qual & LR_SHIFTKEY)
|
|
bone->flag &= ~BONE_SELECTED;
|
|
else
|
|
bone->flag |= BONE_SELECTED;
|
|
|
|
for (curBone=bone->childbase.first; curBone; curBone=curBone->next){
|
|
selectconnected_posebonechildren (ob, curBone);
|
|
}
|
|
}
|
|
|
|
/* within active object context */
|
|
void selectconnected_posearmature(void)
|
|
{
|
|
Bone *bone, *curBone, *next;
|
|
Object *ob= OBACT;
|
|
|
|
if(!ob || !ob->pose) return;
|
|
|
|
if (G.qual & LR_SHIFTKEY)
|
|
bone= get_nearest_bone(0);
|
|
else
|
|
bone = get_nearest_bone(1);
|
|
|
|
if (!bone)
|
|
return;
|
|
|
|
/* Select parents */
|
|
for (curBone=bone; curBone; curBone=next){
|
|
select_actionchannel_by_name (ob->action, curBone->name, !(G.qual & LR_SHIFTKEY));
|
|
if (G.qual & LR_SHIFTKEY)
|
|
curBone->flag &= ~BONE_SELECTED;
|
|
else
|
|
curBone->flag |= BONE_SELECTED;
|
|
|
|
if (curBone->flag & BONE_CONNECTED)
|
|
next=curBone->parent;
|
|
else
|
|
next=NULL;
|
|
}
|
|
|
|
/* Select children */
|
|
for (curBone=bone->childbase.first; curBone; curBone=next){
|
|
selectconnected_posebonechildren (ob, curBone);
|
|
}
|
|
|
|
countall(); // flushes selection!
|
|
|
|
allqueue (REDRAWVIEW3D, 0);
|
|
allqueue(REDRAWBUTSEDIT, 0);
|
|
allqueue(REDRAWBUTSOBJECT, 0);
|
|
allqueue (REDRAWACTION, 0);
|
|
allqueue(REDRAWOOPS, 0);
|
|
BIF_undo_push("Select connected");
|
|
|
|
}
|
|
|
|
/* **************** END Posemode stuff ********************** */
|
|
/* **************** EditMode stuff ********************** */
|
|
|
|
/* called in space.c */
|
|
void selectconnected_armature(void)
|
|
{
|
|
EditBone *bone, *curBone, *next;
|
|
|
|
if (G.qual & LR_SHIFTKEY)
|
|
bone= get_nearest_bone(0);
|
|
else
|
|
bone= get_nearest_bone(1);
|
|
|
|
if (!bone)
|
|
return;
|
|
|
|
/* Select parents */
|
|
for (curBone=bone; curBone; curBone=next){
|
|
if (G.qual & LR_SHIFTKEY){
|
|
curBone->flag &= ~(BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL);
|
|
}
|
|
else{
|
|
curBone->flag |= (BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL);
|
|
}
|
|
|
|
if (curBone->flag & BONE_CONNECTED)
|
|
next=curBone->parent;
|
|
else
|
|
next=NULL;
|
|
}
|
|
|
|
/* Select children */
|
|
while (bone){
|
|
for (curBone=G.edbo.first; curBone; curBone=next){
|
|
next = curBone->next;
|
|
if (curBone->parent == bone){
|
|
if (curBone->flag & BONE_CONNECTED){
|
|
if (G.qual & LR_SHIFTKEY)
|
|
curBone->flag &= ~(BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL);
|
|
else
|
|
curBone->flag |= (BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL);
|
|
bone=curBone;
|
|
break;
|
|
}
|
|
else{
|
|
bone=NULL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!curBone)
|
|
bone=NULL;
|
|
|
|
}
|
|
|
|
countall(); // flushes selection!
|
|
|
|
allqueue (REDRAWVIEW3D, 0);
|
|
allqueue (REDRAWBUTSEDIT, 0);
|
|
allqueue(REDRAWBUTSOBJECT, 0);
|
|
allqueue(REDRAWOOPS, 0);
|
|
|
|
BIF_undo_push("Select connected");
|
|
|
|
}
|
|
|
|
/* does bones and points */
|
|
/* note that BONE ROOT only gets drawn for root bones (or without IK) */
|
|
static EditBone * get_nearest_editbonepoint (int findunsel, int *selmask)
|
|
{
|
|
EditBone *ebone;
|
|
unsigned int buffer[MAXPICKBUF];
|
|
unsigned int hitresult, besthitresult=BONESEL_NOSEL;
|
|
int i, mindep= 4;
|
|
short hits, mval[2];
|
|
|
|
persp(PERSP_VIEW);
|
|
|
|
glInitNames();
|
|
|
|
getmouseco_areawin(mval);
|
|
hits= view3d_opengl_select(buffer, MAXPICKBUF, mval[0]-5, mval[1]-5, mval[0]+5, mval[1]+5);
|
|
if(hits==0)
|
|
hits= view3d_opengl_select(buffer, MAXPICKBUF, mval[0]-12, mval[1]-12, mval[0]+12, mval[1]+12);
|
|
|
|
/* See if there are any selected bones in this group */
|
|
if (hits>0) {
|
|
|
|
if(hits==1) {
|
|
if (!(buffer[3] & BONESEL_NOSEL))
|
|
besthitresult= buffer[3];
|
|
}
|
|
else {
|
|
for (i=0; i< hits; i++) {
|
|
hitresult= buffer[3+(i*4)];
|
|
if (!(hitresult & BONESEL_NOSEL)) {
|
|
int dep;
|
|
|
|
ebone = BLI_findlink(&G.edbo, hitresult & ~BONESEL_ANY);
|
|
|
|
/* clicks on bone points get advantage */
|
|
if( hitresult & (BONESEL_ROOT|BONESEL_TIP)) {
|
|
/* but also the unselected one */
|
|
if(findunsel) {
|
|
if( (hitresult & BONESEL_ROOT) && (ebone->flag & BONE_ROOTSEL)==0)
|
|
dep= 1;
|
|
else if( (hitresult & BONESEL_TIP) && (ebone->flag & BONE_TIPSEL)==0)
|
|
dep= 1;
|
|
else
|
|
dep= 2;
|
|
}
|
|
else dep= 2;
|
|
}
|
|
else {
|
|
/* bone found */
|
|
if(findunsel) {
|
|
if((ebone->flag & BONE_SELECTED)==0)
|
|
dep= 2;
|
|
else
|
|
dep= 3;
|
|
}
|
|
else dep= 3;
|
|
}
|
|
if(dep < mindep) {
|
|
mindep= dep;
|
|
besthitresult= hitresult;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!(besthitresult & BONESEL_NOSEL)) {
|
|
|
|
ebone= BLI_findlink(&G.edbo, besthitresult & ~BONESEL_ANY);
|
|
|
|
*selmask = 0;
|
|
if (besthitresult & BONESEL_ROOT)
|
|
*selmask |= BONE_ROOTSEL;
|
|
if (besthitresult & BONESEL_TIP)
|
|
*selmask |= BONE_TIPSEL;
|
|
if (besthitresult & BONESEL_BONE)
|
|
*selmask |= BONE_SELECTED;
|
|
return ebone;
|
|
}
|
|
}
|
|
*selmask = 0;
|
|
return NULL;
|
|
}
|
|
|
|
static void delete_bone(EditBone* exBone)
|
|
{
|
|
EditBone *curBone;
|
|
|
|
/* Find any bones that refer to this bone */
|
|
for (curBone=G.edbo.first;curBone;curBone=curBone->next){
|
|
if (curBone->parent==exBone){
|
|
curBone->parent=exBone->parent;
|
|
curBone->flag &= ~BONE_CONNECTED;
|
|
}
|
|
}
|
|
|
|
BLI_freelinkN (&G.edbo,exBone);
|
|
}
|
|
|
|
/* only editmode! */
|
|
void delete_armature(void)
|
|
{
|
|
bArmature *arm= G.obedit->data;
|
|
EditBone *curBone, *next;
|
|
bConstraint *con;
|
|
|
|
TEST_EDITARMATURE;
|
|
if (okee("Erase selected bone(s)")==0) return;
|
|
|
|
/* Select mirrored bones */
|
|
if (arm->flag & ARM_MIRROR_EDIT) {
|
|
for (curBone=G.edbo.first; curBone; curBone=curBone->next) {
|
|
if (arm->layer & curBone->layer) {
|
|
if (curBone->flag & BONE_SELECTED) {
|
|
next = armature_bone_get_mirrored(curBone);
|
|
if (next)
|
|
next->flag |= BONE_SELECTED;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* First erase any associated pose channel */
|
|
if (G.obedit->pose) {
|
|
bPoseChannel *chan, *next;
|
|
for (chan=G.obedit->pose->chanbase.first; chan; chan=next) {
|
|
next= chan->next;
|
|
curBone = editbone_name_exists (&G.edbo, chan->name);
|
|
|
|
if (curBone && (curBone->flag & BONE_SELECTED) && (arm->layer & curBone->layer)) {
|
|
free_constraints(&chan->constraints);
|
|
BLI_freelinkN (&G.obedit->pose->chanbase, chan);
|
|
}
|
|
else {
|
|
for (con= chan->constraints.first; con; con= con->next) {
|
|
bConstraintTypeInfo *cti= constraint_get_typeinfo(con);
|
|
ListBase targets = {NULL, NULL};
|
|
bConstraintTarget *ct;
|
|
|
|
if (cti && cti->get_constraint_targets) {
|
|
cti->get_constraint_targets(con, &targets);
|
|
|
|
for (ct= targets.first; ct; ct= ct->next) {
|
|
if (ct->tar == G.obedit) {
|
|
if (ct->subtarget[0]) {
|
|
curBone = editbone_name_exists(&G.edbo, ct->subtarget);
|
|
if (curBone && (curBone->flag & BONE_SELECTED) && (arm->layer & curBone->layer)) {
|
|
con->flag |= CONSTRAINT_DISABLE;
|
|
ct->subtarget[0]= 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (cti->flush_constraint_targets)
|
|
cti->flush_constraint_targets(con, &targets, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
for (curBone=G.edbo.first;curBone;curBone=next) {
|
|
next=curBone->next;
|
|
if (arm->layer & curBone->layer)
|
|
if (curBone->flag & BONE_SELECTED)
|
|
delete_bone(curBone);
|
|
}
|
|
|
|
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
allqueue(REDRAWBUTSEDIT, 0);
|
|
allqueue(REDRAWBUTSOBJECT, 0);
|
|
allqueue(REDRAWOOPS, 0);
|
|
countall(); // flushes selection!
|
|
|
|
BIF_undo_push("Delete bone(s)");
|
|
}
|
|
|
|
/* context: editmode armature */
|
|
void mouse_armature(void)
|
|
{
|
|
EditBone *nearBone = NULL, *ebone;
|
|
int selmask;
|
|
|
|
nearBone= get_nearest_editbonepoint(1, &selmask);
|
|
if (nearBone) {
|
|
|
|
if (!(G.qual & LR_SHIFTKEY)) {
|
|
deselectall_armature(0, 0);
|
|
}
|
|
|
|
/* by definition the non-root connected bones have no root point drawn,
|
|
so a root selection needs to be delivered to the parent tip,
|
|
countall() (bad location) flushes these flags */
|
|
|
|
if(selmask & BONE_SELECTED) {
|
|
if(nearBone->parent && (nearBone->flag & BONE_CONNECTED)) {
|
|
/* click in a chain */
|
|
if(G.qual & LR_SHIFTKEY) {
|
|
/* hold shift inverts this bone's selection */
|
|
if(nearBone->flag & BONE_SELECTED) {
|
|
/* deselect this bone */
|
|
nearBone->flag &= ~(BONE_TIPSEL|BONE_SELECTED);
|
|
/* only deselect parent tip if it is not selected */
|
|
if(!(nearBone->parent->flag & BONE_SELECTED))
|
|
nearBone->parent->flag &= ~BONE_TIPSEL;
|
|
}
|
|
else {
|
|
/* select this bone */
|
|
nearBone->flag |= BONE_TIPSEL;
|
|
nearBone->parent->flag |= BONE_TIPSEL;
|
|
}
|
|
}
|
|
else {
|
|
/* select this bone */
|
|
nearBone->flag |= BONE_TIPSEL;
|
|
nearBone->parent->flag |= BONE_TIPSEL;
|
|
}
|
|
}
|
|
else {
|
|
if(G.qual & LR_SHIFTKEY) {
|
|
/* hold shift inverts this bone's selection */
|
|
if(nearBone->flag & BONE_SELECTED)
|
|
nearBone->flag &= ~(BONE_TIPSEL|BONE_ROOTSEL);
|
|
else
|
|
nearBone->flag |= (BONE_TIPSEL|BONE_ROOTSEL);
|
|
}
|
|
else nearBone->flag |= (BONE_TIPSEL|BONE_ROOTSEL);
|
|
}
|
|
}
|
|
else {
|
|
if ((G.qual & LR_SHIFTKEY) && (nearBone->flag & selmask))
|
|
nearBone->flag &= ~selmask;
|
|
else
|
|
nearBone->flag |= selmask;
|
|
}
|
|
|
|
countall(); // flushes selection!
|
|
|
|
if(nearBone) {
|
|
/* then now check for active status */
|
|
for (ebone=G.edbo.first;ebone;ebone=ebone->next) ebone->flag &= ~BONE_ACTIVE;
|
|
if(nearBone->flag & BONE_SELECTED) nearBone->flag |= BONE_ACTIVE;
|
|
}
|
|
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
allqueue(REDRAWBUTSEDIT, 0);
|
|
allqueue(REDRAWBUTSOBJECT, 0);
|
|
allqueue(REDRAWOOPS, 0);
|
|
}
|
|
|
|
rightmouse_transform();
|
|
}
|
|
|
|
void free_editArmature(void)
|
|
{
|
|
|
|
/* Clear the editbones list */
|
|
if (G.edbo.first){
|
|
BLI_freelistN (&G.edbo);
|
|
}
|
|
}
|
|
|
|
void remake_editArmature(void)
|
|
{
|
|
if(okee("Reload original data")==0) return;
|
|
|
|
make_editArmature();
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
allqueue(REDRAWOOPS, 0);
|
|
allqueue(REDRAWBUTSHEAD, 0);
|
|
allqueue(REDRAWBUTSEDIT, 0);
|
|
allqueue(REDRAWBUTSOBJECT, 0);
|
|
|
|
// BIF_undo_push("Delete bone");
|
|
|
|
}
|
|
|
|
/* Put object in EditMode */
|
|
void make_editArmature(void)
|
|
{
|
|
bArmature *arm;
|
|
|
|
if (G.obedit==0) return;
|
|
|
|
free_editArmature();
|
|
|
|
arm= get_armature(G.obedit);
|
|
if (!arm)
|
|
return;
|
|
|
|
make_boneList (&G.edbo, &arm->bonebase,NULL);
|
|
}
|
|
|
|
/* put EditMode back in Object */
|
|
void load_editArmature(void)
|
|
{
|
|
bArmature *arm;
|
|
|
|
arm= get_armature(G.obedit);
|
|
if (!arm) return;
|
|
|
|
editbones_to_armature(&G.edbo, G.obedit);
|
|
}
|
|
|
|
/* toggle==0: deselect
|
|
toggle==1: swap
|
|
toggle==2: only active tag
|
|
*/
|
|
void deselectall_armature(int toggle, int doundo)
|
|
{
|
|
bArmature *arm= G.obedit->data;
|
|
EditBone *eBone;
|
|
int sel=1;
|
|
|
|
if(toggle==1) {
|
|
/* Determine if there are any selected bones
|
|
And therefore whether we are selecting or deselecting */
|
|
for (eBone=G.edbo.first;eBone;eBone=eBone->next){
|
|
// if(arm->layer & eBone->layer) {
|
|
if (eBone->flag & (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL)){
|
|
sel=0;
|
|
break;
|
|
}
|
|
// }
|
|
}
|
|
}
|
|
else sel= toggle;
|
|
|
|
/* Set the flags */
|
|
for (eBone=G.edbo.first;eBone;eBone=eBone->next){
|
|
if (sel==1) {
|
|
if(arm->layer & eBone->layer && (eBone->flag & BONE_HIDDEN_A)==0) {
|
|
eBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
|
|
if(eBone->parent)
|
|
eBone->parent->flag |= (BONE_TIPSEL);
|
|
}
|
|
}
|
|
else if (sel==2)
|
|
eBone->flag &= ~(BONE_ACTIVE);
|
|
else
|
|
eBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL | BONE_ACTIVE);
|
|
}
|
|
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
allqueue(REDRAWBUTSEDIT, 0);
|
|
allqueue(REDRAWBUTSOBJECT, 0);
|
|
allqueue(REDRAWOOPS, 0);
|
|
|
|
countall(); // flushes selection!
|
|
if (doundo) {
|
|
if (sel==1) BIF_undo_push("Select All");
|
|
else BIF_undo_push("Deselect All");
|
|
}
|
|
}
|
|
|
|
/* Sets the roll value of selected bones, depending on the mode
|
|
* mode == 0: their z-axes point upwards
|
|
* mode == 1: their z-axes point towards 3d-cursor
|
|
*/
|
|
void auto_align_armature(short mode)
|
|
{
|
|
bArmature *arm= G.obedit->data;
|
|
EditBone *ebone;
|
|
EditBone *flipbone = NULL;
|
|
float delta[3];
|
|
float curmat[3][3];
|
|
float *cursor= give_cursor();
|
|
|
|
for (ebone = G.edbo.first; ebone; ebone=ebone->next) {
|
|
if (arm->layer & ebone->layer) {
|
|
if (arm->flag & ARM_MIRROR_EDIT)
|
|
flipbone = armature_bone_get_mirrored(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, G.obedit->obmat);
|
|
Mat4Invert(imat, tmat);
|
|
|
|
/* find position of cursor relative to bone */
|
|
VecMat4MulVecfl(vec, imat, cursor);
|
|
|
|
/* check that cursor is in usable position */
|
|
if ((IS_EQ(vec[0], 0)==0) && (IS_EQ(vec[2], 0)==0)) {
|
|
/* Compute a rotation matrix around y */
|
|
rot[1] = atan2(vec[0], vec[2]);
|
|
rot[0] = rot[2] = 0.0f;
|
|
EulToMat4(rot, rmat);
|
|
|
|
/* Multiply the bone matrix by rotation matrix. This should be new bone-matrix */
|
|
Mat4MulMat4(tmat, rmat, mat);
|
|
Mat3CpyMat4(curmat, tmat);
|
|
|
|
/* Now convert from new bone-matrix, back to a roll value (in radians) */
|
|
mat3_to_vec_roll(curmat, delta, &ebone->roll);
|
|
}
|
|
}
|
|
else {
|
|
/* Z-Axis Point Up */
|
|
float xaxis[3]={1.0, 0.0, 0.0}, yaxis[3], zaxis[3]={0.0, 0.0, 1.0};
|
|
float targetmat[3][3], imat[3][3], diffmat[3][3];
|
|
|
|
/* Find the current bone matrix */
|
|
VecSubf(delta, ebone->tail, ebone->head);
|
|
vec_roll_to_mat3(delta, 0.0, curmat);
|
|
|
|
/* Make new matrix based on y axis & z-up */
|
|
VECCOPY (yaxis, curmat[1]);
|
|
|
|
Mat3One(targetmat);
|
|
VECCOPY (targetmat[0], xaxis);
|
|
VECCOPY (targetmat[1], yaxis);
|
|
VECCOPY (targetmat[2], zaxis);
|
|
Mat3Ortho(targetmat);
|
|
|
|
/* Find the difference between the two matrices */
|
|
Mat3Inv(imat, targetmat);
|
|
Mat3MulMat3(diffmat, imat, curmat);
|
|
|
|
ebone->roll = atan2(diffmat[2][0], diffmat[2][2]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* **************** undo for armatures ************** */
|
|
|
|
static void undoBones_to_editBones(void *lbv)
|
|
{
|
|
ListBase *lb= lbv;
|
|
EditBone *ebo, *newebo;
|
|
|
|
BLI_freelistN(&G.edbo);
|
|
|
|
/* copy */
|
|
for(ebo= lb->first; ebo; ebo= ebo->next) {
|
|
newebo= MEM_dupallocN(ebo);
|
|
ebo->temp= newebo;
|
|
BLI_addtail(&G.edbo, newebo);
|
|
}
|
|
|
|
/* set pointers */
|
|
for(newebo= G.edbo.first; newebo; newebo= newebo->next) {
|
|
if(newebo->parent) newebo->parent= newebo->parent->temp;
|
|
}
|
|
/* be sure they dont hang ever */
|
|
for(newebo= G.edbo.first; newebo; newebo= newebo->next) {
|
|
newebo->temp= NULL;
|
|
}
|
|
}
|
|
|
|
static void *editBones_to_undoBones(void)
|
|
{
|
|
ListBase *lb;
|
|
EditBone *ebo, *newebo;
|
|
|
|
lb= MEM_callocN(sizeof(ListBase), "listbase undo");
|
|
|
|
/* copy */
|
|
for(ebo= G.edbo.first; ebo; ebo= ebo->next) {
|
|
newebo= MEM_dupallocN(ebo);
|
|
ebo->temp= newebo;
|
|
BLI_addtail(lb, newebo);
|
|
}
|
|
|
|
/* set pointers */
|
|
for(newebo= lb->first; newebo; newebo= newebo->next) {
|
|
if(newebo->parent) newebo->parent= newebo->parent->temp;
|
|
}
|
|
|
|
return lb;
|
|
}
|
|
|
|
static void free_undoBones(void *lbv)
|
|
{
|
|
ListBase *lb= lbv;
|
|
|
|
BLI_freelistN(lb);
|
|
MEM_freeN(lb);
|
|
}
|
|
|
|
/* and this is all the undo system needs to know */
|
|
void undo_push_armature(char *name)
|
|
{
|
|
undo_editmode_push(name, free_undoBones, undoBones_to_editBones, editBones_to_undoBones, NULL);
|
|
}
|
|
|
|
|
|
|
|
/* **************** END EditMode stuff ********************** */
|
|
/* *************** Adding stuff in editmode *************** */
|
|
|
|
/* default bone add, returns it selected, but without tail set */
|
|
static EditBone *add_editbone(char *name)
|
|
{
|
|
bArmature *arm= G.obedit->data;
|
|
|
|
EditBone *bone= MEM_callocN(sizeof(EditBone), "eBone");
|
|
|
|
BLI_strncpy(bone->name, name, 32);
|
|
unique_editbone_name(&G.edbo, bone->name);
|
|
|
|
BLI_addtail(&G.edbo, bone);
|
|
|
|
bone->flag |= BONE_TIPSEL;
|
|
bone->weight= 1.0F;
|
|
bone->dist= 0.25F;
|
|
bone->xwidth= 0.1;
|
|
bone->zwidth= 0.1;
|
|
bone->ease1= 1.0;
|
|
bone->ease2= 1.0;
|
|
bone->rad_head= 0.10;
|
|
bone->rad_tail= 0.05;
|
|
bone->segments= 1;
|
|
bone->layer= arm->layer;
|
|
|
|
return bone;
|
|
}
|
|
|
|
static void add_primitive_bone(Object *ob, short newob)
|
|
{
|
|
float obmat[3][3], curs[3], viewmat[3][3], totmat[3][3], imat[3][3];
|
|
EditBone *bone;
|
|
|
|
VECCOPY (curs, give_cursor());
|
|
|
|
/* Get inverse point for head and orientation for tail */
|
|
Mat4Invert(G.obedit->imat, G.obedit->obmat);
|
|
Mat4MulVecfl(G.obedit->imat, curs);
|
|
|
|
if ( !(newob) || U.flag & USER_ADD_VIEWALIGNED) Mat3CpyMat4(obmat, G.vd->viewmat);
|
|
else Mat3One(obmat);
|
|
|
|
Mat3CpyMat4(viewmat, G.obedit->obmat);
|
|
Mat3MulMat3(totmat, obmat, viewmat);
|
|
Mat3Inv(imat, totmat);
|
|
|
|
deselectall_armature(0, 0);
|
|
|
|
/* Create a bone */
|
|
bone= add_editbone("Bone");
|
|
|
|
VECCOPY(bone->head, curs);
|
|
|
|
if ( !(newob) || U.flag & USER_ADD_VIEWALIGNED)
|
|
VecAddf(bone->tail, bone->head, imat[1]); // bone with unit length 1
|
|
else
|
|
VecAddf(bone->tail, bone->head, imat[2]); // bone with unit length 1, pointing up Z
|
|
|
|
}
|
|
|
|
void add_primitiveArmature(int type)
|
|
{
|
|
short newob=0;
|
|
|
|
if(G.scene->id.lib) return;
|
|
|
|
/* this function also comes from an info window */
|
|
if ELEM(curarea->spacetype, SPACE_VIEW3D, SPACE_INFO); else return;
|
|
if(G.vd==NULL) return;
|
|
|
|
G.f &= ~(G_VERTEXPAINT+G_TEXTUREPAINT+G_WEIGHTPAINT);
|
|
setcursor_space(SPACE_VIEW3D, CURSOR_STD);
|
|
|
|
check_editmode(OB_ARMATURE);
|
|
|
|
/* If we're not the "obedit", make a new object and enter editmode */
|
|
if(G.obedit==NULL) {
|
|
add_object(OB_ARMATURE);
|
|
base_init_from_view3d(BASACT, G.vd);
|
|
G.obedit= BASACT->object;
|
|
|
|
where_is_object(G.obedit);
|
|
|
|
make_editArmature();
|
|
setcursor_space(SPACE_VIEW3D, CURSOR_EDIT);
|
|
newob=1;
|
|
}
|
|
|
|
/* no primitive support yet */
|
|
add_primitive_bone(G.obedit, newob);
|
|
|
|
countall(); // flushes selection!
|
|
|
|
if ( (newob) && !(U.flag & USER_ADD_EDITMODE)) {
|
|
exit_editmode(2);
|
|
}
|
|
|
|
allqueue(REDRAWALL, 0);
|
|
BIF_undo_push("Add primitive");
|
|
}
|
|
|
|
/* the ctrl-click method */
|
|
void addvert_armature(void)
|
|
{
|
|
bArmature *arm= G.obedit->data;
|
|
EditBone *ebone, *newbone, *flipbone;
|
|
float *curs, mat[3][3],imat[3][3];
|
|
int a, to_root= 0;
|
|
|
|
TEST_EDITARMATURE;
|
|
|
|
/* find the active or selected bone */
|
|
for (ebone = G.edbo.first; ebone; ebone=ebone->next)
|
|
if(arm->layer & ebone->layer)
|
|
if(ebone->flag & (BONE_ACTIVE|BONE_TIPSEL)) break;
|
|
|
|
if(ebone==NULL) {
|
|
for (ebone = G.edbo.first; ebone; ebone=ebone->next)
|
|
if(arm->layer & ebone->layer)
|
|
if(ebone->flag & (BONE_ACTIVE|BONE_ROOTSEL)) break;
|
|
|
|
if(ebone==NULL)
|
|
return;
|
|
to_root= 1;
|
|
}
|
|
|
|
deselectall_armature(0, 0);
|
|
|
|
/* we re-use code for mirror editing... */
|
|
flipbone= NULL;
|
|
if(arm->flag & ARM_MIRROR_EDIT)
|
|
flipbone= armature_bone_get_mirrored(ebone);
|
|
|
|
for(a=0; a<2; a++) {
|
|
if(a==1) {
|
|
if(flipbone==NULL)
|
|
break;
|
|
else {
|
|
SWAP(EditBone *, flipbone, ebone);
|
|
}
|
|
}
|
|
|
|
newbone= add_editbone(ebone->name);
|
|
newbone->flag |= BONE_ACTIVE;
|
|
|
|
if(to_root) {
|
|
VECCOPY(newbone->head, ebone->head);
|
|
newbone->rad_head= ebone->rad_tail;
|
|
newbone->parent= ebone->parent;
|
|
}
|
|
else {
|
|
VECCOPY(newbone->head, ebone->tail);
|
|
newbone->rad_head= ebone->rad_tail;
|
|
newbone->parent= ebone;
|
|
newbone->flag |= BONE_CONNECTED;
|
|
}
|
|
|
|
curs= give_cursor();
|
|
VECCOPY(newbone->tail, curs);
|
|
VecSubf(newbone->tail, newbone->tail, G.obedit->obmat[3]);
|
|
|
|
if(a==1)
|
|
newbone->tail[0]= -newbone->tail[0];
|
|
|
|
Mat3CpyMat4(mat, G.obedit->obmat);
|
|
Mat3Inv(imat, mat);
|
|
Mat3MulVecfl(imat, newbone->tail);
|
|
|
|
newbone->length= VecLenf(newbone->head, newbone->tail);
|
|
newbone->rad_tail= newbone->length*0.05f;
|
|
newbone->dist= newbone->length*0.25f;
|
|
|
|
}
|
|
|
|
|
|
countall();
|
|
|
|
BIF_undo_push("Add Bone");
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
|
|
while(get_mbut()&R_MOUSE);
|
|
}
|
|
|
|
/* adds an EditBone between the nominated locations (should be in the right space) */
|
|
static EditBone *add_points_bone (float head[], float tail[])
|
|
{
|
|
EditBone *ebo;
|
|
|
|
ebo= add_editbone("Bone");
|
|
|
|
VECCOPY(ebo->head, head);
|
|
VECCOPY(ebo->tail, tail);
|
|
|
|
return ebo;
|
|
}
|
|
|
|
|
|
static EditBone *get_named_editbone(char *name)
|
|
{
|
|
EditBone *eBone;
|
|
|
|
if (name)
|
|
for (eBone=G.edbo.first; eBone; eBone=eBone->next){
|
|
if (!strcmp (name, eBone->name))
|
|
return eBone;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void update_dup_subtarget(EditBone *dupBone)
|
|
{
|
|
/* If an edit bone has been duplicated, lets
|
|
* update it's constraints if the subtarget
|
|
* they point to has also been duplicated
|
|
*/
|
|
EditBone *oldtarget, *newtarget;
|
|
bPoseChannel *chan;
|
|
bConstraint *curcon;
|
|
ListBase *conlist;
|
|
|
|
|
|
if ( (chan = verify_pose_channel(OBACT->pose, dupBone->name)) ) {
|
|
if ( (conlist = &chan->constraints) ) {
|
|
for (curcon = conlist->first; curcon; curcon=curcon->next) {
|
|
/* does this constraint have a subtarget in
|
|
* this armature?
|
|
*/
|
|
bConstraintTypeInfo *cti= constraint_get_typeinfo(curcon);
|
|
ListBase targets = {NULL, NULL};
|
|
bConstraintTarget *ct;
|
|
|
|
if (cti && cti->get_constraint_targets) {
|
|
cti->get_constraint_targets(curcon, &targets);
|
|
|
|
for (ct= targets.first; ct; ct= ct->next) {
|
|
if ((ct->tar == G.obedit) && (ct->subtarget[0])) {
|
|
oldtarget = get_named_editbone(ct->subtarget);
|
|
if (oldtarget) {
|
|
/* was the subtarget bone duplicated too? If
|
|
* so, update the constraint to point at the
|
|
* duplicate of the old subtarget.
|
|
*/
|
|
if (oldtarget->flag & BONE_SELECTED){
|
|
newtarget = (EditBone *) oldtarget->temp;
|
|
strcpy(ct->subtarget, newtarget->name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (cti->flush_constraint_targets)
|
|
cti->flush_constraint_targets(curcon, &targets, 0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void adduplicate_armature(void)
|
|
{
|
|
bArmature *arm= G.obedit->data;
|
|
EditBone *eBone = NULL;
|
|
EditBone *curBone;
|
|
EditBone *firstDup=NULL; /* The beginning of the duplicated bones in the edbo list */
|
|
|
|
countall(); // flushes selection!
|
|
|
|
/* Select mirrored bones */
|
|
if (arm->flag & ARM_MIRROR_EDIT) {
|
|
for (curBone=G.edbo.first; curBone; curBone=curBone->next) {
|
|
if (arm->layer & curBone->layer) {
|
|
if (curBone->flag & BONE_SELECTED) {
|
|
eBone = armature_bone_get_mirrored(curBone);
|
|
if (eBone)
|
|
eBone->flag |= BONE_SELECTED;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Find the selected bones and duplicate them as needed */
|
|
for (curBone=G.edbo.first; curBone && curBone!=firstDup; curBone=curBone->next){
|
|
if (arm->layer & curBone->layer) {
|
|
if (curBone->flag & BONE_SELECTED) {
|
|
|
|
eBone=MEM_callocN(sizeof(EditBone), "addup_editbone");
|
|
eBone->flag |= BONE_SELECTED;
|
|
|
|
/* Copy data from old bone to new bone */
|
|
memcpy (eBone, curBone, sizeof(EditBone));
|
|
|
|
curBone->temp = eBone;
|
|
eBone->temp = curBone;
|
|
|
|
unique_editbone_name (&G.edbo, eBone->name);
|
|
BLI_addtail (&G.edbo, eBone);
|
|
if (!firstDup)
|
|
firstDup=eBone;
|
|
|
|
/* Lets duplicate the list of constraints that the
|
|
* current bone has.
|
|
*/
|
|
if (OBACT->pose) {
|
|
bPoseChannel *chanold, *channew;
|
|
ListBase *listold, *listnew;
|
|
|
|
chanold = verify_pose_channel (OBACT->pose, curBone->name);
|
|
if (chanold) {
|
|
listold = &chanold->constraints;
|
|
if (listold) {
|
|
/* WARNING: this creates a new posechannel, but there will not be an attached bone
|
|
* yet as the new bones created here are still 'EditBones' not 'Bones'.
|
|
*/
|
|
channew =
|
|
verify_pose_channel(OBACT->pose, eBone->name);
|
|
if (channew) {
|
|
/* copy transform locks */
|
|
channew->protectflag = chanold->protectflag;
|
|
|
|
/* ik (dof) settings */
|
|
channew->ikflag = chanold->ikflag;
|
|
VECCOPY(channew->limitmin, chanold->limitmin);
|
|
VECCOPY(channew->limitmax, chanold->limitmax);
|
|
VECCOPY(channew->stiffness, chanold->stiffness);
|
|
channew->ikstretch= chanold->ikstretch;
|
|
|
|
/* constraints */
|
|
listnew = &channew->constraints;
|
|
copy_constraints (listnew, listold);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Run though the list and fix the pointers */
|
|
for (curBone=G.edbo.first; curBone && curBone!=firstDup; curBone=curBone->next){
|
|
if(arm->layer & curBone->layer) {
|
|
if (curBone->flag & BONE_SELECTED){
|
|
eBone=(EditBone*) curBone->temp;
|
|
|
|
/* If this bone has no parent,
|
|
Set the duplicate->parent to NULL
|
|
*/
|
|
if (!curBone->parent){
|
|
eBone->parent = NULL;
|
|
}
|
|
/* If this bone has a parent that IS selected,
|
|
Set the duplicate->parent to the curBone->parent->duplicate
|
|
*/
|
|
else if (curBone->parent->flag & BONE_SELECTED){
|
|
eBone->parent=(EditBone*) curBone->parent->temp;
|
|
}
|
|
/* If this bone has a parent that IS not selected,
|
|
Set the duplicate->parent to the curBone->parent
|
|
*/
|
|
else {
|
|
eBone->parent=(EditBone*) curBone->parent;
|
|
eBone->flag &= ~BONE_CONNECTED;
|
|
}
|
|
|
|
/* Lets try to fix any constraint subtargets that might
|
|
have been duplicated */
|
|
update_dup_subtarget(eBone);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Deselect the old bones and select the new ones */
|
|
|
|
for (curBone=G.edbo.first; curBone && curBone!=firstDup; curBone=curBone->next){
|
|
if(arm->layer & curBone->layer)
|
|
curBone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL | BONE_ACTIVE);
|
|
}
|
|
|
|
BIF_TransformSetUndo("Add Duplicate");
|
|
initTransform(TFM_TRANSLATION, CTX_NO_PET);
|
|
Transform();
|
|
|
|
allqueue(REDRAWBUTSEDIT, 0);
|
|
allqueue(REDRAWBUTSOBJECT, 0);
|
|
allqueue(REDRAWOOPS, 0);
|
|
}
|
|
|
|
|
|
|
|
/* *************** END Adding stuff in editmode *************** */
|
|
/* ************** 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 *list)
|
|
{
|
|
EditBone *curBone, *ebo;
|
|
LinkData *ld;
|
|
|
|
/* note: this is potentially very slow ... there's got to be a better way */
|
|
for (curBone= G.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);
|
|
}
|
|
|
|
// FIXME: this algorithm sucks... it misses things it shouldn't
|
|
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(void)
|
|
{
|
|
bArmature *arm= G.obedit->data;
|
|
EditBone *ebo, *newbone=NULL;
|
|
ListBase points = {NULL, NULL};
|
|
int count;
|
|
|
|
/* loop over all bones, and only consider if visible */
|
|
for (ebo= G.edbo.first; ebo; ebo= ebo->next) {
|
|
if ((arm->layer & ebo->layer) && !(ebo->flag & BONE_HIDDEN_A)) {
|
|
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 curs[3];
|
|
|
|
/* Get Points - selected joint */
|
|
ebp= (EditBonePoint *)points.first;
|
|
|
|
/* Get points - cursor (tail) */
|
|
VECCOPY (curs, give_cursor());
|
|
|
|
Mat4Invert(G.obedit->imat, G.obedit->obmat);
|
|
Mat4MulVecfl(G.obedit->imat, curs);
|
|
|
|
/* Create a bone */
|
|
newbone= add_points_bone(ebp->vec, curs);
|
|
}
|
|
else if (count == 2) {
|
|
EditBonePoint *ebp, *ebp2;
|
|
float head[3], tail[3];
|
|
|
|
/* 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];
|
|
float vecA[3], vecB[3];
|
|
float distA, distB;
|
|
|
|
/* get cursor location */
|
|
VECCOPY (curs, give_cursor());
|
|
|
|
Mat4Invert(G.obedit->imat, G.obedit->obmat);
|
|
Mat4MulVecfl(G.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 */
|
|
if (distA < distB) {
|
|
VECCOPY(head, ebp2->vec);
|
|
VECCOPY(tail, ebp->vec);
|
|
}
|
|
else {
|
|
VECCOPY(head, ebp->vec);
|
|
VECCOPY(tail, ebp2->vec);
|
|
}
|
|
}
|
|
else if (ebp->head_owner) {
|
|
VECCOPY(head, ebp->vec);
|
|
VECCOPY(tail, ebp2->vec);
|
|
}
|
|
else if (ebp2->head_owner) {
|
|
VECCOPY(head, ebp2->vec);
|
|
VECCOPY(tail, ebp->vec);
|
|
}
|
|
|
|
/* add new bone */
|
|
newbone= add_points_bone(head, tail);
|
|
}
|
|
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 */
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
allqueue(REDRAWBUTSEDIT, 0);
|
|
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(EditBone *start, EditBone *end, ListBase *chains)
|
|
{
|
|
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(head, tail);
|
|
newbone->parent = start->parent;
|
|
|
|
/* step 2: 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 */
|
|
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 3: delete all bones between and including start and end */
|
|
for (ebo= end; ebo; ebo= ebone) {
|
|
ebone= (ebo == start) ? (NULL) : (ebo->parent);
|
|
BLI_freelinkN(&G.edbo, ebo);
|
|
}
|
|
}
|
|
|
|
/* bone merging - has a menu! */
|
|
void merge_armature(void)
|
|
{
|
|
bArmature *arm= G.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(&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;
|
|
|
|
/* 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; ebo= ebo->parent) {
|
|
/* check if visible + selected */
|
|
if ( (arm->layer & ebo->layer) && !(ebo->flag & BONE_HIDDEN_A) &&
|
|
((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;
|
|
else
|
|
bstart= ebo;
|
|
}
|
|
else {
|
|
/* chain is broken... merge any continous segments then clear */
|
|
if (bstart && bend)
|
|
bones_merge(bstart, bend, &chains);
|
|
|
|
bstart = NULL;
|
|
bend = NULL;
|
|
}
|
|
}
|
|
|
|
/* merge from bstart to bend if something not merged */
|
|
if (bstart && bend)
|
|
bones_merge(bstart, bend, &chains);
|
|
|
|
/* put back link */
|
|
BLI_insertlinkbefore(&chains, nchain, chain);
|
|
}
|
|
|
|
BLI_freelistN(&chains);
|
|
}
|
|
|
|
/* undo + updates */
|
|
countall();
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
allqueue(REDRAWBUTSEDIT, 0);
|
|
BIF_undo_push("Merge Bones");
|
|
}
|
|
|
|
/* ************** END Add/Remove stuff in editmode ************ */
|
|
/* *************** Tools in editmode *********** */
|
|
|
|
|
|
void hide_selected_armature_bones(void)
|
|
{
|
|
bArmature *arm= G.obedit->data;
|
|
EditBone *ebone;
|
|
|
|
for (ebone = G.edbo.first; ebone; ebone=ebone->next){
|
|
if(arm->layer & ebone->layer) {
|
|
if(ebone->flag & (BONE_SELECTED)) {
|
|
ebone->flag &= ~(BONE_TIPSEL|BONE_SELECTED|BONE_ROOTSEL|BONE_ACTIVE);
|
|
ebone->flag |= BONE_HIDDEN_A;
|
|
}
|
|
}
|
|
}
|
|
countall();
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
allqueue(REDRAWBUTSEDIT, 0);
|
|
BIF_undo_push("Hide Bones");
|
|
}
|
|
|
|
void hide_unselected_armature_bones(void)
|
|
{
|
|
EditBone *ebone;
|
|
|
|
for (ebone = G.edbo.first; ebone; ebone=ebone->next){
|
|
bArmature *arm= G.obedit->data;
|
|
if(arm->layer & ebone->layer) {
|
|
if(ebone->flag & (BONE_TIPSEL|BONE_SELECTED|BONE_ROOTSEL));
|
|
else {
|
|
ebone->flag &= ~BONE_ACTIVE;
|
|
ebone->flag |= BONE_HIDDEN_A;
|
|
}
|
|
}
|
|
}
|
|
countall();
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
allqueue(REDRAWBUTSEDIT, 0);
|
|
BIF_undo_push("Hide Unselected Bones");
|
|
}
|
|
|
|
void show_all_armature_bones(void)
|
|
{
|
|
EditBone *ebone;
|
|
|
|
for (ebone = G.edbo.first; ebone; ebone=ebone->next){
|
|
bArmature *arm= G.obedit->data;
|
|
if(arm->layer & ebone->layer) {
|
|
if(ebone->flag & BONE_HIDDEN_A) {
|
|
ebone->flag |= (BONE_TIPSEL|BONE_SELECTED|BONE_ROOTSEL);
|
|
ebone->flag &= ~BONE_HIDDEN_A;
|
|
}
|
|
}
|
|
}
|
|
countall();
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
allqueue(REDRAWBUTSEDIT, 0);
|
|
BIF_undo_push("Reveal Bones");
|
|
}
|
|
|
|
/* 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(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 = G.edbo.first; ebone; ebone=ebone->next) {
|
|
EditBone *par;
|
|
|
|
for (par= ebone->parent; par; par= par->parent) {
|
|
if (par==selbone) {
|
|
VecAddf(ebone->head, ebone->head, offset);
|
|
VecAddf(ebone->tail, ebone->tail, offset);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
/* Offset: Child bones will retain their distance from the parent tip */
|
|
selbone->flag &= ~BONE_CONNECTED;
|
|
}
|
|
}
|
|
|
|
void make_bone_parent(void)
|
|
{
|
|
bArmature *arm= G.obedit->data;
|
|
EditBone *actbone, *ebone, *selbone;
|
|
EditBone *flipbone, *flippar;
|
|
short allchildbones= 0, foundselbone= 0;
|
|
short val;
|
|
|
|
/* find active bone to parent to */
|
|
for (actbone = G.edbo.first; actbone; actbone=actbone->next) {
|
|
if (arm->layer & actbone->layer) {
|
|
if (actbone->flag & BONE_ACTIVE)
|
|
break;
|
|
}
|
|
}
|
|
if (actbone == NULL) {
|
|
error("Needs an active bone");
|
|
return;
|
|
}
|
|
|
|
/* find selected bones */
|
|
for (ebone = G.edbo.first; ebone; ebone=ebone->next) {
|
|
if (arm->layer & ebone->layer) {
|
|
if ((ebone->flag & BONE_SELECTED) && (ebone != actbone)) {
|
|
foundselbone++;
|
|
if (ebone->parent != actbone) allchildbones= 1;
|
|
}
|
|
}
|
|
}
|
|
/* abort if no selected bones, and active bone doesn't have a parent to work with instead */
|
|
if (foundselbone==0 && actbone->parent==NULL) {
|
|
error("Need selected bone(s)");
|
|
return;
|
|
}
|
|
|
|
/* 'Keep Offset' option is only displayed if it's likely to be useful */
|
|
if (allchildbones)
|
|
val= pupmenu("Make Parent%t|Connected%x1|Keep Offset%x2");
|
|
else
|
|
val= pupmenu("Make Parent%t|Connected%x1");
|
|
|
|
if (val < 1) return;
|
|
|
|
if (foundselbone==0 && actbone->parent) {
|
|
/* When only the active bone is selected, and it has a parent,
|
|
* connect it to the parent, as that is the only possible outcome.
|
|
*/
|
|
bone_connect_to_existing_parent(actbone);
|
|
|
|
if (arm->flag & ARM_MIRROR_EDIT) {
|
|
flipbone = armature_bone_get_mirrored(actbone);
|
|
if (flipbone)
|
|
bone_connect_to_existing_parent(flipbone);
|
|
}
|
|
}
|
|
else {
|
|
/* loop through all editbones, parenting all selected bones to the active bone */
|
|
for (selbone = G.edbo.first; selbone; selbone=selbone->next) {
|
|
if (arm->layer & selbone->layer) {
|
|
if ((selbone->flag & BONE_SELECTED) && (selbone!=actbone)) {
|
|
/* parent selbone to actbone */
|
|
bone_connect_to_new_parent(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(selbone);
|
|
flippar = armature_bone_get_mirrored(actbone);
|
|
|
|
if (flipbone) {
|
|
if (flippar)
|
|
bone_connect_to_new_parent(flipbone, flippar, val);
|
|
else
|
|
bone_connect_to_new_parent(flipbone, actbone, val);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
countall(); /* checks selection */
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
allqueue(REDRAWBUTSEDIT, 0);
|
|
allqueue(REDRAWOOPS, 0);
|
|
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(void)
|
|
{
|
|
bArmature *arm= G.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 = G.edbo.first; ebone; ebone=ebone->next) {
|
|
if(arm->layer & ebone->layer) {
|
|
if(ebone->flag & BONE_SELECTED) {
|
|
|
|
if(arm->flag & ARM_MIRROR_EDIT)
|
|
flipbone = armature_bone_get_mirrored(ebone);
|
|
|
|
if (flipbone)
|
|
editbone_clear_parent(flipbone, val);
|
|
editbone_clear_parent(ebone, val);
|
|
}
|
|
}
|
|
}
|
|
countall(); // checks selection
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
allqueue(REDRAWBUTSEDIT, 0);
|
|
allqueue(REDRAWOOPS, 0);
|
|
BIF_undo_push("Clear Parent");
|
|
}
|
|
|
|
|
|
static EditBone *editbone_name_exists (ListBase *ebones, char *name)
|
|
{
|
|
EditBone *eBone;
|
|
|
|
if (ebones == NULL) ebones = &G.edbo;
|
|
|
|
for (eBone=ebones->first; eBone; eBone=eBone->next){
|
|
if (!strcmp (name, eBone->name))
|
|
return eBone;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/* note: there's a unique_bone_name() too! */
|
|
void unique_editbone_name (ListBase *ebones, char *name)
|
|
{
|
|
char tempname[64];
|
|
int number;
|
|
char *dot;
|
|
|
|
|
|
if (editbone_name_exists(ebones, name)) {
|
|
|
|
/* Strip off the suffix, if it's a number */
|
|
number= strlen(name);
|
|
if(number && isdigit(name[number-1])) {
|
|
dot= strrchr(name, '.'); // last occurrance
|
|
if (dot)
|
|
*dot=0;
|
|
}
|
|
|
|
for (number = 1; number <=999; number++){
|
|
sprintf (tempname, "%s.%03d", name, number);
|
|
if (!editbone_name_exists(ebones, tempname)){
|
|
BLI_strncpy (name, tempname, 32);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* context; editmode armature */
|
|
/* if forked && mirror-edit: makes two bones with flipped names */
|
|
void extrude_armature(int forked)
|
|
{
|
|
bArmature *arm= G.obedit->data;
|
|
EditBone *newbone, *ebone, *flipbone, *first=NULL;
|
|
int a, totbone= 0, do_extrude;
|
|
|
|
TEST_EDITARMATURE;
|
|
|
|
/* since we allow root extrude too, we have to make sure selection is OK */
|
|
for (ebone = G.edbo.first; ebone; ebone=ebone->next){
|
|
if(arm->layer & ebone->layer) {
|
|
if(ebone->flag & BONE_ROOTSEL) {
|
|
if(ebone->parent && (ebone->flag & BONE_CONNECTED)) {
|
|
if(ebone->parent->flag & BONE_TIPSEL)
|
|
ebone->flag &= ~BONE_ROOTSEL;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Duplicate the necessary bones */
|
|
for (ebone = G.edbo.first; ((ebone) && (ebone!=first)); ebone=ebone->next){
|
|
if(arm->layer & ebone->layer) {
|
|
|
|
/* we extrude per definition the tip */
|
|
do_extrude= 0;
|
|
if (ebone->flag & (BONE_TIPSEL|BONE_SELECTED))
|
|
do_extrude= 1;
|
|
else if(ebone->flag & BONE_ROOTSEL) {
|
|
/* but, a bone with parent deselected we do the root... */
|
|
if(ebone->parent && (ebone->parent->flag & BONE_TIPSEL));
|
|
else do_extrude= 2;
|
|
}
|
|
|
|
if (do_extrude) {
|
|
|
|
/* we re-use code for mirror editing... */
|
|
flipbone= NULL;
|
|
if(arm->flag & ARM_MIRROR_EDIT) {
|
|
flipbone= armature_bone_get_mirrored(ebone);
|
|
if (flipbone) {
|
|
forked= 0; // we extrude 2 different bones
|
|
if(flipbone->flag & (BONE_TIPSEL|BONE_ROOTSEL|BONE_SELECTED))
|
|
/* don't want this bone to be selected... */
|
|
flipbone->flag &= ~(BONE_TIPSEL|BONE_SELECTED|BONE_ROOTSEL|BONE_ACTIVE);
|
|
}
|
|
if(flipbone==NULL && forked)
|
|
flipbone= ebone;
|
|
}
|
|
|
|
for(a=0; a<2; a++) {
|
|
if(a==1) {
|
|
if(flipbone==NULL)
|
|
break;
|
|
else {
|
|
SWAP(EditBone *, flipbone, ebone);
|
|
}
|
|
}
|
|
|
|
totbone++;
|
|
newbone = MEM_callocN(sizeof(EditBone), "extrudebone");
|
|
|
|
if(do_extrude==1) {
|
|
VECCOPY (newbone->head, ebone->tail);
|
|
VECCOPY (newbone->tail, newbone->head);
|
|
newbone->parent = ebone;
|
|
|
|
newbone->flag = ebone->flag & BONE_TIPSEL; // copies it, in case mirrored bone
|
|
}
|
|
else {
|
|
VECCOPY(newbone->head, ebone->head);
|
|
VECCOPY(newbone->tail, ebone->head);
|
|
newbone->parent= ebone->parent;
|
|
|
|
newbone->flag= BONE_TIPSEL;
|
|
}
|
|
|
|
newbone->weight= ebone->weight;
|
|
newbone->dist= ebone->dist;
|
|
newbone->xwidth= ebone->xwidth;
|
|
newbone->zwidth= ebone->zwidth;
|
|
newbone->ease1= ebone->ease1;
|
|
newbone->ease2= ebone->ease2;
|
|
newbone->rad_head= ebone->rad_tail; // dont copy entire bone...
|
|
newbone->rad_tail= ebone->rad_tail;
|
|
newbone->segments= 1;
|
|
newbone->layer= ebone->layer;
|
|
|
|
if(newbone->parent) newbone->flag |= BONE_CONNECTED;
|
|
|
|
BLI_strncpy (newbone->name, ebone->name, 32);
|
|
|
|
if(flipbone && forked) { // only set if mirror edit
|
|
if(strlen(newbone->name)<30) {
|
|
if(a==0) strcat(newbone->name, "_L");
|
|
else strcat(newbone->name, "_R");
|
|
}
|
|
}
|
|
unique_editbone_name(&G.edbo, newbone->name);
|
|
|
|
/* Add the new bone to the list */
|
|
BLI_addtail(&G.edbo, newbone);
|
|
if (!first)
|
|
first = newbone;
|
|
|
|
/* restore ebone if we were flipping */
|
|
if(a==1 && flipbone)
|
|
SWAP(EditBone *, flipbone, ebone);
|
|
|
|
}
|
|
}
|
|
|
|
/* Deselect the old bone */
|
|
ebone->flag &= ~(BONE_TIPSEL|BONE_SELECTED|BONE_ROOTSEL|BONE_ACTIVE);
|
|
}
|
|
}
|
|
/* if only one bone, make this one active */
|
|
if(totbone==1 && first) first->flag |= BONE_ACTIVE;
|
|
|
|
/* Transform the endpoints */
|
|
countall(); // flushes selection!
|
|
BIF_TransformSetUndo("Extrude");
|
|
initTransform(TFM_TRANSLATION, CTX_NO_PET);
|
|
Transform();
|
|
|
|
allqueue(REDRAWBUTSEDIT, 0);
|
|
allqueue(REDRAWBUTSOBJECT, 0);
|
|
allqueue(REDRAWOOPS, 0);
|
|
}
|
|
|
|
/* context; editmode armature */
|
|
void subdivide_armature(int numcuts)
|
|
{
|
|
bArmature *arm= G.obedit->data;
|
|
EditBone *ebone, *newbone, *tbone, *mbone;
|
|
int a, i;
|
|
|
|
if(numcuts < 1) return;
|
|
|
|
for (mbone = G.edbo.last; mbone; mbone= mbone->prev) {
|
|
if(arm->layer & mbone->layer) {
|
|
if(mbone->flag & BONE_SELECTED) {
|
|
for(i=numcuts+1; i>1; i--) {
|
|
/* compute cut ratio first */
|
|
float cutratio= 1/(float)i;
|
|
float cutratioI= 1-cutratio;
|
|
|
|
/* take care of mirrored stuff */
|
|
for(a=0; a<2; a++) {
|
|
float val1[3];
|
|
float val2[3];
|
|
float val3[3];
|
|
|
|
/* try to find mirrored bone on a != 0 */
|
|
if(a) {
|
|
if(arm->flag & ARM_MIRROR_EDIT)
|
|
ebone= armature_bone_get_mirrored(mbone);
|
|
else ebone= NULL;
|
|
}
|
|
else
|
|
ebone= mbone;
|
|
|
|
if(ebone) {
|
|
newbone= MEM_mallocN(sizeof(EditBone), "ebone subdiv");
|
|
*newbone = *ebone;
|
|
BLI_addtail(&G.edbo, newbone);
|
|
|
|
/* calculate location of newbone->head */
|
|
VECCOPY(val1, ebone->head);
|
|
VECCOPY(val2, ebone->tail);
|
|
VECCOPY(val3, newbone->head);
|
|
|
|
val3[0]= val1[0]*cutratio+val2[0]*cutratioI;
|
|
val3[1]= val1[1]*cutratio+val2[1]*cutratioI;
|
|
val3[2]= val1[2]*cutratio+val2[2]*cutratioI;
|
|
|
|
VECCOPY(newbone->head, val3);
|
|
VECCOPY(newbone->tail, ebone->tail);
|
|
VECCOPY(ebone->tail, newbone->head);
|
|
|
|
newbone->rad_head= 0.5*(ebone->rad_head+ebone->rad_tail);
|
|
ebone->rad_tail= newbone->rad_head;
|
|
|
|
newbone->flag |= BONE_CONNECTED;
|
|
|
|
unique_editbone_name (&G.edbo, newbone->name);
|
|
|
|
/* correct parent bones */
|
|
for (tbone = G.edbo.first; tbone; tbone=tbone->next){
|
|
if(tbone->parent==ebone)
|
|
tbone->parent= newbone;
|
|
}
|
|
newbone->parent= ebone;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(numcuts==1) BIF_undo_push("Subdivide");
|
|
else BIF_undo_push("Subdivide multi");
|
|
}
|
|
|
|
/* ***************** Pose tools ********************* */
|
|
|
|
void clear_armature(Object *ob, char mode)
|
|
{
|
|
bPoseChannel *pchan;
|
|
bArmature *arm;
|
|
|
|
arm=get_armature(ob);
|
|
|
|
if (!arm)
|
|
return;
|
|
|
|
for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
|
|
if(pchan->bone && (pchan->bone->flag & BONE_SELECTED)) {
|
|
if(arm->layer & pchan->bone->layer) {
|
|
switch (mode) {
|
|
case 'r':
|
|
pchan->quat[1]=pchan->quat[2]=pchan->quat[3]=0.0F; pchan->quat[0]=1.0F;
|
|
break;
|
|
case 'g':
|
|
pchan->loc[0]=pchan->loc[1]=pchan->loc[2]=0.0F;
|
|
break;
|
|
case 's':
|
|
pchan->size[0]=pchan->size[1]=pchan->size[2]=1.0F;
|
|
break;
|
|
|
|
}
|
|
|
|
/* the current values from IPO's may not be zero, so tag as unkeyed */
|
|
pchan->bone->flag |= BONE_UNKEYED;
|
|
}
|
|
}
|
|
}
|
|
|
|
DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA);
|
|
/* no update for this object, this will execute the action again */
|
|
/* is weak... like for ipo editing which uses ctime now... */
|
|
where_is_pose (ob);
|
|
ob->recalc= 0;
|
|
}
|
|
|
|
/* helper for function below */
|
|
static int clear_active_flag(Object *ob, Bone *bone, void *data)
|
|
{
|
|
bone->flag &= ~BONE_ACTIVE;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* called from editview.c, for mode-less pose selection */
|
|
int do_pose_selectbuffer(Base *base, unsigned int *buffer, short hits)
|
|
{
|
|
Object *ob= base->object;
|
|
Bone *nearBone;
|
|
|
|
if (!ob || !ob->pose) return 0;
|
|
|
|
nearBone= get_bone_from_selectbuffer(base, buffer, hits, 1);
|
|
|
|
if (nearBone) {
|
|
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 (!(G.qual & LR_SHIFTKEY) || base!=BASACT){
|
|
deselectall_posearmature(ob, 0, 0);
|
|
nearBone->flag |= (BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL|BONE_ACTIVE);
|
|
select_actionchannel_by_name(ob->action, nearBone->name, 1);
|
|
}
|
|
else {
|
|
if (nearBone->flag & BONE_SELECTED) {
|
|
/* if not active, we make it active */
|
|
if((nearBone->flag & BONE_ACTIVE)==0) {
|
|
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(OBACT, nearBone->name);
|
|
DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA);
|
|
}
|
|
}
|
|
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWIPO, 0); /* To force action/constraint ipo update */
|
|
allqueue(REDRAWBUTSEDIT, 0);
|
|
allqueue(REDRAWBUTSOBJECT, 0);
|
|
allqueue(REDRAWOOPS, 0);
|
|
}
|
|
|
|
return nearBone!=NULL;
|
|
|
|
}
|
|
|
|
/* test==0: deselect all
|
|
test==1: swap select
|
|
test==2: only clear active tag
|
|
*/
|
|
void deselectall_posearmature (Object *ob, int test, int doundo)
|
|
{
|
|
bArmature *arm;
|
|
bPoseChannel *pchan;
|
|
int selectmode= 0;
|
|
|
|
/* we call this from outliner too, but with OBACT set OK */
|
|
if(!ob || !ob->pose) return;
|
|
arm= get_armature(ob);
|
|
|
|
/* Determine if we're selecting or deselecting */
|
|
if (test==1) {
|
|
for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next)
|
|
if(pchan->bone->layer & arm->layer && !(pchan->bone->flag & BONE_HIDDEN_P))
|
|
if(pchan->bone->flag & BONE_SELECTED)
|
|
break;
|
|
|
|
if (pchan==NULL)
|
|
selectmode= 1;
|
|
}
|
|
else if(test==2)
|
|
selectmode= 2;
|
|
|
|
/* Set the flags accordingly */
|
|
for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
|
|
if(pchan->bone->layer & arm->layer && !(pchan->bone->flag & BONE_HIDDEN_P)) {
|
|
if(selectmode==0) pchan->bone->flag &= ~(BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL|BONE_ACTIVE);
|
|
else if(selectmode==1) pchan->bone->flag |= BONE_SELECTED;
|
|
else pchan->bone->flag &= ~BONE_ACTIVE;
|
|
}
|
|
}
|
|
|
|
/* action editor */
|
|
deselect_actionchannels(ob->action, 0); /* deselects for sure */
|
|
if(selectmode==1)
|
|
deselect_actionchannels(ob->action, 1); /* swaps */
|
|
|
|
allqueue(REDRAWBUTSEDIT, 0);
|
|
allqueue(REDRAWBUTSOBJECT, 0);
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
allqueue(REDRAWOOPS, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
|
|
countall();
|
|
|
|
if (doundo) {
|
|
if (selectmode==1) BIF_undo_push("Select All");
|
|
else BIF_undo_push("Deselect All");
|
|
}
|
|
}
|
|
|
|
|
|
int bone_looper(Object *ob, Bone *bone, void *data,
|
|
int (*bone_func)(Object *, Bone *, void *))
|
|
{
|
|
|
|
/* We want to apply the function bone_func to every bone
|
|
* in an armature -- feed bone_looper the first bone and
|
|
* a pointer to the bone_func and watch it go!. The int count
|
|
* can be useful for counting bones with a certain property
|
|
* (e.g. skinnable)
|
|
*/
|
|
int count = 0;
|
|
|
|
if (bone) {
|
|
|
|
/* only do bone_func if the bone is non null
|
|
*/
|
|
count += bone_func(ob, bone, data);
|
|
|
|
/* try to execute bone_func for the first child
|
|
*/
|
|
count += bone_looper(ob, bone->childbase.first, data,
|
|
bone_func);
|
|
|
|
/* try to execute bone_func for the next bone at this
|
|
* depth of the recursion.
|
|
*/
|
|
count += bone_looper(ob, bone->next, data, bone_func);
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
|
|
static int bone_skinnable(Object *ob, Bone *bone, void *datap)
|
|
{
|
|
/* Bones that are deforming
|
|
* are regarded to be "skinnable" and are eligible for
|
|
* auto-skinning.
|
|
*
|
|
* This function performs 2 functions:
|
|
*
|
|
* a) It returns 1 if the bone is skinnable.
|
|
* If we loop over all bones with this
|
|
* function, we can count the number of
|
|
* skinnable bones.
|
|
* b) If the pointer data is non null,
|
|
* it is treated like a handle to a
|
|
* bone pointer -- the bone pointer
|
|
* is set to point at this bone, and
|
|
* the pointer the handle points to
|
|
* is incremented to point to the
|
|
* next member of an array of pointers
|
|
* to bones. This way we can loop using
|
|
* this function to construct an array of
|
|
* pointers to bones that point to all
|
|
* skinnable bones.
|
|
*/
|
|
Bone ***hbone;
|
|
int a, segments;
|
|
struct { Object *armob; void *list; int heat; } *data = datap;
|
|
|
|
if(!(G.f & G_WEIGHTPAINT) || !(bone->flag & BONE_HIDDEN_P)) {
|
|
if (!(bone->flag & BONE_NO_DEFORM)) {
|
|
if(data->heat && data->armob->pose && get_pose_channel(data->armob->pose, bone->name))
|
|
segments = bone->segments;
|
|
else
|
|
segments = 1;
|
|
|
|
if (data->list != NULL) {
|
|
hbone = (Bone ***) &data->list;
|
|
|
|
for(a=0; a<segments; a++) {
|
|
**hbone = bone;
|
|
++*hbone;
|
|
}
|
|
}
|
|
return segments;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int add_defgroup_unique_bone(Object *ob, Bone *bone, void *data)
|
|
{
|
|
/* This group creates a vertex group to ob that has the
|
|
* same name as bone (provided the bone is skinnable).
|
|
* If such a vertex group aleady exist the routine exits.
|
|
*/
|
|
if (!(bone->flag & BONE_NO_DEFORM)) {
|
|
if (!get_named_vertexgroup(ob,bone->name)) {
|
|
add_defgroup_name(ob, bone->name);
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int dgroup_skinnable(Object *ob, Bone *bone, void *datap)
|
|
{
|
|
/* Bones that are deforming
|
|
* are regarded to be "skinnable" and are eligible for
|
|
* auto-skinning.
|
|
*
|
|
* This function performs 2 functions:
|
|
*
|
|
* a) If the bone is skinnable, it creates
|
|
* a vertex group for ob that has
|
|
* the name of the skinnable bone
|
|
* (if one doesn't exist already).
|
|
* b) If the pointer data is non null,
|
|
* it is treated like a handle to a
|
|
* bDeformGroup pointer -- the
|
|
* bDeformGroup pointer is set to point
|
|
* to the deform group with the bone's
|
|
* name, and the pointer the handle
|
|
* points to is incremented to point to the
|
|
* next member of an array of pointers
|
|
* to bDeformGroups. This way we can loop using
|
|
* this function to construct an array of
|
|
* pointers to bDeformGroups, all with names
|
|
* of skinnable bones.
|
|
*/
|
|
bDeformGroup ***hgroup, *defgroup;
|
|
int a, segments;
|
|
struct { Object *armob; void *list; int heat; } *data= datap;
|
|
|
|
if(!(G.f & G_WEIGHTPAINT) || !(bone->flag & BONE_HIDDEN_P)) {
|
|
if (!(bone->flag & BONE_NO_DEFORM)) {
|
|
if(data->heat && data->armob->pose && get_pose_channel(data->armob->pose, bone->name))
|
|
segments = bone->segments;
|
|
else
|
|
segments = 1;
|
|
|
|
if(!(defgroup = get_named_vertexgroup(ob, bone->name)))
|
|
defgroup = add_defgroup_name(ob, bone->name);
|
|
|
|
if (data->list != NULL) {
|
|
hgroup = (bDeformGroup ***) &data->list;
|
|
|
|
for(a=0; a<segments; a++) {
|
|
**hgroup = defgroup;
|
|
++*hgroup;
|
|
}
|
|
}
|
|
return segments;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void add_vgroups__mapFunc(void *userData, int index, float *co, float *no_f, short *no_s)
|
|
{
|
|
/* DerivedMesh mapFunc for getting final coords in weight paint mode */
|
|
|
|
float (*verts)[3] = userData;
|
|
VECCOPY(verts[index], co);
|
|
}
|
|
|
|
static void envelope_bone_weighting(Object *ob, Mesh *mesh, float (*verts)[3], int numbones, Bone **bonelist, bDeformGroup **dgrouplist, bDeformGroup **dgroupflip, float (*root)[3], float (*tip)[3], int *selected, float scale)
|
|
{
|
|
/* Create vertex group weights from envelopes */
|
|
|
|
Bone *bone;
|
|
bDeformGroup *dgroup;
|
|
float distance;
|
|
int i, iflip, j;
|
|
|
|
/* for each vertex in the mesh */
|
|
for (i=0; i < mesh->totvert; i++) {
|
|
iflip = (dgroupflip)? mesh_get_x_mirror_vert(ob, i): 0;
|
|
|
|
/* for each skinnable bone */
|
|
for (j=0; j < numbones; ++j) {
|
|
if(!selected[j])
|
|
continue;
|
|
|
|
bone = bonelist[j];
|
|
dgroup = dgrouplist[j];
|
|
|
|
/* store the distance-factor from the vertex to the bone */
|
|
distance = distfactor_to_bone (verts[i], root[j], tip[j],
|
|
bone->rad_head * scale, bone->rad_tail * scale, bone->dist * scale);
|
|
|
|
/* add the vert to the deform group if weight!=0.0 */
|
|
if (distance!=0.0)
|
|
add_vert_to_defgroup (ob, dgroup, i, distance, WEIGHT_REPLACE);
|
|
else
|
|
remove_vert_defgroup (ob, dgroup, i);
|
|
|
|
/* do same for mirror */
|
|
if (dgroupflip && dgroupflip[j] && iflip >= 0) {
|
|
if (distance!=0.0)
|
|
add_vert_to_defgroup (ob, dgroupflip[j], iflip, distance,
|
|
WEIGHT_REPLACE);
|
|
else
|
|
remove_vert_defgroup (ob, dgroupflip[j], iflip);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void add_verts_to_dgroups(Object *ob, Object *par, int heat, int mirror)
|
|
{
|
|
/* This functions implements the automatic computation of vertex group
|
|
* weights, either through envelopes or using a heat equilibrium.
|
|
*
|
|
* This function can be called both when parenting a mesh to an armature,
|
|
* or in weightpaint + posemode. In the latter case selection is taken
|
|
* into account and vertex weights can be mirrored.
|
|
*
|
|
* The mesh vertex positions used are either the final deformed coords
|
|
* from the derivedmesh in weightpaint mode, the final subsurf coords
|
|
* when parenting, or simply the original mesh coords.
|
|
*/
|
|
|
|
bArmature *arm;
|
|
Bone **bonelist, *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;
|
|
|
|
/* If the parent object is not an armature exit */
|
|
arm = get_armature(par);
|
|
if (!arm)
|
|
return;
|
|
|
|
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(ob, CD_MASK_BAREMESH);
|
|
|
|
if(dm->foreachMappedVert) {
|
|
dm->foreachMappedVert(dm, add_vgroups__mapFunc, (void*)verts);
|
|
vertsfilled = 1;
|
|
}
|
|
|
|
dm->release(dm);
|
|
}
|
|
else if (modifiers_findByType(ob, eModifierType_Subsurf)) {
|
|
/* is subsurf on? Lets use the verts on the limit surface then.
|
|
* = same amount of vertices as mesh, but vertices moved to the
|
|
* subsurfed position, like for 'optimal'. */
|
|
subsurf_calculate_limit_positions(mesh, verts);
|
|
vertsfilled = 1;
|
|
}
|
|
|
|
/* transform verts to global space */
|
|
for (i=0; i < mesh->totvert; i++) {
|
|
if (!vertsfilled)
|
|
VECCOPY(verts[i], mesh->mvert[i].co)
|
|
Mat4MulVecfl(ob->obmat, verts[i]);
|
|
}
|
|
|
|
/* compute the weights based on gathered vertices and bones */
|
|
if (heat)
|
|
heat_bone_weighting(ob, mesh, verts, numbones, dgrouplist, dgroupflip,
|
|
root, tip, selected);
|
|
else
|
|
envelope_bone_weighting(ob, mesh, verts, numbones, bonelist, dgrouplist,
|
|
dgroupflip, root, tip, selected, Mat4ToScalef(par->obmat));
|
|
|
|
/* free the memory allocated */
|
|
MEM_freeN(bonelist);
|
|
MEM_freeN(dgrouplist);
|
|
MEM_freeN(dgroupflip);
|
|
MEM_freeN(root);
|
|
MEM_freeN(tip);
|
|
MEM_freeN(selected);
|
|
MEM_freeN(verts);
|
|
}
|
|
|
|
void create_vgroups_from_armature(Object *ob, Object *par)
|
|
{
|
|
/* Lets try to create some vertex groups
|
|
* based on the bones of the parent armature.
|
|
*/
|
|
|
|
bArmature *arm;
|
|
short mode;
|
|
|
|
/* If the parent object is not an armature exit */
|
|
arm = get_armature(par);
|
|
if (!arm)
|
|
return;
|
|
|
|
/* Prompt the user on whether/how they want the vertex groups
|
|
* added to the child mesh */
|
|
mode= pupmenu("Create Vertex Groups? %t|"
|
|
"Don't Create Groups %x1|"
|
|
"Name Groups %x2|"
|
|
"Create From Envelopes %x3|"
|
|
"Create From Bone Heat %x4|");
|
|
switch (mode){
|
|
case 2:
|
|
/* Traverse the bone list, trying to create empty vertex
|
|
* groups cooresponding to the bone.
|
|
*/
|
|
bone_looper(ob, arm->bonebase.first, NULL,
|
|
add_defgroup_unique_bone);
|
|
if (ob->type == OB_MESH)
|
|
create_dverts(ob->data);
|
|
|
|
break;
|
|
|
|
case 3:
|
|
case 4:
|
|
/* Traverse the bone list, trying to create vertex groups
|
|
* that are populated with the vertices for which the
|
|
* bone is closest.
|
|
*/
|
|
add_verts_to_dgroups(ob, par, (mode == 4), 0);
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
static int hide_selected_pose_bone(Object *ob, Bone *bone, void *ptr)
|
|
{
|
|
bArmature *arm= ob->data;
|
|
|
|
if(arm->layer & bone->layer) {
|
|
if (bone->flag & BONE_SELECTED) {
|
|
bone->flag |= BONE_HIDDEN_P;
|
|
bone->flag &= ~(BONE_SELECTED|BONE_ACTIVE);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* active object is armature */
|
|
void hide_selected_pose_bones(void)
|
|
{
|
|
bArmature *arm= OBACT->data;
|
|
|
|
if (!arm)
|
|
return;
|
|
|
|
bone_looper(OBACT, arm->bonebase.first, NULL,
|
|
hide_selected_pose_bone);
|
|
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
allqueue(REDRAWBUTSEDIT, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
BIF_undo_push("Hide Bones");
|
|
}
|
|
|
|
static int hide_unselected_pose_bone(Object *ob, Bone *bone, void *ptr)
|
|
{
|
|
bArmature *arm= ob->data;
|
|
|
|
if(arm->layer & bone->layer) {
|
|
if (~bone->flag & BONE_SELECTED) {
|
|
bone->flag |= BONE_HIDDEN_P;
|
|
bone->flag &= ~BONE_ACTIVE;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* active object is armature */
|
|
void hide_unselected_pose_bones(void)
|
|
{
|
|
bArmature *arm;
|
|
|
|
arm=get_armature (OBACT);
|
|
|
|
if (!arm)
|
|
return;
|
|
|
|
bone_looper(OBACT, arm->bonebase.first, NULL,
|
|
hide_unselected_pose_bone);
|
|
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
allqueue(REDRAWBUTSEDIT, 0);
|
|
BIF_undo_push("Hide Unselected Bone");
|
|
}
|
|
|
|
static int show_pose_bone(Object *ob, Bone *bone, void *ptr)
|
|
{
|
|
bArmature *arm= ob->data;
|
|
|
|
if(arm->layer & bone->layer) {
|
|
if (bone->flag & BONE_HIDDEN_P) {
|
|
bone->flag &= ~BONE_HIDDEN_P;
|
|
bone->flag |= BONE_SELECTED;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* active object is armature in posemode */
|
|
void show_all_pose_bones(void)
|
|
{
|
|
bArmature *arm;
|
|
|
|
arm=get_armature (OBACT);
|
|
|
|
if (!arm)
|
|
return;
|
|
|
|
bone_looper(OBACT, arm->bonebase.first, NULL,
|
|
show_pose_bone);
|
|
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
allqueue(REDRAWBUTSEDIT, 0);
|
|
BIF_undo_push("Reveal Bones");
|
|
}
|
|
|
|
|
|
/* ************* RENAMING DISASTERS ************ */
|
|
|
|
/* note: there's a unique_editbone_name() too! */
|
|
void unique_bone_name (bArmature *arm, char *name)
|
|
{
|
|
char tempname[64];
|
|
int number;
|
|
char *dot;
|
|
|
|
if (get_named_bone(arm, name)) {
|
|
|
|
/* Strip off the suffix, if it's a number */
|
|
number= strlen(name);
|
|
if(number && isdigit(name[number-1])) {
|
|
dot= strrchr(name, '.'); // last occurrance
|
|
if (dot)
|
|
*dot=0;
|
|
}
|
|
|
|
for (number = 1; number <=999; number++){
|
|
sprintf (tempname, "%s.%03d", name, number);
|
|
if (!get_named_bone(arm, tempname)){
|
|
BLI_strncpy (name, tempname, 32);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#define MAXBONENAME 32
|
|
/* helper call for below */
|
|
static void constraint_bone_name_fix(Object *ob, ListBase *conlist, char *oldname, char *newname)
|
|
{
|
|
bConstraint *curcon;
|
|
bConstraintTarget *ct;
|
|
|
|
for (curcon = conlist->first; curcon; curcon=curcon->next) {
|
|
bConstraintTypeInfo *cti= constraint_get_typeinfo(curcon);
|
|
ListBase targets = {NULL, NULL};
|
|
|
|
if (cti && cti->get_constraint_targets) {
|
|
cti->get_constraint_targets(curcon, &targets);
|
|
|
|
for (ct= targets.first; ct; ct= ct->next) {
|
|
if (ct->tar == ob) {
|
|
if (!strcmp(ct->subtarget, oldname) )
|
|
BLI_strncpy(ct->subtarget, newname, MAXBONENAME);
|
|
}
|
|
}
|
|
|
|
if (cti->flush_constraint_targets)
|
|
cti->flush_constraint_targets(curcon, &targets, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* called by UI for renaming a bone */
|
|
/* warning: make sure the original bone was not renamed yet! */
|
|
/* seems messy, but thats what you get with not using pointers but channel names :) */
|
|
void armature_bone_rename(bArmature *arm, char *oldnamep, char *newnamep)
|
|
{
|
|
Object *ob;
|
|
char newname[MAXBONENAME];
|
|
char oldname[MAXBONENAME];
|
|
|
|
/* names better differ! */
|
|
if(strncmp(oldnamep, newnamep, MAXBONENAME)) {
|
|
|
|
/* we alter newname string... so make copy */
|
|
BLI_strncpy(newname, newnamep, MAXBONENAME);
|
|
/* we use oldname for search... so make copy */
|
|
BLI_strncpy(oldname, oldnamep, MAXBONENAME);
|
|
|
|
/* now check if we're in editmode, we need to find the unique name */
|
|
if(G.obedit && G.obedit->data==arm) {
|
|
EditBone *eBone;
|
|
|
|
eBone= editbone_name_exists(&G.edbo, oldname);
|
|
if(eBone) {
|
|
unique_editbone_name (&G.edbo, newname);
|
|
BLI_strncpy(eBone->name, newname, MAXBONENAME);
|
|
}
|
|
else return;
|
|
}
|
|
else {
|
|
Bone *bone= get_named_bone (arm, oldname);
|
|
|
|
if(bone) {
|
|
unique_bone_name (arm, newname);
|
|
BLI_strncpy(bone->name, newname, MAXBONENAME);
|
|
}
|
|
else return;
|
|
}
|
|
|
|
/* do entire dbase */
|
|
for(ob= G.main->object.first; ob; ob= ob->id.next) {
|
|
/* we have the object using the armature */
|
|
if(arm==ob->data) {
|
|
Object *cob;
|
|
bAction *act;
|
|
bActionChannel *achan;
|
|
bActionStrip *strip;
|
|
|
|
/* Rename action channel if necessary */
|
|
act = ob->action;
|
|
if (act && !act->id.lib) {
|
|
/* Find the appropriate channel */
|
|
achan= get_action_channel(act, oldname);
|
|
if(achan) BLI_strncpy(achan->name, newname, MAXBONENAME);
|
|
}
|
|
|
|
/* Rename the pose channel, if it exists */
|
|
if (ob->pose) {
|
|
bPoseChannel *pchan = get_pose_channel(ob->pose, oldname);
|
|
if (pchan) {
|
|
BLI_strncpy (pchan->name, newname, MAXBONENAME);
|
|
}
|
|
}
|
|
|
|
/* check all nla-strips too */
|
|
for (strip= ob->nlastrips.first; strip; strip= strip->next) {
|
|
/* Rename action channel if necessary */
|
|
act = strip->act;
|
|
if (act && !act->id.lib) {
|
|
/* Find the appropriate channel */
|
|
achan= get_action_channel(act, oldname);
|
|
if(achan) BLI_strncpy(achan->name, newname, MAXBONENAME);
|
|
}
|
|
}
|
|
|
|
/* Update any object constraints to use the new bone name */
|
|
for(cob= G.main->object.first; cob; cob= cob->id.next) {
|
|
if(cob->constraints.first)
|
|
constraint_bone_name_fix(ob, &cob->constraints, oldname, newname);
|
|
if (cob->pose) {
|
|
bPoseChannel *pchan;
|
|
for (pchan = cob->pose->chanbase.first; pchan; pchan=pchan->next) {
|
|
constraint_bone_name_fix(ob, &pchan->constraints, oldname, newname);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* See if an object is parented to this armature */
|
|
if (ob->parent && (ob->parent->data == arm)) {
|
|
if(ob->partype==PARBONE) {
|
|
/* bone name in object */
|
|
if (!strcmp(ob->parsubstr, oldname))
|
|
BLI_strncpy(ob->parsubstr, newname, MAXBONENAME);
|
|
}
|
|
}
|
|
|
|
if(modifiers_usesArmature(ob, arm)) {
|
|
bDeformGroup *dg;
|
|
/* bone name in defgroup */
|
|
for (dg=ob->defbase.first; dg; dg=dg->next) {
|
|
if(!strcmp(dg->name, oldname))
|
|
BLI_strncpy(dg->name, newname, MAXBONENAME);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* context editmode object */
|
|
void armature_flip_names(void)
|
|
{
|
|
bArmature *arm= G.obedit->data;
|
|
EditBone *ebone;
|
|
char newname[32];
|
|
|
|
for (ebone = G.edbo.first; ebone; ebone=ebone->next) {
|
|
if(arm->layer & ebone->layer) {
|
|
if(ebone->flag & BONE_SELECTED) {
|
|
BLI_strncpy(newname, ebone->name, sizeof(newname));
|
|
bone_flip_name(newname, 1); // 1 = do strip off number extensions
|
|
armature_bone_rename(G.obedit->data, ebone->name, newname);
|
|
}
|
|
}
|
|
}
|
|
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
allqueue(REDRAWBUTSEDIT, 0);
|
|
allqueue(REDRAWBUTSOBJECT, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWOOPS, 0);
|
|
BIF_undo_push("Flip names");
|
|
}
|
|
|
|
/* context: edtimode armature */
|
|
void armature_autoside_names(short axis)
|
|
{
|
|
bArmature *arm= G.obedit->data;
|
|
EditBone *ebone;
|
|
char newname[32];
|
|
|
|
for (ebone = G.edbo.first; ebone; ebone=ebone->next) {
|
|
if (arm->layer & ebone->layer) {
|
|
if (ebone->flag & BONE_SELECTED) {
|
|
BLI_strncpy(newname, ebone->name, sizeof(newname));
|
|
bone_autoside_name(newname, 1, axis, ebone->head[axis], ebone->tail[axis]);
|
|
armature_bone_rename(G.obedit->data, ebone->name, newname);
|
|
}
|
|
}
|
|
}
|
|
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
allqueue(REDRAWBUTSEDIT, 0);
|
|
allqueue(REDRAWBUTSOBJECT, 0);
|
|
allqueue(REDRAWACTION, 0);
|
|
allqueue(REDRAWOOPS, 0);
|
|
BIF_undo_push("Auto-side name");
|
|
}
|
|
|
|
/* context: editmode armature */
|
|
EditBone *armature_bone_get_mirrored(EditBone *ebo)
|
|
{
|
|
EditBone *eboflip= NULL;
|
|
char name[32];
|
|
|
|
BLI_strncpy(name, ebo->name, sizeof(name));
|
|
bone_flip_name(name, 0); // 0 = don't strip off number extensions
|
|
|
|
for (eboflip=G.edbo.first; eboflip; eboflip=eboflip->next)
|
|
if(ebo!=eboflip)
|
|
if (!strcmp (name, eboflip->name)) break;
|
|
|
|
return eboflip;
|
|
}
|
|
|
|
/* if editbone (partial) selected, copy data */
|
|
/* context; editmode armature, with mirror editing enabled */
|
|
void transform_armature_mirror_update(void)
|
|
{
|
|
EditBone *ebo, *eboflip;
|
|
|
|
for (ebo=G.edbo.first; ebo; ebo=ebo->next) {
|
|
/* no layer check, correct mirror is more important */
|
|
if(ebo->flag & (BONE_TIPSEL|BONE_ROOTSEL)) {
|
|
|
|
eboflip= armature_bone_get_mirrored(ebo);
|
|
|
|
if(eboflip) {
|
|
/* we assume X-axis flipping for now */
|
|
if(ebo->flag & BONE_TIPSEL) {
|
|
eboflip->tail[0]= -ebo->tail[0];
|
|
eboflip->tail[1]= ebo->tail[1];
|
|
eboflip->tail[2]= ebo->tail[2];
|
|
eboflip->rad_tail= ebo->rad_tail;
|
|
}
|
|
if(ebo->flag & BONE_ROOTSEL) {
|
|
eboflip->head[0]= -ebo->head[0];
|
|
eboflip->head[1]= ebo->head[1];
|
|
eboflip->head[2]= ebo->head[2];
|
|
eboflip->rad_head= ebo->rad_head;
|
|
}
|
|
if(ebo->flag & BONE_SELECTED) {
|
|
eboflip->dist= ebo->dist;
|
|
eboflip->roll= -ebo->roll;
|
|
eboflip->xwidth= ebo->xwidth;
|
|
eboflip->zwidth= ebo->zwidth;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************************************/
|
|
/*************************************** SKELETON GENERATOR ******************************************/
|
|
/*****************************************************************************************************/
|
|
|
|
/**************************************** SYMMETRY HANDLING ******************************************/
|
|
|
|
void markdownSymmetryArc(ReebArc *arc, ReebNode *node, int level);
|
|
|
|
void mirrorAlongAxis(float v[3], float center[3], float axis[3])
|
|
{
|
|
float dv[3], pv[3];
|
|
|
|
VecSubf(dv, v, center);
|
|
Projf(pv, dv, axis);
|
|
VecMulf(pv, -2);
|
|
VecAddf(v, v, pv);
|
|
}
|
|
|
|
/* Helper structure for radial symmetry */
|
|
typedef struct RadialArc
|
|
{
|
|
ReebArc *arc;
|
|
float n[3]; /* normalized vector joining the nodes of the arc */
|
|
} RadialArc;
|
|
|
|
void reestablishRadialSymmetry(ReebNode *node, int depth, float axis[3])
|
|
{
|
|
RadialArc *ring = NULL;
|
|
RadialArc *unit;
|
|
float limit = G.scene->toolsettings->skgen_symmetry_limit;
|
|
int symmetric = 1;
|
|
int count = 0;
|
|
int i;
|
|
|
|
/* count the number of arcs in the symmetry ring */
|
|
for (i = 0; node->arcs[i] != NULL; i++)
|
|
{
|
|
ReebArc *connectedArc = node->arcs[i];
|
|
|
|
/* depth is store as a negative in flag. symmetry level is positive */
|
|
if (connectedArc->flags == -depth)
|
|
{
|
|
count++;
|
|
}
|
|
}
|
|
|
|
ring = MEM_callocN(sizeof(RadialArc) * count, "radial symmetry ring");
|
|
unit = ring;
|
|
|
|
/* fill in the ring */
|
|
for (unit = ring, i = 0; node->arcs[i] != NULL; i++)
|
|
{
|
|
ReebArc *connectedArc = node->arcs[i];
|
|
|
|
/* depth is store as a negative in flag. symmetry level is positive */
|
|
if (connectedArc->flags == -depth)
|
|
{
|
|
ReebNode *otherNode = OTHER_NODE(connectedArc, node);
|
|
float vec[3];
|
|
|
|
unit->arc = connectedArc;
|
|
|
|
/* project the node to node vector on the symmetry plane */
|
|
VecSubf(unit->n, otherNode->p, node->p);
|
|
Projf(vec, unit->n, axis);
|
|
VecSubf(unit->n, unit->n, vec);
|
|
|
|
Normalize(unit->n);
|
|
|
|
unit++;
|
|
}
|
|
}
|
|
|
|
/* sort ring */
|
|
for (i = 0; i < count - 1; i++)
|
|
{
|
|
float minAngle = 3; /* arbitrary high value, higher than 2, at least */
|
|
int minIndex = -1;
|
|
int j;
|
|
|
|
for (j = i + 1; j < count; j++)
|
|
{
|
|
float angle = Inpf(ring[i].n, ring[j].n);
|
|
|
|
/* map negative values to 1..2 */
|
|
if (angle < 0)
|
|
{
|
|
angle = 1 - angle;
|
|
}
|
|
|
|
if (angle < minAngle)
|
|
{
|
|
minIndex = j;
|
|
minAngle = angle;
|
|
}
|
|
}
|
|
|
|
/* swap if needed */
|
|
if (minIndex != i + 1)
|
|
{
|
|
RadialArc tmp;
|
|
tmp = ring[i + 1];
|
|
ring[i + 1] = ring[minIndex];
|
|
ring[minIndex] = tmp;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < count && symmetric; i++)
|
|
{
|
|
ReebNode *node1, *node2;
|
|
float tangent[3];
|
|
float normal[3];
|
|
float p[3];
|
|
int j = (i + 1) % count; /* next arc in the circular list */
|
|
|
|
VecAddf(tangent, ring[i].n, ring[j].n);
|
|
Crossf(normal, tangent, axis);
|
|
|
|
node1 = OTHER_NODE(ring[i].arc, node);
|
|
node2 = OTHER_NODE(ring[j].arc, node);
|
|
|
|
VECCOPY(p, node2->p);
|
|
mirrorAlongAxis(p, node->p, normal);
|
|
|
|
/* check if it's within limit before continuing */
|
|
if (VecLenf(node1->p, p) > limit)
|
|
{
|
|
symmetric = 0;
|
|
}
|
|
|
|
}
|
|
|
|
if (symmetric)
|
|
{
|
|
/* first pass, merge incrementally */
|
|
for (i = 0; i < count - 1; i++)
|
|
{
|
|
ReebNode *node1, *node2;
|
|
float tangent[3];
|
|
float normal[3];
|
|
int j = i + 1;
|
|
|
|
VecAddf(tangent, ring[i].n, ring[j].n);
|
|
Crossf(normal, tangent, axis);
|
|
|
|
node1 = OTHER_NODE(ring[i].arc, node);
|
|
node2 = OTHER_NODE(ring[j].arc, node);
|
|
|
|
/* mirror first node and mix with the second */
|
|
mirrorAlongAxis(node1->p, node->p, normal);
|
|
VecLerpf(node2->p, node2->p, node1->p, 1.0f / (j + 1));
|
|
|
|
/* Merge buckets
|
|
* there shouldn't be any null arcs here, but just to be safe
|
|
* */
|
|
if (ring[i].arc->bcount > 0 && ring[j].arc->bcount > 0)
|
|
{
|
|
ReebArcIterator iter1, iter2;
|
|
EmbedBucket *bucket1 = NULL, *bucket2 = NULL;
|
|
|
|
initArcIterator(&iter1, ring[i].arc, node);
|
|
initArcIterator(&iter2, ring[j].arc, node);
|
|
|
|
bucket1 = nextBucket(&iter1);
|
|
bucket2 = nextBucket(&iter2);
|
|
|
|
/* Make sure they both start at the same value */
|
|
while(bucket1 && bucket1->val < bucket2->val)
|
|
{
|
|
bucket1 = nextBucket(&iter1);
|
|
}
|
|
|
|
while(bucket2 && bucket2->val < bucket1->val)
|
|
{
|
|
bucket2 = nextBucket(&iter2);
|
|
}
|
|
|
|
|
|
for ( ;bucket1 && bucket2; bucket1 = nextBucket(&iter1), bucket2 = nextBucket(&iter2))
|
|
{
|
|
bucket2->nv += bucket1->nv; /* add counts */
|
|
|
|
/* mirror on axis */
|
|
mirrorAlongAxis(bucket1->p, node->p, normal);
|
|
/* add bucket2 in bucket1 */
|
|
VecLerpf(bucket2->p, bucket2->p, bucket1->p, (float)bucket1->nv / (float)(bucket2->nv));
|
|
}
|
|
}
|
|
}
|
|
|
|
/* second pass, mirror back on previous arcs */
|
|
for (i = count - 1; i > 0; i--)
|
|
{
|
|
ReebNode *node1, *node2;
|
|
float tangent[3];
|
|
float normal[3];
|
|
int j = i - 1;
|
|
|
|
VecAddf(tangent, ring[i].n, ring[j].n);
|
|
Crossf(normal, tangent, axis);
|
|
|
|
node1 = OTHER_NODE(ring[i].arc, node);
|
|
node2 = OTHER_NODE(ring[j].arc, node);
|
|
|
|
/* copy first node than mirror */
|
|
VECCOPY(node2->p, node1->p);
|
|
mirrorAlongAxis(node2->p, node->p, normal);
|
|
|
|
/* Copy buckets
|
|
* there shouldn't be any null arcs here, but just to be safe
|
|
* */
|
|
if (ring[i].arc->bcount > 0 && ring[j].arc->bcount > 0)
|
|
{
|
|
ReebArcIterator iter1, iter2;
|
|
EmbedBucket *bucket1 = NULL, *bucket2 = NULL;
|
|
|
|
initArcIterator(&iter1, ring[i].arc, node);
|
|
initArcIterator(&iter2, ring[j].arc, node);
|
|
|
|
bucket1 = nextBucket(&iter1);
|
|
bucket2 = nextBucket(&iter2);
|
|
|
|
/* Make sure they both start at the same value */
|
|
while(bucket1 && bucket1->val < bucket2->val)
|
|
{
|
|
bucket1 = nextBucket(&iter1);
|
|
}
|
|
|
|
while(bucket2 && bucket2->val < bucket1->val)
|
|
{
|
|
bucket2 = nextBucket(&iter2);
|
|
}
|
|
|
|
|
|
for ( ;bucket1 && bucket2; bucket1 = nextBucket(&iter1), bucket2 = nextBucket(&iter2))
|
|
{
|
|
/* copy and mirror back to bucket2 */
|
|
bucket2->nv = bucket1->nv;
|
|
VECCOPY(bucket2->p, bucket1->p);
|
|
mirrorAlongAxis(bucket2->p, node->p, normal);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
MEM_freeN(ring);
|
|
}
|
|
|
|
void reestablishAxialSymmetry(ReebNode *node, int depth, float axis[3])
|
|
{
|
|
ReebArc *arc1 = NULL;
|
|
ReebArc *arc2 = NULL;
|
|
ReebNode *node1 = NULL, *node2 = NULL;
|
|
float limit = G.scene->toolsettings->skgen_symmetry_limit;
|
|
float nor[3], vec[3], p[3];
|
|
int i;
|
|
|
|
for (i = 0; node->arcs[i] != NULL; i++)
|
|
{
|
|
ReebArc *connectedArc = node->arcs[i];
|
|
|
|
/* depth is store as a negative in flag. symmetry level is positive */
|
|
if (connectedArc->flags == -depth)
|
|
{
|
|
if (arc1 == NULL)
|
|
{
|
|
arc1 = connectedArc;
|
|
node1 = OTHER_NODE(arc1, node);
|
|
}
|
|
else
|
|
{
|
|
arc2 = connectedArc;
|
|
node2 = OTHER_NODE(arc2, node);
|
|
break; /* Can stop now, the two arcs have been found */
|
|
}
|
|
}
|
|
}
|
|
|
|
/* shouldn't happen, but just to be sure */
|
|
if (node1 == NULL || node2 == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
VecSubf(p, node1->p, node->p);
|
|
Crossf(vec, p, axis);
|
|
Crossf(nor, vec, axis);
|
|
|
|
/* mirror node2 along axis */
|
|
VECCOPY(p, node2->p);
|
|
mirrorAlongAxis(p, node->p, nor);
|
|
|
|
/* check if it's within limit before continuing */
|
|
if (VecLenf(node1->p, p) <= limit)
|
|
{
|
|
|
|
/* average with node1 */
|
|
VecAddf(node1->p, node1->p, p);
|
|
VecMulf(node1->p, 0.5f);
|
|
|
|
/* mirror back on node2 */
|
|
VECCOPY(node2->p, node1->p);
|
|
mirrorAlongAxis(node2->p, node->p, nor);
|
|
|
|
/* Merge buckets
|
|
* there shouldn't be any null arcs here, but just to be safe
|
|
* */
|
|
if (arc1->bcount > 0 && arc2->bcount > 0)
|
|
{
|
|
ReebArcIterator iter1, iter2;
|
|
EmbedBucket *bucket1 = NULL, *bucket2 = NULL;
|
|
|
|
initArcIterator(&iter1, arc1, node);
|
|
initArcIterator(&iter2, arc2, node);
|
|
|
|
bucket1 = nextBucket(&iter1);
|
|
bucket2 = nextBucket(&iter2);
|
|
|
|
/* Make sure they both start at the same value */
|
|
while(bucket1 && bucket1->val < bucket2->val)
|
|
{
|
|
bucket1 = nextBucket(&iter1);
|
|
}
|
|
|
|
while(bucket2 && bucket2->val < bucket1->val)
|
|
{
|
|
bucket2 = nextBucket(&iter2);
|
|
}
|
|
|
|
|
|
for ( ;bucket1 && bucket2; bucket1 = nextBucket(&iter1), bucket2 = nextBucket(&iter2))
|
|
{
|
|
bucket1->nv += bucket2->nv; /* add counts */
|
|
|
|
/* mirror on axis */
|
|
mirrorAlongAxis(bucket2->p, node->p, nor);
|
|
/* add bucket2 in bucket1 */
|
|
VecLerpf(bucket1->p, bucket1->p, bucket2->p, (float)bucket2->nv / (float)(bucket1->nv));
|
|
|
|
/* copy and mirror back to bucket2 */
|
|
bucket2->nv = bucket1->nv;
|
|
VECCOPY(bucket2->p, bucket1->p);
|
|
mirrorAlongAxis(bucket2->p, node->p, nor);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void markdownSecondarySymmetry(ReebNode *node, int depth, int level)
|
|
{
|
|
float axis[3] = {0, 0, 0};
|
|
int count = 0;
|
|
int i;
|
|
|
|
/* Only reestablish spatial symmetry if needed */
|
|
if (G.scene->toolsettings->skgen_options & SKGEN_SYMMETRY)
|
|
{
|
|
/* count the number of branches in this symmetry group
|
|
* and determinte the axis of symmetry
|
|
* */
|
|
for (i = 0; node->arcs[i] != NULL; i++)
|
|
{
|
|
ReebArc *connectedArc = node->arcs[i];
|
|
|
|
/* depth is store as a negative in flag. symmetry level is positive */
|
|
if (connectedArc->flags == -depth)
|
|
{
|
|
count++;
|
|
}
|
|
/* If arc is on the axis */
|
|
else if (connectedArc->flags == level)
|
|
{
|
|
VecAddf(axis, axis, connectedArc->v1->p);
|
|
VecSubf(axis, axis, connectedArc->v2->p);
|
|
}
|
|
}
|
|
|
|
Normalize(axis);
|
|
|
|
/* Split between axial and radial symmetry */
|
|
if (count == 2)
|
|
{
|
|
reestablishAxialSymmetry(node, depth, axis);
|
|
}
|
|
else
|
|
{
|
|
reestablishRadialSymmetry(node, depth, axis);
|
|
}
|
|
}
|
|
|
|
/* markdown secondary symetries */
|
|
for (i = 0; node->arcs[i] != NULL; i++)
|
|
{
|
|
ReebArc *connectedArc = node->arcs[i];
|
|
|
|
if (connectedArc->flags == -depth)
|
|
{
|
|
/* markdown symmetry for branches corresponding to the depth */
|
|
markdownSymmetryArc(connectedArc, node, level + 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void markdownSymmetryArc(ReebArc *arc, ReebNode *node, int level)
|
|
{
|
|
int i;
|
|
arc->flags = level;
|
|
|
|
node = OTHER_NODE(arc, node);
|
|
|
|
for (i = 0; node->arcs[i] != NULL; i++)
|
|
{
|
|
ReebArc *connectedArc = node->arcs[i];
|
|
|
|
if (connectedArc != arc)
|
|
{
|
|
ReebNode *connectedNode = OTHER_NODE(connectedArc, node);
|
|
|
|
/* symmetry level is positive value, negative values is subtree depth */
|
|
connectedArc->flags = -subtreeDepth(connectedNode, connectedArc);
|
|
}
|
|
}
|
|
|
|
arc = NULL;
|
|
|
|
for (i = 0; node->arcs[i] != NULL; i++)
|
|
{
|
|
int issymmetryAxis = 0;
|
|
ReebArc *connectedArc = node->arcs[i];
|
|
|
|
/* only arcs not already marked as symetric */
|
|
if (connectedArc->flags < 0)
|
|
{
|
|
int j;
|
|
|
|
/* true by default */
|
|
issymmetryAxis = 1;
|
|
|
|
for (j = 0; node->arcs[j] != NULL && issymmetryAxis == 1; j++)
|
|
{
|
|
ReebArc *otherArc = node->arcs[j];
|
|
|
|
/* different arc, same depth */
|
|
if (otherArc != connectedArc && otherArc->flags == connectedArc->flags)
|
|
{
|
|
/* not on the symmetry axis */
|
|
issymmetryAxis = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* arc could be on the symmetry axis */
|
|
if (issymmetryAxis == 1)
|
|
{
|
|
/* no arc as been marked previously, keep this one */
|
|
if (arc == NULL)
|
|
{
|
|
arc = connectedArc;
|
|
}
|
|
else
|
|
{
|
|
/* there can't be more than one symmetry arc */
|
|
arc = NULL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* go down the arc continuing the symmetry axis */
|
|
if (arc)
|
|
{
|
|
markdownSymmetryArc(arc, node, level);
|
|
}
|
|
|
|
|
|
/* secondary symmetry */
|
|
for (i = 0; node->arcs[i] != NULL; i++)
|
|
{
|
|
ReebArc *connectedArc = node->arcs[i];
|
|
|
|
/* only arcs not already marked as symetric and is not the next arc on the symmetry axis */
|
|
if (connectedArc->flags < 0)
|
|
{
|
|
/* subtree depth is store as a negative value in the flag */
|
|
markdownSecondarySymmetry(node, -connectedArc->flags, level);
|
|
}
|
|
}
|
|
}
|
|
|
|
void markdownSymmetry(ReebGraph *rg)
|
|
{
|
|
ReebNode *node;
|
|
ReebArc *arc;
|
|
/* only for Acyclic graphs */
|
|
int cyclic = isGraphCyclic(rg);
|
|
|
|
/* mark down all arcs as non-symetric */
|
|
for (arc = rg->arcs.first; arc; arc = arc->next)
|
|
{
|
|
arc->flags = 0;
|
|
}
|
|
|
|
/* mark down all nodes as not on the symmetry axis */
|
|
for (node = rg->nodes.first; node; node = node->next)
|
|
{
|
|
node->flags = 0;
|
|
}
|
|
|
|
/* node list is sorted, so lowest node is always the head (by design) */
|
|
node = rg->nodes.first;
|
|
|
|
/* only work on acyclic graphs and if only one arc is incident on the first node */
|
|
if (cyclic == 0 && countConnectedArcs(rg, node) == 1)
|
|
{
|
|
arc = node->arcs[0];
|
|
|
|
markdownSymmetryArc(arc, node, 1);
|
|
|
|
/* mark down non-symetric arcs */
|
|
for (arc = rg->arcs.first; arc; arc = arc->next)
|
|
{
|
|
if (arc->flags < 0)
|
|
{
|
|
arc->flags = 0;
|
|
}
|
|
else
|
|
{
|
|
/* mark down nodes with the lowest level symmetry axis */
|
|
if (arc->v1->flags == 0 || arc->v1->flags > arc->flags)
|
|
{
|
|
arc->v1->flags = arc->flags;
|
|
}
|
|
if (arc->v2->flags == 0 || arc->v2->flags > arc->flags)
|
|
{
|
|
arc->v2->flags = arc->flags;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**************************************** SUBDIVISION ALGOS ******************************************/
|
|
|
|
EditBone * subdivideByAngle(ReebArc *arc, ReebNode *head, ReebNode *tail)
|
|
{
|
|
EditBone *lastBone = NULL;
|
|
if (G.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(G.scene->toolsettings->skgen_angle_limit * M_PI / 180.0f);
|
|
|
|
parent = add_editbone("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("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(parent);
|
|
parent = NULL;
|
|
}
|
|
|
|
lastBone = parent; /* set last bone in the chain */
|
|
}
|
|
|
|
return lastBone;
|
|
}
|
|
|
|
float calcCorrelation(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 1.0f - s_xyz / s_t;
|
|
}
|
|
else
|
|
{
|
|
return 1.0f;
|
|
}
|
|
}
|
|
|
|
EditBone * subdivideByCorrelation(ReebArc *arc, ReebNode *head, ReebNode *tail)
|
|
{
|
|
ReebArcIterator iter;
|
|
float n[3];
|
|
float CORRELATION_THRESHOLD = G.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 (G.scene->toolsettings->skgen_options & SKGEN_CUT_CORRELATION &&
|
|
calcCorrelation(arc, iter.start, iter.end, head->p, n) < CORRELATION_THRESHOLD)
|
|
{
|
|
EmbedBucket *bucket = NULL;
|
|
EmbedBucket *previous = NULL;
|
|
EditBone *child = NULL;
|
|
EditBone *parent = NULL;
|
|
int boneStart = iter.start;
|
|
|
|
parent = add_editbone("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))
|
|
{
|
|
/* Calculate normal */
|
|
VecSubf(n, bucket->p, parent->head);
|
|
|
|
if (calcCorrelation(arc, boneStart, iter.index, parent->head, n) < CORRELATION_THRESHOLD)
|
|
{
|
|
VECCOPY(parent->tail, previous->p);
|
|
|
|
child = add_editbone("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
|
|
}
|
|
}
|
|
|
|
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->v1->p, arc->v2->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->v1->p, arc->buckets[0].p);
|
|
embedLength += VecLenf(arc->v2->p, arc->buckets[arc->bcount - 1].p);
|
|
}
|
|
else
|
|
{
|
|
embedLength = arcLength;
|
|
}
|
|
|
|
return embedLength / arcLength;
|
|
}
|
|
|
|
EditBone * subdivideByLength(ReebArc *arc, ReebNode *head, ReebNode *tail)
|
|
{
|
|
EditBone *lastBone = NULL;
|
|
if ((G.scene->toolsettings->skgen_options & SKGEN_CUT_LENGTH) &&
|
|
arcLengthRatio(arc) >= G.scene->toolsettings->skgen_length_ratio)
|
|
{
|
|
ReebArcIterator iter;
|
|
EmbedBucket *bucket = NULL;
|
|
EmbedBucket *previous = NULL;
|
|
EditBone *child = NULL;
|
|
EditBone *parent = NULL;
|
|
float lengthLimit = G.scene->toolsettings->skgen_length_limit;
|
|
int same = 0;
|
|
|
|
parent = add_editbone("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("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(ReebGraph *rg)
|
|
{
|
|
GHash *arcBoneMap = NULL;
|
|
ReebArc *arc = NULL;
|
|
ReebNode *node = NULL;
|
|
Object *src = NULL;
|
|
Object *dst = NULL;
|
|
|
|
src = BASACT->object;
|
|
|
|
if (G.obedit != NULL)
|
|
{
|
|
exit_editmode(EM_FREEDATA|EM_FREEUNDO|EM_WAITCURSOR); // freedata, and undo
|
|
}
|
|
|
|
setcursor_space(SPACE_VIEW3D, CURSOR_WAIT);
|
|
|
|
dst = add_object(OB_ARMATURE);
|
|
base_init_from_view3d(BASACT, G.vd);
|
|
G.obedit= 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(G.obedit);
|
|
|
|
make_editArmature();
|
|
|
|
arcBoneMap = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp);
|
|
|
|
markdownSymmetry(rg);
|
|
|
|
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 (flags == 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 flags: 1 (low -> high), -1 (high -> low)
|
|
*/
|
|
|
|
/* if arc is a symmetry axis, internal bones go up the tree */
|
|
if (arc->flags == 1 && arc->v2->degree != 1)
|
|
{
|
|
head = arc->v2;
|
|
tail = arc->v1;
|
|
|
|
arc->flags = -1; /* mark arc direction */
|
|
}
|
|
/* Bones point AWAY from the symmetry axis */
|
|
else if (arc->v1->flags == 1)
|
|
{
|
|
head = arc->v1;
|
|
tail = arc->v2;
|
|
|
|
arc->flags = 1; /* mark arc direction */
|
|
}
|
|
else if (arc->v2->flags == 1)
|
|
{
|
|
head = arc->v2;
|
|
tail = arc->v1;
|
|
|
|
arc->flags = -1; /* mark arc direction */
|
|
}
|
|
/* otherwise, always go from low weight to high weight */
|
|
else
|
|
{
|
|
head = arc->v1;
|
|
tail = arc->v2;
|
|
|
|
arc->flags = 1; /* mark arc direction */
|
|
}
|
|
|
|
/* Loop over subdivision methods */
|
|
for (i = 0; lastBone == NULL && i < SKGEN_SUB_TOTAL; i++)
|
|
{
|
|
switch(G.scene->toolsettings->skgen_subdivisions[i])
|
|
{
|
|
case SKGEN_SUB_LENGTH:
|
|
lastBone = subdivideByLength(arc, head, tail);
|
|
break;
|
|
case SKGEN_SUB_ANGLE:
|
|
lastBone = subdivideByAngle(arc, head, tail);
|
|
break;
|
|
case SKGEN_SUB_CORRELATION:
|
|
lastBone = subdivideByCorrelation(arc, head, tail);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (lastBone == NULL)
|
|
{
|
|
EditBone *bone;
|
|
bone = add_editbone("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; node->arcs[i] != NULL; i++)
|
|
{
|
|
arc = node->arcs[i];
|
|
|
|
/* if arc is incoming into the node */
|
|
if ((arc->v1 == node && arc->flags == -1) || (arc->v2 == node && arc->flags == 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; node->arcs[i] != NULL; i++)
|
|
{
|
|
arc = node->arcs[i];
|
|
|
|
/* if arc is outgoing from the node */
|
|
if ((arc->v1 == node && arc->flags == 1) || (arc->v2 == node && arc->flags == -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);
|
|
|
|
setcursor_space(SPACE_VIEW3D, CURSOR_EDIT);
|
|
|
|
BIF_undo_push("Generate Skeleton");
|
|
}
|
|
|
|
void generateSkeleton(void)
|
|
{
|
|
EditMesh *em = G.editMesh;
|
|
ReebGraph *rg = NULL;
|
|
int i;
|
|
|
|
if (em == NULL)
|
|
return;
|
|
|
|
setcursor_space(SPACE_VIEW3D, CURSOR_WAIT);
|
|
|
|
if (weightFromDistance(em) == 0)
|
|
{
|
|
error("No selected vertex\n");
|
|
return;
|
|
}
|
|
|
|
renormalizeWeight(em, 1.0f);
|
|
|
|
weightToHarmonic(em);
|
|
|
|
#ifdef DEBUG_REEB
|
|
weightToVCol(em);
|
|
#endif
|
|
|
|
rg = generateReebGraph(em, G.scene->toolsettings->skgen_resolution);
|
|
|
|
verifyBuckets(rg);
|
|
|
|
/* Remove arcs without embedding */
|
|
filterNullReebGraph(rg);
|
|
|
|
verifyBuckets(rg);
|
|
|
|
|
|
i = 1;
|
|
/* filter until there's nothing more to do */
|
|
while (i == 1)
|
|
{
|
|
i = 0; /* no work done yet */
|
|
|
|
if (G.scene->toolsettings->skgen_options & SKGEN_FILTER_EXTERNAL)
|
|
{
|
|
i |= filterExternalReebGraph(rg, G.scene->toolsettings->skgen_threshold_external * G.scene->toolsettings->skgen_resolution);
|
|
}
|
|
|
|
verifyBuckets(rg);
|
|
|
|
if (G.scene->toolsettings->skgen_options & SKGEN_FILTER_INTERNAL)
|
|
{
|
|
i |= filterInternalReebGraph(rg, G.scene->toolsettings->skgen_threshold_internal * G.scene->toolsettings->skgen_resolution);
|
|
}
|
|
}
|
|
|
|
verifyBuckets(rg);
|
|
|
|
repositionNodes(rg);
|
|
|
|
verifyBuckets(rg);
|
|
|
|
/* Filtering might have created degree 2 nodes, so remove them */
|
|
removeNormalNodes(rg);
|
|
|
|
verifyBuckets(rg);
|
|
|
|
for(i = 0; i < G.scene->toolsettings->skgen_postpro_passes; i++)
|
|
{
|
|
postprocessGraph(rg, G.scene->toolsettings->skgen_postpro);
|
|
}
|
|
|
|
buildAdjacencyList(rg);
|
|
|
|
sortNodes(rg);
|
|
|
|
sortArcs(rg);
|
|
|
|
generateSkeletonFromReebGraph(rg);
|
|
|
|
freeGraph(rg);
|
|
}
|