Third and last commit for Brecht's IK work.

Full logs for changes will be added later. Worth to note now;
- support for 'tree IK' added
- DOF and stiffness per IK bone (in pose only)
- Orientation IK support (target rotates -> chain follows)

This is still WIP. Buttons might change, button ranges will change, and the
way 'IK groups' are working will change. You can play with this, but don't
expect saved files to work still by end of this day! :)
This commit is contained in:
Ton Roosendaal 2005-08-27 12:48:45 +00:00
parent a7f2ebf06c
commit ac619bace6
10 changed files with 349 additions and 120 deletions

@ -46,19 +46,23 @@ struct Mesh;
struct PoseChain;
struct ListBase;
typedef struct PoseTarget
{
struct PoseTarget *next, *prev;
struct bConstraint *con;
int tip;
} PoseTarget;
typedef struct PoseChain
{
struct PoseChain *next, *prev; // hurms
struct bPoseChannel **pchanchain;
struct bConstraint *con;
struct Bone *root, *target;
struct bPose *pose;
int totchannel;
float goal[3];
struct ListBase targets;
int totchannel;
float (*basis_change)[3][3];
char group[32];
float tolerance;
int iterations;
float goalinv[4][4];
struct IK_Chain_Extern *solver;
} PoseChain;
/* Core armature functionality */

@ -170,33 +170,24 @@ void BPY_do_all_scripts (short int event){}
/* IKsolver stubs */
#include "IK_solver.h"
extern int IK_LoadChain(IK_Chain_ExternPtr chain,IK_Segment_ExternPtr segments, int num_segs)
{
return 0;
}
extern int IK_SolveChain(
IK_Chain_ExternPtr chain,
float goal[3],
float tolerance,
int max_iterations,
float max_angle_change,
IK_Segment_ExternPtr output
)
{
return 0;
}
IK_Segment *IK_CreateSegment(int flag) { return 0; }
void IK_FreeSegment(IK_Segment *seg) {}
extern void IK_FreeChain(IK_Chain_ExternPtr chain)
{
;
}
void IK_SetParent(IK_Segment *seg, IK_Segment *parent) {}
void IK_SetTransform(IK_Segment *seg, float start[3], float rest_basis[][3], float basis[][3], float length) {}
void IK_GetBasisChange(IK_Segment *seg, float basis_change[][3]) {}
void IK_GetTranslationChange(IK_Segment *seg, float *translation_change) {};
void IK_SetLimit(IK_Segment *seg, IK_SegmentAxis axis, float lower, float upper) {};
void IK_SetStiffness(IK_Segment *seg, IK_SegmentAxis axis, float stiffness) {};
IK_Solver *IK_CreateSolver(IK_Segment *root) { return 0; }
void IK_FreeSolver(IK_Solver *solver) {};
extern IK_Chain_ExternPtr IK_CreateChain(void)
{
return 0;
}
void IK_SolverAddGoal(IK_Solver *solver, IK_Segment *tip, float goal[3], float weight) {}
void IK_SolverAddGoalOrientation(IK_Solver *solver, IK_Segment *tip, float goal[][3], float weight) {}
void IK_SolverAddCenterOfMass(IK_Solver *solver, IK_Segment *root, float goal[3], float weight) {}
int IK_Solve(IK_Solver *solver, float tolerance, int max_iterations) { return 0; }
/* exotic.c */
int BPY_call_importloader(char *name)

@ -207,6 +207,10 @@ bPoseChannel *verify_pose_channel(bPose* pose, const char* name)
chan->size[0] = chan->size[1] = chan->size[2] = 1.0F;
Mat3One(chan->ik_mat);
chan->limitmin[0]= chan->limitmin[1]= chan->limitmin[2]= -180.0f;
chan->limitmax[0]= chan->limitmax[1]= chan->limitmax[2]= 180.0f;
chan->stiffness[0]= chan->stiffness[1]= chan->stiffness[2]= 1.0f;
BLI_addtail (&pose->chanbase, chan);
return chan;

@ -967,15 +967,17 @@ void armature_rebuild_pose(Object *ob, bArmature *arm)
/* ********************** THE IK SOLVER ******************* */
/* allocates PoseChain, and links that to root bone/channel */
/* note; if we got this working, it can become static too? */
static void initialize_posechain(struct Object *ob, bPoseChannel *pchan_tip)
{
bPoseChannel *curchan, *pchan_root=NULL, *chanlist[256];
bPoseChannel *curchan, *pchan_root=NULL, *chanlist[256], **oldchan;
PoseChain *chain;
PoseTarget *target;
bConstraint *con;
bKinematicConstraint *data;
int a, segcount= 0;
int a, segcount= 0, size, newsize;
/* find IK constraint, and validate it */
for(con= pchan_tip->constraints.first; con; con= con->next) {
@ -1006,75 +1008,128 @@ static void initialize_posechain(struct Object *ob, bPoseChannel *pchan_tip)
break;
}
if (!segcount) return;
/* setup the chain data */
chain = MEM_callocN(sizeof(PoseChain), "posechain");
chain->totchannel= segcount;
chain->solver = IK_CreateChain();
chain->con= con;
chain->iterations = data->iterations;
chain->tolerance = data->tolerance;
chain->pchanchain= MEM_callocN(segcount*sizeof(void *), "channel chain");
for(a=0; a<segcount; a++) {
chain->pchanchain[a]= chanlist[segcount-a-1];
chain = NULL;
/* if part of group, look for existing chain */
if(strlen(data->group) > 0)
for(chain= pchan_root->chain.first; chain; chain= chain->next)
if(strcmp(data->group, chain->group)==0) break;
/* create a target */
target= MEM_callocN(sizeof(PoseTarget), "posetarget");
target->con= con;
if(chain==NULL) {
/* make new chain */
chain= MEM_callocN(sizeof(PoseChain), "posechain");
strcpy(chain->group, data->group);
chain->tolerance= data->tolerance;
chain->iterations= data->iterations;
chain->totchannel= segcount;
chain->pchanchain= MEM_callocN(segcount*sizeof(void *), "channel chain");
for(a=0; a<segcount; a++)
chain->pchanchain[a]= chanlist[segcount-a-1];
target->tip= segcount-1;
/* AND! link the chain to the root */
BLI_addtail(&pchan_root->chain, chain);
}
/* AND! link the chain to the root */
BLI_addtail(&pchan_root->chain, chain);
else {
chain->tolerance= MIN2(chain->tolerance, data->tolerance);
chain->iterations= MAX2(data->iterations, chain->iterations);
/* skip common pose channels and add remaining*/
size= MIN2(segcount, chain->totchannel);
for(a=0; a<size && chain->pchanchain[a]==chanlist[segcount-a-1]; a++);
segcount= segcount-a;
target->tip= chain->totchannel + segcount - 1;
if (segcount > 0) {
/* resize array */
newsize= chain->totchannel + segcount;
oldchan= chain->pchanchain;
chain->pchanchain= MEM_callocN(newsize*sizeof(void*), "channel chain");
memcpy(chain->pchanchain, oldchan, sizeof(void*)*chain->totchannel);
MEM_freeN(oldchan);
/* add new pose channels at the end, in reverse order */
for(a=0; a<segcount; a++)
chain->pchanchain[chain->totchannel+a]= chanlist[segcount-a-1];
chain->totchannel= newsize;
}
/* move chain to end of list, for correct evaluation order */
BLI_remlink(&pchan_root->chain, chain);
BLI_addtail(&pchan_root->chain, chain);
}
/* add target to the chain */
BLI_addtail(&chain->targets, target);
}
/* called from within the core where_is_pose loop, all animsystems and constraints
were executed & assigned. Now as last we do an IK pass */
static void execute_posechain(Object *ob, PoseChain *chain)
{
IK_Segment_Extern *segs;
bPoseChannel *pchan;
float R_parmat[3][3];
float iR_parmat[3][3];
float R_bonemat[3][3];
float goalrot[3][3], goalpos[3];
float rootmat[4][4], imat[4][4];
float size[3];
int curseg;
float goal[4][4], goalinv[4][4];
float size[3], bonesize[3], irest_basis[3][3], full_basis[3][3];
float length, basis[3][3], rest_basis[3][3], start[3];
int a, b, flag;
bPoseChannel *pchan;
IK_Segment *seg, *parent, **ikchain, *iktarget;
IK_Solver *solver;
PoseTarget *target;
bKinematicConstraint *data;
Bone *bone;
if (chain->totchannel == 0)
return;
ikchain= MEM_mallocN(sizeof(void*)*chain->totchannel, "ik chain");
for(a=0; a<chain->totchannel; a++) {
pchan= chain->pchanchain[a];
bone = pchan->bone;
/* set DoF flag */
flag= 0;
if((pchan->ikflag & BONE_IK_NO_XDOF) == 0)
flag |= IK_XDOF;
if((pchan->ikflag & BONE_IK_NO_YDOF) == 0)
flag |= IK_YDOF;
if((pchan->ikflag & BONE_IK_NO_ZDOF) == 0)
flag |= IK_ZDOF;
seg= ikchain[a]= IK_CreateSegment(flag);
/* find parent */
if(a == 0)
parent= NULL;
else {
for(b=a-1; chain->pchanchain[b]!=pchan->parent; b--);
parent= ikchain[b];
}
IK_SetParent(seg, parent);
/* first set the goal inverse transform, assuming the root of chain was done ok! */
pchan= chain->pchanchain[0];
Mat4One(rootmat);
VECCOPY(rootmat[3], pchan->pose_head);
Mat4MulMat4 (imat, rootmat, ob->obmat);
Mat4Invert (chain->goalinv, imat);
/* and set and transform goal */
get_constraint_target_matrix(chain->con, TARGET_BONE, NULL, rootmat, size, 1.0); // 1.0=ctime
VECCOPY (chain->goal, rootmat[3]);
/* do we need blending? */
if(chain->con->enforce!=1.0) {
float vec[3];
float fac= chain->con->enforce;
float mfac= 1.0-fac;
pchan= chain->pchanchain[chain->totchannel-1]; // last bone
VECCOPY(vec, pchan->pose_tail);
Mat4MulVecfl(ob->obmat, vec); // world space
chain->goal[0]= fac*chain->goal[0] + mfac*vec[0];
chain->goal[1]= fac*chain->goal[1] + mfac*vec[1];
chain->goal[2]= fac*chain->goal[2] + mfac*vec[2];
}
Mat4MulVecfl (chain->goalinv, chain->goal);
/* Now we construct the IK segments */
segs = MEM_callocN (sizeof(IK_Segment_Extern)*chain->totchannel, "iksegments");
for (curseg=0; curseg<chain->totchannel; curseg++){
pchan= chain->pchanchain[curseg];
/* Get the matrix that transforms from prevbone into this bone */
/* get the matrix that transforms from prevbone into this bone */
Mat3CpyMat4(R_bonemat, pchan->pose_mat);
if (pchan->parent && (pchan->bone->flag & BONE_IK_TOPARENT)) {
if(pchan->parent && (bone->flag & BONE_IK_TOPARENT)) {
Mat3CpyMat4(R_parmat, pchan->parent->pose_mat);
}
else
@ -1082,33 +1137,116 @@ static void execute_posechain(Object *ob, PoseChain *chain)
Mat3Inv(iR_parmat, R_parmat);
/* Mult and Copy the matrix into the basis and transpose (IK lib likes it) */
Mat3MulMat3((void *)segs[curseg].basis, iR_parmat, R_bonemat);
Mat3Transp((void *)segs[curseg].basis);
/* Fill out the IK segment */
segs[curseg].length = pchan->bone->length;
/* gather transformations for this IK segment */
start[0]= start[1]= start[2]= 0.0;
/* change length based on bone size */
Mat3ToSize(R_bonemat, bonesize);
length= bone->length*bonesize[1];
Mat3CpyMat3(rest_basis, bone->bone_mat);
/* compute basis with rest_basis removed */
Mat3Inv(irest_basis, rest_basis);
Mat3MulMat3(full_basis, iR_parmat, R_bonemat);
Mat3MulMat3(basis, irest_basis, full_basis);
/* basis must be pure rotation, size was extracted for length already */
Mat3Ortho(rest_basis);
Mat3Ortho(basis);
IK_SetTransform(seg, start, rest_basis, basis, length);
if (pchan->ikflag & BONE_IK_XLIMIT)
IK_SetLimit(seg, IK_X, pchan->limitmin[0], pchan->limitmax[0]);
if (pchan->ikflag & BONE_IK_YLIMIT)
IK_SetLimit(seg, IK_Y, pchan->limitmin[1], pchan->limitmax[1]);
if (pchan->ikflag & BONE_IK_ZLIMIT)
IK_SetLimit(seg, IK_Z, pchan->limitmin[2], pchan->limitmax[2]);
IK_SetStiffness(seg, IK_X, pchan->stiffness[0]);
IK_SetStiffness(seg, IK_Y, pchan->stiffness[1]);
IK_SetStiffness(seg, IK_Z, pchan->stiffness[2]);
}
solver= IK_CreateSolver(ikchain[0]);
/* set solver goals */
/* first set the goal inverse transform, assuming the root of chain was done ok! */
pchan= chain->pchanchain[0];
Mat4One(rootmat);
VECCOPY(rootmat[3], pchan->pose_head);
Mat4MulMat4 (imat, rootmat, ob->obmat);
Mat4Invert (goalinv, imat);
for(target=chain->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);
/* and set and transform goal */
Mat4MulMat4(goal, rootmat, goalinv);
VECCOPY(goalpos, goal[3]);
Mat3CpyMat4(goalrot, goal);
/* do we need blending? */
if(target->con->enforce!=1.0) {
float vec[3], q1[4], q2[4], q[4];
float fac= target->con->enforce;
float mfac= 1.0-fac;
pchan= chain->pchanchain[target->tip];
/* blend position */
VECCOPY(vec, pchan->pose_tail);
Mat4MulVecfl(ob->obmat, vec); // world space
Mat4MulVecfl(goalinv, vec);
goalpos[0]= fac*goalpos[0] + mfac*vec[0];
goalpos[1]= fac*goalpos[1] + mfac*vec[1];
goalpos[2]= fac*goalpos[2] + mfac*vec[2];
/* blend rotation */
Mat3ToQuat(goalrot, q1);
Mat3CpyMat4(R_bonemat, pchan->pose_mat);
Mat3ToQuat(R_bonemat, q2);
QuatInterpol(q, q1, q2, mfac);
QuatToMat3(q, goalrot);
}
iktarget= ikchain[target->tip];
/*IK_SolverAddCenterOfMass(solver, ikchain[0], goalpos, data->weight);*/
if(data->weight != 0.0)
IK_SolverAddGoal(solver, iktarget, goalpos, data->weight);
if((data->flag & KINEMATIC_ORIENTATION) && (data->orientweight != 0.0))
IK_SolverAddGoalOrientation(solver, iktarget, goalrot, data->orientweight);
}
/* solve */
IK_Solve(solver, chain->tolerance, chain->iterations);
IK_FreeSolver(solver);
/* gather basis changes */
chain->basis_change= MEM_mallocN(sizeof(float[3][3])*chain->totchannel, "ik basis change");
for(a=0; a<chain->totchannel; a++) {
IK_GetBasisChange(ikchain[a], chain->basis_change[a]);
IK_FreeSegment(ikchain[a]);
}
/* Solve the chain */
IK_LoadChain(chain->solver, segs, chain->totchannel);
IK_SolveChain(chain->solver, chain->goal, chain->tolerance,
chain->iterations, 0.1f, chain->solver->segments);
/* not yet free! */
MEM_freeN(ikchain);
}
void free_posechain (PoseChain *chain)
{
if (chain->solver) {
MEM_freeN (chain->solver->segments);
chain->solver->segments = NULL;
IK_FreeChain(chain->solver);
}
BLI_freelistN(&chain->targets);
if(chain->pchanchain) MEM_freeN(chain->pchanchain);
if(chain->basis_change) MEM_freeN(chain->basis_change);
MEM_freeN(chain);
}
@ -1325,16 +1463,19 @@ void where_is_pose (Object *ob)
if(!(chain->pchanchain[a]->flag & POSE_DONE)) // successive chains can set the flag
where_is_pose_bone(ob, chain->pchanchain[a]);
}
/* 5. execute the IK solver */
execute_posechain(ob, chain); // calculates 3x3 difference matrices
/* 5. execute the IK solver, applying differences to
the channels and setting POSE_DONE */
execute_posechain(ob, chain);
/* 6. apply the differences to the channels, we calculate the original differences first */
for(a=0; a<chain->totchannel; a++)
make_dmats(chain->pchanchain[a]);
for(a=0; a<chain->totchannel; a++)
where_is_ik_bone(chain->pchanchain[a], (void *)chain->solver->segments[a].basis_change);
// (sets POSE_DONE)
/* 6. and free */
for(a=0; a<chain->totchannel; a++)
/* sets POSE_DONE */
where_is_ik_bone(chain->pchanchain[a], chain->basis_change[a]);
/* 7. and free */
BLI_remlink(&pchan->chain, chain);
free_posechain(chain);
}

@ -485,6 +485,7 @@ void *new_constraint_data (short type)
data = MEM_callocN(sizeof(bKinematicConstraint), "kinematicConstraint");
data->tolerance = (float)0.001;
data->weight= (float)1.0;
data->iterations = 500;
data->flag= CONSTRAINT_IK_TIP;

@ -4904,6 +4904,26 @@ static void do_versions(FileData *fd, Library *lib, Main *main)
ob->softflag &= ~OB_SB_ENABLE;
}
if(ob->pose) {
bPoseChannel *pchan;
bConstraint *con;
for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
if (pchan->stiffness[0] == 0.0f) {
pchan->stiffness[0]= pchan->stiffness[1]= pchan->stiffness[2]= 1.0;
pchan->limitmin[0]= pchan->limitmin[1]= pchan->limitmin[2]= -180.0f;
pchan->limitmax[0]= pchan->limitmax[1]= pchan->limitmax[2]= 180.0f;
for(con= pchan->constraints.first; con; con= con->next) {
if(con->type == CONSTRAINT_TYPE_KINEMATIC) {
bKinematicConstraint *data = (bKinematicConstraint*)con->data;
data->weight = 1.0f;
data->orientweight = 0.0f;
data->flag &= ~KINEMATIC_ORIENTATION;
}
}
}
}
}
}
for(arm=main->armature.first; arm; arm= arm->id.next) {

@ -47,7 +47,8 @@ typedef struct bPoseChannel {
short flag; /* dynamic, for detecting transform changes */
short constflag; /* for quick detecting which constraints affect this channel */
int pad;
short ikflag; /* settings for IK bones */
short pad;
struct Bone *bone; /* set on read file or rebuild pose */
struct bPoseChannel *parent; /* set on read file or rebuild pose */
@ -65,7 +66,10 @@ typedef struct bPoseChannel {
float pose_head[3]; /* actually pose_mat[3] */
float pose_tail[3]; /* also used for drawing help lines... */
int pad1;
float limitmin[3], limitmax[3]; /* DOF constraint */
float stiffness[3]; /* DOF stiffness */
} bPoseChannel;
@ -135,11 +139,21 @@ enum {
POSE_KEY = 0x1000
};
/* Pose Channel constflag (constraint detection) */
/* PoseChannel constflag (constraint detection) */
#define PCHAN_HAS_IK 1
#define PCHAN_HAS_CONST 2
/* only used for drawing Posemode, not stored in channel */
#define PCHAN_HAS_ACTION 4
/* PoseChannel->ikflag */
#define BONE_IK_NO_XDOF 1
#define BONE_IK_NO_YDOF 2
#define BONE_IK_NO_ZDOF 4
#define BONE_IK_XLIMIT 8
#define BONE_IK_YLIMIT 16
#define BONE_IK_ZLIMIT 32
#endif

@ -67,7 +67,9 @@ typedef struct bKinematicConstraint{
short flag; /* Like IK to Tip */
char subtarget[32]; /* String to specify sub-object target */
char group[32]; /* Name of group */
float weight; /* Weight of goal in IK group */
float orientweight;
} bKinematicConstraint;
typedef struct bTrackToConstraint{
@ -212,7 +214,8 @@ typedef struct bStretchToConstraint{
#define PLANE_Z 0x02
/* bKinematicConstraint->flag */
#define CONSTRAINT_IK_TIP 1
#define CONSTRAINT_IK_TIP 1
#define KINEMATIC_ORIENTATION 2
#endif

@ -2280,7 +2280,7 @@ static void editing_panel_pose_bones(Object *ob, bArmature *arm)
bPoseChannel *pchan;
Bone *curBone;
int bx=148, by=180;
int index;
int index, zerodof, zerolimit;
/* Draw the bone name block */
@ -2320,7 +2320,53 @@ static void editing_panel_pose_bones(Object *ob, bArmature *arm)
uiDefButBitI(block, TOG, BONE_HIDDEN_P, REDRAWVIEW3D, "Hide", bx+245,by-38,88,18, &curBone->flag, 0, 0, 0, 0, "Toggles display of this bone in Pose Mode");
uiBlockEndAlign(block);
by-=60;
zerodof = 1;
zerolimit = 1;
uiBlockBeginAlign(block);
uiDefButBitS(block, TOG, BONE_IK_NO_XDOF, B_ARM_RECALCDATA, "No X DoF", bx-10,by-60,114,19, &pchan->ikflag, 0.0, 0.0, 0.0, 0.0, "Disable X DoF for IK");
if ((pchan->ikflag & BONE_IK_NO_XDOF)==0) {
uiDefButF(block, NUM, B_ARM_RECALCDATA, "Stiff X:", bx-10, by-80, 114, 19, &pchan->stiffness[0], 1.0, 100.0, 10.0f, 2.0f, "Resistance to bending for X axis");
uiDefButBitS(block, TOG, BONE_IK_XLIMIT, B_ARM_RECALCDATA, "Limit X", bx-10,by-100,114,19, &pchan->ikflag, 0.0, 0.0, 0.0, 0.0, "Limit rotation over X axis");
if ((pchan->ikflag & BONE_IK_XLIMIT)) {
uiDefButF(block, NUM, B_ARM_RECALCDATA, "Min X:", bx-10, by-120, 114, 19, &pchan->limitmin[0], -180.0f, 180.0f, 10.0f, 0.0f, "Minimum X limit");
uiDefButF(block, NUM, B_ARM_RECALCDATA, "Max X:", bx-10, by-140, 114, 19, &pchan->limitmax[0], -180.0f, 180.0f, 10.0f, 0.0f, "Maximum X limit");
zerolimit = 0;
}
zerodof = 0;
}
uiBlockEndAlign(block);
uiBlockBeginAlign(block);
uiDefButBitS(block, TOG, BONE_IK_NO_YDOF, B_ARM_RECALCDATA, "No Y DoF", bx+104,by-60,113,19, &pchan->ikflag, 0.0, 0.0, 0.0, 0.0, "Disable Y DoF for IK");
if ((pchan->ikflag & BONE_IK_NO_YDOF)==0) {
uiDefButF(block, NUM, B_ARM_RECALCDATA, "Stiff Y:", bx+104, by-80, 114, 19, &pchan->stiffness[1], 1.0, 100.0, 10.0f, 2.0f, "Resistance to bending for Y axis");
uiDefButBitS(block, TOG, BONE_IK_YLIMIT, B_ARM_RECALCDATA, "Limit Y", bx+104,by-100,113,19, &pchan->ikflag, 0.0, 0.0, 0.0, 0.0, "Limit rotation over Y axis");
if ((pchan->ikflag & BONE_IK_YLIMIT)) {
uiDefButF(block, NUM, B_ARM_RECALCDATA, "Min Y:", bx+104, by-120, 113, 19, &pchan->limitmin[1], -180.0f, 180.0f, 10.0f, 0.0f, "Minimum Y limit");
uiDefButF(block, NUM, B_ARM_RECALCDATA, "Max Y:", bx+104, by-140, 113, 19, &pchan->limitmax[1], -180.0f, 180.0f, 10.0f, 0.0f, "Maximum Y limit");
zerolimit = 0;
}
zerodof = 0;
}
uiBlockEndAlign(block);
uiBlockBeginAlign(block);
uiDefButBitS(block, TOG, BONE_IK_NO_ZDOF, B_ARM_RECALCDATA, "No Z DoF", bx+217,by-60,113,19, &pchan->ikflag, 0.0, 0.0, 0.0, 0.0, "Disable Z DoF for IK");
if ((pchan->ikflag & BONE_IK_NO_ZDOF)==0) {
uiDefButF(block, NUM, B_ARM_RECALCDATA, "Stiff Z:", bx+217, by-80, 114, 19, &pchan->stiffness[2], 1.0, 100.0, 10.0f, 2.0f, "Resistance to bending for Z axis");
uiDefButBitS(block, TOG, BONE_IK_ZLIMIT, B_ARM_RECALCDATA, "Limit Z", bx+217,by-100,113,19, &pchan->flag, 0.0, 0.0, 0.0, 0.0, "Limit rotation over Z axis");
if ((pchan->flag & BONE_IK_ZLIMIT)) {
uiDefButF(block, NUM, B_ARM_RECALCDATA, "Min Z:", bx+217, by-120, 113, 19, &pchan->limitmin[2], -180.0f, 180.0f, 10.0f, 0.0f, "Minimum Z limit");
uiDefButF(block, NUM, B_ARM_RECALCDATA, "Max Z:", bx+217, by-140, 113, 19, &pchan->limitmax[2], -180.0f, 180.0f, 10.0f, 0.0f, "Maximum Z limit");
zerolimit = 0;
}
zerodof = 0;
}
uiBlockEndAlign(block);
by -= (zerodof)? 82: (zerolimit)? 122: 162;
if(by < -200) break; // for time being... extreme long panels are very slow
}
}

@ -585,7 +585,7 @@ static void draw_constraint (uiBlock *block, ListBase *list, bConstraint *con, s
bKinematicConstraint *data = con->data;
bArmature *arm;
height = 66;
height = 108;
uiDefBut(block, ROUNDBOX, B_DIFF, "", *xco-10, *yco-height, width+30,height-1, NULL, 5.0, 0.0, 12, rb_col, "");
uiDefBut(block, LABEL, B_CONSTRAINT_TEST, "Target:", *xco+65, *yco-24, 50, 18, NULL, 0.0, 0.0, 0.0, 0.0, "");
@ -608,6 +608,11 @@ static void draw_constraint (uiBlock *block, ListBase *list, bConstraint *con, s
uiDefButS(block, NUM, B_CONSTRAINT_TEST, "Iterations:", *xco+((width/2)+3), *yco-64, 120, 18, &data->iterations, 1, 10000, 0.0, 0.0, "Maximum number of solving iterations");
uiBlockEndAlign(block);
uiDefBut(block, TEX, B_CONSTRAINT_TEST, "IK group:", *xco+((width/2)-117), *yco-86,120,18, &data->group, 0, 24, 0, 0, "IK group name");
uiDefButF(block, NUMSLI, B_CONSTRAINT_TEST, "Weight ", *xco+((width/2)+3), *yco-86, 120, 18, &data->weight, 0.0, 1.0, 0.0, 0.0, "Weight of position control for this target");
uiDefButBitS(block, TOG, KINEMATIC_ORIENTATION, B_CONSTRAINT_TEST, "Orientation", *xco+((width/2)-117), *yco-108,120,18, &data->flag, 0, 0, 0, 0, "Follow orientation of target");
uiDefButF(block, NUMSLI, B_CONSTRAINT_TEST, "Weight ", *xco+((width/2)+3), *yco-108, 120, 18, &data->orientweight, 0.0, 1.0, 0.0, 0.0, "Weight of orientation control for this target");
}
break;
case CONSTRAINT_TYPE_TRACKTO: