Index: source/blender/editors/sculpt_paint/paint_intern.h =================================================================== --- source/blender/editors/sculpt_paint/paint_intern.h (revision 46566) +++ source/blender/editors/sculpt_paint/paint_intern.h (working copy) @@ -52,7 +52,7 @@ /* 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, 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); @@ -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 46566) +++ 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" @@ -61,6 +62,9 @@ #include typedef struct PaintStroke { + int one_time_init; + int smooth_init; + void *mode_data; void *smooth_stroke_cursor; wmTimer *timer; @@ -85,24 +89,76 @@ 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) { Brush *brush = paint_brush(paint_get_active(CTX_data_scene(C))); PaintStroke *stroke = customdata; - glColor4ubv(paint_get_active(CTX_data_scene(C))->paint_cursor_col); - glEnable(GL_LINE_SMOOTH); - glEnable(GL_BLEND); + if (stroke && brush && is_smooth_stroke(stroke)) { + ARegion *ar = CTX_wm_region(C); - if (stroke && brush && (brush->flag & BRUSH_SMOOTH_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); + sdrawline(x, y, (int)stroke->last_mouse_position[0] - ar->winrct.xmin, - (int)stroke->last_mouse_position[1] - ar->winrct.ymin); + (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 @@ -175,40 +231,57 @@ RNA_boolean_set(&itemptr, "pen_flip", pen_flip); RNA_float_set(&itemptr, "pressure", pressure); - stroke->last_mouse_position[0] = mouse[0]; - stroke->last_mouse_position[1] = mouse[1]; + stroke->update_step(C, stroke, &itemptr); - stroke->update_step(C, stroke, &itemptr); + copy_v2_v2(stroke->last_mouse_position, mouse); } +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], wmEvent *event) +static int smooth_stroke(PaintStroke *stroke, float output[2], wmEvent *event) { - output[0] = event->x; + output[0] = event->x; output[1] = event->y; - 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] - event->x, dy = stroke->last_mouse_position[1] - event->y; + if (is_smooth_stroke(stroke)) { + if (!(stroke->smooth_init)) { + copy_v2_v2(stroke->last_mouse_position, output); - /* 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) + stroke->smooth_init = 1; + return 0; + } + else { + float u = stroke->brush->smooth_stroke_factor; + float v = 1.0f - u; - output[0] = event->x * v + stroke->last_mouse_position[0] * u; - output[1] = event->y * v + stroke->last_mouse_position[1] * u; + float dx = stroke->last_mouse_position[0] - event->x; + float dy = stroke->last_mouse_position[1] - event->y; + + 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 @@ -218,37 +291,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) { - scale = (BKE_brush_size_get(scene, stroke->brush) * pressure * 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); } } } @@ -286,6 +360,35 @@ op->customdata = NULL; } +static void one_time_init( + const struct bContext *C, + struct PointerRNA *ptr, + struct PaintStroke *stroke, + struct wmEvent *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; @@ -306,14 +409,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 */ @@ -347,32 +442,51 @@ { PaintStroke *stroke = op->customdata; 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 */ - // 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) { - stroke->last_mouse_position[0] = event->x; - stroke->last_mouse_position[1] = event->y; - stroke->stroke_started = stroke->test_start(C, op, event); + /* Initialize */ - if (stroke->stroke_started) { - stroke->smooth_stroke_cursor = - WM_paint_cursor_activate(CTX_wm_manager(C), paint_poll, paint_draw_smooth_stroke, stroke); + one_time_init(C, op->ptr, stroke, event); - if (stroke->brush->flag & BRUSH_AIRBRUSH) - stroke->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, stroke->brush->rate); - } + airbrush = is_airbrush(stroke); - first = 1; - //ED_region_tag_redraw(ar); + first_event = (event->type == stroke->event_type && event->val == KM_PRESS); + + 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, event); + } + 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) @@ -381,42 +495,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, event)) { - 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/paint_vertex.c =================================================================== --- source/blender/editors/sculpt_paint/paint_vertex.c (revision 46566) +++ source/blender/editors/sculpt_paint/paint_vertex.c (working copy) @@ -2125,7 +2125,7 @@ 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, float UNUSED(mouse)[2]) { Scene *scene = CTX_data_scene(C); struct PaintStroke *stroke = op->customdata; @@ -2673,7 +2673,7 @@ } } -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, float UNUSED(mouse)[2]) { ToolSettings *ts = CTX_data_tool_settings(C); struct PaintStroke *stroke = op->customdata; Index: source/blender/editors/sculpt_paint/sculpt.c =================================================================== --- source/blender/editors/sculpt_paint/sculpt.c (revision 46566) +++ source/blender/editors/sculpt_paint/sculpt.c (working copy) @@ -3095,7 +3095,7 @@ } /* 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, float mouse[2]) { StrokeCache *cache = MEM_callocN(sizeof(StrokeCache), "stroke cache"); Brush *brush = paint_brush(&sd->paint); @@ -3118,14 +3118,9 @@ 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; + if (mouse) { + copy_v2_v2(cache->initial_mouse, mouse); } - else { - ss->cache->initial_mouse[0] = 0; - ss->cache->initial_mouse[1] = 0; - } mode = RNA_enum_get(op->ptr, "mode"); cache->invert = mode == BRUSH_STROKE_INVERT; @@ -3326,8 +3321,17 @@ * 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 */ @@ -3637,19 +3641,21 @@ return sculpt_stroke_get_location(C, co, mouse); } -static int sculpt_stroke_test_start(bContext *C, struct wmOperator *op, - wmEvent *event) +static int sculpt_stroke_test_start( + bContext *C, + struct wmOperator *op, + 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 (mouse == NULL || 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));