[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [56896] trunk/blender: Painting / Sculpting: more tweaks to pressure sensitivity

Brecht Van Lommel brechtvanlommel at pandora.be
Sat May 18 13:25:24 CEST 2013


Revision: 56896
          http://projects.blender.org/scm/viewvc.php?view=rev&root=bf-blender&revision=56896
Author:   blendix
Date:     2013-05-18 11:25:24 +0000 (Sat, 18 May 2013)
Log Message:
-----------
Painting / Sculpting: more tweaks to pressure sensitivity

* Also do pressure interpolation for brush size and spacing.
* Do smoothing of pressure when smooth stroke and sample average is enabled.
* Revert the OS X specific pressure change to pressure ^ 2.5, for low pressure
  values like 0.05 it makes the pressure 100x lower, which is problematic. If
  we need to adjust the pressure curve it should be done for all platforms.

Still weak:

* Pressure of first touch on tablet is difficult to control, usually it's low
  which makes the stroke start out small or soft, but other times not. Finer
  event capturing at ghost level would help, along with pressure changes without
  mouse movement, but this may also need different paint stroke logic.
* Brush radius is rounded to integers, this gives noticeable stepping.
* Brush falloff is not antialiased, gives noticeable aliasing for small brush
  sizes which was always a problem, but is more common with size pressure control.

Modified Paths:
--------------
    trunk/blender/intern/ghost/intern/GHOST_SystemCocoa.mm
    trunk/blender/source/blender/editors/sculpt_paint/paint_stroke.c

Modified: trunk/blender/intern/ghost/intern/GHOST_SystemCocoa.mm
===================================================================
--- trunk/blender/intern/ghost/intern/GHOST_SystemCocoa.mm	2013-05-18 11:04:29 UTC (rev 56895)
+++ trunk/blender/intern/ghost/intern/GHOST_SystemCocoa.mm	2013-05-18 11:25:24 UTC (rev 56896)
@@ -1390,8 +1390,8 @@
 			// 2. device is not sending [event pointingDeviceType], due no eraser
 			if (ct.Active == GHOST_kTabletModeNone)
 				ct.Active = GHOST_kTabletModeStylus;
-				
-			ct.Pressure = sqrtf(powf([event pressure], 5 )); // experimental: change sensivity curve
+
+			ct.Pressure = [event pressure];
 			ct.Xtilt = [event tilt].x;
 			ct.Ytilt = [event tilt].y;
 			break;

Modified: trunk/blender/source/blender/editors/sculpt_paint/paint_stroke.c
===================================================================
--- trunk/blender/source/blender/editors/sculpt_paint/paint_stroke.c	2013-05-18 11:04:29 UTC (rev 56895)
+++ trunk/blender/source/blender/editors/sculpt_paint/paint_stroke.c	2013-05-18 11:25:24 UTC (rev 56896)
@@ -63,8 +63,7 @@
 
 typedef struct PaintSample {
 	float mouse[2];
-
-	/* TODO: other input properties, e.g. tablet pressure */
+	float pressure;
 } PaintSample;
 
 typedef struct PaintStroke {
@@ -290,6 +289,7 @@
 	/* copy last position -before- jittering, or space fill code
 	 * will create too many dabs */
 	copy_v2_v2(stroke->last_mouse_position, mouse_in);
+	stroke->last_pressure = pressure;
 
 	paint_brush_update(C, brush, mode, stroke, mouse_in, pressure);
 
@@ -338,11 +338,12 @@
 }
 
 /* Returns zero if no sculpt changes should be made, non-zero otherwise */
-static int paint_smooth_stroke(PaintStroke *stroke, float output[2],
+static int paint_smooth_stroke(PaintStroke *stroke, float output[2], float *outpressure,
                                const PaintSample *sample, PaintMode mode)
 {
 	output[0] = sample->mouse[0];
 	output[1] = sample->mouse[1];
+	*outpressure = sample->pressure;
 
 	if (paint_supports_smooth_stroke(stroke->brush, mode)) {
 		float radius = stroke->brush->smooth_stroke_radius * stroke->zoom_2d;
@@ -357,71 +358,98 @@
 
 		output[0] = sample->mouse[0] * v + stroke->last_mouse_position[0] * u;
 		output[1] = sample->mouse[1] * v + stroke->last_mouse_position[1] * u;
+		*outpressure = sample->pressure * v + stroke->last_pressure * u;
 	}
 
 	return 1;
 }
 
-/* For brushes with stroke spacing enabled, moves mouse in steps
- * towards the final mouse location. */
-static int paint_space_stroke(bContext *C, wmOperator *op, const float final_mouse[2], float pressure)
+static float paint_space_stroke_spacing(const Scene *scene, PaintStroke *stroke, float size_pressure, float spacing_pressure)
 {
-	PaintStroke *stroke = op->customdata;
+	/* brushes can have a minimum size of 1.0 but with pressure it can be smaller then a pixel
+	 * causing very high step sizes, hanging blender [#32381] */
+	const float size_clamp = max_ff(1.0f, BKE_brush_size_get(scene, stroke->brush) * size_pressure);
+	float spacing = stroke->brush->spacing;
 
-	int cnt = 0;
+	/* apply spacing pressure */
+	if (stroke->brush->flag & BRUSH_SPACING_PRESSURE)
+		spacing = max_ff(1.0f, spacing * (1.5f - spacing_pressure));
 
-	float mouse[2];
-	float vec[2];
-	float length, scale;
+	/* stroke system is used for 2d paint too, so we need to account for
+	 * the fact that brush can be scaled there. */
+	spacing *= stroke->zoom_2d;
 
-	copy_v2_v2(mouse, stroke->last_mouse_position);
-	sub_v2_v2v2(vec, final_mouse, mouse);
+	return (size_clamp * spacing / 50.0f);
+}
 
-	length = len_v2(vec);
+static float paint_space_stroke_spacing_variable(const Scene *scene, PaintStroke *stroke, float pressure, float dpressure, float length)
+{
+	if (BKE_brush_use_size_pressure(scene, stroke->brush)) {
+		/* use pressure to modify size. set spacing so that at 100%, the circles
+		 * are aligned nicely with no overlap. for this the spacing needs to be
+		 * the average of the previous and next size. */
+		float s = paint_space_stroke_spacing(scene, stroke, 1.0f, pressure);
+		float q = s*dpressure/(2.0f*length);
+		float pressure_fac = (1.0f + q)/(1.0f - q);
 
-	if (length > FLT_EPSILON) {
-		const Scene *scene = CTX_data_scene(C);
-		int steps;
-		int i;
-		float size_pressure = 1.0f;
+		float last_size_pressure = stroke->last_pressure;
+		float new_size_pressure = stroke->last_pressure*pressure_fac;
 
-		/* XXX mysterious :) what has 'use size' do with this here... if you don't check for it, pressure fails */
-		if (BKE_brush_use_size_pressure(scene, stroke->brush))
-			size_pressure = pressure;
+		/* average spacing */
+		float last_spacing = paint_space_stroke_spacing(scene, stroke, last_size_pressure, pressure);
+		float new_spacing = paint_space_stroke_spacing(scene, stroke, new_size_pressure, pressure);
 
-		if (size_pressure > FLT_EPSILON) {
-			/* brushes can have a minimum size of 1.0 but with pressure it can be smaller then a pixel
-			 * causing very high step sizes, hanging blender [#32381] */
-			const float size_clamp = max_ff(1.0f, BKE_brush_size_get(scene, stroke->brush) * size_pressure);
-			float spacing = stroke->brush->spacing;
+		return 0.5f*(last_spacing + new_spacing);
+	}
+	else {
+		/* no size pressure */
+		return paint_space_stroke_spacing(scene, stroke, 1.0f, pressure);
+	}
+}
 
-			/* stroke system is used for 2d paint too, so we need to account for
-				 * the fact that brush can be scaled there. */
+/* For brushes with stroke spacing enabled, moves mouse in steps
+ * towards the final mouse location. */
+static int paint_space_stroke(bContext *C, wmOperator *op, const float final_mouse[2], float final_pressure)
+{
+	const Scene *scene = CTX_data_scene(C);
+	PaintStroke *stroke = op->customdata;
+	PaintMode mode = BKE_paintmode_get_active_from_context(C);
+	int cnt = 0;
 
-			if (stroke->brush->flag & BRUSH_SPACING_PRESSURE)
-				spacing = max_ff(1.0f, spacing * (1.5f - pressure));
+	if (paint_space_stroke_enabled(stroke->brush, mode)) {
+		float pressure, dpressure;
+		float mouse[2], dmouse[2];
+		float length;
 
-			spacing *= stroke->zoom_2d;
+		sub_v2_v2v2(dmouse, final_mouse, stroke->last_mouse_position);
 
-			scale = (size_clamp * spacing / 50.0f) / length;
-			if (scale > FLT_EPSILON) {
-				float pressure_diff = (pressure - stroke->last_pressure)*scale;
-				float final_pressure = stroke->last_pressure;
-				mul_v2_fl(vec, scale);
+		pressure = stroke->last_pressure;
+		dpressure = final_pressure - stroke->last_pressure;
 
-				steps = (int)(1.0f / scale);
+		length = normalize_v2(dmouse);
 
-				for (i = 0; i < steps; ++i, ++cnt) {
-					final_pressure += pressure_diff;
-					add_v2_v2(mouse, vec);
-					paint_brush_stroke_add_step(C, op, mouse, final_pressure);
-				}
+		while (length > 0.0f) {
+			float spacing = paint_space_stroke_spacing_variable(scene, stroke, pressure, dpressure, length);
+			
+			if (length >= spacing) {
+				mouse[0] = stroke->last_mouse_position[0] + dmouse[0]*spacing;
+				mouse[1] = stroke->last_mouse_position[1] + dmouse[1]*spacing;
+				pressure = stroke->last_pressure + (spacing/length)*dpressure;
+
+				paint_brush_stroke_add_step(C, op, mouse, pressure);
+
+				length -= spacing;
+				pressure = stroke->last_pressure;
+				dpressure = final_pressure - stroke->last_pressure;
+
+				cnt++;
 			}
+			else {
+				break;
+			}
 		}
 	}
 
-	stroke->last_pressure = pressure;
-
 	return cnt;
 }
 
@@ -582,7 +610,7 @@
 
 static void paint_stroke_add_sample(const Paint *paint,
                                     PaintStroke *stroke,
-                                    float x, float y)
+                                    float x, float y, float pressure)
 {
 	PaintSample *sample = &stroke->samples[stroke->cur_sample];
 	int max_samples = MIN2(PAINT_MAX_INPUT_SAMPLES,
@@ -590,6 +618,7 @@
 
 	sample->mouse[0] = x;
 	sample->mouse[1] = y;
+	sample->pressure = pressure;
 
 	stroke->cur_sample++;
 	if (stroke->cur_sample >= max_samples)
@@ -607,10 +636,13 @@
 
 	BLI_assert(stroke->num_samples > 0);
 	
-	for (i = 0; i < stroke->num_samples; i++)
+	for (i = 0; i < stroke->num_samples; i++) {
 		add_v2_v2(average->mouse, stroke->samples[i].mouse);
+		average->pressure += stroke->samples[i].pressure;
+	}
 
 	mul_v2_fl(average->mouse, 1.0f / stroke->num_samples);
+	average->pressure /= stroke->num_samples;
 
 	/*printf("avg=(%f, %f), num=%d\n", average->mouse[0], average->mouse[1], stroke->num_samples);*/
 }
@@ -627,15 +659,15 @@
 	bool redraw = false;
 	float pressure;
 
-	paint_stroke_add_sample(p, stroke, event->mval[0], event->mval[1]);
+	/* see if tablet affects event */
+	pressure = event_tablet_data(event, &stroke->pen_flip);
+
+	paint_stroke_add_sample(p, stroke, event->mval[0], event->mval[1], pressure);
 	paint_stroke_sample_average(stroke, &sample_average);
 
 	get_imapaint_zoom(C, &zoomx, &zoomy);
 	stroke->zoom_2d = max_ff(zoomx, zoomy);
 
-	/* see if tablet affects event */
-	pressure = event_tablet_data(event, &stroke->pen_flip);
-
 	/* let NDOF motion pass through to the 3D view so we can paint and rotate simultaneously!
 	 * this isn't perfect... even when an extra MOUSEMOVE is spoofed, the stroke discards it
 	 * since the 2D deltas are zero -- code in this file needs to be updated to use the
@@ -645,6 +677,7 @@
 
 	if (!stroke->stroke_started) {
 		copy_v2_v2(stroke->last_mouse_position, sample_average.mouse);
+		stroke->last_pressure = sample_average.pressure;
 		stroke->stroke_started = stroke->test_start(C, op, sample_average.mouse);
 		BLI_assert((stroke->stroke_started & ~1) == 0);  /* 0/1 */
 		stroke->last_pressure = pressure;
@@ -678,7 +711,7 @@
 	         (event->type == TIMER && (event->customdata == stroke->timer)) )
 	{
 		if (stroke->stroke_started) {
-			if (paint_smooth_stroke(stroke, mouse, &sample_average, mode)) {
+			if (paint_smooth_stroke(stroke, mouse, &pressure, &sample_average, mode)) {
 				if (paint_space_stroke_enabled(stroke->brush, mode)) {
 					if (paint_space_stroke(C, op, mouse, pressure))
 						redraw = true;




More information about the Bf-blender-cvs mailing list