WIP: Brush assets project #106303

Draft
Julian Eisel wants to merge 381 commits from brush-assets-project into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
86 changed files with 3128 additions and 3952 deletions

@ -1 +1 @@
Subproject commit 7181d6dccb9fe4184340f9f5b1c381f8089fe4ec
Subproject commit 1c9b964b58948b369a5d5c84b35db809fc843d45

View File

@ -107,10 +107,6 @@ def generate(context, space_type, *, use_fallback_keys=True, use_reset=True):
kmi_hack_properties = kmi_hack.properties
kmi_hack.active = False
kmi_hack_brush_select = keymap.keymap_items.new("paint.brush_select", 'NONE', 'PRESS')
kmi_hack_brush_select_properties = kmi_hack_brush_select.properties
kmi_hack_brush_select.active = False
if use_release_confirm or use_tap_reset:
kmi_toolbar = wm.keyconfigs.find_item_from_operator(
idname="wm.toolbar",
@ -169,48 +165,6 @@ def generate(context, space_type, *, use_fallback_keys=True, use_reset=True):
include={'KEYBOARD'},
)[1]
if kmi_found is None:
if item.data_block:
# PAINT_OT_brush_select
mode = context.active_object.mode
# See: BKE_paint_get_tool_prop_id_from_paintmode
if space_type == 'IMAGE_EDITOR':
if context.space_data.mode == 'PAINT':
attr = "image_tool"
else:
attr = None
elif space_type == 'VIEW_3D':
attr = {
'SCULPT': "sculpt_tool",
'VERTEX_PAINT': "vertex_tool",
'WEIGHT_PAINT': "weight_tool",
'TEXTURE_PAINT': "image_tool",
'PAINT_GPENCIL': "gpencil_tool",
'PAINT_GREASE_PENCIL': "gpencil_tool",
'VERTEX_GPENCIL': "gpencil_vertex_tool",
'SCULPT_GPENCIL': "gpencil_sculpt_tool",
'WEIGHT_GPENCIL': "gpencil_weight_tool",
'SCULPT_CURVES': "curves_sculpt_tool",
'PAINT_GREASE_PENCIL': "gpencil_tool",
}.get(mode, None)
else:
attr = None
if attr is not None:
setattr(kmi_hack_brush_select_properties, attr, item.data_block)
kmi_found = wm.keyconfigs.find_item_from_operator(
idname="paint.brush_select",
context='INVOKE_REGION_WIN',
properties=kmi_hack_brush_select_properties,
include={'KEYBOARD'},
)[1]
elif mode in {'EDIT', 'PARTICLE_EDIT', 'SCULPT_GPENCIL'}:
# Doesn't use brushes
pass
else:
print("Unsupported mode:", mode)
del mode, attr
else:
kmi_found = None
@ -403,7 +357,6 @@ def generate(context, space_type, *, use_fallback_keys=True, use_reset=True):
if use_hack_properties:
keymap.keymap_items.remove(kmi_hack)
keymap.keymap_items.remove(kmi_hack_brush_select)
# Keep last so we can try add a key without any modifiers
# in the case this toolbar was activated with modifiers.

View File

@ -279,6 +279,10 @@ def op_panel(menu, kmi_args, kmi_data=()):
return ("wm.call_panel", kmi_args, {"properties": [("name", menu), *kmi_data]})
def op_asset_shelf_popup(asset_shelf, kmi_args):
return ("wm.call_asset_shelf_popover", kmi_args, {"properties": [("name", asset_shelf)]})
def op_tool(tool, kmi_args):
return ("wm.tool_set_by_id", kmi_args, {"properties": [("name", tool)]})
@ -3906,6 +3910,10 @@ def km_gpencil_legacy_stroke_paint_mode(params):
("gpencil.select_box", {"type": 'B', "value": 'PRESS'}, None),
# Lasso erase
("gpencil.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None),
op_asset_shelf_popup(
"VIEW3D_AST_brush_gpencil_paint",
{"type": 'SPACE', "value": 'PRESS', "shift": True}
),
])
return keymap
@ -4072,6 +4080,10 @@ def km_gpencil_legacy_stroke_sculpt_mode(params):
"VIEW3D_MT_sculpt_gpencil_automasking_pie",
{"type": 'A', "shift": True, "alt": True, "value": 'PRESS'},
),
op_asset_shelf_popup(
"VIEW3D_AST_brush_gpencil_sculpt",
{"type": 'SPACE', "value": 'PRESS', "shift": True}
),
])
return keymap
@ -4299,6 +4311,10 @@ def km_gpencil_legacy_stroke_weight_mode(params):
("gpencil.weight_toggle_direction", {"type": 'D', "value": 'PRESS'}, None),
# Weight sample
("gpencil.weight_sample", {"type": 'X', "value": 'PRESS', "shift": True}, None),
op_asset_shelf_popup(
"VIEW3D_AST_brush_gpencil_weight",
{"type": 'SPACE', "value": 'PRESS', "shift": True}
),
])
if params.select_mouse == 'LEFTMOUSE':
@ -4428,6 +4444,10 @@ def km_gpencil_legacy_stroke_vertex_mode(params):
# Vertex Paint context menu
op_panel("VIEW3D_PT_gpencil_vertex_context_menu", params.context_menu_event),
op_asset_shelf_popup(
"VIEW3D_AST_brush_gpencil_vertex",
{"type": 'SPACE', "value": 'PRESS', "shift": True}
),
])
return keymap
@ -4579,6 +4599,11 @@ def km_grease_pencil_paint_mode(_params):
# Isolate Layer
("grease_pencil.layer_isolate", {"type": 'NUMPAD_ASTERIX', "value": 'PRESS'}, None),
op_asset_shelf_popup(
"VIEW3D_AST_brush_gpencil_paint",
{"type": 'SPACE', "value": 'PRESS', "shift": True}
),
])
return keymap
@ -4721,6 +4746,10 @@ def km_grease_pencil_sculpt_mode(params):
("grease_pencil.sculpt_paint", {"type": 'LEFTMOUSE', "value": 'PRESS',
"shift": True}, {"properties": [("mode", 'SMOOTH')]}),
*_template_paint_radial_control("gpencil_sculpt_paint"),
op_asset_shelf_popup(
"VIEW3D_AST_brush_gpencil_sculpt",
{"type": 'SPACE', "value": 'PRESS', "shift": True}
),
])
return keymap
@ -4760,6 +4789,11 @@ def km_grease_pencil_weight_paint(params):
# Show/hide layer
*_template_items_hide_reveal_actions("grease_pencil.layer_hide", "grease_pencil.layer_reveal"),
op_asset_shelf_popup(
"VIEW3D_AST_brush_gpencil_weight",
{"type": 'SPACE', "value": 'PRESS', "shift": True}
),
])
if params.select_mouse == 'LEFTMOUSE':
@ -5087,6 +5121,10 @@ def km_paint_curve(params):
("transform.translate", {"type": params.select_mouse, "value": 'CLICK_DRAG'}, None),
("transform.rotate", {"type": 'R', "value": 'PRESS'}, None),
("transform.resize", {"type": 'S', "value": 'PRESS'}, None),
op_asset_shelf_popup(
"VIEW3D_AST_brush_sculpt_curves",
{"type": 'SPACE', "value": 'PRESS', "shift": True}
),
])
return keymap
@ -5364,6 +5402,14 @@ def km_image_paint(params):
("wm.context_menu_enum", {"type": 'E', "value": 'PRESS', "alt": True},
{"properties": [("data_path", 'tool_settings.image_paint.brush.stroke_method')]}),
*_template_items_context_panel("VIEW3D_PT_paint_texture_context_menu", params.context_menu_event),
op_asset_shelf_popup(
"VIEW3D_AST_brush_texture_paint",
{"type": 'SPACE', "value": 'PRESS', "shift": True}
),
op_asset_shelf_popup(
"IMAGE_AST_brush_paint",
{"type": 'SPACE', "value": 'PRESS', "shift": True}
),
])
if params.legacy:
@ -5414,6 +5460,10 @@ def km_vertex_paint(params):
{"properties": [("data_path", 'tool_settings.vertex_paint.brush.stroke_method')]}),
("paint.face_vert_reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None),
*_template_items_context_panel("VIEW3D_PT_paint_vertex_context_menu", params.context_menu_event),
op_asset_shelf_popup(
"VIEW3D_AST_brush_vertex_paint",
{"type": 'SPACE', "value": 'PRESS', "shift": True}
),
])
if params.legacy:
@ -5471,6 +5521,10 @@ def km_weight_paint(params):
op_menu_pie("VIEW3D_MT_wpaint_vgroup_lock_pie", {"type": 'K', "value": 'PRESS'}),
("paint.face_vert_reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None),
*_template_items_context_panel("VIEW3D_PT_paint_weight_context_menu", params.context_menu_event),
op_asset_shelf_popup(
"VIEW3D_AST_brush_weight_paint",
{"type": 'SPACE', "value": 'PRESS', "shift": True}
),
])
if params.select_mouse == 'LEFTMOUSE':
@ -5666,27 +5720,41 @@ def km_sculpt(params):
op_menu_pie("VIEW3D_MT_sculpt_automasking_pie", {"type": 'A', "alt": True, "value": 'PRESS'}),
op_menu_pie("VIEW3D_MT_sculpt_face_sets_edit_pie", {"type": 'W', "value": 'PRESS', "alt": True}),
*_template_items_context_panel("VIEW3D_PT_sculpt_context_menu", params.context_menu_event),
# Tools
("paint.brush_select", {"type": 'V', "value": 'PRESS'},
{"properties": [("sculpt_tool", 'DRAW')]}),
("paint.brush_select", {"type": 'S', "value": 'PRESS'},
{"properties": [("sculpt_tool", 'SMOOTH')]}),
("paint.brush_select", {"type": 'P', "value": 'PRESS'},
{"properties": [("sculpt_tool", 'PINCH')]}),
("paint.brush_select", {"type": 'I', "value": 'PRESS'},
{"properties": [("sculpt_tool", 'INFLATE')]}),
("paint.brush_select", {"type": 'G', "value": 'PRESS'},
{"properties": [("sculpt_tool", 'GRAB')]}),
("paint.brush_select", {"type": 'T', "value": 'PRESS', "shift": True},
{"properties": [("sculpt_tool", 'SCRAPE')]}),
("paint.brush_select", {"type": 'C', "value": 'PRESS'},
{"properties": [("sculpt_tool", 'CLAY_STRIPS')]}),
("paint.brush_select", {"type": 'C', "value": 'PRESS', "shift": True},
{"properties": [("sculpt_tool", 'CREASE')]}),
("paint.brush_select", {"type": 'K', "value": 'PRESS'},
{"properties": [("sculpt_tool", 'SNAKE_HOOK')]}),
("paint.brush_select", {"type": 'M', "value": 'PRESS'},
{"properties": [("sculpt_tool", 'MASK'), ("toggle", True), ("create_missing", True)]}),
# Brushes
("brush.asset_select", {"type": 'V', "value": 'PRESS'},
{"properties": [("asset_library_type", 'ESSENTIALS'),
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Draw")]}),
("brush.asset_select", {"type": 'S', "value": 'PRESS'},
{"properties": [("asset_library_type", 'ESSENTIALS'),
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Smooth")]}),
("brush.asset_select", {"type": 'P', "value": 'PRESS'},
{"properties": [("asset_library_type", 'ESSENTIALS'),
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Pinch/Magnify")]}),
("brush.asset_select", {"type": 'I', "value": 'PRESS'},
{"properties": [("asset_library_type", 'ESSENTIALS'),
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Inflate/Deflate")]}),
("brush.asset_select", {"type": 'G', "value": 'PRESS'},
{"properties": [("asset_library_type", 'ESSENTIALS'),
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Grab")]}),
("brush.asset_select", {"type": 'T', "value": 'PRESS', "shift": True},
{"properties": [("asset_library_type", 'ESSENTIALS'),
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Scrape/Fill")]}),
("brush.asset_select", {"type": 'C', "value": 'PRESS'},
{"properties": [("asset_library_type", 'ESSENTIALS'),
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Clay Strips")]}),
("brush.asset_select", {"type": 'C', "value": 'PRESS', "shift": True},
{"properties": [("asset_library_type", 'ESSENTIALS'),
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Crease")]}),
("brush.asset_select", {"type": 'K', "value": 'PRESS'},
{"properties": [("asset_library_type", 'ESSENTIALS'),
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Snake Hook")]}),
("brush.asset_select", {"type": 'M', "value": 'PRESS'},
{"properties": [("asset_library_type", 'ESSENTIALS'),
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Mask")]}),
op_asset_shelf_popup(
"VIEW3D_AST_brush_sculpt",
{"type": 'SPACE', "value": 'PRESS', "shift": True}
),
])
# Lasso Masking.

View File

@ -2,24 +2,76 @@
#
# SPDX-License-Identifier: GPL-2.0-or-later
import bpy
from bpy.types import Menu
class BrushAssetShelf:
bl_options = {'DEFAULT_VISIBLE', 'NO_ASSET_DRAG', 'STORE_ENABLED_CATALOGS_IN_PREFERENCES'}
bl_activate_operator = "BRUSH_OT_asset_select"
bl_default_preview_size = 48
@classmethod
def poll(cls, context):
prefs = context.preferences
if not prefs.experimental.use_extended_asset_browser:
return False
return context.mode == 'SCULPT'
return context.object and context.object.mode == cls.mode
@classmethod
def asset_poll(cls, asset):
return asset.id_type == 'BRUSH'
if asset.id_type != 'BRUSH':
return False
if hasattr(cls, "mode_prop"):
return asset.metadata.get(cls.mode_prop, False)
return True
@classmethod
def get_active_asset(cls):
# Only show active highlight when using the brush tool.
from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
tool = ToolSelectPanelHelper.tool_active_from_context(bpy.context)
if not tool or tool.idname != "builtin.brush":
return None
paint_settings = UnifiedPaintPanel.paint_settings(bpy.context)
return paint_settings.brush_asset_reference if paint_settings else None
@classmethod
def draw_context_menu(self, context, asset, layout):
# Currently this menu adds operators that deal with the affected brush and don't take the
# asset into account. Luckily that is okay for now, since right clicking in the grid view
# also activates the item.
layout.menu_contents("VIEW3D_MT_brush_context_menu")
@staticmethod
def get_shelf_name_from_context(context):
mode_map = {
'SCULPT': "VIEW3D_AST_brush_sculpt",
'PAINT_VERTEX': "VIEW3D_AST_brush_vertex_paint",
'PAINT_WEIGHT': "VIEW3D_AST_brush_weight_paint",
'PAINT_TEXTURE': "VIEW3D_AST_brush_texture_paint",
'PAINT_2D': "IMAGE_AST_brush_paint",
'PAINT_GPENCIL': "VIEW3D_AST_brush_gpencil_paint",
'SCULPT_GPENCIL': "VIEW3D_AST_brush_gpencil_sculpt",
'WEIGHT_GPENCIL': "VIEW3D_AST_brush_gpencil_weight",
'VERTEX_GPENCIL': "VIEW3D_AST_brush_gpencil_vertex",
'PAINT_GREASE_PENCIL': "VIEW3D_AST_brush_gpencil_paint",
'SCULPT_GREASE_PENCIL': "VIEW3D_AST_brush_gpencil_sculpt",
'WEIGHT_GREASE_PENCIL': "VIEW3D_AST_brush_gpencil_weight",
'VERTEX_GREASE_PENCIL': "VIEW3D_AST_brush_gpencil_vertex",
'SCULPT_CURVES': "VIEW3D_AST_brush_sculpt_curves",
}
mode = UnifiedPaintPanel.get_brush_mode(context)
return mode_map[mode]
@staticmethod
def draw_popup_selector(layout, context, brush, show_name=True):
preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0
layout.template_asset_shelf_popover(
BrushAssetShelf.get_shelf_name_from_context(context),
name=brush.name if (brush and show_name) else None,
icon='BRUSH_DATA' if not preview_icon_id else 'NONE',
icon_value=preview_icon_id,
)
class UnifiedPaintPanel:
@ -156,28 +208,29 @@ class BrushPanel(UnifiedPaintPanel):
class BrushSelectPanel(BrushPanel):
bl_label = "Brushes"
bl_label = "Brush Asset"
def draw(self, context):
layout = self.layout
settings = self.paint_settings(context)
if settings is None:
return
brush = settings.brush
row = layout.row()
large_preview = True
if large_preview:
row.column().template_ID_preview(settings, "brush", new="brush.add", rows=3, cols=8, hide_buttons=False)
else:
row.column().template_ID(settings, "brush", new="brush.add")
col = row.column(align=True)
BrushAssetShelf.draw_popup_selector(col, context, brush, show_name=False)
if brush:
col.prop(brush, "name", text="")
if brush is None:
return
col = row.column()
col.menu("VIEW3D_MT_brush_context_menu", icon='DOWNARROW_HLT', text="")
if brush is not None:
col.prop(brush, "use_custom_icon", toggle=True, icon='FILE_IMAGE', text="")
if brush.use_custom_icon:
layout.prop(brush, "icon_filepath", text="")
class ColorPalettePanel(BrushPanel):
bl_label = "Color Palette"
@ -994,6 +1047,9 @@ def brush_settings_advanced(layout, context, brush, popover=False):
use_frontface = False
if mode == 'SCULPT':
layout.prop(brush, "sculpt_tool")
layout.separator()
capabilities = brush.sculpt_capabilities
use_accumulate = capabilities.has_accumulate
use_frontface = True
@ -1084,6 +1140,9 @@ def brush_settings_advanced(layout, context, brush, popover=False):
# 3D and 2D Texture Paint.
elif mode in {'PAINT_TEXTURE', 'PAINT_2D'}:
layout.prop(brush, "image_tool")
layout.separator()
capabilities = brush.image_paint_capabilities
use_accumulate = capabilities.has_accumulate
@ -1111,6 +1170,9 @@ def brush_settings_advanced(layout, context, brush, popover=False):
# Vertex Paint #
elif mode == 'PAINT_VERTEX':
layout.prop(brush, "vertex_tool")
layout.separator()
layout.prop(brush, "use_alpha")
if brush.vertex_tool != 'SMEAR':
use_accumulate = True
@ -1118,10 +1180,17 @@ def brush_settings_advanced(layout, context, brush, popover=False):
# Weight Paint
elif mode == 'PAINT_WEIGHT':
layout.prop(brush, "weight_tool")
layout.separator()
if brush.weight_tool != 'SMEAR':
use_accumulate = True
use_frontface = True
# Sculpt Curves
elif mode == 'SCULPT_CURVES':
layout.prop(brush, "curves_sculpt_tool")
# Draw shared settings.
if use_accumulate:
layout.prop(brush, "use_accumulate")
@ -1129,6 +1198,29 @@ def brush_settings_advanced(layout, context, brush, popover=False):
if use_frontface:
layout.prop(brush, "use_frontface", text="Front Faces Only")
# Brush modes
header, panel = layout.panel("modes", default_closed=True)
header.label(text="Modes")
if panel:
panel.use_property_split = True
panel.use_property_decorate = False
col = panel.column(align=True)
col.prop(brush, "use_paint_sculpt", text="Sculpt")
col.prop(brush, "use_paint_uv_sculpt", text="UV Sculpt")
col.prop(brush, "use_paint_vertex", text="Vertex Paint")
col.prop(brush, "use_paint_weight", text="Weight Paint")
col.prop(brush, "use_paint_image", text="Texture Paint")
col.prop(brush, "use_paint_sculpt_curves", text="Sculpt Curves")
if len(brush.icon_filepath) > 0:
header, panel = layout.panel("legacy", default_closed=True)
header.label(text="Legacy Icon")
if panel:
panel.label(text="Brush icons have moved to the asset preview image", icon='ERROR')
panel.prop(brush, "use_custom_icon")
panel.prop(brush, "icon_filepath")
def draw_color_settings(context, layout, brush, color_type=False):
"""Draw color wheel and gradient settings."""
@ -1354,7 +1446,6 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False)
row.prop(brush, "size", text="Radius")
row.prop(gp_settings, "use_pressure", text="", icon='STYLUS_PRESSURE')
row.prop(gp_settings, "use_occlude_eraser", text="", icon='XRAY')
row.prop(gp_settings, "use_default_eraser", text="")
row = layout.row(align=True)
row.prop(gp_settings, "eraser_mode", expand=True)

View File

@ -3,6 +3,7 @@
# SPDX-License-Identifier: GPL-2.0-or-later
from bpy.types import (
AssetShelf,
Header,
Menu,
Panel,
@ -23,6 +24,7 @@ from bl_ui.properties_paint_common import (
SmoothStrokePanel,
FalloffPanel,
DisplayPanel,
BrushAssetShelf,
)
from bl_ui.properties_grease_pencil_common import (
AnnotationDataPanel,
@ -764,9 +766,10 @@ class _draw_tool_settings_context_mode:
return
paint = context.tool_settings.image_paint
layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
brush = paint.brush
BrushAssetShelf.draw_popup_selector(layout, context, brush)
if brush is None:
return
@ -1182,7 +1185,7 @@ class IMAGE_PT_udim_tiles(Panel):
class IMAGE_PT_paint_select(Panel, ImagePaintPanel, BrushSelectPanel):
bl_label = "Brushes"
bl_label = "Brush Asset"
bl_context = ".paint_common_2d"
bl_category = "Tool"
@ -1220,7 +1223,7 @@ class IMAGE_PT_paint_settings_advanced(Panel, ImagePaintPanel):
settings = context.tool_settings.image_paint
brush = settings.brush
if brush:
brush_settings_advanced(layout.column(), context, brush, self.is_popover)
@ -1234,15 +1237,16 @@ class IMAGE_PT_paint_color(Panel, ImagePaintPanel):
def poll(cls, context):
settings = context.tool_settings.image_paint
brush = settings.brush
if not brush:
return False
capabilities = brush.image_paint_capabilities
return capabilities.has_color
def draw(self, context):
layout = self.layout
settings = context.tool_settings.image_paint
brush = settings.brush
if brush:
draw_color_settings(context, layout, brush, color_type=True)
@ -1688,6 +1692,18 @@ class IMAGE_PT_annotation(AnnotationDataPanel, Panel):
# Grease Pencil drawing tools.
class ImageAssetShelf(BrushAssetShelf):
bl_space_type = "IMAGE_EDITOR"
class IMAGE_AST_brush_paint(ImageAssetShelf, AssetShelf):
mode_prop = "use_paint_image"
@classmethod
def poll(cls, context):
return context.space_data and context.space_data.ui_mode == 'PAINT'
classes = (
IMAGE_MT_view,
IMAGE_MT_view_zoom,
@ -1757,6 +1773,7 @@ classes = (
IMAGE_PT_overlay_uv_edit_geometry,
IMAGE_PT_overlay_texture_paint,
IMAGE_PT_overlay_image,
IMAGE_AST_brush_paint,
)

View File

@ -18,7 +18,9 @@ from bl_ui.space_toolsystem_common import (
ToolSelectPanelHelper,
ToolDef,
)
from bl_ui.properties_paint_common import (
BrushAssetShelf,
)
from bpy.app.translations import pgettext_tip as tip_
@ -1396,16 +1398,23 @@ class _defs_sculpt:
@staticmethod
def generate_from_brushes(context):
return generate_from_enum_ex(
context,
idname_prefix="builtin_brush.",
icon_prefix="brush.sculpt.",
type=bpy.types.Brush,
attr="sculpt_tool",
# TODO(@ideasman42): we may want to enable this,
# it causes awkward grouping with 2x column button layout.
use_separators=False,
# 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.sculpt.brush
if brush:
tool = brush.sculpt_tool
return [
ToolDef.from_dict(
dict(
idname="builtin.brush",
label="Brush",
icon="brush.sculpt.paint",
data_block=tool
)
)
]
@ToolDef.from_fn
def hide_border():
@ -1726,13 +1735,23 @@ class _defs_vertex_paint:
@staticmethod
def generate_from_brushes(context):
return generate_from_enum_ex(
context,
idname_prefix="builtin_brush.",
icon_prefix="brush.paint_vertex.",
type=bpy.types.Brush,
attr="vertex_tool",
# 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.vertex_paint.brush
if brush:
tool = brush.vertex_tool
return [
ToolDef.from_dict(
dict(
idname="builtin.brush",
label="Brush",
icon="brush.sculpt.paint",
data_block=tool
)
)
]
class _defs_texture_paint:
@ -1747,14 +1766,24 @@ class _defs_texture_paint:
@staticmethod
def generate_from_brushes(context):
return generate_from_enum_ex(
context,
idname_prefix="builtin_brush.",
icon_prefix="brush.paint_texture.",
type=bpy.types.Brush,
attr="image_tool",
# 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.image_paint.brush
if brush:
tool = brush.image_tool
return [
ToolDef.from_dict(
dict(
idname="builtin.brush",
label="Brush",
icon="brush.sculpt.paint",
cursor='PAINT_CROSS',
data_block=tool
)
)
]
class _defs_weight_paint:
@ -1774,13 +1803,23 @@ class _defs_weight_paint:
@staticmethod
def generate_from_brushes(context):
return generate_from_enum_ex(
context,
idname_prefix="builtin_brush.",
icon_prefix="brush.paint_weight.",
type=bpy.types.Brush,
attr="weight_tool",
# 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.weight_paint.brush
if brush:
tool = brush.weight_tool
return [
ToolDef.from_dict(
dict(
idname="builtin.brush",
label="Brush",
icon="brush.sculpt.paint",
data_block=tool
)
)
]
@ToolDef.from_fn
def sample_weight():
@ -1853,45 +1892,25 @@ class _defs_weight_paint:
class _defs_grease_pencil_paint:
# FIXME: Replace brush tools with code below once they are all implemented:
#
# @staticmethod
# def generate_from_brushes(context):
# return generate_from_enum_ex(
# context,
# idname_prefix="builtin_brush.",
# icon_prefix="brush.gpencil_draw.",
# type=bpy.types.Brush,
# attr="gpencil_tool",
# cursor='DOT',
# )
@ToolDef.from_fn
def draw():
return dict(
idname="builtin_brush.Draw",
label="Draw",
icon="brush.gpencil_draw.draw",
data_block='DRAW',
@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_paint.brush
if brush:
tool = brush.gpencil_tool
return [
ToolDef.from_dict(
dict(
idname="builtin.brush",
label="Brush",
icon="brush.sculpt.paint",
data_block=tool
)
@ToolDef.from_fn
def fill():
return dict(
idname="builtin_brush.Fill",
label="Fill",
icon="brush.gpencil_draw.fill",
data_block='FILL',
)
@ToolDef.from_fn
def erase():
return dict(
idname="builtin_brush.Erase",
label="Erase",
icon="brush.gpencil_draw.erase",
data_block='ERASE',
)
]
@ToolDef.from_fn
def cutter():
@ -1934,7 +1953,8 @@ class _defs_grease_pencil_paint:
row = layout.row(align=True)
tool_settings = context.scene.tool_settings
settings = tool_settings.gpencil_paint
row.template_ID_preview(settings, "brush", rows=3, cols=8, hide_buttons=True)
BrushAssetShelf.draw_popup_selector(row, context, brush)
from bl_ui.properties_paint_common import (
brush_basic_grease_pencil_paint_settings,
@ -2316,7 +2336,8 @@ class _defs_gpencil_paint:
row = layout.row(align=True)
tool_settings = context.scene.tool_settings
settings = tool_settings.gpencil_paint
row.template_ID_preview(settings, "brush", rows=3, cols=8, hide_buttons=True)
BrushAssetShelf.draw_popup_selector(layout, context, brush)
from bl_ui.properties_paint_common import (
brush_basic_gpencil_paint_settings,
@ -2329,17 +2350,23 @@ class _defs_gpencil_paint:
@staticmethod
def generate_from_brushes(context):
return generate_from_enum_ex(
context,
idname_prefix="builtin_brush.",
icon_prefix="brush.gpencil_draw.",
type=bpy.types.Brush,
attr="gpencil_tool",
cursor='DOT',
tooldef_keywords=dict(
operator="gpencil.draw",
),
# 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_paint.brush
if brush:
tool = brush.gpencil_tool
return [
ToolDef.from_dict(
dict(
idname="builtin.brush",
label="Brush",
icon="brush.sculpt.paint",
data_block=tool
)
)
]
@ToolDef.from_fn
def cutter():
@ -2695,16 +2722,23 @@ class _defs_gpencil_sculpt:
@staticmethod
def generate_from_brushes(context):
return generate_from_enum_ex(
context,
idname_prefix="builtin_brush.",
icon_prefix="ops.gpencil.sculpt_",
type=bpy.types.Brush,
attr="gpencil_sculpt_tool",
tooldef_keywords=dict(
operator="gpencil.sculpt_paint",
),
# 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_sculpt_paint.brush
if brush:
tool = brush.gpencil_sculpt_tool
return [
ToolDef.from_dict(
dict(
idname="builtin.brush",
label="Brush",
icon="brush.sculpt.paint",
data_block=tool
)
)
]
class _defs_grease_pencil_sculpt:
@ -2725,33 +2759,46 @@ class _defs_grease_pencil_sculpt:
@staticmethod
def generate_from_brushes(context):
return generate_from_enum_ex(
context,
idname_prefix="builtin_brush.",
icon_prefix="ops.gpencil.sculpt_",
type=bpy.types.Brush,
# Uses GPv2 tool settings
attr="gpencil_sculpt_tool",
tooldef_keywords=dict(
operator="grease_pencil.sculpt_paint",
),
# 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_sculpt_paint.brush
if brush:
tool = brush.gpencil_sculpt_tool
return [
ToolDef.from_dict(
dict(
idname="builtin.brush",
label="Brush",
icon="brush.sculpt.paint",
data_block=tool
)
)
]
class _defs_gpencil_weight:
@staticmethod
def generate_from_brushes(context):
return generate_from_enum_ex(
context,
idname_prefix="builtin_brush.",
icon_prefix="ops.gpencil.sculpt_",
type=bpy.types.Brush,
attr="gpencil_weight_tool",
tooldef_keywords=dict(
operator="gpencil.weight_paint",
),
# 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_weight_paint.brush
if brush:
tool = brush.gpencil_weight_tool
return [
ToolDef.from_dict(
dict(
idname="builtin.brush",
label="Brush",
icon="brush.sculpt.paint",
data_block=tool
)
)
]
class _defs_grease_pencil_weight:
@ -2775,17 +2822,23 @@ class _defs_curves_sculpt:
@staticmethod
def generate_from_brushes(context):
return generate_from_enum_ex(
context,
idname_prefix="builtin_brush.",
icon_prefix="ops.curves.sculpt_",
type=bpy.types.Brush,
attr="curves_sculpt_tool",
icon_map={
# Use the generic icon for selection painting.
"ops.curves.sculpt_selection_paint": "ops.generic.select_paint",
},
# 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.curves_sculpt.brush
if brush:
tool = brush.curves_sculpt_tool
return [
ToolDef.from_dict(
dict(
idname="builtin.brush",
label="Brush",
icon="brush.sculpt.paint",
data_block=tool
)
)
]
class _defs_gpencil_vertex:
@ -2807,17 +2860,23 @@ class _defs_gpencil_vertex:
@staticmethod
def generate_from_brushes(context):
return generate_from_enum_ex(
context,
idname_prefix="builtin_brush.",
icon_prefix="brush.paint_vertex.",
type=bpy.types.Brush,
attr="gpencil_vertex_tool",
cursor='DOT',
tooldef_keywords=dict(
operator="gpencil.vertex_paint",
),
# 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_node_select:
@ -3535,9 +3594,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
'PAINT_GREASE_PENCIL': [
_defs_view3d_generic.cursor,
None,
_defs_grease_pencil_paint.draw,
_defs_grease_pencil_paint.fill,
_defs_grease_pencil_paint.erase,
_defs_grease_pencil_paint.generate_from_brushes,
_defs_grease_pencil_paint.cutter,
_defs_grease_pencil_paint.tint,
None,

View File

@ -248,9 +248,10 @@ class _draw_tool_settings_context_mode:
return False
paint = context.tool_settings.sculpt
layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
brush = paint.brush
BrushAssetShelf.draw_popup_selector(layout, context, brush)
if brush is None:
return False
@ -309,9 +310,10 @@ class _draw_tool_settings_context_mode:
return False
paint = context.tool_settings.image_paint
layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
brush = paint.brush
BrushAssetShelf.draw_popup_selector(layout, context, brush)
if brush is None:
return False
@ -325,9 +327,10 @@ class _draw_tool_settings_context_mode:
return False
paint = context.tool_settings.vertex_paint
layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
brush = paint.brush
BrushAssetShelf.draw_popup_selector(layout, context, brush)
if brush is None:
return False
@ -341,8 +344,10 @@ class _draw_tool_settings_context_mode:
return False
paint = context.tool_settings.weight_paint
layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
brush = paint.brush
BrushAssetShelf.draw_popup_selector(layout, context, brush)
if brush is None:
return False
@ -404,7 +409,8 @@ class _draw_tool_settings_context_mode:
row = layout.row(align=True)
settings = tool_settings.gpencil_paint
row.template_ID_preview(settings, "brush", rows=3, cols=8, hide_buttons=True)
BrushAssetShelf.draw_popup_selector(layout, context, brush)
if ob and brush.gpencil_tool in {'FILL', 'DRAW'}:
from bl_ui.properties_paint_common import (
@ -432,6 +438,8 @@ class _draw_tool_settings_context_mode:
paint = tool_settings.gpencil_sculpt_paint
brush = paint.brush
BrushAssetShelf.draw_popup_selector(layout, context, brush)
from bl_ui.properties_paint_common import (
brush_basic_gpencil_sculpt_settings,
)
@ -445,12 +453,12 @@ class _draw_tool_settings_context_mode:
return False
paint = context.tool_settings.gpencil_sculpt_paint
layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
brush = paint.brush
if brush is None:
return False
BrushAssetShelf.draw_popup_selector(layout, context, brush)
tool_settings = context.tool_settings
capabilities = brush.sculpt_capabilities
@ -506,7 +514,7 @@ class _draw_tool_settings_context_mode:
paint = tool_settings.gpencil_weight_paint
brush = paint.brush
layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
BrushAssetShelf.draw_popup_selector(layout, context, brush)
brush_basic_gpencil_weight_settings(layout, context, brush, compact=True)
@ -521,12 +529,12 @@ class _draw_tool_settings_context_mode:
return False
paint = context.tool_settings.gpencil_weight_paint
layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
brush = paint.brush
if brush is None:
return False
BrushAssetShelf.draw_popup_selector(layout, context, brush)
brush_basic_grease_pencil_weight_settings(layout, context, brush, compact=True)
layout.popover("VIEW3D_PT_tools_grease_pencil_weight_options", text="Options")
@ -545,7 +553,8 @@ class _draw_tool_settings_context_mode:
row = layout.row(align=True)
settings = tool_settings.gpencil_vertex_paint
row.template_ID_preview(settings, "brush", rows=3, cols=8, hide_buttons=True)
BrushAssetShelf.draw_popup_selector(layout, context, brush)
if brush.gpencil_vertex_tool not in {'BLUR', 'AVERAGE', 'SMEAR'}:
row.separator(factor=0.4)
@ -698,7 +707,8 @@ class _draw_tool_settings_context_mode:
return False
row = layout.row(align=True)
row.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
BrushAssetShelf.draw_popup_selector(layout, context, brush)
grease_pencil_tool = brush.gpencil_tool
@ -3542,23 +3552,6 @@ class VIEW3D_MT_make_links(Menu):
layout.operator("object.datalayout_transfer")
class VIEW3D_MT_brush_paint_modes(Menu):
bl_label = "Enabled Modes"
def draw(self, context):
layout = self.layout
settings = UnifiedPaintPanel.paint_settings(context)
brush = settings.brush
layout.prop(brush, "use_paint_sculpt", text="Sculpt")
layout.prop(brush, "use_paint_uv_sculpt", text="UV Sculpt")
layout.prop(brush, "use_paint_vertex", text="Vertex Paint")
layout.prop(brush, "use_paint_weight", text="Weight Paint")
layout.prop(brush, "use_paint_image", text="Texture Paint")
layout.prop(brush, "use_paint_sculpt_curves", text="Sculpt Curves")
class VIEW3D_MT_paint_vertex(Menu):
bl_label = "Paint"
@ -9134,11 +9127,58 @@ class VIEW3D_PT_viewport_debug(Panel):
layout.prop(overlay, "use_debug_freeze_view_culling")
class VIEW3D_AST_sculpt_brushes(BrushAssetShelf, bpy.types.AssetShelf):
# Experimental: Asset shelf for sculpt brushes, only shows up if both the
# "Asset Shelf" and the "Extended Asset Browser" experimental features are
# enabled.
bl_space_type = 'VIEW_3D'
class View3DAssetShelf(BrushAssetShelf):
bl_space_type = "VIEW_3D"
class VIEW3D_AST_brush_sculpt(View3DAssetShelf, bpy.types.AssetShelf):
mode = 'SCULPT'
mode_prop = "use_paint_sculpt"
class VIEW3D_AST_brush_sculpt_curves(View3DAssetShelf, bpy.types.AssetShelf):
mode = 'SCULPT_CURVES'
mode_prop = "use_paint_sculpt_curves"
class VIEW3D_AST_brush_vertex_paint(View3DAssetShelf, bpy.types.AssetShelf):
mode = 'VERTEX_PAINT'
mode_prop = "use_paint_vertex"
class VIEW3D_AST_brush_weight_paint(View3DAssetShelf, bpy.types.AssetShelf):
mode = 'WEIGHT_PAINT'
mode_prop = "use_paint_weight"
class VIEW3D_AST_brush_texture_paint(View3DAssetShelf, bpy.types.AssetShelf):
mode = 'TEXTURE_PAINT'
mode_prop = "use_paint_image"
class VIEW3D_AST_brush_gpencil_paint(View3DAssetShelf, bpy.types.AssetShelf):
mode = 'PAINT_GPENCIL'
mode_prop = "use_paint_grease_pencil"
class VIEW3D_AST_brush_grease_pencil_paint(View3DAssetShelf, bpy.types.AssetShelf):
mode = 'PAINT_GREASE_PENCIL'
mode_prop = "use_paint_grease_pencil"
class VIEW3D_AST_brush_gpencil_sculpt(View3DAssetShelf, bpy.types.AssetShelf):
mode = 'SCULPT_GPENCIL'
mode_prop = "use_sculpt_grease_pencil"
class VIEW3D_AST_brush_gpencil_vertex(View3DAssetShelf, bpy.types.AssetShelf):
mode = 'VERTEX_GPENCIL'
mode_prop = "use_vertex_grease_pencil"
class VIEW3D_AST_brush_gpencil_weight(View3DAssetShelf, bpy.types.AssetShelf):
mode = 'WEIGHT_GPENCIL'
mode_prop = "use_weight_grease_pencil"
classes = (
@ -9220,7 +9260,6 @@ classes = (
VIEW3D_MT_object_cleanup,
VIEW3D_MT_make_single_user,
VIEW3D_MT_make_links,
VIEW3D_MT_brush_paint_modes,
VIEW3D_MT_paint_vertex,
VIEW3D_MT_hook,
VIEW3D_MT_vertex_group,
@ -9408,7 +9447,16 @@ classes = (
VIEW3D_PT_curves_sculpt_parameter_falloff,
VIEW3D_PT_curves_sculpt_grow_shrink_scaling,
VIEW3D_PT_viewport_debug,
VIEW3D_AST_sculpt_brushes,
VIEW3D_AST_brush_sculpt,
VIEW3D_AST_brush_sculpt_curves,
VIEW3D_AST_brush_vertex_paint,
VIEW3D_AST_brush_weight_paint,
VIEW3D_AST_brush_texture_paint,
VIEW3D_AST_brush_gpencil_paint,
VIEW3D_AST_brush_grease_pencil_paint,
VIEW3D_AST_brush_gpencil_sculpt,
VIEW3D_AST_brush_gpencil_vertex,
VIEW3D_AST_brush_gpencil_weight,
)

View File

@ -28,6 +28,7 @@ from bl_ui.properties_paint_common import (
brush_settings,
brush_settings_advanced,
draw_color_settings,
BrushAssetShelf,
)
from bl_ui.utils import PresetPanel
@ -43,25 +44,22 @@ class VIEW3D_MT_brush_context_menu(Menu):
# skip if no active brush
if not brush:
layout.label(text="No Brushes currently available", icon='INFO')
layout.label(text="No brush selected", icon='INFO')
return
# brush paint modes
layout.menu("VIEW3D_MT_brush_paint_modes")
if brush.library and brush.library.is_editable:
layout.operator("brush.asset_save_as", text="Duplicate Asset...", icon='DUPLICATE')
layout.operator("brush.asset_delete", text="Delete Asset")
# brush tool
layout.separator()
if context.image_paint_object:
layout.prop_menu_enum(brush, "image_tool")
elif context.vertex_paint_object:
layout.prop_menu_enum(brush, "vertex_tool")
elif context.weight_paint_object:
layout.prop_menu_enum(brush, "weight_tool")
elif context.sculpt_object:
layout.prop_menu_enum(brush, "sculpt_tool")
layout.operator("brush.reset")
elif context.tool_settings.curves_sculpt:
layout.prop_menu_enum(brush, "curves_sculpt_tool")
layout.operator("brush.asset_edit_metadata", text="Edit Metadata")
layout.operator("brush.asset_load_preview", text="Edit Preview Image...")
layout.operator("brush.asset_update", text="Update Asset")
layout.operator("brush.asset_revert", text="Revert to Asset")
else:
layout.operator("brush.asset_save_as", text="Save As Asset...", icon='FILE_TICK')
layout.operator("brush.asset_delete", text="Delete")
class VIEW3D_MT_brush_gpencil_context_menu(Menu):
@ -87,9 +85,6 @@ class VIEW3D_MT_brush_gpencil_context_menu(Menu):
layout.label(text="No Brushes currently available", icon='INFO')
return
layout.operator("gpencil.brush_reset")
layout.operator("gpencil.brush_reset_all")
class View3DPanel:
bl_space_type = 'VIEW_3D'
@ -359,7 +354,7 @@ class VIEW3D_PT_tools_particlemode(Panel, View3DPaintPanel):
# TODO, move to space_view3d.py
class VIEW3D_PT_tools_brush_select(Panel, View3DPaintBrushPanel, BrushSelectPanel):
bl_context = ".paint_common"
bl_label = "Brushes"
bl_label = "Brush Asset"
# TODO, move to space_view3d.py
@ -1603,30 +1598,8 @@ class GreasePencilPaintPanel:
return True
class VIEW3D_PT_tools_grease_pencil_brush_select(Panel, View3DPanel, GreasePencilPaintPanel):
bl_label = "Brushes"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
tool_settings = context.scene.tool_settings
gpencil_paint = tool_settings.gpencil_paint
row = layout.row()
row.column().template_ID_preview(gpencil_paint, "brush", new="brush.add_gpencil", rows=3, cols=8)
col = row.column()
col.menu("VIEW3D_MT_brush_gpencil_context_menu", icon='DOWNARROW_HLT', text="")
if context.mode == 'PAINT_GPENCIL':
brush = tool_settings.gpencil_paint.brush
if brush is not None:
col.prop(brush, "use_custom_icon", toggle=True, icon='FILE_IMAGE', text="")
if brush.use_custom_icon:
layout.row().prop(brush, "icon_filepath", text="")
class VIEW3D_PT_tools_grease_pencil_brush_select(Panel, View3DPanel, GreasePencilPaintPanel, BrushSelectPanel):
bl_label = "Brush Asset"
class VIEW3D_PT_tools_grease_pencil_brush_settings(Panel, View3DPanel, GreasePencilPaintPanel):
@ -2014,30 +1987,8 @@ class GreasePencilSculptPanel:
return True
class VIEW3D_PT_tools_grease_pencil_sculpt_select(Panel, View3DPanel, GreasePencilSculptPanel):
bl_label = "Brushes"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
tool_settings = context.scene.tool_settings
gpencil_paint = tool_settings.gpencil_sculpt_paint
row = layout.row()
row.column().template_ID_preview(gpencil_paint, "brush", new="brush.add_gpencil", rows=3, cols=8)
col = row.column()
col.menu("VIEW3D_MT_brush_gpencil_context_menu", icon='DOWNARROW_HLT', text="")
if context.mode == 'SCULPT_GPENCIL':
brush = tool_settings.gpencil_sculpt_paint.brush
if brush is not None:
col.prop(brush, "use_custom_icon", toggle=True, icon='FILE_IMAGE', text="")
if (brush.use_custom_icon):
layout.row().prop(brush, "icon_filepath", text="")
class VIEW3D_PT_tools_grease_pencil_sculpt_select(Panel, View3DPanel, GreasePencilSculptPanel, BrushSelectPanel):
bl_label = "Brush Asset"
class VIEW3D_PT_tools_grease_pencil_sculpt_settings(Panel, View3DPanel, GreasePencilSculptPanel):
@ -2126,30 +2077,8 @@ class GreasePencilWeightPanel:
return True
class VIEW3D_PT_tools_grease_pencil_weight_paint_select(View3DPanel, Panel, GreasePencilWeightPanel):
bl_label = "Brushes"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
tool_settings = context.scene.tool_settings
gpencil_paint = tool_settings.gpencil_weight_paint
row = layout.row()
row.column().template_ID_preview(gpencil_paint, "brush", new="brush.add_gpencil", rows=3, cols=8)
col = row.column()
col.menu("VIEW3D_MT_brush_gpencil_context_menu", icon='DOWNARROW_HLT', text="")
if context.mode in {'WEIGHT_GPENCIL', 'WEIGHT_GREASE_PENCIL'}:
brush = tool_settings.gpencil_weight_paint.brush
if brush is not None:
col.prop(brush, "use_custom_icon", toggle=True, icon='FILE_IMAGE', text="")
if (brush.use_custom_icon):
layout.row().prop(brush, "icon_filepath", text="")
class VIEW3D_PT_tools_grease_pencil_weight_paint_select(View3DPanel, Panel, GreasePencilWeightPanel, BrushSelectPanel):
bl_label = "Brush Asset"
class VIEW3D_PT_tools_grease_pencil_weight_paint_settings(Panel, View3DPanel, GreasePencilWeightPanel):
@ -2225,30 +2154,8 @@ class GreasePencilVertexPanel:
return True
class VIEW3D_PT_tools_grease_pencil_vertex_paint_select(View3DPanel, Panel, GreasePencilVertexPanel):
bl_label = "Brushes"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
tool_settings = context.scene.tool_settings
gpencil_paint = tool_settings.gpencil_vertex_paint
row = layout.row()
row.column().template_ID_preview(gpencil_paint, "brush", new="brush.add_gpencil", rows=3, cols=8)
col = row.column()
col.menu("VIEW3D_MT_brush_gpencil_context_menu", icon='DOWNARROW_HLT', text="")
if context.mode == 'VERTEX_GPENCIL':
brush = tool_settings.gpencil_vertex_paint.brush
if brush is not None:
col.prop(brush, "use_custom_icon", toggle=True, icon='FILE_IMAGE', text="")
if (brush.use_custom_icon):
layout.row().prop(brush, "icon_filepath", text="")
class VIEW3D_PT_tools_grease_pencil_vertex_paint_select(View3DPanel, Panel, GreasePencilVertexPanel, BrushSelectPanel):
bl_label = "Brush Asset"
class VIEW3D_PT_tools_grease_pencil_vertex_paint_settings(Panel, View3DPanel, GreasePencilVertexPanel):
@ -2466,6 +2373,36 @@ class VIEW3D_PT_tools_grease_pencil_brush_mix_palette(View3DPanel, Panel):
col.template_palette(settings, "palette", color=True)
class VIEW3D_PT_tools_grease_pencil_brush_eraser(View3DPanel, Panel):
bl_context = ".greasepencil_paint"
bl_label = "Eraser"
bl_category = "Tool"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
tool_settings = context.tool_settings
settings = tool_settings.gpencil_paint
if context.region.type == 'TOOL_HEADER':
return False
from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
tool = ToolSelectPanelHelper.tool_active_from_context(context)
return (tool and tool.idname == "builtin.brush")
def draw(self, context):
layout = self.layout
tool_settings = context.tool_settings
settings = tool_settings.gpencil_paint
layout.use_property_split = True
layout.use_property_decorate = False
col = layout.column()
col.prop(settings, "eraser_brush")
# Grease Pencil Brush Appearance (one for each mode)
class VIEW3D_PT_tools_grease_pencil_paint_appearance(GreasePencilDisplayPanel, Panel, View3DPanel):
bl_context = ".greasepencil_paint"
@ -2520,29 +2457,8 @@ class GreasePencilV3PaintPanel:
return True
class VIEW3D_PT_tools_grease_pencil_v3_brush_select(Panel, View3DPanel, GreasePencilV3PaintPanel):
bl_label = "Brushes"
def draw(self, context):
layout = self.layout
layout.use_property_split = True
layout.use_property_decorate = False
tool_settings = context.scene.tool_settings
gpencil_paint = tool_settings.gpencil_paint
row = layout.row()
row.column().template_ID_preview(gpencil_paint, "brush", new="brush.add_gpencil", rows=3, cols=8)
col = row.column()
col.menu("VIEW3D_MT_brush_gpencil_context_menu", icon='DOWNARROW_HLT', text="")
brush = tool_settings.gpencil_paint.brush
if brush is not None:
col.prop(brush, "use_custom_icon", toggle=True, icon='FILE_IMAGE', text="")
if brush.use_custom_icon:
layout.row().prop(brush, "icon_filepath", text="")
class VIEW3D_PT_tools_grease_pencil_v3_brush_select(Panel, View3DPanel, GreasePencilV3PaintPanel, BrushSelectPanel):
bl_label = "Brush Asset"
class VIEW3D_PT_tools_grease_pencil_v3_brush_settings(Panel, View3DPanel, GreasePencilV3PaintPanel):
@ -2777,6 +2693,35 @@ class VIEW3D_PT_tools_grease_pencil_v3_brush_mix_palette(View3DPanel, Panel):
col.template_palette(settings, "palette", color=True)
class VIEW3D_PT_tools_grease_pencil_v3_brush_eraser(View3DPanel, Panel):
bl_context = ".grease_pencil_paint"
bl_label = "Eraser"
bl_category = "Tool"
bl_options = {'DEFAULT_CLOSED'}
@classmethod
def poll(cls, context):
tool_settings = context.tool_settings
settings = tool_settings.gpencil_paint
if context.region.type == 'TOOL_HEADER':
return False
from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
tool = ToolSelectPanelHelper.tool_active_from_context(context)
return (tool and tool.idname == "builtin.brush")
def draw(self, context):
layout = self.layout
settings = tool_settings.gpencil_paint
layout.use_property_split = True
layout.use_property_decorate = False
col = layout.column()
col.prop_search(settings, "eraser_brush", bpy.data, "brushes")
classes = (
VIEW3D_MT_brush_context_menu,
VIEW3D_MT_brush_gpencil_context_menu,
@ -2852,6 +2797,7 @@ classes = (
VIEW3D_PT_tools_grease_pencil_brush_random,
VIEW3D_PT_tools_grease_pencil_brush_stabilizer,
VIEW3D_PT_tools_grease_pencil_brush_gap_closure,
VIEW3D_PT_tools_grease_pencil_brush_eraser,
VIEW3D_PT_tools_grease_pencil_paint_appearance,
VIEW3D_PT_tools_grease_pencil_sculpt_select,
VIEW3D_PT_tools_grease_pencil_sculpt_settings,
@ -2870,6 +2816,7 @@ classes = (
VIEW3D_PT_tools_grease_pencil_v3_brush_select,
VIEW3D_PT_tools_grease_pencil_v3_brush_settings,
VIEW3D_PT_tools_grease_pencil_v3_brush_eraser,
VIEW3D_PT_tools_grease_pencil_v3_brush_advanced,
VIEW3D_PT_tools_grease_pencil_v3_brush_mixcolor,
VIEW3D_PT_tools_grease_pencil_v3_brush_mix_palette,

View File

@ -0,0 +1,67 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#pragma once
/**
* Editing of datablocks from asset libraries.
*
* Asset blend files are linked into the global main database, with the asset
* datablock itself and its dependencies. These datablocks remain linked but
* are marked as editable.
*
* User edited asset datablocks are written to individual blend files per
* asset. These blend files include any datablock dependencies and packaged
* image files.
*
* This way the blend file can be easily saved, reloaded and deleted.
*
* This mechanism is currently only used for brush assets.
*/
#include <optional>
#include <string>
#include "BLI_string_ref.hh"
#include "DNA_ID_enums.h"
struct bUserAssetLibrary;
struct AssetMetaData;
struct AssetWeakReference;
struct ID;
struct Main;
struct ReportList;
namespace blender::bke {
/** Get datablock from weak reference, loading the blend file as needed. */
ID *asset_edit_id_from_weak_reference(Main &global_main,
ID_Type id_type,
const AssetWeakReference &weak_ref);
/** Get asset weak reference from ID. */
std::optional<AssetWeakReference> asset_edit_weak_reference_from_id(ID &id);
/** Asset editing operations. */
bool asset_edit_id_is_editable(const ID &id);
bool asset_edit_id_is_writable(const ID &id);
std::optional<std::string> asset_edit_id_save_as(Main &global_main,
const ID &id,
StringRef name,
const bUserAssetLibrary &user_library,
AssetWeakReference &weak_ref,
ReportList &reports);
bool asset_edit_id_save(Main &global_main, const ID &id, ReportList &reports);
bool asset_edit_id_revert(Main &global_main, ID &id, ReportList &reports);
bool asset_edit_id_delete(Main &global_main, ID &id, ReportList &reports);
} // namespace blender::bke

View File

@ -29,7 +29,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 42
#define BLENDER_FILE_SUBVERSION 43
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and cancel loading the file, showing a warning to

View File

@ -19,6 +19,11 @@ struct ReportList;
struct UserDef;
struct WorkspaceConfigFileData;
/**
* The suffix used for blendfiles managed by the asset system.
*/
#define BLENDER_ASSET_FILE_SUFFIX ".asset.blend"
/**
* Check whether given path ends with a blend file compatible extension
* (`.blend`, `.ble` or `.blend.gz`).

View File

@ -38,10 +38,6 @@ void BKE_brush_system_exit();
* another is assumed to be used by the caller.
*/
Brush *BKE_brush_add(Main *bmain, const char *name, eObjectMode ob_mode);
/**
* Add a new gp-brush.
*/
Brush *BKE_brush_add_gpencil(Main *bmain, ToolSettings *ts, const char *name, eObjectMode mode);
/**
* Delete a Brush.
*/
@ -57,24 +53,6 @@ Brush *BKE_brush_first_search(Main *bmain, eObjectMode ob_mode);
void BKE_brush_sculpt_reset(Brush *brush);
/**
* Create a set of grease pencil Drawing presets.
*/
void BKE_brush_gpencil_paint_presets(Main *bmain, ToolSettings *ts, bool reset);
/**
* Create a set of grease pencil Vertex Paint presets.
*/
void BKE_brush_gpencil_vertex_presets(Main *bmain, ToolSettings *ts, bool reset);
/**
* Create a set of grease pencil Sculpt Paint presets.
*/
void BKE_brush_gpencil_sculpt_presets(Main *bmain, ToolSettings *ts, bool reset);
/**
* Create a set of grease pencil Weight Paint presets.
*/
void BKE_brush_gpencil_weight_presets(Main *bmain, ToolSettings *ts, bool reset);
void BKE_gpencil_brush_preset_set(Main *bmain, Brush *brush, short type);
void BKE_brush_jitter_pos(const Scene &scene,
const Brush &brush,
const float pos[2],
@ -185,16 +163,5 @@ void BKE_brush_scale_size(int *r_brush_size,
*/
bool BKE_brush_has_cube_tip(const Brush *brush, PaintMode paint_mode);
/* Accessors */
#define BKE_brush_tool_get(brush, p) \
(CHECK_TYPE_ANY(brush, Brush *, const Brush *), \
*(const char *)POINTER_OFFSET(brush, (p)->runtime.tool_offset))
#define BKE_brush_tool_set(brush, p, tool) \
{ \
CHECK_TYPE_ANY(brush, Brush *); \
*(char *)POINTER_OFFSET(brush, (p)->runtime.tool_offset) = tool; \
} \
((void)0)
/* debugging only */
void BKE_brush_debug_print_state(Brush *br);

View File

@ -8,6 +8,8 @@
* \ingroup bke
*/
#include <variant>
/* XXX temporary, until AssetHandle is designed properly and queries can return a pointer to it. */
#include "DNA_asset_types.h"
@ -102,7 +104,7 @@ using bContextDataCallback = int /*eContextResult*/ (*)(const bContext *C,
struct bContextStoreEntry {
std::string name;
PointerRNA ptr;
std::variant<PointerRNA, std::string> value;
};
struct bContextStore {
@ -158,6 +160,9 @@ bContext *CTX_copy(const bContext *C);
bContextStore *CTX_store_add(blender::Vector<std::unique_ptr<bContextStore>> &contexts,
blender::StringRefNull name,
const PointerRNA *ptr);
bContextStore *CTX_store_add(blender::Vector<std::unique_ptr<bContextStore>> &contexts,
blender::StringRefNull name,
blender::StringRef str);
bContextStore *CTX_store_add_all(blender::Vector<std::unique_ptr<bContextStore>> &contexts,
const bContextStore *context);
const bContextStore *CTX_store_get(const bContext *C);
@ -165,6 +170,8 @@ void CTX_store_set(bContext *C, const bContextStore *store);
const PointerRNA *CTX_store_ptr_lookup(const bContextStore *store,
blender::StringRefNull name,
const StructRNA *type = nullptr);
std::optional<blender::StringRefNull> CTX_store_string_lookup(const bContextStore *store,
blender::StringRefNull name);
/* need to store if python is initialized or not */
bool CTX_py_init_get(bContext *C);
@ -262,6 +269,7 @@ enum {
CTX_DATA_TYPE_POINTER = 0,
CTX_DATA_TYPE_COLLECTION,
CTX_DATA_TYPE_PROPERTY,
CTX_DATA_TYPE_STRING,
};
PointerRNA CTX_data_pointer_get(const bContext *C, const char *member);
@ -282,6 +290,8 @@ blender::Vector<PointerRNA> CTX_data_collection_get(const bContext *C, const cha
void CTX_data_collection_remap_property(blender::MutableSpan<PointerRNA> collection_pointers,
const char *propname);
std::optional<blender::StringRefNull> CTX_data_string_get(const bContext *C, const char *member);
/**
* \param C: Context.
* \param use_store: Use 'C->wm.store'.

View File

@ -205,6 +205,8 @@ enum {
* duplicate scene/collections, or objects.
*/
LIB_ID_COPY_RIGID_BODY_NO_COLLECTION_HANDLING = 1 << 28,
/* Copy asset metadata. */
LIB_ID_COPY_ASSET_METADATA = 1 << 29,
/* *** Helper 'defines' gathering most common flag sets. *** */
/** Shape-keys are not real ID's, more like local data to geometry IDs. */

View File

@ -143,6 +143,14 @@ struct Main {
* could try to use more refined detection on load. */
bool has_forward_compatibility_issues;
/**
* The currently opened .blend file was created as an asset library storage.
*
* This is used to warn the user when they try to save it from Blender UI, since this will likely
* break the automatic management from the asset library system.
*/
bool is_asset_repository;
/** Commit timestamp from `buildinfo`. */
uint64_t build_commit_timestamp;
/** Commit Hash from `buildinfo`. */
@ -300,6 +308,17 @@ void BKE_main_merge(Main *bmain_dst, Main **r_bmain_src, MainMergeReport &report
*/
bool BKE_main_is_empty(Main *bmain);
/**
* Check whether the bmain has issues, e.g. for reporting in the status bar.
*/
bool BKE_main_has_issues(const Main *bmain);
/**
* Check whether user confirmation should be required when overwriting this `bmain` into its source
* blendfile.
*/
bool BKE_main_needs_overwrite_confirm(const Main *bmain);
void BKE_main_lock(Main *bmain);
void BKE_main_unlock(Main *bmain);

View File

@ -8,6 +8,8 @@
* \ingroup bke
*/
#include <optional>
#include "BLI_array.hh"
#include "BLI_bit_vector.hh"
#include "BLI_math_matrix_types.hh"
@ -183,24 +185,43 @@ void BKE_paint_free(Paint *p);
*/
void BKE_paint_copy(const Paint *src, Paint *tar, int flag);
void BKE_paint_runtime_init(const ToolSettings *ts, Paint *paint);
void BKE_paint_cavity_curve_preset(Paint *p, int preset);
eObjectMode BKE_paint_object_mode_from_paintmode(PaintMode mode);
bool BKE_paint_ensure_from_paintmode(Main *bmain, Scene *sce, PaintMode mode);
Paint *BKE_paint_get_active_from_paintmode(Scene *sce, PaintMode mode);
const EnumPropertyItem *BKE_paint_get_tool_enum_from_paintmode(PaintMode mode);
const char *BKE_paint_get_tool_enum_translation_context_from_paintmode(PaintMode mode);
const char *BKE_paint_get_tool_prop_id_from_paintmode(PaintMode mode);
uint BKE_paint_get_brush_tool_offset_from_paintmode(PaintMode mode);
Paint *BKE_paint_get_active(Scene *sce, ViewLayer *view_layer);
Paint *BKE_paint_get_active_from_context(const bContext *C);
PaintMode BKE_paintmode_get_active_from_context(const bContext *C);
PaintMode BKE_paintmode_get_from_tool(const bToolRef *tref);
/* Paint brush retrieval and assignment. */
Brush *BKE_paint_brush(Paint *paint);
const Brush *BKE_paint_brush_for_read(const Paint *p);
void BKE_paint_brush_set(Paint *paint, Brush *br);
const Brush *BKE_paint_brush_for_read(const Paint *paint);
Brush *BKE_paint_brush_from_essentials(Main *bmain, const char *name);
bool BKE_paint_brush_set(Paint *paint, Brush *brush);
bool BKE_paint_brush_set_default(Main *bmain, Paint *paint);
bool BKE_paint_brush_set_essentials(Main *bmain, Paint *paint, const char *name);
void BKE_paint_brushes_set_default_references(ToolSettings *ts);
void BKE_paint_brushes_validate(Main *bmain, Paint *paint);
/* Secondary eraser brush. */
Brush *BKE_paint_eraser_brush(Paint *paint);
const Brush *BKE_paint_eraser_brush_for_read(const Paint *paint);
Brush *BKE_paint_eraser_brush_from_essentials(Main *bmain, const char *name);
bool BKE_paint_eraser_brush_set(Paint *paint, Brush *brush);
bool BKE_paint_eraser_brush_set_default(Main *bmain, Paint *paint);
bool BKE_paint_eraser_brush_set_essentials(Main *bmain, Paint *paint, const char *name);
/* Paint palette. */
Palette *BKE_paint_palette(Paint *paint);
void BKE_paint_palette_set(Paint *p, Palette *palette);
void BKE_paint_curve_clamp_endpoint_add_index(PaintCurve *pc, int add_index);
@ -255,19 +276,6 @@ void paint_update_brush_rake_rotation(UnifiedPaintSettings &ups,
void BKE_paint_stroke_get_average(const Scene *scene, const Object *ob, float stroke[3]);
/* Tool slot API. */
void BKE_paint_toolslots_init_from_main(Main *bmain);
void BKE_paint_toolslots_len_ensure(Paint *paint, int len);
void BKE_paint_toolslots_brush_update_ex(Paint *paint, Brush *brush);
void BKE_paint_toolslots_brush_update(Paint *paint);
/**
* Run this to ensure brush types are set for each slot on entering modes
* (for new scenes for example).
*/
void BKE_paint_brush_validate(Main *bmain, Paint *paint);
Brush *BKE_paint_toolslots_brush_get(Paint *paint, int slot_index);
/* .blend I/O */
void BKE_paint_blend_write(BlendWriter *writer, Paint *paint);

View File

@ -64,6 +64,7 @@ set(SRC
intern/armature_update.cc
intern/asset.cc
intern/asset_weak_reference.cc
intern/asset_edit.cc
intern/attribute.cc
intern/attribute_access.cc
intern/attribute_math.cc
@ -253,7 +254,6 @@ set(SRC
intern/packedFile.cc
intern/paint.cc
intern/paint_canvas.cc
intern/paint_toolslots.cc
intern/particle.cc
intern/particle_child.cc
intern/particle_distribute.cc
@ -332,6 +332,7 @@ set(SRC
BKE_appdir.hh
BKE_armature.hh
BKE_asset.hh
BKE_asset_edit.hh
BKE_attribute.h
BKE_attribute.hh
BKE_attribute_math.hh

View File

@ -0,0 +1,453 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#include <memory>
#include <utility>
#include "BLI_fileops.h"
#include "BLI_path_util.h"
#include "BLI_string.h"
#include "BLI_vector.hh"
#include "DNA_asset_types.h"
#include "DNA_space_types.h"
#include "AS_asset_identifier.hh"
#include "AS_asset_library.hh"
#include "BKE_asset.hh"
#include "BKE_asset_edit.hh"
#include "BKE_blendfile.hh"
#include "BKE_blendfile_link_append.hh"
#include "BKE_global.hh"
#include "BKE_idtype.hh"
#include "BKE_lib_id.hh"
#include "BKE_lib_remap.hh"
#include "BKE_library.hh"
#include "BKE_main.hh"
#include "BKE_packedFile.h"
#include "BKE_preferences.h"
#include "BKE_report.hh"
#include "BLO_read_write.hh"
#include "BLO_readfile.hh"
#include "BLO_writefile.hh"
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_build.hh"
#include "MEM_guardedalloc.h"
namespace blender::bke {
static ID *asset_link_id(Main &global_main,
const ID_Type id_type,
const char *filepath,
const char *asset_name)
{
/* Load asset from asset library. */
LibraryLink_Params lapp_params{};
lapp_params.bmain = &global_main;
BlendfileLinkAppendContext *lapp_context = BKE_blendfile_link_append_context_new(&lapp_params);
BKE_blendfile_link_append_context_flag_set(lapp_context, BLO_LIBLINK_FORCE_INDIRECT, true);
BKE_blendfile_link_append_context_library_add(lapp_context, filepath, nullptr);
BlendfileLinkAppendContextItem *lapp_item = BKE_blendfile_link_append_context_item_add(
lapp_context, asset_name, id_type, nullptr);
BKE_blendfile_link_append_context_item_library_index_enable(lapp_context, lapp_item, 0);
BKE_blendfile_link(lapp_context, nullptr);
ID *local_asset = BKE_blendfile_link_append_context_item_newid_get(lapp_context, lapp_item);
BKE_blendfile_link_append_context_free(lapp_context);
/* Verify that the name matches. It must for referencing the same asset again to work. */
BLI_assert(local_asset == nullptr || STREQ(local_asset->name + 2, asset_name));
/* Tag library as being editable. */
if (local_asset && local_asset->lib) {
local_asset->lib->runtime.tag |= LIBRARY_ASSET_EDITABLE;
/* Simple check, based on being a writable .asset.blend file in a user asset library. */
if (StringRef(filepath).endswith(BLENDER_ASSET_FILE_SUFFIX) &&
BKE_preferences_asset_library_containing_path(&U, filepath) &&
BLI_file_is_writable(filepath))
{
local_asset->lib->runtime.tag |= LIBRARY_ASSET_FILE_WRITABLE;
}
}
return local_asset;
}
static std::string asset_root_path_for_save(const bUserAssetLibrary &user_library,
const ID_Type id_type)
{
BLI_assert(user_library.dirpath[0] != '\0');
char libpath[FILE_MAX];
BLI_strncpy(libpath, user_library.dirpath, sizeof(libpath));
BLI_path_slash_native(libpath);
BLI_path_normalize(libpath);
/* Capitalize folder name. Ideally this would already available in
* the type info to work correctly with multiple words. */
const IDTypeInfo *id_type_info = BKE_idtype_get_info_from_idcode(id_type);
std::string name = id_type_info->name_plural;
name[0] = BLI_toupper_ascii(name[0]);
return std::string(libpath) + SEP + "Saved" + SEP + name;
}
static std::string asset_blendfile_path_for_save(const bUserAssetLibrary &user_library,
const StringRef base_name,
const ID_Type id_type,
ReportList &reports)
{
std::string root_path = asset_root_path_for_save(user_library, id_type);
BLI_assert(!root_path.empty());
if (!BLI_dir_create_recursive(root_path.c_str())) {
BKE_report(&reports, RPT_ERROR, "Failed to create asset library directory to save asset");
return "";
}
/* Make sure filename only contains valid characters for filesystem. */
char base_name_filesafe[FILE_MAXFILE];
BLI_strncpy(base_name_filesafe,
base_name.data(),
std::min(sizeof(base_name_filesafe), size_t(base_name.size() + 1)));
BLI_path_make_safe_filename(base_name_filesafe);
const std::string filepath = root_path + SEP + base_name_filesafe + BLENDER_ASSET_FILE_SUFFIX;
if (!BLI_is_file(filepath.c_str())) {
return filepath;
}
/* Avoid overwriting existing file by adding number suffix. */
for (int i = 1;; i++) {
const std::string filepath = root_path + SEP + base_name_filesafe + "_" + std::to_string(i++) +
BLENDER_ASSET_FILE_SUFFIX;
if (!BLI_is_file((filepath.c_str()))) {
return filepath;
}
}
return "";
}
static void asset_main_create_expander(void * /*handle*/, Main * /*bmain*/, void *vid)
{
ID *id = static_cast<ID *>(vid);
if (id && (id->tag & LIB_TAG_DOIT) == 0) {
id->tag |= LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT;
}
}
static Main *asset_main_create_from_ID(Main &bmain_src, ID &id_asset, ID **id_asset_new)
{
/* Tag asset ID and its dependencies. */
ID *id_src;
FOREACH_MAIN_ID_BEGIN (&bmain_src, id_src) {
id_src->tag &= ~(LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT);
}
FOREACH_MAIN_ID_END;
id_asset.tag |= LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT;
BLO_expand_main(nullptr, &bmain_src, asset_main_create_expander);
/* Create main and copy all tagged datablocks. */
Main *bmain_dst = BKE_main_new();
STRNCPY(bmain_dst->filepath, bmain_src.filepath);
blender::bke::id::IDRemapper id_remapper;
FOREACH_MAIN_ID_BEGIN (&bmain_src, id_src) {
if (id_src->tag & LIB_TAG_DOIT) {
/* Note that this will not copy Library datablocks, and all copied
* datablocks will become local as a result. */
ID *id_dst = BKE_id_copy_ex(bmain_dst,
id_src,
nullptr,
LIB_ID_CREATE_NO_USER_REFCOUNT | LIB_ID_CREATE_NO_DEG_TAG |
((id_src == &id_asset) ? LIB_ID_COPY_ASSET_METADATA : 0));
id_remapper.add(id_src, id_dst);
if (id_src == &id_asset) {
*id_asset_new = id_dst;
}
}
else {
id_remapper.add(id_src, nullptr);
}
id_src->tag &= ~(LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT);
}
FOREACH_MAIN_ID_END;
/* Remap datablock pointers. */
BKE_libblock_remap_multiple_raw(bmain_dst, id_remapper, ID_REMAP_SKIP_USER_CLEAR);
/* Compute reference counts. */
ID *id_dst;
FOREACH_MAIN_ID_BEGIN (bmain_dst, id_dst) {
id_dst->tag &= ~LIB_TAG_NO_USER_REFCOUNT;
}
FOREACH_MAIN_ID_END;
BKE_main_id_refcount_recompute(bmain_dst, false);
return bmain_dst;
}
static bool asset_write_in_library(Main &bmain,
const ID &id_const,
const StringRef name,
const StringRefNull filepath,
std::string &final_full_file_path,
ReportList &reports)
{
ID &id = const_cast<ID &>(id_const);
ID *new_id = nullptr;
Main *new_main = asset_main_create_from_ID(bmain, id, &new_id);
std::string new_name = name;
BKE_libblock_rename(new_main, new_id, new_name.c_str());
id_fake_user_set(new_id);
BlendFileWriteParams blend_file_write_params{};
blend_file_write_params.remap_mode = BLO_WRITE_PATH_REMAP_RELATIVE;
BKE_packedfile_pack_all(new_main, nullptr, false);
const int write_flags = G_FILE_COMPRESS;
const bool success = BLO_write_file(
new_main, filepath.c_str(), write_flags, &blend_file_write_params, &reports);
if (success) {
const IDTypeInfo *idtype = BKE_idtype_get_info_from_id(&id);
final_full_file_path = std::string(filepath) + SEP + std::string(idtype->name) + SEP + name;
}
BKE_main_free(new_main);
return success;
}
static void asset_reload(Main &global_main, Library *lib, ReportList &reports)
{
/* Fill fresh main database with same datablock as before. */
LibraryLink_Params lapp_params{};
lapp_params.bmain = &global_main;
BlendfileLinkAppendContext *lapp_context = BKE_blendfile_link_append_context_new(&lapp_params);
BKE_blendfile_link_append_context_flag_set(
lapp_context, BLO_LIBLINK_FORCE_INDIRECT | BLO_LIBLINK_USE_PLACEHOLDERS, true);
BKE_blendfile_link_append_context_library_add(lapp_context, lib->runtime.filepath_abs, nullptr);
BKE_blendfile_library_relocate(lapp_context, &reports, lib, true);
BKE_blendfile_link_append_context_free(lapp_context);
/* Clear temporary tag from reloaction. */
BKE_main_id_tag_all(&global_main, LIB_TAG_PRE_EXISTING, false);
/* Recreate dependency graph to include new IDs. */
DEG_relations_tag_update(&global_main);
}
static AssetWeakReference asset_weak_reference_for_user_library(
const bUserAssetLibrary &user_library,
const short idcode,
const char *idname,
const char *filepath)
{
AssetWeakReference weak_ref;
weak_ref.asset_library_type = ASSET_LIBRARY_CUSTOM;
weak_ref.asset_library_identifier = BLI_strdup(user_library.name);
/* BLI_path_rel requires a trailing slash. */
char user_library_dirpath[FILE_MAX];
STRNCPY(user_library_dirpath, user_library.dirpath);
BLI_path_slash_ensure(user_library_dirpath, sizeof(user_library_dirpath));
char relative_filepath[FILE_MAX];
STRNCPY(relative_filepath, filepath);
BLI_path_rel(relative_filepath, user_library_dirpath);
const char *asset_blend_path = relative_filepath + 2; /* Strip out // prefix. */
weak_ref.relative_asset_identifier = BLI_sprintfN(
"%s/%s/%s", asset_blend_path, BKE_idtype_idcode_to_name(idcode), idname);
return weak_ref;
}
static AssetWeakReference asset_weak_reference_for_essentials(const short idcode,
const char *idname,
const char *filepath)
{
AssetWeakReference weak_ref;
weak_ref.asset_library_type = ASSET_LIBRARY_ESSENTIALS;
weak_ref.relative_asset_identifier = BLI_sprintfN("%s/%s/%s/%s",
BKE_idtype_idcode_to_name_plural(idcode),
BLI_path_basename(filepath),
BKE_idtype_idcode_to_name(idcode),
idname);
return weak_ref;
}
/**
* Public API
*/
std::optional<std::string> asset_edit_id_save_as(Main &global_main,
const ID &id,
const StringRef name,
const bUserAssetLibrary &user_library,
AssetWeakReference &new_weak_ref,
ReportList &reports)
{
const std::string filepath = asset_blendfile_path_for_save(
user_library, name, GS(id.name), reports);
std::string final_full_asset_filepath;
const bool success = asset_write_in_library(
global_main, id, name, filepath, final_full_asset_filepath, reports);
if (!success) {
BKE_report(&reports, RPT_ERROR, "Failed to write to asset library");
return std::nullopt;
}
new_weak_ref = asset_weak_reference_for_user_library(
user_library, GS(id.name), id.name + 2, filepath.c_str());
BKE_reportf(&reports, RPT_INFO, "Saved \"%s\"", filepath.c_str());
return final_full_asset_filepath;
}
bool asset_edit_id_save(Main &global_main, const ID &id, ReportList &reports)
{
if (!asset_edit_id_is_editable(id)) {
return false;
}
std::string final_full_asset_filepath;
const bool success = asset_write_in_library(global_main,
id,
id.name + 2,
id.lib->runtime.filepath_abs,
final_full_asset_filepath,
reports);
if (!success) {
BKE_report(&reports, RPT_ERROR, "Failed to write to asset library");
return false;
}
return true;
}
bool asset_edit_id_revert(Main &global_main, ID &id, ReportList &reports)
{
if (!asset_edit_id_is_editable(id)) {
return false;
}
/* Reload entire main, including texture dependencies. This relies on there
* being only a single asset per blend file. */
asset_reload(global_main, id.lib, reports);
return true;
}
bool asset_edit_id_delete(Main &global_main, ID &id, ReportList &reports)
{
if (asset_edit_id_is_editable(id)) {
if (BLI_delete(id.lib->runtime.filepath_abs, false, false) != 0) {
BKE_report(&reports, RPT_ERROR, "Failed to delete asset library file");
return false;
}
}
BKE_id_delete(&global_main, &id);
return true;
}
ID *asset_edit_id_from_weak_reference(Main &global_main,
const ID_Type id_type,
const AssetWeakReference &weak_ref)
{
/* Don't do this in file load. */
BLI_assert(!global_main.is_locked_for_linking);
char asset_full_path_buffer[FILE_MAX_LIBEXTRA];
char *asset_lib_path, *asset_group, *asset_name;
AS_asset_full_path_explode_from_weak_ref(
&weak_ref, asset_full_path_buffer, &asset_lib_path, &asset_group, &asset_name);
if (asset_lib_path == nullptr && asset_group == nullptr && asset_name == nullptr) {
return nullptr;
}
/* If this is the same file as we have open, use local datablock. */
if (asset_lib_path && STREQ(asset_lib_path, global_main.filepath)) {
asset_lib_path = nullptr;
}
BLI_assert(asset_name != nullptr);
/* Test if asset has been loaded already. */
ID *local_asset = BKE_libblock_find_name_and_library_filepath(
&global_main, id_type, asset_name, asset_lib_path);
if (local_asset) {
return local_asset;
}
/* Try linking in the required file. */
if (asset_lib_path == nullptr) {
return nullptr;
}
return asset_link_id(global_main, id_type, asset_lib_path, asset_name);
}
std::optional<AssetWeakReference> asset_edit_weak_reference_from_id(ID &id)
{
if (!asset_edit_id_is_editable(id)) {
return std::nullopt;
}
bUserAssetLibrary *user_library = BKE_preferences_asset_library_containing_path(
&U, id.lib->runtime.filepath_abs);
const short idcode = GS(id.name);
if (user_library && user_library->dirpath[0]) {
return asset_weak_reference_for_user_library(
*user_library, idcode, id.name + 2, id.lib->runtime.filepath_abs);
}
else {
return asset_weak_reference_for_essentials(idcode, id.name + 2, id.lib->runtime.filepath_abs);
}
}
bool asset_edit_id_is_editable(const ID &id)
{
return (id.lib && (id.lib->runtime.tag & LIBRARY_ASSET_EDITABLE));
}
bool asset_edit_id_is_writable(const ID &id)
{
return asset_edit_id_is_editable(id) && (id.lib->runtime.tag & LIBRARY_ASSET_FILE_WRITABLE);
}
} // namespace blender::bke

View File

@ -23,6 +23,7 @@
#include "BKE_addon.h"
#include "BKE_asset.hh"
#include "BKE_asset_edit.hh"
#include "BKE_blender.hh" /* own include */
#include "BKE_blender_user_menu.hh" /* own include */
#include "BKE_blender_version.h" /* own include */

View File

@ -874,6 +874,9 @@ static void setup_app_data(bContext *C,
* (but may have a null `curscreen`). */
else if (ELEM(nullptr, bfd->curscreen, bfd->curscene)) {
BKE_report(reports->reports, RPT_WARNING, "Library file, loading empty scene");
if (blender::StringRefNull(bfd->main->filepath).endswith(BLENDER_ASSET_FILE_SUFFIX)) {
bfd->main->is_asset_repository = true;
}
mode = LOAD_UI_OFF;
}
else if (G.fileflags & G_FILE_NO_UI) {
@ -1468,6 +1471,15 @@ UserDef *BKE_blendfile_userdef_from_defaults()
BKE_preferences_extension_repo_add_default(userdef);
BKE_preferences_extension_repo_add_default_user(userdef);
{
BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(
userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh/Sculpt/Cloth");
BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(
userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh/Sculpt/General");
BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(
userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh/Sculpt/Painting");
}
return userdef;
}

File diff suppressed because it is too large Load Diff

View File

@ -131,9 +131,10 @@ void CTX_free(bContext *C)
/* store */
bContextStore *CTX_store_add(Vector<std::unique_ptr<bContextStore>> &contexts,
const blender::StringRefNull name,
const PointerRNA *ptr)
/**
* Append a new context store to \a contexts, copying entries from the previous one if any.
*/
static bContextStore *ctx_store_extend(Vector<std::unique_ptr<bContextStore>> &contexts)
{
/* ensure we have a context to put the entry in, if it was already used
* we have to copy the context to ensure */
@ -145,25 +146,31 @@ bContextStore *CTX_store_add(Vector<std::unique_ptr<bContextStore>> &contexts,
contexts.append(std::move(new_ctx));
}
bContextStore *ctx = contexts.last().get();
return contexts.last().get();
}
bContextStore *CTX_store_add(Vector<std::unique_ptr<bContextStore>> &contexts,
const blender::StringRefNull name,
const PointerRNA *ptr)
{
bContextStore *ctx = ctx_store_extend(contexts);
ctx->entries.append(bContextStoreEntry{name, *ptr});
return ctx;
}
bContextStore *CTX_store_add(Vector<std::unique_ptr<bContextStore>> &contexts,
const blender::StringRefNull name,
const blender::StringRef str)
{
bContextStore *ctx = ctx_store_extend(contexts);
ctx->entries.append(bContextStoreEntry{name, std::string{str}});
return ctx;
}
bContextStore *CTX_store_add_all(Vector<std::unique_ptr<bContextStore>> &contexts,
const bContextStore *context)
{
/* ensure we have a context to put the entry in, if it was already used
* we have to copy the context to ensure */
if (contexts.is_empty()) {
contexts.append(std::make_unique<bContextStore>());
}
else if (contexts.last()->used) {
auto new_ctx = std::make_unique<bContextStore>(bContextStore{contexts.last()->entries, false});
contexts.append(std::move(new_ctx));
}
bContextStore *ctx = contexts.last().get();
bContextStore *ctx = ctx_store_extend(contexts);
for (const bContextStoreEntry &src_entry : context->entries) {
ctx->entries.append(src_entry);
}
@ -185,15 +192,27 @@ const PointerRNA *CTX_store_ptr_lookup(const bContextStore *store,
const StructRNA *type)
{
for (auto entry = store->entries.rbegin(); entry != store->entries.rend(); ++entry) {
if (entry->name == name) {
if (!type || RNA_struct_is_a(entry->ptr.type, type)) {
return &entry->ptr;
if (entry->name == name && std::holds_alternative<PointerRNA>(entry->value)) {
const PointerRNA &ptr = std::get<PointerRNA>(entry->value);
if (!type || RNA_struct_is_a(ptr.type, type)) {
return &ptr;
}
}
}
return nullptr;
}
std::optional<blender::StringRefNull> CTX_store_string_lookup(const bContextStore *store,
const blender::StringRefNull name)
{
for (auto entry = store->entries.rbegin(); entry != store->entries.rend(); ++entry) {
if (entry->name == name && std::holds_alternative<std::string>(entry->value)) {
return std::get<std::string>(entry->value);
}
}
return {};
}
/* is python initialized? */
bool CTX_py_init_get(bContext *C)
@ -235,6 +254,7 @@ struct bContextDataResult {
Vector<PointerRNA> list;
PropertyRNA *prop;
int index;
blender::StringRefNull str;
const char **dir;
short type; /* 0: normal, 1: seq */
};
@ -313,6 +333,15 @@ static eContextResult ctx_data_get(bContext *C, const char *member, bContextData
result->ptr = *ptr;
done = 1;
}
else {
std::optional<blender::StringRefNull> str = CTX_store_string_lookup(C->wm.store, member);
if (str) {
result->str = *str;
result->type = CTX_DATA_TYPE_STRING;
done = 1;
}
}
}
if (done != 1 && recursion < 2 && (region = CTX_wm_region(C))) {
C->data.recursion = 2;
@ -487,6 +516,17 @@ void CTX_data_collection_remap_property(blender::MutableSpan<PointerRNA> collect
}
}
std::optional<blender::StringRefNull> CTX_data_string_get(const bContext *C, const char *member)
{
bContextDataResult result;
if (ctx_data_get((bContext *)C, member, &result) == CTX_RESULT_OK) {
BLI_assert(result.type == CTX_DATA_TYPE_STRING);
return result.str;
}
return {};
}
int /*eContextResult*/ CTX_data_get(const bContext *C,
const char *member,
PointerRNA *r_ptr,

View File

@ -1572,6 +1572,12 @@ void BKE_libblock_copy_in_lib(Main *bmain,
}
}
if (flag & LIB_ID_COPY_ASSET_METADATA) {
if (id->asset_data) {
new_id->asset_data = BKE_asset_metadata_copy(id->asset_data);
}
}
if ((flag & LIB_ID_CREATE_NO_DEG_TAG) == 0 && (flag & LIB_ID_CREATE_NO_MAIN) == 0) {
DEG_id_type_tag(bmain, GS(new_id->name));
}

View File

@ -24,6 +24,7 @@
#include "DNA_ID.h"
#include "BKE_asset_edit.hh"
#include "BKE_bpath.hh"
#include "BKE_global.hh"
#include "BKE_idtype.hh"
@ -445,6 +446,16 @@ bool BKE_main_is_empty(Main *bmain)
return result;
}
bool BKE_main_has_issues(const Main *bmain)
{
return bmain->has_forward_compatibility_issues || bmain->is_asset_repository;
}
bool BKE_main_needs_overwrite_confirm(const Main *bmain)
{
return bmain->has_forward_compatibility_issues || bmain->is_asset_repository;
}
void BKE_main_lock(Main *bmain)
{
BLI_spin_lock((SpinLock *)bmain->lock);

View File

@ -13,6 +13,7 @@
#include "DNA_object_enums.h"
#include "MEM_guardedalloc.h"
#include "DNA_asset_types.h"
#include "DNA_brush_types.h"
#include "DNA_defaults.h"
#include "DNA_gpencil_legacy_types.h"
@ -33,12 +34,15 @@
#include "BLI_math_matrix.h"
#include "BLI_math_matrix.hh"
#include "BLI_math_vector.h"
#include "BLI_string.h"
#include "BLI_string_utf8.h"
#include "BLI_utildefines.h"
#include "BLI_vector.hh"
#include "BLT_translation.hh"
#include "BKE_asset.hh"
#include "BKE_asset_edit.hh"
#include "BKE_attribute.hh"
#include "BKE_brush.hh"
#include "BKE_ccg.h"
@ -444,61 +448,6 @@ const EnumPropertyItem *BKE_paint_get_tool_enum_from_paintmode(const PaintMode m
return nullptr;
}
const char *BKE_paint_get_tool_prop_id_from_paintmode(const PaintMode mode)
{
switch (mode) {
case PaintMode::Sculpt:
return "sculpt_tool";
case PaintMode::Vertex:
return "vertex_tool";
case PaintMode::Weight:
return "weight_tool";
case PaintMode::Texture2D:
case PaintMode::Texture3D:
return "image_tool";
case PaintMode::GPencil:
return "gpencil_tool";
case PaintMode::VertexGPencil:
return "gpencil_vertex_tool";
case PaintMode::SculptGPencil:
return "gpencil_sculpt_tool";
case PaintMode::WeightGPencil:
return "gpencil_weight_tool";
case PaintMode::SculptCurves:
return "curves_sculpt_tool";
case PaintMode::SculptGreasePencil:
return "gpencil_sculpt_tool";
case PaintMode::Invalid:
break;
}
/* Invalid paint mode. */
return nullptr;
}
const char *BKE_paint_get_tool_enum_translation_context_from_paintmode(const PaintMode mode)
{
switch (mode) {
case PaintMode::Sculpt:
case PaintMode::GPencil:
case PaintMode::Texture2D:
case PaintMode::Texture3D:
return BLT_I18NCONTEXT_ID_BRUSH;
case PaintMode::Vertex:
case PaintMode::Weight:
case PaintMode::VertexGPencil:
case PaintMode::SculptGPencil:
case PaintMode::WeightGPencil:
case PaintMode::SculptCurves:
case PaintMode::SculptGreasePencil:
case PaintMode::Invalid:
break;
}
/* Invalid paint mode. */
return BLT_I18NCONTEXT_DEFAULT;
}
Paint *BKE_paint_get_active(Scene *sce, ViewLayer *view_layer)
{
if (sce && view_layer) {
@ -663,68 +612,337 @@ PaintMode BKE_paintmode_get_from_tool(const bToolRef *tref)
return PaintMode::Invalid;
}
Brush *BKE_paint_brush(Paint *p)
static bool paint_brush_set_from_asset_reference(Main *bmain, Paint *paint)
{
return (Brush *)BKE_paint_brush_for_read((const Paint *)p);
/* Don't resolve this during file read, it will be done after. */
if (bmain->is_locked_for_linking) {
return false;
}
/* Attempt to restore a valid active brush from brush asset information. */
if (paint->brush != nullptr) {
return false;
}
if (paint->brush_asset_reference == nullptr) {
return false;
}
const Brush *BKE_paint_brush_for_read(const Paint *p)
Brush *brush = reinterpret_cast<Brush *>(blender::bke::asset_edit_id_from_weak_reference(
*bmain, ID_BR, *paint->brush_asset_reference));
BLI_assert(brush == nullptr || blender::bke::asset_edit_id_is_editable(brush->id));
/* Ensure we have a brush with appropriate mode to assign.
* Could happen if contents of asset blend was manually changed. */
if (brush == nullptr || (paint->runtime.ob_mode & brush->ob_mode) == 0) {
MEM_delete(paint->brush_asset_reference);
paint->brush_asset_reference = nullptr;
return false;
}
paint->brush = brush;
return true;
}
Brush *BKE_paint_brush(Paint *paint)
{
return p ? p->brush : nullptr;
return (Brush *)BKE_paint_brush_for_read((const Paint *)paint);
}
void BKE_paint_brush_set(Paint *p, Brush *br)
const Brush *BKE_paint_brush_for_read(const Paint *paint)
{
if (p) {
id_us_min((ID *)p->brush);
id_us_plus((ID *)br);
p->brush = br;
return paint ? paint->brush : nullptr;
}
BKE_paint_toolslots_brush_update(p);
bool BKE_paint_brush_set(Paint *paint, Brush *brush)
{
if (paint == nullptr || paint->brush == brush) {
return false;
}
if (brush && (paint->runtime.ob_mode & brush->ob_mode) == 0) {
return false;
}
paint->brush = brush;
MEM_delete(paint->brush_asset_reference);
paint->brush_asset_reference = nullptr;
if (brush != nullptr) {
std::optional<AssetWeakReference> weak_ref = blender::bke::asset_edit_weak_reference_from_id(
brush->id);
if (weak_ref.has_value()) {
paint->brush_asset_reference = MEM_new<AssetWeakReference>(__func__, *weak_ref);
}
}
void BKE_paint_runtime_init(const ToolSettings *ts, Paint *paint)
return true;
}
Brush *BKE_paint_brush_from_essentials(Main *bmain, const char *name)
{
AssetWeakReference weak_ref;
weak_ref.asset_library_type = eAssetLibraryType::ASSET_LIBRARY_ESSENTIALS;
weak_ref.relative_asset_identifier = BLI_sprintfN("brushes/essentials_brushes.blend/Brush/%s",
name);
return reinterpret_cast<Brush *>(
blender::bke::asset_edit_id_from_weak_reference(*bmain, ID_BR, weak_ref));
}
static void paint_brush_set_essentials_reference(Paint *paint, const char *name)
{
/* Set brush asset reference to a named brush in the essentials asset library. */
MEM_delete(paint->brush_asset_reference);
AssetWeakReference *weak_ref = MEM_new<AssetWeakReference>(__func__);
weak_ref->asset_library_type = eAssetLibraryType::ASSET_LIBRARY_ESSENTIALS;
weak_ref->relative_asset_identifier = BLI_sprintfN("brushes/essentials_brushes.blend/Brush/%s",
name);
paint->brush_asset_reference = weak_ref;
paint->brush = nullptr;
}
static void paint_eraser_brush_set_essentials_reference(Paint *paint, const char *name)
{
/* Set brush asset reference to a named brush in the essentials asset library. */
MEM_delete(paint->eraser_brush_asset_reference);
AssetWeakReference *weak_ref = MEM_new<AssetWeakReference>(__func__);
weak_ref->asset_library_type = eAssetLibraryType::ASSET_LIBRARY_ESSENTIALS;
weak_ref->relative_asset_identifier = BLI_sprintfN("brushes/essentials_brushes.blend/Brush/%s",
name);
paint->eraser_brush_asset_reference = weak_ref;
paint->eraser_brush = nullptr;
}
static void paint_brush_set_default_reference(Paint *paint,
const bool do_regular = true,
const bool do_eraser = true)
{
const char *name = nullptr;
const char *eraser_name = nullptr;
switch (paint->runtime.ob_mode) {
case OB_MODE_SCULPT:
name = "Draw";
break;
case OB_MODE_VERTEX_PAINT:
name = "Paint Vertex";
break;
case OB_MODE_WEIGHT_PAINT:
name = "Paint Weight";
break;
case OB_MODE_TEXTURE_PAINT:
name = "Paint Texture";
break;
case OB_MODE_SCULPT_CURVES:
name = "Comb Curves";
break;
case OB_MODE_PAINT_GPENCIL_LEGACY:
name = "Pencil";
eraser_name = "Eraser Soft";
break;
case OB_MODE_VERTEX_GPENCIL_LEGACY:
name = "Paint Point Color";
break;
case OB_MODE_SCULPT_GPENCIL_LEGACY:
name = "Smooth Stroke";
break;
case OB_MODE_WEIGHT_GPENCIL_LEGACY:
name = "Paint Point Weight";
break;
default:
BLI_assert_unreachable();
return;
}
if (do_regular && name) {
paint_brush_set_essentials_reference(paint, name);
}
if (do_eraser && eraser_name) {
paint_eraser_brush_set_essentials_reference(paint, eraser_name);
}
}
void BKE_paint_brushes_set_default_references(ToolSettings *ts)
{
if (ts->sculpt) {
paint_brush_set_default_reference(&ts->sculpt->paint);
}
if (ts->curves_sculpt) {
paint_brush_set_default_reference(&ts->curves_sculpt->paint);
}
if (ts->wpaint) {
paint_brush_set_default_reference(&ts->wpaint->paint);
}
if (ts->vpaint) {
paint_brush_set_default_reference(&ts->vpaint->paint);
}
if (ts->gp_paint) {
paint_brush_set_default_reference(&ts->gp_paint->paint);
}
if (ts->gp_vertexpaint) {
paint_brush_set_default_reference(&ts->gp_vertexpaint->paint);
}
if (ts->gp_sculptpaint) {
paint_brush_set_default_reference(&ts->gp_sculptpaint->paint);
}
if (ts->gp_weightpaint) {
paint_brush_set_default_reference(&ts->gp_weightpaint->paint);
}
paint_brush_set_default_reference(&ts->imapaint.paint);
}
bool BKE_paint_brush_set_default(Main *bmain, Paint *paint)
{
paint_brush_set_default_reference(paint, true, false);
return paint_brush_set_from_asset_reference(bmain, paint);
}
bool BKE_paint_brush_set_essentials(Main *bmain, Paint *paint, const char *name)
{
paint_brush_set_essentials_reference(paint, name);
return paint_brush_set_from_asset_reference(bmain, paint);
}
void BKE_paint_brushes_validate(Main *bmain, Paint *paint)
{
/* Clear brush with invalid mode. Unclear if this can still happen,
* but kept from old paint toolslots code. */
Brush *brush = BKE_paint_brush(paint);
if (brush && (paint->runtime.ob_mode & brush->ob_mode) == 0) {
BKE_paint_brush_set(paint, nullptr);
BKE_paint_brush_set_default(bmain, paint);
}
Brush *eraser_brush = BKE_paint_eraser_brush(paint);
if (eraser_brush && (paint->runtime.ob_mode & eraser_brush->ob_mode) == 0) {
BKE_paint_eraser_brush_set(paint, nullptr);
BKE_paint_eraser_brush_set_default(bmain, paint);
}
}
static bool paint_eraser_brush_set_from_asset_reference(Main *bmain, Paint *paint)
{
/* Don't resolve this during file read, it will be done after. */
if (bmain->is_locked_for_linking) {
return false;
}
/* Attempt to restore a valid active brush from brush asset information. */
if (paint->eraser_brush != nullptr) {
return false;
}
if (paint->eraser_brush_asset_reference == nullptr) {
return false;
}
Brush *brush = reinterpret_cast<Brush *>(blender::bke::asset_edit_id_from_weak_reference(
*bmain, ID_BR, *paint->eraser_brush_asset_reference));
BLI_assert(brush == nullptr || blender::bke::asset_edit_id_is_editable(brush->id));
/* Ensure we have a brush with appropriate mode to assign.
* Could happen if contents of asset blend was manually changed. */
if (brush == nullptr || (paint->runtime.ob_mode & brush->ob_mode) == 0) {
MEM_delete(paint->eraser_brush_asset_reference);
paint->eraser_brush_asset_reference = nullptr;
return false;
}
paint->eraser_brush = brush;
return true;
}
Brush *BKE_paint_eraser_brush(Paint *paint)
{
return (Brush *)BKE_paint_eraser_brush_for_read((const Paint *)paint);
}
const Brush *BKE_paint_eraser_brush_for_read(const Paint *paint)
{
return paint ? paint->eraser_brush : nullptr;
}
bool BKE_paint_eraser_brush_set(Paint *paint, Brush *brush)
{
if (paint == nullptr || paint->eraser_brush == brush) {
return false;
}
if (brush && (paint->runtime.ob_mode & brush->ob_mode) == 0) {
return false;
}
paint->eraser_brush = brush;
MEM_delete(paint->eraser_brush_asset_reference);
paint->eraser_brush_asset_reference = nullptr;
if (brush != nullptr) {
std::optional<AssetWeakReference> weak_ref = blender::bke::asset_edit_weak_reference_from_id(
brush->id);
if (weak_ref.has_value()) {
paint->eraser_brush_asset_reference = MEM_new<AssetWeakReference>(__func__, *weak_ref);
}
}
return true;
}
Brush *BKE_paint_eraser_brush_from_essentials(Main *bmain, const char *name)
{
AssetWeakReference weak_ref;
weak_ref.asset_library_type = eAssetLibraryType::ASSET_LIBRARY_ESSENTIALS;
weak_ref.relative_asset_identifier = BLI_sprintfN("brushes/essentials_brushes.blend/Brush/%s",
name);
return reinterpret_cast<Brush *>(
blender::bke::asset_edit_id_from_weak_reference(*bmain, ID_BR, weak_ref));
}
bool BKE_paint_eraser_brush_set_default(Main *bmain, Paint *paint)
{
paint_brush_set_default_reference(paint, false, true);
return paint_eraser_brush_set_from_asset_reference(bmain, paint);
}
bool BKE_paint_eraser_brush_set_essentials(Main *bmain, Paint *paint, const char *name)
{
paint_eraser_brush_set_essentials_reference(paint, name);
return paint_eraser_brush_set_from_asset_reference(bmain, paint);
}
static void paint_runtime_init(const ToolSettings *ts, Paint *paint)
{
if (paint == &ts->imapaint.paint) {
paint->runtime.tool_offset = offsetof(Brush, imagepaint_tool);
paint->runtime.ob_mode = OB_MODE_TEXTURE_PAINT;
}
else if (ts->sculpt && paint == &ts->sculpt->paint) {
paint->runtime.tool_offset = offsetof(Brush, sculpt_tool);
paint->runtime.ob_mode = OB_MODE_SCULPT;
}
else if (ts->vpaint && paint == &ts->vpaint->paint) {
paint->runtime.tool_offset = offsetof(Brush, vertexpaint_tool);
paint->runtime.ob_mode = OB_MODE_VERTEX_PAINT;
}
else if (ts->wpaint && paint == &ts->wpaint->paint) {
paint->runtime.tool_offset = offsetof(Brush, weightpaint_tool);
paint->runtime.ob_mode = OB_MODE_WEIGHT_PAINT;
}
else if (ts->gp_paint && paint == &ts->gp_paint->paint) {
paint->runtime.tool_offset = offsetof(Brush, gpencil_tool);
paint->runtime.ob_mode = OB_MODE_PAINT_GPENCIL_LEGACY;
}
else if (ts->gp_vertexpaint && paint == &ts->gp_vertexpaint->paint) {
paint->runtime.tool_offset = offsetof(Brush, gpencil_vertex_tool);
paint->runtime.ob_mode = OB_MODE_VERTEX_GPENCIL_LEGACY;
}
else if (ts->gp_sculptpaint && paint == &ts->gp_sculptpaint->paint) {
paint->runtime.tool_offset = offsetof(Brush, gpencil_sculpt_tool);
paint->runtime.ob_mode = OB_MODE_SCULPT_GPENCIL_LEGACY;
}
else if (ts->gp_weightpaint && paint == &ts->gp_weightpaint->paint) {
paint->runtime.tool_offset = offsetof(Brush, gpencil_weight_tool);
paint->runtime.ob_mode = OB_MODE_WEIGHT_GPENCIL_LEGACY;
}
else if (ts->curves_sculpt && paint == &ts->curves_sculpt->paint) {
paint->runtime.tool_offset = offsetof(Brush, curves_sculpt_tool);
paint->runtime.ob_mode = OB_MODE_SCULPT_CURVES;
}
else {
BLI_assert_unreachable();
}
paint->runtime.initialized = true;
}
uint BKE_paint_get_brush_tool_offset_from_paintmode(const PaintMode mode)
@ -1091,17 +1309,15 @@ eObjectMode BKE_paint_object_mode_from_paintmode(const PaintMode mode)
}
}
bool BKE_paint_ensure(Main * /*bmain*/, ToolSettings *ts, Paint **r_paint)
bool BKE_paint_ensure(Main *bmain, ToolSettings *ts, Paint **r_paint)
{
Paint *paint = nullptr;
if (*r_paint) {
/* Tool offset should never be 0 for initialized paint settings, so it's a reliable way to
* check if already initialized. */
if ((*r_paint)->runtime.tool_offset == 0) {
if (!(*r_paint)->runtime.initialized) {
/* Currently only image painting is initialized this way, others have to be allocated. */
BLI_assert(ELEM(*r_paint, (Paint *)&ts->imapaint));
BKE_paint_runtime_init(ts, *r_paint);
paint_runtime_init(ts, *r_paint);
}
else {
BLI_assert(ELEM(*r_paint,
@ -1117,13 +1333,14 @@ bool BKE_paint_ensure(Main * /*bmain*/, ToolSettings *ts, Paint **r_paint)
(Paint *)&ts->imapaint));
#ifndef NDEBUG
Paint paint_test = **r_paint;
BKE_paint_runtime_init(ts, *r_paint);
paint_runtime_init(ts, *r_paint);
/* Swap so debug doesn't hide errors when release fails. */
std::swap(**r_paint, paint_test);
BLI_assert(paint_test.runtime.ob_mode == (*r_paint)->runtime.ob_mode);
BLI_assert(paint_test.runtime.tool_offset == (*r_paint)->runtime.tool_offset);
#endif
}
paint_brush_set_from_asset_reference(bmain, *r_paint);
paint_eraser_brush_set_from_asset_reference(bmain, *r_paint);
return true;
}
@ -1166,7 +1383,9 @@ bool BKE_paint_ensure(Main * /*bmain*/, ToolSettings *ts, Paint **r_paint)
*r_paint = paint;
BKE_paint_runtime_init(ts, paint);
paint_runtime_init(ts, paint);
BKE_paint_brush_set_default(bmain, paint);
BKE_paint_eraser_brush_set_default(bmain, paint);
return false;
}
@ -1178,18 +1397,6 @@ void BKE_paint_init(Main *bmain, Scene *sce, PaintMode mode, const uchar col[3])
BKE_paint_ensure_from_paintmode(bmain, sce, mode);
/* If there's no brush, create one */
Brush *brush = BKE_paint_brush(paint);
if (brush == nullptr) {
eObjectMode ob_mode = BKE_paint_object_mode_from_paintmode(mode);
brush = BKE_brush_first_search(bmain, ob_mode);
if (!brush) {
brush = BKE_brush_add(bmain, "Brush", ob_mode);
id_us_min(&brush->id); /* Fake user only. */
}
BKE_paint_brush_set(paint, brush);
}
copy_v3_v3_uchar(paint->paint_cursor_col, col);
paint->paint_cursor_col[3] = 128;
ups->last_stroke_valid = false;
@ -1203,23 +1410,26 @@ void BKE_paint_init(Main *bmain, Scene *sce, PaintMode mode, const uchar col[3])
void BKE_paint_free(Paint *paint)
{
BKE_curvemapping_free(paint->cavity_curve);
MEM_SAFE_FREE(paint->tool_slots);
MEM_delete(paint->brush_asset_reference);
MEM_delete(paint->eraser_brush_asset_reference);
}
void BKE_paint_copy(const Paint *src, Paint *tar, const int flag)
void BKE_paint_copy(const Paint *src, Paint *dst, const int flag)
{
tar->brush = src->brush;
tar->cavity_curve = BKE_curvemapping_copy(src->cavity_curve);
tar->tool_slots = static_cast<PaintToolSlot *>(MEM_dupallocN(src->tool_slots));
dst->brush = src->brush;
dst->cavity_curve = BKE_curvemapping_copy(src->cavity_curve);
if (src->brush_asset_reference) {
dst->brush_asset_reference = MEM_new<AssetWeakReference>(__func__,
*src->brush_asset_reference);
}
if (src->eraser_brush_asset_reference) {
dst->eraser_brush_asset_reference = MEM_new<AssetWeakReference>(
__func__, *src->eraser_brush_asset_reference);
}
if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
id_us_plus((ID *)tar->brush);
id_us_plus((ID *)tar->palette);
if (src->tool_slots != nullptr) {
for (int i = 0; i < tar->tool_slots_len; i++) {
id_us_plus((ID *)tar->tool_slots[i].brush);
}
}
id_us_plus((ID *)dst->palette);
}
}
@ -1240,7 +1450,12 @@ void BKE_paint_blend_write(BlendWriter *writer, Paint *p)
if (p->cavity_curve) {
BKE_curvemapping_blend_write(writer, p->cavity_curve);
}
BLO_write_struct_array(writer, PaintToolSlot, p->tool_slots_len, p->tool_slots);
if (p->brush_asset_reference) {
BKE_asset_weak_reference_write(writer, p->brush_asset_reference);
}
if (p->eraser_brush_asset_reference) {
BKE_asset_weak_reference_write(writer, p->eraser_brush_asset_reference);
}
}
void BKE_paint_blend_read_data(BlendDataReader *reader, const Scene *scene, Paint *p)
@ -1253,17 +1468,18 @@ void BKE_paint_blend_read_data(BlendDataReader *reader, const Scene *scene, Pain
BKE_paint_cavity_curve_preset(p, CURVE_PRESET_LINE);
}
BLO_read_struct_array(reader, PaintToolSlot, p->tool_slots_len, &p->tool_slots);
BLO_read_struct(reader, AssetWeakReference, &p->brush_asset_reference);
if (p->brush_asset_reference) {
BKE_asset_weak_reference_read(reader, p->brush_asset_reference);
}
/* Workaround for invalid data written in older versions. */
const size_t expected_size = sizeof(PaintToolSlot) * p->tool_slots_len;
if (p->tool_slots && MEM_allocN_len(p->tool_slots) < expected_size) {
MEM_freeN(p->tool_slots);
p->tool_slots = static_cast<PaintToolSlot *>(MEM_callocN(expected_size, "PaintToolSlot"));
BLO_read_struct(reader, AssetWeakReference, &p->eraser_brush_asset_reference);
if (p->eraser_brush_asset_reference) {
BKE_asset_weak_reference_read(reader, p->eraser_brush_asset_reference);
}
p->paint_cursor = nullptr;
BKE_paint_runtime_init(scene->toolsettings, p);
paint_runtime_init(scene->toolsettings, p);
}
bool paint_is_grid_face_hidden(const blender::BoundedBitSpan grid_hidden,

View File

@ -1,167 +0,0 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#include <climits>
#include "MEM_guardedalloc.h"
#include "DNA_brush_types.h"
#include "DNA_scene_types.h"
#include "BLI_utildefines.h"
#include "BKE_brush.hh"
#include "BKE_lib_id.hh"
#include "BKE_main.hh"
#include "BKE_paint.hh"
/* -------------------------------------------------------------------- */
/** \name Tool Slot Initialization / Versioning
*
* These functions run to update old files (while versioning),
* take care only to perform low-level functions here.
* \{ */
void BKE_paint_toolslots_len_ensure(Paint *paint, int len)
{
/* Tool slots are 'uchar'. */
BLI_assert(len <= UCHAR_MAX);
if (paint->tool_slots_len < len) {
paint->tool_slots = static_cast<PaintToolSlot *>(
MEM_recallocN(paint->tool_slots, sizeof(*paint->tool_slots) * len));
paint->tool_slots_len = len;
}
}
static void paint_toolslots_init(Main *bmain, Paint *paint)
{
if (paint == nullptr) {
return;
}
const eObjectMode ob_mode = eObjectMode(paint->runtime.ob_mode);
BLI_assert(paint->runtime.tool_offset && ob_mode);
for (Brush *brush = static_cast<Brush *>(bmain->brushes.first); brush;
brush = static_cast<Brush *>(brush->id.next))
{
if (brush->ob_mode & ob_mode) {
const int slot_index = BKE_brush_tool_get(brush, paint);
BKE_paint_toolslots_len_ensure(paint, slot_index + 1);
if (paint->tool_slots[slot_index].brush == nullptr) {
paint->tool_slots[slot_index].brush = brush;
id_us_plus(&brush->id);
}
}
}
}
/**
* Initialize runtime since this is called from versioning code.
*/
static void paint_toolslots_init_with_runtime(Main *bmain, ToolSettings *ts, Paint *paint)
{
if (paint == nullptr) {
return;
}
/* Needed so #Paint_Runtime is updated when versioning. */
BKE_paint_runtime_init(ts, paint);
paint_toolslots_init(bmain, paint);
}
void BKE_paint_toolslots_init_from_main(Main *bmain)
{
for (Scene *scene = static_cast<Scene *>(bmain->scenes.first); scene;
scene = static_cast<Scene *>(scene->id.next))
{
ToolSettings *ts = scene->toolsettings;
paint_toolslots_init_with_runtime(bmain, ts, &ts->imapaint.paint);
if (ts->sculpt) {
paint_toolslots_init_with_runtime(bmain, ts, &ts->sculpt->paint);
}
if (ts->vpaint) {
paint_toolslots_init_with_runtime(bmain, ts, &ts->vpaint->paint);
}
if (ts->wpaint) {
paint_toolslots_init_with_runtime(bmain, ts, &ts->wpaint->paint);
}
if (ts->gp_paint) {
paint_toolslots_init_with_runtime(bmain, ts, &ts->gp_paint->paint);
}
if (ts->gp_vertexpaint) {
paint_toolslots_init_with_runtime(bmain, ts, &ts->gp_vertexpaint->paint);
}
if (ts->gp_sculptpaint) {
paint_toolslots_init_with_runtime(bmain, ts, &ts->gp_sculptpaint->paint);
}
if (ts->gp_weightpaint) {
paint_toolslots_init_with_runtime(bmain, ts, &ts->gp_weightpaint->paint);
}
if (ts->curves_sculpt) {
paint_toolslots_init_with_runtime(bmain, ts, &ts->curves_sculpt->paint);
}
}
}
/** \} */
void BKE_paint_toolslots_brush_update_ex(Paint *paint, Brush *brush)
{
const uint tool_offset = paint->runtime.tool_offset;
UNUSED_VARS_NDEBUG(tool_offset);
BLI_assert(tool_offset != 0);
const int slot_index = BKE_brush_tool_get(brush, paint);
BKE_paint_toolslots_len_ensure(paint, slot_index + 1);
PaintToolSlot *tslot = &paint->tool_slots[slot_index];
id_us_plus(&brush->id);
if (tslot->brush) {
id_us_min(&tslot->brush->id);
}
tslot->brush = brush;
}
void BKE_paint_toolslots_brush_update(Paint *paint)
{
if (paint->brush == nullptr) {
return;
}
BKE_paint_toolslots_brush_update_ex(paint, paint->brush);
}
void BKE_paint_brush_validate(Main *bmain, Paint *paint)
{
/* Clear slots with invalid slots or mode (unlikely but possible). */
const uint tool_offset = paint->runtime.tool_offset;
UNUSED_VARS_NDEBUG(tool_offset);
const eObjectMode ob_mode = eObjectMode(paint->runtime.ob_mode);
BLI_assert(tool_offset && ob_mode);
for (int i = 0; i < paint->tool_slots_len; i++) {
PaintToolSlot *tslot = &paint->tool_slots[i];
if (tslot->brush) {
if ((i != BKE_brush_tool_get(tslot->brush, paint)) || (tslot->brush->ob_mode & ob_mode) == 0)
{
id_us_min(&tslot->brush->id);
tslot->brush = nullptr;
}
}
}
/* Unlikely but possible the active brush is not currently using a slot. */
BKE_paint_toolslots_brush_update(paint);
/* Fill slots from brushes. */
paint_toolslots_init(bmain, paint);
}
Brush *BKE_paint_toolslots_brush_get(Paint *paint, int slot_index)
{
if (slot_index < paint->tool_slots_len) {
PaintToolSlot *tslot = &paint->tool_slots[slot_index];
return tslot->brush;
}
return nullptr;
}

View File

@ -590,27 +590,17 @@ static void scene_foreach_paint(LibraryForeachIDData *data,
SCENE_FOREACH_UNDO_RESTORE,
reader,
&paint_old->brush,
IDWALK_CB_USER);
IDWALK_CB_NOP);
for (int i = 0; i < paint_old->tool_slots_len; i++) {
/* This is a bit tricky.
* - In case we do not do `undo_restore`, `paint` and `paint_old` pointers are the same, so
* this is equivalent to simply looping over slots from `paint`.
* - In case we do `undo_restore`, we only want to consider the slots from the old one, since
* those are the one we keep in the end.
* + In case the new data has less valid slots, we feed in a dummy null pointer.
* + In case the new data has more valid slots, the extra ones are ignored.
*/
brush_tmp = nullptr;
brush_p = (paint && i < paint->tool_slots_len) ? &paint->tool_slots[i].brush : &brush_tmp;
Brush *eraser_brush_tmp = nullptr;
Brush **eraser_brush_p = paint ? &paint->eraser_brush : &eraser_brush_tmp;
BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER_P(data,
brush_p,
eraser_brush_p,
do_undo_restore,
SCENE_FOREACH_UNDO_RESTORE,
reader,
&paint_old->tool_slots[i].brush,
IDWALK_CB_USER);
}
&paint_old->eraser_brush,
IDWALK_CB_NOP);
Palette *palette_tmp = nullptr;
Palette **palette_p = paint ? &paint->palette : &palette_tmp;

View File

@ -2583,7 +2583,6 @@ void do_versions_after_linking_280(FileData *fd, Main *bmain)
brush->gpencil_tool = brush->gpencil_settings->brush_type;
}
}
BKE_paint_toolslots_init_from_main(bmain);
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 280, 38)) {
@ -2862,13 +2861,9 @@ void do_versions_after_linking_280(FileData *fd, Main *bmain)
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 282, 2)) {
/* Init all Vertex/Sculpt and Weight Paint brushes. */
Brush *brush;
Material *ma;
/* Pen Soft brush. */
brush = (Brush *)do_versions_rename_id(bmain, ID_BR, "Draw Soft", "Pencil Soft");
if (brush) {
brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PEN;
}
do_versions_rename_id(bmain, ID_BR, "Draw Soft", "Pencil Soft");
do_versions_rename_id(bmain, ID_BR, "Draw Pencil", "Pencil");
do_versions_rename_id(bmain, ID_BR, "Draw Pen", "Pen");
do_versions_rename_id(bmain, ID_BR, "Draw Ink", "Ink Pen");
@ -2896,9 +2891,6 @@ void do_versions_after_linking_280(FileData *fd, Main *bmain)
do_versions_rename_id(bmain, ID_MA, "Black Dots", "Dots Stroke");
}
brush = static_cast<Brush *>(
BLI_findstring(&bmain->brushes, "Pencil", offsetof(ID, name) + 2));
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
ToolSettings *ts = scene->toolsettings;
@ -2908,15 +2900,11 @@ void do_versions_after_linking_280(FileData *fd, Main *bmain)
BKE_paint_ensure_from_paintmode(bmain, scene, PaintMode::SculptGPencil);
BKE_paint_ensure_from_paintmode(bmain, scene, PaintMode::WeightGPencil);
/* Set default Draw brush. */
if (brush != nullptr) {
Paint *paint = &ts->gp_paint->paint;
BKE_paint_brush_set(paint, brush);
/* Enable cursor by default. */
Paint *paint = &ts->gp_paint->paint;
paint->flags |= PAINT_SHOW_BRUSH;
}
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 282, 4)) {
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {

View File

@ -27,6 +27,7 @@
#include "DNA_movieclip_types.h"
#include "DNA_scene_types.h"
#include "DNA_sequence_types.h"
#include "DNA_workspace_types.h"
#include "DNA_world_types.h"
#include "DNA_defaults.h"
@ -50,6 +51,7 @@
#include "BKE_armature.hh"
#include "BKE_attribute.hh"
#include "BKE_colortools.hh"
#include "BKE_context.hh"
#include "BKE_curve.hh"
#include "BKE_customdata.hh"
#include "BKE_effect.h"
@ -60,6 +62,7 @@
#include "BKE_mesh_legacy_convert.hh"
#include "BKE_nla.h"
#include "BKE_node_runtime.hh"
#include "BKE_paint.hh"
#include "BKE_scene.hh"
#include "BKE_tracking.h"
@ -2114,6 +2117,40 @@ static bool seq_filter_bilinear_to_auto(Sequence *seq, void * /*user_data*/)
return true;
}
static void update_paint_modes_for_brush_assets(Main &bmain)
{
/* Replace paint brushes with a reference to the default brush asset for that mode. */
LISTBASE_FOREACH (Scene *, scene, &bmain.scenes) {
BKE_paint_brushes_set_default_references(scene->toolsettings);
}
/* Replace persistent tool references with the new single builtin brush tool. */
LISTBASE_FOREACH (WorkSpace *, workspace, &bmain.workspaces) {
LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
if (tref->space_type != SPACE_VIEW3D) {
continue;
}
if (!ELEM(tref->mode,
CTX_MODE_SCULPT,
CTX_MODE_PAINT_VERTEX,
CTX_MODE_PAINT_WEIGHT,
CTX_MODE_PAINT_TEXTURE,
CTX_MODE_PAINT_GPENCIL_LEGACY,
CTX_MODE_PAINT_GREASE_PENCIL,
CTX_MODE_SCULPT_GPENCIL_LEGACY,
CTX_MODE_SCULPT_GREASE_PENCIL,
CTX_MODE_WEIGHT_GPENCIL_LEGACY,
CTX_MODE_WEIGHT_GREASE_PENCIL,
CTX_MODE_VERTEX_GPENCIL_LEGACY,
CTX_MODE_SCULPT_CURVES))
{
continue;
}
STRNCPY(tref->idname, "builtin.brush");
}
}
}
static void image_settings_avi_to_ffmpeg(Scene *scene)
{
if (ELEM(scene->r.im_format.imtype, R_IMF_IMTYPE_AVIRAW, R_IMF_IMTYPE_AVIJPEG)) {
@ -3640,6 +3677,10 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 402, 43)) {
update_paint_modes_for_brush_assets(*bmain);
}
/**
* Always bump subversion in BKE_blender_version.h when adding versioning
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.

View File

@ -21,6 +21,7 @@
#include "BLI_math_rotation.h"
#include "BLI_math_vector.h"
#include "BLI_math_vector_types.hh"
#include "BLI_mempool.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
@ -451,43 +452,8 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template)
BLO_update_defaults_workspace(workspace, app_template);
}
/* New grease pencil brushes and vertex paint setup. */
/* Grease pencil materials and paint modes setup. */
{
/* Update Grease Pencil brushes. */
Brush *brush;
/* Pencil brush. */
do_versions_rename_id(bmain, ID_BR, "Draw Pencil", "Pencil");
/* Pen brush. */
do_versions_rename_id(bmain, ID_BR, "Draw Pen", "Pen");
/* Pen Soft brush. */
brush = reinterpret_cast<Brush *>(
do_versions_rename_id(bmain, ID_BR, "Draw Soft", "Pencil Soft"));
if (brush) {
brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PEN;
}
/* Ink Pen brush. */
do_versions_rename_id(bmain, ID_BR, "Draw Ink", "Ink Pen");
/* Ink Pen Rough brush. */
do_versions_rename_id(bmain, ID_BR, "Draw Noise", "Ink Pen Rough");
/* Marker Bold brush. */
do_versions_rename_id(bmain, ID_BR, "Draw Marker", "Marker Bold");
/* Marker Chisel brush. */
do_versions_rename_id(bmain, ID_BR, "Draw Block", "Marker Chisel");
/* Remove useless Fill Area.001 brush. */
brush = static_cast<Brush *>(
BLI_findstring(&bmain->brushes, "Fill Area.001", offsetof(ID, name) + 2));
if (brush) {
BKE_id_delete(bmain, brush);
}
/* Rename and fix materials and enable default object lights on. */
if (app_template && STREQ(app_template, "2D_Animation")) {
Material *ma = nullptr;
@ -538,23 +504,10 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template)
}
}
/* Reset all grease pencil brushes. */
/* Reset grease pencil paint modes. */
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
ToolSettings *ts = scene->toolsettings;
if (ts->gp_paint) {
BKE_brush_gpencil_paint_presets(bmain, ts, true);
}
if (ts->gp_sculptpaint) {
BKE_brush_gpencil_sculpt_presets(bmain, ts, true);
}
if (ts->gp_vertexpaint) {
BKE_brush_gpencil_vertex_presets(bmain, ts, true);
}
if (ts->gp_weightpaint) {
BKE_brush_gpencil_weight_presets(bmain, ts, true);
}
/* Ensure new Paint modes. */
BKE_paint_ensure_from_paintmode(bmain, scene, PaintMode::VertexGPencil);
BKE_paint_ensure_from_paintmode(bmain, scene, PaintMode::SculptGPencil);
@ -711,203 +664,24 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template)
/* Brushes */
{
/* Enable for UV sculpt (other brush types will be created as needed),
* without this the grab brush will be active but not selectable from the list. */
const char *brush_name = "Grab";
Brush *brush = static_cast<Brush *>(
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
if (brush) {
brush->ob_mode |= OB_MODE_EDIT;
/* Remove default brushes replaced by assets. Also remove outliner `treestore` that may point
* to brushes. Normally the treestore is updated properly but it doesn't seem to update during
* versioning code. It's not helpful anyway. */
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, space_link, &area->spacedata) {
if (space_link->spacetype == SPACE_OUTLINER) {
SpaceOutliner *space_outliner = reinterpret_cast<SpaceOutliner *>(space_link);
if (space_outliner->treestore) {
BLI_mempool_destroy(space_outliner->treestore);
space_outliner->treestore = nullptr;
}
}
LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
brush->blur_kernel_radius = 2;
/* Use full strength for all non-sculpt brushes,
* when painting we want to use full color/weight always.
*
* Note that sculpt is an exception,
* its values are overwritten by #BKE_brush_sculpt_reset below. */
brush->alpha = 1.0;
/* Enable anti-aliasing by default. */
brush->sampling_flag |= BRUSH_PAINT_ANTIALIASING;
/* By default, each brush should use a single input sample. */
brush->input_samples = 1;
}
{
/* Change the spacing of the Smear brush to 3.0% */
const char *brush_name;
Brush *brush;
brush_name = "Smear";
brush = static_cast<Brush *>(
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
if (brush) {
brush->spacing = 3.0;
}
brush_name = "Draw Sharp";
brush = static_cast<Brush *>(
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
if (!brush) {
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
id_us_min(&brush->id);
brush->sculpt_tool = SCULPT_TOOL_DRAW_SHARP;
}
brush_name = "Elastic Deform";
brush = static_cast<Brush *>(
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
if (!brush) {
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
id_us_min(&brush->id);
brush->sculpt_tool = SCULPT_TOOL_ELASTIC_DEFORM;
}
brush_name = "Pose";
brush = static_cast<Brush *>(
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
if (!brush) {
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
id_us_min(&brush->id);
brush->sculpt_tool = SCULPT_TOOL_POSE;
}
brush_name = "Multi-plane Scrape";
brush = static_cast<Brush *>(
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
if (!brush) {
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
id_us_min(&brush->id);
brush->sculpt_tool = SCULPT_TOOL_MULTIPLANE_SCRAPE;
}
brush_name = "Clay Thumb";
brush = static_cast<Brush *>(
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
if (!brush) {
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
id_us_min(&brush->id);
brush->sculpt_tool = SCULPT_TOOL_CLAY_THUMB;
}
brush_name = "Cloth";
brush = static_cast<Brush *>(
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
if (!brush) {
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
id_us_min(&brush->id);
brush->sculpt_tool = SCULPT_TOOL_CLOTH;
}
brush_name = "Slide Relax";
brush = static_cast<Brush *>(
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
if (!brush) {
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
id_us_min(&brush->id);
brush->sculpt_tool = SCULPT_TOOL_SLIDE_RELAX;
}
brush_name = "Paint";
brush = static_cast<Brush *>(
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
if (!brush) {
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
id_us_min(&brush->id);
brush->sculpt_tool = SCULPT_TOOL_PAINT;
}
brush_name = "Smear";
brush = static_cast<Brush *>(
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
if (!brush) {
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
id_us_min(&brush->id);
brush->sculpt_tool = SCULPT_TOOL_SMEAR;
}
brush_name = "Boundary";
brush = static_cast<Brush *>(
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
if (!brush) {
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
id_us_min(&brush->id);
brush->sculpt_tool = SCULPT_TOOL_BOUNDARY;
}
brush_name = "Simplify";
brush = static_cast<Brush *>(
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
if (!brush) {
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
id_us_min(&brush->id);
brush->sculpt_tool = SCULPT_TOOL_SIMPLIFY;
}
brush_name = "Draw Face Sets";
brush = static_cast<Brush *>(
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
if (!brush) {
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
id_us_min(&brush->id);
brush->sculpt_tool = SCULPT_TOOL_DRAW_FACE_SETS;
}
brush_name = "Multires Displacement Eraser";
brush = static_cast<Brush *>(
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
if (!brush) {
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
id_us_min(&brush->id);
brush->sculpt_tool = SCULPT_TOOL_DISPLACEMENT_ERASER;
}
brush_name = "Multires Displacement Smear";
brush = static_cast<Brush *>(
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
if (!brush) {
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
id_us_min(&brush->id);
brush->sculpt_tool = SCULPT_TOOL_DISPLACEMENT_SMEAR;
}
}
{
LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
/* Use the same tool icon color in the brush cursor */
if (brush->ob_mode & OB_MODE_SCULPT) {
BLI_assert(brush->sculpt_tool != 0);
BKE_brush_sculpt_reset(brush);
}
/* Set the default texture mapping.
* Do it for all brushes, since some of them might be coming from the startup file. */
brush->mtex.brush_map_mode = MTEX_MAP_MODE_VIEW;
brush->mask_mtex.brush_map_mode = MTEX_MAP_MODE_VIEW;
}
}
{
const Brush *default_brush = DNA_struct_default_get(Brush);
LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
brush->automasking_start_normal_limit = default_brush->automasking_start_normal_limit;
brush->automasking_start_normal_falloff = default_brush->automasking_start_normal_falloff;
brush->automasking_view_normal_limit = default_brush->automasking_view_normal_limit;
brush->automasking_view_normal_falloff = default_brush->automasking_view_normal_falloff;
}
}
{
LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
if (!brush->automasking_cavity_curve) {
brush->automasking_cavity_curve = BKE_sculpt_default_cavity_curve();
}
LISTBASE_FOREACH_MUTABLE (Brush *, brush, &bmain->brushes) {
BKE_id_delete(bmain, brush);
}
}

View File

@ -961,6 +961,15 @@ void blo_do_versions_userdef(UserDef *userdef)
}
}
if (!USER_VERSION_ATLEAST(402, 43)) {
BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(
userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh Sculpt/Cloth");
BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(
userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh Sculpt/General");
BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(
userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh Sculpt/Paint");
}
/**
* Always bump subversion in BKE_blender_version.h when adding versioning
* code here, and wrap it inside a USER_VERSION_ATLEAST check.

View File

@ -34,7 +34,7 @@ set(SRC
intern/asset_shelf.cc
intern/asset_shelf_asset_view.cc
intern/asset_shelf_catalog_selector.cc
intern/asset_shelf_popup.cc
intern/asset_shelf_popover.cc
intern/asset_shelf_regiondata.cc
intern/asset_shelf_settings.cc
intern/asset_temp_id_consumer.cc

View File

@ -23,6 +23,7 @@ struct Main;
struct SpaceType;
struct uiBlock;
struct RegionPollParams;
struct wmRegionMessageSubscribeParams;
struct wmWindowManager;
namespace blender {
@ -52,6 +53,7 @@ void region_init(wmWindowManager *wm, ARegion *region);
int region_snap(const ARegion *region, int size, int axis);
void region_on_user_resize(const ARegion *region);
void region_listen(const wmRegionListenerParams *params);
void region_message_subscribe(const wmRegionMessageSubscribeParams *params);
void region_layout(const bContext *C, ARegion *region);
void region_draw(const bContext *C, ARegion *region);
void region_on_poll_success(const bContext *C, ARegion *region);
@ -63,7 +65,7 @@ void header_region_init(wmWindowManager *wm, ARegion *region);
void header_region(const bContext *C, ARegion *region);
void header_region_listen(const wmRegionListenerParams *params);
int header_region_size();
void header_regiontype_register(ARegionType *region_type, const int space_type);
void types_register(ARegionType *region_type, const int space_type);
/** \} */
@ -88,7 +90,6 @@ AssetShelfType *type_find_from_idname(const StringRef idname);
/** \name Asset Shelf Popup
* \{ */
uiBlock *popup_block_create(const bContext *C, ARegion *region, AssetShelfType *shelf_type);
void type_popup_unlink(const AssetShelfType &shelf_type);
/** \} */

View File

@ -16,7 +16,8 @@ namespace blender::ed::asset {
bool id_type_is_non_experimental(const ID *id);
#define ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_FLAGS \
(FILTER_ID_MA | FILTER_ID_GR | FILTER_ID_OB | FILTER_ID_AC | FILTER_ID_WO | FILTER_ID_NT)
(FILTER_ID_BR | FILTER_ID_MA | FILTER_ID_GR | FILTER_ID_OB | FILTER_ID_AC | FILTER_ID_WO | \
FILTER_ID_NT)
/**
* Check if the asset type for \a id (which doesn't need to be an asset right now) can be an asset,
@ -39,6 +40,6 @@ int64_t types_supported_as_filter_flags();
* Should start with a consonant, so usages can prefix it with "a" (not "an").
*/
#define ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING \
"Material, Collection, Object, Pose Action, Node Group or World"
"Material, Collection, Object, Brush, Pose Action, Node Group or World"
} // namespace blender::ed::asset

View File

@ -272,6 +272,7 @@ void AssetList::clear(const bContext *C)
filelist_readjob_stop(files, CTX_wm_manager(C));
filelist_freelib(files);
filelist_clear(files);
filelist_tag_force_reset(files);
WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST, nullptr);
}

View File

@ -13,6 +13,7 @@
#include "AS_asset_catalog_path.hh"
#include "AS_asset_library.hh"
#include "BLI_function_ref.hh"
#include "BLI_string.h"
#include "BKE_context.hh"
@ -26,6 +27,7 @@
#include "ED_asset_list.hh"
#include "ED_screen.hh"
#include "RNA_access.hh"
#include "RNA_prototypes.h"
#include "UI_interface.hh"
@ -34,6 +36,7 @@
#include "UI_view2d.hh"
#include "WM_api.hh"
#include "WM_message.hh"
#include "ED_asset_shelf.hh"
#include "asset_shelf.hh"
@ -329,6 +332,20 @@ void region_listen(const wmRegionListenerParams *params)
}
}
void region_message_subscribe(const wmRegionMessageSubscribeParams *params)
{
wmMsgBus *mbus = params->message_bus;
WorkSpace *workspace = params->workspace;
ARegion *region = params->region;
wmMsgSubscribeValue msg_sub_value_region_tag_redraw{};
msg_sub_value_region_tag_redraw.owner = region;
msg_sub_value_region_tag_redraw.user_data = region;
msg_sub_value_region_tag_redraw.notify = ED_region_do_msg_notify_tag_redraw;
WM_msg_subscribe_rna_prop(
mbus, &workspace->id, workspace, WorkSpace, tools, &msg_sub_value_region_tag_redraw);
}
void region_init(wmWindowManager *wm, ARegion *region)
{
/* Region-data should've been created by a previously called #region_before_redraw(). */
@ -815,7 +832,7 @@ static void asset_shelf_header_draw(const bContext *C, Header *header)
uiItemR(sub, &shelf_ptr, "search_filter", UI_ITEM_NONE, "", ICON_VIEWZOOM);
}
void header_regiontype_register(ARegionType *region_type, const int space_type)
static void header_regiontype_register(ARegionType *region_type, const int space_type)
{
HeaderType *ht = MEM_cnew<HeaderType>(__func__);
STRNCPY(ht->idname, "ASSETSHELF_HT_settings");
@ -827,8 +844,13 @@ void header_regiontype_register(ARegionType *region_type, const int space_type)
};
BLI_addtail(&region_type->headertypes, ht);
}
void types_register(ARegionType *region_type, const int space_type)
{
header_regiontype_register(region_type, space_type);
catalog_selector_panel_register(region_type);
popover_panel_register(region_type);
}
/** \} */

View File

@ -38,6 +38,7 @@ void build_asset_view(uiLayout &layout,
const ARegion &region);
void catalog_selector_panel_register(ARegionType *region_type);
void popover_panel_register(ARegionType *region_type);
AssetShelf *active_shelf_from_context(const bContext *C);

View File

@ -11,6 +11,7 @@
#include "AS_asset_library.hh"
#include "AS_asset_representation.hh"
#include "BKE_asset.hh"
#include "BKE_screen.hh"
#include "BLI_fnmatch.h"
@ -251,9 +252,13 @@ void AssetViewItem::on_activate(bContext & /*C*/)
std::optional<bool> AssetViewItem::should_be_active() const
{
const AssetView &asset_view = dynamic_cast<const AssetView &>(this->get_view());
if (!asset_view.active_asset_) {
const AssetShelfType &shelf_type = *asset_view.shelf_.type;
if (!shelf_type.get_active_asset) {
return {};
}
if (!asset_view.active_asset_) {
return false;
}
const asset_system::AssetRepresentation *asset = handle_get_representation(&asset_);
AssetWeakReference weak_ref = asset->make_weak_reference();
const bool matches = *asset_view.active_asset_ == weak_ref;

View File

@ -10,6 +10,8 @@
#include "BKE_screen.hh"
#include "BLI_string.h"
#include "BLT_translation.hh"
#include "UI_interface_c.hh"
@ -22,6 +24,8 @@
#include "RNA_access.hh"
#include "RNA_prototypes.h"
#include "WM_api.hh"
namespace blender::ed::asset::shelf {
class StaticPopupShelves {
@ -51,20 +55,20 @@ void type_popup_unlink(const AssetShelfType &shelf_type)
}
}
static AssetShelf *get_shelf_for_popup(const bContext *C, AssetShelfType &shelf_type)
static AssetShelf *get_shelf_for_popup(const bContext &C, AssetShelfType &shelf_type)
{
Vector<AssetShelf *> &popup_shelves = StaticPopupShelves::shelves();
for (AssetShelf *shelf : popup_shelves) {
if (STREQ(shelf->idname, shelf_type.idname)) {
if (type_poll_for_popup(*C, ensure_shelf_has_type(*shelf))) {
if (type_poll_for_popup(C, ensure_shelf_has_type(*shelf))) {
return shelf;
}
break;
}
}
if (type_poll_for_popup(*C, &shelf_type)) {
if (type_poll_for_popup(C, &shelf_type)) {
AssetShelf *new_shelf = create_shelf_from_type(shelf_type);
new_shelf->settings.display_flag |= ASSETSHELF_SHOW_NAMES;
popup_shelves.append(new_shelf);
@ -157,36 +161,43 @@ static void catalog_tree_draw(uiLayout &layout, AssetShelf &shelf)
ui::TreeViewBuilder::build_tree_view(*tree_view, layout);
}
uiBlock *popup_block_create(const bContext *C, ARegion *region, AssetShelfType *shelf_type)
static AssetShelfType *lookup_type_from_idname_in_context(const bContext *C)
{
bScreen *screen = CTX_wm_screen(C);
uiBlock *block = UI_block_begin(C, region, "_popup", UI_EMBOSS);
UI_block_flag_enable(block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_POPOVER);
UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
UI_block_bounds_set_normal(block, 0.3f * U.widget_unit);
UI_block_direction_set(block, UI_DIR_DOWN);
AssetShelf *shelf = get_shelf_for_popup(C, *shelf_type);
if (!shelf) {
BLI_assert_unreachable();
return block;
const std::optional<StringRefNull> idname = CTX_data_string_get(C, "asset_shelf_idname");
if (!idname) {
return nullptr;
}
return type_find_from_idname(*idname);
}
const uiStyle *style = UI_style_get_dpi();
static void popover_panel_draw(const bContext *C, Panel *panel)
{
AssetShelfType *shelf_type = lookup_type_from_idname_in_context(C);
BLI_assert_msg(shelf_type != nullptr, "couldn't find asset shelf type from context");
const int layout_width = UI_UNIT_X * 40;
const int left_col_width = 10 * UI_UNIT_X;
const int right_col_width = layout_width - left_col_width;
uiLayout *layout = UI_block_layout(
block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, layout_width, 0, 0, style);
const ARegion *region = CTX_wm_region_popup(C) ? CTX_wm_region_popup(C) : CTX_wm_region(C);
const int left_col_width_units = 10;
const int right_col_width_units = 30;
const int layout_width_units = left_col_width_units + right_col_width_units;
uiLayout *layout = panel->layout;
uiLayoutSetUnitsX(layout, layout_width_units);
AssetShelf *shelf = get_shelf_for_popup(*C, *shelf_type);
if (!shelf) {
BLI_assert_unreachable();
return;
}
bScreen *screen = CTX_wm_screen(C);
PointerRNA library_ref_ptr = RNA_pointer_create(
&screen->id, &RNA_AssetLibraryReference, &shelf->settings.asset_library_reference);
uiLayoutSetContextPointer(layout, "asset_library_reference", &library_ref_ptr);
uiLayout *row = uiLayoutRow(layout, false);
uiLayout *catalogs_col = uiLayoutColumn(row, false);
uiLayoutSetUnitsX(catalogs_col, left_col_width / UI_UNIT_X);
uiLayoutSetUnitsX(catalogs_col, left_col_width_units);
uiLayoutSetFixedSize(catalogs_col, true);
library_selector_draw(C, catalogs_col, *shelf);
catalog_tree_draw(*catalogs_col, *shelf);
@ -198,11 +209,40 @@ uiBlock *popup_block_create(const bContext *C, ARegion *region, AssetShelfType *
uiItemR(sub, &shelf_ptr, "search_filter", UI_ITEM_R_IMMEDIATE, "", ICON_VIEWZOOM);
uiLayout *asset_view_col = uiLayoutColumn(right_col, false);
uiLayoutSetUnitsX(asset_view_col, right_col_width / UI_UNIT_X);
uiLayoutSetUnitsX(asset_view_col, right_col_width_units);
uiLayoutSetFixedSize(asset_view_col, true);
build_asset_view(*asset_view_col, shelf->settings.asset_library_reference, *shelf, *C, *region);
return block;
build_asset_view(*asset_view_col, shelf->settings.asset_library_reference, *shelf, *C, *region);
}
static bool popover_panel_poll(const bContext *C, PanelType * /*panel_type*/)
{
const AssetShelfType *shelf_type = lookup_type_from_idname_in_context(C);
if (!shelf_type) {
return false;
}
return type_poll_for_popup(*C, shelf_type);
}
void popover_panel_register(ARegionType *region_type)
{
/* Uses global paneltype registry to allow usage as popover. So only register this once (may be
* called from multiple spaces). */
if (WM_paneltype_find("ASSETSHELF_PT_popover_panel", true)) {
return;
}
PanelType *pt = MEM_cnew<PanelType>(__func__);
STRNCPY(pt->idname, "ASSETSHELF_PT_popover_panel");
STRNCPY(pt->label, N_("Asset Shelf Panel"));
STRNCPY(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
pt->description = N_("Display an asset shelf in a popover panel");
pt->draw = popover_panel_draw;
pt->poll = popover_panel_poll;
pt->listener = asset::list::asset_reading_region_listen_fn;
BLI_addtail(&region_type->paneltypes, pt);
WM_paneltype_add(pt);
}
} // namespace blender::ed::asset::shelf

View File

@ -20,7 +20,7 @@ bool id_type_is_non_experimental(const ID *id)
{
/* Remember to update #ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING and
* #ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_FLAGS() with this! */
return ELEM(GS(id->name), ID_MA, ID_GR, ID_OB, ID_AC, ID_WO, ID_NT);
return ELEM(GS(id->name), ID_BR, ID_MA, ID_GR, ID_OB, ID_AC, ID_WO, ID_NT);
}
bool id_type_is_supported(const ID *id)

View File

@ -1952,283 +1952,6 @@ void GPENCIL_OT_material_lock_unused(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* ************************************************ */
/* Drawing Brushes Operators */
/* ******************* Brush resets ************************** */
static int gpencil_brush_reset_exec(bContext *C, wmOperator * /*op*/)
{
Main *bmain = CTX_data_main(C);
ToolSettings *ts = CTX_data_tool_settings(C);
const enum eContextObjectMode mode = CTX_data_mode_enum(C);
switch (mode) {
case CTX_MODE_PAINT_GPENCIL_LEGACY: {
Paint *paint = &ts->gp_paint->paint;
Brush *brush = BKE_paint_brush(paint);
if (brush && brush->gpencil_settings) {
BKE_gpencil_brush_preset_set(bmain, brush, brush->gpencil_settings->preset_type);
}
break;
}
case CTX_MODE_SCULPT_GPENCIL_LEGACY: {
Paint *paint = &ts->gp_sculptpaint->paint;
Brush *brush = BKE_paint_brush(paint);
if (brush && brush->gpencil_settings) {
BKE_gpencil_brush_preset_set(bmain, brush, brush->gpencil_settings->preset_type);
}
break;
}
case CTX_MODE_WEIGHT_GPENCIL_LEGACY: {
Paint *paint = &ts->gp_weightpaint->paint;
Brush *brush = BKE_paint_brush(paint);
if (brush && brush->gpencil_settings) {
BKE_gpencil_brush_preset_set(bmain, brush, brush->gpencil_settings->preset_type);
}
break;
}
case CTX_MODE_VERTEX_GPENCIL_LEGACY: {
Paint *paint = &ts->gp_vertexpaint->paint;
Brush *brush = BKE_paint_brush(paint);
if (brush && brush->gpencil_settings) {
BKE_gpencil_brush_preset_set(bmain, brush, brush->gpencil_settings->preset_type);
}
break;
}
default:
break;
}
/* notifiers */
WM_main_add_notifier(NC_BRUSH | NA_EDITED, nullptr);
return OPERATOR_FINISHED;
}
void GPENCIL_OT_brush_reset(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Reset Brush";
ot->idname = "GPENCIL_OT_brush_reset";
ot->description = "Reset brush to default parameters";
/* api callbacks */
ot->exec = gpencil_brush_reset_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static Brush *gpencil_brush_get_first_by_mode(Main *bmain,
Paint * /*paint*/,
const enum eContextObjectMode mode,
char tool)
{
Brush *brush_next = nullptr;
for (Brush *brush = static_cast<Brush *>(bmain->brushes.first); brush; brush = brush_next) {
brush_next = static_cast<Brush *>(brush->id.next);
if (brush->gpencil_settings == nullptr) {
continue;
}
if ((mode == CTX_MODE_PAINT_GPENCIL_LEGACY) && (brush->gpencil_tool == tool)) {
return brush;
}
if ((mode == CTX_MODE_SCULPT_GPENCIL_LEGACY) && (brush->gpencil_sculpt_tool == tool)) {
return brush;
}
if ((mode == CTX_MODE_WEIGHT_GPENCIL_LEGACY) && (brush->gpencil_weight_tool == tool)) {
return brush;
}
if ((mode == CTX_MODE_VERTEX_GPENCIL_LEGACY) && (brush->gpencil_vertex_tool == tool)) {
return brush;
}
}
return nullptr;
}
static void gpencil_brush_delete_mode_brushes(Main *bmain,
Paint *paint,
const enum eContextObjectMode mode)
{
Brush *brush_active = BKE_paint_brush(paint);
Brush *brush_next = nullptr;
for (Brush *brush = static_cast<Brush *>(bmain->brushes.first); brush; brush = brush_next) {
brush_next = static_cast<Brush *>(brush->id.next);
if ((brush->gpencil_settings == nullptr) && (brush->ob_mode != OB_MODE_PAINT_GPENCIL_LEGACY)) {
continue;
}
short preset = (brush->gpencil_settings) ? brush->gpencil_settings->preset_type :
short(GP_BRUSH_PRESET_UNKNOWN);
if (preset != GP_BRUSH_PRESET_UNKNOWN) {
/* Verify to delete only the brushes of the current mode. */
if (mode == CTX_MODE_PAINT_GPENCIL_LEGACY) {
if ((preset < GP_BRUSH_PRESET_AIRBRUSH) || (preset > GP_BRUSH_PRESET_TINT)) {
continue;
}
if ((brush_active) && (brush_active->gpencil_tool != brush->gpencil_tool)) {
continue;
}
}
if (mode == CTX_MODE_SCULPT_GPENCIL_LEGACY) {
if ((preset < GP_BRUSH_PRESET_SMOOTH_STROKE) || (preset > GP_BRUSH_PRESET_CLONE_STROKE)) {
continue;
}
if ((brush_active) && (brush_active->gpencil_sculpt_tool != brush->gpencil_sculpt_tool)) {
continue;
}
}
if (mode == CTX_MODE_WEIGHT_GPENCIL_LEGACY) {
if ((preset < GP_BRUSH_PRESET_WEIGHT_DRAW) || (preset > GP_BRUSH_PRESET_WEIGHT_SMEAR)) {
continue;
}
if ((brush_active) && (brush_active->gpencil_weight_tool != brush->gpencil_weight_tool)) {
continue;
}
}
if (mode == CTX_MODE_VERTEX_GPENCIL_LEGACY) {
if ((preset < GP_BRUSH_PRESET_VERTEX_DRAW) || (preset > GP_BRUSH_PRESET_VERTEX_REPLACE)) {
continue;
}
if ((brush_active) && (brush_active->gpencil_vertex_tool != brush->gpencil_vertex_tool)) {
continue;
}
}
}
/* Before delete, un-pin any material of the brush. */
if ((brush->gpencil_settings) && (brush->gpencil_settings->material != nullptr)) {
brush->gpencil_settings->material = nullptr;
brush->gpencil_settings->flag &= ~GP_BRUSH_MATERIAL_PINNED;
}
BKE_brush_delete(bmain, brush);
if (brush == brush_active) {
brush_active = nullptr;
}
}
}
static int gpencil_brush_reset_all_exec(bContext *C, wmOperator * /*op*/)
{
Main *bmain = CTX_data_main(C);
ToolSettings *ts = CTX_data_tool_settings(C);
const enum eContextObjectMode mode = CTX_data_mode_enum(C);
Paint *paint = nullptr;
switch (mode) {
case CTX_MODE_PAINT_GPENCIL_LEGACY: {
paint = &ts->gp_paint->paint;
break;
}
case CTX_MODE_SCULPT_GPENCIL_LEGACY: {
paint = &ts->gp_sculptpaint->paint;
break;
}
case CTX_MODE_WEIGHT_GPENCIL_LEGACY: {
paint = &ts->gp_weightpaint->paint;
break;
}
case CTX_MODE_VERTEX_GPENCIL_LEGACY: {
paint = &ts->gp_vertexpaint->paint;
break;
}
default:
break;
}
char tool = '0';
if (paint) {
Brush *brush_active = BKE_paint_brush(paint);
if (brush_active) {
switch (mode) {
case CTX_MODE_PAINT_GPENCIL_LEGACY: {
tool = brush_active->gpencil_tool;
break;
}
case CTX_MODE_SCULPT_GPENCIL_LEGACY: {
tool = brush_active->gpencil_sculpt_tool;
break;
}
case CTX_MODE_WEIGHT_GPENCIL_LEGACY: {
tool = brush_active->gpencil_weight_tool;
break;
}
case CTX_MODE_VERTEX_GPENCIL_LEGACY: {
tool = brush_active->gpencil_vertex_tool;
break;
}
default: {
tool = brush_active->gpencil_tool;
break;
}
}
}
gpencil_brush_delete_mode_brushes(bmain, paint, mode);
switch (mode) {
case CTX_MODE_PAINT_GPENCIL_LEGACY: {
BKE_brush_gpencil_paint_presets(bmain, ts, true);
break;
}
case CTX_MODE_SCULPT_GPENCIL_LEGACY: {
BKE_brush_gpencil_sculpt_presets(bmain, ts, true);
break;
}
case CTX_MODE_WEIGHT_GPENCIL_LEGACY: {
BKE_brush_gpencil_weight_presets(bmain, ts, true);
break;
}
case CTX_MODE_VERTEX_GPENCIL_LEGACY: {
BKE_brush_gpencil_vertex_presets(bmain, ts, true);
break;
}
default: {
break;
}
}
BKE_paint_brush_validate(bmain, paint);
/* Set Again the first brush of the mode. */
Brush *deft_brush = gpencil_brush_get_first_by_mode(bmain, paint, mode, tool);
if (deft_brush) {
BKE_paint_brush_set(paint, deft_brush);
}
/* notifiers */
DEG_relations_tag_update(bmain);
WM_main_add_notifier(NC_BRUSH | NA_EDITED, nullptr);
}
return OPERATOR_FINISHED;
}
void GPENCIL_OT_brush_reset_all(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Reset All Brushes";
ot->idname = "GPENCIL_OT_brush_reset_all";
ot->description = "Delete all mode brushes and recreate a default set";
/* api callbacks */
ot->exec = gpencil_brush_reset_all_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/*********************** Vertex Groups ***********************************/
static bool gpencil_vertex_group_poll(bContext *C)

View File

@ -423,18 +423,15 @@ static int gpencil_paintmode_toggle_exec(bContext *C, wmOperator *op)
BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_paint);
BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_vertexpaint);
BKE_brush_gpencil_paint_presets(bmain, ts, false);
/* Ensure Palette by default. */
BKE_gpencil_palette_ensure(bmain, CTX_data_scene(C));
Paint *paint = &ts->gp_paint->paint;
Brush *brush = BKE_paint_brush(paint);
/* if not exist, create a new one */
if ((brush == nullptr) || (brush->gpencil_settings == nullptr)) {
BKE_brush_gpencil_paint_presets(bmain, ts, true);
if (brush && !brush->gpencil_settings) {
BKE_brush_init_gpencil_settings(brush);
}
BKE_paint_brush_validate(bmain, &ts->gp_paint->paint);
BKE_paint_brushes_validate(bmain, &ts->gp_paint->paint);
}
if (ob->type == OB_GPENCIL_LEGACY) {
@ -573,11 +570,7 @@ static int gpencil_sculptmode_toggle_exec(bContext *C, wmOperator *op)
if (mode == OB_MODE_SCULPT_GPENCIL_LEGACY) {
/* Be sure we have brushes. */
BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_sculptpaint);
const bool reset_mode = (BKE_paint_brush(&ts->gp_sculptpaint->paint) == nullptr);
BKE_brush_gpencil_sculpt_presets(bmain, ts, reset_mode);
BKE_paint_brush_validate(bmain, &ts->gp_sculptpaint->paint);
BKE_paint_brushes_validate(bmain, &ts->gp_sculptpaint->paint);
}
/* setup other modes */
@ -717,10 +710,7 @@ static int gpencil_weightmode_toggle_exec(bContext *C, wmOperator *op)
ED_paint_cursor_start(weight_paint, grease_pencil_poll_weight_cursor);
}
const bool reset_mode = (BKE_paint_brush(weight_paint) == nullptr);
BKE_brush_gpencil_weight_presets(bmain, ts, reset_mode);
BKE_paint_brush_validate(bmain, weight_paint);
BKE_paint_brushes_validate(bmain, weight_paint);
}
/* setup other modes */
@ -832,10 +822,7 @@ static int gpencil_vertexmode_toggle_exec(bContext *C, wmOperator *op)
BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_paint);
BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_vertexpaint);
const bool reset_mode = (BKE_paint_brush(&ts->gp_vertexpaint->paint) == nullptr);
BKE_brush_gpencil_vertex_presets(bmain, ts, reset_mode);
BKE_paint_brush_validate(bmain, &ts->gp_vertexpaint->paint);
BKE_paint_brushes_validate(bmain, &ts->gp_vertexpaint->paint);
/* Ensure Palette by default. */
BKE_gpencil_palette_ensure(bmain, CTX_data_scene(C));

View File

@ -26,6 +26,7 @@
#include "DNA_object_types.h"
#include "DNA_windowmanager_types.h"
#include "BKE_brush.hh"
#include "BKE_context.hh"
#include "BKE_deform.hh"
#include "BKE_gpencil_geom_legacy.h"
@ -2405,6 +2406,9 @@ static tGPDfill *gpencil_session_init_fill(bContext *C, wmOperator *op)
/* save filling parameters */
Brush *brush = BKE_paint_brush(&ts->gp_paint->paint);
if (brush && !brush->gpencil_settings) {
BKE_brush_init_gpencil_settings(brush);
}
tgpf->brush = brush;
tgpf->flag = brush->gpencil_settings->flag;
tgpf->fill_threshold = brush->gpencil_settings->fill_threshold;

View File

@ -619,9 +619,6 @@ void GPENCIL_OT_extract_palette_vertex(wmOperatorType *ot);
void GPENCIL_OT_transform_fill(wmOperatorType *ot);
void GPENCIL_OT_reset_transform_fill(wmOperatorType *ot);
void GPENCIL_OT_brush_reset(wmOperatorType *ot);
void GPENCIL_OT_brush_reset_all(wmOperatorType *ot);
/* undo stack ---------- */
void gpencil_undo_init(bGPdata *gpd);

View File

@ -85,7 +85,6 @@ static void gpencil_insert_points_to_stroke(bGPDstroke *gps,
static bGPDstroke *gpencil_prepare_stroke(bContext *C, wmOperator *op, int totpoints)
{
Main *bmain = CTX_data_main(C);
ToolSettings *ts = CTX_data_tool_settings(C);
Object *ob = CTX_data_active_object(C);
bGPdata *gpd = static_cast<bGPdata *>(ob->data);
@ -99,12 +98,9 @@ static bGPDstroke *gpencil_prepare_stroke(bContext *C, wmOperator *op, int totpo
Paint *paint = &ts->gp_paint->paint;
Brush *brush = BKE_paint_brush(paint);
/* if not exist, create a new one */
if ((brush == nullptr) || (brush->gpencil_settings == nullptr)) {
/* create new brushes */
BKE_brush_gpencil_paint_presets(bmain, ts, false);
if (brush && !brush->gpencil_settings) {
BKE_brush_init_gpencil_settings(brush);
}
brush = BKE_paint_brush(paint);
/* frame */
short add_frame_mode;

View File

@ -715,9 +715,6 @@ void ED_operatortypes_gpencil_legacy()
WM_operatortype_append(GPENCIL_OT_transform_fill);
WM_operatortype_append(GPENCIL_OT_reset_transform_fill);
WM_operatortype_append(GPENCIL_OT_brush_reset);
WM_operatortype_append(GPENCIL_OT_brush_reset_all);
/* vertex groups */
WM_operatortype_append(GPENCIL_OT_vertex_group_assign);
WM_operatortype_append(GPENCIL_OT_vertex_group_remove_from);

View File

@ -1914,90 +1914,17 @@ static void gpencil_session_validatebuffer(tGPsdata *p)
}
}
/* helper to get default eraser and create one if no eraser brush */
static Brush *gpencil_get_default_eraser(Main *bmain, ToolSettings *ts)
{
Brush *brush_dft = nullptr;
Paint *paint = &ts->gp_paint->paint;
Brush *brush_prev = BKE_paint_brush(paint);
for (Brush *brush = static_cast<Brush *>(bmain->brushes.first); brush;
brush = static_cast<Brush *>(brush->id.next))
{
if (brush->gpencil_settings == nullptr) {
continue;
}
if ((brush->ob_mode == OB_MODE_PAINT_GPENCIL_LEGACY) &&
(brush->gpencil_tool == GPAINT_TOOL_ERASE))
{
/* save first eraser to use later if no default */
if (brush_dft == nullptr) {
brush_dft = brush;
}
/* found default */
if (brush->gpencil_settings->flag & GP_BRUSH_DEFAULT_ERASER) {
return brush;
}
}
}
/* if no default, but exist eraser brush, return this and set as default */
if (brush_dft) {
brush_dft->gpencil_settings->flag |= GP_BRUSH_DEFAULT_ERASER;
return brush_dft;
}
/* create a new soft eraser brush */
brush_dft = BKE_brush_add_gpencil(bmain, ts, "Soft Eraser", OB_MODE_PAINT_GPENCIL_LEGACY);
brush_dft->size = 30.0f;
brush_dft->gpencil_settings->flag |= GP_BRUSH_DEFAULT_ERASER;
brush_dft->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_SOFT;
brush_dft->gpencil_tool = GPAINT_TOOL_ERASE;
brush_dft->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_SOFT;
/* reset current brush */
BKE_paint_brush_set(paint, brush_prev);
return brush_dft;
}
/* helper to set default eraser and disable others */
static void gpencil_set_default_eraser(Main *bmain, Brush *brush_dft)
{
if (brush_dft == nullptr) {
return;
}
for (Brush *brush = static_cast<Brush *>(bmain->brushes.first); brush;
brush = static_cast<Brush *>(brush->id.next))
{
if ((brush->gpencil_settings) && (brush->gpencil_tool == GPAINT_TOOL_ERASE)) {
if (brush == brush_dft) {
brush->gpencil_settings->flag |= GP_BRUSH_DEFAULT_ERASER;
}
else if (brush->gpencil_settings->flag & GP_BRUSH_DEFAULT_ERASER) {
brush->gpencil_settings->flag &= ~GP_BRUSH_DEFAULT_ERASER;
}
}
}
}
/* initialize a drawing brush */
static void gpencil_init_drawing_brush(bContext *C, tGPsdata *p)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ToolSettings *ts = CTX_data_tool_settings(C);
Paint *paint = &ts->gp_paint->paint;
bool changed = false;
Brush *brush = BKE_paint_brush(paint);
/* if not exist, create a new one */
if ((brush == nullptr) || (brush->gpencil_settings == nullptr)) {
/* create new brushes */
BKE_brush_gpencil_paint_presets(bmain, ts, true);
changed = true;
brush = BKE_paint_brush(paint);
if (brush == nullptr) {
return;
}
/* Be sure curves are initialized. */
BKE_curvemapping_init(brush->gpencil_settings->curve_sensitivity);
BKE_curvemapping_init(brush->gpencil_settings->curve_strength);
@ -2010,23 +1937,23 @@ static void gpencil_init_drawing_brush(bContext *C, tGPsdata *p)
BKE_curvemapping_init(brush->gpencil_settings->curve_rand_value);
/* Assign to temp #tGPsdata */
p->brush = BKE_paint_brush(paint);
if (p->brush->gpencil_tool != GPAINT_TOOL_ERASE) {
p->eraser = gpencil_get_default_eraser(p->bmain, ts);
p->brush = brush;
Brush *eraser_brush;
if (p->brush->gpencil_tool != GPAINT_TOOL_ERASE &&
(eraser_brush = BKE_paint_eraser_brush(paint)))
{
if (eraser_brush && !eraser_brush->gpencil_settings) {
BKE_brush_init_gpencil_settings(eraser_brush);
}
p->eraser = eraser_brush;
}
else {
p->eraser = p->brush;
}
/* set new eraser as default */
gpencil_set_default_eraser(p->bmain, p->eraser);
/* use radius of eraser */
p->radius = short(p->eraser->size);
/* Need this update to synchronize brush with draw manager. */
if (changed) {
DEG_id_tag_update(&scene->id, ID_RECALC_SYNC_TO_EVAL);
}
}
/* initialize a paint brush and a default color if not exist */
@ -2125,6 +2052,10 @@ static bool gpencil_session_initdata(bContext *C, wmOperator *op, tGPsdata *p)
/* set brush and create a new one if null */
gpencil_init_drawing_brush(C, p);
if (p->brush == nullptr) {
p->status = GP_STATUS_ERROR;
return false;
}
/* setup active color */
/* region where paint was originated */

View File

@ -1212,15 +1212,11 @@ static void gpencil_primitive_init(bContext *C, wmOperator *op)
/* if brush doesn't exist, create a new set (fix damaged files from old versions) */
Brush *brush = BKE_paint_brush(paint);
if ((brush == nullptr) || (brush->gpencil_settings == nullptr)) {
BKE_brush_gpencil_paint_presets(bmain, ts, true);
if (brush && !brush->gpencil_settings) {
BKE_brush_init_gpencil_settings(brush);
}
/* Set Draw brush. */
brush = BKE_paint_toolslots_brush_get(paint, 0);
BKE_brush_tool_set(brush, paint, 0);
BKE_paint_brush_set(paint, brush);
/* Set brush. */
tgpi->brush = brush;
/* control points */

View File

@ -1204,6 +1204,9 @@ static bool gpencil_sculpt_brush_init(bContext *C, wmOperator *op)
Paint *paint = &ts->gp_sculptpaint->paint;
Brush *brush = BKE_paint_brush(paint);
if (brush && !brush->gpencil_settings) {
BKE_brush_init_gpencil_settings(brush);
}
gso->brush = brush;
BKE_curvemapping_init(gso->brush->curve);
@ -2177,9 +2180,10 @@ static void gpencil_sculpt_brush_apply(bContext *C, wmOperator *op, PointerRNA *
static Brush *gpencil_sculpt_get_smooth_brush(tGP_BrushEditData *gso)
{
Main *bmain = gso->bmain;
Brush *brush = static_cast<Brush *>(
BLI_findstring(&bmain->brushes, "Smooth Stroke", offsetof(ID, name) + 2));
Brush *brush = BKE_paint_brush_from_essentials(bmain, "Smooth Stroke");
if (brush && !brush->gpencil_settings) {
BKE_brush_init_gpencil_settings(brush);
}
return brush;
}

View File

@ -1430,13 +1430,6 @@ void ED_gpencil_add_defaults(bContext *C, Object *ob)
ToolSettings *ts = CTX_data_tool_settings(C);
BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_paint);
Paint *paint = &ts->gp_paint->paint;
Brush *brush = BKE_paint_brush(paint);
/* if not exist, create a new one */
if ((brush == nullptr) || (brush->gpencil_settings == nullptr)) {
/* create new brushes */
BKE_brush_gpencil_paint_presets(bmain, ts, true);
}
/* ensure a color exists and is assigned to object */
BKE_gpencil_object_material_ensure_from_active_input_toolsettings(bmain, ob, ts);

View File

@ -674,6 +674,9 @@ static int grease_pencil_primitive_invoke(bContext *C, wmOperator *op, const wmE
Paint *paint = &vc.scene->toolsettings->gp_paint->paint;
ptd.brush = BKE_paint_brush(paint);
if (ptd.brush->gpencil_settings == nullptr) {
BKE_brush_init_gpencil_settings(ptd.brush);
}
ptd.settings = ptd.brush->gpencil_settings;
BKE_curvemapping_init(ptd.settings->curve_sensitivity);

View File

@ -73,6 +73,9 @@ void attribute_search_add_items(StringRefNull str,
uiSearchItems *items,
bool is_first);
bool asset_shelf_popover_invoke(bContext &C,
blender::StringRef asset_shelf_idname,
ReportList &reports);
/**
* Some drop targets simply allow dropping onto/into them, others support dragging in-between them.
* Classes implementing the drop-target interface can use this type to control the behavior by

View File

@ -1385,6 +1385,8 @@ void UI_but_context_ptr_set(uiBlock *block, uiBut *but, const char *name, const
const PointerRNA *UI_but_context_ptr_get(const uiBut *but,
const char *name,
const StructRNA *type = nullptr);
std::optional<blender::StringRefNull> UI_but_context_string_get(const uiBut *but,
const char *name);
const bContextStore *UI_but_context_get(const uiBut *but);
void UI_but_unit_type_set(uiBut *but, int unit_type);
@ -2174,6 +2176,7 @@ uiBlock *uiLayoutGetBlock(uiLayout *layout);
void uiLayoutSetFunc(uiLayout *layout, uiMenuHandleFunc handlefunc, void *argv);
void uiLayoutSetContextPointer(uiLayout *layout, const char *name, PointerRNA *ptr);
void uiLayoutSetContextString(uiLayout *layout, const char *name, blender::StringRef value);
bContextStore *uiLayoutGetContextStore(uiLayout *layout);
void uiLayoutContextCopy(uiLayout *layout, const bContextStore *context);
@ -2206,6 +2209,10 @@ MenuType *UI_but_menutype_get(const uiBut *but);
* This is a bit of a hack but best keep it in one place at least.
*/
PanelType *UI_but_paneltype_get(const uiBut *but);
/**
* This is a bit of a hack but best keep it in one place at least.
*/
std::optional<blender::StringRefNull> UI_but_asset_shelf_type_idname_get(const uiBut *but);
void UI_menutype_draw(bContext *C, MenuType *mt, uiLayout *layout);
/**
* Used for popup panels only.
@ -2725,8 +2732,11 @@ void uiTemplateAssetView(uiLayout *layout,
namespace blender::ui {
void template_asset_shelf_popover(
uiLayout &layout, const bContext &C, StringRefNull asset_shelf_id, StringRef name, int icon);
void template_asset_shelf_popover(uiLayout &layout,
const bContext &C,
StringRefNull asset_shelf_id,
StringRefNull name,
int icon);
}

View File

@ -5769,6 +5769,14 @@ const PointerRNA *UI_but_context_ptr_get(const uiBut *but, const char *name, con
return CTX_store_ptr_lookup(but->context, name, type);
}
std::optional<blender::StringRefNull> UI_but_context_string_get(const uiBut *but, const char *name)
{
if (!but->context) {
return {};
}
return CTX_store_string_lookup(but->context, name);
}
const bContextStore *UI_but_context_get(const uiBut *but)
{
return but->context;

View File

@ -114,6 +114,13 @@ static const char *shortcut_get_operator_property(bContext *C, uiBut *but, IDPro
return "WM_OT_call_menu";
}
if (std::optional asset_shelf_idname = UI_but_asset_shelf_type_idname_get(but)) {
IDProperty *prop = blender::bke::idprop::create_group(__func__).release();
IDP_AddToGroup(prop, bke::idprop::create("name", *asset_shelf_idname).release());
*r_prop = prop;
return "WM_OT_call_asset_shelf_popover";
}
if (PanelType *pt = UI_but_paneltype_get(but)) {
IDProperty *prop = blender::bke::idprop::create_group(__func__).release();
IDP_AddToGroup(prop, bke::idprop::create("name", pt->idname).release());

View File

@ -543,92 +543,6 @@ static void vicon_gplayer_color_draw(Icon *icon, int x, int y, int w, int h)
immUnbindProgram();
}
static void init_brush_icons()
{
# define INIT_BRUSH_ICON(icon_id, name) \
{ \
const uchar *rect = (const uchar *)datatoc_##name##_png; \
const int size = datatoc_##name##_png_size; \
DrawInfo *di = def_internal_icon(nullptr, icon_id, 0, 0, w, ICON_TYPE_BUFFER, 0); \
di->data.buffer.image->datatoc_rect = rect; \
di->data.buffer.image->datatoc_size = size; \
} \
((void)0)
/* end INIT_BRUSH_ICON */
const int w = 96; /* warning, brush size hardcoded in C, but it gets scaled */
INIT_BRUSH_ICON(ICON_BRUSH_BLOB, blob);
INIT_BRUSH_ICON(ICON_BRUSH_BLUR, blur);
INIT_BRUSH_ICON(ICON_BRUSH_CLAY, clay);
INIT_BRUSH_ICON(ICON_BRUSH_CLAY_STRIPS, claystrips);
INIT_BRUSH_ICON(ICON_BRUSH_CLONE, clone);
INIT_BRUSH_ICON(ICON_BRUSH_CREASE, crease);
INIT_BRUSH_ICON(ICON_BRUSH_SCULPT_DRAW, draw);
INIT_BRUSH_ICON(ICON_BRUSH_FILL, fill);
INIT_BRUSH_ICON(ICON_BRUSH_FLATTEN, flatten);
INIT_BRUSH_ICON(ICON_BRUSH_GRAB, grab);
INIT_BRUSH_ICON(ICON_BRUSH_INFLATE, inflate);
INIT_BRUSH_ICON(ICON_BRUSH_LAYER, layer);
INIT_BRUSH_ICON(ICON_BRUSH_MASK, mask);
INIT_BRUSH_ICON(ICON_BRUSH_MIX, mix);
INIT_BRUSH_ICON(ICON_BRUSH_NUDGE, nudge);
INIT_BRUSH_ICON(ICON_BRUSH_PAINT_SELECT, paint_select);
INIT_BRUSH_ICON(ICON_BRUSH_PINCH, pinch);
INIT_BRUSH_ICON(ICON_BRUSH_SCRAPE, scrape);
INIT_BRUSH_ICON(ICON_BRUSH_SMEAR, smear);
INIT_BRUSH_ICON(ICON_BRUSH_SMOOTH, smooth);
INIT_BRUSH_ICON(ICON_BRUSH_SNAKE_HOOK, snake_hook);
INIT_BRUSH_ICON(ICON_BRUSH_SOFTEN, soften);
INIT_BRUSH_ICON(ICON_BRUSH_TEXDRAW, texdraw);
INIT_BRUSH_ICON(ICON_BRUSH_TEXFILL, texfill);
INIT_BRUSH_ICON(ICON_BRUSH_TEXMASK, texmask);
INIT_BRUSH_ICON(ICON_BRUSH_THUMB, thumb);
INIT_BRUSH_ICON(ICON_BRUSH_ROTATE, twist);
/* grease pencil sculpt */
INIT_BRUSH_ICON(ICON_GPBRUSH_SMOOTH, gp_brush_smooth);
INIT_BRUSH_ICON(ICON_GPBRUSH_THICKNESS, gp_brush_thickness);
INIT_BRUSH_ICON(ICON_GPBRUSH_STRENGTH, gp_brush_strength);
INIT_BRUSH_ICON(ICON_GPBRUSH_GRAB, gp_brush_grab);
INIT_BRUSH_ICON(ICON_GPBRUSH_PUSH, gp_brush_push);
INIT_BRUSH_ICON(ICON_GPBRUSH_TWIST, gp_brush_twist);
INIT_BRUSH_ICON(ICON_GPBRUSH_PINCH, gp_brush_pinch);
INIT_BRUSH_ICON(ICON_GPBRUSH_RANDOMIZE, gp_brush_randomize);
INIT_BRUSH_ICON(ICON_GPBRUSH_CLONE, gp_brush_clone);
INIT_BRUSH_ICON(ICON_GPBRUSH_WEIGHT, gp_brush_weight);
/* grease pencil drawing brushes */
INIT_BRUSH_ICON(ICON_GPBRUSH_PENCIL, gp_brush_pencil);
INIT_BRUSH_ICON(ICON_GPBRUSH_PEN, gp_brush_pen);
INIT_BRUSH_ICON(ICON_GPBRUSH_INK, gp_brush_ink);
INIT_BRUSH_ICON(ICON_GPBRUSH_INKNOISE, gp_brush_inknoise);
INIT_BRUSH_ICON(ICON_GPBRUSH_BLOCK, gp_brush_block);
INIT_BRUSH_ICON(ICON_GPBRUSH_MARKER, gp_brush_marker);
INIT_BRUSH_ICON(ICON_GPBRUSH_FILL, gp_brush_fill);
INIT_BRUSH_ICON(ICON_GPBRUSH_AIRBRUSH, gp_brush_airbrush);
INIT_BRUSH_ICON(ICON_GPBRUSH_CHISEL, gp_brush_chisel);
INIT_BRUSH_ICON(ICON_GPBRUSH_ERASE_SOFT, gp_brush_erase_soft);
INIT_BRUSH_ICON(ICON_GPBRUSH_ERASE_HARD, gp_brush_erase_hard);
INIT_BRUSH_ICON(ICON_GPBRUSH_ERASE_STROKE, gp_brush_erase_stroke);
/* Curves sculpt. */
INIT_BRUSH_ICON(ICON_BRUSH_CURVES_ADD, curves_sculpt_add);
INIT_BRUSH_ICON(ICON_BRUSH_CURVES_COMB, curves_sculpt_comb);
INIT_BRUSH_ICON(ICON_BRUSH_CURVES_CUT, curves_sculpt_cut);
INIT_BRUSH_ICON(ICON_BRUSH_CURVES_DELETE, curves_sculpt_delete);
INIT_BRUSH_ICON(ICON_BRUSH_CURVES_DENSITY, curves_sculpt_density);
INIT_BRUSH_ICON(ICON_BRUSH_CURVES_GROW_SHRINK, curves_sculpt_grow_shrink);
INIT_BRUSH_ICON(ICON_BRUSH_CURVES_PINCH, curves_sculpt_pinch);
INIT_BRUSH_ICON(ICON_BRUSH_CURVES_PUFF, curves_sculpt_puff);
INIT_BRUSH_ICON(ICON_BRUSH_CURVES_SLIDE, curves_sculpt_slide);
INIT_BRUSH_ICON(ICON_BRUSH_CURVES_SMOOTH, curves_sculpt_smooth);
INIT_BRUSH_ICON(ICON_BRUSH_CURVES_SNAKE_HOOK, curves_sculpt_snake_hook);
# undef INIT_BRUSH_ICON
}
static DrawInfo *g_di_event_list = nullptr;
int UI_icon_from_event_type(short event_type, short event_value)
@ -1401,7 +1315,6 @@ void UI_icons_init()
init_iconfile_list(&iconfilelist);
UI_icons_reload_internal_textures();
init_internal_icons();
init_brush_icons();
init_event_icons();
#endif
}
@ -2221,176 +2134,6 @@ static void ui_id_icon_render(const bContext *C, ID *id, bool use_jobs)
}
}
static int ui_id_brush_get_icon(const bContext *C, ID *id)
{
Brush *br = (Brush *)id;
if (br->flag & BRUSH_CUSTOM_ICON) {
BKE_icon_id_ensure(id);
ui_id_icon_render(C, id, true);
}
else {
Object *ob = CTX_data_active_object(C);
const EnumPropertyItem *items = nullptr;
PaintMode paint_mode = PaintMode::Invalid;
ScrArea *area = CTX_wm_area(C);
char space_type = area->spacetype;
/* Fallback to 3D view. */
if (space_type == SPACE_PROPERTIES) {
space_type = SPACE_VIEW3D;
}
/* XXX: this is not nice, should probably make brushes
* be strictly in one paint mode only to avoid
* checking various context stuff here */
if ((space_type == SPACE_VIEW3D) && ob) {
if (ob->mode & OB_MODE_SCULPT) {
paint_mode = PaintMode::Sculpt;
}
else if (ob->mode & OB_MODE_VERTEX_PAINT) {
paint_mode = PaintMode::Vertex;
}
else if (ob->mode & OB_MODE_WEIGHT_PAINT) {
paint_mode = PaintMode::Weight;
}
else if (ob->mode & OB_MODE_TEXTURE_PAINT) {
paint_mode = PaintMode::Texture3D;
}
else if (ob->mode & OB_MODE_SCULPT_CURVES) {
paint_mode = PaintMode::SculptCurves;
}
}
else if (space_type == SPACE_IMAGE) {
if (area->spacetype == space_type) {
const SpaceImage *sima = static_cast<const SpaceImage *>(area->spacedata.first);
if (sima->mode == SI_MODE_PAINT) {
paint_mode = PaintMode::Texture2D;
}
}
}
/* reset the icon */
if ((ob != nullptr) && (ob->mode & OB_MODE_ALL_PAINT_GPENCIL) &&
(br->gpencil_settings != nullptr))
{
switch (br->gpencil_settings->icon_id) {
case GP_BRUSH_ICON_PENCIL:
br->id.icon_id = ICON_GPBRUSH_PENCIL;
break;
case GP_BRUSH_ICON_PEN:
br->id.icon_id = ICON_GPBRUSH_PEN;
break;
case GP_BRUSH_ICON_INK:
br->id.icon_id = ICON_GPBRUSH_INK;
break;
case GP_BRUSH_ICON_INKNOISE:
br->id.icon_id = ICON_GPBRUSH_INKNOISE;
break;
case GP_BRUSH_ICON_BLOCK:
br->id.icon_id = ICON_GPBRUSH_BLOCK;
break;
case GP_BRUSH_ICON_MARKER:
br->id.icon_id = ICON_GPBRUSH_MARKER;
break;
case GP_BRUSH_ICON_FILL:
br->id.icon_id = ICON_GPBRUSH_FILL;
break;
case GP_BRUSH_ICON_AIRBRUSH:
br->id.icon_id = ICON_GPBRUSH_AIRBRUSH;
break;
case GP_BRUSH_ICON_CHISEL:
br->id.icon_id = ICON_GPBRUSH_CHISEL;
break;
case GP_BRUSH_ICON_ERASE_SOFT:
br->id.icon_id = ICON_GPBRUSH_ERASE_SOFT;
break;
case GP_BRUSH_ICON_ERASE_HARD:
br->id.icon_id = ICON_GPBRUSH_ERASE_HARD;
break;
case GP_BRUSH_ICON_ERASE_STROKE:
br->id.icon_id = ICON_GPBRUSH_ERASE_STROKE;
break;
case GP_BRUSH_ICON_TINT:
br->id.icon_id = ICON_BRUSH_TEXDRAW;
break;
case GP_BRUSH_ICON_VERTEX_DRAW:
br->id.icon_id = ICON_BRUSH_MIX;
break;
case GP_BRUSH_ICON_VERTEX_BLUR:
br->id.icon_id = ICON_BRUSH_BLUR;
break;
case GP_BRUSH_ICON_VERTEX_AVERAGE:
br->id.icon_id = ICON_BRUSH_BLUR;
break;
case GP_BRUSH_ICON_VERTEX_SMEAR:
br->id.icon_id = ICON_BRUSH_BLUR;
break;
case GP_BRUSH_ICON_VERTEX_REPLACE:
br->id.icon_id = ICON_BRUSH_MIX;
break;
case GP_BRUSH_ICON_GPBRUSH_SMOOTH:
br->id.icon_id = ICON_GPBRUSH_SMOOTH;
break;
case GP_BRUSH_ICON_GPBRUSH_THICKNESS:
br->id.icon_id = ICON_GPBRUSH_THICKNESS;
break;
case GP_BRUSH_ICON_GPBRUSH_STRENGTH:
br->id.icon_id = ICON_GPBRUSH_STRENGTH;
break;
case GP_BRUSH_ICON_GPBRUSH_RANDOMIZE:
br->id.icon_id = ICON_GPBRUSH_RANDOMIZE;
break;
case GP_BRUSH_ICON_GPBRUSH_GRAB:
br->id.icon_id = ICON_GPBRUSH_GRAB;
break;
case GP_BRUSH_ICON_GPBRUSH_PUSH:
br->id.icon_id = ICON_GPBRUSH_PUSH;
break;
case GP_BRUSH_ICON_GPBRUSH_TWIST:
br->id.icon_id = ICON_GPBRUSH_TWIST;
break;
case GP_BRUSH_ICON_GPBRUSH_PINCH:
br->id.icon_id = ICON_GPBRUSH_PINCH;
break;
case GP_BRUSH_ICON_GPBRUSH_CLONE:
br->id.icon_id = ICON_GPBRUSH_CLONE;
break;
case GP_BRUSH_ICON_GPBRUSH_WEIGHT:
br->id.icon_id = ICON_GPBRUSH_WEIGHT;
break;
case GP_BRUSH_ICON_GPBRUSH_BLUR:
br->id.icon_id = ICON_BRUSH_BLUR;
break;
case GP_BRUSH_ICON_GPBRUSH_AVERAGE:
br->id.icon_id = ICON_BRUSH_BLUR;
break;
case GP_BRUSH_ICON_GPBRUSH_SMEAR:
br->id.icon_id = ICON_BRUSH_BLUR;
break;
default:
br->id.icon_id = ICON_GPBRUSH_PEN;
break;
}
return id->icon_id;
}
if (paint_mode != PaintMode::Invalid) {
items = BKE_paint_get_tool_enum_from_paintmode(paint_mode);
const uint tool_offset = BKE_paint_get_brush_tool_offset_from_paintmode(paint_mode);
const int tool_type = *(char *)POINTER_OFFSET(br, tool_offset);
if (!items || !RNA_enum_icon_from_value(items, tool_type, &id->icon_id)) {
id->icon_id = 0;
}
}
else {
id->icon_id = 0;
}
}
return id->icon_id;
}
static int ui_id_screen_get_icon(const bContext *C, ID *id)
{
BKE_icon_id_ensure(id);
@ -2406,9 +2149,6 @@ int ui_id_icon_get(const bContext *C, ID *id, const bool big)
/* icon */
switch (GS(id->name)) {
case ID_BR:
iconid = ui_id_brush_get_icon(C, id);
break;
case ID_MA: /* fall through */
case ID_TE: /* fall through */
case ID_IM: /* fall through */

View File

@ -997,10 +997,12 @@ uiPopupBlockHandle *ui_popup_menu_create(
/* `interface_region_popover.cc` */
using uiPopoverCreateFunc = std::function<void(bContext *, uiLayout *, PanelType *)>;
uiPopupBlockHandle *ui_popover_panel_create(bContext *C,
ARegion *butregion,
uiBut *but,
uiMenuCreateFunc menu_func,
uiPopoverCreateFunc popover_func,
const PanelType *panel_type);
/* `interface_region_menu_pie.cc` */
@ -1523,6 +1525,9 @@ void UI_OT_eyedropper_driver(wmOperatorType *ot);
void UI_OT_eyedropper_gpencil_color(wmOperatorType *ot);
/* interface_template_asset_shelf_popover.cc */
std::optional<blender::StringRefNull> UI_asset_shelf_idname_from_button_context(const uiBut *but);
/* interface_template_asset_view.cc */
uiListType *UI_UL_asset_view();

View File

@ -25,6 +25,7 @@
#include "BLI_memory_utils.hh"
#include "BLI_rect.h"
#include "BLI_string.h"
#include "BLI_string_ref.hh"
#include "BLI_utildefines.h"
#include "BLT_translation.hh"
@ -3187,6 +3188,10 @@ void uiItemPopoverPanel_ptr(
icon = ICON_BLANK1;
}
const bContextStore *previous_ctx = CTX_store_get(C);
/* Set context for polling (and panel header drawing). */
CTX_store_set(const_cast<bContext *>(C), layout->context);
const bool ok = (pt->poll == nullptr) || pt->poll(C, pt);
if (ok && (pt->draw_header != nullptr)) {
layout = uiLayoutRow(layout, true);
@ -3198,6 +3203,9 @@ void uiItemPopoverPanel_ptr(
panel.flag = PNL_POPOVER;
pt->draw_header(C, &panel);
}
CTX_store_set(const_cast<bContext *>(C), previous_ctx);
uiBut *but = ui_item_menu(
layout, name, icon, ui_item_paneltype_func, pt, nullptr, TIP_(pt->description), true);
but->type = UI_BTYPE_POPOVER;
@ -5999,6 +6007,12 @@ void uiLayoutSetContextPointer(uiLayout *layout, const char *name, PointerRNA *p
layout->context = CTX_store_add(block->contexts, name, ptr);
}
void uiLayoutSetContextString(uiLayout *layout, const char *name, blender::StringRef value)
{
uiBlock *block = layout->root->block;
layout->context = CTX_store_add(block->contexts, name, value);
}
bContextStore *uiLayoutGetContextStore(uiLayout *layout)
{
return layout->context;
@ -6092,6 +6106,11 @@ PanelType *UI_but_paneltype_get(const uiBut *but)
return nullptr;
}
std::optional<blender::StringRefNull> UI_but_asset_shelf_type_idname_get(const uiBut *but)
{
return UI_asset_shelf_idname_from_button_context(but);
}
void UI_menutype_draw(bContext *C, MenuType *mt, uiLayout *layout)
{
Menu menu{};

View File

@ -61,8 +61,8 @@ struct uiPopover {
wmKeyMap *keymap;
wmEventHandler_Keymap *keymap_handler;
uiMenuCreateFunc menu_func;
const PanelType *menu_arg;
uiPopoverCreateFunc popover_func;
const PanelType *panel_type;
/* Size in pixels (ui scale applied). */
int ui_size_x;
@ -114,9 +114,9 @@ static uiBlock *ui_block_func_POPOVER(bContext *C, uiPopupBlockHandle *handle, v
if (!pup->layout) {
ui_popover_create_block(C, handle->region, pup, WM_OP_INVOKE_REGION_WIN);
if (pup->menu_func) {
if (pup->popover_func) {
pup->block->handle = handle;
pup->menu_func(C, pup->layout, const_cast<PanelType *>(pup->menu_arg));
pup->popover_func(C, pup->layout, const_cast<PanelType *>(pup->panel_type));
pup->block->handle = nullptr;
}
@ -233,20 +233,20 @@ static void ui_block_free_func_POPOVER(void *arg_pup)
wmWindow *window = pup->window;
WM_event_remove_keymap_handler(&window->modalhandlers, pup->keymap);
}
MEM_freeN(pup);
MEM_delete(pup);
}
uiPopupBlockHandle *ui_popover_panel_create(bContext *C,
ARegion *butregion,
uiBut *but,
uiMenuCreateFunc menu_func,
uiPopoverCreateFunc popover_func,
const PanelType *panel_type)
{
wmWindow *window = CTX_wm_window(C);
const uiStyle *style = UI_style_get_dpi();
/* Create popover, buttons are created from callback. */
uiPopover *pup = MEM_cnew<uiPopover>(__func__);
uiPopover *pup = MEM_new<uiPopover>(__func__);
pup->but = but;
/* FIXME: maybe one day we want non panel popovers? */
@ -259,8 +259,8 @@ uiPopupBlockHandle *ui_popover_panel_create(bContext *C,
(text_points_max / float(UI_DEFAULT_TEXT_POINTS));
}
pup->menu_func = menu_func;
pup->menu_arg = panel_type;
pup->popover_func = popover_func;
pup->panel_type = panel_type;
#ifdef USE_UI_POPOVER_ONCE
{
@ -335,7 +335,7 @@ int UI_popover_panel_invoke(bContext *C, const char *idname, bool keep_open, Rep
uiPopover *UI_popover_begin(bContext *C, int ui_menu_width, bool from_active_button)
{
uiPopover *pup = MEM_cnew<uiPopover>(__func__);
uiPopover *pup = MEM_new<uiPopover>(__func__);
if (ui_menu_width == 0) {
ui_menu_width = U.widget_unit * UI_POPOVER_WIDTH_UNITS;
}

View File

@ -571,7 +571,6 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
/* There are different kinds of shortcuts:
*
* - Direct access to the tool (as if the toolbar button is pressed).
* - The key is bound to a brush type (not the exact brush name).
* - The key is assigned to the operator itself
* (bypassing the tool, executing the operator).
*
@ -579,36 +578,6 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
*/
std::string shortcut = UI_but_string_get_operator_keymap(*C, *but);
if (shortcut.empty()) {
const PaintMode paint_mode = BKE_paintmode_get_active_from_context(C);
const char *tool_attr = BKE_paint_get_tool_prop_id_from_paintmode(paint_mode);
if (tool_attr != nullptr) {
const EnumPropertyItem *items = BKE_paint_get_tool_enum_from_paintmode(paint_mode);
const char *tool_id_lstrip = strrchr(tool_id, '.');
const int tool_id_offset = tool_id_lstrip ? ((tool_id_lstrip - tool_id) + 1) : 0;
const int i = RNA_enum_from_name(items, tool_id + tool_id_offset);
if (i != -1) {
wmOperatorType *ot = WM_operatortype_find("paint.brush_select", true);
PointerRNA op_props;
WM_operator_properties_create_ptr(&op_props, ot);
RNA_enum_set(&op_props, tool_attr, items[i].value);
/* Check for direct access to the tool. */
if (std::optional<std::string> shortcut_brush = WM_key_event_operator_string(
C,
ot->idname,
WM_OP_INVOKE_REGION_WIN,
static_cast<IDProperty *>(op_props.data),
true))
{
shortcut = *shortcut_brush;
}
WM_operator_properties_free(&op_props);
}
}
}
if (shortcut.empty()) {
/* Check for direct access to the tool. */
if (std::optional<std::string> shortcut_toolbar = WM_key_event_operator_string(

View File

@ -7,28 +7,28 @@
*/
#include "BKE_context.hh"
#include "BKE_report.hh"
#include "BKE_screen.hh"
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "RNA_prototypes.h"
#include "UI_interface.hh"
#include "UI_interface_c.hh"
#include "UI_resources.hh"
#include "interface_intern.hh"
#include "ED_asset_shelf.hh"
namespace blender::ui {
#include "WM_api.hh"
static uiBlock *asset_shelf_block_fn(bContext *C, ARegion *region, void *arg_shelf_type)
{
AssetShelfType *shelf_type = reinterpret_cast<AssetShelfType *>(arg_shelf_type);
return ed::asset::shelf::popup_block_create(C, region, shelf_type);
}
namespace blender::ui {
void template_asset_shelf_popover(uiLayout &layout,
const bContext &C,
const StringRefNull asset_shelf_id,
const StringRef name,
const StringRefNull name,
const BIFIconID icon)
{
AssetShelfType *shelf_type = ed::asset::shelf::type_find_from_idname(asset_shelf_id);
@ -38,30 +38,64 @@ void template_asset_shelf_popover(uiLayout &layout,
}
const ARegion *region = CTX_wm_region(&C);
uiBlock *block = uiLayoutGetBlock(&layout);
uiLayout *row = uiLayoutRow(&layout, true);
const bool use_big_size = !RGN_TYPE_IS_HEADER_ANY(region->regiontype);
const bool use_preview_icon = use_big_size;
const short width = [&]() -> short {
if (use_big_size) {
return UI_UNIT_X * 6;
}
return UI_UNIT_X * (name.is_empty() ? 1.6f : 7);
}();
const short height = UI_UNIT_Y * (use_big_size ? 6 : 1);
uiBlock *block = uiLayoutGetBlock(&layout);
uiBut *but = uiDefBlockBut(
block, asset_shelf_block_fn, shelf_type, name, 0, 0, width, height, "Select an asset");
uiLayoutSetContextString(row, "asset_shelf_idname", asset_shelf_id);
if (use_big_size) {
uiLayoutSetScaleX(row, 6);
uiLayoutSetScaleY(row, 6);
}
else {
uiLayoutSetUnitsX(row, name.is_empty() ? 1.6f : 7);
}
uiItemPopoverPanel(row, &C, "ASSETSHELF_PT_popover_panel", name.c_str(), icon);
uiBut *but = static_cast<uiBut *>(block->buttons.last);
if (use_preview_icon) {
ui_def_but_icon(but, icon, UI_HAS_ICON | UI_BUT_ICON_PREVIEW);
}
else {
ui_def_but_icon(but, icon, UI_HAS_ICON);
UI_but_drawflag_enable(but, UI_BUT_ICON_LEFT);
}
bool asset_shelf_popover_invoke(bContext &C, StringRef asset_shelf_idname, ReportList &reports)
{
AssetShelfType *shelf_type = ed::asset::shelf::type_find_from_idname(asset_shelf_idname);
if (ed::asset::shelf::type_poll_for_popup(C, shelf_type) == false) {
UI_but_flag_enable(but, UI_BUT_DISABLED);
return false;
}
PanelType *pt = WM_paneltype_find("ASSETSHELF_PT_popover_panel", true);
if (pt == nullptr) {
BKE_reportf(&reports, RPT_ERROR, "Asset shelf popover panel type not found");
return false;
}
/* Skip panel poll check here. Should usually be done, but requires passing the asset shelf type
* name via some context-store, but there's nothing to provide that here. Asset shelf type is
* polled above, so it's okay. */
std::string asset_shelf_id_str = asset_shelf_idname;
ui_popover_panel_create(
&C,
nullptr,
nullptr,
[asset_shelf_id_str](bContext *C, uiLayout *layout, void *arg_pt) {
uiLayoutSetContextString(layout, "asset_shelf_idname", asset_shelf_id_str);
ui_item_paneltype_func(C, layout, arg_pt);
},
pt);
return true;
}
} // namespace blender::ui
using namespace blender;
std::optional<StringRefNull> UI_asset_shelf_idname_from_button_context(const uiBut *but)
{
return UI_but_context_string_get(but, "asset_shelf_idname");
}

View File

@ -6423,13 +6423,55 @@ void uiTemplateInputStatus(uiLayout *layout, bContext *C)
}
}
static void ui_template_status_info_warnings_messages(Main *bmain,
Scene *scene,
ViewLayer *view_layer,
std::string &warning_message,
std::string &regular_message)
{
char statusbar_info_flag = U.statusbar_flag;
if (bmain->has_forward_compatibility_issues) {
warning_message = ED_info_statusbar_string_ex(
bmain, scene, view_layer, STATUSBAR_SHOW_VERSION);
statusbar_info_flag &= ~STATUSBAR_SHOW_VERSION;
}
regular_message = ED_info_statusbar_string_ex(bmain, scene, view_layer, statusbar_info_flag);
}
static std::string ui_template_status_tooltip(bContext *C, void * /*argN*/, const char * /*tip*/)
{
Main *bmain = CTX_data_main(C);
std::string tooltip_message = "";
if (bmain->has_forward_compatibility_issues) {
char writer_ver_str[12];
BKE_blender_version_blendfile_string_from_values(
writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, -1);
tooltip_message += fmt::format(RPT_("File saved by newer Blender\n({}), expect loss of data"),
writer_ver_str);
}
if (bmain->is_asset_repository) {
if (!tooltip_message.empty()) {
tooltip_message += "\n\n";
}
tooltip_message += RPT_(
"This file is managed by the Blender asset system\n"
"and is expected to contain a single asset data-block.\n"
"Take care to avoid data loss when editing assets.");
}
return tooltip_message;
}
void uiTemplateStatusInfo(uiLayout *layout, bContext *C)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
if (!bmain->has_forward_compatibility_issues) {
if (!BKE_main_has_issues(bmain)) {
const char *status_info_txt = ED_info_statusbar_string(bmain, scene, view_layer);
uiItemL(layout, status_info_txt, ICON_NONE);
return;
@ -6438,13 +6480,12 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C)
/* Blender version part is shown as warning area when there are forward compatibility issues with
* currently loaded .blend file. */
const char *status_info_txt = ED_info_statusbar_string_ex(
bmain, scene, view_layer, (U.statusbar_flag & ~STATUSBAR_SHOW_VERSION));
uiItemL(layout, status_info_txt, ICON_NONE);
std::string warning_message;
std::string regular_message;
ui_template_status_info_warnings_messages(
bmain, scene, view_layer, warning_message, regular_message);
status_info_txt = ED_info_statusbar_string_ex(bmain, scene, view_layer, STATUSBAR_SHOW_VERSION);
uiBut *but;
uiItemL(layout, regular_message.c_str(), ICON_NONE);
const uiStyle *style = UI_style_get();
uiLayout *ui_abs = uiLayoutAbsolute(layout, false);
@ -6452,14 +6493,15 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C)
eUIEmbossType previous_emboss = UI_block_emboss_get(block);
UI_fontstyle_set(&style->widgetlabel);
int width = int(
BLF_width(style->widgetlabel.uifont_id, status_info_txt, strlen(status_info_txt)));
width = max_ii(width, int(10 * UI_SCALE_FAC));
const int width = max_ii(int(BLF_width(style->widgetlabel.uifont_id,
warning_message.c_str(),
warning_message.length())),
int(10 * UI_SCALE_FAC));
UI_block_align_begin(block);
/* Background for icon. */
but = uiDefBut(block,
uiBut *but = uiDefBut(block,
UI_BTYPE_ROUNDBOX,
0,
"",
@ -6495,14 +6537,7 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C)
UI_block_align_end(block);
UI_block_emboss_set(block, UI_EMBOSS_NONE);
/* The report icon itself. */
static char compat_error_msg[256];
char writer_ver_str[12];
BKE_blender_version_blendfile_string_from_values(
writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, -1);
SNPRINTF(compat_error_msg,
RPT_("File saved by newer Blender\n(%s), expect loss of data"),
writer_ver_str);
/* The warning icon itself. */
but = uiDefIconBut(block,
UI_BTYPE_BUT,
0,
@ -6514,15 +6549,17 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C)
nullptr,
0.0f,
0.0f,
compat_error_msg);
nullptr);
UI_but_func_tooltip_set(but, ui_template_status_tooltip, nullptr, nullptr);
UI_GetThemeColorType4ubv(TH_INFO_WARNING_TEXT, SPACE_INFO, but->col);
but->col[3] = 255; /* This theme color is RBG only, so have to set alpha here. */
/* The report message. */
/* The warning message, if any. */
if (!warning_message.empty()) {
but = uiDefBut(block,
UI_BTYPE_BUT,
0,
status_info_txt,
warning_message.c_str(),
UI_UNIT_X,
0,
short(width + UI_UNIT_X),
@ -6530,7 +6567,9 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C)
nullptr,
0.0f,
0.0f,
compat_error_msg);
nullptr);
UI_but_func_tooltip_set(but, ui_template_status_tooltip, nullptr, nullptr);
}
UI_block_emboss_set(block, previous_emboss);
}

View File

@ -4,8 +4,12 @@
set(INC
../include
../asset
../uvedit
../../asset_system
../../blenkernel
../../blenlib
../../blenloader
../../blentranslation
../../bmesh
../../draw
@ -26,6 +30,7 @@ set(INC_SYS
)
set(SRC
brush_asset_ops.cc
curves_sculpt_add.cc
curves_sculpt_brush.cc
curves_sculpt_comb.cc

View File

@ -0,0 +1,795 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_fileops.h"
#include "BLI_path_util.h"
#include "BLI_string.h"
#include "DNA_brush_types.h"
#include "DNA_scene_types.h"
#include "DNA_space_types.h"
#include "BKE_asset.hh"
#include "BKE_asset_edit.hh"
#include "BKE_blendfile.hh"
#include "BKE_brush.hh"
#include "BKE_context.hh"
#include "BKE_paint.hh"
#include "BKE_preferences.h"
#include "BKE_preview_image.hh"
#include "BKE_report.hh"
#include "AS_asset_catalog_path.hh"
#include "AS_asset_catalog_tree.hh"
#include "AS_asset_library.hh"
#include "AS_asset_representation.hh"
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "ED_asset_handle.hh"
#include "ED_asset_library.hh"
#include "ED_asset_list.hh"
#include "ED_asset_mark_clear.hh"
#include "ED_asset_menu_utils.hh"
#include "ED_asset_shelf.hh"
#include "UI_interface_icons.hh"
#include "UI_resources.hh"
#include "BLT_translation.hh"
#include "WM_api.hh"
#include "WM_toolsystem.hh"
#include "paint_intern.hh"
namespace blender::ed::sculpt_paint {
static int brush_asset_select_exec(bContext *C, wmOperator *op)
{
/* This operator currently covers both cases: the file/asset browser file list and the asset list
* used for the asset-view template. Once the asset list design is used by the Asset Browser,
* this can be simplified to just that case. */
Main *bmain = CTX_data_main(C);
const asset_system::AssetRepresentation *asset =
asset::operator_asset_reference_props_get_asset_from_all_library(*C, *op->ptr, op->reports);
if (!asset) {
return OPERATOR_CANCELLED;
}
AssetWeakReference brush_asset_reference = asset->make_weak_reference();
Brush *brush = reinterpret_cast<Brush *>(
bke::asset_edit_id_from_weak_reference(*bmain, ID_BR, brush_asset_reference));
Paint *paint = BKE_paint_get_active_from_context(C);
if (!BKE_paint_brush_set(paint, brush)) {
/* Note brush datablock was still added, so was not a no-op. */
BKE_report(op->reports, RPT_WARNING, "Unable to select brush, wrong object mode");
return OPERATOR_FINISHED;
}
WM_main_add_notifier(NC_ASSET | NA_ACTIVATED, nullptr);
WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, nullptr);
WM_toolsystem_ref_set_by_id(C, "builtin.brush");
return OPERATOR_FINISHED;
}
void BRUSH_OT_asset_select(wmOperatorType *ot)
{
ot->name = "Select Brush Asset";
ot->description = "Select a brush asset as current sculpt and paint tool";
ot->idname = "BRUSH_OT_asset_select";
ot->exec = brush_asset_select_exec;
asset::operator_asset_reference_props_register(*ot->srna);
}
static std::optional<AssetLibraryReference> library_to_library_ref(
const asset_system::AssetLibrary &library)
{
for (const AssetLibraryReference &ref : asset_system::all_valid_asset_library_refs()) {
const std::string root_path = AS_asset_library_root_path_from_library_ref(ref);
/* Use #BLI_path_cmp_normalized because `library.root_path()` ends with a slash while
* `root_path` doesn't. */
if (BLI_path_cmp_normalized(root_path.c_str(), library.root_path().c_str()) == 0) {
return ref;
}
}
return std::nullopt;
}
static AssetLibraryReference user_library_to_library_ref(const bUserAssetLibrary &user_library)
{
AssetLibraryReference library_ref{};
library_ref.custom_library_index = BLI_findindex(&U.asset_libraries, &user_library);
library_ref.type = ASSET_LIBRARY_CUSTOM;
return library_ref;
}
static const bUserAssetLibrary *library_ref_to_user_library(
const AssetLibraryReference &library_ref)
{
if (library_ref.type != ASSET_LIBRARY_CUSTOM) {
return nullptr;
}
return static_cast<const bUserAssetLibrary *>(
BLI_findlink(&U.asset_libraries, library_ref.custom_library_index));
}
static void refresh_asset_library(const bContext *C, const AssetLibraryReference &library_ref)
{
asset::list::clear(&library_ref, C);
/* TODO: Should the all library reference be automatically cleared? */
AssetLibraryReference all_lib_ref = asset_system::all_library_reference();
asset::list::clear(&all_lib_ref, C);
}
static void refresh_asset_library(const bContext *C, const bUserAssetLibrary &user_library)
{
refresh_asset_library(C, user_library_to_library_ref(user_library));
}
static bool brush_asset_save_as_poll(bContext *C)
{
Paint *paint = BKE_paint_get_active_from_context(C);
Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr;
if (paint == nullptr || brush == nullptr) {
return false;
}
if (!paint->brush_asset_reference) {
/* The brush should always be an imported asset. We use this asset reference to find
* which library and catalog the brush came from, as defaults for the popup. */
return false;
}
if (BLI_listbase_is_empty(&U.asset_libraries)) {
CTX_wm_operator_poll_msg_set(C, "No asset library available to save to");
return false;
}
return true;
}
static const bUserAssetLibrary *get_asset_library_from_prop(PointerRNA &ptr)
{
const int enum_value = RNA_enum_get(&ptr, "asset_library_reference");
const AssetLibraryReference lib_ref = asset::library_reference_from_enum_value(enum_value);
return BKE_preferences_asset_library_find_index(&U, lib_ref.custom_library_index);
}
static asset_system::AssetCatalog &asset_library_ensure_catalog(
asset_system::AssetLibrary &library, const asset_system::AssetCatalogPath &path)
{
if (asset_system::AssetCatalog *catalog = library.catalog_service().find_catalog_by_path(path)) {
return *catalog;
}
return *library.catalog_service().create_catalog(path);
}
static asset_system::AssetCatalog &asset_library_ensure_catalogs_in_path(
asset_system::AssetLibrary &library, const asset_system::AssetCatalogPath &path)
{
/* Adding multiple catalogs in a path at a time with #AssetCatalogService::create_catalog()
* doesn't work; add each potentially new catalog in the hierarchy manually here. */
asset_system::AssetCatalogPath parent = "";
path.iterate_components([&](StringRef component_name, bool /*is_last_component*/) {
asset_library_ensure_catalog(library, parent / component_name);
parent = parent / component_name;
});
return *library.catalog_service().find_catalog_by_path(path);
}
static void show_catalog_in_asset_shelf(const bContext &C, const StringRefNull catalog_path)
{
/* Enable catalog in all visible asset shelves. */
wmWindowManager *wm = CTX_wm_manager(&C);
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
const bScreen *screen = WM_window_get_active_screen(win);
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
const AssetShelf *shelf = asset::shelf::active_shelf_from_area(area);
if (shelf && BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(
&U, shelf->idname, catalog_path.c_str()))
{
U.runtime.is_dirty = true;
}
}
}
}
static int brush_asset_save_as_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Paint *paint = BKE_paint_get_active_from_context(C);
Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr;
/* Determine file path to save to. */
PropertyRNA *name_prop = RNA_struct_find_property(op->ptr, "name");
char name[MAX_NAME] = "";
if (RNA_property_is_set(op->ptr, name_prop)) {
RNA_property_string_get(op->ptr, name_prop, name);
}
if (name[0] == '\0') {
STRNCPY(name, brush->id.name + 2);
}
const bUserAssetLibrary *user_library = get_asset_library_from_prop(*op->ptr);
if (!user_library) {
return OPERATOR_CANCELLED;
}
asset_system::AssetLibrary *library = AS_asset_library_load(
bmain, user_library_to_library_ref(*user_library));
if (!library) {
BKE_report(op->reports, RPT_ERROR, "Failed to load asset library");
return OPERATOR_CANCELLED;
}
/* Turn brush into asset if it isn't yet. */
if (!ID_IS_ASSET(&brush->id)) {
asset::mark_id(&brush->id);
asset::generate_preview(C, &brush->id);
}
BLI_assert(ID_IS_ASSET(&brush->id));
/* Add asset to catalog. */
char catalog_path[MAX_NAME];
RNA_string_get(op->ptr, "catalog_path", catalog_path);
AssetMetaData &meta_data = *brush->id.asset_data;
if (catalog_path[0]) {
const asset_system::AssetCatalog &catalog = asset_library_ensure_catalogs_in_path(
*library, catalog_path);
BKE_asset_metadata_catalog_id_set(&meta_data, catalog.catalog_id, catalog.simple_name.c_str());
}
AssetWeakReference brush_asset_reference;
const std::optional<std::string> final_full_asset_filepath = bke::asset_edit_id_save_as(
*bmain, brush->id, name, *user_library, brush_asset_reference, *op->reports);
if (!final_full_asset_filepath) {
return OPERATOR_CANCELLED;
}
library->catalog_service().write_to_disk(*final_full_asset_filepath);
show_catalog_in_asset_shelf(*C, catalog_path);
brush = reinterpret_cast<Brush *>(
bke::asset_edit_id_from_weak_reference(*bmain, ID_BR, brush_asset_reference));
if (!BKE_paint_brush_set(paint, brush)) {
/* Note brush sset was still saved in editable asset library, so was not a no-op. */
BKE_report(op->reports, RPT_WARNING, "Unable to activate just-saved brush asset");
}
refresh_asset_library(C, *user_library);
WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_ADDED, nullptr);
WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush);
return OPERATOR_FINISHED;
}
static bool library_is_editable(const AssetLibraryReference &library)
{
if (library.type == ASSET_LIBRARY_ESSENTIALS) {
return false;
}
return true;
}
static int brush_asset_save_as_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
{
Paint *paint = BKE_paint_get_active_from_context(C);
const AssetWeakReference &brush_weak_ref = *paint->brush_asset_reference;
const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref(
*C, brush_weak_ref, op->reports);
if (!asset) {
return OPERATOR_CANCELLED;
}
const asset_system::AssetLibrary &library = asset->owner_asset_library();
const std::optional<AssetLibraryReference> library_ref = library_to_library_ref(library);
if (!library_ref) {
BLI_assert_unreachable();
return OPERATOR_CANCELLED;
}
RNA_string_set(op->ptr, "name", asset->get_name().c_str());
/* If the library isn't saved from the operator's last execution, find the current library or the
* first library if the current library isn't editable. */
if (!RNA_struct_property_is_set_ex(op->ptr, "asset_library_reference", false)) {
if (library_is_editable(*library_ref)) {
RNA_enum_set(op->ptr,
"asset_library_reference",
asset::library_reference_to_enum_value(&*library_ref));
}
else {
const AssetLibraryReference first_library = user_library_to_library_ref(
*static_cast<const bUserAssetLibrary *>(U.asset_libraries.first));
RNA_enum_set(op->ptr,
"asset_library_reference",
asset::library_reference_to_enum_value(&first_library));
}
}
/* By default, put the new asset in the same catalog as the existing asset. */
if (!RNA_struct_property_is_set(op->ptr, "catalog_path")) {
const asset_system::CatalogID &id = asset->get_metadata().catalog_id;
if (const asset_system::AssetCatalog *catalog = library.catalog_service().find_catalog(id)) {
RNA_string_set(op->ptr, "catalog_path", catalog->path.c_str());
}
}
return WM_operator_props_dialog_popup(C, op, 400, std::nullopt, IFACE_("Save"));
}
static const EnumPropertyItem *rna_asset_library_reference_itemf(bContext * /*C*/,
PointerRNA * /*ptr*/,
PropertyRNA * /*prop*/,
bool *r_free)
{
const EnumPropertyItem *items = asset::library_reference_to_rna_enum_itemf(false);
if (!items) {
*r_free = false;
return nullptr;
}
*r_free = true;
return items;
}
static void visit_library_catalogs_catalog_for_search(
const Main &bmain,
const bUserAssetLibrary &user_library,
const StringRef edit_text,
const FunctionRef<void(StringPropertySearchVisitParams)> visit_fn)
{
const asset_system::AssetLibrary *library = AS_asset_library_load(
&bmain, user_library_to_library_ref(user_library));
if (!library) {
return;
}
if (!edit_text.is_empty()) {
const asset_system::AssetCatalogPath edit_path = edit_text;
if (!library->catalog_service().find_catalog_by_path(edit_path)) {
visit_fn(StringPropertySearchVisitParams{edit_path.str(), std::nullopt, ICON_ADD});
}
}
const asset_system::AssetCatalogTree &full_tree = library->catalog_service().catalog_tree();
full_tree.foreach_item([&](const asset_system::AssetCatalogTreeItem &item) {
visit_fn(StringPropertySearchVisitParams{item.catalog_path().str(), std::nullopt});
});
}
static void visit_library_prop_catalogs_catalog_for_search_fn(
const bContext *C,
PointerRNA *ptr,
PropertyRNA * /*prop*/,
const char *edit_text,
FunctionRef<void(StringPropertySearchVisitParams)> visit_fn)
{
/* NOTE: Using the all library would also be a valid choice. */
if (const bUserAssetLibrary *user_library = get_asset_library_from_prop(*ptr)) {
visit_library_catalogs_catalog_for_search(
*CTX_data_main(C), *user_library, edit_text, visit_fn);
}
}
void BRUSH_OT_asset_save_as(wmOperatorType *ot)
{
ot->name = "Save as Brush Asset";
ot->description =
"Save a copy of the active brush asset into the default asset library, and make it the "
"active brush";
ot->idname = "BRUSH_OT_asset_save_as";
ot->exec = brush_asset_save_as_exec;
ot->invoke = brush_asset_save_as_invoke;
ot->poll = brush_asset_save_as_poll;
ot->prop = RNA_def_string(
ot->srna, "name", nullptr, MAX_NAME, "Name", "Name for the new brush asset");
PropertyRNA *prop = RNA_def_property(ot->srna, "asset_library_reference", PROP_ENUM, PROP_NONE);
RNA_def_enum_funcs(prop, rna_asset_library_reference_itemf);
RNA_def_property_ui_text(prop, "Library", "Asset library used to store the new brush");
prop = RNA_def_string(
ot->srna, "catalog_path", nullptr, MAX_NAME, "Catalog", "Catalog to use for the new asset");
RNA_def_property_string_search_func_runtime(
prop, visit_library_prop_catalogs_catalog_for_search_fn, PROP_STRING_SEARCH_SUGGESTION);
}
static int brush_asset_edit_metadata_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Paint *paint = BKE_paint_get_active_from_context(C);
Brush *brush = BKE_paint_brush(paint);
BLI_assert(ID_IS_ASSET(&brush->id));
const AssetWeakReference &brush_weak_ref = *paint->brush_asset_reference;
const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref(
*C, brush_weak_ref, op->reports);
if (!asset) {
return OPERATOR_CANCELLED;
}
const asset_system::AssetLibrary &library_const = asset->owner_asset_library();
const AssetLibraryReference library_ref = *library_to_library_ref(library_const);
asset_system::AssetLibrary *library = AS_asset_library_load(bmain, library_ref);
char catalog_path[MAX_NAME];
RNA_string_get(op->ptr, "catalog_path", catalog_path);
AssetMetaData &meta_data = *brush->id.asset_data;
MEM_SAFE_FREE(meta_data.author);
meta_data.author = RNA_string_get_alloc(op->ptr, "author", nullptr, 0, nullptr);
MEM_SAFE_FREE(meta_data.description);
meta_data.description = RNA_string_get_alloc(op->ptr, "description", nullptr, 0, nullptr);
if (catalog_path[0]) {
const asset_system::AssetCatalog &catalog = asset_library_ensure_catalogs_in_path(
*library, catalog_path);
BKE_asset_metadata_catalog_id_set(&meta_data, catalog.catalog_id, catalog.simple_name.c_str());
}
if (!bke::asset_edit_id_save(*bmain, brush->id, *op->reports)) {
return OPERATOR_CANCELLED;
}
char asset_full_path_buffer[FILE_MAX_LIBEXTRA];
char *file_path = nullptr;
AS_asset_full_path_explode_from_weak_ref(
&brush_weak_ref, asset_full_path_buffer, &file_path, nullptr, nullptr);
if (!file_path) {
BLI_assert_unreachable();
return OPERATOR_CANCELLED;
}
library->catalog_service().write_to_disk(file_path);
refresh_asset_library(C, library_ref);
WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_EDITED, nullptr);
return OPERATOR_FINISHED;
}
static int brush_asset_edit_metadata_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
{
const Paint *paint = BKE_paint_get_active_from_context(C);
const AssetWeakReference &brush_weak_ref = *paint->brush_asset_reference;
const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref(
*C, brush_weak_ref, op->reports);
if (!asset) {
return OPERATOR_CANCELLED;
}
const asset_system::AssetLibrary &library = asset->owner_asset_library();
const AssetMetaData &meta_data = asset->get_metadata();
if (!RNA_struct_property_is_set(op->ptr, "catalog_path")) {
const asset_system::CatalogID &id = meta_data.catalog_id;
if (const asset_system::AssetCatalog *catalog = library.catalog_service().find_catalog(id)) {
RNA_string_set(op->ptr, "catalog_path", catalog->path.c_str());
}
}
if (!RNA_struct_property_is_set(op->ptr, "author")) {
RNA_string_set(op->ptr, "author", meta_data.author ? meta_data.author : "");
}
if (!RNA_struct_property_is_set(op->ptr, "description")) {
RNA_string_set(op->ptr, "description", meta_data.description ? meta_data.description : "");
}
return WM_operator_props_dialog_popup(C, op, 400, std::nullopt, IFACE_("Edit Metadata"));
}
static void visit_active_library_catalogs_catalog_for_search_fn(
const bContext *C,
PointerRNA * /*ptr*/,
PropertyRNA * /*prop*/,
const char *edit_text,
FunctionRef<void(StringPropertySearchVisitParams)> visit_fn)
{
const Paint *paint = BKE_paint_get_active_from_context(C);
const AssetWeakReference &brush_weak_ref = *paint->brush_asset_reference;
const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref(
*C, brush_weak_ref, nullptr);
if (!asset) {
return;
}
const asset_system::AssetLibrary &library = asset->owner_asset_library();
/* NOTE: Using the all library would also be a valid choice. */
visit_library_catalogs_catalog_for_search(
*CTX_data_main(C),
*library_ref_to_user_library(*library_to_library_ref(library)),
edit_text,
visit_fn);
}
static bool brush_asset_edit_metadata_poll(bContext *C)
{
Paint *paint = BKE_paint_get_active_from_context(C);
Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr;
if (paint == nullptr || brush == nullptr) {
return false;
}
if (!ID_IS_ASSET(&brush->id)) {
BLI_assert_unreachable();
return false;
}
const AssetWeakReference *brush_weak_ref = paint->brush_asset_reference;
if (!brush_weak_ref) {
BLI_assert_unreachable();
return false;
}
const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref(
*C, *brush_weak_ref, nullptr);
if (!asset) {
BLI_assert_unreachable();
return false;
}
const std::optional<AssetLibraryReference> library_ref = library_to_library_ref(
asset->owner_asset_library());
if (!library_ref) {
BLI_assert_unreachable();
return false;
}
if (!library_is_editable(*library_ref)) {
CTX_wm_operator_poll_msg_set(C, "Asset library is not editable");
return false;
}
if (!bke::asset_edit_id_is_writable(brush->id)) {
CTX_wm_operator_poll_msg_set(C, "Asset file is not editable");
return false;
}
return true;
}
void BRUSH_OT_asset_edit_metadata(wmOperatorType *ot)
{
ot->name = "Edit Metadata";
ot->description = "Edit asset information like the catalog, preview image, tags, or author";
ot->idname = "BRUSH_OT_asset_edit_metadata";
ot->exec = brush_asset_edit_metadata_exec;
ot->invoke = brush_asset_edit_metadata_invoke;
ot->poll = brush_asset_edit_metadata_poll;
PropertyRNA *prop = RNA_def_string(
ot->srna, "catalog_path", nullptr, MAX_NAME, "Catalog", "The asset's catalog path");
RNA_def_property_string_search_func_runtime(
prop, visit_active_library_catalogs_catalog_for_search_fn, PROP_STRING_SEARCH_SUGGESTION);
RNA_def_string(ot->srna, "author", nullptr, MAX_NAME, "Author", "");
RNA_def_string(ot->srna, "description", nullptr, MAX_NAME, "Description", "");
}
static int brush_asset_load_preview_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Paint *paint = BKE_paint_get_active_from_context(C);
Brush *brush = BKE_paint_brush(paint);
BLI_assert(ID_IS_ASSET(&brush->id));
const AssetWeakReference &brush_weak_ref = *paint->brush_asset_reference;
const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref(
*C, brush_weak_ref, op->reports);
if (!asset) {
return OPERATOR_CANCELLED;
}
const AssetLibraryReference library_ref = *library_to_library_ref(asset->owner_asset_library());
char filepath[FILE_MAX];
RNA_string_get(op->ptr, "filepath", filepath);
if (!BLI_is_file(filepath)) {
BKE_reportf(op->reports, RPT_ERROR, "File not found '%s'", filepath);
return OPERATOR_CANCELLED;
}
BKE_previewimg_id_custom_set(&brush->id, filepath);
if (!bke::asset_edit_id_save(*bmain, brush->id, *op->reports)) {
return OPERATOR_CANCELLED;
}
refresh_asset_library(C, library_ref);
WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_EDITED, nullptr);
return OPERATOR_FINISHED;
}
static int brush_asset_load_preview_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
if (RNA_struct_property_is_set(op->ptr, "filepath")) {
return brush_asset_load_preview_exec(C, op);
}
return WM_operator_filesel(C, op, event);
}
void BRUSH_OT_asset_load_preview(wmOperatorType *ot)
{
ot->name = "Load Preview Image";
ot->description = "Choose a preview image for the brush";
ot->idname = "BRUSH_OT_asset_load_preview";
ot->exec = brush_asset_load_preview_exec;
ot->invoke = brush_asset_load_preview_invoke;
ot->poll = brush_asset_edit_metadata_poll;
WM_operator_properties_filesel(ot,
FILE_TYPE_FOLDER | FILE_TYPE_IMAGE,
FILE_SPECIAL,
FILE_OPENFILE,
WM_FILESEL_FILEPATH,
FILE_DEFAULTDISPLAY,
FILE_SORT_DEFAULT);
}
static bool brush_asset_delete_poll(bContext *C)
{
Paint *paint = BKE_paint_get_active_from_context(C);
Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr;
if (paint == nullptr || brush == nullptr) {
return false;
}
/* Linked brush, check if belongs to an editable blend file. */
if (ID_IS_LINKED(brush)) {
if (!bke::asset_edit_id_is_writable(brush->id)) {
CTX_wm_operator_poll_msg_set(C, "Asset blend file is not editable");
return false;
}
}
return true;
}
static int brush_asset_delete_exec(bContext *C, wmOperator *op)
{
Paint *paint = BKE_paint_get_active_from_context(C);
Brush *brush = BKE_paint_brush(paint);
Main *bmain = CTX_data_main(C);
bUserAssetLibrary *library = (paint->brush_asset_reference) ?
BKE_preferences_asset_library_find_by_name(
&U,
paint->brush_asset_reference->asset_library_identifier) :
nullptr;
bke::asset_edit_id_delete(*bmain, brush->id, *op->reports);
BKE_paint_brush_set_default(bmain, paint);
if (library) {
refresh_asset_library(C, *library);
}
WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_REMOVED, nullptr);
WM_main_add_notifier(NC_BRUSH | NA_EDITED, nullptr);
return OPERATOR_FINISHED;
}
static int brush_asset_delete_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
{
Paint *paint = BKE_paint_get_active_from_context(C);
Brush *brush = BKE_paint_brush(paint);
return WM_operator_confirm_ex(
C,
op,
IFACE_("Delete Brush Asset"),
ID_IS_LINKED(brush) ?
IFACE_("Permanently delete brush asset blend file. This can't be undone.") :
IFACE_("Premanently delete brush. This can't be undo."),
IFACE_("Delete"),
ALERT_ICON_WARNING,
false);
}
void BRUSH_OT_asset_delete(wmOperatorType *ot)
{
ot->name = "Delete Brush Asset";
ot->description = "Delete the active brush asset";
ot->idname = "BRUSH_OT_asset_delete";
ot->exec = brush_asset_delete_exec;
ot->invoke = brush_asset_delete_invoke;
ot->poll = brush_asset_delete_poll;
}
static bool brush_asset_update_poll(bContext *C)
{
Paint *paint = BKE_paint_get_active_from_context(C);
Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr;
if (paint == nullptr || brush == nullptr) {
return false;
}
if (!bke::asset_edit_id_is_editable(brush->id)) {
return false;
}
if (!(paint->brush_asset_reference && ID_IS_ASSET(brush))) {
return false;
}
if (!bke::asset_edit_id_is_writable(brush->id)) {
CTX_wm_operator_poll_msg_set(C, "Asset blend file is not editable");
return false;
}
return true;
}
static int brush_asset_update_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Paint *paint = BKE_paint_get_active_from_context(C);
Brush *brush = BKE_paint_brush(paint);
const AssetWeakReference *asset_weak_ref = paint->brush_asset_reference;
const bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_by_name(
&U, asset_weak_ref->asset_library_identifier);
if (!user_library) {
return OPERATOR_CANCELLED;
}
BLI_assert(ID_IS_ASSET(brush));
bke::asset_edit_id_save(*bmain, brush->id, *op->reports);
refresh_asset_library(C, *user_library);
WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_EDITED, nullptr);
WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush);
return OPERATOR_FINISHED;
}
void BRUSH_OT_asset_update(wmOperatorType *ot)
{
ot->name = "Update Brush Asset";
ot->description = "Update the active brush asset in the asset library with current settings";
ot->idname = "BRUSH_OT_asset_update";
ot->exec = brush_asset_update_exec;
ot->poll = brush_asset_update_poll;
}
static bool brush_asset_revert_poll(bContext *C)
{
Paint *paint = BKE_paint_get_active_from_context(C);
Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr;
if (paint == nullptr || brush == nullptr) {
return false;
}
return paint->brush_asset_reference && bke::asset_edit_id_is_editable(brush->id);
}
static int brush_asset_revert_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Paint *paint = BKE_paint_get_active_from_context(C);
Brush *brush = BKE_paint_brush(paint);
bke::asset_edit_id_revert(*bmain, brush->id, *op->reports);
WM_main_add_notifier(NC_BRUSH | NA_EDITED, nullptr);
WM_main_add_notifier(NC_TEXTURE | ND_NODES, nullptr);
return OPERATOR_FINISHED;
}
void BRUSH_OT_asset_revert(wmOperatorType *ot)
{
ot->name = "Revert Brush Asset";
ot->description =
"Revert the active brush settings to the default values from the asset library";
ot->idname = "BRUSH_OT_asset_revert";
ot->exec = brush_asset_revert_exec;
ot->poll = brush_asset_revert_poll;
}
} // namespace blender::ed::sculpt_paint

View File

@ -901,7 +901,7 @@ void ED_object_texture_paint_mode_enter_ex(Main &bmain,
BKE_paint_init(&bmain, &scene, PaintMode::Texture3D, PAINT_CURSOR_TEXTURE_PAINT);
BKE_paint_brush_validate(&bmain, &imapaint.paint);
BKE_paint_brushes_validate(&bmain, &imapaint.paint);
if (U.glreslimit != 0) {
BKE_image_free_all_gputextures(&bmain);

View File

@ -118,6 +118,14 @@ bool paint_stroke_started(PaintStroke *stroke);
bool paint_brush_tool_poll(bContext *C);
void BRUSH_OT_asset_select(wmOperatorType *ot);
void BRUSH_OT_asset_save_as(wmOperatorType *ot);
void BRUSH_OT_asset_edit_metadata(wmOperatorType *ot);
void BRUSH_OT_asset_load_preview(wmOperatorType *ot);
void BRUSH_OT_asset_delete(wmOperatorType *ot);
void BRUSH_OT_asset_update(wmOperatorType *ot);
void BRUSH_OT_asset_revert(wmOperatorType *ot);
} // namespace blender::ed::sculpt_paint
/**

View File

@ -47,222 +47,6 @@
#include "paint_intern.hh"
#include "sculpt_intern.hh"
/* Brush operators */
static int brush_add_exec(bContext *C, wmOperator * /*op*/)
{
// int type = RNA_enum_get(op->ptr, "type");
Paint *paint = BKE_paint_get_active_from_context(C);
Brush *br = BKE_paint_brush(paint);
Main *bmain = CTX_data_main(C);
PaintMode mode = BKE_paintmode_get_active_from_context(C);
if (br) {
br = (Brush *)BKE_id_copy(bmain, &br->id);
}
else {
br = BKE_brush_add(bmain, "Brush", BKE_paint_object_mode_from_paintmode(mode));
}
id_us_min(&br->id); /* fake user only */
BKE_paint_brush_set(paint, br);
return OPERATOR_FINISHED;
}
static void BRUSH_OT_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Brush";
ot->description = "Add brush by mode type";
ot->idname = "BRUSH_OT_add";
/* api callbacks */
ot->exec = brush_add_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static eGPBrush_Presets gpencil_get_brush_preset_from_tool(bToolRef *tool,
enum eContextObjectMode mode)
{
switch (mode) {
case CTX_MODE_PAINT_GPENCIL_LEGACY: {
if (STREQ(tool->runtime->data_block, "DRAW")) {
return GP_BRUSH_PRESET_PENCIL;
}
if (STREQ(tool->runtime->data_block, "FILL")) {
return GP_BRUSH_PRESET_FILL_AREA;
}
if (STREQ(tool->runtime->data_block, "ERASE")) {
return GP_BRUSH_PRESET_ERASER_SOFT;
}
if (STREQ(tool->runtime->data_block, "TINT")) {
return GP_BRUSH_PRESET_TINT;
}
break;
}
case CTX_MODE_SCULPT_GPENCIL_LEGACY: {
if (STREQ(tool->runtime->data_block, "SMOOTH")) {
return GP_BRUSH_PRESET_SMOOTH_STROKE;
}
if (STREQ(tool->runtime->data_block, "STRENGTH")) {
return GP_BRUSH_PRESET_STRENGTH_STROKE;
}
if (STREQ(tool->runtime->data_block, "THICKNESS")) {
return GP_BRUSH_PRESET_THICKNESS_STROKE;
}
if (STREQ(tool->runtime->data_block, "GRAB")) {
return GP_BRUSH_PRESET_GRAB_STROKE;
}
if (STREQ(tool->runtime->data_block, "PUSH")) {
return GP_BRUSH_PRESET_PUSH_STROKE;
}
if (STREQ(tool->runtime->data_block, "TWIST")) {
return GP_BRUSH_PRESET_TWIST_STROKE;
}
if (STREQ(tool->runtime->data_block, "PINCH")) {
return GP_BRUSH_PRESET_PINCH_STROKE;
}
if (STREQ(tool->runtime->data_block, "RANDOMIZE")) {
return GP_BRUSH_PRESET_RANDOMIZE_STROKE;
}
if (STREQ(tool->runtime->data_block, "CLONE")) {
return GP_BRUSH_PRESET_CLONE_STROKE;
}
break;
}
case CTX_MODE_WEIGHT_GPENCIL_LEGACY: {
if (STREQ(tool->runtime->data_block, "DRAW")) {
return GP_BRUSH_PRESET_WEIGHT_DRAW;
}
if (STREQ(tool->runtime->data_block, "BLUR")) {
return GP_BRUSH_PRESET_WEIGHT_BLUR;
}
if (STREQ(tool->runtime->data_block, "AVERAGE")) {
return GP_BRUSH_PRESET_WEIGHT_AVERAGE;
}
if (STREQ(tool->runtime->data_block, "SMEAR")) {
return GP_BRUSH_PRESET_WEIGHT_SMEAR;
}
break;
}
case CTX_MODE_VERTEX_GPENCIL_LEGACY: {
if (STREQ(tool->runtime->data_block, "DRAW")) {
return GP_BRUSH_PRESET_VERTEX_DRAW;
}
if (STREQ(tool->runtime->data_block, "BLUR")) {
return GP_BRUSH_PRESET_VERTEX_BLUR;
}
if (STREQ(tool->runtime->data_block, "AVERAGE")) {
return GP_BRUSH_PRESET_VERTEX_AVERAGE;
}
if (STREQ(tool->runtime->data_block, "SMEAR")) {
return GP_BRUSH_PRESET_VERTEX_SMEAR;
}
if (STREQ(tool->runtime->data_block, "REPLACE")) {
return GP_BRUSH_PRESET_VERTEX_REPLACE;
}
break;
}
default:
return GP_BRUSH_PRESET_UNKNOWN;
}
return GP_BRUSH_PRESET_UNKNOWN;
}
static int brush_add_gpencil_exec(bContext *C, wmOperator * /*op*/)
{
Paint *paint = BKE_paint_get_active_from_context(C);
Brush *br = BKE_paint_brush(paint);
Main *bmain = CTX_data_main(C);
if (br) {
br = (Brush *)BKE_id_copy(bmain, &br->id);
}
else {
/* Get the active tool to determine what type of brush is active. */
bScreen *screen = CTX_wm_screen(C);
if (screen == nullptr) {
return OPERATOR_CANCELLED;
}
bToolRef *tool = nullptr;
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
if (area->spacetype == SPACE_VIEW3D) {
/* Check the current tool is a brush. */
bToolRef *tref = area->runtime.tool;
if (tref && tref->runtime && tref->runtime->data_block[0]) {
tool = tref;
break;
}
}
}
if (tool == nullptr) {
return OPERATOR_CANCELLED;
}
/* Get Brush mode base on context mode. */
const enum eContextObjectMode mode = CTX_data_mode_enum(C);
eObjectMode obmode = OB_MODE_PAINT_GPENCIL_LEGACY;
switch (mode) {
case CTX_MODE_PAINT_GPENCIL_LEGACY:
obmode = OB_MODE_PAINT_GPENCIL_LEGACY;
break;
case CTX_MODE_SCULPT_GPENCIL_LEGACY:
obmode = OB_MODE_SCULPT_GPENCIL_LEGACY;
break;
case CTX_MODE_WEIGHT_GPENCIL_LEGACY:
obmode = OB_MODE_WEIGHT_GPENCIL_LEGACY;
break;
case CTX_MODE_VERTEX_GPENCIL_LEGACY:
obmode = OB_MODE_VERTEX_GPENCIL_LEGACY;
break;
default:
return OPERATOR_CANCELLED;
break;
}
/* Get brush preset using the actual tool. */
eGPBrush_Presets preset = gpencil_get_brush_preset_from_tool(tool, mode);
/* Capitalize Brush name first letter using the tool name. */
char name[64];
STRNCPY(name, tool->runtime->data_block);
BLI_str_tolower_ascii(name, sizeof(name));
name[0] = BLI_toupper_ascii(name[0]);
/* Create the brush and assign default values. */
br = BKE_brush_add(bmain, name, obmode);
if (br) {
BKE_brush_init_gpencil_settings(br);
BKE_gpencil_brush_preset_set(bmain, br, preset);
}
}
if (br) {
id_us_min(&br->id); /* fake user only */
BKE_paint_brush_set(paint, br);
}
return OPERATOR_FINISHED;
}
static void BRUSH_OT_add_gpencil(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Drawing Brush";
ot->description = "Add brush for Grease Pencil";
ot->idname = "BRUSH_OT_add_gpencil";
/* api callbacks */
ot->exec = brush_add_gpencil_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static int brush_scale_size_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
@ -734,303 +518,6 @@ static void PALETTE_OT_join(wmOperatorType *ot)
RNA_def_string(ot->srna, "palette", nullptr, MAX_ID_NAME - 2, "Palette", "Name of the Palette");
}
static int brush_reset_exec(bContext *C, wmOperator * /*op*/)
{
Paint *paint = BKE_paint_get_active_from_context(C);
Brush *brush = BKE_paint_brush(paint);
Object *ob = CTX_data_active_object(C);
if (!ob || !brush) {
return OPERATOR_CANCELLED;
}
/* TODO: other modes */
if (ob->mode & OB_MODE_SCULPT) {
BKE_brush_sculpt_reset(brush);
}
else {
return OPERATOR_CANCELLED;
}
WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush);
return OPERATOR_FINISHED;
}
static void BRUSH_OT_reset(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Reset Brush";
ot->description = "Return brush to defaults based on current tool";
ot->idname = "BRUSH_OT_reset";
/* api callbacks */
ot->exec = brush_reset_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static int brush_tool(const Brush *brush, size_t tool_offset)
{
return *(((char *)brush) + tool_offset);
}
static void brush_tool_set(const Brush *brush, size_t tool_offset, int tool)
{
*(((char *)brush) + tool_offset) = tool;
}
static Brush *brush_tool_cycle(Main *bmain, Paint *paint, Brush *brush_orig, const int tool)
{
Brush *brush, *first_brush;
if (!brush_orig && !(brush_orig = static_cast<Brush *>(bmain->brushes.first))) {
return nullptr;
}
if (brush_tool(brush_orig, paint->runtime.tool_offset) != tool) {
/* If current brush's tool is different from what we need,
* start cycling from the beginning of the list.
* Such logic will activate the same exact brush not relating from
* which tool user requests other tool.
*/
/* Try to tool-slot first. */
first_brush = BKE_paint_toolslots_brush_get(paint, tool);
if (first_brush == nullptr) {
first_brush = static_cast<Brush *>(bmain->brushes.first);
}
}
else {
/* If user wants to switch to brush with the same tool as
* currently active brush do a cycling via all possible
* brushes with requested tool. */
first_brush = brush_orig->id.next ? static_cast<Brush *>(brush_orig->id.next) :
static_cast<Brush *>(bmain->brushes.first);
}
/* get the next brush with the active tool */
brush = first_brush;
do {
if ((brush->ob_mode & paint->runtime.ob_mode) &&
(brush_tool(brush, paint->runtime.tool_offset) == tool))
{
return brush;
}
brush = brush->id.next ? static_cast<Brush *>(brush->id.next) :
static_cast<Brush *>(bmain->brushes.first);
} while (brush != first_brush);
return nullptr;
}
static Brush *brush_tool_toggle(Main *bmain, Paint *paint, Brush *brush_orig, const int tool)
{
if (!brush_orig || brush_tool(brush_orig, paint->runtime.tool_offset) != tool) {
Brush *br;
/* if the current brush is not using the desired tool, look
* for one that is */
br = brush_tool_cycle(bmain, paint, brush_orig, tool);
/* store the previously-selected brush */
if (br) {
br->toggle_brush = brush_orig;
}
return br;
}
if (brush_orig->toggle_brush) {
/* if current brush is using the desired tool, try to toggle
* back to the previously selected brush. */
return brush_orig->toggle_brush;
}
return nullptr;
}
/** The name of the active tool is "builtin_brush." concatenated with the returned string. */
static blender::StringRefNull curves_active_tool_name_get(const eBrushCurvesSculptTool tool)
{
switch (tool) {
case CURVES_SCULPT_TOOL_COMB:
return "comb";
case CURVES_SCULPT_TOOL_DELETE:
return "delete";
case CURVES_SCULPT_TOOL_SNAKE_HOOK:
return "snake_hook";
case CURVES_SCULPT_TOOL_ADD:
return "add";
case CURVES_SCULPT_TOOL_GROW_SHRINK:
return "grow_shrink";
case CURVES_SCULPT_TOOL_SELECTION_PAINT:
return "selection_paint";
case CURVES_SCULPT_TOOL_PINCH:
return "pinch";
case CURVES_SCULPT_TOOL_SMOOTH:
return "smooth";
case CURVES_SCULPT_TOOL_PUFF:
return "puff";
case CURVES_SCULPT_TOOL_DENSITY:
return "density";
case CURVES_SCULPT_TOOL_SLIDE:
return "slide";
}
return "";
}
static bool brush_generic_tool_set(bContext *C,
Main *bmain,
Paint *paint,
const int tool,
const char *tool_name,
const bool create_missing,
const bool toggle)
{
Brush *brush, *brush_orig = BKE_paint_brush(paint);
if (toggle) {
brush = brush_tool_toggle(bmain, paint, brush_orig, tool);
}
else {
brush = brush_tool_cycle(bmain, paint, brush_orig, tool);
}
if (((brush == nullptr) && create_missing) &&
((brush_orig == nullptr) || brush_tool(brush_orig, paint->runtime.tool_offset) != tool))
{
brush = BKE_brush_add(bmain, tool_name, eObjectMode(paint->runtime.ob_mode));
id_us_min(&brush->id); /* fake user only */
brush_tool_set(brush, paint->runtime.tool_offset, tool);
brush->toggle_brush = brush_orig;
}
if (brush) {
BKE_paint_brush_set(paint, brush);
BKE_paint_invalidate_overlay_all();
WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush);
/* Tool System
* This is needed for when there is a non-sculpt tool active (transform for e.g.).
* In case we are toggling (and the brush changed to the toggle_brush), we need to get the
* tool_name again. */
int tool_result = brush_tool(brush, paint->runtime.tool_offset);
PaintMode paint_mode = BKE_paintmode_get_active_from_context(C);
if (paint_mode == PaintMode::SculptCurves) {
tool_name = curves_active_tool_name_get(eBrushCurvesSculptTool(tool)).c_str();
}
else {
const EnumPropertyItem *items = BKE_paint_get_tool_enum_from_paintmode(paint_mode);
RNA_enum_name_from_value(items, tool_result, &tool_name);
}
char tool_id[MAX_NAME];
SNPRINTF(tool_id, "builtin_brush.%s", tool_name);
WM_toolsystem_ref_set_by_id(C, tool_id);
return true;
}
return false;
}
static const PaintMode brush_select_paint_modes[] = {
PaintMode::Sculpt,
PaintMode::Vertex,
PaintMode::Weight,
PaintMode::Texture3D,
PaintMode::GPencil,
PaintMode::VertexGPencil,
PaintMode::SculptGPencil,
PaintMode::WeightGPencil,
PaintMode::SculptCurves,
PaintMode::SculptGreasePencil,
};
static int brush_select_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
const bool create_missing = RNA_boolean_get(op->ptr, "create_missing");
const bool toggle = RNA_boolean_get(op->ptr, "toggle");
const char *tool_name = "Brush";
int tool = 0;
PaintMode paint_mode = PaintMode::Invalid;
for (int i = 0; i < ARRAY_SIZE(brush_select_paint_modes); i++) {
paint_mode = brush_select_paint_modes[i];
const char *op_prop_id = BKE_paint_get_tool_prop_id_from_paintmode(paint_mode);
PropertyRNA *prop = RNA_struct_find_property(op->ptr, op_prop_id);
if (RNA_property_is_set(op->ptr, prop)) {
tool = RNA_property_enum_get(op->ptr, prop);
break;
}
}
if (paint_mode == PaintMode::Invalid) {
return OPERATOR_CANCELLED;
}
Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode);
if (paint == nullptr) {
return OPERATOR_CANCELLED;
}
if (paint_mode == PaintMode::SculptCurves) {
tool_name = curves_active_tool_name_get(eBrushCurvesSculptTool(tool)).c_str();
}
else {
const EnumPropertyItem *items = BKE_paint_get_tool_enum_from_paintmode(paint_mode);
RNA_enum_name_from_value(items, tool, &tool_name);
}
if (brush_generic_tool_set(C, bmain, paint, tool, tool_name, create_missing, toggle)) {
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
static void PAINT_OT_brush_select(wmOperatorType *ot)
{
PropertyRNA *prop;
/* identifiers */
ot->name = "Brush Select";
ot->description = "Select a paint mode's brush by tool type";
ot->idname = "PAINT_OT_brush_select";
/* api callbacks */
ot->exec = brush_select_exec;
/* flags */
ot->flag = 0;
/* props */
/* All properties are hidden, so as not to show the redo panel. */
for (int i = 0; i < ARRAY_SIZE(brush_select_paint_modes); i++) {
const PaintMode paint_mode = brush_select_paint_modes[i];
const char *prop_id = BKE_paint_get_tool_prop_id_from_paintmode(paint_mode);
/* Prevent a duplicate `gpencil_sculpt_tool` property. */
if (RNA_struct_type_find_property_no_base(ot->srna, prop_id)) {
continue;
}
prop = RNA_def_enum(
ot->srna, prop_id, BKE_paint_get_tool_enum_from_paintmode(paint_mode), 0, prop_id, "");
RNA_def_property_translation_context(
prop, BKE_paint_get_tool_enum_translation_context_from_paintmode(paint_mode));
RNA_def_property_flag(prop, PROP_HIDDEN);
}
prop = RNA_def_boolean(
ot->srna, "toggle", false, "Toggle", "Toggle between two brushes rather than cycling");
RNA_def_property_flag(prop, PropertyFlag(PROP_HIDDEN | PROP_SKIP_SAVE));
prop = RNA_def_boolean(ot->srna,
"create_missing",
false,
"Create Missing",
"If the requested brush type does not exist, create a new brush");
RNA_def_property_flag(prop, PropertyFlag(PROP_HIDDEN | PROP_SKIP_SAVE));
}
/***** Stencil Control *****/
enum StencilControlMode {
@ -1483,18 +970,19 @@ void ED_operatortypes_paint()
WM_operatortype_append(PAINTCURVE_OT_cursor);
/* brush */
WM_operatortype_append(BRUSH_OT_add);
WM_operatortype_append(BRUSH_OT_add_gpencil);
WM_operatortype_append(BRUSH_OT_scale_size);
WM_operatortype_append(BRUSH_OT_curve_preset);
WM_operatortype_append(BRUSH_OT_sculpt_curves_falloff_preset);
WM_operatortype_append(BRUSH_OT_reset);
WM_operatortype_append(BRUSH_OT_stencil_control);
WM_operatortype_append(BRUSH_OT_stencil_fit_image_aspect);
WM_operatortype_append(BRUSH_OT_stencil_reset_transform);
/* NOTE: particle uses a different system, can be added with existing operators in `wm.py`. */
WM_operatortype_append(PAINT_OT_brush_select);
WM_operatortype_append(BRUSH_OT_asset_select);
WM_operatortype_append(BRUSH_OT_asset_save_as);
WM_operatortype_append(BRUSH_OT_asset_edit_metadata);
WM_operatortype_append(BRUSH_OT_asset_load_preview);
WM_operatortype_append(BRUSH_OT_asset_delete);
WM_operatortype_append(BRUSH_OT_asset_update);
WM_operatortype_append(BRUSH_OT_asset_revert);
/* image */
WM_operatortype_append(PAINT_OT_texture_paint_toggle);

View File

@ -422,19 +422,17 @@ bool mode_toggle_poll_test(bContext *C)
void smooth_brush_toggle_off(const bContext *C, Paint *paint, StrokeCache *cache)
{
Main *bmain = CTX_data_main(C);
Brush *brush = BKE_paint_brush(paint);
/* The current brush should match with what we have stored in the cache. */
BLI_assert(brush == cache->brush);
/* If saved_active_brush_name is not set, brush was not switched/affected in
/* If saved_active_brush is not set, brush was not switched/affected in
* smooth_brush_toggle_on(). */
Brush *saved_active_brush = (Brush *)BKE_libblock_find_name(
bmain, ID_BR, cache->saved_active_brush_name);
if (saved_active_brush) {
if (cache->saved_active_brush) {
Scene *scene = CTX_data_scene(C);
BKE_brush_size_set(scene, brush, cache->saved_smooth_size);
BKE_paint_brush_set(paint, saved_active_brush);
BKE_paint_brush_set(paint, cache->saved_active_brush);
cache->saved_active_brush = nullptr;
}
}
/* Initialize the stroke cache invariants from operator properties */
@ -579,24 +577,27 @@ void last_stroke_update(Scene &scene, const float location[3])
/* -------------------------------------------------------------------- */
void smooth_brush_toggle_on(const bContext *C, Paint *paint, StrokeCache *cache)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
Brush *cur_brush = BKE_paint_brush(paint);
/* Switch to the blur (smooth) brush if possible. */
/* NOTE: used for both vertexpaint and weightpaint, VPAINT_TOOL_BLUR & WPAINT_TOOL_BLUR are the
* same, see comments for eBrushVertexPaintTool & eBrushWeightPaintTool. */
Brush *smooth_brush = BKE_paint_toolslots_brush_get(paint, WPAINT_TOOL_BLUR);
BKE_paint_brush_set_essentials(bmain,
paint,
(paint->runtime.ob_mode = OB_MODE_WEIGHT_PAINT) ? "Blur Weight" :
"Blur Vertex");
Brush *smooth_brush = BKE_paint_brush(paint);
if (!smooth_brush) {
BKE_paint_brush_set(paint, cur_brush);
CLOG_WARN(&LOG, "Switching to the blur (smooth) brush not possible, corresponding brush not");
cache->saved_active_brush_name[0] = '\0';
cache->saved_active_brush = nullptr;
return;
}
Brush *cur_brush = paint->brush;
int cur_brush_size = BKE_brush_size_get(scene, cur_brush);
STRNCPY(cache->saved_active_brush_name, cur_brush->id.name + 2);
BKE_paint_brush_set(paint, smooth_brush);
cache->saved_active_brush = cur_brush;
cache->saved_smooth_size = BKE_brush_size_get(scene, smooth_brush);
BKE_brush_size_set(scene, smooth_brush, cur_brush_size);
BKE_curvemapping_init(smooth_brush->curve);
@ -842,7 +843,7 @@ static int vpaint_mode_toggle_exec(bContext *C, wmOperator *op)
depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
}
ED_object_vpaintmode_enter_ex(bmain, *depsgraph, scene, ob);
BKE_paint_brush_validate(&bmain, &ts.vpaint->paint);
BKE_paint_brushes_validate(&bmain, &ts.vpaint->paint);
}
BKE_mesh_batch_cache_dirty_tag((Mesh *)ob.data, BKE_MESH_BATCH_DIRTY_ALL);

View File

@ -1631,7 +1631,7 @@ static int wpaint_mode_toggle_exec(bContext *C, wmOperator *op)
depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
}
ED_object_wpaintmode_enter_ex(bmain, *depsgraph, scene, ob);
BKE_paint_brush_validate(&bmain, &ts.wpaint->paint);
BKE_paint_brushes_validate(&bmain, &ts.wpaint->paint);
}
/* Prepare armature posemode. */

View File

@ -4227,8 +4227,9 @@ static void sculpt_init_mirror_clipping(Object &ob, SculptSession &ss)
static void smooth_brush_toggle_on(const bContext *C, Paint *paint, StrokeCache *cache)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
Brush *cur_brush = paint->brush;
Brush *cur_brush = BKE_paint_brush(paint);
if (cur_brush->sculpt_tool == SCULPT_TOOL_MASK) {
cache->saved_mask_brush_tool = cur_brush->mask_tool;
@ -4247,18 +4248,20 @@ static void smooth_brush_toggle_on(const bContext *C, Paint *paint, StrokeCache
}
/* Switch to the smooth brush if possible. */
Brush *smooth_brush = BKE_paint_toolslots_brush_get(paint, SCULPT_TOOL_SMOOTH);
BKE_paint_brush_set_essentials(bmain, paint, "Smooth");
Brush *smooth_brush = BKE_paint_brush(paint);
if (!smooth_brush) {
BKE_paint_brush_set(paint, cur_brush);
CLOG_WARN(&LOG, "Switching to the smooth brush not possible, corresponding brush not");
cache->saved_active_brush_name[0] = '\0';
cache->saved_active_brush = nullptr;
return;
}
int cur_brush_size = BKE_brush_size_get(scene, cur_brush);
STRNCPY(cache->saved_active_brush_name, cur_brush->id.name + 2);
cache->saved_active_brush = cur_brush;
BKE_paint_brush_set(paint, smooth_brush);
cache->saved_smooth_size = BKE_brush_size_get(scene, smooth_brush);
BKE_brush_size_set(scene, smooth_brush, cur_brush_size);
BKE_curvemapping_init(smooth_brush->curve);
@ -4266,7 +4269,6 @@ static void smooth_brush_toggle_on(const bContext *C, Paint *paint, StrokeCache
static void smooth_brush_toggle_off(const bContext *C, Paint *paint, StrokeCache *cache)
{
Main *bmain = CTX_data_main(C);
Brush &brush = *BKE_paint_brush(paint);
if (brush.sculpt_tool == SCULPT_TOOL_MASK) {
@ -4284,14 +4286,13 @@ static void smooth_brush_toggle_off(const bContext *C, Paint *paint, StrokeCache
return;
}
/* If saved_active_brush_name is not set, brush was not switched/affected in
/* If saved_active_brush is not set, brush was not switched/affected in
* smooth_brush_toggle_on(). */
Brush *saved_active_brush = (Brush *)BKE_libblock_find_name(
bmain, ID_BR, cache->saved_active_brush_name);
if (saved_active_brush) {
if (cache->saved_active_brush) {
Scene *scene = CTX_data_scene(C);
BKE_brush_size_set(scene, &brush, cache->saved_smooth_size);
BKE_paint_brush_set(paint, saved_active_brush);
BKE_paint_brush_set(paint, cache->saved_active_brush);
cache->saved_active_brush = nullptr;
}
}

View File

@ -509,7 +509,7 @@ struct StrokeCache {
float vertex_rotation; /* amount to rotate the vertices when using rotate brush */
Dial *dial;
char saved_active_brush_name[MAX_ID_NAME];
Brush *saved_active_brush;
char saved_mask_brush_tool;
int saved_smooth_size; /* smooth tool copies the size of the current tool */
bool alt_smooth;

View File

@ -526,7 +526,7 @@ static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op)
depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
}
ED_object_sculptmode_enter_ex(bmain, *depsgraph, scene, ob, false, op->reports);
BKE_paint_brush_validate(&bmain, &ts.sculpt->paint);
BKE_paint_brushes_validate(&bmain, &ts.sculpt->paint);
if (ob.mode & mode_flag) {
Mesh *mesh = static_cast<Mesh *>(ob.data);

View File

@ -1202,6 +1202,7 @@ void ED_spacetype_image()
art->free = asset::shelf::region_free;
art->on_poll_success = asset::shelf::region_on_poll_success;
art->listener = asset::shelf::region_listen;
art->message_subscribe = asset::shelf::region_message_subscribe;
art->poll = asset::shelf::regions_poll;
art->snap_size = asset::shelf::region_snap;
art->on_user_resize = asset::shelf::region_on_user_resize;
@ -1221,7 +1222,7 @@ void ED_spacetype_image()
art->listener = asset::shelf::header_region_listen;
art->context = asset::shelf::context;
BLI_addhead(&st->regiontypes, art);
asset::shelf::header_regiontype_register(art, SPACE_IMAGE);
asset::shelf::types_register(art, SPACE_IMAGE);
/* regions: hud */
art = ED_area_type_hud(st->spaceid);

View File

@ -2219,6 +2219,7 @@ void ED_spacetype_view3d()
art->free = asset::shelf::region_free;
art->on_poll_success = asset::shelf::region_on_poll_success;
art->listener = asset::shelf::region_listen;
art->message_subscribe = asset::shelf::region_message_subscribe;
art->poll = asset::shelf::regions_poll;
art->snap_size = asset::shelf::region_snap;
art->on_user_resize = asset::shelf::region_on_user_resize;
@ -2238,7 +2239,7 @@ void ED_spacetype_view3d()
art->listener = asset::shelf::header_region_listen;
art->context = asset::shelf::context;
BLI_addhead(&st->regiontypes, art);
asset::shelf::header_regiontype_register(art, SPACE_VIEW3D);
asset::shelf::types_register(art, SPACE_VIEW3D);
/* regions: hud */
art = ED_area_type_hud(st->spaceid);

View File

@ -117,6 +117,7 @@ void ED_editors_init(bContext *C)
}
else if (mode & OB_MODE_ALL_PAINT_GPENCIL) {
ED_gpencil_toggle_brush_cursor(C, true, nullptr);
BKE_paint_ensure_from_paintmode(bmain, scene, BKE_paintmode_get_active_from_context(C));
}
continue;
}

View File

@ -76,8 +76,8 @@ typedef enum eGPDbrush_Flag {
GP_BRUSH_STABILIZE_MOUSE = (1 << 8),
/* lazy mouse override (internal only) */
GP_BRUSH_STABILIZE_MOUSE_TEMP = (1 << 9),
/* default eraser brush for quick switch */
GP_BRUSH_DEFAULT_ERASER = (1 << 10),
/* deprecated, was default eraser brush for quick switch */
GP_BRUSH_DEPRECATED1 = (1 << 10),
/* settings group */
GP_BRUSH_GROUP_SETTINGS = (1 << 11),
/* Random settings group */
@ -165,41 +165,6 @@ typedef enum eGP_BrushMode {
GP_BRUSH_MODE_VERTEXCOLOR = 2,
} eGP_BrushMode;
/* BrushGpencilSettings default brush icons */
typedef enum eGP_BrushIcons {
GP_BRUSH_ICON_PENCIL = 1,
GP_BRUSH_ICON_PEN = 2,
GP_BRUSH_ICON_INK = 3,
GP_BRUSH_ICON_INKNOISE = 4,
GP_BRUSH_ICON_BLOCK = 5,
GP_BRUSH_ICON_MARKER = 6,
GP_BRUSH_ICON_FILL = 7,
GP_BRUSH_ICON_ERASE_SOFT = 8,
GP_BRUSH_ICON_ERASE_HARD = 9,
GP_BRUSH_ICON_ERASE_STROKE = 10,
GP_BRUSH_ICON_AIRBRUSH = 11,
GP_BRUSH_ICON_CHISEL = 12,
GP_BRUSH_ICON_TINT = 13,
GP_BRUSH_ICON_VERTEX_DRAW = 14,
GP_BRUSH_ICON_VERTEX_BLUR = 15,
GP_BRUSH_ICON_VERTEX_AVERAGE = 16,
GP_BRUSH_ICON_VERTEX_SMEAR = 17,
GP_BRUSH_ICON_VERTEX_REPLACE = 18,
GP_BRUSH_ICON_GPBRUSH_SMOOTH = 19,
GP_BRUSH_ICON_GPBRUSH_THICKNESS = 20,
GP_BRUSH_ICON_GPBRUSH_STRENGTH = 21,
GP_BRUSH_ICON_GPBRUSH_RANDOMIZE = 22,
GP_BRUSH_ICON_GPBRUSH_GRAB = 23,
GP_BRUSH_ICON_GPBRUSH_PUSH = 24,
GP_BRUSH_ICON_GPBRUSH_TWIST = 25,
GP_BRUSH_ICON_GPBRUSH_PINCH = 26,
GP_BRUSH_ICON_GPBRUSH_CLONE = 27,
GP_BRUSH_ICON_GPBRUSH_WEIGHT = 28,
GP_BRUSH_ICON_GPBRUSH_BLUR = 29,
GP_BRUSH_ICON_GPBRUSH_AVERAGE = 30,
GP_BRUSH_ICON_GPBRUSH_SMEAR = 31,
} eGP_BrushIcons;
typedef enum eBrushCurvePreset {
BRUSH_CURVE_CUSTOM = 0,
BRUSH_CURVE_SMOOTH = 1,

View File

@ -59,7 +59,7 @@ typedef struct BrushGpencilSettings {
char _pad2[2];
/* Type of caps: eGPDstroke_Caps. */
int8_t caps_type;
char _pad[5];
char _pad[1];
int flag2;
@ -69,8 +69,6 @@ typedef struct BrushGpencilSettings {
int fill_draw_mode;
/** Type of gap filling extension to use. */
int fill_extend_mode;
/** Icon identifier. */
int icon_id;
/** Maximum distance before generate new point for very fast mouse movements. */
int input_samples;

View File

@ -915,27 +915,29 @@ typedef struct TimeMarker {
typedef struct Paint_Runtime {
/** Avoid having to compare with scene pointer everywhere. */
unsigned int tool_offset;
unsigned int initialized;
unsigned short ob_mode;
char _pad[2];
} Paint_Runtime;
/** We might want to store other things here. */
typedef struct PaintToolSlot {
struct Brush *brush;
} PaintToolSlot;
/** Paint Tool Base. */
typedef struct Paint {
/**
* The active brush. Possibly null. Possibly stored in a separate #Main data-base and not user-
* counted.
*/
struct Brush *brush;
/**
* Each tool has its own active brush,
* The currently active tool is defined by the current 'brush'.
* A weak asset reference to the #brush, if not NULL.
* Used to attempt restoring the active brush from the AssetLibrary system, typically on
* file load.
*/
struct PaintToolSlot *tool_slots;
int tool_slots_len;
char _pad1[4];
struct AssetWeakReference *brush_asset_reference;
/** Default eraser brush and associated weak reference. */
struct Brush *eraser_brush;
struct AssetWeakReference *eraser_brush_asset_reference;
struct Palette *palette;
/** Cavity curve. */

View File

@ -399,50 +399,6 @@ static EnumPropertyItem rna_enum_gpencil_brush_modes_items[] = {
{GP_BRUSH_MODE_VERTEXCOLOR, "VERTEXCOLOR", 0, "Vertex Color", "Use always Vertex Color mode"},
{0, nullptr, 0, nullptr, nullptr}};
static EnumPropertyItem rna_enum_gpencil_brush_paint_icons_items[] = {
{GP_BRUSH_ICON_PENCIL, "PENCIL", ICON_GPBRUSH_PENCIL, "Pencil", ""},
{GP_BRUSH_ICON_PEN, "PEN", ICON_GPBRUSH_PEN, "Pen", ""},
{GP_BRUSH_ICON_INK, "INK", ICON_GPBRUSH_INK, "Ink", ""},
{GP_BRUSH_ICON_INKNOISE, "INKNOISE", ICON_GPBRUSH_INKNOISE, "Ink Noise", ""},
{GP_BRUSH_ICON_BLOCK, "BLOCK", ICON_GPBRUSH_BLOCK, "Block", ""},
{GP_BRUSH_ICON_MARKER, "MARKER", ICON_GPBRUSH_MARKER, "Marker", ""},
{GP_BRUSH_ICON_AIRBRUSH, "AIRBRUSH", ICON_GPBRUSH_AIRBRUSH, "Airbrush", ""},
{GP_BRUSH_ICON_CHISEL, "CHISEL", ICON_GPBRUSH_CHISEL, "Chisel", ""},
{GP_BRUSH_ICON_FILL, "FILL", ICON_GPBRUSH_FILL, "Fill", ""},
{GP_BRUSH_ICON_ERASE_SOFT, "SOFT", ICON_GPBRUSH_ERASE_SOFT, "Eraser Soft", ""},
{GP_BRUSH_ICON_ERASE_HARD, "HARD", ICON_GPBRUSH_ERASE_HARD, "Eraser Hard", ""},
{GP_BRUSH_ICON_ERASE_STROKE, "STROKE", ICON_GPBRUSH_ERASE_STROKE, "Eraser Stroke", ""},
{0, nullptr, 0, nullptr, nullptr},
};
static EnumPropertyItem rna_enum_gpencil_brush_sculpt_icons_items[] = {
{GP_BRUSH_ICON_GPBRUSH_SMOOTH, "SMOOTH", ICON_GPBRUSH_SMOOTH, "Smooth", ""},
{GP_BRUSH_ICON_GPBRUSH_THICKNESS, "THICKNESS", ICON_GPBRUSH_THICKNESS, "Thickness", ""},
{GP_BRUSH_ICON_GPBRUSH_STRENGTH, "STRENGTH", ICON_GPBRUSH_STRENGTH, "Strength", ""},
{GP_BRUSH_ICON_GPBRUSH_RANDOMIZE, "RANDOMIZE", ICON_GPBRUSH_RANDOMIZE, "Randomize", ""},
{GP_BRUSH_ICON_GPBRUSH_GRAB, "GRAB", ICON_GPBRUSH_GRAB, "Grab", ""},
{GP_BRUSH_ICON_GPBRUSH_PUSH, "PUSH", ICON_GPBRUSH_PUSH, "Push", ""},
{GP_BRUSH_ICON_GPBRUSH_TWIST, "TWIST", ICON_GPBRUSH_TWIST, "Twist", ""},
{GP_BRUSH_ICON_GPBRUSH_PINCH, "PINCH", ICON_GPBRUSH_PINCH, "Pinch", ""},
{GP_BRUSH_ICON_GPBRUSH_CLONE, "CLONE", ICON_GPBRUSH_CLONE, "Clone", ""},
{0, nullptr, 0, nullptr, nullptr},
};
static EnumPropertyItem rna_enum_gpencil_brush_weight_icons_items[] = {
{GP_BRUSH_ICON_GPBRUSH_WEIGHT, "DRAW", ICON_GPBRUSH_WEIGHT, "Draw", ""},
{GP_BRUSH_ICON_GPBRUSH_BLUR, "BLUR", ICON_BRUSH_BLUR, "Blur", ""},
{GP_BRUSH_ICON_GPBRUSH_AVERAGE, "AVERAGE", ICON_BRUSH_BLUR, "Average", ""},
{GP_BRUSH_ICON_GPBRUSH_SMEAR, "SMEAR", ICON_BRUSH_BLUR, "Smear", ""},
{0, nullptr, 0, nullptr, nullptr},
};
static EnumPropertyItem rna_enum_gpencil_brush_vertex_icons_items[] = {
{GP_BRUSH_ICON_VERTEX_DRAW, "DRAW", ICON_BRUSH_MIX, "Draw", ""},
{GP_BRUSH_ICON_VERTEX_BLUR, "BLUR", ICON_BRUSH_BLUR, "Blur", ""},
{GP_BRUSH_ICON_VERTEX_AVERAGE, "AVERAGE", ICON_BRUSH_BLUR, "Average", ""},
{GP_BRUSH_ICON_VERTEX_SMEAR, "SMEAR", ICON_BRUSH_BLUR, "Smear", ""},
{GP_BRUSH_ICON_VERTEX_REPLACE, "REPLACE", ICON_BRUSH_MIX, "Replace", ""},
{0, nullptr, 0, nullptr, nullptr},
};
#endif
#ifdef RNA_RUNTIME
@ -1081,26 +1037,6 @@ static std::optional<std::string> rna_BrushGpencilSettings_path(const PointerRNA
return "gpencil_settings";
}
static void rna_BrushGpencilSettings_default_eraser_update(Main *bmain,
Scene *scene,
PointerRNA * /*ptr*/)
{
ToolSettings *ts = scene->toolsettings;
Paint *paint = &ts->gp_paint->paint;
Brush *brush_cur = BKE_paint_brush(paint);
/* disable default eraser in all brushes */
for (Brush *brush = static_cast<Brush *>(bmain->brushes.first); brush;
brush = static_cast<Brush *>(brush->id.next))
{
if ((brush != brush_cur) && (brush->ob_mode == OB_MODE_PAINT_GPENCIL_LEGACY) &&
(brush->gpencil_tool == GPAINT_TOOL_ERASE))
{
brush->gpencil_settings->flag &= ~GP_BRUSH_DEFAULT_ERASER;
}
}
}
static void rna_BrushGpencilSettings_use_material_pin_update(bContext *C, PointerRNA *ptr)
{
const Scene *scene = CTX_data_scene(C);
@ -1121,33 +1057,6 @@ static void rna_BrushGpencilSettings_use_material_pin_update(bContext *C, Pointe
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_PROPERTIES, nullptr);
}
static void rna_BrushGpencilSettings_eraser_mode_update(Main * /*bmain*/,
Scene *scene,
PointerRNA * /*ptr*/)
{
ToolSettings *ts = scene->toolsettings;
Paint *paint = &ts->gp_paint->paint;
Brush *brush = BKE_paint_brush(paint);
/* set eraser icon */
if ((brush) && (brush->gpencil_tool == GPAINT_TOOL_ERASE)) {
switch (brush->gpencil_settings->eraser_mode) {
case GP_BRUSH_ERASER_SOFT:
brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_SOFT;
break;
case GP_BRUSH_ERASER_HARD:
brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_HARD;
break;
case GP_BRUSH_ERASER_STROKE:
brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_STROKE;
break;
default:
brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_SOFT;
break;
}
}
}
static bool rna_BrushGpencilSettings_material_poll(PointerRNA * /*ptr*/, PointerRNA value)
{
Material *ma = (Material *)value.data;
@ -1730,32 +1639,6 @@ static void rna_def_gpencil_options(BlenderRNA *brna)
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, nullptr);
/* brush standard icon */
prop = RNA_def_property(srna, "gpencil_paint_icon", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, nullptr, "icon_id");
RNA_def_property_enum_items(prop, rna_enum_gpencil_brush_paint_icons_items);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_ui_text(prop, "Grease Pencil Icon", "");
prop = RNA_def_property(srna, "gpencil_sculpt_icon", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, nullptr, "icon_id");
RNA_def_property_enum_items(prop, rna_enum_gpencil_brush_sculpt_icons_items);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_GPENCIL);
RNA_def_property_ui_text(prop, "Grease Pencil Icon", "");
prop = RNA_def_property(srna, "gpencil_weight_icon", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, nullptr, "icon_id");
RNA_def_property_enum_items(prop, rna_enum_gpencil_brush_weight_icons_items);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_ui_text(prop, "Grease Pencil Icon", "");
prop = RNA_def_property(srna, "gpencil_vertex_icon", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, nullptr, "icon_id");
RNA_def_property_enum_items(prop, rna_enum_gpencil_brush_vertex_icons_items);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_ui_text(prop, "Grease Pencil Icon", "");
/* Mode type. */
prop = RNA_def_property(srna, "vertex_mode", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, nullptr, "vertex_mode");
@ -1952,8 +1835,7 @@ static void rna_def_gpencil_options(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Mode", "Eraser Mode");
RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_GPENCIL);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(
prop, NC_GPENCIL | ND_DATA, "rna_BrushGpencilSettings_eraser_mode_update");
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, nullptr);
prop = RNA_def_property(srna, "caps_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, nullptr, "caps_type");
@ -2088,16 +1970,6 @@ static void rna_def_gpencil_options(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Limit to Viewport", "Fill only visible areas in viewport");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
prop = RNA_def_property(srna, "use_default_eraser", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", GP_BRUSH_DEFAULT_ERASER);
RNA_def_property_boolean_default(prop, true);
RNA_def_property_ui_icon(prop, ICON_UNPINNED, 1);
RNA_def_property_ui_text(
prop, "Default Eraser", "Use this brush when enable eraser with fast switch key");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(
prop, NC_GPENCIL | ND_DATA, "rna_BrushGpencilSettings_default_eraser_update");
prop = RNA_def_property(srna, "use_settings_postprocess", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", GP_BRUSH_GROUP_SETTINGS);
RNA_def_property_ui_text(

View File

@ -271,103 +271,56 @@ static std::optional<std::string> rna_ParticleEdit_path(const PointerRNA * /*ptr
return "tool_settings.particle_edit";
}
static bool rna_Brush_mode_poll(PointerRNA *ptr, PointerRNA value)
static PointerRNA rna_Paint_brush_get(PointerRNA *ptr)
{
Paint *paint = static_cast<Paint *>(ptr->data);
Brush *brush = BKE_paint_brush(paint);
if (!brush) {
return PointerRNA_NULL;
}
return RNA_id_pointer_create(&brush->id);
}
static void rna_Paint_brush_set(PointerRNA *ptr, PointerRNA value, ReportList * /*reports*/)
{
Paint *paint = static_cast<Paint *>(ptr->data);
Brush *brush = static_cast<Brush *>(value.data);
BKE_paint_brush_set(paint, brush);
BKE_paint_invalidate_overlay_all();
}
static bool rna_Paint_brush_poll(PointerRNA *ptr, PointerRNA value)
{
const Paint *paint = static_cast<Paint *>(ptr->data);
Brush *brush = (Brush *)value.owner_id;
const uint tool_offset = paint->runtime.tool_offset;
const eObjectMode ob_mode = eObjectMode(paint->runtime.ob_mode);
UNUSED_VARS_NDEBUG(tool_offset);
BLI_assert(tool_offset && ob_mode);
const Brush *brush = static_cast<Brush *>(value.data);
if (brush->ob_mode & ob_mode) {
if (paint->brush) {
if (BKE_brush_tool_get(paint->brush, paint) == BKE_brush_tool_get(brush, paint)) {
return true;
}
}
else {
return true;
}
return (brush == nullptr) || (paint->runtime.ob_mode & brush->ob_mode) != 0;
}
return false;
}
static bool paint_contains_brush_slot(const Paint *paint, const PaintToolSlot *tslot, int *r_index)
static PointerRNA rna_Paint_eraser_brush_get(PointerRNA *ptr)
{
if ((tslot >= paint->tool_slots) && (tslot < (paint->tool_slots + paint->tool_slots_len))) {
*r_index = int(tslot - paint->tool_slots);
return true;
Paint *paint = static_cast<Paint *>(ptr->data);
Brush *brush = BKE_paint_eraser_brush(paint);
if (!brush) {
return PointerRNA_NULL;
}
return false;
return RNA_id_pointer_create(&brush->id);
}
static bool rna_Brush_mode_with_tool_poll(PointerRNA *ptr, PointerRNA value)
static void rna_Paint_eraser_brush_set(PointerRNA *ptr, PointerRNA value, ReportList * /*reports*/)
{
Scene *scene = (Scene *)ptr->owner_id;
const PaintToolSlot *tslot = static_cast<PaintToolSlot *>(ptr->data);
ToolSettings *ts = scene->toolsettings;
Brush *brush = (Brush *)value.owner_id;
int mode = 0;
int slot_index = 0;
if (paint_contains_brush_slot(&ts->imapaint.paint, tslot, &slot_index)) {
if (slot_index != brush->imagepaint_tool) {
return false;
}
mode = OB_MODE_TEXTURE_PAINT;
}
else if (paint_contains_brush_slot(&ts->sculpt->paint, tslot, &slot_index)) {
if (slot_index != brush->sculpt_tool) {
return false;
}
mode = OB_MODE_SCULPT;
}
else if (paint_contains_brush_slot(&ts->vpaint->paint, tslot, &slot_index)) {
if (slot_index != brush->vertexpaint_tool) {
return false;
}
mode = OB_MODE_VERTEX_PAINT;
}
else if (paint_contains_brush_slot(&ts->wpaint->paint, tslot, &slot_index)) {
if (slot_index != brush->weightpaint_tool) {
return false;
}
mode = OB_MODE_WEIGHT_PAINT;
}
else if (paint_contains_brush_slot(&ts->gp_paint->paint, tslot, &slot_index)) {
if (slot_index != brush->gpencil_tool) {
return false;
}
mode = OB_MODE_PAINT_GPENCIL_LEGACY;
}
else if (paint_contains_brush_slot(&ts->gp_vertexpaint->paint, tslot, &slot_index)) {
if (slot_index != brush->gpencil_vertex_tool) {
return false;
}
mode = OB_MODE_VERTEX_GPENCIL_LEGACY;
}
else if (paint_contains_brush_slot(&ts->gp_sculptpaint->paint, tslot, &slot_index)) {
if (slot_index != brush->gpencil_sculpt_tool) {
return false;
}
mode = OB_MODE_SCULPT_GPENCIL_LEGACY;
}
else if (paint_contains_brush_slot(&ts->gp_weightpaint->paint, tslot, &slot_index)) {
if (slot_index != brush->gpencil_weight_tool) {
return false;
}
mode = OB_MODE_WEIGHT_GPENCIL_LEGACY;
}
else if (paint_contains_brush_slot(&ts->curves_sculpt->paint, tslot, &slot_index)) {
if (slot_index != brush->curves_sculpt_tool) {
return false;
}
mode = OB_MODE_SCULPT_CURVES;
Paint *paint = static_cast<Paint *>(ptr->data);
Brush *brush = static_cast<Brush *>(value.data);
BKE_paint_eraser_brush_set(paint, brush);
BKE_paint_invalidate_overlay_all();
}
return brush->ob_mode & mode;
static bool rna_Paint_eraser_brush_poll(PointerRNA *ptr, PointerRNA value)
{
const Paint *paint = static_cast<Paint *>(ptr->data);
const Brush *brush = static_cast<Brush *>(value.data);
return (brush == nullptr) || (paint->runtime.ob_mode & brush->ob_mode) != 0;
}
static void rna_Sculpt_update(bContext *C, PointerRNA * /*ptr*/)
@ -443,16 +396,6 @@ static std::optional<std::string> rna_ParticleBrush_path(const PointerRNA * /*pt
return "tool_settings.particle_edit.brush";
}
static void rna_Paint_brush_update(Main * /*bmain*/, Scene * /*scene*/, PointerRNA *ptr)
{
Paint *paint = static_cast<Paint *>(ptr->data);
Brush *br = paint->brush;
BKE_paint_invalidate_overlay_all();
/* Needed because we're not calling 'BKE_paint_brush_set' which handles this. */
BKE_paint_toolslots_brush_update(paint);
WM_main_add_notifier(NC_BRUSH | NA_SELECTED, br);
}
static void rna_ImaPaint_viewport_update(Main * /*bmain*/, Scene * /*scene*/, PointerRNA * /*ptr*/)
{
/* not the best solution maybe, but will refresh the 3D viewport */
@ -600,20 +543,6 @@ static void rna_def_paint_curve(BlenderRNA *brna)
RNA_def_struct_ui_icon(srna, ICON_CURVE_BEZCURVE);
}
static void rna_def_paint_tool_slot(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "PaintToolSlot", nullptr);
RNA_def_struct_ui_text(srna, "Paint Tool Slot", "");
prop = RNA_def_property(srna, "brush", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_pointer_funcs(prop, nullptr, nullptr, nullptr, "rna_Brush_mode_with_tool_poll");
RNA_def_property_ui_text(prop, "Brush", "");
}
static void rna_def_paint(BlenderRNA *brna)
{
StructRNA *srna;
@ -625,25 +554,38 @@ static void rna_def_paint(BlenderRNA *brna)
/* Global Settings */
prop = RNA_def_property(srna, "brush", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_pointer_funcs(prop, nullptr, nullptr, nullptr, "rna_Brush_mode_poll");
RNA_def_property_ui_text(prop, "Brush", "Active Brush");
RNA_def_property_update(prop, 0, "rna_Paint_brush_update");
RNA_def_property_struct_type(prop, "Brush");
RNA_def_property_pointer_funcs(
prop, "rna_Paint_brush_get", "rna_Paint_brush_set", nullptr, "rna_Paint_brush_poll");
RNA_def_property_ui_text(prop, "Brush", "Active brush");
RNA_def_property_update(prop, NC_BRUSH | NA_SELECTED, nullptr);
/* paint_tool_slots */
prop = RNA_def_property(srna, "tool_slots", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, nullptr, "tool_slots", "tool_slots_len");
RNA_def_property_struct_type(prop, "PaintToolSlot");
/* don't dereference pointer! */
RNA_def_property_collection_funcs(prop,
prop = RNA_def_property(srna, "brush_asset_reference", PROP_POINTER, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop,
"Brush Asset Reference",
"A weak reference to the matching brush asset, used e.g. to restore "
"the last used brush on file load");
prop = RNA_def_property(srna, "eraser_brush", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_struct_type(prop, "Brush");
RNA_def_property_pointer_funcs(prop,
"rna_Paint_eraser_brush_get",
"rna_Paint_eraser_brush_set",
nullptr,
nullptr,
nullptr,
"rna_iterator_array_get",
nullptr,
nullptr,
nullptr,
nullptr);
RNA_def_property_ui_text(prop, "Paint Tool Slots", "");
"rna_Paint_eraser_brush_poll");
RNA_def_property_ui_text(prop,
"Default Eraser Brush",
"Default eraser bnush for quickly alternating with the main brush");
RNA_def_property_update(prop, NC_BRUSH | NA_SELECTED, nullptr);
prop = RNA_def_property(srna, "eraser_brush_asset_reference", PROP_POINTER, PROP_NONE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop,
"Eraser Brush Asset Reference",
"A weak reference to the matching brush asset, used e.g. to restore "
"the last used brush on file load");
prop = RNA_def_property(srna, "palette", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_EDITABLE);
@ -1706,7 +1648,6 @@ void RNA_def_sculpt_paint(BlenderRNA *brna)
/* *** Non-Animated *** */
RNA_define_animate_sdna(false);
rna_def_paint_curve(brna);
rna_def_paint_tool_slot(brna);
rna_def_paint(brna);
rna_def_sculpt(brna);
rna_def_uv_sculpt(brna);

View File

@ -974,7 +974,7 @@ void rna_uiTemplateAssetShelfPopover(uiLayout *layout,
icon = icon_value;
}
blender::ui::template_asset_shelf_popover(*layout, *C, asset_shelf_id, name, icon);
blender::ui::template_asset_shelf_popover(*layout, *C, asset_shelf_id, name ? name : "", icon);
}
#else

View File

@ -3639,7 +3639,7 @@ static int wm_save_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *
}
if (blendfile_path[0] != '\0') {
if (CTX_data_main(C)->has_forward_compatibility_issues) {
if (BKE_main_needs_overwrite_confirm(CTX_data_main(C))) {
wm_save_file_overwrite_dialog(C, op);
ret = OPERATOR_INTERFACE;
}
@ -4001,6 +4001,7 @@ static void file_overwrite_detailed_info_show(uiLayout *parent_layout, Main *bma
* block. */
uiLayoutSetScaleY(layout, 0.70f);
if (bmain->has_forward_compatibility_issues) {
char writer_ver_str[16];
char current_ver_str[16];
if (bmain->versionfile == BLENDER_VERSION) {
@ -4028,6 +4029,17 @@ static void file_overwrite_detailed_info_show(uiLayout *parent_layout, Main *bma
uiItemL(layout, message_line2, ICON_NONE);
}
if (bmain->is_asset_repository) {
if (bmain->has_forward_compatibility_issues) {
uiItemS_ex(layout, 1.4f);
}
uiItemL(layout, RPT_("This file is managed by the Blender asset system."), ICON_NONE);
uiItemL(layout, RPT_("and is expected to contain a single asset data-block."), ICON_NONE);
uiItemL(layout, RPT_("Take care to avoid data loss when editing assets."), ICON_NONE);
}
}
static void save_file_overwrite_cancel(bContext *C, void *arg_block, void * /*arg_data*/)
{
wmWindow *win = CTX_wm_window(C);
@ -4090,7 +4102,12 @@ static void save_file_overwrite_saveas(bContext *C, void *arg_block, void * /*ar
wmWindow *win = CTX_wm_window(C);
UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
WM_operator_name_call(C, "WM_OT_save_as_mainfile", WM_OP_INVOKE_DEFAULT, nullptr, nullptr);
PointerRNA props_ptr;
wmOperatorType *ot = WM_operatortype_find("WM_OT_save_as_mainfile", false);
WM_operator_properties_create_ptr(&props_ptr, ot);
WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr, nullptr);
WM_operator_properties_free(&props_ptr);
}
static void save_file_overwrite_saveas_button(uiBlock *block, wmGenericCallback *post_action)
@ -4126,8 +4143,27 @@ static uiBlock *block_create_save_file_overwrite_dialog(bContext *C, ARegion *re
uiLayout *layout = uiItemsAlertBox(block, 34, ALERT_ICON_WARNING);
/* Title. */
if (bmain->has_forward_compatibility_issues) {
if (bmain->is_asset_repository) {
uiItemL_ex(
layout,
RPT_("Convert asset blend file to regular blend file with an older Blender version?"),
ICON_NONE,
true,
false);
}
else {
uiItemL_ex(
layout, RPT_("Overwrite file with an older Blender version?"), ICON_NONE, true, false);
}
}
else if (bmain->is_asset_repository) {
uiItemL_ex(
layout, RPT_("Convert asset blend file to regular blend file?"), ICON_NONE, true, false);
}
else {
BLI_assert_unreachable();
}
/* Filename. */
const char *blendfile_path = BKE_main_blendfile_path(CTX_data_main(C));
@ -4339,7 +4375,7 @@ static uiBlock *block_create__close_file_dialog(bContext *C, ARegion *region, vo
uiLayout *layout = uiItemsAlertBox(block, 34, ALERT_ICON_QUESTION);
const bool needs_overwrite_confirm = bmain->has_forward_compatibility_issues;
const bool needs_overwrite_confirm = BKE_main_needs_overwrite_confirm(bmain);
/* Title. */
uiItemL_ex(layout, RPT_("Save changes before closing?"), ICON_NONE, true, false);

View File

@ -210,7 +210,6 @@ wmKeyMap *WM_keymap_guess_from_context(const bContext *C)
wmKeyMap *WM_keymap_guess_opname(const bContext *C, const char *opname)
{
/* Op types purposely skipped for now:
* BRUSH_OT
* BOID_OT
* BUTTONS_OT
* CONSTRAINT_OT
@ -226,7 +225,7 @@ wmKeyMap *WM_keymap_guess_opname(const bContext *C, const char *opname)
/* Window. */
if (STRPREFIX(opname, "WM_OT") || STRPREFIX(opname, "ED_OT_undo")) {
if (STREQ(opname, "WM_OT_tool_set_by_id")) {
if (STREQ(opname, "WM_OT_tool_set_by_id") || STREQ(opname, "WM_OT_call_asset_shelf_popover")) {
km = WM_keymap_guess_from_context(C);
}
@ -336,7 +335,7 @@ wmKeyMap *WM_keymap_guess_opname(const bContext *C, const char *opname)
km = WM_keymap_find_all(
wm, "Paint Face Mask (Weight, Vertex, Texture)", SPACE_EMPTY, RGN_TYPE_WINDOW);
}
else if (STRPREFIX(opname, "PAINT_OT")) {
else if (STRPREFIX(opname, "PAINT_OT") || STRPREFIX(opname, "BRUSH_OT")) {
/* Check for relevant mode. */
switch (CTX_data_mode_enum(C)) {
case CTX_MODE_PAINT_WEIGHT:

View File

@ -2212,6 +2212,39 @@ static void WM_OT_call_panel(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
static int asset_shelf_popover_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
{
char *asset_shelf_id = RNA_string_get_alloc(op->ptr, "name", nullptr, 0, nullptr);
BLI_SCOPED_DEFER([&]() { MEM_freeN(asset_shelf_id); });
if (!blender::ui::asset_shelf_popover_invoke(*C, asset_shelf_id, *op->reports)) {
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
}
return OPERATOR_INTERFACE;
}
/* Needs to be defined at WM level to be globally accessible. */
static void WM_OT_call_asset_shelf_popover(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Call Asset Shelf Popover";
ot->idname = "WM_OT_call_asset_shelf_popover";
ot->description = "Open a predefined asset shelf in a popup";
/* api callbacks */
ot->invoke = asset_shelf_popover_invoke;
ot->flag = OPTYPE_INTERNAL;
RNA_def_string(ot->srna,
"name",
nullptr,
0,
"Asset Shelf Name",
"Identifier of the asset shelf to display");
}
/** \} */
/* -------------------------------------------------------------------- */
@ -4065,6 +4098,7 @@ void wm_operatortypes_register()
WM_operatortype_append(WM_OT_call_menu);
WM_operatortype_append(WM_OT_call_menu_pie);
WM_operatortype_append(WM_OT_call_panel);
WM_operatortype_append(WM_OT_call_asset_shelf_popover);
WM_operatortype_append(WM_OT_radial_control);
WM_operatortype_append(WM_OT_stereo3d_set);
#if defined(WIN32)

View File

@ -174,43 +174,6 @@ static void toolsystem_ref_link(bContext *C, WorkSpace *workspace, bToolRef *tre
}
}
}
else {
const PaintMode paint_mode = BKE_paintmode_get_from_tool(tref);
BLI_assert(paint_mode != PaintMode::Invalid);
const EnumPropertyItem *items = BKE_paint_get_tool_enum_from_paintmode(paint_mode);
BLI_assert(items != nullptr);
const int i = items ? RNA_enum_from_identifier(items, tref_rt->data_block) : -1;
if (i != -1) {
const int slot_index = items[i].value;
wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
if (workspace == WM_window_get_active_workspace(win)) {
Scene *scene = WM_window_get_active_scene(win);
BKE_paint_ensure_from_paintmode(bmain, scene, paint_mode);
Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode);
Brush *brush = BKE_paint_toolslots_brush_get(paint, slot_index);
if (brush == nullptr) {
/* Could make into a function. */
brush = (Brush *)BKE_libblock_find_name(bmain, ID_BR, items[i].name);
if (brush && slot_index == BKE_brush_tool_get(brush, paint)) {
/* Pass. */
}
else {
brush = BKE_brush_add(bmain, items[i].name, eObjectMode(paint->runtime.ob_mode));
BKE_brush_tool_set(brush, paint, slot_index);
if (paint_mode == PaintMode::Sculpt) {
BKE_brush_sculpt_reset(brush);
}
}
}
BKE_paint_brush_set(paint, brush);
}
}
}
}
}
}
@ -383,25 +346,6 @@ void WM_toolsystem_ref_sync_from_context(Main *bmain, WorkSpace *workspace, bToo
}
}
}
else {
const PaintMode paint_mode = BKE_paintmode_get_from_tool(tref);
Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode);
const EnumPropertyItem *items = BKE_paint_get_tool_enum_from_paintmode(paint_mode);
if (paint && paint->brush && items) {
const ID *brush = (ID *)paint->brush;
const char tool_type = BKE_brush_tool_get((Brush *)brush, paint);
const int i = RNA_enum_from_value(items, tool_type);
/* Possible when loading files from the future. */
if (i != -1) {
const char *name = items[i].name;
const char *identifier = items[i].identifier;
if (!STREQ(tref_rt->data_block, identifier)) {
STRNCPY(tref_rt->data_block, identifier);
SNPRINTF(tref->idname, "builtin_brush.%s", name);
}
}
}
}
}
}
@ -711,26 +655,19 @@ static const char *toolsystem_default_tool(const bToolKey *tkey)
switch (tkey->space_type) {
case SPACE_VIEW3D:
switch (tkey->mode) {
/* Use the names of the enums for each brush tool. */
case CTX_MODE_SCULPT:
case CTX_MODE_PAINT_VERTEX:
case CTX_MODE_PAINT_WEIGHT:
case CTX_MODE_PAINT_TEXTURE:
case CTX_MODE_PAINT_GPENCIL_LEGACY:
case CTX_MODE_PAINT_GREASE_PENCIL:
return "builtin_brush.Draw";
case CTX_MODE_SCULPT_GPENCIL_LEGACY:
case CTX_MODE_SCULPT_GREASE_PENCIL:
return "builtin_brush.Push";
case CTX_MODE_WEIGHT_GPENCIL_LEGACY:
case CTX_MODE_WEIGHT_GREASE_PENCIL:
return "builtin_brush.Weight";
case CTX_MODE_VERTEX_GPENCIL_LEGACY:
return "builtin_brush.Draw";
case CTX_MODE_SCULPT_CURVES:
return "builtin_brush.Density";
/* End temporary hack. */
return "builtin.brush";
case CTX_MODE_PARTICLE:
return "builtin_brush.Comb";
case CTX_MODE_EDIT_TEXT:
@ -740,7 +677,7 @@ static const char *toolsystem_default_tool(const bToolKey *tkey)
case SPACE_IMAGE:
switch (tkey->mode) {
case SI_MODE_PAINT:
return "builtin_brush.Draw";
return "builtin.brush";
}
break;
case SPACE_NODE: {

View File

@ -1701,6 +1701,12 @@ install(
set(ASSET_BUNDLE_DIR ${CMAKE_SOURCE_DIR}/release/datafiles/assets/publish/)
# TODO temporary change for development only. Remove before merging.
set(ASSET_BUNDLE_TESTING_DIR "${ASSET_BUNDLE_DIR}/../testing/")
if(EXISTS "${ASSET_BUNDLE_TESTING_DIR}")
set(ASSET_BUNDLE_DIR "${ASSET_BUNDLE_TESTING_DIR}")
endif()
if(EXISTS "${ASSET_BUNDLE_DIR}")
install(
DIRECTORY ${ASSET_BUNDLE_DIR}