[Bf-blender-cvs] [036efa1ce56] experimental-build: Squashed commits of the staging smooth-curves branch:

Alexander Gavrilov noreply at git.blender.org
Mon May 29 20:40:47 CEST 2017


Commit: 036efa1ce5640b894afa2ff5d0226aa75d1e5b3b
Author: Alexander Gavrilov
Date:   Mon May 29 21:39:57 2017 +0300
Branches: experimental-build
https://developer.blender.org/rB036efa1ce5640b894afa2ff5d0226aa75d1e5b3b

Squashed commits of the staging smooth-curves branch:

commit b4423d0f2c1ccd522dbc689d75649b4e31c96be9
Author: Alexander Gavrilov <angavrilov at gmail.com>
Date:   Mon May 8 12:03:40 2017 +0300

    Implement smoothed auto handle placement with cyclic extrapolation.

    Cyclic extrapolation is implemented as an f-curve modifier, so this
    technically violates abstraction separation and is something of a hack.
    However without such behavior achieving smooth looping with cyclic
    extrapolation is extremely cumbersome.

    The new behavior is applied when the first modifier is Cyclic
    extrapolation in Repeat or Repeat with Offset mode without
    using influence, repeat count or range restrictions.

commit fd8c81ec91ba4c3d1656387c879256289654afde
Author: Alexander Gavrilov <angavrilov at gmail.com>
Date:   Mon May 1 20:39:44 2017 +0300

    Implement a new automatic handle algorithm to produce smooth F-Curves.

    The legacy algorithm only considers two adjacent points when computing
    the bezier handles, which cannot produce satisfactory results. Animators
    are often forced to manually adjust all curves.

    The new approach instead solves a system of equations to trace a cubic spline
    with continuous second derivative through the whole segment of auto points,
    delimited at ends by keyframes with handles set by other requirements.

    This algorithm also adjusts Vector handles that face ordinary bezier keyframes
    to achieve zero acceleration at the Vector keyframe, instead of simply pointing
    it at the adjacent point.

    Original idea and implementation by Benoit Bolsee <benoit.bolsee at online.be>;
    code mostly rewritten to improve code clarity and extensibility.

commit ed3d6782a58bb840febe11f91103aabd9fc00e5f
Author: Alexander Gavrilov <angavrilov at gmail.com>
Date:   Sun Apr 30 16:14:39 2017 +0300

    Trigger FCurve handle recalculation on updates to curve modifiers.

    If automatic curve handles are changed to be aware of the Cycles
    modifier to provide a smooth loop transition, they have to be updated
    when the modifier is added, removed or its options change.

    Due to the way code is structured, it seems it requires a helper
    link to the containing curve from the modifier object.

commit fe21825f65c0e8647d36d2e5514fc0cdd614d5fe
Author: Dalai Felinto <dfelinto at gmail.com>
Date:   Mon Nov 18 16:58:23 2013 -0200

    force fcurve AUTO handlers to use 1/3 of the distance for smoothness
    see: http://www.blender.org/forum/viewtopic.php?p=65508&sid=02e88fe6e3a43338ec8bc354991bc402

    Note: this is also used by the curve code. The magic number 2.5614f
    comes from review 290361776e5858b3903a83c0cddf722b8340e699

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

M	source/blender/blenkernel/BKE_curve.h
M	source/blender/blenkernel/BKE_fcurve.h
M	source/blender/blenkernel/intern/curve.c
M	source/blender/blenkernel/intern/fcurve.c
M	source/blender/blenkernel/intern/fmodifier.c
M	source/blender/blenkernel/intern/ipo.c
M	source/blender/blenlib/BLI_math_solvers.h
M	source/blender/blenlib/intern/math_solvers.c
M	source/blender/blenloader/intern/readfile.c
M	source/blender/editors/animation/drivers.c
M	source/blender/editors/animation/fmodifier_ui.c
M	source/blender/editors/include/ED_anim_api.h
M	source/blender/editors/object/object_constraint.c
M	source/blender/editors/object/object_relations.c
M	source/blender/editors/space_action/action_edit.c
M	source/blender/editors/space_graph/graph_edit.c
M	source/blender/editors/space_nla/nla_edit.c
M	source/blender/makesdna/DNA_anim_types.h
M	source/blender/makesdna/DNA_curve_types.h
M	source/blender/makesrna/intern/rna_fcurve.c

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

diff --git a/source/blender/blenkernel/BKE_curve.h b/source/blender/blenkernel/BKE_curve.h
index e111bd0e16b..5bbd46e5906 100644
--- a/source/blender/blenkernel/BKE_curve.h
+++ b/source/blender/blenkernel/BKE_curve.h
@@ -204,6 +204,8 @@ void BKE_nurb_handle_calc(struct BezTriple *bezt, struct BezTriple *prev,  struc
 void BKE_nurb_handle_calc_simple(struct Nurb *nu, struct BezTriple *bezt);
 void BKE_nurb_handle_calc_simple_auto(struct Nurb *nu, struct BezTriple *bezt);
 
+void BKE_nurb_handle_smooth_all(struct BezTriple *bezt, int total, bool cyclic);
+
 void BKE_nurb_handles_calc(struct Nurb *nu);
 void BKE_nurb_handles_autocalc(struct Nurb *nu, int flag);
 void BKE_nurb_bezt_handle_test(struct BezTriple *bezt, const bool use_handle);
diff --git a/source/blender/blenkernel/BKE_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h
index 3a45097efc5..6105802ec82 100644
--- a/source/blender/blenkernel/BKE_fcurve.h
+++ b/source/blender/blenkernel/BKE_fcurve.h
@@ -188,7 +188,8 @@ const FModifierTypeInfo *get_fmodifier_typeinfo(int type);
 
 /* ---------------------- */
 
-struct FModifier *add_fmodifier(ListBase *modifiers, int type);
+struct FModifier *add_fmodifier_raw(ListBase *modifiers, int type);
+struct FModifier *add_fmodifier(struct FCurve *fcu, int type);
 struct FModifier *copy_fmodifier(struct FModifier *src);
 void copy_fmodifiers(ListBase *dst, ListBase *src);
 bool remove_fmodifier(ListBase *modifiers, struct FModifier *fcm);
diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c
index 439abb1d593..767ce7f27d9 100644
--- a/source/blender/blenkernel/intern/curve.c
+++ b/source/blender/blenkernel/intern/curve.c
@@ -3121,6 +3121,9 @@ static void calchandleNurb_intern(
 	float len_ratio;
 	const float eps = 1e-5;
 
+	/* assume normal handle until we check */
+	bezt->f5 = HD_AUTOTYPE_NORMAL;
+
 	if (bezt->h1 == 0 && bezt->h2 == 0) {
 		return;
 	}
@@ -3172,7 +3175,8 @@ static void calchandleNurb_intern(
 		tvec[2] = dvec_b[2] / len_b + dvec_a[2] / len_a;
 
 		if (is_fcurve) {
-			len = tvec[0];
+			/* force the handlers transition to be 1/3 */
+			len = 6.0f/2.5614f;
 		}
 		else {
 			len = len_v3(tvec);
@@ -3183,10 +3187,12 @@ static void calchandleNurb_intern(
 			/* only for fcurves */
 			bool leftviolate = false, rightviolate = false;
 
-			if (len_a > 5.0f * len_b)
-				len_a = 5.0f * len_b;
-			if (len_b > 5.0f * len_a)
-				len_b = 5.0f * len_a;
+			if (!is_fcurve) {
+				if (len_a > 5.0f * len_b)
+					len_a = 5.0f * len_b;
+				if (len_b > 5.0f * len_a)
+					len_b = 5.0f * len_a;
+			}
 
 			if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM)) {
 				len_a /= len;
@@ -3197,6 +3203,7 @@ static void calchandleNurb_intern(
 					float ydiff2 = next->vec[1][1] - bezt->vec[1][1];
 					if ((ydiff1 <= 0.0f && ydiff2 <= 0.0f) || (ydiff1 >= 0.0f && ydiff2 >= 0.0f)) {
 						bezt->vec[0][1] = bezt->vec[1][1];
+						bezt->f5 = HD_AUTOTYPE_SPECIAL;
 					}
 					else { /* handles should not be beyond y coord of two others */
 						if (ydiff1 <= 0.0f) {
@@ -3223,6 +3230,7 @@ static void calchandleNurb_intern(
 					float ydiff2 = next->vec[1][1] - bezt->vec[1][1];
 					if ( (ydiff1 <= 0.0f && ydiff2 <= 0.0f) || (ydiff1 >= 0.0f && ydiff2 >= 0.0f) ) {
 						bezt->vec[2][1] = bezt->vec[1][1];
+						bezt->f5 = HD_AUTOTYPE_SPECIAL;
 					}
 					else { /* handles should not be beyond y coord of two others */
 						if (ydiff1 <= 0.0f) {
@@ -3385,6 +3393,517 @@ static void calchandlesNurb_intern(Nurb *nu, bool skip_align)
 	}
 }
 
+/*
+ * This function computes the handles of a series of auto bezier points
+ * on the basis of 'no acceleration discontinuities' at the points.
+ * The first and last bezier points are considered 'fixed' (their handles are not touched)
+ * The result is the smoothest possible trajectory going through intemediate points.
+ * The difficulty is that the handles depends on their neighbours.
+ *
+ * The exact solution is found by solving a tridiagonal matrix equation formed
+ * by the continuity and boundary conditions. Although theoretically handle position
+ * is affected by all other points of the curve segment, in practice the influence
+ * decreases exponentially with distance.
+ *
+ * Note: this algorithm assumes that the handle horizontal size if always 1/3 of the
+ * of the interval to the next point. This rule ensures linear interpolation of time.
+ *
+ * ^ height (co 1)
+ * |                                            yN
+ * |                                   yN-1     |
+ * |                      y2           |        |
+ * |           y1         |            |        |
+ * |    y0     |          |            |        |
+ * |    |      |          |            |        |
+ * |    |      |          |            |        |
+ * |    |      |          |            |        |
+ * |-------t1---------t2--------- ~ --------tN-------------------> time (co 0)
+ *
+ *
+ * Mathematical basis:
+ *
+ *   1. Handle lengths on either side of each point are connected by a factor
+ *      ensuring continuity of the first derivative:
+ *
+ *      l[i] = t[i+1]/t[i]
+ *
+ *   2. The tridiagonal system is formed by the following equation, which is derived
+ *      by differentiating the bezier curve and specifies second derivative continuity
+ *      at every point:
+ *
+ *      l[i]^2 * h[i-1] + (2*l[i]+2) * h[i] + 1/l[i+1] * h[i+1] = (y[i]-y[i-1])*l[i]^2 + y[i+1]-y[i]
+ *
+ *   3. If this point is adjacent to a manually set handle with X size not equal to 1/3
+ *      of the horizontal interval, this equation becomes slightly more complex:
+ *
+ *      l[i]^2 * h[i-1] + (3*(1-R[i-1])*l[i] + 3*(1-L[i+1])) * h[i] + 1/l[i+1] * h[i+1] = (y[i]-y[i-1])*l[i]^2 + y[i+1]-y[i]
+ *
+ *      The difference between equations amounts to this, and it's obvious that when R[i-1]
+ *      and L[i+1] are both 1/3, it becomes zero:
+ *
+ *      ( (1-3*R[i-1])*l[i] + (1-3*L[i+1]) ) * h[i]
+ *
+ *   4. The equations for zero acceleration border conditions are basically the above
+ *      equation with parts omitted, so the handle size correction also applies.
+ */
+
+static void nurb_lock_unknown(float *a, float *b, float *c, float *d, int i, float value)
+{
+	a[i] = c[i] = 0.0f;
+	b[i] = 1.0f;
+	d[i] = value;
+}
+
+static void nurb_eq_continuous(float *a, float *b, float *c, float *d, float *y, float *l, int i)
+{
+	a[i] = l[i]*l[i];
+	b[i] = 2.0f*(l[i] + 1);
+	c[i] = 1.0f/l[i+1];
+	d[i] = (y[i]-y[i-1])*l[i]*l[i] + (y[i+1]-y[i]);
+}
+
+static void nurb_eq_noaccel_right(float *a, float *b, float *c, float *d, float *y, float *l, int i)
+{
+	a[i] = 0.0f;
+	b[i] = 2.0f;
+	c[i] = 1.0f/l[i+1];
+	d[i] = y[i+1]-y[i];
+}
+
+static void nurb_eq_noaccel_left(float *a, float *b, float *c, float *d, float *y, float *l, int i)
+{
+	a[i] = l[i]*l[i];
+	b[i] = 2.0f*l[i];
+	c[i] = 0.0f;
+	d[i] = (y[i]-y[i-1])*l[i]*l[i];
+}
+
+#if 0
+static void nurb_eq_constaccel_right(float *a, float *b, float *c, float *d, float *y, float *l, int i)
+{
+	a[i] = 0.0f;
+	b[i] = 3.0f*l[i+1];
+	c[i] = 3.0f;
+	d[i] = 2.0f*(y[i+1]-y[i])*l[i+1];
+}
+
+static void nurb_eq_constaccel_left(float *a, float *b, float *c, float *d, float *y, float *l, int i)
+{
+	a[i] = 3.0f*l[i];
+	b[i] = 3.0f;
+	c[i] = 0.0f;
+	d[i] = 2.0f*(y[i]-y[i-1])*l[i];
+}
+#endif
+
+/* auto clamp prevents its own point going the wrong way, and adjacent handles overshooting */
+static void nurb_clamp(float *hmax, float *hmin, int i, float dy, bool no_reverse, bool no_overshoot)
+{
+	if (dy > 0)
+	{
+		if (no_overshoot)
+			hmax[i] = min_ff(hmax[i], dy);
+		if (no_reverse)
+			hmin[i] = 0.0f;
+	}
+	else if (dy < 0)
+	{
+		if (no_reverse)
+			hmax[i] = 0.0f;
+		if (no_overshoot)
+			hmin[i] = max_ff(hmin[i], dy);
+	}
+	else if (no_reverse || no_overshoot)
+	{
+		hmax[i] = hmin[i] = 0.0f;
+	}
+}
+
+/* computes in which direction to change h[i] to satisfy conditions better */
+static float nurb_relax_direction(float *a, float *b, float *c, float *d, float *h, int i, int count)
+{
+	/* current deviation between sides of the equation */
+	float state = a[i] * h[(i+count-1)%count] + b[i] * h[i] + c[i] * h[(i+1)%count] - d[i];
+
+	/* only the sign is meaningful */
+	return -state * b[i];
+}
+
+/* write changes to a bezier handle */
+static void nurb_output_handle(BezTriple *bezt, bool right, float dy, bool endpoint)
+{
+	int idx = right ? 2 : 0;
+	char hr = right ? bezt->h2 : bezt->h1;
+	char hm = right ? bezt->h1 : bezt->h2;
+
+	/* only assign Auto/Vector handles */
+	if (!ELEM(hr, HD_AUTO, HD_AUTO_ANIM, HD_VECT))
+		return;
+
+	bezt->vec[idx][1] = bezt->vec[1][1] + dy;
+
+	/* fix up the Align handle if any */
+	if (ELEM(hm, HD_ALIGN, HD_ALIGN_DOUBLESIDE))
+	{
+		float hlen = len_v2v2(bezt->vec[1], bezt->vec[2-idx]);
+		float h2len = len_v2v2(bezt->vec[1], bezt->vec[idx]);
+		float tmp[2];
+
+		sub_v2_v2v2(tmp, bezt->vec[1], bezt->vec[idx]);
+		madd_v2_v2v2fl(bezt->vec[2-idx], bezt->vec[1], tmp, hlen/h2len);
+	}
+	/* at end points of the curve, mirror handle to the other side */
+	else if (endpoint && ELEM(hm, HD_AUTO, HD_AUTO_ANIM, HD_VECT))
+	{
+		float midx = bezt->vec[1][0];
+		float ratio = (bezt->vec[2-idx][0] - midx) / (bezt->vec[idx][0] - midx);
+
+		bezt->vec[2-idx][1] = bezt->vec[1][1] + dy * ratio;
+	}
+}
+
+static bool nurb_check_solve_end_handle(BezTriple *bezt, char htype, bool end)
+{
+	return (htype == HD_VECT) || (end && ELEM(htype, HD_AUTO, HD_AUTO_ANIM) && bezt->f5 == HD_AUTOTYPE_NORMAL);
+}
+
+static float nurb_calc_handle_adj(float hsize[2], float dx)
+{
+	/* if handles intersect in x direction, they are scaled to fit */
+	float fac = dx/(hsize[0] + dx/3.0f);
+	if (fac < 1.0f)
+		mul_v2_fl(hsize, fac);
+
+	return 1.0f - 3.0f*hsize[0]/dx;
+}
+
+static void nurb_handle_calc_smooth(BezTriple *bezt, int total, int start, int count, bool cycle)
+{
+	float *x, *y, *l, *a, *b, *c, *d, *h, *hmax, *hmin, *a0, *b0, *c0, *d0;
+	float **arrays[] = { &x, &y, &l, &a, &b, &c, &d, &h, &hmax, &hmin, &a0, &b0, &c0, &d0 };
+	char *is_locked, *num_unlocks;
+	char **flagarrays[] = { &is_locked, &num_unlocks };
+	const int num_arrays = sizeof(arrays)/sizeof(float**), num_flagarrays = sizeof(flagarrays)/sizeof(char**);
+
+	int solve_count = count;
+
+	/* verify index ranges */
+
+	if (count < 2)

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list