Index: source/blender/editors/sculpt_paint/paint_intern.h =================================================================== --- source/blender/editors/sculpt_paint/paint_intern.h (revision 51201) +++ source/blender/editors/sculpt_paint/paint_intern.h (working copy) @@ -61,8 +61,6 @@ StrokeUpdateStep update_step, StrokeDone done, int event_type); void paint_stroke_data_free(struct wmOperator *op); -int paint_space_stroke_enabled(struct Brush *br); - struct wmKeyMap *paint_stroke_modal_keymap(struct wmKeyConfig *keyconf); int paint_stroke_modal(struct bContext *C, struct wmOperator *op, struct wmEvent *event); int paint_stroke_exec(struct bContext *C, struct wmOperator *op); Index: source/blender/editors/sculpt_paint/paint_stroke.c =================================================================== --- source/blender/editors/sculpt_paint/paint_stroke.c (revision 51201) +++ source/blender/editors/sculpt_paint/paint_stroke.c (working copy) @@ -48,6 +48,7 @@ #include "WM_api.h" #include "WM_types.h" +#include "wm_draw.h" /* XXX: for wm_tag_redraw_overlay */ #include "BIF_gl.h" #include "BIF_glutil.h" @@ -67,6 +68,9 @@ } PaintSample; typedef struct PaintStroke { + int one_time_init; + int smooth_init; + void *mode_data; void *smooth_stroke_cursor; wmTimer *timer; @@ -97,25 +101,72 @@ StrokeDone done; } PaintStroke; + +/* Stroke Predicates + *****************************************************************************/ + +static int is_airbrush(const PaintStroke *stroke) +{ + return stroke->brush->flag & BRUSH_AIRBRUSH; +} + +static int is_grab_tool(const PaintStroke *stroke) +{ + return + ELEM4(stroke->brush->sculpt_tool, + SCULPT_TOOL_GRAB, + SCULPT_TOOL_THUMB, + SCULPT_TOOL_ROTATE, + SCULPT_TOOL_SNAKE_HOOK); +} + +static int is_anchor(const PaintStroke *stroke) +{ + return stroke->brush->flag & BRUSH_ANCHORED; +} + +static int is_drag(const PaintStroke *stroke) +{ + return stroke->brush->flag & BRUSH_RESTORE_MESH; +} + +static int is_smooth_stroke(const PaintStroke *stroke) +{ + return + (stroke->brush->flag & BRUSH_SMOOTH_STROKE) && + !is_grab_tool(stroke) && + !is_anchor(stroke) && + !is_drag(stroke); +} + +static int is_space(const PaintStroke *stroke) +{ + return + (stroke->brush->flag & BRUSH_SPACE) && + !is_grab_tool(stroke) && + !is_anchor(stroke) && + !is_drag(stroke); +} + + /*** Cursor ***/ -static void paint_draw_smooth_stroke(bContext *C, int x, int y, void *customdata) +static void paint_draw_smooth_stroke(bContext *C, int x, int y, void *customdata) { - Paint *paint = paint_get_active_from_context(C); - Brush *brush = paint_brush(paint); + Brush *brush = paint_brush(paint_get_active(CTX_data_scene(C))); PaintStroke *stroke = customdata; + if (stroke && brush && is_smooth_stroke(stroke)) { + ARegion *ar = CTX_wm_region(C); + glColor4ubv(paint_get_active(CTX_data_scene(C))->paint_cursor_col); + glEnable(GL_LINE_SMOOTH); + glEnable(GL_BLEND); - glColor4ubv(paint->paint_cursor_col); - glEnable(GL_LINE_SMOOTH); - glEnable(GL_BLEND); + sdrawline(x, y, + (int)stroke->last_mouse_position[0] - ar->winrct.xmin, + (int)stroke->last_mouse_position[1] - ar->winrct.ymin); - if (stroke && brush && (brush->flag & BRUSH_SMOOTH_STROKE)) { - ARegion *ar = CTX_wm_region(C); - sdrawline(x, y, (int)stroke->last_mouse_position[0] - ar->winrct.xmin, - (int)stroke->last_mouse_position[1] - ar->winrct.ymin); + glDisable(GL_BLEND); + glDisable(GL_LINE_SMOOTH); } - - glDisable(GL_BLEND); - glDisable(GL_LINE_SMOOTH); } /* if this is a tablet event, return tablet pressure and set *pen_flip @@ -188,41 +239,54 @@ RNA_boolean_set(&itemptr, "pen_flip", pen_flip); RNA_float_set(&itemptr, "pressure", pressure); + stroke->update_step(C, stroke, &itemptr); + copy_v2_v2(stroke->last_mouse_position, mouse_out); +} - stroke->update_step(C, stroke, &itemptr); +static void smooth(float a[2], float b[2], float v, float u) +{ + a[0]= v * a[0] + u * b[0]; + a[1]= v * a[1] + u * b[1]; } /* Returns zero if no sculpt changes should be made, non-zero otherwise */ -static int paint_smooth_stroke(PaintStroke *stroke, float output[2], - const PaintSample *sample) +static int smooth_stroke(PaintStroke *stroke, float output[2], + const PaintSample *sample) { output[0] = sample->mouse[0]; output[1] = sample->mouse[1]; - if ((stroke->brush->flag & BRUSH_SMOOTH_STROKE) && - !ELEM4(stroke->brush->sculpt_tool, - SCULPT_TOOL_GRAB, - SCULPT_TOOL_THUMB, - SCULPT_TOOL_ROTATE, - SCULPT_TOOL_SNAKE_HOOK) && - !(stroke->brush->flag & BRUSH_ANCHORED) && - !(stroke->brush->flag & BRUSH_RESTORE_MESH)) - { - float u = stroke->brush->smooth_stroke_factor, v = 1.0f - u; - 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) + if (is_smooth_stroke(stroke)) { + if (!(stroke->smooth_init)) { + copy_v2_v2(stroke->last_mouse_position, output); return 0; + } + else { + float u = stroke->brush->smooth_stroke_factor; + float v = 1.0f - u; + float dx = stroke->last_mouse_position[0] - sample->mouse[0]; + float dy = stroke->last_mouse_position[1] - sample->mouse[1]; - output[0] = sample->mouse[0] * v + stroke->last_mouse_position[0] * u; - output[1] = sample->mouse[1] * v + stroke->last_mouse_position[1] * u; + float dist_sq = dx*dx + dy*dy; + float radius_sq = stroke->brush->smooth_stroke_radius * stroke->brush->smooth_stroke_radius; + + /* If the mouse is moving within the radius of the last move, + * don't update the mouse position. This allows sharp turns. */ + + if (dist_sq < radius_sq) { + return 0; + } + else { + smooth(output, stroke->last_mouse_position, v, u); + + return 1; + } + } } - - return 1; + else { + return 1; + } } /* For brushes with stroke spacing enabled, moves mouse in steps @@ -232,40 +296,38 @@ PaintStroke *stroke = op->customdata; int cnt = 0; - if (paint_space_stroke_enabled(stroke->brush)) { - float mouse[2]; - float vec[2]; - float length, scale; + float mouse[2]; + float vec[2]; + float length, scale; - copy_v2_v2(mouse, stroke->last_mouse_position); - sub_v2_v2v2(vec, final_mouse, mouse); + copy_v2_v2(mouse, stroke->last_mouse_position); + sub_v2_v2v2(vec, final_mouse, mouse); - length = len_v2(vec); + length = len_v2(vec); - if (length > FLT_EPSILON) { - const Scene *scene = CTX_data_scene(C); - int steps; - int i; - float pressure = 1.0f; + if (length > FLT_EPSILON) { + const Scene *scene = CTX_data_scene(C); + int steps; + int i; + float pressure; - /* 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)) - pressure = event_tablet_data(event, NULL); - - if (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 = maxf(1.0f, BKE_brush_size_get(scene, stroke->brush) * pressure); - scale = (size_clamp * stroke->brush->spacing / 50.0f) / length; - if (scale > FLT_EPSILON) { - mul_v2_fl(vec, scale); + if (BKE_brush_use_size_pressure(scene, stroke->brush)) { + pressure = event_tablet_data(event, NULL); + } + else { + pressure = 1; + } - steps = (int)(1.0f / scale); + if (pressure > FLT_EPSILON) { + scale = (BKE_brush_size_get(scene, stroke->brush) * pressure * stroke->brush->spacing / 50.0f) / length; + if (scale > FLT_EPSILON) { + mul_v2_fl(vec, scale); - for (i = 0; i < steps; ++i, ++cnt) { - add_v2_v2(mouse, vec); - paint_brush_stroke_add_step(C, op, event, mouse); - } + steps = (int)(1.0f / scale); + + for (i = 0; i < steps; ++i, ++cnt) { + add_v2_v2(mouse, vec); + paint_brush_stroke_add_step(C, op, event, mouse); } } } @@ -303,6 +365,35 @@ op->customdata = NULL; } +static void one_time_init( + const struct bContext *C, + struct PointerRNA *UNUSED(ptr), + struct PaintStroke *stroke, + struct wmEvent *UNUSED(evt)) +{ + if (!(stroke->one_time_init)) { + if (is_smooth_stroke(stroke)) { + stroke->smooth_stroke_cursor = + WM_paint_cursor_activate( + CTX_wm_manager(C), + paint_poll, + paint_draw_smooth_stroke, + stroke); + } + + if (is_airbrush(stroke)) { + stroke->timer = + WM_event_add_timer( + CTX_wm_manager(C), + CTX_wm_window(C), + TIMER, + stroke->brush->rate); + } + + stroke->one_time_init= 1; + } +} + static void stroke_done(struct bContext *C, struct wmOperator *op) { struct PaintStroke *stroke = op->customdata; @@ -323,14 +414,6 @@ paint_stroke_data_free(op); } -/* Returns zero if the stroke dots should not be spaced, non-zero otherwise */ -int paint_space_stroke_enabled(Brush *br) -{ - return (br->flag & BRUSH_SPACE) && - !(br->flag & BRUSH_ANCHORED) && - !ELEM4(br->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_SNAKE_HOOK); -} - #define PAINT_STROKE_MODAL_CANCEL 1 /* called in paint_ops.c, on each regeneration of keymaps */ @@ -401,34 +484,53 @@ PaintStroke *stroke = op->customdata; PaintSample sample_average; float mouse[2]; - int first = 0; + int airbrush; /* using either a timer or mouse events but not both */ + int event_ok; /* is event type matched to the airbrush mode */ + int input_ok; /* the conditions required by the input mode have been met */ + int first_event; /* true only during first call where test_start passes */ 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 - // post-NDOF_MOTION MOUSEMOVE - if (event->type == NDOF_MOTION) + /* 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 + post-NDOF_MOTION MOUSEMOVE */ + if (event->type == NDOF_MOTION) { return OPERATOR_PASS_THROUGH; + } - if (!stroke->stroke_started) { - 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 = - WM_paint_cursor_activate(CTX_wm_manager(C), paint_poll, paint_draw_smooth_stroke, stroke); + /* Initialize */ + one_time_init(C, op->ptr, stroke, event); + airbrush = is_airbrush(stroke); - if (stroke->brush->flag & BRUSH_AIRBRUSH) - stroke->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, stroke->brush->rate); - } + first_event = (event->type == stroke->event_type && event->val == KM_PRESS); - first = 1; - //ED_region_tag_redraw(ar); + if (first_event) { + event_ok = 1; } + else if (airbrush) { + event_ok = (event->type == TIMER && event->customdata == stroke->timer); + } + else { + event_ok = ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE); + } + if (event_ok) { + input_ok = smooth_stroke(stroke, mouse, &sample_average); + } + else { + /* input cannot be ok if the event was not. */ + input_ok = 0; + } + if (input_ok && !stroke->stroke_started) { + copy_v2_v2(stroke->last_mouse_position, mouse); + + stroke->stroke_started = stroke->test_start(C, op, mouse); + } + + /* Cancel */ if (event->type == EVT_MODAL_MAP && event->val == PAINT_STROKE_MODAL_CANCEL) { if (op->type->cancel) @@ -437,40 +539,32 @@ return paint_stroke_cancel(C, op); } + + /* Finish */ if (event->type == stroke->event_type && event->val == KM_RELEASE) { stroke_done(C, op); return OPERATOR_FINISHED; } - else if ((first) || - (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) || - (event->type == TIMER && (event->customdata == stroke->timer)) ) - { - if (stroke->stroke_started) { - 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); - } - } - else { - paint_brush_stroke_add_step(C, op, event, mouse); - } - } - else { - ; //ED_region_tag_redraw(ar); - } + + + /* Steps */ + if (input_ok && stroke->stroke_started) { + if (!first_event && is_space(stroke)) { + paint_space_stroke(C, op, event, mouse); } + else { + paint_brush_stroke_add_step(C, op, event, mouse); + } } - /* we want the stroke to have the first daub at the start location - * instead of waiting till we have moved the space distance */ - if (first && - stroke->stroke_started && - paint_space_stroke_enabled(stroke->brush) && - !(stroke->brush->flag & BRUSH_ANCHORED) && - !(stroke->brush->flag & BRUSH_SMOOTH_STROKE)) + /* XXX: cursor is not refreshing unless the update_step callback + explicitly does something to cause a refresh, but the cursor should + always be refreshed so that it is redrawn during a stroke. it does + not seem like this should have to be explicit, and this function + is not part of the public API of the window manager */ { - paint_brush_stroke_add_step(C, op, event, mouse); + struct wmWindow *win = CTX_wm_window(C); + wm_tag_redraw_overlay(win, stroke->vc.ar); } return OPERATOR_RUNNING_MODAL; Index: source/blender/editors/sculpt_paint/sculpt.c =================================================================== --- source/blender/editors/sculpt_paint/sculpt.c (revision 51201) +++ source/blender/editors/sculpt_paint/sculpt.c (working copy) @@ -71,6 +71,7 @@ #include "WM_api.h" #include "WM_types.h" +#include "wm_draw.h" /* XXX: for wm_tag_redraw_overlay */ #include "ED_sculpt.h" #include "ED_screen.h" @@ -3557,8 +3558,18 @@ * brush coord/pressure/etc. * It's more an events design issue, which doesn't split coordinate/pressure/angle * changing events. We should avoid this after events system re-design */ - if (paint_space_stroke_enabled(brush) || cache->first_time) + + /* XXX: I'm going to re-evaluate this situation soon -- jwilkins */ + if (cache->first_time || + ((brush->flag & BRUSH_SPACE) && + !ELEM4(brush->sculpt_tool, + SCULPT_TOOL_GRAB, + SCULPT_TOOL_THUMB, + SCULPT_TOOL_ROTATE, + SCULPT_TOOL_SNAKE_HOOK))) + { cache->pressure = RNA_float_get(ptr, "pressure"); + } /* Truly temporary data that isn't stored in properties */