[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [24210] trunk/blender: Rigging Goodies: Spline IK Constraint

Joshua Leung aligorith at gmail.com
Sun Nov 1 12:29:40 CET 2009


Revision: 24210
          http://projects.blender.org/plugins/scmsvn/viewcvs.php?view=rev&root=bf-blender&revision=24210
Author:   aligorith
Date:     2009-11-01 12:29:40 +0100 (Sun, 01 Nov 2009)

Log Message:
-----------
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!

Modified Paths:
--------------
    trunk/blender/release/scripts/ui/properties_object_constraint.py
    trunk/blender/source/blender/blenkernel/BKE_armature.h
    trunk/blender/source/blender/blenkernel/intern/action.c
    trunk/blender/source/blender/blenkernel/intern/armature.c
    trunk/blender/source/blender/blenkernel/intern/constraint.c
    trunk/blender/source/blender/blenkernel/intern/depsgraph.c
    trunk/blender/source/blender/blenloader/intern/readfile.c
    trunk/blender/source/blender/blenloader/intern/writefile.c
    trunk/blender/source/blender/editors/object/object_constraint.c
    trunk/blender/source/blender/editors/space_view3d/drawarmature.c
    trunk/blender/source/blender/ikplugin/intern/iksolver_plugin.c
    trunk/blender/source/blender/makesdna/DNA_action_types.h
    trunk/blender/source/blender/makesdna/DNA_armature_types.h
    trunk/blender/source/blender/makesdna/DNA_constraint_types.h
    trunk/blender/source/blender/makesrna/RNA_access.h
    trunk/blender/source/blender/makesrna/intern/rna_constraint.c

Modified: trunk/blender/release/scripts/ui/properties_object_constraint.py
===================================================================
--- trunk/blender/release/scripts/ui/properties_object_constraint.py	2009-11-01 10:45:42 UTC (rev 24209)
+++ trunk/blender/release/scripts/ui/properties_object_constraint.py	2009-11-01 11:29:40 UTC (rev 24210)
@@ -581,6 +581,13 @@
         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):

Modified: trunk/blender/source/blender/blenkernel/BKE_armature.h
===================================================================
--- trunk/blender/source/blender/blenkernel/BKE_armature.h	2009-11-01 10:45:42 UTC (rev 24209)
+++ trunk/blender/source/blender/blenkernel/BKE_armature.h	2009-11-01 11:29:40 UTC (rev 24210)
@@ -55,11 +55,14 @@
 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 */

Modified: trunk/blender/source/blender/blenkernel/intern/action.c
===================================================================
--- trunk/blender/source/blender/blenkernel/intern/action.c	2009-11-01 10:45:42 UTC (rev 24209)
+++ trunk/blender/source/blender/blenkernel/intern/action.c	2009-11-01 11:29:40 UTC (rev 24210)
@@ -622,7 +622,7 @@
 	}
 }
 
-/* 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 @@
 				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;
 		}

Modified: trunk/blender/source/blender/blenkernel/intern/armature.c
===================================================================
--- trunk/blender/source/blender/blenkernel/intern/armature.c	2009-11-01 10:45:42 UTC (rev 24209)
+++ trunk/blender/source/blender/blenkernel/intern/armature.c	2009-11-01 11:29:40 UTC (rev 24210)
@@ -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 @@
 }
 
 
+/* ********************** 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]);
+	

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list