From 8c81b3fb8b9c857626c037fe0c95c5d48a3ca20f Mon Sep 17 00:00:00 2001 From: Pablo Dobarro Date: Sat, 26 Sep 2020 21:59:30 +0200 Subject: [PATCH] Sculpt: Line gestures and Mask Line tool This adds support for line gesture to SculptGestureContext and implements a Mask Line tool, which affects everything to the right of a plane defined by the straightline gesture. For this to work, a new WM_gesture_straightline_oneshot_modal is needed which only runs exec when the gesture is over. Added as experimental as it does not have icon. Reviewed By: Severin Differential Revision: https://developer.blender.org/D8722 --- .../keyconfig/keymap_data/blender_default.py | 12 ++ .../startup/bl_ui/space_toolsystem_toolbar.py | 24 +++- .../editors/sculpt_paint/paint_intern.h | 1 + .../blender/editors/sculpt_paint/paint_mask.c | 117 +++++++++++++++++- .../blender/editors/sculpt_paint/paint_ops.c | 1 + source/blender/windowmanager/WM_api.h | 4 + .../windowmanager/intern/wm_gesture_ops.c | 72 +++++++++++ .../windowmanager/intern/wm_operators.c | 1 + 8 files changed, 226 insertions(+), 6 deletions(-) diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index fdd654009a7..f3e4c82090e 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -6388,6 +6388,17 @@ def km_3d_view_tool_sculpt_lasso_trim(params): ]}, ) +def km_3d_view_tool_sculpt_line_mask(params): + return ( + "3D View Tool: Sculpt, Line Mask", + {"space_type": 'VIEW_3D', "region_type": 'WINDOW'}, + {"items": [ + ("paint.mask_line_gesture", {"type": params.tool_tweak, "value": 'ANY'}, + {"properties": [("value", 1.0)]}), + ("paint.mask_line_gesture", {"type": params.tool_tweak, "value": 'ANY', "ctrl": True}, + {"properties": [("value", 0.0)]}), + ]}, + ) def km_3d_view_tool_sculpt_mesh_filter(params): return ( @@ -6993,6 +7004,7 @@ def generate_keymaps(params=None): km_3d_view_tool_sculpt_lasso_face_set(params), km_3d_view_tool_sculpt_box_trim(params), km_3d_view_tool_sculpt_lasso_trim(params), + km_3d_view_tool_sculpt_line_mask(params), km_3d_view_tool_sculpt_mesh_filter(params), km_3d_view_tool_sculpt_cloth_filter(params), km_3d_view_tool_sculpt_color_filter(params), diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 75dfd60b1d4..3f7a3604741 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -1258,6 +1258,21 @@ class _defs_sculpt: draw_settings=draw_settings, ) + @ToolDef.from_fn + def mask_line(): + def draw_settings(_context, layout, tool): + props = tool.operator_properties("paint.mask_line_gesture") + layout.prop(props, "use_front_faces_only", expand=False) + + return dict( + idname="builtin.line_mask", + label="Line Mask", + icon="ops.sculpt.line_mask", + widget=None, + keymap=(), + draw_settings=draw_settings, + ) + @ToolDef.from_fn def face_set_box(): def draw_settings(_context, layout, tool): @@ -1273,6 +1288,7 @@ class _defs_sculpt: draw_settings=draw_settings, ) + @ToolDef.from_fn def face_set_lasso(): def draw_settings(_context, layout, tool): @@ -1308,7 +1324,6 @@ class _defs_sculpt: keymap=(), ) - @ToolDef.from_fn def mesh_filter(): def draw_settings(_context, layout, tool): @@ -2652,6 +2667,13 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel): _defs_sculpt.mask_lasso, ), _defs_sculpt.hide_border, + lambda context: ( + (_defs_sculpt.mask_line,) + if context is None or ( + context.preferences.view.show_developer_ui and + context.preferences.experimental.use_tools_missing_icons) + else () + ), lambda context: ( (_defs_sculpt.face_set_box,) if context is None or ( diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index a1894e1b4c6..175d98ba9aa 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -341,6 +341,7 @@ typedef enum { void PAINT_OT_mask_flood_fill(struct wmOperatorType *ot); void PAINT_OT_mask_lasso_gesture(struct wmOperatorType *ot); void PAINT_OT_mask_box_gesture(struct wmOperatorType *ot); +void PAINT_OT_mask_line_gesture(struct wmOperatorType *ot); /* paint_curve.c */ void PAINTCURVE_OT_new(struct wmOperatorType *ot); diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index fc17af8d2cb..0f2ebdd28aa 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -234,6 +234,7 @@ void PAINT_OT_mask_flood_fill(struct wmOperatorType *ot) typedef enum eSculptGestureShapeType { SCULPT_GESTURE_SHAPE_BOX, SCULPT_GESTURE_SHAPE_LASSO, + SCULPT_GESTURE_SHAPE_LINE, } eMaskGesturesShapeType; typedef struct LassoGestureData { @@ -246,6 +247,11 @@ typedef struct LassoGestureData { BLI_bitmap *mask_px; } LassoGestureData; +typedef struct LineGestureData { + float true_plane[4]; + float plane[4]; +} LineGestureData; + struct SculptGestureOperation; typedef struct SculptGestureContext { @@ -280,6 +286,9 @@ typedef struct SculptGestureContext { /* Lasso Gesture. */ LassoGestureData lasso; + /* Line Gesture. */ + LineGestureData line; + /* Task Callback Data. */ PBVHNode **nodes; int totnode; @@ -430,6 +439,44 @@ static SculptGestureContext *sculpt_gesture_init_from_box(bContext *C, wmOperato return sgcontext; } +static SculptGestureContext *sculpt_gesture_init_from_line(bContext *C, wmOperator *op) +{ + SculptGestureContext *sgcontext = MEM_callocN(sizeof(SculptGestureContext), + "sculpt gesture context line"); + sgcontext->shape_type = SCULPT_GESTURE_SHAPE_LINE; + + sculpt_gesture_context_init_common(C, op, sgcontext); + + float line_points[2][2]; + line_points[0][0] = RNA_int_get(op->ptr, "xstart"); + line_points[0][1] = RNA_int_get(op->ptr, "ystart"); + line_points[1][0] = RNA_int_get(op->ptr, "xend"); + line_points[1][1] = RNA_int_get(op->ptr, "yend"); + + float depth_point[3]; + float plane_points[3][3]; + + /* Calculate a triangle in the line's plane. */ + add_v3_v3v3(depth_point, sgcontext->true_view_origin, sgcontext->true_view_normal); + ED_view3d_win_to_3d( + sgcontext->vc.v3d, sgcontext->vc.region, depth_point, line_points[0], plane_points[0]); + + madd_v3_v3v3fl(depth_point, sgcontext->true_view_origin, sgcontext->true_view_normal, 10.0f); + ED_view3d_win_to_3d( + sgcontext->vc.v3d, sgcontext->vc.region, depth_point, line_points[0], plane_points[1]); + ED_view3d_win_to_3d( + sgcontext->vc.v3d, sgcontext->vc.region, depth_point, line_points[1], plane_points[2]); + + /* Calculate final line plane and normal using the triangle. */ + float normal[3]; + normal_tri_v3(normal, plane_points[0], plane_points[1], plane_points[2]); + if (!sgcontext->vc.rv3d->is_persp) { + mul_v3_fl(normal, -1.0f); + } + plane_from_point_normal_v3(sgcontext->line.true_plane, plane_points[0], normal); + return sgcontext; +} + static void sculpt_gesture_context_free(SculptGestureContext *sgcontext) { MEM_SAFE_FREE(sgcontext->lasso.mask_px); @@ -470,12 +517,28 @@ static void sculpt_gesture_flip_for_symmetry_pass(SculptGestureContext *sgcontex for (int j = 0; j < 4; j++) { flip_plane(sgcontext->clip_planes[j], sgcontext->true_clip_planes[j], symmpass); } + negate_m4(sgcontext->clip_planes); + flip_v3_v3(sgcontext->view_normal, sgcontext->true_view_normal, symmpass); flip_v3_v3(sgcontext->view_origin, sgcontext->true_view_origin, symmpass); + flip_plane(sgcontext->line.plane, sgcontext->line.true_plane, symmpass); } -static void sculpt_gesture_update_effected_nodes(SculptGestureContext *sgcontext) +static void sculpt_gesture_update_effected_nodes_by_line_plane(SculptGestureContext *sgcontext) +{ + SculptSession *ss = sgcontext->ss; + float clip_planes[1][4]; + copy_v4_v4(clip_planes[0], sgcontext->line.plane); + PBVHFrustumPlanes frustum = {.planes = clip_planes, .num_planes = 1}; + BKE_pbvh_search_gather(ss->pbvh, + BKE_pbvh_node_frustum_contain_AABB, + &frustum, + &sgcontext->nodes, + &sgcontext->totnode); +} + +static void sculpt_gesture_update_effected_nodes_by_clip_planes(SculptGestureContext *sgcontext) { SculptSession *ss = sgcontext->ss; float clip_planes[4][4]; @@ -489,6 +552,19 @@ static void sculpt_gesture_update_effected_nodes(SculptGestureContext *sgcontext &sgcontext->totnode); } +static void sculpt_gesture_update_effected_nodes(SculptGestureContext *sgcontext) +{ + switch (sgcontext->shape_type) { + case SCULPT_GESTURE_SHAPE_BOX: + case SCULPT_GESTURE_SHAPE_LASSO: + sculpt_gesture_update_effected_nodes_by_clip_planes(sgcontext); + break; + case SCULPT_GESTURE_SHAPE_LINE: + sculpt_gesture_update_effected_nodes_by_line_plane(sgcontext); + break; + } +} + static bool sculpt_gesture_is_effected_lasso(SculptGestureContext *sgcontext, const float co[3]) { float scr_co_f[2]; @@ -532,6 +608,8 @@ static bool sculpt_gesture_is_vertex_effected(SculptGestureContext *sgcontext, P return isect_point_planes_v3(sgcontext->clip_planes, 4, vd->co); case SCULPT_GESTURE_SHAPE_LASSO: return sculpt_gesture_is_effected_lasso(sgcontext, vd->co); + case SCULPT_GESTURE_SHAPE_LINE: + return plane_point_side_v3(sgcontext->line.plane, vd->co) > 0.0f; } return false; } @@ -1117,6 +1195,18 @@ static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static int paint_mask_gesture_line_exec(bContext *C, wmOperator *op) +{ + SculptGestureContext *sgcontext = sculpt_gesture_init_from_line(C, op); + if (!sgcontext) { + return OPERATOR_CANCELLED; + } + sculpt_gesture_init_mask_properties(sgcontext, op); + sculpt_gesture_apply(C, sgcontext); + sculpt_gesture_context_free(sgcontext); + return OPERATOR_FINISHED; +} + static int face_set_gesture_box_exec(bContext *C, wmOperator *op) { SculptGestureContext *sgcontext = sculpt_gesture_init_from_box(C, op); @@ -1222,6 +1312,27 @@ void PAINT_OT_mask_box_gesture(wmOperatorType *ot) paint_mask_gesture_operator_properties(ot); } +void PAINT_OT_mask_line_gesture(wmOperatorType *ot) +{ + ot->name = "Mask Line Gesture"; + ot->idname = "PAINT_OT_mask_line_gesture"; + ot->description = "Add mask to the right of a line as you move the brush"; + + ot->invoke = WM_gesture_straightline_invoke; + ot->modal = WM_gesture_straightline_oneshot_modal; + ot->exec = paint_mask_gesture_line_exec; + + ot->poll = SCULPT_mode_poll; + + ot->flag = OPTYPE_REGISTER; + + /* Properties. */ + WM_operator_properties_gesture_straightline(ot, WM_CURSOR_EDIT); + sculpt_gesture_operator_properties(ot); + + paint_mask_gesture_operator_properties(ot); +} + void SCULPT_OT_face_set_lasso_gesture(wmOperatorType *ot) { ot->name = "Face Set Lasso Gesture"; @@ -1232,10 +1343,6 @@ void SCULPT_OT_face_set_lasso_gesture(wmOperatorType *ot) ot->modal = WM_gesture_lasso_modal; ot->exec = face_set_gesture_lasso_exec; - ot->poll = SCULPT_mode_poll; - - ot->flag = OPTYPE_REGISTER; - /* Properties. */ WM_operator_properties_gesture_lasso(ot); sculpt_gesture_operator_properties(ot); diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index 43ff03ea7e2..e239b9619ec 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -1357,6 +1357,7 @@ void ED_operatortypes_paint(void) WM_operatortype_append(PAINT_OT_mask_flood_fill); WM_operatortype_append(PAINT_OT_mask_lasso_gesture); WM_operatortype_append(PAINT_OT_mask_box_gesture); + WM_operatortype_append(PAINT_OT_mask_line_gesture); } void ED_keymap_paint(wmKeyConfig *keyconf) diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 80f330d91ca..8fba1ed342e 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -632,12 +632,16 @@ void WM_gesture_lasso_cancel(struct bContext *C, struct wmOperator *op); const int (*WM_gesture_lasso_path_to_array(struct bContext *C, struct wmOperator *op, int *mcoords_len))[2]; + int WM_gesture_straightline_invoke(struct bContext *C, struct wmOperator *op, const struct wmEvent *event); int WM_gesture_straightline_modal(struct bContext *C, struct wmOperator *op, const struct wmEvent *event); +int WM_gesture_straightline_oneshot_modal(struct bContext *C, + struct wmOperator *op, + const struct wmEvent *event); void WM_gesture_straightline_cancel(struct bContext *C, struct wmOperator *op); /* Gesture manager API */ diff --git a/source/blender/windowmanager/intern/wm_gesture_ops.c b/source/blender/windowmanager/intern/wm_gesture_ops.c index a38cbb920c4..25de76d4428 100644 --- a/source/blender/windowmanager/intern/wm_gesture_ops.c +++ b/source/blender/windowmanager/intern/wm_gesture_ops.c @@ -792,6 +792,16 @@ void WM_OT_lasso_gesture(wmOperatorType *ot) /* -------------------------------------------------------------------- */ /** \name Straight Line Gesture + * + * Gesture defined by the start and end points of a line that is created between the posistion of + * the initial event and the position of the current event. + * + * Straight Line Gesture has two modal callbacks depending on the tool that is being implemented: a + * regular modal callback intended to update the data during the execution of the gesture and a + * oneshot callback that only updates the data once when the gesture finishes. + * + * It stores 4 values (xstart, ystart, xend, yend). + * * \{ */ static bool gesture_straightline_apply(bContext *C, wmOperator *op) @@ -841,6 +851,11 @@ int WM_gesture_straightline_invoke(bContext *C, wmOperator *op, const wmEvent *e return OPERATOR_RUNNING_MODAL; } +/** + * This modal callback calls exec once per mouse move event while the gesture is active with the + * updated line start and end values, so it can be used for tools that have a real time preview + * (like a gradient updating in real time over the mesh). + */ int WM_gesture_straightline_modal(bContext *C, wmOperator *op, const wmEvent *event) { wmGesture *gesture = op->customdata; @@ -886,6 +901,63 @@ int WM_gesture_straightline_modal(bContext *C, wmOperator *op, const wmEvent *ev return OPERATOR_RUNNING_MODAL; } +/** + * This modal oneshot callback only calls exec once after the gesture finishes without any updates + * during the gesture execution. Should be used for operations that are intended to be applied once + * without real time preview (like a trimming tool that only applies the bisect operation once + * after finishing the gesture as the bisect operation is too heavy to be computed in real time for + * a preview). + */ +int WM_gesture_straightline_oneshot_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + wmGesture *gesture = op->customdata; + wmWindow *win = CTX_wm_window(C); + rcti *rect = gesture->customdata; + + if (event->type == MOUSEMOVE) { + if (gesture->is_active == false) { + rect->xmin = rect->xmax = event->x - gesture->winrct.xmin; + rect->ymin = rect->ymax = event->y - gesture->winrct.ymin; + } + else { + rect->xmax = event->x - gesture->winrct.xmin; + rect->ymax = event->y - gesture->winrct.ymin; + } + + wm_gesture_tag_redraw(win); + } + else if (event->type == EVT_MODAL_MAP) { + switch (event->val) { + case GESTURE_MODAL_BEGIN: + if (gesture->is_active == false) { + gesture->is_active = true; + wm_gesture_tag_redraw(win); + } + break; + case GESTURE_MODAL_SELECT: + case GESTURE_MODAL_DESELECT: + case GESTURE_MODAL_IN: + case GESTURE_MODAL_OUT: + if (gesture->wait_for_input) { + gesture->modal_state = event->val; + } + if (gesture_straightline_apply(C, op)) { + gesture_modal_end(C, op); + return OPERATOR_FINISHED; + } + gesture_modal_end(C, op); + return OPERATOR_CANCELLED; + + case GESTURE_MODAL_CANCEL: + gesture_modal_end(C, op); + return OPERATOR_CANCELLED; + } + } + + gesture->is_active_prev = gesture->is_active; + return OPERATOR_RUNNING_MODAL; +} + void WM_gesture_straightline_cancel(bContext *C, wmOperator *op) { gesture_modal_end(C, op); diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 2b01c8fe90e..0e4bb1cc33a 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -3904,6 +3904,7 @@ static void gesture_box_modal_keymap(wmKeyConfig *keyconf) WM_modalkeymap_assign(keymap, "PAINT_OT_mask_box_gesture"); WM_modalkeymap_assign(keymap, "SCULPT_OT_face_set_box_gesture"); WM_modalkeymap_assign(keymap, "SCULPT_OT_trim_box_gesture"); + WM_modalkeymap_assign(keymap, "PAINT_OT_mask_line_gesture"); WM_modalkeymap_assign(keymap, "VIEW2D_OT_zoom_border"); WM_modalkeymap_assign(keymap, "VIEW3D_OT_clip_border"); WM_modalkeymap_assign(keymap, "VIEW3D_OT_render_border");