GPv3: Implementation of sculpt mode tools

This implements all the sculpt tools in Grease Pencil 3.

UI changes in the 3D view header and keymap entries for sculpt mode are
still minimal, more entries should be added once the relevant operators
are supported.

A set of utility functions and a shared base class
`GreasePencilStrokeOperationCommon` for sculpt operations has been added
to make individual operations less verbose.
The `GreasePencilStrokeParams` struct bundles common arguments to reduce
the amount of boilerplate code. The `foreach_editable_drawing` utility
function takes care of setting up the parameters and finding the right
drawings, so the tool only has to modify the data. Common features like
tracking mouse movement and inverting brush influence are handled by the
common base class.

Most operations are then relatively simple, with the exception of the
Grab and Clone operations.
- __Grab__ stores a stroke mask and weights on initialization of the
  stroke, rather than working with the usual selection mask.
- __Clone__ needs access to the clipboard, which requires exposing the
  clipboard in the editor API.

Pull Request: https://projects.blender.org/blender/blender/pulls/120508
This commit is contained in:
Lukas Tönne
2024-04-25 20:20:27 +02:00
parent fea1d1d71f
commit 91f1f3fc06
24 changed files with 1707 additions and 112 deletions

View File

@@ -474,9 +474,14 @@ class _draw_tool_settings_context_mode:
)
# direction
if not capabilities.has_direction:
if brush.gpencil_sculpt_tool in {'THICKNESS', 'STRENGTH', 'PINCH', 'TWIST'}:
layout.row().prop(brush, "direction", expand=True, text="")
# Brush falloff
layout.popover("VIEW3D_PT_tools_brush_falloff")
# Active layer only switch
layout.prop(brush.gpencil_settings, "use_active_layer_only")
return True
@staticmethod
@@ -894,11 +899,11 @@ class VIEW3D_HT_header(Header):
row = layout.row(align=True)
row.prop(tool_settings, "use_gpencil_draw_additive", text="", icon='FREEZE')
if object_mode in {'PAINT_GPENCIL', 'EDIT', 'WEIGHT_GPENCIL'}:
if object_mode in {'PAINT_GPENCIL', 'EDIT', 'SCULPT_GPENCIL', 'WEIGHT_GPENCIL'}:
row = layout.row(align=True)
row.prop(tool_settings, "use_grease_pencil_multi_frame_editing", text="")
if object_mode in {'EDIT', 'WEIGHT_GPENCIL'}:
if object_mode in {'EDIT', 'SCULPT_GPENCIL', 'WEIGHT_GPENCIL'}:
sub = row.row(align=True)
sub.enabled = tool_settings.use_grease_pencil_multi_frame_editing
sub.popover(
@@ -975,6 +980,35 @@ class VIEW3D_HT_header(Header):
text="Multiframe",
)
# Grease Pencil
if obj and obj.type == 'GREASEPENCIL':
if object_mode == 'PAINT_GREASE_PENCIL':
row = layout.row()
sub = row.row(align=True)
sub.prop(tool_settings, "use_gpencil_draw_onback", text="", icon='MOD_OPACITY')
sub.separator(factor=0.4)
sub.prop(tool_settings, "use_gpencil_automerge_strokes", text="")
sub.separator(factor=0.4)
sub.prop(tool_settings, "use_gpencil_weight_data_add", text="", icon='WPAINT_HLT')
sub.separator(factor=0.4)
sub.prop(tool_settings, "use_gpencil_draw_additive", text="", icon='FREEZE')
# Select mode for Editing
if object_mode == 'EDIT':
row = layout.row(align=True)
row.prop_enum(tool_settings, "gpencil_selectmode_edit", text="", value='POINT')
row.prop_enum(tool_settings, "gpencil_selectmode_edit", text="", value='STROKE')
subrow = row.row(align=True)
subrow.prop_enum(tool_settings, "gpencil_selectmode_edit", text="", value='SEGMENT')
# Select mode for Sculpt
if object_mode == 'SCULPT_GPENCIL':
row = layout.row(align=True)
row.prop(tool_settings, "use_gpencil_select_mask_point", text="")
row.prop(tool_settings, "use_gpencil_select_mask_stroke", text="")
row.prop(tool_settings, "use_gpencil_select_mask_segment", text="")
overlay = view.overlay
VIEW3D_MT_editor_menus.draw_collapsible(context, layout)
@@ -1188,7 +1222,12 @@ class VIEW3D_MT_editor_menus(Menu):
mode_string = context.mode
edit_object = context.edit_object
gp_edit = obj and obj.type == 'GPENCIL' and obj.mode in {
'EDIT_GPENCIL', 'PAINT_GPENCIL', 'SCULPT_GPENCIL', 'WEIGHT_GPENCIL', 'VERTEX_GPENCIL',
'EDIT_GPENCIL',
'PAINT_GPENCIL',
'SCULPT_GPENCIL',
'SCULPT_GREASE_PENCIL',
'WEIGHT_GPENCIL',
'VERTEX_GPENCIL',
}
tool_settings = context.tool_settings
@@ -1196,18 +1235,16 @@ class VIEW3D_MT_editor_menus(Menu):
# Select Menu
if gp_edit:
if mode_string not in {'PAINT_GPENCIL', 'WEIGHT_GPENCIL'}:
if (
mode_string == 'SCULPT_GPENCIL' and
(tool_settings.use_gpencil_select_mask_point or
tool_settings.use_gpencil_select_mask_stroke or
tool_settings.use_gpencil_select_mask_segment)
):
layout.menu("VIEW3D_MT_select_edit_gpencil")
elif mode_string == 'EDIT_GPENCIL':
layout.menu("VIEW3D_MT_select_edit_gpencil")
elif mode_string == 'VERTEX_GPENCIL':
layout.menu("VIEW3D_MT_select_edit_gpencil")
use_gpencil_masking = (tool_settings.use_gpencil_select_mask_point or
tool_settings.use_gpencil_select_mask_stroke or
tool_settings.use_gpencil_select_mask_segment)
if mode_string in {
'EDIT_GPENCIL',
'VERTEX_GPENCIL'} or (
mode_string in {
'SCULPT_GPENCIL',
'SCULPT_GREASE_PENCIL'} and use_gpencil_masking):
layout.menu("VIEW3D_MT_select_edit_gpencil")
elif mode_string in {'PAINT_WEIGHT', 'PAINT_VERTEX', 'PAINT_TEXTURE'}:
mesh = obj.data
if mesh.use_paint_mask: