WIP: Brush assets project #106303

Draft
Julian Eisel wants to merge 370 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.
81 changed files with 3147 additions and 3900 deletions

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

@ -5666,27 +5666,37 @@ 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")]}),
])
# 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 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,8 +1223,8 @@ class IMAGE_PT_paint_settings_advanced(Panel, ImagePaintPanel):
settings = context.tool_settings.image_paint
brush = settings.brush
brush_settings_advanced(layout.column(), context, brush, self.is_popover)
if brush:
brush_settings_advanced(layout.column(), context, brush, self.is_popover)
class IMAGE_PT_paint_color(Panel, ImagePaintPanel):
@ -1234,16 +1237,17 @@ 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
draw_color_settings(context, layout, brush, color_type=True)
if brush:
draw_color_settings(context, layout, brush, color_type=True)
class IMAGE_PT_paint_swatches(Panel, ImagePaintPanel, ColorPalettePanel):
@ -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",
cursor='PAINT_CROSS',
)
# 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',
)
@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',
)
@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 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:
@ -3509,9 +3568,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 39
#define BLENDER_FILE_SUBVERSION 40
/* 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,
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

@ -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. */
@ -247,6 +249,12 @@ void BKE_libblock_copy_in_lib(Main *bmain,
*/
void *BKE_libblock_copy(Main *bmain, const ID *id) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
/**
* For newly created IDs, move it into same library as owner ID.
* This assumes the ID is local.
*/
void BKE_id_move_to_same_lib(Main &bmain, ID &id, const ID &owner_id);
/**
* Sets the name of a block to name, suitably adjusted for uniqueness.
*/
@ -259,6 +267,11 @@ ID *BKE_libblock_find_name_and_library(Main *bmain,
short type,
const char *name,
const char *lib_name);
ID *BKE_libblock_find_name_and_library_filepath(Main *bmain,
short type,
const char *name,
const char *lib_filepath_abs);
/**
* Duplicate (a.k.a. deep copy) common processing options.
* See also eDupli_ID_Flags for options controlling what kind of IDs to duplicate.
@ -710,7 +723,7 @@ void BKE_id_tag_clear_atomic(ID *id, int tag);
/**
* Check that given ID pointer actually is in G_MAIN.
* Main intended use is for debug asserts in places we cannot easily get rid of #G_MAIN.
* Main intended use is for debug asserts in places we cannot easily get rid of #G_Main.
*/
bool BKE_id_is_in_global_main(ID *id);
@ -739,6 +752,13 @@ ID *BKE_id_owner_get(ID *id, const bool debug_relationship_assert = true);
*/
bool BKE_id_is_editable(const Main *bmain, const ID *id);
/**
* Check that a pointer from one ID to another is possible.
*
* Taking into account lib linking and main database membership.
*/
bool BKE_id_can_link(const ID &id_from, const ID &id_to);
/**
* Returns ordered list of data-blocks for display in the UI.
*/

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);
@ -253,19 +274,6 @@ void paint_update_brush_rake_rotation(UnifiedPaintSettings *ups, Brush *brush, f
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

@ -65,6 +65,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
@ -333,6 +333,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,446 @@
/* 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)) {
return false;
}
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;
}
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;
}
/* If weak reference resolves to a null library path, assume we are in local asset case. */
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

@ -306,6 +306,200 @@ static bool reuse_bmain_data_remapper_is_id_remapped(id::IDRemapper &remapper, I
return false;
}
static bool reuse_bmain_move_id(ReuseOldBMainData *reuse_data,
ID *id,
Library *lib,
const bool reuse_existing)
{
id::IDRemapper &remapper = reuse_bmain_data_remapper_ensure(reuse_data);
Main *new_bmain = reuse_data->new_bmain;
Main *old_bmain = reuse_data->old_bmain;
ListBase *new_lb = which_libbase(new_bmain, GS(id->name));
ListBase *old_lb = which_libbase(old_bmain, GS(id->name));
if (reuse_existing) {
/* A 'new' version of the same data may already exist in new_bmain, in the rare case
* that the same asset blend file was linked explicitly into the blend file we are loading.
* Don't move the old linked ID, but remap its usages to the new one instead. */
LISTBASE_FOREACH_BACKWARD (ID *, id_iter, new_lb) {
if (!ELEM(id_iter->lib, id->lib, lib)) {
continue;
}
if (!STREQ(id_iter->name + 2, id->name + 2)) {
continue;
}
remapper.add(id, id_iter);
return false;
}
}
/* If ID is already in the new_bmain, this should not have been called. */
BLI_assert(BLI_findindex(new_lb, id) < 0);
BLI_assert(BLI_findindex(old_lb, id) >= 0);
/* Move from one list to another, and ensure name is valid. */
BLI_remlink_safe(old_lb, id);
BKE_main_namemap_remove_name(old_bmain, id, id->name + 2);
id->lib = lib;
BLI_addtail(new_lb, id);
BKE_id_new_name_validate(new_bmain, new_lb, id, nullptr, true);
BKE_lib_libblock_session_uid_renew(id);
/* Remap to itself, to avoid re-processing this ID again. */
remapper.add(id, id);
return true;
}
static Library *reuse_bmain_data_dependencies_new_library_get(ReuseOldBMainData *reuse_data,
Library *old_lib)
{
id::IDRemapper &remapper = reuse_bmain_data_remapper_ensure(reuse_data);
Library *new_lib = old_lib;
IDRemapperApplyResult result = remapper.apply(reinterpret_cast<ID **>(&new_lib),
ID_REMAP_APPLY_DEFAULT);
switch (result) {
case ID_REMAP_RESULT_SOURCE_UNAVAILABLE: {
/* Move library to new bmain.
* There should be no filepath conflicts, as #reuse_bmain_data_remapper_ensure has
* already remapped existing libraries with matching filepath. */
reuse_bmain_move_id(reuse_data, &old_lib->id, nullptr, false);
return old_lib;
}
case ID_REMAP_RESULT_SOURCE_NOT_MAPPABLE: {
BLI_assert_unreachable();
return nullptr;
}
case ID_REMAP_RESULT_SOURCE_REMAPPED: {
/* Already in new bmain, only transfer flags. */
new_lib->runtime.tag |= old_lib->runtime.tag &
(LIBRARY_ASSET_EDITABLE | LIBRARY_ASSET_FILE_WRITABLE);
return new_lib;
}
case ID_REMAP_RESULT_SOURCE_UNASSIGNED: {
/* Happens when the library is the newly opened blend file. */
return nullptr;
}
}
BLI_assert_unreachable();
return nullptr;
}
static int reuse_editable_asset_bmain_data_dependencies_process_cb(
LibraryIDLinkCallbackData *cb_data)
{
ID *id = *cb_data->id_pointer;
if (id == nullptr) {
return IDWALK_RET_NOP;
}
ReuseOldBMainData *reuse_data = static_cast<ReuseOldBMainData *>(cb_data->user_data);
/* First check if it has already been remapped. */
id::IDRemapper &remapper = reuse_bmain_data_remapper_ensure(reuse_data);
if (reuse_bmain_data_remapper_is_id_remapped(remapper, id)) {
return IDWALK_RET_STOP_RECURSION;
}
if (id->lib == nullptr) {
/* There should be no links to local datablocks from linked editable data. */
remapper.add(id, nullptr);
BLI_assert_unreachable();
return IDWALK_RET_STOP_RECURSION;
}
/* Only preserve specific datablock types. */
if (!ID_TYPE_SUPPORTS_ASSET_EDITABLE(GS(id->name))) {
remapper.add(id, nullptr);
return IDWALK_RET_STOP_RECURSION;
}
/* There may be a new library pointer in new_bmain, matching a library in old_bmain, even
* though pointer values are not the same. So we need to check new linked IDs in new_bmain
* against both potential library pointers. */
Library *old_id_new_lib = reuse_bmain_data_dependencies_new_library_get(reuse_data, id->lib);
/* Happens when the library is the newly opened blend file. */
if (old_id_new_lib == nullptr) {
remapper.add(id, nullptr);
return IDWALK_RET_STOP_RECURSION;
}
/* Move to new main database. */
return reuse_bmain_move_id(reuse_data, id, old_id_new_lib, true) ? IDWALK_RET_STOP_RECURSION :
IDWALK_RET_NOP;
}
static bool reuse_editable_asset_needed(ReuseOldBMainData *reuse_data)
{
Main *old_bmain = reuse_data->old_bmain;
LISTBASE_FOREACH (Library *, lib, &old_bmain->libraries) {
if (lib->runtime.tag & LIBRARY_ASSET_EDITABLE) {
return true;
}
}
return false;
}
/**
* Selectively 'import' data from old Main into new Main, provided it does not conflict with data
* already present in the new Main (name-wise and library-wise).
*
* Dependencies from moved over old data are also imported into the new Main, (unless, in case of
* linked data, a matching linked ID is already available in new Main).
*
* When a conflict is found, usages of the conflicted ID by the old data are stored in the
* `remapper` of `ReuseOldBMainData` to be remapped to the matching data in the new Main later.
*
* NOTE: This function will never remove any original new data from the new Main, it only moves
* (some of) the old data to the new Main.
*/
static void reuse_editable_asset_bmain_data_for_blendfile(ReuseOldBMainData *reuse_data,
const short idcode)
{
Main *new_bmain = reuse_data->new_bmain;
Main *old_bmain = reuse_data->old_bmain;
id::IDRemapper &remapper = reuse_bmain_data_remapper_ensure(reuse_data);
ListBase *old_lb = which_libbase(old_bmain, idcode);
ID *old_id_iter;
FOREACH_MAIN_LISTBASE_ID_BEGIN (old_lb, old_id_iter) {
/* Keep any datablocks from libraries marked as LIBRARY_ASSET_EDITABLE. */
if (!((old_id_iter->lib && old_id_iter->lib->runtime.tag & LIBRARY_ASSET_EDITABLE))) {
continue;
}
Library *old_id_new_lib = reuse_bmain_data_dependencies_new_library_get(reuse_data,
old_id_iter->lib);
/* Happens when the library is the newly opened blend file. */
if (old_id_new_lib == nullptr) {
remapper.add(old_id_iter, nullptr);
continue;
}
if (reuse_bmain_move_id(reuse_data, old_id_iter, old_id_new_lib, true)) {
/* Port over dependencies of re-used ID, unless matching already existing ones in
* new_bmain can be found.
*
* NOTE : No pointers are remapped here, this code only moves dependencies from old_bmain
* to new_bmain if needed, and add necessary remapping rules to the reuse_data.remapper. */
BKE_library_foreach_ID_link(new_bmain,
old_id_iter,
reuse_editable_asset_bmain_data_dependencies_process_cb,
reuse_data,
IDWALK_RECURSE | IDWALK_DO_LIBRARY_POINTER);
}
}
FOREACH_MAIN_LISTBASE_ID_END;
}
/**
* Does a complete replacement of data in `new_bmain` by data from `old_bmain. Original new data
* are moved to the `old_bmain`, and will be freed together with it.
@ -313,8 +507,9 @@ static bool reuse_bmain_data_remapper_is_id_remapped(id::IDRemapper &remapper, I
* WARNING: Currently only expects to work on local data, won't work properly if some of the IDs of
* given type are linked.
*
* NOTE: There is no support at all for potential dependencies of the IDs moved around. This is not
* expected to be necessary for the current use cases (UI-related IDs).
* NOTE: Unlike with #reuse_editable_asset_bmain_data_for_blendfile, there is no support at all for
* potential dependencies of the IDs moved around. This is not expected to be necessary for the
* current use cases (UI-related IDs).
*/
static void swap_old_bmain_data_for_blendfile(ReuseOldBMainData *reuse_data, const short id_code)
{
@ -660,7 +855,6 @@ static void setup_app_data(bContext *C,
{
Main *bmain = G_MAIN;
const bool recover = (G.fileflags & G_FILE_RECOVER_READ) != 0;
const bool is_startup = params->is_startup;
enum {
LOAD_UI = 1,
LOAD_UI_OFF,
@ -675,6 +869,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) {
@ -728,6 +925,18 @@ static void setup_app_data(bContext *C,
}
BKE_main_idmap_destroy(reuse_data.id_map);
if (!params->is_factory_settings && reuse_editable_asset_needed(&reuse_data)) {
/* Keep linked brush asset data, similar to UI data. Only does a known
* subset know. Could do everything, but that risks dragging along more
* scene data than we want. */
for (short idtype_index = 0; idtype_index < INDEX_ID_MAX; idtype_index++) {
const IDTypeInfo *idtype_info = BKE_idtype_get_info_from_idtype_index(idtype_index);
if (ID_TYPE_SUPPORTS_ASSET_EDITABLE(idtype_info->id_code)) {
reuse_editable_asset_bmain_data_for_blendfile(&reuse_data, idtype_info->id_code);
}
}
}
}
/* Logic for 'track_undo_scene' is to keep using the scene which the active screen has, as long
@ -901,7 +1110,7 @@ static void setup_app_data(bContext *C,
bmain->recovered = false;
/* `startup.blend` or recovered startup. */
if (is_startup) {
if (params->is_startup) {
bmain->filepath[0] = '\0';
}
else if (recover) {
@ -1257,6 +1466,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;
}

View File

@ -1912,7 +1912,9 @@ void BKE_blendfile_library_relocate(BlendfileLinkAppendContext *lapp_context,
BKE_library_main_rebuild_hierarchy(bmain);
/* Resync overrides if needed. */
if (!USER_EXPERIMENTAL_TEST(&U, no_override_auto_resync)) {
if (!USER_EXPERIMENTAL_TEST(&U, no_override_auto_resync) &&
lapp_context->params->context.scene != nullptr)
{
BlendFileReadReport report{};
report.reports = reports;
BKE_lib_override_library_main_resync(bmain,

File diff suppressed because it is too large Load Diff

View File

@ -822,6 +822,21 @@ ID *BKE_id_copy_for_use_in_bmain(Main *bmain, const ID *id)
return newid;
}
void BKE_id_move_to_same_lib(Main &bmain, ID &id, const ID &owner_id)
{
BLI_assert(id.lib == nullptr);
if (owner_id.lib == nullptr) {
return;
}
id.lib = owner_id.lib;
id.tag |= LIB_TAG_INDIRECT;
BKE_main_namemap_remove_name(&bmain, &id, id.name + 2);
ListBase *lb = which_libbase(&bmain, GS(id.name));
BKE_id_new_name_validate(&bmain, lb, &id, id.name, true);
}
static void id_embedded_swap(ID **embedded_id_a,
ID **embedded_id_b,
const bool do_full_id,
@ -1557,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));
}
@ -1624,6 +1645,28 @@ ID *BKE_libblock_find_name_and_library(Main *bmain,
return nullptr;
}
ID *BKE_libblock_find_name_and_library_filepath(Main *bmain,
short type,
const char *name,
const char *lib_filepath_abs)
{
ListBase *lb = which_libbase(bmain, type);
BLI_assert(lb != nullptr);
LISTBASE_FOREACH (ID *, id, lb) {
if (!STREQ(id->name + 2, name)) {
continue;
}
if (id->lib == nullptr && lib_filepath_abs == nullptr) {
return id;
}
else if (id->lib && lib_filepath_abs && STREQ(id->lib->runtime.filepath_abs, lib_filepath_abs))
{
return id;
}
}
return nullptr;
}
void id_sort_by_name(ListBase *lb, ID *id, ID *id_sorting_hint)
{
#define ID_SORT_STEP_SIZE 512
@ -2151,7 +2194,7 @@ void BKE_libblock_rename(Main *bmain, ID *id, const char *name)
}
BKE_main_namemap_remove_name(bmain, id, id->name + 2);
ListBase *lb = which_libbase(bmain, GS(id->name));
if (BKE_id_new_name_validate(bmain, lb, id, name, false)) {
if (BKE_id_new_name_validate(bmain, lb, id, name, true)) {
bmain->is_memfile_undo_written = false;
}
}
@ -2246,6 +2289,20 @@ bool BKE_id_is_editable(const Main *bmain, const ID *id)
return ID_IS_EDITABLE(id) && !BKE_lib_override_library_is_system_defined(bmain, id);
}
bool BKE_id_can_link(const ID &id_from, const ID &id_to)
{
/* Can't link from linked to local. */
if (id_from.lib && !id_to.lib) {
return false;
}
/* Can't link from ID in main database to one outside of it. */
if (!(id_from.tag & LIB_TAG_NO_MAIN) && (id_to.tag & LIB_TAG_NO_MAIN)) {
return false;
}
return true;
}
/************************* Datablock order in UI **************************/
static int *id_order_get(ID *id)

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,338 @@ 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;
}
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;
}
const Brush *BKE_paint_brush_for_read(const Paint *p)
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);
}
}
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";
// TODO: should be soft, but missing from essentials still.
eraser_name = "Eraser Hard";
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_runtime_init(const ToolSettings *ts, Paint *paint)
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 +1310,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 +1334,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 +1384,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 +1398,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 +1411,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 +1451,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 +1469,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;
BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER_P(data,
brush_p,
do_undo_restore,
SCENE_FOREACH_UNDO_RESTORE,
reader,
&paint_old->tool_slots[i].brush,
IDWALK_CB_USER);
}
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,
eraser_brush_p,
do_undo_restore,
SCENE_FOREACH_UNDO_RESTORE,
reader,
&paint_old->eraser_brush,
IDWALK_CB_NOP);
Palette *palette_tmp = nullptr;
Palette **palette_p = paint ? &paint->palette : &palette_tmp;

View File

@ -89,6 +89,7 @@ struct BlendFileReadWMSetupData {
struct BlendFileReadParams {
uint skip_flags : 3; /* #eBLOReadSkip */
uint is_startup : 1;
uint is_factory_settings : 1;
/** Whether we are reading the memfile for an undo or a redo. */
int undo_direction; /* #eUndoStepDir */

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,13 +2900,9 @@ 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->flags |= PAINT_SHOW_BRUSH;
}
/* Enable cursor by default. */
Paint *paint = &ts->gp_paint->paint;
paint->flags |= PAINT_SHOW_BRUSH;
}
}

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)) {
@ -3625,6 +3662,10 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 402, 40)) {
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;
}
}
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);
/* 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;
}
}
}
}
/* 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

@ -954,6 +954,15 @@ void blo_do_versions_userdef(UserDef *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/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

@ -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);

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(). */

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

@ -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

@ -1429,13 +1429,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

@ -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

@ -930,6 +930,8 @@ uiPopupBlockHandle *ui_popup_block_create(bContext *C,
handle->can_refresh = true;
}
/* keep centered on window resizing */
if (block->bounds_type == UI_BLOCK_BOUNDS_POPUP_CENTER) {
type.listener = ui_block_region_popup_window_listener;

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

@ -1382,7 +1382,7 @@ static void template_ID(const bContext *C,
template_id_workspace_pin_extra_icon(template_ui, but);
if (!hide_buttons) {
if (!hide_buttons && !(idfrom && ID_IS_LINKED(idfrom))) {
if (ID_IS_LINKED(id)) {
const bool disabled = !BKE_idtype_idcode_is_localizable(GS(id->name));
if (id->tag & LIB_TAG_INDIRECT) {
@ -6419,13 +6419,47 @@ 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,
std::string &tooltip_message)
{
tooltip_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;
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.");
}
regular_message = ED_info_statusbar_string_ex(bmain, scene, view_layer, statusbar_info_flag);
}
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;
@ -6434,13 +6468,13 @@ 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;
std::string tooltip_message;
ui_template_status_info_warnings_messages(
bmain, scene, view_layer, warning_message, regular_message, tooltip_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);
@ -6448,25 +6482,26 @@ 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,
UI_BTYPE_ROUNDBOX,
0,
"",
0,
0,
UI_UNIT_X + (6 * UI_SCALE_FAC),
UI_UNIT_Y,
nullptr,
0.0f,
0.0f,
"");
uiBut *but = uiDefBut(block,
UI_BTYPE_ROUNDBOX,
0,
"",
0,
0,
UI_UNIT_X + (6 * UI_SCALE_FAC),
UI_UNIT_Y,
nullptr,
0.0f,
0.0f,
"");
/* UI_BTYPE_ROUNDBOX's bg color is set in but->col. */
UI_GetThemeColorType4ubv(TH_INFO_WARNING, SPACE_INFO, but->col);
@ -6491,14 +6526,13 @@ 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);
/* Tool tips have to be static currently.
* FIXME This is a horrible requirement from uiBut, should probably just store an std::string for
* the tooltip as well? */
static char tooltip_static_storage[256];
BLI_strncpy(tooltip_static_storage, tooltip_message.c_str(), sizeof(tooltip_static_storage));
/* The warning icon itself. */
but = uiDefIconBut(block,
UI_BTYPE_BUT,
0,
@ -6510,23 +6544,25 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C)
nullptr,
0.0f,
0.0f,
compat_error_msg);
tooltip_static_storage);
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. */
but = uiDefBut(block,
UI_BTYPE_BUT,
0,
status_info_txt,
UI_UNIT_X,
0,
short(width + UI_UNIT_X),
UI_UNIT_Y,
nullptr,
0.0f,
0.0f,
compat_error_msg);
/* The warning message, if any. */
if (!warning_message.empty()) {
but = uiDefBut(block,
UI_BTYPE_BUT,
0,
warning_message.c_str(),
UI_UNIT_X,
0,
short(width + UI_UNIT_X),
UI_UNIT_Y,
nullptr,
0.0f,
0.0f,
tooltip_static_storage);
}
UI_block_emboss_set(block, previous_emboss);
}

View File

@ -789,6 +789,10 @@ static int new_material_exec(bContext *C, wmOperator * /*op*/)
* pointer use also increases user, so this compensates it */
id_us_min(&ma->id);
if (ptr.owner_id) {
BKE_id_move_to_same_lib(*bmain, ma->id, *ptr.owner_id);
}
PointerRNA idptr = RNA_id_pointer_create(&ma->id);
RNA_property_pointer_set(&ptr, prop, idptr, nullptr);
RNA_property_update(C, &ptr, prop);
@ -843,6 +847,10 @@ static int new_texture_exec(bContext *C, wmOperator * /*op*/)
* pointer use also increases user, so this compensates it */
id_us_min(&tex->id);
if (ptr.owner_id) {
BKE_id_move_to_same_lib(*bmain, tex->id, *ptr.owner_id);
}
PointerRNA idptr = RNA_id_pointer_create(&tex->id);
RNA_property_pointer_set(&ptr, prop, idptr, nullptr);
RNA_property_update(C, &ptr, prop);
@ -900,6 +908,10 @@ static int new_world_exec(bContext *C, wmOperator * /*op*/)
* pointer use also increases user, so this compensates it */
id_us_min(&wo->id);
if (ptr.owner_id) {
BKE_id_move_to_same_lib(*bmain, wo->id, *ptr.owner_id);
}
PointerRNA idptr = RNA_id_pointer_create(&wo->id);
RNA_property_pointer_set(&ptr, prop, idptr, nullptr);
RNA_property_update(C, &ptr, prop);

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,792 @@
/* 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;
}
if (!paint->brush_asset_reference) {
return false;
}
/* Asset brush, check if belongs to an editable blend file. */
if (ID_IS_ASSET(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 = BKE_preferences_asset_library_find_by_name(
&U, paint->brush_asset_reference->asset_library_identifier);
if (!library) {
return OPERATOR_CANCELLED;
}
bke::asset_edit_id_delete(*bmain, brush->id, *op->reports);
refresh_asset_library(C, *library);
BKE_paint_brush_set_default(bmain, paint);
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*/)
{
return WM_operator_confirm_ex(
C,
op,
IFACE_("Delete Brush Asset"),
IFACE_("Permanently delete brush asset blend file. This can't be undone."),
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 both from the local session and asset library";
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

@ -421,19 +421,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 */
@ -578,24 +576,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);
@ -841,7 +842,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

@ -1646,7 +1646,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

@ -4228,8 +4228,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;
@ -4248,18 +4249,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);
@ -4267,7 +4270,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) {
@ -4285,14 +4287,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

@ -527,7 +527,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

@ -34,6 +34,7 @@
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "BKE_asset_edit.hh"
#include "BKE_colortools.hh"
#include "BKE_context.hh"
#include "BKE_global.hh"
@ -1262,14 +1263,20 @@ static void image_open_cancel(bContext * /*C*/, wmOperator *op)
static Image *image_open_single(Main *bmain,
wmOperator *op,
ImageFrameRange *range,
bool use_multiview)
const ImageFrameRange *range,
const bool use_multiview,
const bool check_exists)
{
bool exists = false;
Image *ima = nullptr;
errno = 0;
ima = BKE_image_load_exists_ex(bmain, range->filepath, &exists);
if (check_exists) {
ima = BKE_image_load_exists_ex(bmain, range->filepath, &exists);
}
else {
ima = BKE_image_load(bmain, range->filepath);
}
if (!ima) {
if (op->customdata) {
@ -1326,6 +1333,7 @@ static int image_open_exec(bContext *C, wmOperator *op)
Main *bmain = CTX_data_main(C);
ScrArea *area = CTX_wm_area(C);
Scene *scene = CTX_data_scene(C);
ImageOpenData *iod = static_cast<ImageOpenData *>(op->customdata);
ImageUser *iuser = nullptr;
Image *ima = nullptr;
int frame_seq_len = 0;
@ -1334,13 +1342,18 @@ static int image_open_exec(bContext *C, wmOperator *op)
const bool use_multiview = RNA_boolean_get(op->ptr, "use_multiview");
const bool use_udim = RNA_boolean_get(op->ptr, "use_udim_detecting");
/* For editable assets always create a new image datablock. We can't assign
* a local datablock to linked asset datablocks. */
const bool check_exists = !(iod->pprop.prop && iod->pprop.ptr.owner_id &&
blender::bke::asset_edit_id_is_editable(*iod->pprop.ptr.owner_id));
if (!op->customdata) {
image_open_init(C, op);
}
ListBase ranges = ED_image_filesel_detect_sequences(bmain, op, use_udim);
LISTBASE_FOREACH (ImageFrameRange *, range, &ranges) {
Image *ima_range = image_open_single(bmain, op, range, use_multiview);
Image *ima_range = image_open_single(bmain, op, range, use_multiview, check_exists);
/* take the first image */
if ((ima == nullptr) && ima_range) {
@ -1358,13 +1371,15 @@ static int image_open_exec(bContext *C, wmOperator *op)
}
/* hook into UI */
ImageOpenData *iod = static_cast<ImageOpenData *>(op->customdata);
if (iod->pprop.prop) {
/* when creating new ID blocks, use is already 1, but RNA
* pointer use also increases user, so this compensates it */
id_us_min(&ima->id);
if (iod->pprop.ptr.owner_id) {
BKE_id_move_to_same_lib(*bmain, ima->id, *iod->pprop.ptr.owner_id);
}
PointerRNA imaptr = RNA_id_pointer_create(&ima->id);
RNA_property_pointer_set(&iod->pprop.ptr, iod->pprop.prop, imaptr, nullptr);
RNA_property_update(C, &iod->pprop.ptr, iod->pprop.prop);
@ -2589,6 +2604,10 @@ static int image_new_exec(bContext *C, wmOperator *op)
* pointer use also increases user, so this compensates it */
id_us_min(&ima->id);
if (data->pprop.ptr.owner_id) {
BKE_id_move_to_same_lib(*bmain, ima->id, *data->pprop.ptr.owner_id);
}
PointerRNA imaptr = RNA_id_pointer_create(&ima->id);
RNA_property_pointer_set(&data->pprop.ptr, data->pprop.prop, imaptr, nullptr);
RNA_property_update(C, &data->pprop.ptr, data->pprop.prop);

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;

View File

@ -1071,6 +1071,10 @@ static int new_node_tree_exec(bContext *C, wmOperator *op)
* user. */
id_us_min(&ntree->id);
if (ptr.owner_id) {
BKE_id_move_to_same_lib(*bmain, ntree->id, *ptr.owner_id);
}
PointerRNA idptr = RNA_id_pointer_create(&ntree->id);
RNA_property_pointer_set(&ptr, prop, idptr, nullptr);
RNA_property_update(C, &ptr, prop);

View File

@ -1215,6 +1215,8 @@ static bNode *node_group_make_from_nodes(const bContext &C,
/* New node-tree. */
bNodeTree *ngroup = bke::ntreeAddTree(bmain, "NodeGroup", ntreetype);
BKE_id_move_to_same_lib(*bmain, ngroup->id, ntree.id);
/* make group node */
bNode *gnode = bke::nodeAddNode(&C, &ntree, ntype);
gnode->id = (ID *)ngroup;

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;

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

@ -581,6 +581,11 @@ typedef struct Library {
enum eLibrary_Tag {
/* Automatic recursive resync was needed when linking/loading data from that library. */
LIBRARY_TAG_RESYNC_REQUIRED = 1 << 0,
/* Datablocks from this library are editable in the UI despite being linked.
* Used for asset that can be temporarily or permantently edited. */
LIBRARY_ASSET_EDITABLE = 1 << 1,
/* The blend file of this library is writable for asset editing. */
LIBRARY_ASSET_FILE_WRITABLE = 1 << 2,
};
/**
@ -670,7 +675,12 @@ typedef struct PreviewImage {
#define ID_IS_LINKED(_id) (((const ID *)(_id))->lib != NULL)
#define ID_IS_EDITABLE(_id) (((const ID *)(_id))->lib == NULL)
#define ID_TYPE_SUPPORTS_ASSET_EDITABLE(id_type) ELEM(id_type, ID_BR, ID_TE, ID_NT, ID_IM)
#define ID_IS_EDITABLE(_id) \
((((const ID *)(_id))->lib == NULL) || \
((((const ID *)(_id))->lib->runtime.tag & LIBRARY_ASSET_EDITABLE) && \
ID_TYPE_SUPPORTS_ASSET_EDITABLE(GS((((const ID *)(_id))->name)))))
/* Note that these are fairly high-level checks, should be used at user interaction level, not in
* BKE_library_override typically (especially due to the check on LIB_TAG_EXTERN). */

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

@ -1269,14 +1269,28 @@ static char *rna_def_property_set_func(
else {
rna_print_data_get(f, dp);
PointerPropertyRNA *pprop = (PointerPropertyRNA *)dp->prop;
StructRNA *type = (pprop->type) ? rna_find_struct((const char *)pprop->type) : nullptr;
if (prop->flag & PROP_ID_SELF_CHECK) {
/* No pointers to self allowed. */
rna_print_id_get(f, dp);
fprintf(f, " if (id == value.data) {\n");
fprintf(f, " return;\n");
fprintf(f, " }\n");
}
if (type && (type->flag & STRUCT_ID)) {
/* Check if pointers between datablocks are allowed. */
fprintf(f,
" if (value.data && ptr->owner_id && value.owner_id && "
"!BKE_id_can_link(*ptr->owner_id, *value.owner_id)) {\n");
fprintf(f, " return;\n");
fprintf(f, " }\n");
}
if (prop->flag & PROP_ID_REFCOUNT) {
/* Perform reference counting. */
fprintf(f, "\n if (data->%s) {\n", dp->dnaname);
fprintf(f, " id_us_min((ID *)data->%s);\n", dp->dnaname);
fprintf(f, " }\n");
@ -1284,14 +1298,11 @@ static char *rna_def_property_set_func(
fprintf(f, " id_us_plus((ID *)value.data);\n");
fprintf(f, " }\n");
}
else {
PointerPropertyRNA *pprop = (PointerPropertyRNA *)dp->prop;
StructRNA *type = (pprop->type) ? rna_find_struct((const char *)pprop->type) : nullptr;
if (type && (type->flag & STRUCT_ID)) {
fprintf(f, " if (value.data) {\n");
fprintf(f, " id_lib_extern((ID *)value.data);\n");
fprintf(f, " }\n");
}
else if (type && (type->flag & STRUCT_ID)) {
/* Still mark linked data as used if not reference counting. */
fprintf(f, " if (value.data) {\n");
fprintf(f, " id_lib_extern((ID *)value.data);\n");
fprintf(f, " }\n");
}
fprintf(f, " *(void **)&data->%s = value.data;\n", dp->dnaname);

View File

@ -285,7 +285,6 @@ int rna_ID_name_length(PointerRNA *ptr)
void rna_ID_name_set(PointerRNA *ptr, const char *value)
{
ID *id = (ID *)ptr->data;
BLI_assert(BKE_id_is_in_global_main(id));
BLI_assert(ID_IS_EDITABLE(id));
BKE_libblock_rename(G_MAIN, id, value);
@ -296,6 +295,8 @@ void rna_ID_name_set(PointerRNA *ptr, const char *value)
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
}
}
/* TODO: this does not update immediately in the asset shelf. */
}
static int rna_ID_name_editable(const PointerRNA *ptr, const char **r_info)
@ -2554,6 +2555,14 @@ static void rna_def_library(BlenderRNA *brna)
"current blendfile, and that had to be recursively resynced on load "
"(it is recommended to open and re-save that library blendfile then)");
prop = RNA_def_property(srna, "is_editable", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "runtime.tag", LIBRARY_ASSET_EDITABLE);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop,
"Editable",
"Datablocks in this library are editable despite being linked. Used by "
"brush assets and their dependencies");
func = RNA_def_function(srna, "reload", "rna_Library_reload");
RNA_def_function_flag(func, FUNC_USE_REPORTS | FUNC_USE_CONTEXT);
RNA_def_function_ui_description(func, "Reload this library and all its linked data-blocks");

View File

@ -40,6 +40,7 @@
#include "BKE_global.hh"
#include "BKE_idprop.hh"
#include "BKE_idtype.hh"
#include "BKE_lib_id.hh"
#include "BKE_lib_override.hh"
#include "BKE_main.hh"
#include "BKE_node.hh"
@ -1582,22 +1583,28 @@ bool RNA_property_pointer_poll(PointerRNA *ptr, PropertyRNA *prop, PointerRNA *v
{
prop = rna_ensure_property(prop);
if (prop->type == PROP_POINTER) {
PointerPropertyRNA *pprop = (PointerPropertyRNA *)prop;
if (prop->type != PROP_POINTER) {
printf("%s: %s is not a pointer property.\n", __func__, prop->identifier);
return false;
}
if (pprop->poll) {
if (rna_idproperty_check(&prop, ptr)) {
return reinterpret_cast<PropPointerPollFuncPy>(reinterpret_cast<void *>(pprop->poll))(
ptr, *value, prop);
}
return pprop->poll(ptr, *value);
}
PointerPropertyRNA *pprop = (PointerPropertyRNA *)prop;
/* Can't point from linked to local datablock. */
if (ptr->owner_id && value->owner_id && !BKE_id_can_link(*ptr->owner_id, *value->owner_id)) {
return false;
}
/* Check custom poll function. */
if (!pprop->poll) {
return true;
}
printf("%s: %s is not a pointer property.\n", __func__, prop->identifier);
return false;
if (rna_idproperty_check(&prop, ptr)) {
return reinterpret_cast<PropPointerPollFuncPy>(reinterpret_cast<void *>(pprop->poll))(
ptr, *value, prop);
}
return pprop->poll(ptr, *value);
}
void RNA_property_enum_items_ex(bContext *C,

View File

@ -391,50 +391,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
@ -1073,26 +1029,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);
@ -1113,33 +1049,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;
@ -1722,32 +1631,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");
@ -1944,8 +1827,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");
@ -2080,16 +1962,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 false;
return (brush == nullptr) || (paint->runtime.ob_mode & brush->ob_mode) != 0;
}
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;
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();
}
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;
}
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->ob_mode & mode;
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,
nullptr,
nullptr,
nullptr,
"rna_iterator_array_get",
nullptr,
nullptr,
nullptr,
nullptr);
RNA_def_property_ui_text(prop, "Paint Tool Slots", "");
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,
"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

@ -1360,6 +1360,7 @@ void wm_homefile_read_ex(bContext *C,
if (BLI_access(filepath_startup, R_OK) == 0) {
BlendFileReadParams params{};
params.is_startup = true;
params.is_factory_settings = use_factory_settings;
params.skip_flags = skip_flags | BLO_READ_SKIP_USERDEF;
BlendFileReadReport bf_reports{};
bf_reports.reports = reports;
@ -1402,6 +1403,7 @@ void wm_homefile_read_ex(bContext *C,
if (success == false) {
BlendFileReadParams read_file_params{};
read_file_params.is_startup = true;
read_file_params.is_factory_settings = use_factory_settings;
read_file_params.skip_flags = skip_flags;
BlendFileData *bfd = BKE_blendfile_read_from_memory(
datatoc_startup_blend, datatoc_startup_blend_size, &read_file_params, nullptr);
@ -3632,7 +3634,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;
}
@ -3994,31 +3996,43 @@ static void file_overwrite_detailed_info_show(uiLayout *parent_layout, Main *bma
* block. */
uiLayoutSetScaleY(layout, 0.70f);
char writer_ver_str[16];
char current_ver_str[16];
if (bmain->versionfile == BLENDER_VERSION) {
BKE_blender_version_blendfile_string_from_values(
writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, bmain->subversionfile);
BKE_blender_version_blendfile_string_from_values(
current_ver_str, sizeof(current_ver_str), BLENDER_FILE_VERSION, BLENDER_FILE_SUBVERSION);
}
else {
BKE_blender_version_blendfile_string_from_values(
writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, -1);
BKE_blender_version_blendfile_string_from_values(
current_ver_str, sizeof(current_ver_str), BLENDER_VERSION, -1);
if (bmain->has_forward_compatibility_issues) {
char writer_ver_str[16];
char current_ver_str[16];
if (bmain->versionfile == BLENDER_VERSION) {
BKE_blender_version_blendfile_string_from_values(
writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, bmain->subversionfile);
BKE_blender_version_blendfile_string_from_values(
current_ver_str, sizeof(current_ver_str), BLENDER_FILE_VERSION, BLENDER_FILE_SUBVERSION);
}
else {
BKE_blender_version_blendfile_string_from_values(
writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, -1);
BKE_blender_version_blendfile_string_from_values(
current_ver_str, sizeof(current_ver_str), BLENDER_VERSION, -1);
}
char message_line1[256];
char message_line2[256];
SNPRINTF(message_line1,
RPT_("This file was saved by a newer version of Blender (%s)"),
writer_ver_str);
SNPRINTF(message_line2,
RPT_("Saving it with this Blender (%s) may cause loss of data"),
current_ver_str);
uiItemL(layout, message_line1, ICON_NONE);
uiItemL(layout, message_line2, ICON_NONE);
}
char message_line1[256];
char message_line2[256];
SNPRINTF(message_line1,
RPT_("This file was saved by a newer version of Blender (%s)"),
writer_ver_str);
SNPRINTF(message_line2,
RPT_("Saving it with this Blender (%s) may cause loss of data"),
current_ver_str);
uiItemL(layout, message_line1, ICON_NONE);
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*/)
@ -4083,7 +4097,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)
@ -4119,8 +4138,27 @@ static uiBlock *block_create_save_file_overwrite_dialog(bContext *C, ARegion *re
uiLayout *layout = uiItemsAlertBox(block, 34, ALERT_ICON_WARNING);
/* Title. */
uiItemL_ex(
layout, RPT_("Overwrite file with an older Blender version?"), ICON_NONE, true, false);
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));
@ -4332,7 +4370,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
@ -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

@ -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}