[Bf-blender-cvs] [89014098efc] experimental-build: Squashed commits of the staging smooth-curves branch.

Alexander Gavrilov noreply at git.blender.org
Wed May 31 17:18:46 CEST 2017


Commit: 89014098efcb3bc233a533e49ae48b205f38f7ed
Author: Alexander Gavrilov
Date:   Wed May 31 18:17:48 2017 +0300
Branches: experimental-build
https://developer.blender.org/rB89014098efcb3bc233a533e49ae48b205f38f7ed

Squashed commits of the staging smooth-curves branch.

commit 0f0e26e8b9908c4cc7b5c7812db5d313d5e68a38
Author: Alexander Gavrilov <alexander.gavrilov at jetbrains.com>
Date:   Wed May 31 17:00:46 2017 +0300

    Only apply clamping to the actual auto clamp keyframes.

    Clamping adjacent handles doesn't seem to be that good after all.

commit 874b6ae97c0736a1076799110a11c3458bb28a3d
Author: Alexander Gavrilov <alexander.gavrilov at jetbrains.com>
Date:   Wed May 31 16:59:05 2017 +0300

    Fix smoothing failure caused by keyframes with same X during grab.

commit 63f371a7f8bf04398f635a8d0e1a0e8626061eb7
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 c553b614bf3d3c16d90551c0adc5755797df8d79
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 54776a5f443e542b2d2bc612451c805b1b723bb1
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 22bd2381fff670a7691cc2c0b8784a3bad201526
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

commit 0cb4eec3c097fa281f266ffc2b8f908a4a386efb
Author: Alexander Gavrilov <alexander.gavrilov at jetbrains.com>
Date:   Wed May 31 16:48:44 2017 +0300

    Add an FCurve flag to toggle new smoothing behavior.

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

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/blenkernel/intern/mask.c
M	source/blender/blenkernel/intern/nla.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/animation/keyframing.c
M	source/blender/editors/curve/editcurve.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_buttons.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..b0aafc03486 100644
--- a/source/blender/blenkernel/BKE_curve.h
+++ b/source/blender/blenkernel/BKE_curve.h
@@ -199,11 +199,13 @@ void BKE_nurb_bezt_calc_plane(struct Nurb *nu, struct BezTriple *bezt, float r_p
 
 void BKE_nurb_bpoint_calc_normal(struct Nurb *nu, struct BPoint *bp, float r_normal[3]);
 
-void BKE_nurb_handle_calc(struct BezTriple *bezt, struct BezTriple *prev,  struct BezTriple *next,
-                          const bool is_fcurve);
+void BKE_nurb_handle_calc(struct BezTriple *bezt, struct BezTriple *prev,  struct BezTriple *next);
+void BKE_nurb_handle_calc_fcurve(struct BezTriple *bezt, struct BezTriple *prev, struct BezTriple *next, bool smoothing);
 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..6236d1e8593 100644
--- a/source/blender/blenkernel/intern/curve.c
+++ b/source/blender/blenkernel/intern/curve.c
@@ -3107,7 +3107,7 @@ void BKE_curve_bevelList_make(Object *ob, ListBase *nurbs, bool for_render)
 
 static void calchandleNurb_intern(
         BezTriple *bezt, const BezTriple *prev, const BezTriple *next,
-        bool is_fcurve, bool skip_align)
+        bool is_fcurve, bool skip_align, bool fcurve_smoothing)
 {
 	/* defines to avoid confusion */
 #define p2_h1 ((p2) - 3)
@@ -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,13 @@ static void calchandleNurb_intern(
 		tvec[2] = dvec_b[2] / len_b + dvec_a[2] / len_a;
 
 		if (is_fcurve) {
-			len = tvec[0];
+			if (fcurve_smoothing) {
+				/* force the handlers transition to be 1/3 */
+				len = 6.0f/2.5614f;
+			}
+			else {
+				len = tvec[0];
+			}
 		}
 		else {
 			len = len_v3(tvec);
@@ -3183,10 +3192,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 || !fcurve_smoothing) {
+				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 +3208,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 +3235,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) {
@@ -3370,7 +3383,7 @@ static void calchandlesNurb_intern(Nurb *nu, bool skip_align)
 	next = bezt + 1;
 
 	while (a--) {
-		calchandleNurb_intern(bezt, prev, next, 0, skip_align);
+		calchandleNurb_intern(bezt, prev, next, 0, skip_align, 0);
 		prev = bezt;
 		if (a == 1) {
 			if (nu->flagu & CU_NURB_CYCLIC)
@@ -3385,9 +3398,525 @@ static void calchandlesNurb_intern(Nurb *nu, bool skip_align)
 	}
 }
 
-void BKE_nurb_handle_calc(BezTriple *bezt, BezTriple *prev, BezTriple *next, const bool is_fcurve)
+/*
+ * 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-

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list