Armature-Pose IK goodies!
- Added option to the IK buttons, to have it use the 'tip' as end of the IK chain. I never really understood this old convention (IK didn't work on the Bone itself). Old files still will read OK though. But I made the "To Tip" a default when adding new IK constraints. - Hotkey CTRL+I: add IK, with option to have it adding an Empty target, or use a selected Bone as target. With the new non-modal PoseMode, it gives instant access to playing with the IK chain. - Hotkey ALT+I: clears IK, on all selected Bones - Hotkey ALT+C: clears Constraints on all selected Bones (incl IK)
This commit is contained in:
@@ -60,7 +60,7 @@ void free_constraint_channels (ListBase *chanbase);
|
||||
|
||||
char constraint_has_target (struct bConstraint *con);
|
||||
struct Object *get_constraint_target(struct bConstraint *con, char **subtarget);
|
||||
void set_constraint_target(struct bConstraint *con, struct Object *ob);
|
||||
void set_constraint_target(struct bConstraint *con, struct Object *ob, char *subtarget);
|
||||
|
||||
|
||||
/* Constraint target/owner types */
|
||||
|
||||
@@ -791,11 +791,15 @@ static void initialize_posechain(struct Object *ob, bPoseChannel *pchan_tip)
|
||||
/* Find the chain's root & count the segments needed */
|
||||
for (curchan = pchan_tip; curchan; curchan=curchan->parent){
|
||||
pchan_root = curchan;
|
||||
/* tip is not in the chain */
|
||||
if (curchan!=pchan_tip){
|
||||
chanlist[segcount]=curchan;
|
||||
segcount++;
|
||||
|
||||
chanlist[segcount]=curchan;
|
||||
segcount++;
|
||||
|
||||
/* exclude tip from chain? */
|
||||
if(curchan==pchan_tip) {
|
||||
if(!(data->flag & CONSTRAINT_IK_TIP)) segcount--;
|
||||
}
|
||||
|
||||
if(segcount>255) break; // also weak
|
||||
|
||||
if (!(curchan->bone->flag & BONE_IK_TOPARENT))
|
||||
|
||||
@@ -92,10 +92,9 @@ void free_constraints (ListBase *conlist)
|
||||
bConstraint *con;
|
||||
|
||||
/* Do any specific freeing */
|
||||
for (con=conlist->first; con; con=con->next)
|
||||
{
|
||||
for (con=conlist->first; con; con=con->next) {
|
||||
free_constraint_data (con);
|
||||
};
|
||||
}
|
||||
|
||||
/* Free the whole list */
|
||||
BLI_freelistN(conlist);
|
||||
@@ -374,7 +373,7 @@ Object *get_constraint_target(bConstraint *con, char **subtarget)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void set_constraint_target(bConstraint *con, Object *ob)
|
||||
void set_constraint_target(bConstraint *con, Object *ob, char *subtarget)
|
||||
{
|
||||
/*
|
||||
* Set the target for this constraint
|
||||
@@ -384,36 +383,42 @@ void set_constraint_target(bConstraint *con, Object *ob)
|
||||
{
|
||||
bActionConstraint *data = con->data;
|
||||
data->tar= ob;
|
||||
if(subtarget) BLI_strncpy(data->subtarget, subtarget, 32);
|
||||
}
|
||||
break;
|
||||
case CONSTRAINT_TYPE_LOCLIKE:
|
||||
{
|
||||
bLocateLikeConstraint *data = con->data;
|
||||
data->tar= ob;
|
||||
if(subtarget) BLI_strncpy(data->subtarget, subtarget, 32);
|
||||
}
|
||||
break;
|
||||
case CONSTRAINT_TYPE_ROTLIKE:
|
||||
{
|
||||
bRotateLikeConstraint *data = con->data;
|
||||
data->tar= ob;
|
||||
if(subtarget) BLI_strncpy(data->subtarget, subtarget, 32);
|
||||
}
|
||||
break;
|
||||
case CONSTRAINT_TYPE_KINEMATIC:
|
||||
{
|
||||
bKinematicConstraint *data = con->data;
|
||||
data->tar= ob;
|
||||
if(subtarget) BLI_strncpy(data->subtarget, subtarget, 32);
|
||||
}
|
||||
break;
|
||||
case CONSTRAINT_TYPE_TRACKTO:
|
||||
{
|
||||
bTrackToConstraint *data = con->data;
|
||||
data->tar= ob;
|
||||
if(subtarget) BLI_strncpy(data->subtarget, subtarget, 32);
|
||||
}
|
||||
break;
|
||||
case CONSTRAINT_TYPE_LOCKTRACK:
|
||||
{
|
||||
bLockTrackConstraint *data = con->data;
|
||||
data->tar= ob;
|
||||
if(subtarget) BLI_strncpy(data->subtarget, subtarget, 32);
|
||||
}
|
||||
break;
|
||||
case CONSTRAINT_TYPE_FOLLOWPATH:
|
||||
@@ -426,6 +431,7 @@ void set_constraint_target(bConstraint *con, Object *ob)
|
||||
{
|
||||
bStretchToConstraint *data = con->data;
|
||||
data->tar= ob;
|
||||
if(subtarget) BLI_strncpy(data->subtarget, subtarget, 32);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -488,7 +494,8 @@ void *new_constraint_data (short type)
|
||||
|
||||
data->tolerance = (float)0.001;
|
||||
data->iterations = 500;
|
||||
|
||||
data->flag= CONSTRAINT_IK_TIP;
|
||||
|
||||
result = data;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -322,7 +322,7 @@ void unlink_object(Object *ob)
|
||||
for(pchan= obt->pose->chanbase.first; pchan; pchan= pchan->next) {
|
||||
for (con = pchan->constraints.first; con; con=con->next) {
|
||||
if(ob==get_constraint_target(con, &str)) {
|
||||
set_constraint_target(con, NULL);
|
||||
set_constraint_target(con, NULL, NULL);
|
||||
obt->recalc |= OB_RECALC_DATA;
|
||||
}
|
||||
}
|
||||
@@ -333,7 +333,7 @@ void unlink_object(Object *ob)
|
||||
|
||||
for (con = obt->constraints.first; con; con=con->next) {
|
||||
if(ob==get_constraint_target(con, &str)) {
|
||||
set_constraint_target(con, NULL);
|
||||
set_constraint_target(con, NULL, NULL);
|
||||
obt->recalc |= OB_RECALC_OB;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,11 @@ void set_pose_keys(struct Object *ob);
|
||||
*/
|
||||
void exit_posemode(void);
|
||||
|
||||
/* tools */
|
||||
void pose_special_editmenu(void);
|
||||
void pose_add_IK(void);
|
||||
void pose_clear_IK(void);
|
||||
void pose_clear_constraints(void);
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -67,13 +67,11 @@ typedef struct bConstraint{
|
||||
typedef struct bKinematicConstraint{
|
||||
Object *tar;
|
||||
float tolerance; /* Acceptable distance from target */
|
||||
int iterations; /* Maximum number of iterations to try */
|
||||
short iterations; /* Maximum number of iterations to try */
|
||||
short flag; /* Like IK to Tip */
|
||||
char subtarget[32]; /* String to specify sub-object target */
|
||||
|
||||
float cacheeff[3]; /* Target location cache */
|
||||
int reserved1;
|
||||
|
||||
float cachemat[4][4]; /* Result cache */
|
||||
} bKinematicConstraint;
|
||||
|
||||
typedef struct bTrackToConstraint{
|
||||
@@ -219,5 +217,8 @@ typedef struct bStretchToConstraint{
|
||||
#define PLANE_Y 0x00000001
|
||||
#define PLANE_Z 0x00000002
|
||||
|
||||
/* bKinematicConstraint->flag */
|
||||
#define CONSTRAINT_IK_TIP 1
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -529,15 +529,18 @@ static void draw_constraint (uiBlock *block, ListBase *list, bConstraint *con, s
|
||||
uiDefIDPoinBut(block, test_obpoin_but, B_CONSTRAINT_CHANGETARGET, "OB:", *xco+120, *yco-24, 135, 18, &data->tar, "Target Object");
|
||||
|
||||
arm = get_armature(data->tar);
|
||||
if (arm){
|
||||
if (arm)
|
||||
but=uiDefBut(block, TEX, B_CONSTRAINT_CHANGETARGET, "BO:", *xco+120, *yco-42,135,18, &data->subtarget, 0, 24, 0, 0, "Subtarget Bone");
|
||||
}
|
||||
else
|
||||
strcpy (data->subtarget, "");
|
||||
uiBlockEndAlign(block);
|
||||
|
||||
uiDefButBitS(block, TOG, CONSTRAINT_IK_TIP, B_CONSTRAINT_REDRAW, "Use Tip", *xco+((width/2)-117), *yco-42, 80, 18, &data->flag, 0, 0, 0, 0, "Include Bone's tip als last element in Chain");
|
||||
|
||||
uiBlockBeginAlign(block);
|
||||
uiDefButF(block, NUM, B_CONSTRAINT_REDRAW, "Tolerance:", *xco+((width/2)-117), *yco-64, 120, 18, &data->tolerance, 0.0001f, 1.0, 0.0, 0.0, "Maximum distance to target after solving");
|
||||
uiDefButI(block, NUM, B_CONSTRAINT_REDRAW, "Iterations:", *xco+((width/2)+3), *yco-64, 120, 18, &data->iterations, 1, 10000, 0.0, 0.0, "Maximum number of solving iterations");
|
||||
uiDefButS(block, NUM, B_CONSTRAINT_REDRAW, "Iterations:", *xco+((width/2)+3), *yco-64, 120, 18, &data->iterations, 1, 10000, 0.0, 0.0, "Maximum number of solving iterations");
|
||||
uiBlockEndAlign(block);
|
||||
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1155,7 +1155,7 @@ static EditBone *add_editbone(void)
|
||||
strcpy (bone->name,"Bone");
|
||||
unique_editbone_name (bone->name);
|
||||
|
||||
bone->flag |= BONE_SELECTED|BONE_TIPSEL|BONE_ROOTSEL;
|
||||
bone->flag |= BONE_TIPSEL;
|
||||
bone->weight= 1.0F;
|
||||
bone->dist= 1.0F;
|
||||
bone->xwidth= 0.1;
|
||||
@@ -1188,7 +1188,6 @@ static void add_primitive_bone(Object *ob)
|
||||
|
||||
/* Create a bone */
|
||||
bone= add_editbone();
|
||||
bone->flag |= BONE_ACTIVE;
|
||||
|
||||
VECCOPY(bone->head, curs);
|
||||
VecAddf(bone->tail, bone->head, imat[1]); // bone with unit length 1
|
||||
|
||||
@@ -6,10 +6,7 @@
|
||||
* 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.
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
@@ -23,20 +20,16 @@
|
||||
* 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.
|
||||
* Contributor(s): Ton Roosendaal, Blender Foundation '05, full recode.
|
||||
*
|
||||
* ***** END GPL/BL DUAL LICENSE BLOCK *****
|
||||
* support for animation modes - Reevan McKay
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_arithb.h"
|
||||
#include "BLI_blenlib.h"
|
||||
|
||||
@@ -51,16 +44,19 @@
|
||||
#include "BKE_action.h"
|
||||
#include "BKE_armature.h"
|
||||
#include "BKE_constraint.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_depsgraph.h"
|
||||
#include "BKE_displist.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_object.h"
|
||||
|
||||
#include "BIF_editconstraint.h"
|
||||
#include "BIF_gl.h"
|
||||
#include "BIF_graphics.h"
|
||||
#include "BIF_interface.h"
|
||||
#include "BIF_poseobject.h"
|
||||
#include "BIF_space.h"
|
||||
#include "BIF_toolbox.h"
|
||||
#include "BIF_screen.h"
|
||||
#include "BIF_poseobject.h"
|
||||
|
||||
#include "BDR_editobject.h"
|
||||
|
||||
@@ -144,13 +140,16 @@ void exit_posemode(void)
|
||||
scrarea_queue_headredraw(curarea);
|
||||
}
|
||||
|
||||
/* context: active channel */
|
||||
void pose_special_editmenu(void)
|
||||
{
|
||||
Object *ob= OBACT;
|
||||
bPoseChannel *pchan;
|
||||
short nr;
|
||||
|
||||
/* paranoia checks */
|
||||
if(!ob && !ob->pose) return;
|
||||
if(ob==G.obedit || (ob->flag & OB_POSEMODE)==0) return;
|
||||
|
||||
for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next)
|
||||
if(pchan->bone->flag & BONE_ACTIVE) break;
|
||||
@@ -175,4 +174,155 @@ void pose_special_editmenu(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* context: active channel, optional selected channel */
|
||||
void pose_add_IK(void)
|
||||
{
|
||||
Object *ob= OBACT;
|
||||
bPoseChannel *pchanact, *pchansel;
|
||||
bConstraint *con;
|
||||
short nr;
|
||||
|
||||
/* paranoia checks */
|
||||
if(!ob && !ob->pose) return;
|
||||
if(ob==G.obedit || (ob->flag & OB_POSEMODE)==0) return;
|
||||
|
||||
/* find active */
|
||||
for(pchanact= ob->pose->chanbase.first; pchanact; pchanact= pchanact->next)
|
||||
if(pchanact->bone->flag & BONE_ACTIVE) break;
|
||||
if(pchanact==NULL) return;
|
||||
|
||||
/* find selected */
|
||||
for(pchansel= ob->pose->chanbase.first; pchansel; pchansel= pchansel->next) {
|
||||
if(pchansel!=pchanact)
|
||||
if(pchansel->bone->flag & BONE_SELECTED) break;
|
||||
}
|
||||
|
||||
for(con= pchanact->constraints.first; con; con= con->next) {
|
||||
if(con->type==CONSTRAINT_TYPE_KINEMATIC) break;
|
||||
}
|
||||
if(con) {
|
||||
error("Pose Channel already has IK");
|
||||
return;
|
||||
}
|
||||
|
||||
if(pchansel)
|
||||
nr= pupmenu("Add IK Constraint%t|To new Empty Object%x1|To selected Bone%x2");
|
||||
else
|
||||
nr= pupmenu("Add IK Constraint%t|To new Empty Object%x1");
|
||||
|
||||
if(nr<1) return;
|
||||
|
||||
/* prevent weird chains... */
|
||||
if(nr==2) {
|
||||
bPoseChannel *pchan= pchanact;
|
||||
while(pchan) {
|
||||
if(pchan==pchansel) break;
|
||||
if(pchan->bone->flag & BONE_IK_TOPARENT)
|
||||
pchan= pchan->parent;
|
||||
else pchan= NULL;
|
||||
}
|
||||
if(pchan) {
|
||||
error("IK target should not be in the IK chain itself");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
/* add new empty as target */
|
||||
if(nr==1) {
|
||||
Base *base= BASACT;
|
||||
Object *obt;
|
||||
|
||||
obt= add_object(OB_EMPTY);
|
||||
/* transform cent to global coords for loc */
|
||||
VecMat4MulVecfl(obt->loc, ob->obmat, pchanact->pose_tail);
|
||||
|
||||
set_constraint_target(con, obt, NULL);
|
||||
|
||||
/* restore, add_object sets active */
|
||||
BASACT= base;
|
||||
base->flag |= SELECT;
|
||||
}
|
||||
else if(nr==2) {
|
||||
set_constraint_target(con, ob, pchansel->name);
|
||||
}
|
||||
|
||||
ob->pose->flag |= POSE_RECALC; // sort pose channels
|
||||
DAG_scene_sort(G.scene); // sort order of objects
|
||||
|
||||
DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA); // and all its relations
|
||||
|
||||
allqueue (REDRAWVIEW3D, 0);
|
||||
allqueue (REDRAWBUTSOBJECT, 0);
|
||||
|
||||
BIF_undo_push("Add IK constraint");
|
||||
}
|
||||
|
||||
/* context: all selected channels */
|
||||
void pose_clear_IK(void)
|
||||
{
|
||||
Object *ob= OBACT;
|
||||
bPoseChannel *pchan;
|
||||
bConstraint *con;
|
||||
bConstraint *next;
|
||||
|
||||
/* paranoia checks */
|
||||
if(!ob && !ob->pose) return;
|
||||
if(ob==G.obedit || (ob->flag & OB_POSEMODE)==0) return;
|
||||
|
||||
if(okee("Remove IK constraint(s)")==0) return;
|
||||
|
||||
for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
|
||||
if(pchan->bone->flag & (BONE_ACTIVE|BONE_SELECTED)) {
|
||||
|
||||
for(con= pchan->constraints.first; con; con= next) {
|
||||
next= con->next;
|
||||
if(con->type==CONSTRAINT_TYPE_KINEMATIC) {
|
||||
BLI_remlink(&pchan->constraints, con);
|
||||
free_constraint_data(con);
|
||||
MEM_freeN(con);
|
||||
}
|
||||
}
|
||||
pchan->constflag &= ~PCHAN_HAS_IK;
|
||||
}
|
||||
}
|
||||
|
||||
DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA); // and all its relations
|
||||
|
||||
allqueue (REDRAWVIEW3D, 0);
|
||||
allqueue (REDRAWBUTSOBJECT, 0);
|
||||
|
||||
BIF_undo_push("Remove IK constraint(s)");
|
||||
}
|
||||
|
||||
void pose_clear_constraints(void)
|
||||
{
|
||||
Object *ob= OBACT;
|
||||
bPoseChannel *pchan;
|
||||
|
||||
/* paranoia checks */
|
||||
if(!ob && !ob->pose) return;
|
||||
if(ob==G.obedit || (ob->flag & OB_POSEMODE)==0) return;
|
||||
|
||||
if(okee("Remove Constraints")==0) return;
|
||||
|
||||
/* find active */
|
||||
for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
|
||||
if(pchan->bone->flag & (BONE_ACTIVE|BONE_SELECTED)) {
|
||||
free_constraints(&pchan->constraints);
|
||||
pchan->constflag= 0;
|
||||
}
|
||||
}
|
||||
|
||||
DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA); // and all its relations
|
||||
|
||||
allqueue (REDRAWVIEW3D, 0);
|
||||
allqueue (REDRAWBUTSOBJECT, 0);
|
||||
|
||||
BIF_undo_push("Remove Constraint(s)");
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -103,6 +103,7 @@
|
||||
#include "BIF_meshtools.h"
|
||||
#include "BIF_mywindow.h"
|
||||
#include "BIF_oops.h"
|
||||
#include "BIF_poseobject.h"
|
||||
#include "BIF_outliner.h"
|
||||
#include "BIF_resources.h"
|
||||
#include "BIF_screen.h"
|
||||
@@ -1122,7 +1123,10 @@ static void winqreadview3dspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
|
||||
copy_attr_menu();
|
||||
}
|
||||
else if(G.qual==LR_ALTKEY) {
|
||||
convertmenu(); /* editobject.c */
|
||||
if(ob && (ob->flag & OB_POSEMODE))
|
||||
pose_clear_constraints(); /* poseobject.c */
|
||||
else
|
||||
convertmenu(); /* editobject.c */
|
||||
}
|
||||
else if((G.qual==LR_SHIFTKEY)) {
|
||||
view3d_home(1);
|
||||
@@ -1295,6 +1299,17 @@ static void winqreadview3dspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
|
||||
}
|
||||
break;
|
||||
case IKEY:
|
||||
if(G.obedit);
|
||||
else if(G.qual==LR_CTRLKEY) {
|
||||
if(ob && ob->type==OB_ARMATURE)
|
||||
if(ob->flag & OB_POSEMODE)
|
||||
pose_add_IK();
|
||||
}
|
||||
else if(G.qual==LR_ALTKEY) {
|
||||
if(ob && ob->type==OB_ARMATURE)
|
||||
if(ob->flag & OB_POSEMODE)
|
||||
pose_clear_IK();
|
||||
}
|
||||
break;
|
||||
|
||||
case JKEY:
|
||||
|
||||
Reference in New Issue
Block a user