GPv3: Vertex Paint Mode
This adds the vertex paint mode and all of the tools from GPv2: * Average * Blur * Draw * Replace * Smear Pull Request: https://projects.blender.org/blender/blender/pulls/125674
This commit is contained in:
@@ -4841,7 +4841,33 @@ def km_grease_pencil_weight_paint(params):
|
||||
return keymap
|
||||
|
||||
|
||||
def km_grease_pencil_vertex_paint(params):
|
||||
items = []
|
||||
keymap = (
|
||||
"Grease Pencil Vertex Paint",
|
||||
{"space_type": 'EMPTY', "region_type": 'WINDOW'},
|
||||
{"items": items}
|
||||
)
|
||||
|
||||
items.extend([
|
||||
# Paint vertex
|
||||
("grease_pencil.vertex_brush_stroke", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
|
||||
("grease_pencil.vertex_brush_stroke", {"type": 'LEFTMOUSE', "value": 'PRESS', "ctrl": True},
|
||||
{"properties": [("mode", 'INVERT')]}),
|
||||
# Increase/Decrease brush size
|
||||
("brush.scale_size", {"type": 'LEFT_BRACKET', "value": 'PRESS', "repeat": True},
|
||||
{"properties": [("scalar", 0.9)]}),
|
||||
("brush.scale_size", {"type": 'RIGHT_BRACKET', "value": 'PRESS', "repeat": True},
|
||||
{"properties": [("scalar", 1.0 / 0.9)]}),
|
||||
# Radial controls
|
||||
*_template_paint_radial_control("gpencil_vertex_paint"),
|
||||
])
|
||||
|
||||
return keymap
|
||||
|
||||
# Grease Pencil v3 Fill Tool.
|
||||
|
||||
|
||||
def km_grease_pencil_fill_tool(_params):
|
||||
items = []
|
||||
keymap = (
|
||||
@@ -9147,6 +9173,7 @@ def generate_keymaps(params=None):
|
||||
km_grease_pencil_edit_mode(params),
|
||||
km_grease_pencil_sculpt_mode(params),
|
||||
km_grease_pencil_weight_paint(params),
|
||||
km_grease_pencil_vertex_paint(params),
|
||||
km_grease_pencil_brush_stroke(params),
|
||||
km_grease_pencil_fill_tool(params),
|
||||
# Object mode.
|
||||
|
||||
@@ -93,7 +93,7 @@ class GreasePencilDisplayPanel:
|
||||
settings = tool_settings.gpencil_sculpt_paint
|
||||
elif context.mode == 'WEIGHT_GPENCIL' or context.mode == 'WEIGHT_GREASE_PENCIL':
|
||||
settings = tool_settings.gpencil_weight_paint
|
||||
elif context.mode == 'VERTEX_GPENCIL':
|
||||
elif context.mode == 'VERTEX_GPENCIL' or context.mode == 'VERTEX_GREASE_PENCIL':
|
||||
settings = tool_settings.gpencil_vertex_paint
|
||||
brush = settings.brush
|
||||
if brush:
|
||||
@@ -111,7 +111,7 @@ class GreasePencilDisplayPanel:
|
||||
settings = tool_settings.gpencil_sculpt_paint
|
||||
elif context.mode == 'WEIGHT_GPENCIL' or context.mode == 'WEIGHT_GREASE_PENCIL':
|
||||
settings = tool_settings.gpencil_weight_paint
|
||||
elif context.mode == 'VERTEX_GPENCIL':
|
||||
elif context.mode == 'VERTEX_GPENCIL' or context.mode == 'VERTEX_GREASE_PENCIL':
|
||||
settings = tool_settings.gpencil_vertex_paint
|
||||
brush = settings.brush
|
||||
gp_settings = brush.gpencil_settings
|
||||
@@ -146,7 +146,7 @@ class GreasePencilDisplayPanel:
|
||||
|
||||
col.prop(brush, "cursor_color_add", text="Cursor Color")
|
||||
|
||||
elif ob.mode == 'VERTEX_GPENCIL':
|
||||
elif ob.mode == 'VERTEX_GPENCIL' or ob.mode == 'VERTEX_GREASE_PENCIL':
|
||||
row = layout.row(align=True)
|
||||
row.prop(settings, "show_brush", text="Display Cursor")
|
||||
|
||||
|
||||
@@ -162,6 +162,8 @@ class UnifiedPaintPanel:
|
||||
return tool_settings.gpencil_sculpt_paint
|
||||
elif mode == 'WEIGHT_GREASE_PENCIL':
|
||||
return tool_settings.gpencil_weight_paint
|
||||
elif mode == 'VERTEX_GREASE_PENCIL':
|
||||
return tool_settings.gpencil_vertex_paint
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
@@ -1749,6 +1751,37 @@ def brush_basic_grease_pencil_weight_settings(layout, context, brush, *, compact
|
||||
layout.prop(brush, "direction", expand=True, text="" if compact else "Direction")
|
||||
|
||||
|
||||
def brush_basic_grease_pencil_vertex_settings(layout, context, brush, *, compact=False):
|
||||
UnifiedPaintPanel.prop_unified(
|
||||
layout,
|
||||
context,
|
||||
brush,
|
||||
"size",
|
||||
pressure_name="use_pressure_size",
|
||||
unified_name="use_unified_size",
|
||||
text="Radius",
|
||||
slider=True,
|
||||
header=compact,
|
||||
)
|
||||
|
||||
if brush.gpencil_vertex_tool in {'DRAW', 'BLUR', 'SMEAR'}:
|
||||
UnifiedPaintPanel.prop_unified(
|
||||
layout,
|
||||
context,
|
||||
brush,
|
||||
"strength",
|
||||
pressure_name="use_pressure_strength",
|
||||
unified_name="use_unified_strength",
|
||||
text="Strength",
|
||||
header=compact,
|
||||
)
|
||||
|
||||
gp_settings = brush.gpencil_settings
|
||||
if brush.gpencil_vertex_tool in {'DRAW', 'REPLACE'}:
|
||||
row = layout.row(align=True)
|
||||
row.prop(gp_settings, "vertex_mode", text="Mode")
|
||||
|
||||
|
||||
classes = (
|
||||
VIEW3D_MT_tools_projectpaint_clone,
|
||||
)
|
||||
|
||||
@@ -3026,6 +3026,44 @@ class _defs_grease_pencil_weight:
|
||||
]
|
||||
|
||||
|
||||
class _defs_grease_pencil_vertex:
|
||||
|
||||
@staticmethod
|
||||
def poll_select_mask(context):
|
||||
if context is None:
|
||||
return False
|
||||
ob = context.active_object
|
||||
tool_settings = context.scene.tool_settings
|
||||
return (
|
||||
ob is not None and
|
||||
ob.type == 'GREASEPENCIL' and (
|
||||
tool_settings.use_gpencil_vertex_select_mask_point or
|
||||
tool_settings.use_gpencil_vertex_select_mask_stroke or
|
||||
tool_settings.use_gpencil_vertex_select_mask_segment
|
||||
)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
# Though `data_block` is conceptually unnecessary with a single brush tool,
|
||||
# it's still used because many areas assume that brush tools have it set #bToolRef.
|
||||
tool = None
|
||||
if context:
|
||||
brush = context.tool_settings.gpencil_vertex_paint.brush
|
||||
if brush:
|
||||
tool = brush.gpencil_vertex_tool
|
||||
return [
|
||||
ToolDef.from_dict(
|
||||
dict(
|
||||
idname="builtin.brush",
|
||||
label="Brush",
|
||||
icon="brush.sculpt.paint",
|
||||
data_block=tool
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class _defs_curves_sculpt:
|
||||
|
||||
@staticmethod
|
||||
@@ -3890,6 +3928,17 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
|
||||
else ()
|
||||
),
|
||||
],
|
||||
'VERTEX_GREASE_PENCIL': [
|
||||
_defs_grease_pencil_vertex.generate_from_brushes,
|
||||
None,
|
||||
*_tools_annotate,
|
||||
None,
|
||||
lambda context: (
|
||||
VIEW3D_PT_tools_active._tools_select
|
||||
if _defs_grease_pencil_vertex.poll_select_mask(context)
|
||||
else ()
|
||||
),
|
||||
],
|
||||
'SCULPT_CURVES': [
|
||||
_defs_curves_sculpt.generate_from_brushes,
|
||||
None,
|
||||
|
||||
@@ -13,6 +13,7 @@ from bl_ui.properties_paint_common import (
|
||||
brush_basic_texpaint_settings,
|
||||
brush_basic_gpencil_weight_settings,
|
||||
brush_basic_grease_pencil_weight_settings,
|
||||
brush_basic_grease_pencil_vertex_settings,
|
||||
BrushAssetShelf,
|
||||
)
|
||||
from bl_ui.properties_grease_pencil_common import (
|
||||
@@ -144,7 +145,7 @@ class VIEW3D_HT_tool_header(Header):
|
||||
elif tool_mode == 'WEIGHT_GPENCIL' or tool_mode == 'WEIGHT_GREASE_PENCIL':
|
||||
if is_valid_context:
|
||||
layout.popover("VIEW3D_PT_tools_grease_pencil_weight_appearance")
|
||||
elif tool_mode == 'VERTEX_GPENCIL':
|
||||
elif tool_mode == 'VERTEX_GPENCIL' or tool_mode == 'VERTEX_GREASE_PENCIL':
|
||||
if is_valid_context:
|
||||
layout.popover("VIEW3D_PT_tools_grease_pencil_vertex_appearance")
|
||||
|
||||
@@ -551,7 +552,6 @@ class _draw_tool_settings_context_mode:
|
||||
brush = paint.brush
|
||||
|
||||
row = layout.row(align=True)
|
||||
settings = tool_settings.gpencil_vertex_paint
|
||||
|
||||
BrushAssetShelf.draw_popup_selector(layout, context, brush)
|
||||
|
||||
@@ -567,6 +567,27 @@ class _draw_tool_settings_context_mode:
|
||||
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def VERTEX_GREASE_PENCIL(context, layout, tool):
|
||||
if (tool is None) or (not tool.has_datablock):
|
||||
return False
|
||||
|
||||
tool_settings = context.tool_settings
|
||||
paint = tool_settings.gpencil_vertex_paint
|
||||
brush = paint.brush
|
||||
|
||||
BrushAssetShelf.draw_popup_selector(layout, context, brush)
|
||||
|
||||
if brush.gpencil_vertex_tool not in {'BLUR', 'AVERAGE', 'SMEAR'}:
|
||||
layout.separator(factor=0.4)
|
||||
ups = context.tool_settings.unified_paint_settings
|
||||
prop_owner = ups if ups.use_unified_color else brush
|
||||
layout.prop_with_popover(prop_owner, "color", text="", panel="TOPBAR_PT_grease_pencil_vertex_color")
|
||||
|
||||
brush_basic_grease_pencil_vertex_settings(layout, context, brush, compact=True)
|
||||
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def PARTICLE(context, layout, tool):
|
||||
if (tool is None) or (not tool.has_datablock):
|
||||
@@ -938,18 +959,23 @@ class VIEW3D_HT_header(Header):
|
||||
row.separator(factor=0.4)
|
||||
row.prop(tool_settings, "use_gpencil_draw_additive", text="", icon='FREEZE')
|
||||
|
||||
# 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="")
|
||||
|
||||
if object_mode in {'PAINT_GPENCIL', 'EDIT', 'SCULPT_GPENCIL', 'WEIGHT_GPENCIL'}:
|
||||
if object_mode == 'VERTEX_GPENCIL':
|
||||
row = layout.row(align=True)
|
||||
row.prop(tool_settings, "use_gpencil_vertex_select_mask_point", text="")
|
||||
row.prop(tool_settings, "use_gpencil_vertex_select_mask_stroke", text="")
|
||||
row.prop(tool_settings, "use_gpencil_vertex_select_mask_segment", text="")
|
||||
|
||||
if object_mode in {'PAINT_GPENCIL', 'EDIT', 'SCULPT_GPENCIL', 'WEIGHT_GPENCIL', 'VERTEX_GPENCIL'}:
|
||||
row = layout.row(align=True)
|
||||
row.prop(tool_settings, "use_grease_pencil_multi_frame_editing", text="")
|
||||
|
||||
if object_mode in {'EDIT', 'SCULPT_GPENCIL', 'WEIGHT_GPENCIL'}:
|
||||
if object_mode in {'EDIT', 'SCULPT_GPENCIL', 'WEIGHT_GPENCIL', 'VERTEX_GPENCIL'}:
|
||||
sub = row.row(align=True)
|
||||
sub.enabled = tool_settings.use_grease_pencil_multi_frame_editing
|
||||
sub.popover(
|
||||
@@ -1288,6 +1314,7 @@ class VIEW3D_MT_editor_menus(Menu):
|
||||
layout.menu("VIEW3D_MT_select_paint_mask_vertex")
|
||||
elif mode_string not in {
|
||||
'SCULPT', 'SCULPT_CURVES', 'PAINT_GREASE_PENCIL', 'SCULPT_GREASE_PENCIL', 'WEIGHT_GREASE_PENCIL',
|
||||
'VERTEX_GREASE_PENCIL',
|
||||
}:
|
||||
layout.menu("VIEW3D_MT_select_" + mode_string.lower())
|
||||
|
||||
@@ -1341,7 +1368,7 @@ class VIEW3D_MT_editor_menus(Menu):
|
||||
layout.menu("VIEW3D_MT_edit_greasepencil_stroke")
|
||||
|
||||
elif obj:
|
||||
if mode_string not in {'PAINT_TEXTURE', 'SCULPT_CURVES', 'SCULPT_GREASE_PENCIL'}:
|
||||
if mode_string not in {'PAINT_TEXTURE', 'SCULPT_CURVES', 'SCULPT_GREASE_PENCIL', 'VERTEX_GREASE_PENCIL'}:
|
||||
layout.menu("VIEW3D_MT_" + mode_string.lower())
|
||||
if mode_string == 'SCULPT':
|
||||
layout.menu("VIEW3D_MT_mask")
|
||||
@@ -1351,6 +1378,10 @@ class VIEW3D_MT_editor_menus(Menu):
|
||||
layout.menu("VIEW3D_MT_select_sculpt_curves")
|
||||
layout.menu("VIEW3D_MT_sculpt_curves")
|
||||
layout.template_node_operator_asset_root_items()
|
||||
elif mode_string == 'VERTEX_GREASE_PENCIL':
|
||||
layout.menu("VIEW3D_MT_select_edit_grease_pencil")
|
||||
layout.menu("VIEW3D_MT_paint_vertex_grease_pencil")
|
||||
layout.template_node_operator_asset_root_items()
|
||||
else:
|
||||
layout.template_node_operator_asset_root_items()
|
||||
|
||||
@@ -2369,6 +2400,13 @@ class VIEW3D_MT_paint_gpencil(Menu):
|
||||
layout.operator("gpencil.vertex_color_brightness_contrast", text="Brightness/Contrast")
|
||||
|
||||
|
||||
class VIEW3D_MT_paint_vertex_grease_pencil(Menu):
|
||||
bl_label = "Paint"
|
||||
|
||||
def draw(self, _context):
|
||||
pass
|
||||
|
||||
|
||||
class VIEW3D_MT_select_edit_gpencil(Menu):
|
||||
bl_label = "Select"
|
||||
|
||||
@@ -9274,6 +9312,41 @@ class TOPBAR_PT_gpencil_vertexcolor(GreasePencilVertexcolorPanel, Panel):
|
||||
return ob and ob.type == 'GPENCIL'
|
||||
|
||||
|
||||
class TOPBAR_PT_grease_pencil_vertex_color(Panel):
|
||||
bl_space_type = 'VIEW_3D'
|
||||
bl_region_type = 'HEADER'
|
||||
bl_label = "Color Attribute"
|
||||
bl_ui_units_x = 10
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
ob = context.object
|
||||
return ob and ob.type == 'GREASEPENCIL' and context.mode == 'VERTEX_GREASE_PENCIL'
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
gpencil_paint = context.scene.tool_settings.gpencil_vertex_paint
|
||||
brush = gpencil_paint.brush
|
||||
|
||||
col = layout.column()
|
||||
col.template_color_picker(brush, "color", value_slider=True)
|
||||
|
||||
sub_row = layout.row(align=True)
|
||||
UnifiedPaintPanel.prop_unified_color(sub_row, context, brush, "color", text="")
|
||||
UnifiedPaintPanel.prop_unified_color(sub_row, context, brush, "secondary_color", text="")
|
||||
|
||||
# TODO
|
||||
# sub_row.operator("gpencil.tint_flip", icon='FILE_REFRESH', text="")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.template_ID(gpencil_paint, "palette", new="palette.new")
|
||||
if gpencil_paint.palette:
|
||||
layout.template_palette(gpencil_paint, "palette", color=True)
|
||||
|
||||
|
||||
class VIEW3D_PT_curves_sculpt_add_shape(Panel):
|
||||
# Only for popover, these are dummy values.
|
||||
bl_space_type = 'VIEW_3D'
|
||||
@@ -9559,6 +9632,7 @@ classes = (
|
||||
VIEW3D_MT_greasepencil_material_active,
|
||||
VIEW3D_MT_paint_grease_pencil,
|
||||
VIEW3D_MT_paint_gpencil,
|
||||
VIEW3D_MT_paint_vertex_grease_pencil,
|
||||
VIEW3D_MT_draw_gpencil,
|
||||
VIEW3D_MT_assign_material,
|
||||
VIEW3D_MT_edit_gpencil,
|
||||
@@ -9688,6 +9762,7 @@ classes = (
|
||||
VIEW3D_PT_sculpt_context_menu,
|
||||
TOPBAR_PT_gpencil_materials,
|
||||
TOPBAR_PT_gpencil_vertexcolor,
|
||||
TOPBAR_PT_grease_pencil_vertex_color,
|
||||
TOPBAR_PT_annotation_layers,
|
||||
VIEW3D_PT_curves_sculpt_add_shape,
|
||||
VIEW3D_PT_curves_sculpt_parameter_falloff,
|
||||
|
||||
@@ -145,8 +145,9 @@ enum eContextObjectMode {
|
||||
CTX_MODE_PAINT_GREASE_PENCIL,
|
||||
CTX_MODE_SCULPT_GREASE_PENCIL,
|
||||
CTX_MODE_WEIGHT_GREASE_PENCIL,
|
||||
CTX_MODE_VERTEX_GREASE_PENCIL,
|
||||
};
|
||||
#define CTX_MODE_NUM (CTX_MODE_WEIGHT_GREASE_PENCIL + 1)
|
||||
#define CTX_MODE_NUM (CTX_MODE_VERTEX_GREASE_PENCIL + 1)
|
||||
|
||||
/* Context */
|
||||
|
||||
|
||||
@@ -1254,7 +1254,12 @@ enum eContextObjectMode CTX_data_mode_enum_ex(const Object *obedit,
|
||||
}
|
||||
}
|
||||
if (object_mode & OB_MODE_VERTEX_GPENCIL_LEGACY) {
|
||||
return CTX_MODE_VERTEX_GPENCIL_LEGACY;
|
||||
if (ob->type == OB_GPENCIL_LEGACY) {
|
||||
return CTX_MODE_VERTEX_GPENCIL_LEGACY;
|
||||
}
|
||||
if (ob->type == OB_GREASE_PENCIL) {
|
||||
return CTX_MODE_VERTEX_GREASE_PENCIL;
|
||||
}
|
||||
}
|
||||
if (object_mode & OB_MODE_SCULPT_CURVES) {
|
||||
return CTX_MODE_SCULPT_CURVES;
|
||||
@@ -1304,6 +1309,7 @@ static const char *data_mode_strings[] = {
|
||||
"grease_pencil_paint",
|
||||
"grease_pencil_sculpt",
|
||||
"grease_pencil_weight",
|
||||
"grease_pencil_vertex",
|
||||
nullptr,
|
||||
};
|
||||
BLI_STATIC_ASSERT(ARRAY_SIZE(data_mode_strings) == CTX_MODE_NUM + 1,
|
||||
|
||||
@@ -548,6 +548,8 @@ PaintMode BKE_paintmode_get_active_from_context(const bContext *C)
|
||||
return PaintMode::GPencil;
|
||||
case OB_MODE_WEIGHT_GPENCIL_LEGACY:
|
||||
return PaintMode::WeightGPencil;
|
||||
case OB_MODE_VERTEX_GPENCIL_LEGACY:
|
||||
return PaintMode::VertexGPencil;
|
||||
case OB_MODE_VERTEX_PAINT:
|
||||
return PaintMode::Vertex;
|
||||
case OB_MODE_WEIGHT_PAINT:
|
||||
@@ -583,6 +585,7 @@ PaintMode BKE_paintmode_get_from_tool(const bToolRef *tref)
|
||||
return PaintMode::GPencil;
|
||||
case CTX_MODE_PAINT_TEXTURE:
|
||||
return PaintMode::Texture3D;
|
||||
case CTX_MODE_VERTEX_GREASE_PENCIL:
|
||||
case CTX_MODE_VERTEX_GPENCIL_LEGACY:
|
||||
return PaintMode::VertexGPencil;
|
||||
case CTX_MODE_SCULPT_GPENCIL_LEGACY:
|
||||
|
||||
@@ -2653,6 +2653,7 @@ static void update_paint_modes_for_brush_assets(Main &bmain)
|
||||
CTX_MODE_SCULPT_GREASE_PENCIL,
|
||||
CTX_MODE_WEIGHT_GPENCIL_LEGACY,
|
||||
CTX_MODE_WEIGHT_GREASE_PENCIL,
|
||||
CTX_MODE_VERTEX_GREASE_PENCIL,
|
||||
CTX_MODE_VERTEX_GPENCIL_LEGACY,
|
||||
CTX_MODE_SCULPT_CURVES))
|
||||
{
|
||||
|
||||
@@ -185,6 +185,7 @@ static void OVERLAY_cache_init(void *vedata)
|
||||
case CTX_MODE_SCULPT_GREASE_PENCIL:
|
||||
case CTX_MODE_EDIT_GREASE_PENCIL:
|
||||
case CTX_MODE_WEIGHT_GREASE_PENCIL:
|
||||
case CTX_MODE_VERTEX_GREASE_PENCIL:
|
||||
OVERLAY_edit_grease_pencil_cache_init(data);
|
||||
break;
|
||||
case CTX_MODE_PARTICLE:
|
||||
@@ -782,6 +783,7 @@ static void OVERLAY_draw_scene(void *vedata)
|
||||
case CTX_MODE_EDIT_GREASE_PENCIL:
|
||||
case CTX_MODE_SCULPT_GREASE_PENCIL:
|
||||
case CTX_MODE_WEIGHT_GREASE_PENCIL:
|
||||
case CTX_MODE_VERTEX_GREASE_PENCIL:
|
||||
OVERLAY_edit_grease_pencil_draw(data);
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -755,51 +755,72 @@ void GPENCIL_OT_weightmode_toggle(wmOperatorType *ot)
|
||||
/** \name Toggle Vertex Paint Mode Operator
|
||||
* \{ */
|
||||
|
||||
static bool grease_pencil_poll_vertex_cursor(bContext *C)
|
||||
{
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
return ob && (ob->mode & OB_MODE_VERTEX_GPENCIL_LEGACY) && (ob->type == OB_GREASE_PENCIL) &&
|
||||
CTX_wm_region_view3d(C) && WM_toolsystem_active_tool_is_brush(C);
|
||||
}
|
||||
|
||||
static bool gpencil_vertexmode_toggle_poll(bContext *C)
|
||||
{
|
||||
/* if using gpencil object, use this gpd */
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
if ((ob) && (ob->type == OB_GPENCIL_LEGACY)) {
|
||||
if ((ob) && ELEM(ob->type, OB_GPENCIL_LEGACY, OB_GREASE_PENCIL)) {
|
||||
return ob->data != nullptr;
|
||||
}
|
||||
return ED_gpencil_data_get_active(C) != nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
static int gpencil_vertexmode_toggle_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
const bool back = RNA_boolean_get(op->ptr, "back");
|
||||
|
||||
wmMsgBus *mbus = CTX_wm_message_bus(C);
|
||||
Main *bmain = CTX_data_main(C);
|
||||
bGPdata *gpd = ED_gpencil_data_get_active(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
ToolSettings *ts = CTX_data_tool_settings(C);
|
||||
|
||||
bool is_object = false;
|
||||
short mode;
|
||||
/* if using a gpencil object, use this datablock */
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
const bool is_mode_set = (ob->mode & OB_MODE_VERTEX_GPENCIL_LEGACY) != 0;
|
||||
if ((ob) && (ob->type == OB_GPENCIL_LEGACY)) {
|
||||
gpd = static_cast<bGPdata *>(ob->data);
|
||||
bGPdata *gpd = static_cast<bGPdata *>(ob->data);
|
||||
is_object = true;
|
||||
}
|
||||
|
||||
if (gpd == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
if (gpd == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* Just toggle paintmode flag... */
|
||||
gpd->flag ^= GP_DATA_STROKE_VERTEXMODE;
|
||||
/* set mode */
|
||||
if (gpd->flag & GP_DATA_STROKE_VERTEXMODE) {
|
||||
mode = OB_MODE_VERTEX_GPENCIL_LEGACY;
|
||||
/* Just toggle paintmode flag... */
|
||||
gpd->flag ^= GP_DATA_STROKE_VERTEXMODE;
|
||||
/* Set mode. */
|
||||
if (gpd->flag & GP_DATA_STROKE_VERTEXMODE) {
|
||||
mode = OB_MODE_VERTEX_GPENCIL_LEGACY;
|
||||
}
|
||||
else {
|
||||
mode = OB_MODE_OBJECT;
|
||||
}
|
||||
}
|
||||
else {
|
||||
mode = OB_MODE_OBJECT;
|
||||
else if ((ob) && (ob->type == OB_GREASE_PENCIL)) {
|
||||
is_object = true;
|
||||
if (!is_mode_set) {
|
||||
mode = OB_MODE_VERTEX_GPENCIL_LEGACY;
|
||||
}
|
||||
else {
|
||||
mode = OB_MODE_OBJECT;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_object) {
|
||||
/* try to back previous mode */
|
||||
if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_VERTEXMODE) == 0) && (back == 1)) {
|
||||
mode = ob->restore_mode;
|
||||
if (ob->type == OB_GPENCIL_LEGACY) {
|
||||
bGPdata *gpd = static_cast<bGPdata *>(ob->data);
|
||||
/* Try to go to back previous mode. */
|
||||
if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_VERTEXMODE) == 0) && (back == 1)) {
|
||||
mode = ob->restore_mode;
|
||||
}
|
||||
}
|
||||
ob->restore_mode = ob->mode;
|
||||
ob->mode = mode;
|
||||
@@ -810,17 +831,29 @@ static int gpencil_vertexmode_toggle_exec(bContext *C, wmOperator *op)
|
||||
* Need Draw as well (used for Palettes). */
|
||||
BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_paint);
|
||||
BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_vertexpaint);
|
||||
Paint *vertex_paint = BKE_paint_get_active_from_paintmode(scene, PaintMode::VertexGPencil);
|
||||
|
||||
BKE_paint_brushes_validate(bmain, &ts->gp_vertexpaint->paint);
|
||||
BKE_paint_brushes_validate(bmain, vertex_paint);
|
||||
|
||||
if (ob->type == OB_GREASE_PENCIL) {
|
||||
ED_paint_cursor_start(vertex_paint, grease_pencil_poll_vertex_cursor);
|
||||
}
|
||||
|
||||
/* Ensure Palette by default. */
|
||||
BKE_gpencil_palette_ensure(bmain, CTX_data_scene(C));
|
||||
BKE_gpencil_palette_ensure(bmain, scene);
|
||||
}
|
||||
|
||||
/* setup other modes */
|
||||
ED_gpencil_setup_modes(C, gpd, mode);
|
||||
/* set cache as dirty */
|
||||
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
|
||||
/* Setup other modes. */
|
||||
if (ob->type == OB_GPENCIL_LEGACY) {
|
||||
bGPdata *gpd = static_cast<bGPdata *>(ob->data);
|
||||
ED_gpencil_setup_modes(C, gpd, mode);
|
||||
/* Set cache as dirty. */
|
||||
DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
|
||||
}
|
||||
else if (ob->type == OB_GREASE_PENCIL) {
|
||||
GreasePencil *grease_pencil = static_cast<GreasePencil *>(ob->data);
|
||||
DEG_id_tag_update(&grease_pencil->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
|
||||
}
|
||||
|
||||
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, nullptr);
|
||||
WM_event_add_notifier(C, NC_SCENE | ND_MODE, nullptr);
|
||||
|
||||
@@ -117,6 +117,22 @@ bool grease_pencil_weight_painting_poll(bContext *C)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool grease_pencil_vertex_painting_poll(bContext *C)
|
||||
{
|
||||
if (!active_grease_pencil_poll(C)) {
|
||||
return false;
|
||||
}
|
||||
Object *object = CTX_data_active_object(C);
|
||||
if ((object->mode & OB_MODE_VERTEX_GPENCIL_LEGACY) == 0) {
|
||||
return false;
|
||||
}
|
||||
ToolSettings *ts = CTX_data_tool_settings(C);
|
||||
if (!ts || !ts->gp_vertexpaint) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void keymap_grease_pencil_edit_mode(wmKeyConfig *keyconf)
|
||||
{
|
||||
wmKeyMap *keymap = WM_keymap_ensure(
|
||||
@@ -145,6 +161,13 @@ static void keymap_grease_pencil_weight_paint_mode(wmKeyConfig *keyconf)
|
||||
keymap->poll = grease_pencil_weight_painting_poll;
|
||||
}
|
||||
|
||||
static void keymap_grease_pencil_vertex_paint_mode(wmKeyConfig *keyconf)
|
||||
{
|
||||
wmKeyMap *keymap = WM_keymap_ensure(
|
||||
keyconf, "Grease Pencil Vertex Paint", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
keymap->poll = grease_pencil_vertex_painting_poll;
|
||||
}
|
||||
|
||||
/* Enabled for all tools except the fill tool. */
|
||||
static bool keymap_grease_pencil_brush_stroke_poll(bContext *C)
|
||||
{
|
||||
@@ -246,6 +269,7 @@ void ED_keymap_grease_pencil(wmKeyConfig *keyconf)
|
||||
keymap_grease_pencil_paint_mode(keyconf);
|
||||
keymap_grease_pencil_sculpt_mode(keyconf);
|
||||
keymap_grease_pencil_weight_paint_mode(keyconf);
|
||||
keymap_grease_pencil_vertex_paint_mode(keyconf);
|
||||
keymap_grease_pencil_brush_stroke(keyconf);
|
||||
keymap_grease_pencil_fill_tool(keyconf);
|
||||
ED_primitivetool_modal_keymap(keyconf);
|
||||
|
||||
@@ -875,6 +875,21 @@ static VectorSet<int> get_hidden_material_indices(Object &object)
|
||||
return hidden_material_indices;
|
||||
}
|
||||
|
||||
static VectorSet<int> get_fill_material_indices(Object &object)
|
||||
{
|
||||
BLI_assert(object.type == OB_GREASE_PENCIL);
|
||||
VectorSet<int> fill_material_indices;
|
||||
for (const int mat_i : IndexRange(object.totcol)) {
|
||||
Material *material = BKE_object_material_get(&object, mat_i + 1);
|
||||
if (material != nullptr && material->gp_style != nullptr &&
|
||||
(material->gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0)
|
||||
{
|
||||
fill_material_indices.add_new(mat_i);
|
||||
}
|
||||
}
|
||||
return fill_material_indices;
|
||||
}
|
||||
|
||||
IndexMask retrieve_editable_strokes(Object &object,
|
||||
const bke::greasepencil::Drawing &drawing,
|
||||
int layer_index,
|
||||
@@ -916,6 +931,50 @@ IndexMask retrieve_editable_strokes(Object &object,
|
||||
});
|
||||
}
|
||||
|
||||
IndexMask retrieve_editable_fill_strokes(Object &object,
|
||||
const bke::greasepencil::Drawing &drawing,
|
||||
int layer_index,
|
||||
IndexMaskMemory &memory)
|
||||
{
|
||||
using namespace blender;
|
||||
const bke::CurvesGeometry &curves = drawing.strokes();
|
||||
const IndexRange curves_range = drawing.strokes().curves_range();
|
||||
|
||||
if (object.totcol == 0) {
|
||||
return IndexMask(curves_range);
|
||||
}
|
||||
|
||||
/* Get all the editable material indices */
|
||||
const VectorSet<int> editable_material_indices = get_editable_material_indices(object);
|
||||
if (editable_material_indices.is_empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const bke::AttributeAccessor attributes = curves.attributes();
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
|
||||
const bke::greasepencil::Layer &layer = *grease_pencil.layers()[layer_index];
|
||||
const VectorSet<int> fill_material_indices = get_fill_material_indices(object);
|
||||
|
||||
const VArray<int> materials = *attributes.lookup<int>("material_index", bke::AttrDomain::Curve);
|
||||
if (!materials) {
|
||||
/* If the attribute does not exist then the default is the first material. */
|
||||
if (editable_material_indices.contains(0)) {
|
||||
return curves_range;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
/* Get all the strokes that have their material unlocked. */
|
||||
return IndexMask::from_predicate(
|
||||
curves_range, GrainSize(4096), memory, [&](const int64_t curve_i) {
|
||||
const int material_index = materials[curve_i];
|
||||
/* The stroke is editable if the material is editable. If the material is not editable,
|
||||
* then the stroke is only editable if the layer disables the locked material option. */
|
||||
return (editable_material_indices.contains(material_index) ||
|
||||
layer.use_locked_material()) &&
|
||||
fill_material_indices.contains(material_index);
|
||||
});
|
||||
}
|
||||
|
||||
IndexMask retrieve_editable_strokes_by_material(Object &object,
|
||||
const bke::greasepencil::Drawing &drawing,
|
||||
const int mat_i,
|
||||
@@ -1136,6 +1195,21 @@ IndexMask retrieve_editable_and_selected_strokes(Object &object,
|
||||
return IndexMask::from_intersection(editable_strokes, selected_strokes, memory);
|
||||
}
|
||||
|
||||
IndexMask retrieve_editable_and_selected_fill_strokes(Object &object,
|
||||
const bke::greasepencil::Drawing &drawing,
|
||||
int layer_index,
|
||||
IndexMaskMemory &memory)
|
||||
{
|
||||
using namespace blender;
|
||||
const bke::CurvesGeometry &curves = drawing.strokes();
|
||||
|
||||
const IndexMask editable_strokes = retrieve_editable_fill_strokes(
|
||||
object, drawing, layer_index, memory);
|
||||
const IndexMask selected_strokes = ed::curves::retrieve_selected_curves(curves, memory);
|
||||
|
||||
return IndexMask::from_intersection(editable_strokes, selected_strokes, memory);
|
||||
}
|
||||
|
||||
IndexMask retrieve_editable_and_selected_points(Object &object,
|
||||
const bke::greasepencil::Drawing &drawing,
|
||||
int layer_index,
|
||||
|
||||
@@ -270,6 +270,7 @@ bool editable_grease_pencil_point_selection_poll(bContext *C);
|
||||
bool grease_pencil_painting_poll(bContext *C);
|
||||
bool grease_pencil_sculpting_poll(bContext *C);
|
||||
bool grease_pencil_weight_painting_poll(bContext *C);
|
||||
bool grease_pencil_vertex_painting_poll(bContext *C);
|
||||
|
||||
float opacity_from_input_sample(const float pressure,
|
||||
const Brush *brush,
|
||||
@@ -323,6 +324,10 @@ IndexMask retrieve_editable_strokes(Object &grease_pencil_object,
|
||||
const bke::greasepencil::Drawing &drawing,
|
||||
int layer_index,
|
||||
IndexMaskMemory &memory);
|
||||
IndexMask retrieve_editable_fill_strokes(Object &grease_pencil_object,
|
||||
const bke::greasepencil::Drawing &drawing,
|
||||
int layer_index,
|
||||
IndexMaskMemory &memory);
|
||||
IndexMask retrieve_editable_strokes_by_material(Object &object,
|
||||
const bke::greasepencil::Drawing &drawing,
|
||||
const int mat_i,
|
||||
@@ -357,6 +362,10 @@ IndexMask retrieve_editable_and_selected_strokes(Object &grease_pencil_object,
|
||||
const bke::greasepencil::Drawing &drawing,
|
||||
int layer_index,
|
||||
IndexMaskMemory &memory);
|
||||
IndexMask retrieve_editable_and_selected_fill_strokes(Object &grease_pencil_object,
|
||||
const bke::greasepencil::Drawing &drawing,
|
||||
int layer_index,
|
||||
IndexMaskMemory &memory);
|
||||
IndexMask retrieve_editable_and_selected_points(Object &object,
|
||||
const bke::greasepencil::Drawing &drawing,
|
||||
int layer_index,
|
||||
|
||||
@@ -145,7 +145,7 @@ bool mode_compat_test(const Object *ob, eObjectMode mode)
|
||||
break;
|
||||
case OB_GREASE_PENCIL:
|
||||
if (mode & (OB_MODE_EDIT | OB_MODE_PAINT_GPENCIL_LEGACY | OB_MODE_SCULPT_GPENCIL_LEGACY |
|
||||
OB_MODE_WEIGHT_GPENCIL_LEGACY))
|
||||
OB_MODE_WEIGHT_GPENCIL_LEGACY | OB_MODE_VERTEX_GPENCIL_LEGACY))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -52,8 +52,8 @@ set(SRC
|
||||
grease_pencil_fill.cc
|
||||
grease_pencil_interpolate.cc
|
||||
grease_pencil_paint.cc
|
||||
grease_pencil_paint_common.cc
|
||||
grease_pencil_sculpt_clone.cc
|
||||
grease_pencil_sculpt_common.cc
|
||||
grease_pencil_sculpt_grab.cc
|
||||
grease_pencil_sculpt_pinch.cc
|
||||
grease_pencil_sculpt_push.cc
|
||||
@@ -69,6 +69,11 @@ set(SRC
|
||||
grease_pencil_weight_blur.cc
|
||||
grease_pencil_weight_draw.cc
|
||||
grease_pencil_weight_smear.cc
|
||||
grease_pencil_vertex_average.cc
|
||||
grease_pencil_vertex_blur.cc
|
||||
grease_pencil_vertex_paint.cc
|
||||
grease_pencil_vertex_replace.cc
|
||||
grease_pencil_vertex_smear.cc
|
||||
paint_canvas.cc
|
||||
paint_cursor.cc
|
||||
paint_curve.cc
|
||||
|
||||
@@ -147,6 +147,24 @@ static std::unique_ptr<GreasePencilStrokeOperation> get_stroke_operation(bContex
|
||||
return greasepencil::new_weight_paint_smear_operation();
|
||||
}
|
||||
}
|
||||
else if (mode == PaintMode::VertexGPencil) {
|
||||
switch (eBrushGPVertexType(brush.gpencil_vertex_brush_type)) {
|
||||
case GPVERTEX_BRUSH_TYPE_DRAW:
|
||||
return greasepencil::new_vertex_paint_operation(stroke_mode);
|
||||
case GPVERTEX_BRUSH_TYPE_BLUR:
|
||||
return greasepencil::new_vertex_blur_operation();
|
||||
case GPVERTEX_BRUSH_TYPE_AVERAGE:
|
||||
return greasepencil::new_vertex_average_operation();
|
||||
case GPVERTEX_BRUSH_TYPE_SMEAR:
|
||||
return greasepencil::new_vertex_smear_operation();
|
||||
case GPVERTEX_BRUSH_TYPE_REPLACE:
|
||||
return greasepencil::new_vertex_replace_operation();
|
||||
case GPVERTEX_BRUSH_TYPE_TINT:
|
||||
/* Unused. */
|
||||
BLI_assert_unreachable();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -327,7 +345,6 @@ static int grease_pencil_sculpt_paint_invoke(bContext *C, wmOperator *op, const
|
||||
}
|
||||
|
||||
bke::greasepencil::Layer &active_layer = *grease_pencil.get_active_layer();
|
||||
|
||||
if (!active_layer.is_editable()) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Active layer is locked or hidden");
|
||||
return OPERATOR_CANCELLED;
|
||||
@@ -380,7 +397,7 @@ static void GREASE_PENCIL_OT_sculpt_paint(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Grease Pencil Draw";
|
||||
ot->idname = "GREASE_PENCIL_OT_sculpt_paint";
|
||||
ot->description = "Draw a new stroke in the active Grease Pencil object";
|
||||
ot->description = "Sculpt strokes in the active Grease Pencil object";
|
||||
|
||||
ot->poll = grease_pencil_sculpt_paint_poll;
|
||||
ot->invoke = grease_pencil_sculpt_paint_invoke;
|
||||
@@ -487,6 +504,111 @@ static void GREASE_PENCIL_OT_weight_brush_stroke(wmOperatorType *ot)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Vertex Brush Stroke Operator
|
||||
* \{ */
|
||||
|
||||
static bool grease_pencil_vertex_brush_stroke_poll(bContext *C)
|
||||
{
|
||||
if (!ed::greasepencil::grease_pencil_vertex_painting_poll(C)) {
|
||||
return false;
|
||||
}
|
||||
if (!WM_toolsystem_active_tool_is_brush(C)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int grease_pencil_vertex_brush_stroke_invoke(bContext *C,
|
||||
wmOperator *op,
|
||||
const wmEvent *event)
|
||||
{
|
||||
const Object *object = CTX_data_active_object(C);
|
||||
if (!object || object->type != OB_GREASE_PENCIL) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
|
||||
if (!grease_pencil.has_active_layer()) {
|
||||
BKE_report(op->reports, RPT_ERROR, "No active Grease Pencil layer");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
bke::greasepencil::Layer &active_layer = *grease_pencil.get_active_layer();
|
||||
if (!active_layer.is_editable()) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Active layer is locked or hidden");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
const Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
const Brush *brush = BKE_paint_brush_for_read(paint);
|
||||
if (brush == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* Ensure a drawing at the current keyframe. */
|
||||
bool inserted_keyframe = false;
|
||||
/* For the vertex paint tools, we don't want the auto-key to create an empty keyframe, so we
|
||||
* duplicate the previous key. */
|
||||
const bool use_duplicate_previous_key = true;
|
||||
if (!ed::greasepencil::ensure_active_keyframe(
|
||||
C, grease_pencil, use_duplicate_previous_key, inserted_keyframe))
|
||||
{
|
||||
BKE_report(op->reports, RPT_ERROR, "No Grease Pencil frame to draw on");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
if (inserted_keyframe) {
|
||||
WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, nullptr);
|
||||
}
|
||||
|
||||
op->customdata = paint_stroke_new(C,
|
||||
op,
|
||||
stroke_get_location,
|
||||
stroke_test_start,
|
||||
stroke_update_step,
|
||||
stroke_redraw,
|
||||
stroke_done,
|
||||
event->type);
|
||||
|
||||
const int return_value = op->type->modal(C, op, event);
|
||||
if (return_value == OPERATOR_FINISHED) {
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
WM_event_add_modal_handler(C, op);
|
||||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
static int grease_pencil_vertex_brush_stroke_modal(bContext *C,
|
||||
wmOperator *op,
|
||||
const wmEvent *event)
|
||||
{
|
||||
return paint_stroke_modal(C, op, event, reinterpret_cast<PaintStroke **>(&op->customdata));
|
||||
}
|
||||
|
||||
static void grease_pencil_vertex_brush_stroke_cancel(bContext *C, wmOperator *op)
|
||||
{
|
||||
paint_stroke_cancel(C, op, static_cast<PaintStroke *>(op->customdata));
|
||||
}
|
||||
|
||||
static void GREASE_PENCIL_OT_vertex_brush_stroke(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Grease Pencil Paint Vertex";
|
||||
ot->idname = "GREASE_PENCIL_OT_vertex_brush_stroke";
|
||||
ot->description = "Draw on vertex colors in the active Grease Pencil object";
|
||||
|
||||
ot->poll = grease_pencil_vertex_brush_stroke_poll;
|
||||
ot->invoke = grease_pencil_vertex_brush_stroke_invoke;
|
||||
ot->modal = grease_pencil_vertex_brush_stroke_modal;
|
||||
ot->cancel = grease_pencil_vertex_brush_stroke_cancel;
|
||||
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
||||
paint_stroke_operator_properties(ot);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Bucket Fill Operator
|
||||
* \{ */
|
||||
@@ -1602,6 +1724,7 @@ void ED_operatortypes_grease_pencil_draw()
|
||||
WM_operatortype_append(GREASE_PENCIL_OT_brush_stroke);
|
||||
WM_operatortype_append(GREASE_PENCIL_OT_sculpt_paint);
|
||||
WM_operatortype_append(GREASE_PENCIL_OT_weight_brush_stroke);
|
||||
WM_operatortype_append(GREASE_PENCIL_OT_vertex_brush_stroke);
|
||||
WM_operatortype_append(GREASE_PENCIL_OT_fill);
|
||||
}
|
||||
|
||||
|
||||
@@ -44,28 +44,39 @@ class GreasePencilStrokeOperation : public PaintModeData {
|
||||
namespace greasepencil {
|
||||
|
||||
/* Get list of drawings the tool should be operating on. */
|
||||
Vector<ed::greasepencil::MutableDrawingInfo> get_drawings_for_sculpt(const bContext &C);
|
||||
Vector<ed::greasepencil::MutableDrawingInfo> get_drawings_for_painting(const bContext &C);
|
||||
/* Get the brush radius accounting for pen pressure. */
|
||||
float brush_radius(const Scene &scene, const Brush &brush, float pressure);
|
||||
|
||||
/* Make sure the brush has all necessary grease pencil settings. */
|
||||
void init_brush(Brush &brush);
|
||||
|
||||
/* Index mask of all points within the brush radius. */
|
||||
IndexMask brush_influence_mask(const Scene &scene,
|
||||
const Brush &brush,
|
||||
const float2 &mouse_position,
|
||||
float pressure,
|
||||
float multi_frame_falloff,
|
||||
const IndexMask &selection,
|
||||
Span<float2> view_positions,
|
||||
Vector<float> &influences,
|
||||
IndexMaskMemory &memory);
|
||||
IndexMask brush_point_influence_mask(const Scene &scene,
|
||||
const Brush &brush,
|
||||
const float2 &mouse_position,
|
||||
float pressure,
|
||||
float multi_frame_falloff,
|
||||
const IndexMask &selection,
|
||||
Span<float2> view_positions,
|
||||
Vector<float> &influences,
|
||||
IndexMaskMemory &memory);
|
||||
|
||||
/* Influence value at point co for the brush. */
|
||||
float brush_influence(const Scene &scene,
|
||||
const Brush &brush,
|
||||
const float2 &co,
|
||||
const InputSample &sample,
|
||||
float multi_frame_falloff);
|
||||
float brush_point_influence(const Scene &scene,
|
||||
const Brush &brush,
|
||||
const float2 &co,
|
||||
const InputSample &sample,
|
||||
float multi_frame_falloff);
|
||||
/* Compute the closest distance to a polygon. If the point is inside the polygon, the distance is
|
||||
* 0.0f. If the point is outside the polygon, the distance to the closest point is returned. */
|
||||
float closest_distance_to_surface_2d(const float2 pt, const Span<float2> verts);
|
||||
/* Influence value for an entire fill. */
|
||||
float brush_fill_influence(const Scene &scene,
|
||||
const Brush &brush,
|
||||
Span<float2> fill_positions,
|
||||
const InputSample &sample,
|
||||
float multi_frame_falloff);
|
||||
|
||||
/* True if influence of the brush should be inverted. */
|
||||
bool is_brush_inverted(const Brush &brush, BrushStrokeMode stroke_mode);
|
||||
@@ -95,7 +106,15 @@ struct GreasePencilStrokeParams {
|
||||
};
|
||||
|
||||
/* Point index mask for a drawing based on selection tool settings. */
|
||||
IndexMask point_selection_mask(const GreasePencilStrokeParams ¶ms, IndexMaskMemory &memory);
|
||||
IndexMask point_selection_mask(const GreasePencilStrokeParams ¶ms,
|
||||
const bool use_masking,
|
||||
IndexMaskMemory &memory);
|
||||
IndexMask stroke_selection_mask(const GreasePencilStrokeParams ¶ms,
|
||||
const bool use_masking,
|
||||
IndexMaskMemory &memory);
|
||||
IndexMask fill_selection_mask(const GreasePencilStrokeParams ¶ms,
|
||||
const bool use_masking,
|
||||
IndexMaskMemory &memory);
|
||||
|
||||
bke::crazyspace::GeometryDeformation get_drawing_deformation(
|
||||
const GreasePencilStrokeParams ¶ms);
|
||||
@@ -103,6 +122,11 @@ bke::crazyspace::GeometryDeformation get_drawing_deformation(
|
||||
/* Project points from layer space into 2D view space. */
|
||||
Array<float2> calculate_view_positions(const GreasePencilStrokeParams ¶ms,
|
||||
const IndexMask &selection);
|
||||
Array<float> calculate_view_radii(const GreasePencilStrokeParams ¶ms,
|
||||
const IndexMask &selection);
|
||||
|
||||
bool do_vertex_color_points(const Brush &brush);
|
||||
bool do_vertex_color_fill(const Brush &brush);
|
||||
|
||||
/* Stroke operation base class that performs various common initializations. */
|
||||
class GreasePencilStrokeOperationCommon : public GreasePencilStrokeOperation {
|
||||
@@ -130,11 +154,17 @@ class GreasePencilStrokeOperationCommon : public GreasePencilStrokeOperation {
|
||||
|
||||
void foreach_editable_drawing(
|
||||
const bContext &C, FunctionRef<bool(const GreasePencilStrokeParams ¶ms)> fn) const;
|
||||
void foreach_editable_drawing(
|
||||
const bContext &C,
|
||||
GrainSize grain_size,
|
||||
FunctionRef<bool(const GreasePencilStrokeParams ¶ms)> fn) const;
|
||||
void foreach_editable_drawing(const bContext &C,
|
||||
FunctionRef<bool(const GreasePencilStrokeParams ¶ms,
|
||||
const DrawingPlacement &placement)> fn) const;
|
||||
};
|
||||
|
||||
/* Operations */
|
||||
|
||||
std::unique_ptr<GreasePencilStrokeOperation> new_paint_operation();
|
||||
std::unique_ptr<GreasePencilStrokeOperation> new_erase_operation(bool temp_eraser);
|
||||
std::unique_ptr<GreasePencilStrokeOperation> new_tint_operation();
|
||||
@@ -152,6 +182,12 @@ std::unique_ptr<GreasePencilStrokeOperation> new_push_operation(BrushStrokeMode
|
||||
std::unique_ptr<GreasePencilStrokeOperation> new_pinch_operation(BrushStrokeMode stroke_mode);
|
||||
std::unique_ptr<GreasePencilStrokeOperation> new_twist_operation(BrushStrokeMode stroke_mode);
|
||||
std::unique_ptr<GreasePencilStrokeOperation> new_clone_operation(BrushStrokeMode stroke_mode);
|
||||
std::unique_ptr<GreasePencilStrokeOperation> new_vertex_average_operation();
|
||||
std::unique_ptr<GreasePencilStrokeOperation> new_vertex_blur_operation();
|
||||
std::unique_ptr<GreasePencilStrokeOperation> new_vertex_paint_operation(
|
||||
BrushStrokeMode stroke_mode);
|
||||
std::unique_ptr<GreasePencilStrokeOperation> new_vertex_replace_operation();
|
||||
std::unique_ptr<GreasePencilStrokeOperation> new_vertex_smear_operation();
|
||||
|
||||
} // namespace greasepencil
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
|
||||
namespace blender::ed::sculpt_paint::greasepencil {
|
||||
|
||||
Vector<ed::greasepencil::MutableDrawingInfo> get_drawings_for_sculpt(const bContext &C)
|
||||
Vector<ed::greasepencil::MutableDrawingInfo> get_drawings_for_painting(const bContext &C)
|
||||
{
|
||||
using namespace blender::bke::greasepencil;
|
||||
|
||||
@@ -62,6 +62,7 @@ void init_brush(Brush &brush)
|
||||
BKE_brush_init_gpencil_settings(&brush);
|
||||
}
|
||||
BLI_assert(brush.gpencil_settings != nullptr);
|
||||
BKE_curvemapping_init(brush.curve);
|
||||
BKE_curvemapping_init(brush.gpencil_settings->curve_strength);
|
||||
BKE_curvemapping_init(brush.gpencil_settings->curve_sensitivity);
|
||||
BKE_curvemapping_init(brush.gpencil_settings->curve_jitter);
|
||||
@@ -73,7 +74,7 @@ void init_brush(Brush &brush)
|
||||
BKE_curvemapping_init(brush.gpencil_settings->curve_rand_value);
|
||||
}
|
||||
|
||||
static float brush_radius(const Scene &scene, const Brush &brush, const float pressure = 1.0f)
|
||||
float brush_radius(const Scene &scene, const Brush &brush, const float pressure = 1.0f)
|
||||
{
|
||||
float radius = BKE_brush_size_get(&scene, &brush);
|
||||
if (BKE_brush_use_size_pressure(&brush)) {
|
||||
@@ -82,11 +83,11 @@ static float brush_radius(const Scene &scene, const Brush &brush, const float pr
|
||||
return radius;
|
||||
}
|
||||
|
||||
float brush_influence(const Scene &scene,
|
||||
const Brush &brush,
|
||||
const float2 &co,
|
||||
const InputSample &sample,
|
||||
const float multi_frame_falloff)
|
||||
float brush_point_influence(const Scene &scene,
|
||||
const Brush &brush,
|
||||
const float2 &co,
|
||||
const InputSample &sample,
|
||||
const float multi_frame_falloff)
|
||||
{
|
||||
const float radius = brush_radius(scene, brush, sample.pressure);
|
||||
/* Basic strength factor from brush settings. */
|
||||
@@ -103,15 +104,56 @@ float brush_influence(const Scene &scene,
|
||||
return influence_base * brush_falloff;
|
||||
}
|
||||
|
||||
IndexMask brush_influence_mask(const Scene &scene,
|
||||
const Brush &brush,
|
||||
const float2 &mouse_position,
|
||||
const float pressure,
|
||||
const float multi_frame_falloff,
|
||||
const IndexMask &selection,
|
||||
const Span<float2> view_positions,
|
||||
Vector<float> &influences,
|
||||
IndexMaskMemory &memory)
|
||||
/* Compute the closest distance to the "surface". When the point is outside the polygon, compute
|
||||
* the closest distance to the polygon points. When the point is inside the polygon return 0.*/
|
||||
float closest_distance_to_surface_2d(const float2 pt, const Span<float2> verts)
|
||||
{
|
||||
int j = verts.size() - 1;
|
||||
bool isect = false;
|
||||
float distance = FLT_MAX;
|
||||
for (int i = 0; i < verts.size(); i++) {
|
||||
/* Based on implementation of #isect_point_poly_v2. */
|
||||
if (((verts[i].y > pt.y) != (verts[j].y > pt.y)) &&
|
||||
(pt.x <
|
||||
(verts[j].x - verts[i].x) * (pt.y - verts[i].y) / (verts[j].y - verts[i].y) + verts[i].x))
|
||||
{
|
||||
isect = !isect;
|
||||
}
|
||||
distance = math::min(distance, math::distance(pt, verts[i]));
|
||||
j = i;
|
||||
}
|
||||
return isect ? 0.0f : distance;
|
||||
}
|
||||
|
||||
float brush_fill_influence(const Scene &scene,
|
||||
const Brush &brush,
|
||||
const Span<float2> fill_positions,
|
||||
const InputSample &sample,
|
||||
const float multi_frame_falloff)
|
||||
{
|
||||
const float radius = brush_radius(scene, brush, sample.pressure);
|
||||
/* Basic strength factor from brush settings. */
|
||||
const float brush_pressure = BKE_brush_use_alpha_pressure(&brush) ? sample.pressure : 1.0f;
|
||||
const float influence_base = BKE_brush_alpha_get(&scene, &brush) * brush_pressure *
|
||||
multi_frame_falloff;
|
||||
|
||||
/* Distance falloff. */
|
||||
const float distance = closest_distance_to_surface_2d(sample.mouse_position, fill_positions);
|
||||
/* Apply Brush curve. */
|
||||
const float brush_falloff = BKE_brush_curve_strength(&brush, distance, radius);
|
||||
|
||||
return influence_base * brush_falloff;
|
||||
}
|
||||
|
||||
IndexMask brush_point_influence_mask(const Scene &scene,
|
||||
const Brush &brush,
|
||||
const float2 &mouse_position,
|
||||
const float pressure,
|
||||
const float multi_frame_falloff,
|
||||
const IndexMask &selection,
|
||||
const Span<float2> view_positions,
|
||||
Vector<float> &influences,
|
||||
IndexMaskMemory &memory)
|
||||
{
|
||||
if (selection.is_empty()) {
|
||||
return {};
|
||||
@@ -177,13 +219,33 @@ GreasePencilStrokeParams GreasePencilStrokeParams::from_context(
|
||||
drawing};
|
||||
}
|
||||
|
||||
IndexMask point_selection_mask(const GreasePencilStrokeParams ¶ms, IndexMaskMemory &memory)
|
||||
IndexMask point_selection_mask(const GreasePencilStrokeParams ¶ms,
|
||||
const bool use_masking,
|
||||
IndexMaskMemory &memory)
|
||||
{
|
||||
const bool is_masking = GPENCIL_ANY_SCULPT_MASK(
|
||||
eGP_Sculpt_SelectMaskFlag(params.toolsettings.gpencil_selectmode_sculpt));
|
||||
return (is_masking ? ed::greasepencil::retrieve_editable_and_selected_points(
|
||||
params.ob_eval, params.drawing, params.layer_index, memory) :
|
||||
params.drawing.strokes().points_range());
|
||||
|
||||
return (use_masking ? ed::greasepencil::retrieve_editable_and_selected_points(
|
||||
params.ob_eval, params.drawing, params.layer_index, memory) :
|
||||
params.drawing.strokes().points_range());
|
||||
}
|
||||
|
||||
IndexMask stroke_selection_mask(const GreasePencilStrokeParams ¶ms,
|
||||
const bool use_masking,
|
||||
IndexMaskMemory &memory)
|
||||
{
|
||||
|
||||
return (use_masking ? ed::greasepencil::retrieve_editable_and_selected_strokes(
|
||||
params.ob_eval, params.drawing, params.layer_index, memory) :
|
||||
params.drawing.strokes().curves_range());
|
||||
}
|
||||
|
||||
IndexMask fill_selection_mask(const GreasePencilStrokeParams ¶ms,
|
||||
const bool use_masking,
|
||||
IndexMaskMemory &memory)
|
||||
{
|
||||
return (use_masking ? ed::greasepencil::retrieve_editable_and_selected_fill_strokes(
|
||||
params.ob_eval, params.drawing, params.layer_index, memory) :
|
||||
params.drawing.strokes().curves_range());
|
||||
}
|
||||
|
||||
bke::crazyspace::GeometryDeformation get_drawing_deformation(
|
||||
@@ -216,6 +278,39 @@ Array<float2> calculate_view_positions(const GreasePencilStrokeParams ¶ms,
|
||||
return view_positions;
|
||||
}
|
||||
|
||||
Array<float> calculate_view_radii(const GreasePencilStrokeParams ¶ms,
|
||||
const IndexMask &selection)
|
||||
{
|
||||
const RegionView3D *rv3d = static_cast<RegionView3D *>(params.region.regiondata);
|
||||
bke::crazyspace::GeometryDeformation deformation = get_drawing_deformation(params);
|
||||
|
||||
const VArray<float> radii = params.drawing.radii();
|
||||
Array<float> view_radii(radii.size());
|
||||
/* Compute screen space radii. */
|
||||
const float4x4 transform = params.layer.to_world_space(params.ob_eval);
|
||||
selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
|
||||
const float pixel_size = ED_view3d_pixel_size(
|
||||
rv3d, math::transform_point(transform, deformation.positions[point_i]));
|
||||
view_radii[point_i] = radii[point_i] / pixel_size;
|
||||
});
|
||||
|
||||
return view_radii;
|
||||
}
|
||||
|
||||
bool do_vertex_color_points(const Brush &brush)
|
||||
{
|
||||
return brush.gpencil_settings != nullptr &&
|
||||
((brush.gpencil_settings->vertex_mode == GPPAINT_MODE_STROKE) ||
|
||||
(brush.gpencil_settings->vertex_mode == GPPAINT_MODE_BOTH));
|
||||
}
|
||||
|
||||
bool do_vertex_color_fill(const Brush &brush)
|
||||
{
|
||||
return brush.gpencil_settings != nullptr &&
|
||||
((brush.gpencil_settings->vertex_mode == GPPAINT_MODE_FILL) ||
|
||||
(brush.gpencil_settings->vertex_mode == GPPAINT_MODE_BOTH));
|
||||
}
|
||||
|
||||
bool GreasePencilStrokeOperationCommon::is_inverted(const Brush &brush) const
|
||||
{
|
||||
return is_brush_inverted(brush, this->stroke_mode);
|
||||
@@ -238,8 +333,9 @@ void GreasePencilStrokeOperationCommon::foreach_editable_drawing(
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
|
||||
|
||||
std::atomic<bool> changed = false;
|
||||
const Vector<MutableDrawingInfo> drawings = get_drawings_for_sculpt(C);
|
||||
threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
|
||||
const Vector<MutableDrawingInfo> drawings = get_drawings_for_painting(C);
|
||||
for (const int64_t i : drawings.index_range()) {
|
||||
const MutableDrawingInfo &info = drawings[i];
|
||||
GreasePencilStrokeParams params = GreasePencilStrokeParams::from_context(
|
||||
scene,
|
||||
depsgraph,
|
||||
@@ -252,6 +348,45 @@ void GreasePencilStrokeOperationCommon::foreach_editable_drawing(
|
||||
if (fn(params)) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
|
||||
WM_event_add_notifier(&C, NC_GEOM | ND_DATA, &grease_pencil);
|
||||
}
|
||||
}
|
||||
|
||||
void GreasePencilStrokeOperationCommon::foreach_editable_drawing(
|
||||
const bContext &C,
|
||||
const GrainSize grain_size,
|
||||
FunctionRef<bool(const GreasePencilStrokeParams ¶ms)> fn) const
|
||||
{
|
||||
using namespace blender::bke::greasepencil;
|
||||
|
||||
const Scene &scene = *CTX_data_scene(&C);
|
||||
Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(&C);
|
||||
ARegion ®ion = *CTX_wm_region(&C);
|
||||
Object &object = *CTX_data_active_object(&C);
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
|
||||
|
||||
std::atomic<bool> changed = false;
|
||||
const Vector<MutableDrawingInfo> drawings = get_drawings_for_painting(C);
|
||||
threading::parallel_for(drawings.index_range(), grain_size.value, [&](const IndexRange range) {
|
||||
for (const int64_t i : range) {
|
||||
const MutableDrawingInfo &info = drawings[i];
|
||||
GreasePencilStrokeParams params = GreasePencilStrokeParams::from_context(
|
||||
scene,
|
||||
depsgraph,
|
||||
region,
|
||||
object,
|
||||
info.layer_index,
|
||||
info.frame_number,
|
||||
info.multi_frame_falloff,
|
||||
info.drawing);
|
||||
if (fn(params)) {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (changed) {
|
||||
@@ -276,7 +411,7 @@ void GreasePencilStrokeOperationCommon::foreach_editable_drawing(
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object.data);
|
||||
|
||||
std::atomic<bool> changed = false;
|
||||
const Vector<MutableDrawingInfo> drawings = get_drawings_for_sculpt(C);
|
||||
const Vector<MutableDrawingInfo> drawings = get_drawings_for_painting(C);
|
||||
threading::parallel_for_each(drawings, [&](const MutableDrawingInfo &info) {
|
||||
const Layer &layer = *grease_pencil.layer(info.layer_index);
|
||||
|
||||
@@ -135,11 +135,14 @@ void GrabOperation::on_stroke_begin(const bContext &C, const InputSample &start_
|
||||
Object &ob_eval = *DEG_get_evaluated_object(&depsgraph, &ob_orig);
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(ob_orig.data);
|
||||
|
||||
const bool is_masking = GPENCIL_ANY_SCULPT_MASK(
|
||||
eGP_Sculpt_SelectMaskFlag(scene.toolsettings->gpencil_selectmode_sculpt));
|
||||
|
||||
init_brush(brush);
|
||||
|
||||
this->prev_mouse_position = start_sample.mouse_position;
|
||||
|
||||
const Vector<MutableDrawingInfo> drawings = get_drawings_for_sculpt(C);
|
||||
const Vector<MutableDrawingInfo> drawings = get_drawings_for_painting(C);
|
||||
this->drawing_data.reinitialize(drawings.size());
|
||||
threading::parallel_for_each(drawings.index_range(), [&](const int i) {
|
||||
const MutableDrawingInfo &info = drawings[i];
|
||||
@@ -158,23 +161,22 @@ void GrabOperation::on_stroke_begin(const bContext &C, const InputSample &start_
|
||||
info.frame_number,
|
||||
info.multi_frame_falloff,
|
||||
info.drawing};
|
||||
|
||||
IndexMaskMemory selection_memory;
|
||||
IndexMask selection = point_selection_mask(params, selection_memory);
|
||||
IndexMask selection = point_selection_mask(params, is_masking, selection_memory);
|
||||
|
||||
Array<float2> view_positions = calculate_view_positions(params, selection);
|
||||
|
||||
/* Cache points under brush influence. */
|
||||
Vector<float> weights;
|
||||
IndexMask point_mask = brush_influence_mask(scene,
|
||||
brush,
|
||||
start_sample.mouse_position,
|
||||
1.0f,
|
||||
info.multi_frame_falloff,
|
||||
selection,
|
||||
view_positions,
|
||||
weights,
|
||||
data.memory);
|
||||
IndexMask point_mask = brush_point_influence_mask(scene,
|
||||
brush,
|
||||
start_sample.mouse_position,
|
||||
1.0f,
|
||||
info.multi_frame_falloff,
|
||||
selection,
|
||||
view_positions,
|
||||
weights,
|
||||
data.memory);
|
||||
|
||||
if (point_mask.is_empty()) {
|
||||
/* Set empty point mask to skip. */
|
||||
|
||||
@@ -38,13 +38,15 @@ void PinchOperation::on_stroke_extended(const bContext &C, const InputSample &ex
|
||||
Paint &paint = *BKE_paint_get_active_from_context(&C);
|
||||
const Brush &brush = *BKE_paint_brush(&paint);
|
||||
const bool invert = this->is_inverted(brush);
|
||||
const bool is_masking = GPENCIL_ANY_SCULPT_MASK(
|
||||
eGP_Sculpt_SelectMaskFlag(scene.toolsettings->gpencil_selectmode_sculpt));
|
||||
|
||||
this->foreach_editable_drawing(
|
||||
C,
|
||||
[&](const GreasePencilStrokeParams ¶ms,
|
||||
const ed::greasepencil::DrawingPlacement &placement) {
|
||||
IndexMaskMemory selection_memory;
|
||||
const IndexMask selection = point_selection_mask(params, selection_memory);
|
||||
const IndexMask selection = point_selection_mask(params, is_masking, selection_memory);
|
||||
if (selection.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
@@ -57,7 +59,7 @@ void PinchOperation::on_stroke_extended(const bContext &C, const InputSample &ex
|
||||
|
||||
selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
|
||||
const float2 &co = view_positions[point_i];
|
||||
const float influence = brush_influence(
|
||||
const float influence = brush_point_influence(
|
||||
scene, brush, co, extension_sample, params.multi_frame_falloff);
|
||||
if (influence <= 0.0f) {
|
||||
return;
|
||||
|
||||
@@ -38,12 +38,15 @@ void PushOperation::on_stroke_extended(const bContext &C, const InputSample &ext
|
||||
Paint &paint = *BKE_paint_get_active_from_context(&C);
|
||||
const Brush &brush = *BKE_paint_brush(&paint);
|
||||
|
||||
const bool is_masking = GPENCIL_ANY_SCULPT_MASK(
|
||||
eGP_Sculpt_SelectMaskFlag(scene.toolsettings->gpencil_selectmode_sculpt));
|
||||
|
||||
this->foreach_editable_drawing(
|
||||
C,
|
||||
[&](const GreasePencilStrokeParams ¶ms,
|
||||
const ed::greasepencil::DrawingPlacement &placement) {
|
||||
IndexMaskMemory selection_memory;
|
||||
const IndexMask selection = point_selection_mask(params, selection_memory);
|
||||
const IndexMask selection = point_selection_mask(params, is_masking, selection_memory);
|
||||
if (selection.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
@@ -56,7 +59,7 @@ void PushOperation::on_stroke_extended(const bContext &C, const InputSample &ext
|
||||
|
||||
selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
|
||||
const float2 &co = view_positions[point_i];
|
||||
const float influence = brush_influence(
|
||||
const float influence = brush_point_influence(
|
||||
scene, brush, co, extension_sample, params.multi_frame_falloff);
|
||||
if (influence <= 0.0f) {
|
||||
return;
|
||||
|
||||
@@ -58,6 +58,9 @@ void RandomizeOperation::on_stroke_extended(const bContext &C, const InputSample
|
||||
const Brush &brush = *BKE_paint_brush(&paint);
|
||||
const int sculpt_mode_flag = brush.gpencil_settings->sculpt_mode_flag;
|
||||
|
||||
const bool is_masking = GPENCIL_ANY_SCULPT_MASK(
|
||||
eGP_Sculpt_SelectMaskFlag(scene.toolsettings->gpencil_selectmode_sculpt));
|
||||
|
||||
this->foreach_editable_drawing(
|
||||
C,
|
||||
[&](const GreasePencilStrokeParams ¶ms,
|
||||
@@ -65,7 +68,7 @@ void RandomizeOperation::on_stroke_extended(const bContext &C, const InputSample
|
||||
const uint32_t seed = this->unique_seed();
|
||||
|
||||
IndexMaskMemory selection_memory;
|
||||
const IndexMask selection = point_selection_mask(params, selection_memory);
|
||||
const IndexMask selection = point_selection_mask(params, is_masking, selection_memory);
|
||||
if (selection.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
@@ -84,7 +87,7 @@ void RandomizeOperation::on_stroke_extended(const bContext &C, const InputSample
|
||||
|
||||
selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
|
||||
const float2 &co = view_positions[point_i];
|
||||
const float influence = brush_influence(
|
||||
const float influence = brush_point_influence(
|
||||
scene, brush, co, extension_sample, params.multi_frame_falloff);
|
||||
if (influence <= 0.0f) {
|
||||
return;
|
||||
@@ -100,7 +103,7 @@ void RandomizeOperation::on_stroke_extended(const bContext &C, const InputSample
|
||||
MutableSpan<float> opacities = params.drawing.opacities_for_write();
|
||||
selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
|
||||
const float2 &co = view_positions[point_i];
|
||||
const float influence = brush_influence(
|
||||
const float influence = brush_point_influence(
|
||||
scene, brush, co, extension_sample, params.multi_frame_falloff);
|
||||
if (influence <= 0.0f) {
|
||||
return;
|
||||
@@ -114,7 +117,7 @@ void RandomizeOperation::on_stroke_extended(const bContext &C, const InputSample
|
||||
const MutableSpan<float> radii = params.drawing.radii_for_write();
|
||||
selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
|
||||
const float2 &co = view_positions[point_i];
|
||||
const float influence = brush_influence(
|
||||
const float influence = brush_point_influence(
|
||||
scene, brush, co, extension_sample, params.multi_frame_falloff);
|
||||
if (influence <= 0.0f) {
|
||||
return;
|
||||
@@ -130,7 +133,7 @@ void RandomizeOperation::on_stroke_extended(const bContext &C, const InputSample
|
||||
attributes.lookup_or_add_for_write_span<float>("rotation", bke::AttrDomain::Point);
|
||||
selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
|
||||
const float2 &co = view_positions[point_i];
|
||||
const float influence = brush_influence(
|
||||
const float influence = brush_point_influence(
|
||||
scene, brush, co, extension_sample, params.multi_frame_falloff);
|
||||
if (influence <= 0.0f) {
|
||||
return;
|
||||
|
||||
@@ -46,9 +46,12 @@ void SmoothOperation::on_stroke_extended(const bContext &C, const InputSample &e
|
||||
const Brush &brush = *BKE_paint_brush(&paint);
|
||||
const int sculpt_mode_flag = brush.gpencil_settings->sculpt_mode_flag;
|
||||
|
||||
const bool is_masking = GPENCIL_ANY_SCULPT_MASK(
|
||||
eGP_Sculpt_SelectMaskFlag(scene.toolsettings->gpencil_selectmode_sculpt));
|
||||
|
||||
this->foreach_editable_drawing(C, [&](const GreasePencilStrokeParams ¶ms) {
|
||||
IndexMaskMemory selection_memory;
|
||||
const IndexMask selection = point_selection_mask(params, selection_memory);
|
||||
const IndexMask selection = point_selection_mask(params, is_masking, selection_memory);
|
||||
if (selection.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
@@ -62,7 +65,7 @@ void SmoothOperation::on_stroke_extended(const bContext &C, const InputSample &e
|
||||
|
||||
const VArray<float> influences = VArray<float>::ForFunc(
|
||||
view_positions.size(), [&](const int64_t point_) {
|
||||
return brush_influence(
|
||||
return brush_point_influence(
|
||||
scene, brush, view_positions[point_], extension_sample, params.multi_frame_falloff);
|
||||
});
|
||||
Array<bool> selection_array(curves.points_num());
|
||||
|
||||
@@ -40,9 +40,12 @@ void StrengthOperation::on_stroke_extended(const bContext &C, const InputSample
|
||||
const Brush &brush = *BKE_paint_brush(&paint);
|
||||
const bool invert = this->is_inverted(brush);
|
||||
|
||||
const bool is_masking = GPENCIL_ANY_SCULPT_MASK(
|
||||
eGP_Sculpt_SelectMaskFlag(scene.toolsettings->gpencil_selectmode_sculpt));
|
||||
|
||||
this->foreach_editable_drawing(C, [&](const GreasePencilStrokeParams ¶ms) {
|
||||
IndexMaskMemory selection_memory;
|
||||
const IndexMask selection = point_selection_mask(params, selection_memory);
|
||||
const IndexMask selection = point_selection_mask(params, is_masking, selection_memory);
|
||||
if (selection.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
@@ -52,7 +55,7 @@ void StrengthOperation::on_stroke_extended(const bContext &C, const InputSample
|
||||
|
||||
selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
|
||||
float &opacity = opacities[point_i];
|
||||
const float influence = brush_influence(
|
||||
const float influence = brush_point_influence(
|
||||
scene, brush, view_positions[point_i], extension_sample, params.multi_frame_falloff);
|
||||
/* Brush influence mapped to opacity by a factor of 0.125. */
|
||||
const float delta_opacity = (invert ? -influence : influence) * 0.125f;
|
||||
|
||||
@@ -41,9 +41,12 @@ void ThicknessOperation::on_stroke_extended(const bContext &C, const InputSample
|
||||
const Brush &brush = *BKE_paint_brush(&paint);
|
||||
const bool invert = this->is_inverted(brush);
|
||||
|
||||
const bool is_masking = GPENCIL_ANY_SCULPT_MASK(
|
||||
eGP_Sculpt_SelectMaskFlag(scene.toolsettings->gpencil_selectmode_sculpt));
|
||||
|
||||
this->foreach_editable_drawing(C, [&](const GreasePencilStrokeParams ¶ms) {
|
||||
IndexMaskMemory selection_memory;
|
||||
const IndexMask selection = point_selection_mask(params, selection_memory);
|
||||
const IndexMask selection = point_selection_mask(params, is_masking, selection_memory);
|
||||
if (selection.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
@@ -55,7 +58,7 @@ void ThicknessOperation::on_stroke_extended(const bContext &C, const InputSample
|
||||
|
||||
selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
|
||||
float &radius = radii[point_i];
|
||||
const float influence = brush_influence(
|
||||
const float influence = brush_point_influence(
|
||||
scene, brush, view_positions[point_i], extension_sample, params.multi_frame_falloff);
|
||||
/* Factor 1/1000 is used to map arbitrary influence value to a sensible radius. */
|
||||
const float delta_radius = (invert ? -influence : influence) * 0.001f;
|
||||
|
||||
@@ -48,12 +48,15 @@ void TwistOperation::on_stroke_extended(const bContext &C, const InputSample &ex
|
||||
const Brush &brush = *BKE_paint_brush(&paint);
|
||||
const bool invert = this->is_inverted(brush);
|
||||
|
||||
const bool is_masking = GPENCIL_ANY_SCULPT_MASK(
|
||||
eGP_Sculpt_SelectMaskFlag(scene.toolsettings->gpencil_selectmode_sculpt));
|
||||
|
||||
this->foreach_editable_drawing(
|
||||
C,
|
||||
[&](const GreasePencilStrokeParams ¶ms,
|
||||
const ed::greasepencil::DrawingPlacement &placement) {
|
||||
IndexMaskMemory selection_memory;
|
||||
const IndexMask selection = point_selection_mask(params, selection_memory);
|
||||
const IndexMask selection = point_selection_mask(params, is_masking, selection_memory);
|
||||
if (selection.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
@@ -66,7 +69,7 @@ void TwistOperation::on_stroke_extended(const bContext &C, const InputSample &ex
|
||||
|
||||
selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
|
||||
const float2 &co = view_positions[point_i];
|
||||
const float influence = brush_influence(
|
||||
const float influence = brush_point_influence(
|
||||
scene, brush, co, extension_sample, params.multi_frame_falloff);
|
||||
if (influence <= 0.0f) {
|
||||
return;
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BKE_brush.hh"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_grease_pencil.hh"
|
||||
#include "BKE_paint.hh"
|
||||
|
||||
#include "grease_pencil_intern.hh"
|
||||
|
||||
namespace blender::ed::sculpt_paint::greasepencil {
|
||||
|
||||
class VertexAverageOperation : public GreasePencilStrokeOperationCommon {
|
||||
using GreasePencilStrokeOperationCommon::GreasePencilStrokeOperationCommon;
|
||||
|
||||
public:
|
||||
void on_stroke_begin(const bContext &C, const InputSample &start_sample) override;
|
||||
void on_stroke_extended(const bContext &C, const InputSample &extension_sample) override;
|
||||
void on_stroke_done(const bContext &C) override;
|
||||
};
|
||||
|
||||
void VertexAverageOperation::on_stroke_begin(const bContext &C, const InputSample &start_sample)
|
||||
{
|
||||
this->init_stroke(C, start_sample);
|
||||
this->on_stroke_extended(C, start_sample);
|
||||
}
|
||||
|
||||
void VertexAverageOperation::on_stroke_extended(const bContext &C,
|
||||
const InputSample &extension_sample)
|
||||
{
|
||||
const Scene &scene = *CTX_data_scene(&C);
|
||||
Paint &paint = *BKE_paint_get_active_from_context(&C);
|
||||
const Brush &brush = *BKE_paint_brush(&paint);
|
||||
const float radius = brush_radius(scene, brush, extension_sample.pressure);
|
||||
const float radius_squared = radius * radius;
|
||||
|
||||
const bool is_masking = GPENCIL_ANY_VERTEX_MASK(
|
||||
eGP_vertex_SelectMaskFlag(scene.toolsettings->gpencil_selectmode_vertex));
|
||||
|
||||
const bool do_points = do_vertex_color_points(brush);
|
||||
const bool do_fill = do_vertex_color_fill(brush);
|
||||
|
||||
/* Compute the average color under the brush. */
|
||||
float3 average_color;
|
||||
int color_count = 0;
|
||||
this->foreach_editable_drawing(C, [&](const GreasePencilStrokeParams ¶ms) {
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask point_selection = point_selection_mask(params, is_masking, memory);
|
||||
if (!point_selection.is_empty() && do_points) {
|
||||
const Array<float2> view_positions = calculate_view_positions(params, point_selection);
|
||||
const VArray<ColorGeometry4f> vertex_colors = params.drawing.vertex_colors();
|
||||
|
||||
point_selection.foreach_index([&](const int64_t point_i) {
|
||||
const ColorGeometry4f color = vertex_colors[point_i];
|
||||
if (color.a > 0.0f && math::distance_squared(extension_sample.mouse_position,
|
||||
view_positions[point_i]) < radius_squared)
|
||||
{
|
||||
average_color += float3(color.r, color.g, color.b);
|
||||
color_count++;
|
||||
}
|
||||
});
|
||||
}
|
||||
const IndexMask fill_selection = fill_selection_mask(params, is_masking, memory);
|
||||
if (!fill_selection.is_empty() && do_fill) {
|
||||
const OffsetIndices<int> points_by_curve = params.drawing.strokes().points_by_curve();
|
||||
const Array<float2> view_positions = calculate_view_positions(params, point_selection);
|
||||
const VArray<ColorGeometry4f> fill_colors = params.drawing.fill_colors();
|
||||
|
||||
fill_selection.foreach_index([&](const int64_t curve_i) {
|
||||
const IndexRange points = points_by_curve[curve_i];
|
||||
const Span<float2> curve_view_positions = view_positions.as_span().slice(points);
|
||||
const ColorGeometry4f color = fill_colors[curve_i];
|
||||
if (color.a > 0.0f && closest_distance_to_surface_2d(extension_sample.mouse_position,
|
||||
curve_view_positions) < radius)
|
||||
{
|
||||
average_color += float3(color.r, color.g, color.b);
|
||||
color_count++;
|
||||
}
|
||||
});
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
if (color_count <= 0) {
|
||||
return;
|
||||
}
|
||||
average_color = average_color / color_count;
|
||||
/* The average color is the color that will be mixed in. */
|
||||
const ColorGeometry4f mix_color(average_color.x, average_color.y, average_color.z, 1.0f);
|
||||
|
||||
this->foreach_editable_drawing(C, GrainSize(1), [&](const GreasePencilStrokeParams ¶ms) {
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask point_selection = point_selection_mask(params, is_masking, memory);
|
||||
if (!point_selection.is_empty() && do_points) {
|
||||
const Array<float2> view_positions = calculate_view_positions(params, point_selection);
|
||||
MutableSpan<ColorGeometry4f> vertex_colors = params.drawing.vertex_colors_for_write();
|
||||
|
||||
point_selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
|
||||
const float influence = brush_point_influence(
|
||||
scene, brush, view_positions[point_i], extension_sample, params.multi_frame_falloff);
|
||||
|
||||
ColorGeometry4f &color = vertex_colors[point_i];
|
||||
color = math::interpolate(color, mix_color, influence);
|
||||
});
|
||||
}
|
||||
|
||||
const IndexMask fill_selection = fill_selection_mask(params, is_masking, memory);
|
||||
if (!fill_selection.is_empty() && do_fill) {
|
||||
const OffsetIndices<int> points_by_curve = params.drawing.strokes().points_by_curve();
|
||||
const Array<float2> view_positions = calculate_view_positions(params, point_selection);
|
||||
MutableSpan<ColorGeometry4f> fill_colors = params.drawing.fill_colors_for_write();
|
||||
|
||||
fill_selection.foreach_index(GrainSize(1024), [&](const int64_t curve_i) {
|
||||
const IndexRange points = points_by_curve[curve_i];
|
||||
const Span<float2> curve_view_positions = view_positions.as_span().slice(points);
|
||||
const float influence = brush_fill_influence(
|
||||
scene, brush, curve_view_positions, extension_sample, params.multi_frame_falloff);
|
||||
|
||||
ColorGeometry4f &color = fill_colors[curve_i];
|
||||
color = math::interpolate(color, mix_color, influence);
|
||||
});
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void VertexAverageOperation::on_stroke_done(const bContext & /*C*/) {}
|
||||
|
||||
std::unique_ptr<GreasePencilStrokeOperation> new_vertex_average_operation()
|
||||
{
|
||||
return std::make_unique<VertexAverageOperation>();
|
||||
}
|
||||
|
||||
} // namespace blender::ed::sculpt_paint::greasepencil
|
||||
@@ -0,0 +1,93 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BKE_brush.hh"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_grease_pencil.hh"
|
||||
#include "BKE_paint.hh"
|
||||
|
||||
#include "grease_pencil_intern.hh"
|
||||
|
||||
namespace blender::ed::sculpt_paint::greasepencil {
|
||||
|
||||
class VertexBlurOperation : public GreasePencilStrokeOperationCommon {
|
||||
using GreasePencilStrokeOperationCommon::GreasePencilStrokeOperationCommon;
|
||||
|
||||
public:
|
||||
void on_stroke_begin(const bContext &C, const InputSample &start_sample) override;
|
||||
void on_stroke_extended(const bContext &C, const InputSample &extension_sample) override;
|
||||
void on_stroke_done(const bContext &C) override;
|
||||
};
|
||||
|
||||
void VertexBlurOperation::on_stroke_begin(const bContext &C, const InputSample &start_sample)
|
||||
{
|
||||
this->init_stroke(C, start_sample);
|
||||
this->on_stroke_extended(C, start_sample);
|
||||
}
|
||||
|
||||
void VertexBlurOperation::on_stroke_extended(const bContext &C,
|
||||
const InputSample &extension_sample)
|
||||
{
|
||||
const Scene &scene = *CTX_data_scene(&C);
|
||||
Paint &paint = *BKE_paint_get_active_from_context(&C);
|
||||
const Brush &brush = *BKE_paint_brush(&paint);
|
||||
const float radius = brush_radius(scene, brush, extension_sample.pressure);
|
||||
const float radius_squared = radius * radius;
|
||||
|
||||
const bool is_masking = GPENCIL_ANY_VERTEX_MASK(
|
||||
eGP_vertex_SelectMaskFlag(scene.toolsettings->gpencil_selectmode_vertex));
|
||||
|
||||
this->foreach_editable_drawing(C, GrainSize(1), [&](const GreasePencilStrokeParams ¶ms) {
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask stroke_selection = stroke_selection_mask(params, is_masking, memory);
|
||||
if (stroke_selection.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
const IndexMask point_selection = point_selection_mask(params, is_masking, memory);
|
||||
const Array<float2> view_positions = calculate_view_positions(params, point_selection);
|
||||
const OffsetIndices<int> points_by_curve = params.drawing.strokes().points_by_curve();
|
||||
MutableSpan<ColorGeometry4f> vertex_colors = params.drawing.vertex_colors_for_write();
|
||||
stroke_selection.foreach_index(GrainSize(1024), [&](const int64_t curve) {
|
||||
const IndexRange points = points_by_curve[curve];
|
||||
|
||||
float3 average_color(0.0f);
|
||||
int color_count = 0;
|
||||
for (const int point : points) {
|
||||
const ColorGeometry4f color = vertex_colors[point];
|
||||
const float distance = math::distance_squared(extension_sample.mouse_position,
|
||||
view_positions[point]);
|
||||
if (color.a > 0.0f && distance < radius_squared) {
|
||||
average_color += float3(color.r, color.g, color.b);
|
||||
color_count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (color_count == 0) {
|
||||
return;
|
||||
}
|
||||
average_color = average_color / color_count;
|
||||
const ColorGeometry4f mix_color(average_color.x, average_color.y, average_color.z, 1.0f);
|
||||
|
||||
for (const int point : points) {
|
||||
const float influence = brush_point_influence(
|
||||
scene, brush, view_positions[point], extension_sample, params.multi_frame_falloff);
|
||||
ColorGeometry4f &color = vertex_colors[point];
|
||||
if (color.a > 0.0f && influence > 0.0f) {
|
||||
color = math::interpolate(color, mix_color, influence);
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void VertexBlurOperation::on_stroke_done(const bContext & /*C*/) {}
|
||||
|
||||
std::unique_ptr<GreasePencilStrokeOperation> new_vertex_blur_operation()
|
||||
{
|
||||
return std::make_unique<VertexBlurOperation>();
|
||||
}
|
||||
|
||||
} // namespace blender::ed::sculpt_paint::greasepencil
|
||||
@@ -0,0 +1,121 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BLI_math_color.hh"
|
||||
|
||||
#include "BKE_brush.hh"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_grease_pencil.hh"
|
||||
#include "BKE_paint.hh"
|
||||
|
||||
#include "grease_pencil_intern.hh"
|
||||
|
||||
namespace blender::ed::sculpt_paint::greasepencil {
|
||||
|
||||
class VertexPaintOperation : public GreasePencilStrokeOperationCommon {
|
||||
using GreasePencilStrokeOperationCommon::GreasePencilStrokeOperationCommon;
|
||||
|
||||
public:
|
||||
void on_stroke_begin(const bContext &C, const InputSample &start_sample) override;
|
||||
void on_stroke_extended(const bContext &C, const InputSample &extension_sample) override;
|
||||
void on_stroke_done(const bContext & /*C*/) override {}
|
||||
};
|
||||
|
||||
void VertexPaintOperation::on_stroke_begin(const bContext &C, const InputSample &start_sample)
|
||||
{
|
||||
this->init_stroke(C, start_sample);
|
||||
this->on_stroke_extended(C, start_sample);
|
||||
}
|
||||
|
||||
void VertexPaintOperation::on_stroke_extended(const bContext &C,
|
||||
const InputSample &extension_sample)
|
||||
{
|
||||
const Scene &scene = *CTX_data_scene(&C);
|
||||
Paint &paint = *BKE_paint_get_active_from_context(&C);
|
||||
const Brush &brush = *BKE_paint_brush(&paint);
|
||||
const bool invert = this->is_inverted(brush);
|
||||
|
||||
const bool is_masking = GPENCIL_ANY_VERTEX_MASK(
|
||||
eGP_vertex_SelectMaskFlag(scene.toolsettings->gpencil_selectmode_vertex));
|
||||
|
||||
const bool do_points = do_vertex_color_points(brush);
|
||||
const bool do_fill = do_vertex_color_fill(brush);
|
||||
|
||||
float color_linear[3];
|
||||
srgb_to_linearrgb_v3_v3(color_linear, BKE_brush_color_get(&scene, &brush));
|
||||
const ColorGeometry4f mix_color(color_linear[0], color_linear[1], color_linear[2], 1.0f);
|
||||
|
||||
this->foreach_editable_drawing(C, GrainSize(1), [&](const GreasePencilStrokeParams ¶ms) {
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask point_selection = point_selection_mask(params, is_masking, memory);
|
||||
if (!point_selection.is_empty() && do_points) {
|
||||
Array<float2> view_positions = calculate_view_positions(params, point_selection);
|
||||
MutableSpan<ColorGeometry4f> vertex_colors = params.drawing.vertex_colors_for_write();
|
||||
|
||||
if (invert) {
|
||||
/* Erase vertex colors. */
|
||||
point_selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
|
||||
const float influence = brush_point_influence(
|
||||
scene, brush, view_positions[point_i], extension_sample, params.multi_frame_falloff);
|
||||
|
||||
ColorGeometry4f &color = vertex_colors[point_i];
|
||||
color.a -= influence;
|
||||
color.a = math::max(color.a, 0.0f);
|
||||
});
|
||||
}
|
||||
else {
|
||||
/* Mix brush color into vertex colors by influence using alpha over. */
|
||||
point_selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
|
||||
const float influence = brush_point_influence(
|
||||
scene, brush, view_positions[point_i], extension_sample, params.multi_frame_falloff);
|
||||
|
||||
ColorGeometry4f &color = vertex_colors[point_i];
|
||||
color = math::interpolate(color, mix_color, influence);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const IndexMask fill_selection = fill_selection_mask(params, is_masking, memory);
|
||||
if (!fill_selection.is_empty() && do_fill) {
|
||||
const OffsetIndices<int> points_by_curve = params.drawing.strokes().points_by_curve();
|
||||
Array<float2> view_positions = calculate_view_positions(params, point_selection);
|
||||
MutableSpan<ColorGeometry4f> fill_colors = params.drawing.fill_colors_for_write();
|
||||
|
||||
if (invert) {
|
||||
fill_selection.foreach_index(GrainSize(1024), [&](const int64_t curve_i) {
|
||||
const IndexRange points = points_by_curve[curve_i];
|
||||
const Span<float2> curve_view_positions = view_positions.as_span().slice(points);
|
||||
const float influence = brush_fill_influence(
|
||||
scene, brush, curve_view_positions, extension_sample, params.multi_frame_falloff);
|
||||
|
||||
ColorGeometry4f &color = fill_colors[curve_i];
|
||||
color.a -= influence;
|
||||
color.a = math::max(color.a, 0.0f);
|
||||
});
|
||||
}
|
||||
else {
|
||||
fill_selection.foreach_index(GrainSize(1024), [&](const int64_t curve_i) {
|
||||
const IndexRange points = points_by_curve[curve_i];
|
||||
const Span<float2> curve_view_positions = view_positions.as_span().slice(points);
|
||||
const float influence = brush_fill_influence(
|
||||
scene, brush, curve_view_positions, extension_sample, params.multi_frame_falloff);
|
||||
|
||||
ColorGeometry4f &color = fill_colors[curve_i];
|
||||
color = math::interpolate(color, mix_color, influence);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
std::unique_ptr<GreasePencilStrokeOperation> new_vertex_paint_operation(
|
||||
const BrushStrokeMode stroke_mode)
|
||||
{
|
||||
return std::make_unique<VertexPaintOperation>(stroke_mode);
|
||||
}
|
||||
|
||||
} // namespace blender::ed::sculpt_paint::greasepencil
|
||||
@@ -0,0 +1,89 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BKE_brush.hh"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_grease_pencil.hh"
|
||||
#include "BKE_paint.hh"
|
||||
|
||||
#include "grease_pencil_intern.hh"
|
||||
|
||||
namespace blender::ed::sculpt_paint::greasepencil {
|
||||
|
||||
class VertexReplaceOperation : public GreasePencilStrokeOperationCommon {
|
||||
using GreasePencilStrokeOperationCommon::GreasePencilStrokeOperationCommon;
|
||||
|
||||
public:
|
||||
void on_stroke_begin(const bContext &C, const InputSample &start_sample) override;
|
||||
void on_stroke_extended(const bContext &C, const InputSample &extension_sample) override;
|
||||
void on_stroke_done(const bContext &C) override;
|
||||
};
|
||||
|
||||
void VertexReplaceOperation::on_stroke_begin(const bContext &C, const InputSample &start_sample)
|
||||
{
|
||||
this->init_stroke(C, start_sample);
|
||||
this->on_stroke_extended(C, start_sample);
|
||||
}
|
||||
|
||||
void VertexReplaceOperation::on_stroke_extended(const bContext &C,
|
||||
const InputSample &extension_sample)
|
||||
{
|
||||
const Scene &scene = *CTX_data_scene(&C);
|
||||
Paint &paint = *BKE_paint_get_active_from_context(&C);
|
||||
const Brush &brush = *BKE_paint_brush(&paint);
|
||||
|
||||
const bool is_masking = GPENCIL_ANY_VERTEX_MASK(
|
||||
eGP_vertex_SelectMaskFlag(scene.toolsettings->gpencil_selectmode_vertex));
|
||||
|
||||
const bool do_points = do_vertex_color_points(brush);
|
||||
const bool do_fill = do_vertex_color_fill(brush);
|
||||
|
||||
float3 color_linear;
|
||||
srgb_to_linearrgb_v3_v3(color_linear, BKE_brush_color_get(&scene, &brush));
|
||||
const ColorGeometry4f replace_color(color_linear.x, color_linear.y, color_linear.z, 1.0f);
|
||||
|
||||
this->foreach_editable_drawing(C, GrainSize(1), [&](const GreasePencilStrokeParams ¶ms) {
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask point_selection = point_selection_mask(params, is_masking, memory);
|
||||
if (!point_selection.is_empty() && do_points) {
|
||||
Array<float2> view_positions = calculate_view_positions(params, point_selection);
|
||||
MutableSpan<ColorGeometry4f> vertex_colors = params.drawing.vertex_colors_for_write();
|
||||
point_selection.foreach_index(GrainSize(4096), [&](const int64_t point_i) {
|
||||
const float influence = brush_point_influence(
|
||||
scene, brush, view_positions[point_i], extension_sample, params.multi_frame_falloff);
|
||||
if (influence > 0.0f && vertex_colors[point_i].a > 0.0f) {
|
||||
vertex_colors[point_i] = replace_color;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const IndexMask fill_selection = fill_selection_mask(params, is_masking, memory);
|
||||
if (!fill_selection.is_empty() && do_fill) {
|
||||
const OffsetIndices<int> points_by_curve = params.drawing.strokes().points_by_curve();
|
||||
Array<float2> view_positions = calculate_view_positions(params, point_selection);
|
||||
MutableSpan<ColorGeometry4f> fill_colors = params.drawing.fill_colors_for_write();
|
||||
|
||||
fill_selection.foreach_index(GrainSize(1024), [&](const int64_t curve_i) {
|
||||
const IndexRange points = points_by_curve[curve_i];
|
||||
const Span<float2> curve_view_positions = view_positions.as_span().slice(points);
|
||||
const float influence = brush_fill_influence(
|
||||
scene, brush, curve_view_positions, extension_sample, params.multi_frame_falloff);
|
||||
if (influence > 0.0f && fill_colors[curve_i].a > 0.0f) {
|
||||
fill_colors[curve_i] = replace_color;
|
||||
}
|
||||
});
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void VertexReplaceOperation::on_stroke_done(const bContext & /*C*/) {}
|
||||
|
||||
std::unique_ptr<GreasePencilStrokeOperation> new_vertex_replace_operation()
|
||||
{
|
||||
return std::make_unique<VertexReplaceOperation>();
|
||||
}
|
||||
|
||||
} // namespace blender::ed::sculpt_paint::greasepencil
|
||||
@@ -0,0 +1,200 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_curves.hh"
|
||||
#include "BKE_grease_pencil.hh"
|
||||
#include "BKE_paint.hh"
|
||||
|
||||
#include "BLI_task.hh"
|
||||
|
||||
#include "grease_pencil_intern.hh"
|
||||
|
||||
namespace blender::ed::sculpt_paint::greasepencil {
|
||||
|
||||
struct ColorGrid {
|
||||
/* Flat array of colors. The length of this is size^2. */
|
||||
Array<float4> colors;
|
||||
/* Size of the grid. Used as the width and height. Should be divisible by 2. */
|
||||
int size;
|
||||
/* The size of each cell in pixels (screen space). Used as the cell width and height. */
|
||||
int cell_size_px;
|
||||
/* The center position of the grid (screen space). */
|
||||
float2 center;
|
||||
|
||||
/* Compute the screen space position based on a grid position and a center. */
|
||||
float2 pos_to_coords(const int2 pos, const float2 center) const
|
||||
{
|
||||
const float2 centered = float2(pos - this->size / 2) + float2(0.5f);
|
||||
return (centered * this->cell_size_px) + center;
|
||||
}
|
||||
|
||||
/* Compute a grid position based on a screen space position and a center. */
|
||||
int2 coords_to_pos(const float2 coord, const float2 center) const
|
||||
{
|
||||
const int2 pos = int2(math::floor((coord - center) / float(this->cell_size_px)));
|
||||
return pos + ((this->size + 1) / 2);
|
||||
}
|
||||
|
||||
/* Compute a grid index (into the colors array) based on a grid position. Returns -1 if the
|
||||
* position is out of bounds. */
|
||||
int pos_to_index(const int2 pos) const
|
||||
{
|
||||
if (pos.x >= 0 && pos.x < this->size && pos.y >= 0 && pos.y < this->size) {
|
||||
return pos.y * this->size + pos.x;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
class VertexSmearOperation : public GreasePencilStrokeOperationCommon {
|
||||
using GreasePencilStrokeOperationCommon::GreasePencilStrokeOperationCommon;
|
||||
|
||||
public:
|
||||
void on_stroke_begin(const bContext &C, const InputSample &start_sample) override;
|
||||
void on_stroke_extended(const bContext &C, const InputSample &extension_sample) override;
|
||||
void on_stroke_done(const bContext &C) override;
|
||||
|
||||
private:
|
||||
ColorGrid color_grid_;
|
||||
void init_color_grid(const bContext &C, float2 start_position);
|
||||
};
|
||||
|
||||
void VertexSmearOperation::init_color_grid(const bContext &C, const float2 start_position)
|
||||
{
|
||||
const Scene &scene = *CTX_data_scene(&C);
|
||||
Paint &paint = *BKE_paint_get_active_from_context(&C);
|
||||
const Brush &brush = *BKE_paint_brush(&paint);
|
||||
const bool is_masking = GPENCIL_ANY_VERTEX_MASK(
|
||||
eGP_vertex_SelectMaskFlag(scene.toolsettings->gpencil_selectmode_vertex));
|
||||
const float radius = brush_radius(scene, brush, 1.0f);
|
||||
|
||||
/* Setup grid values. */
|
||||
/* TODO: Make this a setting. */
|
||||
color_grid_.cell_size_px = 10.0f;
|
||||
color_grid_.center = start_position;
|
||||
color_grid_.size = int(math::ceil((radius * 2.0f) / color_grid_.cell_size_px));
|
||||
|
||||
/* Initialize the color array. */
|
||||
const int grid_array_length = color_grid_.size * color_grid_.size;
|
||||
color_grid_.colors.reinitialize(grid_array_length);
|
||||
color_grid_.colors.fill(float4(0.0f));
|
||||
|
||||
/* Initialize grid values. */
|
||||
this->foreach_editable_drawing(C, [&](const GreasePencilStrokeParams ¶ms) {
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask point_selection = point_selection_mask(params, is_masking, memory);
|
||||
if (point_selection.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
const Array<float2> view_positions = calculate_view_positions(params, point_selection);
|
||||
const Array<float> radii = calculate_view_radii(params, point_selection);
|
||||
const VArray<ColorGeometry4f> vertex_colors = params.drawing.vertex_colors();
|
||||
/* Compute the colors in the grid by averaging the vertex colors of the points that
|
||||
* intersect each cell. */
|
||||
Array<int> points_per_cell(grid_array_length, 0);
|
||||
point_selection.foreach_index([&](const int point_i) {
|
||||
const float2 view_pos = view_positions[point_i];
|
||||
const float view_radius = radii[point_i];
|
||||
const ColorGeometry4f color = vertex_colors[point_i];
|
||||
|
||||
const int bounds_size = math::floor(view_radius / color_grid_.cell_size_px) * 2 + 1;
|
||||
const int2 bounds_center = color_grid_.coords_to_pos(view_pos, color_grid_.center);
|
||||
const int2 bounds_min = bounds_center - (bounds_size / 2);
|
||||
const int2 bounds_max = bounds_center + (bounds_size / 2);
|
||||
if (!(bounds_min.x < color_grid_.size && bounds_max.x >= 0 &&
|
||||
bounds_min.y < color_grid_.size && bounds_max.y >= 0))
|
||||
{
|
||||
/* Point is out of bounds. */
|
||||
return;
|
||||
}
|
||||
for (int y = bounds_min.y; y <= bounds_max.y; y++) {
|
||||
for (int x = bounds_min.x; x <= bounds_max.x; x++) {
|
||||
const int2 grid_pos = int2(x, y);
|
||||
const int cell_i = color_grid_.pos_to_index(grid_pos);
|
||||
if (cell_i == -1) {
|
||||
continue;
|
||||
}
|
||||
const float2 cell_pos = color_grid_.pos_to_coords(grid_pos, color_grid_.center);
|
||||
if (math::distance_squared(cell_pos, view_pos) <= view_radius * view_radius) {
|
||||
color_grid_.colors[cell_i] += float4(color.r, color.g, color.b, 1.0f);
|
||||
points_per_cell[cell_i]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
/* Divide by the total to get the average color per cell. */
|
||||
for (const int cell_i : color_grid_.colors.index_range()) {
|
||||
if (points_per_cell[cell_i] > 0) {
|
||||
color_grid_.colors[cell_i] *= 1.0f / float(points_per_cell[cell_i]);
|
||||
}
|
||||
}
|
||||
/* Don't trigger updates for the grid initialization. */
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
void VertexSmearOperation::on_stroke_begin(const bContext &C, const InputSample &start_sample)
|
||||
{
|
||||
this->init_stroke(C, start_sample);
|
||||
this->init_color_grid(C, start_sample.mouse_position);
|
||||
}
|
||||
|
||||
void VertexSmearOperation::on_stroke_extended(const bContext &C,
|
||||
const InputSample &extension_sample)
|
||||
{
|
||||
const Scene &scene = *CTX_data_scene(&C);
|
||||
Paint &paint = *BKE_paint_get_active_from_context(&C);
|
||||
const Brush &brush = *BKE_paint_brush(&paint);
|
||||
const float radius = brush_radius(scene, brush, extension_sample.pressure);
|
||||
|
||||
const bool is_masking = GPENCIL_ANY_VERTEX_MASK(
|
||||
eGP_vertex_SelectMaskFlag(scene.toolsettings->gpencil_selectmode_vertex));
|
||||
|
||||
this->foreach_editable_drawing(C, GrainSize(1), [&](const GreasePencilStrokeParams ¶ms) {
|
||||
IndexMaskMemory memory;
|
||||
const IndexMask point_selection = point_selection_mask(params, is_masking, memory);
|
||||
if (point_selection.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
const Array<float2> view_positions = calculate_view_positions(params, point_selection);
|
||||
MutableSpan<ColorGeometry4f> vertex_colors = params.drawing.vertex_colors_for_write();
|
||||
point_selection.foreach_index(GrainSize(1024), [&](const int64_t point_i) {
|
||||
const float2 view_pos = view_positions[point_i];
|
||||
const int2 grid_pos = color_grid_.coords_to_pos(view_pos, extension_sample.mouse_position);
|
||||
const int cell_i = color_grid_.pos_to_index(grid_pos);
|
||||
if (cell_i == -1 || color_grid_.colors[cell_i][3] == 0.0f) {
|
||||
return;
|
||||
}
|
||||
const ColorGeometry4f mix_color = ColorGeometry4f(color_grid_.colors[cell_i]);
|
||||
|
||||
const float distance_falloff = math::clamp(
|
||||
1.0f - (math::distance(color_grid_.center, view_pos) / radius * 2), 0.0f, 1.0f);
|
||||
const float influence = brush_point_influence(scene,
|
||||
brush,
|
||||
view_pos,
|
||||
extension_sample,
|
||||
params.multi_frame_falloff) *
|
||||
distance_falloff;
|
||||
if (influence > 0.0f) {
|
||||
ColorGeometry4f &color = vertex_colors[point_i];
|
||||
const float alpha = color.a;
|
||||
color = math::interpolate(color, mix_color, influence);
|
||||
color.a = alpha;
|
||||
}
|
||||
});
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
void VertexSmearOperation::on_stroke_done(const bContext & /*C*/) {}
|
||||
|
||||
std::unique_ptr<GreasePencilStrokeOperation> new_vertex_smear_operation()
|
||||
{
|
||||
return std::make_unique<VertexSmearOperation>();
|
||||
}
|
||||
|
||||
} // namespace blender::ed::sculpt_paint::greasepencil
|
||||
@@ -1451,7 +1451,7 @@ static void paint_update_mouse_cursor(PaintCursorContext *pcontext)
|
||||
* with the UI (dragging a number button for e.g.), see: #102792. */
|
||||
return;
|
||||
}
|
||||
if (pcontext->mode == PaintMode::GPencil) {
|
||||
if (ELEM(pcontext->mode, PaintMode::GPencil, PaintMode::VertexGPencil)) {
|
||||
WM_cursor_set(pcontext->win, WM_CURSOR_DOT);
|
||||
}
|
||||
else {
|
||||
@@ -1528,8 +1528,10 @@ static void grease_pencil_brush_cursor_draw(PaintCursorContext *pcontext)
|
||||
return;
|
||||
}
|
||||
|
||||
/* default radius and color */
|
||||
pcontext->pixel_radius = brush->size;
|
||||
/* Hide the cursor while drawing. */
|
||||
if (grease_pencil->runtime->is_drawing_stroke) {
|
||||
return;
|
||||
}
|
||||
|
||||
float3 color(1.0f);
|
||||
const int x = pcontext->x;
|
||||
@@ -1544,10 +1546,10 @@ static void grease_pencil_brush_cursor_draw(PaintCursorContext *pcontext)
|
||||
/* If we use the eraser from the draw tool with a "scene" radius unit, we need to draw the
|
||||
* cursor with the appropriate size. */
|
||||
if (grease_pencil->runtime->temp_use_eraser && (brush->flag & BRUSH_LOCK_SIZE) != 0) {
|
||||
pcontext->pixel_radius = grease_pencil->runtime->temp_eraser_size;
|
||||
pcontext->pixel_radius = int(grease_pencil->runtime->temp_eraser_size);
|
||||
}
|
||||
else {
|
||||
pcontext->pixel_radius = float(pcontext->brush->size);
|
||||
pcontext->pixel_radius = brush->size;
|
||||
}
|
||||
grease_pencil_eraser_draw(pcontext);
|
||||
return;
|
||||
@@ -1596,6 +1598,10 @@ static void grease_pencil_brush_cursor_draw(PaintCursorContext *pcontext)
|
||||
color = scale * float3(paint->paint_cursor_col);
|
||||
}
|
||||
}
|
||||
else if (pcontext->mode == PaintMode::VertexGPencil) {
|
||||
pcontext->pixel_radius = BKE_brush_size_get(pcontext->vc.scene, brush);
|
||||
color = BKE_brush_color_get(pcontext->vc.scene, brush);
|
||||
}
|
||||
|
||||
GPU_line_width(1.0f);
|
||||
/* Inner Ring: Color from UI panel */
|
||||
@@ -1611,13 +1617,12 @@ static void grease_pencil_brush_cursor_draw(PaintCursorContext *pcontext)
|
||||
static void paint_draw_2D_view_brush_cursor(PaintCursorContext *pcontext)
|
||||
{
|
||||
switch (pcontext->mode) {
|
||||
case PaintMode::GPencil: {
|
||||
case PaintMode::GPencil:
|
||||
case PaintMode::VertexGPencil:
|
||||
grease_pencil_brush_cursor_draw(pcontext);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
default:
|
||||
paint_draw_2D_view_brush_cursor_default(pcontext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -418,6 +418,10 @@ static void view3d_main_region_init(wmWindowManager *wm, ARegion *region)
|
||||
wm->defaultconf, "Grease Pencil Weight Paint", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
WM_event_add_keymap_handler(®ion->handlers, keymap);
|
||||
|
||||
keymap = WM_keymap_ensure(
|
||||
wm->defaultconf, "Grease Pencil Vertex Paint", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
WM_event_add_keymap_handler(®ion->handlers, keymap);
|
||||
|
||||
keymap = WM_keymap_ensure(
|
||||
wm->defaultconf, "Grease Pencil Brush Stroke", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
WM_event_add_keymap_handler(®ion->handlers, keymap);
|
||||
@@ -1731,6 +1735,9 @@ void ED_view3d_buttons_region_layout_ex(const bContext *C,
|
||||
case CTX_MODE_WEIGHT_GREASE_PENCIL:
|
||||
ARRAY_SET_ITEMS(contexts, ".greasepencil_weight");
|
||||
break;
|
||||
case CTX_MODE_VERTEX_GREASE_PENCIL:
|
||||
ARRAY_SET_ITEMS(contexts, ".grease_pencil_vertex");
|
||||
break;
|
||||
case CTX_MODE_EDIT_POINT_CLOUD:
|
||||
ARRAY_SET_ITEMS(contexts, ".point_cloud_edit");
|
||||
break;
|
||||
|
||||
@@ -48,6 +48,7 @@ const EnumPropertyItem rna_enum_context_mode_items[] = {
|
||||
{CTX_MODE_PAINT_GREASE_PENCIL, "PAINT_GREASE_PENCIL", 0, "Grease Pencil Paint", ""},
|
||||
{CTX_MODE_SCULPT_GREASE_PENCIL, "SCULPT_GREASE_PENCIL", 0, "Grease Pencil Sculpt", ""},
|
||||
{CTX_MODE_WEIGHT_GREASE_PENCIL, "WEIGHT_GREASE_PENCIL", 0, "Grease Pencil Weight Paint", ""},
|
||||
{CTX_MODE_VERTEX_GREASE_PENCIL, "VERTEX_GREASE_PENCIL", 0, "Grease Pencil Vertex Paint", ""},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
|
||||
@@ -162,6 +162,9 @@ wmKeyMap *WM_keymap_guess_from_context(const bContext *C)
|
||||
case CTX_MODE_WEIGHT_GREASE_PENCIL:
|
||||
km_id = "Grease Pencil Weight Mode";
|
||||
break;
|
||||
case CTX_MODE_VERTEX_GREASE_PENCIL:
|
||||
km_id = "Grease Pencil Vertex Mode";
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (sl->spacetype == SPACE_IMAGE) {
|
||||
|
||||
@@ -670,6 +670,7 @@ static const char *toolsystem_default_tool(const bToolKey *tkey)
|
||||
case CTX_MODE_WEIGHT_GPENCIL_LEGACY:
|
||||
case CTX_MODE_WEIGHT_GREASE_PENCIL:
|
||||
case CTX_MODE_VERTEX_GPENCIL_LEGACY:
|
||||
case CTX_MODE_VERTEX_GREASE_PENCIL:
|
||||
case CTX_MODE_SCULPT_CURVES:
|
||||
return "builtin.brush";
|
||||
case CTX_MODE_PARTICLE:
|
||||
|
||||
Reference in New Issue
Block a user