diff options
Diffstat (limited to 'source/blender')
50 files changed, 3714 insertions, 705 deletions
diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt index 703c5acd8a2..99297714fd2 100644 --- a/source/blender/CMakeLists.txt +++ b/source/blender/CMakeLists.txt @@ -40,6 +40,7 @@ ADD_SUBDIRECTORY(makesrna) ADD_SUBDIRECTORY(readblenfile) ADD_SUBDIRECTORY(render) ADD_SUBDIRECTORY(blenfont) +ADD_SUBDIRECTORY(ikplugin) IF(WITH_OPENEXR) ADD_SUBDIRECTORY(imbuf/intern/openexr) diff --git a/source/blender/Makefile b/source/blender/Makefile index 31636f838c3..6bc874c3c93 100644 --- a/source/blender/Makefile +++ b/source/blender/Makefile @@ -34,7 +34,7 @@ DIRS = windowmanager editors blenloader readblenfile DIRS += avi imbuf render blenlib blenkernel blenpluginapi DIRS += makesdna makesrna DIRS += python nodes gpu -DIRS += blenfont +DIRS += blenfont ikplugin ifeq ($(WITH_QUICKTIME), true) DIRS += quicktime diff --git a/source/blender/SConscript b/source/blender/SConscript index a064850c170..af2c81a3b45 100644 --- a/source/blender/SConscript +++ b/source/blender/SConscript @@ -16,6 +16,7 @@ SConscript(['avi/SConscript', 'readblenfile/SConscript', 'render/SConscript', 'nodes/SConscript', + 'ikplugin/SConscript', 'windowmanager/SConscript', 'blenfont/SConscript']) diff --git a/source/blender/blenkernel/BKE_action.h b/source/blender/blenkernel/BKE_action.h index f079cc08281..17b56864d1e 100644 --- a/source/blender/blenkernel/BKE_action.h +++ b/source/blender/blenkernel/BKE_action.h @@ -41,6 +41,7 @@ struct bAction; struct bActionGroup; struct FCurve; struct bPose; +struct bItasc; struct bPoseChannel; struct Object; struct Scene; @@ -154,11 +155,21 @@ struct bPoseChannel *get_active_posechannel(struct Object *ob); */ struct bPoseChannel *verify_pose_channel(struct bPose* pose, const char* name); - +/* Copy the data from the action-pose (src) into the pose */ +void extract_pose_from_pose(struct bPose *pose, const struct bPose *src); /* sets constraint flags */ void update_pose_constraint_flags(struct bPose *pose); +/* return the name of structure pointed by pose->ikparam */ +const char *get_ikparam_name(struct bPose *pose); + +/* allocate and initialize pose->ikparam according to pose->iksolver */ +void init_pose_ikparam(struct bPose *pose); + +/* initialize a bItasc structure with default value */ +void init_pose_itasc(struct bItasc *itasc); + /* clears BONE_UNKEYED flags for frame changing */ // XXX to be depreceated for a more general solution in animsys... void framechange_poses_clear_unkeyed(void); @@ -181,16 +192,6 @@ void copy_pose_result(struct bPose *to, struct bPose *from); /* clear all transforms */ void rest_pose(struct bPose *pose); -/* Game Engine ------------------------- */ - -/* exported for game engine */ -void game_blend_poses(struct bPose *dst, struct bPose *src, float srcweight/*, short mode*/); /* was blend_poses */ -void extract_pose_from_pose(struct bPose *pose, const struct bPose *src); - -/* functions used by the game engine */ -void game_copy_pose(struct bPose **dst, struct bPose *src); -void game_free_pose(struct bPose *pose); - #ifdef __cplusplus }; #endif diff --git a/source/blender/blenkernel/BKE_armature.h b/source/blender/blenkernel/BKE_armature.h index 1cbb2331782..8dbd2721fb9 100644 --- a/source/blender/blenkernel/BKE_armature.h +++ b/source/blender/blenkernel/BKE_armature.h @@ -89,6 +89,7 @@ void where_is_armature (struct bArmature *arm); void where_is_armature_bone(struct Bone *bone, struct Bone *prevbone); void armature_rebuild_pose(struct Object *ob, struct bArmature *arm); void where_is_pose (struct Scene *scene, struct Object *ob); +void where_is_pose_bone(struct Scene *scene, struct Object *ob, struct bPoseChannel *pchan, float ctime); /* get_objectspace_bone_matrix has to be removed still */ void get_objectspace_bone_matrix (struct Bone* bone, float M_accumulatedMatrix[][4], int root, int posed); @@ -102,11 +103,6 @@ void armature_mat_pose_to_bone(struct bPoseChannel *pchan, float inmat[][4], flo void armature_loc_pose_to_bone(struct bPoseChannel *pchan, float *inloc, float *outloc); void armature_mat_pose_to_delta(float delta_mat[][4], float pose_mat[][4], float arm_mat[][4]); -/* Animation functions */ -struct PoseTree *ik_tree_to_posetree(struct Object *ob, struct Bone *bone); -void solve_posetree(PoseTree *tree); -void free_posetree(PoseTree *tree); - /* B-Bone support */ typedef struct Mat4 { float mat[4][4]; diff --git a/source/blender/blenkernel/BKE_constraint.h b/source/blender/blenkernel/BKE_constraint.h index a0061173438..126816f5a95 100644 --- a/source/blender/blenkernel/BKE_constraint.h +++ b/source/blender/blenkernel/BKE_constraint.h @@ -38,6 +38,9 @@ struct Scene; struct bPoseChannel; /* ---------------------------------------------------------------------------- */ +#ifdef __cplusplus +extern "C" { +#endif /* special struct for use in constraint evaluation */ typedef struct bConstraintOb { @@ -131,6 +134,9 @@ void constraint_mat_convertspace(struct Object *ob, struct bPoseChannel *pchan, void get_constraint_target_matrix(struct bConstraint *con, int n, short ownertype, void *ownerdata, float mat[][4], float ctime); void solve_constraints(struct ListBase *conlist, struct bConstraintOb *cob, float ctime); +#ifdef __cplusplus +} +#endif #endif diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 68aed2b0184..3473950ab3a 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -30,7 +30,7 @@ SET(INC . ../../../intern/guardedalloc ../../../intern/memutil ../editors/include ../blenlib ../makesdna ../render/extern/include ../../../intern/decimation/extern ../imbuf ../avi ../../../intern/elbeem/extern ../../../intern/opennl/extern - ../../../intern/iksolver/extern ../blenloader + ../../../intern/iksolver/extern ../blenloader ../ikplugin ../nodes ../../../extern/glew/include ../gpu ../makesrna ../../../intern/smoke/extern ../../../intern/bsp/extern ../blenfont ../../../intern/audaspace/intern diff --git a/source/blender/blenkernel/SConscript b/source/blender/blenkernel/SConscript index 1f42390504d..944667e2963 100644 --- a/source/blender/blenkernel/SConscript +++ b/source/blender/blenkernel/SConscript @@ -5,7 +5,7 @@ sources = env.Glob('intern/*.c') incs = '. #/intern/guardedalloc #/intern/memutil ../editors/include ../blenlib ../blenfont ../makesdna' incs += ' ../render/extern/include #/intern/decimation/extern ../makesrna' -incs += ' ../imbuf ../avi #/intern/elbeem/extern ../nodes' +incs += ' ../imbuf ../ikplugin ../avi #/intern/elbeem/extern ../nodes' incs += ' #/intern/iksolver/extern ../blenloader' incs += ' #/extern/bullet2/src' incs += ' #/intern/opennl/extern #/intern/bsp/extern' diff --git a/source/blender/blenkernel/intern/Makefile b/source/blender/blenkernel/intern/Makefile index 6c2edc9e25f..f16b57c8469 100644 --- a/source/blender/blenkernel/intern/Makefile +++ b/source/blender/blenkernel/intern/Makefile @@ -47,6 +47,7 @@ CPPFLAGS += -I$(NAN_AUDASPACE)/include CPPFLAGS += -I../../makesdna CPPFLAGS += -I../../makesrna CPPFLAGS += -I../../imbuf +CPPFLAGS += -I../../ikplugin # This mod uses the BLI and BLO module CPPFLAGS += -I../../blenlib CPPFLAGS += -I../../blenloader diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index 1ff5d9b5c01..4cfd35a494d 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -62,6 +62,7 @@ #include "BKE_main.h" #include "BKE_object.h" #include "BKE_utildefines.h" +#include "BIK_api.h" #include "BLI_arithb.h" #include "BLI_blenlib.h" @@ -451,7 +452,7 @@ bPoseChannel *verify_pose_channel(bPose* pose, const char* name) 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]= 0.0f; - + chan->ikrotweight = chan->iklinweight = 0.0f; Mat4One(chan->constinv); BLI_addtail(&pose->chanbase, chan); @@ -477,7 +478,18 @@ bPoseChannel *get_active_posechannel (Object *ob) return NULL; } - +const char *get_ikparam_name(bPose *pose) +{ + if (pose) { + switch (pose->iksolver) { + case IKSOLVER_LEGACY: + return NULL; + case IKSOLVER_ITASC: + return "bItasc"; + } + } + return NULL; +} /* dst should be freed already, makes entire duplicate */ void copy_pose (bPose **dst, bPose *src, int copycon) { @@ -499,7 +511,10 @@ void copy_pose (bPose **dst, bPose *src, int copycon) outPose= MEM_callocN(sizeof(bPose), "pose"); BLI_duplicatelist(&outPose->chanbase, &src->chanbase); - + outPose->iksolver = src->iksolver; + outPose->ikdata = NULL; + outPose->ikparam = MEM_dupallocN(src->ikparam); + if (copycon) { for (pchan=outPose->chanbase.first; pchan; pchan=pchan->next) { copy_constraints(&listb, &pchan->constraints); // copy_constraints NULLs listb @@ -511,6 +526,39 @@ void copy_pose (bPose **dst, bPose *src, int copycon) *dst=outPose; } +void init_pose_itasc(bItasc *itasc) +{ + if (itasc) { + itasc->iksolver = IKSOLVER_ITASC; + itasc->minstep = 0.01f; + itasc->maxstep = 0.06f; + itasc->numiter = 100; + itasc->numstep = 4; + itasc->precision = 0.005f; + itasc->flag = ITASC_AUTO_STEP|ITASC_INITIAL_REITERATION|ITASC_SIMULATION; + itasc->feedback = 20.f; + itasc->maxvel = 50.f; + itasc->solver = ITASC_SOLVER_SDLS; + itasc->dampmax = 0.5; + itasc->dampeps = 0.15; + } +} +void init_pose_ikparam(bPose *pose) +{ + bItasc *itasc; + switch (pose->iksolver) { + case IKSOLVER_ITASC: + itasc = MEM_callocN(sizeof(bItasc), "itasc"); + init_pose_itasc(itasc); + pose->ikparam = itasc; + break; + case IKSOLVER_LEGACY: + default: + pose->ikparam = NULL; + break; + } +} + void free_pose_channels(bPose *pose) { bPoseChannel *pchan; @@ -534,133 +582,15 @@ void free_pose(bPose *pose) /* free pose-groups */ if (pose->agroups.first) BLI_freelistN(&pose->agroups); - - /* free pose */ - MEM_freeN(pose); - } -} - -void game_copy_pose(bPose **dst, bPose *src) -{ - bPose *out; - bPoseChannel *pchan, *outpchan; - GHash *ghash; - - /* the game engine copies the current armature pose and then swaps - * the object pose pointer. this makes it possible to change poses - * without affecting the original blender data. */ - - if (!src) { - *dst=NULL; - return; - } - else if (*dst==src) { - printf("copy_pose source and target are the same\n"); - *dst=NULL; - return; - } - - out= MEM_dupallocN(src); - out->agroups.first= out->agroups.last= NULL; - BLI_duplicatelist(&out->chanbase, &src->chanbase); - - /* remap pointers */ - ghash= BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp); - pchan= src->chanbase.first; - outpchan= out->chanbase.first; - for (; pchan; pchan=pchan->next, outpchan=outpchan->next) - BLI_ghash_insert(ghash, pchan, outpchan); - - for (pchan=out->chanbase.first; pchan; pchan=pchan->next) { - pchan->parent= BLI_ghash_lookup(ghash, pchan->parent); - pchan->child= BLI_ghash_lookup(ghash, pchan->child); - pchan->path= NULL; - } - - BLI_ghash_free(ghash, NULL, NULL); - - *dst=out; -} + /* free IK solver state */ + BIK_clear_data(pose); + /* free IK solver param */ + if (pose->ikparam) + MEM_freeN(pose->ikparam); -/* Only allowed for Poses with identical channels */ -void game_blend_poses(bPose *dst, bPose *src, float srcweight/*, short mode*/) -{ - short mode= ACTSTRIPMODE_BLEND; - - bPoseChannel *dchan; - const bPoseChannel *schan; - bConstraint *dcon, *scon; - float dstweight; - int i; - - switch (mode){ - case ACTSTRIPMODE_BLEND: - dstweight = 1.0F - srcweight; - break; - case ACTSTRIPMODE_ADD: - dstweight = 1.0F; - break; - default : - dstweight = 1.0F; - } - - schan= src->chanbase.first; - for (dchan = dst->chanbase.first; dchan; dchan=dchan->next, schan= schan->next){ - if (schan->flag & (POSE_ROT|POSE_LOC|POSE_SIZE)) { - /* replaced quat->matrix->quat conversion with decent quaternion interpol (ton) */ - - /* Do the transformation blend */ - if (schan->flag & POSE_ROT) { - /* quat interpolation done separate */ - if (schan->rotmode == PCHAN_ROT_QUAT) { - float dquat[4], squat[4]; - - QUATCOPY(dquat, dchan->quat); - QUATCOPY(squat, schan->quat); - if (mode==ACTSTRIPMODE_BLEND) - QuatInterpol(dchan->quat, dquat, squat, srcweight); - else { - QuatMulFac(squat, srcweight); - QuatMul(dchan->quat, dquat, squat); - } - - NormalQuat(dchan->quat); - } - } - - for (i=0; i<3; i++) { - /* blending for loc and scale are pretty self-explanatory... */ - if (schan->flag & POSE_LOC) - dchan->loc[i] = (dchan->loc[i]*dstweight) + (schan->loc[i]*srcweight); - if (schan->flag & POSE_SIZE) - dchan->size[i] = 1.0f + ((dchan->size[i]-1.0f)*dstweight) + ((schan->size[i]-1.0f)*srcweight); - - /* euler-rotation interpolation done here instead... */ - // FIXME: are these results decent? - if ((schan->flag & POSE_ROT) && (schan->rotmode)) - dchan->eul[i] = (dchan->eul[i]*dstweight) + (schan->eul[i]*srcweight); - } - dchan->flag |= schan->flag; - } - for(dcon= dchan->constraints.first, scon= schan->constraints.first; dcon && scon; dcon= dcon->next, scon= scon->next) { - /* no 'add' option for constraint blending */ - dcon->enforce= dcon->enforce*(1.0f-srcweight) + scon->enforce*srcweight; - } - } - - /* this pose is now in src time */ - dst->ctime= src->ctime; -} - -void game_free_pose(bPose *pose) -{ - if (pose) { - /* we don't free constraints, those are owned by the original pose */ - if(pose->chanbase.first) - BLI_freelistN(&pose->chanbase); - + /* free pose */ MEM_freeN(pose); } } @@ -917,7 +847,6 @@ void calc_action_range(const bAction *act, float *start, float *end, short incl_ } } - /* Return flags indicating which transforms the given object/posechannel has * - if 'curves' is provided, a list of links to these curves are also returned */ diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index c880925aa94..b2368451414 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -66,10 +66,9 @@ #include "BKE_object.h" #include "BKE_object.h" #include "BKE_utildefines.h" +#include "BIK_api.h" #include "BKE_sketch.h" -#include "IK_solver.h" - #ifdef HAVE_CONFIG_H #include <config.h> #endif @@ -1569,409 +1568,10 @@ void armature_rebuild_pose(Object *ob, bArmature *arm) DAG_pose_sort(ob); ob->pose->flag &= ~POSE_RECALC; + ob->pose->flag |= POSE_WAS_REBUILT; } -/* ********************** THE IK SOLVER ******************* */ - - - -/* allocates PoseTree, and links that to root bone/channel */ -/* 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; - PoseTree *tree; - PoseTarget *target; - bConstraint *con; - bKinematicConstraint *data= NULL; - int a, segcount= 0, size, newsize, *oldparent, parent; - - /* find IK constraint, and validate it */ - for(con= pchan_tip->constraints.first; con; con= con->next) { - if(con->type==CONSTRAINT_TYPE_KINEMATIC) { - data=(bKinematicConstraint*)con->data; - if (data->flag & CONSTRAINT_IK_AUTO) break; - if (data->tar==NULL) continue; - if (data->tar->type==OB_ARMATURE && data->subtarget[0]==0) continue; - if ((con->flag & CONSTRAINT_DISABLE)==0 && (con->enforce!=0.0)) break; - } - } - if(con==NULL) return; - - /* exclude tip from chain? */ - if(!(data->flag & CONSTRAINT_IK_TIP)) - pchan_tip= pchan_tip->parent; - - /* Find the chain's root & count the segments needed */ - for (curchan = pchan_tip; curchan; curchan=curchan->parent){ - pchan_root = curchan; - - curchan->flag |= POSE_CHAIN; // don't forget to clear this - chanlist[segcount]=curchan; - segcount++; - - if(segcount==data->rootbone || segcount>255) break; // 255 is weak - } - if (!segcount) return; - - /* setup the chain data */ - - /* we make tree-IK, unless all existing targets are in this chain */ - for(tree= pchan_root->iktree.first; tree; tree= tree->next) { - for(target= tree->targets.first; target; target= target->next) { - curchan= tree->pchan[target->tip]; - if(curchan->flag & POSE_CHAIN) - curchan->flag &= ~POSE_CHAIN; - else - break; - } - if(target) break; - } - - /* create a target */ - target= MEM_callocN(sizeof(PoseTarget), "posetarget"); - target->con= con; - pchan_tip->flag &= ~POSE_CHAIN; - - if(tree==NULL) { - /* make new tree */ - tree= MEM_callocN(sizeof(PoseTree), "posetree"); - - tree->iterations= data->iterations; - tree->totchannel= segcount; - tree->stretch = (data->flag & CONSTRAINT_IK_STRETCH); - - tree->pchan= MEM_callocN(segcount*sizeof(void*), "ik tree pchan"); - tree->parent= MEM_callocN(segcount*sizeof(int), "ik tree parent"); - for(a=0; a<segcount; a++) { - tree->pchan[a]= chanlist[segcount-a-1]; - tree->parent[a]= a-1; - } - target->tip= segcount-1; - - /* AND! link the tree to the root */ - BLI_addtail(&pchan_root->iktree, tree); - } - else { - tree->iterations= MAX2(data->iterations, tree->iterations); - tree->stretch= tree->stretch && !(data->flag & CONSTRAINT_IK_STRETCH); - - /* skip common pose channels and add remaining*/ - size= MIN2(segcount, tree->totchannel); - for(a=0; a<size && tree->pchan[a]==chanlist[segcount-a-1]; a++); - parent= a-1; - - segcount= segcount-a; - target->tip= tree->totchannel + segcount - 1; - - if (segcount > 0) { - /* resize array */ - newsize= tree->totchannel + segcount; - oldchan= tree->pchan; - oldparent= tree->parent; - - tree->pchan= MEM_callocN(newsize*sizeof(void*), "ik tree pchan"); - tree->parent= MEM_callocN(newsize*sizeof(int), "ik tree parent"); - memcpy(tree->pchan, oldchan, sizeof(void*)*tree->totchannel); - memcpy(tree->parent, oldparent, sizeof(int)*tree->totchannel); - MEM_freeN(oldchan); - MEM_freeN(oldparent); - - /* add new pose channels at the end, in reverse order */ - for(a=0; a<segcount; a++) { - tree->pchan[tree->totchannel+a]= chanlist[segcount-a-1]; - tree->parent[tree->totchannel+a]= tree->totchannel+a-1; - } - tree->parent[tree->totchannel]= parent; - - tree->totchannel= newsize; - } - - /* move tree to end of list, for correct evaluation order */ - BLI_remlink(&pchan_root->iktree, tree); - BLI_addtail(&pchan_root->iktree, tree); - } - - /* add target to the tree */ - BLI_addtail(&tree->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_posetree(Object *ob, PoseTree *tree) -{ - float R_parmat[3][3], identity[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 goal[4][4], goalinv[4][4]; - float irest_basis[3][3], full_basis[3][3]; - float end_pose[4][4], world_pose[4][4]; - float length, basis[3][3], rest_basis[3][3], start[3], *ikstretch=NULL; - float resultinf=0.0f; - int a, flag, hasstretch=0, resultblend=0; - bPoseChannel *pchan; - IK_Segment *seg, *parent, **iktree, *iktarget; - IK_Solver *solver; - PoseTarget *target; - bKinematicConstraint *data, *poleangledata=NULL; - Bone *bone; - - if (tree->totchannel == 0) - return; - - iktree= MEM_mallocN(sizeof(void*)*tree->totchannel, "ik tree"); - - for(a=0; a<tree->totchannel; a++) { - pchan= tree->pchan[a]; - bone= pchan->bone; - - /* set DoF flag */ - flag= 0; - if(!(pchan->ikflag & BONE_IK_NO_XDOF) && !(pchan->ikflag & BONE_IK_NO_XDOF_TEMP)) - flag |= IK_XDOF; - if(!(pchan->ikflag & BONE_IK_NO_YDOF) && !(pchan->ikflag & BONE_IK_NO_YDOF_TEMP)) - flag |= IK_YDOF; - if(!(pchan->ikflag & BONE_IK_NO_ZDOF) && !(pchan->ikflag & BONE_IK_NO_ZDOF_TEMP)) - flag |= IK_ZDOF; - - if(tree->stretch && (pchan->ikstretch > 0.0)) { - flag |= IK_TRANS_YDOF; - hasstretch = 1; - } - - seg= iktree[a]= IK_CreateSegment(flag); - - /* find parent */ - if(a == 0) - parent= NULL; - else - parent= iktree[tree->parent[a]]; - - IK_SetParent(seg, parent); - - /* get the matrix that transforms from prevbone into this bone */ - Mat3CpyMat4(R_bonemat, pchan->pose_mat); - - /* gather transformations for this IK segment */ - - if (pchan->parent) - Mat3CpyMat4(R_parmat, pchan->parent->pose_mat); - else - Mat3One(R_parmat); - - /* bone offset */ - if (pchan->parent && (a > 0)) - VecSubf(start, pchan->pose_head, pchan->parent->pose_tail); - else - /* only root bone (a = 0) has no parent */ - start[0]= start[1]= start[2]= 0.0f; - - /* change length based on bone size */ - length= bone->length*VecLength(R_bonemat[1]); - - /* compute rest basis and its inverse */ - Mat3CpyMat3(rest_basis, bone->bone_mat); - Mat3CpyMat3(irest_basis, bone->bone_mat); - Mat3Transp(irest_basis); - - /* compute basis with rest_basis removed */ - Mat3Inv(iR_parmat, R_parmat); - Mat3MulMat3(full_basis, iR_parmat, R_bonemat); - Mat3MulMat3(basis, irest_basis, full_basis); - - /* basis must be pure rotation */ - Mat3Ortho(basis); - - /* transform offset into local bone space */ - Mat3Ortho(iR_parmat); - Mat3MulVecfl(iR_parmat, start); - - 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]); - - if(tree->stretch && (pchan->ikstretch > 0.0f)) { - float ikstretch = pchan->ikstretch*pchan->ikstretch; - IK_SetStiffness(seg, IK_TRANS_Y, MIN2(1.0f-ikstretch, 0.99f)); - IK_SetLimit(seg, IK_TRANS_Y, 0.001f, 1e10); - } - } - - solver= IK_CreateSolver(iktree[0]); - - /* set solver goals */ - - /* first set the goal inverse transform, assuming the root of tree was done ok! */ - pchan= tree->pchan[0]; - if (pchan->parent) - /* transform goal by parent mat, so this rotation is not part of the - segment's basis. otherwise rotation limits do not work on the - local transform of the segment itself. */ - Mat4CpyMat4(rootmat, pchan->parent->pose_mat); - else - Mat4One(rootmat); - VECCOPY(rootmat[3], pchan->pose_head); - - Mat4MulMat4 (imat, rootmat, ob->obmat); - Mat4Invert (goalinv, imat); - - for (target=tree->targets.first; target; target=target->next) { - float polepos[3]; - int poleconstrain= 0; - - data= (bKinematicConstraint*)target->con->data; - - /* 1.0=ctime, we pass on object for auto-ik (owner-type here is object, even though - * strictly speaking, it is a posechannel) - */ - get_constraint_target_matrix(target->con, 0, CONSTRAINT_OBTYPE_OBJECT, ob, rootmat, 1.0); - - /* and set and transform goal */ - Mat4MulMat4(goal, rootmat, goalinv); - - VECCOPY(goalpos, goal[3]); - Mat3CpyMat4(goalrot, goal); - - /* same for pole vector target */ - if(data->poletar) { - get_constraint_target_matrix(target->con, 1, CONSTRAINT_OBTYPE_OBJECT, ob, rootmat, 1.0); - - if(data->flag & CONSTRAINT_IK_SETANGLE) { - /* don't solve IK when we are setting the pole angle */ - break; - } - else { - Mat4MulMat4(goal, rootmat, goalinv); - VECCOPY(polepos, goal[3]); - poleconstrain= 1; - - /* for pole targets, we blend the result of the ik solver - * instead of the target position, otherwise we can't get - * a smooth transition */ - resultblend= 1; - resultinf= target->con->enforce; - - if(data->flag & CONSTRAINT_IK_GETANGLE) { - poleangledata= data; - data->flag &= ~CONSTRAINT_IK_GETANGLE; - } - } - } - - /* do we need blending? */ - if (!resultblend && target->con->enforce!=1.0f) { - float q1[4], q2[4], q[4]; - float fac= target->con->enforce; - float mfac= 1.0f-fac; - - pchan= tree->pchan[target->tip]; - - /* end effector in world space */ - Mat4CpyMat4(end_pose, pchan->pose_mat); - VECCOPY(end_pose[3], pchan->pose_tail); - Mat4MulSerie(world_pose, goalinv, ob->obmat, end_pose, 0, 0, 0, 0, 0); - - /* blend position */ - goalpos[0]= fac*goalpos[0] + mfac*world_pose[3][0]; - goalpos[1]= fac*goalpos[1] + mfac*world_pose[3][1]; - goalpos[2]= fac*goalpos[2] + mfac*world_pose[3][2]; - - /* blend rotation */ - Mat3ToQuat(goalrot, q1); - Mat4ToQuat(world_pose, q2); - QuatInterpol(q, q1, q2, mfac); - QuatToMat3(q, goalrot); - } - - iktarget= iktree[target->tip]; - - if(data->weight != 0.0f) { - if(poleconstrain) - IK_SolverSetPoleVectorConstraint(solver, iktarget, goalpos, - polepos, data->poleangle*(float)M_PI/180.0f, (poleangledata == data)); - IK_SolverAddGoal(solver, iktarget, goalpos, data->weight); - } - if((data->flag & CONSTRAINT_IK_ROT) && (data->orientweight != 0.0f)) - if((data->flag & CONSTRAINT_IK_AUTO)==0) - IK_SolverAddGoalOrientation(solver, iktarget, goalrot, - data->orientweight); - } - - /* solve */ - IK_Solve(solver, 0.0f, tree->iterations); - - if(poleangledata) - poleangledata->poleangle= IK_SolverGetPoleAngle(solver)*180.0f/(float)M_PI; - - IK_FreeSolver(solver); - - /* gather basis changes */ - tree->basis_change= MEM_mallocN(sizeof(float[3][3])*tree->totchannel, "ik basis change"); - if(hasstretch) - ikstretch= MEM_mallocN(sizeof(float)*tree->totchannel, "ik stretch"); - - for(a=0; a<tree->totchannel; a++) { - IK_GetBasisChange(iktree[a], tree->basis_change[a]); - - if(hasstretch) { - /* have to compensate for scaling received from parent */ - float parentstretch, stretch; - - pchan= tree->pchan[a]; - parentstretch= (tree->parent[a] >= 0)? ikstretch[tree->parent[a]]: 1.0f; - - if(tree->stretch && (pchan->ikstretch > 0.0f)) { - float trans[3], length; - - IK_GetTranslationChange(iktree[a], trans); - length= pchan->bone->length*VecLength(pchan->pose_mat[1]); - - ikstretch[a]= (length == 0.0f)? 1.0f: (trans[1]+length)/length; - } - else - ikstretch[a] = 1.0f; - - stretch= (parentstretch == 0.0f)? 1.0f: ikstretch[a]/parentstretch; - - VecMulf(tree->basis_change[a][0], stretch); - VecMulf(tree->basis_change[a][1], stretch); - VecMulf(tree->basis_change[a][2], stretch); - } - - if(resultblend && resultinf!=1.0f) { - Mat3One(identity); - Mat3BlendMat3(tree->basis_change[a], identity, - tree->basis_change[a], resultinf); - } - - IK_FreeSegment(iktree[a]); - } - - MEM_freeN(iktree); - if(ikstretch) MEM_freeN(ikstretch); -} - -void free_posetree(PoseTree *tree) -{ - BLI_freelistN(&tree->targets); - if(tree->pchan) MEM_freeN(tree->pchan); - if(tree->parent) MEM_freeN(tree->parent); - if(tree->basis_change) MEM_freeN(tree->basis_change); - MEM_freeN(tree); -} - /* ********************** THE POSE SOLVER ******************* */ @@ -2012,41 +1612,6 @@ void chan_calc_mat(bPoseChannel *chan) } } -/* transform from bone(b) to bone(b+1), store in chan_mat */ -static void make_dmats(bPoseChannel *pchan) -{ - if (pchan->parent) { - float iR_parmat[4][4]; - Mat4Invert(iR_parmat, pchan->parent->pose_mat); - Mat4MulMat4(pchan->chan_mat, pchan->pose_mat, iR_parmat); // delta mat - } - else Mat4CpyMat4(pchan->chan_mat, pchan->pose_mat); -} - -/* applies IK matrix to pchan, IK is done separated */ -/* formula: pose_mat(b) = pose_mat(b-1) * diffmat(b-1, b) * ik_mat(b) */ -/* to make this work, the diffmats have to be precalculated! Stored in chan_mat */ -static void where_is_ik_bone(bPoseChannel *pchan, float ik_mat[][3]) // nr = to detect if this is first bone -{ - float vec[3], ikmat[4][4]; - - Mat4CpyMat3(ikmat, ik_mat); - - if (pchan->parent) - Mat4MulSerie(pchan->pose_mat, pchan->parent->pose_mat, pchan->chan_mat, ikmat, NULL, NULL, NULL, NULL, NULL); - else - Mat4MulMat4(pchan->pose_mat, ikmat, pchan->chan_mat); - - /* calculate head */ - VECCOPY(pchan->pose_head, pchan->pose_mat[3]); - /* calculate tail */ - VECCOPY(vec, pchan->pose_mat[1]); - VecMulf(vec, pchan->bone->length); - VecAddf(pchan->pose_tail, pchan->pose_head, vec); - - pchan->flag |= POSE_DONE; -} - /* NLA strip modifiers */ static void do_strip_modifiers(Scene *scene, Object *armob, Bone *bone, bPoseChannel *pchan) { @@ -2172,7 +1737,7 @@ static void do_strip_modifiers(Scene *scene, Object *armob, Bone *bone, bPoseCha /* The main armature solver, does all constraints excluding IK */ /* pchan is validated, as having bone and parent pointer */ -static void where_is_pose_bone(Scene *scene, Object *ob, bPoseChannel *pchan, float ctime) +void where_is_pose_bone(Scene *scene, Object *ob, bPoseChannel *pchan, float ctime) { Bone *bone, *parbone; bPoseChannel *parchan; @@ -2312,48 +1877,27 @@ void where_is_pose (Scene *scene, Object *ob) else { Mat4Invert(ob->imat, ob->obmat); // imat is needed - /* 1. construct the PoseTrees, clear flags */ + /* 1. clear flags */ for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) { - pchan->flag &= ~(POSE_DONE|POSE_CHAIN); - if(pchan->constflag & PCHAN_HAS_IK) // flag is set on editing constraints - initialize_posetree(ob, pchan); // will attach it to root! + pchan->flag &= ~(POSE_DONE|POSE_CHAIN|POSE_IKTREE); } - - /* 2. the main loop, channels are already hierarchical sorted from root to children */ + /* 2. construct the IK tree */ + BIK_initialize_tree(scene, ob, ctime); + + /* 3. the main loop, channels are already hierarchical sorted from root to children */ for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) { - /* 3. if we find an IK root, we handle it separated */ - if(pchan->iktree.first) { - while(pchan->iktree.first) { - PoseTree *tree= pchan->iktree.first; - int a; - - /* 4. walk over the tree for regular solving */ - for(a=0; a<tree->totchannel; a++) { - if(!(tree->pchan[a]->flag & POSE_DONE)) // successive trees can set the flag - where_is_pose_bone(scene, ob, tree->pchan[a], ctime); - } - /* 5. execute the IK solver */ - execute_posetree(ob, tree); - - /* 6. apply the differences to the channels, - we need to calculate the original differences first */ - for(a=0; a<tree->totchannel; a++) - make_dmats(tree->pchan[a]); - - for(a=0; a<tree->totchannel; a++) - /* sets POSE_DONE */ - where_is_ik_bone(tree->pchan[a], tree->basis_change[a]); - - /* 7. and free */ - BLI_remlink(&pchan->iktree, tree); - free_posetree(tree); - } + /* 4. if we find an IK root, we handle it separated */ + if(pchan->flag & POSE_IKTREE) { + BIK_execute_tree(scene, ob, pchan, ctime); } + /* 5. otherwise just call the normal solver */ else if(!(pchan->flag & POSE_DONE)) { where_is_pose_bone(scene, ob, pchan, ctime); } } + /* 6. release the IK tree */ + BIK_release_tree(scene, ob, ctime); } /* calculating deform matrices */ diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index a3d59720645..b8d6b333674 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -1053,6 +1053,7 @@ static void kinematic_new_data (void *cdata) data->weight= (float)1.0; data->orientweight= (float)1.0; data->iterations = 500; + data->dist= (float)1.0; data->flag= CONSTRAINT_IK_TIP|CONSTRAINT_IK_STRETCH|CONSTRAINT_IK_POS; } @@ -3643,7 +3644,7 @@ void solve_constraints (ListBase *conlist, bConstraintOb *cob, float ctime) /* these we can skip completely (invalid constraints...) */ if (cti == NULL) continue; - if (con->flag & CONSTRAINT_DISABLE) continue; + if (con->flag & (CONSTRAINT_DISABLE|CONSTRAINT_OFF)) continue; /* these constraints can't be evaluated anyway */ if (cti->evaluate_constraint == NULL) continue; /* influence == 0 should be ignored */ diff --git a/source/blender/blenkernel/intern/pointcache.c b/source/blender/blenkernel/intern/pointcache.c index dfc5b4cd770..e5f89727ab8 100644 --- a/source/blender/blenkernel/intern/pointcache.c +++ b/source/blender/blenkernel/intern/pointcache.c @@ -56,6 +56,7 @@ #include "BKE_smoke.h" #include "BKE_softbody.h" #include "BKE_utildefines.h" +#include "BIK_api.h" #include "BLI_blenlib.h" @@ -2007,6 +2008,9 @@ int BKE_ptcache_object_reset(Scene *scene, Object *ob, int mode) } } + if (ob->type == OB_ARMATURE) + BIK_clear_cache(ob->pose); + return reset; } diff --git a/source/blender/blenkernel/intern/sca.c b/source/blender/blenkernel/intern/sca.c index de2118af202..5cd554725ff 100644 --- a/source/blender/blenkernel/intern/sca.c +++ b/source/blender/blenkernel/intern/sca.c @@ -128,6 +128,9 @@ void init_sensor(bSensor *sens) case SENS_PROPERTY: sens->data= MEM_callocN(sizeof(bPropertySensor), "propsens"); break; + case SENS_ARMATURE: + sens->data= MEM_callocN(sizeof(bArmatureSensor), "armsens"); + break; case SENS_ACTUATOR: sens->data= MEM_callocN(sizeof(bActuatorSensor), "actsens"); break; @@ -455,6 +458,9 @@ void init_actuator(bActuator *act) case ACT_STATE: act->data = MEM_callocN(sizeof( bStateActuator ), "state act"); break; + case ACT_ARMATURE: + act->data = MEM_callocN(sizeof( bArmatureActuator ), "armature act"); + break; default: ; /* this is very severe... I cannot make any memory for this */ /* logic brick... */ @@ -596,6 +602,8 @@ void sca_remove_ob_poin(Object *obt, Object *ob) bEditObjectActuator *eoa; bPropertyActuator *pa; bMessageActuator *ma; + bParentActuator *para; + bArmatureActuator *aa; sens= obt->sensors.first; while(sens) { @@ -634,7 +642,15 @@ void sca_remove_ob_poin(Object *obt, Object *ob) ma= act->data; if(ma->toObject==ob) ma->toObject= NULL; break; - + case ACT_PARENT: + para = act->data; + if (para->ob==ob) para->ob = NULL; + break; + case ACT_ARMATURE: + aa = act->data; + if (aa->target == ob) aa->target = NULL; + if (aa->subtarget == ob) aa->subtarget = NULL; + break; } act= act->next; } diff --git a/source/blender/blenlib/BLI_ghash.h b/source/blender/blenlib/BLI_ghash.h index c77e82f0a2b..c9a8b1b841f 100644 --- a/source/blender/blenlib/BLI_ghash.h +++ b/source/blender/blenlib/BLI_ghash.h @@ -32,6 +32,10 @@ #ifndef BLI_GHASH_H #define BLI_GHASH_H +#ifdef __cplusplus +extern "C" { +#endif + struct GHash; typedef struct GHash GHash; @@ -125,5 +129,9 @@ int BLI_ghashutil_strcmp (void *a, void *b); unsigned int BLI_ghashutil_inthash (void *ptr); int BLI_ghashutil_intcmp(void *a, void *b); +#ifdef __cplusplus +} +#endif + #endif diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index d26a2a79f05..204176f64c3 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -2184,6 +2184,8 @@ static void lib_link_constraints(FileData *fd, ID *id, ListBase *conlist) data = ((bKinematicConstraint*)con->data); data->tar = newlibadr(fd, id->lib, data->tar); data->poletar = newlibadr(fd, id->lib, data->poletar); + con->lin_error = 0.f; + con->rot_error = 0.f; } break; case CONSTRAINT_TYPE_TRACKTO: @@ -3615,6 +3617,11 @@ static void lib_link_object(FileData *fd, Main *main) else if(act->type==ACT_STATE) { /* bStateActuator *statea = act->data; */ } + else if(act->type==ACT_ARMATURE) { + bArmatureActuator *arma= act->data; + arma->target= newlibadr(fd, ob->id.lib, arma->target); + arma->subtarget= newlibadr(fd, ob->id.lib, arma->subtarget); + } act= act->next; } @@ -3676,6 +3683,10 @@ static void direct_link_pose(FileData *fd, bPose *pose) pchan->iktree.first= pchan->iktree.last= NULL; pchan->path= NULL; } + pose->ikdata = NULL; + if (pose->ikparam != NULL) { + pose->ikparam= newdataadr(fd, pose->ikparam); + } } static void direct_link_modifiers(FileData *fd, ListBase *lb) @@ -10579,11 +10590,19 @@ static void expand_object(FileData *fd, Main *mainvar, Object *ob) bObjectActuator *oa= act->data; expand_doit(fd, mainvar, oa->reference); } + else if(act->type==ACT_ADD_OBJECT) { + bAddObjectActuator *aoa= act->data; + expand_doit(fd, mainvar, aoa->ob); + } else if(act->type==ACT_SCENE) { bSceneActuator *sa= act->data; expand_doit(fd, mainvar, sa->camera); expand_doit(fd, mainvar, sa->scene); } + else if(act->type==ACT_2DFILTER) { + bTwoDFilterActuator *tdfa= act->data; + expand_doit(fd, mainvar, tdfa->text); + } else if(act->type==ACT_ACTION) { bActionActuator *aa= act->data; expand_doit(fd, mainvar, aa->act); @@ -10600,6 +10619,14 @@ static void expand_object(FileData *fd, Main *mainvar, Object *ob) bMessageActuator *ma= act->data; expand_doit(fd, mainvar, ma->toObject); } + else if(act->type==ACT_PARENT) { + bParentActuator *pa= act->data; + expand_doit(fd, mainvar, pa->ob); + } + else if(act->type==ACT_ARMATURE) { + bArmatureActuator *arma= act->data; + expand_doit(fd, mainvar, arma->target); + } act= act->next; } diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 37d0ee58bb2..fda35d28d0e 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -712,6 +712,9 @@ static void write_sensors(WriteData *wd, ListBase *lb) case SENS_PROPERTY: writestruct(wd, DATA, "bPropertySensor", 1, sens->data); break; + case SENS_ARMATURE: + writestruct(wd, DATA, "bArmatureSensor", 1, sens->data); + break; case SENS_ACTUATOR: writestruct(wd, DATA, "bActuatorSensor", 1, sens->data); break; @@ -830,6 +833,9 @@ static void write_actuators(WriteData *wd, ListBase *lb) case ACT_STATE: writestruct(wd, DATA, "bStateActuator", 1, act->data); break; + case ACT_ARMATURE: + writestruct(wd, DATA, "bArmatureActuator", 1, act->data); + break; default: ; /* error: don't know how to write this file */ } @@ -1093,8 +1099,16 @@ static void write_pose(WriteData *wd, bPose *pose) for (grp=pose->agroups.first; grp; grp=grp->next) writestruct(wd, DATA, "bActionGroup", 1, grp); + /* write IK param */ + if (pose->ikparam) { + const char *structname = get_ikparam_name(pose); + if (structname) + writestruct(wd, DATA, structname, 1, pose->ikparam); + } + /* Write this pose */ writestruct(wd, DATA, "bPose", 1, pose); + } static void write_defgroups(WriteData *wd, ListBase *defbase) diff --git a/source/blender/editors/CMakeLists.txt b/source/blender/editors/CMakeLists.txt index 066d42e723e..d13d7ce2ff2 100644 --- a/source/blender/editors/CMakeLists.txt +++ b/source/blender/editors/CMakeLists.txt @@ -40,6 +40,7 @@ SET(INC ../windowmanager ../nodes ../gpu ../blenfont + ../ikplugin ) IF(WITH_GAMEENGINE) diff --git a/source/blender/editors/armature/editarmature.c b/source/blender/editors/armature/editarmature.c index 80f8c2b07aa..bc210fbcb54 100644 --- a/source/blender/editors/armature/editarmature.c +++ b/source/blender/editors/armature/editarmature.c @@ -2539,6 +2539,8 @@ EditBone *duplicateEditBoneObjects(EditBone *curBone, char *name, ListBase *edit VECCOPY(channew->limitmax, chanold->limitmax); VECCOPY(channew->stiffness, chanold->stiffness); channew->ikstretch= chanold->ikstretch; + channew->ikrotweight= chanold->ikrotweight; + channew->iklinweight= chanold->iklinweight; /* constraints */ listnew = &channew->constraints; @@ -2642,6 +2644,8 @@ static int armature_duplicate_selected_exec(bContext *C, wmOperator *op) VECCOPY(channew->limitmax, chanold->limitmax); VECCOPY(channew->stiffness, chanold->stiffness); channew->ikstretch= chanold->ikstretch; + channew->ikrotweight= chanold->ikrotweight; + channew->iklinweight= chanold->iklinweight; /* constraints */ listnew = &channew->constraints; diff --git a/source/blender/editors/armature/poseobject.c b/source/blender/editors/armature/poseobject.c index 305e153f805..f40476f6f59 100644 --- a/source/blender/editors/armature/poseobject.c +++ b/source/blender/editors/armature/poseobject.c @@ -798,6 +798,8 @@ void pose_copy_menu(Scene *scene) VECCOPY(pchan->limitmax, pchanact->limitmax); VECCOPY(pchan->stiffness, pchanact->stiffness); pchan->ikstretch= pchanact->ikstretch; + pchan->ikrotweight= pchanact->ikrotweight; + pchan->iklinweight= pchanact->iklinweight; } break; case 8: /* Custom Bone Shape */ diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index 67dc6dada5f..363795afeab 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -88,6 +88,8 @@ void object_test_constraints(struct Object *ob); void ED_object_constraint_rename(struct Object *ob, struct bConstraint *con, char *oldname); void ED_object_constraint_set_active(struct Object *ob, struct bConstraint *con); +void ED_object_constraint_update(struct Object *ob); +void ED_object_constraint_dependency_update(struct Scene *scene, struct Object *ob); /* object_lattice.c */ void mouse_lattice(struct bContext *C, short mval[2], int extend); diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 51678327cdd..31f371c5553 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -676,6 +676,8 @@ void do_constraint_panels(bContext *C, void *arg, int event) if(ob->type==OB_ARMATURE) DAG_id_flush_update(&ob->id, OB_RECALC_DATA|OB_RECALC_OB); else DAG_id_flush_update(&ob->id, OB_RECALC_OB); + + WM_event_add_notifier(C, NC_OBJECT|ND_CONSTRAINT, ob); // XXX allqueue(REDRAWVIEW3D, 0); // XXX allqueue(REDRAWBUTSOBJECT, 0); @@ -687,19 +689,15 @@ static void constraint_active_func(bContext *C, void *ob_v, void *con_v) ED_object_constraint_set_active(ob_v, con_v); } -static void verify_constraint_name_func (bContext *C, void *con_v, void *name_v) +static void verify_constraint_name_func (bContext *C, void *con_v, void *dummy) { Object *ob= CTX_data_active_object(C); bConstraint *con= con_v; - char oldname[32]; if (!con) return; - /* put on the stack */ - BLI_strncpy(oldname, (char *)name_v, 32); - - ED_object_constraint_rename(ob, con, oldname); + ED_object_constraint_rename(ob, con, NULL); ED_object_constraint_set_active(ob, con); // XXX allqueue(REDRAWACTION, 0); } @@ -904,11 +902,13 @@ static uiLayout *draw_constraint(uiLayout *layout, Object *ob, bConstraint *con) uiDefIconButO(block, BUT, "CONSTRAINT_OT_move_down", WM_OP_INVOKE_DEFAULT, VICON_MOVE_DOWN, xco+width-50+18, yco, 16, 18, "Move constraint down in constraint stack"); uiBlockEndAlign(block); } - - + /* Close 'button' - emboss calls here disable drawing of 'button' behind X */ uiBlockSetEmboss(block, UI_EMBOSSN); + uiBlockBeginAlign(block); + uiDefIconButBitS(block, ICONTOGN, CONSTRAINT_OFF, B_CONSTRAINT_TEST, ICON_CHECKBOX_DEHLT, xco+243, yco, 19, 19, &con->flag, 0.0, 0.0, 0.0, 0.0, "enable/disable constraint"); uiDefIconButO(block, BUT, "CONSTRAINT_OT_delete", WM_OP_INVOKE_DEFAULT, ICON_X, xco+262, yco, 19, 19, "Delete constraint"); + uiBlockEndAlign(block); uiBlockSetEmboss(block, UI_EMBOSS); } diff --git a/source/blender/editors/object/Makefile b/source/blender/editors/object/Makefile index 70ada46c80f..fd2af305d87 100644 --- a/source/blender/editors/object/Makefile +++ b/source/blender/editors/object/Makefile @@ -47,6 +47,7 @@ CPPFLAGS += -I../../makesdna CPPFLAGS += -I../../makesrna CPPFLAGS += -I../../python CPPFLAGS += -I../../imbuf +CPPFLAGS += -I../../ikplugin # own include diff --git a/source/blender/editors/object/SConscript b/source/blender/editors/object/SConscript index 3371e172a82..6ecc80f2d81 100644 --- a/source/blender/editors/object/SConscript +++ b/source/blender/editors/object/SConscript @@ -6,7 +6,7 @@ sources = env.Glob('*.c') incs = '../include ../../blenlib ../../blenkernel ../../makesdna ../../imbuf' incs += ' ../../windowmanager #/intern/guardedalloc' incs += ' #/intern/guardedalloc' -incs += ' ../../makesrna ../../python' +incs += ' ../../makesrna ../../python ../../ikplugin' defs = [] diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c index efdef506331..8c0da354938 100644 --- a/source/blender/editors/object/object_constraint.c +++ b/source/blender/editors/object/object_constraint.c @@ -56,6 +56,7 @@ #include "BKE_object.h" #include "BKE_report.h" #include "BKE_utildefines.h" +#include "BIK_api.h" #ifndef DISABLE_PYTHON #include "BPY_extern.h" @@ -334,6 +335,7 @@ static void test_constraints (Object *owner, const char substring[]) * optional... otherwise poletarget must exist too or else * the constraint is deemed invalid */ + /* default IK check ... */ if (exist_object(data->tar) == 0) { data->tar = NULL; curcon->flag |= CONSTRAINT_DISABLE; @@ -355,7 +357,8 @@ static void test_constraints (Object *owner, const char substring[]) } } } - + /* ... can be overwritten here */ + BIK_test_constraint(owner, curcon); /* targets have already been checked for this */ continue; } @@ -702,6 +705,25 @@ void ED_object_constraint_set_active(Object *ob, bConstraint *con) } } +void ED_object_constraint_update(Object *ob) +{ + + if(ob->pose) update_pose_constraint_flags(ob->pose); + + object_test_constraints(ob); + + if(ob->type==OB_ARMATURE) DAG_id_flush_update(&ob->id, OB_RECALC_DATA|OB_RECALC_OB); + else DAG_id_flush_update(&ob->id, OB_RECALC_OB); +} + +void ED_object_constraint_dependency_update(Scene *scene, Object *ob) +{ + ED_object_constraint_update(ob); + + if(ob->pose) ob->pose->flag |= POSE_RECALC; // checks & sorts pose channels + DAG_scene_sort(scene); +} + static int constraint_poll(bContext *C) { PointerRNA ptr= CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint); @@ -717,6 +739,10 @@ static int constraint_delete_exec (bContext *C, wmOperator *op) /* remove constraint itself */ lb= get_active_constraints(ob); + if (BLI_findindex(lb, con) == -1) + /* abnormal situation which happens on bone constraint when the armature is not in pose mode */ + return OPERATOR_CANCELLED; + free_constraint_data(con); BLI_freelinkN(lb, con); diff --git a/source/blender/editors/space_logic/logic_window.c b/source/blender/editors/space_logic/logic_window.c index b99f7b94170..dc8b111821d 100644 --- a/source/blender/editors/space_logic/logic_window.c +++ b/source/blender/editors/space_logic/logic_window.c @@ -39,6 +39,9 @@ #include "DNA_screen_types.h" #include "DNA_sensor_types.h" #include "DNA_sound_types.h" +#include "DNA_armature_types.h" +#include "DNA_constraint_types.h" +#include "DNA_action_types.h" #include "DNA_windowmanager_types.h" #include "MEM_guardedalloc.h" @@ -608,6 +611,8 @@ static char *sensor_name(int type) return "Keyboard"; case SENS_PROPERTY: return "Property"; + case SENS_ARMATURE: + return "Armature"; case SENS_ACTUATOR: return "Actuator"; case SENS_DELAY: @@ -635,7 +640,7 @@ static char *sensor_pup(void) /* the number needs to match defines in game.h */ return "Sensors %t|Always %x0|Delay %x13|Keyboard %x3|Mouse %x5|" "Touch %x1|Collision %x6|Near %x2|Radar %x7|" - "Property %x4|Random %x8|Ray %x9|Message %x10|Joystick %x11|Actuator %x12"; + "Property %x4|Random %x8|Ray %x9|Message %x10|Joystick %x11|Actuator %x12|Armature %x14"; } static char *controller_name(int type) @@ -709,6 +714,8 @@ static char *actuator_name(int type) return "Parent"; case ACT_STATE: return "State"; + case ACT_ARMATURE: + return "Armature"; } return "unknown"; } @@ -721,7 +728,7 @@ static char *actuator_pup(Object *owner) switch (owner->type) { case OB_ARMATURE: - return "Actuators %t|Action %x15|Motion %x0|Constraint %x9|Ipo %x1" + return "Actuators %t|Action %x15|Armature %x23|Motion %x0|Constraint %x9|Ipo %x1" "|Camera %x3|Sound %x5|Property %x6|Edit Object %x10" "|Scene %x11|Random %x13|Message %x14|Game %x17" "|Visibility %x18|2D Filter %x19|Parent %x20|State %x22"; @@ -936,6 +943,7 @@ static int get_col_sensor(int type) case SENS_NEAR: return TH_PANEL; case SENS_KEYBOARD: return TH_PANEL; case SENS_PROPERTY: return TH_PANEL; + case SENS_ARMATURE: return TH_PANEL; case SENS_ACTUATOR: return TH_PANEL; case SENS_MOUSE: return TH_PANEL; case SENS_RADAR: return TH_PANEL; @@ -1129,12 +1137,51 @@ static void draw_default_sensor_header(bSensor *sens, "Invert the level (output) of this sensor"); } -static short draw_sensorbuttons(bSensor *sens, uiBlock *block, short xco, short yco, short width,char* objectname) +static void check_armature_bone_constraint(Object *ob, char *posechannel, char *constraint) +{ + /* check that bone exist in the active object */ + if (ob->type == OB_ARMATURE && ob->pose) { + bPoseChannel *pchan; + bPose *pose = ob->pose; + for (pchan=pose->chanbase.first; pchan; pchan=pchan->next) { + if (!strcmp(pchan->name, posechannel)) { + /* found it, now look for constraint channel */ + bConstraint *con; + for (con=pchan->constraints.first; con; con=con->next) { + if (!strcmp(con->name, constraint)) { + /* found it, all ok */ + return; + } + } + /* didn't find constraint, make empty */ + constraint[0] = 0; + return; + } + } + } + /* didn't find any */ + posechannel[0] = 0; + constraint[0] = 0; +} + +static void check_armature_sensor(bContext *C, void *arg1_but, void *arg2_sens) +{ + bArmatureSensor *sens = arg2_sens; + uiBut *but = arg1_but; + Object *ob= CTX_data_active_object(C); + + /* check that bone exist in the active object */ + but->retval = B_REDR; + check_armature_bone_constraint(ob, sens->posechannel, sens->constraint); +} + +static short draw_sensorbuttons(Object *ob, bSensor *sens, uiBlock *block, short xco, short yco, short width,char* objectname) { bNearSensor *ns = NULL; bTouchSensor *ts = NULL; bKeyboardSensor *ks = NULL; bPropertySensor *ps = NULL; + bArmatureSensor *arm = NULL; bMouseSensor *ms = NULL; bCollisionSensor *cs = NULL; bRadarSensor *rs = NULL; @@ -1360,6 +1407,45 @@ static short draw_sensorbuttons(bSensor *sens, uiBlock *block, short xco, short yco-= ysize; break; } + case SENS_ARMATURE: + { + ysize= 70; + + glRects(xco, yco-ysize, xco+width, yco); + uiEmboss((float)xco, (float)yco-ysize, + (float)xco+width, (float)yco, 1); + + draw_default_sensor_header(sens, block, xco, yco, width); + arm= sens->data; + + if (ob->type == OB_ARMATURE) { + uiBlockBeginAlign(block); + but = uiDefBut(block, TEX, 1, "Bone: ", + (xco+10), (yco-44), (width-20)/2, 19, + arm->posechannel, 0, 31, 0, 0, + "Bone on which you want to check a constraint"); + uiButSetFunc(but, check_armature_sensor, but, arm); + but = uiDefBut(block, TEX, 1, "Cons: ", + (xco+10)+(width-20)/2, (yco-44), (width-20)/2, 19, + arm->constraint, 0, 31, 0, 0, + "Name of the constraint you want to control"); + uiButSetFunc(but, check_armature_sensor, but, arm); + uiBlockEndAlign(block); + + str= "Type %t|State changed %x0|Lin error below %x1|Lin error above %x2|Rot error below %x3|Rot error above %x4"; + + uiDefButI(block, MENU, B_REDR, str, xco+10,yco-66,0.4*(width-20), 19, + &arm->type, 0, 31, 0, 0, "Type"); + + if (arm->type != SENS_ARM_STATE_CHANGED) + { + uiDefButF(block, NUM, 1, "Value: ", xco+10+0.4*(width-20),yco-66,0.6*(width-20), 19, + &arm->value, -10000.0, 10000.0, 100, 0, "Test the error against this value"); + } + } + yco-= ysize; + break; + } case SENS_ACTUATOR: { ysize= 48; @@ -1694,6 +1780,7 @@ static int get_col_actuator(int type) case ACT_VISIBILITY: return TH_PANEL; case ACT_CONSTRAINT: return TH_PANEL; case ACT_STATE: return TH_PANEL; + case ACT_ARMATURE: return TH_PANEL; default: return TH_PANEL; } } @@ -1774,6 +1861,18 @@ static void check_state_mask(bContext *C, void *arg1_but, void *arg2_mask) but->retval = B_REDR; } +static void check_armature_actuator(bContext *C, void *arg1_but, void *arg2_act) +{ + bArmatureActuator *act = arg2_act; + uiBut *but = arg1_but; + Object *ob= CTX_data_active_object(C); + + /* check that bone exist in the active object */ + but->retval = B_REDR; + check_armature_bone_constraint(ob, act->posechannel, act->constraint); +} + + static short draw_actuatorbuttons(Object *ob, bActuator *act, uiBlock *block, short xco, short yco, short width) { bSoundActuator *sa = NULL; @@ -1793,6 +1892,7 @@ static short draw_actuatorbuttons(Object *ob, bActuator *act, uiBlock *block, sh bTwoDFilterActuator *tdfa = NULL; bParentActuator *parAct = NULL; bStateActuator *staAct = NULL; + bArmatureActuator *armAct = NULL; float *fp; short ysize = 0, wval; @@ -2820,6 +2920,48 @@ static short draw_actuatorbuttons(Object *ob, bActuator *act, uiBlock *block, sh yco-= ysize; break; + case ACT_ARMATURE: + armAct = act->data; + + if (ob->type == OB_ARMATURE) { + str= "Constraint %t|Run armature %x0|Enable %x1|Disable %x2|Set target %x3|Set weight %x4"; + uiDefButI(block, MENU, B_REDR, str, xco+5, yco-24, (width-10)*0.35, 19, &armAct->type, 0.0, 0.0, 0, 0, ""); + + switch (armAct->type) { + case ACT_ARM_RUN: + ysize = 28; + break; + default: + uiBlockBeginAlign(block); + but = uiDefBut(block, TEX, 1, "Bone: ", + (xco+5), (yco-44), (width-10)/2, 19, + armAct->posechannel, 0, 31, 0, 0, + "Bone on which the constraint is defined"); + uiButSetFunc(but, check_armature_actuator, but, armAct); + but = uiDefBut(block, TEX, 1, "Cons: ", + (xco+5)+(width-10)/2, (yco-44), (width-10)/2, 19, + armAct->constraint, 0, 31, 0, 0, + "Name of the constraint you want to controle"); + uiButSetFunc(but, check_armature_actuator, but, armAct); + uiBlockEndAlign(block); + ysize = 48; + switch (armAct->type) { + case ACT_ARM_SETTARGET: + uiDefIDPoinBut(block, test_obpoin_but, ID_OB, 1, "Target: ", xco+5, yco-64, (width-10), 19, &(armAct->target), "Set this object as the target of the constraint"); + uiDefIDPoinBut(block, test_obpoin_but, ID_OB, 1, "Secondary Target: ", xco+5, yco-84, (width-10), 19, &(armAct->subtarget), "Set this object as the secondary target of the constraint (only IK polar target at the moment)"); + ysize += 40; + break; + case ACT_ARM_SETWEIGHT: + uiDefButF(block, NUM, B_REDR, "Weight:", xco+5+(width-10)*0.35,yco-24,(width-10)*0.65,19,&armAct->weight,0.0,1.0,0.0,0.0,"Set weight of this constraint"); + break; + } + } + } + glRects(xco, yco-ysize, xco+width, yco); + uiEmboss((float)xco, (float)yco-ysize, (float)xco+width, (float)yco, 1); + yco-= ysize; + break; + default: ysize= 4; @@ -3334,7 +3476,7 @@ void logic_buttons(bContext *C, ARegion *ar) uiButSetFunc(but, make_unique_prop_names_cb, sens->name, (void*) 0); sens->otype= sens->type; - yco= draw_sensorbuttons(sens, block, xco, yco, width,ob->id.name); + yco= draw_sensorbuttons(ob, sens, block, xco, yco, width,ob->id.name); if(yco-6 < ycoo) ycoo= (yco+ycoo-20)/2; } else { diff --git a/source/blender/ikplugin/BIK_api.h b/source/blender/ikplugin/BIK_api.h new file mode 100644 index 00000000000..a259d0aa62a --- /dev/null +++ b/source/blender/ikplugin/BIK_api.h @@ -0,0 +1,93 @@ +/** + * $Id$ + * ***** BEGIN GPL 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. + * + * 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. + * + * Original author: Benoit Bolsee + * Contributor(s): + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef BIK_API_H +#define BIK_API_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct Object; +struct bPoseChannel; +struct bPose; +struct bArmature; +struct Scene; +struct bConstraint; + +enum BIK_ParamType { + BIK_PARAM_TYPE_FLOAT = 0, + BIK_PARAM_TYPE_INT, + BIK_PARAM_TYPE_STRING, +}; + +struct BIK_ParamValue { + short type; /* BIK_PARAM_TYPE_.. */ + short length; /* for string, does not include terminating 0 */ + union { + float f[8]; + int i[8]; + char s[32]; + } value; +}; +typedef struct BIK_ParamValue BIK_ParamValue; + +void BIK_initialize_tree(struct Scene *scene, struct Object *ob, float ctime); +void BIK_execute_tree(struct Scene *scene, struct Object *ob, struct bPoseChannel *pchan, float ctime); +void BIK_release_tree(struct Scene *scene, struct Object *ob, float ctime); +void BIK_clear_data(struct bPose *pose); +void BIK_clear_cache(struct bPose *pose); +void BIK_update_param(struct bPose *pose); +void BIK_test_constraint(struct Object *ob, struct bConstraint *cons); +// not yet implemented +int BIK_get_constraint_param(struct bPose *pose, struct bConstraint *cons, int id, BIK_ParamValue *value); +int BIK_get_channel_param(struct bPose *pose, struct bPoseChannel *pchan, int id, BIK_ParamValue *value); +int BIK_get_solver_param(struct bPose *pose, struct bPoseChannel *pchan, int id, BIK_ParamValue *value); + +// number of solver available +// 0 = iksolver +// 1 = iTaSC +#define BIK_SOLVER_COUNT 2 + +/* for use in BIK_get_constraint_param */ +#define BIK_PARAM_CONSTRAINT_ERROR 0 + +/* for use in BIK_get_channel_param */ +#define BIK_PARAM_CHANNEL_JOINT 0 + +/* for use in BIK_get_solver_param */ +#define BIK_PARAM_SOLVER_RANK 0 +#define BIK_PARAM_SOLVER_ITERATION 1 + +#ifdef __cplusplus +} +#endif + +#endif // BIK_API_H + diff --git a/source/blender/ikplugin/CMakeLists.txt b/source/blender/ikplugin/CMakeLists.txt new file mode 100644 index 00000000000..5790d4ef12f --- /dev/null +++ b/source/blender/ikplugin/CMakeLists.txt @@ -0,0 +1,35 @@ +# $Id: CMakeLists.txt 20156 2009-05-11 16:31:30Z ben2610 $ +# ***** BEGIN GPL 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. +# +# 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) 2006, Blender Foundation +# All rights reserved. +# +# The Original Code is: all of this file. +# +# Contributor(s): Jacques Beaurain. +# +# ***** END GPL LICENSE BLOCK ***** + +FILE(GLOB SRC intern/*.c intern/*.cpp) + +SET(INC + ../../../intern/guardedalloc ../../../intern/iksolver/extern + ../../../intern/itasc ../../../extern/Eigen2 + ../blenlib ../makesdna ../blenkernel ../include ../ikplugin +) + +BLENDERLIB(bf_ikplugin "${SRC}" "${INC}") diff --git a/source/blender/ikplugin/Makefile b/source/blender/ikplugin/Makefile new file mode 100644 index 00000000000..370ed418464 --- /dev/null +++ b/source/blender/ikplugin/Makefile @@ -0,0 +1,31 @@ +# ***** BEGIN GPL 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. +# +# 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 LICENSE BLOCK ***** +# +# Bounces make to subdirectories. + +SOURCEDIR = source/blender/ikplugin +DIRS = intern + +include nan_subdirs.mk diff --git a/source/blender/ikplugin/SConscript b/source/blender/ikplugin/SConscript new file mode 100644 index 00000000000..a745a93077a --- /dev/null +++ b/source/blender/ikplugin/SConscript @@ -0,0 +1,9 @@ +#!/usr/bin/python +Import ('env') + +sources = env.Glob('intern/*.c') + env.Glob('intern/*.cpp') + +incs = '#/intern/guardedalloc #/intern/iksolver/extern ../makesdna ../blenlib' +incs += ' ../blenkernel ../include ../ikplugin #/intern/itasc #/extern/Eigen2' + +env.BlenderLib ( 'bf_ikplugin', sources, Split(incs), [], libtype=['core','player'], priority=[180, 190] ) diff --git a/source/blender/ikplugin/intern/Makefile b/source/blender/ikplugin/intern/Makefile new file mode 100644 index 00000000000..9254b65b7b7 --- /dev/null +++ b/source/blender/ikplugin/intern/Makefile @@ -0,0 +1,49 @@ +# ***** BEGIN GPL 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. +# +# 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 LICENSE BLOCK ***** +# +# + +LIBNAME = ikplugin +DIR = $(OCGDIR)/blender/ikplugin + +include nan_compile.mk + +CFLAGS += $(LEVEL_1_C_WARNINGS) +CFLAGS += -I$(NAN_GUARDEDALLOC)/include +CFLAGS += -I../../makesdna +CFLAGS += -I../../blenkernel +CFLAGS += -I../../blenlib +CFLAGS += -I../../include +CFLAGS += -I../../../intern/itasc +CFLAGS += -I../../../extern/Eigen2 +CFLAGS += -I.. + +CPPFLAGS += -I$(NAN_GUARDEDALLOC)/include +CPPFLAGS += -I$(NAN_IKSOLVER)/include +CPPFLAGS += -I../../makesdna +CPPFLAGS += -I../../blenkernel +CPPFLAGS += -I../../blenlib +CPPFLAGS += -I../../include +CPPFLAGS += -I.. diff --git a/source/blender/ikplugin/intern/ikplugin_api.c b/source/blender/ikplugin/intern/ikplugin_api.c new file mode 100644 index 00000000000..714843fc5e5 --- /dev/null +++ b/source/blender/ikplugin/intern/ikplugin_api.c @@ -0,0 +1,140 @@ +/** + * $Id$ + * ***** BEGIN GPL 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. + * + * 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. + * + * Original author: Benoit Bolsee + * Contributor(s): + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" + +#include "BIK_api.h" +#include "BLI_blenlib.h" +#include "BLI_arithb.h" + +#include "BKE_armature.h" +#include "BKE_utildefines.h" +#include "DNA_object_types.h" +#include "DNA_action_types.h" +#include "DNA_scene_types.h" +#include "DNA_constraint_types.h" +#include "DNA_armature_types.h" + +#include "ikplugin_api.h" +#include "iksolver_plugin.h" +#include "itasc_plugin.h" + + +static IKPlugin ikplugin_tab[BIK_SOLVER_COUNT] = { + /* Legacy IK solver */ + { + iksolver_initialize_tree, + iksolver_execute_tree, + NULL, + NULL, + NULL, + NULL, + NULL, + }, + /* iTaSC IK solver */ + { + itasc_initialize_tree, + itasc_execute_tree, + itasc_release_tree, + itasc_clear_data, + itasc_clear_cache, + itasc_update_param, + itasc_test_constraint, + } +}; + + +static IKPlugin *get_plugin(bPose *pose) +{ + IKPlugin *plugin; + + if (!pose || pose->iksolver < 0 || pose->iksolver >= BIK_SOLVER_COUNT) + return NULL; + + return &ikplugin_tab[pose->iksolver]; +} + +/*----------------------------------------*/ +/* Plugin API */ + +void BIK_initialize_tree(Scene *scene, Object *ob, float ctime) +{ + IKPlugin *plugin = get_plugin(ob->pose); + + if (plugin && plugin->initialize_tree_func) + plugin->initialize_tree_func(scene, ob, ctime); +} + +void BIK_execute_tree(struct Scene *scene, Object *ob, bPoseChannel *pchan, float ctime) +{ + IKPlugin *plugin = get_plugin(ob->pose); + + if (plugin && plugin->execute_tree_func) + plugin->execute_tree_func(scene, ob, pchan, ctime); +} + +void BIK_release_tree(struct Scene *scene, Object *ob, float ctime) +{ + IKPlugin *plugin = get_plugin(ob->pose); + + if (plugin && plugin->release_tree_func) + plugin->release_tree_func(scene, ob, ctime); +} + +void BIK_clear_data(struct bPose *pose) +{ + IKPlugin *plugin = get_plugin(pose); + + if (plugin && plugin->remove_armature_func) + plugin->remove_armature_func(pose); +} + +void BIK_clear_cache(struct bPose *pose) +{ + IKPlugin *plugin = get_plugin(pose); + + if (plugin && plugin->clear_cache) + plugin->clear_cache(pose); +} + +void BIK_update_param(struct bPose *pose) +{ + IKPlugin *plugin = get_plugin(pose); + + if (plugin && plugin->update_param) + plugin->update_param(pose); +} + +void BIK_test_constraint(struct Object *ob, struct bConstraint *cons) +{ + IKPlugin *plugin = get_plugin(ob->pose); + + if (plugin && plugin->test_constraint) + plugin->test_constraint(ob, cons); +} diff --git a/source/blender/ikplugin/intern/ikplugin_api.h b/source/blender/ikplugin/intern/ikplugin_api.h new file mode 100644 index 00000000000..cc4dff4ec75 --- /dev/null +++ b/source/blender/ikplugin/intern/ikplugin_api.h @@ -0,0 +1,60 @@ +/** + * $Id$ + * ***** BEGIN GPL 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. + * + * 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. + * + * Original author: Benoit Bolsee + * Contributor(s): + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef IKPLUGIN_API_H +#define IKPLUGIN_API_H + +#ifdef __cplusplus +extern "C" { +#endif + +struct Object; +struct bPoseChannel; +struct bArmature; +struct Scene; + + +struct IKPlugin { + void (*initialize_tree_func)(struct Scene *scene, struct Object *ob, float ctime); + void (*execute_tree_func)(struct Scene *scene, struct Object *ob, struct bPoseChannel *pchan, float ctime); + void (*release_tree_func)(struct Scene *scene, struct Object *ob, float ctime); + void (*remove_armature_func)(struct bPose *pose); + void (*clear_cache)(struct bPose *pose); + void (*update_param)(struct bPose *pose); + void (*test_constraint)(struct Object *ob, struct bConstraint *cons); +}; + +typedef struct IKPlugin IKPlugin; + +#ifdef __cplusplus +} +#endif + +#endif // IKPLUGIN_API_H + diff --git a/source/blender/ikplugin/intern/iksolver_plugin.c b/source/blender/ikplugin/intern/iksolver_plugin.c new file mode 100644 index 00000000000..262185fef1b --- /dev/null +++ b/source/blender/ikplugin/intern/iksolver_plugin.c @@ -0,0 +1,527 @@ +/** + * $Id$ + * ***** BEGIN GPL 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. + * + * 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. + * + * Original author: Benoit Bolsee + * Contributor(s): + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "MEM_guardedalloc.h" + +#include "BIK_api.h" +#include "BLI_blenlib.h" +#include "BLI_arithb.h" + +#include "BKE_armature.h" +#include "BKE_utildefines.h" +#include "DNA_object_types.h" +#include "DNA_action_types.h" +#include "DNA_constraint_types.h" +#include "DNA_armature_types.h" + +#include "IK_solver.h" +#include "iksolver_plugin.h" + +/* ********************** THE IK SOLVER ******************* */ + +/* allocates PoseTree, and links that to root bone/channel */ +/* 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; + PoseTree *tree; + PoseTarget *target; + bConstraint *con; + bKinematicConstraint *data; + int a, segcount= 0, size, newsize, *oldparent, parent; + + /* find IK constraint, and validate it */ + for(con= pchan_tip->constraints.first; con; con= con->next) { + if(con->type==CONSTRAINT_TYPE_KINEMATIC) { + data=(bKinematicConstraint*)con->data; + if (data->flag & CONSTRAINT_IK_AUTO) break; + if (data->tar==NULL) continue; + if (data->tar->type==OB_ARMATURE && data->subtarget[0]==0) continue; + if ((con->flag & (CONSTRAINT_DISABLE|CONSTRAINT_OFF))==0 && (con->enforce!=0.0)) break; + } + } + if(con==NULL) return; + + /* exclude tip from chain? */ + if(!(data->flag & CONSTRAINT_IK_TIP)) + pchan_tip= pchan_tip->parent; + + /* Find the chain's root & count the segments needed */ + for (curchan = pchan_tip; curchan; curchan=curchan->parent){ + pchan_root = curchan; + + curchan->flag |= POSE_CHAIN; // don't forget to clear this + chanlist[segcount]=curchan; + segcount++; + + if(segcount==data->rootbone || segcount>255) break; // 255 is weak + } + if (!segcount) return; + + /* setup the chain data */ + + /* we make tree-IK, unless all existing targets are in this chain */ + for(tree= pchan_root->iktree.first; tree; tree= tree->next) { + for(target= tree->targets.first; target; target= target->next) { + curchan= tree->pchan[target->tip]; + if(curchan->flag & POSE_CHAIN) + curchan->flag &= ~POSE_CHAIN; + else + break; + } + if(target) break; + } + + /* create a target */ + target= MEM_callocN(sizeof(PoseTarget), "posetarget"); + target->con= con; + pchan_tip->flag &= ~POSE_CHAIN; + + if(tree==NULL) { + /* make new tree */ + tree= MEM_callocN(sizeof(PoseTree), "posetree"); + + tree->iterations= data->iterations; + tree->totchannel= segcount; + tree->stretch = (data->flag & CONSTRAINT_IK_STRETCH); + + tree->pchan= MEM_callocN(segcount*sizeof(void*), "ik tree pchan"); + tree->parent= MEM_callocN(segcount*sizeof(int), "ik tree parent"); + for(a=0; a<segcount; a++) { + tree->pchan[a]= chanlist[segcount-a-1]; + tree->parent[a]= a-1; + } + target->tip= segcount-1; + + /* AND! link the tree to the root */ + BLI_addtail(&pchan_root->iktree, tree); + } + else { + tree->iterations= MAX2(data->iterations, tree->iterations); + tree->stretch= tree->stretch && !(data->flag & CONSTRAINT_IK_STRETCH); + + /* skip common pose channels and add remaining*/ + size= MIN2(segcount, tree->totchannel); + for(a=0; a<size && tree->pchan[a]==chanlist[segcount-a-1]; a++); + parent= a-1; + + segcount= segcount-a; + target->tip= tree->totchannel + segcount - 1; + + if (segcount > 0) { + /* resize array */ + newsize= tree->totchannel + segcount; + oldchan= tree->pchan; + oldparent= tree->parent; + + tree->pchan= MEM_callocN(newsize*sizeof(void*), "ik tree pchan"); + tree->parent= MEM_callocN(newsize*sizeof(int), "ik tree parent"); + memcpy(tree->pchan, oldchan, sizeof(void*)*tree->totchannel); + memcpy(tree->parent, oldparent, sizeof(int)*tree->totchannel); + MEM_freeN(oldchan); + MEM_freeN(oldparent); + + /* add new pose channels at the end, in reverse order */ + for(a=0; a<segcount; a++) { + tree->pchan[tree->totchannel+a]= chanlist[segcount-a-1]; + tree->parent[tree->totchannel+a]= tree->totchannel+a-1; + } + tree->parent[tree->totchannel]= parent; + + tree->totchannel= newsize; + } + + /* move tree to end of list, for correct evaluation order */ + BLI_remlink(&pchan_root->iktree, tree); + BLI_addtail(&pchan_root->iktree, tree); + } + + /* add target to the tree */ + BLI_addtail(&tree->targets, target); + /* mark root channel having an IK tree */ + pchan_root->flag |= POSE_IKTREE; +} + + +/* transform from bone(b) to bone(b+1), store in chan_mat */ +static void make_dmats(bPoseChannel *pchan) +{ + if (pchan->parent) { + float iR_parmat[4][4]; + Mat4Invert(iR_parmat, pchan->parent->pose_mat); + Mat4MulMat4(pchan->chan_mat, pchan->pose_mat, iR_parmat); // delta mat + } + else Mat4CpyMat4(pchan->chan_mat, pchan->pose_mat); +} + +/* applies IK matrix to pchan, IK is done separated */ +/* formula: pose_mat(b) = pose_mat(b-1) * diffmat(b-1, b) * ik_mat(b) */ +/* to make this work, the diffmats have to be precalculated! Stored in chan_mat */ +static void where_is_ik_bone(bPoseChannel *pchan, float ik_mat[][3]) // nr = to detect if this is first bone +{ + float vec[3], ikmat[4][4]; + + Mat4CpyMat3(ikmat, ik_mat); + + if (pchan->parent) + Mat4MulSerie(pchan->pose_mat, pchan->parent->pose_mat, pchan->chan_mat, ikmat, NULL, NULL, NULL, NULL, NULL); + else + Mat4MulMat4(pchan->pose_mat, ikmat, pchan->chan_mat); + + /* calculate head */ + VECCOPY(pchan->pose_head, pchan->pose_mat[3]); + /* calculate tail */ + VECCOPY(vec, pchan->pose_mat[1]); + VecMulf(vec, pchan->bone->length); + VecAddf(pchan->pose_tail, pchan->pose_head, vec); + + pchan->flag |= POSE_DONE; +} + + +/* 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_posetree(Object *ob, PoseTree *tree) +{ + float R_parmat[3][3], identity[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 goal[4][4], goalinv[4][4]; + float irest_basis[3][3], full_basis[3][3]; + float end_pose[4][4], world_pose[4][4]; + float length, basis[3][3], rest_basis[3][3], start[3], *ikstretch=NULL; + float resultinf=0.0f; + int a, flag, hasstretch=0, resultblend=0; + bPoseChannel *pchan; + IK_Segment *seg, *parent, **iktree, *iktarget; + IK_Solver *solver; + PoseTarget *target; + bKinematicConstraint *data, *poleangledata=NULL; + Bone *bone; + + if (tree->totchannel == 0) + return; + + iktree= MEM_mallocN(sizeof(void*)*tree->totchannel, "ik tree"); + + for(a=0; a<tree->totchannel; a++) { + pchan= tree->pchan[a]; + bone= pchan->bone; + + /* set DoF flag */ + flag= 0; + if(!(pchan->ikflag & BONE_IK_NO_XDOF) && !(pchan->ikflag & BONE_IK_NO_XDOF_TEMP)) + flag |= IK_XDOF; + if(!(pchan->ikflag & BONE_IK_NO_YDOF) && !(pchan->ikflag & BONE_IK_NO_YDOF_TEMP)) + flag |= IK_YDOF; + if(!(pchan->ikflag & BONE_IK_NO_ZDOF) && !(pchan->ikflag & BONE_IK_NO_ZDOF_TEMP)) + flag |= IK_ZDOF; + + if(tree->stretch && (pchan->ikstretch > 0.0)) { + flag |= IK_TRANS_YDOF; + hasstretch = 1; + } + + seg= iktree[a]= IK_CreateSegment(flag); + + /* find parent */ + if(a == 0) + parent= NULL; + else + parent= iktree[tree->parent[a]]; + + IK_SetParent(seg, parent); + + /* get the matrix that transforms from prevbone into this bone */ + Mat3CpyMat4(R_bonemat, pchan->pose_mat); + + /* gather transformations for this IK segment */ + + if (pchan->parent) + Mat3CpyMat4(R_parmat, pchan->parent->pose_mat); + else + Mat3One(R_parmat); + + /* bone offset */ + if (pchan->parent && (a > 0)) + VecSubf(start, pchan->pose_head, pchan->parent->pose_tail); + else + /* only root bone (a = 0) has no parent */ + start[0]= start[1]= start[2]= 0.0f; + + /* change length based on bone size */ + length= bone->length*VecLength(R_bonemat[1]); + + /* compute rest basis and its inverse */ + Mat3CpyMat3(rest_basis, bone->bone_mat); + Mat3CpyMat3(irest_basis, bone->bone_mat); + Mat3Transp(irest_basis); + + /* compute basis with rest_basis removed */ + Mat3Inv(iR_parmat, R_parmat); + Mat3MulMat3(full_basis, iR_parmat, R_bonemat); + Mat3MulMat3(basis, irest_basis, full_basis); + + /* basis must be pure rotation */ + Mat3Ortho(basis); + + /* transform offset into local bone space */ + Mat3Ortho(iR_parmat); + Mat3MulVecfl(iR_parmat, start); + + 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]); + + if(tree->stretch && (pchan->ikstretch > 0.0)) { + float ikstretch = pchan->ikstretch*pchan->ikstretch; + IK_SetStiffness(seg, IK_TRANS_Y, MIN2(1.0-ikstretch, 0.99)); + IK_SetLimit(seg, IK_TRANS_Y, 0.001, 1e10); + } + } + + solver= IK_CreateSolver(iktree[0]); + + /* set solver goals */ + + /* first set the goal inverse transform, assuming the root of tree was done ok! */ + pchan= tree->pchan[0]; + if (pchan->parent) + /* transform goal by parent mat, so this rotation is not part of the + segment's basis. otherwise rotation limits do not work on the + local transform of the segment itself. */ + Mat4CpyMat4(rootmat, pchan->parent->pose_mat); + else + Mat4One(rootmat); + VECCOPY(rootmat[3], pchan->pose_head); + + Mat4MulMat4 (imat, rootmat, ob->obmat); + Mat4Invert (goalinv, imat); + + for (target=tree->targets.first; target; target=target->next) { + float polepos[3]; + int poleconstrain= 0; + + data= (bKinematicConstraint*)target->con->data; + + /* 1.0=ctime, we pass on object for auto-ik (owner-type here is object, even though + * strictly speaking, it is a posechannel) + */ + get_constraint_target_matrix(target->con, 0, CONSTRAINT_OBTYPE_OBJECT, ob, rootmat, 1.0); + + /* and set and transform goal */ + Mat4MulMat4(goal, rootmat, goalinv); + + VECCOPY(goalpos, goal[3]); + Mat3CpyMat4(goalrot, goal); + + /* same for pole vector target */ + if(data->poletar) { + get_constraint_target_matrix(target->con, 1, CONSTRAINT_OBTYPE_OBJECT, ob, rootmat, 1.0); + + if(data->flag & CONSTRAINT_IK_SETANGLE) { + /* don't solve IK when we are setting the pole angle */ + break; + } + else { + Mat4MulMat4(goal, rootmat, goalinv); + VECCOPY(polepos, goal[3]); + poleconstrain= 1; + + /* for pole targets, we blend the result of the ik solver + * instead of the target position, otherwise we can't get + * a smooth transition */ + resultblend= 1; + resultinf= target->con->enforce; + + if(data->flag & CONSTRAINT_IK_GETANGLE) { + poleangledata= data; + data->flag &= ~CONSTRAINT_IK_GETANGLE; + } + } + } + + /* do we need blending? */ + if (!resultblend && target->con->enforce!=1.0) { + float q1[4], q2[4], q[4]; + float fac= target->con->enforce; + float mfac= 1.0-fac; + + pchan= tree->pchan[target->tip]; + + /* end effector in world space */ + Mat4CpyMat4(end_pose, pchan->pose_mat); + VECCOPY(end_pose[3], pchan->pose_tail); + Mat4MulSerie(world_pose, goalinv, ob->obmat, end_pose, 0, 0, 0, 0, 0); + + /* blend position */ + goalpos[0]= fac*goalpos[0] + mfac*world_pose[3][0]; + goalpos[1]= fac*goalpos[1] + mfac*world_pose[3][1]; + goalpos[2]= fac*goalpos[2] + mfac*world_pose[3][2]; + + /* blend rotation */ + Mat3ToQuat(goalrot, q1); + Mat4ToQuat(world_pose, q2); + QuatInterpol(q, q1, q2, mfac); + QuatToMat3(q, goalrot); + } + + iktarget= iktree[target->tip]; + + if(data->weight != 0.0) { + if(poleconstrain) + IK_SolverSetPoleVectorConstraint(solver, iktarget, goalpos, + polepos, data->poleangle*M_PI/180, (poleangledata == data)); + IK_SolverAddGoal(solver, iktarget, goalpos, data->weight); + } + if((data->flag & CONSTRAINT_IK_ROT) && (data->orientweight != 0.0)) + if((data->flag & CONSTRAINT_IK_AUTO)==0) + IK_SolverAddGoalOrientation(solver, iktarget, goalrot, + data->orientweight); + } + + /* solve */ + IK_Solve(solver, 0.0f, tree->iterations); + + if(poleangledata) + poleangledata->poleangle= IK_SolverGetPoleAngle(solver)*180/M_PI; + + IK_FreeSolver(solver); + + /* gather basis changes */ + tree->basis_change= MEM_mallocN(sizeof(float[3][3])*tree->totchannel, "ik basis change"); + if(hasstretch) + ikstretch= MEM_mallocN(sizeof(float)*tree->totchannel, "ik stretch"); + + for(a=0; a<tree->totchannel; a++) { + IK_GetBasisChange(iktree[a], tree->basis_change[a]); + + if(hasstretch) { + /* have to compensate for scaling received from parent */ + float parentstretch, stretch; + + pchan= tree->pchan[a]; + parentstretch= (tree->parent[a] >= 0)? ikstretch[tree->parent[a]]: 1.0; + + if(tree->stretch && (pchan->ikstretch > 0.0)) { + float trans[3], length; + + IK_GetTranslationChange(iktree[a], trans); + length= pchan->bone->length*VecLength(pchan->pose_mat[1]); + + ikstretch[a]= (length == 0.0)? 1.0: (trans[1]+length)/length; + } + else + ikstretch[a] = 1.0; + + stretch= (parentstretch == 0.0)? 1.0: ikstretch[a]/parentstretch; + + VecMulf(tree->basis_change[a][0], stretch); + VecMulf(tree->basis_change[a][1], stretch); + VecMulf(tree->basis_change[a][2], stretch); + } + + if(resultblend && resultinf!=1.0f) { + Mat3One(identity); + Mat3BlendMat3(tree->basis_change[a], identity, + tree->basis_change[a], resultinf); + } + + IK_FreeSegment(iktree[a]); + } + + MEM_freeN(iktree); + if(ikstretch) MEM_freeN(ikstretch); +} + +static void free_posetree(PoseTree *tree) +{ + BLI_freelistN(&tree->targets); + if(tree->pchan) MEM_freeN(tree->pchan); + if(tree->parent) MEM_freeN(tree->parent); + if(tree->basis_change) MEM_freeN(tree->basis_change); + MEM_freeN(tree); +} + +///---------------------------------------- +/// Plugin API for legacy iksolver + +void iksolver_initialize_tree(struct Scene *scene, struct Object *ob, float ctime) +{ + bPoseChannel *pchan; + + for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) { + if(pchan->constflag & PCHAN_HAS_IK) // flag is set on editing constraints + initialize_posetree(ob, pchan); // will attach it to root! + } + ob->pose->flag &= ~POSE_WAS_REBUILT; +} + +void iksolver_execute_tree(struct Scene *scene, struct Object *ob, struct bPoseChannel *pchan, float ctime) +{ + while(pchan->iktree.first) { + PoseTree *tree= pchan->iktree.first; + int a; + + /* 4. walk over the tree for regular solving */ + for(a=0; a<tree->totchannel; a++) { + if(!(tree->pchan[a]->flag & POSE_DONE)) // successive trees can set the flag + where_is_pose_bone(scene, ob, tree->pchan[a], ctime); + // tell blender that this channel was controlled by IK, it's cleared on each where_is_pose() + tree->pchan[a]->flag |= POSE_CHAIN; + } + /* 5. execute the IK solver */ + execute_posetree(ob, tree); + + /* 6. apply the differences to the channels, + we need to calculate the original differences first */ + for(a=0; a<tree->totchannel; a++) + make_dmats(tree->pchan[a]); + + for(a=0; a<tree->totchannel; a++) + /* sets POSE_DONE */ + where_is_ik_bone(tree->pchan[a], tree->basis_change[a]); + + /* 7. and free */ + BLI_remlink(&pchan->iktree, tree); + free_posetree(tree); + } +} + diff --git a/source/blender/ikplugin/intern/iksolver_plugin.h b/source/blender/ikplugin/intern/iksolver_plugin.h new file mode 100644 index 00000000000..d5d1d9a77da --- /dev/null +++ b/source/blender/ikplugin/intern/iksolver_plugin.h @@ -0,0 +1,47 @@ +/** + * $Id$ + * ***** BEGIN GPL 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. + * + * 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. + * + * Original author: Benoit Bolsee + * Contributor(s): + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef IKSOLVER_PLUGIN_H +#define IKSOLVER_PLUGIN_H + +#include "ikplugin_api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void iksolver_initialize_tree(struct Scene *scene, struct Object *ob, float ctime); +void iksolver_execute_tree(struct Scene *scene, struct Object *ob, struct bPoseChannel *pchan, float ctime); + +#ifdef __cplusplus +} +#endif + +#endif // IKSOLVER_PLUGIN_H + diff --git a/source/blender/ikplugin/intern/itasc_plugin.cpp b/source/blender/ikplugin/intern/itasc_plugin.cpp new file mode 100644 index 00000000000..b6278e40ea5 --- /dev/null +++ b/source/blender/ikplugin/intern/itasc_plugin.cpp @@ -0,0 +1,1786 @@ +/** + * $Id$ + * ***** BEGIN GPL 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. + * + * 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. + * + * Original author: Benoit Bolsee + * Contributor(s): + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <stdlib.h> +#include <string.h> +#include <vector> + +// iTaSC headers +#include "Armature.hpp" +#include "MovingFrame.hpp" +#include "CopyPose.hpp" +#include "WSDLSSolver.hpp" +#include "WDLSSolver.hpp" +#include "Scene.hpp" +#include "Cache.hpp" +#include "Distance.hpp" + +#include "MEM_guardedalloc.h" + +extern "C" { +#include "BIK_api.h" +#include "BLI_blenlib.h" +#include "BLI_arithb.h" + +#include "BKE_global.h" +#include "BKE_armature.h" +#include "BKE_action.h" +#include "BKE_utildefines.h" +#include "BKE_constraint.h" +#include "DNA_object_types.h" +#include "DNA_action_types.h" +#include "DNA_constraint_types.h" +#include "DNA_armature_types.h" +#include "DNA_scene_types.h" +}; + +#include "itasc_plugin.h" + +// default parameters +bItasc DefIKParam; + +// in case of animation mode, feedback and timestep is fixed +#define ANIM_TIMESTEP 1.0 +#define ANIM_FEEDBACK 0.8 +#define ANIM_QMAX 0.52 + + +// Structure pointed by bPose.ikdata +// It contains everything needed to simulate the armatures +// There can be several simulation islands independent to each other +struct IK_Data +{ + struct IK_Scene* first; +}; + +typedef float Vector3[3]; +typedef float Vector4[4]; +struct IK_Target; +typedef void (*ErrorCallback)(const iTaSC::ConstraintValues* values, unsigned int nvalues, IK_Target* iktarget); +// For some reason, gcc doesn't find the declaration of this function in linux +void KDL::SetToZero(JntArray& array); + +// one structure for each target in the scene +struct IK_Target +{ + iTaSC::MovingFrame* target; + iTaSC::ConstraintSet* constraint; + struct bConstraint* blenderConstraint; + struct bPoseChannel* rootChannel; + Object* owner; //for auto IK + ErrorCallback errorCallback; + std::string targetName; + std::string constraintName; + unsigned short controlType; + short channel; //index in IK channel array of channel on which this target is defined + short ee; //end effector number + bool simulation; //true when simulation mode is used (update feedback) + bool eeBlend; //end effector affected by enforce blending + float eeRest[4][4]; //end effector initial pose relative to armature + + IK_Target() { + target = NULL; + constraint = NULL; + blenderConstraint = NULL; + rootChannel = NULL; + owner = NULL; + controlType = 0; + channel = 0; + ee = 0; + eeBlend = true; + simulation = true; + targetName.reserve(32); + constraintName.reserve(32); + } + ~IK_Target() { + if (constraint) + delete constraint; + if (target) + delete target; + } +}; + +struct IK_Channel { + bPoseChannel* pchan; // channel where we must copy matrix back + KDL::Frame frame; // frame of the bone relative to object base, not armature base + std::string tail; // segment name of the joint from which we get the bone tail + std::string head; // segment name of the joint from which we get the bone head + int parent; // index in this array of the parent channel + short jointType; // type of joint, combination of IK_SegmentFlag + char ndof; // number of joint angles for this channel + char jointValid; // set to 1 when jointValue has been computed + // for joint constraint + Object* owner; // for pose and IK param + double jointValue[4]; // computed joint value + + IK_Channel() { + pchan = NULL; + parent = -1; + jointType = 0; + ndof = 0; + jointValid = 0; + owner = NULL; + jointValue[0] = 0.0; + jointValue[1] = 0.0; + jointValue[2] = 0.0; + jointValue[3] = 0.0; + } +}; + +struct IK_Scene +{ + IK_Scene* next; + int numchan; // number of channel in pchan + int numjoint; // number of joint in jointArray + // array of bone information, one per channel in the tree + IK_Channel* channels; + iTaSC::Armature* armature; + iTaSC::Cache* cache; + iTaSC::Scene* scene; + iTaSC::MovingFrame* base; // armature base object + KDL::Frame baseFrame; // frame of armature base relative to blArmature + KDL::JntArray jointArray; // buffer for storing temporary joint array + iTaSC::Solver* solver; + Object* blArmature; + struct bConstraint* polarConstraint; + std::vector<IK_Target*> targets; + + IK_Scene() { + next = NULL; + channels = NULL; + armature = NULL; + cache = NULL; + scene = NULL; + base = NULL; + solver = NULL; + blArmature = NULL; + numchan = 0; + numjoint = 0; + polarConstraint = NULL; + } + + ~IK_Scene() { + // delete scene first + if (scene) + delete scene; + for(std::vector<IK_Target*>::iterator it = targets.begin(); it != targets.end(); ++it) + delete (*it); + targets.clear(); + if (channels) + delete [] channels; + if (solver) + delete solver; + if (armature) + delete armature; + if (base) + delete base; + // delete cache last + if (cache) + delete cache; + } +}; + +// type of IK joint, can be combined to list the joints corresponding to a bone +enum IK_SegmentFlag { + IK_XDOF = 1, + IK_YDOF = 2, + IK_ZDOF = 4, + IK_SWING = 8, + IK_REVOLUTE = 16, + IK_TRANSY = 32, +}; + +enum IK_SegmentAxis { + IK_X = 0, + IK_Y = 1, + IK_Z = 2, + IK_TRANS_X = 3, + IK_TRANS_Y = 4, + IK_TRANS_Z = 5 +}; + +static int initialize_chain(Object *ob, bPoseChannel *pchan_tip, bConstraint *con) +{ + bPoseChannel *curchan, *pchan_root=NULL, *chanlist[256], **oldchan; + PoseTree *tree; + PoseTarget *target; + bKinematicConstraint *data; + int a, t, segcount= 0, size, newsize, *oldparent, parent, rootbone, treecount; + + data=(bKinematicConstraint*)con->data; + + /* exclude tip from chain? */ + if(!(data->flag & CONSTRAINT_IK_TIP)) + pchan_tip= pchan_tip->parent; + + rootbone = data->rootbone; + /* Find the chain's root & count the segments needed */ + for (curchan = pchan_tip; curchan; curchan=curchan->parent){ + pchan_root = curchan; + + if (++segcount > 255) // 255 is weak + break; + + if(segcount==rootbone){ + // reached this end of the chain but if the chain is overlapping with a + // previous one, we must go back up to the root of the other chain + if ((curchan->flag & POSE_CHAIN) && curchan->iktree.first == NULL){ + rootbone++; + continue; + } + break; + } + + if (curchan->iktree.first != NULL) + // Oh oh, there is already a chain starting from this channel and our chain is longer... + // Should handle this by moving the previous chain up to the begining of our chain + // For now we just stop here + break; + } + if (!segcount) return 0; + // we reached a limit and still not the end of a previous chain, quit + if ((pchan_root->flag & POSE_CHAIN) && pchan_root->iktree.first == NULL) return 0; + + // now that we know how many segment we have, set the flag + for (rootbone = segcount, segcount = 0, curchan = pchan_tip; segcount < rootbone; segcount++, curchan=curchan->parent) { + chanlist[segcount]=curchan; + curchan->flag |= POSE_CHAIN; + } + + /* setup the chain data */ + /* create a target */ + target= (PoseTarget*)MEM_callocN(sizeof(PoseTarget), "posetarget"); + target->con= con; + // by contruction there can be only one tree per channel and each channel can be part of at most one tree. + tree = (PoseTree*)pchan_root->iktree.first; + + if(tree==NULL) { + /* make new tree */ + tree= (PoseTree*)MEM_callocN(sizeof(PoseTree), "posetree"); + + tree->iterations= data->iterations; + tree->totchannel= segcount; + tree->stretch = (data->flag & CONSTRAINT_IK_STRETCH); + + tree->pchan= (bPoseChannel**)MEM_callocN(segcount*sizeof(void*), "ik tree pchan"); + tree->parent= (int*)MEM_callocN(segcount*sizeof(int), "ik tree parent"); + for(a=0; a<segcount; a++) { + tree->pchan[a]= chanlist[segcount-a-1]; + tree->parent[a]= a-1; + } + target->tip= segcount-1; + + /* AND! link the tree to the root */ + BLI_addtail(&pchan_root->iktree, tree); + // new tree + treecount = 1; + } + else { + tree->iterations= MAX2(data->iterations, tree->iterations); + tree->stretch= tree->stretch && !(data->flag & CONSTRAINT_IK_STRETCH); + + /* skip common pose channels and add remaining*/ + size= MIN2(segcount, tree->totchannel); + a = t = 0; + while (a<size && t<tree->totchannel) { + // locate first matching channel + for (;t<tree->totchannel && tree->pchan[t]!=chanlist[segcount-a-1];t++); + if (t>=tree->totchannel) + break; + for(; a<size && t<tree->totchannel && tree->pchan[t]==chanlist[segcount-a-1]; a++, t++); + } + parent= a-1; + segcount= segcount-a; + target->tip= tree->totchannel + segcount - 1; + + if (segcount > 0) { + /* resize array */ + newsize= tree->totchannel + segcount; + oldchan= tree->pchan; + oldparent= tree->parent; + + tree->pchan= (bPoseChannel**)MEM_callocN(newsize*sizeof(void*), "ik tree pchan"); + tree->parent= (int*)MEM_callocN(newsize*sizeof(int), "ik tree parent"); + memcpy(tree->pchan, oldchan, sizeof(void*)*tree->totchannel); + memcpy(tree->parent, oldparent, sizeof(int)*tree->totchannel); + MEM_freeN(oldchan); + MEM_freeN(oldparent); + + /* add new pose channels at the end, in reverse order */ + for(a=0; a<segcount; a++) { + tree->pchan[tree->totchannel+a]= chanlist[segcount-a-1]; + tree->parent[tree->totchannel+a]= tree->totchannel+a-1; + } + tree->parent[tree->totchannel]= parent; + + tree->totchannel= newsize; + } + // reusing tree + treecount = 0; + } + + /* add target to the tree */ + BLI_addtail(&tree->targets, target); + /* mark root channel having an IK tree */ + pchan_root->flag |= POSE_IKTREE; + return treecount; +} + +static bool is_cartesian_constraint(bConstraint *con) +{ + bKinematicConstraint* data=(bKinematicConstraint*)con->data; + + return true; +} + +static bool constraint_valid(bConstraint *con) +{ + bKinematicConstraint* data=(bKinematicConstraint*)con->data; + + if (data->flag & CONSTRAINT_IK_AUTO) + return true; + if (con->flag & CONSTRAINT_DISABLE) + return false; + if (is_cartesian_constraint(con)) { + /* cartesian space constraint */ + if (data->tar==NULL) + return false; + if (data->tar->type==OB_ARMATURE && data->subtarget[0]==0) + return false; + } + return true; +} + +int initialize_scene(Object *ob, bPoseChannel *pchan_tip) +{ + bPoseChannel *curchan, *pchan_root=NULL, *chanlist[256], **oldchan; + PoseTree *tree; + PoseTarget *target; + bConstraint *con; + bKinematicConstraint *data; + int a, segcount= 0, size, newsize, *oldparent, parent, rootbone, treecount; + + /* find all IK constraints and validate them */ + treecount = 0; + for(con= (bConstraint *)pchan_tip->constraints.first; con; con= (bConstraint *)con->next) { + if(con->type==CONSTRAINT_TYPE_KINEMATIC) { + if (constraint_valid(con)) + treecount += initialize_chain(ob, pchan_tip, con); + } + } + return treecount; +} + +static IK_Data* get_ikdata(bPose *pose) +{ + if (pose->ikdata) + return (IK_Data*)pose->ikdata; + pose->ikdata = MEM_callocN(sizeof(IK_Data), "iTaSC ikdata"); + // here init ikdata if needed + // now that we have scene, make sure the default param are initialized + if (!DefIKParam.iksolver) + init_pose_itasc(&DefIKParam); + + return (IK_Data*)pose->ikdata; +} +static double EulerAngleFromMatrix(const KDL::Rotation& R, int axis) +{ + double t = KDL::sqrt(R(0,0)*R(0,0) + R(0,1)*R(0,1)); + + if (t > 16.0*KDL::epsilon) { + if (axis == 0) return -KDL::atan2(R(1,2), R(2,2)); + else if(axis == 1) return KDL::atan2(-R(0,2), t); + else return -KDL::atan2(R(0,1), R(0,0)); + } else { + if (axis == 0) return -KDL::atan2(-R(2,1), R(1,1)); + else if(axis == 1) return KDL::atan2(-R(0,2), t); + else return 0.0f; + } +} + +static double ComputeTwist(const KDL::Rotation& R) +{ + // qy and qw are the y and w components of the quaternion from R + double qy = R(0,2) - R(2,0); + double qw = R(0,0) + R(1,1) + R(2,2) + 1; + + double tau = 2*KDL::atan2(qy, qw); + + return tau; +} + +static void RemoveEulerAngleFromMatrix(KDL::Rotation& R, double angle, int axis) +{ + // compute twist parameter + KDL::Rotation T; + switch (axis) { + case 0: + T = KDL::Rotation::RotX(-angle); + break; + case 1: + T = KDL::Rotation::RotY(-angle); + break; + case 2: + T = KDL::Rotation::RotZ(-angle); + break; + default: + return; + } + // remove angle + R = R*T; +} + +static void GetEulerXZY(const KDL::Rotation& R, double& X,double& Z,double& Y) +{ + if (fabs(R(0,1)) > 1.0 - KDL::epsilon ) { + X = -KDL::sign(R(0,1)) * KDL::atan2(R(1,2), R(1,0)); + Z = -KDL::sign(R(0,1)) * KDL::PI / 2; + Y = 0.0 ; + } else { + X = KDL::atan2(R(2,1), R(1,1)); + Z = KDL::atan2(-R(0,1), KDL::sqrt( KDL::sqr(R(0,0)) + KDL::sqr(R(0,2)))); + Y = KDL::atan2(R(0,2), R(0,0)); + } +} + +static void GetEulerXYZ(const KDL::Rotation& R, double& X,double& Y,double& Z) +{ + if (fabs(R(0,2)) > 1.0 - KDL::epsilon ) { + X = KDL::sign(R(0,2)) * KDL::atan2(-R(1,0), R(1,1)); + Y = KDL::sign(R(0,2)) * KDL::PI / 2; + Z = 0.0 ; + } else { + X = KDL::atan2(-R(1,2), R(2,2)); + Y = KDL::atan2(R(0,2), KDL::sqrt( KDL::sqr(R(0,0)) + KDL::sqr(R(0,1)))); + Z = KDL::atan2(-R(0,1), R(0,0)); + } +} + +static void GetJointRotation(KDL::Rotation& boneRot, int type, double* rot) +{ + switch (type & ~IK_TRANSY) + { + default: + // fixed bone, no joint + break; + case IK_XDOF: + // RX only, get the X rotation + rot[0] = EulerAngleFromMatrix(boneRot, 0); + break; + case IK_YDOF: + // RY only, get the Y rotation + rot[0] = ComputeTwist(boneRot); + break; + case IK_ZDOF: + // RZ only, get the Z rotation + rot[0] = EulerAngleFromMatrix(boneRot, 2); + break; + case IK_XDOF|IK_YDOF: + rot[1] = ComputeTwist(boneRot); + RemoveEulerAngleFromMatrix(boneRot, rot[1], 1); + rot[0] = EulerAngleFromMatrix(boneRot, 0); + break; + case IK_SWING: + // RX+RZ + boneRot.GetXZRot().GetValue(rot); + break; + case IK_YDOF|IK_ZDOF: + // RZ+RY + rot[1] = ComputeTwist(boneRot); + RemoveEulerAngleFromMatrix(boneRot, rot[1], 1); + rot[0] = EulerAngleFromMatrix(boneRot, 2); + break; + case IK_SWING|IK_YDOF: + rot[2] = ComputeTwist(boneRot); + RemoveEulerAngleFromMatrix(boneRot, rot[2], 1); + boneRot.GetXZRot().GetValue(rot); + break; + case IK_REVOLUTE: + boneRot.GetRot().GetValue(rot); + break; + } +} + +static bool target_callback(const iTaSC::Timestamp& timestamp, const iTaSC::Frame& current, iTaSC::Frame& next, void *param) +{ + IK_Target* target = (IK_Target*)param; + // compute next target position + // get target matrix from constraint. + bConstraint* constraint = (bConstraint*)target->blenderConstraint; + float tarmat[4][4]; + + get_constraint_target_matrix(constraint, 0, CONSTRAINT_OBTYPE_OBJECT, target->owner, tarmat, 1.0); + + // rootmat contains the target pose in world coordinate + // if enforce is != 1.0, blend the target position with the end effector position + // if the armature was in rest position. This information is available in eeRest + if (constraint->enforce != 1.0f && target->eeBlend) { + // eeRest is relative to the reference frame of the IK root + // get this frame in world reference + float restmat[4][4]; + bPoseChannel* pchan = target->rootChannel; + if (pchan->parent) { + pchan = pchan->parent; + float chanmat[4][4]; + Mat4CpyMat4(chanmat, pchan->pose_mat); + VECCOPY(chanmat[3], pchan->pose_tail); + Mat4MulSerie(restmat, target->owner->obmat, chanmat, target->eeRest, NULL, NULL, NULL, NULL, NULL); + } + else { + Mat4MulMat4(restmat, target->eeRest, target->owner->obmat); + } + // blend the target + Mat4BlendMat4(tarmat, restmat, tarmat, constraint->enforce); + } + next.setValue(&tarmat[0][0]); + return true; +} + +static bool base_callback(const iTaSC::Timestamp& timestamp, const iTaSC::Frame& current, iTaSC::Frame& next, void *param) +{ + IK_Scene* ikscene = (IK_Scene*)param; + // compute next armature base pose + // algorithm: + // ikscene->pchan[0] is the root channel of the tree + // if it has a parent, get the pose matrix from it and replace [3] by parent pchan->tail + // then multiply by the armature matrix to get ikscene->armature base position + bPoseChannel* pchan = ikscene->channels[0].pchan; + float rootmat[4][4]; + if (pchan->parent) { + pchan = pchan->parent; + float chanmat[4][4]; + Mat4CpyMat4(chanmat, pchan->pose_mat); + VECCOPY(chanmat[3], pchan->pose_tail); + // save the base as a frame too so that we can compute deformation + // after simulation + ikscene->baseFrame.setValue(&chanmat[0][0]); + Mat4MulMat4(rootmat, chanmat, ikscene->blArmature->obmat); + } + else { + Mat4CpyMat4(rootmat, ikscene->blArmature->obmat); + ikscene->baseFrame = iTaSC::F_identity; + } + next.setValue(&rootmat[0][0]); + // if there is a polar target (only during solving otherwise we don't have end efffector) + if (ikscene->polarConstraint && timestamp.update) { + // compute additional rotation of base frame so that armature follows the polar target + float imat[4][4]; // IK tree base inverse matrix + float polemat[4][4]; // polar target in IK tree base frame + float goalmat[4][4]; // target in IK tree base frame + float mat[4][4]; // temp matrix + bKinematicConstraint* poledata = (bKinematicConstraint*)ikscene->polarConstraint->data; + + Mat4Invert(imat, rootmat); + // polar constraint imply only one target + IK_Target *iktarget = ikscene->targets[0]; + // root channel from which we take the bone initial orientation + IK_Channel &rootchan = ikscene->channels[0]; + + // get polar target matrix in world space + get_constraint_target_matrix(ikscene->polarConstraint, 1, CONSTRAINT_OBTYPE_OBJECT, ikscene->blArmature, mat, 1.0); + // convert to armature space + Mat4MulMat4(polemat, mat, imat); + // get the target in world space (was computed before as target object are defined before base object) + iktarget->target->getPose().getValue(mat[0]); + // convert to armature space + Mat4MulMat4(goalmat, mat, imat); + // take position of target, polar target, end effector, in armature space + KDL::Vector goalpos(goalmat[3]); + KDL::Vector polepos(polemat[3]); + KDL::Vector endpos = ikscene->armature->getPose(iktarget->ee).p; + // get root bone orientation + KDL::Frame rootframe; + ikscene->armature->getRelativeFrame(rootframe, rootchan.tail); + KDL::Vector rootx = rootframe.M.UnitX(); + KDL::Vector rootz = rootframe.M.UnitZ(); + // and compute root bone head + double q_rest[3], q[3], length; + const KDL::Joint* joint; + const KDL::Frame* tip; + ikscene->armature->getSegment(rootchan.tail, 3, joint, q_rest[0], q[0], tip); + length = (joint->getType() == KDL::Joint::TransY) ? q[0] : tip->p(1); + KDL::Vector rootpos = rootframe.p - length*rootframe.M.UnitY(); + + // compute main directions + KDL::Vector dir = KDL::Normalize(endpos - rootpos); + KDL::Vector poledir = KDL::Normalize(goalpos-rootpos); + // compute up directions + KDL::Vector poleup = KDL::Normalize(polepos-rootpos); + KDL::Vector up = rootx*KDL::cos(poledata->poleangle) + rootz*KDL::sin(poledata->poleangle); + // from which we build rotation matrix + KDL::Rotation endrot, polerot; + // for the armature, using the root bone orientation + KDL::Vector x = KDL::Normalize(dir*up); + endrot.UnitX(x); + endrot.UnitY(KDL::Normalize(x*dir)); + endrot.UnitZ(-dir); + // for the polar target + x = KDL::Normalize(poledir*poleup); + polerot.UnitX(x); + polerot.UnitY(KDL::Normalize(x*poledir)); + polerot.UnitZ(-poledir); + // the difference between the two is the rotation we want to apply + KDL::Rotation result(polerot*endrot.Inverse()); + // apply on base frame as this is an artificial additional rotation + next.M = next.M*result; + ikscene->baseFrame.M = ikscene->baseFrame.M*result; + } + return true; +} + +static bool copypose_callback(const iTaSC::Timestamp& timestamp, iTaSC::ConstraintValues* const _values, unsigned int _nvalues, void* _param) +{ + IK_Target* iktarget =(IK_Target*)_param; + bKinematicConstraint *condata = (bKinematicConstraint *)iktarget->blenderConstraint->data; + iTaSC::ConstraintValues* values = _values; + bItasc* ikparam = (bItasc*) iktarget->owner->pose->ikparam; + iTaSC::ConstraintSingleValue* value; + double error; + int i; + + // we need default parameters + if (!ikparam) + ikparam = &DefIKParam; + + if (iktarget->blenderConstraint->flag & CONSTRAINT_OFF) { + if (iktarget->controlType & iTaSC::CopyPose::CTL_POSITION) { + values->alpha = 0.0; + values->action = iTaSC::ACT_ALPHA; + values++; + } + if (iktarget->controlType & iTaSC::CopyPose::CTL_ROTATION) { + values->alpha = 0.0; + values->action = iTaSC::ACT_ALPHA; + values++; + } + } else { + if (iktarget->controlType & iTaSC::CopyPose::CTL_POSITION) { + // update error + values->alpha = condata->weight; + values->action = iTaSC::ACT_ALPHA|iTaSC::ACT_FEEDBACK; + values->feedback = (iktarget->simulation) ? ikparam->feedback : ANIM_FEEDBACK; + values++; + } + if (iktarget->controlType & iTaSC::CopyPose::CTL_ROTATION) { + // update error + values->alpha = condata->orientweight; + values->action = iTaSC::ACT_ALPHA|iTaSC::ACT_FEEDBACK; + values->feedback = (iktarget->simulation) ? ikparam->feedback : ANIM_FEEDBACK; + values++; + } + } + return true; +} + +static void copypose_error(const iTaSC::ConstraintValues* values, unsigned int nvalues, IK_Target* iktarget) +{ + iTaSC::ConstraintSingleValue* value; + double error; + int i; + + if (iktarget->controlType & iTaSC::CopyPose::CTL_POSITION) { + // update error + for (i=0, error=0.0, value=values->values; i<values->number; ++i, ++value) + error += KDL::sqr(value->y - value->yd); + iktarget->blenderConstraint->lin_error = (float)KDL::sqrt(error); + values++; + } + if (iktarget->controlType & iTaSC::CopyPose::CTL_ROTATION) { + // update error + for (i=0, error=0.0, value=values->values; i<values->number; ++i, ++value) + error += KDL::sqr(value->y - value->yd); + iktarget->blenderConstraint->rot_error = (float)KDL::sqrt(error); + values++; + } +} + +static bool distance_callback(const iTaSC::Timestamp& timestamp, iTaSC::ConstraintValues* const _values, unsigned int _nvalues, void* _param) +{ + IK_Target* iktarget =(IK_Target*)_param; + bKinematicConstraint *condata = (bKinematicConstraint *)iktarget->blenderConstraint->data; + iTaSC::ConstraintValues* values = _values; + bItasc* ikparam = (bItasc*) iktarget->owner->pose->ikparam; + // we need default parameters + if (!ikparam) + ikparam = &DefIKParam; + + // update weight according to mode + if (iktarget->blenderConstraint->flag & CONSTRAINT_OFF) { + values->alpha = 0.0; + } else { + switch (condata->mode) { + case LIMITDIST_INSIDE: + values->alpha = (values->values[0].y > condata->dist) ? condata->weight : 0.0; + break; + case LIMITDIST_OUTSIDE: + values->alpha = (values->values[0].y < condata->dist) ? condata->weight : 0.0; + break; + default: + values->alpha = condata->weight; + break; + } + if (!timestamp.substep) { + // only update value on first timestep + switch (condata->mode) { + case LIMITDIST_INSIDE: + values->values[0].yd = condata->dist*0.95; + break; + case LIMITDIST_OUTSIDE: + values->values[0].yd = condata->dist*1.05; + break; + default: + values->values[0].yd = condata->dist; + break; + } + values->values[0].action = iTaSC::ACT_VALUE|iTaSC::ACT_FEEDBACK; + values->feedback = (iktarget->simulation) ? ikparam->feedback : ANIM_FEEDBACK; + } + } + values->action |= iTaSC::ACT_ALPHA; + return true; +} + +static void distance_error(const iTaSC::ConstraintValues* values, unsigned int _nvalues, IK_Target* iktarget) +{ + iktarget->blenderConstraint->lin_error = (float)(values->values[0].y - values->values[0].yd); +} + +static bool joint_callback(const iTaSC::Timestamp& timestamp, iTaSC::ConstraintValues* const _values, unsigned int _nvalues, void* _param) +{ + IK_Channel* ikchan = (IK_Channel*)_param; + bItasc* ikparam = (bItasc*)ikchan->owner->pose->ikparam; + bPoseChannel* chan = ikchan->pchan; + int dof; + + // a channel can be splitted into multiple joints, so we get called multiple + // times for one channel (this callback is only for 1 joint in the armature) + // the IK_JointTarget structure is shared between multiple joint constraint + // and the target joint values is computed only once, remember this in jointValid + // Don't forget to reset it before each frame + if (!ikchan->jointValid) { + float rmat[3][3]; + + if (chan->rotmode > 0) { + /* euler rotations (will cause gimble lock, but this can be alleviated a bit with rotation orders) */ + EulOToMat3(chan->eul, chan->rotmode, rmat); + } + else if (chan->rotmode == PCHAN_ROT_AXISANGLE) { + /* axis-angle - stored in quaternion data, but not really that great for 3D-changing orientations */ + AxisAngleToMat3(&chan->quat[1], chan->quat[0], rmat); + } + else { + /* quats are normalised before use to eliminate scaling issues */ + NormalQuat(chan->quat); + QuatToMat3(chan->quat, rmat); + } + KDL::Rotation jointRot( + rmat[0][0], rmat[1][0], rmat[2][0], + rmat[0][1], rmat[1][1], rmat[2][1], + rmat[0][2], rmat[1][2], rmat[2][2]); + GetJointRotation(jointRot, ikchan->jointType, ikchan->jointValue); + ikchan->jointValid = 1; + } + // determine which part of jointValue is used for this joint + // closely related to the way the joints are defined + switch (ikchan->jointType & ~IK_TRANSY) + { + case IK_XDOF: + case IK_YDOF: + case IK_ZDOF: + dof = 0; + break; + case IK_XDOF|IK_YDOF: + // X + Y + dof = (_values[0].id == iTaSC::Armature::ID_JOINT_RX) ? 0 : 1; + break; + case IK_SWING: + // XZ + dof = 0; + break; + case IK_YDOF|IK_ZDOF: + // Z + Y + dof = (_values[0].id == iTaSC::Armature::ID_JOINT_RZ) ? 0 : 1; + break; + case IK_SWING|IK_YDOF: + // XZ + Y + dof = (_values[0].id == iTaSC::Armature::ID_JOINT_RY) ? 2 : 0; + break; + case IK_REVOLUTE: + dof = 0; + break; + default: + dof = -1; + break; + } + if (dof >= 0) { + for (int i=0; i<_nvalues; i++, dof++) { + _values[i].values[0].yd = ikchan->jointValue[dof]; + _values[i].alpha = chan->ikrotweight; + _values[i].feedback = ikparam->feedback; + } + } + return true; +} + +// build array of joint corresponding to IK chain +static int convert_channels(IK_Scene *ikscene, PoseTree *tree) +{ + IK_Channel *ikchan; + bPoseChannel *pchan; + PoseTarget* target; + Bone *bone; + int a, flag, njoint; + + njoint = 0; + for(a=0, ikchan = ikscene->channels; a<ikscene->numchan; ++a, ++ikchan) { + pchan= tree->pchan[a]; + bone= pchan->bone; + ikchan->pchan = pchan; + ikchan->parent = (a>0) ? tree->parent[a] : -1; + ikchan->owner = ikscene->blArmature; + + /* set DoF flag */ + flag = 0; + if(!(pchan->ikflag & BONE_IK_NO_XDOF) && !(pchan->ikflag & BONE_IK_NO_XDOF_TEMP) && + (!(pchan->ikflag & BONE_IK_XLIMIT) || pchan->limitmin[0]<0.f || pchan->limitmax[0]>0.f)) + flag |= IK_XDOF; + if(!(pchan->ikflag & BONE_IK_NO_YDOF) && !(pchan->ikflag & BONE_IK_NO_YDOF_TEMP) && + (!(pchan->ikflag & BONE_IK_YLIMIT) || pchan->limitmin[1]<0.f || pchan->limitmax[1]>0.f)) + flag |= IK_YDOF; + if(!(pchan->ikflag & BONE_IK_NO_ZDOF) && !(pchan->ikflag & BONE_IK_NO_ZDOF_TEMP) && + (!(pchan->ikflag & BONE_IK_ZLIMIT) || pchan->limitmin[2]<0.f || pchan->limitmax[2]>0.f)) + flag |= IK_ZDOF; + + if(tree->stretch && (pchan->ikstretch > 0.0)) { + flag |= IK_TRANSY; + } + /* + Logic to create the segments: + RX,RY,RZ = rotational joints with no length + RY(tip) = rotational joints with a fixed length arm = (0,length,0) + TY = translational joint on Y axis + F(pos) = fixed joint with an arm at position pos + Conversion rule of the above flags: + - ==> F(tip) + X ==> RX(tip) + Y ==> RY(tip) + Z ==> RZ(tip) + XY ==> RX+RY(tip) + XZ ==> RX+RZ(tip) + YZ ==> RZ+RY(tip) + XYZ ==> full spherical unless there are limits, in which case RX+RZ+RY(tip) + In case of stretch, tip=(0,0,0) and there is an additional TY joint + The frame at last of these joints represents the tail of the bone. + The head is computed by a reverse translation on Y axis of the bone length + or in case of TY joint, by the frame at previous joint. + In case of separation of bones, there is an additional F(head) joint + + Computing rest pose and length is complicated: the solver works in world space + Here is the logic: + rest position is computed only from bone->bone_mat. + bone length is computed from bone->length multiplied by the scaling factor of + the armature. Non-uniform scaling will give bad result! + + */ + switch (flag & (IK_XDOF|IK_YDOF|IK_ZDOF)) + { + default: + ikchan->jointType = 0; + ikchan->ndof = 0; + break; + case IK_XDOF: + // RX only, get the X rotation + ikchan->jointType = IK_XDOF; + ikchan->ndof = 1; + break; + case IK_YDOF: + // RY only, get the Y rotation + ikchan->jointType = IK_YDOF; + ikchan->ndof = 1; + break; + case IK_ZDOF: + // RZ only, get the Zz rotation + ikchan->jointType = IK_ZDOF; + ikchan->ndof = 1; + break; + case IK_XDOF|IK_YDOF: + ikchan->jointType = IK_XDOF|IK_YDOF; + ikchan->ndof = 2; + break; + case IK_XDOF|IK_ZDOF: + // RX+RZ + ikchan->jointType = IK_SWING; + ikchan->ndof = 2; + break; + case IK_YDOF|IK_ZDOF: + // RZ+RY + ikchan->jointType = IK_ZDOF|IK_YDOF; + ikchan->ndof = 2; + break; + case IK_XDOF|IK_YDOF|IK_ZDOF: + // spherical joint + if (pchan->ikflag & (BONE_IK_XLIMIT|BONE_IK_YLIMIT|BONE_IK_ZLIMIT)) + // decompose in a Swing+RotY joint + ikchan->jointType = IK_SWING|IK_YDOF; + else + ikchan->jointType = IK_REVOLUTE; + ikchan->ndof = 3; + break; + } + if (flag & IK_TRANSY) { + ikchan->jointType |= IK_TRANSY; + ikchan->ndof++; + } + njoint += ikchan->ndof; + } + // njoint is the joint coordinate, create the Joint Array + ikscene->jointArray.resize(njoint); + ikscene->numjoint = njoint; + return njoint; +} + +// compute array of joint value corresponding to current pose +static void convert_pose(IK_Scene *ikscene) +{ + KDL::Rotation boneRot; + bPoseChannel *pchan; + IK_Channel *ikchan; + Bone *bone; + float rmat[4][4]; // rest pose of bone with parent taken into account + float bmat[4][4]; // difference + float scale; + double *rot; + int a, joint; + + // assume uniform scaling and take Y scale as general scale for the armature + scale = VecLength(ikscene->blArmature->obmat[1]); + rot = &ikscene->jointArray(0); + for(joint=a=0, ikchan = ikscene->channels; a<ikscene->numchan && joint<ikscene->numjoint; ++a, ++ikchan) { + pchan= ikchan->pchan; + bone= pchan->bone; + + if (pchan->parent) { + Mat4One(bmat); + Mat4MulMat43(bmat, pchan->parent->pose_mat, bone->bone_mat); + } else { + Mat4CpyMat4(bmat, bone->arm_mat); + } + Mat4Invert(rmat, bmat); + Mat4MulMat4(bmat, pchan->pose_mat, rmat); + Mat4Ortho(bmat); + boneRot.setValue(bmat[0]); + GetJointRotation(boneRot, ikchan->jointType, rot); + if (ikchan->jointType & IK_TRANSY) { + // compute actual length + rot[ikchan->ndof-1] = VecLenf(pchan->pose_tail, pchan->pose_head) * scale; + } + rot += ikchan->ndof; + joint += ikchan->ndof; + } +} + +// compute array of joint value corresponding to current pose +static void rest_pose(IK_Scene *ikscene) +{ + bPoseChannel *pchan; + IK_Channel *ikchan; + Bone *bone; + float scale; + double *rot; + int a, joint; + + // assume uniform scaling and take Y scale as general scale for the armature + scale = VecLength(ikscene->blArmature->obmat[1]); + // rest pose is 0 + KDL::SetToZero(ikscene->jointArray); + // except for transY joints + rot = &ikscene->jointArray(0); + for(joint=a=0, ikchan = ikscene->channels; a<ikscene->numchan && joint<ikscene->numjoint; ++a, ++ikchan) { + pchan= ikchan->pchan; + bone= pchan->bone; + + if (ikchan->jointType & IK_TRANSY) + rot[ikchan->ndof-1] = bone->length*scale; + rot += ikchan->ndof; + joint += ikchan->ndof; + } +} + +static IK_Scene* convert_tree(Scene *blscene, Object *ob, bPoseChannel *pchan) +{ + PoseTree *tree = (PoseTree*)pchan->iktree.first; + PoseTarget *target; + bKinematicConstraint *condata; + bConstraint *polarcon; + bItasc *ikparam; + iTaSC::Armature* arm; + iTaSC::Scene* scene; + IK_Scene* ikscene; + IK_Channel* ikchan; + KDL::Frame initPose; + KDL::Rotation boneRot; + Bone *bone; + int a, t, numtarget; + float length; + bool ret = true, ingame; + double *rot; + double lmin[3], lmax[3]; + + if (tree->totchannel == 0) + return NULL; + + ikscene = new IK_Scene; + arm = new iTaSC::Armature(); + scene = new iTaSC::Scene(); + ikscene->channels = new IK_Channel[tree->totchannel]; + ikscene->numchan = tree->totchannel; + ikscene->armature = arm; + ikscene->scene = scene; + ikparam = (bItasc*)ob->pose->ikparam; + ingame = (ob->pose->flag & POSE_GAME_ENGINE); + if (!ikparam) { + // you must have our own copy + ikparam = &DefIKParam; + } else if (ingame) { + // tweak the param when in game to have efficient stepping + // using fixed substep is not effecient since frames in the GE are often + // shorter than in animation => move to auto step automatically and set + // the target substep duration via min/max + if (!(ikparam->flag & ITASC_AUTO_STEP)) { + float timestep = blscene->r.frs_sec_base/blscene->r.frs_sec; + if (ikparam->numstep > 0) + timestep /= ikparam->numstep; + // with equal min and max, the algorythm will take this step and the indicative substep most of the time + ikparam->minstep = ikparam->maxstep = timestep; + ikparam->flag |= ITASC_AUTO_STEP; + } + } + if ((ikparam->flag & ITASC_SIMULATION) && !ingame) + // no cache in animation mode + ikscene->cache = new iTaSC::Cache(); + + switch (ikparam->solver) { + case ITASC_SOLVER_SDLS: + ikscene->solver = new iTaSC::WSDLSSolver(); + break; + case ITASC_SOLVER_DLS: + ikscene->solver = new iTaSC::WDLSSolver(); + break; + default: + delete ikscene; + return NULL; + } + ikscene->blArmature = ob; + + std::string joint; + std::string root("root"); + std::string parent; + std::vector<double> weights; + double weight[3]; + // assume uniform scaling and take Y scale as general scale for the armature + float scale = VecLength(ob->obmat[1]); + double X, Y, Z; + // build the array of joints corresponding to the IK chain + convert_channels(ikscene, tree); + if (ingame) { + // in the GE, set the initial joint angle to match the current pose + // this will update the jointArray in ikscene + convert_pose(ikscene); + } else { + // in Blender, the rest pose is always 0 for joints + rest_pose(ikscene); + } + rot = &ikscene->jointArray(0); + for(a=0, ikchan = ikscene->channels; a<tree->totchannel; ++a, ++ikchan) { + pchan= ikchan->pchan; + bone= pchan->bone; + + KDL::Frame tip(iTaSC::F_identity); + Vector3 *fl = bone->bone_mat; + KDL::Frame head(KDL::Rotation( + fl[0][0], fl[1][0], fl[2][0], + fl[0][1], fl[1][1], fl[2][1], + fl[0][2], fl[1][2], fl[2][2]), + KDL::Vector(bone->head[0], bone->head[1], bone->head[2])*scale); + + // rest pose length of the bone taking scaling into account + length= bone->length*scale; + parent = (a > 0) ? ikscene->channels[tree->parent[a]].tail : root; + // first the fixed segment to the bone head + if (head.p.Norm() > KDL::epsilon || head.M.GetRot().Norm() > KDL::epsilon) { + joint = bone->name; + joint += ":H"; + ret = arm->addSegment(joint, parent, KDL::Joint::None, 0.0, head); + parent = joint; + } + if (!(ikchan->jointType & IK_TRANSY)) { + // fixed length, put it in tip + tip.p[1] = length; + } + joint = bone->name; + weight[0] = (1.0-pchan->stiffness[0]); + weight[1] = (1.0-pchan->stiffness[1]); + weight[2] = (1.0-pchan->stiffness[2]); + switch (ikchan->jointType & ~IK_TRANSY) + { + case 0: + // fixed bone + if (!(ikchan->jointType & IK_TRANSY)) { + joint += ":F"; + ret = arm->addSegment(joint, parent, KDL::Joint::None, 0.0, tip); + } + break; + case IK_XDOF: + // RX only, get the X rotation + joint += ":RX"; + ret = arm->addSegment(joint, parent, KDL::Joint::RotX, rot[0], tip); + weights.push_back(weight[0]); + break; + case IK_YDOF: + // RY only, get the Y rotation + joint += ":RY"; + ret = arm->addSegment(joint, parent, KDL::Joint::RotY, rot[0], tip); + weights.push_back(weight[1]); + break; + case IK_ZDOF: + // RZ only, get the Zz rotation + joint += ":RZ"; + ret = arm->addSegment(joint, parent, KDL::Joint::RotZ, rot[0], tip); + weights.push_back(weight[2]); + break; + case IK_XDOF|IK_YDOF: + joint += ":RX"; + ret = arm->addSegment(joint, parent, KDL::Joint::RotX, rot[0]); + weights.push_back(weight[0]); + if (ret) { + parent = joint; + joint = bone->name; + joint += ":RY"; + ret = arm->addSegment(joint, parent, KDL::Joint::RotY, rot[1], tip); + weights.push_back(weight[1]); + } + break; + case IK_SWING: + joint += ":SW"; + ret = arm->addSegment(joint, parent, KDL::Joint::Swing, rot[0], tip); + weights.push_back(weight[0]); + weights.push_back(weight[2]); + break; + case IK_YDOF|IK_ZDOF: + // RZ+RY + joint += ":RZ"; + ret = arm->addSegment(joint, parent, KDL::Joint::RotZ, rot[0]); + weights.push_back(weight[2]); + if (ret) { + parent = joint; + joint = bone->name; + joint += ":RY"; + ret = arm->addSegment(joint, parent, KDL::Joint::RotY, rot[1], tip); + weights.push_back(weight[1]); + } + break; + case IK_SWING|IK_YDOF: + // decompose in a Swing+RotY joint + joint += ":SW"; + ret = arm->addSegment(joint, parent, KDL::Joint::Swing, rot[0]); + weights.push_back(weight[0]); + weights.push_back(weight[2]); + if (ret) { + parent = joint; + joint = bone->name; + joint += ":RY"; + ret = arm->addSegment(joint, parent, KDL::Joint::RotY, rot[2], tip); + weights.push_back(weight[1]); + } + break; + case IK_REVOLUTE: + joint += ":SJ"; + ret = arm->addSegment(joint, parent, KDL::Joint::Sphere, rot[0], tip); + weights.push_back(weight[0]); + weights.push_back(weight[1]); + weights.push_back(weight[2]); + break; + } + if (ret && (ikchan->jointType & IK_TRANSY)) { + parent = joint; + joint = bone->name; + joint += ":TY"; + ret = arm->addSegment(joint, parent, KDL::Joint::TransY, rot[ikchan->ndof-1]); + float ikstretch = pchan->ikstretch*pchan->ikstretch; + weight[1] = (1.0-MIN2(1.0-ikstretch, 0.99)); + weights.push_back(weight[1]); + } + if (!ret) + // error making the armature?? + break; + // joint points to the segment that correspond to the bone per say + ikchan->tail = joint; + ikchan->head = parent; + // in case of error + ret = false; + if ((ikchan->jointType & IK_XDOF) && (pchan->ikflag & (BONE_IK_XLIMIT|BONE_IK_ROTCTL))) { + joint = bone->name; + joint += ":RX"; + if (pchan->ikflag & BONE_IK_XLIMIT) { + if (arm->addLimitConstraint(joint, 0, pchan->limitmin[0], pchan->limitmax[0]) < 0) + break; + } + if (pchan->ikflag & BONE_IK_ROTCTL) { + if (arm->addConstraint(joint, joint_callback, ikchan, false, false) < 0) + break; + } + } + if ((ikchan->jointType & IK_YDOF) && (pchan->ikflag & (BONE_IK_YLIMIT|BONE_IK_ROTCTL))) { + joint = bone->name; + joint += ":RY"; + if (pchan->ikflag & BONE_IK_YLIMIT) { + if (arm->addLimitConstraint(joint, 0, pchan->limitmin[1], pchan->limitmax[1]) < 0) + break; + } + if (pchan->ikflag & BONE_IK_ROTCTL) { + if (arm->addConstraint(joint, joint_callback, ikchan, false, false) < 0) + break; + } + } + if ((ikchan->jointType & IK_ZDOF) && (pchan->ikflag & (BONE_IK_ZLIMIT|BONE_IK_ROTCTL))) { + joint = bone->name; + joint += ":RZ"; + if (pchan->ikflag & BONE_IK_ZLIMIT) { + if (arm->addLimitConstraint(joint, 0, pchan->limitmin[2], pchan->limitmax[2]) < 0) + break; + } + if (pchan->ikflag & BONE_IK_ROTCTL) { + if (arm->addConstraint(joint, joint_callback, ikchan, false, false) < 0) + break; + } + } + if ((ikchan->jointType & IK_SWING) && (pchan->ikflag & (BONE_IK_XLIMIT|BONE_IK_ZLIMIT|BONE_IK_ROTCTL))) { + joint = bone->name; + joint += ":SW"; + if (pchan->ikflag & BONE_IK_XLIMIT) { + if (arm->addLimitConstraint(joint, 0, pchan->limitmin[0], pchan->limitmax[0]) < 0) + break; + } + if (pchan->ikflag & BONE_IK_ZLIMIT) { + if (arm->addLimitConstraint(joint, 1, pchan->limitmin[2], pchan->limitmax[2]) < 0) + break; + } + if (pchan->ikflag & BONE_IK_ROTCTL) { + if (arm->addConstraint(joint, joint_callback, ikchan, false, false) < 0) + break; + } + } + if ((ikchan->jointType & IK_REVOLUTE) && (pchan->ikflag & BONE_IK_ROTCTL)) { + joint = bone->name; + joint += ":SJ"; + if (arm->addConstraint(joint, joint_callback, ikchan, false, false) < 0) + break; + } + // no error, so restore + ret = true; + rot += ikchan->ndof; + } + if (!ret) { + delete ikscene; + return NULL; + } + // for each target, we need to add an end effector in the armature + for (numtarget=0, polarcon=NULL, ret = true, target=(PoseTarget*)tree->targets.first; target; target=(PoseTarget*)target->next) { + condata= (bKinematicConstraint*)target->con->data; + pchan = tree->pchan[target->tip]; + + if (is_cartesian_constraint(target->con)) { + // add the end effector + IK_Target* iktarget = new IK_Target(); + ikscene->targets.push_back(iktarget); + iktarget->ee = arm->addEndEffector(ikscene->channels[target->tip].tail); + if (iktarget->ee == -1) { + ret = false; + break; + } + // initialize all the fields that we can set at this time + iktarget->blenderConstraint = target->con; + iktarget->channel = target->tip; + iktarget->simulation = (ikparam->flag & ITASC_SIMULATION); + iktarget->rootChannel = ikscene->channels[0].pchan; + iktarget->owner = ob; + iktarget->targetName = pchan->bone->name; + iktarget->targetName += ":T:"; + iktarget->targetName += target->con->name; + iktarget->constraintName = pchan->bone->name; + iktarget->constraintName += ":C:"; + iktarget->constraintName += target->con->name; + numtarget++; + if (condata->poletar) + // this constraint has a polar target + polarcon = target->con; + } + } + // deal with polar target if any + if (numtarget == 1 && polarcon) { + ikscene->polarConstraint = polarcon; + } + // we can now add the armature + // the armature is based on a moving frame. + // initialize with the correct position in case there is no cache + base_callback(iTaSC::Timestamp(), iTaSC::F_identity, initPose, ikscene); + ikscene->base = new iTaSC::MovingFrame(initPose); + ikscene->base->setCallback(base_callback, ikscene); + std::string armname; + armname = ob->id.name; + armname += ":B"; + ret = scene->addObject(armname, ikscene->base); + armname = ob->id.name; + armname += ":AR"; + if (ret) + ret = scene->addObject(armname, ikscene->armature, ikscene->base); + if (!ret) { + delete ikscene; + return NULL; + } + // set the weight + e_matrix& Wq = arm->getWq(); + assert(Wq.cols() == weights.size()); + for (unsigned int q=0; q<Wq.cols(); q++) + Wq(q,q)=weights[q]; + // get the inverse rest pose frame of the base to compute relative rest pose of end effectors + // this is needed to handle the enforce parameter + // ikscene->pchan[0] is the root channel of the tree + // if it has no parent, then it's just the identify Frame + float invBaseFrame[4][4]; + pchan = ikscene->channels[0].pchan; + if (pchan->parent) { + // it has a parent, get the pose matrix from it + float baseFrame[4][4]; + pchan = pchan->parent; + Mat4CpyMat4(baseFrame, pchan->bone->arm_mat); + // move to the tail and scale to get rest pose of armature base + VecCopyf(baseFrame[3], pchan->bone->arm_tail); + Mat4Invert(invBaseFrame, baseFrame); + } else { + Mat4One(invBaseFrame); + } + // finally add the constraint + for (t=0; t<ikscene->targets.size(); t++) { + IK_Target* iktarget = ikscene->targets[t]; + condata= (bKinematicConstraint*)iktarget->blenderConstraint->data; + pchan = tree->pchan[iktarget->channel]; + unsigned int controltype, bonecnt; + double bonelen; + float mat[4][4]; + + // add the end effector + // estimate the average bone length, used to clamp feedback error + for (bonecnt=0, bonelen=0.f, a=iktarget->channel; a>=0; a=tree->parent[a], bonecnt++) + bonelen += scale*tree->pchan[a]->bone->length; + bonelen /= bonecnt; + + // store the rest pose of the end effector to compute enforce target + Mat4CpyMat4(mat, pchan->bone->arm_mat); + VecCopyf(mat[3], pchan->bone->arm_tail); + // get the rest pose relative to the armature base + Mat4MulMat4(iktarget->eeRest, mat, invBaseFrame); + iktarget->eeBlend = (!ikscene->polarConstraint && condata->type==CONSTRAINT_IK_COPYPOSE) ? true : false; + // use target_callback to make sure the initPose includes enforce coefficient + target_callback(iTaSC::Timestamp(), iTaSC::F_identity, initPose, iktarget); + iktarget->target = new iTaSC::MovingFrame(initPose); + iktarget->target->setCallback(target_callback, iktarget); + ret = scene->addObject(iktarget->targetName, iktarget->target); + if (!ret) + break; + + switch (condata->type) { + case CONSTRAINT_IK_COPYPOSE: + controltype = 0; + if ((condata->flag & CONSTRAINT_IK_ROT) && (condata->orientweight != 0.0)) + controltype |= iTaSC::CopyPose::CTL_ROTATION; + if ((condata->weight != 0.0)) + controltype |= iTaSC::CopyPose::CTL_POSITION; + if (controltype) { + iktarget->constraint = new iTaSC::CopyPose(controltype, controltype, bonelen); + // set the gain + if (controltype & iTaSC::CopyPose::CTL_POSITION) + iktarget->constraint->setControlParameter(iTaSC::CopyPose::ID_POSITION, iTaSC::ACT_ALPHA, condata->weight); + if (controltype & iTaSC::CopyPose::CTL_ROTATION) + iktarget->constraint->setControlParameter(iTaSC::CopyPose::ID_ROTATION, iTaSC::ACT_ALPHA, condata->orientweight); + iktarget->constraint->registerCallback(copypose_callback, iktarget); + iktarget->errorCallback = copypose_error; + iktarget->controlType = controltype; + // add the constraint + ret = scene->addConstraintSet(iktarget->constraintName, iktarget->constraint, armname, iktarget->targetName, ikscene->channels[iktarget->channel].tail); + } + break; + case CONSTRAINT_IK_DISTANCE: + iktarget->constraint = new iTaSC::Distance(bonelen); + iktarget->constraint->setControlParameter(iTaSC::Distance::ID_DISTANCE, iTaSC::ACT_VALUE, condata->dist); + iktarget->constraint->registerCallback(distance_callback, iktarget); + iktarget->errorCallback = distance_error; + // we can update the weight on each substep + iktarget->constraint->substep(true); + // add the constraint + ret = scene->addConstraintSet(iktarget->constraintName, iktarget->constraint, armname, iktarget->targetName, ikscene->channels[iktarget->channel].tail); + break; + } + if (!ret) + break; + } + if (!ret || + !scene->addCache(ikscene->cache) || + !scene->addSolver(ikscene->solver) || + !scene->initialize()) { + delete ikscene; + ikscene = NULL; + } + return ikscene; +} + +static void create_scene(Scene *scene, Object *ob) +{ + bPoseChannel *pchan; + + // create the IK scene + for(pchan= (bPoseChannel *)ob->pose->chanbase.first; pchan; pchan= (bPoseChannel *)pchan->next) { + // by construction there is only one tree + PoseTree *tree = (PoseTree*)pchan->iktree.first; + if (tree) { + IK_Data* ikdata = get_ikdata(ob->pose); + // convert tree in iTaSC::Scene + IK_Scene* ikscene = convert_tree(scene, ob, pchan); + if (ikscene) { + ikscene->next = ikdata->first; + ikdata->first = ikscene; + } + // delete the trees once we are done + while(tree) { + BLI_remlink(&pchan->iktree, tree); + BLI_freelistN(&tree->targets); + if(tree->pchan) MEM_freeN(tree->pchan); + if(tree->parent) MEM_freeN(tree->parent); + if(tree->basis_change) MEM_freeN(tree->basis_change); + MEM_freeN(tree); + tree = (PoseTree*)pchan->iktree.first; + } + } + } +} + +static void init_scene(Object *ob) +{ + bPoseChannel *pchan; + + if (ob->pose->ikdata) { + for(IK_Scene* scene = ((IK_Data*)ob->pose->ikdata)->first; + scene != NULL; + scene = scene->next) { + scene->channels[0].pchan->flag |= POSE_IKTREE; + } + } +} + +static void execute_scene(Scene* blscene, IK_Scene* ikscene, bItasc* ikparam, float ctime, float frtime) +{ + int i; + IK_Channel* ikchan; + if (ikparam->flag & ITASC_SIMULATION) { + for (i=0, ikchan=ikscene->channels; i<ikscene->numchan; i++, ++ikchan) { + // In simulation mode we don't allow external contraint to change our bones, mark the channel done + // also tell Blender that this channel is part of IK tree (cleared on each where_is_pose() + ikchan->pchan->flag |= (POSE_DONE|POSE_CHAIN); + ikchan->jointValid = 0; + } + } else { + // in animation mode, we must get the bone position from action and constraints + for(i=0, ikchan=ikscene->channels; i<ikscene->numchan; i++, ++ikchan) { + if (!(ikchan->pchan->flag & POSE_DONE)) + where_is_pose_bone(blscene, ikscene->blArmature, ikchan->pchan, ctime); + // tell blender that this channel was controlled by IK, it's cleared on each where_is_pose() + ikchan->pchan->flag |= (POSE_DONE|POSE_CHAIN); + ikchan->jointValid = 0; + } + } + // only run execute the scene if at least one of our target is enabled + for (i=ikscene->targets.size(); i > 0; --i) { + IK_Target* iktarget = ikscene->targets[i-1]; + if (!(iktarget->blenderConstraint->flag & CONSTRAINT_OFF)) + break; + } + if (i == 0 && ikscene->armature->getNrOfConstraints() == 0) + // all constraint disabled + return; + + // compute timestep + double timestamp = ctime * frtime + 2147483.648; + double timestep = frtime; + bool reiterate = (ikparam->flag & ITASC_REITERATION) ? true : false; + int numstep = (ikparam->flag & ITASC_AUTO_STEP) ? 0 : ikparam->numstep; + bool simulation = true; + + if (ikparam->flag & ITASC_SIMULATION) { + ikscene->solver->setParam(iTaSC::Solver::DLS_QMAX, ikparam->maxvel); + } + else { + // in animation mode we start from the pose after action and constraint + convert_pose(ikscene); + ikscene->armature->setJointArray(ikscene->jointArray); + // and we don't handle velocity + reiterate = true; + simulation = false; + // time is virtual, so take fixed value for velocity parameters (see itasc_update_param) + // and choose 1s timestep to allow having velocity parameters in radiant + timestep = 1.0; + // use auto setup to let the solver test the variation of the joints + numstep = 0; + } + + if (ikscene->cache && !reiterate && simulation) { + iTaSC::CacheTS sts, cts, dts; + sts = cts = (iTaSC::CacheTS)(timestamp*1000.0+0.5); + if (ikscene->cache->getPreviousCacheItem(ikscene->armature, 0, &cts) == NULL || cts == 0) { + // the cache is empty before this time, reiterate + if (ikparam->flag & ITASC_INITIAL_REITERATION) + reiterate = true; + } else { + // can take the cache as a start point. + sts -= cts; + timestep = sts/1000.0; + } + } + // don't cache if we are reiterating because we don't want to distroy the cache unnecessarily + ikscene->scene->update(timestamp, timestep, numstep, false, !reiterate, simulation); + if (reiterate) { + // how many times do we reiterate? + for (i=0; i<ikparam->numiter; i++) { + if (ikscene->armature->getMaxJointChange() < ikparam->precision || + ikscene->armature->getMaxEndEffectorChange() < ikparam->precision) + break; + ikscene->scene->update(timestamp, timestep, numstep, true, false, simulation); + } + if (simulation) { + // one more fake iteration to cache + ikscene->scene->update(timestamp, 0.0, 1, true, true, true); + } + } + // compute constraint error + for (i=ikscene->targets.size(); i > 0; --i) { + IK_Target* iktarget = ikscene->targets[i-1]; + if (!(iktarget->blenderConstraint->flag & CONSTRAINT_OFF)) { + unsigned int nvalues; + const iTaSC::ConstraintValues* values; + values = iktarget->constraint->getControlParameters(&nvalues); + iktarget->errorCallback(values, nvalues, iktarget); + } + } + // Apply result to bone: + // walk the ikscene->channels + // for each, get the Frame of the joint corresponding to the bone relative to its parent + // combine the parent and the joint frame to get the frame relative to armature + // a backward translation of the bone length gives the head + // if TY, compute the scale as the ratio of the joint length with rest pose length + iTaSC::Armature* arm = ikscene->armature; + KDL::Frame frame; + double q_rest[3], q[3]; + const KDL::Joint* joint; + const KDL::Frame* tip; + bPoseChannel* pchan; + float scale; + float length; + float yaxis[3]; + for (i=0, ikchan=ikscene->channels; i<ikscene->numchan; ++i, ++ikchan) { + if (i == 0) { + if (!arm->getRelativeFrame(frame, ikchan->tail)) + break; + // this frame is relative to base, make it relative to object + ikchan->frame = ikscene->baseFrame * frame; + } + else { + if (!arm->getRelativeFrame(frame, ikchan->tail, ikscene->channels[ikchan->parent].tail)) + break; + // combine with parent frame to get frame relative to object + ikchan->frame = ikscene->channels[ikchan->parent].frame * frame; + } + // ikchan->frame is the tail frame relative to object + // get bone length + if (!arm->getSegment(ikchan->tail, 3, joint, q_rest[0], q[0], tip)) + break; + if (joint->getType() == KDL::Joint::TransY) { + // stretch bones have a TY joint, compute the scale + scale = (float)(q[0]/q_rest[0]); + // the length is the joint itself + length = (float)q[0]; + } + else { + scale = 1.0f; + // for fixed bone, the length is in the tip (always along Y axis) + length = tip->p(1); + } + // ready to compute the pose mat + pchan = ikchan->pchan; + // tail mat + ikchan->frame.getValue(&pchan->pose_mat[0][0]); + VECCOPY(pchan->pose_tail, pchan->pose_mat[3]); + // shift to head + VECCOPY(yaxis, pchan->pose_mat[1]); + VecMulf(yaxis, length); + VecSubf(pchan->pose_mat[3], pchan->pose_mat[3], yaxis); + VECCOPY(pchan->pose_head, pchan->pose_mat[3]); + // add scale + VecMulf(pchan->pose_mat[0], scale); + VecMulf(pchan->pose_mat[1], scale); + VecMulf(pchan->pose_mat[2], scale); + } + if (i<ikscene->numchan) { + // big problem + ; + } +} + +//--------------------------------------------------- +// plugin interface +// +void itasc_initialize_tree(struct Scene *scene, Object *ob, float ctime) +{ + bPoseChannel *pchan; + int count = 0; + + if (ob->pose->ikdata != NULL && !(ob->pose->flag & POSE_WAS_REBUILT)) { + init_scene(ob); + return; + } + // first remove old scene + itasc_clear_data(ob->pose); + // we should handle all the constraint and mark them all disabled + // for blender but we'll start with the IK constraint alone + for(pchan= (bPoseChannel *)ob->pose->chanbase.first; pchan; pchan= (bPoseChannel *)pchan->next) { + if(pchan->constflag & PCHAN_HAS_IK) + count += initialize_scene(ob, pchan); + } + // if at least one tree, create the scenes from the PoseTree stored in the channels + if (count) + create_scene(scene, ob); + itasc_update_param(ob->pose); + // make sure we don't rebuilt until the user changes something important + ob->pose->flag &= ~POSE_WAS_REBUILT; +} + +void itasc_execute_tree(struct Scene *scene, struct Object *ob, struct bPoseChannel *pchan, float ctime) +{ + if (ob->pose->ikdata) { + IK_Data* ikdata = (IK_Data*)ob->pose->ikdata; + bItasc* ikparam = (bItasc*) ob->pose->ikparam; + // we need default parameters + if (!ikparam) ikparam = &DefIKParam; + + for (IK_Scene* ikscene = ikdata->first; ikscene; ikscene = ikscene->next) { + if (ikscene->channels[0].pchan == pchan) { + float timestep = scene->r.frs_sec_base/scene->r.frs_sec; + if (ob->pose->flag & POSE_GAME_ENGINE) { + timestep = ob->pose->ctime; + // limit the timestep to avoid excessive number of iteration + if (timestep > 0.2f) + timestep = 0.2f; + } + execute_scene(scene, ikscene, ikparam, ctime, timestep); + break; + } + } + } +} + +void itasc_release_tree(struct Scene *scene, struct Object *ob, float ctime) +{ + // not used for iTaSC +} + +void itasc_clear_data(struct bPose *pose) +{ + if (pose->ikdata) { + IK_Data* ikdata = (IK_Data*)pose->ikdata; + for (IK_Scene* scene = ikdata->first; scene; scene = ikdata->first) { + ikdata->first = scene->next; + delete scene; + } + MEM_freeN(ikdata); + pose->ikdata = NULL; + } +} + +void itasc_clear_cache(struct bPose *pose) +{ + if (pose->ikdata) { + IK_Data* ikdata = (IK_Data*)pose->ikdata; + for (IK_Scene* scene = ikdata->first; scene; scene = scene->next) { + if (scene->cache) + // clear all cache but leaving the timestamp 0 (=rest pose) + scene->cache->clearCacheFrom(NULL, 1); + } + } +} + +void itasc_update_param(struct bPose *pose) +{ + if (pose->ikdata && pose->ikparam) { + IK_Data* ikdata = (IK_Data*)pose->ikdata; + bItasc* ikparam = (bItasc*)pose->ikparam; + for (IK_Scene* ikscene = ikdata->first; ikscene; ikscene = ikscene->next) { + double armlength = ikscene->armature->getArmLength(); + ikscene->solver->setParam(iTaSC::Solver::DLS_LAMBDA_MAX, ikparam->dampmax*armlength); + ikscene->solver->setParam(iTaSC::Solver::DLS_EPSILON, ikparam->dampeps*armlength); + if (ikparam->flag & ITASC_SIMULATION) { + ikscene->scene->setParam(iTaSC::Scene::MIN_TIMESTEP, ikparam->minstep); + ikscene->scene->setParam(iTaSC::Scene::MAX_TIMESTEP, ikparam->maxstep); + ikscene->solver->setParam(iTaSC::Solver::DLS_QMAX, ikparam->maxvel); + ikscene->armature->setControlParameter(CONSTRAINT_ID_ALL, iTaSC::Armature::ID_JOINT, iTaSC::ACT_FEEDBACK, ikparam->feedback); + } else { + // in animation mode timestep is 1s by convention => + // qmax becomes radiant and feedback becomes fraction of error gap corrected in one iteration + ikscene->scene->setParam(iTaSC::Scene::MIN_TIMESTEP, 1.0); + ikscene->scene->setParam(iTaSC::Scene::MAX_TIMESTEP, 1.0); + ikscene->solver->setParam(iTaSC::Solver::DLS_QMAX, 0.52); + ikscene->armature->setControlParameter(CONSTRAINT_ID_ALL, iTaSC::Armature::ID_JOINT, iTaSC::ACT_FEEDBACK, 0.8); + } + } + } +} + +void itasc_test_constraint(struct Object *ob, struct bConstraint *cons) +{ + struct bKinematicConstraint *data = (struct bKinematicConstraint *)cons->data; + + /* only for IK constraint */ + if (cons->type != CONSTRAINT_TYPE_KINEMATIC || data == NULL) + return; + + switch (data->type) { + case CONSTRAINT_IK_COPYPOSE: + case CONSTRAINT_IK_DISTANCE: + /* cartesian space constraint */ + break; + } +} + diff --git a/source/blender/ikplugin/intern/itasc_plugin.h b/source/blender/ikplugin/intern/itasc_plugin.h new file mode 100644 index 00000000000..25e48965a52 --- /dev/null +++ b/source/blender/ikplugin/intern/itasc_plugin.h @@ -0,0 +1,52 @@ +/** + * $Id$ + * ***** BEGIN GPL 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. + * + * 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. + * + * Original author: Benoit Bolsee + * Contributor(s): + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef ITASC_PLUGIN_H +#define ITASC_PLUGIN_H + +#include "ikplugin_api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +void itasc_initialize_tree(struct Scene *scene, struct Object *ob, float ctime); +void itasc_execute_tree(struct Scene *scene, struct Object *ob, struct bPoseChannel *pchan, float ctime); +void itasc_release_tree(struct Scene *scene, struct Object *ob, float ctime); +void itasc_clear_data(struct bPose *pose); +void itasc_clear_cache(struct bPose *pose); +void itasc_update_param(struct bPose *pose); +void itasc_test_constraint(struct Object *ob, struct bConstraint *cons); + +#ifdef __cplusplus +} +#endif + +#endif // ITASC_PLUGIN_H + diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h index 42696eeb092..43ef9f28828 100644 --- a/source/blender/makesdna/DNA_action_types.h +++ b/source/blender/makesdna/DNA_action_types.h @@ -146,7 +146,9 @@ typedef struct bPoseChannel { float limitmin[3], limitmax[3]; /* DOF constraint */ float stiffness[3]; /* DOF stiffness */ float ikstretch; - + float ikrotweight; /* weight of joint rotation constraint */ + float iklinweight; /* weight of joint stretch constraint */ + float *path; /* totpath x 3 x float */ struct Object *custom; /* draws custom object instead of this channel */ } bPoseChannel; @@ -166,7 +168,8 @@ typedef enum ePchan_Flag { POSE_CHAIN = 0x0200, POSE_DONE = 0x0400, POSE_KEY = 0x1000, - POSE_STRIDE = 0x2000 + POSE_STRIDE = 0x2000, + POSE_IKTREE = 0x4000, } ePchan_Flag; /* PoseChannel constflag (constraint detection) */ @@ -190,9 +193,13 @@ typedef enum ePchan_IkFlag { BONE_IK_YLIMIT = (1<<4), BONE_IK_ZLIMIT = (1<<5), + BONE_IK_ROTCTL = (1<<6), + BONE_IK_LINCTL = (1<<7), + BONE_IK_NO_XDOF_TEMP = (1<<10), BONE_IK_NO_YDOF_TEMP = (1<<11), - BONE_IK_NO_ZDOF_TEMP = (1<<12) + BONE_IK_NO_ZDOF_TEMP = (1<<12), + } ePchan_IkFlag; /* PoseChannel->rotmode */ @@ -209,6 +216,7 @@ typedef enum ePchan_RotMode { /* NOTE: space is reserved here for 18 other possible * euler rotation orders not implemented */ + PCHAN_ROT_MAX, /* sentinel for Py API*/ /* axis angle rotations */ PCHAN_ROT_AXISANGLE = -1 } ePchan_RotMode; @@ -233,7 +241,9 @@ typedef struct bPose { ListBase agroups; /* list of bActionGroups */ int active_group; /* index of active group (starts from 1) */ - int pad; + int iksolver; /* ik solver to use, see ePose_IKSolverType */ + void *ikdata; /* temporary IK data, depends on the IK solver. Not saved in file */ + void *ikparam; /* IK solver parameters, structure depends on iksolver */ } bPose; @@ -249,8 +259,53 @@ typedef enum ePose_Flags { POSE_CONSTRAINTS_TIMEDEPEND = (1<<3), /* recalculate bone paths */ POSE_RECALCPATHS = (1<<4), + /* set by armature_rebuild_pose to give a chance to the IK solver to rebuild IK tree */ + POSE_WAS_REBUILT = (1<<5), + /* set by game_copy_pose to indicate that this pose is used in the game engine */ + POSE_GAME_ENGINE = (1<<6), } ePose_Flags; +/* bPose->iksolver and bPose->ikparam->iksolver */ +typedef enum { + IKSOLVER_LEGACY = 0, + IKSOLVER_ITASC, +} ePose_IKSolverType; + +/* header for all bPose->ikparam structures */ +typedef struct bIKParam { + int iksolver; +} bIKParam; + +/* bPose->ikparam when bPose->iksolver=1 */ +typedef struct bItasc { + int iksolver; + float precision; + short numiter; + short numstep; + float minstep; + float maxstep; + short solver; + short flag; + float feedback; + float maxvel; /* max velocity to SDLS solver */ + float dampmax; /* maximum damping for DLS solver */ + float dampeps; /* threshold of singular value from which the damping start progressively */ +} bItasc; + +/* bItasc->flag */ +typedef enum { + ITASC_AUTO_STEP = (1<<0), + ITASC_INITIAL_REITERATION = (1<<1), + ITASC_REITERATION = (1<<2), + ITASC_SIMULATION = (1<<3), +} eItasc_Flags; + +/* bItasc->solver */ +typedef enum { + ITASC_SOLVER_SDLS = 0, /* selective damped least square, suitable for CopyPose constraint */ + ITASC_SOLVER_DLS /* damped least square with numerical filtering of damping */ +} eItasc_Solver; + /* ************************************************ */ /* Action */ diff --git a/source/blender/makesdna/DNA_actuator_types.h b/source/blender/makesdna/DNA_actuator_types.h index 278da27faf9..58fa38ae159 100644 --- a/source/blender/makesdna/DNA_actuator_types.h +++ b/source/blender/makesdna/DNA_actuator_types.h @@ -142,7 +142,7 @@ typedef struct bGroupActuator { char name[32]; /* property or groupkey */ short pad[3], cur, butsta, butend;/* not referenced, can remove? */ - struct Group *group; /* only during game */ + /* struct Group *group; not used, remove */ } bGroupActuator; @@ -224,6 +224,15 @@ typedef struct bStateActuator { unsigned int mask; /* the bits to change */ } bStateActuator; +typedef struct bArmatureActuator { + char posechannel[32]; + char constraint[32]; + int type; /* 0=run, 1=enable, 2=disable, 3=set target, 4=set weight */ + float weight; + struct Object *target; + struct Object *subtarget; +} bArmatureActuator; + typedef struct bActuator { struct bActuator *next, *prev, *mynew; short type; @@ -295,6 +304,7 @@ typedef struct FreeCamera { #define ACT_PARENT 20 #define ACT_SHAPEACTION 21 #define ACT_STATE 22 +#define ACT_ARMATURE 23 /* actuator flag */ #define ACT_SHOW 1 @@ -484,6 +494,15 @@ typedef struct FreeCamera { #define ACT_PARENT_COMPOUND 1 #define ACT_PARENT_GHOST 2 +/* armatureactuator->type */ +#define ACT_ARM_RUN 0 +#define ACT_ARM_ENABLE 1 +#define ACT_ARM_DISABLE 2 +#define ACT_ARM_SETTARGET 3 +#define ACT_ARM_SETWEIGHT 4 +/* update this define if more type are addedd */ +#define ACT_ARM_MAXTYPE 4 + #endif diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h index 70430af3fc8..fccec7a556f 100644 --- a/source/blender/makesdna/DNA_constraint_types.h +++ b/source/blender/makesdna/DNA_constraint_types.h @@ -66,6 +66,9 @@ typedef struct bConstraint { int pad; struct Ipo *ipo; /* local influence ipo or driver */ // XXX depreceated for 2.5... old animation system hack + /* below are readonly fields that are set at runtime by the solver for use in the GE (only IK atm) */ + float lin_error; /* residual error on constraint expressed in blender unit*/ + float rot_error; /* residual error on constraint expressed in radiant */ } bConstraint; @@ -119,24 +122,34 @@ typedef struct bPythonConstraint { } bPythonConstraint; -/* Inverse-Kinematics (IK) constraint */ +/* inverse-Kinematics (IK) constraint + This constraint supports a variety of mode determine by the type field + according to B_CONSTRAINT_IK_TYPE. + Some fields are used by all types, some are specific to some types + This is indicated in the comments for each field + */ typedef struct bKinematicConstraint { - Object *tar; - short iterations; /* Maximum number of iterations to try */ - short flag; /* Like CONSTRAINT_IK_TIP */ - short rootbone; /* index to rootbone, if zero go all the way to mother bone */ - short max_rootbone; /* for auto-ik, maximum length of chain */ - char subtarget[32]; /* String to specify sub-object target */ - - Object *poletar; /* Pole vector target */ - char polesubtarget[32]; /* Pole vector sub-object target */ - float poleangle; /* Pole vector rest angle */ - - 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 */ + Object *tar; /* All: target object in case constraint needs a target */ + short iterations; /* All: Maximum number of iterations to try */ + short flag; /* All & CopyPose: some options Like CONSTRAINT_IK_TIP */ + short rootbone; /* All: index to rootbone, if zero go all the way to mother bone */ + short max_rootbone; /* CopyPose: for auto-ik, maximum length of chain */ + char subtarget[32]; /* All: String to specify sub-object target */ + Object *poletar; /* All: Pole vector target */ + char polesubtarget[32]; /* All: Pole vector sub-object target */ + float poleangle; /* All: Pole vector rest angle */ + float weight; /* All: Weight of constraint in IK tree */ + float orientweight; /* CopyPose: Amount of rotation a target applies on chain */ + float grabtarget[3]; /* CopyPose: for target-less IK */ + short type; /* subtype of IK constraint: B_CONSTRAINT_IK_TYPE */ + short mode; /* Distance: how to limit in relation to clamping sphere: LIMITDIST_.. */ + float dist; /* Distance: distance (radius of clamping sphere) from target */ } bKinematicConstraint; +typedef enum B_CONSTRAINT_IK_TYPE { + CONSTRAINT_IK_COPYPOSE = 0, /* 'standard' IK constraint: match position and/or orientation of target */ + CONSTRAINT_IK_DISTANCE /* maintain distance with target */ +} B_CONSTRAINT_IK_TYPE; /* Single-target subobject constraints --------------------- */ /* Track To Constraint */ @@ -376,7 +389,9 @@ typedef enum B_CONSTRAINT_FLAG { /* influence ipo is on constraint itself, not in action channel */ CONSTRAINT_OWN_IPO = (1<<7), /* indicates that constraint was added locally (i.e. didn't come from the proxy-lib) */ - CONSTRAINT_PROXY_LOCAL = (1<<8) + CONSTRAINT_PROXY_LOCAL = (1<<8), + /* indicates that constraint is temporarily disabled (only used in GE) */ + CONSTRAINT_OFF = (1<<9) } B_CONSTRAINT_FLAG; /* bConstraint->ownspace/tarspace */ diff --git a/source/blender/makesdna/DNA_sensor_types.h b/source/blender/makesdna/DNA_sensor_types.h index cc998de7eec..a5ba5886ed5 100644 --- a/source/blender/makesdna/DNA_sensor_types.h +++ b/source/blender/makesdna/DNA_sensor_types.h @@ -126,6 +126,13 @@ typedef struct bRaySensor { int axisflag; } bRaySensor; +typedef struct bArmatureSensor { + char posechannel[32]; + char constraint[32]; + int type; + float value; +} bArmatureSensor; + typedef struct bMessageSensor { /** * (Possible future use) pointer to a single sender object @@ -202,6 +209,15 @@ typedef struct bJoystickSensor { #define SENS_MESG_MESG 0 #define SENS_MESG_PROP 1 +/* bArmatureSensor->type */ +#define SENS_ARM_STATE_CHANGED 0 +#define SENS_ARM_LIN_ERROR_BELOW 1 +#define SENS_ARM_LIN_ERROR_ABOVE 2 +#define SENS_ARM_ROT_ERROR_BELOW 3 +#define SENS_ARM_ROT_ERROR_ABOVE 4 +/* update this when adding new type */ +#define SENS_ARM_MAXTYPE 4 + /* sensor->type */ #define SENS_ALWAYS 0 #define SENS_TOUCH 1 @@ -217,6 +233,7 @@ typedef struct bJoystickSensor { #define SENS_JOYSTICK 11 #define SENS_ACTUATOR 12 #define SENS_DELAY 13 +#define SENS_ARMATURE 14 /* sensor->flag */ #define SENS_SHOW 1 #define SENS_DEL 2 diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index b1f5292f9d4..9ea7725b855 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -333,6 +333,7 @@ extern StructRNA RNA_Pose; extern StructRNA RNA_PoseChannel; extern StructRNA RNA_Property; extern StructRNA RNA_PropertySensor; +extern StructRNA RNA_ArmatureSensor; extern StructRNA RNA_PythonConstraint; extern StructRNA RNA_PythonController; extern StructRNA RNA_RadarSensor; diff --git a/source/blender/makesrna/SConscript b/source/blender/makesrna/SConscript index 845abf636e2..8fc9df0fbf6 100644 --- a/source/blender/makesrna/SConscript +++ b/source/blender/makesrna/SConscript @@ -7,7 +7,7 @@ o = SConscript('intern/SConscript') objs += o incs = '#/intern/guardedalloc ../blenkernel ../blenlib ../makesdna intern .' -incs += ' ../windowmanager ../editors/include ../imbuf' +incs += ' ../windowmanager ../editors/include ../imbuf ../ikplugin' incs += ' ../render/extern/include' defs = [] diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index 709c5d017ec..50cf0b00b84 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -39,7 +39,7 @@ SET(SRC ../../../../intern/guardedalloc/intern/mallocn.c ../../../../intern/guardedalloc/intern/mmap_win.c) -INCLUDE_DIRECTORIES(../../../../intern/guardedalloc .. ../../makesdna ../../blenkernel ../../blenlib ../../windowmanager ../../editors/include ../../imbuf ../../render/extern/include .) +INCLUDE_DIRECTORIES(../../../../intern/guardedalloc .. ../../makesdna ../../blenkernel ../../blenlib ../../ikplugin ../../windowmanager ../../editors/include ../../imbuf ../../render/extern/include .) FILE(GLOB INC_FILES ../*.h ../../makesdna/*.h) IF(WITH_GAMEENGINE) diff --git a/source/blender/makesrna/intern/Makefile b/source/blender/makesrna/intern/Makefile index 4a4e41edd15..7923ea1e7de 100644 --- a/source/blender/makesrna/intern/Makefile +++ b/source/blender/makesrna/intern/Makefile @@ -49,6 +49,7 @@ CPPFLAGS += -I$(NAN_GUARDEDALLOC)/include CPPFLAGS += -I../../blenlib CPPFLAGS += -I../../blenkernel CPPFLAGS += -I../../imbuf +CPPFLAGS += -I../../ikplugin CPPFLAGS += -I../../makesdna CPPFLAGS += -I../../windowmanager CPPFLAGS += -I../../editors/include diff --git a/source/blender/makesrna/intern/SConscript b/source/blender/makesrna/intern/SConscript index 569f0547731..0f8bc752f09 100644 --- a/source/blender/makesrna/intern/SConscript +++ b/source/blender/makesrna/intern/SConscript @@ -30,7 +30,7 @@ makesrna_tool.Append(CCFLAGS = '-DBASE_HEADER="\\"source/blender/makesrna/\\"" ' defs = [] incs = '#/intern/guardedalloc ../../blenlib ../../blenkernel' -incs += ' ../../imbuf ../../makesdna ../../makesrna' +incs += ' ../../imbuf ../../makesdna ../../makesrna ../../ikplugin' incs += ' ../../windowmanager ../../editors/include' incs += ' ../../render/extern/include' diff --git a/source/blender/makesrna/intern/rna_actuator.c b/source/blender/makesrna/intern/rna_actuator.c index 473e726db60..ce83d1c469b 100644 --- a/source/blender/makesrna/intern/rna_actuator.c +++ b/source/blender/makesrna/intern/rna_actuator.c @@ -58,6 +58,7 @@ void RNA_def_actuator(BlenderRNA *brna) {ACT_PARENT, "PARENT", 0, "Parent", ""}, {ACT_SHAPEACTION, "SHAPE_ACTION", 0, "Shape Action", ""}, {ACT_STATE, "STATE", 0, "State", ""}, + {ACT_ARMATURE, "ARMATURE", 0, "Armature", ""}, {0, NULL, 0, NULL, NULL}}; srna= RNA_def_struct(brna, "Actuator", NULL); diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c index 86aa2a1d135..b630e61a680 100644 --- a/source/blender/makesrna/intern/rna_constraint.c +++ b/source/blender/makesrna/intern/rna_constraint.c @@ -33,6 +33,7 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "ED_object.h" #include "WM_types.h" EnumPropertyItem constraint_type_items[] ={ @@ -80,6 +81,19 @@ EnumPropertyItem space_object_items[] = { {1, "LOCAL", 0, "Local (Without Parent) Space", ""}, {0, NULL, 0, NULL, NULL}}; +EnumPropertyItem constraint_ik_type_items[] ={ + {CONSTRAINT_IK_COPYPOSE, "COPY_POSE", 0, "Copy Pose", ""}, + {CONSTRAINT_IK_DISTANCE, "DISTANCE", 0, "Distance", ""}, + {0, NULL, 0, NULL, NULL}, +}; + +static EnumPropertyItem constraint_distance_items[] = { + {LIMITDIST_INSIDE, "LIMITDIST_INSIDE", 0, "Inside", ""}, + {LIMITDIST_OUTSIDE, "LIMITDIST_OUTSIDE", 0, "Outside", ""}, + {LIMITDIST_ONSURFACE, "LIMITDIST_ONSURFACE", 0, "On Surface", ""}, + {0, NULL, 0, NULL, NULL} +}; + #ifdef RNA_RUNTIME #include "BKE_action.h" @@ -160,24 +174,12 @@ static char *rna_Constraint_path(PointerRNA *ptr) static void rna_Constraint_update(bContext *C, PointerRNA *ptr) { - Object *ob= ptr->id.data; - - if(ob->pose) update_pose_constraint_flags(ob->pose); - - object_test_constraints(ob); - - if(ob->type==OB_ARMATURE) DAG_id_flush_update(&ob->id, OB_RECALC_DATA|OB_RECALC_OB); - else DAG_id_flush_update(&ob->id, OB_RECALC_OB); + ED_object_constraint_update(ptr->id.data); } static void rna_Constraint_dependency_update(bContext *C, PointerRNA *ptr) { - Object *ob= ptr->id.data; - - rna_Constraint_update(C, ptr); - - if(ob->pose) ob->pose->flag |= POSE_RECALC; // checks & sorts pose channels - DAG_scene_sort(CTX_data_scene(C)); + ED_object_constraint_dependency_update(CTX_data_scene(C), ptr->id.data); } static void rna_Constraint_influence_update(bContext *C, PointerRNA *ptr) @@ -190,6 +192,24 @@ static void rna_Constraint_influence_update(bContext *C, PointerRNA *ptr) rna_Constraint_update(C, ptr); } +static void rna_Constraint_ik_type_set(struct PointerRNA *ptr, int value) +{ + bConstraint *con = ptr->data; + bKinematicConstraint *ikdata = con->data; + + if (ikdata->type != value) { + // the type of IK constraint has changed, set suitable default values + // in case constraints reuse same fields incompatible + switch (value) { + case CONSTRAINT_IK_COPYPOSE: + break; + case CONSTRAINT_IK_DISTANCE: + break; + } + ikdata->type = value; + } +} + static EnumPropertyItem *rna_Constraint_owner_space_itemf(bContext *C, PointerRNA *ptr, int *free) { Object *ob= (Object*)ptr->id.data; @@ -456,21 +476,40 @@ static void rna_def_constraint_kinematic(BlenderRNA *brna) prop= RNA_def_property(srna, "tail", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", CONSTRAINT_IK_TIP); RNA_def_property_ui_text(prop, "Use Tail", "Include bone's tail as last element in chain."); - RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_update"); + RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_dependency_update"); prop= RNA_def_property(srna, "rotation", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", CONSTRAINT_IK_ROT); RNA_def_property_ui_text(prop, "Rotation", "Chain follows rotation of target."); - RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_update"); + RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_dependency_update"); prop= RNA_def_property(srna, "targetless", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", CONSTRAINT_IK_AUTO); RNA_def_property_ui_text(prop, "Targetless", "Use targetless IK."); - RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_update"); + RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_dependency_update"); prop= RNA_def_property(srna, "stretch", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", CONSTRAINT_IK_STRETCH); RNA_def_property_ui_text(prop, "Stretch", "Enable IK Stretching."); + RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_dependency_update"); + + prop= RNA_def_property(srna, "ik_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "type"); + RNA_def_property_enum_funcs(prop, NULL, "rna_Constraint_ik_type_set", NULL); + RNA_def_property_enum_items(prop, constraint_ik_type_items); + RNA_def_property_ui_text(prop, "IK Type", ""); + RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_dependency_update"); + + prop= RNA_def_property(srna, "limit_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "mode"); + RNA_def_property_enum_items(prop, constraint_distance_items); + RNA_def_property_ui_text(prop, "Limit Mode", "Distances in relation to sphere of influence to allow."); + RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_dependency_update"); + + prop= RNA_def_property(srna, "distance", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "dist"); + RNA_def_property_range(prop, 0.0, 100.f); + RNA_def_property_ui_text(prop, "Distance", "Radius of limiting sphere."); RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_update"); } @@ -1475,12 +1514,6 @@ static void rna_def_constraint_distance_limit(BlenderRNA *brna) StructRNA *srna; PropertyRNA *prop; - static EnumPropertyItem distance_items[] = { - {LIMITDIST_INSIDE, "LIMITDIST_INSIDE", 0, "Inside", ""}, - {LIMITDIST_OUTSIDE, "LIMITDIST_OUTSIDE", 0, "Outside", ""}, - {LIMITDIST_ONSURFACE, "LIMITDIST_ONSURFACE", 0, "On Surface", ""}, - {0, NULL, 0, NULL, NULL}}; - srna= RNA_def_struct(brna, "LimitDistanceConstraint", "Constraint"); RNA_def_struct_ui_text(srna, "Limit Distance Constraint", "Limits the distance from target object."); RNA_def_struct_sdna_from(srna, "bDistLimitConstraint", "data"); @@ -1504,7 +1537,7 @@ static void rna_def_constraint_distance_limit(BlenderRNA *brna) prop= RNA_def_property(srna, "limit_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "mode"); - RNA_def_property_enum_items(prop, distance_items); + RNA_def_property_enum_items(prop, constraint_distance_items); RNA_def_property_ui_text(prop, "Limit Mode", "Distances in relation to sphere of influence to allow."); RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_update"); } @@ -1622,7 +1655,18 @@ void RNA_def_constraint(BlenderRNA *brna) RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_ui_text(prop, "Influence", "Amount of influence constraint will have on the final solution."); RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_influence_update"); - + + /* readonly values */ + prop= RNA_def_property(srna, "lin_error", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "lin_error"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Lin error", "Amount of residual error in Blender space unit for constraints that work on position."); + + prop= RNA_def_property(srna, "rot_error", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "rot_error"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Rot error", "Amount of residual error in radiant for constraints that work on orientation."); + /* pointers */ rna_def_constrainttarget(brna); diff --git a/source/blender/makesrna/intern/rna_pose.c b/source/blender/makesrna/intern/rna_pose.c index 7cb7513f474..e76cd56af4e 100644 --- a/source/blender/makesrna/intern/rna_pose.c +++ b/source/blender/makesrna/intern/rna_pose.c @@ -23,6 +23,7 @@ */ #include <stdlib.h> +#include <string.h> #include "RNA_define.h" #include "RNA_types.h" @@ -39,8 +40,8 @@ #ifdef RNA_RUNTIME -#include <string.h> - +#include "BIK_api.h" +#include "BKE_action.h" #include "BLI_arithb.h" #include "DNA_userdef_types.h" @@ -49,8 +50,11 @@ #include "BKE_depsgraph.h" #include "BKE_idprop.h" +#include "ED_object.h" #include "ED_armature.h" +#include "MEM_guardedalloc.h" + static void rna_Pose_update(bContext *C, PointerRNA *ptr) { // XXX when to use this? ob->pose->flag |= (POSE_LOCKED|POSE_DO_UNLOCK); @@ -58,6 +62,15 @@ static void rna_Pose_update(bContext *C, PointerRNA *ptr) DAG_id_flush_update(ptr->id.data, OB_RECALC_DATA); } +static void rna_Pose_IK_update(bContext *C, PointerRNA *ptr) +{ + // XXX when to use this? ob->pose->flag |= (POSE_LOCKED|POSE_DO_UNLOCK); + Object *ob= ptr->id.data; + + DAG_id_flush_update(&ob->id, OB_RECALC_DATA); + BIK_clear_data(ob->pose); +} + static char *rna_PoseChannel_path(PointerRNA *ptr) { return BLI_sprintfN("pose.pose_channels[\"%s\"]", ((bPoseChannel*)ptr->data)->name); @@ -110,6 +123,38 @@ static IDProperty *rna_PoseChannel_idproperties(PointerRNA *ptr, int create) return pchan->prop; } +static void rna_Pose_ik_solver_set(struct PointerRNA *ptr, int value) +{ + bPose *pose= (bPose*)ptr->data; + + if (pose->iksolver != value) { + // the solver has changed, must clean any temporary structures + BIK_clear_data(pose); + if (pose->ikparam) { + MEM_freeN(pose->ikparam); + pose->ikparam = NULL; + } + pose->iksolver = value; + init_pose_ikparam(pose); + } +} + +static void rna_Pose_ik_solver_update(bContext *C, PointerRNA *ptr) +{ + Object *ob= ptr->id.data; + bPose *pose = ptr->data; + Scene *scene = CTX_data_scene(C); + + pose->flag |= POSE_RECALC; // checks & sorts pose channels + DAG_scene_sort(scene); + + update_pose_constraint_flags(pose); + + object_test_constraints(ob); + + DAG_id_flush_update(&ob->id, OB_RECALC_DATA|OB_RECALC_OB); +} + /* rotation - euler angles */ static void rna_PoseChannel_euler_rotation_get(PointerRNA *ptr, float *value) { @@ -236,6 +281,70 @@ static int rna_PoseChannel_has_ik_get(PointerRNA *ptr) return ED_pose_channel_in_IK_chain(ob, pchan); } +StructRNA *rna_IKParam_refine(PointerRNA *ptr) +{ + bIKParam *param = (bIKParam *)ptr->data; + + switch (param->iksolver) { + case IKSOLVER_ITASC: + return &RNA_Itasc; + default: + return &RNA_IKParam; + } +} + +PointerRNA rna_Pose_ikparam_get(struct PointerRNA *ptr) +{ + bPose *pose= (bPose*)ptr->data; + return rna_pointer_inherit_refine(ptr, &RNA_IKParam, pose->ikparam); +} + +static StructRNA *rna_Pose_ikparam_typef(PointerRNA *ptr) +{ + bPose *pose= (bPose*)ptr->data; + + switch (pose->iksolver) { + case IKSOLVER_ITASC: + return &RNA_Itasc; + default: + return &RNA_IKParam; + } +} + +static void rna_Itasc_update(bContext *C, PointerRNA *ptr) +{ + Object *ob = ptr->id.data; + bItasc *itasc = ptr->data; + + /* verify values */ + if (itasc->precision < 0.0001f) + itasc->precision = 0.0001f; + if (itasc->minstep < 0.001f) + itasc->minstep = 0.001f; + if (itasc->maxstep < itasc->minstep) + itasc->maxstep = itasc->minstep; + if (itasc->feedback < 0.01f) + itasc->feedback = 0.01f; + if (itasc->feedback > 100.f) + itasc->feedback = 100.f; + if (itasc->maxvel < 0.01f) + itasc->maxvel = 0.01f; + if (itasc->maxvel > 100.f) + itasc->maxvel = 100.f; + BIK_update_param(ob->pose); + + DAG_id_flush_update(&ob->id, OB_RECALC_DATA); +} + +static void rna_Itasc_update_rebuild(bContext *C, PointerRNA *ptr) +{ + Object *ob= ptr->id.data; + bPose *pose = ob->pose; + + pose->flag |= POSE_RECALC; // checks & sorts pose channels + rna_Itasc_update(C, ptr); +} + static PointerRNA rna_PoseChannel_bone_group_get(PointerRNA *ptr) { Object *ob= (Object*)ptr->id.data; @@ -438,6 +547,16 @@ static void rna_def_bone_group(BlenderRNA *brna) RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_update"); } +static EnumPropertyItem prop_iksolver_items[] = { + {IKSOLVER_LEGACY, "LEGACY", 0, "Legacy", "Original IK solver."}, + {IKSOLVER_ITASC, "ITASC", 0, "iTaSC", "Multi constraint, stateful IK solver."}, + {0, NULL, 0, NULL, NULL}}; + +static EnumPropertyItem prop_solver_items[] = { + {ITASC_SOLVER_SDLS, "SDLS", 0, "SDLS", "Selective Damped Least Square"}, + {ITASC_SOLVER_DLS, "DLS", 0, "DLS", "Damped Least Square with Numerical Filtering"}, + {0, NULL, 0, NULL, NULL}}; + static void rna_def_pose_channel(BlenderRNA *brna) { static EnumPropertyItem prop_rotmode_items[] = { @@ -470,7 +589,7 @@ static void rna_def_pose_channel(BlenderRNA *brna) RNA_def_property_string_funcs(prop, NULL, NULL, "rna_PoseChannel_name_set"); RNA_def_property_ui_text(prop, "Name", ""); RNA_def_struct_name_property(srna, prop); - + prop= RNA_def_property(srna, "selected", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "selectflag", BONE_SELECTED); RNA_def_property_ui_text(prop, "Selected", ""); @@ -480,13 +599,13 @@ static void rna_def_pose_channel(BlenderRNA *brna) RNA_def_property_int_sdna(prop, NULL, "pathsf"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Bone Paths Calculation Start Frame", "Starting frame of range of frames to use for Bone Path calculations."); - RNA_def_property_update(prop, NC_OBJECT|ND_POSE|ND_TRANSFORM, "rna_Pose_update"); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_update"); prop= RNA_def_property(srna, "path_end_frame", PROP_INT, PROP_TIME); RNA_def_property_int_sdna(prop, NULL, "pathef"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Bone Paths Calculation End Frame", "End frame of range of frames to use for Bone Path calculations."); - RNA_def_property_update(prop, NC_OBJECT|ND_POSE|ND_TRANSFORM, "rna_Pose_update"); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_update"); /* Relationships to other bones */ prop= RNA_def_property(srna, "bone", PROP_POINTER, PROP_NONE); @@ -581,96 +700,118 @@ static void rna_def_pose_channel(BlenderRNA *brna) RNA_def_property_boolean_funcs(prop, "rna_PoseChannel_has_ik_get", NULL); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Has IK", "Is part of an IK chain."); - RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_update"); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_IK_update"); prop= RNA_def_property(srna, "ik_dof_x", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna(prop, NULL, "ikflag", BONE_IK_NO_XDOF); RNA_def_property_ui_text(prop, "IK X DoF", "Allow movement around the X axis."); - RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_update"); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_IK_update"); prop= RNA_def_property(srna, "ik_dof_y", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna(prop, NULL, "ikflag", BONE_IK_NO_YDOF); RNA_def_property_ui_text(prop, "IK Y DoF", "Allow movement around the Y axis."); - RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_update"); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE|ND_TRANSFORM, "rna_Pose_IK_update"); prop= RNA_def_property(srna, "ik_dof_z", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna(prop, NULL, "ikflag", BONE_IK_NO_ZDOF); RNA_def_property_ui_text(prop, "IK Z DoF", "Allow movement around the Z axis."); - RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_update"); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_IK_update"); prop= RNA_def_property(srna, "ik_limit_x", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "ikflag", BONE_IK_XLIMIT); RNA_def_property_ui_text(prop, "IK X Limit", "Limit movement around the X axis."); - RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_update"); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_IK_update"); prop= RNA_def_property(srna, "ik_limit_y", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "ikflag", BONE_IK_YLIMIT); RNA_def_property_ui_text(prop, "IK Y Limit", "Limit movement around the Y axis."); - RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_update"); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_IK_update"); prop= RNA_def_property(srna, "ik_limit_z", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "ikflag", BONE_IK_ZLIMIT); RNA_def_property_ui_text(prop, "IK Z Limit", "Limit movement around the Z axis."); - RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_update"); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_IK_update"); + + prop= RNA_def_property(srna, "ik_rot_control", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "ikflag", BONE_IK_ROTCTL); + RNA_def_property_ui_text(prop, "IK rot control", "Apply channel rotation as IK constraint"); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_IK_update"); + + prop= RNA_def_property(srna, "ik_lin_control", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "ikflag", BONE_IK_LINCTL); + RNA_def_property_ui_text(prop, "IK rot control", "Apply channel size as IK constraint if stretching is enabled"); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_IK_update"); prop= RNA_def_property(srna, "ik_min_x", PROP_FLOAT, PROP_ANGLE); RNA_def_property_float_sdna(prop, NULL, "limitmin[0]"); RNA_def_property_range(prop, -180.0f, 0.0f); RNA_def_property_ui_text(prop, "IK X Minimum", "Minimum angles for IK Limit"); - RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_update"); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_IK_update"); prop= RNA_def_property(srna, "ik_max_x", PROP_FLOAT, PROP_ANGLE); RNA_def_property_float_sdna(prop, NULL, "limitmax[0]"); RNA_def_property_range(prop, 0.0f, 180.0f); RNA_def_property_ui_text(prop, "IK X Maximum", "Maximum angles for IK Limit"); - RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_update"); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_IK_update"); prop= RNA_def_property(srna, "ik_min_y", PROP_FLOAT, PROP_ANGLE); RNA_def_property_float_sdna(prop, NULL, "limitmin[1]"); RNA_def_property_range(prop, -180.0f, 0.0f); RNA_def_property_ui_text(prop, "IK Y Minimum", "Minimum angles for IK Limit"); - RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_update"); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_IK_update"); prop= RNA_def_property(srna, "ik_max_y", PROP_FLOAT, PROP_ANGLE); RNA_def_property_float_sdna(prop, NULL, "limitmax[1]"); RNA_def_property_range(prop, 0.0f, 180.0f); RNA_def_property_ui_text(prop, "IK Y Maximum", "Maximum angles for IK Limit"); - RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_update"); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_IK_update"); prop= RNA_def_property(srna, "ik_min_z", PROP_FLOAT, PROP_ANGLE); RNA_def_property_float_sdna(prop, NULL, "limitmin[2]"); RNA_def_property_range(prop, -180.0f, 0.0f); RNA_def_property_ui_text(prop, "IK Z Minimum", "Minimum angles for IK Limit"); - RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_update"); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_IK_update"); prop= RNA_def_property(srna, "ik_max_z", PROP_FLOAT, PROP_ANGLE); RNA_def_property_float_sdna(prop, NULL, "limitmax[2]"); RNA_def_property_range(prop, 0.0f, 180.0f); RNA_def_property_ui_text(prop, "IK Z Maximum", "Maximum angles for IK Limit"); - RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_update"); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_IK_update"); prop= RNA_def_property(srna, "ik_stiffness_x", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "stiffness[0]"); RNA_def_property_range(prop, 0.0f, 0.99f); RNA_def_property_ui_text(prop, "IK X Stiffness", "IK stiffness around the X axis."); - RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_update"); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_IK_update"); prop= RNA_def_property(srna, "ik_stiffness_y", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "stiffness[1]"); RNA_def_property_range(prop, 0.0f, 0.99f); RNA_def_property_ui_text(prop, "IK Y Stiffness", "IK stiffness around the Y axis."); - RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_update"); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_IK_update"); prop= RNA_def_property(srna, "ik_stiffness_z", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "stiffness[2]"); RNA_def_property_range(prop, 0.0f, 0.99f); RNA_def_property_ui_text(prop, "IK Z Stiffness", "IK stiffness around the Z axis."); - RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_update"); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_IK_update"); prop= RNA_def_property(srna, "ik_stretch", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "ikstretch"); RNA_def_property_range(prop, 0.0f,1.0f); RNA_def_property_ui_text(prop, "IK Stretch", "Allow scaling of the bone for IK."); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_IK_update"); + + prop= RNA_def_property(srna, "ik_rot_weight", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "ikrotweight"); + RNA_def_property_range(prop, 0.0f,1.0f); + RNA_def_property_ui_text(prop, "IK Rot Weight", "Weight of rotation constraint for IK."); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_update"); + + prop= RNA_def_property(srna, "ik_lin_weight", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "iklinweight"); + RNA_def_property_range(prop, 0.0f,1.0f); + RNA_def_property_ui_text(prop, "IK Lin Weight", "Weight of scale constraint for IK."); RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_update"); /* custom bone shapes */ @@ -727,6 +868,113 @@ static void rna_def_pose_channel(BlenderRNA *brna) RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_update"); } +static void rna_def_pose_itasc(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna= RNA_def_struct(brna, "Itasc", "IKParam"); + RNA_def_struct_sdna(srna, "bItasc"); + RNA_def_struct_ui_text(srna, "bItasc", "Parameters for the iTaSC IK solver."); + + prop= RNA_def_property(srna, "precision", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "precision"); + RNA_def_property_range(prop, 0.0f,0.1f); + RNA_def_property_ui_text(prop, "Precision", "Precision of convergence in case of reiteration."); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Itasc_update"); + + prop= RNA_def_property(srna, "num_iter", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "numiter"); + RNA_def_property_range(prop, 1.f,1000.f); + RNA_def_property_ui_text(prop, "Iterations", "Maximum number of iterations for convergence in case of reiteration."); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Itasc_update"); + + prop= RNA_def_property(srna, "num_step", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "numstep"); + RNA_def_property_range(prop, 1.f, 50.f); + RNA_def_property_ui_text(prop, "Num steps", "Divides the frame interval into this many steps."); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Itasc_update"); + + prop= RNA_def_property(srna, "simulation", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", ITASC_SIMULATION); + RNA_def_property_ui_text(prop, "Simulation", "Simulation mode: solver is statefull, runs in real-time context and ignores actions and non-IK constraints (i.e. solver is in full charge of the IK chain)."); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Itasc_update_rebuild"); + + prop= RNA_def_property(srna, "initial_reiteration", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", ITASC_INITIAL_REITERATION); + RNA_def_property_ui_text(prop, "Initial Reiteration", "Allow reiteration for initial frame."); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Itasc_update"); + + prop= RNA_def_property(srna, "reiteration", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", ITASC_REITERATION); + RNA_def_property_ui_text(prop, "Reiteration", "Allow reiteration for all frames."); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Itasc_update"); + + prop= RNA_def_property(srna, "auto_step", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", ITASC_AUTO_STEP); + RNA_def_property_ui_text(prop, "Auto step", "Automatically determine the optimal number of steps for best performance/accurary trade off."); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Itasc_update"); + + prop= RNA_def_property(srna, "min_step", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "minstep"); + RNA_def_property_range(prop, 0.0f,0.1f); + RNA_def_property_ui_text(prop, "Min step", "Lower bound for timestep in second in case of automatic substeps."); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Itasc_update"); + + prop= RNA_def_property(srna, "max_step", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "maxstep"); + RNA_def_property_range(prop, 0.0f,1.0f); + RNA_def_property_ui_text(prop, "Max step", "Higher bound for timestep in second in case of automatic substeps."); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Itasc_update"); + + prop= RNA_def_property(srna, "feedback", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "feedback"); + RNA_def_property_range(prop, 0.0f,100.0f); + RNA_def_property_ui_text(prop, "Feedback", "Feedback coefficient for error correction. Average response time=1/feedback. Default=20."); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Itasc_update"); + + prop= RNA_def_property(srna, "max_velocity", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "maxvel"); + RNA_def_property_range(prop, 0.0f,100.0f); + RNA_def_property_ui_text(prop, "Max Velocity", "Maximum joint velocity in rad/s. Default=50."); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Itasc_update"); + + prop= RNA_def_property(srna, "solver", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "solver"); + RNA_def_property_enum_items(prop, prop_solver_items); + RNA_def_property_ui_text(prop, "Solver", "Solving method selection: Automatic damping or manual damping"); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Itasc_update_rebuild"); + + prop= RNA_def_property(srna, "dampmax", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "dampmax"); + RNA_def_property_range(prop, 0.0f,1.0f); + RNA_def_property_ui_text(prop, "Damp", "Maximum damping coefficient when singular value is nearly 0. Higher values=more stability, less reactivity. Default=0.5"); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Itasc_update"); + + prop= RNA_def_property(srna, "dampeps", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "dampeps"); + RNA_def_property_range(prop, 0.0f,1.0f); + RNA_def_property_ui_text(prop, "Epsilon", "Singular value under which damping is progressively applied. Higher values=more stability, less reactivity. Default=0.1"); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Itasc_update"); +} + +static void rna_def_pose_ikparam(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna= RNA_def_struct(brna, "IKParam", NULL); + RNA_def_struct_sdna(srna, "bIKParam"); + RNA_def_struct_ui_text(srna, "IKParam", "Base type for IK solver parameters."); + RNA_def_struct_refine_func(srna, "rna_IKParam_refine"); + + prop= RNA_def_property(srna, "ik_solver", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "iksolver"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_enum_items(prop, prop_iksolver_items); + RNA_def_property_ui_text(prop, "IK Solver", "IK solver for which these parameters are defined, 0 for Legacy, 1 for iTaSC."); +} + static void rna_def_pose(BlenderRNA *brna) { StructRNA *srna; @@ -762,6 +1010,19 @@ static void rna_def_pose(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Active Bone Group Index", "Active index in bone groups array."); RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_update"); + prop= RNA_def_property(srna, "ik_solver", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "iksolver"); + RNA_def_property_enum_funcs(prop, NULL, "rna_Pose_ik_solver_set", NULL); + RNA_def_property_enum_items(prop, prop_iksolver_items); + RNA_def_property_ui_text(prop, "IK Solver", "Selection of IK solver for IK chain, current choice is 0 for Legacy, 1 for iTaSC."); + RNA_def_property_update(prop, NC_OBJECT|ND_POSE, "rna_Pose_ik_solver_update"); + + prop= RNA_def_property(srna, "ik_param", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "IKParam"); + RNA_def_property_pointer_funcs(prop, "rna_Pose_ikparam_get", NULL, "rna_Pose_ikparam_typef"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "IK Param", "Parameters for IK solver."); + /* RNA_api_pose(srna); */ } @@ -769,7 +1030,8 @@ void RNA_def_pose(BlenderRNA *brna) { rna_def_pose(brna); rna_def_pose_channel(brna); - + rna_def_pose_ikparam(brna); + rna_def_pose_itasc(brna); rna_def_bone_group(brna); } diff --git a/source/blender/makesrna/intern/rna_sensor.c b/source/blender/makesrna/intern/rna_sensor.c index a5d76fdb039..1003af6d4d1 100644 --- a/source/blender/makesrna/intern/rna_sensor.c +++ b/source/blender/makesrna/intern/rna_sensor.c @@ -48,6 +48,8 @@ static StructRNA* rna_Sensor_refine(struct PointerRNA *ptr) return &RNA_KeyboardSensor; case SENS_PROPERTY: return &RNA_PropertySensor; + case SENS_ARMATURE: + return &RNA_ArmatureSensor; case SENS_MOUSE: return &RNA_MouseSensor; case SENS_COLLISION: @@ -92,6 +94,7 @@ static void rna_def_sensor(BlenderRNA *brna) {SENS_JOYSTICK, "JOYSTICK", 0, "joystick", ""}, {SENS_ACTUATOR, "ACTUATOR", 0, "Actuator", ""}, {SENS_DELAY, "DELAY", 0, "Delay", ""}, + {SENS_ARMATURE, "ARMATURE", 0, "Armature", ""}, {0, NULL, 0, NULL, NULL}}; srna= RNA_def_struct(brna, "Sensor", NULL); @@ -278,6 +281,40 @@ static void rna_def_property_sensor(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Maximum Value", "Specify maximum value in Interval type."); } +static void rna_def_armature_sensor(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + static EnumPropertyItem prop_type_items[] ={ + {SENS_ARM_STATE_CHANGED, "STATECHG", 0, "State Changed", ""}, + {SENS_ARM_LIN_ERROR_BELOW, "LINERRORBELOW", 0, "Lin error below", ""}, + {SENS_ARM_LIN_ERROR_ABOVE, "LINERRORABOVE", 0, "Lin error above", ""}, + {SENS_ARM_ROT_ERROR_BELOW, "ROTERRORBELOW", 0, "Rot error below", ""}, + {SENS_ARM_ROT_ERROR_ABOVE, "ROTERRORBELOW", 0, "Rot error above", ""}, + {0, NULL, 0, NULL, NULL}}; + + srna= RNA_def_struct(brna, "ArmatureSensor", "Sensor"); + RNA_def_struct_ui_text(srna, "Armature Sensor", "Sensor to detect values and changes in values of IK solver."); + RNA_def_struct_sdna_from(srna, "bArmatureSensor", "data"); + + prop= RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "type"); + RNA_def_property_enum_items(prop, prop_type_items); + RNA_def_property_ui_text(prop, "Test Type", "Type of value and test."); + + prop= RNA_def_property(srna, "channel_name", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "posechannel"); + RNA_def_property_ui_text(prop, "Bone name", "Identify the bone to check value from"); + + prop= RNA_def_property(srna, "constraint_name", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "constraint"); + RNA_def_property_ui_text(prop, "Constraint name", "Identify the bone constraint to check value from."); + + prop= RNA_def_property(srna, "value", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "value"); + RNA_def_property_ui_text(prop, "Compare Value", "Specify value to be used in comparison."); +} + static void rna_def_actuator_sensor(BlenderRNA *brna) { StructRNA *srna; @@ -531,6 +568,7 @@ void RNA_def_sensor(BlenderRNA *brna) rna_def_touch_sensor(brna); rna_def_keyboard_sensor(brna); rna_def_property_sensor(brna); + rna_def_armature_sensor(brna); rna_def_actuator_sensor(brna); rna_def_delay_sensor(brna); rna_def_collision_sensor(brna); |