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
path: root/source
diff options
context:
space:
mode:
authorBenoit Bolsee <benoit.bolsee@online.be>2009-09-25 01:22:24 +0400
committerBenoit Bolsee <benoit.bolsee@online.be>2009-09-25 01:22:24 +0400
commit1483fafd1372a3d3e025d08634e798adb7da512f (patch)
tree9191765749e29866339f4c31d892603f5f8b334d /source
parentc995c605f640d8d688e6e58e0fe247ca83f91696 (diff)
parent222fe6b1a5d49f67177cbb762f55a0e482145f5d (diff)
Merge of itasc branch. Project files, scons and cmake should be working. Makefile updated but not tested. Comes with Eigen2 2.0.6 C++ matrix library.
Diffstat (limited to 'source')
-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
-rw-r--r--source/gameengine/Converter/BL_ActionActuator.cpp6
-rw-r--r--source/gameengine/Converter/BL_ActionActuator.h2
-rw-r--r--source/gameengine/Converter/BL_ArmatureActuator.cpp265
-rw-r--r--source/gameengine/Converter/BL_ArmatureActuator.h89
-rw-r--r--source/gameengine/Converter/BL_ArmatureChannel.cpp461
-rw-r--r--source/gameengine/Converter/BL_ArmatureChannel.h91
-rw-r--r--source/gameengine/Converter/BL_ArmatureConstraint.cpp447
-rw-r--r--source/gameengine/Converter/BL_ArmatureConstraint.h117
-rw-r--r--source/gameengine/Converter/BL_ArmatureObject.cpp492
-rw-r--r--source/gameengine/Converter/BL_ArmatureObject.h48
-rw-r--r--source/gameengine/Converter/BL_BlenderDataConversion.cpp8
-rw-r--r--source/gameengine/Converter/BL_ShapeActionActuator.h2
-rw-r--r--source/gameengine/Converter/CMakeLists.txt1
-rw-r--r--source/gameengine/Converter/KX_ConvertActuators.cpp10
-rw-r--r--source/gameengine/Converter/KX_ConvertSensors.cpp26
-rw-r--r--source/gameengine/Converter/Makefile1
-rw-r--r--source/gameengine/Converter/SConscript1
-rw-r--r--source/gameengine/Expressions/CMakeLists.txt1
-rw-r--r--source/gameengine/Expressions/PyObjectPlus.cpp380
-rw-r--r--source/gameengine/Expressions/PyObjectPlus.h192
-rw-r--r--source/gameengine/Expressions/SConscript2
-rw-r--r--source/gameengine/GameLogic/SCA_2DFilterActuator.cpp2
-rw-r--r--source/gameengine/GameLogic/SCA_BasicEventManager.cpp62
-rw-r--r--source/gameengine/GameLogic/SCA_BasicEventManager.h59
-rw-r--r--source/gameengine/GameLogic/SCA_EventManager.h4
-rw-r--r--source/gameengine/GameLogic/SCA_IActuator.cpp3
-rw-r--r--source/gameengine/GameLogic/SCA_IActuator.h32
-rw-r--r--source/gameengine/GameLogic/SCA_IObject.cpp29
-rw-r--r--source/gameengine/GameLogic/SCA_IObject.h14
-rw-r--r--source/gameengine/GameLogic/SCA_PropertyActuator.cpp2
-rw-r--r--source/gameengine/GameLogic/SCA_RandomActuator.cpp2
-rw-r--r--source/gameengine/Ketsji/KXNetwork/KX_NetworkMessageActuator.cpp2
-rw-r--r--source/gameengine/Ketsji/KX_ArmatureSensor.cpp205
-rw-r--r--source/gameengine/Ketsji/KX_ArmatureSensor.h85
-rw-r--r--source/gameengine/Ketsji/KX_CameraActuator.cpp2
-rw-r--r--source/gameengine/Ketsji/KX_ConstraintActuator.cpp2
-rw-r--r--source/gameengine/Ketsji/KX_GameActuator.cpp2
-rw-r--r--source/gameengine/Ketsji/KX_GameObject.cpp17
-rw-r--r--source/gameengine/Ketsji/KX_GameObject.h9
-rw-r--r--source/gameengine/Ketsji/KX_IpoActuator.cpp2
-rw-r--r--source/gameengine/Ketsji/KX_KetsjiEngine.cpp5
-rw-r--r--source/gameengine/Ketsji/KX_KetsjiEngine.h4
-rw-r--r--source/gameengine/Ketsji/KX_ObjectActuator.cpp2
-rw-r--r--source/gameengine/Ketsji/KX_ParentActuator.cpp2
-rw-r--r--source/gameengine/Ketsji/KX_PythonInit.cpp49
-rw-r--r--source/gameengine/Ketsji/KX_PythonInitTypes.cpp63
-rw-r--r--source/gameengine/Ketsji/KX_PythonSeq.cpp36
-rw-r--r--source/gameengine/Ketsji/KX_PythonSeq.h4
-rw-r--r--source/gameengine/Ketsji/KX_SCA_AddObjectActuator.cpp2
-rw-r--r--source/gameengine/Ketsji/KX_SCA_DynamicActuator.cpp2
-rw-r--r--source/gameengine/Ketsji/KX_SCA_EndObjectActuator.cpp2
-rw-r--r--source/gameengine/Ketsji/KX_SCA_ReplaceMeshActuator.cpp2
-rw-r--r--source/gameengine/Ketsji/KX_Scene.cpp27
-rw-r--r--source/gameengine/Ketsji/KX_SceneActuator.cpp2
-rw-r--r--source/gameengine/Ketsji/KX_SoundActuator.cpp2
-rw-r--r--source/gameengine/Ketsji/KX_StateActuator.cpp2
-rw-r--r--source/gameengine/Ketsji/KX_TrackToActuator.cpp2
-rw-r--r--source/gameengine/Ketsji/KX_VisibilityActuator.cpp2
-rw-r--r--source/gameengine/PyDoc/GameTypes.py334
-rw-r--r--source/gameengine/SceneGraph/SG_DList.h79
110 files changed, 7295 insertions, 924 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);
diff --git a/source/gameengine/Converter/BL_ActionActuator.cpp b/source/gameengine/Converter/BL_ActionActuator.cpp
index ca4290703e1..2f0e3e9fa65 100644
--- a/source/gameengine/Converter/BL_ActionActuator.cpp
+++ b/source/gameengine/Converter/BL_ActionActuator.cpp
@@ -526,12 +526,11 @@ KX_PYMETHODDEF_DOC(BL_ActionActuator, setChannel,
mat.setValue((const float *)matrix);
BL_ArmatureObject *obj = (BL_ArmatureObject*)GetParent();
- obj->GetPose(&m_pose); /* Get the underlying pose from the armature */
if (!m_userpose) {
if(!m_pose)
obj->GetPose(&m_pose); /* Get the underlying pose from the armature */
- game_copy_pose(&m_userpose, m_pose);
+ game_copy_pose(&m_userpose, m_pose, 0);
}
// pchan= verify_pose_channel(m_userpose, string); // adds the channel if its not there.
pchan= get_pose_channel(m_userpose, string); // adds the channel if its not there.
@@ -554,7 +553,7 @@ KX_PYMETHODDEF_DOC(BL_ActionActuator, setChannel,
if (!m_userpose) {
if(!m_pose)
obj->GetPose(&m_pose); /* Get the underlying pose from the armature */
- game_copy_pose(&m_userpose, m_pose);
+ game_copy_pose(&m_userpose, m_pose, 0);
}
// pchan= verify_pose_channel(m_userpose, string);
pchan= get_pose_channel(m_userpose, string); // adds the channel if its not there.
@@ -572,7 +571,6 @@ KX_PYMETHODDEF_DOC(BL_ActionActuator, setChannel,
return NULL;
}
- pchan->flag |= POSE_ROT|POSE_LOC|POSE_SIZE;
Py_RETURN_NONE;
}
diff --git a/source/gameengine/Converter/BL_ActionActuator.h b/source/gameengine/Converter/BL_ActionActuator.h
index a6b4c4a055d..2e0e38b4cc3 100644
--- a/source/gameengine/Converter/BL_ActionActuator.h
+++ b/source/gameengine/Converter/BL_ActionActuator.h
@@ -50,7 +50,7 @@ public:
short priority,
short end_reset,
float stride)
- : SCA_IActuator(gameobj),
+ : SCA_IActuator(gameobj, KX_ACT_ACTION),
m_lastpos(0, 0, 0),
m_blendframe(0),
diff --git a/source/gameengine/Converter/BL_ArmatureActuator.cpp b/source/gameengine/Converter/BL_ArmatureActuator.cpp
new file mode 100644
index 00000000000..741f1cf0553
--- /dev/null
+++ b/source/gameengine/Converter/BL_ArmatureActuator.cpp
@@ -0,0 +1,265 @@
+/**
+ * $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.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "DNA_action_types.h"
+#include "DNA_constraint_types.h"
+#include "DNA_actuator_types.h"
+#include "BKE_constraint.h"
+#include "BLI_arithb.h"
+#include "BL_ArmatureActuator.h"
+#include "BL_ArmatureObject.h"
+
+/**
+ * This class is the conversion of the Pose channel constraint.
+ * It makes a link between the pose constraint and the KX scene.
+ * The main purpose is to give access to the constraint target
+ * to link it to a game object.
+ * It also allows to activate/deactivate constraints during the game.
+ * Later it will also be possible to create constraint on the fly
+ */
+
+BL_ArmatureActuator::BL_ArmatureActuator(SCA_IObject* obj,
+ int type,
+ const char *posechannel,
+ const char *constraintname,
+ KX_GameObject* targetobj,
+ KX_GameObject* subtargetobj,
+ float weight) :
+ SCA_IActuator(obj, KX_ACT_ARMATURE),
+ m_constraint(NULL),
+ m_gametarget(targetobj),
+ m_gamesubtarget(subtargetobj),
+ m_posechannel(posechannel),
+ m_constraintname(constraintname),
+ m_weight(weight),
+ m_type(type)
+{
+ if (m_gametarget)
+ m_gametarget->RegisterActuator(this);
+ if (m_gamesubtarget)
+ m_gamesubtarget->RegisterActuator(this);
+ FindConstraint();
+}
+
+BL_ArmatureActuator::~BL_ArmatureActuator()
+{
+ if (m_gametarget)
+ m_gametarget->UnregisterActuator(this);
+ if (m_gamesubtarget)
+ m_gamesubtarget->UnregisterActuator(this);
+}
+
+void BL_ArmatureActuator::ProcessReplica()
+{
+ // the replica is tracking the same object => register it (this may be changed in Relnk())
+ if (m_gametarget)
+ m_gametarget->RegisterActuator(this);
+ if (m_gamesubtarget)
+ m_gamesubtarget->UnregisterActuator(this);
+ SCA_IActuator::ProcessReplica();
+}
+
+void BL_ArmatureActuator::ReParent(SCA_IObject* parent)
+{
+ SCA_IActuator::ReParent(parent);
+ // must remap the constraint
+ FindConstraint();
+}
+
+bool BL_ArmatureActuator::UnlinkObject(SCA_IObject* clientobj)
+{
+ bool res=false;
+ if (clientobj == m_gametarget)
+ {
+ // this object is being deleted, we cannot continue to track it.
+ m_gametarget = NULL;
+ res = true;
+ }
+ if (clientobj == m_gamesubtarget)
+ {
+ // this object is being deleted, we cannot continue to track it.
+ m_gamesubtarget = NULL;
+ res = true;
+ }
+ return res;
+}
+
+void BL_ArmatureActuator::Relink(GEN_Map<GEN_HashedPtr, void*> *obj_map)
+{
+ void **h_obj = (*obj_map)[m_gametarget];
+ if (h_obj) {
+ if (m_gametarget)
+ m_gametarget->UnregisterActuator(this);
+ m_gametarget = (KX_GameObject*)(*h_obj);
+ m_gametarget->RegisterActuator(this);
+ }
+ h_obj = (*obj_map)[m_gamesubtarget];
+ if (h_obj) {
+ if (m_gamesubtarget)
+ m_gamesubtarget->UnregisterActuator(this);
+ m_gamesubtarget = (KX_GameObject*)(*h_obj);
+ m_gamesubtarget->RegisterActuator(this);
+ }
+}
+
+void BL_ArmatureActuator::FindConstraint()
+{
+ m_constraint = NULL;
+
+ if (m_gameobj->GetGameObjectType() == SCA_IObject::OBJ_ARMATURE) {
+ BL_ArmatureObject* armobj = (BL_ArmatureObject*)m_gameobj;
+ m_constraint = armobj->GetConstraint(m_posechannel, m_constraintname);
+ }
+}
+
+bool BL_ArmatureActuator::Update(double curtime, bool frame)
+{
+ // the only role of this actuator is to ensure that the armature pose will be evaluated
+ bool result = false;
+ bool bNegativeEvent = IsNegativeEvent();
+ RemoveAllEvents();
+
+ if (!bNegativeEvent) {
+ BL_ArmatureObject *obj = (BL_ArmatureObject*)GetParent();
+ switch (m_type) {
+ case ACT_ARM_RUN:
+ result = true;
+ obj->SetActiveAction(NULL, 0, curtime);
+ break;
+ case ACT_ARM_ENABLE:
+ if (m_constraint)
+ m_constraint->ClrConstraintFlag(CONSTRAINT_OFF);
+ break;
+ case ACT_ARM_DISABLE:
+ if (m_constraint)
+ m_constraint->SetConstraintFlag(CONSTRAINT_OFF);
+ break;
+ case ACT_ARM_SETTARGET:
+ if (m_constraint) {
+ m_constraint->SetTarget(m_gametarget);
+ m_constraint->SetSubtarget(m_gamesubtarget);
+ }
+ break;
+ case ACT_ARM_SETWEIGHT:
+ if (m_constraint)
+ m_constraint->SetWeight(m_weight);
+ break;
+ }
+ }
+ return result;
+}
+
+/* ------------------------------------------------------------------------- */
+/* Python Integration Hooks */
+/* ------------------------------------------------------------------------- */
+
+PyTypeObject BL_ArmatureActuator::Type = {
+#if (PY_VERSION_HEX >= 0x02060000)
+ PyVarObject_HEAD_INIT(NULL, 0)
+#else
+ /* python 2.5 and below */
+ PyObject_HEAD_INIT( NULL ) /* required py macro */
+ 0, /* ob_size */
+#endif
+ "BL_ArmatureActuator",
+ sizeof(PyObjectPlus_Proxy),
+ 0,
+ py_base_dealloc,
+ 0,
+ 0,
+ 0,
+ 0,
+ py_base_repr,
+ 0,0,0,0,0,0,0,0,0,
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ 0,0,0,0,0,0,0,
+ Methods,
+ 0,
+ 0,
+ &SCA_IActuator::Type,
+ 0,0,0,0,0,0,
+ py_base_new
+};
+
+
+PyMethodDef BL_ArmatureActuator::Methods[] = {
+ {NULL,NULL} //Sentinel
+};
+
+PyAttributeDef BL_ArmatureActuator::Attributes[] = {
+ KX_PYATTRIBUTE_RO_FUNCTION("constraint", BL_ArmatureActuator, pyattr_get_constraint),
+ KX_PYATTRIBUTE_RW_FUNCTION("target", BL_ArmatureActuator, pyattr_get_object, pyattr_set_object),
+ KX_PYATTRIBUTE_RW_FUNCTION("subtarget", BL_ArmatureActuator, pyattr_get_object, pyattr_set_object),
+ KX_PYATTRIBUTE_FLOAT_RW("weight",0.0f,1.0f,BL_ArmatureActuator,m_weight),
+ KX_PYATTRIBUTE_INT_RW("type",0,ACT_ARM_MAXTYPE,false,BL_ArmatureActuator,m_type),
+ { NULL } //Sentinel
+};
+
+PyObject* BL_ArmatureActuator::pyattr_get_object(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
+{
+ BL_ArmatureActuator* actuator = static_cast<BL_ArmatureActuator*>(self);
+ KX_GameObject *target = (!strcmp(attrdef->m_name, "target")) ? actuator->m_gametarget : actuator->m_gamesubtarget;
+ if (!target)
+ Py_RETURN_NONE;
+ else
+ return target->GetProxy();
+}
+
+int BL_ArmatureActuator::pyattr_set_object(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
+{
+ BL_ArmatureActuator* actuator = static_cast<BL_ArmatureActuator*>(self);
+ KX_GameObject* &target = (!strcmp(attrdef->m_name, "target")) ? actuator->m_gametarget : actuator->m_gamesubtarget;
+ KX_GameObject *gameobj;
+
+ if (!ConvertPythonToGameObject(value, &gameobj, true, "actuator.object = value: BL_ArmatureActuator"))
+ return PY_SET_ATTR_FAIL; // ConvertPythonToGameObject sets the error
+
+ if (target != NULL)
+ target->UnregisterActuator(actuator);
+
+ target = gameobj;
+
+ if (target)
+ target->RegisterActuator(actuator);
+
+ return PY_SET_ATTR_SUCCESS;
+}
+
+PyObject* BL_ArmatureActuator::pyattr_get_constraint(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
+{
+ BL_ArmatureActuator* actuator = static_cast<BL_ArmatureActuator*>(self);
+ BL_ArmatureConstraint* constraint = actuator->m_constraint;
+ if (!constraint)
+ Py_RETURN_NONE;
+ else
+ return constraint->GetProxy();
+}
+
+
+
diff --git a/source/gameengine/Converter/BL_ArmatureActuator.h b/source/gameengine/Converter/BL_ArmatureActuator.h
new file mode 100644
index 00000000000..5a7752b8169
--- /dev/null
+++ b/source/gameengine/Converter/BL_ArmatureActuator.h
@@ -0,0 +1,89 @@
+/**
+ * $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.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+#ifndef BL_ARMATUREACTUATOR
+#define BL_ARMATUREACTUATOR
+
+#include "SCA_IActuator.h"
+#include "BL_ArmatureConstraint.h"
+
+/**
+ * This class is the conversion of the Pose channel constraint.
+ * It makes a link between the pose constraint and the KX scene.
+ * The main purpose is to give access to the constraint target
+ * to link it to a game object.
+ * It also allows to activate/deactivate constraints during the game.
+ * Later it will also be possible to create constraint on the fly
+ */
+
+class BL_ArmatureActuator : public SCA_IActuator
+{
+ Py_Header;
+public:
+ BL_ArmatureActuator(SCA_IObject* gameobj,
+ int type,
+ const char *posechannel,
+ const char *constraintname,
+ KX_GameObject* targetobj,
+ KX_GameObject* subtargetobj,
+ float weight);
+
+ virtual ~BL_ArmatureActuator();
+
+ virtual CValue* GetReplica() {
+ BL_ArmatureActuator* replica = new BL_ArmatureActuator(*this);
+ replica->ProcessReplica();
+ return replica;
+ };
+ virtual void ProcessReplica();
+ virtual bool UnlinkObject(SCA_IObject* clientobj);
+ virtual void Relink(GEN_Map<GEN_HashedPtr, void*> *obj_map);
+ virtual bool Update(double curtime, bool frame);
+ virtual void ReParent(SCA_IObject* parent);
+
+ /* These are used to get and set m_target */
+ static PyObject* pyattr_get_constraint(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef);
+ static PyObject* pyattr_get_object(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef);
+ static int pyattr_set_object(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value);
+
+private:
+ // identify the constraint that this actuator controls
+ void FindConstraint();
+
+ BL_ArmatureConstraint* m_constraint;
+ KX_GameObject* m_gametarget;
+ KX_GameObject* m_gamesubtarget;
+ STR_String m_posechannel;
+ STR_String m_constraintname;
+ float m_weight;
+ int m_type;
+};
+
+#endif //BL_ARMATUREACTUATOR
+
+
diff --git a/source/gameengine/Converter/BL_ArmatureChannel.cpp b/source/gameengine/Converter/BL_ArmatureChannel.cpp
new file mode 100644
index 00000000000..c0afeb008f6
--- /dev/null
+++ b/source/gameengine/Converter/BL_ArmatureChannel.cpp
@@ -0,0 +1,461 @@
+/**
+ * $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.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "DNA_armature_types.h"
+#include "BL_ArmatureChannel.h"
+#include "BL_ArmatureObject.h"
+#include "BL_ArmatureConstraint.h"
+#include "BLI_arithb.h"
+#include "BLI_string.h"
+
+PyTypeObject BL_ArmatureChannel::Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "BL_ArmatureChannel",
+ sizeof(PyObjectPlus_Proxy),
+ 0,
+ py_base_dealloc,
+ 0,
+ 0,
+ 0,
+ 0,
+ py_base_repr,
+ 0,0,0,0,0,0,0,0,0,
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ 0,0,0,0,0,0,0,
+ Methods,
+ 0,
+ 0,
+ &CValue::Type,
+ 0,0,0,0,0,0,
+ py_base_new
+};
+
+PyObject* BL_ArmatureChannel::py_repr(void)
+{
+ return PyUnicode_FromString(m_posechannel->name);
+}
+
+PyObject *BL_ArmatureChannel::GetProxy()
+{
+ return GetProxyPlus_Ext(this, &Type, m_posechannel);
+}
+
+PyObject *BL_ArmatureChannel::NewProxy(bool py_owns)
+{
+ return NewProxyPlus_Ext(this, &Type, m_posechannel, py_owns);
+}
+
+BL_ArmatureChannel::BL_ArmatureChannel(
+ BL_ArmatureObject *armature,
+ bPoseChannel *posechannel)
+ : PyObjectPlus(), m_posechannel(posechannel), m_armature(armature)
+{
+}
+
+BL_ArmatureChannel::~BL_ArmatureChannel()
+{
+}
+
+// PYTHON
+
+PyMethodDef BL_ArmatureChannel::Methods[] = {
+ {NULL,NULL} //Sentinel
+};
+
+// order of definition of attributes, must match Attributes[] array
+#define BCA_BONE 0
+#define BCA_PARENT 1
+
+PyAttributeDef BL_ArmatureChannel::Attributes[] = {
+ // Keep these attributes in order of BCA_ defines!!! used by py_attr_getattr and py_attr_setattr
+ KX_PYATTRIBUTE_RO_FUNCTION("bone",BL_ArmatureChannel,py_attr_getattr),
+ KX_PYATTRIBUTE_RO_FUNCTION("parent",BL_ArmatureChannel,py_attr_getattr),
+
+ { NULL } //Sentinel
+};
+
+/* attributes directly taken from bPoseChannel */
+PyAttributeDef BL_ArmatureChannel::AttributesPtr[] = {
+ KX_PYATTRIBUTE_CHAR_RO("name",bPoseChannel,name),
+ KX_PYATTRIBUTE_FLAG_RO("has_ik",bPoseChannel,flag, POSE_CHAIN),
+ KX_PYATTRIBUTE_FLAG_NEGATIVE_RO("ik_dof_x",bPoseChannel,ikflag, BONE_IK_NO_XDOF),
+ KX_PYATTRIBUTE_FLAG_NEGATIVE_RO("ik_dof_y",bPoseChannel,ikflag, BONE_IK_NO_YDOF),
+ KX_PYATTRIBUTE_FLAG_NEGATIVE_RO("ik_dof_z",bPoseChannel,ikflag, BONE_IK_NO_ZDOF),
+ KX_PYATTRIBUTE_FLAG_RO("ik_limit_x",bPoseChannel,ikflag, BONE_IK_XLIMIT),
+ KX_PYATTRIBUTE_FLAG_RO("ik_limit_y",bPoseChannel,ikflag, BONE_IK_YLIMIT),
+ KX_PYATTRIBUTE_FLAG_RO("ik_limit_z",bPoseChannel,ikflag, BONE_IK_ZLIMIT),
+ KX_PYATTRIBUTE_FLAG_RO("ik_rot_control",bPoseChannel,ikflag, BONE_IK_ROTCTL),
+ KX_PYATTRIBUTE_FLAG_RO("ik_lin_control",bPoseChannel,ikflag, BONE_IK_LINCTL),
+ KX_PYATTRIBUTE_FLOAT_VECTOR_RW("location",-FLT_MAX,FLT_MAX,bPoseChannel,loc,3),
+ KX_PYATTRIBUTE_FLOAT_VECTOR_RW("scale",-FLT_MAX,FLT_MAX,bPoseChannel,size,3),
+ KX_PYATTRIBUTE_FLOAT_VECTOR_RW("rotation",-1.0f,1.0f,bPoseChannel,quat,4),
+ KX_PYATTRIBUTE_FLOAT_VECTOR_RW("euler_rotation",-10.f,10.f,bPoseChannel,eul,3),
+ KX_PYATTRIBUTE_SHORT_RW("rotation_mode",0,PCHAN_ROT_MAX-1,false,bPoseChannel,rotmode),
+ KX_PYATTRIBUTE_FLOAT_MATRIX_RO("channel_matrix",bPoseChannel,chan_mat,4),
+ KX_PYATTRIBUTE_FLOAT_MATRIX_RO("pose_matrix",bPoseChannel,pose_mat,4),
+ KX_PYATTRIBUTE_FLOAT_VECTOR_RO("pose_head",bPoseChannel,pose_head,3),
+ KX_PYATTRIBUTE_FLOAT_VECTOR_RO("pose_tail",bPoseChannel,pose_tail,3),
+ KX_PYATTRIBUTE_FLOAT_RO("ik_min_x",bPoseChannel,limitmin[0]),
+ KX_PYATTRIBUTE_FLOAT_RO("ik_max_x",bPoseChannel,limitmax[0]),
+ KX_PYATTRIBUTE_FLOAT_RO("ik_min_y",bPoseChannel,limitmin[1]),
+ KX_PYATTRIBUTE_FLOAT_RO("ik_max_y",bPoseChannel,limitmax[1]),
+ KX_PYATTRIBUTE_FLOAT_RO("ik_min_z",bPoseChannel,limitmin[2]),
+ KX_PYATTRIBUTE_FLOAT_RO("ik_max_z",bPoseChannel,limitmax[2]),
+ KX_PYATTRIBUTE_FLOAT_RO("ik_stiffness_x",bPoseChannel,stiffness[0]),
+ KX_PYATTRIBUTE_FLOAT_RO("ik_stiffness_y",bPoseChannel,stiffness[1]),
+ KX_PYATTRIBUTE_FLOAT_RO("ik_stiffness_z",bPoseChannel,stiffness[2]),
+ KX_PYATTRIBUTE_FLOAT_RO("ik_stretch",bPoseChannel,ikstretch),
+ KX_PYATTRIBUTE_FLOAT_RW("ik_rot_weight",0,1.0f,bPoseChannel,ikrotweight),
+ KX_PYATTRIBUTE_FLOAT_RW("ik_lin_weight",0,1.0f,bPoseChannel,iklinweight),
+ KX_PYATTRIBUTE_RW_FUNCTION("joint_rotation",BL_ArmatureChannel,py_attr_get_joint_rotation,py_attr_set_joint_rotation),
+ { NULL } //Sentinel
+};
+
+PyObject* BL_ArmatureChannel::py_attr_getattr(void *self_v, const struct KX_PYATTRIBUTE_DEF *attrdef)
+{
+ BL_ArmatureChannel* self= static_cast<BL_ArmatureChannel*>(self_v);
+ bPoseChannel* channel = self->m_posechannel;
+ int attr_order = attrdef-Attributes;
+
+ if (!channel) {
+ PyErr_SetString(PyExc_AttributeError, "channel is NULL");
+ return NULL;
+ }
+
+ switch (attr_order) {
+ case BCA_BONE:
+ // bones are standalone proxy
+ return NewProxyPlus_Ext(NULL,&BL_ArmatureBone::Type,channel->bone,false);
+ case BCA_PARENT:
+ {
+ BL_ArmatureChannel* parent = self->m_armature->GetChannel(channel->parent);
+ if (parent)
+ return parent->GetProxy();
+ else
+ Py_RETURN_NONE;
+ }
+ }
+ PyErr_SetString(PyExc_AttributeError, "channel unknown attribute");
+ return NULL;
+}
+
+int BL_ArmatureChannel::py_attr_setattr(void *self_v, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
+{
+ BL_ArmatureChannel* self= static_cast<BL_ArmatureChannel*>(self_v);
+ bPoseChannel* channel = self->m_posechannel;
+ int attr_order = attrdef-Attributes;
+
+ int ival;
+ double dval;
+ char* sval;
+ KX_GameObject *oval;
+
+ if (!channel) {
+ PyErr_SetString(PyExc_AttributeError, "channel is NULL");
+ return PY_SET_ATTR_FAIL;
+ }
+
+ switch (attr_order) {
+ default:
+ break;
+ }
+
+ PyErr_SetString(PyExc_AttributeError, "channel unknown attribute");
+ return PY_SET_ATTR_FAIL;
+}
+
+PyObject* BL_ArmatureChannel::py_attr_get_joint_rotation(void *self_v, const struct KX_PYATTRIBUTE_DEF *attrdef)
+{
+ BL_ArmatureChannel* self= static_cast<BL_ArmatureChannel*>(self_v);
+ bPoseChannel* pchan = self->m_posechannel;
+ // decompose the pose matrix in euler rotation
+ float rest_mat[3][3];
+ float pose_mat[3][3];
+ float joint_mat[3][3];
+ float joints[3];
+ float norm;
+ double sa, ca;
+ // get rotation in armature space
+ Mat3CpyMat4(pose_mat, pchan->pose_mat);
+ Mat3Ortho(pose_mat);
+ if (pchan->parent) {
+ // bone has a parent, compute the rest pose of the bone taking actual pose of parent
+ Mat3IsMat3MulMat4(rest_mat, pchan->bone->bone_mat, pchan->parent->pose_mat);
+ Mat3Ortho(rest_mat);
+ } else {
+ // otherwise, the bone matrix in armature space is the rest pose
+ Mat3CpyMat4(rest_mat, pchan->bone->arm_mat);
+ }
+ // remove the rest pose to get the joint movement
+ Mat3Transp(rest_mat);
+ Mat3MulMat3(joint_mat, rest_mat, pose_mat);
+ joints[0] = joints[1] = joints[2] = 0.f;
+ // returns a 3 element list that gives corresponding joint
+ int flag = 0;
+ if (!(pchan->ikflag & BONE_IK_NO_XDOF))
+ flag |= 1;
+ if (!(pchan->ikflag & BONE_IK_NO_YDOF))
+ flag |= 2;
+ if (!(pchan->ikflag & BONE_IK_NO_ZDOF))
+ flag |= 4;
+ switch (flag) {
+ case 0: // fixed joint
+ break;
+ case 1: // X only
+ Mat3ToEulO(joint_mat, joints, EULER_ORDER_XYZ);
+ joints[1] = joints[2] = 0.f;
+ break;
+ case 2: // Y only
+ Mat3ToEulO(joint_mat, joints, EULER_ORDER_XYZ);
+ joints[0] = joints[2] = 0.f;
+ break;
+ case 3: // X+Y
+ Mat3ToEulO(joint_mat, joints, EULER_ORDER_ZYX);
+ joints[2] = 0.f;
+ break;
+ case 4: // Z only
+ Mat3ToEulO(joint_mat, joints, EULER_ORDER_XYZ);
+ joints[0] = joints[1] = 0.f;
+ break;
+ case 5: // X+Z
+ // decompose this as an equivalent rotation vector in X/Z plane
+ joints[0] = joint_mat[1][2];
+ joints[2] = -joint_mat[1][0];
+ norm = Normalize(joints);
+ if (norm < FLT_EPSILON) {
+ norm = (joint_mat[1][1] < 0.f) ? M_PI : 0.f;
+ } else {
+ norm = acos(joint_mat[1][1]);
+ }
+ VecMulf(joints, norm);
+ break;
+ case 6: // Y+Z
+ Mat3ToEulO(joint_mat, joints, EULER_ORDER_XYZ);
+ joints[0] = 0.f;
+ break;
+ case 7: // X+Y+Z
+ // equivalent axis
+ joints[0] = (joint_mat[1][2]-joint_mat[2][1])*0.5f;
+ joints[1] = (joint_mat[2][0]-joint_mat[0][2])*0.5f;
+ joints[2] = (joint_mat[0][1]-joint_mat[1][0])*0.5f;
+ sa = VecLength(joints);
+ ca = (joint_mat[0][0]+joint_mat[1][1]+joint_mat[1][1]-1.0f)*0.5f;
+ if (sa > FLT_EPSILON) {
+ norm = atan2(sa,ca)/sa;
+ } else {
+ if (ca < 0.0) {
+ norm = M_PI;
+ VecMulf(joints,0.f);
+ if (joint_mat[0][0] > 0.f) {
+ joints[0] = 1.0f;
+ } else if (joint_mat[1][1] > 0.f) {
+ joints[1] = 1.0f;
+ } else {
+ joints[2] = 1.0f;
+ }
+ } else {
+ norm = 0.0;
+ }
+ }
+ VecMulf(joints,norm);
+ break;
+ }
+ return newVectorObject(joints, 3, Py_NEW, NULL);
+}
+
+int BL_ArmatureChannel::py_attr_set_joint_rotation(void *self_v, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
+{
+ BL_ArmatureChannel* self= static_cast<BL_ArmatureChannel*>(self_v);
+ bPoseChannel* pchan = self->m_posechannel;
+ PyObject *item;
+ float joints[3];
+ float quat[4];
+
+ if (!PySequence_Check(value) || PySequence_Size(value) != 3) {
+ PyErr_SetString(PyExc_AttributeError, "expected a sequence of [3] floats");
+ return PY_SET_ATTR_FAIL;
+ }
+ for (int i=0; i<3; i++) {
+ item = PySequence_GetItem(value, i); /* new ref */
+ joints[i] = PyFloat_AsDouble(item);
+ Py_DECREF(item);
+ if (joints[i] == -1.0f && PyErr_Occurred()) {
+ PyErr_SetString(PyExc_AttributeError, "expected a sequence of [3] floats");
+ return PY_SET_ATTR_FAIL;
+ }
+ }
+
+ int flag = 0;
+ if (!(pchan->ikflag & BONE_IK_NO_XDOF))
+ flag |= 1;
+ if (!(pchan->ikflag & BONE_IK_NO_YDOF))
+ flag |= 2;
+ if (!(pchan->ikflag & BONE_IK_NO_ZDOF))
+ flag |= 4;
+ QuatOne(quat);
+ switch (flag) {
+ case 0: // fixed joint
+ break;
+ case 1: // X only
+ joints[1] = joints[2] = 0.f;
+ EulOToQuat(joints, EULER_ORDER_XYZ, quat);
+ break;
+ case 2: // Y only
+ joints[0] = joints[2] = 0.f;
+ EulOToQuat(joints, EULER_ORDER_XYZ, quat);
+ break;
+ case 3: // X+Y
+ joints[2] = 0.f;
+ EulOToQuat(joints, EULER_ORDER_ZYX, quat);
+ break;
+ case 4: // Z only
+ joints[0] = joints[1] = 0.f;
+ EulOToQuat(joints, EULER_ORDER_XYZ, quat);
+ break;
+ case 5: // X+Z
+ // X and Z are components of an equivalent rotation axis
+ joints[1] = 0;
+ VecRotToQuat(joints, VecLength(joints), quat);
+ break;
+ case 6: // Y+Z
+ joints[0] = 0.f;
+ EulOToQuat(joints, EULER_ORDER_XYZ, quat);
+ break;
+ case 7: // X+Y+Z
+ // equivalent axis
+ VecRotToQuat(joints, VecLength(joints), quat);
+ break;
+ }
+ if (pchan->rotmode > 0) {
+ QuatToEulO(quat, joints, pchan->rotmode);
+ VecCopyf(pchan->eul, joints);
+ } else
+ QuatCopy(pchan->quat, quat);
+ return PY_SET_ATTR_SUCCESS;
+}
+
+// *************************
+// BL_ArmatureBone
+//
+// Access to Bone structure
+PyTypeObject BL_ArmatureBone::Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "BL_ArmatureBone",
+ sizeof(PyObjectPlus_Proxy),
+ 0,
+ py_base_dealloc,
+ 0,
+ 0,
+ 0,
+ 0,
+ py_bone_repr,
+ 0,0,0,0,0,0,0,0,0,
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ 0,0,0,0,0,0,0,
+ Methods,
+ 0,
+ 0,
+ &CValue::Type,
+ 0,0,0,0,0,0,
+ py_base_new
+};
+
+// not used since this class is never instantiated
+PyObject *BL_ArmatureBone::GetProxy()
+{
+ return NULL;
+}
+PyObject *BL_ArmatureBone::NewProxy(bool py_owns)
+{
+ return NULL;
+}
+
+PyObject *BL_ArmatureBone::py_bone_repr(PyObject *self)
+{
+ Bone* bone = static_cast<Bone*>BGE_PROXY_PTR(self);
+ return PyUnicode_FromString(bone->name);
+}
+
+PyMethodDef BL_ArmatureBone::Methods[] = {
+ {NULL,NULL} //Sentinel
+};
+
+/* no attributes on C++ class since it is never instantiated */
+PyAttributeDef BL_ArmatureBone::Attributes[] = {
+ { NULL } //Sentinel
+};
+
+// attributes that work on proxy ptr (points to a Bone structure)
+PyAttributeDef BL_ArmatureBone::AttributesPtr[] = {
+ KX_PYATTRIBUTE_CHAR_RO("name",Bone,name),
+ KX_PYATTRIBUTE_FLAG_RO("connected",Bone,flag, BONE_CONNECTED),
+ KX_PYATTRIBUTE_FLAG_RO("hinge",Bone,flag, BONE_HINGE),
+ KX_PYATTRIBUTE_FLAG_NEGATIVE_RO("inherit_scale",Bone,flag, BONE_NO_SCALE),
+ KX_PYATTRIBUTE_SHORT_RO("bbone_segments",Bone,segments),
+ KX_PYATTRIBUTE_FLOAT_RO("roll",Bone,roll),
+ KX_PYATTRIBUTE_FLOAT_VECTOR_RO("head",Bone,head,3),
+ KX_PYATTRIBUTE_FLOAT_VECTOR_RO("tail",Bone,tail,3),
+ KX_PYATTRIBUTE_FLOAT_RO("length",Bone,length),
+ KX_PYATTRIBUTE_FLOAT_VECTOR_RO("arm_head",Bone,arm_head,3),
+ KX_PYATTRIBUTE_FLOAT_VECTOR_RO("arm_tail",Bone,arm_tail,3),
+ KX_PYATTRIBUTE_FLOAT_MATRIX_RO("arm_mat",Bone,arm_mat,4),
+ KX_PYATTRIBUTE_FLOAT_MATRIX_RO("bone_mat",Bone,bone_mat,4),
+ KX_PYATTRIBUTE_RO_FUNCTION("parent",BL_ArmatureBone,py_bone_get_parent),
+ KX_PYATTRIBUTE_RO_FUNCTION("children",BL_ArmatureBone,py_bone_get_parent),
+ { NULL } //Sentinel
+};
+
+PyObject *BL_ArmatureBone::py_bone_get_parent(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
+{
+ Bone* bone = reinterpret_cast<Bone*>BGE_PROXY_PTR(self);
+ if (bone->parent) {
+ // create a proxy unconnected to any GE object
+ return NewProxyPlus_Ext(NULL,&Type,bone->parent,false);
+ }
+ Py_RETURN_NONE;
+}
+
+PyObject *BL_ArmatureBone::py_bone_get_children(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
+{
+ Bone* bone = reinterpret_cast<Bone*>BGE_PROXY_PTR(self);
+ Bone* child;
+ int count = 0;
+ for (child=(Bone*)bone->childbase.first; child; child=(Bone*)child->next)
+ count++;
+
+ PyObject* childrenlist = PyList_New(count);
+
+ for (count = 0, child=(Bone*)bone->childbase.first; child; child=(Bone*)child->next, ++count)
+ PyList_SET_ITEM(childrenlist,count,NewProxyPlus_Ext(NULL,&Type,child,false));
+
+ return childrenlist;
+}
diff --git a/source/gameengine/Converter/BL_ArmatureChannel.h b/source/gameengine/Converter/BL_ArmatureChannel.h
new file mode 100644
index 00000000000..0f0f5a062e0
--- /dev/null
+++ b/source/gameengine/Converter/BL_ArmatureChannel.h
@@ -0,0 +1,91 @@
+/**
+ * $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.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+#ifndef __BL_ARMATURECHANNEL
+#define __BL_ARMATURECHANNEL
+
+#include "DNA_action_types.h"
+#include "GEN_HashedPtr.h"
+#include "GEN_Map.h"
+#include "PyObjectPlus.h"
+
+class SCA_IObject;
+class KX_GameObject;
+class BL_ArmatureObject;
+struct bConstraint;
+struct bPoseChannel;
+struct Object;
+struct bPose;
+
+class BL_ArmatureChannel : public PyObjectPlus
+{
+ // use Py_HeaderPtr since we use generic pointer in proxy
+ Py_HeaderPtr;
+
+private:
+ friend class BL_ArmatureObject;
+ struct bPoseChannel* m_posechannel;
+ BL_ArmatureObject* m_armature;
+
+public:
+ BL_ArmatureChannel(class BL_ArmatureObject *armature,
+ struct bPoseChannel *posechannel);
+ virtual ~BL_ArmatureChannel();
+
+ virtual PyObject* py_repr(void);
+
+ // Python access
+ static PyObject* py_attr_getattr(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef);
+ static int py_attr_setattr(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value);
+ static PyObject* py_attr_get_joint_rotation(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef);
+ static int py_attr_set_joint_rotation(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value);
+
+};
+
+/* this is a factory class to access bBone data field in the GE.
+ It's not supposed to be instantiated, we only need it for the Attributes and Method array.
+ The actual proxy object will be manually created using NewProxyPtr */
+class BL_ArmatureBone : public PyObjectPlus
+{
+ // use Py_HeaderPtr since we use generic pointer in proxy
+ Py_HeaderPtr;
+private:
+ // make constructor private to make sure no one tries to instantiate this class
+ BL_ArmatureBone() {}
+ virtual ~BL_ArmatureBone() {}
+
+public:
+ static PyObject *py_bone_repr(PyObject *self);
+ static PyObject *py_bone_get_parent(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef);
+ static PyObject *py_bone_get_children(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef);
+
+};
+
+
+#endif //__BL_ARMATURECHANNEL
+
diff --git a/source/gameengine/Converter/BL_ArmatureConstraint.cpp b/source/gameengine/Converter/BL_ArmatureConstraint.cpp
new file mode 100644
index 00000000000..15e12611483
--- /dev/null
+++ b/source/gameengine/Converter/BL_ArmatureConstraint.cpp
@@ -0,0 +1,447 @@
+/**
+ * $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.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "DNA_constraint_types.h"
+#include "DNA_action_types.h"
+#include "BL_ArmatureConstraint.h"
+#include "BL_ArmatureObject.h"
+#include "BLI_arithb.h"
+#include "BLI_string.h"
+
+PyTypeObject BL_ArmatureConstraint::Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "BL_ArmatureConstraint",
+ sizeof(PyObjectPlus_Proxy),
+ 0,
+ py_base_dealloc,
+ 0,
+ 0,
+ 0,
+ 0,
+ py_base_repr,
+ 0,0,0,0,0,0,0,0,0,
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ 0,0,0,0,0,0,0,
+ Methods,
+ 0,
+ 0,
+ &CValue::Type,
+ 0,0,0,0,0,0,
+ py_base_new
+};
+
+PyObject* BL_ArmatureConstraint::py_repr(void)
+{
+ return PyUnicode_FromString(m_name);
+}
+
+BL_ArmatureConstraint::BL_ArmatureConstraint(
+ BL_ArmatureObject *armature,
+ bPoseChannel *posechannel,
+ bConstraint *constraint,
+ KX_GameObject* target,
+ KX_GameObject* subtarget)
+ : PyObjectPlus(), m_armature(armature), m_constraint(constraint), m_posechannel(posechannel)
+{
+ m_target = target;
+ m_blendtarget = (target) ? target->GetBlenderObject() : NULL;
+ m_subtarget = subtarget;
+ m_blendsubtarget = (subtarget) ? subtarget->GetBlenderObject() : NULL;
+ m_pose = m_subpose = NULL;
+ if (m_blendtarget) {
+ Mat4CpyMat4(m_blendmat, m_blendtarget->obmat);
+ if (m_blendtarget->type == OB_ARMATURE)
+ m_pose = m_blendtarget->pose;
+ }
+ if (m_blendsubtarget) {
+ Mat4CpyMat4(m_blendsubmat, m_blendsubtarget->obmat);
+ if (m_blendsubtarget->type == OB_ARMATURE)
+ m_subpose = m_blendsubtarget->pose;
+ }
+ if (m_target)
+ m_target->RegisterObject(m_armature);
+ if (m_subtarget)
+ m_subtarget->RegisterObject(m_armature);
+ BLI_snprintf(m_name, sizeof(m_name), "%s:%s", m_posechannel->name, m_constraint->name);
+}
+
+BL_ArmatureConstraint::~BL_ArmatureConstraint()
+{
+ if (m_target)
+ m_target->UnregisterObject(m_armature);
+ if (m_subtarget)
+ m_subtarget->UnregisterObject(m_armature);
+}
+
+BL_ArmatureConstraint* BL_ArmatureConstraint::GetReplica() const
+{
+ BL_ArmatureConstraint* replica = new BL_ArmatureConstraint(*this);
+ replica->ProcessReplica();
+ return replica;
+}
+
+void BL_ArmatureConstraint::ReParent(BL_ArmatureObject* armature)
+{
+ m_armature = armature;
+ if (m_target)
+ m_target->RegisterObject(armature);
+ if (m_subtarget)
+ m_subtarget->RegisterObject(armature);
+ // find the corresponding constraint in the new armature object
+ if (m_constraint) {
+ bPose* newpose = armature->GetOrigPose();
+ char* constraint = m_constraint->name;
+ char* posechannel = m_posechannel->name;
+ bPoseChannel* pchan;
+ bConstraint* pcon;
+ m_constraint = NULL;
+ m_posechannel = NULL;
+ // and locate the constraint
+ for (pchan = (bPoseChannel*)newpose->chanbase.first; pchan; pchan=(bPoseChannel*)pchan->next) {
+ if (!strcmp(pchan->name, posechannel)) {
+ // now locate the constraint
+ for (pcon = (bConstraint*)pchan->constraints.first; pcon; pcon=(bConstraint*)pcon->next) {
+ if (!strcmp(pcon->name, constraint)) {
+ m_constraint = pcon;
+ m_posechannel = pchan;
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+}
+
+void BL_ArmatureConstraint::Relink(GEN_Map<GEN_HashedPtr, void*> *obj_map)
+{
+ void **h_obj = (*obj_map)[m_target];
+ if (h_obj) {
+ m_target->UnregisterObject(m_armature);
+ m_target = (KX_GameObject*)(*h_obj);
+ m_target->RegisterObject(m_armature);
+ }
+ h_obj = (*obj_map)[m_subtarget];
+ if (h_obj) {
+ m_subtarget->UnregisterObject(m_armature);
+ m_subtarget = (KX_GameObject*)(*h_obj);
+ m_subtarget->RegisterObject(m_armature);
+ }
+}
+
+bool BL_ArmatureConstraint::UnlinkObject(SCA_IObject* clientobj)
+{
+ bool res=false;
+ if (clientobj == m_target) {
+ m_target = NULL;
+ res = true;
+ }
+ if (clientobj == m_subtarget) {
+ m_subtarget = NULL;
+ res = true;
+ }
+ return res;
+}
+
+void BL_ArmatureConstraint::UpdateTarget()
+{
+ if (m_constraint && !(m_constraint->flag&CONSTRAINT_OFF) && (!m_blendtarget || m_target)) {
+ if (m_blendtarget) {
+ // external target, must be updated
+ m_target->UpdateBlenderObjectMatrix(m_blendtarget);
+ if (m_pose && m_target->GetGameObjectType() == SCA_IObject::OBJ_ARMATURE)
+ // update the pose in case a bone is specified in the constraint target
+ m_blendtarget->pose = ((BL_ArmatureObject*)m_target)->GetOrigPose();
+ }
+ if (m_blendsubtarget && m_subtarget) {
+ m_subtarget->UpdateBlenderObjectMatrix(m_blendsubtarget);
+ if (m_subpose && m_subtarget->GetGameObjectType() == SCA_IObject::OBJ_ARMATURE)
+ m_blendsubtarget->pose = ((BL_ArmatureObject*)m_target)->GetOrigPose();
+ }
+ }
+}
+
+void BL_ArmatureConstraint::RestoreTarget()
+{
+ if (m_constraint && !(m_constraint->flag&CONSTRAINT_OFF) && (!m_blendtarget || m_target)) {
+ if (m_blendtarget) {
+ Mat4CpyMat4(m_blendtarget->obmat, m_blendmat);
+ if (m_pose)
+ m_blendtarget->pose = m_pose;
+ }
+ if (m_blendsubtarget && m_subtarget) {
+ Mat4CpyMat4(m_blendsubtarget->obmat, m_blendsubmat);
+ if (m_subpose)
+ m_blendsubtarget->pose = m_subpose;
+ }
+ }
+}
+
+bool BL_ArmatureConstraint::Match(const char* posechannel, const char* constraint)
+{
+ return (!strcmp(m_posechannel->name, posechannel) && !strcmp(m_constraint->name, constraint));
+}
+
+void BL_ArmatureConstraint::SetTarget(KX_GameObject* target)
+{
+ if (m_blendtarget) {
+ if (target != m_target) {
+ m_target->UnregisterObject(m_armature);
+ m_target = target;
+ if (m_target)
+ m_target->RegisterObject(m_armature);
+ }
+ }
+
+}
+
+void BL_ArmatureConstraint::SetSubtarget(KX_GameObject* subtarget)
+{
+ if (m_blendsubtarget) {
+ if (subtarget != m_subtarget) {
+ m_subtarget->UnregisterObject(m_armature);
+ m_subtarget = subtarget;
+ if (m_subtarget)
+ m_subtarget->RegisterObject(m_armature);
+ }
+ }
+
+}
+
+// PYTHON
+
+PyMethodDef BL_ArmatureConstraint::Methods[] = {
+ {NULL,NULL} //Sentinel
+};
+
+// order of definition of attributes, must match Attributes[] array
+#define BCA_TYPE 0
+#define BCA_NAME 1
+#define BCA_ENFORCE 2
+#define BCA_HEADTAIL 3
+#define BCA_LINERROR 4
+#define BCA_ROTERROR 5
+#define BCA_TARGET 6
+#define BCA_SUBTARGET 7
+#define BCA_ACTIVE 8
+#define BCA_IKWEIGHT 9
+#define BCA_IKTYPE 10
+#define BCA_IKFLAG 11
+#define BCA_IKDIST 12
+#define BCA_IKMODE 13
+
+PyAttributeDef BL_ArmatureConstraint::Attributes[] = {
+ // Keep these attributes in order of BCA_ defines!!! used by py_attr_getattr and py_attr_setattr
+ KX_PYATTRIBUTE_RO_FUNCTION("type",BL_ArmatureConstraint,py_attr_getattr),
+ KX_PYATTRIBUTE_RO_FUNCTION("name",BL_ArmatureConstraint,py_attr_getattr),
+ KX_PYATTRIBUTE_RW_FUNCTION("enforce",BL_ArmatureConstraint,py_attr_getattr,py_attr_setattr),
+ KX_PYATTRIBUTE_RW_FUNCTION("headtail",BL_ArmatureConstraint,py_attr_getattr,py_attr_setattr),
+ KX_PYATTRIBUTE_RO_FUNCTION("lin_error",BL_ArmatureConstraint,py_attr_getattr),
+ KX_PYATTRIBUTE_RO_FUNCTION("rot_error",BL_ArmatureConstraint,py_attr_getattr),
+ KX_PYATTRIBUTE_RW_FUNCTION("target",BL_ArmatureConstraint,py_attr_getattr,py_attr_setattr),
+ KX_PYATTRIBUTE_RW_FUNCTION("subtarget",BL_ArmatureConstraint,py_attr_getattr,py_attr_setattr),
+ KX_PYATTRIBUTE_RW_FUNCTION("active",BL_ArmatureConstraint,py_attr_getattr,py_attr_setattr),
+ KX_PYATTRIBUTE_RW_FUNCTION("ik_weight",BL_ArmatureConstraint,py_attr_getattr,py_attr_setattr),
+ KX_PYATTRIBUTE_RO_FUNCTION("ik_type",BL_ArmatureConstraint,py_attr_getattr),
+ KX_PYATTRIBUTE_RO_FUNCTION("ik_flag",BL_ArmatureConstraint,py_attr_getattr),
+ KX_PYATTRIBUTE_RW_FUNCTION("ik_dist",BL_ArmatureConstraint,py_attr_getattr,py_attr_setattr),
+ KX_PYATTRIBUTE_RW_FUNCTION("ik_mode",BL_ArmatureConstraint,py_attr_getattr,py_attr_setattr),
+
+ { NULL } //Sentinel
+};
+
+
+PyObject* BL_ArmatureConstraint::py_attr_getattr(void *self_v, const struct KX_PYATTRIBUTE_DEF *attrdef)
+{
+ BL_ArmatureConstraint* self= static_cast<BL_ArmatureConstraint*>(self_v);
+ bConstraint* constraint = self->m_constraint;
+ bKinematicConstraint* ikconstraint = (constraint && constraint->type == CONSTRAINT_TYPE_KINEMATIC) ? (bKinematicConstraint*)constraint->data : NULL;
+ int attr_order = attrdef-Attributes;
+
+ if (!constraint) {
+ PyErr_SetString(PyExc_AttributeError, "constraint is NULL");
+ return NULL;
+ }
+
+ switch (attr_order) {
+ case BCA_TYPE:
+ return PyLong_FromLong(constraint->type);
+ case BCA_NAME:
+ return PyUnicode_FromString(constraint->name);
+ case BCA_ENFORCE:
+ return PyFloat_FromDouble(constraint->enforce);
+ case BCA_HEADTAIL:
+ return PyFloat_FromDouble(constraint->headtail);
+ case BCA_LINERROR:
+ return PyFloat_FromDouble(constraint->lin_error);
+ case BCA_ROTERROR:
+ return PyFloat_FromDouble(constraint->rot_error);
+ case BCA_TARGET:
+ if (!self->m_target)
+ Py_RETURN_NONE;
+ else
+ return self->m_target->GetProxy();
+ case BCA_SUBTARGET:
+ if (!self->m_subtarget)
+ Py_RETURN_NONE;
+ else
+ return self->m_subtarget->GetProxy();
+ case BCA_ACTIVE:
+ return PyBool_FromLong(constraint->flag & CONSTRAINT_OFF);
+ case BCA_IKWEIGHT:
+ case BCA_IKTYPE:
+ case BCA_IKFLAG:
+ case BCA_IKDIST:
+ case BCA_IKMODE:
+ if (!ikconstraint) {
+ PyErr_SetString(PyExc_AttributeError, "constraint is not of IK type");
+ return NULL;
+ }
+ switch (attr_order) {
+ case BCA_IKWEIGHT:
+ return PyFloat_FromDouble((ikconstraint)?ikconstraint->weight:0.0);
+ case BCA_IKTYPE:
+ return PyLong_FromLong(ikconstraint->type);
+ case BCA_IKFLAG:
+ return PyLong_FromLong(ikconstraint->flag);
+ case BCA_IKDIST:
+ return PyFloat_FromDouble(ikconstraint->dist);
+ case BCA_IKMODE:
+ return PyLong_FromLong(ikconstraint->mode);
+ }
+ // should not come here
+ break;
+ }
+ PyErr_SetString(PyExc_AttributeError, "constraint unknown attribute");
+ return NULL;
+}
+
+int BL_ArmatureConstraint::py_attr_setattr(void *self_v, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value)
+{
+ BL_ArmatureConstraint* self= static_cast<BL_ArmatureConstraint*>(self_v);
+ bConstraint* constraint = self->m_constraint;
+ bKinematicConstraint* ikconstraint = (constraint && constraint->type == CONSTRAINT_TYPE_KINEMATIC) ? (bKinematicConstraint*)constraint->data : NULL;
+ int attr_order = attrdef-Attributes;
+ int ival;
+ double dval;
+ char* sval;
+ KX_GameObject *oval;
+
+ if (!constraint) {
+ PyErr_SetString(PyExc_AttributeError, "constraint is NULL");
+ return PY_SET_ATTR_FAIL;
+ }
+
+ switch (attr_order) {
+ case BCA_ENFORCE:
+ dval = PyFloat_AsDouble(value);
+ if (dval < 0.0f || dval > 1.0f) { /* also accounts for non float */
+ PyErr_SetString(PyExc_AttributeError, "constraint.enforce = float: BL_ArmatureConstraint, expected a float between 0 and 1");
+ return PY_SET_ATTR_FAIL;
+ }
+ constraint->enforce = dval;
+ return PY_SET_ATTR_SUCCESS;
+
+ case BCA_HEADTAIL:
+ dval = PyFloat_AsDouble(value);
+ if (dval < 0.0f || dval > 1.0f) { /* also accounts for non float */
+ PyErr_SetString(PyExc_AttributeError, "constraint.headtail = float: BL_ArmatureConstraint, expected a float between 0 and 1");
+ return PY_SET_ATTR_FAIL;
+ }
+ constraint->headtail = dval;
+ return PY_SET_ATTR_SUCCESS;
+
+ case BCA_TARGET:
+ if (!ConvertPythonToGameObject(value, &oval, true, "constraint.target = value: BL_ArmatureConstraint"))
+ return PY_SET_ATTR_FAIL; // ConvertPythonToGameObject sets the error
+ self->SetTarget(oval);
+ return PY_SET_ATTR_SUCCESS;
+
+ case BCA_SUBTARGET:
+ if (!ConvertPythonToGameObject(value, &oval, true, "constraint.subtarget = value: BL_ArmatureConstraint"))
+ return PY_SET_ATTR_FAIL; // ConvertPythonToGameObject sets the error
+ self->SetSubtarget(oval);
+ return PY_SET_ATTR_SUCCESS;
+
+ case BCA_ACTIVE:
+ ival = PyObject_IsTrue( value );
+ if (ival == -1) {
+ PyErr_SetString(PyExc_AttributeError, "constraint.active = bool: BL_ArmatureConstraint, expected True or False");
+ return PY_SET_ATTR_FAIL;
+ }
+ self->m_constraint->flag = (self->m_constraint->flag & ~CONSTRAINT_OFF) | ((ival)?0:CONSTRAINT_OFF);
+ return PY_SET_ATTR_SUCCESS;
+
+ case BCA_IKWEIGHT:
+ case BCA_IKDIST:
+ case BCA_IKMODE:
+ if (!ikconstraint) {
+ PyErr_SetString(PyExc_AttributeError, "constraint is not of IK type");
+ return PY_SET_ATTR_FAIL;
+ }
+ switch (attr_order) {
+ case BCA_IKWEIGHT:
+ dval = PyFloat_AsDouble(value);
+ if (dval < 0.0f || dval > 1.0f) { /* also accounts for non float */
+ PyErr_SetString(PyExc_AttributeError, "constraint.weight = float: BL_ArmatureConstraint, expected a float between 0 and 1");
+ return PY_SET_ATTR_FAIL;
+ }
+ ikconstraint->weight = dval;
+ return PY_SET_ATTR_SUCCESS;
+
+ case BCA_IKDIST:
+ dval = PyFloat_AsDouble(value);
+ if (dval < 0.0f) { /* also accounts for non float */
+ PyErr_SetString(PyExc_AttributeError, "constraint.ik_dist = float: BL_ArmatureConstraint, expected a positive float");
+ return PY_SET_ATTR_FAIL;
+ }
+ ikconstraint->dist = dval;
+ return PY_SET_ATTR_SUCCESS;
+
+ case BCA_IKMODE:
+ ival = PyLong_AsLong(value);
+ if (ival < 0) {
+ PyErr_SetString(PyExc_AttributeError, "constraint.ik_mode = integer: BL_ArmatureConstraint, expected a positive integer");
+ return PY_SET_ATTR_FAIL;
+ }
+ ikconstraint->mode = ival;
+ return PY_SET_ATTR_SUCCESS;
+ }
+ // should not come here
+ break;
+
+ }
+
+ PyErr_SetString(PyExc_AttributeError, "constraint unknown attribute");
+ return PY_SET_ATTR_FAIL;
+}
+
diff --git a/source/gameengine/Converter/BL_ArmatureConstraint.h b/source/gameengine/Converter/BL_ArmatureConstraint.h
new file mode 100644
index 00000000000..f10849ea05b
--- /dev/null
+++ b/source/gameengine/Converter/BL_ArmatureConstraint.h
@@ -0,0 +1,117 @@
+/**
+ * $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.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+#ifndef __BL_ARMATURECONSTRAINT
+#define __BL_ARMATURECONSTRAINT
+
+#include "DNA_constraint_types.h"
+#include "GEN_HashedPtr.h"
+#include "GEN_Map.h"
+#include "PyObjectPlus.h"
+
+class SCA_IObject;
+class KX_GameObject;
+class BL_ArmatureObject;
+struct bConstraint;
+struct bPoseChannel;
+struct Object;
+struct bPose;
+
+/**
+ * SG_DList : element of controlled constraint list
+ * head = BL_ArmatureObject::m_controlledConstraints
+ * SG_QList : not used
+ */
+class BL_ArmatureConstraint : public PyObjectPlus
+{
+ Py_Header;
+
+private:
+ struct bConstraint* m_constraint;
+ struct bPoseChannel* m_posechannel;
+ class BL_ArmatureObject* m_armature;
+ char m_name[64];
+ KX_GameObject* m_target;
+ KX_GameObject* m_subtarget;
+ struct Object* m_blendtarget;
+ struct Object* m_blendsubtarget;
+ float m_blendmat[4][4];
+ float m_blendsubmat[4][4];
+ struct bPose* m_pose;
+ struct bPose* m_subpose;
+
+public:
+ BL_ArmatureConstraint(class BL_ArmatureObject *armature,
+ struct bPoseChannel *posechannel,
+ struct bConstraint *constraint,
+ KX_GameObject* target,
+ KX_GameObject* subtarget);
+ virtual ~BL_ArmatureConstraint();
+
+
+ virtual PyObject* py_repr(void);
+
+ BL_ArmatureConstraint* GetReplica() const;
+ void ReParent(BL_ArmatureObject* armature);
+ void Relink(GEN_Map<GEN_HashedPtr, void*> *map);
+ bool UnlinkObject(SCA_IObject* clientobj);
+
+ void UpdateTarget();
+ void RestoreTarget();
+
+ bool Match(const char* posechannel, const char* constraint);
+ const char* GetName() { return m_name; }
+
+ void SetConstraintFlag(int flag)
+ {
+ if (m_constraint)
+ m_constraint->flag |= flag;
+ }
+ void ClrConstraintFlag(int flag)
+ {
+ if (m_constraint)
+ m_constraint->flag &= ~flag;
+ }
+ void SetWeight(float weight)
+ {
+ if (m_constraint && m_constraint->type == CONSTRAINT_TYPE_KINEMATIC && m_constraint->data) {
+ bKinematicConstraint* con = (bKinematicConstraint*)m_constraint->data;
+ con->weight = weight;
+ }
+ }
+ void SetTarget(KX_GameObject* target);
+ void SetSubtarget(KX_GameObject* subtarget);
+
+ // Python access
+ static PyObject* py_attr_getattr(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef);
+ static int py_attr_setattr(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef, PyObject *value);
+
+};
+
+#endif //__BL_ARMATURECONSTRAINT
+
diff --git a/source/gameengine/Converter/BL_ArmatureObject.cpp b/source/gameengine/Converter/BL_ArmatureObject.cpp
index cfd90813a16..d5660d9ef84 100644
--- a/source/gameengine/Converter/BL_ArmatureObject.cpp
+++ b/source/gameengine/Converter/BL_ArmatureObject.cpp
@@ -29,9 +29,15 @@
#include "BL_ArmatureObject.h"
#include "BL_ActionActuator.h"
+#include "KX_BlenderSceneConverter.h"
#include "BLI_blenlib.h"
+#include "BLI_ghash.h"
+#include "BLI_arithb.h"
+#include "BIK_api.h"
#include "BKE_action.h"
#include "BKE_armature.h"
+#include "BKE_utildefines.h"
+#include "BKE_constraint.h"
#include "GEN_Map.h"
#include "GEN_HashedPtr.h"
#include "MEM_guardedalloc.h"
@@ -39,6 +45,11 @@
#include "DNA_armature_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
+#include "DNA_nla_types.h"
+#include "DNA_constraint_types.h"
+#include "KX_PythonSeq.h"
+#include "KX_PythonInit.h"
+#include "KX_KetsjiEngine.h"
#include "MT_Matrix4x4.h"
@@ -46,6 +57,153 @@
#include <config.h>
#endif
+/**
+ * Move here pose function for game engine so that we can mix with GE objects
+ * Principle is as follow:
+ * Use Blender structures so that where_is_pose can be used unchanged
+ * Copy the constraint so that they can be enabled/disabled/added/removed at runtime
+ * Don't copy the constraints for the pose used by the Action actuator, it does not need them.
+ * Scan the constraint structures so that the KX equivalent of target objects are identified and
+ * stored in separate list.
+ * When it is about to evaluate the pose, set the KX object position in the obmat of the corresponding
+ * Blender objects and restore after the evaluation.
+ */
+void game_copy_pose(bPose **dst, bPose *src, int copy_constraint)
+{
+ 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= (bPose*)MEM_dupallocN(src);
+ out->agroups.first= out->agroups.last= NULL;
+ out->ikdata = NULL;
+ out->ikparam = MEM_dupallocN(out->ikparam);
+ out->flag |= POSE_GAME_ENGINE;
+ BLI_duplicatelist(&out->chanbase, &src->chanbase);
+
+ /* remap pointers */
+ ghash= BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp);
+
+ pchan= (bPoseChannel*)src->chanbase.first;
+ outpchan= (bPoseChannel*)out->chanbase.first;
+ for (; pchan; pchan=pchan->next, outpchan=outpchan->next)
+ BLI_ghash_insert(ghash, pchan, outpchan);
+
+ for (pchan=(bPoseChannel*)out->chanbase.first; pchan; pchan=(bPoseChannel*)pchan->next) {
+ pchan->parent= (bPoseChannel*)BLI_ghash_lookup(ghash, pchan->parent);
+ pchan->child= (bPoseChannel*)BLI_ghash_lookup(ghash, pchan->child);
+ pchan->path= NULL;
+
+ if (copy_constraint) {
+ ListBase listb;
+ // copy all constraint for backward compatibility
+ copy_constraints(&listb, &pchan->constraints); // copy_constraints NULLs listb
+ pchan->constraints= listb;
+ } else {
+ pchan->constraints.first = NULL;
+ pchan->constraints.last = NULL;
+ }
+ }
+
+ BLI_ghash_free(ghash, NULL, NULL);
+
+ *dst=out;
+}
+
+
+
+/* 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= (bPoseChannel*)src->chanbase.first;
+ for (dchan = (bPoseChannel*)dst->chanbase.first; dchan; dchan=(bPoseChannel*)dchan->next, schan= (bPoseChannel*)schan->next){
+ // always blend on all channels since we don't know which one has been set
+ /* 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... */
+ dchan->loc[i] = (dchan->loc[i]*dstweight) + (schan->loc[i]*srcweight);
+ 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->rotmode)
+ dchan->eul[i] = (dchan->eul[i]*dstweight) + (schan->eul[i]*srcweight);
+ }
+ for(dcon= (bConstraint*)dchan->constraints.first, scon= (bConstraint*)schan->constraints.first; dcon && scon; dcon= (bConstraint*)dcon->next, scon= (bConstraint*)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) {
+ /* free pose-channels and constraints */
+ free_pose_channels(pose);
+
+ /* free IK solver state */
+ BIK_clear_data(pose);
+
+ /* free IK solver param */
+ if (pose->ikparam)
+ MEM_freeN(pose->ikparam);
+
+ MEM_freeN(pose);
+ }
+}
+
BL_ArmatureObject::BL_ArmatureObject(
void* sgReplicationInfo,
SG_Callbacks callbacks,
@@ -53,12 +211,17 @@ BL_ArmatureObject::BL_ArmatureObject(
Scene *scene)
: KX_GameObject(sgReplicationInfo,callbacks),
+ m_controlledConstraints(),
+ m_poseChannels(),
m_objArma(armature),
m_framePose(NULL),
m_scene(scene), // maybe remove later. needed for where_is_pose
m_lastframe(0.0),
+ m_timestep(0.040),
m_activeAct(NULL),
m_activePriority(999),
+ m_constraintNumber(0),
+ m_channelNumber(0),
m_lastapplyframe(0.0)
{
m_armature = (bArmature *)armature->data;
@@ -67,7 +230,177 @@ BL_ArmatureObject::BL_ArmatureObject(
* the original pose before calling into blender functions, to deal with
* replica's or other objects using the same blender object */
m_pose = NULL;
- game_copy_pose(&m_pose, m_objArma->pose);
+ game_copy_pose(&m_pose, m_objArma->pose, 1);
+ // store the original armature object matrix
+ memcpy(m_obmat, m_objArma->obmat, sizeof(m_obmat));
+}
+
+BL_ArmatureObject::~BL_ArmatureObject()
+{
+ BL_ArmatureConstraint* constraint;
+ while ((constraint = m_controlledConstraints.Remove()) != NULL) {
+ delete constraint;
+ }
+ BL_ArmatureChannel* channel;
+ while ((channel = static_cast<BL_ArmatureChannel*>(m_poseChannels.Remove())) != NULL) {
+ delete channel;
+ }
+ if (m_pose)
+ game_free_pose(m_pose);
+ if (m_framePose)
+ game_free_pose(m_framePose);
+}
+
+
+void BL_ArmatureObject::LoadConstraints(KX_BlenderSceneConverter* converter)
+{
+ // first delete any existing constraint (should not have any)
+ while (!m_controlledConstraints.Empty()) {
+ BL_ArmatureConstraint* constraint = m_controlledConstraints.Remove();
+ delete constraint;
+ }
+ m_constraintNumber = 0;
+
+ // list all the constraint and convert them to BL_ArmatureConstraint
+ // get the persistent pose structure
+ bPoseChannel* pchan;
+ bConstraint* pcon;
+ bConstraintTypeInfo* cti;
+ Object* blendtarget;
+ KX_GameObject* gametarget;
+ KX_GameObject* gamesubtarget;
+
+ // and locate the constraint
+ for (pchan = (bPoseChannel*)m_pose->chanbase.first; pchan; pchan=(bPoseChannel*)pchan->next) {
+ for (pcon = (bConstraint*)pchan->constraints.first; pcon; pcon=(bConstraint*)pcon->next) {
+ if (pcon->flag & CONSTRAINT_DISABLE)
+ continue;
+ // which constraint should we support?
+ switch (pcon->type) {
+ case CONSTRAINT_TYPE_TRACKTO:
+ case CONSTRAINT_TYPE_KINEMATIC:
+ case CONSTRAINT_TYPE_ROTLIKE:
+ case CONSTRAINT_TYPE_LOCLIKE:
+ case CONSTRAINT_TYPE_MINMAX:
+ case CONSTRAINT_TYPE_SIZELIKE:
+ case CONSTRAINT_TYPE_LOCKTRACK:
+ case CONSTRAINT_TYPE_STRETCHTO:
+ case CONSTRAINT_TYPE_CLAMPTO:
+ case CONSTRAINT_TYPE_TRANSFORM:
+ case CONSTRAINT_TYPE_DISTLIMIT:
+ cti = constraint_get_typeinfo(pcon);
+ gametarget = gamesubtarget = NULL;
+ if (cti && cti->get_constraint_targets) {
+ ListBase listb = { NULL, NULL };
+ cti->get_constraint_targets(pcon, &listb);
+ if (listb.first) {
+ bConstraintTarget* target = (bConstraintTarget*)listb.first;
+ if (target->tar && target->tar != m_objArma) {
+ // only remember external objects, self target is handled automatically
+ blendtarget = target->tar;
+ gametarget = converter->FindGameObject(blendtarget);
+ }
+ if (target->next != NULL) {
+ // secondary target
+ target = (bConstraintTarget*)target->next;
+ if (target->tar && target->tar != m_objArma) {
+ // only track external object
+ blendtarget = target->tar;
+ gamesubtarget = converter->FindGameObject(blendtarget);
+ }
+ }
+ }
+ if (cti->flush_constraint_targets)
+ cti->flush_constraint_targets(pcon, &listb, 1);
+ }
+ BL_ArmatureConstraint* constraint = new BL_ArmatureConstraint(this, pchan, pcon, gametarget, gamesubtarget);
+ m_controlledConstraints.AddBack(constraint);
+ m_constraintNumber++;
+ }
+ }
+ }
+}
+
+BL_ArmatureConstraint* BL_ArmatureObject::GetConstraint(const char* posechannel, const char* constraintname)
+{
+ SG_DList::iterator<BL_ArmatureConstraint> cit(m_controlledConstraints);
+ for (cit.begin(); !cit.end(); ++cit) {
+ BL_ArmatureConstraint* constraint = *cit;
+ if (constraint->Match(posechannel, constraintname))
+ return constraint;
+ }
+ return NULL;
+}
+
+BL_ArmatureConstraint* BL_ArmatureObject::GetConstraint(const char* posechannelconstraint)
+{
+ // performance: use hash string instead of plain string compare
+ SG_DList::iterator<BL_ArmatureConstraint> cit(m_controlledConstraints);
+ for (cit.begin(); !cit.end(); ++cit) {
+ BL_ArmatureConstraint* constraint = *cit;
+ if (!strcmp(constraint->GetName(), posechannelconstraint))
+ return constraint;
+ }
+ return NULL;
+}
+
+BL_ArmatureConstraint* BL_ArmatureObject::GetConstraint(int index)
+{
+ SG_DList::iterator<BL_ArmatureConstraint> cit(m_controlledConstraints);
+ for (cit.begin(); !cit.end() && index; ++cit, --index);
+ return (cit.end()) ? NULL : *cit;
+}
+
+/* this function is called to populate the m_poseChannels list */
+void BL_ArmatureObject::LoadChannels()
+{
+ if (m_poseChannels.Empty()) {
+ bPoseChannel* pchan;
+ BL_ArmatureChannel* proxy;
+
+ m_channelNumber = 0;
+ for (pchan = (bPoseChannel*)m_pose->chanbase.first; pchan; pchan=(bPoseChannel*)pchan->next) {
+ proxy = new BL_ArmatureChannel(this, pchan);
+ m_poseChannels.AddBack(proxy);
+ m_channelNumber++;
+ }
+ }
+}
+
+BL_ArmatureChannel* BL_ArmatureObject::GetChannel(bPoseChannel* pchan)
+{
+ LoadChannels();
+ SG_DList::iterator<BL_ArmatureChannel> cit(m_poseChannels);
+ for (cit.begin(); !cit.end(); ++cit)
+ {
+ BL_ArmatureChannel* channel = *cit;
+ if (channel->m_posechannel == pchan)
+ return channel;
+ }
+ return NULL;
+}
+
+BL_ArmatureChannel* BL_ArmatureObject::GetChannel(const char* str)
+{
+ LoadChannels();
+ SG_DList::iterator<BL_ArmatureChannel> cit(m_poseChannels);
+ for (cit.begin(); !cit.end(); ++cit)
+ {
+ BL_ArmatureChannel* channel = *cit;
+ if (!strcmp(channel->m_posechannel->name, str))
+ return channel;
+ }
+ return NULL;
+}
+
+BL_ArmatureChannel* BL_ArmatureObject::GetChannel(int index)
+{
+ LoadChannels();
+ if (index < 0 || index >= m_channelNumber)
+ return NULL;
+ SG_DList::iterator<BL_ArmatureChannel> cit(m_poseChannels);
+ for (cit.begin(); !cit.end() && index; ++cit, --index);
+ return (cit.end()) ? NULL : *cit;
}
CValue* BL_ArmatureObject::GetReplica()
@@ -83,22 +416,61 @@ void BL_ArmatureObject::ProcessReplica()
KX_GameObject::ProcessReplica();
m_pose = NULL;
- game_copy_pose(&m_pose, pose);
+ m_framePose = NULL;
+ game_copy_pose(&m_pose, pose, 1);
}
-BL_ArmatureObject::~BL_ArmatureObject()
+void BL_ArmatureObject::ReParentLogic()
{
- if (m_pose)
- game_free_pose(m_pose);
+ SG_DList::iterator<BL_ArmatureConstraint> cit(m_controlledConstraints);
+ for (cit.begin(); !cit.end(); ++cit) {
+ (*cit)->ReParent(this);
+ }
+ KX_GameObject::ReParentLogic();
+}
+
+void BL_ArmatureObject::Relink(GEN_Map<GEN_HashedPtr, void*> *obj_map)
+{
+ SG_DList::iterator<BL_ArmatureConstraint> cit(m_controlledConstraints);
+ for (cit.begin(); !cit.end(); ++cit) {
+ (*cit)->Relink(obj_map);
+ }
+ KX_GameObject::Relink(obj_map);
+}
+
+bool BL_ArmatureObject::UnlinkObject(SCA_IObject* clientobj)
+{
+ // clientobj is being deleted, make sure we don't hold any reference to it
+ bool res = false;
+ SG_DList::iterator<BL_ArmatureConstraint> cit(m_controlledConstraints);
+ for (cit.begin(); !cit.end(); ++cit) {
+ res |= (*cit)->UnlinkObject(clientobj);
+ }
+ return res;
}
void BL_ArmatureObject::ApplyPose()
{
m_armpose = m_objArma->pose;
m_objArma->pose = m_pose;
+ // in the GE, we use ctime to store the timestep
+ m_pose->ctime = (float)m_timestep;
//m_scene->r.cfra++;
if(m_lastapplyframe != m_lastframe) {
+ // update the constraint if any, first put them all off so that only the active ones will be updated
+ SG_DList::iterator<BL_ArmatureConstraint> cit(m_controlledConstraints);
+ for (cit.begin(); !cit.end(); ++cit) {
+ (*cit)->UpdateTarget();
+ }
+ // update ourself
+ UpdateBlenderObjectMatrix(m_objArma);
where_is_pose(m_scene, m_objArma); // XXX
+ // restore ourself
+ memcpy(m_objArma->obmat, m_obmat, sizeof(m_obmat));
+ // restore active targets
+ for (cit.begin(); !cit.end(); ++cit) {
+ (*cit)->RestoreTarget();
+ }
m_lastapplyframe = m_lastframe;
}
}
@@ -119,30 +491,37 @@ bool BL_ArmatureObject::SetActiveAction(BL_ActionActuator *act, short priority,
{
if (curtime != m_lastframe){
m_activePriority = 9999;
+ // compute the timestep for the underlying IK algorithm
+ m_timestep = curtime-m_lastframe;
m_lastframe= curtime;
m_activeAct = NULL;
// remember the pose at the start of the frame
- m_framePose = m_pose;
+ GetPose(&m_framePose);
}
- if (priority<=m_activePriority)
+ if (act)
{
- if (priority<m_activePriority)
- // this action overwrites the previous ones, start from initial pose to cancel their effects
- m_pose = m_framePose;
- if (m_activeAct && (m_activeAct!=act))
- m_activeAct->SetBlendTime(0.0); /* Reset the blend timer */
- m_activeAct = act;
- m_activePriority = priority;
- m_lastframe = curtime;
-
- return true;
- }
- else{
- act->SetBlendTime(0.0);
- return false;
+ if (priority<=m_activePriority)
+ {
+ if (priority<m_activePriority) {
+ // this action overwrites the previous ones, start from initial pose to cancel their effects
+ SetPose(m_framePose);
+ if (m_activeAct && (m_activeAct!=act))
+ /* Reset the blend timer since this new action cancels the old one */
+ m_activeAct->SetBlendTime(0.0);
+ }
+ m_activeAct = act;
+ m_activePriority = priority;
+ m_lastframe = curtime;
+
+ return true;
+ }
+ else{
+ act->SetBlendTime(0.0);
+ return false;
+ }
}
-
+ return false;
}
BL_ActionActuator * BL_ArmatureObject::GetActiveAction()
@@ -161,7 +540,7 @@ void BL_ArmatureObject::GetPose(bPose **pose)
a crash and memory leakage when
&BL_ActionActuator::m_pose is freed
*/
- game_copy_pose(pose, m_pose);
+ game_copy_pose(pose, m_pose, 0);
}
else {
if (*pose == m_pose)
@@ -178,7 +557,7 @@ void BL_ArmatureObject::GetMRDPose(bPose **pose)
/* Otherwise, copy the armature's pose channels into the caller-supplied pose */
if (!*pose)
- game_copy_pose(pose, m_pose);
+ game_copy_pose(pose, m_pose, 0);
else
extract_pose_from_pose(*pose, m_pose);
}
@@ -210,3 +589,68 @@ float BL_ArmatureObject::GetBoneLength(Bone* bone) const
{
return (float)(MT_Point3(bone->head) - MT_Point3(bone->tail)).length();
}
+
+// PYTHON
+
+PyTypeObject BL_ArmatureObject::Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "BL_ArmatureObject",
+ sizeof(PyObjectPlus_Proxy),
+ 0,
+ py_base_dealloc,
+ 0,
+ 0,
+ 0,
+ 0,
+ py_base_repr,
+ 0,
+ &KX_GameObject::Sequence,
+ &KX_GameObject::Mapping,
+ 0,0,0,
+ NULL,
+ NULL,
+ 0,
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ 0,0,0,0,0,0,0,
+ Methods,
+ 0,
+ 0,
+ &KX_GameObject::Type,
+ 0,0,0,0,0,0,
+ py_base_new
+};
+
+PyMethodDef BL_ArmatureObject::Methods[] = {
+
+ KX_PYMETHODTABLE_NOARGS(BL_ArmatureObject, update),
+ {NULL,NULL} //Sentinel
+};
+
+PyAttributeDef BL_ArmatureObject::Attributes[] = {
+
+ KX_PYATTRIBUTE_RO_FUNCTION("constraints", BL_ArmatureObject, pyattr_get_constraints),
+ KX_PYATTRIBUTE_RO_FUNCTION("channels", BL_ArmatureObject, pyattr_get_channels),
+ {NULL} //Sentinel
+};
+
+PyObject* BL_ArmatureObject::pyattr_get_constraints(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
+{
+ return KX_PythonSeq_CreatePyObject((static_cast<BL_ArmatureObject*>(self_v))->m_proxy, KX_PYGENSEQ_OB_TYPE_CONSTRAINTS);
+}
+
+PyObject* BL_ArmatureObject::pyattr_get_channels(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
+{
+ BL_ArmatureObject* self = static_cast<BL_ArmatureObject*>(self_v);
+ self->LoadChannels(); // make sure we have the channels
+ return KX_PythonSeq_CreatePyObject((static_cast<BL_ArmatureObject*>(self_v))->m_proxy, KX_PYGENSEQ_OB_TYPE_CHANNELS);
+}
+
+KX_PYMETHODDEF_DOC_NOARGS(BL_ArmatureObject, update,
+ "update()\n"
+ "Make sure that the armature will be updated on next graphic frame.\n"
+ "This is automatically done if a KX_ArmatureActuator with mode run is active\n"
+ "or if an action is playing. This function is usefull in other cases.\n")
+{
+ SetActiveAction(NULL, 0, KX_GetActiveEngine()->GetFrameTime());
+ Py_RETURN_NONE;
+}
diff --git a/source/gameengine/Converter/BL_ArmatureObject.h b/source/gameengine/Converter/BL_ArmatureObject.h
index af0b7dc201c..3e917e08001 100644
--- a/source/gameengine/Converter/BL_ArmatureObject.h
+++ b/source/gameengine/Converter/BL_ArmatureObject.h
@@ -31,21 +31,34 @@
#define BL_ARMATUREOBJECT
#include "KX_GameObject.h"
+#include "BL_ArmatureConstraint.h"
+#include "BL_ArmatureChannel.h"
#include "SG_IObject.h"
+#include <vector>
+#include <algorithm>
struct bArmature;
struct Bone;
+struct bConstraint;
class BL_ActionActuator;
+class BL_ArmatureActuator;
class MT_Matrix4x4;
struct Object;
+class KX_BlenderSceneConverter;
class BL_ArmatureObject : public KX_GameObject
{
+ Py_Header;
public:
+
double GetLastFrame ();
short GetActivePriority();
virtual void ProcessReplica();
+ virtual void ReParentLogic();
+ virtual void Relink(GEN_Map<GEN_HashedPtr, void*> *obj_map);
+ virtual bool UnlinkObject(SCA_IObject* clientobj);
+
class BL_ActionActuator * GetActiveAction();
BL_ArmatureObject(
@@ -73,6 +86,19 @@ public:
Object* GetArmatureObject() {return m_objArma;}
+ // for constraint python API
+ void LoadConstraints(KX_BlenderSceneConverter* converter);
+ size_t GetConstraintNumber() const { return m_constraintNumber; }
+ BL_ArmatureConstraint* GetConstraint(const char* posechannel, const char* constraint);
+ BL_ArmatureConstraint* GetConstraint(const char* posechannelconstraint);
+ BL_ArmatureConstraint* GetConstraint(int index);
+ // for pose channel python API
+ void LoadChannels();
+ size_t GetChannelNumber() const { return m_constraintNumber; }
+ BL_ArmatureChannel* GetChannel(bPoseChannel* channel);
+ BL_ArmatureChannel* GetChannel(const char* channel);
+ BL_ArmatureChannel* GetChannel(int index);
+
/// Retrieve the pose matrix for the specified bone.
/// Returns true on success.
bool GetBoneMatrix(Bone* bone, MT_Matrix4x4& matrix);
@@ -81,7 +107,17 @@ public:
float GetBoneLength(Bone* bone) const;
virtual int GetGameObjectType() { return OBJ_ARMATURE; }
+
+ // PYTHON
+ static PyObject* pyattr_get_constraints(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef);
+ static PyObject* pyattr_get_channels(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef);
+ KX_PYMETHOD_DOC_NOARGS(BL_ArmatureObject, update);
+
protected:
+ /* list element: BL_ArmatureConstraint. Use SG_DListHead to have automatic list replication */
+ SG_DListHead<BL_ArmatureConstraint> m_controlledConstraints;
+ /* list element: BL_ArmatureChannel. Use SG_DList to avoid list replication */
+ SG_DList m_poseChannels;
Object *m_objArma;
struct bArmature *m_armature;
struct bPose *m_pose;
@@ -89,8 +125,13 @@ protected:
struct bPose *m_framePose;
struct Scene *m_scene; // need for where_is_pose
double m_lastframe;
+ double m_timestep; // delta since last pose evaluation.
class BL_ActionActuator *m_activeAct;
short m_activePriority;
+ size_t m_constraintNumber;
+ size_t m_channelNumber;
+ // store the original armature object matrix
+ float m_obmat[4][4];
double m_lastapplyframe;
@@ -102,5 +143,12 @@ public:
#endif
};
+/* Pose function specific to the 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);
+void game_copy_pose(struct bPose **dst, struct bPose *src, int copy_con);
+void game_free_pose(struct bPose *pose);
+
+
#endif
diff --git a/source/gameengine/Converter/BL_BlenderDataConversion.cpp b/source/gameengine/Converter/BL_BlenderDataConversion.cpp
index 230820719aa..8feae48f12e 100644
--- a/source/gameengine/Converter/BL_BlenderDataConversion.cpp
+++ b/source/gameengine/Converter/BL_BlenderDataConversion.cpp
@@ -2501,6 +2501,14 @@ void BL_ConvertBlenderObjects(struct Main* maggie,
gameobj->GetDeformer()->UpdateBuckets();
}
+ // Set up armature constraints
+ for (i=0;i<sumolist->GetCount();++i)
+ {
+ KX_GameObject* gameobj = (KX_GameObject*) sumolist->GetValue(i);
+ if (gameobj->GetGameObjectType() == SCA_IObject::OBJ_ARMATURE)
+ ((BL_ArmatureObject*)gameobj)->LoadConstraints(converter);
+ }
+
bool processCompoundChildren = false;
// create physics information
diff --git a/source/gameengine/Converter/BL_ShapeActionActuator.h b/source/gameengine/Converter/BL_ShapeActionActuator.h
index 28a6d90abdf..80157630e27 100644
--- a/source/gameengine/Converter/BL_ShapeActionActuator.h
+++ b/source/gameengine/Converter/BL_ShapeActionActuator.h
@@ -51,7 +51,7 @@ public:
short blendin,
short priority,
float stride)
- : SCA_IActuator(gameobj),
+ : SCA_IActuator(gameobj, KX_ACT_SHAPEACTION),
m_lastpos(0, 0, 0),
m_blendframe(0),
diff --git a/source/gameengine/Converter/CMakeLists.txt b/source/gameengine/Converter/CMakeLists.txt
index 257ca856b2c..d617934ec01 100644
--- a/source/gameengine/Converter/CMakeLists.txt
+++ b/source/gameengine/Converter/CMakeLists.txt
@@ -59,6 +59,7 @@ SET(INC
../../../source/blender/misc
../../../source/blender/blenloader
../../../source/blender/gpu
+ ../../../source/blender/ikplugin
../../../extern/bullet2/src
${PYTHON_INC}
)
diff --git a/source/gameengine/Converter/KX_ConvertActuators.cpp b/source/gameengine/Converter/KX_ConvertActuators.cpp
index 91a39bd7686..1cb16acf148 100644
--- a/source/gameengine/Converter/KX_ConvertActuators.cpp
+++ b/source/gameengine/Converter/KX_ConvertActuators.cpp
@@ -88,6 +88,7 @@
#include "DNA_packedFile_types.h"
#include "BL_ActionActuator.h"
#include "BL_ShapeActionActuator.h"
+#include "BL_ArmatureActuator.h"
/* end of blender include block */
#include "BL_BlenderDataConversion.h"
@@ -1021,6 +1022,15 @@ void BL_ConvertActuators(char* maggiename,
break;
}
+ case ACT_ARMATURE:
+ {
+ bArmatureActuator* armAct = (bArmatureActuator*) bact->data;
+ KX_GameObject *tmpgob = converter->FindGameObject(armAct->target);
+ KX_GameObject *subgob = converter->FindGameObject(armAct->subtarget);
+ BL_ArmatureActuator* tmparmact = new BL_ArmatureActuator(gameobj, armAct->type, armAct->posechannel, armAct->constraint, tmpgob, subgob, armAct->weight);
+ baseact = tmparmact;
+ break;
+ }
default:
; /* generate some error */
}
diff --git a/source/gameengine/Converter/KX_ConvertSensors.cpp b/source/gameengine/Converter/KX_ConvertSensors.cpp
index 09027f18844..99a2ec3ca64 100644
--- a/source/gameengine/Converter/KX_ConvertSensors.cpp
+++ b/source/gameengine/Converter/KX_ConvertSensors.cpp
@@ -64,6 +64,7 @@ probably misplaced */
#include "KX_NearSensor.h"
#include "KX_RadarSensor.h"
#include "KX_MouseFocusSensor.h"
+#include "KX_ArmatureSensor.h"
#include "SCA_JoystickSensor.h"
#include "KX_NetworkMessageSensor.h"
#include "SCA_ActuatorSensor.h"
@@ -302,7 +303,7 @@ void BL_ConvertSensors(struct Object* blenderobject,
case SENS_ALWAYS:
{
- SCA_EventManager* eventmgr = logicmgr->FindEventManager(SCA_EventManager::ALWAYS_EVENTMGR);
+ SCA_EventManager* eventmgr = logicmgr->FindEventManager(SCA_EventManager::BASIC_EVENTMGR);
if (eventmgr)
{
gamesensor = new SCA_AlwaysSensor(eventmgr, gameobj);
@@ -314,7 +315,7 @@ void BL_ConvertSensors(struct Object* blenderobject,
case SENS_DELAY:
{
// we can reuse the Always event manager for the delay sensor
- SCA_EventManager* eventmgr = logicmgr->FindEventManager(SCA_EventManager::ALWAYS_EVENTMGR);
+ SCA_EventManager* eventmgr = logicmgr->FindEventManager(SCA_EventManager::BASIC_EVENTMGR);
if (eventmgr)
{
bDelaySensor* delaysensor = (bDelaySensor*)sens->data;
@@ -550,7 +551,7 @@ void BL_ConvertSensors(struct Object* blenderobject,
{
bPropertySensor* blenderpropsensor = (bPropertySensor*) sens->data;
SCA_EventManager* eventmgr
- = logicmgr->FindEventManager(SCA_EventManager::PROPERTY_EVENTMGR);
+ = logicmgr->FindEventManager(SCA_EventManager::BASIC_EVENTMGR);
if (eventmgr)
{
STR_String propname=blenderpropsensor->name;
@@ -601,6 +602,21 @@ void BL_ConvertSensors(struct Object* blenderobject,
break;
}
+ case SENS_ARMATURE:
+ {
+ bArmatureSensor* blenderarmsensor = (bArmatureSensor*) sens->data;
+ // we will reuse the property event manager, there is nothing special with this sensor
+ SCA_EventManager* eventmgr
+ = logicmgr->FindEventManager(SCA_EventManager::BASIC_EVENTMGR);
+ if (eventmgr)
+ {
+ STR_String bonename=blenderarmsensor->posechannel;
+ STR_String constraintname=blenderarmsensor->constraint;
+ gamesensor = new KX_ArmatureSensor(eventmgr,gameobj,bonename,constraintname, blenderarmsensor->type, blenderarmsensor->value);
+ }
+ break;
+ }
+
case SENS_RADAR:
{
@@ -659,7 +675,7 @@ void BL_ConvertSensors(struct Object* blenderobject,
bRaySensor* blenderraysensor = (bRaySensor*) sens->data;
//blenderradarsensor->angle;
- SCA_EventManager* eventmgr = logicmgr->FindEventManager(SCA_EventManager::RAY_EVENTMGR);
+ SCA_EventManager* eventmgr = logicmgr->FindEventManager(SCA_EventManager::BASIC_EVENTMGR);
if (eventmgr)
{
bool bFindMaterial = (blenderraysensor->mode & SENS_COLLISION_MATERIAL);
@@ -691,7 +707,7 @@ void BL_ConvertSensors(struct Object* blenderobject,
// some files didn't write randomsensor, avoid crash now for NULL ptr's
if (blenderrndsensor)
{
- SCA_EventManager* eventmgr = logicmgr->FindEventManager(SCA_EventManager::RANDOM_EVENTMGR);
+ SCA_EventManager* eventmgr = logicmgr->FindEventManager(SCA_EventManager::BASIC_EVENTMGR);
if (eventmgr)
{
int randomSeed = blenderrndsensor->seed;
diff --git a/source/gameengine/Converter/Makefile b/source/gameengine/Converter/Makefile
index e261f9350e9..d41cc705087 100644
--- a/source/gameengine/Converter/Makefile
+++ b/source/gameengine/Converter/Makefile
@@ -53,6 +53,7 @@ CPPFLAGS += -I../../blender/blenlib
CPPFLAGS += -I../../blender/blenkernel
CPPFLAGS += -I../../blender/render/extern/include
CPPFLAGS += -I../../blender/gpu
+CPPFLAGS += -I../../blender/ikplugin
CPPFLAGS += -I$(NAN_GUARDEDALLOC)/include
CPPFLAGS += -I../Expressions -I../Rasterizer -I../GameLogic
CPPFLAGS += -I../Ketsji -I../BlenderRoutines -I../SceneGraph
diff --git a/source/gameengine/Converter/SConscript b/source/gameengine/Converter/SConscript
index 2d126310475..5f6e9df521b 100644
--- a/source/gameengine/Converter/SConscript
+++ b/source/gameengine/Converter/SConscript
@@ -19,6 +19,7 @@ incs += ' #source/gameengine/Network/LoopBackNetwork'
incs += ' #source/blender/misc #source/blender/blenloader #source/blender/gpu'
incs += ' #source/blender/windowmanager'
incs += ' #source/blender/makesrna'
+incs += ' #source/blender/ikplugin'
incs += ' ' + env['BF_PYTHON_INC']
incs += ' ' + env['BF_BULLET_INC']
diff --git a/source/gameengine/Expressions/CMakeLists.txt b/source/gameengine/Expressions/CMakeLists.txt
index 439a50852a7..ec3738d1fe8 100644
--- a/source/gameengine/Expressions/CMakeLists.txt
+++ b/source/gameengine/Expressions/CMakeLists.txt
@@ -30,6 +30,7 @@ SET(INC
.
../../../source/kernel/gen_system
../../../intern/string
+ ../../../intern/guardedalloc
../../../intern/moto/include
../../../source/gameengine/SceneGraph
../../../source/blender/blenloader
diff --git a/source/gameengine/Expressions/PyObjectPlus.cpp b/source/gameengine/Expressions/PyObjectPlus.cpp
index 1d1d9e6103b..7adbf5ac651 100644
--- a/source/gameengine/Expressions/PyObjectPlus.cpp
+++ b/source/gameengine/Expressions/PyObjectPlus.cpp
@@ -51,6 +51,7 @@
#include "PyObjectPlus.h"
#include "STR_String.h"
#include "MT_Vector3.h"
+#include "MEM_guardedalloc.h"
/*------------------------------
* PyObjectPlus Type -- Every class, even the abstract one should have a Type
------------------------------*/
@@ -95,7 +96,6 @@ PyObject *PyObjectPlus::py_base_repr(PyObject *self) // This should be the ent
PyErr_SetString(PyExc_SystemError, BGE_PROXY_ERROR_MSG);
return NULL;
}
-
return self_plus->py_repr();
}
@@ -145,42 +145,55 @@ PyObject * PyObjectPlus::py_base_new(PyTypeObject *type, PyObject *args, PyObjec
PyObjectPlus_Proxy *ret = (PyObjectPlus_Proxy *) type->tp_alloc(type, 0); /* starts with 1 ref, used for the return ref' */
ret->ref= base->ref;
- base->ref= NULL; /* invalidate! disallow further access */
-
+ ret->ptr= base->ptr;
ret->py_owns= base->py_owns;
+ ret->py_ref = base->py_ref;
- ret->ref->m_proxy= NULL;
-
- /* 'base' may be free'd after this func finished but not necessarily
- * there is no reference to the BGE data now so it will throw an error on access */
- Py_DECREF(base);
-
- ret->ref->m_proxy= (PyObject *)ret; /* no need to add a ref because one is added when creating. */
- Py_INCREF(ret); /* we return a new ref but m_proxy holds a ref so we need to add one */
-
-
- /* 'ret' will have 2 references.
- * - One ref is needed because ret->ref->m_proxy holds a refcount to the current proxy.
- * - Another is needed for returning the value.
- *
- * So we should be ok with 2 refs, but for some reason this crashes. so adding a new ref...
- * */
+ if (ret->py_ref) {
+ base->ref= NULL; /* invalidate! disallow further access */
+ base->ptr = NULL;
+ if (ret->ref)
+ ret->ref->m_proxy= NULL;
+ /* 'base' may be free'd after this func finished but not necessarily
+ * there is no reference to the BGE data now so it will throw an error on access */
+ Py_DECREF(base);
+ if (ret->ref) {
+ ret->ref->m_proxy= (PyObject *)ret; /* no need to add a ref because one is added when creating. */
+ Py_INCREF(ret); /* we return a new ref but m_proxy holds a ref so we need to add one */
+ }
+ } else {
+ // generic structures don't hold a reference to this proxy, so don't increment ref count
+ if (ret->py_owns)
+ // but if the proxy owns the structure, there can be only one owner
+ base->ptr= NULL;
+ }
return (PyObject *)ret;
}
void PyObjectPlus::py_base_dealloc(PyObject *self) // python wrapper
{
- PyObjectPlus *self_plus= BGE_PROXY_REF(self);
- if(self_plus) {
- if(BGE_PROXY_PYOWNS(self)) { /* Does python own this?, then delete it */
- self_plus->m_proxy = NULL; /* Need this to stop ~PyObjectPlus from decrefing m_proxy otherwise its decref'd twice and py-debug crashes */
- delete self_plus;
+ if (BGE_PROXY_PYREF(self)) {
+ PyObjectPlus *self_plus= BGE_PROXY_REF(self);
+ if(self_plus) {
+ if(BGE_PROXY_PYOWNS(self)) { /* Does python own this?, then delete it */
+ self_plus->m_proxy = NULL; /* Need this to stop ~PyObjectPlus from decrefing m_proxy otherwise its decref'd twice and py-debug crashes */
+ delete self_plus;
+ }
+ BGE_PROXY_REF(self)= NULL; // not really needed
+ }
+ // the generic pointer is not deleted directly, only through self_plus
+ BGE_PROXY_PTR(self)= NULL; // not really needed
+ } else {
+ void *ptr= BGE_PROXY_PTR(self);
+ if(ptr) {
+ if(BGE_PROXY_PYOWNS(self)) { /* Does python own this?, then delete it */
+ // generic structure owned by python MUST be created though MEM_alloc
+ MEM_freeN(ptr);
+ }
+ BGE_PROXY_PTR(self)= NULL; // not really needed
}
-
- BGE_PROXY_REF(self)= NULL; // not really needed
}
-
#if 0
/* is ok normally but not for subtyping, use tp_free instead. */
PyObject_DEL( self );
@@ -217,8 +230,9 @@ PyObject* PyObjectPlus::pyattr_get_invalid(void *self_v, const KX_PYATTRIBUTE_DE
/* note, this is called as a python 'getset, where the PyAttributeDef is the closure */
PyObject *PyObjectPlus::py_get_attrdef(PyObject *self_py, const PyAttributeDef *attrdef)
{
- void *self= (void *)(BGE_PROXY_REF(self_py));
- if(self==NULL) {
+ PyObjectPlus *ref= (BGE_PROXY_REF(self_py));
+ char* ptr = (attrdef->m_usePtr) ? (char*)BGE_PROXY_PTR(self_py) : (char*)ref;
+ if(ptr == NULL || (BGE_PROXY_PYREF(self_py) && (ref==NULL || !ref->py_is_valid()))) {
if(attrdef == attr_invalid)
Py_RETURN_TRUE; // dont bother running the function
@@ -226,7 +240,6 @@ PyObject *PyObjectPlus::py_get_attrdef(PyObject *self_py, const PyAttributeDef *
return NULL;
}
-
if (attrdef->m_type == KX_PYATTRIBUTE_TYPE_DUMMY)
{
// fake attribute, ignore
@@ -237,9 +250,9 @@ PyObject *PyObjectPlus::py_get_attrdef(PyObject *self_py, const PyAttributeDef *
// the attribute has no field correspondance, handover processing to function.
if (attrdef->m_getFunction == NULL)
return NULL;
- return (*attrdef->m_getFunction)(self, attrdef);
+ return (*attrdef->m_getFunction)(ref, attrdef);
}
- char *ptr = reinterpret_cast<char*>(self)+attrdef->m_offset;
+ ptr += attrdef->m_offset;
if (attrdef->m_length > 1)
{
PyObject* resultlist = PyList_New(attrdef->m_length);
@@ -293,6 +306,35 @@ PyObject *PyObjectPlus::py_get_attrdef(PyObject *self_py, const PyAttributeDef *
else
{
switch (attrdef->m_type) {
+ case KX_PYATTRIBUTE_TYPE_FLAG:
+ {
+ bool bval;
+ switch (attrdef->m_size) {
+ case 1:
+ {
+ unsigned char *val = reinterpret_cast<unsigned char*>(ptr);
+ bval = (*val & attrdef->m_imin);
+ break;
+ }
+ case 2:
+ {
+ unsigned short *val = reinterpret_cast<unsigned short*>(ptr);
+ bval = (*val & attrdef->m_imin);
+ break;
+ }
+ case 4:
+ {
+ unsigned int *val = reinterpret_cast<unsigned int*>(ptr);
+ bval = (*val & attrdef->m_imin);
+ break;
+ }
+ default:
+ return NULL;
+ }
+ if (attrdef->m_imax)
+ bval = !bval;
+ return PyLong_FromSsize_t(bval);
+ }
case KX_PYATTRIBUTE_TYPE_BOOL:
{
bool *val = reinterpret_cast<bool*>(ptr);
@@ -318,7 +360,49 @@ PyObject *PyObjectPlus::py_get_attrdef(PyObject *self_py, const PyAttributeDef *
case KX_PYATTRIBUTE_TYPE_FLOAT:
{
float *val = reinterpret_cast<float*>(ptr);
- return PyFloat_FromDouble(*val);
+ if (attrdef->m_imin == 0) {
+ if (attrdef->m_imax == 0) {
+ return PyFloat_FromDouble(*val);
+ } else {
+ // vector, verify size
+ if (attrdef->m_size != attrdef->m_imax*sizeof(float))
+ {
+ return NULL;
+ }
+#ifdef USE_MATHUTILS
+ return newVectorObject(val, attrdef->m_imax, Py_NEW, NULL);
+#else
+ PyObject* resultlist = PyList_New(attrdef->m_imax);
+ for (unsigned int i=0; i<attrdef->m_imax; i++)
+ {
+ PyList_SET_ITEM(resultlist,i,PyFloat_FromDouble(val[i]));
+ }
+ return resultlist;
+#endif
+ }
+ } else {
+ // matrix case
+ if (attrdef->m_size != attrdef->m_imax*attrdef->m_imin*sizeof(float))
+ {
+ return NULL;
+ }
+#ifdef USE_MATHUTILS
+ return newMatrixObject(val, attrdef->m_imin, attrdef->m_imax, Py_WRAP, NULL);
+#else
+ PyObject* rowlist = PyList_New(attrdef->m_imin);
+ for (unsigned int i=0; i<attrdef->m_imin; i++)
+ {
+ PyObject* collist = PyList_New(attrdef->m_imax);
+ for (unsigned int j=0; j<attrdef->m_imax; j++)
+ {
+ PyList_SET_ITEM(collist,j,PyFloat_FromDouble(val[j]));
+ }
+ PyList_SET_ITEM(rowlist,i,collist);
+ val += attrdef->m_imax;
+ }
+ return rowlist;
+#endif
+ }
}
case KX_PYATTRIBUTE_TYPE_VECTOR:
{
@@ -340,17 +424,47 @@ PyObject *PyObjectPlus::py_get_attrdef(PyObject *self_py, const PyAttributeDef *
STR_String *val = reinterpret_cast<STR_String*>(ptr);
return PyUnicode_FromString(*val);
}
+ case KX_PYATTRIBUTE_TYPE_CHAR:
+ {
+ return PyUnicode_FromString(ptr);
+ }
default:
return NULL;
}
}
}
+
+static bool py_check_attr_float(float *var, PyObject *value, const PyAttributeDef *attrdef)
+{
+ double val = PyFloat_AsDouble(value);
+ if (val == -1.0 && PyErr_Occurred())
+ {
+ PyErr_Format(PyExc_TypeError, "expected float value for attribute \"%s\"", attrdef->m_name);
+ return false;
+ }
+ if (attrdef->m_clamp)
+ {
+ if (val < attrdef->m_fmin)
+ val = attrdef->m_fmin;
+ else if (val > attrdef->m_fmax)
+ val = attrdef->m_fmax;
+ }
+ else if (val < attrdef->m_fmin || val > attrdef->m_fmax)
+ {
+ PyErr_Format(PyExc_ValueError, "value out of range for attribute \"%s\"", attrdef->m_name);
+ return false;
+ }
+ *var = (float)val;
+ return true;
+}
+
/* note, this is called as a python getset */
int PyObjectPlus::py_set_attrdef(PyObject *self_py, PyObject *value, const PyAttributeDef *attrdef)
{
- void *self= (void *)(BGE_PROXY_REF(self_py));
- if(self==NULL) {
+ PyObjectPlus *ref= (BGE_PROXY_REF(self_py));
+ char* ptr = (attrdef->m_usePtr) ? (char*)BGE_PROXY_PTR(self_py) : (char*)ref;
+ if(ref==NULL || !ref->py_is_valid() || ptr==NULL) {
PyErr_SetString(PyExc_SystemError, BGE_PROXY_ERROR_MSG);
return PY_SET_ATTR_FAIL;
}
@@ -358,8 +472,10 @@ int PyObjectPlus::py_set_attrdef(PyObject *self_py, PyObject *value, const PyAtt
void *undoBuffer = NULL;
void *sourceBuffer = NULL;
size_t bufferSize = 0;
+ PyObject *item = NULL; // to store object that must be dereferenced in case of error
+ PyObject *list = NULL; // to store object that must be dereferenced in case of error
- char *ptr = reinterpret_cast<char*>(self)+attrdef->m_offset;
+ ptr += attrdef->m_offset;
if (attrdef->m_length > 1)
{
if (!PySequence_Check(value))
@@ -380,7 +496,7 @@ int PyObjectPlus::py_set_attrdef(PyObject *self_py, PyObject *value, const PyAtt
PyErr_Format(PyExc_AttributeError, "function attribute without function for attribute \"%s\", report to blender.org", attrdef->m_name);
return PY_SET_ATTR_FAIL;
}
- return (*attrdef->m_setFunction)(self, attrdef, value);
+ return (*attrdef->m_setFunction)(ref, attrdef, value);
case KX_PYATTRIBUTE_TYPE_BOOL:
bufferSize = sizeof(bool);
break;
@@ -409,10 +525,7 @@ int PyObjectPlus::py_set_attrdef(PyObject *self_py, PyObject *value, const PyAtt
}
for (int i=0; i<attrdef->m_length; i++)
{
- PyObject *item = PySequence_GetItem(value, i); /* new ref */
- // we can decrement the reference immediately, the reference count
- // is at least 1 because the item is part of an array
- Py_DECREF(item);
+ item = PySequence_GetItem(value, i); /* new ref */
switch (attrdef->m_type)
{
case KX_PYATTRIBUTE_TYPE_BOOL:
@@ -528,11 +641,14 @@ int PyObjectPlus::py_set_attrdef(PyObject *self_py, PyObject *value, const PyAtt
PyErr_Format(PyExc_AttributeError, "type check error for attribute \"%s\", report to blender.org", attrdef->m_name);
goto UNDO_AND_ERROR;
}
+ // finished using item, release
+ Py_DECREF(item);
+ item = NULL;
}
// no error, call check function if any
if (attrdef->m_checkFunction != NULL)
{
- if ((*attrdef->m_checkFunction)(self, attrdef) != 0)
+ if ((*attrdef->m_checkFunction)(ref, attrdef) != 0)
{
// if the checing function didnt set an error then set a generic one here so we dont set an error with no exception
if (PyErr_Occurred()==0)
@@ -545,6 +661,8 @@ int PyObjectPlus::py_set_attrdef(PyObject *self_py, PyObject *value, const PyAtt
memcpy(sourceBuffer, undoBuffer, bufferSize);
free(undoBuffer);
}
+ if (item)
+ Py_DECREF(item);
return PY_SET_ATTR_FAIL;
}
}
@@ -561,7 +679,7 @@ int PyObjectPlus::py_set_attrdef(PyObject *self_py, PyObject *value, const PyAtt
PyErr_Format(PyExc_AttributeError, "function attribute without function \"%s\", report to blender.org", attrdef->m_name);
return PY_SET_ATTR_FAIL;
}
- return (*attrdef->m_setFunction)(self, attrdef, value);
+ return (*attrdef->m_setFunction)(ref, attrdef, value);
}
if (attrdef->m_checkFunction != NULL || attrdef->m_type == KX_PYATTRIBUTE_TYPE_VECTOR)
{
@@ -576,11 +694,19 @@ int PyObjectPlus::py_set_attrdef(PyObject *self_py, PyObject *value, const PyAtt
bufferSize = sizeof(short);
break;
case KX_PYATTRIBUTE_TYPE_ENUM:
+ case KX_PYATTRIBUTE_TYPE_FLAG:
+ case KX_PYATTRIBUTE_TYPE_CHAR:
+ bufferSize = attrdef->m_size;
+ break;
case KX_PYATTRIBUTE_TYPE_INT:
bufferSize = sizeof(int);
break;
case KX_PYATTRIBUTE_TYPE_FLOAT:
bufferSize = sizeof(float);
+ if (attrdef->m_imax)
+ bufferSize *= attrdef->m_imax;
+ if (attrdef->m_imin)
+ bufferSize *= attrdef->m_imin;
break;
case KX_PYATTRIBUTE_TYPE_STRING:
sourceBuffer = reinterpret_cast<STR_String*>(ptr)->Ptr();
@@ -624,6 +750,49 @@ int PyObjectPlus::py_set_attrdef(PyObject *self_py, PyObject *value, const PyAtt
}
break;
}
+ case KX_PYATTRIBUTE_TYPE_FLAG:
+ {
+ bool bval;
+ if (PyLong_Check(value))
+ {
+ bval = (PyLong_AsSsize_t(value) != 0);
+ }
+ else if (PyBool_Check(value))
+ {
+ bval = (value == Py_True);
+ }
+ else
+ {
+ PyErr_Format(PyExc_TypeError, "expected an integer or a bool for attribute \"%s\"", attrdef->m_name);
+ goto FREE_AND_ERROR;
+ }
+ if (attrdef->m_imax)
+ bval = !bval;
+ switch (attrdef->m_size) {
+ case 1:
+ {
+ unsigned char *val = reinterpret_cast<unsigned char*>(ptr);
+ *val = (*val & ~attrdef->m_imin) | ((bval)?attrdef->m_imin:0);
+ break;
+ }
+ case 2:
+ {
+ unsigned short *val = reinterpret_cast<unsigned short*>(ptr);
+ *val = (*val & ~attrdef->m_imin) | ((bval)?attrdef->m_imin:0);
+ break;
+ }
+ case 4:
+ {
+ unsigned int *val = reinterpret_cast<unsigned int*>(ptr);
+ *val = (*val & ~attrdef->m_imin) | ((bval)?attrdef->m_imin:0);
+ break;
+ }
+ default:
+ PyErr_Format(PyExc_TypeError, "internal error: unsupported flag field \"%s\"", attrdef->m_name);
+ goto FREE_AND_ERROR;
+ }
+ break;
+ }
case KX_PYATTRIBUTE_TYPE_SHORT:
{
short int *var = reinterpret_cast<short int*>(ptr);
@@ -689,25 +858,71 @@ int PyObjectPlus::py_set_attrdef(PyObject *self_py, PyObject *value, const PyAtt
case KX_PYATTRIBUTE_TYPE_FLOAT:
{
float *var = reinterpret_cast<float*>(ptr);
- double val = PyFloat_AsDouble(value);
- if (val == -1.0 && PyErr_Occurred())
+ if (attrdef->m_imin != 0)
{
- PyErr_Format(PyExc_TypeError, "expected a float for attribute \"%s\"", attrdef->m_name);
- goto FREE_AND_ERROR;
- }
- else if (attrdef->m_clamp)
+ if (attrdef->m_size != attrdef->m_imin*attrdef->m_imax*sizeof(float))
+ {
+ PyErr_Format(PyExc_TypeError, "internal error: incorrect field size for attribute \"%s\"", attrdef->m_name);
+ goto FREE_AND_ERROR;
+ }
+ if (!PySequence_Check(value) || PySequence_Size(value) != attrdef->m_imin)
+ {
+ PyErr_Format(PyExc_TypeError, "expected a sequence of [%d][%d] floats for attribute \"%s\"", attrdef->m_imin, attrdef->m_imax, attrdef->m_name);
+ goto FREE_AND_ERROR;
+ }
+ for (int i=0; i<attrdef->m_imin; i++)
+ {
+ PyObject *list = PySequence_GetItem(value, i); /* new ref */
+ if (!PySequence_Check(list) || PySequence_Size(list) != attrdef->m_imax)
+ {
+ PyErr_Format(PyExc_TypeError, "expected a sequence of [%d][%d] floats for attribute \"%s\"", attrdef->m_imin, attrdef->m_imax, attrdef->m_name);
+ goto RESTORE_AND_ERROR;
+ }
+ for (int j=0; j<attrdef->m_imax; j++)
+ {
+ item = PySequence_GetItem(list, j); /* new ref */
+ if (!py_check_attr_float(var, item, attrdef))
+ {
+ PyErr_Format(PyExc_TypeError, "expected a sequence of [%d][%d] floats for attribute \"%s\"", attrdef->m_imin, attrdef->m_imax, attrdef->m_name);
+ goto RESTORE_AND_ERROR;
+ }
+ Py_DECREF(item);
+ item = NULL;
+ ++var;
+ }
+ Py_DECREF(list);
+ list = NULL;
+ }
+ }
+ else if (attrdef->m_imax != 0)
{
- if (val < attrdef->m_fmin)
- val = attrdef->m_fmin;
- else if (val > attrdef->m_fmax)
- val = attrdef->m_fmax;
- }
- else if (val < attrdef->m_fmin || val > attrdef->m_fmax)
+ if (attrdef->m_size != attrdef->m_imax*sizeof(float))
+ {
+ PyErr_Format(PyExc_TypeError, "internal error: incorrect field size for attribute \"%s\"", attrdef->m_name);
+ goto FREE_AND_ERROR;
+ }
+ if (!PySequence_Check(value) || PySequence_Size(value) != attrdef->m_imax)
+ {
+ PyErr_Format(PyExc_TypeError, "expected a sequence of [%d] floats for attribute \"%s\"", attrdef->m_imax, attrdef->m_name);
+ goto FREE_AND_ERROR;
+ }
+ for (int i=0; i<attrdef->m_imax; i++)
+ {
+ item = PySequence_GetItem(value, i); /* new ref */
+ if (!py_check_attr_float(var, item, attrdef))
+ {
+ goto RESTORE_AND_ERROR;
+ }
+ Py_DECREF(item);
+ item = NULL;
+ ++var;
+ }
+ }
+ else
{
- PyErr_Format(PyExc_ValueError, "value out of range for attribute \"%s\"", attrdef->m_name);
- goto FREE_AND_ERROR;
+ if (!py_check_attr_float(var, value, attrdef))
+ goto FREE_AND_ERROR;
}
- *var = (float)val;
break;
}
case KX_PYATTRIBUTE_TYPE_VECTOR:
@@ -715,16 +930,15 @@ int PyObjectPlus::py_set_attrdef(PyObject *self_py, PyObject *value, const PyAtt
if (!PySequence_Check(value) || PySequence_Size(value) != 3)
{
PyErr_Format(PyExc_TypeError, "expected a sequence of 3 floats for attribute \"%s\"", attrdef->m_name);
- return PY_SET_ATTR_FAIL;
+ goto FREE_AND_ERROR;
}
MT_Vector3 *var = reinterpret_cast<MT_Vector3*>(ptr);
for (int i=0; i<3; i++)
{
- PyObject *item = PySequence_GetItem(value, i); /* new ref */
- // we can decrement the reference immediately, the reference count
- // is at least 1 because the item is part of an array
- Py_DECREF(item);
+ item = PySequence_GetItem(value, i); /* new ref */
double val = PyFloat_AsDouble(item);
+ Py_DECREF(item);
+ item = NULL;
if (val == -1.0 && PyErr_Occurred())
{
PyErr_Format(PyExc_TypeError, "expected a sequence of 3 floats for attribute \"%s\"", attrdef->m_name);
@@ -746,6 +960,22 @@ int PyObjectPlus::py_set_attrdef(PyObject *self_py, PyObject *value, const PyAtt
}
break;
}
+ case KX_PYATTRIBUTE_TYPE_CHAR:
+ {
+ if (PyUnicode_Check(value))
+ {
+ Py_ssize_t val_len;
+ char *val = _PyUnicode_AsStringAndSize(value, &val_len);
+ strncpy(ptr, val, attrdef->m_size);
+ ptr[attrdef->m_size-1] = 0;
+ }
+ else
+ {
+ PyErr_Format(PyExc_TypeError, "expected a string for attribute \"%s\"", attrdef->m_name);
+ goto FREE_AND_ERROR;
+ }
+ break;
+ }
case KX_PYATTRIBUTE_TYPE_STRING:
{
STR_String *var = reinterpret_cast<STR_String*>(ptr);
@@ -793,7 +1023,7 @@ int PyObjectPlus::py_set_attrdef(PyObject *self_py, PyObject *value, const PyAtt
// check if post processing is needed
if (attrdef->m_checkFunction != NULL)
{
- if ((*attrdef->m_checkFunction)(self, attrdef) != 0)
+ if ((*attrdef->m_checkFunction)(ref, attrdef) != 0)
{
// restore value
RESTORE_AND_ERROR:
@@ -814,6 +1044,10 @@ int PyObjectPlus::py_set_attrdef(PyObject *self_py, PyObject *value, const PyAtt
FREE_AND_ERROR:
if (undoBuffer)
free(undoBuffer);
+ if (list)
+ Py_DECREF(list);
+ if (item)
+ Py_DECREF(item);
return 1;
}
}
@@ -857,23 +1091,35 @@ void PyObjectPlus::InvalidateProxy() // check typename of each parent
}
}
-PyObject *PyObjectPlus::GetProxy_Ext(PyObjectPlus *self, PyTypeObject *tp)
+PyObject *PyObjectPlus::GetProxyPlus_Ext(PyObjectPlus *self, PyTypeObject *tp, void *ptr)
{
if (self->m_proxy==NULL)
{
self->m_proxy = reinterpret_cast<PyObject *>PyObject_NEW( PyObjectPlus_Proxy, tp);
BGE_PROXY_PYOWNS(self->m_proxy) = false;
+ BGE_PROXY_PYREF(self->m_proxy) = true;
}
//PyObject_Print(self->m_proxy, stdout, 0);
//printf("ref %d\n", self->m_proxy->ob_refcnt);
BGE_PROXY_REF(self->m_proxy) = self; /* Its possible this was set to NULL, so set it back here */
+ BGE_PROXY_PTR(self->m_proxy) = ptr;
Py_INCREF(self->m_proxy); /* we own one, thos ones fore the return */
return self->m_proxy;
}
-PyObject *PyObjectPlus::NewProxy_Ext(PyObjectPlus *self, PyTypeObject *tp, bool py_owns)
+PyObject *PyObjectPlus::NewProxyPlus_Ext(PyObjectPlus *self, PyTypeObject *tp, void *ptr, bool py_owns)
{
+ if (!self)
+ {
+ // in case of proxy without reference to game object
+ PyObject* proxy = reinterpret_cast<PyObject *>PyObject_NEW( PyObjectPlus_Proxy, tp);
+ BGE_PROXY_PYREF(proxy) = false;
+ BGE_PROXY_PYOWNS(proxy) = py_owns;
+ BGE_PROXY_REF(proxy) = NULL;
+ BGE_PROXY_PTR(proxy) = ptr;
+ return proxy;
+ }
if (self->m_proxy)
{
if(py_owns)
@@ -889,7 +1135,7 @@ PyObject *PyObjectPlus::NewProxy_Ext(PyObjectPlus *self, PyTypeObject *tp, bool
}
- GetProxy_Ext(self, tp);
+ GetProxyPlus_Ext(self, tp, ptr);
if(py_owns) {
BGE_PROXY_PYOWNS(self->m_proxy) = py_owns;
Py_DECREF(self->m_proxy); /* could avoid thrashing here but for now its ok */
diff --git a/source/gameengine/Expressions/PyObjectPlus.h b/source/gameengine/Expressions/PyObjectPlus.h
index d8ab007dde9..237f01f5a3a 100644
--- a/source/gameengine/Expressions/PyObjectPlus.h
+++ b/source/gameengine/Expressions/PyObjectPlus.h
@@ -88,13 +88,17 @@ typedef struct {
typedef struct PyObjectPlus_Proxy {
PyObject_HEAD /* required python macro */
- class PyObjectPlus *ref;
- bool py_owns;
+ class PyObjectPlus *ref; // pointer to GE object, it holds a reference to this proxy
+ void *ptr; // optional pointer to generic structure, the structure holds no reference to this proxy
+ bool py_owns; // true if the object pointed by ref should be deleted when the proxy is deleted
+ bool py_ref; // true if proxy is connected to a GE object (ref is used)
} PyObjectPlus_Proxy;
#define BGE_PROXY_ERROR_MSG "Blender Game Engine data has been freed, cannot use this python variable"
#define BGE_PROXY_REF(_self) (((PyObjectPlus_Proxy *)_self)->ref)
+#define BGE_PROXY_PTR(_self) (((PyObjectPlus_Proxy *)_self)->ptr)
#define BGE_PROXY_PYOWNS(_self) (((PyObjectPlus_Proxy *)_self)->py_owns)
+#define BGE_PROXY_PYREF(_self) (((PyObjectPlus_Proxy *)_self)->py_ref)
/* Note, sometimes we dont care what BGE type this is as long as its a proxy */
#define BGE_PROXY_CHECK_TYPE(_type) ((_type)->tp_dealloc == PyObjectPlus::py_base_dealloc)
@@ -103,27 +107,51 @@ typedef struct PyObjectPlus_Proxy {
#define BGE_PROXY_FROM_REF(_self) (((PyObjectPlus *)_self)->GetProxy())
- // This must be the first line of each
- // PyC++ class
+// This must be the first line of each
+// PyC++ class
+// AttributesPtr correspond to attributes of proxy generic pointer
+// each PyC++ class must be registered in KX_PythonInitTypes.cpp
#define __Py_Header \
public: \
static PyTypeObject Type; \
static PyMethodDef Methods[]; \
static PyAttributeDef Attributes[]; \
virtual PyTypeObject *GetType(void) {return &Type;}; \
- virtual PyObject *GetProxy() {return GetProxy_Ext(this, &Type);}; \
- virtual PyObject *NewProxy(bool py_owns) {return NewProxy_Ext(this, &Type, py_owns);};
+ virtual PyObject *GetProxy() {return GetProxyPlus_Ext(this, &Type, NULL);}; \
+ virtual PyObject *NewProxy(bool py_owns) {return NewProxyPlus_Ext(this, &Type, NULL, py_owns);}; \
+// leave above line empty (macro)!
+// use this macro for class that use generic pointer in proxy
+// GetProxy() and NewProxy() must be defined to set the correct pointer in the proxy
+#define __Py_HeaderPtr \
+ public: \
+ static PyTypeObject Type; \
+ static PyMethodDef Methods[]; \
+ static PyAttributeDef Attributes[]; \
+ static PyAttributeDef AttributesPtr[]; \
+ virtual PyTypeObject *GetType(void) {return &Type;}; \
+ virtual PyObject *GetProxy(); \
+ virtual PyObject *NewProxy(bool py_owns); \
+// leave above line empty (macro)!
#ifdef WITH_CXX_GUARDEDALLOC
#define Py_Header __Py_Header \
void *operator new( unsigned int num_bytes) { return MEM_mallocN(num_bytes, Type.tp_name); } \
- void operator delete( void *mem ) { MEM_freeN(mem); }
+ void operator delete( void *mem ) { MEM_freeN(mem); } \
#else
#define Py_Header __Py_Header
#endif
+#ifdef WITH_CXX_GUARDEDALLOC
+#define Py_HeaderPtr __Py_HeaderPtr \
+ void *operator new( unsigned int num_bytes) { return MEM_mallocN(num_bytes, Type.tp_name); } \
+ void operator delete( void *mem ) { MEM_freeN(mem); } \
+
+#else
+#define Py_HeaderPtr __Py_HeaderPtr
+#endif
+
/*
* nonzero values are an error for setattr
* however because of the nested lookups we need to know if the errors
@@ -245,6 +273,8 @@ enum KX_PYATTRIBUTE_TYPE {
KX_PYATTRIBUTE_TYPE_DUMMY,
KX_PYATTRIBUTE_TYPE_FUNCTION,
KX_PYATTRIBUTE_TYPE_VECTOR,
+ KX_PYATTRIBUTE_TYPE_FLAG,
+ KX_PYATTRIBUTE_TYPE_CHAR,
};
enum KX_PYATTRIBUTE_ACCESS {
@@ -261,11 +291,14 @@ typedef struct KX_PYATTRIBUTE_DEF {
const char *m_name; // name of the python attribute
KX_PYATTRIBUTE_TYPE m_type; // type of value
KX_PYATTRIBUTE_ACCESS m_access; // read/write access or read-only
- int m_imin; // minimum value in case of integer attributes (for string: minimum string length)
- int m_imax; // maximum value in case of integer attributes (for string: maximum string length)
+ int m_imin; // minimum value in case of integer attributes
+ // (for string: minimum string length, for flag: mask value, for float: matrix row size)
+ int m_imax; // maximum value in case of integer attributes
+ // (for string: maximum string length, for flag: 1 if flag is negative, float: vector/matrix col size)
float m_fmin; // minimum value in case of float attributes
float m_fmax; // maximum value in case of float attributes
bool m_clamp; // enforce min/max value by clamping
+ bool m_usePtr; // the attribute uses the proxy generic pointer, set at runtime
size_t m_offset; // position of field in structure
size_t m_size; // size of field for runtime verification (enum only)
size_t m_length; // length of array, 1=simple attribute
@@ -283,104 +316,146 @@ typedef struct KX_PYATTRIBUTE_DEF {
float *m_floatPtr;
STR_String *m_stringPtr;
MT_Vector3 *m_vectorPtr;
+ char *m_charPtr;
} m_typeCheck;
} PyAttributeDef;
#define KX_PYATTRIBUTE_DUMMY(name) \
- { name, KX_PYATTRIBUTE_TYPE_DUMMY, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, 0, 0, 1, NULL, NULL, NULL, {NULL, NULL, NULL, NULL, NULL, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_DUMMY, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, false, 0, 0, 1, NULL, NULL, NULL, {NULL, NULL, NULL, NULL, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_BOOL_RW(name,object,field) \
- { name, KX_PYATTRIBUTE_TYPE_BOOL, KX_PYATTRIBUTE_RW, 0, 1, 0.f, 0.f, false, offsetof(object,field), 0, 1, NULL, NULL, NULL, {&((object *)0)->field, NULL, NULL, NULL, NULL, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_BOOL, KX_PYATTRIBUTE_RW, 0, 1, 0.f, 0.f, false, false, offsetof(object,field), 0, 1, NULL, NULL, NULL, {&((object *)0)->field, NULL, NULL, NULL, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_BOOL_RW_CHECK(name,object,field,function) \
- { name, KX_PYATTRIBUTE_TYPE_BOOL, KX_PYATTRIBUTE_RW, 0, 1, 0.f, 0.f, false, offsetof(object,field), 0, 1, &object::function, NULL, NULL, {&((object *)0)->field, NULL, NULL, NULL, NULL, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_BOOL, KX_PYATTRIBUTE_RW, 0, 1, 0.f, 0.f, false, false, offsetof(object,field), 0, 1, &object::function, NULL, NULL, {&((object *)0)->field, NULL, NULL, NULL, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_BOOL_RO(name,object,field) \
- { name, KX_PYATTRIBUTE_TYPE_BOOL, KX_PYATTRIBUTE_RO, 0, 1, 0.f, 0.f, false, offsetof(object,field), 0, 1, NULL, NULL, NULL, {&((object *)0)->field, NULL, NULL, NULL, NULL, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_BOOL, KX_PYATTRIBUTE_RO, 0, 1, 0.f, 0.f, false, false, offsetof(object,field), 0, 1, NULL, NULL, NULL, {&((object *)0)->field, NULL, NULL, NULL, NULL, NULL, NULL} }
+
+/* attribute points to a single bit of an integer field, attribute=true if bit is set */
+#define KX_PYATTRIBUTE_FLAG_RW(name,object,field,bit) \
+ { name, KX_PYATTRIBUTE_TYPE_FLAG, KX_PYATTRIBUTE_RW, bit, 0, 0.f, 0.f, false, false, offsetof(object,field), sizeof(((object *)0)->field), 1, NULL, NULL, NULL, {NULL, NULL, NULL, NULL, NULL, NULL, NULL} }
+#define KX_PYATTRIBUTE_FLAG_RW_CHECK(name,object,field,bit,function) \
+ { name, KX_PYATTRIBUTE_TYPE_FLAG, KX_PYATTRIBUTE_RW, bit, 0, 0.f, 0.f, false, false, offsetof(object,field), sizeof(((object *)0)->field), 1, &object::function, NULL, NULL, {NULL, NULL, NULL, NULL, NULL, NULL, NULL} }
+#define KX_PYATTRIBUTE_FLAG_RO(name,object,field,bit) \
+ { name, KX_PYATTRIBUTE_TYPE_FLAG, KX_PYATTRIBUTE_RO, bit, 0, 0.f, 0.f, false, false, offsetof(object,field), sizeof(((object *)0)->field), 1, NULL, NULL, NULL, {NULL, NULL, NULL, NULL, NULL, NULL, NULL} }
+
+/* attribute points to a single bit of an integer field, attribute=true if bit is set*/
+#define KX_PYATTRIBUTE_FLAG_NEGATIVE_RW(name,object,field,bit) \
+ { name, KX_PYATTRIBUTE_TYPE_FLAG, KX_PYATTRIBUTE_RW, bit, 1, 0.f, 0.f, false, false, offsetof(object,field), sizeof(((object *)0)->field), 1, NULL, NULL, NULL, {NULL, NULL, NULL, NULL, NULL, NULL, NULL} }
+#define KX_PYATTRIBUTE_FLAG_NEGATIVE_RW_CHECK(name,object,field,bit,function) \
+ { name, KX_PYATTRIBUTE_TYPE_FLAG, KX_PYATTRIBUTE_RW, bit, 1, 0.f, 0.f, false, false, offsetof(object,field), sizeof(((object *)0)->field), 1, &object::function, NULL, NULL, {NULL, NULL, NULL, NULL, NULL, NULL, NULL} }
+#define KX_PYATTRIBUTE_FLAG_NEGATIVE_RO(name,object,field,bit) \
+ { name, KX_PYATTRIBUTE_TYPE_FLAG, KX_PYATTRIBUTE_RO, bit, 1, 0.f, 0.f, false, false, offsetof(object,field), sizeof(((object *)0)->field), 1, NULL, NULL, NULL, {NULL, NULL, NULL, NULL, NULL, NULL, NULL} }
// enum field cannot be mapped to pointer (because we would need a pointer for each enum)
// use field size to verify mapping at runtime only, assuming enum size is equal to int size.
#define KX_PYATTRIBUTE_ENUM_RW(name,min,max,clamp,object,field) \
- { name, KX_PYATTRIBUTE_TYPE_ENUM, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, offsetof(object,field), sizeof(((object *)0)->field), 1, NULL, NULL, NULL, {NULL, NULL, NULL, NULL, NULL, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_ENUM, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, false, offsetof(object,field), sizeof(((object *)0)->field), 1, NULL, NULL, NULL, {NULL, NULL, NULL, NULL, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_ENUM_RW_CHECK(name,min,max,clamp,object,field,function) \
- { name, KX_PYATTRIBUTE_TYPE_ENUM, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, offsetof(object,field), sizeof(((object *)0)->field), 1, &object::function, NULL, NULL, {NULL, NULL, NULL, NULL, NULL, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_ENUM, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, false, offsetof(object,field), sizeof(((object *)0)->field), 1, &object::function, NULL, NULL, {NULL, NULL, NULL, NULL, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_ENUM_RO(name,object,field) \
- { name, KX_PYATTRIBUTE_TYPE_ENUM, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, offsetof(object,field), sizeof(((object *)0)->field), 1, NULL, NULL, NULL, {NULL, NULL, NULL, NULL, NULL, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_ENUM, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, false, offsetof(object,field), sizeof(((object *)0)->field), 1, NULL, NULL, NULL, {NULL, NULL, NULL, NULL, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_SHORT_RW(name,min,max,clamp,object,field) \
- { name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, offsetof(object,field), 0, 1, NULL, NULL, NULL, {NULL, &((object *)0)->field, NULL, NULL, NULL, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, false, offsetof(object,field), 0, 1, NULL, NULL, NULL, {NULL, &((object *)0)->field, NULL, NULL, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_SHORT_RW_CHECK(name,min,max,clamp,object,field,function) \
- { name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, offsetof(object,field), 0, 1, &object::function, NULL, NULL, {NULL, &((object *)0)->field, NULL, NULL, NULL, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, false, offsetof(object,field), 0, 1, &object::function, NULL, NULL, {NULL, &((object *)0)->field, NULL, NULL, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_SHORT_RO(name,object,field) \
- { name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, offsetof(object,field), 0, 1, NULL, NULL, NULL, {NULL, &((object *)0)->field, NULL, NULL, NULL, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, false, offsetof(object,field), 0, 1, NULL, NULL, NULL, {NULL, &((object *)0)->field, NULL, NULL, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_SHORT_ARRAY_RW(name,min,max,clamp,object,field,length) \
- { name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, offsetof(object,field), 0, length, NULL, NULL, NULL, {NULL, ((object *)0)->field, NULL, NULL, NULL, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, false, offsetof(object,field), 0, length, NULL, NULL, NULL, {NULL, ((object *)0)->field, NULL, NULL, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_SHORT_ARRAY_RW_CHECK(name,min,max,clamp,object,field,length,function) \
- { name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, offsetof(object,field), 0, length, &object::function, NULL, NULL, {NULL, ((object *)0)->field, NULL, NULL, NULL, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, false, offsetof(object,field), 0, length, &object::function, NULL, NULL, {NULL, ((object *)0)->field, NULL, NULL, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_SHORT_ARRAY_RO(name,object,field,length) \
- { name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, offsetof(object,field), 0, length, NULL, NULL, NULL, {NULL, ((object *)0)->field, NULL, NULL, NULL, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, false, offsetof(object,field), 0, length, NULL, NULL, NULL, {NULL, ((object *)0)->field, NULL, NULL, NULL, NULL, NULL} }
// SHORT_LIST
#define KX_PYATTRIBUTE_SHORT_LIST_RW(name,min,max,clamp,object,field,length) \
- { name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, offsetof(object,field), 0, length, NULL, NULL, NULL, {NULL, &((object *)0)->field, NULL, NULL, NULL, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, false, offsetof(object,field), 0, length, NULL, NULL, NULL, {NULL, &((object *)0)->field, NULL, NULL, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_SHORT_LIST_RW_CHECK(name,min,max,clamp,object,field,length,function) \
- { name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, offsetof(object,field), 0, length, &object::function, NULL, NULL, {NULL, &((object *)0)->field, NULL, NULL, NULL, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, false, offsetof(object,field), 0, length, &object::function, NULL, NULL, {NULL, &((object *)0)->field, NULL, NULL, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_SHORT_LIST_RO(name,object,field,length) \
- { name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, offsetof(object,field), 0, length, NULL, NULL, NULL, {NULL, &((object *)0)->field, NULL, NULL, NULL, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_SHORT, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, false, offsetof(object,field), 0, length, NULL, NULL, NULL, {NULL, &((object *)0)->field, NULL, NULL, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_INT_RW(name,min,max,clamp,object,field) \
- { name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, offsetof(object,field), 0, 1, NULL, NULL, NULL, {NULL, NULL, &((object *)0)->field, NULL, NULL, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, false, offsetof(object,field), 0, 1, NULL, NULL, NULL, {NULL, NULL, &((object *)0)->field, NULL, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_INT_RW_CHECK(name,min,max,clamp,object,field,function) \
- { name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, offsetof(object,field), 0, 1, &object::function, NULL, NULL, {NULL, NULL, &((object *)0)->field, NULL, NULL, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, false, offsetof(object,field), 0, 1, &object::function, NULL, NULL, {NULL, NULL, &((object *)0)->field, NULL, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_INT_RO(name,object,field) \
- { name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, offsetof(object,field), 0, 1, NULL, NULL, NULL, {NULL, NULL, &((object *)0)->field, NULL, NULL, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, false, offsetof(object,field), 0, 1, NULL, NULL, NULL, {NULL, NULL, &((object *)0)->field, NULL, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_INT_ARRAY_RW(name,min,max,clamp,object,field,length) \
- { name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, offsetof(object,field), 0, length, NULL, NULL, NULL, {NULL, NULL, ((object *)0)->field, NULL, NULL, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, false, offsetof(object,field), 0, length, NULL, NULL, NULL, {NULL, NULL, ((object *)0)->field, NULL, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_INT_ARRAY_RW_CHECK(name,min,max,clamp,object,field,length,function) \
- { name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, offsetof(object,field), 0, length, &object::function, NULL, NULL, {NULL, NULL, ((object *)0)->field, NULL, NULL, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, false, offsetof(object,field), 0, length, &object::function, NULL, NULL, {NULL, NULL, ((object *)0)->field, NULL, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_INT_ARRAY_RO(name,object,field,length) \
- { name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, offsetof(object,field), 0, length, NULL, NULL, NULL, {NULL, NULL, ((object *)0)->field, NULL, NULL, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, false, offsetof(object,field), 0, length, NULL, NULL, NULL, {NULL, NULL, ((object *)0)->field, NULL, NULL, NULL, NULL} }
// INT_LIST
#define KX_PYATTRIBUTE_INT_LIST_RW(name,min,max,clamp,object,field,length) \
- { name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, offsetof(object,field), 0, length, NULL, NULL, NULL, {NULL, NULL, &((object *)0)->field, NULL, NULL, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, false, offsetof(object,field), 0, length, NULL, NULL, NULL, {NULL, NULL, &((object *)0)->field, NULL, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_INT_LIST_RW_CHECK(name,min,max,clamp,object,field,length,function) \
- { name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, offsetof(object,field), 0, length, &object::function, NULL, NULL, {NULL, NULL, &((object *)0)->field, NULL, NULL, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, false, offsetof(object,field), 0, length, &object::function, NULL, NULL, {NULL, NULL, &((object *)0)->field, NULL, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_INT_LIST_RO(name,object,field,length) \
- { name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, offsetof(object,field), 0, length, NULL, NULL, NULL, {NULL, NULL, &((object *)0)->field, NULL, NULL, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_INT, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, false, offsetof(object,field), 0, length, NULL, NULL, NULL, {NULL, NULL, &((object *)0)->field, NULL, NULL, NULL, NULL} }
// always clamp for float
#define KX_PYATTRIBUTE_FLOAT_RW(name,min,max,object,field) \
- { name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RW, 0, 0, min, max, true, offsetof(object,field), 0, 1, NULL, NULL, NULL, {NULL, NULL, NULL, &((object *)0)->field, NULL, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RW, 0, 0, min, max, true, false, offsetof(object,field), 0, 1, NULL, NULL, NULL, {NULL, NULL, NULL, &((object *)0)->field, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_FLOAT_RW_CHECK(name,min,max,object,field,function) \
- { name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RW, 0, 0, min, max, true, offsetof(object,field), 0, 1, &object::function, NULL, NULL, {NULL, NULL, NULL, &((object *)0)->field, NULL, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RW, 0, 0, min, max, true, false, offsetof(object,field), 0, 1, &object::function, NULL, NULL, {NULL, NULL, NULL, &((object *)0)->field, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_FLOAT_RO(name,object,field) \
- { name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, offsetof(object,field), 0, 1, NULL, NULL, NULL, {NULL, NULL, NULL, &((object *)0)->field, NULL, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, false, offsetof(object,field), 0, 1, NULL, NULL, NULL, {NULL, NULL, NULL, &((object *)0)->field, NULL, NULL, NULL} }
+// field must be float[n], returns a sequence
#define KX_PYATTRIBUTE_FLOAT_ARRAY_RW(name,min,max,object,field,length) \
- { name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RW, 0, 0, min, max, true, offsetof(object,field), 0, length, NULL, NULL, NULL, {NULL, NULL, NULL, ((object *)0)->field, NULL, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RW, 0, 0, min, max, true, false, offsetof(object,field), 0, length, NULL, NULL, NULL, {NULL, NULL, NULL, ((object *)0)->field, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_FLOAT_ARRAY_RW_CHECK(name,min,max,object,field,length,function) \
- { name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RW, 0, 0, min, max, true, offsetof(object,field), 0, length, &object::function, NULL, NULL, {NULL, NULL, NULL, ((object *)0)->field, NULL, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RW, 0, 0, min, max, true, false, offsetof(object,field), 0, length, &object::function, NULL, NULL, {NULL, NULL, NULL, ((object *)0)->field, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_FLOAT_ARRAY_RO(name,object,field,length) \
- { name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, offsetof(object,field), 0, length, NULL, NULL, NULL, {NULL, NULL, NULL, ((object *)0)->field, NULL, NULL} }
-
+ { name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, false, offsetof(object,field), 0, length, NULL, NULL, NULL, {NULL, NULL, NULL, ((object *)0)->field, NULL, NULL, NULL} }
+// field must be float[n], returns a vector
+#define KX_PYATTRIBUTE_FLOAT_VECTOR_RW(name,min,max,object,field,length) \
+ { name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RW, 0, length, min, max, true, false, offsetof(object,field), sizeof(((object *)0)->field), 1, NULL, NULL, NULL, {NULL, NULL, NULL, ((object *)0)->field, NULL, NULL, NULL} }
+#define KX_PYATTRIBUTE_FLOAT_VECTOR_RW_CHECK(name,min,max,object,field,length,function) \
+ { name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RW, 0, length, min, max, true, false, offsetof(object,field), sizeof(((object *)0)->field), 1, &object::function, NULL, NULL, {NULL, NULL, NULL, ((object *)0)->field, NULL, NULL, NULL} }
+#define KX_PYATTRIBUTE_FLOAT_VECTOR_RO(name,object,field,length) \
+ { name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RO, 0, length, 0.f, 0.f, false, false, offsetof(object,field), sizeof(((object *)0)->field), 1, NULL, NULL, NULL, {NULL, NULL, NULL, ((object *)0)->field, NULL, NULL, NULL} }
+// field must be float[n][n], returns a matrix
+#define KX_PYATTRIBUTE_FLOAT_MATRIX_RW(name,min,max,object,field,length) \
+ { name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RW, length, length, min, max, true, false, offsetof(object,field), sizeof(((object *)0)->field), 1, NULL, NULL, NULL, {NULL, NULL, NULL, ((object *)0)->field[0], NULL, NULL, NULL} }
+#define KX_PYATTRIBUTE_FLOAT_MATRIX_RW_CHECK(name,min,max,object,field,length,function) \
+ { name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RW, length, length, min, max, true, false, offsetof(object,field), sizeof(((object *)0)->field), 1, &object::function, NULL, NULL, {NULL, NULL, NULL, ((object *)0)->field[0], NULL, NULL, NULL} }
+#define KX_PYATTRIBUTE_FLOAT_MATRIX_RO(name,object,field,length) \
+ { name, KX_PYATTRIBUTE_TYPE_FLOAT, KX_PYATTRIBUTE_RO, length, length, 0.f, 0.f, false, false, offsetof(object,field), sizeof(((object *)0)->field), 1, NULL, NULL, NULL, {NULL, NULL, NULL, ((object *)0)->field[0], NULL, NULL, NULL} }
+
+// only for STR_String member
#define KX_PYATTRIBUTE_STRING_RW(name,min,max,clamp,object,field) \
- { name, KX_PYATTRIBUTE_TYPE_STRING, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, offsetof(object,field), 0, 1, NULL, NULL, NULL, {NULL, NULL, NULL, NULL, &((object *)0)->field, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_STRING, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, false, offsetof(object,field), 0, 1, NULL, NULL, NULL, {NULL, NULL, NULL, NULL, &((object *)0)->field, NULL, NULL} }
#define KX_PYATTRIBUTE_STRING_RW_CHECK(name,min,max,clamp,object,field,function) \
- { name, KX_PYATTRIBUTE_TYPE_STRING, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, offsetof(object,field), 0, 1, &object::function, NULL, NULL, {NULL, NULL, NULL, NULL, &((object *)0)->field, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_STRING, KX_PYATTRIBUTE_RW, min, max, 0.f, 0.f, clamp, false, offsetof(object,field), 0, 1, &object::function, NULL, NULL, {NULL, NULL, NULL, NULL, &((object *)0)->field, NULL, NULL} }
#define KX_PYATTRIBUTE_STRING_RO(name,object,field) \
- { name, KX_PYATTRIBUTE_TYPE_STRING, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, offsetof(object,field), 0, 1 , NULL, NULL, NULL, {NULL, NULL, NULL, NULL, &((object *)0)->field, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_STRING, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, false, offsetof(object,field), 0, 1 , NULL, NULL, NULL, {NULL, NULL, NULL, NULL, &((object *)0)->field, NULL, NULL} }
+
+// only for char [] array
+#define KX_PYATTRIBUTE_CHAR_RW(name,object,field) \
+ { name, KX_PYATTRIBUTE_TYPE_CHAR, KX_PYATTRIBUTE_RW, 0, 0, 0.f, 0.f, true, false, offsetof(object,field), sizeof(((object *)0)->field), 1, NULL, NULL, NULL, {NULL, NULL, NULL, NULL, NULL, NULL, ((object *)0)->field} }
+#define KX_PYATTRIBUTE_CHAR_RW_CHECK(name,object,field,function) \
+ { name, KX_PYATTRIBUTE_TYPE_CHAR, KX_PYATTRIBUTE_RW, 0, 0, 0.f, 0.f, true, false, offsetof(object,field), sizeof(((object *)0)->field), 1, &object::function, NULL, NULL, {NULL, NULL, NULL, NULL, NULL, NULL, ((object *)0)->field} }
+#define KX_PYATTRIBUTE_CHAR_RO(name,object,field) \
+ { name, KX_PYATTRIBUTE_TYPE_CHAR, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, false, offsetof(object,field), sizeof(((object *)0)->field), 1 , NULL, NULL, NULL, {NULL, NULL, NULL, NULL, NULL, NULL, ((object *)0)->field} }
+// for MT_Vector3 member
#define KX_PYATTRIBUTE_VECTOR_RW(name,min,max,object,field) \
- { name, KX_PYATTRIBUTE_TYPE_VECTOR, KX_PYATTRIBUTE_RW, 0, 0, min, max, true, offsetof(object,field), 0, 1, NULL, NULL, NULL, {NULL, NULL, NULL, NULL, NULL, &((object *)0)->field} }
+ { name, KX_PYATTRIBUTE_TYPE_VECTOR, KX_PYATTRIBUTE_RW, 0, 0, min, max, true, false, offsetof(object,field), 0, 1, NULL, NULL, NULL, {NULL, NULL, NULL, NULL, NULL, &((object *)0)->field, NULL} }
#define KX_PYATTRIBUTE_VECTOR_RW_CHECK(name,min,max,clamp,object,field,function) \
- { name, KX_PYATTRIBUTE_TYPE_VECTOR, KX_PYATTRIBUTE_RW, 0, 0, min, max, true, offsetof(object,field), 0, 1, &object::function, NULL, NULL, {NULL, NULL, NULL, NULL, NULL, &((object *)0)->field} }
+ { name, KX_PYATTRIBUTE_TYPE_VECTOR, KX_PYATTRIBUTE_RW, 0, 0, min, max, true, false, offsetof(object,field), 0, 1, &object::function, NULL, NULL, {NULL, NULL, NULL, NULL, NULL, &((object *)0)->field, NULL} }
#define KX_PYATTRIBUTE_VECTOR_RO(name,object,field) \
- { name, KX_PYATTRIBUTE_TYPE_VECTOR, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, offsetof(object,field), 0, 1 , NULL, NULL, NULL, {NULL, NULL, NULL, NULL, NULL, &((object *)0)->field} }
+ { name, KX_PYATTRIBUTE_TYPE_VECTOR, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, false, offsetof(object,field), 0, 1 , NULL, NULL, NULL, {NULL, NULL, NULL, NULL, NULL, &((object *)0)->field, NULL} }
#define KX_PYATTRIBUTE_RW_FUNCTION(name,object,getfunction,setfunction) \
- { name, KX_PYATTRIBUTE_TYPE_FUNCTION, KX_PYATTRIBUTE_RW, 0, 0, 0.f, 0.f, false, 0, 0, 1, NULL, &object::setfunction, &object::getfunction, {NULL, NULL, NULL, NULL, NULL, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_FUNCTION, KX_PYATTRIBUTE_RW, 0, 0, 0.f, 0.f, false, false, 0, 0, 1, NULL, &object::setfunction, &object::getfunction, {NULL, NULL, NULL, NULL, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_RO_FUNCTION(name,object,getfunction) \
- { name, KX_PYATTRIBUTE_TYPE_FUNCTION, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, 0, 0, 1, NULL, NULL, &object::getfunction, {NULL, NULL, NULL, NULL, NULL, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_FUNCTION, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0.f, false, false, 0, 0, 1, NULL, NULL, &object::getfunction, {NULL, NULL, NULL, NULL, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_ARRAY_RW_FUNCTION(name,object,length,getfunction,setfunction) \
- { name, KX_PYATTRIBUTE_TYPE_FUNCTION, KX_PYATTRIBUTE_RW, 0, 0, 0.f, 0,f, false, 0, 0, length, NULL, &object::setfunction, &object::getfunction, {NULL, NULL, NULL, NULL, NULL, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_FUNCTION, KX_PYATTRIBUTE_RW, 0, 0, 0.f, 0,f, false, false, 0, 0, length, NULL, &object::setfunction, &object::getfunction, {NULL, NULL, NULL, NULL, NULL, NULL, NULL} }
#define KX_PYATTRIBUTE_ARRAY_RO_FUNCTION(name,object,length,getfunction) \
- { name, KX_PYATTRIBUTE_TYPE_FUNCTION, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0,f, false, 0, 0, length, NULL, NULL, &object::getfunction, {NULL, NULL, NULL, NULL, NULL, NULL} }
+ { name, KX_PYATTRIBUTE_TYPE_FUNCTION, KX_PYATTRIBUTE_RO, 0, 0, 0.f, 0,f, false, false, 0, 0, length, NULL, NULL, &object::getfunction, {NULL, NULL, NULL, NULL, NULL, NULL, NULL} }
/*------------------------------
@@ -422,16 +497,23 @@ public:
/* These are all virtual python methods that are defined in each class
* Our own fake subclassing calls these on each class, then calls the parent */
virtual PyObject* py_repr(void);
+ /* subclass may overwrite this function to implement more sophisticated method of validating a proxy */
+ virtual bool py_is_valid(void) { return true; }
static PyObject* py_get_attrdef(PyObject *self_py, const PyAttributeDef *attrdef);
static int py_set_attrdef(PyObject *self_py, PyObject *value, const PyAttributeDef *attrdef);
/* Kindof dumb, always returns True, the false case is checked for, before this function gets accessed */
static PyObject* pyattr_get_invalid(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef);
-
- static PyObject *GetProxy_Ext(PyObjectPlus *self, PyTypeObject *tp);
- static PyObject *NewProxy_Ext(PyObjectPlus *self, PyTypeObject *tp, bool py_owns);
-
+
+ static PyObject *GetProxyPlus_Ext(PyObjectPlus *self, PyTypeObject *tp, void *ptr);
+ /* self=NULL => proxy to generic pointer detached from GE object
+ if py_owns is true, the memory pointed by ptr will be deleted automatially with MEM_freeN
+ self!=NULL=> proxy attached to GE object, ptr is optional and point to a struct from which attributes can be defined
+ if py_owns is true, the object will be deleted automatically, ptr will NOT be deleted
+ (assume object destructor takes care of it) */
+ static PyObject *NewProxyPlus_Ext(PyObjectPlus *self, PyTypeObject *tp, void *ptr, bool py_owns);
+
void InvalidateProxy();
/**
diff --git a/source/gameengine/Expressions/SConscript b/source/gameengine/Expressions/SConscript
index c819bfb0d3e..26cafb004b2 100644
--- a/source/gameengine/Expressions/SConscript
+++ b/source/gameengine/Expressions/SConscript
@@ -3,7 +3,7 @@ Import ('env')
sources = env.Glob('*.cpp')
-incs ='. #source/kernel/gen_system #intern/string #intern/moto/include #source/gameengine/SceneGraph #source/blender/blenloader'
+incs ='. #source/kernel/gen_system #intern/guardedalloc #intern/string #intern/moto/include #source/gameengine/SceneGraph #source/blender/blenloader'
incs += ' ' + env['BF_PYTHON_INC']
env.BlenderLib ( 'bf_expressions', sources, Split(incs), [], libtype=['core','player'], priority = [360,80], cxx_compileflags=env['BGE_CXXFLAGS'])
diff --git a/source/gameengine/GameLogic/SCA_2DFilterActuator.cpp b/source/gameengine/GameLogic/SCA_2DFilterActuator.cpp
index 9dfb5ddc38f..9b04e263350 100644
--- a/source/gameengine/GameLogic/SCA_2DFilterActuator.cpp
+++ b/source/gameengine/GameLogic/SCA_2DFilterActuator.cpp
@@ -43,7 +43,7 @@ SCA_2DFilterActuator::SCA_2DFilterActuator(
int int_arg,
RAS_IRasterizer* rasterizer,
RAS_IRenderTools* rendertools)
- : SCA_IActuator(gameobj),
+ : SCA_IActuator(gameobj, KX_ACT_2DFILTER),
m_type(type),
m_disableMotionBlur(flag),
m_float_arg(float_arg),
diff --git a/source/gameengine/GameLogic/SCA_BasicEventManager.cpp b/source/gameengine/GameLogic/SCA_BasicEventManager.cpp
new file mode 100644
index 00000000000..24cae439fb7
--- /dev/null
+++ b/source/gameengine/GameLogic/SCA_BasicEventManager.cpp
@@ -0,0 +1,62 @@
+/**
+ * Manager for 'always' events. Since always sensors can operate in pulse
+ * mode, they need to be activated.
+ *
+ * $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.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "SCA_BasicEventManager.h"
+#include "SCA_LogicManager.h"
+#include <vector>
+#include "SCA_ISensor.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+using namespace std;
+
+SCA_BasicEventManager::SCA_BasicEventManager(class SCA_LogicManager* logicmgr)
+ : SCA_EventManager(BASIC_EVENTMGR),
+ m_logicmgr(logicmgr)
+{
+}
+
+SCA_BasicEventManager::~SCA_BasicEventManager()
+{
+}
+
+void SCA_BasicEventManager::NextFrame()
+{
+ SG_DList::iterator<SCA_ISensor> it(m_sensors);
+ for (it.begin();!it.end();++it)
+ {
+ (*it)->Activate(m_logicmgr);
+ }
+}
+
diff --git a/source/gameengine/GameLogic/SCA_BasicEventManager.h b/source/gameengine/GameLogic/SCA_BasicEventManager.h
new file mode 100644
index 00000000000..1aae5f9be5c
--- /dev/null
+++ b/source/gameengine/GameLogic/SCA_BasicEventManager.h
@@ -0,0 +1,59 @@
+/**
+ * Manager for sensor that only need to call Update
+ *
+ * $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.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __SCA_BASICEVENTMGR
+#define __SCA_BASICEVENTMGR
+
+#include "SCA_EventManager.h"
+#include <vector>
+
+using namespace std;
+
+class SCA_BasicEventManager : public SCA_EventManager
+{
+ class SCA_LogicManager* m_logicmgr;
+
+public:
+ SCA_BasicEventManager(class SCA_LogicManager* logicmgr);
+ ~SCA_BasicEventManager();
+
+ virtual void NextFrame();
+
+
+#ifdef WITH_CXX_GUARDEDALLOC
+public:
+ void *operator new( unsigned int num_bytes) { return MEM_mallocN(num_bytes, "GE:SCA_BasicEventManager"); }
+ void operator delete( void *mem ) { MEM_freeN(mem); }
+#endif
+};
+
+#endif //__SCA_BASICEVENTMGR
+
diff --git a/source/gameengine/GameLogic/SCA_EventManager.h b/source/gameengine/GameLogic/SCA_EventManager.h
index 424150ffa63..f77b115ee03 100644
--- a/source/gameengine/GameLogic/SCA_EventManager.h
+++ b/source/gameengine/GameLogic/SCA_EventManager.h
@@ -52,10 +52,10 @@ public:
TIME_EVENTMGR,
RANDOM_EVENTMGR,
RAY_EVENTMGR,
- RADAR_EVENTMGR,
NETWORK_EVENTMGR,
JOY_EVENTMGR,
- ACTUATOR_EVENTMGR
+ ACTUATOR_EVENTMGR,
+ BASIC_EVENTMGR
};
SCA_EventManager(EVENT_MANAGER_TYPE mgrtype);
diff --git a/source/gameengine/GameLogic/SCA_IActuator.cpp b/source/gameengine/GameLogic/SCA_IActuator.cpp
index 0fda75590c1..0338213b3cf 100644
--- a/source/gameengine/GameLogic/SCA_IActuator.cpp
+++ b/source/gameengine/GameLogic/SCA_IActuator.cpp
@@ -34,8 +34,9 @@
using namespace std;
-SCA_IActuator::SCA_IActuator(SCA_IObject* gameobj) :
+SCA_IActuator::SCA_IActuator(SCA_IObject* gameobj, KX_ACTUATOR_TYPE type) :
SCA_ILogicBrick(gameobj),
+ m_type(type),
m_links(0),
m_posevent(false),
m_negevent(false)
diff --git a/source/gameengine/GameLogic/SCA_IActuator.h b/source/gameengine/GameLogic/SCA_IActuator.h
index 00ba8c9ce4e..2f1c04192ab 100644
--- a/source/gameengine/GameLogic/SCA_IActuator.h
+++ b/source/gameengine/GameLogic/SCA_IActuator.h
@@ -41,6 +41,7 @@ class SCA_IActuator : public SCA_ILogicBrick
{
friend class SCA_LogicManager;
protected:
+ int m_type;
int m_links; // number of active links to controllers
// when 0, the actuator is automatically stopped
//std::vector<CValue*> m_events;
@@ -60,8 +61,33 @@ public:
/**
* This class also inherits the default copy constructors
*/
-
- SCA_IActuator(SCA_IObject* gameobj);
+ enum KX_ACTUATOR_TYPE {
+ KX_ACT_OBJECT,
+ KX_ACT_IPO,
+ KX_ACT_CAMERA,
+ KX_ACT_SOUND,
+ KX_ACT_PROPERTY,
+ KX_ACT_ADD_OBJECT,
+ KX_ACT_END_OBJECT,
+ KX_ACT_DYNAMIC,
+ KX_ACT_REPLACE_MESH,
+ KX_ACT_TRACKTO,
+ KX_ACT_CONSTRAINT,
+ KX_ACT_SCENE,
+ KX_ACT_RANDOM,
+ KX_ACT_MESSAGE,
+ KX_ACT_ACTION,
+ KX_ACT_CD,
+ KX_ACT_GAME,
+ KX_ACT_VISIBILITY,
+ KX_ACT_2DFILTER,
+ KX_ACT_PARENT,
+ KX_ACT_SHAPEACTION,
+ KX_ACT_STATE,
+ KX_ACT_ARMATURE,
+ };
+
+ SCA_IActuator(SCA_IObject* gameobj, KX_ACTUATOR_TYPE type);
/**
* UnlinkObject(...)
@@ -127,7 +153,7 @@ public:
void IncLink() { m_links++; }
void DecLink();
bool IsNoLink() const { return !m_links; }
-
+ bool IsType(KX_ACTUATOR_TYPE type) { return m_type == type; }
#ifdef WITH_CXX_GUARDEDALLOC
public:
diff --git a/source/gameengine/GameLogic/SCA_IObject.cpp b/source/gameengine/GameLogic/SCA_IObject.cpp
index 2bffd029bd4..fbf66b64d08 100644
--- a/source/gameengine/GameLogic/SCA_IObject.cpp
+++ b/source/gameengine/GameLogic/SCA_IObject.cpp
@@ -26,6 +26,7 @@
* ***** END GPL LICENSE BLOCK *****
*/
#include <iostream>
+#include <algorithm>
#include "SCA_IObject.h"
#include "SCA_ISensor.h"
@@ -76,6 +77,12 @@ SCA_IObject::~SCA_IObject()
(*ita)->Delete();
}
+ SCA_ObjectList::iterator ito;
+ for (ito = m_registeredObjects.begin(); !(ito==m_registeredObjects.end()); ++ito)
+ {
+ (*ito)->UnlinkObject(this);
+ }
+
//T_InterpolatorList::iterator i;
//for (i = m_interpolators.begin(); !(i == m_interpolators.end()); ++i) {
// delete *i;
@@ -123,6 +130,26 @@ void SCA_IObject::UnregisterActuator(SCA_IActuator* act)
}
}
+void SCA_IObject::RegisterObject(SCA_IObject* obj)
+{
+ // one object may be registered multiple times via constraint target
+ // store multiple reference, this will serve as registration counter
+ m_registeredObjects.push_back(obj);
+}
+
+void SCA_IObject::UnregisterObject(SCA_IObject* obj)
+{
+ SCA_ObjectList::iterator ito;
+ for (ito = m_registeredObjects.begin(); ito != m_registeredObjects.end(); ++ito)
+ {
+ if ((*ito) == obj) {
+ (*ito) = m_registeredObjects.back();
+ m_registeredObjects.pop_back();
+ break;
+ }
+ }
+}
+
void SCA_IObject::ReParentLogic()
{
SCA_ActuatorList& oldactuators = GetActuators();
@@ -165,7 +192,7 @@ void SCA_IObject::ReParentLogic()
// a new object cannot be client of any actuator
m_registeredActuators.clear();
-
+ m_registeredObjects.clear();
}
diff --git a/source/gameengine/GameLogic/SCA_IObject.h b/source/gameengine/GameLogic/SCA_IObject.h
index 3060410dc6b..64ea0a76af1 100644
--- a/source/gameengine/GameLogic/SCA_IObject.h
+++ b/source/gameengine/GameLogic/SCA_IObject.h
@@ -36,6 +36,7 @@
#include "Value.h"
#include <vector>
+class SCA_IObject;
class SCA_ISensor;
class SCA_IController;
class SCA_IActuator;
@@ -45,6 +46,7 @@ template<class T> T PyVecTo(PyObject*);
typedef std::vector<SCA_ISensor *> SCA_SensorList;
typedef std::vector<SCA_IController *> SCA_ControllerList;
typedef std::vector<SCA_IActuator *> SCA_ActuatorList;
+typedef std::vector<SCA_IObject *> SCA_ObjectList;
class SCA_IObject : public CValue
{
@@ -59,6 +61,7 @@ protected:
SCA_ControllerList m_controllers;
SCA_ActuatorList m_actuators;
SCA_ActuatorList m_registeredActuators; // actuators that use a pointer to this object
+ SCA_ObjectList m_registeredObjects; // objects that hold reference to this object
// SG_Dlist: element of objects with active actuators
// Head: SCA_LogicManager::m_activeActuators
@@ -143,13 +146,22 @@ public:
void RegisterActuator(SCA_IActuator* act);
void UnregisterActuator(SCA_IActuator* act);
+ void RegisterObject(SCA_IObject* objs);
+ void UnregisterObject(SCA_IObject* objs);
+ /**
+ * UnlinkObject(...)
+ * this object is informed that one of the object to which it holds a reference is deleted
+ * returns true if there was indeed a reference.
+ */
+ virtual bool UnlinkObject(SCA_IObject* clientobj) { return false; }
+
SCA_ISensor* FindSensor(const STR_String& sensorname);
SCA_IActuator* FindActuator(const STR_String& actuatorname);
SCA_IController* FindController(const STR_String& controllername);
void SetCurrentTime(float currentTime) {}
- void ReParentLogic();
+ virtual void ReParentLogic();
/**
* Set whether or not to ignore activity culling requests
diff --git a/source/gameengine/GameLogic/SCA_PropertyActuator.cpp b/source/gameengine/GameLogic/SCA_PropertyActuator.cpp
index 9446487cdb7..2a3e600a653 100644
--- a/source/gameengine/GameLogic/SCA_PropertyActuator.cpp
+++ b/source/gameengine/GameLogic/SCA_PropertyActuator.cpp
@@ -43,7 +43,7 @@
/* ------------------------------------------------------------------------- */
SCA_PropertyActuator::SCA_PropertyActuator(SCA_IObject* gameobj,SCA_IObject* sourceObj,const STR_String& propname,const STR_String& expr,int acttype)
- : SCA_IActuator(gameobj),
+ : SCA_IActuator(gameobj, KX_ACT_PROPERTY),
m_type(acttype),
m_propname(propname),
m_exprtxt(expr),
diff --git a/source/gameengine/GameLogic/SCA_RandomActuator.cpp b/source/gameengine/GameLogic/SCA_RandomActuator.cpp
index a3a5cc2303e..8f9482b7826 100644
--- a/source/gameengine/GameLogic/SCA_RandomActuator.cpp
+++ b/source/gameengine/GameLogic/SCA_RandomActuator.cpp
@@ -51,7 +51,7 @@ SCA_RandomActuator::SCA_RandomActuator(SCA_IObject *gameobj,
float para1,
float para2,
const STR_String &propName)
- : SCA_IActuator(gameobj),
+ : SCA_IActuator(gameobj, KX_ACT_RANDOM),
m_propname(propName),
m_parameter1(para1),
m_parameter2(para2),
diff --git a/source/gameengine/Ketsji/KXNetwork/KX_NetworkMessageActuator.cpp b/source/gameengine/Ketsji/KXNetwork/KX_NetworkMessageActuator.cpp
index 3af8f765251..1cfab87d78b 100644
--- a/source/gameengine/Ketsji/KXNetwork/KX_NetworkMessageActuator.cpp
+++ b/source/gameengine/Ketsji/KXNetwork/KX_NetworkMessageActuator.cpp
@@ -42,7 +42,7 @@ KX_NetworkMessageActuator::KX_NetworkMessageActuator(
const STR_String &subject,
int bodyType,
const STR_String &body) :
- SCA_IActuator(gameobj),
+ SCA_IActuator(gameobj, KX_ACT_MESSAGE),
m_networkscene(networkscene),
m_toPropName(toPropName),
m_subject(subject),
diff --git a/source/gameengine/Ketsji/KX_ArmatureSensor.cpp b/source/gameengine/Ketsji/KX_ArmatureSensor.cpp
new file mode 100644
index 00000000000..1b759bda28e
--- /dev/null
+++ b/source/gameengine/Ketsji/KX_ArmatureSensor.cpp
@@ -0,0 +1,205 @@
+/**
+ * Armature sensor
+ *
+ * $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.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include "DNA_action_types.h"
+#include "DNA_constraint_types.h"
+#include "BKE_constraint.h"
+#include "DNA_sensor_types.h"
+
+#include "BL_ArmatureObject.h"
+#include "KX_ArmatureSensor.h"
+#include "SCA_EventManager.h"
+#include "SCA_LogicManager.h"
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+KX_ArmatureSensor::KX_ArmatureSensor(class SCA_EventManager* eventmgr,
+ SCA_IObject* gameobj,
+ const char *posechannel,
+ const char *constraintname,
+ int type,
+ float value)
+ : SCA_ISensor(gameobj,eventmgr),
+ m_constraint(NULL),
+ m_posechannel(posechannel),
+ m_constraintname(constraintname),
+ m_type(type),
+ m_value(value)
+{
+ FindConstraint();
+}
+
+void KX_ArmatureSensor::Init()
+{
+ m_lastresult = m_invert?true:false;
+ m_result = false;
+ m_reset = true;
+}
+
+void KX_ArmatureSensor::FindConstraint()
+{
+ m_constraint = NULL;
+
+ if (m_gameobj->GetGameObjectType() == SCA_IObject::OBJ_ARMATURE) {
+ BL_ArmatureObject* armobj = (BL_ArmatureObject*)m_gameobj;
+ // get the persistent pose structure
+ bPose* pose = armobj->GetOrigPose();
+ bPoseChannel* pchan;
+ bConstraint* pcon;
+ // and locate the constraint
+ for (pchan = (bPoseChannel*)pose->chanbase.first; pchan; pchan=(bPoseChannel*)pchan->next) {
+ if (!strcmp(pchan->name, m_posechannel)) {
+ // now locate the constraint
+ for (pcon = (bConstraint*)pchan->constraints.first; pcon; pcon=(bConstraint*)pcon->next) {
+ if (!strcmp(pcon->name, m_constraintname)) {
+ if (pcon->flag & CONSTRAINT_DISABLE)
+ /* this constraint is not valid, can't use it */
+ break;
+ m_constraint = pcon;
+ break;
+ }
+ }
+ break;
+ }
+ }
+ }
+}
+
+
+CValue* KX_ArmatureSensor::GetReplica()
+{
+ KX_ArmatureSensor* replica = new KX_ArmatureSensor(*this);
+ // m_range_expr must be recalculated on replica!
+ replica->ProcessReplica();
+ return replica;
+}
+
+void KX_ArmatureSensor::ReParent(SCA_IObject* parent)
+{
+ SCA_ISensor::ReParent(parent);
+ // must remap the constraint
+ FindConstraint();
+}
+
+bool KX_ArmatureSensor::IsPositiveTrigger()
+{
+ return (m_invert) ? !m_result : m_result;
+}
+
+
+KX_ArmatureSensor::~KX_ArmatureSensor()
+{
+}
+
+bool KX_ArmatureSensor::Evaluate()
+{
+ bool reset = m_reset && m_level;
+
+ m_reset = false;
+ if (!m_constraint)
+ return false;
+ switch (m_type) {
+ case SENS_ARM_STATE_CHANGED:
+ m_result = !(m_constraint->flag & CONSTRAINT_OFF);
+ break;
+ case SENS_ARM_LIN_ERROR_BELOW:
+ m_result = (m_constraint->lin_error < m_value);
+ break;
+ case SENS_ARM_LIN_ERROR_ABOVE:
+ m_result = (m_constraint->lin_error > m_value);
+ break;
+ case SENS_ARM_ROT_ERROR_BELOW:
+ m_result = (m_constraint->rot_error < m_value);
+ break;
+ case SENS_ARM_ROT_ERROR_ABOVE:
+ m_result = (m_constraint->rot_error > m_value);
+ break;
+ }
+ if (m_lastresult!=m_result)
+ {
+ m_lastresult = m_result;
+ return true;
+ }
+ return (reset) ? true : false;
+}
+
+
+/* ------------------------------------------------------------------------- */
+/* Python functions */
+/* ------------------------------------------------------------------------- */
+
+/* Integration hooks ------------------------------------------------------- */
+PyTypeObject KX_ArmatureSensor::Type = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "KX_ArmatureSensor",
+ sizeof(PyObjectPlus_Proxy),
+ 0,
+ py_base_dealloc,
+ 0,
+ 0,
+ 0,
+ 0,
+ py_base_repr,
+ 0,0,0,0,0,0,0,0,0,
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ 0,0,0,0,0,0,0,
+ Methods,
+ 0,
+ 0,
+ &SCA_ISensor::Type,
+ 0,0,0,0,0,0,
+ py_base_new
+};
+
+PyMethodDef KX_ArmatureSensor::Methods[] = {
+ {NULL,NULL} //Sentinel
+};
+
+PyAttributeDef KX_ArmatureSensor::Attributes[] = {
+ KX_PYATTRIBUTE_RO_FUNCTION("constraint", KX_ArmatureSensor, pyattr_get_constraint),
+ KX_PYATTRIBUTE_FLOAT_RW("value",-FLT_MAX,FLT_MAX,KX_ArmatureSensor,m_value),
+ KX_PYATTRIBUTE_INT_RW("type",0,SENS_ARM_MAXTYPE,false,KX_ArmatureSensor,m_type),
+ { NULL } //Sentinel
+};
+
+PyObject* KX_ArmatureSensor::pyattr_get_constraint(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef)
+{
+ KX_ArmatureSensor* sensor = static_cast<KX_ArmatureSensor*>(self);
+ if (sensor->m_gameobj->GetGameObjectType() == SCA_IObject::OBJ_ARMATURE) {
+ BL_ArmatureObject* armobj = (BL_ArmatureObject*)sensor->m_gameobj;
+ BL_ArmatureConstraint* constraint = armobj->GetConstraint(sensor->m_posechannel, sensor->m_constraintname);
+ if (constraint)
+ return constraint->GetProxy();
+ }
+ Py_RETURN_NONE;
+}
diff --git a/source/gameengine/Ketsji/KX_ArmatureSensor.h b/source/gameengine/Ketsji/KX_ArmatureSensor.h
new file mode 100644
index 00000000000..2af002afff9
--- /dev/null
+++ b/source/gameengine/Ketsji/KX_ArmatureSensor.h
@@ -0,0 +1,85 @@
+/**
+ * Property sensor
+ *
+ * $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.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __KX_ARMATURESENSOR
+#define __KX_ARMATURESENSOR
+
+struct bConstraint;
+
+#include "SCA_ISensor.h"
+#include "DNA_sensor_types.h"
+
+class KX_ArmatureSensor : public SCA_ISensor
+{
+ Py_Header;
+ //class CExpression* m_rightexpr;
+
+protected:
+
+public:
+ KX_ArmatureSensor(class SCA_EventManager* eventmgr,
+ SCA_IObject* gameobj,
+ const char *posechannel,
+ const char *constraintname,
+ int type,
+ float value);
+
+ /**
+ * For property sensor, it is used to release the pre-calculated expression
+ * so that self references are removed before the sensor itself is released
+ */
+ virtual ~KX_ArmatureSensor();
+ virtual CValue* GetReplica();
+ virtual void ReParent(SCA_IObject* parent);
+ virtual void Init();
+ virtual bool Evaluate();
+ virtual bool IsPositiveTrigger();
+
+ // identify the constraint that this actuator controls
+ void FindConstraint();
+
+ /* --------------------------------------------------------------------- */
+ /* Python interface ---------------------------------------------------- */
+ /* --------------------------------------------------------------------- */
+ static PyObject* pyattr_get_constraint(void *self, const struct KX_PYATTRIBUTE_DEF *attrdef);
+
+private:
+ struct bConstraint* m_constraint;
+ STR_String m_posechannel;
+ STR_String m_constraintname;
+ int m_type;
+ float m_value;
+ bool m_result;
+ bool m_lastresult;
+};
+
+#endif
+
diff --git a/source/gameengine/Ketsji/KX_CameraActuator.cpp b/source/gameengine/Ketsji/KX_CameraActuator.cpp
index 9c00b5991af..99618fab8e3 100644
--- a/source/gameengine/Ketsji/KX_CameraActuator.cpp
+++ b/source/gameengine/Ketsji/KX_CameraActuator.cpp
@@ -55,7 +55,7 @@ KX_CameraActuator::KX_CameraActuator(
float maxhght,
bool xytog
):
- SCA_IActuator(gameobj),
+ SCA_IActuator(gameobj, KX_ACT_CAMERA),
m_ob (obj),
m_height (hght),
m_minHeight (minhght),
diff --git a/source/gameengine/Ketsji/KX_ConstraintActuator.cpp b/source/gameengine/Ketsji/KX_ConstraintActuator.cpp
index 7f1d2c7d53c..ea0b9f4f1e2 100644
--- a/source/gameengine/Ketsji/KX_ConstraintActuator.cpp
+++ b/source/gameengine/Ketsji/KX_ConstraintActuator.cpp
@@ -56,7 +56,7 @@ KX_ConstraintActuator::KX_ConstraintActuator(SCA_IObject *gameobj,
int time,
int option,
char *property) :
- SCA_IActuator(gameobj),
+ SCA_IActuator(gameobj, KX_ACT_CONSTRAINT),
m_refDirVector(refDir),
m_currentTime(0)
{
diff --git a/source/gameengine/Ketsji/KX_GameActuator.cpp b/source/gameengine/Ketsji/KX_GameActuator.cpp
index 42dc4d8fd24..71980a3347e 100644
--- a/source/gameengine/Ketsji/KX_GameActuator.cpp
+++ b/source/gameengine/Ketsji/KX_GameActuator.cpp
@@ -50,7 +50,7 @@ KX_GameActuator::KX_GameActuator(SCA_IObject *gameobj,
const STR_String& loadinganimationname,
KX_Scene* scene,
KX_KetsjiEngine* ketsjiengine)
- : SCA_IActuator(gameobj)
+ : SCA_IActuator(gameobj, KX_ACT_GAME)
{
m_mode = mode;
m_filename = filename;
diff --git a/source/gameengine/Ketsji/KX_GameObject.cpp b/source/gameengine/Ketsji/KX_GameObject.cpp
index 8193aa8c37b..a02fd9cec1b 100644
--- a/source/gameengine/Ketsji/KX_GameObject.cpp
+++ b/source/gameengine/Ketsji/KX_GameObject.cpp
@@ -46,6 +46,7 @@ typedef unsigned long uint_ptr;
#define KX_INERTIA_INFINITE 10000
+#include "BLI_arithb.h"
#include "RAS_IPolygonMaterial.h"
#include "KX_BlenderMaterial.h"
#include "KX_GameObject.h"
@@ -454,6 +455,22 @@ double* KX_GameObject::GetOpenGLMatrix()
return fl;
}
+void KX_GameObject::UpdateBlenderObjectMatrix(Object* blendobj)
+{
+ if (!blendobj)
+ blendobj = m_pBlenderObject;
+ if (blendobj) {
+ const MT_Matrix3x3& rot = NodeGetWorldOrientation();
+ const MT_Vector3& scale = NodeGetWorldScaling();
+ const MT_Vector3& pos = NodeGetWorldPosition();
+ rot.getValue(blendobj->obmat[0]);
+ pos.getValue(blendobj->obmat[3]);
+ VecMulf(blendobj->obmat[0], scale[0]);
+ VecMulf(blendobj->obmat[1], scale[1]);
+ VecMulf(blendobj->obmat[2], scale[2]);
+ }
+}
+
void KX_GameObject::AddMeshUser()
{
for (size_t i=0;i<m_meshes.size();i++)
diff --git a/source/gameengine/Ketsji/KX_GameObject.h b/source/gameengine/Ketsji/KX_GameObject.h
index 845cead1cdb..4c4eed9ca71 100644
--- a/source/gameengine/Ketsji/KX_GameObject.h
+++ b/source/gameengine/Ketsji/KX_GameObject.h
@@ -158,6 +158,15 @@ public:
return &m_OpenGL_4x4Matrix;
};
+ /**
+ * Update the blender object obmat field from the object world position
+ * if blendobj is NULL, update the object pointed by m_pBlenderObject
+ * The user must take action to restore the matrix before leaving the GE.
+ * Used in Armature evaluation
+ */
+ void
+ UpdateBlenderObjectMatrix(Object* blendobj=NULL);
+
/**
* Get a pointer to the game object that is the parent of
* this object. Or NULL if there is no parent. The returned
diff --git a/source/gameengine/Ketsji/KX_IpoActuator.cpp b/source/gameengine/Ketsji/KX_IpoActuator.cpp
index b71907be961..d7f800fe5bd 100644
--- a/source/gameengine/Ketsji/KX_IpoActuator.cpp
+++ b/source/gameengine/Ketsji/KX_IpoActuator.cpp
@@ -71,7 +71,7 @@ KX_IpoActuator::KX_IpoActuator(SCA_IObject* gameobj,
bool ipo_as_force,
bool ipo_add,
bool ipo_local)
- : SCA_IActuator(gameobj),
+ : SCA_IActuator(gameobj, KX_ACT_IPO),
m_bNegativeEvent(false),
m_startframe (starttime),
m_endframe(endtime),
diff --git a/source/gameengine/Ketsji/KX_KetsjiEngine.cpp b/source/gameengine/Ketsji/KX_KetsjiEngine.cpp
index 4117e493322..e95676ff9d2 100644
--- a/source/gameengine/Ketsji/KX_KetsjiEngine.cpp
+++ b/source/gameengine/Ketsji/KX_KetsjiEngine.cpp
@@ -1783,6 +1783,11 @@ double KX_KetsjiEngine::GetClockTime(void) const
return m_clockTime;
}
+double KX_KetsjiEngine::GetFrameTime(void) const
+{
+ return m_frameTime;
+}
+
double KX_KetsjiEngine::GetRealTime(void) const
{
return m_kxsystem->GetTimeInSeconds();
diff --git a/source/gameengine/Ketsji/KX_KetsjiEngine.h b/source/gameengine/Ketsji/KX_KetsjiEngine.h
index acb9e53df8a..373e8bf218c 100644
--- a/source/gameengine/Ketsji/KX_KetsjiEngine.h
+++ b/source/gameengine/Ketsji/KX_KetsjiEngine.h
@@ -269,6 +269,10 @@ public:
* Returns current render frame clock time
*/
double GetClockTime(void) const;
+ /**
+ * Returns current logic frame clock time
+ */
+ double GetFrameTime(void) const;
double GetRealTime(void) const;
/**
diff --git a/source/gameengine/Ketsji/KX_ObjectActuator.cpp b/source/gameengine/Ketsji/KX_ObjectActuator.cpp
index 2601ced9c38..924e4a47008 100644
--- a/source/gameengine/Ketsji/KX_ObjectActuator.cpp
+++ b/source/gameengine/Ketsji/KX_ObjectActuator.cpp
@@ -55,7 +55,7 @@ KX_ObjectActuator(
const short damping,
const KX_LocalFlags& flag
) :
- SCA_IActuator(gameobj),
+ SCA_IActuator(gameobj, KX_ACT_OBJECT),
m_force(force),
m_torque(torque),
m_dloc(dloc),
diff --git a/source/gameengine/Ketsji/KX_ParentActuator.cpp b/source/gameengine/Ketsji/KX_ParentActuator.cpp
index 20e982f03e0..a4a6b67ad10 100644
--- a/source/gameengine/Ketsji/KX_ParentActuator.cpp
+++ b/source/gameengine/Ketsji/KX_ParentActuator.cpp
@@ -51,7 +51,7 @@ KX_ParentActuator::KX_ParentActuator(SCA_IObject *gameobj,
bool addToCompound,
bool ghost,
SCA_IObject *ob)
- : SCA_IActuator(gameobj),
+ : SCA_IActuator(gameobj, KX_ACT_PARENT),
m_mode(mode),
m_addToCompound(addToCompound),
m_ghost(ghost),
diff --git a/source/gameengine/Ketsji/KX_PythonInit.cpp b/source/gameengine/Ketsji/KX_PythonInit.cpp
index 298d485aaaf..bef00fb7a06 100644
--- a/source/gameengine/Ketsji/KX_PythonInit.cpp
+++ b/source/gameengine/Ketsji/KX_PythonInit.cpp
@@ -51,6 +51,7 @@ extern "C" {
#include "KX_KetsjiEngine.h"
#include "KX_RadarSensor.h"
#include "KX_RaySensor.h"
+#include "KX_ArmatureSensor.h"
#include "KX_SceneActuator.h"
#include "KX_GameActuator.h"
#include "KX_ParentActuator.h"
@@ -65,6 +66,7 @@ extern "C" {
#include "KX_SoundActuator.h"
#include "KX_StateActuator.h"
#include "BL_ActionActuator.h"
+#include "BL_ArmatureObject.h"
#include "RAS_IRasterizer.h"
#include "RAS_ICanvas.h"
#include "RAS_BucketManager.h"
@@ -1218,6 +1220,53 @@ PyObject* initGameLogic(KX_KetsjiEngine *engine, KX_Scene* scene) // quick hack
KX_MACRO_addTypesToDict(d, KX_PARENT_SET, KX_ParentActuator::KX_PARENT_SET);
KX_MACRO_addTypesToDict(d, KX_PARENT_REMOVE, KX_ParentActuator::KX_PARENT_REMOVE);
+ /* BL_ArmatureConstraint type */
+ KX_MACRO_addTypesToDict(d, CONSTRAINT_TYPE_TRACKTO, CONSTRAINT_TYPE_TRACKTO);
+ KX_MACRO_addTypesToDict(d, CONSTRAINT_TYPE_KINEMATIC, CONSTRAINT_TYPE_KINEMATIC);
+ KX_MACRO_addTypesToDict(d, CONSTRAINT_TYPE_ROTLIKE, CONSTRAINT_TYPE_ROTLIKE);
+ KX_MACRO_addTypesToDict(d, CONSTRAINT_TYPE_LOCLIKE, CONSTRAINT_TYPE_LOCLIKE);
+ KX_MACRO_addTypesToDict(d, CONSTRAINT_TYPE_MINMAX, CONSTRAINT_TYPE_MINMAX);
+ KX_MACRO_addTypesToDict(d, CONSTRAINT_TYPE_SIZELIKE, CONSTRAINT_TYPE_SIZELIKE);
+ KX_MACRO_addTypesToDict(d, CONSTRAINT_TYPE_LOCKTRACK, CONSTRAINT_TYPE_LOCKTRACK);
+ KX_MACRO_addTypesToDict(d, CONSTRAINT_TYPE_STRETCHTO, CONSTRAINT_TYPE_STRETCHTO);
+ KX_MACRO_addTypesToDict(d, CONSTRAINT_TYPE_CLAMPTO, CONSTRAINT_TYPE_CLAMPTO);
+ KX_MACRO_addTypesToDict(d, CONSTRAINT_TYPE_TRANSFORM, CONSTRAINT_TYPE_TRANSFORM);
+ KX_MACRO_addTypesToDict(d, CONSTRAINT_TYPE_DISTLIMIT, CONSTRAINT_TYPE_DISTLIMIT);
+ /* BL_ArmatureConstraint ik_type */
+ KX_MACRO_addTypesToDict(d, CONSTRAINT_IK_COPYPOSE, CONSTRAINT_IK_COPYPOSE);
+ KX_MACRO_addTypesToDict(d, CONSTRAINT_IK_DISTANCE, CONSTRAINT_IK_DISTANCE);
+ /* BL_ArmatureConstraint ik_mode */
+ KX_MACRO_addTypesToDict(d, CONSTRAINT_IK_MODE_INSIDE, LIMITDIST_INSIDE);
+ KX_MACRO_addTypesToDict(d, CONSTRAINT_IK_MODE_OUTSIDE, LIMITDIST_OUTSIDE);
+ KX_MACRO_addTypesToDict(d, CONSTRAINT_IK_MODE_ONSURFACE, LIMITDIST_ONSURFACE);
+ /* BL_ArmatureConstraint ik_flag */
+ KX_MACRO_addTypesToDict(d, CONSTRAINT_IK_FLAG_TIP, CONSTRAINT_IK_TIP);
+ KX_MACRO_addTypesToDict(d, CONSTRAINT_IK_FLAG_ROT, CONSTRAINT_IK_ROT);
+ KX_MACRO_addTypesToDict(d, CONSTRAINT_IK_FLAG_STRETCH, CONSTRAINT_IK_STRETCH);
+ KX_MACRO_addTypesToDict(d, CONSTRAINT_IK_FLAG_POS, CONSTRAINT_IK_POS);
+ /* KX_ArmatureSensor type */
+ KX_MACRO_addTypesToDict(d, KX_ARMSENSOR_STATE_CHANGED, SENS_ARM_STATE_CHANGED);
+ KX_MACRO_addTypesToDict(d, KX_ARMSENSOR_LIN_ERROR_BELOW, SENS_ARM_LIN_ERROR_BELOW);
+ KX_MACRO_addTypesToDict(d, KX_ARMSENSOR_LIN_ERROR_ABOVE, SENS_ARM_LIN_ERROR_ABOVE);
+ KX_MACRO_addTypesToDict(d, KX_ARMSENSOR_ROT_ERROR_BELOW, SENS_ARM_ROT_ERROR_BELOW);
+ KX_MACRO_addTypesToDict(d, KX_ARMSENSOR_ROT_ERROR_ABOVE, SENS_ARM_ROT_ERROR_ABOVE);
+
+ /* BL_ArmatureActuator type */
+ KX_MACRO_addTypesToDict(d, KX_ACT_ARMATURE_RUN, ACT_ARM_RUN);
+ KX_MACRO_addTypesToDict(d, KX_ACT_ARMATURE_ENABLE, ACT_ARM_ENABLE);
+ KX_MACRO_addTypesToDict(d, KX_ACT_ARMATURE_DISABLE, ACT_ARM_DISABLE);
+ KX_MACRO_addTypesToDict(d, KX_ACT_ARMATURE_SETTARGET, ACT_ARM_SETTARGET);
+ KX_MACRO_addTypesToDict(d, KX_ACT_ARMATURE_SETWEIGHT, ACT_ARM_SETWEIGHT);
+
+ /* BL_Armature Channel rotation_mode */
+ KX_MACRO_addTypesToDict(d, PCHAN_ROT_QUAT, PCHAN_ROT_QUAT);
+ KX_MACRO_addTypesToDict(d, PCHAN_ROT_XYZ, PCHAN_ROT_XYZ);
+ KX_MACRO_addTypesToDict(d, PCHAN_ROT_XZY, PCHAN_ROT_XZY);
+ KX_MACRO_addTypesToDict(d, PCHAN_ROT_YXZ, PCHAN_ROT_YXZ);
+ KX_MACRO_addTypesToDict(d, PCHAN_ROT_YZX, PCHAN_ROT_YZX);
+ KX_MACRO_addTypesToDict(d, PCHAN_ROT_ZXY, PCHAN_ROT_ZXY);
+ KX_MACRO_addTypesToDict(d, PCHAN_ROT_ZYX, PCHAN_ROT_ZYX);
+
// Check for errors
if (PyErr_Occurred())
{
diff --git a/source/gameengine/Ketsji/KX_PythonInitTypes.cpp b/source/gameengine/Ketsji/KX_PythonInitTypes.cpp
index 61e563791c3..c2c33918172 100644
--- a/source/gameengine/Ketsji/KX_PythonInitTypes.cpp
+++ b/source/gameengine/Ketsji/KX_PythonInitTypes.cpp
@@ -35,6 +35,10 @@
/* Only for Class::Parents */
#include "BL_BlenderShader.h"
#include "BL_ShapeActionActuator.h"
+#include "BL_ArmatureActuator.h"
+#include "BL_ArmatureConstraint.h"
+#include "BL_ArmatureObject.h"
+#include "BL_ArmatureChannel.h"
#include "KX_BlenderMaterial.h"
#include "KX_CameraActuator.h"
#include "KX_ConstraintActuator.h"
@@ -86,7 +90,22 @@
#include "SCA_RandomActuator.h"
#include "SCA_IController.h"
-static void PyType_Ready_ADD(PyObject *dict, PyTypeObject *tp, PyAttributeDef *attributes, int init_getset)
+static void PyType_Attr_Set(PyGetSetDef *attr_getset, PyAttributeDef *attr)
+{
+ attr_getset->name= (char *)attr->m_name;
+ attr_getset->doc= NULL;
+
+ attr_getset->get= reinterpret_cast<getter>(PyObjectPlus::py_get_attrdef);
+
+ if(attr->m_access==KX_PYATTRIBUTE_RO)
+ attr_getset->set= NULL;
+ else
+ attr_getset->set= reinterpret_cast<setter>(PyObjectPlus::py_set_attrdef);
+
+ attr_getset->closure= reinterpret_cast<void *>(attr);
+}
+
+static void PyType_Ready_ADD(PyObject *dict, PyTypeObject *tp, PyAttributeDef *attributes, PyAttributeDef *attributesPtr, int init_getset)
{
PyAttributeDef *attr;
@@ -97,29 +116,31 @@ static void PyType_Ready_ADD(PyObject *dict, PyTypeObject *tp, PyAttributeDef *a
//if(tp->tp_base==NULL)
// printf("Debug: No Parents - '%s'\n" , tp->tp_name);
- if(tp->tp_getset==NULL && attributes->m_name) {
+ if(tp->tp_getset==NULL && ((attributes && attributes->m_name) || (attributesPtr && attributesPtr->m_name))) {
PyGetSetDef *attr_getset;
int attr_tot= 0;
- for(attr= attributes; attr->m_name; attr++, attr_tot++) {};
+ if (attributes) {
+ for(attr= attributes; attr->m_name; attr++, attr_tot++)
+ attr->m_usePtr = false;
+ }
+ if (attributesPtr) {
+ for(attr= attributesPtr; attr->m_name; attr++, attr_tot++)
+ attr->m_usePtr = true;
+ }
tp->tp_getset = attr_getset = reinterpret_cast<PyGetSetDef *>(PyMem_Malloc((attr_tot+1) * sizeof(PyGetSetDef))); // XXX - Todo, free
-
- for(attr= attributes; attr->m_name; attr++, attr_getset++) {
- attr_getset->name= (char *)attr->m_name;
- attr_getset->doc= NULL;
-
- attr_getset->get= reinterpret_cast<getter>(PyObjectPlus::py_get_attrdef);
-
- if(attr->m_access==KX_PYATTRIBUTE_RO)
- attr_getset->set= NULL;
- else
- attr_getset->set= reinterpret_cast<setter>(PyObjectPlus::py_set_attrdef);
-
- attr_getset->closure= reinterpret_cast<void *>(attr);
+ if (attributes) {
+ for(attr= attributes; attr->m_name; attr++, attr_getset++) {
+ PyType_Attr_Set(attr_getset, attr);
+ }
+ }
+ if (attributesPtr) {
+ for(attr= attributesPtr; attr->m_name; attr++, attr_getset++) {
+ PyType_Attr_Set(attr_getset, attr);
+ }
}
-
memset(attr_getset, 0, sizeof(PyGetSetDef));
}
} else {
@@ -130,7 +151,8 @@ static void PyType_Ready_ADD(PyObject *dict, PyTypeObject *tp, PyAttributeDef *a
}
-#define PyType_Ready_Attr(d, n, i) PyType_Ready_ADD(d, &n::Type, n::Attributes, i)
+#define PyType_Ready_Attr(d, n, i) PyType_Ready_ADD(d, &n::Type, n::Attributes, NULL, i)
+#define PyType_Ready_AttrPtr(d, n, i) PyType_Ready_ADD(d, &n::Type, n::Attributes, n::AttributesPtr, i)
void initPyTypes(void)
{
@@ -151,6 +173,11 @@ void initPyTypes(void)
PyType_Ready_Attr(dict, BL_ActionActuator, init_getset);
PyType_Ready_Attr(dict, BL_Shader, init_getset);
PyType_Ready_Attr(dict, BL_ShapeActionActuator, init_getset);
+ PyType_Ready_Attr(dict, BL_ArmatureObject, init_getset);
+ PyType_Ready_Attr(dict, BL_ArmatureActuator, init_getset);
+ PyType_Ready_Attr(dict, BL_ArmatureConstraint, init_getset);
+ PyType_Ready_AttrPtr(dict, BL_ArmatureBone, init_getset);
+ PyType_Ready_AttrPtr(dict, BL_ArmatureChannel, init_getset);
PyType_Ready_Attr(dict, CListValue, init_getset);
PyType_Ready_Attr(dict, CValue, init_getset);
PyType_Ready_Attr(dict, KX_BlenderMaterial, init_getset);
diff --git a/source/gameengine/Ketsji/KX_PythonSeq.cpp b/source/gameengine/Ketsji/KX_PythonSeq.cpp
index 75a7c9b8aeb..f7ad7acb252 100644
--- a/source/gameengine/Ketsji/KX_PythonSeq.cpp
+++ b/source/gameengine/Ketsji/KX_PythonSeq.cpp
@@ -31,6 +31,7 @@
#include "KX_PythonSeq.h"
#include "KX_GameObject.h"
+#include "BL_ArmatureObject.h"
#include "SCA_ISensor.h"
#include "SCA_IController.h"
#include "SCA_IActuator.h"
@@ -72,6 +73,10 @@ static Py_ssize_t KX_PythonSeq_len( PyObject * self )
return ((KX_GameObject *)self_plus)->GetControllers().size();
case KX_PYGENSEQ_OB_TYPE_ACTUATORS:
return ((KX_GameObject *)self_plus)->GetActuators().size();
+ case KX_PYGENSEQ_OB_TYPE_CONSTRAINTS:
+ return ((BL_ArmatureObject *)self_plus)->GetConstraintNumber();
+ case KX_PYGENSEQ_OB_TYPE_CHANNELS:
+ return ((BL_ArmatureObject *)self_plus)->GetChannelNumber();
default:
/* Should never happen */
PyErr_SetString(PyExc_SystemError, "invalid type, internal error");
@@ -139,6 +144,29 @@ static PyObject *KX_PythonSeq_getIndex(PyObject* self, int index)
}
return linkedactuators[index]->GetProxy();
}
+ case KX_PYGENSEQ_OB_TYPE_CONSTRAINTS:
+ {
+ int nb_constraint = ((BL_ArmatureObject *)self_plus)->GetConstraintNumber();
+ if(index<0)
+ index += nb_constraint;
+ if(index<0 || index>= nb_constraint) {
+ PyErr_SetString(PyExc_IndexError, "seq[i]: index out of range");
+ return NULL;
+ }
+ return ((BL_ArmatureObject *)self_plus)->GetConstraint(index)->GetProxy();
+ }
+ case KX_PYGENSEQ_OB_TYPE_CHANNELS:
+ {
+ int nb_channel = ((BL_ArmatureObject *)self_plus)->GetChannelNumber();
+ if(index<0)
+ index += nb_channel;
+ if(index<0 || index>= nb_channel) {
+ PyErr_SetString(PyExc_IndexError, "seq[i]: index out of range");
+ return NULL;
+ }
+ return ((BL_ArmatureObject *)self_plus)->GetChannel(index)->GetProxy();
+ }
+
}
PyErr_SetString(PyExc_SystemError, "invalid sequence type, this is a bug");
@@ -206,6 +234,14 @@ static PyObjectPlus * KX_PythonSeq_subscript__internal(PyObject *self, char *key
}
break;
}
+ case KX_PYGENSEQ_OB_TYPE_CONSTRAINTS:
+ {
+ return ((BL_ArmatureObject*)self_plus)->GetConstraint(key);
+ }
+ case KX_PYGENSEQ_OB_TYPE_CHANNELS:
+ {
+ return ((BL_ArmatureObject*)self_plus)->GetChannel(key);
+ }
}
return NULL;
diff --git a/source/gameengine/Ketsji/KX_PythonSeq.h b/source/gameengine/Ketsji/KX_PythonSeq.h
index 15a016224a9..34243aa9b9c 100644
--- a/source/gameengine/Ketsji/KX_PythonSeq.h
+++ b/source/gameengine/Ketsji/KX_PythonSeq.h
@@ -39,7 +39,9 @@ enum KX_PYGENSEQ_TYPE {
KX_PYGENSEQ_CONT_TYPE_ACTUATORS,
KX_PYGENSEQ_OB_TYPE_SENSORS,
KX_PYGENSEQ_OB_TYPE_CONTROLLERS,
- KX_PYGENSEQ_OB_TYPE_ACTUATORS
+ KX_PYGENSEQ_OB_TYPE_ACTUATORS,
+ KX_PYGENSEQ_OB_TYPE_CONSTRAINTS,
+ KX_PYGENSEQ_OB_TYPE_CHANNELS,
};
/* The Main PyType Object defined in Main.c */
diff --git a/source/gameengine/Ketsji/KX_SCA_AddObjectActuator.cpp b/source/gameengine/Ketsji/KX_SCA_AddObjectActuator.cpp
index 099403fc28d..c1e74070d72 100644
--- a/source/gameengine/Ketsji/KX_SCA_AddObjectActuator.cpp
+++ b/source/gameengine/Ketsji/KX_SCA_AddObjectActuator.cpp
@@ -57,7 +57,7 @@ KX_SCA_AddObjectActuator::KX_SCA_AddObjectActuator(SCA_IObject *gameobj,
const float *angvel,
bool angv_local)
:
- SCA_IActuator(gameobj),
+ SCA_IActuator(gameobj, KX_ACT_ADD_OBJECT),
m_OriginalObject(original),
m_scene(scene),
diff --git a/source/gameengine/Ketsji/KX_SCA_DynamicActuator.cpp b/source/gameengine/Ketsji/KX_SCA_DynamicActuator.cpp
index 646cfb7219f..73c85ad07b3 100644
--- a/source/gameengine/Ketsji/KX_SCA_DynamicActuator.cpp
+++ b/source/gameengine/Ketsji/KX_SCA_DynamicActuator.cpp
@@ -87,7 +87,7 @@ KX_SCA_DynamicActuator::KX_SCA_DynamicActuator(SCA_IObject *gameobj,
short dyn_operation,
float setmass) :
- SCA_IActuator(gameobj),
+ SCA_IActuator(gameobj, KX_ACT_DYNAMIC),
m_dyn_operation(dyn_operation),
m_setmass(setmass)
{
diff --git a/source/gameengine/Ketsji/KX_SCA_EndObjectActuator.cpp b/source/gameengine/Ketsji/KX_SCA_EndObjectActuator.cpp
index dd9d8015724..e7dc71d9b27 100644
--- a/source/gameengine/Ketsji/KX_SCA_EndObjectActuator.cpp
+++ b/source/gameengine/Ketsji/KX_SCA_EndObjectActuator.cpp
@@ -44,7 +44,7 @@
KX_SCA_EndObjectActuator::KX_SCA_EndObjectActuator(SCA_IObject *gameobj,
SCA_IScene* scene):
- SCA_IActuator(gameobj),
+ SCA_IActuator(gameobj, KX_ACT_END_OBJECT),
m_scene(scene)
{
// intentionally empty
diff --git a/source/gameengine/Ketsji/KX_SCA_ReplaceMeshActuator.cpp b/source/gameengine/Ketsji/KX_SCA_ReplaceMeshActuator.cpp
index e85b8a32798..e118972edc8 100644
--- a/source/gameengine/Ketsji/KX_SCA_ReplaceMeshActuator.cpp
+++ b/source/gameengine/Ketsji/KX_SCA_ReplaceMeshActuator.cpp
@@ -122,7 +122,7 @@ KX_SCA_ReplaceMeshActuator::KX_SCA_ReplaceMeshActuator(SCA_IObject *gameobj,
bool use_gfx,
bool use_phys) :
- SCA_IActuator(gameobj),
+ SCA_IActuator(gameobj, KX_ACT_REPLACE_MESH),
m_mesh(mesh),
m_scene(scene),
m_use_gfx(use_gfx),
diff --git a/source/gameengine/Ketsji/KX_Scene.cpp b/source/gameengine/Ketsji/KX_Scene.cpp
index 3483496c3a6..140be3fb3db 100644
--- a/source/gameengine/Ketsji/KX_Scene.cpp
+++ b/source/gameengine/Ketsji/KX_Scene.cpp
@@ -40,14 +40,15 @@
#include "ListValue.h"
#include "SCA_LogicManager.h"
#include "SCA_TimeEventManager.h"
-#include "SCA_AlwaysEventManager.h"
-#include "SCA_RandomEventManager.h"
-#include "KX_RayEventManager.h"
+//#include "SCA_AlwaysEventManager.h"
+//#include "SCA_RandomEventManager.h"
+//#include "KX_RayEventManager.h"
#include "KX_TouchEventManager.h"
#include "SCA_KeyboardManager.h"
#include "SCA_MouseManager.h"
-#include "SCA_PropertyEventManager.h"
+//#include "SCA_PropertyEventManager.h"
#include "SCA_ActuatorEventManager.h"
+#include "SCA_BasicEventManager.h"
#include "KX_Camera.h"
#include "SCA_JoystickManager.h"
@@ -168,25 +169,27 @@ KX_Scene::KX_Scene(class SCA_IInputDevice* keyboarddevice,
m_keyboardmgr = new SCA_KeyboardManager(m_logicmgr,keyboarddevice);
m_mousemgr = new SCA_MouseManager(m_logicmgr,mousedevice);
- SCA_AlwaysEventManager* alwaysmgr = new SCA_AlwaysEventManager(m_logicmgr);
- SCA_PropertyEventManager* propmgr = new SCA_PropertyEventManager(m_logicmgr);
+ //SCA_AlwaysEventManager* alwaysmgr = new SCA_AlwaysEventManager(m_logicmgr);
+ //SCA_PropertyEventManager* propmgr = new SCA_PropertyEventManager(m_logicmgr);
SCA_ActuatorEventManager* actmgr = new SCA_ActuatorEventManager(m_logicmgr);
- SCA_RandomEventManager* rndmgr = new SCA_RandomEventManager(m_logicmgr);
- KX_RayEventManager* raymgr = new KX_RayEventManager(m_logicmgr);
+ //SCA_RandomEventManager* rndmgr = new SCA_RandomEventManager(m_logicmgr);
+ SCA_BasicEventManager* basicmgr = new SCA_BasicEventManager(m_logicmgr);
+ //KX_RayEventManager* raymgr = new KX_RayEventManager(m_logicmgr);
KX_NetworkEventManager* netmgr = new KX_NetworkEventManager(m_logicmgr, ndi);
- m_logicmgr->RegisterEventManager(alwaysmgr);
- m_logicmgr->RegisterEventManager(propmgr);
+ //m_logicmgr->RegisterEventManager(alwaysmgr);
+ //m_logicmgr->RegisterEventManager(propmgr);
m_logicmgr->RegisterEventManager(actmgr);
m_logicmgr->RegisterEventManager(m_keyboardmgr);
m_logicmgr->RegisterEventManager(m_mousemgr);
m_logicmgr->RegisterEventManager(m_timemgr);
- m_logicmgr->RegisterEventManager(rndmgr);
- m_logicmgr->RegisterEventManager(raymgr);
+ //m_logicmgr->RegisterEventManager(rndmgr);
+ //m_logicmgr->RegisterEventManager(raymgr);
m_logicmgr->RegisterEventManager(netmgr);
+ m_logicmgr->RegisterEventManager(basicmgr);
SYS_SystemHandle hSystem = SYS_GetSystem();
diff --git a/source/gameengine/Ketsji/KX_SceneActuator.cpp b/source/gameengine/Ketsji/KX_SceneActuator.cpp
index ea1be7bca6c..c7721c480e0 100644
--- a/source/gameengine/Ketsji/KX_SceneActuator.cpp
+++ b/source/gameengine/Ketsji/KX_SceneActuator.cpp
@@ -50,7 +50,7 @@ KX_SceneActuator::KX_SceneActuator(SCA_IObject *gameobj,
KX_KetsjiEngine* ketsjiEngine,
const STR_String& nextSceneName,
KX_Camera* camera)
- : SCA_IActuator(gameobj)
+ : SCA_IActuator(gameobj, KX_ACT_SCENE)
{
m_mode = mode;
m_scene = scene;
diff --git a/source/gameengine/Ketsji/KX_SoundActuator.cpp b/source/gameengine/Ketsji/KX_SoundActuator.cpp
index e2b4022a312..9d261137497 100644
--- a/source/gameengine/Ketsji/KX_SoundActuator.cpp
+++ b/source/gameengine/Ketsji/KX_SoundActuator.cpp
@@ -49,7 +49,7 @@ KX_SoundActuator::KX_SoundActuator(SCA_IObject* gameobj,
bool is3d,
KX_3DSoundSettings settings,
KX_SOUNDACT_TYPE type)//,
- : SCA_IActuator(gameobj)
+ : SCA_IActuator(gameobj, KX_ACT_SOUND)
{
m_sound = sound;
m_volume = volume;
diff --git a/source/gameengine/Ketsji/KX_StateActuator.cpp b/source/gameengine/Ketsji/KX_StateActuator.cpp
index 4159e9c373d..60812220953 100644
--- a/source/gameengine/Ketsji/KX_StateActuator.cpp
+++ b/source/gameengine/Ketsji/KX_StateActuator.cpp
@@ -40,7 +40,7 @@ KX_StateActuator::KX_StateActuator(
int operation,
unsigned int mask
)
- : SCA_IActuator(gameobj),
+ : SCA_IActuator(gameobj, KX_ACT_STATE),
m_operation(operation),
m_mask(mask)
{
diff --git a/source/gameengine/Ketsji/KX_TrackToActuator.cpp b/source/gameengine/Ketsji/KX_TrackToActuator.cpp
index e6c2f86bbce..ace1cf8a6f4 100644
--- a/source/gameengine/Ketsji/KX_TrackToActuator.cpp
+++ b/source/gameengine/Ketsji/KX_TrackToActuator.cpp
@@ -60,7 +60,7 @@ KX_TrackToActuator::KX_TrackToActuator(SCA_IObject *gameobj,
bool allow3D,
int trackflag,
int upflag)
- : SCA_IActuator(gameobj)
+ : SCA_IActuator(gameobj, KX_ACT_TRACKTO)
{
m_time = time;
m_allow3D = allow3D;
diff --git a/source/gameengine/Ketsji/KX_VisibilityActuator.cpp b/source/gameengine/Ketsji/KX_VisibilityActuator.cpp
index 184e127209f..5b0f6e6e9f2 100644
--- a/source/gameengine/Ketsji/KX_VisibilityActuator.cpp
+++ b/source/gameengine/Ketsji/KX_VisibilityActuator.cpp
@@ -41,7 +41,7 @@ KX_VisibilityActuator::KX_VisibilityActuator(
bool occlusion,
bool recursive
)
- : SCA_IActuator(gameobj),
+ : SCA_IActuator(gameobj, KX_ACT_VISIBILITY),
m_visible(visible),
m_occlusion(occlusion),
m_recursive(recursive)
diff --git a/source/gameengine/PyDoc/GameTypes.py b/source/gameengine/PyDoc/GameTypes.py
index c391d0c3dec..60511f41c2b 100644
--- a/source/gameengine/PyDoc/GameTypes.py
+++ b/source/gameengine/PyDoc/GameTypes.py
@@ -4,15 +4,17 @@ Documentation for the GameTypes Module.
@group Base: PyObjectPlus, CValue, CPropValue, SCA_ILogicBrick, SCA_IObject, SCA_ISensor, SCA_IController, SCA_IActuator
-@group Object: KX_GameObject, KX_LightObject, KX_Camera
+@group Object: KX_GameObject, KX_LightObject, KX_Camera, BL_ArmatureObject
+
+@group Animation: BL_ArmatureConstraint
@group Mesh: KX_MeshProxy, KX_PolyProxy, KX_VertexProxy
@group Shading: KX_PolygonMaterial, KX_BlenderMaterial, BL_Shader
-@group Sensors: SCA_ActuatorSensor, SCA_AlwaysSensor, SCA_DelaySensor, SCA_JoystickSensor, SCA_KeyboardSensor, KX_MouseFocusSensor, SCA_MouseSensor, KX_NearSensor, KX_NetworkMessageSensor, SCA_PropertySensor, KX_RadarSensor, SCA_RandomSensor, KX_RaySensor, KX_TouchSensor
+@group Sensors: SCA_ActuatorSensor, SCA_AlwaysSensor, SCA_DelaySensor, SCA_JoystickSensor, SCA_KeyboardSensor, KX_MouseFocusSensor, SCA_MouseSensor, KX_NearSensor, KX_NetworkMessageSensor, SCA_PropertySensor, KX_RadarSensor, SCA_RandomSensor, KX_RaySensor, KX_TouchSensor, KX_ArmatureSensor
-@group Actuators: SCA_2DFilterActuator, BL_ActionActuator, KX_SCA_AddObjectActuator, KX_CameraActuator, KX_ConstraintActuator, KX_SCA_DynamicActuator, KX_SCA_EndObjectActuator, KX_GameActuator, KX_IpoActuator, KX_NetworkMessageActuator, KX_ObjectActuator, KX_ParentActuator, SCA_PropertyActuator, SCA_RandomActuator, KX_SCA_ReplaceMeshActuator, KX_SceneActuator, BL_ShapeActionActuator, KX_SoundActuator, KX_StateActuator, KX_TrackToActuator, KX_VisibilityActuator
+@group Actuators: SCA_2DFilterActuator, BL_ActionActuator, BL_ArmatureActuator, KX_SCA_AddObjectActuator, KX_CameraActuator, KX_ConstraintActuator, KX_SCA_DynamicActuator, KX_SCA_EndObjectActuator, KX_GameActuator, KX_IpoActuator, KX_NetworkMessageActuator, KX_ObjectActuator, KX_ParentActuator, SCA_PropertyActuator, SCA_RandomActuator, KX_SCA_ReplaceMeshActuator, KX_SceneActuator, BL_ShapeActionActuator, KX_SoundActuator, KX_StateActuator, KX_TrackToActuator, KX_VisibilityActuator
@group Controllers: SCA_ANDController, SCA_NANDController, SCA_NORController, SCA_ORController, SCA_PythonController, SCA_XNORController, SCA_XORController
"""
@@ -5686,6 +5688,332 @@ class KX_Camera(KX_GameObject):
@return: the first object hit or None if no object or object does not match prop
"""
+class BL_ArmatureObject(KX_GameObject):
+ """
+ An armature object.
+
+ @ivar constraints: The list of armature constraint defined on this armature
+ Elements of the list can be accessed by index or string.
+ The key format for string access is '<bone_name>:<constraint_name>'
+ @type constraints: list of L{BL_ArmatureConstraint}
+ @ivar channels: The list of armature channels.
+ Elements of the list can be accessed by index or name the bone.
+ @type channels: list of L{BL_ArmatureChannel}
+ """
+
+ def update():
+ """
+ Ensures that the armature will be updated on next graphic frame.
+
+ This action is unecessary if a KX_ArmatureActuator with mode run is active
+ or if an action is playing. Use this function in other cases. It must be called
+ on each frame to ensure that the armature is updated continously.
+ """
+
+class BL_ArmatureActuator(SCA_IActuator):
+ """
+ Armature Actuators change constraint condition on armatures.
+
+ @group Constants: KX_ACT_ARMATURE_RUN, KX_ACT_ARMATURE_ENABLE, KX_ACT_ARMATURE_DISABLE, KX_ACT_ARMATURE_SETTARGET, KX_ACT_ARMATURE_SETWEIGHT
+ @ivar KX_ACT_ARMATURE_RUN: see type
+ @ivar KX_ACT_ARMATURE_ENABLE: see type
+ @ivar KX_ACT_ARMATURE_DISABLE: see type
+ @ivar KX_ACT_ARMATURE_SETTARGET: see type
+ @ivar KX_ACT_ARMATURE_SETWEIGHT: see type
+ @ivar type: The type of action that the actuator executes when it is active.
+
+ KX_ACT_ARMATURE_RUN(0): just make sure the armature will be updated on the next graphic frame
+ This is the only persistent mode of the actuator: it executes automatically once per frame until stopped by a controller
+
+ KX_ACT_ARMATURE_ENABLE(1): enable the constraint.
+
+ KX_ACT_ARMATURE_DISABLE(2): disable the constraint (runtime constraint values are not updated).
+
+ KX_ACT_ARMATURE_SETTARGET(3): change target and subtarget of constraint
+
+ KX_ACT_ARMATURE_SETWEIGHT(4): change weight of (only for IK constraint)
+ @type type: integer
+ @ivar constraint: The constraint object this actuator is controlling.
+ @type constraint: L{BL_ArmatureConstraint}
+ @ivar target: The object that this actuator will set as primary target to the constraint it controls
+ @type target: L{KX_GameObject}
+ @ivar subtarget: The object that this actuator will set as secondary target to the constraint it controls.
+ Currently, the only secondary target is the pole target for IK constraint.
+ @type subtarget: L{KX_GameObject}
+ @ivar weight: The weight this actuator will set on the constraint it controls.
+ Currently only the IK constraint has a weight. It must be a value between 0 and 1.
+ A weight of 0 disables a constraint while still updating constraint runtime values (see L{BL_ArmatureConstraint})
+ @type weight: float
+ """
+
+class KX_ArmatureSensor(SCA_ISensor):
+ """
+ Armature sensor detect conditions on armatures.
+
+ @group Constants: KX_ARMSENSOR_STATE_CHANGED, KX_ARMSENSOR_LIN_ERROR_BELOW, KX_ARMSENSOR_LIN_ERROR_ABOVE, KX_ARMSENSOR_ROT_ERROR_BELOW, KX_ARMSENSOR_ROT_ERROR_ABOVE
+ @ivar KX_ARMSENSOR_STATE_CHANGED: see type
+ @ivar KX_ARMSENSOR_LIN_ERROR_BELOW: see type
+ @ivar KX_ARMSENSOR_LIN_ERROR_ABOVE: see type
+ @ivar KX_ARMSENSOR_ROT_ERROR_BELOW: see type
+ @ivar KX_ARMSENSOR_ROT_ERROR_ABOVE: see type
+ @ivar type: The type of measurement that the sensor make when it is active.
+
+ KX_ARMSENSOR_STATE_CHANGED(0): detect that the constraint is changing state (active/inactive)
+
+ KX_ARMSENSOR_LIN_ERROR_BELOW(1): detect that the constraint linear error is above a threshold
+
+ KX_ARMSENSOR_LIN_ERROR_ABOVE(2): detect that the constraint linear error is below a threshold
+
+ KX_ARMSENSOR_ROT_ERROR_BELOW(3): detect that the constraint rotation error is above a threshold
+
+ KX_ARMSENSOR_ROT_ERROR_ABOVE(4): detect that the constraint rotation error is below a threshold
+ @type type: integer
+ @ivar constraint: The constraint object this sensor is watching.
+ @type constraint: L{BL_ArmatureConstraint}
+ @ivar value: The threshold used in the comparison with the constraint error
+ The linear error is only updated on CopyPose/Distance IK constraint with iTaSC solver
+ The rotation error is only updated on CopyPose+rotation IK constraint with iTaSC solver
+ The linear error on CopyPose is always >= 0: it is the norm of the distance between the target and the bone
+ The rotation error on CopyPose is always >= 0: it is the norm of the equivalent rotation vector between the bone and the target orientations
+ The linear error on Distance can be positive if the distance between the bone and the target is greater than the desired distance, and negative if the distance is smaller
+ @type value: float
+ """
+
+class BL_ArmatureConstraint(PyObjectPlus):
+ """
+ Proxy to Armature Constraint. Allows to change constraint on the fly.
+ Obtained through L{BL_ArmatureObject}.constraints.
+ Note: not all armature constraints are supported in the GE.
+
+ @group Constants: CONSTRAINT_TYPE_TRACKTO, CONSTRAINT_TYPE_KINEMATIC, CONSTRAINT_TYPE_ROTLIKE, CONSTRAINT_TYPE_LOCLIKE, CONSTRAINT_TYPE_MINMAX, CONSTRAINT_TYPE_SIZELIKE, CONSTRAINT_TYPE_LOCKTRACK, CONSTRAINT_TYPE_STRETCHTO, CONSTRAINT_TYPE_CLAMPTO, CONSTRAINT_TYPE_TRANSFORM, CONSTRAINT_TYPE_DISTLIMIT,CONSTRAINT_IK_COPYPOSE, CONSTRAINT_IK_DISTANCE,CONSTRAINT_IK_MODE_INSIDE, CONSTRAINT_IK_MODE_OUTSIDE,CONSTRAINT_IK_MODE_ONSURFACE,CONSTRAINT_IK_FLAG_TIP,CONSTRAINT_IK_FLAG_ROT, CONSTRAINT_IK_FLAG_STRETCH, CONSTRAINT_IK_FLAG_POS
+ @ivar CONSTRAINT_TYPE_TRACKTO: see type
+ @ivar CONSTRAINT_TYPE_KINEMATIC: see type
+ @ivar CONSTRAINT_TYPE_ROTLIKE: see type
+ @ivar CONSTRAINT_TYPE_LOCLIKE: see type
+ @ivar CONSTRAINT_TYPE_MINMAX: see type
+ @ivar CONSTRAINT_TYPE_SIZELIKE: see type
+ @ivar CONSTRAINT_TYPE_LOCKTRACK: see type
+ @ivar CONSTRAINT_TYPE_STRETCHTO: see type
+ @ivar CONSTRAINT_TYPE_CLAMPTO: see type
+ @ivar CONSTRAINT_TYPE_TRANSFORM: see type
+ @ivar CONSTRAINT_TYPE_DISTLIMIT: see type
+ @ivar CONSTRAINT_IK_COPYPOSE: see ik_type
+ @ivar CONSTRAINT_IK_DISTANCE: see ik_type
+ @ivar CONSTRAINT_IK_MODE_INSIDE: see ik_mode
+ @ivar CONSTRAINT_IK_MODE_OUTSIDE: see ik_mode
+ @ivar CONSTRAINT_IK_MODE_ONSURFACE: see ik_mode
+ @ivar CONSTRAINT_IK_FLAG_TIP: see ik_flag
+ @ivar CONSTRAINT_IK_FLAG_ROT: see ik_flag
+ @ivar CONSTRAINT_IK_FLAG_STRETCH: see ik_flag
+ @ivar CONSTRAINT_IK_FLAG_POS: see ik_flag
+ @ivar type: Type of constraint, read-only
+ @type type: integer, one of CONSTRAINT_TYPE_ constant
+ @ivar name: Name of constraint constructed as <bone_name>:<constraint_name>
+ This name is also the key subscript on L{BL_ArmatureObject}.constraints list
+ @type name: string
+ @ivar enforce: fraction of constraint effect that is enforced. Between 0 and 1.
+ @type enforce: float
+ @ivar headtail: position of target between head and tail of the target bone: 0=head, 1=tail
+ Only used if the target is a bone (i.e target object is an armature)
+ @type headtail: float
+ @ivar lin_error: runtime linear error (in Blender unit) on constraint at the current frame.
+ This is a runtime value updated on each frame by the IK solver. Only available on IK constraint and iTaSC solver.
+ @type lin_error: float
+ @ivar rot_error: runtime rotation error (in radiant) on constraint at the current frame.
+ This is a runtime value updated on each frame by the IK solver. Only available on IK constraint and iTaSC solver.
+ It is only set if the constraint has a rotation part, for example, a CopyPose+Rotation IK constraint.
+ @type rot_error: float
+ @ivar target: Primary target object for the constraint. The position of this object in the GE will be used as target for the constraint.
+ @type target: L{KX_GameObject}
+ @ivar subtarget: Secondary target object for the constraint. The position of this object in the GE will be used as secondary target for the constraint.
+ Currently this is only used for pole target on IK constraint.
+ @type subtarget: L{KX_GameObject}
+ @ivar active: True if the constraint is active.
+ Note: an inactive constraint does not update lin_error and rot_error.
+ @type active: boolean
+ @ivar ik_weight: Weight of the IK constraint between 0 and 1.
+ Only defined for IK constraint.
+ @type ik_weight: float
+ @ivar ik_type: Type of IK constraint, read-only
+
+ CONSTRAINT_IK_COPYPOSE(0): constraint is trying to match the position and eventually the rotation of the target.
+
+ CONSTRAINT_IK_DISTANCE(1): constraint is maintaining a certain distance to target subject to ik_mode
+ @type ik_type: integer
+ @ivar ik_flag: Combination of IK constraint option flags, read-only
+
+ CONSTRAINT_IK_FLAG_TIP(1) : set when the constraint operates on the head of the bone and not the tail
+
+ CONSTRAINT_IK_FLAG_ROT(2) : set when the constraint tries to match the orientation of the target
+
+ CONSTRAINT_IK_FLAG_STRETCH(16) : set when the armature is allowed to stretch (only the bones with stretch factor > 0.0)
+
+ CONSTRAINT_IK_FLAG_POS(32) : set when the constraint tries to match the position of the target
+ @type ik_flag: integer
+ @ivar ik_dist: Distance the constraint is trying to maintain with target, only used when ik_type=CONSTRAINT_IK_DISTANCE
+ @type ik_dist: float
+ @ivar ik_mode: Additional mode for IK constraint. Currently only used for Distance constraint:
+
+ CONSTRAINT_IK_MODE_INSIDE(0) : the constraint tries to keep the bone within ik_dist of target
+
+ CONSTRAINT_IK_MODE_OUTSIDE(1) : the constraint tries to keep the bone outside ik_dist of the target
+
+ CONSTRAINT_IK_MODE_ONSURFACE(2) : the constraint tries to keep the bone exactly at ik_dist of the target
+ @type ik_mode: integer
+ """
+
+class BL_ArmatureChannel(PyObjectPlus):
+ """
+ Proxy to armature pose channel. Allows to read and set armature pose.
+ The attributes are identical to RNA attributes, but mostly in read-only mode.
+
+ @group Constants: PCHAN_ROT_QUAT, PCHAN_ROT_XYZ, PCHAN_ROT_XZY, PCHAN_ROT_YXZ, PCHAN_ROT_YZX, PCHAN_ROT_ZXY, PCHAN_ROT_ZYX
+ @ivar PCHAN_ROT_QUAT: see rotation_mode
+ @ivar PCHAN_ROT_XYZ: see rotation_mode
+ @ivar PCHAN_ROT_XZY: see rotation_mode
+ @ivar PCHAN_ROT_YXZ: see rotation_mode
+ @ivar PCHAN_ROT_YZX: see rotation_mode
+ @ivar PCHAN_ROT_ZXY: see rotation_mode
+ @ivar PCHAN_ROT_ZYX: see rotation_mode
+ @ivar name: channel name (=bone name), read-only.
+ @type name: string
+ @ivar bone: return the bone object corresponding to this pose channel, read-only.
+ @type bone: L{BL_ArmatureBone}
+ @ivar parent: return the parent channel object, None if root channel, read-only.
+ @type parent: L{BL_ArmatureChannel}
+ @ivar has_ik: true if the bone is part of an active IK chain, read-only.
+ This flag is not set when an IK constraint is defined but not enabled (miss target information for example)
+ @type has_ik: boolean
+ @ivar ik_dof_x: true if the bone is free to rotation in the X axis, read-only.
+ @type ik_dof_x: boolean
+ @ivar ik_dof_y: true if the bone is free to rotation in the Y axis, read-only.
+ @type ik_dof_y: boolean
+ @ivar ik_dof_z: true if the bone is free to rotation in the Z axis, read-only.
+ @type ik_dof_z: boolean
+ @ivar ik_limit_x: true if a limit is imposed on X rotation, read-only.
+ @type ik_limit_x: boolean
+ @ivar ik_limit_y: true if a limit is imposed on Y rotation, read-only.
+ @type ik_limit_y: boolean
+ @ivar ik_limit_z: true if a limit is imposed on Z rotation, read-only.
+ @type ik_limit_z: boolean
+ @ivar ik_rot_control: true if channel rotation should applied as IK constraint, read-only.
+ @type ik_rot_control: boolean
+ @ivar ik_lin_control: true if channel size should applied as IK constraint, read-only.
+ @type ik_lin_control: boolean
+ @ivar location: displacement of the bone head in armature local space, read-write.
+ You can only move a bone if it is unconnected to its parent. An action playing on the armature may change the value. An IK chain does not update this value, see joint_rotation.
+ Changing this field has no immediate effect, the pose is updated when the armature is updated during the graphic render (see L{BL_ArmatureObject.update})
+ @type location: vector [X,Y,Z]
+ @ivar scale: scale of the bone relative to its parent, read-write.
+ An action playing on the armature may change the value. An IK chain does not update this value, see joint_rotation.
+ Changing this field has no immediate effect, the pose is updated when the armature is updated during the graphic render (see L{BL_ArmatureObject.update})
+ @type scale: vector [sizeX, sizeY, sizeZ]
+ @ivar rotation: rotation of the bone relative to its parent expressed as a quaternion, read-write.
+ This field is only used if rotation_mode is 0. An action playing on the armature may change the value. An IK chain does not update this value, see joint_rotation.
+ Changing this field has no immediate effect, the pose is updated when the armature is updated during the graphic render (see L{BL_ArmatureObject.update})
+ @type rotation: vector [qr, qi, qj, qk]
+ @ivar euler_rotation: rotation of the bone relative to its parent expressed as a set of euler angles, read-write.
+ This field is only used if rotation_mode is > 0. You must always pass the angles in [X,Y,Z] order; the order of applying the angles to the bone depends on rotation_mode. An action playing on the armature may change this field. An IK chain does not update this value, see joint_rotation.
+ Changing this field has no immediate effect, the pose is updated when the armature is updated during the graphic render (see L{BL_ArmatureObject.update})
+ @type euler_rotation: vector [X, Y, Z]
+ @ivar rotation_mode: method of updating the bone rotation, read-write.
+ Use the following constants (euler mode are named as in BLender UI but the actual axis order is reversed):
+ - PCHAN_ROT_QUAT(0) : use quaternioin in rotation attribute to update bone rotation
+ - PCHAN_ROT_XYZ(1) : use euler_rotation and apply angles on bone's Z, Y, X axis successively
+ - PCHAN_ROT_XZY(2) : use euler_rotation and apply angles on bone's Y, Z, X axis successively
+ - PCHAN_ROT_YXZ(3) : use euler_rotation and apply angles on bone's Z, X, Y axis successively
+ - PCHAN_ROT_YZX(4) : use euler_rotation and apply angles on bone's X, Z, Y axis successively
+ - PCHAN_ROT_ZXY(5) : use euler_rotation and apply angles on bone's Y, X, Z axis successively
+ - PCHAN_ROT_ZYX(6) : use euler_rotation and apply angles on bone's X, Y, Z axis successively
+ @type rotation_mode: integer
+ @ivar channel_matrix: pose matrix in bone space (deformation of the bone due to action, constraint, etc), Read-only.
+ This field is updated after the graphic render, it represents the current pose.
+ @type channel_matrix: matrix [4][4]
+ @ivar pose_matrix: pose matrix in armature space, read-only,
+ This field is updated after the graphic render, it represents the current pose.
+ @type pose_matrix: matrix [4][4]
+ @ivar pose_head: position of bone head in armature space, read-only.
+ @type pose_head: vector [x, y, z]
+ @ivar pose_tail: position of bone tail in armature space, read-only.
+ @type pose_tail: vector [x, y, z]
+ @ivar ik_min_x: minimum value of X rotation in degree (<= 0) when X rotation is limited (see ik_limit_x), read-only.
+ @type ik_min_x: float
+ @ivar ik_max_x: maximum value of X rotation in degree (>= 0) when X rotation is limited (see ik_limit_x), read-only.
+ @type ik_max_x: float
+ @ivar ik_min_y: minimum value of Y rotation in degree (<= 0) when Y rotation is limited (see ik_limit_y), read-only.
+ @type ik_min_y: float
+ @ivar ik_max_y: maximum value of Y rotation in degree (>= 0) when Y rotation is limited (see ik_limit_y), read-only.
+ @type ik_max_y: float
+ @ivar ik_min_z: minimum value of Z rotation in degree (<= 0) when Z rotation is limited (see ik_limit_z), read-only.
+ @type ik_min_z: float
+ @ivar ik_max_z: maximum value of Z rotation in degree (>= 0) when Z rotation is limited (see ik_limit_z), read-only.
+ @type ik_max_z: float
+ @ivar ik_stiffness_x: bone rotation stiffness in X axis, read-only
+ @type ik_stiffness_x: float between 0 and 1
+ @ivar ik_stiffness_y: bone rotation stiffness in Y axis, read-only
+ @type ik_stiffness_y: float between 0 and 1
+ @ivar ik_stiffness_z: bone rotation stiffness in Z axis, read-only
+ @type ik_stiffness_z: float between 0 and 1
+ @ivar ik_stretch: ratio of scale change that is allowed, 0=bone can't change size, read-only.
+ @type ik_stretch: float
+ @ivar ik_rot_weight: weight of rotation constraint when ik_rot_control is set, read-write.
+ @type ik_rot_weight: float between 0 and 1
+ @ivar ik_lin_weight: weight of size constraint when ik_lin_control is set, read-write.
+ @type ik_lin_weight: float between 0 and 1
+ @ivar joint_rotation: control bone rotation in term of joint angle (for robotic applications), read-write.
+ When writing to this attribute, you pass a [x, y, z] vector and an appropriate set of euler angles or quaternion is calculated according to the rotation_mode.
+ When you read this attribute, the current pose matrix is converted into a [x, y, z] vector representing the joint angles.
+ The value and the meaning of the x, y, z depends on the ik_dof_ attributes:
+ - 1DoF joint X, Y or Z: the corresponding x, y, or z value is used an a joint angle in radiant
+ - 2DoF joint X+Y or Z+Y: treated as 2 successive 1DoF joints: first X or Z, then Y. The x or z value is used as a joint angle in radiant along the X or Z axis, followed by a rotation along the new Y axis of y radiants.
+ - 2DoF joint X+Z: treated as a 2DoF joint with rotation axis on the X/Z plane. The x and z values are used as the coordinates of the rotation vector in the X/Z plane.
+ - 3DoF joint X+Y+Z: treated as a revolute joint. The [x,y,z] vector represents the equivalent rotation vector to bring the joint from the rest pose to the new pose.
+
+ Notes:
+ - The bone must be part of an IK chain if you want to set the ik_dof_ attributes via the UI, but this will interfere with this attribute since the IK solver will overwrite the pose. You can stay in control of the armature if you create an IK constraint but do not finalize it (e.g. don't set a target): the IK solver will not run but the IK panel will show up on the UI for each bone in the chain.
+ - [0,0,0] always corresponds to the rest pose.
+ - You must request the armature pose to update and wait for the next graphic frame to see the effect of setting this attribute (see L{BL_ArmatureObject.update}).
+ - You can read the result of the calculation in rotation or euler_rotation attributes after setting this attribute.
+ @type joint_rotation: vector [x, y, z]
+ """
+
+class BL_ArmatureBone(PyObjectPlus):
+ """
+ Proxy to Blender bone structure. All fields are read-only and comply to RNA names.
+ All space attribute correspond to the rest pose.
+
+ @ivar name: bone name
+ @type name: string
+ @ivar connected: true when the bone head is struck to the parent's tail
+ @type connected: boolean
+ @ivar hinge: true when bone doesn't inherit rotation or scale from parent bone
+ @type hinge: boolean
+ @ivar inherit_scale: true when bone inherits scaling from parent bone
+ @type inherit_scale: boolean
+ @ivar bbone_segments: number of B-bone segments
+ @type bbone_segments: integer
+ @ivar roll: bone rotation around head-tail axis
+ @type roll: float
+ @ivar head: location of head end of the bone in parent bone space
+ @type head: vector [x, y, z]
+ @ivar tail: location of head end of the bone in parent bone space
+ @type tail: vector [x, y, z]
+ @ivar length: bone length
+ @type length: float
+ @ivar arm_head: location of head end of the bone in armature space
+ @type arm_head: vector [x, y, z]
+ @ivar arm_tail: location of tail end of the bone in armature space
+ @type arm_tail: vector [x, y, z]
+ @ivar arm_mat: matrix of the bone head in armature space
+ This matrix has no scale part.
+ @type arm_mat: matrix [4][4]
+ @ivar bone_mat: rotation matrix of the bone in parent bone space.
+ @type bone_mat: matrix [3][3]
+ @ivar parent: parent bone, or None for root bone
+ @type parent: L{BL_ArmatureBone}
+ @ivar children: list of bone's children
+ @type children: list of L{BL_ArmatureBone}
+ """
# Util func to extract all attrs
"""
import types
diff --git a/source/gameengine/SceneGraph/SG_DList.h b/source/gameengine/SceneGraph/SG_DList.h
index 3e17fb55dc0..97951792ab5 100644
--- a/source/gameengine/SceneGraph/SG_DList.h
+++ b/source/gameengine/SceneGraph/SG_DList.h
@@ -89,6 +89,46 @@ public:
}
};
+ template<typename T> class const_iterator
+ {
+ private:
+ const SG_DList& m_head;
+ const T* m_current;
+ public:
+ typedef const_iterator<T> _myT;
+ const_iterator(const SG_DList& head) : m_head(head), m_current(NULL) {}
+ ~const_iterator() {}
+
+ void begin()
+ {
+ m_current = (const T*)m_head.Peek();
+ }
+ void back()
+ {
+ m_current = (const T*)m_head.Back();
+ }
+ bool end()
+ {
+ return (m_current == (const T*)m_head.Self());
+ }
+ const T* operator*()
+ {
+ return m_current;
+ }
+ _myT& operator++()
+ {
+ // no check of NULL! make sure you don't try to increment beyond end
+ m_current = (const T*)m_current->Peek();
+ return *this;
+ }
+ _myT& operator--()
+ {
+ // no check of NULL! make sure you don't try to increment beyond end
+ m_current = (const T*)m_current->Back();
+ return *this;
+ }
+ };
+
SG_DList()
{
m_flink = m_blink = this;
@@ -159,6 +199,18 @@ public:
{
return this;
}
+ inline const SG_DList *Peek() const // Look at front without removing
+ {
+ return (const SG_DList*)m_flink;
+ }
+ inline const SG_DList *Back() const // Look at front without removing
+ {
+ return (const SG_DList*)m_blink;
+ }
+ inline const SG_DList *Self() const
+ {
+ return this;
+ }
#ifdef WITH_CXX_GUARDEDALLOC
@@ -168,5 +220,32 @@ public:
#endif
};
+/**
+ * SG_DListHead : Template class that implements copy constructor to duplicate list automatically
+ * The elements of the list must have themselves a copy constructor.
+ */
+template<typename T> class SG_DListHead : public SG_DList
+{
+public:
+ typedef SG_DListHead<T> _myT;
+ SG_DListHead() : SG_DList() {}
+ SG_DListHead(const _myT& other) : SG_DList()
+ {
+ // copy the list, assuming objects of type T
+ const_iterator<T> eit(other);
+ T* elem;
+ for (eit.begin(); !eit.end(); ++eit) {
+ elem = (*eit)->GetReplica();
+ AddBack(elem);
+ }
+ }
+ virtual ~SG_DListHead() {}
+ T* Remove()
+ {
+ return static_cast<T*>(SG_DList::Remove());
+ }
+
+};
+
#endif //__SG_DLIST