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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--release/scripts/ui/properties_object_constraint.py7
-rw-r--r--source/blender/blenkernel/BKE_armature.h7
-rw-r--r--source/blender/blenkernel/intern/action.c4
-rw-r--r--source/blender/blenkernel/intern/armature.c277
-rw-r--r--source/blender/blenkernel/intern/constraint.c93
-rw-r--r--source/blender/blenkernel/intern/depsgraph.c4
-rw-r--r--source/blender/blenloader/intern/readfile.c5
-rw-r--r--source/blender/blenloader/intern/writefile.c10
-rw-r--r--source/blender/editors/object/object_constraint.c32
-rw-r--r--source/blender/editors/space_view3d/drawarmature.c98
-rw-r--r--source/blender/ikplugin/intern/iksolver_plugin.c8
-rw-r--r--source/blender/makesdna/DNA_action_types.h43
-rw-r--r--source/blender/makesdna/DNA_armature_types.h1
-rw-r--r--source/blender/makesdna/DNA_constraint_types.h40
-rw-r--r--source/blender/makesrna/RNA_access.h1
-rw-r--r--source/blender/makesrna/intern/rna_constraint.c30
16 files changed, 589 insertions, 71 deletions
diff --git a/release/scripts/ui/properties_object_constraint.py b/release/scripts/ui/properties_object_constraint.py
index 29df85abf6a..d33b18b1b09 100644
--- a/release/scripts/ui/properties_object_constraint.py
+++ b/release/scripts/ui/properties_object_constraint.py
@@ -581,6 +581,13 @@ class ConstraintButtonsPanel(bpy.types.Panel):
row = layout.row()
row.itemL(text="To:")
row.itemR(con, "track", expand=True)
+
+ def SPLINE_IK(self, context, layout, con):
+ self.target_template(layout, con)
+
+ row = layout.row()
+ row.itemR(con, "chain_length")
+ # TODO: add the various options this constraint has...
class OBJECT_PT_constraints(ConstraintButtonsPanel):
diff --git a/source/blender/blenkernel/BKE_armature.h b/source/blender/blenkernel/BKE_armature.h
index 19c2662054b..03f23f07c8c 100644
--- a/source/blender/blenkernel/BKE_armature.h
+++ b/source/blender/blenkernel/BKE_armature.h
@@ -55,11 +55,14 @@ typedef struct PoseTarget
typedef struct PoseTree
{
struct PoseTree *next, *prev;
-
+
+ int type; /* type of IK that this serves (CONSTRAINT_TYPE_KINEMATIC or ..._SPLINEIK) */
+ int totchannel; /* number of pose channels */
+
struct ListBase targets; /* list of targets of the tree */
struct bPoseChannel **pchan; /* array of pose channels */
int *parent; /* and their parents */
- int totchannel; /* number of pose channels */
+
float (*basis_change)[3][3]; /* basis change result from solver */
int iterations; /* iterations from the constraint */
int stretch; /* disable stretching */
diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c
index 25f539a1992..2dec76d1b6b 100644
--- a/source/blender/blenkernel/intern/action.c
+++ b/source/blender/blenkernel/intern/action.c
@@ -622,7 +622,7 @@ static void copy_pose_channel_data(bPoseChannel *pchan, const bPoseChannel *chan
}
}
-/* checks for IK constraint, and also for Follow-Path constraint.
+/* checks for IK constraint, Spline IK, and also for Follow-Path constraint.
* can do more constraints flags later
*/
/* pose should be entirely OK */
@@ -675,6 +675,8 @@ void update_pose_constraint_flags(bPose *pose)
if ((data->tar) && (data->tar->type==OB_CURVE))
pose->flag |= POSE_CONSTRAINTS_TIMEDEPEND;
}
+ else if (con->type == CONSTRAINT_TYPE_SPLINEIK)
+ pchan->constflag |= PCHAN_HAS_SPLINEIK;
else
pchan->constflag |= PCHAN_HAS_CONST;
}
diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c
index 139ff9d6b19..0bb0041cece 100644
--- a/source/blender/blenkernel/intern/armature.c
+++ b/source/blender/blenkernel/intern/armature.c
@@ -52,6 +52,7 @@
#include "BKE_armature.h"
#include "BKE_action.h"
+#include "BKE_anim.h"
#include "BKE_blender.h"
#include "BKE_constraint.h"
#include "BKE_curve.h"
@@ -1604,6 +1605,260 @@ void armature_rebuild_pose(Object *ob, bArmature *arm)
}
+/* ********************** SPLINE IK SOLVER ******************* */
+
+/* Temporary evaluation tree data used for Spline IK */
+typedef struct tSplineIK_Tree {
+ struct tSplineIK_Tree *next, *prev;
+
+ int type; /* type of IK that this serves (CONSTRAINT_TYPE_KINEMATIC or ..._SPLINEIK) */
+
+ int chainlen; /* number of bones in the chain */
+ bPoseChannel **chain; /* chain of bones to affect using Spline IK (ordered from the tip) */
+
+ bPoseChannel *root; /* bone that is the root node of the chain */
+
+ bConstraint *con; /* constraint for this chain */
+ bSplineIKConstraint *ikData; /* constraint settings for this chain */
+} tSplineIK_Tree;
+
+/* ----------- */
+
+/* Tag the bones in the chain formed by the given bone for IK */
+static void splineik_init_tree_from_pchan(Object *ob, bPoseChannel *pchan_tip)
+{
+ bPoseChannel *pchan, *pchanRoot=NULL;
+ bPoseChannel *pchanChain[255];
+ bConstraint *con = NULL;
+ bSplineIKConstraint *ikData = NULL;
+ float boneLengths[255];
+ float totLength = 0.0f;
+ int segcount = 0;
+
+ /* find the SplineIK constraint */
+ for (con= pchan_tip->constraints.first; con; con= con->next) {
+ if (con->type == CONSTRAINT_TYPE_SPLINEIK) {
+ ikData= con->data;
+
+ /* target can only be curve */
+ if ((ikData->tar == NULL) || (ikData->tar->type != OB_CURVE))
+ continue;
+ /* skip if disabled */
+ if ( (con->enforce == 0.0f) || (con->flag & (CONSTRAINT_DISABLE|CONSTRAINT_OFF)) )
+ continue;
+
+ /* otherwise, constraint is ok... */
+ break;
+ }
+ }
+ if (con == NULL)
+ return;
+
+ /* find the root bone and the chain of bones from the root to the tip
+ * NOTE: this assumes that the bones are connected, but that may not be true...
+ */
+ for (pchan= pchan_tip; pchan; pchan= pchan->parent) {
+ /* store this segment in the chain */
+ pchanChain[segcount]= pchan;
+
+ /* if performing rebinding, calculate the length of the bone */
+ if ((ikData->flag & CONSTRAINT_SPLINEIK_BOUND) == 0) {
+ boneLengths[segcount]= pchan->bone->length;
+ totLength += boneLengths[segcount];
+ }
+
+ /* check if we've gotten the number of bones required yet (after incrementing the count first)
+ * NOTE: the 255 limit here is rather ugly, but the standard IK does this too!
+ */
+ segcount++;
+ if ((segcount == ikData->chainlen) || (segcount > 255))
+ break;
+ }
+
+ if (segcount == 0)
+ return;
+ else
+ pchanRoot= pchanChain[segcount-1];
+
+ /* perform binding step if required */
+ if ((ikData->flag & CONSTRAINT_SPLINEIK_BOUND) == 0) {
+ int i;
+
+ /* setup new empty array for the points list */
+ if (ikData->points)
+ MEM_freeN(ikData->points);
+ ikData->numpoints= (ikData->flag & CONSTRAINT_SPLINEIK_NO_ROOT)? ikData->chainlen : ikData->chainlen+1;
+ ikData->points= MEM_callocN(sizeof(float)*ikData->numpoints, "Spline IK Binding");
+
+ /* perform binding of the joints to parametric positions along the curve based
+ * proportion of the total length that each bone occupies
+ */
+ for (i = 0; i < segcount; i++) {
+ if (i != 0) {
+ /* 'head' joints
+ * - 2 methods; the one chosen depends on whether we've got usable lengths
+ */
+ if (totLength == 0.0f) {
+ /* 1) equi-spaced joints */
+ // TODO: maybe this should become an option too, in case we want this option by default
+ ikData->points[i]= (1.0f / (float)segcount); // TODO: optimize by puttig this outside the loop!
+ }
+ else {
+ /* 2) to find this point on the curve, we take a step from the previous joint
+ * a distance given by the proportion that this bone takes
+ */
+ ikData->points[i]= ikData->points[i-1] - (boneLengths[i] / totLength);
+ }
+ }
+ else {
+ /* 'tip' of chain, special exception for the first joint */
+ ikData->points[0]= 1.0f;
+ }
+ }
+
+ /* spline has now been bound */
+ ikData->flag |= CONSTRAINT_SPLINEIK_BOUND;
+ }
+
+ /* make a new Spline-IK chain, and store it in the IK chains */
+ // TODO: we should check if there is already an IK chain on this, since that would take presidence...
+ {
+ /* make new tree */
+ tSplineIK_Tree *tree= MEM_callocN(sizeof(tSplineIK_Tree), "SplineIK Tree");
+ tree->type= CONSTRAINT_TYPE_SPLINEIK;
+
+ tree->chainlen= segcount;
+
+ /* copy over the array of links to bones in the chain (from tip to root) */
+ tree->chain= MEM_callocN(sizeof(bPoseChannel*)*segcount, "SplineIK Chain");
+ memcpy(tree->chain, pchanChain, sizeof(bPoseChannel*)*segcount);
+
+ tree->root= pchanRoot;
+ tree->con= con;
+ tree->ikData= ikData;
+
+ /* AND! link the tree to the root */
+ BLI_addtail(&pchanRoot->iktree, tree);
+ }
+
+ /* mark root channel having an IK tree */
+ pchanRoot->flag |= POSE_IKSPLINE;
+}
+
+/* Tag which bones are members of Spline IK chains */
+static void splineik_init_tree(Scene *scene, Object *ob, float ctime)
+{
+ bPoseChannel *pchan;
+
+ /* find the tips of Spline IK chains, which are simply the bones which have been tagged as such */
+ for (pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
+ if (pchan->constflag & PCHAN_HAS_SPLINEIK)
+ splineik_init_tree_from_pchan(ob, pchan);
+ }
+}
+
+/* ----------- */
+
+/* Evaluate spline IK for a given bone */
+// TODO: this method doesn't allow for non-strechiness...
+// TODO: include code for dealing with constraint blending
+static void splineik_evaluate_bone(tSplineIK_Tree *tree, Object *ob, bPoseChannel *pchan, int index)
+{
+ bSplineIKConstraint *ikData= tree->ikData;
+ float dirX[3]={1,0,0}, dirZ[3]={0,0,1};
+ float axis1[3], axis2[3], tmpVec[3];
+ float splineVec[3], scaleFac;
+ float vec[4], dir[3];
+
+ /* step 1: get xyz positions for the endpoints of the bone */
+ /* tail */
+ if ( where_on_path(ikData->tar, ikData->points[index], vec, dir, NULL, NULL) ) {
+ /* convert the position to pose-space, then store it */
+ Mat4MulVecfl(ob->imat, vec);
+ VECCOPY(pchan->pose_tail, vec);
+ }
+ /* head */ // TODO: only calculate here when we're
+ if ( where_on_path(ikData->tar, ikData->points[index+1], vec, dir, NULL, NULL) ) {
+ /* store the position, and convert it to pose space */
+ Mat4MulVecfl(ob->imat, vec);
+ VECCOPY(pchan->pose_head, vec);
+ }
+
+
+ /* step 2a: determine the implied transform from these endpoints
+ * - splineVec: the vector direction that the spline applies on the bone
+ * - scaleFac: the factor that the bone length is scaled by to get the desired amount
+ */
+ VecSubf(splineVec, pchan->pose_tail, pchan->pose_head);
+ scaleFac= VecLength(splineVec) / pchan->bone->length; // TODO: this will need to be modified by blending factor
+
+ /* step 2b: the spline vector now becomes the y-axis of the bone
+ * - we need to normalise the splineVec first, so that it's just a unit direction vector
+ */
+ Mat4One(pchan->pose_mat);
+
+ Normalize(splineVec);
+ VECCOPY(pchan->pose_mat[1], splineVec);
+
+
+ /* step 3: determine two vectors which will both be at right angles to the bone vector
+ * based on the method described at
+ * http://ltcconline.net/greenl/courses/203/Vectors/orthonormalBases.htm
+ * and normalise them to make sure they they don't act strangely
+ */
+ /* x-axis = dirX - projection(dirX onto splineVec) */
+ Projf(axis1, dirX, splineVec); /* project dirX onto splineVec */
+ VecSubf(pchan->pose_mat[0], dirX, axis1);
+
+ Normalize(pchan->pose_mat[0]);
+
+ /* z-axis = dirZ - projection(dirZ onto splineVec) - projection(dirZ onto dirX) */
+ Projf(axis1, dirZ, splineVec); /* project dirZ onto Y-Axis */
+ Projf(axis2, dirZ, pchan->pose_mat[0]); /* project dirZ onto X-Axis */
+
+ VecSubf(tmpVec, dirZ, axis1); /* dirZ - proj(dirZ->YAxis) */
+ VecSubf(pchan->pose_mat[2], tmpVec, axis2); /* (dirZ - proj(dirZ->YAxis)) - proj(dirZ->XAxis) */
+
+ Normalize(pchan->pose_mat[2]);
+
+
+ /* step 4a: multiply all the axes of the bone by the scaling factor to get uniform scaling */
+ // TODO: maybe this can be extended to give non-uniform scaling?
+ //VecMulf(pchan->pose_mat[0], scaleFac);
+ VecMulf(pchan->pose_mat[1], scaleFac);
+ //VecMulf(pchan->pose_mat[2], scaleFac);
+
+ /* step 5: set the location of the bone in the matrix */
+ VECCOPY(pchan->pose_mat[3], pchan->pose_head);
+
+ /* done! */
+ pchan->flag |= POSE_DONE;
+}
+
+/* Evaluate the chain starting from the nominated bone */
+static void splineik_execute_tree(Scene *scene, Object *ob, bPoseChannel *pchan_root, float ctime)
+{
+ tSplineIK_Tree *tree;
+
+ /* for each pose-tree, execute it if it is spline, otherwise just free it */
+ for (tree= pchan_root->iktree.first; tree; tree= pchan_root->iktree.first) {
+ /* only evaluate if tagged for Spline IK */
+ if (tree->type == CONSTRAINT_TYPE_SPLINEIK) {
+ int i;
+
+ /* walk over each bone in the chain, calculating the effects of spline IK */
+ for (i= 0; i < tree->chainlen; i++) {
+ bPoseChannel *pchan= tree->chain[i];
+ splineik_evaluate_bone(tree, ob, pchan, i);
+ }
+ }
+
+ /* free the tree info now */
+ if (tree->chain) MEM_freeN(tree->chain);
+ BLI_freelinkN(&pchan_root->iktree, tree);
+ }
+}
+
/* ********************** THE POSE SOLVER ******************* */
@@ -1629,7 +1884,7 @@ void chan_calc_mat(bPoseChannel *chan)
}
else {
/* quats are normalised before use to eliminate scaling issues */
- NormalQuat(chan->quat);
+ NormalQuat(chan->quat); // TODO: do this with local vars only!
QuatToMat3(chan->quat, rmat);
}
@@ -1908,21 +2163,31 @@ void where_is_pose (Scene *scene, Object *ob)
}
else {
Mat4Invert(ob->imat, ob->obmat); // imat is needed
-
+
/* 1. clear flags */
for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
pchan->flag &= ~(POSE_DONE|POSE_CHAIN|POSE_IKTREE);
}
- /* 2. construct the IK tree */
+
+ /* 2a. construct the IK tree (standard IK) */
BIK_initialize_tree(scene, ob, ctime);
-
+
+ /* 2b. construct the Spline IK trees
+ * - this is not integrated as an IK plugin, since it should be able
+ * to function in conjunction with standard IK
+ */
+ splineik_init_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) {
-
- /* 4. if we find an IK root, we handle it separated */
+ /* 4a. if we find an IK root, we handle it separated */
if(pchan->flag & POSE_IKTREE) {
BIK_execute_tree(scene, ob, pchan, ctime);
}
+ /* 4b. if we find a Spline IK root, we handle it separated too */
+ else if(pchan->flag & POSE_IKSPLINE) {
+ splineik_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);
diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c
index 93a4ba80d7c..a319838a56f 100644
--- a/source/blender/blenkernel/intern/constraint.c
+++ b/source/blender/blenkernel/intern/constraint.c
@@ -1224,14 +1224,14 @@ static void followpath_get_tarmat (bConstraint *con, bConstraintOb *cob, bConstr
QuatToMat4(quat, totmat);
}
-
+
if (data->followflag & FOLLOWPATH_RADIUS) {
float tmat[4][4], rmat[4][4];
Mat4Scale(tmat, radius);
Mat4MulMat4(rmat, totmat, tmat);
Mat4CpyMat4(totmat, rmat);
}
-
+
VECCOPY(totmat[3], vec);
Mat4MulSerie(ct->matrix, ct->tar->obmat, totmat, NULL, NULL, NULL, NULL, NULL, NULL);
@@ -1265,7 +1265,7 @@ static void followpath_evaluate (bConstraint *con, bConstraintOb *cob, ListBase
/* un-apply scaling caused by path */
if ((data->followflag & FOLLOWPATH_RADIUS)==0) { /* XXX - assume that scale correction means that radius will have some scale error in it - Campbell */
float obsize[3];
-
+
Mat4ToSize(cob->matrix, obsize);
if (obsize[0])
VecMulf(cob->matrix[0], size[0] / obsize[0]);
@@ -3446,13 +3446,95 @@ static bConstraintTypeInfo CTI_DAMPTRACK = {
damptrack_evaluate /* evaluate */
};
+/* ----------- Spline IK ------------ */
+
+static void splineik_free (bConstraint *con)
+{
+ bSplineIKConstraint *data= con->data;
+
+ /* binding array */
+ if (data->points)
+ MEM_freeN(data->points);
+}
+
+static void splineik_copy (bConstraint *con, bConstraint *srccon)
+{
+ bSplineIKConstraint *src= srccon->data;
+ bSplineIKConstraint *dst= con->data;
+
+ /* copy the binding array */
+ dst->points= MEM_dupallocN(src->points);
+}
+
+static int splineik_get_tars (bConstraint *con, ListBase *list)
+{
+ if (con && list) {
+ bSplineIKConstraint *data= con->data;
+ bConstraintTarget *ct;
+
+ /* standard target-getting macro for single-target constraints without subtargets */
+ SINGLETARGETNS_GET_TARS(con, data->tar, ct, list)
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static void splineik_flush_tars (bConstraint *con, ListBase *list, short nocopy)
+{
+ if (con && list) {
+ bSplineIKConstraint *data= con->data;
+ bConstraintTarget *ct= list->first;
+
+ /* the following macro is used for all standard single-target constraints */
+ SINGLETARGETNS_FLUSH_TARS(con, data->tar, ct, list, nocopy)
+ }
+}
+
+static void splineik_get_tarmat (bConstraint *con, bConstraintOb *cob, bConstraintTarget *ct, float ctime)
+{
+ if (VALID_CONS_TARGET(ct)) {
+ Curve *cu= ct->tar->data;
+
+ /* note: when creating constraints that follow path, the curve gets the CU_PATH set now,
+ * currently for paths to work it needs to go through the bevlist/displist system (ton)
+ */
+
+ /* only happens on reload file, but violates depsgraph still... fix! */
+ if (cu->path==NULL || cu->path->data==NULL)
+ makeDispListCurveTypes(cob->scene, ct->tar, 0);
+ }
+
+ /* technically, this isn't really needed for evaluation, but we don't know what else
+ * might end up calling this...
+ */
+ if (ct)
+ Mat4One(ct->matrix);
+}
+
+static bConstraintTypeInfo CTI_SPLINEIK = {
+ CONSTRAINT_TYPE_SPLINEIK, /* type */
+ sizeof(bSplineIKConstraint), /* size */
+ "Spline IK", /* name */
+ "bSplineIKConstraint", /* struct name */
+ splineik_free, /* free data */
+ NULL, /* relink data */
+ splineik_copy, /* copy data */
+ NULL, /* new data */
+ splineik_get_tars, /* get constraint targets */
+ splineik_flush_tars, /* flush constraint targets */
+ splineik_get_tarmat, /* get target matrix */
+ NULL /* evaluate - solved as separate loop */
+};
+
/* ************************* Constraints Type-Info *************************** */
/* All of the constraints api functions use bConstraintTypeInfo structs to carry out
* and operations that involve constraint specific code.
*/
/* These globals only ever get directly accessed in this file */
-static bConstraintTypeInfo *constraintsTypeInfo[NUM_CONSTRAINT_TYPES+1];
+static bConstraintTypeInfo *constraintsTypeInfo[NUM_CONSTRAINT_TYPES];
static short CTI_INIT= 1; /* when non-zero, the list needs to be updated */
/* This function only gets called when CTI_INIT is non-zero */
@@ -3479,6 +3561,7 @@ static void constraints_init_typeinfo () {
constraintsTypeInfo[19]= &CTI_TRANSFORM; /* Transformation Constraint */
constraintsTypeInfo[20]= &CTI_SHRINKWRAP; /* Shrinkwrap Constraint */
constraintsTypeInfo[21]= &CTI_DAMPTRACK; /* Damped TrackTo Constraint */
+ constraintsTypeInfo[22]= &CTI_SPLINEIK; /* Spline IK Constraint */
}
/* This function should be used for getting the appropriate type-info when only
@@ -3494,7 +3577,7 @@ bConstraintTypeInfo *get_constraint_typeinfo (int type)
/* only return for valid types */
if ( (type >= CONSTRAINT_TYPE_NULL) &&
- (type <= NUM_CONSTRAINT_TYPES ) )
+ (type < NUM_CONSTRAINT_TYPES ) )
{
/* there shouldn't be any segfaults here... */
return constraintsTypeInfo[type];
diff --git a/source/blender/blenkernel/intern/depsgraph.c b/source/blender/blenkernel/intern/depsgraph.c
index d60a26e8feb..36568ee5667 100644
--- a/source/blender/blenkernel/intern/depsgraph.c
+++ b/source/blender/blenkernel/intern/depsgraph.c
@@ -399,7 +399,7 @@ static void build_dag_object(DagForest *dag, DagNode *scenenode, Scene *scene, O
if (ct->subtarget[0])
dag_add_relation(dag,node3,node, DAG_RL_OB_DATA|DAG_RL_DATA_DATA, cti->name);
- else if(ELEM(con->type, CONSTRAINT_TYPE_FOLLOWPATH, CONSTRAINT_TYPE_CLAMPTO))
+ else if(ELEM3(con->type, CONSTRAINT_TYPE_FOLLOWPATH, CONSTRAINT_TYPE_CLAMPTO, CONSTRAINT_TYPE_SPLINEIK))
dag_add_relation(dag,node3,node, DAG_RL_DATA_DATA|DAG_RL_OB_DATA, cti->name);
else
dag_add_relation(dag,node3,node, DAG_RL_OB_DATA, cti->name);
@@ -2407,7 +2407,7 @@ void DAG_pose_sort(Object *ob)
bPoseChannel *target= get_pose_channel(ob->pose, ct->subtarget);
if (target) {
node2= dag_get_node(dag, target);
- dag_add_relation(dag, node2, node, 0, "IK Constraint");
+ dag_add_relation(dag, node2, node, 0, "Pose Constraint");
if (con->type==CONSTRAINT_TYPE_KINEMATIC) {
bKinematicConstraint *data = (bKinematicConstraint *)con->data;
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 5d6338af409..5fc5fd8fb7d 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -2288,6 +2288,11 @@ static void direct_link_constraints(FileData *fd, ListBase *lb)
if (data->prop)
IDP_DirectLinkProperty(data->prop, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
}
+ else if (cons->type == CONSTRAINT_TYPE_SPLINEIK) {
+ bSplineIKConstraint *data= cons->data;
+
+ data->points= newdataadr(fd, data->points);
+ }
}
}
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index ee84ae59974..8ec12496e59 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -1077,7 +1077,15 @@ static void write_constraints(WriteData *wd, ListBase *conlist)
of library blocks that implement this.*/
IDP_WriteProperty(data->prop, wd);
}
- break;
+ break;
+ case CONSTRAINT_TYPE_SPLINEIK:
+ {
+ bSplineIKConstraint *data= (bSplineIKConstraint*)con->data;
+
+ /* write points array */
+ writedata(wd, DATA, sizeof(float)*(data->numpoints), data->points);
+ }
+ break;
}
}
diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c
index 8c0da354938..15ec8d6116d 100644
--- a/source/blender/editors/object/object_constraint.c
+++ b/source/blender/editors/object/object_constraint.c
@@ -327,7 +327,7 @@ static void test_constraints (Object *owner, const char substring[])
/* clear disabled-flag first */
curcon->flag &= ~CONSTRAINT_DISABLE;
-
+
if (curcon->type == CONSTRAINT_TYPE_KINEMATIC) {
bKinematicConstraint *data = curcon->data;
@@ -395,6 +395,26 @@ static void test_constraints (Object *owner, const char substring[])
if (data->lockflag+3==data->trackflag)
curcon->flag |= CONSTRAINT_DISABLE;
}
+ else if (curcon->type == CONSTRAINT_TYPE_SPLINEIK) {
+ bSplineIKConstraint *data = curcon->data;
+
+ /* if the number of points does not match the amount required by the chain length,
+ * free the points array and request a rebind...
+ */
+ if ( (data->points == NULL) ||
+ (!(data->flag & CONSTRAINT_SPLINEIK_NO_ROOT) && (data->numpoints != data->chainlen+1)) ||
+ ( (data->flag & CONSTRAINT_SPLINEIK_NO_ROOT) && (data->numpoints != data->chainlen)) )
+ {
+ /* free the points array */
+ if (data->points) {
+ MEM_freeN(data->points);
+ data->points = NULL;
+ }
+
+ /* clear the bound flag, forcing a rebind next time this is evaluated */
+ data->flag &= ~CONSTRAINT_SPLINEIK_BOUND;
+ }
+ }
/* Check targets for constraints */
if (cti && cti->get_constraint_targets) {
@@ -414,7 +434,7 @@ static void test_constraints (Object *owner, const char substring[])
}
/* target checks for specific constraints */
- if (ELEM(curcon->type, CONSTRAINT_TYPE_FOLLOWPATH, CONSTRAINT_TYPE_CLAMPTO)) {
+ if (ELEM3(curcon->type, CONSTRAINT_TYPE_FOLLOWPATH, CONSTRAINT_TYPE_CLAMPTO, CONSTRAINT_TYPE_SPLINEIK)) {
if (ct->tar) {
if (ct->tar->type != OB_CURVE) {
ct->tar= NULL;
@@ -855,7 +875,7 @@ static int pose_constraints_clear_exec(bContext *C, wmOperator *op)
CTX_DATA_BEGIN(C, bPoseChannel*, pchan, selected_pchans)
{
free_constraints(&pchan->constraints);
- pchan->constflag &= ~(PCHAN_HAS_IK|PCHAN_HAS_CONST);
+ pchan->constflag &= ~(PCHAN_HAS_IK|PCHAN_HAS_SPLINEIK|PCHAN_HAS_CONST);
}
CTX_DATA_END;
@@ -947,6 +967,7 @@ static short get_new_constraint_target(bContext *C, int con_type, Object **tar_o
/* curve-based constraints - set the only_curve and only_ob flags */
case CONSTRAINT_TYPE_CLAMPTO:
case CONSTRAINT_TYPE_FOLLOWPATH:
+ case CONSTRAINT_TYPE_SPLINEIK:
only_curve= 1;
only_ob= 1;
add= 0;
@@ -1070,6 +1091,10 @@ static int constraint_add_exec(bContext *C, wmOperator *op, Object *ob, ListBase
BKE_report(op->reports, RPT_ERROR, "IK Constraint can only be added to Bones.");
return OPERATOR_CANCELLED;
}
+ if ( (type == CONSTRAINT_TYPE_SPLINEIK) && ((!pchan) || (list != &pchan->constraints)) ) {
+ BKE_report(op->reports, RPT_ERROR, "Spline IK Constraint can only be added to Bones.");
+ return OPERATOR_CANCELLED;
+ }
/* create a new constraint of the type requried, and add it to the active/given constraints list */
con = add_new_constraint(type);
@@ -1119,6 +1144,7 @@ static int constraint_add_exec(bContext *C, wmOperator *op, Object *ob, ListBase
}
/* do type-specific tweaking to the constraint settings */
+ // TODO: does action constraint need anything here - i.e. spaceonce?
switch (type) {
case CONSTRAINT_TYPE_CHILDOF:
{
diff --git a/source/blender/editors/space_view3d/drawarmature.c b/source/blender/editors/space_view3d/drawarmature.c
index 26135cd8d31..656d7061d48 100644
--- a/source/blender/editors/space_view3d/drawarmature.c
+++ b/source/blender/editors/space_view3d/drawarmature.c
@@ -209,6 +209,7 @@ static short set_pchan_glColor (short colCode, int armflag, int boneflag, int co
if (constflag & PCHAN_HAS_STRIDE) glColor4ub(0, 0, 200, 80);
else if (constflag & PCHAN_HAS_TARGET) glColor4ub(255, 150, 0, 80);
else if (constflag & PCHAN_HAS_IK) glColor4ub(255, 255, 0, 80);
+ else if (constflag & PCHAN_HAS_SPLINEIK) glColor4ub(200, 255, 0, 80);
else if (constflag & PCHAN_HAS_CONST) glColor4ub(0, 255, 120, 80);
else if (constflag) UI_ThemeColor4(TH_BONE_POSE); // PCHAN_HAS_ACTION
@@ -280,6 +281,7 @@ static short set_pchan_glColor (short colCode, int armflag, int boneflag, int co
if (constflag & PCHAN_HAS_STRIDE) glColor3ub(0, 0, 200);
else if (constflag & PCHAN_HAS_TARGET) glColor3ub(255, 150, 0);
else if (constflag & PCHAN_HAS_IK) glColor3ub(255, 255, 0);
+ else if (constflag & PCHAN_HAS_SPLINEIK) glColor3ub(200, 255, 0);
else if (constflag & PCHAN_HAS_CONST) glColor3ub(0, 255, 120);
else if (constflag) UI_ThemeColor(TH_BONE_POSE); /* PCHAN_HAS_ACTION */
}
@@ -1296,36 +1298,68 @@ static void pchan_draw_IK_root_lines(bPoseChannel *pchan, short only_temp)
bPoseChannel *parchan;
for (con= pchan->constraints.first; con; con= con->next) {
- if (con->type == CONSTRAINT_TYPE_KINEMATIC && (con->enforce!=0.0)) {
- bKinematicConstraint *data = (bKinematicConstraint*)con->data;
- int segcount= 0;
-
- /* if only_temp, only draw if it is a temporary ik-chain */
- if ((only_temp) && !(data->flag & CONSTRAINT_IK_TEMP))
- continue;
-
- setlinestyle(3);
- glBegin(GL_LINES);
-
- /* exclude tip from chain? */
- if ((data->flag & CONSTRAINT_IK_TIP)==0)
- parchan= pchan->parent;
- else
+ if (con->enforce == 0.0f)
+ continue;
+
+ switch (con->type) {
+ case CONSTRAINT_TYPE_KINEMATIC:
+ {
+ bKinematicConstraint *data = (bKinematicConstraint*)con->data;
+ int segcount= 0;
+
+ /* if only_temp, only draw if it is a temporary ik-chain */
+ if ((only_temp) && !(data->flag & CONSTRAINT_IK_TEMP))
+ continue;
+
+ setlinestyle(3);
+ glBegin(GL_LINES);
+
+ /* exclude tip from chain? */
+ if ((data->flag & CONSTRAINT_IK_TIP)==0)
+ parchan= pchan->parent;
+ else
+ parchan= pchan;
+
+ glVertex3fv(parchan->pose_tail);
+
+ /* Find the chain's root */
+ while (parchan->parent) {
+ segcount++;
+ if(segcount==data->rootbone || segcount>255) break; // 255 is weak
+ parchan= parchan->parent;
+ }
+ if (parchan)
+ glVertex3fv(parchan->pose_head);
+
+ glEnd();
+ setlinestyle(0);
+ }
+ break;
+ case CONSTRAINT_TYPE_SPLINEIK:
+ {
+ bSplineIKConstraint *data = (bSplineIKConstraint*)con->data;
+ int segcount= 0;
+
+ setlinestyle(3);
+ glBegin(GL_LINES);
+
parchan= pchan;
-
- glVertex3fv(parchan->pose_tail);
-
- /* Find the chain's root */
- while (parchan->parent) {
- segcount++;
- if(segcount==data->rootbone || segcount>255) break; // 255 is weak
- parchan= parchan->parent;
+ glVertex3fv(parchan->pose_tail);
+
+ /* Find the chain's root */
+ while (parchan->parent) {
+ segcount++;
+ // FIXME: revise the breaking conditions
+ if(segcount==data->chainlen || segcount>255) break; // 255 is weak
+ parchan= parchan->parent;
+ }
+ if (parchan) // XXX revise the breaking conditions to only stop at the tail?
+ glVertex3fv(parchan->pose_head);
+
+ glEnd();
+ setlinestyle(0);
}
- if (parchan)
- glVertex3fv(parchan->pose_head);
-
- glEnd();
- setlinestyle(0);
+ break;
}
}
}
@@ -1743,6 +1777,14 @@ static void draw_pose_channels(Scene *scene, View3D *v3d, ARegion *ar, Base *bas
pchan_draw_IK_root_lines(pchan, !(do_dashed & 2));
}
}
+ else if (pchan->constflag & PCHAN_HAS_SPLINEIK) {
+ if (bone->flag & BONE_SELECTED) {
+ glColor3ub(150, 200, 50); // add theme!
+
+ glLoadName(index & 0xFFFF);
+ pchan_draw_IK_root_lines(pchan, !(do_dashed & 2));
+ }
+ }
}
}
diff --git a/source/blender/ikplugin/intern/iksolver_plugin.c b/source/blender/ikplugin/intern/iksolver_plugin.c
index b160e7346bd..6eb1ef56094 100644
--- a/source/blender/ikplugin/intern/iksolver_plugin.c
+++ b/source/blender/ikplugin/intern/iksolver_plugin.c
@@ -109,7 +109,9 @@ static void initialize_posetree(struct Object *ob, bPoseChannel *pchan_tip)
if(tree==NULL) {
/* make new tree */
tree= MEM_callocN(sizeof(PoseTree), "posetree");
-
+
+ tree->type= CONSTRAINT_TYPE_KINEMATIC;
+
tree->iterations= data->iterations;
tree->totchannel= segcount;
tree->stretch = (data->flag & CONSTRAINT_IK_STRETCH);
@@ -503,6 +505,10 @@ void iksolver_execute_tree(struct Scene *scene, struct Object *ob, struct bPose
PoseTree *tree= pchan->iktree.first;
int a;
+ /* stop on the first tree that isn't a standard IK chain */
+ if (tree->type != CONSTRAINT_TYPE_KINEMATIC)
+ return;
+
/* 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
diff --git a/source/blender/makesdna/DNA_action_types.h b/source/blender/makesdna/DNA_action_types.h
index 8d590692794..7067c967da3 100644
--- a/source/blender/makesdna/DNA_action_types.h
+++ b/source/blender/makesdna/DNA_action_types.h
@@ -159,20 +159,30 @@ typedef struct bPoseChannel {
/* PoseChannel (transform) flags */
typedef enum ePchan_Flag {
- POSE_LOC = 0x0001,
- POSE_ROT = 0x0002,
- POSE_SIZE = 0x0004,
- POSE_IK_MAT = 0x0008,
- POSE_UNUSED2 = 0x0010,
- POSE_UNUSED3 = 0x0020,
- POSE_UNUSED4 = 0x0040,
- POSE_UNUSED5 = 0x0080,
- POSE_HAS_IK = 0x0100,
- POSE_CHAIN = 0x0200,
- POSE_DONE = 0x0400,
- POSE_KEY = 0x1000,
- POSE_STRIDE = 0x2000,
- POSE_IKTREE = 0x4000,
+ /* has transforms */
+ POSE_LOC = (1<<0),
+ POSE_ROT = (1<<1),
+ POSE_SIZE = (1<<2),
+ /* old IK/cache stuff... */
+ POSE_IK_MAT = (1<<3),
+ POSE_UNUSED2 = (1<<4),
+ POSE_UNUSED3 = (1<<5),
+ POSE_UNUSED4 = (1<<6),
+ POSE_UNUSED5 = (1<<7),
+ /* has Standard IK */
+ POSE_HAS_IK = (1<<8),
+ /* IK/Pose solving*/
+ POSE_CHAIN = (1<<9),
+ POSE_DONE = (1<<10),
+ /* visualisation */
+ POSE_KEY = (1<<11),
+ POSE_STRIDE = (1<<12),
+ /* standard IK solving */
+ POSE_IKTREE = (1<<13),
+ /* has Spline IK */
+ POSE_HAS_IKS = (1<<14),
+ /* spline IK solving */
+ POSE_IKSPLINE = (1<<15),
} ePchan_Flag;
/* PoseChannel constflag (constraint detection) */
@@ -183,7 +193,9 @@ typedef enum ePchan_ConstFlag {
PCHAN_HAS_ACTION = (1<<2),
PCHAN_HAS_TARGET = (1<<3),
/* only for drawing Posemode too */
- PCHAN_HAS_STRIDE = (1<<4)
+ PCHAN_HAS_STRIDE = (1<<4),
+ /* spline IK */
+ PCHAN_HAS_SPLINEIK = (1<<5),
} ePchan_ConstFlag;
/* PoseChannel->ikflag */
@@ -202,7 +214,6 @@ typedef enum ePchan_IkFlag {
BONE_IK_NO_XDOF_TEMP = (1<<10),
BONE_IK_NO_YDOF_TEMP = (1<<11),
BONE_IK_NO_ZDOF_TEMP = (1<<12),
-
} ePchan_IkFlag;
/* PoseChannel->rotmode and Object->rotmode */
diff --git a/source/blender/makesdna/DNA_armature_types.h b/source/blender/makesdna/DNA_armature_types.h
index 0f80fb4821f..d3e611178fe 100644
--- a/source/blender/makesdna/DNA_armature_types.h
+++ b/source/blender/makesdna/DNA_armature_types.h
@@ -73,6 +73,7 @@ typedef struct Bone {
typedef struct bArmature {
ID id;
struct AnimData *adt;
+
ListBase bonebase;
ListBase chainbase;
ListBase *edbo; /* editbone listbase, we use pointer so we can check state */
diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h
index 7b0b1c3d814..bf8139ce6a0 100644
--- a/source/blender/makesdna/DNA_constraint_types.h
+++ b/source/blender/makesdna/DNA_constraint_types.h
@@ -32,7 +32,6 @@
#define DNA_CONSTRAINT_TYPES_H
#include "DNA_ID.h"
-#include "DNA_ipo_types.h"
#include "DNA_listBase.h"
#include "DNA_object_types.h"
@@ -41,9 +40,10 @@ struct Text;
struct Ipo;
/* channels reside in Object or Action (ListBase) constraintChannels */
+// XXX depreceated... old AnimSys
typedef struct bConstraintChannel {
struct bConstraintChannel *next, *prev;
- Ipo *ipo;
+ struct Ipo *ipo;
short flag;
char name[30];
} bConstraintChannel;
@@ -66,6 +66,7 @@ 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 */
@@ -122,7 +123,7 @@ 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
@@ -151,6 +152,27 @@ typedef enum B_CONSTRAINT_IK_TYPE {
CONSTRAINT_IK_DISTANCE /* maintain distance with target */
} B_CONSTRAINT_IK_TYPE;
+
+/* Spline IK Constraint
+ * Aligns 'n' bones to the curvature defined by the curve,
+ * with the chain ending on the bone that owns this constraint,
+ * and starting on the nth parent.
+ */
+typedef struct bSplineIKConstraint {
+ /* target(s) */
+ Object *tar; /* curve object (with follow path enabled) which drives the bone chain */
+
+ /* binding details */
+ float *points; /* array of numpoints items, denoting parametric positions along curve that joints should follow */
+ short numpoints; /* number of points to bound in points array */
+ short chainlen; /* number of bones ('n') that are in the chain */
+
+ /* settings */
+ short flag; /* general settings for constraint */
+ short upflag; /* axis of bone that points up */
+} bSplineIKConstraint;
+
+
/* Single-target subobject constraints --------------------- */
/* Track To Constraint */
typedef struct bTrackToConstraint {
@@ -378,9 +400,10 @@ typedef enum B_CONSTAINT_TYPES {
CONSTRAINT_TYPE_TRANSFORM, /* transformation (loc/rot/size -> loc/rot/size) constraint */
CONSTRAINT_TYPE_SHRINKWRAP, /* shrinkwrap (loc/rot) constraint */
CONSTRAINT_TYPE_DAMPTRACK, /* New Tracking constraint that minimises twisting */
+ CONSTRAINT_TYPE_SPLINEIK, /* Spline-IK - Align 'n' bones to a curve */
- /* NOTE: everytime a new constraint is added, update this */
- NUM_CONSTRAINT_TYPES= CONSTRAINT_TYPE_DAMPTRACK
+ /* NOTE: no constraints are allowed to be added after this */
+ NUM_CONSTRAINT_TYPES
} B_CONSTRAINT_TYPES;
/* bConstraint->flag */
@@ -521,6 +544,13 @@ typedef enum B_CONSTRAINTCHANNEL_FLAG {
/* axis relative to target */
#define CONSTRAINT_IK_TARGETAXIS 16384
+/* bSplineIKConstraint->flag */
+ /* chain has been attached to spline */
+#define CONSTRAINT_SPLINEIK_BOUND (1<<0)
+ /* roll on chains is not determined by the constraint */
+#define CONSTRAINT_SPLINEIK_NO_TWIST (1<<1)
+ /* root of chain is not influence by the constraint */
+#define CONSTRAINT_SPLINEIK_NO_ROOT (1<<2)
/* MinMax (floor) flags */
#define MINMAX_STICKY 0x01
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index 1d4aa100fce..44b36e65aa3 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -413,6 +413,7 @@ extern StructRNA RNA_SoftBodyModifier;
extern StructRNA RNA_SoftBodySettings;
extern StructRNA RNA_Sound;
extern StructRNA RNA_SoundSequence;
+extern StructRNA RNA_SplineIKConstraint;
extern StructRNA RNA_Space;
extern StructRNA RNA_Space3DView;
extern StructRNA RNA_SpaceConsole;
diff --git a/source/blender/makesrna/intern/rna_constraint.c b/source/blender/makesrna/intern/rna_constraint.c
index 7a9a07939b1..63b1538354f 100644
--- a/source/blender/makesrna/intern/rna_constraint.c
+++ b/source/blender/makesrna/intern/rna_constraint.c
@@ -55,6 +55,7 @@ EnumPropertyItem constraint_type_items[] ={
{CONSTRAINT_TYPE_CLAMPTO, "CLAMP_TO", ICON_CONSTRAINT_DATA, "Clamp To", ""},
{CONSTRAINT_TYPE_STRETCHTO, "STRETCH_TO",ICON_CONSTRAINT_DATA, "Stretch To", ""},
{CONSTRAINT_TYPE_KINEMATIC, "IK", ICON_CONSTRAINT_DATA, "Inverse Kinematics", ""},
+ {CONSTRAINT_TYPE_SPLINEIK, "SPLINE_IK", ICON_CONSTRAINT_DATA, "Spline IK", ""},
{0, "", 0, "Relationship", ""},
{CONSTRAINT_TYPE_CHILDOF, "CHILD_OF", ICON_CONSTRAINT_DATA, "Child Of", ""},
{CONSTRAINT_TYPE_MINMAX, "FLOOR", ICON_CONSTRAINT_DATA, "Floor", ""},
@@ -147,6 +148,8 @@ static StructRNA *rna_ConstraintType_refine(struct PointerRNA *ptr)
return &RNA_ShrinkwrapConstraint;
case CONSTRAINT_TYPE_DAMPTRACK:
return &RNA_DampedTrackConstraint;
+ case CONSTRAINT_TYPE_SPLINEIK:
+ return &RNA_SplineIKConstraint;
default:
return &RNA_UnknownType;
}
@@ -1169,7 +1172,7 @@ static void rna_def_constraint_clamp_to(BlenderRNA *brna)
RNA_def_struct_sdna_from(srna, "bClampToConstraint", "data");
prop= RNA_def_property(srna, "target", PROP_POINTER, PROP_NONE);
- RNA_def_property_pointer_sdna(prop, NULL, "tar");
+ RNA_def_property_pointer_sdna(prop, NULL, "tar"); // TODO: curve only!
RNA_def_property_ui_text(prop, "Target", "Target Object");
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_dependency_update");
@@ -1672,6 +1675,30 @@ static void rna_def_constraint_damped_track(BlenderRNA *brna)
RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_update");
}
+static void rna_def_constraint_spline_ik(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna= RNA_def_struct(brna, "SplineIKConstraint", "Constraint");
+ RNA_def_struct_ui_text(srna, "Spline IK Constraint", "Align 'n' bones along a curve.");
+ RNA_def_struct_sdna_from(srna, "bSplineIKConstraint", "data");
+
+ prop= RNA_def_property(srna, "target", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "tar"); // TODO: curve only
+ RNA_def_property_ui_text(prop, "Target", "Target Object");
+ RNA_def_property_flag(prop, PROP_EDITABLE);
+ RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_dependency_update");
+
+ prop= RNA_def_property(srna, "chain_length", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "chainlen");
+ RNA_def_property_range(prop, 1, 255); // TODO: this should really check the max length of the chain the constraint is attached to
+ RNA_def_property_ui_text(prop, "Chain Length", "How many bones are included in the chain");
+ RNA_def_property_update(prop, NC_OBJECT|ND_CONSTRAINT, "rna_Constraint_dependency_update");
+
+ // TODO: add access to the positions array to allow more flexible aligning?
+}
+
/* base struct for constraints */
void RNA_def_constraint(BlenderRNA *brna)
{
@@ -1773,6 +1800,7 @@ void RNA_def_constraint(BlenderRNA *brna)
rna_def_constraint_transform(brna);
rna_def_constraint_shrinkwrap(brna);
rna_def_constraint_damped_track(brna);
+ rna_def_constraint_spline_ik(brna);
}
#endif