[Bf-blender-cvs] [51c8a6f491d] master: Fix T37500: implement Bendy bone segment deformation interpolation.

Alexander Gavrilov noreply at git.blender.org
Sat Apr 13 15:28:04 CEST 2019


Commit: 51c8a6f491d3b21a6c91af6267f58d4f0add58af
Author: Alexander Gavrilov
Date:   Fri Apr 5 18:40:59 2019 +0300
Branches: master
https://developer.blender.org/rB51c8a6f491d3b21a6c91af6267f58d4f0add58af

Fix T37500: implement Bendy bone segment deformation interpolation.

Previously B-Bone deformation mapped every vertex to just one
B-Bone segment. This results in abrupt transformation differences
between the sides of each threshold plane, reducing the quality
of B-Bone deformation and making the use of shape keys impractical.

This commit replaces this approach with a linear blend between
the two closest segment transformations, effectively representing
the B-Bone as two weight-blended plain bones for each vertex.

In order to distribute the interpolation more evenly along the
bone, segment matrices for deformation are now computed at points
between the segments and at the ends of the B-Bone. The computation
also uses the true tangents of the Bezier curve for the orientation.
The nodes at the end of the bone require some special handling to
deal with zero-length Bezier handles caused by a zero ease value.

The Copy Transforms constraint now also smoothly interpolates
rotation and scaling along the bone shape when enabled.

The initial version of the patch was submitted by @Sam200.

Differential Revision: https://developer.blender.org/D4635

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

M	source/blender/blenkernel/BKE_armature.h
M	source/blender/blenkernel/intern/armature.c
M	source/blender/blenkernel/intern/constraint.c
M	source/blender/blenlib/BLI_math_matrix.h
M	source/blender/blenlib/intern/math_matrix.c
M	source/blender/draw/intern/draw_armature.c
M	source/blender/editors/armature/armature_skinning.c
M	source/blender/editors/gpencil/gpencil_armature.c
M	source/blender/makesrna/intern/rna_pose_api.c
M	source/blender/physics/intern/implicit_blender.c

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

diff --git a/source/blender/blenkernel/BKE_armature.h b/source/blender/blenkernel/BKE_armature.h
index 09d8cbf933c..7182561a038 100644
--- a/source/blender/blenkernel/BKE_armature.h
+++ b/source/blender/blenkernel/BKE_armature.h
@@ -184,7 +184,7 @@ void BKE_pchan_bbone_spline_params_get(
         struct bPoseChannel *pchan, const bool rest, struct BBoneSplineParameters *r_param);
 
 void BKE_pchan_bbone_spline_setup(
-        struct bPoseChannel *pchan, const bool rest, Mat4 result_array[MAX_BBONE_SUBDIV]);
+        struct bPoseChannel *pchan, const bool rest, const bool for_deform, Mat4 *result_array);
 
 void BKE_pchan_bbone_handles_compute(
         const BBoneSplineParameters *param,
@@ -192,13 +192,15 @@ void BKE_pchan_bbone_handles_compute(
         float h2[3], float *r_roll2,
         bool ease, bool offsets);
 int  BKE_pchan_bbone_spline_compute(
-        struct BBoneSplineParameters *param, Mat4 result_array[MAX_BBONE_SUBDIV]);
+        struct BBoneSplineParameters *param, const bool for_deform, Mat4 *result_array);
 
 void BKE_pchan_bbone_segments_cache_compute(
         struct bPoseChannel *pchan);
 void BKE_pchan_bbone_segments_cache_copy(
         struct bPoseChannel *pchan, struct bPoseChannel *pchan_from);
 
+void BKE_pchan_bbone_deform_segment_index(const struct bPoseChannel *pchan, float pos, int *r_index, float *r_blend_next);
+
 /* like EBONE_VISIBLE */
 #define PBONE_VISIBLE(arm, bone) ( \
 	CHECK_TYPE_INLINE(arm, bArmature *), \
diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c
index 6e0767836d7..5123b159440 100644
--- a/source/blender/blenkernel/intern/armature.c
+++ b/source/blender/blenkernel/intern/armature.c
@@ -36,6 +36,7 @@
 #include "BLI_ghash.h"
 #include "BLI_task.h"
 #include "BLI_utildefines.h"
+#include "BLI_alloca.h"
 
 #include "DNA_anim_types.h"
 #include "DNA_armature_types.h"
@@ -389,45 +390,60 @@ int bone_autoside_name(char name[MAXBONENAME], int UNUSED(strip_number), short a
 
 /* ************* B-Bone support ******************* */
 
-/* data has MAX_BBONE_SUBDIV+1 interpolated points, will become desired amount with equal distances */
-static void equalize_bbone_bezier(float *data, int desired)
+/* Compute a set of bezier parameter values that produce approximately equally spaced points. */
+static void equalize_cubic_bezier(const float control[4][3], int temp_segments, int final_segments, float *r_t_points)
 {
-	float *fp, totdist, ddist, dist, fac1, fac2;
-	float pdist[MAX_BBONE_SUBDIV + 1];
-	float temp[MAX_BBONE_SUBDIV + 1][4];
-	int a, nr;
+	float (*coords)[3] = BLI_array_alloca(coords, temp_segments + 1);
+	float *pdist = BLI_array_alloca(pdist, temp_segments + 1);
 
+	/* Compute the first pass of bezier point coordinates. */
+	for (int i = 0; i < 3; i++)	{
+		BKE_curve_forward_diff_bezier(
+			control[0][i], control[1][i], control[2][i], control[3][i],
+		    &coords[0][i], temp_segments, sizeof(*coords)
+		);
+	}
+
+	/* Calculate the length of the polyline at each point. */
 	pdist[0] = 0.0f;
-	for (a = 0, fp = data; a < MAX_BBONE_SUBDIV; a++, fp += 4) {
-		copy_qt_qt(temp[a], fp);
-		pdist[a + 1] = pdist[a] + len_v3v3(fp, fp + 4);
-	}
-	/* do last point */
-	copy_qt_qt(temp[a], fp);
-	totdist = pdist[a];
-
-	/* go over distances and calculate new points */
-	ddist = totdist / ((float)desired);
-	nr = 1;
-	for (a = 1, fp = data + 4; a < desired; a++, fp += 4) {
-		dist = ((float)a) * ddist;
-
-		/* we're looking for location (distance) 'dist' in the array */
-		while ((nr < MAX_BBONE_SUBDIV) && (dist >= pdist[nr]))
+
+	for (int i = 0; i < temp_segments; i++)
+		pdist[i + 1] = pdist[i] + len_v3v3(coords[i], coords[i + 1]);
+
+	/* Go over distances and calculate new parameter values. */
+	float dist_step = pdist[temp_segments] / final_segments;
+
+	r_t_points[0] = 0.0f;
+
+	for (int i = 1, nr = 1; i <= final_segments; i++) {
+		float dist = i * dist_step;
+
+		/* We're looking for location (distance) 'dist' in the array. */
+		while ((nr < temp_segments) && (dist >= pdist[nr]))
 			nr++;
 
-		fac1 = pdist[nr] - pdist[nr - 1];
-		fac2 = pdist[nr] - dist;
-		fac1 = fac2 / fac1;
-		fac2 = 1.0f - fac1;
+		float fac = (pdist[nr] - dist) / (pdist[nr] - pdist[nr - 1]);
 
-		fp[0] = fac1 * temp[nr - 1][0] + fac2 * temp[nr][0];
-		fp[1] = fac1 * temp[nr - 1][1] + fac2 * temp[nr][1];
-		fp[2] = fac1 * temp[nr - 1][2] + fac2 * temp[nr][2];
-		fp[3] = fac1 * temp[nr - 1][3] + fac2 * temp[nr][3];
+		r_t_points[i] = (nr - fac) / temp_segments;
 	}
-	/* set last point, needed for orientation calculus */
-	copy_qt_qt(fp, temp[MAX_BBONE_SUBDIV]);
+
+	r_t_points[final_segments] = 1.0f;
+}
+
+/* Evaluate bezier position and tangent at a specific parameter value using the De Casteljau algorithm. */
+static void evaluate_cubic_bezier(const float control[4][3], float t, float r_pos[3], float r_tangent[3])
+{
+	float layer1[3][3];
+	interp_v3_v3v3(layer1[0], control[0], control[1], t);
+	interp_v3_v3v3(layer1[1], control[1], control[2], t);
+	interp_v3_v3v3(layer1[2], control[2], control[3], t);
+
+	float layer2[2][3];
+	interp_v3_v3v3(layer2[0], layer1[0], layer1[1], t);
+	interp_v3_v3v3(layer2[1], layer1[1], layer1[2], t);
+
+	sub_v3_v3v3(r_tangent, layer2[1], layer2[0]);
+	madd_v3_v3v3fl(r_pos, layer2[0], r_tangent, t);
 }
 
 /* Get "next" and "prev" bones - these are used for handle calculations. */
@@ -640,13 +656,13 @@ void BKE_pchan_bbone_spline_params_get(struct bPoseChannel *pchan, const bool re
 
 /* Fills the array with the desired amount of bone->segments elements.
  * This calculation is done within unit bone space. */
-void BKE_pchan_bbone_spline_setup(bPoseChannel *pchan, const bool rest, Mat4 result_array[MAX_BBONE_SUBDIV])
+void BKE_pchan_bbone_spline_setup(bPoseChannel *pchan, const bool rest, const bool for_deform, Mat4 *result_array)
 {
 	BBoneSplineParameters param;
 
 	BKE_pchan_bbone_spline_params_get(pchan, rest, &param);
 
-	pchan->bone->segments = BKE_pchan_bbone_spline_compute(&param, result_array);
+	pchan->bone->segments = BKE_pchan_bbone_spline_compute(&param, for_deform, result_array);
 }
 
 /* Computes the bezier handle vectors and rolls coming from custom handles. */
@@ -654,6 +670,7 @@ void BKE_pchan_bbone_handles_compute(const BBoneSplineParameters *param, float h
 {
 	float mat3[3][3];
 	float length = param->length;
+	float epsilon = 1e-5 * length;
 
 	if (param->do_scale) {
 		length *= param->scale[1];
@@ -669,7 +686,9 @@ void BKE_pchan_bbone_handles_compute(const BBoneSplineParameters *param, float h
 			h1[1] -= length;
 		}
 
-		normalize_v3(h1);
+		if (normalize_v3(h1) < epsilon)
+			copy_v3_fl3(h1, 0.0f, -1.0f, 0.0f);
+
 		negate_v3(h1);
 
 		if (!param->prev_bbone) {
@@ -693,7 +712,8 @@ void BKE_pchan_bbone_handles_compute(const BBoneSplineParameters *param, float h
 			h2[1] -= length;
 		}
 
-		normalize_v3(h2);
+		if (normalize_v3(h2) < epsilon)
+			copy_v3_fl3(h2, 0.0f, 1.0f, 0.0f);
 
 		/* Find the next roll to interpolate as well. */
 		copy_m3_m4(mat3, param->next_mat);
@@ -749,20 +769,55 @@ void BKE_pchan_bbone_handles_compute(const BBoneSplineParameters *param, float h
 	}
 }
 
+static void make_bbone_spline_matrix(
+        BBoneSplineParameters *param, float scalemats[2][4][4],
+		float pos[3], float axis[3], float roll, float scalefac,
+		float result[4][4]
+) {
+	float mat3[3][3];
+
+	vec_roll_to_mat3(axis, roll, mat3);
+
+	copy_m4_m3(result, mat3);
+	copy_v3_v3(result[3], pos);
+
+	if (param->do_scale) {
+		/* Correct for scaling when this matrix is used in scaled space. */
+		mul_m4_series(result, scalemats[0], result, scalemats[1]);
+	}
+
+	/* BBone scale... */
+	mul_v3_fl(result[0], scalefac);
+	mul_v3_fl(result[2], scalefac);
+}
+
+/* Fade from first to second derivative when the handle is very short. */
+static void ease_handle_axis(const float deriv1[3], const float deriv2[3], float r_axis[3])
+{
+	const float gap = 0.1f;
+
+	copy_v3_v3(r_axis, deriv1);
+
+	float len1 = len_squared_v3(deriv1), len2 = len_squared_v3(deriv2);
+	float ratio = len1 / len2;
+
+	if (ratio < gap * gap) {
+		madd_v3_v3fl(r_axis, deriv2, gap - sqrtf(ratio));
+	}
+}
+
 /* Fills the array with the desired amount of bone->segments elements.
  * This calculation is done within unit bone space. */
-int BKE_pchan_bbone_spline_compute(BBoneSplineParameters *param, Mat4 result_array[MAX_BBONE_SUBDIV])
+int BKE_pchan_bbone_spline_compute(BBoneSplineParameters *param, const bool for_deform, Mat4 *result_array)
 {
-	float scalemat[4][4], iscalemat[4][4];
-	float mat3[3][3];
-	float h1[3], roll1, h2[3], roll2;
-	float data[MAX_BBONE_SUBDIV + 1][4], *fp;
+	float scalemats[2][4][4];
+	float bezt_controls[4][3];
+	float h1[3], roll1, h2[3], roll2, prev[3], cur[3], axis[3];
 	float length = param->length;
-	int a;
 
 	if (param->do_scale) {
-		size_to_mat4(scalemat, param->scale);
-		invert_m4_m4(iscalemat, scalemat);
+		size_to_mat4(scalemats[1], param->scale);
+		invert_m4_m4(scalemats[0], scalemats[1]);
 
 		length *= param->scale[1];
 	}
@@ -772,48 +827,59 @@ int BKE_pchan_bbone_spline_compute(BBoneSplineParameters *param, Mat4 result_arr
 	/* Make curve. */
 	CLAMP_MAX(param->segments, MAX_BBONE_SUBDIV);
 
-	BKE_curve_forward_diff_bezier(0.0f,  h1[0],                               h2[0],                               0.0f,   data[0],     MAX_BBONE_SUBDIV, 4 * sizeof(float));
-	BKE_curve_forward_diff_bezier(0.0f,  h1[1],                               length + h2[1],                      length, data[0] + 1, MAX_BBONE_SUBDIV, 4 * sizeof(float));
-	BKE_curve_forward_diff_bezier(0.0f,  h1[2],                               h2[2],                               0.0f,   data[0] + 2, MAX_BBONE_SUBDIV, 4 * sizeof(float));
-	BKE_curve_forward_diff_bezier(roll1, roll1 + 0.390464f * (roll2 - roll1), roll2 - 0.390464f * (roll2 - roll1), roll2,  data[0] + 3, MAX_BBONE_SUBDIV, 4 * sizeof(float));
+	copy_v3_fl3(bezt_controls[3], 0.0f, length, 0.0f);
+	add_v3_v3v3(bezt_controls[2], bezt_controls[3], h2);
+	copy_v3_v3(bezt_controls[1], h1);
+	zero_v3(bezt_controls[0]);
 
-	equalize_bbone_bezier(data[0], param->segments); /* note: does stride 4! */
+	float bezt_points[MAX_BBONE_SUBDIV + 1];
 
-	/* Make transformation matrices for the segments for drawing. */


@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list