[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