Index: source/blender/editors/sculpt_paint/paint_intern.h =================================================================== --- source/blender/editors/sculpt_paint/paint_intern.h (revision 52534) +++ 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 52534) +++ source/blender/editors/sculpt_paint/paint_stroke.c (working copy) @@ -67,6 +67,9 @@ } PaintSample; typedef struct PaintStroke { + int one_time_init; + int smooth_init; + void *mode_data; void *smooth_stroke_cursor; wmTimer *timer; @@ -97,25 +100,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 +238,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 +295,46 @@ 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 = max_ff(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) { + /* brushes can have a minimum size of 1.0 but with pressure it + * can be smaller then a pixel causing very high number of steps and + * hanging blender [#32381] */ + const float size_clamp = max_ff( + 1.0f, + BKE_brush_size_get(scene, stroke->brush) * pressure); - for (i = 0; i < steps; ++i, ++cnt) { - add_v2_v2(mouse, vec); - paint_brush_stroke_add_step(C, op, event, mouse); - } + scale = (size_clamp * stroke->brush->spacing / 50.0f) / length; + + if (scale > FLT_EPSILON) { + mul_v2_fl(vec, scale); + + 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 +372,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 +421,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,7 +491,10 @@ 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 event */ paint_stroke_add_sample(p, stroke, event->x, event->y); paint_stroke_sample_average(stroke, &sample_average); @@ -410,25 +503,42 @@ * 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) + 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,42 +547,26 @@ 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)) - { - paint_brush_stroke_add_step(C, op, event, mouse); - } - + WM_tag_redraw_overlay(CTX_wm_window(C), stroke->vc.ar); + return OPERATOR_RUNNING_MODAL; } Index: source/blender/editors/sculpt_paint/sculpt.c =================================================================== --- source/blender/editors/sculpt_paint/sculpt.c (revision 52534) +++ source/blender/editors/sculpt_paint/sculpt.c (working copy) @@ -3558,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) + + /* 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 */ Index: source/blender/windowmanager/intern/wm_draw.c =================================================================== --- source/blender/windowmanager/intern/wm_draw.c (revision 52534) +++ source/blender/windowmanager/intern/wm_draw.c (working copy) @@ -734,7 +790,7 @@ return win->drawmethod; } -void wm_tag_redraw_overlay(wmWindow *win, ARegion *ar) +void WM_tag_redraw_overlay(wmWindow *win, ARegion *ar) { /* for draw triple gestures, paint cursors don't need region redraw */ if (ar && win) { Index: source/blender/windowmanager/intern/wm_gesture.c =================================================================== --- source/blender/windowmanager/intern/wm_gesture.c (revision 52534) +++ source/blender/windowmanager/intern/wm_gesture.c (working copy) @@ -359,5 +359,5 @@ if (screen) screen->do_draw_gesture = TRUE; - wm_tag_redraw_overlay(win, ar); + WM_tag_redraw_overlay(win, ar); } Index: source/blender/windowmanager/WM_api.h =================================================================== --- source/blender/windowmanager/WM_api.h (revision 52534) +++ source/blender/windowmanager/WM_api.h (working copy) @@ -375,8 +375,11 @@ /* Draw (for screenshot) */ void WM_redraw_windows(struct bContext *C); -void WM_main_playanim(int argc, const char **argv); +void WM_main_playanim(int argc, const char **argv); + /* ask for just the overlay to be redrawn */ +void WM_tag_redraw_overlay(struct wmWindow *win, struct ARegion *ar); + /* debugging only, convenience function to write on crash */ int write_crash_blend(void); @@ -385,4 +388,3 @@ #endif #endif /* __WM_API_H__ */ - Index: source/blender/windowmanager/wm_draw.h =================================================================== --- source/blender/windowmanager/wm_draw.h (revision 52534) +++ source/blender/windowmanager/wm_draw.h (working copy) @@ -41,7 +41,5 @@ void wm_draw_window_clear (struct wmWindow *win); void wm_draw_region_clear (struct wmWindow *win, struct ARegion *ar); -void wm_tag_redraw_overlay (struct wmWindow *win, struct ARegion *ar); - #endif /* __WM_DRAW_H__ */