Two new IK features.

1) Target-less IK

If you add an IK constraint without a target set (no object or bone target),
it now can be grabbed and moved with IK, using its own Bone tip or root as
target itself. This way you can use IK for posing, without having the IK
executed while it animates or while a Pose is being solved for real IK.

After grabbing "Target-less IK", it applies the resulted motion in the
pose-channels, which then can be used to insert keypositions.

The Target-less IK bone can still be rotated without IK, also its chain
can be edited as usual.

UI: The CTRL+I menu gives this as an option too. In the 3D window it is
drawn with orangish color.

Note that IK is not resistant to non-uniform scaling yet.

2) Auto-IK

When the option "Automatic IK" is set, in Edit Buttons Armature Panel,
it creates automatic temporal Target-less IK for the Bone you grab or
translate.

The rules are:

- it only works when a single Bone is selected
- if the Bone is a root bone (no parent), it adds IK to the end of the
  chain(s)
- otherwise it adds the IK to the active Bone
- the temporal IK chain only consists of connected Bones.

This method is still a bit experimental. Maybe it should become a special
grabbing option (like SHIFT+G in Pose Mode). It also only works OK for rigs
that fit for it well... when a rig already is fully setup with IK it can't
do much good. :)
This commit is contained in:
2005-10-30 13:50:42 +00:00
parent b7a465b437
commit ea8b08c2cc
11 changed files with 434 additions and 107 deletions

View File

@@ -292,6 +292,10 @@ void update_pose_constraint_flags(bPose *pose)
bKinematicConstraint *data = (bKinematicConstraint*)con->data;
pchan->constflag |= PCHAN_HAS_IK;
if(data->tar==NULL || (data->tar->type==OB_ARMATURE && data->subtarget[0]==0))
pchan->constflag |= PCHAN_HAS_TARGET;
/* negative rootbone = recalc rootbone index. used in do_versions */
if(data->rootbone<0) {
data->rootbone= 0;

View File

@@ -986,7 +986,7 @@ void armature_rebuild_pose(Object *ob, bArmature *arm)
/* allocates PoseTree, and links that to root bone/channel */
/* note; if we got this working, it can become static too? */
/* Note: detecting the IK chain is duplicate code... in drawarmature.c and in transform_conversions.c */
static void initialize_posetree(struct Object *ob, bPoseChannel *pchan_tip)
{
bPoseChannel *curchan, *pchan_root=NULL, *chanlist[256], **oldchan;
@@ -1001,11 +1001,16 @@ static void initialize_posetree(struct Object *ob, bPoseChannel *pchan_tip)
if(con->type==CONSTRAINT_TYPE_KINEMATIC) break;
}
if(con==NULL) return;
if(con->flag & CONSTRAINT_DISABLE) return; // not sure...
data=(bKinematicConstraint*)con->data;
if(data->tar==NULL) return;
if(data->tar->type==OB_ARMATURE && data->subtarget[0]==0) return;
/* two types of targets */
if(data->flag & CONSTRAINT_IK_AUTO);
else {
if(con->flag & CONSTRAINT_DISABLE) return; /* checked in editconstraint.c */
if(data->tar==NULL) return;
if(data->tar->type==OB_ARMATURE && data->subtarget[0]==0) return;
}
/* exclude tip from chain? */
if(!(data->flag & CONSTRAINT_IK_TIP))
@@ -1232,8 +1237,8 @@ static void execute_posetree(Object *ob, PoseTree *tree)
for(target=tree->targets.first; target; target=target->next) {
data= (bKinematicConstraint*)target->con->data;
/* 1.0=ctime */
get_constraint_target_matrix(target->con, TARGET_BONE, NULL, rootmat, size, 1.0);
/* 1.0=ctime, we pass on object for auto-ik<69> */
get_constraint_target_matrix(target->con, TARGET_BONE, ob, rootmat, size, 1.0);
/* and set and transform goal */
Mat4MulMat4(goal, rootmat, goalinv);

View File

@@ -748,7 +748,7 @@ static void constraint_target_to_mat4 (Object *ob, const char *substring, float
/* called during solve_constraints */
/* also for make_parent, to find correct inverse of "follow path" */
/* warning, ownerdata is void... is not Bone anymore, but posechannel */
/* warning, ownerdata is void... is not Bone anymore, but PoseChannel or Object */
short get_constraint_target_matrix (bConstraint *con, short ownertype, void* ownerdata, float mat[][4], float size[3], float ctime)
{
short valid=0;
@@ -908,6 +908,20 @@ short get_constraint_target_matrix (bConstraint *con, short ownertype, void* own
constraint_target_to_mat4(data->tar, data->subtarget, mat, size, ctime);
valid=1;
}
else if (data->flag & CONSTRAINT_IK_AUTO) {
Object *ob= ownerdata;
if(ob==NULL)
Mat4One(mat);
else {
float vec[3];
/* move grabtarget into world space */
VECCOPY(vec, data->grabtarget);
Mat4MulVecfl(ob->obmat, vec);
Mat4CpyMat4(mat, ob->obmat);
VECCOPY(mat[3], vec);
}
}
else
Mat4One (mat);
}

View File

@@ -147,6 +147,7 @@ enum {
#define PCHAN_HAS_CONST 2
/* only used for drawing Posemode, not stored in channel */
#define PCHAN_HAS_ACTION 4
#define PCHAN_HAS_TARGET 8
/* PoseChannel->ikflag */
#define BONE_IK_NO_XDOF 1

View File

@@ -84,16 +84,17 @@ typedef struct bArmature {
/* dont use bit 7, was saved in files to disable stuff */
/* armature->flag */
#define ARM_RESTPOS 0x0001
#define ARM_RESTPOS 0x001
/* XRAY is here only for backwards converting */
#define ARM_DRAWXRAY 0x0002
#define ARM_DRAWAXES 0x0004
#define ARM_DRAWNAMES 0x0008
#define ARM_POSEMODE 0x0010
#define ARM_EDITMODE 0x0020
#define ARM_DELAYDEFORM 0x0040
#define ARM_DONT_USE 0x0080
#define ARM_MIRROR_EDIT 0x0100
#define ARM_DRAWXRAY 0x002
#define ARM_DRAWAXES 0x004
#define ARM_DRAWNAMES 0x008
#define ARM_POSEMODE 0x010
#define ARM_EDITMODE 0x020
#define ARM_DELAYDEFORM 0x040
#define ARM_DONT_USE 0x080
#define ARM_MIRROR_EDIT 0x100
#define ARM_AUTO_IK 0x200
/* armature->drawtype */
#define ARM_OCTA 0

View File

@@ -70,6 +70,8 @@ typedef struct bKinematicConstraint{
float weight; /* Weight of goal in IK tree */
float orientweight; /* Amount of rotation a target applies on chain */
float grabtarget[3]; /* for target-less IK */
float pad2;
} bKinematicConstraint;
typedef struct bTrackToConstraint{
@@ -232,6 +234,8 @@ typedef struct bStretchToConstraint{
/* bKinematicConstraint->flag */
#define CONSTRAINT_IK_TIP 1
#define CONSTRAINT_IK_ROT 2
#define CONSTRAINT_IK_AUTO 4
#define CONSTRAINT_IK_TEMP 8
#endif

View File

@@ -2671,25 +2671,26 @@ static void editing_panel_armature_type(Object *ob, bArmature *arm)
uiBlockBeginAlign(block);
uiDefButBitI(block, TOG, ARM_MIRROR_EDIT, B_DIFF, "X-Axis Mirror Edit", 10, 160,150,20, &arm->flag, 0, 0, 0, 0, "Enable X-axis mirrored editing");
uiDefButBitC(block, TOG, OB_DRAWXRAY,REDRAWVIEW3D, "X-Ray", 160,160,150,20, &ob->dtx, 0, 0, 0, 0, "Draw armature in front of solid objects");
uiDefButBitI(block, TOG, ARM_AUTO_IK, B_DIFF, "Automatic IK", 10, 140,300,20, &arm->flag, 0, 0, 0, 0, "Adds temporal IK chains while grabbing Bones");
uiBlockEndAlign(block);
uiDefBut(block, LABEL, 0, "Display Options", 10,140,150,20, 0, 0, 0, 0, 0, "");
uiDefBut(block, LABEL, 0, "Display Options", 10,120,150,20, 0, 0, 0, 0, 0, "");
uiBlockBeginAlign(block);
uiDefButI(block, ROW, REDRAWVIEW3D, "Octahedron", 10, 120,90,20, &arm->drawtype, 0, ARM_OCTA, 0, 0, "Draw bones as octahedra");
uiDefButI(block, ROW, REDRAWVIEW3D, "Stick", 100, 120,55,20, &arm->drawtype, 0, ARM_LINE, 0, 0, "Draw bones as simple 2d lines with dots");
uiDefButI(block, ROW, REDRAWVIEW3D, "B-Bone", 155, 120,70,20, &arm->drawtype, 0, ARM_B_BONE, 0, 0, "Draw bones as boxes, showing subdivision and b-splines");
uiDefButI(block, ROW, REDRAWVIEW3D, "Envelope", 225, 120,85,20, &arm->drawtype, 0, ARM_ENVELOPE, 0, 0, "Draw bones as extruded spheres, showing deformation influence volume");
uiDefButI(block, ROW, REDRAWVIEW3D, "Octahedron", 10, 100,90,20, &arm->drawtype, 0, ARM_OCTA, 0, 0, "Draw bones as octahedra");
uiDefButI(block, ROW, REDRAWVIEW3D, "Stick", 100, 100,55,20, &arm->drawtype, 0, ARM_LINE, 0, 0, "Draw bones as simple 2d lines with dots");
uiDefButI(block, ROW, REDRAWVIEW3D, "B-Bone", 155, 100,70,20, &arm->drawtype, 0, ARM_B_BONE, 0, 0, "Draw bones as boxes, showing subdivision and b-splines");
uiDefButI(block, ROW, REDRAWVIEW3D, "Envelope", 225, 100,85,20, &arm->drawtype, 0, ARM_ENVELOPE, 0, 0, "Draw bones as extruded spheres, showing deformation influence volume");
uiDefButBitI(block, TOG, ARM_DRAWAXES, REDRAWVIEW3D, "Draw Axes", 10, 100,150,20, &arm->flag, 0, 0, 0, 0, "Draw bone axes");
uiDefButBitI(block, TOG, ARM_DRAWNAMES, REDRAWVIEW3D, "Draw Names", 160,100,150,20, &arm->flag, 0, 0, 0, 0, "Draw bone names");
uiDefButBitI(block, TOG, ARM_DRAWAXES, REDRAWVIEW3D, "Draw Axes", 10, 80,150,20, &arm->flag, 0, 0, 0, 0, "Draw bone axes");
uiDefButBitI(block, TOG, ARM_DRAWNAMES, REDRAWVIEW3D, "Draw Names", 160,80,150,20, &arm->flag, 0, 0, 0, 0, "Draw bone names");
uiBlockEndAlign(block);
uiDefBut(block, LABEL, 0, "Deform Options", 10,80,150,20, 0, 0, 0, 0, 0, "");
uiDefBut(block, LABEL, 0, "Deform Options", 10,60,150,20, 0, 0, 0, 0, 0, "");
uiBlockBeginAlign(block);
uiDefButBitI(block, TOG, ARM_DEF_VGROUP, B_ARM_RECALCDATA, "Vertex Groups", 10, 60,150,20, &arm->deformflag, 0, 0, 0, 0, "Enable VertexGroups defining deform (not for Modifiers)");
uiDefButBitI(block, TOG, ARM_DEF_ENVELOPE, B_ARM_RECALCDATA, "Envelopes", 160,60,150,20, &arm->deformflag, 0, 0, 0, 0, "Enable Bone Envelopes defining deform (not for Modifiers)");
uiDefButBitI(block, TOG, ARM_RESTPOS, B_ARM_RECALCDATA,"Rest Position", 10,40,150,20, &arm->flag, 0, 0, 0, 0, "Show armature rest position, no posing possible");
uiDefButBitI(block, TOG, ARM_DELAYDEFORM, REDRAWVIEW3D, "Delay Deform", 160,40,150,20, &arm->flag, 0, 0, 0, 0, "Don't deform children when manipulating bones in pose mode");
uiDefButBitI(block, TOG, ARM_DEF_VGROUP, B_ARM_RECALCDATA, "Vertex Groups", 10, 40,150,20, &arm->deformflag, 0, 0, 0, 0, "Enable VertexGroups defining deform (not for Modifiers)");
uiDefButBitI(block, TOG, ARM_DEF_ENVELOPE, B_ARM_RECALCDATA, "Envelopes", 160,40,150,20, &arm->deformflag, 0, 0, 0, 0, "Enable Bone Envelopes defining deform (not for Modifiers)");
uiDefButBitI(block, TOG, ARM_RESTPOS, B_ARM_RECALCDATA,"Rest Position", 10,20,150,20, &arm->flag, 0, 0, 0, 0, "Show armature rest position, no posing possible");
uiDefButBitI(block, TOG, ARM_DELAYDEFORM, REDRAWVIEW3D, "Delay Deform", 160,20,150,20, &arm->flag, 0, 0, 0, 0, "Don't deform children when manipulating bones in pose mode");
}

View File

@@ -829,7 +829,8 @@ static void draw_line_bone(int armflag, int boneflag, int constflag, unsigned in
if(armflag & ARM_POSEMODE) {
/* inner part in background color or constraint */
if(constflag) {
if(constflag & PCHAN_HAS_IK) glColor3ub(255, 255, 0);
if(constflag & PCHAN_HAS_TARGET) glColor3ub(255, 150, 0);
else if(constflag & PCHAN_HAS_IK) glColor3ub(255, 255, 0);
else if(constflag & PCHAN_HAS_CONST) glColor3ub(0, 255, 120);
else BIF_ThemeColor(TH_BONE_POSE); // PCHAN_HAS_ACTION
}
@@ -969,7 +970,8 @@ static void draw_b_bone(int dt, int armflag, int boneflag, int constflag, unsign
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
if(constflag & PCHAN_HAS_IK) glColor4ub(255, 255, 0, 80);
if(constflag & PCHAN_HAS_TARGET) glColor4ub(255, 150, 0, 80);
else if(constflag & PCHAN_HAS_IK) glColor4ub(255, 255, 0, 80);
else if(constflag & PCHAN_HAS_CONST) glColor4ub(0, 255, 120, 80);
else BIF_ThemeColor4(TH_BONE_POSE); // PCHAN_HAS_ACTION
@@ -1036,7 +1038,8 @@ static void draw_bone(int dt, int armflag, int boneflag, int constflag, unsigned
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
if(constflag & PCHAN_HAS_IK) glColor4ub(255, 255, 0, 80);
if(constflag & PCHAN_HAS_TARGET) glColor4ub(255, 150, 0, 80);
else if(constflag & PCHAN_HAS_IK) glColor4ub(255, 255, 0, 80);
else if(constflag & PCHAN_HAS_CONST) glColor4ub(0, 255, 120, 80);
else BIF_ThemeColor4(TH_BONE_POSE); // PCHAN_HAS_ACTION
@@ -1397,8 +1400,11 @@ static void draw_pose_channels(Base *base, int dt)
if(arm->flag & ARM_POSEMODE) {
if(pchan->constflag & PCHAN_HAS_IK) {
if(bone->flag & BONE_SELECTED) {
if(pchan->constflag & PCHAN_HAS_TARGET) glColor3ub(200, 120, 0);
else glColor3ub(200, 200, 50); // add theme!
glLoadName (index & 0xFFFF);
glColor3ub(200, 200, 50); // add theme!
pchan_draw_IK_root_lines(pchan);
}
}

View File

@@ -622,7 +622,7 @@ void add_constraint(int only_IK)
else if(obsel)
nr= pupmenu("Add IK Constraint%t|To Selected Object%x10");
else
nr= pupmenu("Add IK Constraint%t|To New Empty Object%x10");
nr= pupmenu("Add IK Constraint%t|To New Empty Object%x10|Without Target%x11");
}
else {
if(pchanact) {
@@ -648,7 +648,7 @@ void add_constraint(int only_IK)
if(nr<1) return;
/* handle IK separate */
if(nr==10) {
if(nr==10 || nr==11) {
/* prevent weird chains... */
if(pchansel) {
@@ -675,6 +675,7 @@ void add_constraint(int only_IK)
con = add_new_constraint(CONSTRAINT_TYPE_KINEMATIC);
BLI_addtail(&pchanact->constraints, con);
pchanact->constflag |= PCHAN_HAS_IK; // for draw, but also for detecting while pose solving
if(nr==11) pchanact->constflag |= PCHAN_HAS_TARGET;
}
else {
@@ -704,7 +705,7 @@ void add_constraint(int only_IK)
else if(obsel) {
set_constraint_target(con, obsel, NULL);
}
else { /* add new empty as target */
else if(nr!=11) { /* add new empty as target */
Base *base= BASACT, *newbase;
Object *obt;

View File

@@ -356,7 +356,7 @@ void pose_clear_IK(void)
MEM_freeN(con);
}
}
pchan->constflag &= ~PCHAN_HAS_IK;
pchan->constflag &= ~(PCHAN_HAS_IK|PCHAN_HAS_TARGET);
}
}

View File

@@ -114,6 +114,7 @@
#include "BDR_editobject.h" // reset_slowparents()
#include "BLI_arithb.h"
#include "BLI_blenlib.h"
#include "BLI_editVert.h"
#include "blendef.h"
@@ -395,79 +396,356 @@ void count_bone_select(TransInfo *t, ListBase *lb, int do_it)
}
}
static int add_pose_transdata(TransInfo *t, bPoseChannel *pchan, Object *ob, TransData *td)
static bKinematicConstraint *has_targetless_ik(bPoseChannel *pchan)
{
bConstraint *con= pchan->constraints.first;
for(;con; con= con->next) {
if(con->type==CONSTRAINT_TYPE_KINEMATIC) {
bKinematicConstraint *data= con->data;
if(data->tar==NULL)
return data;
if(data->tar->type==OB_ARMATURE && data->subtarget[0]==0)
return data;
}
}
return NULL;
}
static void apply_targetless_ik(Object *ob)
{
bPoseChannel *pchan, *parchan, *chanlist[256];
bKinematicConstraint *data;
int segcount;
/* now we got a difficult situation... we have to find the
target-less IK pchans, and apply transformation to the all
pchans that were in the chain */
for (pchan=ob->pose->chanbase.first; pchan; pchan=pchan->next) {
data= has_targetless_ik(pchan);
if(data && (data->flag & CONSTRAINT_IK_AUTO)) {
/* fill the array with the bones of the chain (armature.c does same, keep it synced) */
segcount= 0;
/* exclude tip from chain? */
if(!(data->flag & CONSTRAINT_IK_TIP))
parchan= pchan->parent;
else
parchan= pchan;
/* Find the chain's root & count the segments needed */
for (; parchan; parchan=parchan->parent){
chanlist[segcount]= parchan;
segcount++;
if(segcount==data->rootbone || segcount>255) break; // 255 is weak
}
for(;segcount;segcount--) {
Bone *bone;
float rmat[4][4], tmat[4][4], imat[4][4];
/* pose_mat(b) = pose_mat(b-1) * offs_bone * channel * constraint * IK */
/* we put in channel the entire result of rmat= (channel * constraint * IK) */
/* pose_mat(b) = pose_mat(b-1) * offs_bone * rmat */
/* rmat = pose_mat(b) * inv( pose_mat(b-1) * offs_bone ) */
parchan= chanlist[segcount-1];
bone= parchan->bone;
if(parchan->parent) {
float offs_bone[4][4];
/* offs_bone = yoffs(b-1) + root(b) + bonemat(b) */
Mat4CpyMat3(offs_bone, bone->bone_mat);
/* The bone's root offset (is in the parent's coordinate system) */
VECCOPY(offs_bone[3], bone->head);
/* Get the length translation of parent (length along y axis) */
offs_bone[3][1]+= parchan->parent->bone->length;
/* pose_mat(b-1) * offs_bone */
Mat4MulMat4(tmat, offs_bone, parchan->parent->pose_mat);
Mat4Invert(imat, tmat);
}
else {
Mat4CpyMat3(tmat, bone->bone_mat);
VECCOPY(tmat[3], bone->head);
Mat4Invert(imat, tmat);
}
/* result matrix */
Mat4MulMat4(rmat, parchan->pose_mat, imat);
/* apply and decompose, doesn't work for constraints or non-uniform scale well */
{
float rmat3[3][3], qmat[3][3], imat[3][3], smat[3][3];
Mat3CpyMat4(rmat3, rmat);
/* quaternion */
Mat3ToQuat(rmat3, parchan->quat);
/* for size, remove rotation */
QuatToMat3(parchan->quat, qmat);
Mat3Inv(imat, qmat);
Mat3MulMat3(smat, rmat3, imat);
Mat3ToSize(smat, parchan->size);
VECCOPY(parchan->loc, rmat[3]);
}
}
data->flag &= ~CONSTRAINT_IK_AUTO;
}
}
}
static void add_pose_transdata(TransInfo *t, bPoseChannel *pchan, Object *ob, TransData *td)
{
Bone *bone= pchan->bone;
float pmat[3][3], omat[3][3];
float vec[3];
if(bone) {
if (bone->flag & BONE_TRANSFORM) {
/* We don't let connected children get "grabbed" */
if ( (t->mode!=TFM_TRANSLATION) || (bone->flag & BONE_CONNECTED)==0 ) {
VECCOPY(vec, pchan->pose_mat[3]);
VECCOPY(td->center, vec);
VECCOPY(vec, pchan->pose_mat[3]);
VECCOPY(td->center, vec);
td->ob = ob;
td->flag= TD_SELECTED|TD_USEQUAT;
td->protectflag= pchan->protectflag;
td->ob = ob;
td->flag= TD_SELECTED|TD_USEQUAT;
td->protectflag= pchan->protectflag;
td->loc = pchan->loc;
VECCOPY(td->iloc, pchan->loc);
td->loc = pchan->loc;
VECCOPY(td->iloc, pchan->loc);
td->ext->rot= NULL;
td->ext->quat= pchan->quat;
td->ext->size= pchan->size;
td->ext->rot= NULL;
td->ext->quat= pchan->quat;
td->ext->size= pchan->size;
QUATCOPY(td->ext->iquat, pchan->quat);
VECCOPY(td->ext->isize, pchan->size);
QUATCOPY(td->ext->iquat, pchan->quat);
VECCOPY(td->ext->isize, pchan->size);
/* proper way to get the parent transform + own transform */
Mat3CpyMat4(omat, ob->obmat);
if(pchan->parent) {
if(pchan->bone->flag & BONE_HINGE)
Mat3CpyMat4(pmat, pchan->parent->bone->arm_mat);
else
Mat3CpyMat4(pmat, pchan->parent->pose_mat);
/* proper way to get the parent transform + own transform */
Mat3CpyMat4(omat, ob->obmat);
if(pchan->parent) {
if(pchan->bone->flag & BONE_HINGE)
Mat3CpyMat4(pmat, pchan->parent->bone->arm_mat);
else
Mat3CpyMat4(pmat, pchan->parent->pose_mat);
Mat3MulSerie(td->mtx, pchan->bone->bone_mat, pmat, omat, 0,0,0,0,0); // dang mulserie swaps args
Mat3MulSerie(td->mtx, pchan->bone->bone_mat, pmat, omat, 0,0,0,0,0); // dang mulserie swaps args
}
else {
Mat3MulMat3(td->mtx, omat, pchan->bone->bone_mat); // huh, transposed?
}
Mat3Inv (td->smtx, td->mtx);
/* for axismat we use bone's own transform */
Mat3CpyMat4(pmat, pchan->pose_mat);
Mat3MulMat3(td->axismtx, omat, pmat);
Mat3Ortho(td->axismtx);
if(t->mode==TFM_BONESIZE) {
bArmature *arm= t->poseobj->data;
if(arm->drawtype==ARM_ENVELOPE) {
td->loc= NULL;
td->val= &bone->dist;
td->ival= bone->dist;
}
else {
// abusive storage of scale in the loc pointer :)
td->loc= &bone->xwidth;
VECCOPY (td->iloc, td->loc);
td->val= NULL;
}
}
/* in this case we can do target-less IK grabbing */
if(t->mode==TFM_TRANSLATION) {
bKinematicConstraint *data= has_targetless_ik(pchan);
if(data) {
if(data->flag & CONSTRAINT_IK_TIP) {
VECCOPY(data->grabtarget, pchan->pose_tail);
}
else {
VECCOPY(data->grabtarget, pchan->pose_head);
}
td->loc = data->grabtarget;
VECCOPY(td->iloc, td->loc);
data->flag |= CONSTRAINT_IK_AUTO;
/* only object matrix correction */
Mat3CpyMat3 (td->mtx, omat);
Mat3Inv (td->smtx, td->mtx);
}
}
}
static void bone_children_clear_transflag(ListBase *lb)
{
Bone *bone= lb->first;
for(;bone;bone= bone->next) {
bone->flag &= ~BONE_TRANSFORM;
bone_children_clear_transflag(&bone->childbase);
}
}
/* sets transform flags in the bones, returns total */
static void set_pose_transflags(TransInfo *t, Object *ob)
{
bPoseChannel *pchan;
Bone *bone;
t->total= 0;
for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
bone= pchan->bone;
if(bone->flag & BONE_SELECTED)
bone->flag |= BONE_TRANSFORM;
else
bone->flag &= ~BONE_TRANSFORM;
}
/* make sure no bone can be transformed when a parent is transformed */
/* since pchans are depsgraph sorted, the parents are in beginning of list */
for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
bone= pchan->bone;
if(bone->flag & BONE_TRANSFORM)
bone_children_clear_transflag(&bone->childbase);
}
/* now count, and check if we have autoIK or have to switch from translate to rotate */
for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
bone= pchan->bone;
if(bone->flag & BONE_TRANSFORM) {
t->total++;
if(t->mode==TFM_TRANSLATION && pchan->parent) {
if( has_targetless_ik(pchan)==NULL ) {
t->mode= TFM_ROTATION;
}
else {
Mat3MulMat3(td->mtx, omat, pchan->bone->bone_mat); // huh, transposed?
}
Mat3Inv (td->smtx, td->mtx);
/* for axismat we use bone's own transform */
Mat3CpyMat4(pmat, pchan->pose_mat);
Mat3MulMat3(td->axismtx, omat, pmat);
Mat3Ortho(td->axismtx);
if(t->mode==TFM_BONESIZE) {
bArmature *arm= t->poseobj->data;
if(arm->drawtype==ARM_ENVELOPE) {
td->loc= NULL;
td->val= &bone->dist;
td->ival= bone->dist;
}
else {
// abusive storage of scale in the loc pointer :)
td->loc= &bone->xwidth;
VECCOPY (td->iloc, td->loc);
td->val= NULL;
}
}
return 1;
}
}
}
return 0;
}
/* frees temporal IKs */
static void pose_grab_with_ik_clear(Object *ob)
{
bKinematicConstraint *data;
bPoseChannel *pchan;
bConstraint *con;
for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
for(con= pchan->constraints.first; con; con= con->next) {
if(con->type==CONSTRAINT_TYPE_KINEMATIC) {
data= con->data;
if(data->flag & CONSTRAINT_IK_TEMP) {
BLI_remlink(&pchan->constraints, con);
MEM_freeN(con->data);
MEM_freeN(con);
pchan->constflag &= ~(PCHAN_HAS_IK|PCHAN_HAS_TARGET);
break;
}
}
}
}
}
/* adds the IK to pchan */
static void pose_grab_with_ik_add(bPoseChannel *pchan)
{
bKinematicConstraint *data;
bConstraint *con;
/* rule: not if there's already an IK on this channel */
for(con= pchan->constraints.first; con; con= con->next)
if(con->type==CONSTRAINT_TYPE_KINEMATIC)
break;
if(con) {
/* but, if this is a targetless IK, we make it auto anyway (for the children loop) */
data= has_targetless_ik(pchan);
if(data)
data->flag |= CONSTRAINT_IK_AUTO;
return;
}
con = add_new_constraint(CONSTRAINT_TYPE_KINEMATIC);
BLI_addtail(&pchan->constraints, con);
pchan->constflag |= (PCHAN_HAS_IK|PCHAN_HAS_TARGET); /* for draw, but also for detecting while pose solving */
data= con->data;
data->flag= CONSTRAINT_IK_TIP|CONSTRAINT_IK_TEMP|CONSTRAINT_IK_AUTO;
VECCOPY(data->grabtarget, pchan->pose_tail);
data->rootbone= 1;
/* we include only a connected chain */
while(pchan && (pchan->bone->flag & BONE_CONNECTED)) {
data->rootbone++;
pchan= pchan->parent;
}
}
/* bone is a canditate to get IK, but we don't do it if it has children connected */
static void pose_grab_with_ik_children(bPose *pose, Bone *bone)
{
Bone *bonec;
int wentdeeper= 0;
/* go deeper if children & children are connected */
for(bonec= bone->childbase.first; bonec; bonec= bonec->next) {
if(bonec->flag & BONE_CONNECTED) {
wentdeeper= 1;
pose_grab_with_ik_children(pose, bonec);
}
}
if(wentdeeper==0) {
bPoseChannel *pchan= get_pose_channel(pose, bone->name);
if(pchan)
pose_grab_with_ik_add(pchan);
}
}
/* main call which adds temporal IK chains */
static void pose_grab_with_ik(Object *ob)
{
bPoseChannel *pchan, *pchansel= NULL;
if(ob==NULL || ob->pose==NULL || (ob->flag & OB_POSEMODE)==0)
return;
/* rule: only one Bone */
for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
if(pchan->bone->flag & BONE_SELECTED) {
if(pchansel)
break;
pchansel= pchan;
}
}
if(pchan || pchansel==NULL) return;
/* rule: if selected Bone is not a root bone, it gets a temporal IK */
if(pchansel->parent) {
/* only adds if there's no IK yet */
pose_grab_with_ik_add(pchansel);
}
else {
/* rule: go over the children and add IK to the tips */
pose_grab_with_ik_children(ob->pose, pchansel->bone);
}
}
/* only called with pose mode active object now */
static void createTransPose(Object *ob, TransInfo *t)
static void createTransPose(TransInfo *t, Object *ob)
{
bArmature *arm;
bPoseChannel *pchan;
@@ -489,13 +767,13 @@ static void createTransPose(Object *ob, TransInfo *t)
}
if (!(ob->lay & G.vd->lay)) return;
/* count total */
count_bone_select(t, &arm->bonebase, 1);
/* do we need to add temporal IK chains? */
if((arm->flag & ARM_AUTO_IK) && t->mode==TFM_TRANSLATION)
pose_grab_with_ik(ob);
/* set flags and count total (warning, can change transform to rotate) */
set_pose_transflags(t, ob);
if(t->total==0 && t->mode==TFM_TRANSLATION) {
t->mode= TFM_ROTATION;
count_bone_select(t, &arm->bonebase, 1);
}
if(t->total==0) return;
t->flag |= T_POSE;
@@ -514,8 +792,12 @@ static void createTransPose(Object *ob, TransInfo *t)
/* use pose channels to fill trans data */
td= t->data;
for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
if( add_pose_transdata(t, pchan, ob, td) ) td++;
if(pchan->bone->flag & BONE_TRANSFORM) {
add_pose_transdata(t, pchan, ob, td);
td++;
}
}
if(td != (t->data+t->total)) printf("Bone selection count error\n");
}
@@ -1729,13 +2011,21 @@ void special_aftertrans_update(TransInfo *t)
ob= t->poseobj;
arm= ob->data;
pose= ob->pose;
/* this signal does one recalc on pose, then unlocks, so ESC or edit will work */
ob->pose->flag |= POSE_DO_UNLOCK;
pose->flag |= POSE_DO_UNLOCK;
/* if target-less IK grabbing, we calculate the pchan transforms and clear flag */
if(!cancelled && t->mode==TFM_TRANSLATION)
apply_targetless_ik(ob);
if(t->mode==TFM_TRANSLATION)
pose_grab_with_ik_clear(ob);
/* automatic inserting of keys */
if((G.flags & G_RECORDKEYS) && (!cancelled)) {
act= ob->action;
pose= ob->pose;
if (!act)
act= ob->action= add_empty_action(ID_PO);
@@ -2018,7 +2308,7 @@ void createTransData(TransInfo *t)
}
}
else if (ob && (ob->flag & OB_POSEMODE)) {
createTransPose(OBACT, t);
createTransPose(t, OBACT);
}
else if (G.f & G_WEIGHTPAINT) {
/* exception, we look for the one selected armature */
@@ -2031,7 +2321,7 @@ void createTransData(TransInfo *t)
}
}
if(base) {
createTransPose(base->object, t);
createTransPose(t, base->object);
}
}
else {