WIP: Brush assets project #106303

Draft
Julian Eisel wants to merge 352 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.
124 changed files with 3494 additions and 4142 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,47 +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",
}.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
@ -402,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

@ -5599,27 +5599,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,67 @@
#
# 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):
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",
'SCULPT_CURVES': "VIEW3D_AST_brush_sculpt_curves",
'PAINT_GREASE_PENCIL': "VIEW3D_AST_brush_grease_pencil_paint",
}
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,7 +199,7 @@ class BrushPanel(UnifiedPaintPanel):
class BrushSelectPanel(BrushPanel):
bl_label = "Brushes"
bl_label = "Brush Asset"
def draw(self, context):
layout = self.layout
@ -164,20 +207,18 @@ class BrushSelectPanel(BrushPanel):
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"
@ -993,6 +1034,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
@ -1083,6 +1127,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
@ -1110,6 +1157,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
@ -1117,10 +1167,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")
@ -1128,6 +1185,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."""

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,
@ -763,9 +765,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
@ -1181,7 +1184,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"
@ -1219,8 +1222,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):
@ -1233,16 +1236,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):
@ -1687,6 +1691,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,
@ -1756,6 +1772,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,36 +1892,25 @@ class _defs_weight_paint:
class _defs_paint_grease_pencil:
# 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 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():
@ -1925,7 +1953,8 @@ class _defs_paint_grease_pencil:
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,
@ -2307,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,
@ -2320,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():
@ -2686,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:
@ -2716,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:
@ -2766,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:
@ -2798,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:
@ -3500,8 +3568,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
'PAINT_GREASE_PENCIL': [
_defs_view3d_generic.cursor,
None,
_defs_paint_grease_pencil.draw,
_defs_paint_grease_pencil.erase,
_defs_paint_grease_pencil.generate_from_brushes,
_defs_paint_grease_pencil.cutter,
_defs_paint_grease_pencil.tint,
None,

View File

@ -236,9 +236,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
@ -297,9 +298,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
@ -313,9 +315,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
@ -329,8 +332,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
@ -392,7 +397,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 (
@ -420,6 +426,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,
)
@ -433,12 +441,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
@ -494,7 +502,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)
@ -509,12 +517,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")
@ -533,7 +541,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)
@ -686,7 +695,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
@ -3523,23 +3533,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"
@ -9087,11 +9080,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 = (
@ -9173,7 +9213,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,
@ -9359,7 +9398,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.is_asset_library_data:
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):
@ -2520,29 +2427,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):

View File

@ -0,0 +1,69 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
*/
#pragma once
/**
* Editing of datablocks from asset libraries, separate from the current open
* blend file.
*
* Each asset blend file is loaded into a separate main database, including the
* asset datablocks and their dependencies. These datablocks are all tagged with
* LIB_TAG_ASSET_EDIT_MAIN. These can not be linked with other datablocks in the
* current blend file.
*
* For editable assets in user asset libraries, each asset is stored in its own
* blend file. 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 "AS_asset_catalog.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 main database that a given asset datablock corresponds to. */
Main *asset_edit_main(const ID &id);
/** Asset editing operations. */
bool asset_edit_id_is_editable(const ID &id);
std::optional<std::string> asset_edit_id_save_as(Main &global_main,
const ID &id,
StringRef name,
const bUserAssetLibrary &user_library,
ReportList &reports);
bool asset_edit_id_save(Main &global_main, const ID &id, ReportList &reports);
bool asset_edit_id_revert(Main &global_main, const ID &id, ReportList &reports);
bool asset_edit_id_delete(Main &global_main, const ID &id, ReportList &reports);
/** Clean up on exit. */
void asset_edit_main_free_all();
} // 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 29
#define BLENDER_FILE_SUBVERSION 30
/* 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

@ -347,6 +347,7 @@ int ctx_data_list_count(const bContext *C,
/* Data Context Members */
Main *CTX_data_main(const bContext *C);
Main *CTX_data_main_from_id(const bContext *C, const ID *id);
Scene *CTX_data_scene(const bContext *C);
/**
* This is tricky. Sometimes the user overrides the render_layer

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. */
@ -708,12 +710,6 @@ void BKE_library_make_local(
void BKE_id_tag_set_atomic(ID *id, int tag);
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.
*/
bool BKE_id_is_in_global_main(ID *id);
bool BKE_id_can_be_asset(const ID *id);
/**

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`. */
@ -183,6 +191,11 @@ struct Main {
*/
bool is_global_main;
/**
* True if main used to store weakly referenced assets.
*/
bool is_asset_edit_main;
BlendThumbnail *blen_thumb;
Library *curlib;
@ -301,6 +314,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);
@ -489,6 +513,20 @@ ListBase *which_libbase(Main *bmain, short type);
*/
int set_listbasepointers(Main *main, ListBase *lb[]);
/**
* Return main database this ID is a member of.
*
* This works for the global main database and asset edit databases.
* So only datablocks that are directly editable in the user interface.
*
* Use this in operator and draw code instead of assuming the main
* in the context owns datablocks.
*
* Optionally can verify that this datablock is one of these databases.
* This is slow and mainly meant for asserts.
*/
Main *BKE_main_from_id(Main *global_main, const ID *id, bool verify = false);
#define MAIN_VERSION_FILE_ATLEAST(main, ver, subver) \
((main)->versionfile > (ver) || \
((main)->versionfile == (ver) && (main)->subversionfile >= (subver)))

View File

@ -402,7 +402,7 @@ struct bNodeTreeType {
/* callbacks */
/* Iteration over all node classes. */
void (*foreach_nodeclass)(Scene *scene, void *calldata, bNodeClassCallback func);
void (*foreach_nodeclass)(void *calldata, bNodeClassCallback func);
/* Check visibility in the node editor */
bool (*poll)(const bContext *C, bNodeTreeType *ntreetype);
/* Select a node tree from the context */

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"
@ -23,6 +25,7 @@
#include "BKE_pbvh.hh"
struct AssetWeakReference;
struct BMFace;
struct BMLog;
struct BMesh;
@ -183,24 +186,42 @@ 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_brush_set_default_references(ToolSettings *ts);
void BKE_paint_brush_validate(Main *bmain, Paint *paint);
/**
* Set the active brush of given paint struct, and store the weak asset reference to it.
* \note Takes ownership of the given `weak_asset_reference`.
*/
bool BKE_paint_brush_asset_set(Paint *paint,
Brush *brush,
const AssetWeakReference &weak_asset_reference);
/* 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

@ -66,6 +66,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
@ -254,7 +255,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
@ -335,6 +335,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,531 @@
/* 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_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 "DNA_asset_types.h"
#include "MEM_guardedalloc.h"
namespace blender::bke {
/**
* Asset library blend file, with editable contents.
*/
struct AssetEditBlend {
std::string filepath;
Main *main;
bool is_editable = false;
AssetEditBlend(const std::string &filepath);
~AssetEditBlend();
AssetEditBlend(const AssetEditBlend &) = delete;
AssetEditBlend(AssetEditBlend &&other);
AssetEditBlend &operator=(AssetEditBlend &&other);
ID *ensure_id(ID_Type id_type, const char *asset_name);
void reload(Main &global_main);
void clear_users(Main &global_main);
};
AssetEditBlend::AssetEditBlend(const std::string &filepath)
: filepath(std::move(filepath)), main(BKE_main_new())
{
this->main->is_asset_edit_main = true;
BLI_assert(!BLI_path_is_rel(filepath.c_str()));
/* Simple check, based on being a writable .asset.blend file in a user asset library. */
this->is_editable = StringRef(filepath).endswith(BLENDER_ASSET_FILE_SUFFIX) &&
BKE_preferences_asset_library_containing_path(&U, filepath.c_str()) &&
BLI_file_is_writable(filepath.c_str());
}
AssetEditBlend::~AssetEditBlend()
{
if (main) {
BKE_main_free(main);
}
}
AssetEditBlend::AssetEditBlend(AssetEditBlend &&other)
: filepath(std::exchange(other.filepath, "")), main(std::exchange(other.main, nullptr))
{
}
AssetEditBlend &AssetEditBlend::operator=(AssetEditBlend &&other)
{
if (this == &other) {
return *this;
}
this->filepath = std::exchange(other.filepath, "");
this->main = std::exchange(other.main, nullptr);
return *this;
}
ID *AssetEditBlend::ensure_id(const ID_Type id_type, const char *asset_name)
{
/* Check if we have the asset already. */
ID *local_asset = BKE_libblock_find_name(this->main, id_type, asset_name);
if (local_asset) {
BLI_assert(ID_IS_ASSET(local_asset));
return local_asset;
}
/* Load asset from asset library. */
LibraryLink_Params lapp_params{};
lapp_params.bmain = this->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_flag_set(lapp_context, 0, true);
BKE_blendfile_link_append_context_library_add(lapp_context, filepath.c_str(), 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);
BKE_blendfile_append(lapp_context, nullptr);
local_asset = BKE_blendfile_link_append_context_item_newid_get(lapp_context, lapp_item);
BKE_blendfile_link_append_context_free(lapp_context);
BKE_main_id_tag_all(this->main, LIB_TAG_ASSET_EDIT_MAIN, true);
/* 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));
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_main_expander(asset_main_create_expander);
BLO_expand_main(nullptr, bmain_src);
/* 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;
}
void AssetEditBlend::reload(Main &global_main)
{
Main *old_main = this->main;
this->main = BKE_main_new();
this->main->is_asset_edit_main = true;
/* Fill fresh main database with same datablock as before. */
LibraryLink_Params lapp_params{};
lapp_params.bmain = this->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_flag_set(lapp_context, 0, true);
BKE_blendfile_link_append_context_library_add(lapp_context, this->filepath.c_str(), nullptr);
/* Requests all existing datablocks to be appended again. */
ID *old_id;
FOREACH_MAIN_ID_BEGIN (old_main, old_id) {
ID_Type old_id_code = GS(old_id->name);
if (BKE_idtype_idcode_is_linkable(old_id_code)) {
BlendfileLinkAppendContextItem *lapp_item = BKE_blendfile_link_append_context_item_add(
lapp_context, old_id->name + 2, old_id_code, nullptr);
BKE_blendfile_link_append_context_item_library_index_enable(lapp_context, lapp_item, 0);
}
}
FOREACH_MAIN_ID_END;
BKE_blendfile_link(lapp_context, nullptr);
BKE_blendfile_append(lapp_context, nullptr);
BKE_blendfile_link_append_context_free(lapp_context);
BKE_main_id_tag_all(this->main, LIB_TAG_ASSET_EDIT_MAIN, true);
/* Remap old to new. */
bke::id::IDRemapper mappings;
FOREACH_MAIN_ID_BEGIN (old_main, old_id) {
ID *new_id = BKE_libblock_find_name(this->main, GS(old_id->name), old_id->name + 2);
mappings.add(old_id, new_id);
}
FOREACH_MAIN_ID_END;
BKE_libblock_remap_multiple(&global_main, mappings, 0);
/* Free old database. */
BKE_main_free(old_main);
}
void AssetEditBlend::clear_users(Main &global_main)
{
/* Remap old to null pointer. */
bke::id::IDRemapper mappings;
ID *old_id;
FOREACH_MAIN_ID_BEGIN (this->main, old_id) {
mappings.add(old_id, nullptr);
}
FOREACH_MAIN_ID_END;
BKE_libblock_remap_multiple(&global_main, mappings, 0);
}
/**
* Public API
*/
static Vector<AssetEditBlend> &asset_edit_blend_get_all()
{
static Vector<AssetEditBlend> mains;
return mains;
}
static AssetEditBlend *asset_edit_blend_from_id(const ID &id)
{
BLI_assert(id.tag & LIB_TAG_ASSET_EDIT_MAIN);
/* TODO: It would be good to make this more efficient, though it's unlikely to be a bottleneck
* for brush assets. It's not easy to add a hash map here because it needs to be kept up to date
* as the main database is edited, which can be done in many places. So this would require hooks
* quite deep in ID management. */
for (AssetEditBlend &asset_blend : asset_edit_blend_get_all()) {
ListBase *lb = which_libbase(asset_blend.main, GS(id.name));
LISTBASE_FOREACH (ID *, other_id, lb) {
if (&id == other_id) {
return &asset_blend;
}
}
}
BLI_assert_unreachable();
return nullptr;
}
Main *asset_edit_main(const ID &id)
{
const AssetEditBlend *asset_blend = asset_edit_blend_from_id(id);
return (asset_blend) ? asset_blend->main : nullptr;
}
static AssetEditBlend &asset_edit_blend_file_ensure(const StringRef filepath)
{
for (AssetEditBlend &asset_blend : asset_edit_blend_get_all()) {
if (asset_blend.filepath == filepath) {
return asset_blend;
}
}
asset_edit_blend_get_all().append_as(filepath);
return asset_edit_blend_get_all().last();
}
std::optional<std::string> asset_edit_id_save_as(Main &global_main,
const ID &id,
const StringRef name,
const bUserAssetLibrary &user_library,
ReportList &reports)
{
const std::string filepath = asset_blendfile_path_for_save(
user_library, name, GS(id.name), reports);
/* Save to asset library. */
Main *asset_main = BKE_main_from_id(&global_main, &id);
std::string final_full_asset_filepath;
const bool success = asset_write_in_library(
asset_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;
}
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)
{
AssetEditBlend *asset_blend = asset_edit_blend_from_id(id);
if (asset_blend == nullptr) {
return false;
}
std::string final_full_asset_filepath;
const bool success = asset_write_in_library(asset_blend->main,
id,
id.name + 2,
asset_blend->filepath.c_str(),
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, const ID &id, ReportList & /*reports*/)
{
AssetEditBlend *asset_blend = asset_edit_blend_from_id(id);
if (asset_blend == nullptr) {
return false;
}
/* Reload entire main, including texture dependencies. This relies on there
* being only a single asset per blend file. */
asset_blend->reload(global_main);
return true;
}
bool asset_edit_id_delete(Main &global_main, const ID &id, ReportList &reports)
{
AssetEditBlend *asset_blend = asset_edit_blend_from_id(id);
if (asset_blend == nullptr) {
return false;
}
if (BLI_delete(asset_blend->filepath.c_str(), false, false) != 0) {
BKE_report(&reports, RPT_ERROR, "Failed to delete asset library file");
return false;
}
asset_blend->clear_users(global_main);
int index = 0;
for (AssetEditBlend &asset_blend_iter : asset_edit_blend_get_all()) {
if (&asset_blend_iter == asset_blend) {
asset_edit_blend_get_all().remove(index);
break;
}
index++;
}
return true;
}
ID *asset_edit_id_from_weak_reference(Main &global_main,
const ID_Type id_type,
const AssetWeakReference &weak_ref)
{
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);
/* Find asset in current blend file. */
if (asset_lib_path == nullptr) {
ID *local_asset = BKE_libblock_find_name(&global_main, id_type, asset_name);
BLI_assert(local_asset == nullptr || ID_IS_ASSET(local_asset));
return local_asset;
}
/* If weak reference resolves to a null library path, assume we are in local asset case. */
AssetEditBlend &asset_blend = asset_edit_blend_file_ensure(asset_lib_path);
return asset_blend.ensure_id(id_type, asset_name);
}
bool asset_edit_id_is_editable(const ID &id)
{
if (!(id.tag & LIB_TAG_ASSET_EDIT_MAIN)) {
return false;
}
const AssetEditBlend *asset_blend = asset_edit_blend_from_id(id);
return (asset_blend) ? asset_blend->is_editable : false;
}
void asset_edit_main_free_all()
{
asset_edit_blend_get_all().clear_and_shrink();
}
} // 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 */
@ -60,6 +61,11 @@ void BKE_blender_free()
BKE_blender_globals_clear();
/* Free separate data-bases after #BKE_blender_globals_clear in case any data-blocks in the
* global main use pointers to asset main data-blocks when they're freed. That generally
* shouldn't happen but it's better to be safe. */
blender::bke::asset_edit_main_free_all();
if (G.log.file != nullptr) {
fclose(static_cast<FILE *>(G.log.file));
}

View File

@ -675,6 +675,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) {
@ -1259,6 +1262,15 @@ UserDef *BKE_blendfile_userdef_from_defaults()
BKE_preferences_extension_repo_add_default(userdef);
BKE_preferences_extension_repo_add_default_user(userdef);
{
BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(
userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh/Sculpt/Cloth");
BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(
userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh/Sculpt/General");
BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(
userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh/Sculpt/Painting");
}
return userdef;
}

File diff suppressed because it is too large Load Diff

View File

@ -1058,6 +1058,11 @@ Main *CTX_data_main(const bContext *C)
return C->data.main;
}
Main *CTX_data_main_from_id(const bContext *C, const ID *id)
{
return BKE_main_from_id(CTX_data_main(C), id);
}
void CTX_data_main_set(bContext *C, Main *bmain)
{
C->data.main = bmain;

View File

@ -1334,6 +1334,9 @@ void *BKE_libblock_alloc_in_lib(Main *bmain,
if ((flag & LIB_ID_CREATE_NO_DEG_TAG) == 0) {
DEG_id_type_tag(bmain, type);
}
if (bmain->is_asset_edit_main) {
id->tag |= LIB_TAG_ASSET_EDIT_MAIN;
}
}
else {
BLI_strncpy(id->name + 2, name, sizeof(id->name) - 2);
@ -1556,10 +1559,20 @@ 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));
}
if (bmain && bmain->is_asset_edit_main) {
new_id->tag |= LIB_TAG_ASSET_EDIT_MAIN;
}
*r_newid = new_id;
}
@ -2215,13 +2228,6 @@ void BKE_id_tag_clear_atomic(ID *id, int tag)
atomic_fetch_and_and_int32(&id->tag, ~tag);
}
bool BKE_id_is_in_global_main(ID *id)
{
/* We do not want to fail when id is nullptr here, even though this is a bit strange behavior...
*/
return (id == nullptr || BLI_findindex(which_libbase(G_MAIN, GS(id->name)), id) != -1);
}
bool BKE_id_can_be_asset(const ID *id)
{
return !ID_IS_LINKED(id) && !ID_IS_OVERRIDE_LIBRARY(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);
@ -955,3 +966,34 @@ int set_listbasepointers(Main *bmain, ListBase *lb[/*INDEX_ID_MAX*/])
return (INDEX_ID_MAX - 1);
}
Main *BKE_main_from_id(Main *global_main, const ID *id, const bool verify)
{
if (id == nullptr || (id->tag & LIB_TAG_NO_MAIN)) {
return nullptr;
}
if (id->tag & LIB_TAG_ASSET_EDIT_MAIN) {
return blender::bke::asset_edit_main(*id);
}
if (verify) {
/* This is rather expensive, so don't do by default and assume valid input. */
if (BLI_findindex(which_libbase(global_main, GS(id->name)), id) == -1) {
return nullptr;
}
}
else {
/* Debug assert, especially for places that pass in G_MAIN. */
#ifndef NDEBUG
if (id->flag & LIB_EMBEDDED_DATA) {
const ID *id_owner = BKE_id_owner_get(const_cast<ID *>(id));
BLI_assert(BLI_findindex(which_libbase(global_main, GS(id_owner->name)), id_owner) != -1);
}
else {
BLI_assert(BLI_findindex(which_libbase(global_main, GS(id->name)), id) != -1);
}
#endif
}
return global_main;
}

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,237 @@ 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);
/* 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 || (brush->id.tag & LIB_TAG_ASSET_EDIT_MAIN));
/* 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;
return true;
}
static void paint_brush_asset_update(Paint &paint,
Brush *brush,
const AssetWeakReference &brush_asset_reference)
{
BLI_assert(&brush_asset_reference != paint.brush_asset_reference);
MEM_delete(paint.brush_asset_reference);
paint.brush_asset_reference = nullptr;
if (brush == nullptr || brush != paint.brush || !(brush->id.tag & LIB_TAG_ASSET_EDIT_MAIN)) {
return;
}
paint.brush_asset_reference = MEM_new<AssetWeakReference>(__func__, brush_asset_reference);
}
bool BKE_paint_brush_asset_set(Paint *paint,
Brush *brush,
const AssetWeakReference &weak_asset_reference)
{
/* Should not happen for users if brush assets are properly filtered by mode, but still protect
* against it in case of invalid API usage. */
if (brush && paint->runtime.ob_mode != brush->ob_mode) {
return false;
}
BKE_paint_brush_set(paint, brush);
paint_brush_asset_update(*paint, brush, weak_asset_reference);
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_brush_set_default_reference(Paint *paint)
{
const char *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";
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 (name) {
paint_brush_set_essentials_reference(paint, name);
}
}
void BKE_paint_runtime_init(const ToolSettings *ts, Paint *paint)
void BKE_paint_brush_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);
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_brush_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);
}
}
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 +1209,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 +1233,13 @@ 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);
return true;
}
@ -1166,7 +1282,8 @@ 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);
return false;
}
@ -1178,18 +1295,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 +1308,21 @@ 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);
}
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 ((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 +1343,9 @@ 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);
}
}
void BKE_paint_blend_read_data(BlendDataReader *reader, const Scene *scene, Paint *p)
@ -1253,17 +1358,13 @@ 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);
/* 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->brush_asset_reference);
if (p->brush_asset_reference) {
BKE_asset_weak_reference_read(reader, p->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,7 @@ static void scene_foreach_paint(LibraryForeachIDData *data,
SCENE_FOREACH_UNDO_RESTORE,
reader,
&paint_old->brush,
IDWALK_CB_USER);
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);
}
IDWALK_CB_NOP);
Palette *palette_tmp = nullptr;
Palette **palette_p = paint ? &paint->palette : &palette_tmp;

View File

@ -2554,7 +2554,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)) {
@ -2837,9 +2836,6 @@ void do_versions_after_linking_280(FileData *fd, Main *bmain)
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 Pencil", "Pencil");
do_versions_rename_id(bmain, ID_BR, "Draw Pen", "Pen");
do_versions_rename_id(bmain, ID_BR, "Draw Ink", "Ink Pen");

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_effect.h"
#include "BKE_grease_pencil.hh"
@ -59,6 +61,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"
@ -2060,6 +2063,33 @@ 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_brush_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_SCULPT_CURVES,
CTX_MODE_PAINT_TEXTURE,
CTX_MODE_PAINT_VERTEX,
CTX_MODE_PAINT_WEIGHT))
{
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)) {
@ -3405,6 +3435,10 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 402, 30)) {
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);
@ -710,203 +663,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

@ -950,6 +950,15 @@ void blo_do_versions_userdef(UserDef *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");
}
/**
* Always bump subversion in BKE_blender_version.h when adding versioning
* code here, and wrap it inside a USER_VERSION_ATLEAST check.

View File

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

View File

@ -21,6 +21,7 @@ struct BlendDataReader;
struct BlendWriter;
struct Main;
struct SpaceType;
struct uiBlock;
struct RegionPollParams;
struct wmWindowManager;
@ -73,15 +74,25 @@ void header_regiontype_register(ARegionType *region_type, const int space_type);
void type_register(std::unique_ptr<AssetShelfType> type);
void type_unregister(const AssetShelfType &shelf_type);
/**
* Poll an asset shelf type for display as a permanent region in a space of a given type (the
* type's #bl_space_type).
* Poll an asset shelf type for display as a popup. Doesn't check for space-type (the type's
* #bl_space_type) since popups should ignore this to allow displaying in any space.
*
* Permanent/non-popup asset shelf regions should use #type_poll_for_space_type() instead.
*/
bool type_poll(const bContext &C, const AssetShelfType *shelf_type, const int space_type);
bool type_poll_for_popup(const bContext &C, const AssetShelfType *shelf_type);
AssetShelfType *type_find_from_idname(const StringRef idname);
/** \} */
/* -------------------------------------------------------------------- */
/** \name Asset Shelf Popup
* \{ */
uiBlock *popup_block_create(const bContext *C, ARegion *region, AssetShelfType *shelf_type);
void type_popup_unlink(const AssetShelfType &shelf_type);
/** \} */
/* -------------------------------------------------------------------- */
void type_unlink(const Main &bmain, const AssetShelfType &shelf_type);

View File

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

View File

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

View File

@ -13,6 +13,7 @@
#include "AS_asset_catalog_path.hh"
#include "AS_asset_library.hh"
#include "BLI_function_ref.hh"
#include "BLI_string.h"
#include "BKE_context.hh"
@ -76,14 +77,11 @@ void type_unregister(const AssetShelfType &shelf_type)
shelf_types.remove(it - shelf_types.begin());
}
bool type_poll(const bContext &C, const AssetShelfType *shelf_type, const int space_type)
static bool type_poll_no_spacetype_check(const bContext &C, const AssetShelfType *shelf_type)
{
if (!shelf_type) {
return false;
}
if (shelf_type->space_type && (space_type != shelf_type->space_type)) {
return false;
}
#ifndef NDEBUG
const Vector<std::unique_ptr<AssetShelfType>> &shelf_types = static_shelf_types();
@ -98,6 +96,31 @@ bool type_poll(const bContext &C, const AssetShelfType *shelf_type, const int sp
return !shelf_type->poll || shelf_type->poll(&C, shelf_type);
}
bool type_poll_for_popup(const bContext &C, const AssetShelfType *shelf_type)
{
return type_poll_no_spacetype_check(C, shelf_type);
}
/**
* Poll an asset shelf type for display as a permanent region in a space of a given type (the
* type's #bl_space_type).
*
* Popup asset shelves should use #type_poll_for_popup() instead.
*/
static bool type_poll_for_non_popup(const bContext &C,
const AssetShelfType *shelf_type,
const int space_type)
{
if (!shelf_type) {
return false;
}
if (shelf_type->space_type && (space_type != shelf_type->space_type)) {
return false;
}
return type_poll_no_spacetype_check(C, shelf_type);
}
AssetShelfType *type_find_from_idname(const StringRef idname)
{
for (const std::unique_ptr<AssetShelfType> &shelf_type : static_shelf_types()) {
@ -186,7 +209,8 @@ static AssetShelf *update_active_shelf(const bContext &C,
/* Case 1: */
if (shelf_regiondata.active_shelf &&
type_poll(C, ensure_shelf_has_type(*shelf_regiondata.active_shelf), space_type))
type_poll_for_non_popup(
C, ensure_shelf_has_type(*shelf_regiondata.active_shelf), space_type))
{
/* Not a strong precondition, but if this is wrong something weird might be going on. */
BLI_assert(shelf_regiondata.active_shelf == shelf_regiondata.shelves.first);
@ -200,7 +224,7 @@ static AssetShelf *update_active_shelf(const bContext &C,
continue;
}
if (type_poll(C, ensure_shelf_has_type(*shelf), space_type)) {
if (type_poll_for_non_popup(C, ensure_shelf_has_type(*shelf), space_type)) {
/* Found a valid previously activated shelf, reactivate it. */
activate_shelf(shelf_regiondata, *shelf);
return shelf;
@ -209,7 +233,7 @@ static AssetShelf *update_active_shelf(const bContext &C,
/* Case 3: */
for (const std::unique_ptr<AssetShelfType> &shelf_type : static_shelf_types()) {
if (type_poll(C, shelf_type.get(), space_type)) {
if (type_poll_for_non_popup(C, shelf_type.get(), space_type)) {
AssetShelf *new_shelf = create_shelf_from_type(*shelf_type);
BLI_addhead(&shelf_regiondata.shelves, new_shelf);
/* Moves ownership to the regiondata. */
@ -258,7 +282,7 @@ static bool asset_shelf_space_poll(const bContext *C, const SpaceLink *space_lin
{
/* Is there any asset shelf type registered that returns true for it's poll? */
for (const std::unique_ptr<AssetShelfType> &shelf_type : static_shelf_types()) {
if (type_poll(*C, shelf_type.get(), space_link->spacetype)) {
if (type_poll_for_non_popup(*C, shelf_type.get(), space_link->spacetype)) {
return true;
}
}
@ -842,6 +866,8 @@ void type_unlink(const Main &bmain, const AssetShelfType &shelf_type)
}
}
}
type_popup_unlink(shelf_type);
}
/** \} */

View File

@ -46,6 +46,8 @@ void send_redraw_notifier(const bContext &C);
AssetShelfType *ensure_shelf_has_type(AssetShelf &shelf);
AssetShelf *create_shelf_from_type(AssetShelfType &type);
void library_selector_draw(const bContext *C, uiLayout *layout, AssetShelf &shelf);
/**
* Deep-copies \a shelf_regiondata into newly allocated memory. Must be freed using
* #regiondata_free().

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"
@ -44,12 +45,13 @@ class AssetView : public ui::AbstractGridView {
* end of the string, for `fnmatch()` to work. */
char search_string[sizeof(AssetShelfSettings::search_string) + 2] = "";
std::optional<asset_system::AssetCatalogFilter> catalog_filter_ = std::nullopt;
bool is_popup_ = false;
friend class AssetViewItem;
friend class AssetDragController;
public:
AssetView(const AssetLibraryReference &library_ref, const AssetShelf &shelf);
AssetView(const AssetLibraryReference &library_ref, const AssetShelf &shelf, bool is_popup);
void build_items() override;
bool begin_filtering(const bContext &C) const override;
@ -70,6 +72,7 @@ class AssetViewItem : public ui::PreviewGridItem {
void disable_asset_drag();
void build_grid_tile(uiLayout &layout) const override;
void build_context_menu(bContext &C, uiLayout &column) const override;
void on_activate(bContext &C) override;
std::optional<bool> should_be_active() const override;
bool is_filtered_visible() const override;
@ -86,8 +89,10 @@ class AssetDragController : public ui::AbstractViewItemDragController {
void *create_drag_data() const override;
};
AssetView::AssetView(const AssetLibraryReference &library_ref, const AssetShelf &shelf)
: library_ref_(library_ref), shelf_(shelf)
AssetView::AssetView(const AssetLibraryReference &library_ref,
const AssetShelf &shelf,
const bool is_popup)
: library_ref_(library_ref), shelf_(shelf), is_popup_(is_popup)
{
if (shelf.settings.search_string[0]) {
BLI_strncpy_ensure_pad(
@ -236,6 +241,14 @@ void AssetViewItem::build_context_menu(bContext &C, uiLayout &column) const
}
}
void AssetViewItem::on_activate(bContext & /*C*/)
{
const AssetView &asset_view = dynamic_cast<const AssetView &>(this->get_view());
if (asset_view.is_popup_) {
UI_popup_menu_close_from_but(reinterpret_cast<uiBut *>(this->view_item_button()));
}
}
std::optional<bool> AssetViewItem::should_be_active() const
{
const AssetView &asset_view = dynamic_cast<const AssetView &>(this->get_view());
@ -290,7 +303,8 @@ void build_asset_view(uiLayout &layout,
BLI_assert(tile_width != 0);
BLI_assert(tile_height != 0);
std::unique_ptr asset_view = std::make_unique<AssetView>(library_ref, shelf);
const bool is_popup = region.regiontype == RGN_TYPE_TEMPORARY;
std::unique_ptr asset_view = std::make_unique<AssetView>(library_ref, shelf, is_popup);
asset_view->set_catalog_filter(catalog_filter_from_shelf_settings(shelf.settings, *library));
asset_view->set_tile_size(tile_width, tile_height);

View File

@ -174,32 +174,37 @@ void AssetCatalogSelectorTree::update_shelf_settings_from_enabled_catalogs()
});
}
void library_selector_draw(const bContext *C, uiLayout *layout, AssetShelf &shelf)
{
uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT);
PointerRNA shelf_ptr = RNA_pointer_create(&CTX_wm_screen(C)->id, &RNA_AssetShelf, &shelf);
uiLayout *row = uiLayoutRow(layout, true);
uiItemR(row, &shelf_ptr, "asset_library_reference", UI_ITEM_NONE, "", ICON_NONE);
if (shelf.settings.asset_library_reference.type != ASSET_LIBRARY_LOCAL) {
uiItemO(row, "", ICON_FILE_REFRESH, "ASSET_OT_library_refresh");
}
}
static void catalog_selector_panel_draw(const bContext *C, Panel *panel)
{
const AssetLibraryReference *library_ref = CTX_wm_asset_library_ref(C);
AssetShelf *shelf = active_shelf_from_context(C);
if (!shelf) {
return;
}
uiLayout *layout = panel->layout;
uiBlock *block = uiLayoutGetBlock(layout);
uiLayoutSetOperatorContext(layout, WM_OP_INVOKE_DEFAULT);
library_selector_draw(C, layout, *shelf);
PointerRNA shelf_ptr = RNA_pointer_create(&CTX_wm_screen(C)->id, &RNA_AssetShelf, shelf);
uiLayout *row = uiLayoutRow(layout, true);
uiItemR(row, &shelf_ptr, "asset_library_reference", UI_ITEM_NONE, "", ICON_NONE);
if (library_ref->type != ASSET_LIBRARY_LOCAL) {
uiItemO(row, "", ICON_FILE_REFRESH, "ASSET_OT_library_refresh");
}
asset_system::AssetLibrary *library = list::library_get_once_available(*library_ref);
asset_system::AssetLibrary *library = list::library_get_once_available(
shelf->settings.asset_library_reference);
if (!library) {
return;
}
uiBlock *block = uiLayoutGetBlock(layout);
ui::AbstractTreeView *tree_view = UI_block_add_view(
*block,
"asset catalog tree view",

View File

@ -0,0 +1,208 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edasset
*/
#include "asset_shelf.hh"
#include "BKE_screen.hh"
#include "BLT_translation.hh"
#include "UI_interface_c.hh"
#include "UI_tree_view.hh"
#include "ED_asset_filter.hh"
#include "ED_asset_list.hh"
#include "ED_asset_shelf.hh"
#include "RNA_access.hh"
#include "RNA_prototypes.h"
namespace blender::ed::asset::shelf {
class StaticPopupShelves {
public:
Vector<AssetShelf *> popup_shelves;
~StaticPopupShelves()
{
for (AssetShelf *shelf : popup_shelves) {
MEM_delete(shelf);
}
}
static Vector<AssetShelf *> &shelves()
{
static StaticPopupShelves storage;
return storage.popup_shelves;
}
};
void type_popup_unlink(const AssetShelfType &shelf_type)
{
for (AssetShelf *shelf : StaticPopupShelves::shelves()) {
if (shelf->type == &shelf_type) {
shelf->type = nullptr;
}
}
}
static AssetShelf *get_shelf_for_popup(const bContext *C, AssetShelfType &shelf_type)
{
Vector<AssetShelf *> &popup_shelves = StaticPopupShelves::shelves();
for (AssetShelf *shelf : popup_shelves) {
if (STREQ(shelf->idname, shelf_type.idname)) {
if (type_poll_for_popup(*C, ensure_shelf_has_type(*shelf))) {
return shelf;
}
break;
}
}
if (type_poll_for_popup(*C, &shelf_type)) {
AssetShelf *new_shelf = create_shelf_from_type(shelf_type);
new_shelf->settings.display_flag |= ASSETSHELF_SHOW_NAMES;
popup_shelves.append(new_shelf);
return new_shelf;
}
return nullptr;
}
class AssetCatalogTreeView : public ui::AbstractTreeView {
AssetShelf &shelf_;
asset_system::AssetCatalogTree catalog_tree_;
public:
AssetCatalogTreeView(const asset_system::AssetLibrary &library, AssetShelf &shelf)
: shelf_(shelf)
{
catalog_tree_ = build_filtered_catalog_tree(
library,
shelf_.settings.asset_library_reference,
[this](const asset_system::AssetRepresentation &asset) {
return (!shelf_.type->asset_poll || shelf_.type->asset_poll(shelf_.type, &asset));
});
}
void build_tree() override
{
if (catalog_tree_.is_empty()) {
auto &item = this->add_tree_item<ui::BasicTreeViewItem>(RPT_("No applicable assets found"),
ICON_INFO);
item.disable_interaction();
return;
}
auto &all_item = this->add_tree_item<ui::BasicTreeViewItem>(IFACE_("All"));
all_item.set_on_activate_fn([this](bContext &C, ui::BasicTreeViewItem &) {
settings_set_all_catalog_active(shelf_.settings);
send_redraw_notifier(C);
});
all_item.set_is_active_fn(
[this]() { return settings_is_all_catalog_active(shelf_.settings); });
all_item.uncollapse_by_default();
catalog_tree_.foreach_root_item([&, this](
const asset_system::AssetCatalogTreeItem &catalog_item) {
ui::BasicTreeViewItem &item = this->build_catalog_items_recursive(all_item, catalog_item);
item.uncollapse_by_default();
});
}
ui::BasicTreeViewItem &build_catalog_items_recursive(
ui::TreeViewOrItem &parent_view_item,
const asset_system::AssetCatalogTreeItem &catalog_item) const
{
ui::BasicTreeViewItem &view_item = parent_view_item.add_tree_item<ui::BasicTreeViewItem>(
catalog_item.get_name());
std::string catalog_path = catalog_item.catalog_path().str();
view_item.set_on_activate_fn([this, catalog_path](bContext &C, ui::BasicTreeViewItem &) {
settings_set_active_catalog(shelf_.settings, catalog_path);
send_redraw_notifier(C);
});
view_item.set_is_active_fn([this, catalog_path]() {
return settings_is_active_catalog(shelf_.settings, catalog_path);
});
catalog_item.foreach_child(
[&view_item, this](const asset_system::AssetCatalogTreeItem &child) {
build_catalog_items_recursive(view_item, child);
});
return view_item;
}
};
static void catalog_tree_draw(uiLayout &layout, AssetShelf &shelf)
{
const asset_system::AssetLibrary *library = list::library_get_once_available(
shelf.settings.asset_library_reference);
if (!library) {
return;
}
uiBlock *block = uiLayoutGetBlock(&layout);
ui::AbstractTreeView *tree_view = UI_block_add_view(
*block,
"asset shelf catalog tree view",
std::make_unique<AssetCatalogTreeView>(*library, shelf));
ui::TreeViewBuilder::build_tree_view(*tree_view, layout);
}
uiBlock *popup_block_create(const bContext *C, ARegion *region, AssetShelfType *shelf_type)
{
bScreen *screen = CTX_wm_screen(C);
uiBlock *block = UI_block_begin(C, region, "_popup", UI_EMBOSS);
UI_block_flag_enable(block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_POPOVER);
UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
UI_block_bounds_set_normal(block, 0.3f * U.widget_unit);
UI_block_direction_set(block, UI_DIR_DOWN);
AssetShelf *shelf = get_shelf_for_popup(C, *shelf_type);
if (!shelf) {
BLI_assert_unreachable();
return block;
}
const uiStyle *style = UI_style_get_dpi();
const int layout_width = UI_UNIT_X * 40;
const int left_col_width = 10 * UI_UNIT_X;
const int right_col_width = layout_width - left_col_width;
uiLayout *layout = UI_block_layout(
block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, layout_width, 0, 0, style);
PointerRNA library_ref_ptr = RNA_pointer_create(
&screen->id, &RNA_AssetLibraryReference, &shelf->settings.asset_library_reference);
uiLayoutSetContextPointer(layout, "asset_library_reference", &library_ref_ptr);
uiLayout *row = uiLayoutRow(layout, false);
uiLayout *catalogs_col = uiLayoutColumn(row, false);
uiLayoutSetUnitsX(catalogs_col, left_col_width / UI_UNIT_X);
uiLayoutSetFixedSize(catalogs_col, true);
library_selector_draw(C, catalogs_col, *shelf);
catalog_tree_draw(*catalogs_col, *shelf);
uiLayout *right_col = uiLayoutColumn(row, false);
uiLayout *sub = uiLayoutRow(right_col, false);
/* Same as file/asset browser header. */
PointerRNA shelf_ptr = RNA_pointer_create(&screen->id, &RNA_AssetShelf, shelf);
uiItemR(sub, &shelf_ptr, "search_filter", UI_ITEM_R_IMMEDIATE, "", ICON_VIEWZOOM);
uiLayout *asset_view_col = uiLayoutColumn(right_col, false);
uiLayoutSetUnitsX(asset_view_col, right_col_width / UI_UNIT_X);
uiLayoutSetFixedSize(asset_view_col, true);
build_asset_view(*asset_view_col, shelf->settings.asset_library_reference, *shelf, *C, *region);
return block;
}
} // namespace blender::ed::asset::shelf

View File

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

View File

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

View File

@ -423,16 +423,13 @@ 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);
}
@ -573,10 +570,6 @@ 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);
}
@ -717,9 +710,6 @@ 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);
}
@ -832,9 +822,6 @@ 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);
/* Ensure Palette by default. */

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,18 @@ 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);
p->brush = brush;
if (p->brush->gpencil_tool != GPAINT_TOOL_ERASE) {
p->eraser = gpencil_get_default_eraser(p->bmain, ts);
/* TODO: make this work again with "Smooth Eraser" essentials brush.
* See od gpencil_set_default_eraser and gpencil_set_default_eraser. */
p->eraser = p->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 +2047,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

@ -676,6 +676,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

@ -121,9 +121,5 @@ void apply_keyb_grid(
bool shift, bool ctrl, float *val, float fac1, float fac2, float fac3, int invert);
/* where else to go ? */
void unpack_menu(bContext *C,
const char *opname,
const char *id_name,
const char *abs_name,
const char *folder,
PackedFile *pf);
void unpack_menu(
bContext *C, const char *opname, const char *abs_name, const char *folder, PackedFile *pf);

View File

@ -710,6 +710,18 @@ uiLayout *UI_popup_menu_layout(uiPopupMenu *pup);
void UI_popup_menu_reports(bContext *C, ReportList *reports) ATTR_NONNULL();
int UI_popup_menu_invoke(bContext *C, const char *idname, ReportList *reports) ATTR_NONNULL(1, 2);
/**
* If \a block is displayed in a popup menu, tag it for closing.
* \param is_cancel: If set to true, the popup will be closed as being cancelled (e.g. when
* pressing escape) as opposed to being handled successfully.
*/
void UI_popup_menu_close(const uiBlock *block, bool is_cancel = false);
/**
* Version of #UI_popup_menu_close() that can be called on a button contained in a popup menu
* block. Convenience since the block may not be available.
*/
void UI_popup_menu_close_from_but(const uiBut *but, bool is_cancel = false);
/**
* Allow setting menu return value from externals.
* E.g. WM might need to do this for exiting files correctly.
@ -1491,6 +1503,10 @@ uiBut *uiDefIconMenuBut(uiBlock *block,
short height,
const char *tip);
/**
* Note that \a fun can set the #UI_BLOCK_KEEP_OPEN flag to the block it creates, to allow
* refreshing the popup. That is, redrawing the layout, potentially affecting the popup size.
*/
uiBut *uiDefBlockBut(uiBlock *block,
uiBlockCreateFunc func,
void *arg,
@ -2706,6 +2722,13 @@ void uiTemplateAssetView(uiLayout *layout,
const char *drag_opname,
PointerRNA *r_drag_op_properties);
namespace blender::ui {
void template_asset_shelf_popover(
uiLayout &layout, const bContext &C, StringRefNull asset_shelf_id, StringRef name, int icon);
}
void uiTemplateLightLinkingCollection(uiLayout *layout,
uiLayout *context_layout,
PointerRNA *ptr,

View File

@ -5,6 +5,7 @@
set(INC
.
../include
../asset
../../asset_system
../../blenkernel
../../blenloader
@ -64,6 +65,7 @@ set(SRC
regions/interface_regions.cc
interface_string_search.cc
interface_style.cc
templates/interface_template_asset_shelf_popover.cc
templates/interface_template_asset_view.cc
templates/interface_template_attribute_search.cc
templates/interface_template_bone_collection_tree.cc

View File

@ -4048,6 +4048,8 @@ static void ui_do_but_textedit(
if (changed || (retval == WM_UI_HANDLER_BREAK)) {
ED_region_tag_redraw(data->region);
/* In case of popup regions, tag for popup refreshing too (contents may have changed). */
ED_region_tag_refresh_ui(data->region);
}
}
@ -11503,6 +11505,8 @@ static int ui_handle_menus_recursive(bContext *C,
if (!menu->retvalue) {
ui_handle_viewlist_items_hover(event, menu->region);
}
/* Handle mouse clicks on overlapping view item button. */
ui_handle_view_item_event(C, event, but, menu->region);
if (do_towards_reinit) {
ui_mouse_motion_towards_reinit(menu, event->xy);

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

@ -979,6 +979,10 @@ uiBlock *ui_popup_block_refresh(bContext *C,
ARegion *butregion,
uiBut *but);
/**
* Note that callbacks can set the #UI_BLOCK_KEEP_OPEN flag to the block it creates, to allow
* refreshing the popup. That is, redrawing the layout, potentially affecting the popup size.
*/
uiPopupBlockHandle *ui_popup_block_create(bContext *C,
ARegion *butregion,
uiBut *but,

View File

@ -764,4 +764,14 @@ bool UI_popup_block_name_exists(const bScreen *screen, const blender::StringRef
return false;
}
void UI_popup_menu_close(const uiBlock *block, const bool is_cancel)
{
UI_popup_menu_retval_set(block, is_cancel ? UI_RETURN_CANCEL : UI_RETURN_OK, true);
}
void UI_popup_menu_close_from_but(const uiBut *but, const bool is_cancel)
{
UI_popup_menu_close(but->block, is_cancel);
}
/** \} */

View File

@ -897,6 +897,10 @@ uiPopupBlockHandle *ui_popup_block_create(bContext *C,
uiBlock *block = ui_popup_block_refresh(C, handle, butregion, but);
handle = block->handle;
if (block->flag & UI_BLOCK_KEEP_OPEN) {
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

@ -0,0 +1,67 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edinterface
*/
#include "BKE_context.hh"
#include "BKE_screen.hh"
#include "RNA_access.hh"
#include "UI_interface_c.hh"
#include "UI_resources.hh"
#include "interface_intern.hh"
#include "ED_asset_shelf.hh"
namespace blender::ui {
static uiBlock *asset_shelf_block_fn(bContext *C, ARegion *region, void *arg_shelf_type)
{
AssetShelfType *shelf_type = reinterpret_cast<AssetShelfType *>(arg_shelf_type);
return ed::asset::shelf::popup_block_create(C, region, shelf_type);
}
void template_asset_shelf_popover(uiLayout &layout,
const bContext &C,
const StringRefNull asset_shelf_id,
const StringRef name,
const BIFIconID icon)
{
AssetShelfType *shelf_type = ed::asset::shelf::type_find_from_idname(asset_shelf_id);
if (!shelf_type) {
RNA_warning("Asset shelf type not found: %s", asset_shelf_id.c_str());
return;
}
const ARegion *region = CTX_wm_region(&C);
const bool use_big_size = !RGN_TYPE_IS_HEADER_ANY(region->regiontype);
const bool use_preview_icon = use_big_size;
const short width = [&]() -> short {
if (use_big_size) {
return UI_UNIT_X * 6;
}
return UI_UNIT_X * (name.is_empty() ? 1.6f : 7);
}();
const short height = UI_UNIT_Y * (use_big_size ? 6 : 1);
uiBlock *block = uiLayoutGetBlock(&layout);
uiBut *but = uiDefBlockBut(
block, asset_shelf_block_fn, shelf_type, name, 0, 0, width, height, "Select an asset");
if (use_preview_icon) {
ui_def_but_icon(but, icon, UI_HAS_ICON | UI_BUT_ICON_PREVIEW);
}
else {
ui_def_but_icon(but, icon, UI_HAS_ICON);
UI_but_drawflag_enable(but, UI_BUT_ICON_LEFT);
}
if (ed::asset::shelf::type_poll_for_popup(C, shelf_type) == false) {
UI_but_flag_enable(but, UI_BUT_DISABLED);
}
}
} // namespace blender::ui

View File

@ -962,6 +962,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
TemplateID *template_ui = (TemplateID *)arg_litem;
PointerRNA idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
ID *id = static_cast<ID *>(idptr.data);
Main *bmain = CTX_data_main_from_id(C, id);
const int event = POINTER_AS_INT(arg_event);
const char *undo_push_label = nullptr;
@ -1012,7 +1013,6 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
break;
case UI_ID_LOCAL:
if (id) {
Main *bmain = CTX_data_main(C);
if (CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) {
template_id_liboverride_hierarchy_make(C, bmain, template_ui, &idptr, &undo_push_label);
}
@ -1033,7 +1033,6 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
break;
case UI_ID_OVERRIDE:
if (id && ID_IS_OVERRIDE_LIBRARY(id)) {
Main *bmain = CTX_data_main(C);
if (CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) {
template_id_liboverride_hierarchy_make(C, bmain, template_ui, &idptr, &undo_push_label);
}
@ -1054,14 +1053,12 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
/* make copy */
if (do_scene_obj) {
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
blender::ed::object::object_single_user_make(bmain, scene, (Object *)id);
WM_event_add_notifier(C, NC_WINDOW, nullptr);
DEG_relations_tag_update(bmain);
}
else {
Main *bmain = CTX_data_main(C);
id_single_user(C, id, &template_ui->ptr, template_ui->prop);
DEG_relations_tag_update(bmain);
}
@ -1767,10 +1764,12 @@ static void ui_template_id(uiLayout *layout,
flag |= UI_ID_OPEN;
}
Main *bmain = (ptr->owner_id) ? CTX_data_main_from_id(C, ptr->owner_id) : CTX_data_main(C);
StructRNA *type = RNA_property_pointer_type(ptr, prop);
short idcode = RNA_type_to_ID_code(type);
template_ui->idcode = idcode;
template_ui->idlb = which_libbase(CTX_data_main(C), idcode);
template_ui->idlb = which_libbase(bmain, idcode);
/* create UI elements for this template
* - template_ID makes a copy of the template data and assigns it to the relevant buttons
@ -6363,13 +6362,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;
@ -6378,13 +6411,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);
@ -6392,25 +6425,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);
@ -6435,14 +6469,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,
@ -6454,23 +6487,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

@ -205,12 +205,12 @@ GridViewItemDropTarget::GridViewItemDropTarget(AbstractGridView &view) : view_(v
* side(s) as well.
*/
class BuildOnlyVisibleButtonsHelper {
const View2D &v2d_;
const AbstractGridView &grid_view_;
const GridViewStyle &style_;
const int cols_per_row_ = 0;
/* Indices of items within the view. Calculated by constructor */
IndexRange visible_items_range_{};
/* Indices of items within the view. Calculated by constructor. If this is unset it means all
* items/buttons should be drawn. */
std::optional<IndexRange> visible_items_range_;
public:
BuildOnlyVisibleButtonsHelper(const View2D &v2d,
@ -222,30 +222,34 @@ class BuildOnlyVisibleButtonsHelper {
void fill_layout_after_visible(uiBlock &block) const;
private:
IndexRange get_visible_range() const;
IndexRange get_visible_range(const View2D &v2d) const;
void add_spacer_button(uiBlock &block, int row_count) const;
};
BuildOnlyVisibleButtonsHelper::BuildOnlyVisibleButtonsHelper(const View2D &v2d,
const AbstractGridView &grid_view,
const int cols_per_row)
: v2d_(v2d), grid_view_(grid_view), style_(grid_view.get_style()), cols_per_row_(cols_per_row)
: grid_view_(grid_view), style_(grid_view.get_style()), cols_per_row_(cols_per_row)
{
visible_items_range_ = this->get_visible_range();
if ((v2d.flag & V2D_IS_INIT) && grid_view.get_item_count_filtered()) {
visible_items_range_ = this->get_visible_range(v2d);
}
}
IndexRange BuildOnlyVisibleButtonsHelper::get_visible_range() const
IndexRange BuildOnlyVisibleButtonsHelper::get_visible_range(const View2D &v2d) const
{
BLI_assert(v2d.flag & V2D_IS_INIT);
int first_idx_in_view = 0;
const float scroll_ofs_y = std::abs(v2d_.cur.ymax - v2d_.tot.ymax);
const float scroll_ofs_y = std::abs(v2d.cur.ymax - v2d.tot.ymax);
if (!IS_EQF(scroll_ofs_y, 0)) {
const int scrolled_away_rows = int(scroll_ofs_y) / style_.tile_height;
first_idx_in_view = scrolled_away_rows * cols_per_row_;
}
const int view_height = BLI_rcti_size_y(&v2d_.mask);
const int view_height = BLI_rcti_size_y(&v2d.mask);
const int count_rows_in_view = std::max(view_height / style_.tile_height, 1);
const int max_items_in_view = (count_rows_in_view + 1) * cols_per_row_;
@ -255,12 +259,15 @@ IndexRange BuildOnlyVisibleButtonsHelper::get_visible_range() const
bool BuildOnlyVisibleButtonsHelper::is_item_visible(const int item_idx) const
{
return visible_items_range_.contains(item_idx);
return !visible_items_range_ || visible_items_range_->contains(item_idx);
}
void BuildOnlyVisibleButtonsHelper::fill_layout_before_visible(uiBlock &block) const
{
const int first_idx_in_view = visible_items_range_.first();
if (!visible_items_range_ || visible_items_range_->is_empty()) {
return;
}
const int first_idx_in_view = visible_items_range_->first();
if (first_idx_in_view < 1) {
return;
}
@ -271,8 +278,11 @@ void BuildOnlyVisibleButtonsHelper::fill_layout_before_visible(uiBlock &block) c
void BuildOnlyVisibleButtonsHelper::fill_layout_after_visible(uiBlock &block) const
{
if (!visible_items_range_ || visible_items_range_->is_empty()) {
return;
}
const int last_item_idx = grid_view_.get_item_count_filtered() - 1;
const int last_visible_idx = visible_items_range_.last();
const int last_visible_idx = visible_items_range_->last();
if (last_item_idx > last_visible_idx) {
const int remaining_rows = (cols_per_row_ > 0) ? ceilf((last_item_idx - last_visible_idx) /
@ -346,7 +356,13 @@ void GridViewLayoutBuilder::build_from_view(const AbstractGridView &grid_view,
uiLayout &layout = *uiLayoutColumn(parent_layout, true);
const GridViewStyle &style = grid_view.get_style();
const int cols_per_row = std::max(uiLayoutGetWidth(&layout) / style.tile_width, 1);
/* We might not actually know the width available for the grid view. Let's just assume that
* either there is a fixed width defined via #uiLayoutSetUnitsX() or that the layout is close to
* the root level and inherits its width. Might need a more reliable method. */
const int guessed_layout_width = (uiLayoutGetUnitsX(parent_layout) > 0) ?
uiLayoutGetUnitsX(parent_layout) * UI_UNIT_X :
uiLayoutGetWidth(parent_layout);
const int cols_per_row = std::max(guessed_layout_width / style.tile_width, 1);
BuildOnlyVisibleButtonsHelper build_visible_helper(v2d, grid_view, cols_per_row);

View File

@ -35,6 +35,7 @@
#include "BKE_anim_data.hh"
#include "BKE_animsys.h"
#include "BKE_appdir.hh"
#include "BKE_asset.hh"
#include "BKE_blender_copybuffer.hh"
#include "BKE_brush.hh"
#include "BKE_context.hh"
@ -822,12 +823,15 @@ void MATERIAL_OT_new(wmOperatorType *ot)
static int new_texture_exec(bContext *C, wmOperator * /*op*/)
{
Tex *tex = static_cast<Tex *>(CTX_data_pointer_get_type(C, "texture", &RNA_Texture).data);
Main *bmain = CTX_data_main(C);
PointerRNA ptr;
PropertyRNA *prop;
UI_context_active_but_prop_get_templateID(C, &ptr, &prop);
Main *bmain = (ptr.owner_id) ? CTX_data_main_from_id(C, ptr.owner_id) : CTX_data_main(C);
/* add or copy texture */
Tex *tex = static_cast<Tex *>(CTX_data_pointer_get_type(C, "texture", &RNA_Texture).data);
if (tex) {
tex = (Tex *)BKE_id_copy(bmain, &tex->id);
}
@ -836,8 +840,6 @@ static int new_texture_exec(bContext *C, wmOperator * /*op*/)
}
/* hook into UI */
UI_context_active_but_prop_get_templateID(C, &ptr, &prop);
if (prop) {
/* when creating new ID blocks, use is already 1, but RNA
* pointer use also increases user, so this compensates it */

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,814 @@
/* 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_asset_set(paint, brush, brush_asset_reference)) {
/* 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);
}
/* FIXME Quick dirty hack to generate a weak ref from 'raw' paths.
* This needs to be properly implemented in assetlib code.
*/
static AssetWeakReference brush_asset_create_weakref_hack(const bUserAssetLibrary *user_asset_lib,
const std::string &file_path)
{
AssetWeakReference asset_weak_ref{};
StringRef asset_root_path = user_asset_lib->dirpath;
BLI_assert(file_path.find(asset_root_path) == 0);
std::string relative_asset_path = file_path.substr(size_t(asset_root_path.size()) + 1);
asset_weak_ref.asset_library_type = ASSET_LIBRARY_CUSTOM;
asset_weak_ref.asset_library_identifier = BLI_strdup(user_asset_lib->name);
asset_weak_ref.relative_asset_identifier = BLI_strdupn(relative_asset_path.c_str(),
relative_asset_path.size());
return asset_weak_ref;
}
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());
}
const std::optional<std::string> final_full_asset_filepath = bke::asset_edit_id_save_as(
*bmain, brush->id, name, *user_library, *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);
AssetWeakReference new_brush_weak_ref = brush_asset_create_weakref_hack(
user_library, *final_full_asset_filepath);
brush = reinterpret_cast<Brush *>(
bke::asset_edit_id_from_weak_reference(*bmain, ID_BR, new_brush_weak_ref));
if (!BKE_paint_brush_asset_set(paint, brush, new_brush_weak_ref)) {
/* 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_editable(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_editable(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 ((brush->id.tag & LIB_TAG_ASSET_EDIT_MAIN) == 0) {
return false;
}
if (!(paint->brush_asset_reference && ID_IS_ASSET(brush))) {
return false;
}
if (!bke::asset_edit_id_is_editable(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 && (brush->id.tag & LIB_TAG_ASSET_EDIT_MAIN);
}
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

@ -108,6 +108,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

@ -420,19 +420,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 */
@ -577,24 +575,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);

View File

@ -4200,8 +4200,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;
@ -4220,18 +4221,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);
@ -4239,7 +4242,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) {
@ -4257,14 +4259,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

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

@ -797,15 +797,14 @@ static int sound_unpack_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
int method = RNA_enum_get(op->ptr, "method");
bSound *sound = nullptr;
Editing *ed = CTX_data_scene(C)->ed;
/* find the supplied image by name */
if (RNA_struct_property_is_set(op->ptr, "id")) {
char sndname[MAX_ID_NAME - 2];
RNA_string_get(op->ptr, "id", sndname);
sound = static_cast<bSound *>(BLI_findstring(&bmain->sounds, sndname, offsetof(ID, name) + 2));
if (!ed || !ed->act_seq || ed->act_seq->type != SEQ_TYPE_SOUND_RAM) {
return OPERATOR_CANCELLED;
}
bSound *sound = ed->act_seq->sound;
if (!sound || !sound->packedfile) {
return OPERATOR_CANCELLED;
}
@ -824,17 +823,12 @@ static int sound_unpack_exec(bContext *C, wmOperator *op)
static int sound_unpack_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
{
Editing *ed = CTX_data_scene(C)->ed;
bSound *sound;
if (RNA_struct_property_is_set(op->ptr, "id")) {
return sound_unpack_exec(C, op);
}
if (!ed || !ed->act_seq || ed->act_seq->type != SEQ_TYPE_SOUND_RAM) {
return OPERATOR_CANCELLED;
}
sound = ed->act_seq->sound;
bSound *sound = ed->act_seq->sound;
if (!sound || !sound->packedfile) {
return OPERATOR_CANCELLED;
@ -846,8 +840,7 @@ static int sound_unpack_invoke(bContext *C, wmOperator *op, const wmEvent * /*ev
"AutoPack is enabled, so image will be packed again on file save");
}
unpack_menu(
C, "SOUND_OT_unpack", sound->id.name + 2, sound->filepath, "sounds", sound->packedfile);
unpack_menu(C, "SOUND_OT_unpack", sound->filepath, "sounds", sound->packedfile);
return OPERATOR_FINISHED;
}
@ -870,9 +863,6 @@ static void SOUND_OT_unpack(wmOperatorType *ot)
/* properties */
RNA_def_enum(
ot->srna, "method", rna_enum_unpack_method_items, PF_USE_LOCAL, "Method", "How to unpack");
/* XXX: weak!, will fail with library, name collisions */
RNA_def_string(
ot->srna, "id", nullptr, MAX_ID_NAME - 2, "Sound Name", "Sound data-block name to unpack");
}
/* ******************************************************* */

View File

@ -1323,7 +1323,6 @@ static Image *image_open_single(Main *bmain,
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);
ImageUser *iuser = nullptr;
@ -1338,6 +1337,11 @@ static int image_open_exec(bContext *C, wmOperator *op)
image_open_init(C, op);
}
ImageOpenData *iod = static_cast<ImageOpenData *>(op->customdata);
Main *bmain = (iod->pprop.ptr.owner_id) ? CTX_data_main_from_id(C, iod->pprop.ptr.owner_id) :
CTX_data_main(C);
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);
@ -1357,9 +1361,6 @@ static int image_open_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
/* 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 */
@ -1816,6 +1817,7 @@ void IMAGE_OT_replace(wmOperatorType *ot)
* \{ */
struct ImageSaveData {
Main *bmain;
ImageUser *iuser;
Image *image;
ImageSaveOptions opts;
@ -1855,16 +1857,16 @@ static bool save_image_op(
static ImageSaveData *image_save_as_init(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Image *image = image_from_context(C);
ImageUser *iuser = image_user_from_context(C);
Scene *scene = CTX_data_scene(C);
ImageSaveData *isd = static_cast<ImageSaveData *>(MEM_callocN(sizeof(*isd), __func__));
isd->bmain = CTX_data_main_from_id(C, &image->id);
isd->image = image;
isd->iuser = iuser;
if (!BKE_image_save_options_init(&isd->opts, bmain, scene, image, iuser, true, false)) {
if (!BKE_image_save_options_init(&isd->opts, isd->bmain, scene, image, iuser, true, false)) {
BKE_image_save_options_free(&isd->opts);
MEM_freeN(isd);
return nullptr;
@ -1910,7 +1912,6 @@ static void image_save_as_free(wmOperator *op)
static int image_save_as_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
ImageSaveData *isd;
if (op->customdata) {
@ -1923,10 +1924,10 @@ static int image_save_as_exec(bContext *C, wmOperator *op)
}
}
image_save_options_from_op(bmain, &isd->opts, op);
image_save_options_from_op(isd->bmain, &isd->opts, op);
BKE_image_save_options_update(&isd->opts, isd->image);
save_image_op(bmain, isd->image, isd->iuser, op, &isd->opts);
save_image_op(isd->bmain, isd->image, isd->iuser, op, &isd->opts);
if (isd->opts.save_copy == false) {
BKE_image_free_packedfiles(isd->image);
@ -1937,12 +1938,11 @@ static int image_save_as_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
static bool image_save_as_check(bContext *C, wmOperator *op)
static bool image_save_as_check(bContext * /*C*/, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
ImageSaveData *isd = static_cast<ImageSaveData *>(op->customdata);
image_save_options_from_op(bmain, &isd->opts, op);
image_save_options_from_op(isd->bmain, &isd->opts, op);
BKE_image_save_options_update(&isd->opts, isd->image);
return WM_operator_filesel_ensure_ext_imtype(op, &isd->opts.im_format);
@ -2124,8 +2124,8 @@ static bool image_save_poll(bContext *C)
static int image_save_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Image *image = image_from_context(C);
Main *bmain = CTX_data_main_from_id(C, &image->id);
ImageUser *iuser = image_user_from_context(C);
Scene *scene = CTX_data_scene(C);
ImageSaveOptions opts;
@ -2458,8 +2458,8 @@ void IMAGE_OT_save_all_modified(wmOperatorType *ot)
static int image_reload_exec(bContext *C, wmOperator * /*op*/)
{
Main *bmain = CTX_data_main(C);
Image *ima = image_from_context(C);
Main *bmain = CTX_data_main_from_id(C, &ima->id);
ImageUser *iuser = image_user_from_context(C);
if (!ima) {
@ -2467,7 +2467,7 @@ static int image_reload_exec(bContext *C, wmOperator * /*op*/)
}
/* XXX BKE_packedfile_unpack_image frees image buffers */
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
BKE_image_signal(bmain, ima, iuser, IMA_SIGNAL_RELOAD);
DEG_id_tag_update(&ima->id, 0);
@ -2528,9 +2528,7 @@ static void image_new_free(wmOperator *op)
static int image_new_exec(bContext *C, wmOperator *op)
{
SpaceImage *sima;
Image *ima;
Main *bmain;
PropertyRNA *prop;
char name_buffer[MAX_ID_NAME - 2];
const char *name;
@ -2538,9 +2536,12 @@ static int image_new_exec(bContext *C, wmOperator *op)
int width, height, floatbuf, gen_type, alpha;
int stereo3d;
ImageNewData *data = image_new_init(C, op);
/* retrieve state */
sima = CTX_wm_space_image(C);
bmain = CTX_data_main(C);
SpaceImage *sima = CTX_wm_space_image(C);
Main *bmain = (data->pprop.ptr.owner_id) ? CTX_data_main_from_id(C, data->pprop.ptr.owner_id) :
CTX_data_main(C);
prop = RNA_struct_find_property(op->ptr, "name");
RNA_property_string_get(op->ptr, prop, name_buffer);
@ -2582,8 +2583,6 @@ static int image_new_exec(bContext *C, wmOperator *op)
}
/* hook into UI */
ImageNewData *data = image_new_init(C, op);
if (data->pprop.prop) {
/* when creating new ID blocks, use is already 1, but RNA
* pointer use also increases user, so this compensates it */
@ -3255,28 +3254,16 @@ void IMAGE_OT_resize(wmOperatorType *ot)
/** \name Pack Operator
* \{ */
static bool image_pack_test(bContext *C, wmOperator *op)
static int image_pack_exec(bContext *C, wmOperator *op)
{
Image *ima = image_from_context(C);
if (!ima) {
return false;
return OPERATOR_CANCELLED;
}
if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE)) {
BKE_report(op->reports, RPT_ERROR, "Packing movies or image sequences not supported");
return false;
}
return true;
}
static int image_pack_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Image *ima = image_from_context(C);
if (!image_pack_test(C, op)) {
return OPERATOR_CANCELLED;
}
@ -3284,6 +3271,7 @@ static int image_pack_exec(bContext *C, wmOperator *op)
BKE_image_memorypack(ima);
}
else {
Main *bmain = CTX_data_main_from_id(C, &ima->id);
BKE_image_packfiles(op->reports, ima, ID_BLEND_PATH(bmain, &ima->id));
}
@ -3314,20 +3302,9 @@ void IMAGE_OT_pack(wmOperatorType *ot)
static int image_unpack_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Image *ima = image_from_context(C);
int method = RNA_enum_get(op->ptr, "method");
/* find the supplied image by name */
if (RNA_struct_property_is_set(op->ptr, "id")) {
char imaname[MAX_ID_NAME - 2];
RNA_string_get(op->ptr, "id", imaname);
ima = static_cast<Image *>(BLI_findstring(&bmain->images, imaname, offsetof(ID, name) + 2));
if (!ima) {
ima = image_from_context(C);
}
}
if (!ima || !BKE_image_has_packedfile(ima)) {
return OPERATOR_CANCELLED;
}
@ -3344,9 +3321,10 @@ static int image_unpack_exec(bContext *C, wmOperator *op)
}
/* XXX BKE_packedfile_unpack_image frees image buffers */
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
Main *bmain = CTX_data_main_from_id(C, &ima->id);
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
BKE_packedfile_unpack_image(CTX_data_main(C), op->reports, ima, ePF_FileStatus(method));
BKE_packedfile_unpack_image(bmain, op->reports, ima, ePF_FileStatus(method));
WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima);
@ -3357,10 +3335,6 @@ static int image_unpack_invoke(bContext *C, wmOperator *op, const wmEvent * /*ev
{
Image *ima = image_from_context(C);
if (RNA_struct_property_is_set(op->ptr, "id")) {
return image_unpack_exec(C, op);
}
if (!ima || !BKE_image_has_packedfile(ima)) {
return OPERATOR_CANCELLED;
}
@ -3378,7 +3352,6 @@ static int image_unpack_invoke(bContext *C, wmOperator *op, const wmEvent * /*ev
unpack_menu(C,
"IMAGE_OT_unpack",
ima->id.name + 2,
ima->filepath,
"textures",
BKE_image_has_packedfile(ima) ?
@ -3405,9 +3378,6 @@ void IMAGE_OT_unpack(wmOperatorType *ot)
/* properties */
RNA_def_enum(
ot->srna, "method", rna_enum_unpack_method_items, PF_USE_LOCAL, "Method", "How to unpack");
/* XXX, weak!, will fail with library, name collisions */
RNA_def_string(
ot->srna, "id", nullptr, MAX_ID_NAME - 2, "Image Name", "Image data-block name to unpack");
}
/** \} */

View File

@ -335,9 +335,9 @@ void NODE_OT_clipboard_copy(wmOperatorType *ot)
static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
SpaceNode &snode = *CTX_wm_space_node(C);
bNodeTree &tree = *snode.edittree;
Main *bmain = CTX_data_main_from_id(C, &tree.id);
NodeClipboard &clipboard = get_node_clipboard();
if (clipboard.nodes.is_empty()) {
@ -351,7 +351,7 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
"Some nodes references to other IDs could not be restored, will be left empty");
}
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
node_deselect_all(tree);

View File

@ -95,11 +95,12 @@ static void add_group_input_node_fn(nodes::LinkSearchOpParams &params)
nullptr);
socket_iface->init_from_socket_instance(&params.socket);
params.node_tree.tree_interface.active_item_set(&socket_iface->item);
Main *bmain = CTX_data_main_from_id(&params.C, &params.node_tree.id);
bNode &group_input = params.add_node("NodeGroupInput");
/* This is necessary to create the new sockets in the other input nodes. */
ED_node_tree_propagate_change(&params.C, CTX_data_main(&params.C), &params.node_tree);
ED_node_tree_propagate_change(&params.C, bmain, &params.node_tree);
/* Hide the new input in all other group input nodes, to avoid making them taller. */
for (bNode *node : params.node_tree.all_nodes()) {
@ -123,8 +124,7 @@ static void add_group_input_node_fn(nodes::LinkSearchOpParams &params)
socket->flag &= ~SOCK_HIDDEN;
nodeAddLink(&params.node_tree, &group_input, socket, &params.node, &params.socket);
bke::node_socket_move_default_value(
*CTX_data_main(&params.C), params.node_tree, params.socket, *socket);
bke::node_socket_move_default_value(*bmain, params.node_tree, params.socket, *socket);
}
}
@ -201,7 +201,7 @@ static void search_link_ops_for_asset_metadata(const bNodeTree &node_tree,
search_link_ops.append(
{asset_name + " " + UI_MENU_ARROW_SEP + socket_name,
[&asset, socket_property, in_out](nodes::LinkSearchOpParams &params) {
Main &bmain = *CTX_data_main(&params.C);
Main &bmain = *CTX_data_main_from_id(&params.C, &params.node_tree.id);
bNode &node = params.add_node(params.node_tree.typeinfo->group_idname);
node.flag &= ~NODE_OPTIONS;
@ -345,7 +345,6 @@ static void link_drag_search_update_fn(
static void link_drag_search_exec_fn(bContext *C, void *arg1, void *arg2)
{
Main &bmain = *CTX_data_main(C);
SpaceNode &snode = *CTX_wm_space_node(C);
bNodeTree &node_tree = *snode.edittree;
LinkDragSearchStorage &storage = *static_cast<LinkDragSearchStorage *>(arg1);
@ -379,6 +378,7 @@ static void link_drag_search_exec_fn(bContext *C, void *arg1, void *arg2)
/* Ideally it would be possible to tag the node tree in some way so it updates only after the
* translate operation is finished, but normally moving nodes around doesn't cause updates. */
Main &bmain = *CTX_data_main_from_id(C, &node_tree.id);
ED_node_tree_propagate_change(C, &bmain, &node_tree);
/* Start translation operator with the new node. */

View File

@ -71,8 +71,8 @@ static void position_node_based_on_mouse(bNode &node, const float2 &location)
bNode *add_node(const bContext &C, const StringRef idname, const float2 &location)
{
SpaceNode &snode = *CTX_wm_space_node(&C);
Main &bmain = *CTX_data_main(&C);
bNodeTree &node_tree = *snode.edittree;
Main &bmain = *CTX_data_main_from_id(&C, &node_tree.id);
node_deselect_all(node_tree);
@ -93,8 +93,8 @@ bNode *add_node(const bContext &C, const StringRef idname, const float2 &locatio
bNode *add_static_node(const bContext &C, int type, const float2 &location)
{
SpaceNode &snode = *CTX_wm_space_node(&C);
Main &bmain = *CTX_data_main(&C);
bNodeTree &node_tree = *snode.edittree;
Main &bmain = *CTX_data_main_from_id(&C, &node_tree.id);
node_deselect_all(node_tree);
@ -145,6 +145,7 @@ static int add_reroute_exec(bContext *C, wmOperator *op)
const ARegion &region = *CTX_wm_region(C);
SpaceNode &snode = *CTX_wm_space_node(C);
bNodeTree &ntree = *snode.edittree;
Main &bmain = *CTX_data_main_from_id(C, &ntree.id);
Vector<float2> path;
RNA_BEGIN (op->ptr, itemptr, "path") {
@ -168,7 +169,7 @@ static int add_reroute_exec(bContext *C, wmOperator *op)
ntree.ensure_topology_cache();
const Vector<bNode *> frame_nodes = ntree.nodes_by_type("NodeFrame");
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
ED_preview_kill_jobs(CTX_wm_manager(C), &bmain);
/* All link "cuts" that start at a particular output socket. Deduplicating new reroutes per
* output socket is useful because it allows reusing reroutes for connected intersections.
@ -223,7 +224,7 @@ static int add_reroute_exec(bContext *C, wmOperator *op)
}
}
ED_node_tree_propagate_change(C, CTX_data_main(C), &ntree);
ED_node_tree_propagate_change(C, &bmain, &ntree);
return OPERATOR_FINISHED;
}
@ -291,9 +292,9 @@ static bool node_group_add_poll(const bNodeTree &node_tree,
static int node_add_group_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
Main *bmain = CTX_data_main_from_id(C, &ntree->id);
bNodeTree *node_group = reinterpret_cast<bNodeTree *>(
WM_operator_properties_id_lookup_from_name_or_session_uid(bmain, op->ptr, ID_NT));
@ -304,7 +305,7 @@ static int node_add_group_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
const char *node_idname = node_group_idname(C);
if (node_idname[0] == '\0') {
@ -398,9 +399,9 @@ static bool add_node_group_asset(const bContext &C,
const asset_system::AssetRepresentation &asset,
ReportList &reports)
{
Main &bmain = *CTX_data_main(&C);
SpaceNode &snode = *CTX_wm_space_node(&C);
bNodeTree &edit_tree = *snode.edittree;
Main &bmain = *CTX_data_main_from_id(&C, &edit_tree.id);
bNodeTree *node_group = reinterpret_cast<bNodeTree *>(
asset::asset_local_id_ensure_imported(bmain, asset));
@ -414,7 +415,7 @@ static bool add_node_group_asset(const bContext &C,
return false;
}
ED_preview_kill_jobs(CTX_wm_manager(&C), CTX_data_main(&C));
ED_preview_kill_jobs(CTX_wm_manager(&C), &bmain);
bNode *group_node = add_node(
C, ntreeTypeFind(node_group->idname)->group_idname, snode.runtime->cursor);
@ -510,9 +511,9 @@ void NODE_OT_add_group_asset(wmOperatorType *ot)
static int node_add_object_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
Main *bmain = CTX_data_main_from_id(C, &ntree->id);
Object *object = reinterpret_cast<Object *>(
WM_operator_properties_id_lookup_from_name_or_session_uid(bmain, op->ptr, ID_OB));
@ -521,7 +522,7 @@ static int node_add_object_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
bNode *object_node = add_static_node(*C, GEO_NODE_OBJECT_INFO, snode->runtime->cursor);
if (!object_node) {
@ -596,9 +597,9 @@ void NODE_OT_add_object(wmOperatorType *ot)
static int node_add_collection_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
SpaceNode &snode = *CTX_wm_space_node(C);
bNodeTree &ntree = *snode.edittree;
Main *bmain = CTX_data_main_from_id(C, &ntree.id);
Collection *collection = reinterpret_cast<Collection *>(
WM_operator_properties_id_lookup_from_name_or_session_uid(bmain, op->ptr, ID_GR));
@ -607,7 +608,7 @@ static int node_add_collection_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
bNode *collection_node = add_static_node(*C, GEO_NODE_COLLECTION_INFO, snode.runtime->cursor);
if (!collection_node) {
@ -736,8 +737,10 @@ static int node_add_file_modal(bContext *C, wmOperator *op, const wmEvent *event
static int node_add_file_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
SpaceNode &snode = *CTX_wm_space_node(C);
bNodeTree &node_tree = *snode.edittree;
Main *bmain = CTX_data_main_from_id(C, &node_tree.id);
int type = 0;
switch (snode.nodetree->type) {
case NTREE_SHADER:
@ -807,14 +810,13 @@ static int node_add_file_exec(bContext *C, wmOperator *op)
}
/* Set new nodes as selected. */
bNodeTree &node_tree = *snode.edittree;
node_deselect_all(node_tree);
for (bNode *node : nodes) {
nodeSetSelected(node, true);
}
ED_node_set_active(bmain, &snode, &node_tree, nodes[0], nullptr);
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
ED_node_tree_propagate_change(C, bmain, snode.edittree);
DEG_relations_tag_update(bmain);
@ -898,15 +900,16 @@ static bool node_add_mask_poll(bContext *C)
static int node_add_mask_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
SpaceNode &snode = *CTX_wm_space_node(C);
bNodeTree &node_tree = *snode.edittree;
Main *bmain = CTX_data_main_from_id(C, &node_tree.id);
ID *mask = WM_operator_properties_id_lookup_from_name_or_session_uid(bmain, op->ptr, ID_MSK);
if (!mask) {
return OPERATOR_CANCELLED;
}
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
bNode *node = add_static_node(*C, CMP_NODE_MASK, snode.runtime->cursor);
@ -949,9 +952,9 @@ void NODE_OT_add_mask(wmOperatorType *ot)
static int node_add_material_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
Main *bmain = CTX_data_main_from_id(C, &ntree->id);
Material *material = reinterpret_cast<Material *>(
WM_operator_properties_id_lookup_from_name_or_session_uid(bmain, op->ptr, ID_MA));
@ -960,7 +963,7 @@ static int node_add_material_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
bNode *material_node = add_static_node(*C, GEO_NODE_INPUT_MATERIAL, snode->runtime->cursor);
if (!material_node) {
@ -1028,16 +1031,10 @@ void NODE_OT_add_material(wmOperatorType *ot)
static int new_node_tree_exec(bContext *C, wmOperator *op)
{
SpaceNode *snode = CTX_wm_space_node(C);
Main *bmain = CTX_data_main(C);
bNodeTree *ntree;
PointerRNA ptr;
PropertyRNA *prop;
const char *idname;
char treename_buf[MAX_ID_NAME - 2];
const char *treename;
if (RNA_struct_property_is_set(op->ptr, "type")) {
prop = RNA_struct_find_property(op->ptr, "type");
PropertyRNA *prop = RNA_struct_find_property(op->ptr, "type");
RNA_property_enum_identifier(C, op->ptr, prop, RNA_property_enum_get(op->ptr, prop), &idname);
}
else if (snode) {
@ -1052,6 +1049,8 @@ static int new_node_tree_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
char treename_buf[MAX_ID_NAME - 2];
const char *treename;
if (RNA_struct_property_is_set(op->ptr, "name")) {
RNA_string_get(op->ptr, "name", treename_buf);
treename = treename_buf;
@ -1061,11 +1060,16 @@ static int new_node_tree_exec(bContext *C, wmOperator *op)
treename = type->ui_name;
}
ntree = ntreeAddTree(bmain, treename, idname);
/* Hook into UI. */
PointerRNA ptr;
PropertyRNA *prop;
UI_context_active_but_prop_get_templateID(C, &ptr, &prop);
Main *bmain = (ptr.owner_id) ? CTX_data_main_from_id(C, ptr.owner_id) : CTX_data_main(C);
/* Add node tree and assign. */
bNodeTree *ntree = ntreeAddTree(bmain, treename, idname);
if (prop) {
/* #RNA_property_pointer_set increases the user count, fixed here as the editor is the initial
* user. */

View File

@ -2361,9 +2361,9 @@ static void node_draw_sockets(const View2D &v2d,
static void node_panel_toggle_button_cb(bContext *C, void *panel_state_argv, void *ntree_argv)
{
Main *bmain = CTX_data_main(C);
bNodePanelState *panel_state = static_cast<bNodePanelState *>(panel_state_argv);
bNodeTree *ntree = static_cast<bNodeTree *>(ntree_argv);
Main *bmain = CTX_data_main_from_id(C, &ntree->id);
panel_state->flag ^= NODE_PANEL_COLLAPSED;

View File

@ -373,7 +373,7 @@ void ED_node_composite_job(const bContext *C, bNodeTree *nodetree, Scene *scene_
{
using namespace blender::ed::space_node;
Main *bmain = CTX_data_main(C);
Main *bmain = CTX_data_main_from_id(C, &nodetree->id);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
@ -545,7 +545,7 @@ bool ED_node_supports_preview(SpaceNode *snode)
void ED_node_shader_default(const bContext *C, ID *id)
{
Main *bmain = CTX_data_main(C);
Main *bmain = CTX_data_main_from_id(C, id);
if (GS(id->name) == ID_MA) {
/* Materials */
@ -642,7 +642,8 @@ void ED_node_composit_default(const bContext *C, Scene *sce)
bNodeSocket *tosock = (bNodeSocket *)out->inputs.first;
nodeAddLink(sce->nodetree, in, fromsock, out, tosock);
BKE_ntree_update_main_tree(CTX_data_main(C), sce->nodetree, nullptr);
Main *bmain = CTX_data_main_from_id(C, &sce->nodetree->id);
BKE_ntree_update_main_tree(bmain, sce->nodetree, nullptr);
}
void ED_node_texture_default(const bContext *C, Tex *tex)
@ -670,7 +671,8 @@ void ED_node_texture_default(const bContext *C, Tex *tex)
bNodeSocket *tosock = (bNodeSocket *)out->inputs.first;
nodeAddLink(tex->nodetree, in, fromsock, out, tosock);
BKE_ntree_update_main_tree(CTX_data_main(C), tex->nodetree, nullptr);
Main *bmain = CTX_data_main_from_id(C, &tex->nodetree->id);
BKE_ntree_update_main_tree(bmain, tex->nodetree, nullptr);
}
namespace blender::ed::space_node {
@ -1364,9 +1366,9 @@ void remap_node_pairing(bNodeTree &dst_tree, const Map<const bNode *, bNode *> &
static int node_duplicate_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
Main *bmain = CTX_data_main_from_id(C, &ntree->id);
const bool keep_inputs = RNA_boolean_get(op->ptr, "keep_inputs");
bool linked = RNA_boolean_get(op->ptr, "linked") || ((U.dupflag & USER_DUP_NTREE) == 0);
const bool dupli_node_tree = !linked;
@ -1504,10 +1506,10 @@ void NODE_OT_duplicate(wmOperatorType *ot)
/* Goes over all scenes, reads render layers. */
static int node_read_viewlayers_exec(bContext *C, wmOperator * /*op*/)
{
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
Scene *curscene = CTX_data_scene(C);
bNodeTree &edit_tree = *snode->edittree;
Main *bmain = CTX_data_main_from_id(C, &edit_tree.id);
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
@ -1698,7 +1700,8 @@ static int node_preview_toggle_exec(bContext *C, wmOperator * /*op*/)
node_flag_toggle_exec(snode, NODE_PREVIEW);
ED_node_tree_propagate_change(C, CTX_data_main(C), snode->edittree);
Main *bmain = CTX_data_main_from_id(C, &snode->edittree->id);
ED_node_tree_propagate_change(C, bmain, snode->edittree);
return OPERATOR_FINISHED;
}
@ -1749,7 +1752,8 @@ static int node_deactivate_viewer_exec(bContext *C, wmOperator * /*op*/)
}
}
ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree);
Main *bmain = CTX_data_main_from_id(C, &snode.edittree->id);
ED_node_tree_propagate_change(C, bmain, snode.edittree);
return OPERATOR_FINISHED;
}
@ -1803,13 +1807,14 @@ void NODE_OT_options_toggle(wmOperatorType *ot)
static int node_socket_toggle_exec(bContext *C, wmOperator * /*op*/)
{
SpaceNode *snode = CTX_wm_space_node(C);
Main *bmain = CTX_data_main_from_id(C, &snode->edittree->id);
/* Sanity checking (poll callback checks this already). */
if ((snode == nullptr) || (snode->edittree == nullptr)) {
return OPERATOR_CANCELLED;
}
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
/* Toggle for all selected nodes */
bool hidden = false;
@ -1828,7 +1833,7 @@ static int node_socket_toggle_exec(bContext *C, wmOperator * /*op*/)
}
}
ED_node_tree_propagate_change(C, CTX_data_main(C), snode->edittree);
ED_node_tree_propagate_change(C, bmain, snode->edittree);
WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
/* Hack to force update of the button state after drawing, see #112462. */
@ -1860,8 +1865,8 @@ void NODE_OT_hide_socket_toggle(wmOperatorType *ot)
static int node_mute_exec(bContext *C, wmOperator * /*op*/)
{
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
Main *bmain = CTX_data_main_from_id(C, &snode->edittree->id);
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
@ -1900,8 +1905,8 @@ void NODE_OT_mute_toggle(wmOperatorType *ot)
static int node_delete_exec(bContext *C, wmOperator * /*op*/)
{
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
Main *bmain = CTX_data_main_from_id(C, &snode->edittree->id);
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
@ -1954,6 +1959,7 @@ static bool node_switch_view_poll(bContext *C)
static int node_switch_view_exec(bContext *C, wmOperator * /*op*/)
{
SpaceNode *snode = CTX_wm_space_node(C);
Main *bmain = CTX_data_main_from_id(C, &snode->edittree->id);
LISTBASE_FOREACH_MUTABLE (bNode *, node, &snode->edittree->nodes) {
if (node->flag & SELECT) {
@ -1962,7 +1968,7 @@ static int node_switch_view_exec(bContext *C, wmOperator * /*op*/)
}
}
ED_node_tree_propagate_change(C, CTX_data_main(C), snode->edittree);
ED_node_tree_propagate_change(C, bmain, snode->edittree);
return OPERATOR_FINISHED;
}
@ -1990,10 +1996,10 @@ void NODE_OT_switch_view_update(wmOperatorType *ot)
static int node_delete_reconnect_exec(bContext *C, wmOperator * /*op*/)
{
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
Main *bmain = CTX_data_main_from_id(C, &snode->edittree->id);
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
/* Delete paired nodes as well. */
node_select_paired(*snode->edittree);
@ -2056,7 +2062,8 @@ static int node_output_file_add_socket_exec(bContext *C, wmOperator *op)
RNA_string_get(op->ptr, "file_path", file_path);
ntreeCompositOutputFileAddSocket(ntree, node, file_path, &scene->r.im_format);
ED_node_tree_propagate_change(C, CTX_data_main(C), snode->edittree);
Main *bmain = CTX_data_main_from_id(C, &snode->edittree->id);
ED_node_tree_propagate_change(C, bmain, snode->edittree);
return OPERATOR_FINISHED;
}
@ -2109,7 +2116,8 @@ static int node_output_file_remove_active_socket_exec(bContext *C, wmOperator *
return OPERATOR_CANCELLED;
}
ED_node_tree_propagate_change(C, CTX_data_main(C), ntree);
Main *bmain = CTX_data_main_from_id(C, &ntree->id);
ED_node_tree_propagate_change(C, bmain, ntree);
return OPERATOR_FINISHED;
}
@ -2180,8 +2188,9 @@ static int node_output_file_move_active_socket_exec(bContext *C, wmOperator *op)
nimf->active_input++;
}
Main *bmain = CTX_data_main_from_id(C, &snode->edittree->id);
BKE_ntree_update_tag_node_property(snode->edittree, node);
ED_node_tree_propagate_change(C, CTX_data_main(C), snode->edittree);
ED_node_tree_propagate_change(C, bmain, snode->edittree);
return OPERATOR_FINISHED;
}
@ -2327,7 +2336,6 @@ static bool node_shader_script_update_text_recursive(RenderEngine *engine,
static int node_shader_script_update_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
SpaceNode *snode = CTX_wm_space_node(C);
PointerRNA nodeptr = CTX_data_pointer_get_type(C, "node", &RNA_ShaderNodeScript);
@ -2360,7 +2368,7 @@ static int node_shader_script_update_exec(bContext *C, wmOperator *op)
Text *text = (Text *)CTX_data_pointer_get_type(C, "edit_text", &RNA_Text).data;
if (text) {
Main *bmain = CTX_data_main(C);
VectorSet<bNodeTree *> done_trees;
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
@ -2502,9 +2510,10 @@ static int clear_viewer_border_exec(bContext *C, wmOperator * /*op*/)
{
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *btree = snode->nodetree;
Main *bmain = CTX_data_main_from_id(C, &btree->id);
btree->flag &= ~NTREE_VIEWER_BORDER;
ED_node_tree_propagate_change(C, CTX_data_main(C), btree);
ED_node_tree_propagate_change(C, bmain, btree);
WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
return OPERATOR_FINISHED;
@ -2553,7 +2562,8 @@ static int node_cryptomatte_add_socket_exec(bContext *C, wmOperator * /*op*/)
ntreeCompositCryptomatteAddSocket(ntree, node);
ED_node_tree_propagate_change(C, CTX_data_main(C), ntree);
Main *bmain = CTX_data_main_from_id(C, &ntree->id);
ED_node_tree_propagate_change(C, bmain, ntree);
return OPERATOR_FINISHED;
}
@ -2603,7 +2613,8 @@ static int node_cryptomatte_remove_socket_exec(bContext *C, wmOperator * /*op*/)
return OPERATOR_CANCELLED;
}
ED_node_tree_propagate_change(C, CTX_data_main(C), ntree);
Main *bmain = CTX_data_main_from_id(C, &ntree->id);
ED_node_tree_propagate_change(C, bmain, ntree);
return OPERATOR_FINISHED;
}

View File

@ -211,7 +211,8 @@ static void attribute_search_exec_fn(bContext *C, void *data_v, void *item_v)
/* Make the output socket with the new type on the attribute input node active. */
nodes::update_node_declaration_and_sockets(*node_tree, *node);
BKE_ntree_update_tag_node_property(node_tree, node);
ED_node_tree_propagate_change(C, CTX_data_main(C), node_tree);
Main *bmain = CTX_data_main_from_id(C, &node_tree->id);
ED_node_tree_propagate_change(C, bmain, node_tree);
}
}

View File

@ -169,10 +169,11 @@ static void remap_pairing(bNodeTree &dst_tree,
static int node_group_edit_exec(bContext *C, wmOperator *op)
{
SpaceNode *snode = CTX_wm_space_node(C);
Main *bmain = CTX_data_main_from_id(C, &snode->edittree->id);
const char *node_idname = node_group_idname(C);
const bool exit = RNA_boolean_get(op->ptr, "exit");
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
stop_preview_job(*CTX_wm_manager(C));
bNode *gnode = node_group_get_active(C, node_idname);
@ -452,8 +453,8 @@ static void node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
static int node_group_ungroup_exec(bContext *C, wmOperator * /*op*/)
{
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
Main *bmain = CTX_data_main_from_id(C, &snode->edittree->id);
const char *node_idname = node_group_idname(C);
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
@ -474,7 +475,7 @@ static int node_group_ungroup_exec(bContext *C, wmOperator * /*op*/)
for (bNode *node : nodes_to_ungroup) {
node_group_ungroup(bmain, snode->edittree, node);
}
ED_node_tree_propagate_change(C, CTX_data_main(C), nullptr);
ED_node_tree_propagate_change(C, bmain, nullptr);
return OPERATOR_FINISHED;
}
@ -625,8 +626,8 @@ static const EnumPropertyItem node_group_separate_types[] = {
static int node_group_separate_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
Main *bmain = CTX_data_main_from_id(C, &snode->edittree->id);
int type = RNA_enum_get(op->ptr, "type");
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
@ -660,7 +661,7 @@ static int node_group_separate_exec(bContext *C, wmOperator *op)
/* switch to parent tree */
ED_node_tree_pop(snode);
ED_node_tree_propagate_change(C, CTX_data_main(C), nullptr);
ED_node_tree_propagate_change(C, bmain, nullptr);
return OPERATOR_FINISHED;
}
@ -926,7 +927,7 @@ static void node_group_make_insert_selected(const bContext &C,
bNode *gnode,
const VectorSet<bNode *> &nodes_to_move)
{
Main *bmain = CTX_data_main(&C);
Main *bmain = CTX_data_main_from_id(&C, &ntree.id);
bNodeTree &group = *reinterpret_cast<bNodeTree *>(gnode->id);
BLI_assert(!nodes_to_move.contains(gnode));
@ -1206,7 +1207,7 @@ static bNode *node_group_make_from_nodes(const bContext &C,
const char *ntype,
const char *ntreetype)
{
Main *bmain = CTX_data_main(&C);
Main *bmain = CTX_data_main_from_id(&C, &ntree.id);
float2 min, max;
get_min_max_of_nodes(nodes_to_group, false, min, max);
@ -1232,9 +1233,9 @@ static int node_group_make_exec(bContext *C, wmOperator *op)
bNodeTree &ntree = *snode.edittree;
const char *ntree_idname = group_ntree_idname(C);
const char *node_idname = node_group_idname(C);
Main *bmain = CTX_data_main(C);
Main *bmain = CTX_data_main_from_id(C, &ntree.id);
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
stop_preview_job(*CTX_wm_manager(C));
VectorSet<bNode *> nodes_to_group = get_nodes_to_group(ntree, nullptr);
@ -1286,9 +1287,10 @@ static int node_group_insert_exec(bContext *C, wmOperator *op)
{
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
Main *bmain = CTX_data_main_from_id(C, &ntree->id);
const char *node_idname = node_group_idname(C);
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
stop_preview_job(*CTX_wm_manager(C));
bNode *gnode = node_group_get_active(C, node_idname);

View File

@ -576,7 +576,7 @@ static void finalize_viewer_link(const bContext &C,
bNode &viewer_node,
bNodeLink &viewer_link)
{
Main *bmain = CTX_data_main(&C);
Main *bmain = CTX_data_main_from_id(&C, &snode.edittree->id);
remove_links_to_unavailable_viewer_sockets(*snode.edittree, viewer_node);
viewer_link.flag &= ~NODE_LINK_MUTED;
viewer_node.flag &= ~NODE_MUTED;
@ -683,12 +683,13 @@ static int node_active_link_viewer_exec(bContext *C, wmOperator * /*op*/)
{
SpaceNode &snode = *CTX_wm_space_node(C);
bNode *node = nodeGetActive(snode.edittree);
Main *bmain = CTX_data_main_from_id(C, &snode.edittree->id);
if (!node) {
return OPERATOR_CANCELLED;
}
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
bNodeSocket *socket_to_view = nullptr;
LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) {
@ -702,7 +703,7 @@ static int node_active_link_viewer_exec(bContext *C, wmOperator * /*op*/)
return OPERATOR_CANCELLED;
}
ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree);
ED_node_tree_propagate_change(C, bmain, snode.edittree);
return OPERATOR_FINISHED;
}
@ -1027,10 +1028,10 @@ static void node_remove_existing_links_if_needed(bNodeLinkDrag &nldrag, bNodeTre
static void add_dragged_links_to_tree(bContext &C, bNodeLinkDrag &nldrag)
{
Main *bmain = CTX_data_main(&C);
ARegion &region = *CTX_wm_region(&C);
SpaceNode &snode = *CTX_wm_space_node(&C);
bNodeTree &ntree = *snode.edittree;
Main *bmain = CTX_data_main_from_id(&C, &ntree.id);
/* Handle node links already occupying the socket. */
if (const bNodeSocket *linked_socket = nldrag.hovered_socket) {
@ -1353,9 +1354,9 @@ static std::unique_ptr<bNodeLinkDrag> node_link_init(ARegion &region,
static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
Main &bmain = *CTX_data_main(C);
SpaceNode &snode = *CTX_wm_space_node(C);
ARegion &region = *CTX_wm_region(C);
Main *bmain = CTX_data_main_from_id(C, &snode.edittree->id);
bool detach = RNA_boolean_get(op->ptr, "detach");
@ -1366,7 +1367,7 @@ static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event)
UI_view2d_region_to_view(&region.v2d, mval[0], mval[1], &cursor[0], &cursor[1]);
RNA_float_set_array(op->ptr, "drag_start", cursor);
ED_preview_kill_jobs(CTX_wm_manager(C), &bmain);
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
std::unique_ptr<bNodeLinkDrag> nldrag = node_link_init(region, snode, cursor, detach);
if (!nldrag) {
@ -1433,12 +1434,12 @@ void NODE_OT_link(wmOperatorType *ot)
/* Makes a link between selected output and input sockets. */
static int node_make_link_exec(bContext *C, wmOperator *op)
{
Main &bmain = *CTX_data_main(C);
SpaceNode &snode = *CTX_wm_space_node(C);
bNodeTree &node_tree = *snode.edittree;
Main *bmain = CTX_data_main_from_id(C, &node_tree.id);
const bool replace = RNA_boolean_get(op->ptr, "replace");
ED_preview_kill_jobs(CTX_wm_manager(C), &bmain);
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
snode_autoconnect(snode, true, replace);
@ -1446,7 +1447,7 @@ static int node_make_link_exec(bContext *C, wmOperator *op)
node_deselect_all_input_sockets(node_tree, false);
node_deselect_all_output_sockets(node_tree, false);
ED_node_tree_propagate_change(C, &bmain, &node_tree);
ED_node_tree_propagate_change(C, bmain, &node_tree);
return OPERATOR_FINISHED;
}
@ -1478,7 +1479,6 @@ void NODE_OT_link_make(wmOperatorType *ot)
static int cut_links_exec(bContext *C, wmOperator *op)
{
Main &bmain = *CTX_data_main(C);
SpaceNode &snode = *CTX_wm_space_node(C);
const ARegion &region = *CTX_wm_region(C);
@ -1501,9 +1501,11 @@ static int cut_links_exec(bContext *C, wmOperator *op)
bool found = false;
bNodeTree &node_tree = *snode.edittree;
Main &bmain = *CTX_data_main_from_id(C, &node_tree.id);
ED_preview_kill_jobs(CTX_wm_manager(C), &bmain);
bNodeTree &node_tree = *snode.edittree;
node_tree.ensure_topology_cache();
Set<bNodeLink *> links_to_remove;
@ -1535,7 +1537,7 @@ static int cut_links_exec(bContext *C, wmOperator *op)
update_multi_input_indices_for_removed_links(*node);
}
ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree);
ED_node_tree_propagate_change(C, &bmain, &node_tree);
if (found) {
return OPERATOR_FINISHED;
}
@ -1586,10 +1588,10 @@ bool all_links_muted(const bNodeSocket &socket)
static int mute_links_exec(bContext *C, wmOperator *op)
{
Main &bmain = *CTX_data_main(C);
SpaceNode &snode = *CTX_wm_space_node(C);
const ARegion &region = *CTX_wm_region(C);
bNodeTree &ntree = *snode.edittree;
Main &bmain = *CTX_data_main_from_id(C, &ntree.id);
Vector<float2> path;
RNA_BEGIN (op->ptr, itemptr, "path") {
@ -1665,7 +1667,7 @@ static int mute_links_exec(bContext *C, wmOperator *op)
}
}
ED_node_tree_propagate_change(C, CTX_data_main(C), &ntree);
ED_node_tree_propagate_change(C, &bmain, &ntree);
return OPERATOR_FINISHED;
}
@ -1704,8 +1706,9 @@ static int detach_links_exec(bContext *C, wmOperator * /*op*/)
{
SpaceNode &snode = *CTX_wm_space_node(C);
bNodeTree &ntree = *snode.edittree;
Main &bmain = *CTX_data_main_from_id(C, &ntree.id);
ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C));
ED_preview_kill_jobs(CTX_wm_manager(C), &bmain);
for (bNode *node : ntree.all_nodes()) {
if (node->flag & SELECT) {
@ -1713,7 +1716,7 @@ static int detach_links_exec(bContext *C, wmOperator * /*op*/)
}
}
ED_node_tree_propagate_change(C, CTX_data_main(C), &ntree);
ED_node_tree_propagate_change(C, &bmain, &ntree);
return OPERATOR_FINISHED;
}
@ -1824,9 +1827,9 @@ static void node_join_attach_recursive(bNodeTree &ntree,
static int node_join_exec(bContext *C, wmOperator * /*op*/)
{
Main &bmain = *CTX_data_main(C);
SpaceNode &snode = *CTX_wm_space_node(C);
bNodeTree &ntree = *snode.edittree;
Main &bmain = *CTX_data_main_from_id(C, &ntree.id);
const VectorSet<bNode *> selected_nodes = get_selected_nodes(ntree);

View File

@ -512,9 +512,9 @@ void NODE_OT_select_grouped(wmOperatorType *ot)
void node_select_single(bContext &C, bNode &node)
{
Main *bmain = CTX_data_main(&C);
SpaceNode &snode = *CTX_wm_space_node(&C);
bNodeTree &node_tree = *snode.edittree;
Main *bmain = CTX_data_main_from_id(&C, &node_tree.id);
const Object *ob = CTX_data_active_object(&C);
const Scene *scene = CTX_data_scene(&C);
const wmWindowManager *wm = CTX_wm_manager(&C);
@ -543,9 +543,9 @@ static bool node_mouse_select(bContext *C,
const int2 mval,
SelectPick_Params *params)
{
Main &bmain = *CTX_data_main(C);
SpaceNode &snode = *CTX_wm_space_node(C);
bNodeTree &node_tree = *snode.edittree;
Main &bmain = *CTX_data_main_from_id(C, &node_tree.id);
ARegion &region = *CTX_wm_region(C);
const Object *ob = CTX_data_active_object(C);
const Scene *scene = CTX_data_scene(C);

View File

@ -803,7 +803,7 @@ static void ensure_nodetree_previews(const bContext &C,
job_data->scene = scene;
job_data->tree_previews = &tree_previews;
job_data->bmain = CTX_data_main(&C);
job_data->bmain = CTX_data_main_from_id(&C, &displayed_nodetree->id);
job_data->mat_copy = duplicate_material(material);
job_data->rendering_node = nullptr;
job_data->rendering_AOVs = false;

View File

@ -195,7 +195,7 @@ static void node_socket_add_replace(const bContext *C,
int type,
NodeLinkItem *item)
{
Main *bmain = CTX_data_main(C);
Main *bmain = CTX_data_main_from_id(C, &ntree->id);
bNode *node_from;
bNodeSocket *sock_from_tmp;
bNode *node_prev = nullptr;
@ -600,7 +600,6 @@ static void node_menu_column_foreach_cb(void *calldata, int nclass, const char *
static void ui_template_node_link_menu(bContext *C, uiLayout *layout, void *but_p)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
uiBlock *block = uiLayoutGetBlock(layout);
uiBut *but = (uiBut *)but_p;
@ -608,6 +607,7 @@ static void ui_template_node_link_menu(bContext *C, uiLayout *layout, void *but_
NodeLinkArg *arg = (NodeLinkArg *)but->func_argN;
bNodeSocket *sock = arg->sock;
bNodeTreeType *ntreetype = arg->ntree->typeinfo;
Main *bmain = CTX_data_main_from_id(C, &arg->ntree->id);
UI_block_layout_set_current(block, layout);
split = uiLayoutSplit(layout, 0.0f, false);
@ -617,7 +617,7 @@ static void ui_template_node_link_menu(bContext *C, uiLayout *layout, void *but_
arg->layout = split;
if (ntreetype && ntreetype->foreach_nodeclass) {
ntreetype->foreach_nodeclass(scene, arg, node_menu_column_foreach_cb);
ntreetype->foreach_nodeclass(arg, node_menu_column_foreach_cb);
}
column = uiLayoutColumn(split, false);
@ -722,9 +722,9 @@ static void ui_node_draw_input(uiLayout &layout,
static void node_panel_toggle_button_cb(bContext *C, void *panel_state_argv, void *ntree_argv)
{
Main *bmain = CTX_data_main(C);
bNodePanelState *panel_state = static_cast<bNodePanelState *>(panel_state_argv);
bNodeTree *ntree = static_cast<bNodeTree *>(ntree_argv);
Main *bmain = CTX_data_main_from_id(C, &ntree->id);
panel_state->flag ^= NODE_PANEL_COLLAPSED;

View File

@ -358,12 +358,8 @@ void apply_keyb_grid(
}
}
void unpack_menu(bContext *C,
const char *opname,
const char *id_name,
const char *abs_name,
const char *folder,
PackedFile *pf)
void unpack_menu(
bContext *C, const char *opname, const char *abs_name, const char *folder, PackedFile *pf)
{
Main *bmain = CTX_data_main(C);
PointerRNA props_ptr;
@ -385,7 +381,6 @@ void unpack_menu(bContext *C,
UI_ITEM_NONE,
&props_ptr);
RNA_enum_set(&props_ptr, "method", PF_REMOVE);
RNA_string_set(&props_ptr, "id", id_name);
if (blendfile_path[0] != '\0') {
char local_name[FILE_MAXDIR + FILE_MAX], fi[FILE_MAX];
@ -399,7 +394,6 @@ void unpack_menu(bContext *C,
uiItemFullO_ptr(
layout, ot, line, ICON_NONE, nullptr, WM_OP_EXEC_DEFAULT, UI_ITEM_NONE, &props_ptr);
RNA_enum_set(&props_ptr, "method", PF_WRITE_LOCAL);
RNA_string_set(&props_ptr, "id", id_name);
break;
case PF_CMP_EQUAL:
@ -408,7 +402,6 @@ void unpack_menu(bContext *C,
uiItemFullO_ptr(
layout, ot, line, ICON_NONE, nullptr, WM_OP_EXEC_DEFAULT, UI_ITEM_NONE, &props_ptr);
RNA_enum_set(&props_ptr, "method", PF_USE_LOCAL);
RNA_string_set(&props_ptr, "id", id_name);
break;
case PF_CMP_DIFFERS:
@ -417,14 +410,12 @@ void unpack_menu(bContext *C,
uiItemFullO_ptr(
layout, ot, line, ICON_NONE, nullptr, WM_OP_EXEC_DEFAULT, UI_ITEM_NONE, &props_ptr);
RNA_enum_set(&props_ptr, "method", PF_USE_LOCAL);
RNA_string_set(&props_ptr, "id", id_name);
SNPRINTF(line, IFACE_("Overwrite %s"), local_name);
// uiItemEnumO_ptr(layout, ot, line, ICON_NONE, "method", PF_WRITE_LOCAL);
uiItemFullO_ptr(
layout, ot, line, ICON_NONE, nullptr, WM_OP_EXEC_DEFAULT, UI_ITEM_NONE, &props_ptr);
RNA_enum_set(&props_ptr, "method", PF_WRITE_LOCAL);
RNA_string_set(&props_ptr, "id", id_name);
break;
}
}
@ -437,7 +428,6 @@ void unpack_menu(bContext *C,
uiItemFullO_ptr(
layout, ot, line, ICON_NONE, nullptr, WM_OP_EXEC_DEFAULT, UI_ITEM_NONE, &props_ptr);
RNA_enum_set(&props_ptr, "method", PF_WRITE_ORIGINAL);
RNA_string_set(&props_ptr, "id", id_name);
break;
case PF_CMP_EQUAL:
SNPRINTF(line, IFACE_("Use %s (identical)"), abs_name);
@ -445,7 +435,6 @@ void unpack_menu(bContext *C,
uiItemFullO_ptr(
layout, ot, line, ICON_NONE, nullptr, WM_OP_EXEC_DEFAULT, UI_ITEM_NONE, &props_ptr);
RNA_enum_set(&props_ptr, "method", PF_USE_ORIGINAL);
RNA_string_set(&props_ptr, "id", id_name);
break;
case PF_CMP_DIFFERS:
SNPRINTF(line, IFACE_("Use %s (differs)"), abs_name);
@ -453,14 +442,12 @@ void unpack_menu(bContext *C,
uiItemFullO_ptr(
layout, ot, line, ICON_NONE, nullptr, WM_OP_EXEC_DEFAULT, UI_ITEM_NONE, &props_ptr);
RNA_enum_set(&props_ptr, "method", PF_USE_ORIGINAL);
RNA_string_set(&props_ptr, "id", id_name);
SNPRINTF(line, IFACE_("Overwrite %s"), abs_name);
// uiItemEnumO_ptr(layout, ot, line, ICON_NONE, "method", PF_WRITE_ORIGINAL);
uiItemFullO_ptr(
layout, ot, line, ICON_NONE, nullptr, WM_OP_EXEC_DEFAULT, UI_ITEM_NONE, &props_ptr);
RNA_enum_set(&props_ptr, "method", PF_WRITE_ORIGINAL);
RNA_string_set(&props_ptr, "id", id_name);
break;
}

View File

@ -1000,6 +1000,15 @@ enum {
* RESET_NEVER
*/
LIB_TAG_NOT_ALLOCATED = 1 << 26,
/**
* ID is part of an asset #Main separate from the regular main database.
*
* RESET_NEVER
*
* Datablocks like this can not be linked to and from datablocks in regular main.
* They should stay isolated from each other.
*/
LIB_TAG_ASSET_EDIT_MAIN = 1 << 27,
/* ------------------------------------------------------------------------------------------- */

View File

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

@ -897,27 +897,26 @@ 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.
* //TODO: Or always stored in a separate #Main?
*/
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;
struct Palette *palette;
/** Cavity curve. */

View File

@ -833,6 +833,7 @@ typedef struct AssetShelf {
AssetShelfSettings settings;
/** Only for the permanent asset shelf regions, not asset shelves in temporary popups. */
short preferred_row_count;
char _pad[6];
} AssetShelf;

View File

@ -1269,14 +1269,30 @@ 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)) {
/* Can only assign pointers between datablocks in the same main database. */
fprintf(f, " if (value.data) {\n");
fprintf(f, " Main *owner_main = BKE_main_from_id(G_MAIN, ptr->owner_id);\n");
fprintf(f, " Main *value_main = BKE_main_from_id(G_MAIN, (ID *)value.data);\n");
fprintf(f, " if (owner_main && owner_main != value_main) {\n");
fprintf(f, " return;\n");
fprintf(f, " }\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 +1300,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);
@ -3365,7 +3378,8 @@ static void rna_def_function_funcs(FILE *f, StructDefRNA *dsrna, FunctionDefRNA
fprintf(f, ", ");
}
first = 0;
fprintf(f, "CTX_data_main(C)"); /* may have direct access later */
/* May have direct access later. */
fprintf(f, "(_ptr->owner_id) ? CTX_data_main_from_id(C, _ptr->owner_id) : CTX_data_main(C)");
}
if (func->flag & FUNC_USE_CONTEXT) {
@ -4880,6 +4894,7 @@ static void rna_generate(BlenderRNA *brna, FILE *f, const char *filename, const
fprintf(f, "#include \"BLI_utildefines.h\"\n\n");
fprintf(f, "#include \"BKE_context.hh\"\n");
fprintf(f, "#include \"BKE_global.hh\"\n");
fprintf(f, "#include \"BKE_lib_id.hh\"\n");
fprintf(f, "#include \"BKE_main.hh\"\n");
fprintf(f, "#include \"BKE_report.hh\"\n");

View File

@ -289,10 +289,9 @@ 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_LINKED(id));
Main *bmain = BKE_main_from_id(G_MAIN, id);
BKE_libblock_rename(G_MAIN, id, value);
BKE_libblock_rename(bmain, id, value);
if (GS(id->name) == ID_OB) {
Object *ob = (Object *)id;
@ -300,6 +299,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)
@ -325,7 +326,7 @@ static int rna_ID_name_editable(const PointerRNA *ptr, const char **r_info)
return 0;
}
}
else if (!BKE_id_is_in_global_main(id)) {
else if (BKE_main_from_id(G_MAIN, id, true) == nullptr) {
if (r_info) {
*r_info = N_("Datablocks not in global Main data-base cannot be renamed");
}
@ -1166,16 +1167,20 @@ bool rna_IDMaterials_assign_int(PointerRNA *ptr, int key, const PointerRNA *assi
{
ID *id = ptr->owner_id;
short *totcol = BKE_id_material_len_p(id);
Material *mat_id = (Material *)assign_ptr->owner_id;
if (totcol && (key >= 0 && key < *totcol)) {
BLI_assert(BKE_id_is_in_global_main(id));
BLI_assert(BKE_id_is_in_global_main(&mat_id->id));
BKE_id_material_assign(G_MAIN, id, mat_id, key + 1);
return true;
}
else {
Material *mat = (Material *)assign_ptr->owner_id;
if (!(totcol && (key >= 0 && key < *totcol))) {
return false;
}
Main *bmain = BKE_main_from_id(G_MAIN, id);
if (mat) {
if (bmain != BKE_main_from_id(G_MAIN, &mat->id)) {
return false;
}
}
BKE_id_material_assign(bmain, id, mat, key + 1);
return true;
}
static void rna_IDMaterials_append_id(ID *id, Main *bmain, Material *ma)
@ -1226,8 +1231,8 @@ static void rna_IDMaterials_clear_id(ID *id, Main *bmain)
static void rna_Library_filepath_set(PointerRNA *ptr, const char *value)
{
Library *lib = (Library *)ptr->data;
BLI_assert(BKE_id_is_in_global_main(&lib->id));
BKE_library_filepath_set(G_MAIN, lib, value);
Main *bmain = BKE_main_from_id(G_MAIN, &lib->id);
BKE_library_filepath_set(bmain, lib, value);
}
/* ***** ImagePreview ***** */
@ -2315,6 +2320,14 @@ static void rna_def_ID(BlenderRNA *brna)
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Is Indirect", "Is this ID block linked indirectly");
prop = RNA_def_property(srna, "is_asset_library_data", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "tag", LIB_TAG_ASSET_EDIT_MAIN);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop,
"Asset Library Data",
"This data-block is part of an asset library blend file, not the blend "
"file opened for editing");
prop = RNA_def_property(srna, "library", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, nullptr, "lib");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);

View File

@ -2144,7 +2144,8 @@ static bool rna_property_editable_do(const PointerRNA *ptr,
return false;
}
if (ID_IS_OVERRIDE_LIBRARY(id)) {
const bool is_liboverride_system = BKE_lib_override_library_is_system_defined(G_MAIN, id);
Main *main = BKE_main_from_id(G_MAIN, id);
const bool is_liboverride_system = BKE_lib_override_library_is_system_defined(main, id);
if (!RNA_property_overridable_get(ptr, prop_orig)) {
if (r_info != nullptr && (*r_info)[0] == '\0') {
*r_info = N_("Can't edit this property from an override data-block");
@ -2389,7 +2390,8 @@ bool RNA_property_update_check(PropertyRNA *prop)
void RNA_property_update(bContext *C, PointerRNA *ptr, PropertyRNA *prop)
{
rna_property_update(C, CTX_data_main(C), CTX_data_scene(C), ptr, prop);
Main *bmain = (ptr->owner_id) ? CTX_data_main_from_id(C, ptr->owner_id) : CTX_data_main(C);
rna_property_update(C, bmain, CTX_data_scene(C), ptr, prop);
}
void RNA_property_update_main(Main *bmain, Scene *scene, PointerRNA *ptr, PropertyRNA *prop)

View File

@ -791,28 +791,36 @@ static void rna_EditBone_name_set(PointerRNA *ptr, const char *value)
{
bArmature *arm = (bArmature *)ptr->owner_id;
EditBone *ebone = (EditBone *)ptr->data;
char oldname[sizeof(ebone->name)], newname[sizeof(ebone->name)];
Main *main = BKE_main_from_id(G_MAIN, &arm->id);
if (main == nullptr) {
return;
}
char oldname[sizeof(ebone->name)], newname[sizeof(ebone->name)];
/* need to be on the stack */
STRNCPY_UTF8(newname, value);
STRNCPY(oldname, ebone->name);
BLI_assert(BKE_id_is_in_global_main(&arm->id));
ED_armature_bone_rename(G_MAIN, arm, oldname, newname);
ED_armature_bone_rename(main, arm, oldname, newname);
}
static void rna_Bone_name_set(PointerRNA *ptr, const char *value)
{
bArmature *arm = (bArmature *)ptr->owner_id;
Bone *bone = (Bone *)ptr->data;
char oldname[sizeof(bone->name)], newname[sizeof(bone->name)];
Main *main = BKE_main_from_id(G_MAIN, &arm->id);
if (main == nullptr) {
return;
}
char oldname[sizeof(bone->name)], newname[sizeof(bone->name)];
/* need to be on the stack */
STRNCPY_UTF8(newname, value);
STRNCPY(oldname, bone->name);
BLI_assert(BKE_id_is_in_global_main(&arm->id));
ED_armature_bone_rename(G_MAIN, arm, oldname, newname);
ED_armature_bone_rename(main, arm, oldname, newname);
}
static void rna_EditBone_connected_check(EditBone *ebone)

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
@ -774,20 +730,20 @@ static void rna_Brush_material_update(bContext * /*C*/, PointerRNA * /*ptr*/)
static void rna_Brush_main_tex_update(bContext *C, PointerRNA *ptr)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Brush *br = (Brush *)ptr->data;
Main *bmain = CTX_data_main_from_id(C, &br->id);
BKE_paint_invalidate_overlay_tex(scene, view_layer, br->mtex.tex);
rna_Brush_update(bmain, scene, ptr);
}
static void rna_Brush_secondary_tex_update(bContext *C, PointerRNA *ptr)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Brush *br = (Brush *)ptr->data;
Main *bmain = CTX_data_main_from_id(C, &br->id);
BKE_paint_invalidate_overlay_tex(scene, view_layer, br->mask_mtex.tex);
rna_Brush_update(bmain, scene, ptr);
}
@ -1113,33 +1069,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 +1651,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 +1847,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");

View File

@ -86,18 +86,23 @@ static void rna_Image_source_set(PointerRNA *ptr, int value)
{
Image *ima = (Image *)ptr->owner_id;
if (value != ima->source) {
ima->source = value;
BLI_assert(BKE_id_is_in_global_main(&ima->id));
BKE_image_signal(G_MAIN, ima, nullptr, IMA_SIGNAL_SRC_CHANGE);
if (ima->source == IMA_SRC_TILED) {
BKE_image_signal(G_MAIN, ima, nullptr, IMA_SIGNAL_RELOAD);
}
DEG_id_tag_update(&ima->id, 0);
DEG_id_tag_update(&ima->id, ID_RECALC_EDITORS | ID_RECALC_SOURCE);
DEG_relations_tag_update(G_MAIN);
if (value == ima->source) {
return;
}
ima->source = value;
Main *main = BKE_main_from_id(G_MAIN, &ima->id);
if (main == nullptr) {
return;
}
BKE_image_signal(main, ima, nullptr, IMA_SIGNAL_SRC_CHANGE);
if (ima->source == IMA_SRC_TILED) {
BKE_image_signal(main, ima, nullptr, IMA_SIGNAL_RELOAD);
}
DEG_id_tag_update(&ima->id, 0);
DEG_id_tag_update(&ima->id, ID_RECALC_EDITORS | ID_RECALC_SOURCE);
DEG_relations_tag_update(main);
}
static void rna_Image_reload_update(Main *bmain, Scene * /*scene*/, PointerRNA *ptr)

View File

@ -56,7 +56,7 @@ static void rna_Image_save_render(Image *image,
Scene *scene,
const int quality)
{
Main *bmain = CTX_data_main(C);
Main *bmain = CTX_data_main_from_id(C, &image->id);
if (scene == nullptr) {
scene = CTX_data_scene(C);

View File

@ -78,7 +78,7 @@ static void rna_Light_use_nodes_update(bContext *C, PointerRNA *ptr)
ED_node_shader_default(C, &la->id);
}
rna_Light_update(CTX_data_main(C), CTX_data_scene(C), ptr);
rna_Light_update(CTX_data_main_from_id(C, &la->id), CTX_data_scene(C), ptr);
}
#else

View File

@ -368,13 +368,14 @@ static void rna_LineStyle_update(Main * /*bmain*/, Scene * /*scene*/, PointerRNA
static void rna_LineStyle_use_nodes_update(bContext *C, PointerRNA *ptr)
{
Main *bmain = CTX_data_main_from_id(C, ptr->owner_id);
FreestyleLineStyle *linestyle = (FreestyleLineStyle *)ptr->data;
if (linestyle->use_nodes && linestyle->nodetree == nullptr) {
BKE_linestyle_default_shader(C, linestyle);
}
rna_LineStyle_update(CTX_data_main(C), CTX_data_scene(C), ptr);
rna_LineStyle_update(bmain, CTX_data_scene(C), ptr);
}
static LineStyleModifier *rna_LineStyle_color_modifier_add(FreestyleLineStyle *linestyle,

Some files were not shown because too many files have changed in this diff Show More