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