WIP: Brush assets project #106303
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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`).
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
/**
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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
|
@ -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;
|
||||
|
|
|
@ -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) &&
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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().
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
|
@ -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 ®ular_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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
/* ******************************************************* */
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -95,11 +95,12 @@ static void add_group_input_node_fn(nodes::LinkSearchOpParams ¶ms)
|
|||
nullptr);
|
||||
socket_iface->init_from_socket_instance(¶ms.socket);
|
||||
params.node_tree.tree_interface.active_item_set(&socket_iface->item);
|
||||
Main *bmain = CTX_data_main_from_id(¶ms.C, ¶ms.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(¶ms.C, CTX_data_main(¶ms.C), ¶ms.node_tree);
|
||||
ED_node_tree_propagate_change(¶ms.C, bmain, ¶ms.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 ¶ms)
|
|||
socket->flag &= ~SOCK_HIDDEN;
|
||||
nodeAddLink(¶ms.node_tree, &group_input, socket, ¶ms.node, ¶ms.socket);
|
||||
|
||||
bke::node_socket_move_default_value(
|
||||
*CTX_data_main(¶ms.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 ¶ms) {
|
||||
Main &bmain = *CTX_data_main(¶ms.C);
|
||||
Main &bmain = *CTX_data_main_from_id(¶ms.C, ¶ms.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. */
|
||||
|
|
|
@ -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 ®ion = *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. */
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 ®ion = *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 ®ion,
|
|||
|
||||
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 ®ion = *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(®ion.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 ®ion = *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 ®ion = *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);
|
||||
|
||||
|
|
|
@ -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 ®ion = *CTX_wm_region(C);
|
||||
const Object *ob = CTX_data_active_object(C);
|
||||
const Scene *scene = CTX_data_scene(C);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
||||
/* ------------------------------------------------------------------------------------------- */
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue