[Bf-blender-cvs] [6a59e123649] master: Fix T59622: dependency problems with Spline IK.

Alexander Gavrilov noreply at git.blender.org
Sun Apr 14 19:01:06 CEST 2019


Commit: 6a59e123649105a07316c87ac4dcc78a87fed532
Author: Alexander Gavrilov
Date:   Sun Apr 14 13:44:12 2019 +0300
Branches: master
https://developer.blender.org/rB6a59e123649105a07316c87ac4dcc78a87fed532

Fix T59622: dependency problems with Spline IK.

The bug is caused by problems in the dependency graph. Unfortunately,
fixing is not just a matter of fixing the graph, because correct
dependencies would cause a cycle here and in other reasonable use
cases. The real fix thus requires refactoring Spline IK to require
curve data only in the actual evaluation phase, and not in POSE_INIT_IK.

In addition, this separates the normal bone evaluation loop from
Spline IK computations for two reasons:

- That still needs to be done even if spline IK can't evaluate
  due to missing curve data.

- It should reduce issues with induced shearing, as Spline IK now
  controls how parent-child relations are handled in the chain, and
  can take care to only carry over rotation and location.

===================================================================

M	source/blender/blenkernel/intern/armature_update.c
M	source/blender/depsgraph/intern/builder/deg_builder_relations_rig.cc

===================================================================

diff --git a/source/blender/blenkernel/intern/armature_update.c b/source/blender/blenkernel/intern/armature_update.c
index 1e9febd4d8e..790f5cb2b31 100644
--- a/source/blender/blenkernel/intern/armature_update.c
+++ b/source/blender/blenkernel/intern/armature_update.c
@@ -56,10 +56,10 @@ typedef struct tSplineIK_Tree {
 
 	int type;                    /* type of IK that this serves (CONSTRAINT_TYPE_KINEMATIC or ..._SPLINEIK) */
 
-	bool free_points;            /* free the point positions array */
 	short chainlen;              /* number of bones in the chain */
+	float totlength;             /* total length of bones in the chain */
 
-	float *points;               /* parametric positions for the joints along the curve */
+	const float *points;         /* parametric positions for the joints along the curve */
 	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 */
@@ -77,9 +77,8 @@ static void splineik_init_tree_from_pchan(Scene *UNUSED(scene), Object *UNUSED(o
 	bPoseChannel *pchanChain[255];
 	bConstraint *con = NULL;
 	bSplineIKConstraint *ikData = NULL;
-	float boneLengths[255], *jointPoints;
+	float boneLengths[255];
 	float totLength = 0.0f;
-	bool free_joints = 0;
 	int segcount = 0;
 
 	/* find the SplineIK constraint */
@@ -101,17 +100,6 @@ static void splineik_init_tree_from_pchan(Scene *UNUSED(scene), Object *UNUSED(o
 	if (con == NULL)
 		return;
 
-	/* make sure that the constraint targets are ok
-	 *     - this is a workaround for a depsgraph bug or dependency cycle...
-	 */
-	if (ikData->tar) {
-		CurveCache *cache = ikData->tar->runtime.curve_cache;
-
-		if (ELEM(NULL, cache, cache->path, cache->path->data)) {
-			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 && (segcount < ikData->chainlen); pchan = pchan->parent, segcount++) {
@@ -168,41 +156,6 @@ static void splineik_init_tree_from_pchan(Scene *UNUSED(scene), Object *UNUSED(o
 	/* disallow negative values (happens with float precision) */
 	CLAMP_MIN(ikData->points[segcount], 0.0f);
 
-	/* apply corrections for sensitivity to scaling on a copy of the bind points,
-	 * since it's easier to determine the positions of all the joints beforehand this way
-	 */
-	if ((ikData->flag & CONSTRAINT_SPLINEIK_SCALE_LIMITED) && (totLength != 0.0f)) {
-		float splineLen, maxScale;
-		int i;
-
-		/* make a copy of the points array, that we'll store in the tree
-		 *     - although we could just multiply the points on the fly, this approach means that
-		 *       we can introduce per-segment stretchiness later if it is necessary
-		 */
-		jointPoints = MEM_dupallocN(ikData->points);
-		free_joints = 1;
-
-		/* get the current length of the curve */
-		/* NOTE: this is assumed to be correct even after the curve was resized */
-		splineLen = ikData->tar->runtime.curve_cache->path->totdist;
-
-		/* calculate the scale factor to multiply all the path values by so that the
-		 * bone chain retains its current length, such that
-		 *     maxScale * splineLen = totLength
-		 */
-		maxScale = totLength / splineLen;
-
-		/* apply scaling correction to all of the temporary points */
-		/* TODO: this is really not adequate enough on really short chains */
-		for (i = 0; i < segcount; i++)
-			jointPoints[i] *= maxScale;
-	}
-	else {
-		/* just use the existing points array */
-		jointPoints = ikData->points;
-		free_joints = 0;
-	}
-
 	/* 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 precedence... */
 	{
@@ -211,14 +164,14 @@ static void splineik_init_tree_from_pchan(Scene *UNUSED(scene), Object *UNUSED(o
 		tree->type = CONSTRAINT_TYPE_SPLINEIK;
 
 		tree->chainlen = segcount;
+		tree->totlength = totLength;
 
 		/* copy over the array of links to bones in the chain (from tip to root) */
 		tree->chain = MEM_mallocN(sizeof(bPoseChannel *) * segcount, "SplineIK Chain");
 		memcpy(tree->chain, pchanChain, sizeof(bPoseChannel *) * segcount);
 
 		/* store reference to joint position array */
-		tree->points = jointPoints;
-		tree->free_points = free_joints;
+		tree->points = ikData->points;
 
 		/* store references to different parts of the chain */
 		tree->root = pchanRoot;
@@ -247,20 +200,69 @@ static void splineik_init_tree(Scene *scene, Object *ob, float UNUSED(ctime))
 
 /* ----------- */
 
-/* Evaluate spline IK for a given bone */
-static void splineik_evaluate_bone(
-        struct Depsgraph *depsgraph, tSplineIK_Tree *tree, Scene *scene, Object *ob, bPoseChannel *pchan,
-        int index, float ctime)
+typedef struct tSplineIk_EvalState {
+	float curve_position;        /* Current position along the curve. */
+	float curve_scale;           /* Global scale to apply to curve positions. */
+	float locrot_offset[4][4];   /* Bone rotation and location offset inherited from parent. */
+} tSplineIk_EvalState;
+
+/* Prepare data to evaluate spline IK. */
+static bool splineik_evaluate_init(tSplineIK_Tree *tree, tSplineIk_EvalState *state)
+{
+	bSplineIKConstraint *ikData = tree->ikData;
+
+	/* Make sure that the constraint targets are ok, to avoid crashes
+	 * in case of a depsgraph bug or dependency cycle.
+	 */
+	if (ikData->tar == NULL) {
+		return false;
+	}
+
+	CurveCache *cache = ikData->tar->runtime.curve_cache;
+
+	if (ELEM(NULL, cache, cache->path, cache->path->data)) {
+		return false;
+	}
+
+	/* Initialize the evaluation state. */
+	state->curve_position = 0.0f;
+	state->curve_scale = 1.0f;
+	unit_m4(state->locrot_offset);
+
+	/* Apply corrections for sensitivity to scaling. */
+	if ((ikData->flag & CONSTRAINT_SPLINEIK_SCALE_LIMITED) && (tree->totlength != 0.0f)) {
+		/* get the current length of the curve */
+		/* NOTE: this is assumed to be correct even after the curve was resized */
+		float splineLen = cache->path->totdist;
+
+		/* calculate the scale factor to multiply all the path values by so that the
+		 * bone chain retains its current length, such that
+		 *     maxScale * splineLen = totLength
+		 */
+		state->curve_scale = tree->totlength / splineLen;
+	}
+
+	return true;
+}
+
+/* Evaluate spline IK for a given bone. */
+static void splineik_evaluate_bone(tSplineIK_Tree *tree, Object *ob, bPoseChannel *pchan, int index, tSplineIk_EvalState *state)
 {
 	bSplineIKConstraint *ikData = tree->ikData;
-	float poseHead[3], poseTail[3], poseMat[4][4];
+	float origHead[3], origTail[3], poseHead[3], poseTail[3], poseMat[4][4];
 	float splineVec[3], scaleFac, radius = 1.0f;
 
-	/* firstly, calculate the bone matrix the standard way, since this is needed for roll control */
-	BKE_pose_where_is_bone(depsgraph, scene, ob, pchan, ctime, 1);
+	mul_v3_m4v3(poseHead, state->locrot_offset, pchan->pose_head);
+	mul_v3_m4v3(poseTail, state->locrot_offset, pchan->pose_tail);
+
+	copy_v3_v3(origHead, poseHead);
 
-	copy_v3_v3(poseHead, pchan->pose_head);
-	copy_v3_v3(poseTail, pchan->pose_tail);
+	/* first, adjust the point positions on the curve */
+	float curveLen = tree->points[index] - tree->points[index + 1];
+	float pointStart = state->curve_position;
+	float pointEnd = pointStart + curveLen * state->curve_scale;
+
+	state->curve_position = pointEnd;
 
 	/* step 1: determine the positions for the endpoints of the bone */
 	{
@@ -268,18 +270,18 @@ static void splineik_evaluate_bone(
 		float tailBlendFac = 1.0f;
 
 		/* determine if the bone should still be affected by SplineIK */
-		if (tree->points[index + 1] >= 1.0f) {
+		if (pointStart >= 1.0f) {
 			/* spline doesn't affect the bone anymore, so done... */
 			pchan->flag |= POSE_DONE;
 			return;
 		}
-		else if ((tree->points[index] >= 1.0f) && (tree->points[index + 1] < 1.0f)) {
+		else if ((pointEnd >= 1.0f) && (pointStart < 1.0f)) {
 			/* blending factor depends on the amount of the bone still left on the chain */
-			tailBlendFac = (1.0f - tree->points[index + 1]) / (tree->points[index] - tree->points[index + 1]);
+			tailBlendFac = (1.0f - pointStart) / (pointEnd - pointStart);
 		}
 
 		/* tail endpoint */
-		if (where_on_path(ikData->tar, tree->points[index], vec, dir, NULL, &rad, NULL)) {
+		if (where_on_path(ikData->tar, pointEnd, vec, dir, NULL, &rad, NULL)) {
 			/* apply curve's object-mode transforms to the position
 			 * unless the option to allow curve to be positioned elsewhere is activated (i.e. no root)
 			 */
@@ -295,7 +297,7 @@ static void splineik_evaluate_bone(
 		}
 
 		/* head endpoint */
-		if (where_on_path(ikData->tar, tree->points[index + 1], vec, dir, NULL, &rad, NULL)) {
+		if (where_on_path(ikData->tar, pointStart, vec, dir, NULL, &rad, NULL)) {
 			/* apply curve's object-mode transforms to the position
 			 * unless the option to allow curve to be positioned elsewhere is activated (i.e. no root)
 			 */
@@ -328,9 +330,7 @@ static void splineik_evaluate_bone(
 		/* compute the raw rotation matrix from the bone's current matrix by extracting only the
 		 * orientation-relevant axes, and normalizing them
 		 */
-		copy_v3_v3(rmat[0], pchan->pose_mat[0]);
-		copy_v3_v3(rmat[1], pchan->pose_mat[1]);
-		copy_v3_v3(rmat[2], pchan->pose_mat[2]);
+		mul_m3_m4m4(rmat, state->locrot_offset, pchan->pose_mat);
 		normalize_m3(rmat);
 
 		/* also, normalize the orientation imposed by the bone, now that we've extracted the scale factor */
@@ -361,6 +361,9 @@ static void splineik_evaluate_bone(
 		mul_m3_m3m3(tmat, dmat, rmat); /* m1, m3, m2 */
 		normalize_m3(tmat); /* attempt to reduce shearing, though I doubt this'll really help too much now... */
 		copy_m4_m3(poseMat, tmat);
+
+		/* apply rotation to the accumulated parent transform */
+		mul_m4_m3m4(state->locrot_offset, dmat, state->locrot_offset);
 	}
 
 	/* step 4: set the scaling factors for the axes */
@@ -470,19 +473,18 @@ static void splineik_evaluate_bone(
 		/* when the 'no-root' option is affected, the chain can retain
 		 * the shape but be moved elsewhere
 		 */
-		copy_v3_v3(poseHead, pchan->pose_head);
+		copy_v3_v3(poseHead, origHead);
 	}
 	else if (tree->con->enforce 

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list