Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender')
-rw-r--r--source/blender/CMakeLists.txt1
-rw-r--r--source/blender/Makefile2
-rw-r--r--source/blender/SConscript1
-rw-r--r--source/blender/blenkernel/BKE_action.h23
-rw-r--r--source/blender/blenkernel/BKE_armature.h6
-rw-r--r--source/blender/blenkernel/BKE_constraint.h6
-rw-r--r--source/blender/blenkernel/CMakeLists.txt2
-rw-r--r--source/blender/blenkernel/SConscript2
-rw-r--r--source/blender/blenkernel/intern/Makefile1
-rw-r--r--source/blender/blenkernel/intern/action.c185
-rw-r--r--source/blender/blenkernel/intern/armature.c486
-rw-r--r--source/blender/blenkernel/intern/constraint.c3
-rw-r--r--source/blender/blenkernel/intern/pointcache.c4
-rw-r--r--source/blender/blenkernel/intern/sca.c18
-rw-r--r--source/blender/blenlib/BLI_ghash.h8
-rw-r--r--source/blender/blenloader/intern/readfile.c27
-rw-r--r--source/blender/blenloader/intern/writefile.c14
-rw-r--r--source/blender/editors/CMakeLists.txt1
-rw-r--r--source/blender/editors/armature/editarmature.c4
-rw-r--r--source/blender/editors/armature/poseobject.c2
-rw-r--r--source/blender/editors/include/ED_object.h2
-rw-r--r--source/blender/editors/interface/interface_templates.c16
-rw-r--r--source/blender/editors/object/Makefile1
-rw-r--r--source/blender/editors/object/SConscript2
-rw-r--r--source/blender/editors/object/object_constraint.c28
-rw-r--r--source/blender/editors/space_logic/logic_window.c150
-rw-r--r--source/blender/ikplugin/BIK_api.h93
-rw-r--r--source/blender/ikplugin/CMakeLists.txt35
-rw-r--r--source/blender/ikplugin/Makefile31
-rw-r--r--source/blender/ikplugin/SConscript9
-rw-r--r--source/blender/ikplugin/intern/Makefile49
-rw-r--r--source/blender/ikplugin/intern/ikplugin_api.c140
-rw-r--r--source/blender/ikplugin/intern/ikplugin_api.h60
-rw-r--r--source/blender/ikplugin/intern/iksolver_plugin.c527
-rw-r--r--source/blender/ikplugin/intern/iksolver_plugin.h47
-rw-r--r--source/blender/ikplugin/intern/itasc_plugin.cpp1786
-rw-r--r--source/blender/ikplugin/intern/itasc_plugin.h52
-rw-r--r--source/blender/makesdna/DNA_action_types.h63
-rw-r--r--source/blender/makesdna/DNA_actuator_types.h21
-rw-r--r--source/blender/makesdna/DNA_constraint_types.h47
-rw-r--r--source/blender/makesdna/DNA_sensor_types.h17
-rw-r--r--source/blender/makesrna/RNA_access.h1
-rw-r--r--source/blender/makesrna/SConscript2
-rw-r--r--source/blender/makesrna/intern/CMakeLists.txt2
-rw-r--r--source/blender/makesrna/intern/Makefile1
-rw-r--r--source/blender/makesrna/intern/SConscript2
-rw-r--r--source/blender/makesrna/intern/rna_actuator.c1
-rw-r--r--source/blender/makesrna/intern/rna_constraint.c94
-rw-r--r--source/blender/makesrna/intern/rna_pose.c306
-rw-r--r--source/blender/makesrna/intern/rna_sensor.c38
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);