diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index d3a8427aa16..a1f616cc237 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -873,6 +873,8 @@ class VIEW3D_PT_sculpt_options(Panel, View3DPaintPanel): layout.prop(sculpt, "show_brush") layout.prop(sculpt, "use_deform_only") + layout.prop(sculpt, "input_samples") + self.unified_paint_settings(layout, context) @@ -984,6 +986,8 @@ class VIEW3D_PT_tools_weightpaint_options(Panel, View3DPaintPanel): col.prop(mesh, "use_mirror_x") col.prop(mesh, "use_mirror_topology") + col.prop(wpaint, "input_samples") + self.unified_paint_settings(col, context) # Commented out because the Apply button isn't an operator yet, making these settings useless @@ -1014,6 +1018,8 @@ class VIEW3D_PT_tools_vertexpaint(Panel, View3DPaintPanel): col.prop(vpaint, "use_normal") col.prop(vpaint, "use_spray") + col.prop(vpaint, "input_samples") + self.unified_paint_settings(col, context) # Commented out because the Apply button isn't an operator yet, making these settings useless diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index c5ecaf8d49a..c1942144ece 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -4897,8 +4897,10 @@ static void link_recurs_seq(FileData *fd, ListBase *lb) static void direct_link_paint(FileData *fd, Paint **paint) { -/* TODO. is this needed */ + /* TODO. is this needed */ (*paint) = newdataadr(fd, (*paint)); + if (*paint && (*paint)->num_input_samples < 1) + (*paint)->num_input_samples = 1; } static void direct_link_scene(FileData *fd, Scene *sce) diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index a97563b258f..de149bf2806 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -52,7 +52,7 @@ struct wmOperatorType; /* paint_stroke.c */ typedef int (*StrokeGetLocation)(struct bContext *C, float location[3], float mouse[2]); -typedef int (*StrokeTestStart)(struct bContext *C, struct wmOperator *op, struct wmEvent *event); +typedef int (*StrokeTestStart)(struct bContext *C, struct wmOperator *op, const float mouse[2]); typedef void (*StrokeUpdateStep)(struct bContext *C, struct PaintStroke *stroke, struct PointerRNA *itemptr); typedef void (*StrokeDone)(const struct bContext *C, struct PaintStroke *stroke); diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index 9832bcf1528..987ab932fd6 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -60,6 +60,12 @@ #include #include +typedef struct PaintSample { + float mouse[2]; + + /* TODO: other input properties, e.g. tablet pressure */ +} PaintSample; + typedef struct PaintStroke { void *mode_data; void *smooth_stroke_cursor; @@ -70,6 +76,12 @@ typedef struct PaintStroke { bglMats mats; Brush *brush; + /* Paint stroke can use up to PAINT_MAX_INPUT_SAMPLES prior inputs + * to smooth the stroke */ + PaintSample samples[PAINT_MAX_INPUT_SAMPLES]; + int num_samples; + int cur_sample; + float last_mouse_position[2]; /* Set whether any stroke step has yet occurred @@ -182,10 +194,11 @@ static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, wmEvent *ev } /* Returns zero if no sculpt changes should be made, non-zero otherwise */ -static int paint_smooth_stroke(PaintStroke *stroke, float output[2], wmEvent *event) +static int paint_smooth_stroke(PaintStroke *stroke, float output[2], + const PaintSample *sample) { - output[0] = event->x; - output[1] = event->y; + output[0] = sample->mouse[0]; + output[1] = sample->mouse[1]; if ((stroke->brush->flag & BRUSH_SMOOTH_STROKE) && !ELEM4(stroke->brush->sculpt_tool, @@ -197,15 +210,16 @@ static int paint_smooth_stroke(PaintStroke *stroke, float output[2], wmEvent *ev !(stroke->brush->flag & BRUSH_RESTORE_MESH)) { float u = stroke->brush->smooth_stroke_factor, v = 1.0f - u; - float dx = stroke->last_mouse_position[0] - event->x, dy = stroke->last_mouse_position[1] - event->y; + float dx = stroke->last_mouse_position[0] - sample->mouse[0]; + float dy = stroke->last_mouse_position[1] - sample->mouse[1]; /* If the mouse is moving within the radius of the last move, * don't update the mouse position. This allows sharp turns. */ if (dx * dx + dy * dy < stroke->brush->smooth_stroke_radius * stroke->brush->smooth_stroke_radius) return 0; - output[0] = event->x * v + stroke->last_mouse_position[0] * u; - output[1] = event->y * v + stroke->last_mouse_position[1] * u; + output[0] = sample->mouse[0] * v + stroke->last_mouse_position[0] * u; + output[1] = sample->mouse[1] * v + stroke->last_mouse_position[1] * u; } return 1; @@ -343,12 +357,52 @@ struct wmKeyMap *paint_stroke_modal_keymap(struct wmKeyConfig *keyconf) return keymap; } +static void paint_stroke_add_sample(const Paint *paint, + PaintStroke *stroke, + float x, float y) +{ + PaintSample *sample = &stroke->samples[stroke->cur_sample]; + int max_samples = MIN2(PAINT_MAX_INPUT_SAMPLES, + MAX2(paint->num_input_samples, 1)); + + sample->mouse[0] = x; + sample->mouse[1] = y; + + stroke->cur_sample++; + if (stroke->cur_sample >= max_samples) + stroke->cur_sample = 0; + if (stroke->num_samples < max_samples) + stroke->num_samples++; +} + +static void paint_stroke_sample_average(const PaintStroke *stroke, + PaintSample *average) +{ + int i; + + memset(average, 0, sizeof(*average)); + + BLI_assert(stroke->num_samples > 0); + + for (i = 0; i < stroke->num_samples; i++) + add_v2_v2(average->mouse, stroke->samples[i].mouse); + + mul_v2_fl(average->mouse, 1.0f / stroke->num_samples); + + /*printf("avg=(%f, %f), num=%d\n", average->mouse[0], average->mouse[1], stroke->num_samples);*/ +} + int paint_stroke_modal(bContext *C, wmOperator *op, wmEvent *event) { + Paint *p = paint_get_active(CTX_data_scene(C)); PaintStroke *stroke = op->customdata; + PaintSample sample_average; float mouse[2]; int first = 0; + paint_stroke_add_sample(p, stroke, event->x, event->y); + paint_stroke_sample_average(stroke, &sample_average); + // 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 @@ -357,9 +411,8 @@ int paint_stroke_modal(bContext *C, wmOperator *op, wmEvent *event) return OPERATOR_PASS_THROUGH; if (!stroke->stroke_started) { - stroke->last_mouse_position[0] = event->x; - stroke->last_mouse_position[1] = event->y; - stroke->stroke_started = stroke->test_start(C, op, event); + copy_v2_v2(stroke->last_mouse_position, sample_average.mouse); + stroke->stroke_started = stroke->test_start(C, op, sample_average.mouse); if (stroke->stroke_started) { stroke->smooth_stroke_cursor = @@ -390,7 +443,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, wmEvent *event) (event->type == TIMER && (event->customdata == stroke->timer)) ) { if (stroke->stroke_started) { - if (paint_smooth_stroke(stroke, mouse, event)) { + if (paint_smooth_stroke(stroke, mouse, &sample_average)) { if (paint_space_stroke_enabled(stroke->brush)) { if (!paint_space_stroke(C, op, event, mouse)) { //ED_region_tag_redraw(ar); diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index 7a9ebe9aec4..513b5bbaa98 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -2125,7 +2125,7 @@ static char *wpaint_make_validmap(Object *ob) return vgroup_validmap; } -static int wpaint_stroke_test_start(bContext *C, wmOperator *op, wmEvent *UNUSED(event)) +static int wpaint_stroke_test_start(bContext *C, wmOperator *op, const float UNUSED(mouse[2])) { Scene *scene = CTX_data_scene(C); struct PaintStroke *stroke = op->customdata; @@ -2673,7 +2673,7 @@ static void vpaint_build_poly_facemap(struct VPaintData *vd, Mesh *me) } } -static int vpaint_stroke_test_start(bContext *C, struct wmOperator *op, wmEvent *UNUSED(event)) +static int vpaint_stroke_test_start(bContext *C, struct wmOperator *op, const float UNUSED(mouse[2])) { ToolSettings *ts = CTX_data_tool_settings(C); struct PaintStroke *stroke = op->customdata; diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index f5f8a790c55..7b8337ff957 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -3224,7 +3224,7 @@ static void sculpt_init_mirror_clipping(Object *ob, SculptSession *ss) } /* Initialize the stroke cache invariants from operator properties */ -static void sculpt_update_cache_invariants(bContext *C, Sculpt *sd, SculptSession *ss, wmOperator *op, wmEvent *event) +static void sculpt_update_cache_invariants(bContext *C, Sculpt *sd, SculptSession *ss, wmOperator *op, const float mouse[2]) { StrokeCache *cache = MEM_callocN(sizeof(StrokeCache), "stroke cache"); Brush *brush = paint_brush(&sd->paint); @@ -3247,14 +3247,7 @@ static void sculpt_update_cache_invariants(bContext *C, Sculpt *sd, SculptSessio sculpt_init_mirror_clipping(ob, ss); /* Initial mouse location */ - if (event) { - ss->cache->initial_mouse[0] = event->x; - ss->cache->initial_mouse[1] = event->y; - } - else { - ss->cache->initial_mouse[0] = 0; - ss->cache->initial_mouse[1] = 0; - } + copy_v2_v2(ss->cache->initial_mouse, mouse); mode = RNA_enum_get(op->ptr, "mode"); cache->invert = mode == BRUSH_STROKE_INVERT; @@ -3768,18 +3761,18 @@ static int over_mesh(bContext *C, struct wmOperator *UNUSED(op), float x, float } static int sculpt_stroke_test_start(bContext *C, struct wmOperator *op, - wmEvent *event) + const float mouse[2]) { /* Don't start the stroke until mouse goes over the mesh. * note: event will only be null when re-executing the saved stroke. */ - if (event == NULL || over_mesh(C, op, event->x, event->y)) { + if (over_mesh(C, op, mouse[0], mouse[1])) { Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; Sculpt *sd = CTX_data_tool_settings(C)->sculpt; ED_view3d_init_mats_rv3d(ob, CTX_wm_region_view3d(C)); - sculpt_update_cache_invariants(C, sd, ss, op, event); + sculpt_update_cache_invariants(C, sd, ss, op, mouse); sculpt_undo_push_begin(sculpt_tool_name(sd)); diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 83666068812..673156e3d7d 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -702,6 +702,8 @@ typedef struct TimeMarker { /* *************************************************************** */ /* Paint Mode/Tool Data */ +#define PAINT_MAX_INPUT_SAMPLES 64 + /* Paint Tool Base */ typedef struct Paint { struct Brush *brush; @@ -710,7 +712,14 @@ typedef struct Paint { void *paint_cursor; unsigned char paint_cursor_col[4]; + /* enum PaintFlags */ int flags; + + /* Paint stroke can use up to PAINT_MAX_INPUT_SAMPLES inputs to + * smooth the stroke */ + int num_input_samples; + + int pad; } Paint; /* ------------------------------------------- */ diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index ad40e71eb71..58d676555c1 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -234,6 +234,11 @@ static void rna_def_paint(BlenderRNA *brna) prop = RNA_def_property(srna, "show_low_resolution", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flags", PAINT_FAST_NAVIGATE); RNA_def_property_ui_text(prop, "Fast Navigate", "For multires, show low resolution while navigating the view"); + + prop = RNA_def_property(srna, "input_samples", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_sdna(prop, NULL, "num_input_samples"); + RNA_def_property_ui_range(prop, 1, PAINT_MAX_INPUT_SAMPLES, 0, 0); + RNA_def_property_ui_text(prop, "Input Samples", "Average multiple input samples together to smooth the brush stroke"); } static void rna_def_sculpt(BlenderRNA *brna)