forked from bartvdbraak/blender
Rigging Goodies: Spline IK Constraint
At last, this commit introduces the Spline IK Constraint to Blender. Spline IK is a constraint that makes n bones follow the shape of a specified curve. Simply add a chain of bones, add a curve, add a Spline IK Constraint to the tip bone and set the number of bones in the chain to make it work. Or, try the following test file: http://download.blender.org/ftp/incoming/250_splineik_spine01.blend Screenshots of this in action (as proof): http://download.blender.org/ftp/incoming/b250_splineik_001_before.png http://download.blender.org/ftp/incoming/b250_splineik_001_after.png I've implemented this in a similar way to how standard IK solvers are done. However, this code is currently not an IK plugin, since I imagine that it would be useful to be able to combine the 2 types of IK. This can be easily changed though :) Finally, a few notes on what to expect still: * Constraint blending currently doesn't affect this. Getting that to work correctly will take a bit more work still. * Options for not affecting the root joint (to make it easier to attach the chain to a stump or whatever), and non-uniform scaling options have yet to be added. I've marked the places where they can be added though * Control over the twisting of the chain still needs investigation. Have fun!
This commit is contained in:
parent
cb45db0336
commit
2068eaf1b7
@ -582,6 +582,13 @@ class ConstraintButtonsPanel(bpy.types.Panel):
|
||||
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):
|
||||
bl_label = "Constraints"
|
||||
|
@ -56,10 +56,13 @@ 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 */
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
@ -1913,16 +2168,26 @@ void where_is_pose (Scene *scene, Object *ob)
|
||||
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);
|
||||
|
@ -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];
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
{
|
||||
|
@ -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 (con->enforce == 0.0f)
|
||||
continue;
|
||||
|
||||
/* if only_temp, only draw if it is a temporary ik-chain */
|
||||
if ((only_temp) && !(data->flag & CONSTRAINT_IK_TEMP))
|
||||
continue;
|
||||
switch (con->type) {
|
||||
case CONSTRAINT_TYPE_KINEMATIC:
|
||||
{
|
||||
bKinematicConstraint *data = (bKinematicConstraint*)con->data;
|
||||
int segcount= 0;
|
||||
|
||||
setlinestyle(3);
|
||||
glBegin(GL_LINES);
|
||||
/* if only_temp, only draw if it is a temporary ik-chain */
|
||||
if ((only_temp) && !(data->flag & CONSTRAINT_IK_TEMP))
|
||||
continue;
|
||||
|
||||
/* exclude tip from chain? */
|
||||
if ((data->flag & CONSTRAINT_IK_TIP)==0)
|
||||
parchan= pchan->parent;
|
||||
else
|
||||
parchan= pchan;
|
||||
setlinestyle(3);
|
||||
glBegin(GL_LINES);
|
||||
|
||||
glVertex3fv(parchan->pose_tail);
|
||||
/* exclude tip from chain? */
|
||||
if ((data->flag & CONSTRAINT_IK_TIP)==0)
|
||||
parchan= pchan->parent;
|
||||
else
|
||||
parchan= pchan;
|
||||
|
||||
/* 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++;
|
||||
if(segcount==data->rootbone || segcount>255) break; // 255 is weak
|
||||
parchan= parchan->parent;
|
||||
}
|
||||
if (parchan)
|
||||
glVertex3fv(parchan->pose_head);
|
||||
|
||||
glEnd();
|
||||
setlinestyle(0);
|
||||
}
|
||||
if (parchan)
|
||||
glVertex3fv(parchan->pose_head);
|
||||
break;
|
||||
case CONSTRAINT_TYPE_SPLINEIK:
|
||||
{
|
||||
bSplineIKConstraint *data = (bSplineIKConstraint*)con->data;
|
||||
int segcount= 0;
|
||||
|
||||
glEnd();
|
||||
setlinestyle(0);
|
||||
setlinestyle(3);
|
||||
glBegin(GL_LINES);
|
||||
|
||||
parchan= pchan;
|
||||
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);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1739,6 +1773,14 @@ static void draw_pose_channels(Scene *scene, View3D *v3d, ARegion *ar, Base *bas
|
||||
if (pchan->constflag & PCHAN_HAS_TARGET) glColor3ub(200, 120, 0);
|
||||
else glColor3ub(200, 200, 50); // add theme!
|
||||
|
||||
glLoadName(index & 0xFFFF);
|
||||
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));
|
||||
}
|
||||
|
@ -110,6 +110,8 @@ static void initialize_posetree(struct Object *ob, bPoseChannel *pchan_tip)
|
||||
/* 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
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user