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,48 +165,6 @@ def generate(context, space_type, *, use_fallback_keys=True, use_reset=True):
|
|||
include={'KEYBOARD'},
|
||||
)[1]
|
||||
|
||||
if kmi_found is None:
|
||||
if item.data_block:
|
||||
# PAINT_OT_brush_select
|
||||
mode = context.active_object.mode
|
||||
# See: BKE_paint_get_tool_prop_id_from_paintmode
|
||||
if space_type == 'IMAGE_EDITOR':
|
||||
if context.space_data.mode == 'PAINT':
|
||||
attr = "image_tool"
|
||||
else:
|
||||
attr = None
|
||||
elif space_type == 'VIEW_3D':
|
||||
attr = {
|
||||
'SCULPT': "sculpt_tool",
|
||||
'VERTEX_PAINT': "vertex_tool",
|
||||
'WEIGHT_PAINT': "weight_tool",
|
||||
'TEXTURE_PAINT': "image_tool",
|
||||
'PAINT_GPENCIL': "gpencil_tool",
|
||||
'PAINT_GREASE_PENCIL': "gpencil_tool",
|
||||
'VERTEX_GPENCIL': "gpencil_vertex_tool",
|
||||
'SCULPT_GPENCIL': "gpencil_sculpt_tool",
|
||||
'WEIGHT_GPENCIL': "gpencil_weight_tool",
|
||||
'SCULPT_CURVES': "curves_sculpt_tool",
|
||||
'PAINT_GREASE_PENCIL': "gpencil_tool",
|
||||
}.get(mode, None)
|
||||
else:
|
||||
attr = None
|
||||
|
||||
if attr is not None:
|
||||
setattr(kmi_hack_brush_select_properties, attr, item.data_block)
|
||||
kmi_found = wm.keyconfigs.find_item_from_operator(
|
||||
idname="paint.brush_select",
|
||||
context='INVOKE_REGION_WIN',
|
||||
properties=kmi_hack_brush_select_properties,
|
||||
include={'KEYBOARD'},
|
||||
)[1]
|
||||
elif mode in {'EDIT', 'PARTICLE_EDIT', 'SCULPT_GPENCIL'}:
|
||||
# Doesn't use brushes
|
||||
pass
|
||||
else:
|
||||
print("Unsupported mode:", mode)
|
||||
del mode, attr
|
||||
|
||||
else:
|
||||
kmi_found = None
|
||||
|
||||
|
@ -403,7 +357,6 @@ def generate(context, space_type, *, use_fallback_keys=True, use_reset=True):
|
|||
|
||||
if use_hack_properties:
|
||||
keymap.keymap_items.remove(kmi_hack)
|
||||
keymap.keymap_items.remove(kmi_hack_brush_select)
|
||||
|
||||
# Keep last so we can try add a key without any modifiers
|
||||
# in the case this toolbar was activated with modifiers.
|
||||
|
|
|
@ -5666,27 +5666,37 @@ def km_sculpt(params):
|
|||
op_menu_pie("VIEW3D_MT_sculpt_automasking_pie", {"type": 'A', "alt": True, "value": 'PRESS'}),
|
||||
op_menu_pie("VIEW3D_MT_sculpt_face_sets_edit_pie", {"type": 'W', "value": 'PRESS', "alt": True}),
|
||||
*_template_items_context_panel("VIEW3D_PT_sculpt_context_menu", params.context_menu_event),
|
||||
# Tools
|
||||
("paint.brush_select", {"type": 'V', "value": 'PRESS'},
|
||||
{"properties": [("sculpt_tool", 'DRAW')]}),
|
||||
("paint.brush_select", {"type": 'S', "value": 'PRESS'},
|
||||
{"properties": [("sculpt_tool", 'SMOOTH')]}),
|
||||
("paint.brush_select", {"type": 'P', "value": 'PRESS'},
|
||||
{"properties": [("sculpt_tool", 'PINCH')]}),
|
||||
("paint.brush_select", {"type": 'I', "value": 'PRESS'},
|
||||
{"properties": [("sculpt_tool", 'INFLATE')]}),
|
||||
("paint.brush_select", {"type": 'G', "value": 'PRESS'},
|
||||
{"properties": [("sculpt_tool", 'GRAB')]}),
|
||||
("paint.brush_select", {"type": 'T', "value": 'PRESS', "shift": True},
|
||||
{"properties": [("sculpt_tool", 'SCRAPE')]}),
|
||||
("paint.brush_select", {"type": 'C', "value": 'PRESS'},
|
||||
{"properties": [("sculpt_tool", 'CLAY_STRIPS')]}),
|
||||
("paint.brush_select", {"type": 'C', "value": 'PRESS', "shift": True},
|
||||
{"properties": [("sculpt_tool", 'CREASE')]}),
|
||||
("paint.brush_select", {"type": 'K', "value": 'PRESS'},
|
||||
{"properties": [("sculpt_tool", 'SNAKE_HOOK')]}),
|
||||
("paint.brush_select", {"type": 'M', "value": 'PRESS'},
|
||||
{"properties": [("sculpt_tool", 'MASK'), ("toggle", True), ("create_missing", True)]}),
|
||||
# Brushes
|
||||
("brush.asset_select", {"type": 'V', "value": 'PRESS'},
|
||||
{"properties": [("asset_library_type", 'ESSENTIALS'),
|
||||
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Draw")]}),
|
||||
("brush.asset_select", {"type": 'S', "value": 'PRESS'},
|
||||
{"properties": [("asset_library_type", 'ESSENTIALS'),
|
||||
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Smooth")]}),
|
||||
("brush.asset_select", {"type": 'P', "value": 'PRESS'},
|
||||
{"properties": [("asset_library_type", 'ESSENTIALS'),
|
||||
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Pinch/Magnify")]}),
|
||||
("brush.asset_select", {"type": 'I', "value": 'PRESS'},
|
||||
{"properties": [("asset_library_type", 'ESSENTIALS'),
|
||||
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Inflate/Deflate")]}),
|
||||
("brush.asset_select", {"type": 'G', "value": 'PRESS'},
|
||||
{"properties": [("asset_library_type", 'ESSENTIALS'),
|
||||
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Grab")]}),
|
||||
("brush.asset_select", {"type": 'T', "value": 'PRESS', "shift": True},
|
||||
{"properties": [("asset_library_type", 'ESSENTIALS'),
|
||||
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Scrape/Fill")]}),
|
||||
("brush.asset_select", {"type": 'C', "value": 'PRESS'},
|
||||
{"properties": [("asset_library_type", 'ESSENTIALS'),
|
||||
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Clay Strips")]}),
|
||||
("brush.asset_select", {"type": 'C', "value": 'PRESS', "shift": True},
|
||||
{"properties": [("asset_library_type", 'ESSENTIALS'),
|
||||
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Crease")]}),
|
||||
("brush.asset_select", {"type": 'K', "value": 'PRESS'},
|
||||
{"properties": [("asset_library_type", 'ESSENTIALS'),
|
||||
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Snake Hook")]}),
|
||||
("brush.asset_select", {"type": 'M', "value": 'PRESS'},
|
||||
{"properties": [("asset_library_type", 'ESSENTIALS'),
|
||||
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Mask")]}),
|
||||
])
|
||||
|
||||
# Lasso Masking.
|
||||
|
|
|
@ -2,24 +2,76 @@
|
|||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import bpy
|
||||
from bpy.types import Menu
|
||||
|
||||
|
||||
class BrushAssetShelf:
|
||||
bl_options = {'DEFAULT_VISIBLE', 'NO_ASSET_DRAG', 'STORE_ENABLED_CATALOGS_IN_PREFERENCES'}
|
||||
bl_activate_operator = "BRUSH_OT_asset_select"
|
||||
bl_default_preview_size = 48
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
prefs = context.preferences
|
||||
if not prefs.experimental.use_extended_asset_browser:
|
||||
return False
|
||||
|
||||
return context.mode == 'SCULPT'
|
||||
return context.object and context.object.mode == cls.mode
|
||||
|
||||
@classmethod
|
||||
def asset_poll(cls, asset):
|
||||
return asset.id_type == 'BRUSH'
|
||||
if asset.id_type != 'BRUSH':
|
||||
return False
|
||||
if hasattr(cls, "mode_prop"):
|
||||
return asset.metadata.get(cls.mode_prop, False)
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def get_active_asset(cls):
|
||||
# Only show active highlight when using the brush tool.
|
||||
from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
|
||||
tool = ToolSelectPanelHelper.tool_active_from_context(bpy.context)
|
||||
if tool.idname != "builtin.brush":
|
||||
return None
|
||||
|
||||
paint_settings = UnifiedPaintPanel.paint_settings(bpy.context)
|
||||
return paint_settings.brush_asset_reference if paint_settings else None
|
||||
|
||||
@classmethod
|
||||
def draw_context_menu(self, context, asset, layout):
|
||||
# Currently this menu adds operators that deal with the affected brush and don't take the
|
||||
# asset into account. Luckily that is okay for now, since right clicking in the grid view
|
||||
# also activates the item.
|
||||
layout.menu_contents("VIEW3D_MT_brush_context_menu")
|
||||
|
||||
@staticmethod
|
||||
def get_shelf_name_from_context(context):
|
||||
mode_map = {
|
||||
'SCULPT': "VIEW3D_AST_brush_sculpt",
|
||||
'PAINT_VERTEX': "VIEW3D_AST_brush_vertex_paint",
|
||||
'PAINT_WEIGHT': "VIEW3D_AST_brush_weight_paint",
|
||||
'PAINT_TEXTURE': "VIEW3D_AST_brush_texture_paint",
|
||||
'PAINT_2D': "IMAGE_AST_brush_paint",
|
||||
'PAINT_GPENCIL': "VIEW3D_AST_brush_gpencil_paint",
|
||||
'SCULPT_GPENCIL': "VIEW3D_AST_brush_gpencil_sculpt",
|
||||
'WEIGHT_GPENCIL': "VIEW3D_AST_brush_gpencil_weight",
|
||||
'VERTEX_GPENCIL': "VIEW3D_AST_brush_gpencil_vertex",
|
||||
'PAINT_GREASE_PENCIL': "VIEW3D_AST_brush_gpencil_paint",
|
||||
'SCULPT_GREASE_PENCIL': "VIEW3D_AST_brush_gpencil_sculpt",
|
||||
'WEIGHT_GREASE_PENCIL': "VIEW3D_AST_brush_gpencil_weight",
|
||||
'VERTEX_GREASE_PENCIL': "VIEW3D_AST_brush_gpencil_vertex",
|
||||
'SCULPT_CURVES': "VIEW3D_AST_brush_sculpt_curves",
|
||||
}
|
||||
mode = UnifiedPaintPanel.get_brush_mode(context)
|
||||
return mode_map[mode]
|
||||
|
||||
@staticmethod
|
||||
def draw_popup_selector(layout, context, brush, show_name=True):
|
||||
preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0
|
||||
|
||||
layout.template_asset_shelf_popover(
|
||||
BrushAssetShelf.get_shelf_name_from_context(context),
|
||||
name=brush.name if (brush and show_name) else None,
|
||||
icon='BRUSH_DATA' if not preview_icon_id else 'NONE',
|
||||
icon_value=preview_icon_id,
|
||||
)
|
||||
|
||||
|
||||
class UnifiedPaintPanel:
|
||||
|
@ -156,28 +208,29 @@ class BrushPanel(UnifiedPaintPanel):
|
|||
|
||||
|
||||
class BrushSelectPanel(BrushPanel):
|
||||
bl_label = "Brushes"
|
||||
bl_label = "Brush Asset"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
settings = self.paint_settings(context)
|
||||
if settings is None:
|
||||
return
|
||||
|
||||
brush = settings.brush
|
||||
|
||||
row = layout.row()
|
||||
large_preview = True
|
||||
if large_preview:
|
||||
row.column().template_ID_preview(settings, "brush", new="brush.add", rows=3, cols=8, hide_buttons=False)
|
||||
else:
|
||||
row.column().template_ID(settings, "brush", new="brush.add")
|
||||
|
||||
col = row.column(align=True)
|
||||
BrushAssetShelf.draw_popup_selector(col, context, brush, show_name=False)
|
||||
if brush:
|
||||
col.prop(brush, "name", text="")
|
||||
|
||||
if brush is None:
|
||||
return
|
||||
|
||||
col = row.column()
|
||||
col.menu("VIEW3D_MT_brush_context_menu", icon='DOWNARROW_HLT', text="")
|
||||
|
||||
if brush is not None:
|
||||
col.prop(brush, "use_custom_icon", toggle=True, icon='FILE_IMAGE', text="")
|
||||
|
||||
if brush.use_custom_icon:
|
||||
layout.prop(brush, "icon_filepath", text="")
|
||||
|
||||
|
||||
class ColorPalettePanel(BrushPanel):
|
||||
bl_label = "Color Palette"
|
||||
|
@ -994,6 +1047,9 @@ def brush_settings_advanced(layout, context, brush, popover=False):
|
|||
use_frontface = False
|
||||
|
||||
if mode == 'SCULPT':
|
||||
layout.prop(brush, "sculpt_tool")
|
||||
layout.separator()
|
||||
|
||||
capabilities = brush.sculpt_capabilities
|
||||
use_accumulate = capabilities.has_accumulate
|
||||
use_frontface = True
|
||||
|
@ -1084,6 +1140,9 @@ def brush_settings_advanced(layout, context, brush, popover=False):
|
|||
|
||||
# 3D and 2D Texture Paint.
|
||||
elif mode in {'PAINT_TEXTURE', 'PAINT_2D'}:
|
||||
layout.prop(brush, "image_tool")
|
||||
layout.separator()
|
||||
|
||||
capabilities = brush.image_paint_capabilities
|
||||
use_accumulate = capabilities.has_accumulate
|
||||
|
||||
|
@ -1111,6 +1170,9 @@ def brush_settings_advanced(layout, context, brush, popover=False):
|
|||
|
||||
# Vertex Paint #
|
||||
elif mode == 'PAINT_VERTEX':
|
||||
layout.prop(brush, "vertex_tool")
|
||||
layout.separator()
|
||||
|
||||
layout.prop(brush, "use_alpha")
|
||||
if brush.vertex_tool != 'SMEAR':
|
||||
use_accumulate = True
|
||||
|
@ -1118,10 +1180,17 @@ def brush_settings_advanced(layout, context, brush, popover=False):
|
|||
|
||||
# Weight Paint
|
||||
elif mode == 'PAINT_WEIGHT':
|
||||
layout.prop(brush, "weight_tool")
|
||||
layout.separator()
|
||||
|
||||
if brush.weight_tool != 'SMEAR':
|
||||
use_accumulate = True
|
||||
use_frontface = True
|
||||
|
||||
# Sculpt Curves
|
||||
elif mode == 'SCULPT_CURVES':
|
||||
layout.prop(brush, "curves_sculpt_tool")
|
||||
|
||||
# Draw shared settings.
|
||||
if use_accumulate:
|
||||
layout.prop(brush, "use_accumulate")
|
||||
|
@ -1129,6 +1198,29 @@ def brush_settings_advanced(layout, context, brush, popover=False):
|
|||
if use_frontface:
|
||||
layout.prop(brush, "use_frontface", text="Front Faces Only")
|
||||
|
||||
# Brush modes
|
||||
header, panel = layout.panel("modes", default_closed=True)
|
||||
header.label(text="Modes")
|
||||
if panel:
|
||||
panel.use_property_split = True
|
||||
panel.use_property_decorate = False
|
||||
|
||||
col = panel.column(align=True)
|
||||
col.prop(brush, "use_paint_sculpt", text="Sculpt")
|
||||
col.prop(brush, "use_paint_uv_sculpt", text="UV Sculpt")
|
||||
col.prop(brush, "use_paint_vertex", text="Vertex Paint")
|
||||
col.prop(brush, "use_paint_weight", text="Weight Paint")
|
||||
col.prop(brush, "use_paint_image", text="Texture Paint")
|
||||
col.prop(brush, "use_paint_sculpt_curves", text="Sculpt Curves")
|
||||
|
||||
if len(brush.icon_filepath) > 0:
|
||||
header, panel = layout.panel("legacy", default_closed=True)
|
||||
header.label(text="Legacy Icon")
|
||||
if panel:
|
||||
panel.label(text="Brush icons have moved to the asset preview image", icon='ERROR')
|
||||
panel.prop(brush, "use_custom_icon")
|
||||
panel.prop(brush, "icon_filepath")
|
||||
|
||||
|
||||
def draw_color_settings(context, layout, brush, color_type=False):
|
||||
"""Draw color wheel and gradient settings."""
|
||||
|
@ -1354,7 +1446,6 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False)
|
|||
row.prop(brush, "size", text="Radius")
|
||||
row.prop(gp_settings, "use_pressure", text="", icon='STYLUS_PRESSURE')
|
||||
row.prop(gp_settings, "use_occlude_eraser", text="", icon='XRAY')
|
||||
row.prop(gp_settings, "use_default_eraser", text="")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(gp_settings, "eraser_mode", expand=True)
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
from bpy.types import (
|
||||
AssetShelf,
|
||||
Header,
|
||||
Menu,
|
||||
Panel,
|
||||
|
@ -23,6 +24,7 @@ from bl_ui.properties_paint_common import (
|
|||
SmoothStrokePanel,
|
||||
FalloffPanel,
|
||||
DisplayPanel,
|
||||
BrushAssetShelf,
|
||||
)
|
||||
from bl_ui.properties_grease_pencil_common import (
|
||||
AnnotationDataPanel,
|
||||
|
@ -764,9 +766,10 @@ class _draw_tool_settings_context_mode:
|
|||
return
|
||||
|
||||
paint = context.tool_settings.image_paint
|
||||
layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
|
||||
|
||||
brush = paint.brush
|
||||
|
||||
BrushAssetShelf.draw_popup_selector(layout, context, brush)
|
||||
|
||||
if brush is None:
|
||||
return
|
||||
|
||||
|
@ -1182,7 +1185,7 @@ class IMAGE_PT_udim_tiles(Panel):
|
|||
|
||||
|
||||
class IMAGE_PT_paint_select(Panel, ImagePaintPanel, BrushSelectPanel):
|
||||
bl_label = "Brushes"
|
||||
bl_label = "Brush Asset"
|
||||
bl_context = ".paint_common_2d"
|
||||
bl_category = "Tool"
|
||||
|
||||
|
@ -1220,8 +1223,8 @@ class IMAGE_PT_paint_settings_advanced(Panel, ImagePaintPanel):
|
|||
|
||||
settings = context.tool_settings.image_paint
|
||||
brush = settings.brush
|
||||
|
||||
brush_settings_advanced(layout.column(), context, brush, self.is_popover)
|
||||
if brush:
|
||||
brush_settings_advanced(layout.column(), context, brush, self.is_popover)
|
||||
|
||||
|
||||
class IMAGE_PT_paint_color(Panel, ImagePaintPanel):
|
||||
|
@ -1234,16 +1237,17 @@ class IMAGE_PT_paint_color(Panel, ImagePaintPanel):
|
|||
def poll(cls, context):
|
||||
settings = context.tool_settings.image_paint
|
||||
brush = settings.brush
|
||||
if not brush:
|
||||
return False
|
||||
capabilities = brush.image_paint_capabilities
|
||||
|
||||
return capabilities.has_color
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
settings = context.tool_settings.image_paint
|
||||
brush = settings.brush
|
||||
|
||||
draw_color_settings(context, layout, brush, color_type=True)
|
||||
if brush:
|
||||
draw_color_settings(context, layout, brush, color_type=True)
|
||||
|
||||
|
||||
class IMAGE_PT_paint_swatches(Panel, ImagePaintPanel, ColorPalettePanel):
|
||||
|
@ -1688,6 +1692,18 @@ class IMAGE_PT_annotation(AnnotationDataPanel, Panel):
|
|||
# Grease Pencil drawing tools.
|
||||
|
||||
|
||||
class ImageAssetShelf(BrushAssetShelf):
|
||||
bl_space_type = "IMAGE_EDITOR"
|
||||
|
||||
|
||||
class IMAGE_AST_brush_paint(ImageAssetShelf, AssetShelf):
|
||||
mode_prop = "use_paint_image"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.space_data and context.space_data.ui_mode == 'PAINT'
|
||||
|
||||
|
||||
classes = (
|
||||
IMAGE_MT_view,
|
||||
IMAGE_MT_view_zoom,
|
||||
|
@ -1757,6 +1773,7 @@ classes = (
|
|||
IMAGE_PT_overlay_uv_edit_geometry,
|
||||
IMAGE_PT_overlay_texture_paint,
|
||||
IMAGE_PT_overlay_image,
|
||||
IMAGE_AST_brush_paint,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -18,7 +18,9 @@ from bl_ui.space_toolsystem_common import (
|
|||
ToolSelectPanelHelper,
|
||||
ToolDef,
|
||||
)
|
||||
|
||||
from bl_ui.properties_paint_common import (
|
||||
BrushAssetShelf,
|
||||
)
|
||||
from bpy.app.translations import pgettext_tip as tip_
|
||||
|
||||
|
||||
|
@ -1396,16 +1398,23 @@ class _defs_sculpt:
|
|||
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
return generate_from_enum_ex(
|
||||
context,
|
||||
idname_prefix="builtin_brush.",
|
||||
icon_prefix="brush.sculpt.",
|
||||
type=bpy.types.Brush,
|
||||
attr="sculpt_tool",
|
||||
# TODO(@ideasman42): we may want to enable this,
|
||||
# it causes awkward grouping with 2x column button layout.
|
||||
use_separators=False,
|
||||
)
|
||||
# Though `data_block` is conceptually unnecessary with a single brush tool,
|
||||
# it's still used because many areas assume that brush tools have it set #bToolRef.
|
||||
tool = None
|
||||
if context:
|
||||
brush = context.tool_settings.sculpt.brush
|
||||
if brush:
|
||||
tool = brush.sculpt_tool
|
||||
return [
|
||||
ToolDef.from_dict(
|
||||
dict(
|
||||
idname="builtin.brush",
|
||||
label="Brush",
|
||||
icon="brush.sculpt.paint",
|
||||
data_block=tool
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
@ToolDef.from_fn
|
||||
def hide_border():
|
||||
|
@ -1726,13 +1735,23 @@ class _defs_vertex_paint:
|
|||
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
return generate_from_enum_ex(
|
||||
context,
|
||||
idname_prefix="builtin_brush.",
|
||||
icon_prefix="brush.paint_vertex.",
|
||||
type=bpy.types.Brush,
|
||||
attr="vertex_tool",
|
||||
)
|
||||
# Though `data_block` is conceptually unnecessary with a single brush tool,
|
||||
# it's still used because many areas assume that brush tools have it set #bToolRef.
|
||||
tool = None
|
||||
if context:
|
||||
brush = context.tool_settings.vertex_paint.brush
|
||||
if brush:
|
||||
tool = brush.vertex_tool
|
||||
return [
|
||||
ToolDef.from_dict(
|
||||
dict(
|
||||
idname="builtin.brush",
|
||||
label="Brush",
|
||||
icon="brush.sculpt.paint",
|
||||
data_block=tool
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class _defs_texture_paint:
|
||||
|
@ -1747,14 +1766,24 @@ class _defs_texture_paint:
|
|||
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
return generate_from_enum_ex(
|
||||
context,
|
||||
idname_prefix="builtin_brush.",
|
||||
icon_prefix="brush.paint_texture.",
|
||||
type=bpy.types.Brush,
|
||||
attr="image_tool",
|
||||
cursor='PAINT_CROSS',
|
||||
)
|
||||
# Though `data_block` is conceptually unnecessary with a single brush tool,
|
||||
# it's still used because many areas assume that brush tools have it set #bToolRef.
|
||||
tool = None
|
||||
if context:
|
||||
brush = context.tool_settings.image_paint.brush
|
||||
if brush:
|
||||
tool = brush.image_tool
|
||||
return [
|
||||
ToolDef.from_dict(
|
||||
dict(
|
||||
idname="builtin.brush",
|
||||
label="Brush",
|
||||
icon="brush.sculpt.paint",
|
||||
cursor='PAINT_CROSS',
|
||||
data_block=tool
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class _defs_weight_paint:
|
||||
|
@ -1774,13 +1803,23 @@ class _defs_weight_paint:
|
|||
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
return generate_from_enum_ex(
|
||||
context,
|
||||
idname_prefix="builtin_brush.",
|
||||
icon_prefix="brush.paint_weight.",
|
||||
type=bpy.types.Brush,
|
||||
attr="weight_tool",
|
||||
)
|
||||
# Though `data_block` is conceptually unnecessary with a single brush tool,
|
||||
# it's still used because many areas assume that brush tools have it set #bToolRef.
|
||||
tool = None
|
||||
if context:
|
||||
brush = context.tool_settings.weight_paint.brush
|
||||
if brush:
|
||||
tool = brush.weight_tool
|
||||
return [
|
||||
ToolDef.from_dict(
|
||||
dict(
|
||||
idname="builtin.brush",
|
||||
label="Brush",
|
||||
icon="brush.sculpt.paint",
|
||||
data_block=tool
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
@ToolDef.from_fn
|
||||
def sample_weight():
|
||||
|
@ -1853,45 +1892,25 @@ class _defs_weight_paint:
|
|||
|
||||
class _defs_grease_pencil_paint:
|
||||
|
||||
# FIXME: Replace brush tools with code below once they are all implemented:
|
||||
#
|
||||
# @staticmethod
|
||||
# def generate_from_brushes(context):
|
||||
# return generate_from_enum_ex(
|
||||
# context,
|
||||
# idname_prefix="builtin_brush.",
|
||||
# icon_prefix="brush.gpencil_draw.",
|
||||
# type=bpy.types.Brush,
|
||||
# attr="gpencil_tool",
|
||||
# cursor='DOT',
|
||||
# )
|
||||
|
||||
@ToolDef.from_fn
|
||||
def draw():
|
||||
return dict(
|
||||
idname="builtin_brush.Draw",
|
||||
label="Draw",
|
||||
icon="brush.gpencil_draw.draw",
|
||||
data_block='DRAW',
|
||||
)
|
||||
|
||||
@ToolDef.from_fn
|
||||
def fill():
|
||||
return dict(
|
||||
idname="builtin_brush.Fill",
|
||||
label="Fill",
|
||||
icon="brush.gpencil_draw.fill",
|
||||
data_block='FILL',
|
||||
)
|
||||
|
||||
@ToolDef.from_fn
|
||||
def erase():
|
||||
return dict(
|
||||
idname="builtin_brush.Erase",
|
||||
label="Erase",
|
||||
icon="brush.gpencil_draw.erase",
|
||||
data_block='ERASE',
|
||||
)
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
# Though `data_block` is conceptually unnecessary with a single brush tool,
|
||||
# it's still used because many areas assume that brush tools have it set #bToolRef.
|
||||
tool = None
|
||||
if context:
|
||||
brush = context.tool_settings.gpencil_paint.brush
|
||||
if brush:
|
||||
tool = brush.gpencil_tool
|
||||
return [
|
||||
ToolDef.from_dict(
|
||||
dict(
|
||||
idname="builtin.brush",
|
||||
label="Brush",
|
||||
icon="brush.sculpt.paint",
|
||||
data_block=tool
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
@ToolDef.from_fn
|
||||
def cutter():
|
||||
|
@ -1934,7 +1953,8 @@ class _defs_grease_pencil_paint:
|
|||
row = layout.row(align=True)
|
||||
tool_settings = context.scene.tool_settings
|
||||
settings = tool_settings.gpencil_paint
|
||||
row.template_ID_preview(settings, "brush", rows=3, cols=8, hide_buttons=True)
|
||||
|
||||
BrushAssetShelf.draw_popup_selector(row, context, brush)
|
||||
|
||||
from bl_ui.properties_paint_common import (
|
||||
brush_basic_grease_pencil_paint_settings,
|
||||
|
@ -2316,7 +2336,8 @@ class _defs_gpencil_paint:
|
|||
row = layout.row(align=True)
|
||||
tool_settings = context.scene.tool_settings
|
||||
settings = tool_settings.gpencil_paint
|
||||
row.template_ID_preview(settings, "brush", rows=3, cols=8, hide_buttons=True)
|
||||
|
||||
BrushAssetShelf.draw_popup_selector(layout, context, brush)
|
||||
|
||||
from bl_ui.properties_paint_common import (
|
||||
brush_basic_gpencil_paint_settings,
|
||||
|
@ -2329,17 +2350,23 @@ class _defs_gpencil_paint:
|
|||
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
return generate_from_enum_ex(
|
||||
context,
|
||||
idname_prefix="builtin_brush.",
|
||||
icon_prefix="brush.gpencil_draw.",
|
||||
type=bpy.types.Brush,
|
||||
attr="gpencil_tool",
|
||||
cursor='DOT',
|
||||
tooldef_keywords=dict(
|
||||
operator="gpencil.draw",
|
||||
),
|
||||
)
|
||||
# Though `data_block` is conceptually unnecessary with a single brush tool,
|
||||
# it's still used because many areas assume that brush tools have it set #bToolRef.
|
||||
tool = None
|
||||
if context:
|
||||
brush = context.tool_settings.gpencil_paint.brush
|
||||
if brush:
|
||||
tool = brush.gpencil_tool
|
||||
return [
|
||||
ToolDef.from_dict(
|
||||
dict(
|
||||
idname="builtin.brush",
|
||||
label="Brush",
|
||||
icon="brush.sculpt.paint",
|
||||
data_block=tool
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
@ToolDef.from_fn
|
||||
def cutter():
|
||||
|
@ -2695,16 +2722,23 @@ class _defs_gpencil_sculpt:
|
|||
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
return generate_from_enum_ex(
|
||||
context,
|
||||
idname_prefix="builtin_brush.",
|
||||
icon_prefix="ops.gpencil.sculpt_",
|
||||
type=bpy.types.Brush,
|
||||
attr="gpencil_sculpt_tool",
|
||||
tooldef_keywords=dict(
|
||||
operator="gpencil.sculpt_paint",
|
||||
),
|
||||
)
|
||||
# Though `data_block` is conceptually unnecessary with a single brush tool,
|
||||
# it's still used because many areas assume that brush tools have it set #bToolRef.
|
||||
tool = None
|
||||
if context:
|
||||
brush = context.tool_settings.gpencil_sculpt_paint.brush
|
||||
if brush:
|
||||
tool = brush.gpencil_sculpt_tool
|
||||
return [
|
||||
ToolDef.from_dict(
|
||||
dict(
|
||||
idname="builtin.brush",
|
||||
label="Brush",
|
||||
icon="brush.sculpt.paint",
|
||||
data_block=tool
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class _defs_grease_pencil_sculpt:
|
||||
|
@ -2725,33 +2759,46 @@ class _defs_grease_pencil_sculpt:
|
|||
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
return generate_from_enum_ex(
|
||||
context,
|
||||
idname_prefix="builtin_brush.",
|
||||
icon_prefix="ops.gpencil.sculpt_",
|
||||
type=bpy.types.Brush,
|
||||
# Uses GPv2 tool settings
|
||||
attr="gpencil_sculpt_tool",
|
||||
tooldef_keywords=dict(
|
||||
operator="grease_pencil.sculpt_paint",
|
||||
),
|
||||
)
|
||||
# Though `data_block` is conceptually unnecessary with a single brush tool,
|
||||
# it's still used because many areas assume that brush tools have it set #bToolRef.
|
||||
tool = None
|
||||
if context:
|
||||
brush = context.tool_settings.gpencil_sculpt_paint.brush
|
||||
if brush:
|
||||
tool = brush.gpencil_sculpt_tool
|
||||
return [
|
||||
ToolDef.from_dict(
|
||||
dict(
|
||||
idname="builtin.brush",
|
||||
label="Brush",
|
||||
icon="brush.sculpt.paint",
|
||||
data_block=tool
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class _defs_gpencil_weight:
|
||||
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
return generate_from_enum_ex(
|
||||
context,
|
||||
idname_prefix="builtin_brush.",
|
||||
icon_prefix="ops.gpencil.sculpt_",
|
||||
type=bpy.types.Brush,
|
||||
attr="gpencil_weight_tool",
|
||||
tooldef_keywords=dict(
|
||||
operator="gpencil.weight_paint",
|
||||
),
|
||||
)
|
||||
# Though `data_block` is conceptually unnecessary with a single brush tool,
|
||||
# it's still used because many areas assume that brush tools have it set #bToolRef.
|
||||
tool = None
|
||||
if context:
|
||||
brush = context.tool_settings.gpencil_weight_paint.brush
|
||||
if brush:
|
||||
tool = brush.gpencil_weight_tool
|
||||
return [
|
||||
ToolDef.from_dict(
|
||||
dict(
|
||||
idname="builtin.brush",
|
||||
label="Brush",
|
||||
icon="brush.sculpt.paint",
|
||||
data_block=tool
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class _defs_grease_pencil_weight:
|
||||
|
@ -2775,17 +2822,23 @@ class _defs_curves_sculpt:
|
|||
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
return generate_from_enum_ex(
|
||||
context,
|
||||
idname_prefix="builtin_brush.",
|
||||
icon_prefix="ops.curves.sculpt_",
|
||||
type=bpy.types.Brush,
|
||||
attr="curves_sculpt_tool",
|
||||
icon_map={
|
||||
# Use the generic icon for selection painting.
|
||||
"ops.curves.sculpt_selection_paint": "ops.generic.select_paint",
|
||||
},
|
||||
)
|
||||
# Though `data_block` is conceptually unnecessary with a single brush tool,
|
||||
# it's still used because many areas assume that brush tools have it set #bToolRef.
|
||||
tool = None
|
||||
if context:
|
||||
brush = context.tool_settings.curves_sculpt.brush
|
||||
if brush:
|
||||
tool = brush.curves_sculpt_tool
|
||||
return [
|
||||
ToolDef.from_dict(
|
||||
dict(
|
||||
idname="builtin.brush",
|
||||
label="Brush",
|
||||
icon="brush.sculpt.paint",
|
||||
data_block=tool
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class _defs_gpencil_vertex:
|
||||
|
@ -2807,17 +2860,23 @@ class _defs_gpencil_vertex:
|
|||
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
return generate_from_enum_ex(
|
||||
context,
|
||||
idname_prefix="builtin_brush.",
|
||||
icon_prefix="brush.paint_vertex.",
|
||||
type=bpy.types.Brush,
|
||||
attr="gpencil_vertex_tool",
|
||||
cursor='DOT',
|
||||
tooldef_keywords=dict(
|
||||
operator="gpencil.vertex_paint",
|
||||
),
|
||||
)
|
||||
# Though `data_block` is conceptually unnecessary with a single brush tool,
|
||||
# it's still used because many areas assume that brush tools have it set #bToolRef.
|
||||
tool = None
|
||||
if context:
|
||||
brush = context.tool_settings.gpencil_vertex_paint.brush
|
||||
if brush:
|
||||
tool = brush.gpencil_vertex_tool
|
||||
return [
|
||||
ToolDef.from_dict(
|
||||
dict(
|
||||
idname="builtin.brush",
|
||||
label="Brush",
|
||||
icon="brush.sculpt.paint",
|
||||
data_block=tool
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class _defs_node_select:
|
||||
|
@ -3509,9 +3568,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
|
|||
'PAINT_GREASE_PENCIL': [
|
||||
_defs_view3d_generic.cursor,
|
||||
None,
|
||||
_defs_grease_pencil_paint.draw,
|
||||
_defs_grease_pencil_paint.fill,
|
||||
_defs_grease_pencil_paint.erase,
|
||||
_defs_grease_pencil_paint.generate_from_brushes,
|
||||
_defs_grease_pencil_paint.cutter,
|
||||
_defs_grease_pencil_paint.tint,
|
||||
None,
|
||||
|
|
|
@ -248,9 +248,10 @@ class _draw_tool_settings_context_mode:
|
|||
return False
|
||||
|
||||
paint = context.tool_settings.sculpt
|
||||
layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
|
||||
|
||||
brush = paint.brush
|
||||
|
||||
BrushAssetShelf.draw_popup_selector(layout, context, brush)
|
||||
|
||||
if brush is None:
|
||||
return False
|
||||
|
||||
|
@ -309,9 +310,10 @@ class _draw_tool_settings_context_mode:
|
|||
return False
|
||||
|
||||
paint = context.tool_settings.image_paint
|
||||
layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
|
||||
|
||||
brush = paint.brush
|
||||
|
||||
BrushAssetShelf.draw_popup_selector(layout, context, brush)
|
||||
|
||||
if brush is None:
|
||||
return False
|
||||
|
||||
|
@ -325,9 +327,10 @@ class _draw_tool_settings_context_mode:
|
|||
return False
|
||||
|
||||
paint = context.tool_settings.vertex_paint
|
||||
layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
|
||||
|
||||
brush = paint.brush
|
||||
|
||||
BrushAssetShelf.draw_popup_selector(layout, context, brush)
|
||||
|
||||
if brush is None:
|
||||
return False
|
||||
|
||||
|
@ -341,8 +344,10 @@ class _draw_tool_settings_context_mode:
|
|||
return False
|
||||
|
||||
paint = context.tool_settings.weight_paint
|
||||
layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
|
||||
brush = paint.brush
|
||||
|
||||
BrushAssetShelf.draw_popup_selector(layout, context, brush)
|
||||
|
||||
if brush is None:
|
||||
return False
|
||||
|
||||
|
@ -404,7 +409,8 @@ class _draw_tool_settings_context_mode:
|
|||
|
||||
row = layout.row(align=True)
|
||||
settings = tool_settings.gpencil_paint
|
||||
row.template_ID_preview(settings, "brush", rows=3, cols=8, hide_buttons=True)
|
||||
|
||||
BrushAssetShelf.draw_popup_selector(layout, context, brush)
|
||||
|
||||
if ob and brush.gpencil_tool in {'FILL', 'DRAW'}:
|
||||
from bl_ui.properties_paint_common import (
|
||||
|
@ -432,6 +438,8 @@ class _draw_tool_settings_context_mode:
|
|||
paint = tool_settings.gpencil_sculpt_paint
|
||||
brush = paint.brush
|
||||
|
||||
BrushAssetShelf.draw_popup_selector(layout, context, brush)
|
||||
|
||||
from bl_ui.properties_paint_common import (
|
||||
brush_basic_gpencil_sculpt_settings,
|
||||
)
|
||||
|
@ -445,12 +453,12 @@ class _draw_tool_settings_context_mode:
|
|||
return False
|
||||
|
||||
paint = context.tool_settings.gpencil_sculpt_paint
|
||||
layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
|
||||
|
||||
brush = paint.brush
|
||||
if brush is None:
|
||||
return False
|
||||
|
||||
BrushAssetShelf.draw_popup_selector(layout, context, brush)
|
||||
|
||||
tool_settings = context.tool_settings
|
||||
capabilities = brush.sculpt_capabilities
|
||||
|
||||
|
@ -506,7 +514,7 @@ class _draw_tool_settings_context_mode:
|
|||
paint = tool_settings.gpencil_weight_paint
|
||||
brush = paint.brush
|
||||
|
||||
layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
|
||||
BrushAssetShelf.draw_popup_selector(layout, context, brush)
|
||||
|
||||
brush_basic_gpencil_weight_settings(layout, context, brush, compact=True)
|
||||
|
||||
|
@ -521,12 +529,12 @@ class _draw_tool_settings_context_mode:
|
|||
return False
|
||||
|
||||
paint = context.tool_settings.gpencil_weight_paint
|
||||
layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
|
||||
|
||||
brush = paint.brush
|
||||
if brush is None:
|
||||
return False
|
||||
|
||||
BrushAssetShelf.draw_popup_selector(layout, context, brush)
|
||||
|
||||
brush_basic_grease_pencil_weight_settings(layout, context, brush, compact=True)
|
||||
|
||||
layout.popover("VIEW3D_PT_tools_grease_pencil_weight_options", text="Options")
|
||||
|
@ -545,7 +553,8 @@ class _draw_tool_settings_context_mode:
|
|||
|
||||
row = layout.row(align=True)
|
||||
settings = tool_settings.gpencil_vertex_paint
|
||||
row.template_ID_preview(settings, "brush", rows=3, cols=8, hide_buttons=True)
|
||||
|
||||
BrushAssetShelf.draw_popup_selector(layout, context, brush)
|
||||
|
||||
if brush.gpencil_vertex_tool not in {'BLUR', 'AVERAGE', 'SMEAR'}:
|
||||
row.separator(factor=0.4)
|
||||
|
@ -698,7 +707,8 @@ class _draw_tool_settings_context_mode:
|
|||
return False
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
|
||||
|
||||
BrushAssetShelf.draw_popup_selector(layout, context, brush)
|
||||
|
||||
grease_pencil_tool = brush.gpencil_tool
|
||||
|
||||
|
@ -3542,23 +3552,6 @@ class VIEW3D_MT_make_links(Menu):
|
|||
layout.operator("object.datalayout_transfer")
|
||||
|
||||
|
||||
class VIEW3D_MT_brush_paint_modes(Menu):
|
||||
bl_label = "Enabled Modes"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
settings = UnifiedPaintPanel.paint_settings(context)
|
||||
brush = settings.brush
|
||||
|
||||
layout.prop(brush, "use_paint_sculpt", text="Sculpt")
|
||||
layout.prop(brush, "use_paint_uv_sculpt", text="UV Sculpt")
|
||||
layout.prop(brush, "use_paint_vertex", text="Vertex Paint")
|
||||
layout.prop(brush, "use_paint_weight", text="Weight Paint")
|
||||
layout.prop(brush, "use_paint_image", text="Texture Paint")
|
||||
layout.prop(brush, "use_paint_sculpt_curves", text="Sculpt Curves")
|
||||
|
||||
|
||||
class VIEW3D_MT_paint_vertex(Menu):
|
||||
bl_label = "Paint"
|
||||
|
||||
|
@ -9134,11 +9127,58 @@ class VIEW3D_PT_viewport_debug(Panel):
|
|||
layout.prop(overlay, "use_debug_freeze_view_culling")
|
||||
|
||||
|
||||
class VIEW3D_AST_sculpt_brushes(BrushAssetShelf, bpy.types.AssetShelf):
|
||||
# Experimental: Asset shelf for sculpt brushes, only shows up if both the
|
||||
# "Asset Shelf" and the "Extended Asset Browser" experimental features are
|
||||
# enabled.
|
||||
bl_space_type = 'VIEW_3D'
|
||||
class View3DAssetShelf(BrushAssetShelf):
|
||||
bl_space_type = "VIEW_3D"
|
||||
|
||||
|
||||
class VIEW3D_AST_brush_sculpt(View3DAssetShelf, bpy.types.AssetShelf):
|
||||
mode = 'SCULPT'
|
||||
mode_prop = "use_paint_sculpt"
|
||||
|
||||
|
||||
class VIEW3D_AST_brush_sculpt_curves(View3DAssetShelf, bpy.types.AssetShelf):
|
||||
mode = 'SCULPT_CURVES'
|
||||
mode_prop = "use_paint_sculpt_curves"
|
||||
|
||||
|
||||
class VIEW3D_AST_brush_vertex_paint(View3DAssetShelf, bpy.types.AssetShelf):
|
||||
mode = 'VERTEX_PAINT'
|
||||
mode_prop = "use_paint_vertex"
|
||||
|
||||
|
||||
class VIEW3D_AST_brush_weight_paint(View3DAssetShelf, bpy.types.AssetShelf):
|
||||
mode = 'WEIGHT_PAINT'
|
||||
mode_prop = "use_paint_weight"
|
||||
|
||||
|
||||
class VIEW3D_AST_brush_texture_paint(View3DAssetShelf, bpy.types.AssetShelf):
|
||||
mode = 'TEXTURE_PAINT'
|
||||
mode_prop = "use_paint_image"
|
||||
|
||||
|
||||
class VIEW3D_AST_brush_gpencil_paint(View3DAssetShelf, bpy.types.AssetShelf):
|
||||
mode = 'PAINT_GPENCIL'
|
||||
mode_prop = "use_paint_grease_pencil"
|
||||
|
||||
|
||||
class VIEW3D_AST_brush_grease_pencil_paint(View3DAssetShelf, bpy.types.AssetShelf):
|
||||
mode = 'PAINT_GREASE_PENCIL'
|
||||
mode_prop = "use_paint_grease_pencil"
|
||||
|
||||
|
||||
class VIEW3D_AST_brush_gpencil_sculpt(View3DAssetShelf, bpy.types.AssetShelf):
|
||||
mode = 'SCULPT_GPENCIL'
|
||||
mode_prop = "use_sculpt_grease_pencil"
|
||||
|
||||
|
||||
class VIEW3D_AST_brush_gpencil_vertex(View3DAssetShelf, bpy.types.AssetShelf):
|
||||
mode = 'VERTEX_GPENCIL'
|
||||
mode_prop = "use_vertex_grease_pencil"
|
||||
|
||||
|
||||
class VIEW3D_AST_brush_gpencil_weight(View3DAssetShelf, bpy.types.AssetShelf):
|
||||
mode = 'WEIGHT_GPENCIL'
|
||||
mode_prop = "use_weight_grease_pencil"
|
||||
|
||||
|
||||
classes = (
|
||||
|
@ -9220,7 +9260,6 @@ classes = (
|
|||
VIEW3D_MT_object_cleanup,
|
||||
VIEW3D_MT_make_single_user,
|
||||
VIEW3D_MT_make_links,
|
||||
VIEW3D_MT_brush_paint_modes,
|
||||
VIEW3D_MT_paint_vertex,
|
||||
VIEW3D_MT_hook,
|
||||
VIEW3D_MT_vertex_group,
|
||||
|
@ -9408,7 +9447,16 @@ classes = (
|
|||
VIEW3D_PT_curves_sculpt_parameter_falloff,
|
||||
VIEW3D_PT_curves_sculpt_grow_shrink_scaling,
|
||||
VIEW3D_PT_viewport_debug,
|
||||
VIEW3D_AST_sculpt_brushes,
|
||||
VIEW3D_AST_brush_sculpt,
|
||||
VIEW3D_AST_brush_sculpt_curves,
|
||||
VIEW3D_AST_brush_vertex_paint,
|
||||
VIEW3D_AST_brush_weight_paint,
|
||||
VIEW3D_AST_brush_texture_paint,
|
||||
VIEW3D_AST_brush_gpencil_paint,
|
||||
VIEW3D_AST_brush_grease_pencil_paint,
|
||||
VIEW3D_AST_brush_gpencil_sculpt,
|
||||
VIEW3D_AST_brush_gpencil_vertex,
|
||||
VIEW3D_AST_brush_gpencil_weight,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@ from bl_ui.properties_paint_common import (
|
|||
brush_settings,
|
||||
brush_settings_advanced,
|
||||
draw_color_settings,
|
||||
BrushAssetShelf,
|
||||
)
|
||||
from bl_ui.utils import PresetPanel
|
||||
|
||||
|
@ -43,25 +44,22 @@ class VIEW3D_MT_brush_context_menu(Menu):
|
|||
|
||||
# skip if no active brush
|
||||
if not brush:
|
||||
layout.label(text="No Brushes currently available", icon='INFO')
|
||||
layout.label(text="No brush selected", icon='INFO')
|
||||
return
|
||||
|
||||
# brush paint modes
|
||||
layout.menu("VIEW3D_MT_brush_paint_modes")
|
||||
if brush.library and brush.library.is_editable:
|
||||
layout.operator("brush.asset_save_as", text="Duplicate Asset...", icon='DUPLICATE')
|
||||
layout.operator("brush.asset_delete", text="Delete Asset")
|
||||
|
||||
# brush tool
|
||||
layout.separator()
|
||||
|
||||
if context.image_paint_object:
|
||||
layout.prop_menu_enum(brush, "image_tool")
|
||||
elif context.vertex_paint_object:
|
||||
layout.prop_menu_enum(brush, "vertex_tool")
|
||||
elif context.weight_paint_object:
|
||||
layout.prop_menu_enum(brush, "weight_tool")
|
||||
elif context.sculpt_object:
|
||||
layout.prop_menu_enum(brush, "sculpt_tool")
|
||||
layout.operator("brush.reset")
|
||||
elif context.tool_settings.curves_sculpt:
|
||||
layout.prop_menu_enum(brush, "curves_sculpt_tool")
|
||||
layout.operator("brush.asset_edit_metadata", text="Edit Metadata")
|
||||
layout.operator("brush.asset_load_preview", text="Edit Preview Image...")
|
||||
layout.operator("brush.asset_update", text="Update Asset")
|
||||
layout.operator("brush.asset_revert", text="Revert to Asset")
|
||||
else:
|
||||
layout.operator("brush.asset_save_as", text="Save As Asset...", icon='FILE_TICK')
|
||||
layout.operator("brush.asset_delete", text="Delete")
|
||||
|
||||
|
||||
class VIEW3D_MT_brush_gpencil_context_menu(Menu):
|
||||
|
@ -87,9 +85,6 @@ class VIEW3D_MT_brush_gpencil_context_menu(Menu):
|
|||
layout.label(text="No Brushes currently available", icon='INFO')
|
||||
return
|
||||
|
||||
layout.operator("gpencil.brush_reset")
|
||||
layout.operator("gpencil.brush_reset_all")
|
||||
|
||||
|
||||
class View3DPanel:
|
||||
bl_space_type = 'VIEW_3D'
|
||||
|
@ -359,7 +354,7 @@ class VIEW3D_PT_tools_particlemode(Panel, View3DPaintPanel):
|
|||
# TODO, move to space_view3d.py
|
||||
class VIEW3D_PT_tools_brush_select(Panel, View3DPaintBrushPanel, BrushSelectPanel):
|
||||
bl_context = ".paint_common"
|
||||
bl_label = "Brushes"
|
||||
bl_label = "Brush Asset"
|
||||
|
||||
|
||||
# TODO, move to space_view3d.py
|
||||
|
@ -1603,30 +1598,8 @@ class GreasePencilPaintPanel:
|
|||
return True
|
||||
|
||||
|
||||
class VIEW3D_PT_tools_grease_pencil_brush_select(Panel, View3DPanel, GreasePencilPaintPanel):
|
||||
bl_label = "Brushes"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
tool_settings = context.scene.tool_settings
|
||||
gpencil_paint = tool_settings.gpencil_paint
|
||||
|
||||
row = layout.row()
|
||||
row.column().template_ID_preview(gpencil_paint, "brush", new="brush.add_gpencil", rows=3, cols=8)
|
||||
|
||||
col = row.column()
|
||||
col.menu("VIEW3D_MT_brush_gpencil_context_menu", icon='DOWNARROW_HLT', text="")
|
||||
|
||||
if context.mode == 'PAINT_GPENCIL':
|
||||
brush = tool_settings.gpencil_paint.brush
|
||||
if brush is not None:
|
||||
col.prop(brush, "use_custom_icon", toggle=True, icon='FILE_IMAGE', text="")
|
||||
|
||||
if brush.use_custom_icon:
|
||||
layout.row().prop(brush, "icon_filepath", text="")
|
||||
class VIEW3D_PT_tools_grease_pencil_brush_select(Panel, View3DPanel, GreasePencilPaintPanel, BrushSelectPanel):
|
||||
bl_label = "Brush Asset"
|
||||
|
||||
|
||||
class VIEW3D_PT_tools_grease_pencil_brush_settings(Panel, View3DPanel, GreasePencilPaintPanel):
|
||||
|
@ -2014,30 +1987,8 @@ class GreasePencilSculptPanel:
|
|||
return True
|
||||
|
||||
|
||||
class VIEW3D_PT_tools_grease_pencil_sculpt_select(Panel, View3DPanel, GreasePencilSculptPanel):
|
||||
bl_label = "Brushes"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
tool_settings = context.scene.tool_settings
|
||||
gpencil_paint = tool_settings.gpencil_sculpt_paint
|
||||
|
||||
row = layout.row()
|
||||
row.column().template_ID_preview(gpencil_paint, "brush", new="brush.add_gpencil", rows=3, cols=8)
|
||||
|
||||
col = row.column()
|
||||
col.menu("VIEW3D_MT_brush_gpencil_context_menu", icon='DOWNARROW_HLT', text="")
|
||||
|
||||
if context.mode == 'SCULPT_GPENCIL':
|
||||
brush = tool_settings.gpencil_sculpt_paint.brush
|
||||
if brush is not None:
|
||||
col.prop(brush, "use_custom_icon", toggle=True, icon='FILE_IMAGE', text="")
|
||||
|
||||
if (brush.use_custom_icon):
|
||||
layout.row().prop(brush, "icon_filepath", text="")
|
||||
class VIEW3D_PT_tools_grease_pencil_sculpt_select(Panel, View3DPanel, GreasePencilSculptPanel, BrushSelectPanel):
|
||||
bl_label = "Brush Asset"
|
||||
|
||||
|
||||
class VIEW3D_PT_tools_grease_pencil_sculpt_settings(Panel, View3DPanel, GreasePencilSculptPanel):
|
||||
|
@ -2126,30 +2077,8 @@ class GreasePencilWeightPanel:
|
|||
return True
|
||||
|
||||
|
||||
class VIEW3D_PT_tools_grease_pencil_weight_paint_select(View3DPanel, Panel, GreasePencilWeightPanel):
|
||||
bl_label = "Brushes"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
tool_settings = context.scene.tool_settings
|
||||
gpencil_paint = tool_settings.gpencil_weight_paint
|
||||
|
||||
row = layout.row()
|
||||
row.column().template_ID_preview(gpencil_paint, "brush", new="brush.add_gpencil", rows=3, cols=8)
|
||||
|
||||
col = row.column()
|
||||
col.menu("VIEW3D_MT_brush_gpencil_context_menu", icon='DOWNARROW_HLT', text="")
|
||||
|
||||
if context.mode in {'WEIGHT_GPENCIL', 'WEIGHT_GREASE_PENCIL'}:
|
||||
brush = tool_settings.gpencil_weight_paint.brush
|
||||
if brush is not None:
|
||||
col.prop(brush, "use_custom_icon", toggle=True, icon='FILE_IMAGE', text="")
|
||||
|
||||
if (brush.use_custom_icon):
|
||||
layout.row().prop(brush, "icon_filepath", text="")
|
||||
class VIEW3D_PT_tools_grease_pencil_weight_paint_select(View3DPanel, Panel, GreasePencilWeightPanel, BrushSelectPanel):
|
||||
bl_label = "Brush Asset"
|
||||
|
||||
|
||||
class VIEW3D_PT_tools_grease_pencil_weight_paint_settings(Panel, View3DPanel, GreasePencilWeightPanel):
|
||||
|
@ -2225,30 +2154,8 @@ class GreasePencilVertexPanel:
|
|||
return True
|
||||
|
||||
|
||||
class VIEW3D_PT_tools_grease_pencil_vertex_paint_select(View3DPanel, Panel, GreasePencilVertexPanel):
|
||||
bl_label = "Brushes"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
tool_settings = context.scene.tool_settings
|
||||
gpencil_paint = tool_settings.gpencil_vertex_paint
|
||||
|
||||
row = layout.row()
|
||||
row.column().template_ID_preview(gpencil_paint, "brush", new="brush.add_gpencil", rows=3, cols=8)
|
||||
|
||||
col = row.column()
|
||||
col.menu("VIEW3D_MT_brush_gpencil_context_menu", icon='DOWNARROW_HLT', text="")
|
||||
|
||||
if context.mode == 'VERTEX_GPENCIL':
|
||||
brush = tool_settings.gpencil_vertex_paint.brush
|
||||
if brush is not None:
|
||||
col.prop(brush, "use_custom_icon", toggle=True, icon='FILE_IMAGE', text="")
|
||||
|
||||
if (brush.use_custom_icon):
|
||||
layout.row().prop(brush, "icon_filepath", text="")
|
||||
class VIEW3D_PT_tools_grease_pencil_vertex_paint_select(View3DPanel, Panel, GreasePencilVertexPanel, BrushSelectPanel):
|
||||
bl_label = "Brush Asset"
|
||||
|
||||
|
||||
class VIEW3D_PT_tools_grease_pencil_vertex_paint_settings(Panel, View3DPanel, GreasePencilVertexPanel):
|
||||
|
@ -2466,6 +2373,36 @@ class VIEW3D_PT_tools_grease_pencil_brush_mix_palette(View3DPanel, Panel):
|
|||
col.template_palette(settings, "palette", color=True)
|
||||
|
||||
|
||||
class VIEW3D_PT_tools_grease_pencil_brush_eraser(View3DPanel, Panel):
|
||||
bl_context = ".greasepencil_paint"
|
||||
bl_label = "Eraser"
|
||||
bl_category = "Tool"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
tool_settings = context.tool_settings
|
||||
settings = tool_settings.gpencil_paint
|
||||
|
||||
if context.region.type == 'TOOL_HEADER':
|
||||
return False
|
||||
|
||||
from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
|
||||
tool = ToolSelectPanelHelper.tool_active_from_context(context)
|
||||
return (tool and tool.idname == "builtin.brush")
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
tool_settings = context.tool_settings
|
||||
settings = tool_settings.gpencil_paint
|
||||
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
col = layout.column()
|
||||
col.prop(settings, "eraser_brush")
|
||||
|
||||
|
||||
# Grease Pencil Brush Appearance (one for each mode)
|
||||
class VIEW3D_PT_tools_grease_pencil_paint_appearance(GreasePencilDisplayPanel, Panel, View3DPanel):
|
||||
bl_context = ".greasepencil_paint"
|
||||
|
@ -2520,29 +2457,8 @@ class GreasePencilV3PaintPanel:
|
|||
return True
|
||||
|
||||
|
||||
class VIEW3D_PT_tools_grease_pencil_v3_brush_select(Panel, View3DPanel, GreasePencilV3PaintPanel):
|
||||
bl_label = "Brushes"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
tool_settings = context.scene.tool_settings
|
||||
gpencil_paint = tool_settings.gpencil_paint
|
||||
|
||||
row = layout.row()
|
||||
row.column().template_ID_preview(gpencil_paint, "brush", new="brush.add_gpencil", rows=3, cols=8)
|
||||
|
||||
col = row.column()
|
||||
col.menu("VIEW3D_MT_brush_gpencil_context_menu", icon='DOWNARROW_HLT', text="")
|
||||
|
||||
brush = tool_settings.gpencil_paint.brush
|
||||
if brush is not None:
|
||||
col.prop(brush, "use_custom_icon", toggle=True, icon='FILE_IMAGE', text="")
|
||||
|
||||
if brush.use_custom_icon:
|
||||
layout.row().prop(brush, "icon_filepath", text="")
|
||||
class VIEW3D_PT_tools_grease_pencil_v3_brush_select(Panel, View3DPanel, GreasePencilV3PaintPanel, BrushSelectPanel):
|
||||
bl_label = "Brush Asset"
|
||||
|
||||
|
||||
class VIEW3D_PT_tools_grease_pencil_v3_brush_settings(Panel, View3DPanel, GreasePencilV3PaintPanel):
|
||||
|
@ -2777,6 +2693,35 @@ class VIEW3D_PT_tools_grease_pencil_v3_brush_mix_palette(View3DPanel, Panel):
|
|||
col.template_palette(settings, "palette", color=True)
|
||||
|
||||
|
||||
class VIEW3D_PT_tools_grease_pencil_v3_brush_eraser(View3DPanel, Panel):
|
||||
bl_context = ".grease_pencil_paint"
|
||||
bl_label = "Eraser"
|
||||
bl_category = "Tool"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
tool_settings = context.tool_settings
|
||||
settings = tool_settings.gpencil_paint
|
||||
|
||||
if context.region.type == 'TOOL_HEADER':
|
||||
return False
|
||||
|
||||
from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
|
||||
tool = ToolSelectPanelHelper.tool_active_from_context(context)
|
||||
return (tool and tool.idname == "builtin.brush")
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
settings = tool_settings.gpencil_paint
|
||||
|
||||
layout.use_property_split = True
|
||||
layout.use_property_decorate = False
|
||||
|
||||
col = layout.column()
|
||||
col.prop_search(settings, "eraser_brush", bpy.data, "brushes")
|
||||
|
||||
|
||||
classes = (
|
||||
VIEW3D_MT_brush_context_menu,
|
||||
VIEW3D_MT_brush_gpencil_context_menu,
|
||||
|
@ -2852,6 +2797,7 @@ classes = (
|
|||
VIEW3D_PT_tools_grease_pencil_brush_random,
|
||||
VIEW3D_PT_tools_grease_pencil_brush_stabilizer,
|
||||
VIEW3D_PT_tools_grease_pencil_brush_gap_closure,
|
||||
VIEW3D_PT_tools_grease_pencil_brush_eraser,
|
||||
VIEW3D_PT_tools_grease_pencil_paint_appearance,
|
||||
VIEW3D_PT_tools_grease_pencil_sculpt_select,
|
||||
VIEW3D_PT_tools_grease_pencil_sculpt_settings,
|
||||
|
@ -2870,6 +2816,7 @@ classes = (
|
|||
|
||||
VIEW3D_PT_tools_grease_pencil_v3_brush_select,
|
||||
VIEW3D_PT_tools_grease_pencil_v3_brush_settings,
|
||||
VIEW3D_PT_tools_grease_pencil_v3_brush_eraser,
|
||||
VIEW3D_PT_tools_grease_pencil_v3_brush_advanced,
|
||||
VIEW3D_PT_tools_grease_pencil_v3_brush_mixcolor,
|
||||
VIEW3D_PT_tools_grease_pencil_v3_brush_mix_palette,
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* Editing of datablocks from asset libraries.
|
||||
*
|
||||
* Asset blend files are linked into the global main database, with the asset
|
||||
* datablock itself and its dependencies. These datablocks remain linked but
|
||||
* are marked as editable.
|
||||
*
|
||||
* User edited asset datablocks are written to individual blend files per
|
||||
* asset. These blend files include any datablock dependencies and packaged
|
||||
* image files.
|
||||
*
|
||||
* This way the blend file can be easily saved, reloaded and deleted.
|
||||
*
|
||||
* This mechanism is currently only used for brush assets.
|
||||
*/
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
#include "DNA_ID_enums.h"
|
||||
|
||||
struct bUserAssetLibrary;
|
||||
struct AssetMetaData;
|
||||
struct AssetWeakReference;
|
||||
struct ID;
|
||||
struct Main;
|
||||
struct ReportList;
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
/** Get datablock from weak reference, loading the blend file as needed. */
|
||||
ID *asset_edit_id_from_weak_reference(Main &global_main,
|
||||
ID_Type id_type,
|
||||
const AssetWeakReference &weak_ref);
|
||||
|
||||
/** Get asset weak reference from ID. */
|
||||
std::optional<AssetWeakReference> asset_edit_weak_reference_from_id(ID &id);
|
||||
|
||||
/** Asset editing operations. */
|
||||
|
||||
bool asset_edit_id_is_editable(const ID &id);
|
||||
bool asset_edit_id_is_writable(const ID &id);
|
||||
|
||||
std::optional<std::string> asset_edit_id_save_as(Main &global_main,
|
||||
const ID &id,
|
||||
StringRef name,
|
||||
const bUserAssetLibrary &user_library,
|
||||
AssetWeakReference &weak_ref,
|
||||
ReportList &reports);
|
||||
|
||||
bool asset_edit_id_save(Main &global_main, const ID &id, ReportList &reports);
|
||||
bool asset_edit_id_revert(Main &global_main, ID &id, ReportList &reports);
|
||||
bool asset_edit_id_delete(Main &global_main, ID &id, ReportList &reports);
|
||||
|
||||
} // namespace blender::bke
|
|
@ -29,7 +29,7 @@ extern "C" {
|
|||
|
||||
/* Blender file format version. */
|
||||
#define BLENDER_FILE_VERSION BLENDER_VERSION
|
||||
#define BLENDER_FILE_SUBVERSION 39
|
||||
#define BLENDER_FILE_SUBVERSION 40
|
||||
|
||||
/* Minimum Blender version that supports reading file written with the current
|
||||
* version. Older Blender versions will test this and cancel loading the file, showing a warning to
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -205,6 +205,8 @@ enum {
|
|||
* duplicate scene/collections, or objects.
|
||||
*/
|
||||
LIB_ID_COPY_RIGID_BODY_NO_COLLECTION_HANDLING = 1 << 28,
|
||||
/* Copy asset metadata. */
|
||||
LIB_ID_COPY_ASSET_METADATA = 1 << 29,
|
||||
|
||||
/* *** Helper 'defines' gathering most common flag sets. *** */
|
||||
/** Shape-keys are not real ID's, more like local data to geometry IDs. */
|
||||
|
@ -247,6 +249,12 @@ void BKE_libblock_copy_in_lib(Main *bmain,
|
|||
*/
|
||||
void *BKE_libblock_copy(Main *bmain, const ID *id) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
|
||||
|
||||
/**
|
||||
* For newly created IDs, move it into same library as owner ID.
|
||||
* This assumes the ID is local.
|
||||
*/
|
||||
void BKE_id_move_to_same_lib(Main &bmain, ID &id, const ID &owner_id);
|
||||
|
||||
/**
|
||||
* Sets the name of a block to name, suitably adjusted for uniqueness.
|
||||
*/
|
||||
|
@ -259,6 +267,11 @@ ID *BKE_libblock_find_name_and_library(Main *bmain,
|
|||
short type,
|
||||
const char *name,
|
||||
const char *lib_name);
|
||||
ID *BKE_libblock_find_name_and_library_filepath(Main *bmain,
|
||||
short type,
|
||||
const char *name,
|
||||
const char *lib_filepath_abs);
|
||||
|
||||
/**
|
||||
* Duplicate (a.k.a. deep copy) common processing options.
|
||||
* See also eDupli_ID_Flags for options controlling what kind of IDs to duplicate.
|
||||
|
@ -710,7 +723,7 @@ void BKE_id_tag_clear_atomic(ID *id, int tag);
|
|||
|
||||
/**
|
||||
* Check that given ID pointer actually is in G_MAIN.
|
||||
* Main intended use is for debug asserts in places we cannot easily get rid of #G_MAIN.
|
||||
* Main intended use is for debug asserts in places we cannot easily get rid of #G_Main.
|
||||
*/
|
||||
bool BKE_id_is_in_global_main(ID *id);
|
||||
|
||||
|
@ -739,6 +752,13 @@ ID *BKE_id_owner_get(ID *id, const bool debug_relationship_assert = true);
|
|||
*/
|
||||
bool BKE_id_is_editable(const Main *bmain, const ID *id);
|
||||
|
||||
/**
|
||||
* Check that a pointer from one ID to another is possible.
|
||||
*
|
||||
* Taking into account lib linking and main database membership.
|
||||
*/
|
||||
bool BKE_id_can_link(const ID &id_from, const ID &id_to);
|
||||
|
||||
/**
|
||||
* Returns ordered list of data-blocks for display in the UI.
|
||||
*/
|
||||
|
|
|
@ -143,6 +143,14 @@ struct Main {
|
|||
* could try to use more refined detection on load. */
|
||||
bool has_forward_compatibility_issues;
|
||||
|
||||
/**
|
||||
* The currently opened .blend file was created as an asset library storage.
|
||||
*
|
||||
* This is used to warn the user when they try to save it from Blender UI, since this will likely
|
||||
* break the automatic management from the asset library system.
|
||||
*/
|
||||
bool is_asset_repository;
|
||||
|
||||
/** Commit timestamp from `buildinfo`. */
|
||||
uint64_t build_commit_timestamp;
|
||||
/** Commit Hash from `buildinfo`. */
|
||||
|
@ -300,6 +308,17 @@ void BKE_main_merge(Main *bmain_dst, Main **r_bmain_src, MainMergeReport &report
|
|||
*/
|
||||
bool BKE_main_is_empty(Main *bmain);
|
||||
|
||||
/**
|
||||
* Check whether the bmain has issues, e.g. for reporting in the status bar.
|
||||
*/
|
||||
bool BKE_main_has_issues(const Main *bmain);
|
||||
|
||||
/**
|
||||
* Check whether user confirmation should be required when overwriting this `bmain` into its source
|
||||
* blendfile.
|
||||
*/
|
||||
bool BKE_main_needs_overwrite_confirm(const Main *bmain);
|
||||
|
||||
void BKE_main_lock(Main *bmain);
|
||||
void BKE_main_unlock(Main *bmain);
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "BLI_array.hh"
|
||||
#include "BLI_bit_vector.hh"
|
||||
#include "BLI_math_matrix_types.hh"
|
||||
|
@ -183,24 +185,43 @@ void BKE_paint_free(Paint *p);
|
|||
*/
|
||||
void BKE_paint_copy(const Paint *src, Paint *tar, int flag);
|
||||
|
||||
void BKE_paint_runtime_init(const ToolSettings *ts, Paint *paint);
|
||||
|
||||
void BKE_paint_cavity_curve_preset(Paint *p, int preset);
|
||||
|
||||
eObjectMode BKE_paint_object_mode_from_paintmode(PaintMode mode);
|
||||
bool BKE_paint_ensure_from_paintmode(Main *bmain, Scene *sce, PaintMode mode);
|
||||
Paint *BKE_paint_get_active_from_paintmode(Scene *sce, PaintMode mode);
|
||||
const EnumPropertyItem *BKE_paint_get_tool_enum_from_paintmode(PaintMode mode);
|
||||
const char *BKE_paint_get_tool_enum_translation_context_from_paintmode(PaintMode mode);
|
||||
const char *BKE_paint_get_tool_prop_id_from_paintmode(PaintMode mode);
|
||||
uint BKE_paint_get_brush_tool_offset_from_paintmode(PaintMode mode);
|
||||
Paint *BKE_paint_get_active(Scene *sce, ViewLayer *view_layer);
|
||||
Paint *BKE_paint_get_active_from_context(const bContext *C);
|
||||
PaintMode BKE_paintmode_get_active_from_context(const bContext *C);
|
||||
PaintMode BKE_paintmode_get_from_tool(const bToolRef *tref);
|
||||
|
||||
/* Paint brush retrieval and assignment. */
|
||||
|
||||
Brush *BKE_paint_brush(Paint *paint);
|
||||
const Brush *BKE_paint_brush_for_read(const Paint *p);
|
||||
void BKE_paint_brush_set(Paint *paint, Brush *br);
|
||||
const Brush *BKE_paint_brush_for_read(const Paint *paint);
|
||||
Brush *BKE_paint_brush_from_essentials(Main *bmain, const char *name);
|
||||
|
||||
bool BKE_paint_brush_set(Paint *paint, Brush *brush);
|
||||
bool BKE_paint_brush_set_default(Main *bmain, Paint *paint);
|
||||
bool BKE_paint_brush_set_essentials(Main *bmain, Paint *paint, const char *name);
|
||||
|
||||
void BKE_paint_brushes_set_default_references(ToolSettings *ts);
|
||||
void BKE_paint_brushes_validate(Main *bmain, Paint *paint);
|
||||
|
||||
/* Secondary eraser brush. */
|
||||
|
||||
Brush *BKE_paint_eraser_brush(Paint *paint);
|
||||
const Brush *BKE_paint_eraser_brush_for_read(const Paint *paint);
|
||||
Brush *BKE_paint_eraser_brush_from_essentials(Main *bmain, const char *name);
|
||||
|
||||
bool BKE_paint_eraser_brush_set(Paint *paint, Brush *brush);
|
||||
bool BKE_paint_eraser_brush_set_default(Main *bmain, Paint *paint);
|
||||
bool BKE_paint_eraser_brush_set_essentials(Main *bmain, Paint *paint, const char *name);
|
||||
|
||||
/* Paint palette. */
|
||||
|
||||
Palette *BKE_paint_palette(Paint *paint);
|
||||
void BKE_paint_palette_set(Paint *p, Palette *palette);
|
||||
void BKE_paint_curve_clamp_endpoint_add_index(PaintCurve *pc, int add_index);
|
||||
|
@ -253,19 +274,6 @@ void paint_update_brush_rake_rotation(UnifiedPaintSettings *ups, Brush *brush, f
|
|||
|
||||
void BKE_paint_stroke_get_average(const Scene *scene, const Object *ob, float stroke[3]);
|
||||
|
||||
/* Tool slot API. */
|
||||
|
||||
void BKE_paint_toolslots_init_from_main(Main *bmain);
|
||||
void BKE_paint_toolslots_len_ensure(Paint *paint, int len);
|
||||
void BKE_paint_toolslots_brush_update_ex(Paint *paint, Brush *brush);
|
||||
void BKE_paint_toolslots_brush_update(Paint *paint);
|
||||
/**
|
||||
* Run this to ensure brush types are set for each slot on entering modes
|
||||
* (for new scenes for example).
|
||||
*/
|
||||
void BKE_paint_brush_validate(Main *bmain, Paint *paint);
|
||||
Brush *BKE_paint_toolslots_brush_get(Paint *paint, int slot_index);
|
||||
|
||||
/* .blend I/O */
|
||||
|
||||
void BKE_paint_blend_write(BlendWriter *writer, Paint *paint);
|
||||
|
|
|
@ -65,6 +65,7 @@ set(SRC
|
|||
intern/armature_update.cc
|
||||
intern/asset.cc
|
||||
intern/asset_weak_reference.cc
|
||||
intern/asset_edit.cc
|
||||
intern/attribute.cc
|
||||
intern/attribute_access.cc
|
||||
intern/attribute_math.cc
|
||||
|
@ -253,7 +254,6 @@ set(SRC
|
|||
intern/packedFile.cc
|
||||
intern/paint.cc
|
||||
intern/paint_canvas.cc
|
||||
intern/paint_toolslots.cc
|
||||
intern/particle.cc
|
||||
intern/particle_child.cc
|
||||
intern/particle_distribute.cc
|
||||
|
@ -333,6 +333,7 @@ set(SRC
|
|||
BKE_appdir.hh
|
||||
BKE_armature.hh
|
||||
BKE_asset.hh
|
||||
BKE_asset_edit.hh
|
||||
BKE_attribute.h
|
||||
BKE_attribute.hh
|
||||
BKE_attribute_math.hh
|
||||
|
|
|
@ -0,0 +1,446 @@
|
|||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "BLI_fileops.h"
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "DNA_asset_types.h"
|
||||
#include "DNA_space_types.h"
|
||||
|
||||
#include "AS_asset_identifier.hh"
|
||||
#include "AS_asset_library.hh"
|
||||
|
||||
#include "BKE_asset.hh"
|
||||
#include "BKE_asset_edit.hh"
|
||||
#include "BKE_blendfile.hh"
|
||||
#include "BKE_blendfile_link_append.hh"
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_idtype.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_lib_remap.hh"
|
||||
#include "BKE_library.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_packedFile.h"
|
||||
#include "BKE_preferences.h"
|
||||
#include "BKE_report.hh"
|
||||
|
||||
#include "BLO_read_write.hh"
|
||||
#include "BLO_readfile.hh"
|
||||
#include "BLO_writefile.hh"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
#include "DEG_depsgraph_build.hh"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
static ID *asset_link_id(Main &global_main,
|
||||
const ID_Type id_type,
|
||||
const char *filepath,
|
||||
const char *asset_name)
|
||||
{
|
||||
/* Load asset from asset library. */
|
||||
LibraryLink_Params lapp_params{};
|
||||
lapp_params.bmain = &global_main;
|
||||
BlendfileLinkAppendContext *lapp_context = BKE_blendfile_link_append_context_new(&lapp_params);
|
||||
BKE_blendfile_link_append_context_flag_set(lapp_context, BLO_LIBLINK_FORCE_INDIRECT, true);
|
||||
|
||||
BKE_blendfile_link_append_context_library_add(lapp_context, filepath, nullptr);
|
||||
|
||||
BlendfileLinkAppendContextItem *lapp_item = BKE_blendfile_link_append_context_item_add(
|
||||
lapp_context, asset_name, id_type, nullptr);
|
||||
BKE_blendfile_link_append_context_item_library_index_enable(lapp_context, lapp_item, 0);
|
||||
|
||||
BKE_blendfile_link(lapp_context, nullptr);
|
||||
|
||||
ID *local_asset = BKE_blendfile_link_append_context_item_newid_get(lapp_context, lapp_item);
|
||||
|
||||
BKE_blendfile_link_append_context_free(lapp_context);
|
||||
|
||||
/* Verify that the name matches. It must for referencing the same asset again to work. */
|
||||
BLI_assert(local_asset == nullptr || STREQ(local_asset->name + 2, asset_name));
|
||||
|
||||
/* Tag library as being editable. */
|
||||
if (local_asset && local_asset->lib) {
|
||||
local_asset->lib->runtime.tag |= LIBRARY_ASSET_EDITABLE;
|
||||
|
||||
/* Simple check, based on being a writable .asset.blend file in a user asset library. */
|
||||
if (StringRef(filepath).endswith(BLENDER_ASSET_FILE_SUFFIX) &&
|
||||
BKE_preferences_asset_library_containing_path(&U, filepath) &&
|
||||
BLI_file_is_writable(filepath))
|
||||
{
|
||||
local_asset->lib->runtime.tag |= LIBRARY_ASSET_FILE_WRITABLE;
|
||||
}
|
||||
}
|
||||
|
||||
return local_asset;
|
||||
}
|
||||
|
||||
static std::string asset_root_path_for_save(const bUserAssetLibrary &user_library,
|
||||
const ID_Type id_type)
|
||||
{
|
||||
BLI_assert(user_library.dirpath[0] != '\0');
|
||||
|
||||
char libpath[FILE_MAX];
|
||||
BLI_strncpy(libpath, user_library.dirpath, sizeof(libpath));
|
||||
BLI_path_slash_native(libpath);
|
||||
BLI_path_normalize(libpath);
|
||||
|
||||
/* Capitalize folder name. Ideally this would already available in
|
||||
* the type info to work correctly with multiple words. */
|
||||
const IDTypeInfo *id_type_info = BKE_idtype_get_info_from_idcode(id_type);
|
||||
std::string name = id_type_info->name_plural;
|
||||
name[0] = BLI_toupper_ascii(name[0]);
|
||||
|
||||
return std::string(libpath) + SEP + "Saved" + SEP + name;
|
||||
}
|
||||
|
||||
static std::string asset_blendfile_path_for_save(const bUserAssetLibrary &user_library,
|
||||
const StringRef base_name,
|
||||
const ID_Type id_type,
|
||||
ReportList &reports)
|
||||
{
|
||||
std::string root_path = asset_root_path_for_save(user_library, id_type);
|
||||
BLI_assert(!root_path.empty());
|
||||
|
||||
if (!BLI_dir_create_recursive(root_path.c_str())) {
|
||||
BKE_report(&reports, RPT_ERROR, "Failed to create asset library directory to save asset");
|
||||
return "";
|
||||
}
|
||||
|
||||
/* Make sure filename only contains valid characters for filesystem. */
|
||||
char base_name_filesafe[FILE_MAXFILE];
|
||||
BLI_strncpy(base_name_filesafe,
|
||||
base_name.data(),
|
||||
std::min(sizeof(base_name_filesafe), size_t(base_name.size() + 1)));
|
||||
BLI_path_make_safe_filename(base_name_filesafe);
|
||||
|
||||
const std::string filepath = root_path + SEP + base_name_filesafe + BLENDER_ASSET_FILE_SUFFIX;
|
||||
|
||||
if (!BLI_is_file(filepath.c_str())) {
|
||||
return filepath;
|
||||
}
|
||||
|
||||
/* Avoid overwriting existing file by adding number suffix. */
|
||||
for (int i = 1;; i++) {
|
||||
const std::string filepath = root_path + SEP + base_name_filesafe + "_" + std::to_string(i++) +
|
||||
BLENDER_ASSET_FILE_SUFFIX;
|
||||
if (!BLI_is_file((filepath.c_str()))) {
|
||||
return filepath;
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
static void asset_main_create_expander(void * /*handle*/, Main * /*bmain*/, void *vid)
|
||||
{
|
||||
ID *id = static_cast<ID *>(vid);
|
||||
|
||||
if (id && (id->tag & LIB_TAG_DOIT) == 0) {
|
||||
id->tag |= LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT;
|
||||
}
|
||||
}
|
||||
|
||||
static Main *asset_main_create_from_ID(Main &bmain_src, ID &id_asset, ID **id_asset_new)
|
||||
{
|
||||
/* Tag asset ID and its dependencies. */
|
||||
ID *id_src;
|
||||
FOREACH_MAIN_ID_BEGIN (&bmain_src, id_src) {
|
||||
id_src->tag &= ~(LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT);
|
||||
}
|
||||
FOREACH_MAIN_ID_END;
|
||||
|
||||
id_asset.tag |= LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT;
|
||||
|
||||
BLO_expand_main(nullptr, &bmain_src, asset_main_create_expander);
|
||||
|
||||
/* Create main and copy all tagged datablocks. */
|
||||
Main *bmain_dst = BKE_main_new();
|
||||
STRNCPY(bmain_dst->filepath, bmain_src.filepath);
|
||||
|
||||
blender::bke::id::IDRemapper id_remapper;
|
||||
|
||||
FOREACH_MAIN_ID_BEGIN (&bmain_src, id_src) {
|
||||
if (id_src->tag & LIB_TAG_DOIT) {
|
||||
/* Note that this will not copy Library datablocks, and all copied
|
||||
* datablocks will become local as a result. */
|
||||
ID *id_dst = BKE_id_copy_ex(bmain_dst,
|
||||
id_src,
|
||||
nullptr,
|
||||
LIB_ID_CREATE_NO_USER_REFCOUNT | LIB_ID_CREATE_NO_DEG_TAG |
|
||||
((id_src == &id_asset) ? LIB_ID_COPY_ASSET_METADATA : 0));
|
||||
id_remapper.add(id_src, id_dst);
|
||||
if (id_src == &id_asset) {
|
||||
*id_asset_new = id_dst;
|
||||
}
|
||||
}
|
||||
else {
|
||||
id_remapper.add(id_src, nullptr);
|
||||
}
|
||||
|
||||
id_src->tag &= ~(LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT);
|
||||
}
|
||||
FOREACH_MAIN_ID_END;
|
||||
|
||||
/* Remap datablock pointers. */
|
||||
BKE_libblock_remap_multiple_raw(bmain_dst, id_remapper, ID_REMAP_SKIP_USER_CLEAR);
|
||||
|
||||
/* Compute reference counts. */
|
||||
ID *id_dst;
|
||||
FOREACH_MAIN_ID_BEGIN (bmain_dst, id_dst) {
|
||||
id_dst->tag &= ~LIB_TAG_NO_USER_REFCOUNT;
|
||||
}
|
||||
FOREACH_MAIN_ID_END;
|
||||
BKE_main_id_refcount_recompute(bmain_dst, false);
|
||||
|
||||
return bmain_dst;
|
||||
}
|
||||
|
||||
static bool asset_write_in_library(Main &bmain,
|
||||
const ID &id_const,
|
||||
const StringRef name,
|
||||
const StringRefNull filepath,
|
||||
std::string &final_full_file_path,
|
||||
ReportList &reports)
|
||||
{
|
||||
ID &id = const_cast<ID &>(id_const);
|
||||
|
||||
ID *new_id = nullptr;
|
||||
Main *new_main = asset_main_create_from_ID(bmain, id, &new_id);
|
||||
|
||||
std::string new_name = name;
|
||||
BKE_libblock_rename(new_main, new_id, new_name.c_str());
|
||||
id_fake_user_set(new_id);
|
||||
|
||||
BlendFileWriteParams blend_file_write_params{};
|
||||
blend_file_write_params.remap_mode = BLO_WRITE_PATH_REMAP_RELATIVE;
|
||||
|
||||
BKE_packedfile_pack_all(new_main, nullptr, false);
|
||||
|
||||
const int write_flags = G_FILE_COMPRESS;
|
||||
const bool success = BLO_write_file(
|
||||
new_main, filepath.c_str(), write_flags, &blend_file_write_params, &reports);
|
||||
|
||||
if (success) {
|
||||
const IDTypeInfo *idtype = BKE_idtype_get_info_from_id(&id);
|
||||
final_full_file_path = std::string(filepath) + SEP + std::string(idtype->name) + SEP + name;
|
||||
}
|
||||
|
||||
BKE_main_free(new_main);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static void asset_reload(Main &global_main, Library *lib, ReportList &reports)
|
||||
{
|
||||
/* Fill fresh main database with same datablock as before. */
|
||||
LibraryLink_Params lapp_params{};
|
||||
lapp_params.bmain = &global_main;
|
||||
BlendfileLinkAppendContext *lapp_context = BKE_blendfile_link_append_context_new(&lapp_params);
|
||||
BKE_blendfile_link_append_context_flag_set(
|
||||
lapp_context, BLO_LIBLINK_FORCE_INDIRECT | BLO_LIBLINK_USE_PLACEHOLDERS, true);
|
||||
|
||||
BKE_blendfile_link_append_context_library_add(lapp_context, lib->runtime.filepath_abs, nullptr);
|
||||
BKE_blendfile_library_relocate(lapp_context, &reports, lib, true);
|
||||
BKE_blendfile_link_append_context_free(lapp_context);
|
||||
|
||||
/* Clear temporary tag from reloaction. */
|
||||
BKE_main_id_tag_all(&global_main, LIB_TAG_PRE_EXISTING, false);
|
||||
|
||||
/* Recreate dependency graph to include new IDs. */
|
||||
DEG_relations_tag_update(&global_main);
|
||||
}
|
||||
|
||||
static AssetWeakReference asset_weak_reference_for_user_library(
|
||||
const bUserAssetLibrary &user_library,
|
||||
const short idcode,
|
||||
const char *idname,
|
||||
const char *filepath)
|
||||
{
|
||||
AssetWeakReference weak_ref;
|
||||
weak_ref.asset_library_type = ASSET_LIBRARY_CUSTOM;
|
||||
weak_ref.asset_library_identifier = BLI_strdup(user_library.name);
|
||||
|
||||
/* BLI_path_rel requires a trailing slash. */
|
||||
char user_library_dirpath[FILE_MAX];
|
||||
STRNCPY(user_library_dirpath, user_library.dirpath);
|
||||
BLI_path_slash_ensure(user_library_dirpath, sizeof(user_library_dirpath));
|
||||
|
||||
char relative_filepath[FILE_MAX];
|
||||
STRNCPY(relative_filepath, filepath);
|
||||
BLI_path_rel(relative_filepath, user_library_dirpath);
|
||||
const char *asset_blend_path = relative_filepath + 2; /* Strip out // prefix. */
|
||||
|
||||
weak_ref.relative_asset_identifier = BLI_sprintfN(
|
||||
"%s/%s/%s", asset_blend_path, BKE_idtype_idcode_to_name(idcode), idname);
|
||||
|
||||
return weak_ref;
|
||||
}
|
||||
|
||||
static AssetWeakReference asset_weak_reference_for_essentials(const short idcode,
|
||||
const char *idname,
|
||||
const char *filepath)
|
||||
{
|
||||
AssetWeakReference weak_ref;
|
||||
weak_ref.asset_library_type = ASSET_LIBRARY_ESSENTIALS;
|
||||
weak_ref.relative_asset_identifier = BLI_sprintfN("%s/%s/%s/%s",
|
||||
BKE_idtype_idcode_to_name_plural(idcode),
|
||||
BLI_path_basename(filepath),
|
||||
BKE_idtype_idcode_to_name(idcode),
|
||||
idname);
|
||||
|
||||
return weak_ref;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public API
|
||||
*/
|
||||
std::optional<std::string> asset_edit_id_save_as(Main &global_main,
|
||||
const ID &id,
|
||||
const StringRef name,
|
||||
const bUserAssetLibrary &user_library,
|
||||
AssetWeakReference &new_weak_ref,
|
||||
ReportList &reports)
|
||||
{
|
||||
const std::string filepath = asset_blendfile_path_for_save(
|
||||
user_library, name, GS(id.name), reports);
|
||||
|
||||
std::string final_full_asset_filepath;
|
||||
const bool success = asset_write_in_library(
|
||||
global_main, id, name, filepath, final_full_asset_filepath, reports);
|
||||
if (!success) {
|
||||
BKE_report(&reports, RPT_ERROR, "Failed to write to asset library");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
new_weak_ref = asset_weak_reference_for_user_library(
|
||||
user_library, GS(id.name), id.name + 2, filepath.c_str());
|
||||
|
||||
BKE_reportf(&reports, RPT_INFO, "Saved \"%s\"", filepath.c_str());
|
||||
|
||||
return final_full_asset_filepath;
|
||||
}
|
||||
|
||||
bool asset_edit_id_save(Main &global_main, const ID &id, ReportList &reports)
|
||||
{
|
||||
if (!asset_edit_id_is_editable(id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string final_full_asset_filepath;
|
||||
const bool success = asset_write_in_library(global_main,
|
||||
id,
|
||||
id.name + 2,
|
||||
id.lib->runtime.filepath_abs,
|
||||
final_full_asset_filepath,
|
||||
reports);
|
||||
|
||||
if (!success) {
|
||||
BKE_report(&reports, RPT_ERROR, "Failed to write to asset library");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool asset_edit_id_revert(Main &global_main, ID &id, ReportList &reports)
|
||||
{
|
||||
if (!asset_edit_id_is_editable(id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Reload entire main, including texture dependencies. This relies on there
|
||||
* being only a single asset per blend file. */
|
||||
asset_reload(global_main, id.lib, reports);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool asset_edit_id_delete(Main &global_main, ID &id, ReportList &reports)
|
||||
{
|
||||
if (!asset_edit_id_is_editable(id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (BLI_delete(id.lib->runtime.filepath_abs, false, false) != 0) {
|
||||
BKE_report(&reports, RPT_ERROR, "Failed to delete asset library file");
|
||||
return false;
|
||||
}
|
||||
|
||||
BKE_id_delete(&global_main, &id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ID *asset_edit_id_from_weak_reference(Main &global_main,
|
||||
const ID_Type id_type,
|
||||
const AssetWeakReference &weak_ref)
|
||||
{
|
||||
/* Don't do this in file load. */
|
||||
BLI_assert(!global_main.is_locked_for_linking);
|
||||
|
||||
char asset_full_path_buffer[FILE_MAX_LIBEXTRA];
|
||||
char *asset_lib_path, *asset_group, *asset_name;
|
||||
|
||||
AS_asset_full_path_explode_from_weak_ref(
|
||||
&weak_ref, asset_full_path_buffer, &asset_lib_path, &asset_group, &asset_name);
|
||||
if (asset_lib_path == nullptr && asset_group == nullptr && asset_name == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BLI_assert(asset_name != nullptr);
|
||||
|
||||
/* Test if asset has been loaded already. */
|
||||
ID *local_asset = BKE_libblock_find_name_and_library_filepath(
|
||||
&global_main, id_type, asset_name, asset_lib_path);
|
||||
if (local_asset) {
|
||||
return local_asset;
|
||||
}
|
||||
|
||||
/* If weak reference resolves to a null library path, assume we are in local asset case. */
|
||||
return asset_link_id(global_main, id_type, asset_lib_path, asset_name);
|
||||
}
|
||||
|
||||
std::optional<AssetWeakReference> asset_edit_weak_reference_from_id(ID &id)
|
||||
{
|
||||
if (!asset_edit_id_is_editable(id)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bUserAssetLibrary *user_library = BKE_preferences_asset_library_containing_path(
|
||||
&U, id.lib->runtime.filepath_abs);
|
||||
|
||||
const short idcode = GS(id.name);
|
||||
|
||||
if (user_library && user_library->dirpath[0]) {
|
||||
return asset_weak_reference_for_user_library(
|
||||
*user_library, idcode, id.name + 2, id.lib->runtime.filepath_abs);
|
||||
}
|
||||
else {
|
||||
return asset_weak_reference_for_essentials(idcode, id.name + 2, id.lib->runtime.filepath_abs);
|
||||
}
|
||||
}
|
||||
|
||||
bool asset_edit_id_is_editable(const ID &id)
|
||||
{
|
||||
return (id.lib && (id.lib->runtime.tag & LIBRARY_ASSET_EDITABLE));
|
||||
}
|
||||
|
||||
bool asset_edit_id_is_writable(const ID &id)
|
||||
{
|
||||
return asset_edit_id_is_editable(id) && (id.lib->runtime.tag & LIBRARY_ASSET_FILE_WRITABLE);
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
|
@ -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 */
|
||||
|
|
|
@ -306,6 +306,200 @@ static bool reuse_bmain_data_remapper_is_id_remapped(id::IDRemapper &remapper, I
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool reuse_bmain_move_id(ReuseOldBMainData *reuse_data,
|
||||
ID *id,
|
||||
Library *lib,
|
||||
const bool reuse_existing)
|
||||
{
|
||||
id::IDRemapper &remapper = reuse_bmain_data_remapper_ensure(reuse_data);
|
||||
Main *new_bmain = reuse_data->new_bmain;
|
||||
Main *old_bmain = reuse_data->old_bmain;
|
||||
ListBase *new_lb = which_libbase(new_bmain, GS(id->name));
|
||||
ListBase *old_lb = which_libbase(old_bmain, GS(id->name));
|
||||
|
||||
if (reuse_existing) {
|
||||
/* A 'new' version of the same data may already exist in new_bmain, in the rare case
|
||||
* that the same asset blend file was linked explicitly into the blend file we are loading.
|
||||
* Don't move the old linked ID, but remap its usages to the new one instead. */
|
||||
LISTBASE_FOREACH_BACKWARD (ID *, id_iter, new_lb) {
|
||||
if (!ELEM(id_iter->lib, id->lib, lib)) {
|
||||
continue;
|
||||
}
|
||||
if (!STREQ(id_iter->name + 2, id->name + 2)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
remapper.add(id, id_iter);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* If ID is already in the new_bmain, this should not have been called. */
|
||||
BLI_assert(BLI_findindex(new_lb, id) < 0);
|
||||
BLI_assert(BLI_findindex(old_lb, id) >= 0);
|
||||
|
||||
/* Move from one list to another, and ensure name is valid. */
|
||||
BLI_remlink_safe(old_lb, id);
|
||||
BKE_main_namemap_remove_name(old_bmain, id, id->name + 2);
|
||||
|
||||
id->lib = lib;
|
||||
BLI_addtail(new_lb, id);
|
||||
BKE_id_new_name_validate(new_bmain, new_lb, id, nullptr, true);
|
||||
BKE_lib_libblock_session_uid_renew(id);
|
||||
|
||||
/* Remap to itself, to avoid re-processing this ID again. */
|
||||
remapper.add(id, id);
|
||||
return true;
|
||||
}
|
||||
|
||||
static Library *reuse_bmain_data_dependencies_new_library_get(ReuseOldBMainData *reuse_data,
|
||||
Library *old_lib)
|
||||
{
|
||||
id::IDRemapper &remapper = reuse_bmain_data_remapper_ensure(reuse_data);
|
||||
Library *new_lib = old_lib;
|
||||
IDRemapperApplyResult result = remapper.apply(reinterpret_cast<ID **>(&new_lib),
|
||||
ID_REMAP_APPLY_DEFAULT);
|
||||
|
||||
switch (result) {
|
||||
case ID_REMAP_RESULT_SOURCE_UNAVAILABLE: {
|
||||
/* Move library to new bmain.
|
||||
* There should be no filepath conflicts, as #reuse_bmain_data_remapper_ensure has
|
||||
* already remapped existing libraries with matching filepath. */
|
||||
reuse_bmain_move_id(reuse_data, &old_lib->id, nullptr, false);
|
||||
return old_lib;
|
||||
}
|
||||
case ID_REMAP_RESULT_SOURCE_NOT_MAPPABLE: {
|
||||
BLI_assert_unreachable();
|
||||
return nullptr;
|
||||
}
|
||||
case ID_REMAP_RESULT_SOURCE_REMAPPED: {
|
||||
/* Already in new bmain, only transfer flags. */
|
||||
new_lib->runtime.tag |= old_lib->runtime.tag &
|
||||
(LIBRARY_ASSET_EDITABLE | LIBRARY_ASSET_FILE_WRITABLE);
|
||||
return new_lib;
|
||||
}
|
||||
case ID_REMAP_RESULT_SOURCE_UNASSIGNED: {
|
||||
/* Happens when the library is the newly opened blend file. */
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
BLI_assert_unreachable();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static int reuse_editable_asset_bmain_data_dependencies_process_cb(
|
||||
LibraryIDLinkCallbackData *cb_data)
|
||||
{
|
||||
ID *id = *cb_data->id_pointer;
|
||||
|
||||
if (id == nullptr) {
|
||||
return IDWALK_RET_NOP;
|
||||
}
|
||||
|
||||
ReuseOldBMainData *reuse_data = static_cast<ReuseOldBMainData *>(cb_data->user_data);
|
||||
|
||||
/* First check if it has already been remapped. */
|
||||
id::IDRemapper &remapper = reuse_bmain_data_remapper_ensure(reuse_data);
|
||||
if (reuse_bmain_data_remapper_is_id_remapped(remapper, id)) {
|
||||
return IDWALK_RET_STOP_RECURSION;
|
||||
}
|
||||
|
||||
if (id->lib == nullptr) {
|
||||
/* There should be no links to local datablocks from linked editable data. */
|
||||
remapper.add(id, nullptr);
|
||||
BLI_assert_unreachable();
|
||||
return IDWALK_RET_STOP_RECURSION;
|
||||
}
|
||||
|
||||
/* Only preserve specific datablock types. */
|
||||
if (!ID_TYPE_SUPPORTS_ASSET_EDITABLE(GS(id->name))) {
|
||||
remapper.add(id, nullptr);
|
||||
return IDWALK_RET_STOP_RECURSION;
|
||||
}
|
||||
|
||||
/* There may be a new library pointer in new_bmain, matching a library in old_bmain, even
|
||||
* though pointer values are not the same. So we need to check new linked IDs in new_bmain
|
||||
* against both potential library pointers. */
|
||||
Library *old_id_new_lib = reuse_bmain_data_dependencies_new_library_get(reuse_data, id->lib);
|
||||
|
||||
/* Happens when the library is the newly opened blend file. */
|
||||
if (old_id_new_lib == nullptr) {
|
||||
remapper.add(id, nullptr);
|
||||
return IDWALK_RET_STOP_RECURSION;
|
||||
}
|
||||
|
||||
/* Move to new main database. */
|
||||
return reuse_bmain_move_id(reuse_data, id, old_id_new_lib, true) ? IDWALK_RET_STOP_RECURSION :
|
||||
IDWALK_RET_NOP;
|
||||
}
|
||||
|
||||
static bool reuse_editable_asset_needed(ReuseOldBMainData *reuse_data)
|
||||
{
|
||||
Main *old_bmain = reuse_data->old_bmain;
|
||||
LISTBASE_FOREACH (Library *, lib, &old_bmain->libraries) {
|
||||
if (lib->runtime.tag & LIBRARY_ASSET_EDITABLE) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selectively 'import' data from old Main into new Main, provided it does not conflict with data
|
||||
* already present in the new Main (name-wise and library-wise).
|
||||
*
|
||||
* Dependencies from moved over old data are also imported into the new Main, (unless, in case of
|
||||
* linked data, a matching linked ID is already available in new Main).
|
||||
*
|
||||
* When a conflict is found, usages of the conflicted ID by the old data are stored in the
|
||||
* `remapper` of `ReuseOldBMainData` to be remapped to the matching data in the new Main later.
|
||||
*
|
||||
* NOTE: This function will never remove any original new data from the new Main, it only moves
|
||||
* (some of) the old data to the new Main.
|
||||
*/
|
||||
static void reuse_editable_asset_bmain_data_for_blendfile(ReuseOldBMainData *reuse_data,
|
||||
const short idcode)
|
||||
{
|
||||
Main *new_bmain = reuse_data->new_bmain;
|
||||
Main *old_bmain = reuse_data->old_bmain;
|
||||
|
||||
id::IDRemapper &remapper = reuse_bmain_data_remapper_ensure(reuse_data);
|
||||
|
||||
ListBase *old_lb = which_libbase(old_bmain, idcode);
|
||||
ID *old_id_iter;
|
||||
|
||||
FOREACH_MAIN_LISTBASE_ID_BEGIN (old_lb, old_id_iter) {
|
||||
/* Keep any datablocks from libraries marked as LIBRARY_ASSET_EDITABLE. */
|
||||
if (!((old_id_iter->lib && old_id_iter->lib->runtime.tag & LIBRARY_ASSET_EDITABLE))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Library *old_id_new_lib = reuse_bmain_data_dependencies_new_library_get(reuse_data,
|
||||
old_id_iter->lib);
|
||||
|
||||
/* Happens when the library is the newly opened blend file. */
|
||||
if (old_id_new_lib == nullptr) {
|
||||
remapper.add(old_id_iter, nullptr);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (reuse_bmain_move_id(reuse_data, old_id_iter, old_id_new_lib, true)) {
|
||||
/* Port over dependencies of re-used ID, unless matching already existing ones in
|
||||
* new_bmain can be found.
|
||||
*
|
||||
* NOTE : No pointers are remapped here, this code only moves dependencies from old_bmain
|
||||
* to new_bmain if needed, and add necessary remapping rules to the reuse_data.remapper. */
|
||||
BKE_library_foreach_ID_link(new_bmain,
|
||||
old_id_iter,
|
||||
reuse_editable_asset_bmain_data_dependencies_process_cb,
|
||||
reuse_data,
|
||||
IDWALK_RECURSE | IDWALK_DO_LIBRARY_POINTER);
|
||||
}
|
||||
}
|
||||
FOREACH_MAIN_LISTBASE_ID_END;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does a complete replacement of data in `new_bmain` by data from `old_bmain. Original new data
|
||||
* are moved to the `old_bmain`, and will be freed together with it.
|
||||
|
@ -313,8 +507,9 @@ static bool reuse_bmain_data_remapper_is_id_remapped(id::IDRemapper &remapper, I
|
|||
* WARNING: Currently only expects to work on local data, won't work properly if some of the IDs of
|
||||
* given type are linked.
|
||||
*
|
||||
* NOTE: There is no support at all for potential dependencies of the IDs moved around. This is not
|
||||
* expected to be necessary for the current use cases (UI-related IDs).
|
||||
* NOTE: Unlike with #reuse_editable_asset_bmain_data_for_blendfile, there is no support at all for
|
||||
* potential dependencies of the IDs moved around. This is not expected to be necessary for the
|
||||
* current use cases (UI-related IDs).
|
||||
*/
|
||||
static void swap_old_bmain_data_for_blendfile(ReuseOldBMainData *reuse_data, const short id_code)
|
||||
{
|
||||
|
@ -660,7 +855,6 @@ static void setup_app_data(bContext *C,
|
|||
{
|
||||
Main *bmain = G_MAIN;
|
||||
const bool recover = (G.fileflags & G_FILE_RECOVER_READ) != 0;
|
||||
const bool is_startup = params->is_startup;
|
||||
enum {
|
||||
LOAD_UI = 1,
|
||||
LOAD_UI_OFF,
|
||||
|
@ -675,6 +869,9 @@ static void setup_app_data(bContext *C,
|
|||
* (but may have a null `curscreen`). */
|
||||
else if (ELEM(nullptr, bfd->curscreen, bfd->curscene)) {
|
||||
BKE_report(reports->reports, RPT_WARNING, "Library file, loading empty scene");
|
||||
if (blender::StringRefNull(bfd->main->filepath).endswith(BLENDER_ASSET_FILE_SUFFIX)) {
|
||||
bfd->main->is_asset_repository = true;
|
||||
}
|
||||
mode = LOAD_UI_OFF;
|
||||
}
|
||||
else if (G.fileflags & G_FILE_NO_UI) {
|
||||
|
@ -728,6 +925,18 @@ static void setup_app_data(bContext *C,
|
|||
}
|
||||
|
||||
BKE_main_idmap_destroy(reuse_data.id_map);
|
||||
|
||||
if (!params->is_factory_settings && reuse_editable_asset_needed(&reuse_data)) {
|
||||
/* Keep linked brush asset data, similar to UI data. Only does a known
|
||||
* subset know. Could do everything, but that risks dragging along more
|
||||
* scene data than we want. */
|
||||
for (short idtype_index = 0; idtype_index < INDEX_ID_MAX; idtype_index++) {
|
||||
const IDTypeInfo *idtype_info = BKE_idtype_get_info_from_idtype_index(idtype_index);
|
||||
if (ID_TYPE_SUPPORTS_ASSET_EDITABLE(idtype_info->id_code)) {
|
||||
reuse_editable_asset_bmain_data_for_blendfile(&reuse_data, idtype_info->id_code);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Logic for 'track_undo_scene' is to keep using the scene which the active screen has, as long
|
||||
|
@ -901,7 +1110,7 @@ static void setup_app_data(bContext *C,
|
|||
bmain->recovered = false;
|
||||
|
||||
/* `startup.blend` or recovered startup. */
|
||||
if (is_startup) {
|
||||
if (params->is_startup) {
|
||||
bmain->filepath[0] = '\0';
|
||||
}
|
||||
else if (recover) {
|
||||
|
@ -1257,6 +1466,15 @@ UserDef *BKE_blendfile_userdef_from_defaults()
|
|||
BKE_preferences_extension_repo_add_default(userdef);
|
||||
BKE_preferences_extension_repo_add_default_user(userdef);
|
||||
|
||||
{
|
||||
BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(
|
||||
userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh/Sculpt/Cloth");
|
||||
BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(
|
||||
userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh/Sculpt/General");
|
||||
BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(
|
||||
userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh/Sculpt/Painting");
|
||||
}
|
||||
|
||||
return userdef;
|
||||
}
|
||||
|
||||
|
|
|
@ -1912,7 +1912,9 @@ void BKE_blendfile_library_relocate(BlendfileLinkAppendContext *lapp_context,
|
|||
BKE_library_main_rebuild_hierarchy(bmain);
|
||||
|
||||
/* Resync overrides if needed. */
|
||||
if (!USER_EXPERIMENTAL_TEST(&U, no_override_auto_resync)) {
|
||||
if (!USER_EXPERIMENTAL_TEST(&U, no_override_auto_resync) &&
|
||||
lapp_context->params->context.scene != nullptr)
|
||||
{
|
||||
BlendFileReadReport report{};
|
||||
report.reports = reports;
|
||||
BKE_lib_override_library_main_resync(bmain,
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -822,6 +822,21 @@ ID *BKE_id_copy_for_use_in_bmain(Main *bmain, const ID *id)
|
|||
return newid;
|
||||
}
|
||||
|
||||
void BKE_id_move_to_same_lib(Main &bmain, ID &id, const ID &owner_id)
|
||||
{
|
||||
BLI_assert(id.lib == nullptr);
|
||||
if (owner_id.lib == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
id.lib = owner_id.lib;
|
||||
id.tag |= LIB_TAG_INDIRECT;
|
||||
|
||||
BKE_main_namemap_remove_name(&bmain, &id, id.name + 2);
|
||||
ListBase *lb = which_libbase(&bmain, GS(id.name));
|
||||
BKE_id_new_name_validate(&bmain, lb, &id, id.name, true);
|
||||
}
|
||||
|
||||
static void id_embedded_swap(ID **embedded_id_a,
|
||||
ID **embedded_id_b,
|
||||
const bool do_full_id,
|
||||
|
@ -1557,6 +1572,12 @@ void BKE_libblock_copy_in_lib(Main *bmain,
|
|||
}
|
||||
}
|
||||
|
||||
if (flag & LIB_ID_COPY_ASSET_METADATA) {
|
||||
if (id->asset_data) {
|
||||
new_id->asset_data = BKE_asset_metadata_copy(id->asset_data);
|
||||
}
|
||||
}
|
||||
|
||||
if ((flag & LIB_ID_CREATE_NO_DEG_TAG) == 0 && (flag & LIB_ID_CREATE_NO_MAIN) == 0) {
|
||||
DEG_id_type_tag(bmain, GS(new_id->name));
|
||||
}
|
||||
|
@ -1624,6 +1645,28 @@ ID *BKE_libblock_find_name_and_library(Main *bmain,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
ID *BKE_libblock_find_name_and_library_filepath(Main *bmain,
|
||||
short type,
|
||||
const char *name,
|
||||
const char *lib_filepath_abs)
|
||||
{
|
||||
ListBase *lb = which_libbase(bmain, type);
|
||||
BLI_assert(lb != nullptr);
|
||||
LISTBASE_FOREACH (ID *, id, lb) {
|
||||
if (!STREQ(id->name + 2, name)) {
|
||||
continue;
|
||||
}
|
||||
if (id->lib == nullptr && lib_filepath_abs == nullptr) {
|
||||
return id;
|
||||
}
|
||||
else if (id->lib && lib_filepath_abs && STREQ(id->lib->runtime.filepath_abs, lib_filepath_abs))
|
||||
{
|
||||
return id;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void id_sort_by_name(ListBase *lb, ID *id, ID *id_sorting_hint)
|
||||
{
|
||||
#define ID_SORT_STEP_SIZE 512
|
||||
|
@ -2151,7 +2194,7 @@ void BKE_libblock_rename(Main *bmain, ID *id, const char *name)
|
|||
}
|
||||
BKE_main_namemap_remove_name(bmain, id, id->name + 2);
|
||||
ListBase *lb = which_libbase(bmain, GS(id->name));
|
||||
if (BKE_id_new_name_validate(bmain, lb, id, name, false)) {
|
||||
if (BKE_id_new_name_validate(bmain, lb, id, name, true)) {
|
||||
bmain->is_memfile_undo_written = false;
|
||||
}
|
||||
}
|
||||
|
@ -2246,6 +2289,20 @@ bool BKE_id_is_editable(const Main *bmain, const ID *id)
|
|||
return ID_IS_EDITABLE(id) && !BKE_lib_override_library_is_system_defined(bmain, id);
|
||||
}
|
||||
|
||||
bool BKE_id_can_link(const ID &id_from, const ID &id_to)
|
||||
{
|
||||
/* Can't link from linked to local. */
|
||||
if (id_from.lib && !id_to.lib) {
|
||||
return false;
|
||||
}
|
||||
/* Can't link from ID in main database to one outside of it. */
|
||||
if (!(id_from.tag & LIB_TAG_NO_MAIN) && (id_to.tag & LIB_TAG_NO_MAIN)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/************************* Datablock order in UI **************************/
|
||||
|
||||
static int *id_order_get(ID *id)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "DNA_object_enums.h"
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "DNA_asset_types.h"
|
||||
#include "DNA_brush_types.h"
|
||||
#include "DNA_defaults.h"
|
||||
#include "DNA_gpencil_legacy_types.h"
|
||||
|
@ -33,12 +34,15 @@
|
|||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_math_matrix.hh"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_utf8.h"
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "BKE_asset.hh"
|
||||
#include "BKE_asset_edit.hh"
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_brush.hh"
|
||||
#include "BKE_ccg.h"
|
||||
|
@ -444,61 +448,6 @@ const EnumPropertyItem *BKE_paint_get_tool_enum_from_paintmode(const PaintMode m
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
const char *BKE_paint_get_tool_prop_id_from_paintmode(const PaintMode mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case PaintMode::Sculpt:
|
||||
return "sculpt_tool";
|
||||
case PaintMode::Vertex:
|
||||
return "vertex_tool";
|
||||
case PaintMode::Weight:
|
||||
return "weight_tool";
|
||||
case PaintMode::Texture2D:
|
||||
case PaintMode::Texture3D:
|
||||
return "image_tool";
|
||||
case PaintMode::GPencil:
|
||||
return "gpencil_tool";
|
||||
case PaintMode::VertexGPencil:
|
||||
return "gpencil_vertex_tool";
|
||||
case PaintMode::SculptGPencil:
|
||||
return "gpencil_sculpt_tool";
|
||||
case PaintMode::WeightGPencil:
|
||||
return "gpencil_weight_tool";
|
||||
case PaintMode::SculptCurves:
|
||||
return "curves_sculpt_tool";
|
||||
case PaintMode::SculptGreasePencil:
|
||||
return "gpencil_sculpt_tool";
|
||||
case PaintMode::Invalid:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Invalid paint mode. */
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char *BKE_paint_get_tool_enum_translation_context_from_paintmode(const PaintMode mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case PaintMode::Sculpt:
|
||||
case PaintMode::GPencil:
|
||||
case PaintMode::Texture2D:
|
||||
case PaintMode::Texture3D:
|
||||
return BLT_I18NCONTEXT_ID_BRUSH;
|
||||
case PaintMode::Vertex:
|
||||
case PaintMode::Weight:
|
||||
case PaintMode::VertexGPencil:
|
||||
case PaintMode::SculptGPencil:
|
||||
case PaintMode::WeightGPencil:
|
||||
case PaintMode::SculptCurves:
|
||||
case PaintMode::SculptGreasePencil:
|
||||
case PaintMode::Invalid:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Invalid paint mode. */
|
||||
return BLT_I18NCONTEXT_DEFAULT;
|
||||
}
|
||||
|
||||
Paint *BKE_paint_get_active(Scene *sce, ViewLayer *view_layer)
|
||||
{
|
||||
if (sce && view_layer) {
|
||||
|
@ -663,68 +612,338 @@ PaintMode BKE_paintmode_get_from_tool(const bToolRef *tref)
|
|||
return PaintMode::Invalid;
|
||||
}
|
||||
|
||||
Brush *BKE_paint_brush(Paint *p)
|
||||
static bool paint_brush_set_from_asset_reference(Main *bmain, Paint *paint)
|
||||
{
|
||||
return (Brush *)BKE_paint_brush_for_read((const Paint *)p);
|
||||
/* Don't resolve this during file read, it will be done after. */
|
||||
if (bmain->is_locked_for_linking) {
|
||||
return false;
|
||||
}
|
||||
/* Attempt to restore a valid active brush from brush asset information. */
|
||||
if (paint->brush != nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (paint->brush_asset_reference == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Brush *brush = reinterpret_cast<Brush *>(blender::bke::asset_edit_id_from_weak_reference(
|
||||
*bmain, ID_BR, *paint->brush_asset_reference));
|
||||
BLI_assert(brush == nullptr || blender::bke::asset_edit_id_is_editable(brush->id));
|
||||
|
||||
/* Ensure we have a brush with appropriate mode to assign.
|
||||
* Could happen if contents of asset blend was manually changed. */
|
||||
if (brush == nullptr || (paint->runtime.ob_mode & brush->ob_mode) == 0) {
|
||||
MEM_delete(paint->brush_asset_reference);
|
||||
paint->brush_asset_reference = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
paint->brush = brush;
|
||||
return true;
|
||||
}
|
||||
|
||||
const Brush *BKE_paint_brush_for_read(const Paint *p)
|
||||
Brush *BKE_paint_brush(Paint *paint)
|
||||
{
|
||||
return p ? p->brush : nullptr;
|
||||
return (Brush *)BKE_paint_brush_for_read((const Paint *)paint);
|
||||
}
|
||||
|
||||
void BKE_paint_brush_set(Paint *p, Brush *br)
|
||||
const Brush *BKE_paint_brush_for_read(const Paint *paint)
|
||||
{
|
||||
if (p) {
|
||||
id_us_min((ID *)p->brush);
|
||||
id_us_plus((ID *)br);
|
||||
p->brush = br;
|
||||
return paint ? paint->brush : nullptr;
|
||||
}
|
||||
|
||||
BKE_paint_toolslots_brush_update(p);
|
||||
bool BKE_paint_brush_set(Paint *paint, Brush *brush)
|
||||
{
|
||||
if (paint == nullptr || paint->brush == brush) {
|
||||
return false;
|
||||
}
|
||||
if (brush && (paint->runtime.ob_mode & brush->ob_mode) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
paint->brush = brush;
|
||||
|
||||
MEM_delete(paint->brush_asset_reference);
|
||||
paint->brush_asset_reference = nullptr;
|
||||
|
||||
if (brush != nullptr) {
|
||||
std::optional<AssetWeakReference> weak_ref = blender::bke::asset_edit_weak_reference_from_id(
|
||||
brush->id);
|
||||
if (weak_ref.has_value()) {
|
||||
paint->brush_asset_reference = MEM_new<AssetWeakReference>(__func__, *weak_ref);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Brush *BKE_paint_brush_from_essentials(Main *bmain, const char *name)
|
||||
{
|
||||
AssetWeakReference weak_ref;
|
||||
weak_ref.asset_library_type = eAssetLibraryType::ASSET_LIBRARY_ESSENTIALS;
|
||||
weak_ref.relative_asset_identifier = BLI_sprintfN("brushes/essentials_brushes.blend/Brush/%s",
|
||||
name);
|
||||
|
||||
return reinterpret_cast<Brush *>(
|
||||
blender::bke::asset_edit_id_from_weak_reference(*bmain, ID_BR, weak_ref));
|
||||
}
|
||||
|
||||
static void paint_brush_set_essentials_reference(Paint *paint, const char *name)
|
||||
{
|
||||
/* Set brush asset reference to a named brush in the essentials asset library. */
|
||||
MEM_delete(paint->brush_asset_reference);
|
||||
|
||||
AssetWeakReference *weak_ref = MEM_new<AssetWeakReference>(__func__);
|
||||
weak_ref->asset_library_type = eAssetLibraryType::ASSET_LIBRARY_ESSENTIALS;
|
||||
weak_ref->relative_asset_identifier = BLI_sprintfN("brushes/essentials_brushes.blend/Brush/%s",
|
||||
name);
|
||||
paint->brush_asset_reference = weak_ref;
|
||||
paint->brush = nullptr;
|
||||
}
|
||||
|
||||
static void paint_eraser_brush_set_essentials_reference(Paint *paint, const char *name)
|
||||
{
|
||||
/* Set brush asset reference to a named brush in the essentials asset library. */
|
||||
MEM_delete(paint->eraser_brush_asset_reference);
|
||||
|
||||
AssetWeakReference *weak_ref = MEM_new<AssetWeakReference>(__func__);
|
||||
weak_ref->asset_library_type = eAssetLibraryType::ASSET_LIBRARY_ESSENTIALS;
|
||||
weak_ref->relative_asset_identifier = BLI_sprintfN("brushes/essentials_brushes.blend/Brush/%s",
|
||||
name);
|
||||
paint->eraser_brush_asset_reference = weak_ref;
|
||||
paint->eraser_brush = nullptr;
|
||||
}
|
||||
|
||||
static void paint_brush_set_default_reference(Paint *paint,
|
||||
const bool do_regular = true,
|
||||
const bool do_eraser = true)
|
||||
{
|
||||
const char *name = nullptr;
|
||||
const char *eraser_name = nullptr;
|
||||
|
||||
switch (paint->runtime.ob_mode) {
|
||||
case OB_MODE_SCULPT:
|
||||
name = "Draw";
|
||||
break;
|
||||
case OB_MODE_VERTEX_PAINT:
|
||||
name = "Paint Vertex";
|
||||
break;
|
||||
case OB_MODE_WEIGHT_PAINT:
|
||||
name = "Paint Weight";
|
||||
break;
|
||||
case OB_MODE_TEXTURE_PAINT:
|
||||
name = "Paint Texture";
|
||||
break;
|
||||
case OB_MODE_SCULPT_CURVES:
|
||||
name = "Comb Curves";
|
||||
break;
|
||||
case OB_MODE_PAINT_GPENCIL_LEGACY:
|
||||
name = "Pencil";
|
||||
// TODO: should be soft, but missing from essentials still.
|
||||
eraser_name = "Eraser Hard";
|
||||
break;
|
||||
case OB_MODE_VERTEX_GPENCIL_LEGACY:
|
||||
name = "Paint Point Color";
|
||||
break;
|
||||
case OB_MODE_SCULPT_GPENCIL_LEGACY:
|
||||
name = "Smooth Stroke";
|
||||
break;
|
||||
case OB_MODE_WEIGHT_GPENCIL_LEGACY:
|
||||
name = "Paint Point Weight";
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return;
|
||||
}
|
||||
|
||||
if (do_regular && name) {
|
||||
paint_brush_set_essentials_reference(paint, name);
|
||||
}
|
||||
if (do_eraser && eraser_name) {
|
||||
paint_eraser_brush_set_essentials_reference(paint, eraser_name);
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_paint_runtime_init(const ToolSettings *ts, Paint *paint)
|
||||
void BKE_paint_brushes_set_default_references(ToolSettings *ts)
|
||||
{
|
||||
if (ts->sculpt) {
|
||||
paint_brush_set_default_reference(&ts->sculpt->paint);
|
||||
}
|
||||
if (ts->curves_sculpt) {
|
||||
paint_brush_set_default_reference(&ts->curves_sculpt->paint);
|
||||
}
|
||||
if (ts->wpaint) {
|
||||
paint_brush_set_default_reference(&ts->wpaint->paint);
|
||||
}
|
||||
if (ts->vpaint) {
|
||||
paint_brush_set_default_reference(&ts->vpaint->paint);
|
||||
}
|
||||
if (ts->gp_paint) {
|
||||
paint_brush_set_default_reference(&ts->gp_paint->paint);
|
||||
}
|
||||
if (ts->gp_vertexpaint) {
|
||||
paint_brush_set_default_reference(&ts->gp_vertexpaint->paint);
|
||||
}
|
||||
if (ts->gp_sculptpaint) {
|
||||
paint_brush_set_default_reference(&ts->gp_sculptpaint->paint);
|
||||
}
|
||||
if (ts->gp_weightpaint) {
|
||||
paint_brush_set_default_reference(&ts->gp_weightpaint->paint);
|
||||
}
|
||||
paint_brush_set_default_reference(&ts->imapaint.paint);
|
||||
}
|
||||
|
||||
bool BKE_paint_brush_set_default(Main *bmain, Paint *paint)
|
||||
{
|
||||
paint_brush_set_default_reference(paint, true, false);
|
||||
return paint_brush_set_from_asset_reference(bmain, paint);
|
||||
}
|
||||
|
||||
bool BKE_paint_brush_set_essentials(Main *bmain, Paint *paint, const char *name)
|
||||
{
|
||||
paint_brush_set_essentials_reference(paint, name);
|
||||
return paint_brush_set_from_asset_reference(bmain, paint);
|
||||
}
|
||||
|
||||
void BKE_paint_brushes_validate(Main *bmain, Paint *paint)
|
||||
{
|
||||
/* Clear brush with invalid mode. Unclear if this can still happen,
|
||||
* but kept from old paint toolslots code. */
|
||||
Brush *brush = BKE_paint_brush(paint);
|
||||
if (brush && (paint->runtime.ob_mode & brush->ob_mode) == 0) {
|
||||
BKE_paint_brush_set(paint, nullptr);
|
||||
BKE_paint_brush_set_default(bmain, paint);
|
||||
}
|
||||
|
||||
Brush *eraser_brush = BKE_paint_eraser_brush(paint);
|
||||
if (eraser_brush && (paint->runtime.ob_mode & eraser_brush->ob_mode) == 0) {
|
||||
BKE_paint_eraser_brush_set(paint, nullptr);
|
||||
BKE_paint_eraser_brush_set_default(bmain, paint);
|
||||
}
|
||||
}
|
||||
|
||||
static bool paint_eraser_brush_set_from_asset_reference(Main *bmain, Paint *paint)
|
||||
{
|
||||
/* Don't resolve this during file read, it will be done after. */
|
||||
if (bmain->is_locked_for_linking) {
|
||||
return false;
|
||||
}
|
||||
/* Attempt to restore a valid active brush from brush asset information. */
|
||||
if (paint->eraser_brush != nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (paint->eraser_brush_asset_reference == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Brush *brush = reinterpret_cast<Brush *>(blender::bke::asset_edit_id_from_weak_reference(
|
||||
*bmain, ID_BR, *paint->eraser_brush_asset_reference));
|
||||
BLI_assert(brush == nullptr || blender::bke::asset_edit_id_is_editable(brush->id));
|
||||
|
||||
/* Ensure we have a brush with appropriate mode to assign.
|
||||
* Could happen if contents of asset blend was manually changed. */
|
||||
if (brush == nullptr || (paint->runtime.ob_mode & brush->ob_mode) == 0) {
|
||||
MEM_delete(paint->eraser_brush_asset_reference);
|
||||
paint->eraser_brush_asset_reference = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
paint->eraser_brush = brush;
|
||||
return true;
|
||||
}
|
||||
|
||||
Brush *BKE_paint_eraser_brush(Paint *paint)
|
||||
{
|
||||
return (Brush *)BKE_paint_eraser_brush_for_read((const Paint *)paint);
|
||||
}
|
||||
|
||||
const Brush *BKE_paint_eraser_brush_for_read(const Paint *paint)
|
||||
{
|
||||
return paint ? paint->eraser_brush : nullptr;
|
||||
}
|
||||
|
||||
bool BKE_paint_eraser_brush_set(Paint *paint, Brush *brush)
|
||||
{
|
||||
if (paint == nullptr || paint->eraser_brush == brush) {
|
||||
return false;
|
||||
}
|
||||
if (brush && (paint->runtime.ob_mode & brush->ob_mode) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
paint->eraser_brush = brush;
|
||||
|
||||
MEM_delete(paint->eraser_brush_asset_reference);
|
||||
paint->eraser_brush_asset_reference = nullptr;
|
||||
|
||||
if (brush != nullptr) {
|
||||
std::optional<AssetWeakReference> weak_ref = blender::bke::asset_edit_weak_reference_from_id(
|
||||
brush->id);
|
||||
if (weak_ref.has_value()) {
|
||||
paint->eraser_brush_asset_reference = MEM_new<AssetWeakReference>(__func__, *weak_ref);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Brush *BKE_paint_eraser_brush_from_essentials(Main *bmain, const char *name)
|
||||
{
|
||||
AssetWeakReference weak_ref;
|
||||
weak_ref.asset_library_type = eAssetLibraryType::ASSET_LIBRARY_ESSENTIALS;
|
||||
weak_ref.relative_asset_identifier = BLI_sprintfN("brushes/essentials_brushes.blend/Brush/%s",
|
||||
name);
|
||||
|
||||
return reinterpret_cast<Brush *>(
|
||||
blender::bke::asset_edit_id_from_weak_reference(*bmain, ID_BR, weak_ref));
|
||||
}
|
||||
|
||||
bool BKE_paint_eraser_brush_set_default(Main *bmain, Paint *paint)
|
||||
{
|
||||
paint_brush_set_default_reference(paint, false, true);
|
||||
return paint_eraser_brush_set_from_asset_reference(bmain, paint);
|
||||
}
|
||||
|
||||
bool BKE_paint_eraser_brush_set_essentials(Main *bmain, Paint *paint, const char *name)
|
||||
{
|
||||
paint_eraser_brush_set_essentials_reference(paint, name);
|
||||
return paint_eraser_brush_set_from_asset_reference(bmain, paint);
|
||||
}
|
||||
|
||||
static void paint_runtime_init(const ToolSettings *ts, Paint *paint)
|
||||
{
|
||||
if (paint == &ts->imapaint.paint) {
|
||||
paint->runtime.tool_offset = offsetof(Brush, imagepaint_tool);
|
||||
paint->runtime.ob_mode = OB_MODE_TEXTURE_PAINT;
|
||||
}
|
||||
else if (ts->sculpt && paint == &ts->sculpt->paint) {
|
||||
paint->runtime.tool_offset = offsetof(Brush, sculpt_tool);
|
||||
paint->runtime.ob_mode = OB_MODE_SCULPT;
|
||||
}
|
||||
else if (ts->vpaint && paint == &ts->vpaint->paint) {
|
||||
paint->runtime.tool_offset = offsetof(Brush, vertexpaint_tool);
|
||||
paint->runtime.ob_mode = OB_MODE_VERTEX_PAINT;
|
||||
}
|
||||
else if (ts->wpaint && paint == &ts->wpaint->paint) {
|
||||
paint->runtime.tool_offset = offsetof(Brush, weightpaint_tool);
|
||||
paint->runtime.ob_mode = OB_MODE_WEIGHT_PAINT;
|
||||
}
|
||||
else if (ts->gp_paint && paint == &ts->gp_paint->paint) {
|
||||
paint->runtime.tool_offset = offsetof(Brush, gpencil_tool);
|
||||
paint->runtime.ob_mode = OB_MODE_PAINT_GPENCIL_LEGACY;
|
||||
}
|
||||
else if (ts->gp_vertexpaint && paint == &ts->gp_vertexpaint->paint) {
|
||||
paint->runtime.tool_offset = offsetof(Brush, gpencil_vertex_tool);
|
||||
paint->runtime.ob_mode = OB_MODE_VERTEX_GPENCIL_LEGACY;
|
||||
}
|
||||
else if (ts->gp_sculptpaint && paint == &ts->gp_sculptpaint->paint) {
|
||||
paint->runtime.tool_offset = offsetof(Brush, gpencil_sculpt_tool);
|
||||
paint->runtime.ob_mode = OB_MODE_SCULPT_GPENCIL_LEGACY;
|
||||
}
|
||||
else if (ts->gp_weightpaint && paint == &ts->gp_weightpaint->paint) {
|
||||
paint->runtime.tool_offset = offsetof(Brush, gpencil_weight_tool);
|
||||
paint->runtime.ob_mode = OB_MODE_WEIGHT_GPENCIL_LEGACY;
|
||||
}
|
||||
else if (ts->curves_sculpt && paint == &ts->curves_sculpt->paint) {
|
||||
paint->runtime.tool_offset = offsetof(Brush, curves_sculpt_tool);
|
||||
paint->runtime.ob_mode = OB_MODE_SCULPT_CURVES;
|
||||
}
|
||||
else {
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
|
||||
paint->runtime.initialized = true;
|
||||
}
|
||||
|
||||
uint BKE_paint_get_brush_tool_offset_from_paintmode(const PaintMode mode)
|
||||
|
@ -1091,17 +1310,15 @@ eObjectMode BKE_paint_object_mode_from_paintmode(const PaintMode mode)
|
|||
}
|
||||
}
|
||||
|
||||
bool BKE_paint_ensure(Main * /*bmain*/, ToolSettings *ts, Paint **r_paint)
|
||||
bool BKE_paint_ensure(Main *bmain, ToolSettings *ts, Paint **r_paint)
|
||||
{
|
||||
Paint *paint = nullptr;
|
||||
if (*r_paint) {
|
||||
/* Tool offset should never be 0 for initialized paint settings, so it's a reliable way to
|
||||
* check if already initialized. */
|
||||
if ((*r_paint)->runtime.tool_offset == 0) {
|
||||
if (!(*r_paint)->runtime.initialized) {
|
||||
/* Currently only image painting is initialized this way, others have to be allocated. */
|
||||
BLI_assert(ELEM(*r_paint, (Paint *)&ts->imapaint));
|
||||
|
||||
BKE_paint_runtime_init(ts, *r_paint);
|
||||
paint_runtime_init(ts, *r_paint);
|
||||
}
|
||||
else {
|
||||
BLI_assert(ELEM(*r_paint,
|
||||
|
@ -1117,13 +1334,14 @@ bool BKE_paint_ensure(Main * /*bmain*/, ToolSettings *ts, Paint **r_paint)
|
|||
(Paint *)&ts->imapaint));
|
||||
#ifndef NDEBUG
|
||||
Paint paint_test = **r_paint;
|
||||
BKE_paint_runtime_init(ts, *r_paint);
|
||||
paint_runtime_init(ts, *r_paint);
|
||||
/* Swap so debug doesn't hide errors when release fails. */
|
||||
std::swap(**r_paint, paint_test);
|
||||
BLI_assert(paint_test.runtime.ob_mode == (*r_paint)->runtime.ob_mode);
|
||||
BLI_assert(paint_test.runtime.tool_offset == (*r_paint)->runtime.tool_offset);
|
||||
#endif
|
||||
}
|
||||
paint_brush_set_from_asset_reference(bmain, *r_paint);
|
||||
paint_eraser_brush_set_from_asset_reference(bmain, *r_paint);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1166,7 +1384,9 @@ bool BKE_paint_ensure(Main * /*bmain*/, ToolSettings *ts, Paint **r_paint)
|
|||
|
||||
*r_paint = paint;
|
||||
|
||||
BKE_paint_runtime_init(ts, paint);
|
||||
paint_runtime_init(ts, paint);
|
||||
BKE_paint_brush_set_default(bmain, paint);
|
||||
BKE_paint_eraser_brush_set_default(bmain, paint);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -1178,18 +1398,6 @@ void BKE_paint_init(Main *bmain, Scene *sce, PaintMode mode, const uchar col[3])
|
|||
|
||||
BKE_paint_ensure_from_paintmode(bmain, sce, mode);
|
||||
|
||||
/* If there's no brush, create one */
|
||||
Brush *brush = BKE_paint_brush(paint);
|
||||
if (brush == nullptr) {
|
||||
eObjectMode ob_mode = BKE_paint_object_mode_from_paintmode(mode);
|
||||
brush = BKE_brush_first_search(bmain, ob_mode);
|
||||
if (!brush) {
|
||||
brush = BKE_brush_add(bmain, "Brush", ob_mode);
|
||||
id_us_min(&brush->id); /* Fake user only. */
|
||||
}
|
||||
BKE_paint_brush_set(paint, brush);
|
||||
}
|
||||
|
||||
copy_v3_v3_uchar(paint->paint_cursor_col, col);
|
||||
paint->paint_cursor_col[3] = 128;
|
||||
ups->last_stroke_valid = false;
|
||||
|
@ -1203,23 +1411,26 @@ void BKE_paint_init(Main *bmain, Scene *sce, PaintMode mode, const uchar col[3])
|
|||
void BKE_paint_free(Paint *paint)
|
||||
{
|
||||
BKE_curvemapping_free(paint->cavity_curve);
|
||||
MEM_SAFE_FREE(paint->tool_slots);
|
||||
MEM_delete(paint->brush_asset_reference);
|
||||
MEM_delete(paint->eraser_brush_asset_reference);
|
||||
}
|
||||
|
||||
void BKE_paint_copy(const Paint *src, Paint *tar, const int flag)
|
||||
void BKE_paint_copy(const Paint *src, Paint *dst, const int flag)
|
||||
{
|
||||
tar->brush = src->brush;
|
||||
tar->cavity_curve = BKE_curvemapping_copy(src->cavity_curve);
|
||||
tar->tool_slots = static_cast<PaintToolSlot *>(MEM_dupallocN(src->tool_slots));
|
||||
dst->brush = src->brush;
|
||||
dst->cavity_curve = BKE_curvemapping_copy(src->cavity_curve);
|
||||
|
||||
if (src->brush_asset_reference) {
|
||||
dst->brush_asset_reference = MEM_new<AssetWeakReference>(__func__,
|
||||
*src->brush_asset_reference);
|
||||
}
|
||||
if (src->eraser_brush_asset_reference) {
|
||||
dst->eraser_brush_asset_reference = MEM_new<AssetWeakReference>(
|
||||
__func__, *src->eraser_brush_asset_reference);
|
||||
}
|
||||
|
||||
if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
|
||||
id_us_plus((ID *)tar->brush);
|
||||
id_us_plus((ID *)tar->palette);
|
||||
if (src->tool_slots != nullptr) {
|
||||
for (int i = 0; i < tar->tool_slots_len; i++) {
|
||||
id_us_plus((ID *)tar->tool_slots[i].brush);
|
||||
}
|
||||
}
|
||||
id_us_plus((ID *)dst->palette);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1240,7 +1451,12 @@ void BKE_paint_blend_write(BlendWriter *writer, Paint *p)
|
|||
if (p->cavity_curve) {
|
||||
BKE_curvemapping_blend_write(writer, p->cavity_curve);
|
||||
}
|
||||
BLO_write_struct_array(writer, PaintToolSlot, p->tool_slots_len, p->tool_slots);
|
||||
if (p->brush_asset_reference) {
|
||||
BKE_asset_weak_reference_write(writer, p->brush_asset_reference);
|
||||
}
|
||||
if (p->eraser_brush_asset_reference) {
|
||||
BKE_asset_weak_reference_write(writer, p->eraser_brush_asset_reference);
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_paint_blend_read_data(BlendDataReader *reader, const Scene *scene, Paint *p)
|
||||
|
@ -1253,17 +1469,18 @@ void BKE_paint_blend_read_data(BlendDataReader *reader, const Scene *scene, Pain
|
|||
BKE_paint_cavity_curve_preset(p, CURVE_PRESET_LINE);
|
||||
}
|
||||
|
||||
BLO_read_struct_array(reader, PaintToolSlot, p->tool_slots_len, &p->tool_slots);
|
||||
BLO_read_struct(reader, AssetWeakReference, &p->brush_asset_reference);
|
||||
if (p->brush_asset_reference) {
|
||||
BKE_asset_weak_reference_read(reader, p->brush_asset_reference);
|
||||
}
|
||||
|
||||
/* Workaround for invalid data written in older versions. */
|
||||
const size_t expected_size = sizeof(PaintToolSlot) * p->tool_slots_len;
|
||||
if (p->tool_slots && MEM_allocN_len(p->tool_slots) < expected_size) {
|
||||
MEM_freeN(p->tool_slots);
|
||||
p->tool_slots = static_cast<PaintToolSlot *>(MEM_callocN(expected_size, "PaintToolSlot"));
|
||||
BLO_read_struct(reader, AssetWeakReference, &p->eraser_brush_asset_reference);
|
||||
if (p->eraser_brush_asset_reference) {
|
||||
BKE_asset_weak_reference_read(reader, p->eraser_brush_asset_reference);
|
||||
}
|
||||
|
||||
p->paint_cursor = nullptr;
|
||||
BKE_paint_runtime_init(scene->toolsettings, p);
|
||||
paint_runtime_init(scene->toolsettings, p);
|
||||
}
|
||||
|
||||
bool paint_is_grid_face_hidden(const blender::BoundedBitSpan grid_hidden,
|
||||
|
|
|
@ -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,17 @@ static void scene_foreach_paint(LibraryForeachIDData *data,
|
|||
SCENE_FOREACH_UNDO_RESTORE,
|
||||
reader,
|
||||
&paint_old->brush,
|
||||
IDWALK_CB_USER);
|
||||
IDWALK_CB_NOP);
|
||||
|
||||
for (int i = 0; i < paint_old->tool_slots_len; i++) {
|
||||
/* This is a bit tricky.
|
||||
* - In case we do not do `undo_restore`, `paint` and `paint_old` pointers are the same, so
|
||||
* this is equivalent to simply looping over slots from `paint`.
|
||||
* - In case we do `undo_restore`, we only want to consider the slots from the old one, since
|
||||
* those are the one we keep in the end.
|
||||
* + In case the new data has less valid slots, we feed in a dummy null pointer.
|
||||
* + In case the new data has more valid slots, the extra ones are ignored.
|
||||
*/
|
||||
brush_tmp = nullptr;
|
||||
brush_p = (paint && i < paint->tool_slots_len) ? &paint->tool_slots[i].brush : &brush_tmp;
|
||||
BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER_P(data,
|
||||
brush_p,
|
||||
do_undo_restore,
|
||||
SCENE_FOREACH_UNDO_RESTORE,
|
||||
reader,
|
||||
&paint_old->tool_slots[i].brush,
|
||||
IDWALK_CB_USER);
|
||||
}
|
||||
Brush *eraser_brush_tmp = nullptr;
|
||||
Brush **eraser_brush_p = paint ? &paint->eraser_brush : &eraser_brush_tmp;
|
||||
BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER_P(data,
|
||||
eraser_brush_p,
|
||||
do_undo_restore,
|
||||
SCENE_FOREACH_UNDO_RESTORE,
|
||||
reader,
|
||||
&paint_old->eraser_brush,
|
||||
IDWALK_CB_NOP);
|
||||
|
||||
Palette *palette_tmp = nullptr;
|
||||
Palette **palette_p = paint ? &paint->palette : &palette_tmp;
|
||||
|
|
|
@ -89,6 +89,7 @@ struct BlendFileReadWMSetupData {
|
|||
struct BlendFileReadParams {
|
||||
uint skip_flags : 3; /* #eBLOReadSkip */
|
||||
uint is_startup : 1;
|
||||
uint is_factory_settings : 1;
|
||||
|
||||
/** Whether we are reading the memfile for an undo or a redo. */
|
||||
int undo_direction; /* #eUndoStepDir */
|
||||
|
|
|
@ -2583,7 +2583,6 @@ void do_versions_after_linking_280(FileData *fd, Main *bmain)
|
|||
brush->gpencil_tool = brush->gpencil_settings->brush_type;
|
||||
}
|
||||
}
|
||||
BKE_paint_toolslots_init_from_main(bmain);
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 280, 38)) {
|
||||
|
@ -2862,13 +2861,9 @@ void do_versions_after_linking_280(FileData *fd, Main *bmain)
|
|||
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 282, 2)) {
|
||||
/* Init all Vertex/Sculpt and Weight Paint brushes. */
|
||||
Brush *brush;
|
||||
Material *ma;
|
||||
/* Pen Soft brush. */
|
||||
brush = (Brush *)do_versions_rename_id(bmain, ID_BR, "Draw Soft", "Pencil Soft");
|
||||
if (brush) {
|
||||
brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PEN;
|
||||
}
|
||||
do_versions_rename_id(bmain, ID_BR, "Draw Soft", "Pencil Soft");
|
||||
do_versions_rename_id(bmain, ID_BR, "Draw Pencil", "Pencil");
|
||||
do_versions_rename_id(bmain, ID_BR, "Draw Pen", "Pen");
|
||||
do_versions_rename_id(bmain, ID_BR, "Draw Ink", "Ink Pen");
|
||||
|
@ -2896,9 +2891,6 @@ void do_versions_after_linking_280(FileData *fd, Main *bmain)
|
|||
do_versions_rename_id(bmain, ID_MA, "Black Dots", "Dots Stroke");
|
||||
}
|
||||
|
||||
brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, "Pencil", offsetof(ID, name) + 2));
|
||||
|
||||
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
|
||||
ToolSettings *ts = scene->toolsettings;
|
||||
|
||||
|
@ -2908,13 +2900,9 @@ void do_versions_after_linking_280(FileData *fd, Main *bmain)
|
|||
BKE_paint_ensure_from_paintmode(bmain, scene, PaintMode::SculptGPencil);
|
||||
BKE_paint_ensure_from_paintmode(bmain, scene, PaintMode::WeightGPencil);
|
||||
|
||||
/* Set default Draw brush. */
|
||||
if (brush != nullptr) {
|
||||
Paint *paint = &ts->gp_paint->paint;
|
||||
BKE_paint_brush_set(paint, brush);
|
||||
/* Enable cursor by default. */
|
||||
paint->flags |= PAINT_SHOW_BRUSH;
|
||||
}
|
||||
/* Enable cursor by default. */
|
||||
Paint *paint = &ts->gp_paint->paint;
|
||||
paint->flags |= PAINT_SHOW_BRUSH;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "DNA_movieclip_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_sequence_types.h"
|
||||
#include "DNA_workspace_types.h"
|
||||
#include "DNA_world_types.h"
|
||||
|
||||
#include "DNA_defaults.h"
|
||||
|
@ -50,6 +51,7 @@
|
|||
#include "BKE_armature.hh"
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_colortools.hh"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_curve.hh"
|
||||
#include "BKE_customdata.hh"
|
||||
#include "BKE_effect.h"
|
||||
|
@ -60,6 +62,7 @@
|
|||
#include "BKE_mesh_legacy_convert.hh"
|
||||
#include "BKE_nla.h"
|
||||
#include "BKE_node_runtime.hh"
|
||||
#include "BKE_paint.hh"
|
||||
#include "BKE_scene.hh"
|
||||
#include "BKE_tracking.h"
|
||||
|
||||
|
@ -2114,6 +2117,40 @@ static bool seq_filter_bilinear_to_auto(Sequence *seq, void * /*user_data*/)
|
|||
return true;
|
||||
}
|
||||
|
||||
static void update_paint_modes_for_brush_assets(Main &bmain)
|
||||
{
|
||||
/* Replace paint brushes with a reference to the default brush asset for that mode. */
|
||||
LISTBASE_FOREACH (Scene *, scene, &bmain.scenes) {
|
||||
BKE_paint_brushes_set_default_references(scene->toolsettings);
|
||||
}
|
||||
|
||||
/* Replace persistent tool references with the new single builtin brush tool. */
|
||||
LISTBASE_FOREACH (WorkSpace *, workspace, &bmain.workspaces) {
|
||||
LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
|
||||
if (tref->space_type != SPACE_VIEW3D) {
|
||||
continue;
|
||||
}
|
||||
if (!ELEM(tref->mode,
|
||||
CTX_MODE_SCULPT,
|
||||
CTX_MODE_PAINT_VERTEX,
|
||||
CTX_MODE_PAINT_WEIGHT,
|
||||
CTX_MODE_PAINT_TEXTURE,
|
||||
CTX_MODE_PAINT_GPENCIL_LEGACY,
|
||||
CTX_MODE_PAINT_GREASE_PENCIL,
|
||||
CTX_MODE_SCULPT_GPENCIL_LEGACY,
|
||||
CTX_MODE_SCULPT_GREASE_PENCIL,
|
||||
CTX_MODE_WEIGHT_GPENCIL_LEGACY,
|
||||
CTX_MODE_WEIGHT_GREASE_PENCIL,
|
||||
CTX_MODE_VERTEX_GPENCIL_LEGACY,
|
||||
CTX_MODE_SCULPT_CURVES))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
STRNCPY(tref->idname, "builtin.brush");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void image_settings_avi_to_ffmpeg(Scene *scene)
|
||||
{
|
||||
if (ELEM(scene->r.im_format.imtype, R_IMF_IMTYPE_AVIRAW, R_IMF_IMTYPE_AVIJPEG)) {
|
||||
|
@ -3625,6 +3662,10 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
|
|||
}
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 402, 40)) {
|
||||
update_paint_modes_for_brush_assets(*bmain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Always bump subversion in BKE_blender_version.h when adding versioning
|
||||
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "BLI_math_rotation.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_mempool.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
|
@ -451,43 +452,8 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template)
|
|||
BLO_update_defaults_workspace(workspace, app_template);
|
||||
}
|
||||
|
||||
/* New grease pencil brushes and vertex paint setup. */
|
||||
/* Grease pencil materials and paint modes setup. */
|
||||
{
|
||||
/* Update Grease Pencil brushes. */
|
||||
Brush *brush;
|
||||
|
||||
/* Pencil brush. */
|
||||
do_versions_rename_id(bmain, ID_BR, "Draw Pencil", "Pencil");
|
||||
|
||||
/* Pen brush. */
|
||||
do_versions_rename_id(bmain, ID_BR, "Draw Pen", "Pen");
|
||||
|
||||
/* Pen Soft brush. */
|
||||
brush = reinterpret_cast<Brush *>(
|
||||
do_versions_rename_id(bmain, ID_BR, "Draw Soft", "Pencil Soft"));
|
||||
if (brush) {
|
||||
brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PEN;
|
||||
}
|
||||
|
||||
/* Ink Pen brush. */
|
||||
do_versions_rename_id(bmain, ID_BR, "Draw Ink", "Ink Pen");
|
||||
|
||||
/* Ink Pen Rough brush. */
|
||||
do_versions_rename_id(bmain, ID_BR, "Draw Noise", "Ink Pen Rough");
|
||||
|
||||
/* Marker Bold brush. */
|
||||
do_versions_rename_id(bmain, ID_BR, "Draw Marker", "Marker Bold");
|
||||
|
||||
/* Marker Chisel brush. */
|
||||
do_versions_rename_id(bmain, ID_BR, "Draw Block", "Marker Chisel");
|
||||
|
||||
/* Remove useless Fill Area.001 brush. */
|
||||
brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, "Fill Area.001", offsetof(ID, name) + 2));
|
||||
if (brush) {
|
||||
BKE_id_delete(bmain, brush);
|
||||
}
|
||||
|
||||
/* Rename and fix materials and enable default object lights on. */
|
||||
if (app_template && STREQ(app_template, "2D_Animation")) {
|
||||
Material *ma = nullptr;
|
||||
|
@ -538,23 +504,10 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template)
|
|||
}
|
||||
}
|
||||
|
||||
/* Reset all grease pencil brushes. */
|
||||
/* Reset grease pencil paint modes. */
|
||||
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
|
||||
ToolSettings *ts = scene->toolsettings;
|
||||
|
||||
if (ts->gp_paint) {
|
||||
BKE_brush_gpencil_paint_presets(bmain, ts, true);
|
||||
}
|
||||
if (ts->gp_sculptpaint) {
|
||||
BKE_brush_gpencil_sculpt_presets(bmain, ts, true);
|
||||
}
|
||||
if (ts->gp_vertexpaint) {
|
||||
BKE_brush_gpencil_vertex_presets(bmain, ts, true);
|
||||
}
|
||||
if (ts->gp_weightpaint) {
|
||||
BKE_brush_gpencil_weight_presets(bmain, ts, true);
|
||||
}
|
||||
|
||||
/* Ensure new Paint modes. */
|
||||
BKE_paint_ensure_from_paintmode(bmain, scene, PaintMode::VertexGPencil);
|
||||
BKE_paint_ensure_from_paintmode(bmain, scene, PaintMode::SculptGPencil);
|
||||
|
@ -711,203 +664,24 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template)
|
|||
|
||||
/* Brushes */
|
||||
{
|
||||
/* Enable for UV sculpt (other brush types will be created as needed),
|
||||
* without this the grab brush will be active but not selectable from the list. */
|
||||
const char *brush_name = "Grab";
|
||||
Brush *brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
|
||||
if (brush) {
|
||||
brush->ob_mode |= OB_MODE_EDIT;
|
||||
}
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
|
||||
brush->blur_kernel_radius = 2;
|
||||
|
||||
/* Use full strength for all non-sculpt brushes,
|
||||
* when painting we want to use full color/weight always.
|
||||
*
|
||||
* Note that sculpt is an exception,
|
||||
* its values are overwritten by #BKE_brush_sculpt_reset below. */
|
||||
brush->alpha = 1.0;
|
||||
|
||||
/* Enable anti-aliasing by default. */
|
||||
brush->sampling_flag |= BRUSH_PAINT_ANTIALIASING;
|
||||
|
||||
/* By default, each brush should use a single input sample. */
|
||||
brush->input_samples = 1;
|
||||
}
|
||||
|
||||
{
|
||||
/* Change the spacing of the Smear brush to 3.0% */
|
||||
const char *brush_name;
|
||||
Brush *brush;
|
||||
|
||||
brush_name = "Smear";
|
||||
brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
|
||||
if (brush) {
|
||||
brush->spacing = 3.0;
|
||||
}
|
||||
|
||||
brush_name = "Draw Sharp";
|
||||
brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
|
||||
if (!brush) {
|
||||
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
|
||||
id_us_min(&brush->id);
|
||||
brush->sculpt_tool = SCULPT_TOOL_DRAW_SHARP;
|
||||
}
|
||||
|
||||
brush_name = "Elastic Deform";
|
||||
brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
|
||||
if (!brush) {
|
||||
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
|
||||
id_us_min(&brush->id);
|
||||
brush->sculpt_tool = SCULPT_TOOL_ELASTIC_DEFORM;
|
||||
}
|
||||
|
||||
brush_name = "Pose";
|
||||
brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
|
||||
if (!brush) {
|
||||
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
|
||||
id_us_min(&brush->id);
|
||||
brush->sculpt_tool = SCULPT_TOOL_POSE;
|
||||
}
|
||||
|
||||
brush_name = "Multi-plane Scrape";
|
||||
brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
|
||||
if (!brush) {
|
||||
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
|
||||
id_us_min(&brush->id);
|
||||
brush->sculpt_tool = SCULPT_TOOL_MULTIPLANE_SCRAPE;
|
||||
}
|
||||
|
||||
brush_name = "Clay Thumb";
|
||||
brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
|
||||
if (!brush) {
|
||||
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
|
||||
id_us_min(&brush->id);
|
||||
brush->sculpt_tool = SCULPT_TOOL_CLAY_THUMB;
|
||||
}
|
||||
|
||||
brush_name = "Cloth";
|
||||
brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
|
||||
if (!brush) {
|
||||
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
|
||||
id_us_min(&brush->id);
|
||||
brush->sculpt_tool = SCULPT_TOOL_CLOTH;
|
||||
}
|
||||
|
||||
brush_name = "Slide Relax";
|
||||
brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
|
||||
if (!brush) {
|
||||
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
|
||||
id_us_min(&brush->id);
|
||||
brush->sculpt_tool = SCULPT_TOOL_SLIDE_RELAX;
|
||||
}
|
||||
|
||||
brush_name = "Paint";
|
||||
brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
|
||||
if (!brush) {
|
||||
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
|
||||
id_us_min(&brush->id);
|
||||
brush->sculpt_tool = SCULPT_TOOL_PAINT;
|
||||
}
|
||||
|
||||
brush_name = "Smear";
|
||||
brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
|
||||
if (!brush) {
|
||||
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
|
||||
id_us_min(&brush->id);
|
||||
brush->sculpt_tool = SCULPT_TOOL_SMEAR;
|
||||
}
|
||||
|
||||
brush_name = "Boundary";
|
||||
brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
|
||||
if (!brush) {
|
||||
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
|
||||
id_us_min(&brush->id);
|
||||
brush->sculpt_tool = SCULPT_TOOL_BOUNDARY;
|
||||
}
|
||||
|
||||
brush_name = "Simplify";
|
||||
brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
|
||||
if (!brush) {
|
||||
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
|
||||
id_us_min(&brush->id);
|
||||
brush->sculpt_tool = SCULPT_TOOL_SIMPLIFY;
|
||||
}
|
||||
|
||||
brush_name = "Draw Face Sets";
|
||||
brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
|
||||
if (!brush) {
|
||||
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
|
||||
id_us_min(&brush->id);
|
||||
brush->sculpt_tool = SCULPT_TOOL_DRAW_FACE_SETS;
|
||||
}
|
||||
|
||||
brush_name = "Multires Displacement Eraser";
|
||||
brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
|
||||
if (!brush) {
|
||||
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
|
||||
id_us_min(&brush->id);
|
||||
brush->sculpt_tool = SCULPT_TOOL_DISPLACEMENT_ERASER;
|
||||
}
|
||||
|
||||
brush_name = "Multires Displacement Smear";
|
||||
brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
|
||||
if (!brush) {
|
||||
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
|
||||
id_us_min(&brush->id);
|
||||
brush->sculpt_tool = SCULPT_TOOL_DISPLACEMENT_SMEAR;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
|
||||
/* Use the same tool icon color in the brush cursor */
|
||||
if (brush->ob_mode & OB_MODE_SCULPT) {
|
||||
BLI_assert(brush->sculpt_tool != 0);
|
||||
BKE_brush_sculpt_reset(brush);
|
||||
/* Remove default brushes replaced by assets. Also remove outliner `treestore` that may point
|
||||
* to brushes. Normally the treestore is updated properly but it doesn't seem to update during
|
||||
* versioning code. It's not helpful anyway. */
|
||||
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
|
||||
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
|
||||
LISTBASE_FOREACH (SpaceLink *, space_link, &area->spacedata) {
|
||||
if (space_link->spacetype == SPACE_OUTLINER) {
|
||||
SpaceOutliner *space_outliner = reinterpret_cast<SpaceOutliner *>(space_link);
|
||||
if (space_outliner->treestore) {
|
||||
BLI_mempool_destroy(space_outliner->treestore);
|
||||
space_outliner->treestore = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the default texture mapping.
|
||||
* Do it for all brushes, since some of them might be coming from the startup file. */
|
||||
brush->mtex.brush_map_mode = MTEX_MAP_MODE_VIEW;
|
||||
brush->mask_mtex.brush_map_mode = MTEX_MAP_MODE_VIEW;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const Brush *default_brush = DNA_struct_default_get(Brush);
|
||||
LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
|
||||
brush->automasking_start_normal_limit = default_brush->automasking_start_normal_limit;
|
||||
brush->automasking_start_normal_falloff = default_brush->automasking_start_normal_falloff;
|
||||
|
||||
brush->automasking_view_normal_limit = default_brush->automasking_view_normal_limit;
|
||||
brush->automasking_view_normal_falloff = default_brush->automasking_view_normal_falloff;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
|
||||
if (!brush->automasking_cavity_curve) {
|
||||
brush->automasking_cavity_curve = BKE_sculpt_default_cavity_curve();
|
||||
}
|
||||
LISTBASE_FOREACH_MUTABLE (Brush *, brush, &bmain->brushes) {
|
||||
BKE_id_delete(bmain, brush);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -954,6 +954,15 @@ void blo_do_versions_userdef(UserDef *userdef)
|
|||
BKE_preferences_extension_repo_add_default_user(userdef);
|
||||
}
|
||||
|
||||
{
|
||||
BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(
|
||||
userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh Sculpt/Cloth");
|
||||
BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(
|
||||
userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh Sculpt/General");
|
||||
BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(
|
||||
userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh Sculpt/Paint");
|
||||
}
|
||||
|
||||
/**
|
||||
* Always bump subversion in BKE_blender_version.h when adding versioning
|
||||
* code here, and wrap it inside a USER_VERSION_ATLEAST check.
|
||||
|
|
|
@ -23,6 +23,7 @@ struct Main;
|
|||
struct SpaceType;
|
||||
struct uiBlock;
|
||||
struct RegionPollParams;
|
||||
struct wmRegionMessageSubscribeParams;
|
||||
struct wmWindowManager;
|
||||
|
||||
namespace blender {
|
||||
|
@ -52,6 +53,7 @@ void region_init(wmWindowManager *wm, ARegion *region);
|
|||
int region_snap(const ARegion *region, int size, int axis);
|
||||
void region_on_user_resize(const ARegion *region);
|
||||
void region_listen(const wmRegionListenerParams *params);
|
||||
void region_message_subscribe(const wmRegionMessageSubscribeParams *params);
|
||||
void region_layout(const bContext *C, ARegion *region);
|
||||
void region_draw(const bContext *C, ARegion *region);
|
||||
void region_on_poll_success(const bContext *C, ARegion *region);
|
||||
|
|
|
@ -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"
|
||||
|
@ -26,6 +27,7 @@
|
|||
#include "ED_asset_list.hh"
|
||||
#include "ED_screen.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
|
@ -34,6 +36,7 @@
|
|||
#include "UI_view2d.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_message.hh"
|
||||
|
||||
#include "ED_asset_shelf.hh"
|
||||
#include "asset_shelf.hh"
|
||||
|
@ -329,6 +332,20 @@ void region_listen(const wmRegionListenerParams *params)
|
|||
}
|
||||
}
|
||||
|
||||
void region_message_subscribe(const wmRegionMessageSubscribeParams *params)
|
||||
{
|
||||
wmMsgBus *mbus = params->message_bus;
|
||||
WorkSpace *workspace = params->workspace;
|
||||
ARegion *region = params->region;
|
||||
|
||||
wmMsgSubscribeValue msg_sub_value_region_tag_redraw{};
|
||||
msg_sub_value_region_tag_redraw.owner = region;
|
||||
msg_sub_value_region_tag_redraw.user_data = region;
|
||||
msg_sub_value_region_tag_redraw.notify = ED_region_do_msg_notify_tag_redraw;
|
||||
WM_msg_subscribe_rna_prop(
|
||||
mbus, &workspace->id, workspace, WorkSpace, tools, &msg_sub_value_region_tag_redraw);
|
||||
}
|
||||
|
||||
void region_init(wmWindowManager *wm, ARegion *region)
|
||||
{
|
||||
/* Region-data should've been created by a previously called #region_before_redraw(). */
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "AS_asset_library.hh"
|
||||
#include "AS_asset_representation.hh"
|
||||
|
||||
#include "BKE_asset.hh"
|
||||
#include "BKE_screen.hh"
|
||||
|
||||
#include "BLI_fnmatch.h"
|
||||
|
@ -251,9 +252,13 @@ void AssetViewItem::on_activate(bContext & /*C*/)
|
|||
std::optional<bool> AssetViewItem::should_be_active() const
|
||||
{
|
||||
const AssetView &asset_view = dynamic_cast<const AssetView &>(this->get_view());
|
||||
if (!asset_view.active_asset_) {
|
||||
const AssetShelfType &shelf_type = *asset_view.shelf_.type;
|
||||
if (!shelf_type.get_active_asset) {
|
||||
return {};
|
||||
}
|
||||
if (!asset_view.active_asset_) {
|
||||
return false;
|
||||
}
|
||||
const asset_system::AssetRepresentation *asset = handle_get_representation(&asset_);
|
||||
AssetWeakReference weak_ref = asset->make_weak_reference();
|
||||
const bool matches = *asset_view.active_asset_ == weak_ref;
|
||||
|
|
|
@ -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,18 +423,15 @@ static int gpencil_paintmode_toggle_exec(bContext *C, wmOperator *op)
|
|||
BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_paint);
|
||||
BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_vertexpaint);
|
||||
|
||||
BKE_brush_gpencil_paint_presets(bmain, ts, false);
|
||||
|
||||
/* Ensure Palette by default. */
|
||||
BKE_gpencil_palette_ensure(bmain, CTX_data_scene(C));
|
||||
|
||||
Paint *paint = &ts->gp_paint->paint;
|
||||
Brush *brush = BKE_paint_brush(paint);
|
||||
/* if not exist, create a new one */
|
||||
if ((brush == nullptr) || (brush->gpencil_settings == nullptr)) {
|
||||
BKE_brush_gpencil_paint_presets(bmain, ts, true);
|
||||
if (brush && !brush->gpencil_settings) {
|
||||
BKE_brush_init_gpencil_settings(brush);
|
||||
}
|
||||
BKE_paint_brush_validate(bmain, &ts->gp_paint->paint);
|
||||
BKE_paint_brushes_validate(bmain, &ts->gp_paint->paint);
|
||||
}
|
||||
|
||||
if (ob->type == OB_GPENCIL_LEGACY) {
|
||||
|
@ -573,11 +570,7 @@ static int gpencil_sculptmode_toggle_exec(bContext *C, wmOperator *op)
|
|||
if (mode == OB_MODE_SCULPT_GPENCIL_LEGACY) {
|
||||
/* Be sure we have brushes. */
|
||||
BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_sculptpaint);
|
||||
|
||||
const bool reset_mode = (BKE_paint_brush(&ts->gp_sculptpaint->paint) == nullptr);
|
||||
BKE_brush_gpencil_sculpt_presets(bmain, ts, reset_mode);
|
||||
|
||||
BKE_paint_brush_validate(bmain, &ts->gp_sculptpaint->paint);
|
||||
BKE_paint_brushes_validate(bmain, &ts->gp_sculptpaint->paint);
|
||||
}
|
||||
|
||||
/* setup other modes */
|
||||
|
@ -717,10 +710,7 @@ static int gpencil_weightmode_toggle_exec(bContext *C, wmOperator *op)
|
|||
ED_paint_cursor_start(weight_paint, grease_pencil_poll_weight_cursor);
|
||||
}
|
||||
|
||||
const bool reset_mode = (BKE_paint_brush(weight_paint) == nullptr);
|
||||
BKE_brush_gpencil_weight_presets(bmain, ts, reset_mode);
|
||||
|
||||
BKE_paint_brush_validate(bmain, weight_paint);
|
||||
BKE_paint_brushes_validate(bmain, weight_paint);
|
||||
}
|
||||
|
||||
/* setup other modes */
|
||||
|
@ -832,10 +822,7 @@ static int gpencil_vertexmode_toggle_exec(bContext *C, wmOperator *op)
|
|||
BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_paint);
|
||||
BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_vertexpaint);
|
||||
|
||||
const bool reset_mode = (BKE_paint_brush(&ts->gp_vertexpaint->paint) == nullptr);
|
||||
BKE_brush_gpencil_vertex_presets(bmain, ts, reset_mode);
|
||||
|
||||
BKE_paint_brush_validate(bmain, &ts->gp_vertexpaint->paint);
|
||||
BKE_paint_brushes_validate(bmain, &ts->gp_vertexpaint->paint);
|
||||
|
||||
/* Ensure Palette by default. */
|
||||
BKE_gpencil_palette_ensure(bmain, CTX_data_scene(C));
|
||||
|
|
|
@ -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,23 @@ static void gpencil_init_drawing_brush(bContext *C, tGPsdata *p)
|
|||
BKE_curvemapping_init(brush->gpencil_settings->curve_rand_value);
|
||||
|
||||
/* Assign to temp #tGPsdata */
|
||||
p->brush = BKE_paint_brush(paint);
|
||||
if (p->brush->gpencil_tool != GPAINT_TOOL_ERASE) {
|
||||
p->eraser = gpencil_get_default_eraser(p->bmain, ts);
|
||||
p->brush = brush;
|
||||
|
||||
Brush *eraser_brush;
|
||||
if (p->brush->gpencil_tool != GPAINT_TOOL_ERASE &&
|
||||
(eraser_brush = BKE_paint_eraser_brush(paint)))
|
||||
{
|
||||
if (eraser_brush && !eraser_brush->gpencil_settings) {
|
||||
BKE_brush_init_gpencil_settings(eraser_brush);
|
||||
}
|
||||
p->eraser = eraser_brush;
|
||||
}
|
||||
else {
|
||||
p->eraser = p->brush;
|
||||
}
|
||||
/* set new eraser as default */
|
||||
gpencil_set_default_eraser(p->bmain, p->eraser);
|
||||
|
||||
/* use radius of eraser */
|
||||
p->radius = short(p->eraser->size);
|
||||
|
||||
/* Need this update to synchronize brush with draw manager. */
|
||||
if (changed) {
|
||||
DEG_id_tag_update(&scene->id, ID_RECALC_SYNC_TO_EVAL);
|
||||
}
|
||||
}
|
||||
|
||||
/* initialize a paint brush and a default color if not exist */
|
||||
|
@ -2125,6 +2052,10 @@ static bool gpencil_session_initdata(bContext *C, wmOperator *op, tGPsdata *p)
|
|||
|
||||
/* set brush and create a new one if null */
|
||||
gpencil_init_drawing_brush(C, p);
|
||||
if (p->brush == nullptr) {
|
||||
p->status = GP_STATUS_ERROR;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* setup active color */
|
||||
/* region where paint was originated */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -674,6 +674,9 @@ static int grease_pencil_primitive_invoke(bContext *C, wmOperator *op, const wmE
|
|||
|
||||
Paint *paint = &vc.scene->toolsettings->gp_paint->paint;
|
||||
ptd.brush = BKE_paint_brush(paint);
|
||||
if (ptd.brush->gpencil_settings == nullptr) {
|
||||
BKE_brush_init_gpencil_settings(ptd.brush);
|
||||
}
|
||||
ptd.settings = ptd.brush->gpencil_settings;
|
||||
|
||||
BKE_curvemapping_init(ptd.settings->curve_sensitivity);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -930,6 +930,8 @@ uiPopupBlockHandle *ui_popup_block_create(bContext *C,
|
|||
handle->can_refresh = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* keep centered on window resizing */
|
||||
if (block->bounds_type == UI_BLOCK_BOUNDS_POPUP_CENTER) {
|
||||
type.listener = ui_block_region_popup_window_listener;
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -1382,7 +1382,7 @@ static void template_ID(const bContext *C,
|
|||
|
||||
template_id_workspace_pin_extra_icon(template_ui, but);
|
||||
|
||||
if (!hide_buttons) {
|
||||
if (!hide_buttons && !(idfrom && ID_IS_LINKED(idfrom))) {
|
||||
if (ID_IS_LINKED(id)) {
|
||||
const bool disabled = !BKE_idtype_idcode_is_localizable(GS(id->name));
|
||||
if (id->tag & LIB_TAG_INDIRECT) {
|
||||
|
@ -6419,13 +6419,47 @@ void uiTemplateInputStatus(uiLayout *layout, bContext *C)
|
|||
}
|
||||
}
|
||||
|
||||
static void ui_template_status_info_warnings_messages(Main *bmain,
|
||||
Scene *scene,
|
||||
ViewLayer *view_layer,
|
||||
std::string &warning_message,
|
||||
std::string ®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;
|
||||
|
@ -6434,13 +6468,13 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C)
|
|||
/* Blender version part is shown as warning area when there are forward compatibility issues with
|
||||
* currently loaded .blend file. */
|
||||
|
||||
const char *status_info_txt = ED_info_statusbar_string_ex(
|
||||
bmain, scene, view_layer, (U.statusbar_flag & ~STATUSBAR_SHOW_VERSION));
|
||||
uiItemL(layout, status_info_txt, ICON_NONE);
|
||||
std::string warning_message;
|
||||
std::string regular_message;
|
||||
std::string tooltip_message;
|
||||
ui_template_status_info_warnings_messages(
|
||||
bmain, scene, view_layer, warning_message, regular_message, tooltip_message);
|
||||
|
||||
status_info_txt = ED_info_statusbar_string_ex(bmain, scene, view_layer, STATUSBAR_SHOW_VERSION);
|
||||
|
||||
uiBut *but;
|
||||
uiItemL(layout, regular_message.c_str(), ICON_NONE);
|
||||
|
||||
const uiStyle *style = UI_style_get();
|
||||
uiLayout *ui_abs = uiLayoutAbsolute(layout, false);
|
||||
|
@ -6448,25 +6482,26 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C)
|
|||
eUIEmbossType previous_emboss = UI_block_emboss_get(block);
|
||||
|
||||
UI_fontstyle_set(&style->widgetlabel);
|
||||
int width = int(
|
||||
BLF_width(style->widgetlabel.uifont_id, status_info_txt, strlen(status_info_txt)));
|
||||
width = max_ii(width, int(10 * UI_SCALE_FAC));
|
||||
const int width = max_ii(int(BLF_width(style->widgetlabel.uifont_id,
|
||||
warning_message.c_str(),
|
||||
warning_message.length())),
|
||||
int(10 * UI_SCALE_FAC));
|
||||
|
||||
UI_block_align_begin(block);
|
||||
|
||||
/* Background for icon. */
|
||||
but = uiDefBut(block,
|
||||
UI_BTYPE_ROUNDBOX,
|
||||
0,
|
||||
"",
|
||||
0,
|
||||
0,
|
||||
UI_UNIT_X + (6 * UI_SCALE_FAC),
|
||||
UI_UNIT_Y,
|
||||
nullptr,
|
||||
0.0f,
|
||||
0.0f,
|
||||
"");
|
||||
uiBut *but = uiDefBut(block,
|
||||
UI_BTYPE_ROUNDBOX,
|
||||
0,
|
||||
"",
|
||||
0,
|
||||
0,
|
||||
UI_UNIT_X + (6 * UI_SCALE_FAC),
|
||||
UI_UNIT_Y,
|
||||
nullptr,
|
||||
0.0f,
|
||||
0.0f,
|
||||
"");
|
||||
/* UI_BTYPE_ROUNDBOX's bg color is set in but->col. */
|
||||
UI_GetThemeColorType4ubv(TH_INFO_WARNING, SPACE_INFO, but->col);
|
||||
|
||||
|
@ -6491,14 +6526,13 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C)
|
|||
UI_block_align_end(block);
|
||||
UI_block_emboss_set(block, UI_EMBOSS_NONE);
|
||||
|
||||
/* The report icon itself. */
|
||||
static char compat_error_msg[256];
|
||||
char writer_ver_str[12];
|
||||
BKE_blender_version_blendfile_string_from_values(
|
||||
writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, -1);
|
||||
SNPRINTF(compat_error_msg,
|
||||
RPT_("File saved by newer Blender\n(%s), expect loss of data"),
|
||||
writer_ver_str);
|
||||
/* Tool tips have to be static currently.
|
||||
* FIXME This is a horrible requirement from uiBut, should probably just store an std::string for
|
||||
* the tooltip as well? */
|
||||
static char tooltip_static_storage[256];
|
||||
BLI_strncpy(tooltip_static_storage, tooltip_message.c_str(), sizeof(tooltip_static_storage));
|
||||
|
||||
/* The warning icon itself. */
|
||||
but = uiDefIconBut(block,
|
||||
UI_BTYPE_BUT,
|
||||
0,
|
||||
|
@ -6510,23 +6544,25 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C)
|
|||
nullptr,
|
||||
0.0f,
|
||||
0.0f,
|
||||
compat_error_msg);
|
||||
tooltip_static_storage);
|
||||
UI_GetThemeColorType4ubv(TH_INFO_WARNING_TEXT, SPACE_INFO, but->col);
|
||||
but->col[3] = 255; /* This theme color is RBG only, so have to set alpha here. */
|
||||
|
||||
/* The report message. */
|
||||
but = uiDefBut(block,
|
||||
UI_BTYPE_BUT,
|
||||
0,
|
||||
status_info_txt,
|
||||
UI_UNIT_X,
|
||||
0,
|
||||
short(width + UI_UNIT_X),
|
||||
UI_UNIT_Y,
|
||||
nullptr,
|
||||
0.0f,
|
||||
0.0f,
|
||||
compat_error_msg);
|
||||
/* The warning message, if any. */
|
||||
if (!warning_message.empty()) {
|
||||
but = uiDefBut(block,
|
||||
UI_BTYPE_BUT,
|
||||
0,
|
||||
warning_message.c_str(),
|
||||
UI_UNIT_X,
|
||||
0,
|
||||
short(width + UI_UNIT_X),
|
||||
UI_UNIT_Y,
|
||||
nullptr,
|
||||
0.0f,
|
||||
0.0f,
|
||||
tooltip_static_storage);
|
||||
}
|
||||
|
||||
UI_block_emboss_set(block, previous_emboss);
|
||||
}
|
||||
|
|
|
@ -789,6 +789,10 @@ static int new_material_exec(bContext *C, wmOperator * /*op*/)
|
|||
* pointer use also increases user, so this compensates it */
|
||||
id_us_min(&ma->id);
|
||||
|
||||
if (ptr.owner_id) {
|
||||
BKE_id_move_to_same_lib(*bmain, ma->id, *ptr.owner_id);
|
||||
}
|
||||
|
||||
PointerRNA idptr = RNA_id_pointer_create(&ma->id);
|
||||
RNA_property_pointer_set(&ptr, prop, idptr, nullptr);
|
||||
RNA_property_update(C, &ptr, prop);
|
||||
|
@ -843,6 +847,10 @@ static int new_texture_exec(bContext *C, wmOperator * /*op*/)
|
|||
* pointer use also increases user, so this compensates it */
|
||||
id_us_min(&tex->id);
|
||||
|
||||
if (ptr.owner_id) {
|
||||
BKE_id_move_to_same_lib(*bmain, tex->id, *ptr.owner_id);
|
||||
}
|
||||
|
||||
PointerRNA idptr = RNA_id_pointer_create(&tex->id);
|
||||
RNA_property_pointer_set(&ptr, prop, idptr, nullptr);
|
||||
RNA_property_update(C, &ptr, prop);
|
||||
|
@ -900,6 +908,10 @@ static int new_world_exec(bContext *C, wmOperator * /*op*/)
|
|||
* pointer use also increases user, so this compensates it */
|
||||
id_us_min(&wo->id);
|
||||
|
||||
if (ptr.owner_id) {
|
||||
BKE_id_move_to_same_lib(*bmain, wo->id, *ptr.owner_id);
|
||||
}
|
||||
|
||||
PointerRNA idptr = RNA_id_pointer_create(&wo->id);
|
||||
RNA_property_pointer_set(&ptr, prop, idptr, nullptr);
|
||||
RNA_property_update(C, &ptr, prop);
|
||||
|
|
|
@ -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,792 @@
|
|||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BLI_fileops.h"
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "DNA_brush_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_space_types.h"
|
||||
|
||||
#include "BKE_asset.hh"
|
||||
#include "BKE_asset_edit.hh"
|
||||
#include "BKE_blendfile.hh"
|
||||
#include "BKE_brush.hh"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_paint.hh"
|
||||
#include "BKE_preferences.h"
|
||||
#include "BKE_preview_image.hh"
|
||||
#include "BKE_report.hh"
|
||||
|
||||
#include "AS_asset_catalog_path.hh"
|
||||
#include "AS_asset_catalog_tree.hh"
|
||||
#include "AS_asset_library.hh"
|
||||
#include "AS_asset_representation.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_define.hh"
|
||||
|
||||
#include "ED_asset_handle.hh"
|
||||
#include "ED_asset_library.hh"
|
||||
#include "ED_asset_list.hh"
|
||||
#include "ED_asset_mark_clear.hh"
|
||||
#include "ED_asset_menu_utils.hh"
|
||||
#include "ED_asset_shelf.hh"
|
||||
|
||||
#include "UI_interface_icons.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_toolsystem.hh"
|
||||
|
||||
#include "paint_intern.hh"
|
||||
|
||||
namespace blender::ed::sculpt_paint {
|
||||
|
||||
static int brush_asset_select_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
/* This operator currently covers both cases: the file/asset browser file list and the asset list
|
||||
* used for the asset-view template. Once the asset list design is used by the Asset Browser,
|
||||
* this can be simplified to just that case. */
|
||||
Main *bmain = CTX_data_main(C);
|
||||
const asset_system::AssetRepresentation *asset =
|
||||
asset::operator_asset_reference_props_get_asset_from_all_library(*C, *op->ptr, op->reports);
|
||||
if (!asset) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
AssetWeakReference brush_asset_reference = asset->make_weak_reference();
|
||||
Brush *brush = reinterpret_cast<Brush *>(
|
||||
bke::asset_edit_id_from_weak_reference(*bmain, ID_BR, brush_asset_reference));
|
||||
|
||||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
|
||||
if (!BKE_paint_brush_set(paint, brush)) {
|
||||
/* Note brush datablock was still added, so was not a no-op. */
|
||||
BKE_report(op->reports, RPT_WARNING, "Unable to select brush, wrong object mode");
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
WM_main_add_notifier(NC_ASSET | NA_ACTIVATED, nullptr);
|
||||
WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, nullptr);
|
||||
WM_toolsystem_ref_set_by_id(C, "builtin.brush");
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void BRUSH_OT_asset_select(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Select Brush Asset";
|
||||
ot->description = "Select a brush asset as current sculpt and paint tool";
|
||||
ot->idname = "BRUSH_OT_asset_select";
|
||||
|
||||
ot->exec = brush_asset_select_exec;
|
||||
|
||||
asset::operator_asset_reference_props_register(*ot->srna);
|
||||
}
|
||||
|
||||
static std::optional<AssetLibraryReference> library_to_library_ref(
|
||||
const asset_system::AssetLibrary &library)
|
||||
{
|
||||
for (const AssetLibraryReference &ref : asset_system::all_valid_asset_library_refs()) {
|
||||
const std::string root_path = AS_asset_library_root_path_from_library_ref(ref);
|
||||
/* Use #BLI_path_cmp_normalized because `library.root_path()` ends with a slash while
|
||||
* `root_path` doesn't. */
|
||||
if (BLI_path_cmp_normalized(root_path.c_str(), library.root_path().c_str()) == 0) {
|
||||
return ref;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
static AssetLibraryReference user_library_to_library_ref(const bUserAssetLibrary &user_library)
|
||||
{
|
||||
AssetLibraryReference library_ref{};
|
||||
library_ref.custom_library_index = BLI_findindex(&U.asset_libraries, &user_library);
|
||||
library_ref.type = ASSET_LIBRARY_CUSTOM;
|
||||
return library_ref;
|
||||
}
|
||||
|
||||
static const bUserAssetLibrary *library_ref_to_user_library(
|
||||
const AssetLibraryReference &library_ref)
|
||||
{
|
||||
if (library_ref.type != ASSET_LIBRARY_CUSTOM) {
|
||||
return nullptr;
|
||||
}
|
||||
return static_cast<const bUserAssetLibrary *>(
|
||||
BLI_findlink(&U.asset_libraries, library_ref.custom_library_index));
|
||||
}
|
||||
|
||||
static void refresh_asset_library(const bContext *C, const AssetLibraryReference &library_ref)
|
||||
{
|
||||
asset::list::clear(&library_ref, C);
|
||||
/* TODO: Should the all library reference be automatically cleared? */
|
||||
AssetLibraryReference all_lib_ref = asset_system::all_library_reference();
|
||||
asset::list::clear(&all_lib_ref, C);
|
||||
}
|
||||
|
||||
static void refresh_asset_library(const bContext *C, const bUserAssetLibrary &user_library)
|
||||
{
|
||||
refresh_asset_library(C, user_library_to_library_ref(user_library));
|
||||
}
|
||||
|
||||
static bool brush_asset_save_as_poll(bContext *C)
|
||||
{
|
||||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr;
|
||||
if (paint == nullptr || brush == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (!paint->brush_asset_reference) {
|
||||
/* The brush should always be an imported asset. We use this asset reference to find
|
||||
* which library and catalog the brush came from, as defaults for the popup. */
|
||||
return false;
|
||||
}
|
||||
|
||||
if (BLI_listbase_is_empty(&U.asset_libraries)) {
|
||||
CTX_wm_operator_poll_msg_set(C, "No asset library available to save to");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const bUserAssetLibrary *get_asset_library_from_prop(PointerRNA &ptr)
|
||||
{
|
||||
const int enum_value = RNA_enum_get(&ptr, "asset_library_reference");
|
||||
const AssetLibraryReference lib_ref = asset::library_reference_from_enum_value(enum_value);
|
||||
return BKE_preferences_asset_library_find_index(&U, lib_ref.custom_library_index);
|
||||
}
|
||||
|
||||
static asset_system::AssetCatalog &asset_library_ensure_catalog(
|
||||
asset_system::AssetLibrary &library, const asset_system::AssetCatalogPath &path)
|
||||
{
|
||||
if (asset_system::AssetCatalog *catalog = library.catalog_service().find_catalog_by_path(path)) {
|
||||
return *catalog;
|
||||
}
|
||||
return *library.catalog_service().create_catalog(path);
|
||||
}
|
||||
|
||||
static asset_system::AssetCatalog &asset_library_ensure_catalogs_in_path(
|
||||
asset_system::AssetLibrary &library, const asset_system::AssetCatalogPath &path)
|
||||
{
|
||||
/* Adding multiple catalogs in a path at a time with #AssetCatalogService::create_catalog()
|
||||
* doesn't work; add each potentially new catalog in the hierarchy manually here. */
|
||||
asset_system::AssetCatalogPath parent = "";
|
||||
path.iterate_components([&](StringRef component_name, bool /*is_last_component*/) {
|
||||
asset_library_ensure_catalog(library, parent / component_name);
|
||||
parent = parent / component_name;
|
||||
});
|
||||
return *library.catalog_service().find_catalog_by_path(path);
|
||||
}
|
||||
|
||||
static void show_catalog_in_asset_shelf(const bContext &C, const StringRefNull catalog_path)
|
||||
{
|
||||
/* Enable catalog in all visible asset shelves. */
|
||||
wmWindowManager *wm = CTX_wm_manager(&C);
|
||||
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
|
||||
const bScreen *screen = WM_window_get_active_screen(win);
|
||||
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
|
||||
const AssetShelf *shelf = asset::shelf::active_shelf_from_area(area);
|
||||
if (shelf && BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(
|
||||
&U, shelf->idname, catalog_path.c_str()))
|
||||
{
|
||||
U.runtime.is_dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int brush_asset_save_as_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr;
|
||||
|
||||
/* Determine file path to save to. */
|
||||
PropertyRNA *name_prop = RNA_struct_find_property(op->ptr, "name");
|
||||
char name[MAX_NAME] = "";
|
||||
if (RNA_property_is_set(op->ptr, name_prop)) {
|
||||
RNA_property_string_get(op->ptr, name_prop, name);
|
||||
}
|
||||
if (name[0] == '\0') {
|
||||
STRNCPY(name, brush->id.name + 2);
|
||||
}
|
||||
|
||||
const bUserAssetLibrary *user_library = get_asset_library_from_prop(*op->ptr);
|
||||
if (!user_library) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
asset_system::AssetLibrary *library = AS_asset_library_load(
|
||||
bmain, user_library_to_library_ref(*user_library));
|
||||
if (!library) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Failed to load asset library");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* Turn brush into asset if it isn't yet. */
|
||||
if (!ID_IS_ASSET(&brush->id)) {
|
||||
asset::mark_id(&brush->id);
|
||||
asset::generate_preview(C, &brush->id);
|
||||
}
|
||||
BLI_assert(ID_IS_ASSET(&brush->id));
|
||||
|
||||
/* Add asset to catalog. */
|
||||
char catalog_path[MAX_NAME];
|
||||
RNA_string_get(op->ptr, "catalog_path", catalog_path);
|
||||
|
||||
AssetMetaData &meta_data = *brush->id.asset_data;
|
||||
if (catalog_path[0]) {
|
||||
const asset_system::AssetCatalog &catalog = asset_library_ensure_catalogs_in_path(
|
||||
*library, catalog_path);
|
||||
BKE_asset_metadata_catalog_id_set(&meta_data, catalog.catalog_id, catalog.simple_name.c_str());
|
||||
}
|
||||
|
||||
AssetWeakReference brush_asset_reference;
|
||||
const std::optional<std::string> final_full_asset_filepath = bke::asset_edit_id_save_as(
|
||||
*bmain, brush->id, name, *user_library, brush_asset_reference, *op->reports);
|
||||
if (!final_full_asset_filepath) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
library->catalog_service().write_to_disk(*final_full_asset_filepath);
|
||||
show_catalog_in_asset_shelf(*C, catalog_path);
|
||||
|
||||
brush = reinterpret_cast<Brush *>(
|
||||
bke::asset_edit_id_from_weak_reference(*bmain, ID_BR, brush_asset_reference));
|
||||
|
||||
if (!BKE_paint_brush_set(paint, brush)) {
|
||||
/* Note brush sset was still saved in editable asset library, so was not a no-op. */
|
||||
BKE_report(op->reports, RPT_WARNING, "Unable to activate just-saved brush asset");
|
||||
}
|
||||
|
||||
refresh_asset_library(C, *user_library);
|
||||
WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_ADDED, nullptr);
|
||||
WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static bool library_is_editable(const AssetLibraryReference &library)
|
||||
{
|
||||
if (library.type == ASSET_LIBRARY_ESSENTIALS) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int brush_asset_save_as_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
|
||||
{
|
||||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
const AssetWeakReference &brush_weak_ref = *paint->brush_asset_reference;
|
||||
const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref(
|
||||
*C, brush_weak_ref, op->reports);
|
||||
if (!asset) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
const asset_system::AssetLibrary &library = asset->owner_asset_library();
|
||||
const std::optional<AssetLibraryReference> library_ref = library_to_library_ref(library);
|
||||
if (!library_ref) {
|
||||
BLI_assert_unreachable();
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
RNA_string_set(op->ptr, "name", asset->get_name().c_str());
|
||||
|
||||
/* If the library isn't saved from the operator's last execution, find the current library or the
|
||||
* first library if the current library isn't editable. */
|
||||
if (!RNA_struct_property_is_set_ex(op->ptr, "asset_library_reference", false)) {
|
||||
if (library_is_editable(*library_ref)) {
|
||||
RNA_enum_set(op->ptr,
|
||||
"asset_library_reference",
|
||||
asset::library_reference_to_enum_value(&*library_ref));
|
||||
}
|
||||
else {
|
||||
const AssetLibraryReference first_library = user_library_to_library_ref(
|
||||
*static_cast<const bUserAssetLibrary *>(U.asset_libraries.first));
|
||||
RNA_enum_set(op->ptr,
|
||||
"asset_library_reference",
|
||||
asset::library_reference_to_enum_value(&first_library));
|
||||
}
|
||||
}
|
||||
|
||||
/* By default, put the new asset in the same catalog as the existing asset. */
|
||||
if (!RNA_struct_property_is_set(op->ptr, "catalog_path")) {
|
||||
const asset_system::CatalogID &id = asset->get_metadata().catalog_id;
|
||||
if (const asset_system::AssetCatalog *catalog = library.catalog_service().find_catalog(id)) {
|
||||
RNA_string_set(op->ptr, "catalog_path", catalog->path.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return WM_operator_props_dialog_popup(C, op, 400, std::nullopt, IFACE_("Save"));
|
||||
}
|
||||
|
||||
static const EnumPropertyItem *rna_asset_library_reference_itemf(bContext * /*C*/,
|
||||
PointerRNA * /*ptr*/,
|
||||
PropertyRNA * /*prop*/,
|
||||
bool *r_free)
|
||||
{
|
||||
const EnumPropertyItem *items = asset::library_reference_to_rna_enum_itemf(false);
|
||||
if (!items) {
|
||||
*r_free = false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
*r_free = true;
|
||||
return items;
|
||||
}
|
||||
|
||||
static void visit_library_catalogs_catalog_for_search(
|
||||
const Main &bmain,
|
||||
const bUserAssetLibrary &user_library,
|
||||
const StringRef edit_text,
|
||||
const FunctionRef<void(StringPropertySearchVisitParams)> visit_fn)
|
||||
{
|
||||
const asset_system::AssetLibrary *library = AS_asset_library_load(
|
||||
&bmain, user_library_to_library_ref(user_library));
|
||||
if (!library) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!edit_text.is_empty()) {
|
||||
const asset_system::AssetCatalogPath edit_path = edit_text;
|
||||
if (!library->catalog_service().find_catalog_by_path(edit_path)) {
|
||||
visit_fn(StringPropertySearchVisitParams{edit_path.str(), std::nullopt, ICON_ADD});
|
||||
}
|
||||
}
|
||||
|
||||
const asset_system::AssetCatalogTree &full_tree = library->catalog_service().catalog_tree();
|
||||
full_tree.foreach_item([&](const asset_system::AssetCatalogTreeItem &item) {
|
||||
visit_fn(StringPropertySearchVisitParams{item.catalog_path().str(), std::nullopt});
|
||||
});
|
||||
}
|
||||
|
||||
static void visit_library_prop_catalogs_catalog_for_search_fn(
|
||||
const bContext *C,
|
||||
PointerRNA *ptr,
|
||||
PropertyRNA * /*prop*/,
|
||||
const char *edit_text,
|
||||
FunctionRef<void(StringPropertySearchVisitParams)> visit_fn)
|
||||
{
|
||||
/* NOTE: Using the all library would also be a valid choice. */
|
||||
if (const bUserAssetLibrary *user_library = get_asset_library_from_prop(*ptr)) {
|
||||
visit_library_catalogs_catalog_for_search(
|
||||
*CTX_data_main(C), *user_library, edit_text, visit_fn);
|
||||
}
|
||||
}
|
||||
|
||||
void BRUSH_OT_asset_save_as(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Save as Brush Asset";
|
||||
ot->description =
|
||||
"Save a copy of the active brush asset into the default asset library, and make it the "
|
||||
"active brush";
|
||||
ot->idname = "BRUSH_OT_asset_save_as";
|
||||
|
||||
ot->exec = brush_asset_save_as_exec;
|
||||
ot->invoke = brush_asset_save_as_invoke;
|
||||
ot->poll = brush_asset_save_as_poll;
|
||||
|
||||
ot->prop = RNA_def_string(
|
||||
ot->srna, "name", nullptr, MAX_NAME, "Name", "Name for the new brush asset");
|
||||
|
||||
PropertyRNA *prop = RNA_def_property(ot->srna, "asset_library_reference", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_enum_funcs(prop, rna_asset_library_reference_itemf);
|
||||
RNA_def_property_ui_text(prop, "Library", "Asset library used to store the new brush");
|
||||
|
||||
prop = RNA_def_string(
|
||||
ot->srna, "catalog_path", nullptr, MAX_NAME, "Catalog", "Catalog to use for the new asset");
|
||||
RNA_def_property_string_search_func_runtime(
|
||||
prop, visit_library_prop_catalogs_catalog_for_search_fn, PROP_STRING_SEARCH_SUGGESTION);
|
||||
}
|
||||
|
||||
static int brush_asset_edit_metadata_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
Brush *brush = BKE_paint_brush(paint);
|
||||
BLI_assert(ID_IS_ASSET(&brush->id));
|
||||
const AssetWeakReference &brush_weak_ref = *paint->brush_asset_reference;
|
||||
const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref(
|
||||
*C, brush_weak_ref, op->reports);
|
||||
if (!asset) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
const asset_system::AssetLibrary &library_const = asset->owner_asset_library();
|
||||
const AssetLibraryReference library_ref = *library_to_library_ref(library_const);
|
||||
asset_system::AssetLibrary *library = AS_asset_library_load(bmain, library_ref);
|
||||
|
||||
char catalog_path[MAX_NAME];
|
||||
RNA_string_get(op->ptr, "catalog_path", catalog_path);
|
||||
|
||||
AssetMetaData &meta_data = *brush->id.asset_data;
|
||||
MEM_SAFE_FREE(meta_data.author);
|
||||
meta_data.author = RNA_string_get_alloc(op->ptr, "author", nullptr, 0, nullptr);
|
||||
MEM_SAFE_FREE(meta_data.description);
|
||||
meta_data.description = RNA_string_get_alloc(op->ptr, "description", nullptr, 0, nullptr);
|
||||
|
||||
if (catalog_path[0]) {
|
||||
const asset_system::AssetCatalog &catalog = asset_library_ensure_catalogs_in_path(
|
||||
*library, catalog_path);
|
||||
BKE_asset_metadata_catalog_id_set(&meta_data, catalog.catalog_id, catalog.simple_name.c_str());
|
||||
}
|
||||
|
||||
if (!bke::asset_edit_id_save(*bmain, brush->id, *op->reports)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
char asset_full_path_buffer[FILE_MAX_LIBEXTRA];
|
||||
char *file_path = nullptr;
|
||||
AS_asset_full_path_explode_from_weak_ref(
|
||||
&brush_weak_ref, asset_full_path_buffer, &file_path, nullptr, nullptr);
|
||||
if (!file_path) {
|
||||
BLI_assert_unreachable();
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
library->catalog_service().write_to_disk(file_path);
|
||||
|
||||
refresh_asset_library(C, library_ref);
|
||||
WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_EDITED, nullptr);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static int brush_asset_edit_metadata_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
|
||||
{
|
||||
const Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
const AssetWeakReference &brush_weak_ref = *paint->brush_asset_reference;
|
||||
const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref(
|
||||
*C, brush_weak_ref, op->reports);
|
||||
if (!asset) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
const asset_system::AssetLibrary &library = asset->owner_asset_library();
|
||||
const AssetMetaData &meta_data = asset->get_metadata();
|
||||
|
||||
if (!RNA_struct_property_is_set(op->ptr, "catalog_path")) {
|
||||
const asset_system::CatalogID &id = meta_data.catalog_id;
|
||||
if (const asset_system::AssetCatalog *catalog = library.catalog_service().find_catalog(id)) {
|
||||
RNA_string_set(op->ptr, "catalog_path", catalog->path.c_str());
|
||||
}
|
||||
}
|
||||
if (!RNA_struct_property_is_set(op->ptr, "author")) {
|
||||
RNA_string_set(op->ptr, "author", meta_data.author ? meta_data.author : "");
|
||||
}
|
||||
if (!RNA_struct_property_is_set(op->ptr, "description")) {
|
||||
RNA_string_set(op->ptr, "description", meta_data.description ? meta_data.description : "");
|
||||
}
|
||||
|
||||
return WM_operator_props_dialog_popup(C, op, 400, std::nullopt, IFACE_("Edit Metadata"));
|
||||
}
|
||||
|
||||
static void visit_active_library_catalogs_catalog_for_search_fn(
|
||||
const bContext *C,
|
||||
PointerRNA * /*ptr*/,
|
||||
PropertyRNA * /*prop*/,
|
||||
const char *edit_text,
|
||||
FunctionRef<void(StringPropertySearchVisitParams)> visit_fn)
|
||||
{
|
||||
const Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
const AssetWeakReference &brush_weak_ref = *paint->brush_asset_reference;
|
||||
const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref(
|
||||
*C, brush_weak_ref, nullptr);
|
||||
if (!asset) {
|
||||
return;
|
||||
}
|
||||
const asset_system::AssetLibrary &library = asset->owner_asset_library();
|
||||
|
||||
/* NOTE: Using the all library would also be a valid choice. */
|
||||
visit_library_catalogs_catalog_for_search(
|
||||
*CTX_data_main(C),
|
||||
*library_ref_to_user_library(*library_to_library_ref(library)),
|
||||
edit_text,
|
||||
visit_fn);
|
||||
}
|
||||
|
||||
static bool brush_asset_edit_metadata_poll(bContext *C)
|
||||
{
|
||||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr;
|
||||
if (paint == nullptr || brush == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (!ID_IS_ASSET(&brush->id)) {
|
||||
BLI_assert_unreachable();
|
||||
return false;
|
||||
}
|
||||
const AssetWeakReference *brush_weak_ref = paint->brush_asset_reference;
|
||||
if (!brush_weak_ref) {
|
||||
BLI_assert_unreachable();
|
||||
return false;
|
||||
}
|
||||
const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref(
|
||||
*C, *brush_weak_ref, nullptr);
|
||||
if (!asset) {
|
||||
BLI_assert_unreachable();
|
||||
return false;
|
||||
}
|
||||
const std::optional<AssetLibraryReference> library_ref = library_to_library_ref(
|
||||
asset->owner_asset_library());
|
||||
if (!library_ref) {
|
||||
BLI_assert_unreachable();
|
||||
return false;
|
||||
}
|
||||
if (!library_is_editable(*library_ref)) {
|
||||
CTX_wm_operator_poll_msg_set(C, "Asset library is not editable");
|
||||
return false;
|
||||
}
|
||||
if (!bke::asset_edit_id_is_writable(brush->id)) {
|
||||
CTX_wm_operator_poll_msg_set(C, "Asset file is not editable");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void BRUSH_OT_asset_edit_metadata(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Edit Metadata";
|
||||
ot->description = "Edit asset information like the catalog, preview image, tags, or author";
|
||||
ot->idname = "BRUSH_OT_asset_edit_metadata";
|
||||
|
||||
ot->exec = brush_asset_edit_metadata_exec;
|
||||
ot->invoke = brush_asset_edit_metadata_invoke;
|
||||
ot->poll = brush_asset_edit_metadata_poll;
|
||||
|
||||
PropertyRNA *prop = RNA_def_string(
|
||||
ot->srna, "catalog_path", nullptr, MAX_NAME, "Catalog", "The asset's catalog path");
|
||||
RNA_def_property_string_search_func_runtime(
|
||||
prop, visit_active_library_catalogs_catalog_for_search_fn, PROP_STRING_SEARCH_SUGGESTION);
|
||||
RNA_def_string(ot->srna, "author", nullptr, MAX_NAME, "Author", "");
|
||||
RNA_def_string(ot->srna, "description", nullptr, MAX_NAME, "Description", "");
|
||||
}
|
||||
|
||||
static int brush_asset_load_preview_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
Brush *brush = BKE_paint_brush(paint);
|
||||
BLI_assert(ID_IS_ASSET(&brush->id));
|
||||
const AssetWeakReference &brush_weak_ref = *paint->brush_asset_reference;
|
||||
const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref(
|
||||
*C, brush_weak_ref, op->reports);
|
||||
if (!asset) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
const AssetLibraryReference library_ref = *library_to_library_ref(asset->owner_asset_library());
|
||||
|
||||
char filepath[FILE_MAX];
|
||||
RNA_string_get(op->ptr, "filepath", filepath);
|
||||
if (!BLI_is_file(filepath)) {
|
||||
BKE_reportf(op->reports, RPT_ERROR, "File not found '%s'", filepath);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
BKE_previewimg_id_custom_set(&brush->id, filepath);
|
||||
|
||||
if (!bke::asset_edit_id_save(*bmain, brush->id, *op->reports)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
refresh_asset_library(C, library_ref);
|
||||
WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_EDITED, nullptr);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static int brush_asset_load_preview_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
if (RNA_struct_property_is_set(op->ptr, "filepath")) {
|
||||
return brush_asset_load_preview_exec(C, op);
|
||||
}
|
||||
return WM_operator_filesel(C, op, event);
|
||||
}
|
||||
|
||||
void BRUSH_OT_asset_load_preview(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Load Preview Image";
|
||||
ot->description = "Choose a preview image for the brush";
|
||||
ot->idname = "BRUSH_OT_asset_load_preview";
|
||||
|
||||
ot->exec = brush_asset_load_preview_exec;
|
||||
ot->invoke = brush_asset_load_preview_invoke;
|
||||
ot->poll = brush_asset_edit_metadata_poll;
|
||||
|
||||
WM_operator_properties_filesel(ot,
|
||||
FILE_TYPE_FOLDER | FILE_TYPE_IMAGE,
|
||||
FILE_SPECIAL,
|
||||
FILE_OPENFILE,
|
||||
WM_FILESEL_FILEPATH,
|
||||
FILE_DEFAULTDISPLAY,
|
||||
FILE_SORT_DEFAULT);
|
||||
}
|
||||
|
||||
static bool brush_asset_delete_poll(bContext *C)
|
||||
{
|
||||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr;
|
||||
if (paint == nullptr || brush == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (!paint->brush_asset_reference) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Asset brush, check if belongs to an editable blend file. */
|
||||
if (ID_IS_ASSET(brush)) {
|
||||
if (!bke::asset_edit_id_is_writable(brush->id)) {
|
||||
CTX_wm_operator_poll_msg_set(C, "Asset blend file is not editable");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int brush_asset_delete_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
Brush *brush = BKE_paint_brush(paint);
|
||||
Main *bmain = CTX_data_main(C);
|
||||
|
||||
bUserAssetLibrary *library = BKE_preferences_asset_library_find_by_name(
|
||||
&U, paint->brush_asset_reference->asset_library_identifier);
|
||||
if (!library) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
bke::asset_edit_id_delete(*bmain, brush->id, *op->reports);
|
||||
|
||||
refresh_asset_library(C, *library);
|
||||
|
||||
BKE_paint_brush_set_default(bmain, paint);
|
||||
|
||||
WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_REMOVED, nullptr);
|
||||
WM_main_add_notifier(NC_BRUSH | NA_EDITED, nullptr);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static int brush_asset_delete_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
|
||||
{
|
||||
return WM_operator_confirm_ex(
|
||||
C,
|
||||
op,
|
||||
IFACE_("Delete Brush Asset"),
|
||||
IFACE_("Permanently delete brush asset blend file. This can't be undone."),
|
||||
IFACE_("Delete"),
|
||||
ALERT_ICON_WARNING,
|
||||
false);
|
||||
}
|
||||
|
||||
void BRUSH_OT_asset_delete(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Delete Brush Asset";
|
||||
ot->description = "Delete the active brush asset both from the local session and asset library";
|
||||
ot->idname = "BRUSH_OT_asset_delete";
|
||||
|
||||
ot->exec = brush_asset_delete_exec;
|
||||
ot->invoke = brush_asset_delete_invoke;
|
||||
ot->poll = brush_asset_delete_poll;
|
||||
}
|
||||
|
||||
static bool brush_asset_update_poll(bContext *C)
|
||||
{
|
||||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr;
|
||||
if (paint == nullptr || brush == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bke::asset_edit_id_is_editable(brush->id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(paint->brush_asset_reference && ID_IS_ASSET(brush))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bke::asset_edit_id_is_writable(brush->id)) {
|
||||
CTX_wm_operator_poll_msg_set(C, "Asset blend file is not editable");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int brush_asset_update_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
Brush *brush = BKE_paint_brush(paint);
|
||||
const AssetWeakReference *asset_weak_ref = paint->brush_asset_reference;
|
||||
|
||||
const bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_by_name(
|
||||
&U, asset_weak_ref->asset_library_identifier);
|
||||
if (!user_library) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
BLI_assert(ID_IS_ASSET(brush));
|
||||
|
||||
bke::asset_edit_id_save(*bmain, brush->id, *op->reports);
|
||||
|
||||
refresh_asset_library(C, *user_library);
|
||||
WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_EDITED, nullptr);
|
||||
WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void BRUSH_OT_asset_update(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Update Brush Asset";
|
||||
ot->description = "Update the active brush asset in the asset library with current settings";
|
||||
ot->idname = "BRUSH_OT_asset_update";
|
||||
|
||||
ot->exec = brush_asset_update_exec;
|
||||
ot->poll = brush_asset_update_poll;
|
||||
}
|
||||
|
||||
static bool brush_asset_revert_poll(bContext *C)
|
||||
{
|
||||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr;
|
||||
if (paint == nullptr || brush == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return paint->brush_asset_reference && bke::asset_edit_id_is_editable(brush->id);
|
||||
}
|
||||
|
||||
static int brush_asset_revert_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
Brush *brush = BKE_paint_brush(paint);
|
||||
|
||||
bke::asset_edit_id_revert(*bmain, brush->id, *op->reports);
|
||||
|
||||
WM_main_add_notifier(NC_BRUSH | NA_EDITED, nullptr);
|
||||
WM_main_add_notifier(NC_TEXTURE | ND_NODES, nullptr);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void BRUSH_OT_asset_revert(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Revert Brush Asset";
|
||||
ot->description =
|
||||
"Revert the active brush settings to the default values from the asset library";
|
||||
ot->idname = "BRUSH_OT_asset_revert";
|
||||
|
||||
ot->exec = brush_asset_revert_exec;
|
||||
ot->poll = brush_asset_revert_poll;
|
||||
}
|
||||
|
||||
} // namespace blender::ed::sculpt_paint
|
|
@ -901,7 +901,7 @@ void ED_object_texture_paint_mode_enter_ex(Main *bmain,
|
|||
|
||||
BKE_paint_init(bmain, scene, PaintMode::Texture3D, PAINT_CURSOR_TEXTURE_PAINT);
|
||||
|
||||
BKE_paint_brush_validate(bmain, &imapaint->paint);
|
||||
BKE_paint_brushes_validate(bmain, &imapaint->paint);
|
||||
|
||||
if (U.glreslimit != 0) {
|
||||
BKE_image_free_all_gputextures(bmain);
|
||||
|
|
|
@ -118,6 +118,14 @@ bool paint_stroke_started(PaintStroke *stroke);
|
|||
|
||||
bool paint_brush_tool_poll(bContext *C);
|
||||
|
||||
void BRUSH_OT_asset_select(wmOperatorType *ot);
|
||||
void BRUSH_OT_asset_save_as(wmOperatorType *ot);
|
||||
void BRUSH_OT_asset_edit_metadata(wmOperatorType *ot);
|
||||
void BRUSH_OT_asset_load_preview(wmOperatorType *ot);
|
||||
void BRUSH_OT_asset_delete(wmOperatorType *ot);
|
||||
void BRUSH_OT_asset_update(wmOperatorType *ot);
|
||||
void BRUSH_OT_asset_revert(wmOperatorType *ot);
|
||||
|
||||
} // namespace blender::ed::sculpt_paint
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -421,19 +421,17 @@ bool mode_toggle_poll_test(bContext *C)
|
|||
|
||||
void smooth_brush_toggle_off(const bContext *C, Paint *paint, StrokeCache *cache)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Brush *brush = BKE_paint_brush(paint);
|
||||
/* The current brush should match with what we have stored in the cache. */
|
||||
BLI_assert(brush == cache->brush);
|
||||
|
||||
/* If saved_active_brush_name is not set, brush was not switched/affected in
|
||||
/* If saved_active_brush is not set, brush was not switched/affected in
|
||||
* smooth_brush_toggle_on(). */
|
||||
Brush *saved_active_brush = (Brush *)BKE_libblock_find_name(
|
||||
bmain, ID_BR, cache->saved_active_brush_name);
|
||||
if (saved_active_brush) {
|
||||
if (cache->saved_active_brush) {
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
BKE_brush_size_set(scene, brush, cache->saved_smooth_size);
|
||||
BKE_paint_brush_set(paint, saved_active_brush);
|
||||
BKE_paint_brush_set(paint, cache->saved_active_brush);
|
||||
cache->saved_active_brush = nullptr;
|
||||
}
|
||||
}
|
||||
/* Initialize the stroke cache invariants from operator properties */
|
||||
|
@ -578,24 +576,27 @@ void last_stroke_update(Scene *scene, const float location[3])
|
|||
/* -------------------------------------------------------------------- */
|
||||
void smooth_brush_toggle_on(const bContext *C, Paint *paint, StrokeCache *cache)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Brush *cur_brush = BKE_paint_brush(paint);
|
||||
|
||||
/* Switch to the blur (smooth) brush if possible. */
|
||||
/* NOTE: used for both vertexpaint and weightpaint, VPAINT_TOOL_BLUR & WPAINT_TOOL_BLUR are the
|
||||
* same, see comments for eBrushVertexPaintTool & eBrushWeightPaintTool. */
|
||||
Brush *smooth_brush = BKE_paint_toolslots_brush_get(paint, WPAINT_TOOL_BLUR);
|
||||
BKE_paint_brush_set_essentials(bmain,
|
||||
paint,
|
||||
(paint->runtime.ob_mode = OB_MODE_WEIGHT_PAINT) ? "Blur Weight" :
|
||||
"Blur Vertex");
|
||||
Brush *smooth_brush = BKE_paint_brush(paint);
|
||||
|
||||
if (!smooth_brush) {
|
||||
BKE_paint_brush_set(paint, cur_brush);
|
||||
CLOG_WARN(&LOG, "Switching to the blur (smooth) brush not possible, corresponding brush not");
|
||||
cache->saved_active_brush_name[0] = '\0';
|
||||
cache->saved_active_brush = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
Brush *cur_brush = paint->brush;
|
||||
int cur_brush_size = BKE_brush_size_get(scene, cur_brush);
|
||||
|
||||
STRNCPY(cache->saved_active_brush_name, cur_brush->id.name + 2);
|
||||
|
||||
BKE_paint_brush_set(paint, smooth_brush);
|
||||
cache->saved_active_brush = cur_brush;
|
||||
cache->saved_smooth_size = BKE_brush_size_get(scene, smooth_brush);
|
||||
BKE_brush_size_set(scene, smooth_brush, cur_brush_size);
|
||||
BKE_curvemapping_init(smooth_brush->curve);
|
||||
|
@ -841,7 +842,7 @@ static int vpaint_mode_toggle_exec(bContext *C, wmOperator *op)
|
|||
depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
||||
}
|
||||
ED_object_vpaintmode_enter_ex(bmain, depsgraph, scene, ob);
|
||||
BKE_paint_brush_validate(bmain, &ts->vpaint->paint);
|
||||
BKE_paint_brushes_validate(bmain, &ts->vpaint->paint);
|
||||
}
|
||||
|
||||
BKE_mesh_batch_cache_dirty_tag((Mesh *)ob->data, BKE_MESH_BATCH_DIRTY_ALL);
|
||||
|
|
|
@ -1646,7 +1646,7 @@ static int wpaint_mode_toggle_exec(bContext *C, wmOperator *op)
|
|||
depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
||||
}
|
||||
ED_object_wpaintmode_enter_ex(bmain, depsgraph, scene, ob);
|
||||
BKE_paint_brush_validate(bmain, &ts->wpaint->paint);
|
||||
BKE_paint_brushes_validate(bmain, &ts->wpaint->paint);
|
||||
}
|
||||
|
||||
/* Prepare armature posemode. */
|
||||
|
|
|
@ -4228,8 +4228,9 @@ static void sculpt_init_mirror_clipping(Object *ob, SculptSession *ss)
|
|||
|
||||
static void smooth_brush_toggle_on(const bContext *C, Paint *paint, StrokeCache *cache)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Brush *cur_brush = paint->brush;
|
||||
Brush *cur_brush = BKE_paint_brush(paint);
|
||||
|
||||
if (cur_brush->sculpt_tool == SCULPT_TOOL_MASK) {
|
||||
cache->saved_mask_brush_tool = cur_brush->mask_tool;
|
||||
|
@ -4248,18 +4249,20 @@ static void smooth_brush_toggle_on(const bContext *C, Paint *paint, StrokeCache
|
|||
}
|
||||
|
||||
/* Switch to the smooth brush if possible. */
|
||||
Brush *smooth_brush = BKE_paint_toolslots_brush_get(paint, SCULPT_TOOL_SMOOTH);
|
||||
BKE_paint_brush_set_essentials(bmain, paint, "Smooth");
|
||||
Brush *smooth_brush = BKE_paint_brush(paint);
|
||||
|
||||
if (!smooth_brush) {
|
||||
BKE_paint_brush_set(paint, cur_brush);
|
||||
CLOG_WARN(&LOG, "Switching to the smooth brush not possible, corresponding brush not");
|
||||
cache->saved_active_brush_name[0] = '\0';
|
||||
cache->saved_active_brush = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
int cur_brush_size = BKE_brush_size_get(scene, cur_brush);
|
||||
|
||||
STRNCPY(cache->saved_active_brush_name, cur_brush->id.name + 2);
|
||||
cache->saved_active_brush = cur_brush;
|
||||
|
||||
BKE_paint_brush_set(paint, smooth_brush);
|
||||
cache->saved_smooth_size = BKE_brush_size_get(scene, smooth_brush);
|
||||
BKE_brush_size_set(scene, smooth_brush, cur_brush_size);
|
||||
BKE_curvemapping_init(smooth_brush->curve);
|
||||
|
@ -4267,7 +4270,6 @@ static void smooth_brush_toggle_on(const bContext *C, Paint *paint, StrokeCache
|
|||
|
||||
static void smooth_brush_toggle_off(const bContext *C, Paint *paint, StrokeCache *cache)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Brush *brush = BKE_paint_brush(paint);
|
||||
|
||||
if (brush->sculpt_tool == SCULPT_TOOL_MASK) {
|
||||
|
@ -4285,14 +4287,13 @@ static void smooth_brush_toggle_off(const bContext *C, Paint *paint, StrokeCache
|
|||
return;
|
||||
}
|
||||
|
||||
/* If saved_active_brush_name is not set, brush was not switched/affected in
|
||||
/* If saved_active_brush is not set, brush was not switched/affected in
|
||||
* smooth_brush_toggle_on(). */
|
||||
Brush *saved_active_brush = (Brush *)BKE_libblock_find_name(
|
||||
bmain, ID_BR, cache->saved_active_brush_name);
|
||||
if (saved_active_brush) {
|
||||
if (cache->saved_active_brush) {
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
BKE_brush_size_set(scene, brush, cache->saved_smooth_size);
|
||||
BKE_paint_brush_set(paint, saved_active_brush);
|
||||
BKE_paint_brush_set(paint, cache->saved_active_brush);
|
||||
cache->saved_active_brush = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -509,7 +509,7 @@ struct StrokeCache {
|
|||
float vertex_rotation; /* amount to rotate the vertices when using rotate brush */
|
||||
Dial *dial;
|
||||
|
||||
char saved_active_brush_name[MAX_ID_NAME];
|
||||
Brush *saved_active_brush;
|
||||
char saved_mask_brush_tool;
|
||||
int saved_smooth_size; /* smooth tool copies the size of the current tool */
|
||||
bool alt_smooth;
|
||||
|
|
|
@ -527,7 +527,7 @@ static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op)
|
|||
depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
||||
}
|
||||
ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, false, op->reports);
|
||||
BKE_paint_brush_validate(bmain, &ts->sculpt->paint);
|
||||
BKE_paint_brushes_validate(bmain, &ts->sculpt->paint);
|
||||
|
||||
if (ob->mode & mode_flag) {
|
||||
Mesh *mesh = static_cast<Mesh *>(ob->data);
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "DNA_scene_types.h"
|
||||
#include "DNA_screen_types.h"
|
||||
|
||||
#include "BKE_asset_edit.hh"
|
||||
#include "BKE_colortools.hh"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_global.hh"
|
||||
|
@ -1262,14 +1263,20 @@ static void image_open_cancel(bContext * /*C*/, wmOperator *op)
|
|||
|
||||
static Image *image_open_single(Main *bmain,
|
||||
wmOperator *op,
|
||||
ImageFrameRange *range,
|
||||
bool use_multiview)
|
||||
const ImageFrameRange *range,
|
||||
const bool use_multiview,
|
||||
const bool check_exists)
|
||||
{
|
||||
bool exists = false;
|
||||
Image *ima = nullptr;
|
||||
|
||||
errno = 0;
|
||||
ima = BKE_image_load_exists_ex(bmain, range->filepath, &exists);
|
||||
if (check_exists) {
|
||||
ima = BKE_image_load_exists_ex(bmain, range->filepath, &exists);
|
||||
}
|
||||
else {
|
||||
ima = BKE_image_load(bmain, range->filepath);
|
||||
}
|
||||
|
||||
if (!ima) {
|
||||
if (op->customdata) {
|
||||
|
@ -1326,6 +1333,7 @@ static int image_open_exec(bContext *C, wmOperator *op)
|
|||
Main *bmain = CTX_data_main(C);
|
||||
ScrArea *area = CTX_wm_area(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
ImageOpenData *iod = static_cast<ImageOpenData *>(op->customdata);
|
||||
ImageUser *iuser = nullptr;
|
||||
Image *ima = nullptr;
|
||||
int frame_seq_len = 0;
|
||||
|
@ -1334,13 +1342,18 @@ static int image_open_exec(bContext *C, wmOperator *op)
|
|||
const bool use_multiview = RNA_boolean_get(op->ptr, "use_multiview");
|
||||
const bool use_udim = RNA_boolean_get(op->ptr, "use_udim_detecting");
|
||||
|
||||
/* For editable assets always create a new image datablock. We can't assign
|
||||
* a local datablock to linked asset datablocks. */
|
||||
const bool check_exists = !(iod->pprop.prop && iod->pprop.ptr.owner_id &&
|
||||
blender::bke::asset_edit_id_is_editable(*iod->pprop.ptr.owner_id));
|
||||
|
||||
if (!op->customdata) {
|
||||
image_open_init(C, op);
|
||||
}
|
||||
|
||||
ListBase ranges = ED_image_filesel_detect_sequences(bmain, op, use_udim);
|
||||
LISTBASE_FOREACH (ImageFrameRange *, range, &ranges) {
|
||||
Image *ima_range = image_open_single(bmain, op, range, use_multiview);
|
||||
Image *ima_range = image_open_single(bmain, op, range, use_multiview, check_exists);
|
||||
|
||||
/* take the first image */
|
||||
if ((ima == nullptr) && ima_range) {
|
||||
|
@ -1358,13 +1371,15 @@ static int image_open_exec(bContext *C, wmOperator *op)
|
|||
}
|
||||
|
||||
/* hook into UI */
|
||||
ImageOpenData *iod = static_cast<ImageOpenData *>(op->customdata);
|
||||
|
||||
if (iod->pprop.prop) {
|
||||
/* when creating new ID blocks, use is already 1, but RNA
|
||||
* pointer use also increases user, so this compensates it */
|
||||
id_us_min(&ima->id);
|
||||
|
||||
if (iod->pprop.ptr.owner_id) {
|
||||
BKE_id_move_to_same_lib(*bmain, ima->id, *iod->pprop.ptr.owner_id);
|
||||
}
|
||||
|
||||
PointerRNA imaptr = RNA_id_pointer_create(&ima->id);
|
||||
RNA_property_pointer_set(&iod->pprop.ptr, iod->pprop.prop, imaptr, nullptr);
|
||||
RNA_property_update(C, &iod->pprop.ptr, iod->pprop.prop);
|
||||
|
@ -2589,6 +2604,10 @@ static int image_new_exec(bContext *C, wmOperator *op)
|
|||
* pointer use also increases user, so this compensates it */
|
||||
id_us_min(&ima->id);
|
||||
|
||||
if (data->pprop.ptr.owner_id) {
|
||||
BKE_id_move_to_same_lib(*bmain, ima->id, *data->pprop.ptr.owner_id);
|
||||
}
|
||||
|
||||
PointerRNA imaptr = RNA_id_pointer_create(&ima->id);
|
||||
RNA_property_pointer_set(&data->pprop.ptr, data->pprop.prop, imaptr, nullptr);
|
||||
RNA_property_update(C, &data->pprop.ptr, data->pprop.prop);
|
||||
|
|
|
@ -1202,6 +1202,7 @@ void ED_spacetype_image()
|
|||
art->free = asset::shelf::region_free;
|
||||
art->on_poll_success = asset::shelf::region_on_poll_success;
|
||||
art->listener = asset::shelf::region_listen;
|
||||
art->message_subscribe = asset::shelf::region_message_subscribe;
|
||||
art->poll = asset::shelf::regions_poll;
|
||||
art->snap_size = asset::shelf::region_snap;
|
||||
art->on_user_resize = asset::shelf::region_on_user_resize;
|
||||
|
|
|
@ -1071,6 +1071,10 @@ static int new_node_tree_exec(bContext *C, wmOperator *op)
|
|||
* user. */
|
||||
id_us_min(&ntree->id);
|
||||
|
||||
if (ptr.owner_id) {
|
||||
BKE_id_move_to_same_lib(*bmain, ntree->id, *ptr.owner_id);
|
||||
}
|
||||
|
||||
PointerRNA idptr = RNA_id_pointer_create(&ntree->id);
|
||||
RNA_property_pointer_set(&ptr, prop, idptr, nullptr);
|
||||
RNA_property_update(C, &ptr, prop);
|
||||
|
|
|
@ -1215,6 +1215,8 @@ static bNode *node_group_make_from_nodes(const bContext &C,
|
|||
/* New node-tree. */
|
||||
bNodeTree *ngroup = bke::ntreeAddTree(bmain, "NodeGroup", ntreetype);
|
||||
|
||||
BKE_id_move_to_same_lib(*bmain, ngroup->id, ntree.id);
|
||||
|
||||
/* make group node */
|
||||
bNode *gnode = bke::nodeAddNode(&C, &ntree, ntype);
|
||||
gnode->id = (ID *)ngroup;
|
||||
|
|
|
@ -2219,6 +2219,7 @@ void ED_spacetype_view3d()
|
|||
art->free = asset::shelf::region_free;
|
||||
art->on_poll_success = asset::shelf::region_on_poll_success;
|
||||
art->listener = asset::shelf::region_listen;
|
||||
art->message_subscribe = asset::shelf::region_message_subscribe;
|
||||
art->poll = asset::shelf::regions_poll;
|
||||
art->snap_size = asset::shelf::region_snap;
|
||||
art->on_user_resize = asset::shelf::region_on_user_resize;
|
||||
|
|
|
@ -117,6 +117,7 @@ void ED_editors_init(bContext *C)
|
|||
}
|
||||
else if (mode & OB_MODE_ALL_PAINT_GPENCIL) {
|
||||
ED_gpencil_toggle_brush_cursor(C, true, nullptr);
|
||||
BKE_paint_ensure_from_paintmode(bmain, scene, BKE_paintmode_get_active_from_context(C));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -581,6 +581,11 @@ typedef struct Library {
|
|||
enum eLibrary_Tag {
|
||||
/* Automatic recursive resync was needed when linking/loading data from that library. */
|
||||
LIBRARY_TAG_RESYNC_REQUIRED = 1 << 0,
|
||||
/* Datablocks from this library are editable in the UI despite being linked.
|
||||
* Used for asset that can be temporarily or permantently edited. */
|
||||
LIBRARY_ASSET_EDITABLE = 1 << 1,
|
||||
/* The blend file of this library is writable for asset editing. */
|
||||
LIBRARY_ASSET_FILE_WRITABLE = 1 << 2,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -670,7 +675,12 @@ typedef struct PreviewImage {
|
|||
|
||||
#define ID_IS_LINKED(_id) (((const ID *)(_id))->lib != NULL)
|
||||
|
||||
#define ID_IS_EDITABLE(_id) (((const ID *)(_id))->lib == NULL)
|
||||
#define ID_TYPE_SUPPORTS_ASSET_EDITABLE(id_type) ELEM(id_type, ID_BR, ID_TE, ID_NT, ID_IM)
|
||||
|
||||
#define ID_IS_EDITABLE(_id) \
|
||||
((((const ID *)(_id))->lib == NULL) || \
|
||||
((((const ID *)(_id))->lib->runtime.tag & LIBRARY_ASSET_EDITABLE) && \
|
||||
ID_TYPE_SUPPORTS_ASSET_EDITABLE(GS((((const ID *)(_id))->name)))))
|
||||
|
||||
/* Note that these are fairly high-level checks, should be used at user interaction level, not in
|
||||
* BKE_library_override typically (especially due to the check on LIB_TAG_EXTERN). */
|
||||
|
|
|
@ -76,8 +76,8 @@ typedef enum eGPDbrush_Flag {
|
|||
GP_BRUSH_STABILIZE_MOUSE = (1 << 8),
|
||||
/* lazy mouse override (internal only) */
|
||||
GP_BRUSH_STABILIZE_MOUSE_TEMP = (1 << 9),
|
||||
/* default eraser brush for quick switch */
|
||||
GP_BRUSH_DEFAULT_ERASER = (1 << 10),
|
||||
/* deprecated, was default eraser brush for quick switch */
|
||||
GP_BRUSH_DEPRECATED1 = (1 << 10),
|
||||
/* settings group */
|
||||
GP_BRUSH_GROUP_SETTINGS = (1 << 11),
|
||||
/* Random settings group */
|
||||
|
@ -165,41 +165,6 @@ typedef enum eGP_BrushMode {
|
|||
GP_BRUSH_MODE_VERTEXCOLOR = 2,
|
||||
} eGP_BrushMode;
|
||||
|
||||
/* BrushGpencilSettings default brush icons */
|
||||
typedef enum eGP_BrushIcons {
|
||||
GP_BRUSH_ICON_PENCIL = 1,
|
||||
GP_BRUSH_ICON_PEN = 2,
|
||||
GP_BRUSH_ICON_INK = 3,
|
||||
GP_BRUSH_ICON_INKNOISE = 4,
|
||||
GP_BRUSH_ICON_BLOCK = 5,
|
||||
GP_BRUSH_ICON_MARKER = 6,
|
||||
GP_BRUSH_ICON_FILL = 7,
|
||||
GP_BRUSH_ICON_ERASE_SOFT = 8,
|
||||
GP_BRUSH_ICON_ERASE_HARD = 9,
|
||||
GP_BRUSH_ICON_ERASE_STROKE = 10,
|
||||
GP_BRUSH_ICON_AIRBRUSH = 11,
|
||||
GP_BRUSH_ICON_CHISEL = 12,
|
||||
GP_BRUSH_ICON_TINT = 13,
|
||||
GP_BRUSH_ICON_VERTEX_DRAW = 14,
|
||||
GP_BRUSH_ICON_VERTEX_BLUR = 15,
|
||||
GP_BRUSH_ICON_VERTEX_AVERAGE = 16,
|
||||
GP_BRUSH_ICON_VERTEX_SMEAR = 17,
|
||||
GP_BRUSH_ICON_VERTEX_REPLACE = 18,
|
||||
GP_BRUSH_ICON_GPBRUSH_SMOOTH = 19,
|
||||
GP_BRUSH_ICON_GPBRUSH_THICKNESS = 20,
|
||||
GP_BRUSH_ICON_GPBRUSH_STRENGTH = 21,
|
||||
GP_BRUSH_ICON_GPBRUSH_RANDOMIZE = 22,
|
||||
GP_BRUSH_ICON_GPBRUSH_GRAB = 23,
|
||||
GP_BRUSH_ICON_GPBRUSH_PUSH = 24,
|
||||
GP_BRUSH_ICON_GPBRUSH_TWIST = 25,
|
||||
GP_BRUSH_ICON_GPBRUSH_PINCH = 26,
|
||||
GP_BRUSH_ICON_GPBRUSH_CLONE = 27,
|
||||
GP_BRUSH_ICON_GPBRUSH_WEIGHT = 28,
|
||||
GP_BRUSH_ICON_GPBRUSH_BLUR = 29,
|
||||
GP_BRUSH_ICON_GPBRUSH_AVERAGE = 30,
|
||||
GP_BRUSH_ICON_GPBRUSH_SMEAR = 31,
|
||||
} eGP_BrushIcons;
|
||||
|
||||
typedef enum eBrushCurvePreset {
|
||||
BRUSH_CURVE_CUSTOM = 0,
|
||||
BRUSH_CURVE_SMOOTH = 1,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -915,27 +915,29 @@ typedef struct TimeMarker {
|
|||
|
||||
typedef struct Paint_Runtime {
|
||||
/** Avoid having to compare with scene pointer everywhere. */
|
||||
unsigned int tool_offset;
|
||||
unsigned int initialized;
|
||||
unsigned short ob_mode;
|
||||
char _pad[2];
|
||||
} Paint_Runtime;
|
||||
|
||||
/** We might want to store other things here. */
|
||||
typedef struct PaintToolSlot {
|
||||
struct Brush *brush;
|
||||
} PaintToolSlot;
|
||||
|
||||
/** Paint Tool Base. */
|
||||
typedef struct Paint {
|
||||
/**
|
||||
* The active brush. Possibly null. Possibly stored in a separate #Main data-base and not user-
|
||||
* counted.
|
||||
*/
|
||||
struct Brush *brush;
|
||||
|
||||
/**
|
||||
* Each tool has its own active brush,
|
||||
* The currently active tool is defined by the current 'brush'.
|
||||
* A weak asset reference to the #brush, if not NULL.
|
||||
* Used to attempt restoring the active brush from the AssetLibrary system, typically on
|
||||
* file load.
|
||||
*/
|
||||
struct PaintToolSlot *tool_slots;
|
||||
int tool_slots_len;
|
||||
char _pad1[4];
|
||||
struct AssetWeakReference *brush_asset_reference;
|
||||
|
||||
/** Default eraser brush and associated weak reference. */
|
||||
struct Brush *eraser_brush;
|
||||
struct AssetWeakReference *eraser_brush_asset_reference;
|
||||
|
||||
struct Palette *palette;
|
||||
/** Cavity curve. */
|
||||
|
|
|
@ -1269,14 +1269,28 @@ static char *rna_def_property_set_func(
|
|||
else {
|
||||
rna_print_data_get(f, dp);
|
||||
|
||||
PointerPropertyRNA *pprop = (PointerPropertyRNA *)dp->prop;
|
||||
StructRNA *type = (pprop->type) ? rna_find_struct((const char *)pprop->type) : nullptr;
|
||||
|
||||
if (prop->flag & PROP_ID_SELF_CHECK) {
|
||||
/* No pointers to self allowed. */
|
||||
rna_print_id_get(f, dp);
|
||||
fprintf(f, " if (id == value.data) {\n");
|
||||
fprintf(f, " return;\n");
|
||||
fprintf(f, " }\n");
|
||||
}
|
||||
|
||||
if (type && (type->flag & STRUCT_ID)) {
|
||||
/* Check if pointers between datablocks are allowed. */
|
||||
fprintf(f,
|
||||
" if (value.data && ptr->owner_id && value.owner_id && "
|
||||
"!BKE_id_can_link(*ptr->owner_id, *value.owner_id)) {\n");
|
||||
fprintf(f, " return;\n");
|
||||
fprintf(f, " }\n");
|
||||
}
|
||||
|
||||
if (prop->flag & PROP_ID_REFCOUNT) {
|
||||
/* Perform reference counting. */
|
||||
fprintf(f, "\n if (data->%s) {\n", dp->dnaname);
|
||||
fprintf(f, " id_us_min((ID *)data->%s);\n", dp->dnaname);
|
||||
fprintf(f, " }\n");
|
||||
|
@ -1284,14 +1298,11 @@ static char *rna_def_property_set_func(
|
|||
fprintf(f, " id_us_plus((ID *)value.data);\n");
|
||||
fprintf(f, " }\n");
|
||||
}
|
||||
else {
|
||||
PointerPropertyRNA *pprop = (PointerPropertyRNA *)dp->prop;
|
||||
StructRNA *type = (pprop->type) ? rna_find_struct((const char *)pprop->type) : nullptr;
|
||||
if (type && (type->flag & STRUCT_ID)) {
|
||||
fprintf(f, " if (value.data) {\n");
|
||||
fprintf(f, " id_lib_extern((ID *)value.data);\n");
|
||||
fprintf(f, " }\n");
|
||||
}
|
||||
else if (type && (type->flag & STRUCT_ID)) {
|
||||
/* Still mark linked data as used if not reference counting. */
|
||||
fprintf(f, " if (value.data) {\n");
|
||||
fprintf(f, " id_lib_extern((ID *)value.data);\n");
|
||||
fprintf(f, " }\n");
|
||||
}
|
||||
|
||||
fprintf(f, " *(void **)&data->%s = value.data;\n", dp->dnaname);
|
||||
|
|
|
@ -285,7 +285,6 @@ int rna_ID_name_length(PointerRNA *ptr)
|
|||
void rna_ID_name_set(PointerRNA *ptr, const char *value)
|
||||
{
|
||||
ID *id = (ID *)ptr->data;
|
||||
BLI_assert(BKE_id_is_in_global_main(id));
|
||||
BLI_assert(ID_IS_EDITABLE(id));
|
||||
|
||||
BKE_libblock_rename(G_MAIN, id, value);
|
||||
|
@ -296,6 +295,8 @@ void rna_ID_name_set(PointerRNA *ptr, const char *value)
|
|||
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
||||
}
|
||||
}
|
||||
|
||||
/* TODO: this does not update immediately in the asset shelf. */
|
||||
}
|
||||
|
||||
static int rna_ID_name_editable(const PointerRNA *ptr, const char **r_info)
|
||||
|
@ -2554,6 +2555,14 @@ static void rna_def_library(BlenderRNA *brna)
|
|||
"current blendfile, and that had to be recursively resynced on load "
|
||||
"(it is recommended to open and re-save that library blendfile then)");
|
||||
|
||||
prop = RNA_def_property(srna, "is_editable", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "runtime.tag", LIBRARY_ASSET_EDITABLE);
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Editable",
|
||||
"Datablocks in this library are editable despite being linked. Used by "
|
||||
"brush assets and their dependencies");
|
||||
|
||||
func = RNA_def_function(srna, "reload", "rna_Library_reload");
|
||||
RNA_def_function_flag(func, FUNC_USE_REPORTS | FUNC_USE_CONTEXT);
|
||||
RNA_def_function_ui_description(func, "Reload this library and all its linked data-blocks");
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
#include "BKE_global.hh"
|
||||
#include "BKE_idprop.hh"
|
||||
#include "BKE_idtype.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_lib_override.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_node.hh"
|
||||
|
@ -1582,22 +1583,28 @@ bool RNA_property_pointer_poll(PointerRNA *ptr, PropertyRNA *prop, PointerRNA *v
|
|||
{
|
||||
prop = rna_ensure_property(prop);
|
||||
|
||||
if (prop->type == PROP_POINTER) {
|
||||
PointerPropertyRNA *pprop = (PointerPropertyRNA *)prop;
|
||||
if (prop->type != PROP_POINTER) {
|
||||
printf("%s: %s is not a pointer property.\n", __func__, prop->identifier);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pprop->poll) {
|
||||
if (rna_idproperty_check(&prop, ptr)) {
|
||||
return reinterpret_cast<PropPointerPollFuncPy>(reinterpret_cast<void *>(pprop->poll))(
|
||||
ptr, *value, prop);
|
||||
}
|
||||
return pprop->poll(ptr, *value);
|
||||
}
|
||||
PointerPropertyRNA *pprop = (PointerPropertyRNA *)prop;
|
||||
|
||||
/* Can't point from linked to local datablock. */
|
||||
if (ptr->owner_id && value->owner_id && !BKE_id_can_link(*ptr->owner_id, *value->owner_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Check custom poll function. */
|
||||
if (!pprop->poll) {
|
||||
return true;
|
||||
}
|
||||
|
||||
printf("%s: %s is not a pointer property.\n", __func__, prop->identifier);
|
||||
return false;
|
||||
if (rna_idproperty_check(&prop, ptr)) {
|
||||
return reinterpret_cast<PropPointerPollFuncPy>(reinterpret_cast<void *>(pprop->poll))(
|
||||
ptr, *value, prop);
|
||||
}
|
||||
return pprop->poll(ptr, *value);
|
||||
}
|
||||
|
||||
void RNA_property_enum_items_ex(bContext *C,
|
||||
|
|
|
@ -391,50 +391,6 @@ static EnumPropertyItem rna_enum_gpencil_brush_modes_items[] = {
|
|||
{GP_BRUSH_MODE_VERTEXCOLOR, "VERTEXCOLOR", 0, "Vertex Color", "Use always Vertex Color mode"},
|
||||
{0, nullptr, 0, nullptr, nullptr}};
|
||||
|
||||
static EnumPropertyItem rna_enum_gpencil_brush_paint_icons_items[] = {
|
||||
{GP_BRUSH_ICON_PENCIL, "PENCIL", ICON_GPBRUSH_PENCIL, "Pencil", ""},
|
||||
{GP_BRUSH_ICON_PEN, "PEN", ICON_GPBRUSH_PEN, "Pen", ""},
|
||||
{GP_BRUSH_ICON_INK, "INK", ICON_GPBRUSH_INK, "Ink", ""},
|
||||
{GP_BRUSH_ICON_INKNOISE, "INKNOISE", ICON_GPBRUSH_INKNOISE, "Ink Noise", ""},
|
||||
{GP_BRUSH_ICON_BLOCK, "BLOCK", ICON_GPBRUSH_BLOCK, "Block", ""},
|
||||
{GP_BRUSH_ICON_MARKER, "MARKER", ICON_GPBRUSH_MARKER, "Marker", ""},
|
||||
{GP_BRUSH_ICON_AIRBRUSH, "AIRBRUSH", ICON_GPBRUSH_AIRBRUSH, "Airbrush", ""},
|
||||
{GP_BRUSH_ICON_CHISEL, "CHISEL", ICON_GPBRUSH_CHISEL, "Chisel", ""},
|
||||
{GP_BRUSH_ICON_FILL, "FILL", ICON_GPBRUSH_FILL, "Fill", ""},
|
||||
{GP_BRUSH_ICON_ERASE_SOFT, "SOFT", ICON_GPBRUSH_ERASE_SOFT, "Eraser Soft", ""},
|
||||
{GP_BRUSH_ICON_ERASE_HARD, "HARD", ICON_GPBRUSH_ERASE_HARD, "Eraser Hard", ""},
|
||||
{GP_BRUSH_ICON_ERASE_STROKE, "STROKE", ICON_GPBRUSH_ERASE_STROKE, "Eraser Stroke", ""},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
static EnumPropertyItem rna_enum_gpencil_brush_sculpt_icons_items[] = {
|
||||
{GP_BRUSH_ICON_GPBRUSH_SMOOTH, "SMOOTH", ICON_GPBRUSH_SMOOTH, "Smooth", ""},
|
||||
{GP_BRUSH_ICON_GPBRUSH_THICKNESS, "THICKNESS", ICON_GPBRUSH_THICKNESS, "Thickness", ""},
|
||||
{GP_BRUSH_ICON_GPBRUSH_STRENGTH, "STRENGTH", ICON_GPBRUSH_STRENGTH, "Strength", ""},
|
||||
{GP_BRUSH_ICON_GPBRUSH_RANDOMIZE, "RANDOMIZE", ICON_GPBRUSH_RANDOMIZE, "Randomize", ""},
|
||||
{GP_BRUSH_ICON_GPBRUSH_GRAB, "GRAB", ICON_GPBRUSH_GRAB, "Grab", ""},
|
||||
{GP_BRUSH_ICON_GPBRUSH_PUSH, "PUSH", ICON_GPBRUSH_PUSH, "Push", ""},
|
||||
{GP_BRUSH_ICON_GPBRUSH_TWIST, "TWIST", ICON_GPBRUSH_TWIST, "Twist", ""},
|
||||
{GP_BRUSH_ICON_GPBRUSH_PINCH, "PINCH", ICON_GPBRUSH_PINCH, "Pinch", ""},
|
||||
{GP_BRUSH_ICON_GPBRUSH_CLONE, "CLONE", ICON_GPBRUSH_CLONE, "Clone", ""},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
static EnumPropertyItem rna_enum_gpencil_brush_weight_icons_items[] = {
|
||||
{GP_BRUSH_ICON_GPBRUSH_WEIGHT, "DRAW", ICON_GPBRUSH_WEIGHT, "Draw", ""},
|
||||
{GP_BRUSH_ICON_GPBRUSH_BLUR, "BLUR", ICON_BRUSH_BLUR, "Blur", ""},
|
||||
{GP_BRUSH_ICON_GPBRUSH_AVERAGE, "AVERAGE", ICON_BRUSH_BLUR, "Average", ""},
|
||||
{GP_BRUSH_ICON_GPBRUSH_SMEAR, "SMEAR", ICON_BRUSH_BLUR, "Smear", ""},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
static EnumPropertyItem rna_enum_gpencil_brush_vertex_icons_items[] = {
|
||||
{GP_BRUSH_ICON_VERTEX_DRAW, "DRAW", ICON_BRUSH_MIX, "Draw", ""},
|
||||
{GP_BRUSH_ICON_VERTEX_BLUR, "BLUR", ICON_BRUSH_BLUR, "Blur", ""},
|
||||
{GP_BRUSH_ICON_VERTEX_AVERAGE, "AVERAGE", ICON_BRUSH_BLUR, "Average", ""},
|
||||
{GP_BRUSH_ICON_VERTEX_SMEAR, "SMEAR", ICON_BRUSH_BLUR, "Smear", ""},
|
||||
{GP_BRUSH_ICON_VERTEX_REPLACE, "REPLACE", ICON_BRUSH_MIX, "Replace", ""},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef RNA_RUNTIME
|
||||
|
@ -1073,26 +1029,6 @@ static std::optional<std::string> rna_BrushGpencilSettings_path(const PointerRNA
|
|||
return "gpencil_settings";
|
||||
}
|
||||
|
||||
static void rna_BrushGpencilSettings_default_eraser_update(Main *bmain,
|
||||
Scene *scene,
|
||||
PointerRNA * /*ptr*/)
|
||||
{
|
||||
ToolSettings *ts = scene->toolsettings;
|
||||
Paint *paint = &ts->gp_paint->paint;
|
||||
Brush *brush_cur = BKE_paint_brush(paint);
|
||||
|
||||
/* disable default eraser in all brushes */
|
||||
for (Brush *brush = static_cast<Brush *>(bmain->brushes.first); brush;
|
||||
brush = static_cast<Brush *>(brush->id.next))
|
||||
{
|
||||
if ((brush != brush_cur) && (brush->ob_mode == OB_MODE_PAINT_GPENCIL_LEGACY) &&
|
||||
(brush->gpencil_tool == GPAINT_TOOL_ERASE))
|
||||
{
|
||||
brush->gpencil_settings->flag &= ~GP_BRUSH_DEFAULT_ERASER;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void rna_BrushGpencilSettings_use_material_pin_update(bContext *C, PointerRNA *ptr)
|
||||
{
|
||||
const Scene *scene = CTX_data_scene(C);
|
||||
|
@ -1113,33 +1049,6 @@ static void rna_BrushGpencilSettings_use_material_pin_update(bContext *C, Pointe
|
|||
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_PROPERTIES, nullptr);
|
||||
}
|
||||
|
||||
static void rna_BrushGpencilSettings_eraser_mode_update(Main * /*bmain*/,
|
||||
Scene *scene,
|
||||
PointerRNA * /*ptr*/)
|
||||
{
|
||||
ToolSettings *ts = scene->toolsettings;
|
||||
Paint *paint = &ts->gp_paint->paint;
|
||||
Brush *brush = BKE_paint_brush(paint);
|
||||
|
||||
/* set eraser icon */
|
||||
if ((brush) && (brush->gpencil_tool == GPAINT_TOOL_ERASE)) {
|
||||
switch (brush->gpencil_settings->eraser_mode) {
|
||||
case GP_BRUSH_ERASER_SOFT:
|
||||
brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_SOFT;
|
||||
break;
|
||||
case GP_BRUSH_ERASER_HARD:
|
||||
brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_HARD;
|
||||
break;
|
||||
case GP_BRUSH_ERASER_STROKE:
|
||||
brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_STROKE;
|
||||
break;
|
||||
default:
|
||||
brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_SOFT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool rna_BrushGpencilSettings_material_poll(PointerRNA * /*ptr*/, PointerRNA value)
|
||||
{
|
||||
Material *ma = (Material *)value.data;
|
||||
|
@ -1722,32 +1631,6 @@ static void rna_def_gpencil_options(BlenderRNA *brna)
|
|||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, nullptr);
|
||||
|
||||
/* brush standard icon */
|
||||
prop = RNA_def_property(srna, "gpencil_paint_icon", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, nullptr, "icon_id");
|
||||
RNA_def_property_enum_items(prop, rna_enum_gpencil_brush_paint_icons_items);
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_ui_text(prop, "Grease Pencil Icon", "");
|
||||
|
||||
prop = RNA_def_property(srna, "gpencil_sculpt_icon", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, nullptr, "icon_id");
|
||||
RNA_def_property_enum_items(prop, rna_enum_gpencil_brush_sculpt_icons_items);
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_GPENCIL);
|
||||
RNA_def_property_ui_text(prop, "Grease Pencil Icon", "");
|
||||
|
||||
prop = RNA_def_property(srna, "gpencil_weight_icon", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, nullptr, "icon_id");
|
||||
RNA_def_property_enum_items(prop, rna_enum_gpencil_brush_weight_icons_items);
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_ui_text(prop, "Grease Pencil Icon", "");
|
||||
|
||||
prop = RNA_def_property(srna, "gpencil_vertex_icon", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, nullptr, "icon_id");
|
||||
RNA_def_property_enum_items(prop, rna_enum_gpencil_brush_vertex_icons_items);
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_ui_text(prop, "Grease Pencil Icon", "");
|
||||
|
||||
/* Mode type. */
|
||||
prop = RNA_def_property(srna, "vertex_mode", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_bitflag_sdna(prop, nullptr, "vertex_mode");
|
||||
|
@ -1944,8 +1827,7 @@ static void rna_def_gpencil_options(BlenderRNA *brna)
|
|||
RNA_def_property_ui_text(prop, "Mode", "Eraser Mode");
|
||||
RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_GPENCIL);
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_update(
|
||||
prop, NC_GPENCIL | ND_DATA, "rna_BrushGpencilSettings_eraser_mode_update");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, nullptr);
|
||||
|
||||
prop = RNA_def_property(srna, "caps_type", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, nullptr, "caps_type");
|
||||
|
@ -2080,16 +1962,6 @@ static void rna_def_gpencil_options(BlenderRNA *brna)
|
|||
RNA_def_property_ui_text(prop, "Limit to Viewport", "Fill only visible areas in viewport");
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
|
||||
prop = RNA_def_property(srna, "use_default_eraser", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "flag", GP_BRUSH_DEFAULT_ERASER);
|
||||
RNA_def_property_boolean_default(prop, true);
|
||||
RNA_def_property_ui_icon(prop, ICON_UNPINNED, 1);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Default Eraser", "Use this brush when enable eraser with fast switch key");
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_update(
|
||||
prop, NC_GPENCIL | ND_DATA, "rna_BrushGpencilSettings_default_eraser_update");
|
||||
|
||||
prop = RNA_def_property(srna, "use_settings_postprocess", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "flag", GP_BRUSH_GROUP_SETTINGS);
|
||||
RNA_def_property_ui_text(
|
||||
|
|
|
@ -271,103 +271,56 @@ static std::optional<std::string> rna_ParticleEdit_path(const PointerRNA * /*ptr
|
|||
return "tool_settings.particle_edit";
|
||||
}
|
||||
|
||||
static bool rna_Brush_mode_poll(PointerRNA *ptr, PointerRNA value)
|
||||
static PointerRNA rna_Paint_brush_get(PointerRNA *ptr)
|
||||
{
|
||||
Paint *paint = static_cast<Paint *>(ptr->data);
|
||||
Brush *brush = BKE_paint_brush(paint);
|
||||
if (!brush) {
|
||||
return PointerRNA_NULL;
|
||||
}
|
||||
return RNA_id_pointer_create(&brush->id);
|
||||
}
|
||||
|
||||
static void rna_Paint_brush_set(PointerRNA *ptr, PointerRNA value, ReportList * /*reports*/)
|
||||
{
|
||||
Paint *paint = static_cast<Paint *>(ptr->data);
|
||||
Brush *brush = static_cast<Brush *>(value.data);
|
||||
BKE_paint_brush_set(paint, brush);
|
||||
BKE_paint_invalidate_overlay_all();
|
||||
}
|
||||
|
||||
static bool rna_Paint_brush_poll(PointerRNA *ptr, PointerRNA value)
|
||||
{
|
||||
const Paint *paint = static_cast<Paint *>(ptr->data);
|
||||
Brush *brush = (Brush *)value.owner_id;
|
||||
const uint tool_offset = paint->runtime.tool_offset;
|
||||
const eObjectMode ob_mode = eObjectMode(paint->runtime.ob_mode);
|
||||
UNUSED_VARS_NDEBUG(tool_offset);
|
||||
BLI_assert(tool_offset && ob_mode);
|
||||
const Brush *brush = static_cast<Brush *>(value.data);
|
||||
|
||||
if (brush->ob_mode & ob_mode) {
|
||||
if (paint->brush) {
|
||||
if (BKE_brush_tool_get(paint->brush, paint) == BKE_brush_tool_get(brush, paint)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return (brush == nullptr) || (paint->runtime.ob_mode & brush->ob_mode) != 0;
|
||||
}
|
||||
|
||||
static bool paint_contains_brush_slot(const Paint *paint, const PaintToolSlot *tslot, int *r_index)
|
||||
static PointerRNA rna_Paint_eraser_brush_get(PointerRNA *ptr)
|
||||
{
|
||||
if ((tslot >= paint->tool_slots) && (tslot < (paint->tool_slots + paint->tool_slots_len))) {
|
||||
*r_index = int(tslot - paint->tool_slots);
|
||||
return true;
|
||||
Paint *paint = static_cast<Paint *>(ptr->data);
|
||||
Brush *brush = BKE_paint_eraser_brush(paint);
|
||||
if (!brush) {
|
||||
return PointerRNA_NULL;
|
||||
}
|
||||
return false;
|
||||
return RNA_id_pointer_create(&brush->id);
|
||||
}
|
||||
|
||||
static bool rna_Brush_mode_with_tool_poll(PointerRNA *ptr, PointerRNA value)
|
||||
static void rna_Paint_eraser_brush_set(PointerRNA *ptr, PointerRNA value, ReportList * /*reports*/)
|
||||
{
|
||||
Scene *scene = (Scene *)ptr->owner_id;
|
||||
const PaintToolSlot *tslot = static_cast<PaintToolSlot *>(ptr->data);
|
||||
ToolSettings *ts = scene->toolsettings;
|
||||
Brush *brush = (Brush *)value.owner_id;
|
||||
int mode = 0;
|
||||
int slot_index = 0;
|
||||
Paint *paint = static_cast<Paint *>(ptr->data);
|
||||
Brush *brush = static_cast<Brush *>(value.data);
|
||||
BKE_paint_eraser_brush_set(paint, brush);
|
||||
BKE_paint_invalidate_overlay_all();
|
||||
}
|
||||
|
||||
if (paint_contains_brush_slot(&ts->imapaint.paint, tslot, &slot_index)) {
|
||||
if (slot_index != brush->imagepaint_tool) {
|
||||
return false;
|
||||
}
|
||||
mode = OB_MODE_TEXTURE_PAINT;
|
||||
}
|
||||
else if (paint_contains_brush_slot(&ts->sculpt->paint, tslot, &slot_index)) {
|
||||
if (slot_index != brush->sculpt_tool) {
|
||||
return false;
|
||||
}
|
||||
mode = OB_MODE_SCULPT;
|
||||
}
|
||||
else if (paint_contains_brush_slot(&ts->vpaint->paint, tslot, &slot_index)) {
|
||||
if (slot_index != brush->vertexpaint_tool) {
|
||||
return false;
|
||||
}
|
||||
mode = OB_MODE_VERTEX_PAINT;
|
||||
}
|
||||
else if (paint_contains_brush_slot(&ts->wpaint->paint, tslot, &slot_index)) {
|
||||
if (slot_index != brush->weightpaint_tool) {
|
||||
return false;
|
||||
}
|
||||
mode = OB_MODE_WEIGHT_PAINT;
|
||||
}
|
||||
else if (paint_contains_brush_slot(&ts->gp_paint->paint, tslot, &slot_index)) {
|
||||
if (slot_index != brush->gpencil_tool) {
|
||||
return false;
|
||||
}
|
||||
mode = OB_MODE_PAINT_GPENCIL_LEGACY;
|
||||
}
|
||||
else if (paint_contains_brush_slot(&ts->gp_vertexpaint->paint, tslot, &slot_index)) {
|
||||
if (slot_index != brush->gpencil_vertex_tool) {
|
||||
return false;
|
||||
}
|
||||
mode = OB_MODE_VERTEX_GPENCIL_LEGACY;
|
||||
}
|
||||
else if (paint_contains_brush_slot(&ts->gp_sculptpaint->paint, tslot, &slot_index)) {
|
||||
if (slot_index != brush->gpencil_sculpt_tool) {
|
||||
return false;
|
||||
}
|
||||
mode = OB_MODE_SCULPT_GPENCIL_LEGACY;
|
||||
}
|
||||
else if (paint_contains_brush_slot(&ts->gp_weightpaint->paint, tslot, &slot_index)) {
|
||||
if (slot_index != brush->gpencil_weight_tool) {
|
||||
return false;
|
||||
}
|
||||
mode = OB_MODE_WEIGHT_GPENCIL_LEGACY;
|
||||
}
|
||||
else if (paint_contains_brush_slot(&ts->curves_sculpt->paint, tslot, &slot_index)) {
|
||||
if (slot_index != brush->curves_sculpt_tool) {
|
||||
return false;
|
||||
}
|
||||
mode = OB_MODE_SCULPT_CURVES;
|
||||
}
|
||||
static bool rna_Paint_eraser_brush_poll(PointerRNA *ptr, PointerRNA value)
|
||||
{
|
||||
const Paint *paint = static_cast<Paint *>(ptr->data);
|
||||
const Brush *brush = static_cast<Brush *>(value.data);
|
||||
|
||||
return brush->ob_mode & mode;
|
||||
return (brush == nullptr) || (paint->runtime.ob_mode & brush->ob_mode) != 0;
|
||||
}
|
||||
|
||||
static void rna_Sculpt_update(bContext *C, PointerRNA * /*ptr*/)
|
||||
|
@ -443,16 +396,6 @@ static std::optional<std::string> rna_ParticleBrush_path(const PointerRNA * /*pt
|
|||
return "tool_settings.particle_edit.brush";
|
||||
}
|
||||
|
||||
static void rna_Paint_brush_update(Main * /*bmain*/, Scene * /*scene*/, PointerRNA *ptr)
|
||||
{
|
||||
Paint *paint = static_cast<Paint *>(ptr->data);
|
||||
Brush *br = paint->brush;
|
||||
BKE_paint_invalidate_overlay_all();
|
||||
/* Needed because we're not calling 'BKE_paint_brush_set' which handles this. */
|
||||
BKE_paint_toolslots_brush_update(paint);
|
||||
WM_main_add_notifier(NC_BRUSH | NA_SELECTED, br);
|
||||
}
|
||||
|
||||
static void rna_ImaPaint_viewport_update(Main * /*bmain*/, Scene * /*scene*/, PointerRNA * /*ptr*/)
|
||||
{
|
||||
/* not the best solution maybe, but will refresh the 3D viewport */
|
||||
|
@ -600,20 +543,6 @@ static void rna_def_paint_curve(BlenderRNA *brna)
|
|||
RNA_def_struct_ui_icon(srna, ICON_CURVE_BEZCURVE);
|
||||
}
|
||||
|
||||
static void rna_def_paint_tool_slot(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
|
||||
srna = RNA_def_struct(brna, "PaintToolSlot", nullptr);
|
||||
RNA_def_struct_ui_text(srna, "Paint Tool Slot", "");
|
||||
|
||||
prop = RNA_def_property(srna, "brush", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_pointer_funcs(prop, nullptr, nullptr, nullptr, "rna_Brush_mode_with_tool_poll");
|
||||
RNA_def_property_ui_text(prop, "Brush", "");
|
||||
}
|
||||
|
||||
static void rna_def_paint(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
|
@ -625,25 +554,38 @@ static void rna_def_paint(BlenderRNA *brna)
|
|||
/* Global Settings */
|
||||
prop = RNA_def_property(srna, "brush", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_pointer_funcs(prop, nullptr, nullptr, nullptr, "rna_Brush_mode_poll");
|
||||
RNA_def_property_ui_text(prop, "Brush", "Active Brush");
|
||||
RNA_def_property_update(prop, 0, "rna_Paint_brush_update");
|
||||
RNA_def_property_struct_type(prop, "Brush");
|
||||
RNA_def_property_pointer_funcs(
|
||||
prop, "rna_Paint_brush_get", "rna_Paint_brush_set", nullptr, "rna_Paint_brush_poll");
|
||||
RNA_def_property_ui_text(prop, "Brush", "Active brush");
|
||||
RNA_def_property_update(prop, NC_BRUSH | NA_SELECTED, nullptr);
|
||||
|
||||
/* paint_tool_slots */
|
||||
prop = RNA_def_property(srna, "tool_slots", PROP_COLLECTION, PROP_NONE);
|
||||
RNA_def_property_collection_sdna(prop, nullptr, "tool_slots", "tool_slots_len");
|
||||
RNA_def_property_struct_type(prop, "PaintToolSlot");
|
||||
/* don't dereference pointer! */
|
||||
RNA_def_property_collection_funcs(prop,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
"rna_iterator_array_get",
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr);
|
||||
RNA_def_property_ui_text(prop, "Paint Tool Slots", "");
|
||||
prop = RNA_def_property(srna, "brush_asset_reference", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Brush Asset Reference",
|
||||
"A weak reference to the matching brush asset, used e.g. to restore "
|
||||
"the last used brush on file load");
|
||||
|
||||
prop = RNA_def_property(srna, "eraser_brush", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_struct_type(prop, "Brush");
|
||||
RNA_def_property_pointer_funcs(prop,
|
||||
"rna_Paint_eraser_brush_get",
|
||||
"rna_Paint_eraser_brush_set",
|
||||
nullptr,
|
||||
"rna_Paint_eraser_brush_poll");
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Default Eraser Brush",
|
||||
"Default eraser bnush for quickly alternating with the main brush");
|
||||
RNA_def_property_update(prop, NC_BRUSH | NA_SELECTED, nullptr);
|
||||
|
||||
prop = RNA_def_property(srna, "eraser_brush_asset_reference", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Eraser Brush Asset Reference",
|
||||
"A weak reference to the matching brush asset, used e.g. to restore "
|
||||
"the last used brush on file load");
|
||||
|
||||
prop = RNA_def_property(srna, "palette", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE);
|
||||
|
@ -1706,7 +1648,6 @@ void RNA_def_sculpt_paint(BlenderRNA *brna)
|
|||
/* *** Non-Animated *** */
|
||||
RNA_define_animate_sdna(false);
|
||||
rna_def_paint_curve(brna);
|
||||
rna_def_paint_tool_slot(brna);
|
||||
rna_def_paint(brna);
|
||||
rna_def_sculpt(brna);
|
||||
rna_def_uv_sculpt(brna);
|
||||
|
|
|
@ -1360,6 +1360,7 @@ void wm_homefile_read_ex(bContext *C,
|
|||
if (BLI_access(filepath_startup, R_OK) == 0) {
|
||||
BlendFileReadParams params{};
|
||||
params.is_startup = true;
|
||||
params.is_factory_settings = use_factory_settings;
|
||||
params.skip_flags = skip_flags | BLO_READ_SKIP_USERDEF;
|
||||
BlendFileReadReport bf_reports{};
|
||||
bf_reports.reports = reports;
|
||||
|
@ -1402,6 +1403,7 @@ void wm_homefile_read_ex(bContext *C,
|
|||
if (success == false) {
|
||||
BlendFileReadParams read_file_params{};
|
||||
read_file_params.is_startup = true;
|
||||
read_file_params.is_factory_settings = use_factory_settings;
|
||||
read_file_params.skip_flags = skip_flags;
|
||||
BlendFileData *bfd = BKE_blendfile_read_from_memory(
|
||||
datatoc_startup_blend, datatoc_startup_blend_size, &read_file_params, nullptr);
|
||||
|
@ -3632,7 +3634,7 @@ static int wm_save_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *
|
|||
}
|
||||
|
||||
if (blendfile_path[0] != '\0') {
|
||||
if (CTX_data_main(C)->has_forward_compatibility_issues) {
|
||||
if (BKE_main_needs_overwrite_confirm(CTX_data_main(C))) {
|
||||
wm_save_file_overwrite_dialog(C, op);
|
||||
ret = OPERATOR_INTERFACE;
|
||||
}
|
||||
|
@ -3994,31 +3996,43 @@ static void file_overwrite_detailed_info_show(uiLayout *parent_layout, Main *bma
|
|||
* block. */
|
||||
uiLayoutSetScaleY(layout, 0.70f);
|
||||
|
||||
char writer_ver_str[16];
|
||||
char current_ver_str[16];
|
||||
if (bmain->versionfile == BLENDER_VERSION) {
|
||||
BKE_blender_version_blendfile_string_from_values(
|
||||
writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, bmain->subversionfile);
|
||||
BKE_blender_version_blendfile_string_from_values(
|
||||
current_ver_str, sizeof(current_ver_str), BLENDER_FILE_VERSION, BLENDER_FILE_SUBVERSION);
|
||||
}
|
||||
else {
|
||||
BKE_blender_version_blendfile_string_from_values(
|
||||
writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, -1);
|
||||
BKE_blender_version_blendfile_string_from_values(
|
||||
current_ver_str, sizeof(current_ver_str), BLENDER_VERSION, -1);
|
||||
if (bmain->has_forward_compatibility_issues) {
|
||||
char writer_ver_str[16];
|
||||
char current_ver_str[16];
|
||||
if (bmain->versionfile == BLENDER_VERSION) {
|
||||
BKE_blender_version_blendfile_string_from_values(
|
||||
writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, bmain->subversionfile);
|
||||
BKE_blender_version_blendfile_string_from_values(
|
||||
current_ver_str, sizeof(current_ver_str), BLENDER_FILE_VERSION, BLENDER_FILE_SUBVERSION);
|
||||
}
|
||||
else {
|
||||
BKE_blender_version_blendfile_string_from_values(
|
||||
writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, -1);
|
||||
BKE_blender_version_blendfile_string_from_values(
|
||||
current_ver_str, sizeof(current_ver_str), BLENDER_VERSION, -1);
|
||||
}
|
||||
|
||||
char message_line1[256];
|
||||
char message_line2[256];
|
||||
SNPRINTF(message_line1,
|
||||
RPT_("This file was saved by a newer version of Blender (%s)"),
|
||||
writer_ver_str);
|
||||
SNPRINTF(message_line2,
|
||||
RPT_("Saving it with this Blender (%s) may cause loss of data"),
|
||||
current_ver_str);
|
||||
uiItemL(layout, message_line1, ICON_NONE);
|
||||
uiItemL(layout, message_line2, ICON_NONE);
|
||||
}
|
||||
|
||||
char message_line1[256];
|
||||
char message_line2[256];
|
||||
SNPRINTF(message_line1,
|
||||
RPT_("This file was saved by a newer version of Blender (%s)"),
|
||||
writer_ver_str);
|
||||
SNPRINTF(message_line2,
|
||||
RPT_("Saving it with this Blender (%s) may cause loss of data"),
|
||||
current_ver_str);
|
||||
uiItemL(layout, message_line1, ICON_NONE);
|
||||
uiItemL(layout, message_line2, ICON_NONE);
|
||||
if (bmain->is_asset_repository) {
|
||||
if (bmain->has_forward_compatibility_issues) {
|
||||
uiItemS_ex(layout, 1.4f);
|
||||
}
|
||||
|
||||
uiItemL(layout, RPT_("This file is managed by the Blender asset system."), ICON_NONE);
|
||||
uiItemL(layout, RPT_("and is expected to contain a single asset data-block."), ICON_NONE);
|
||||
uiItemL(layout, RPT_("Take care to avoid data loss when editing assets."), ICON_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
static void save_file_overwrite_cancel(bContext *C, void *arg_block, void * /*arg_data*/)
|
||||
|
@ -4083,7 +4097,12 @@ static void save_file_overwrite_saveas(bContext *C, void *arg_block, void * /*ar
|
|||
wmWindow *win = CTX_wm_window(C);
|
||||
UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
|
||||
|
||||
WM_operator_name_call(C, "WM_OT_save_as_mainfile", WM_OP_INVOKE_DEFAULT, nullptr, nullptr);
|
||||
PointerRNA props_ptr;
|
||||
wmOperatorType *ot = WM_operatortype_find("WM_OT_save_as_mainfile", false);
|
||||
WM_operator_properties_create_ptr(&props_ptr, ot);
|
||||
|
||||
WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr, nullptr);
|
||||
WM_operator_properties_free(&props_ptr);
|
||||
}
|
||||
|
||||
static void save_file_overwrite_saveas_button(uiBlock *block, wmGenericCallback *post_action)
|
||||
|
@ -4119,8 +4138,27 @@ static uiBlock *block_create_save_file_overwrite_dialog(bContext *C, ARegion *re
|
|||
uiLayout *layout = uiItemsAlertBox(block, 34, ALERT_ICON_WARNING);
|
||||
|
||||
/* Title. */
|
||||
uiItemL_ex(
|
||||
layout, RPT_("Overwrite file with an older Blender version?"), ICON_NONE, true, false);
|
||||
if (bmain->has_forward_compatibility_issues) {
|
||||
if (bmain->is_asset_repository) {
|
||||
uiItemL_ex(
|
||||
layout,
|
||||
RPT_("Convert asset blend file to regular blend file with an older Blender version?"),
|
||||
ICON_NONE,
|
||||
true,
|
||||
false);
|
||||
}
|
||||
else {
|
||||
uiItemL_ex(
|
||||
layout, RPT_("Overwrite file with an older Blender version?"), ICON_NONE, true, false);
|
||||
}
|
||||
}
|
||||
else if (bmain->is_asset_repository) {
|
||||
uiItemL_ex(
|
||||
layout, RPT_("Convert asset blend file to regular blend file?"), ICON_NONE, true, false);
|
||||
}
|
||||
else {
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
|
||||
/* Filename. */
|
||||
const char *blendfile_path = BKE_main_blendfile_path(CTX_data_main(C));
|
||||
|
@ -4332,7 +4370,7 @@ static uiBlock *block_create__close_file_dialog(bContext *C, ARegion *region, vo
|
|||
|
||||
uiLayout *layout = uiItemsAlertBox(block, 34, ALERT_ICON_QUESTION);
|
||||
|
||||
const bool needs_overwrite_confirm = bmain->has_forward_compatibility_issues;
|
||||
const bool needs_overwrite_confirm = BKE_main_needs_overwrite_confirm(bmain);
|
||||
|
||||
/* Title. */
|
||||
uiItemL_ex(layout, RPT_("Save changes before closing?"), ICON_NONE, true, false);
|
||||
|
|
|
@ -210,7 +210,6 @@ wmKeyMap *WM_keymap_guess_from_context(const bContext *C)
|
|||
wmKeyMap *WM_keymap_guess_opname(const bContext *C, const char *opname)
|
||||
{
|
||||
/* Op types purposely skipped for now:
|
||||
* BRUSH_OT
|
||||
* BOID_OT
|
||||
* BUTTONS_OT
|
||||
* CONSTRAINT_OT
|
||||
|
@ -336,7 +335,7 @@ wmKeyMap *WM_keymap_guess_opname(const bContext *C, const char *opname)
|
|||
km = WM_keymap_find_all(
|
||||
wm, "Paint Face Mask (Weight, Vertex, Texture)", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
}
|
||||
else if (STRPREFIX(opname, "PAINT_OT")) {
|
||||
else if (STRPREFIX(opname, "PAINT_OT") || STRPREFIX(opname, "BRUSH_OT")) {
|
||||
/* Check for relevant mode. */
|
||||
switch (CTX_data_mode_enum(C)) {
|
||||
case CTX_MODE_PAINT_WEIGHT:
|
||||
|
|
|
@ -174,43 +174,6 @@ static void toolsystem_ref_link(bContext *C, WorkSpace *workspace, bToolRef *tre
|
|||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
const PaintMode paint_mode = BKE_paintmode_get_from_tool(tref);
|
||||
BLI_assert(paint_mode != PaintMode::Invalid);
|
||||
const EnumPropertyItem *items = BKE_paint_get_tool_enum_from_paintmode(paint_mode);
|
||||
BLI_assert(items != nullptr);
|
||||
|
||||
const int i = items ? RNA_enum_from_identifier(items, tref_rt->data_block) : -1;
|
||||
if (i != -1) {
|
||||
const int slot_index = items[i].value;
|
||||
wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
|
||||
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
|
||||
if (workspace == WM_window_get_active_workspace(win)) {
|
||||
Scene *scene = WM_window_get_active_scene(win);
|
||||
BKE_paint_ensure_from_paintmode(bmain, scene, paint_mode);
|
||||
Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode);
|
||||
Brush *brush = BKE_paint_toolslots_brush_get(paint, slot_index);
|
||||
if (brush == nullptr) {
|
||||
/* Could make into a function. */
|
||||
brush = (Brush *)BKE_libblock_find_name(bmain, ID_BR, items[i].name);
|
||||
if (brush && slot_index == BKE_brush_tool_get(brush, paint)) {
|
||||
/* Pass. */
|
||||
}
|
||||
else {
|
||||
brush = BKE_brush_add(bmain, items[i].name, eObjectMode(paint->runtime.ob_mode));
|
||||
|
||||
BKE_brush_tool_set(brush, paint, slot_index);
|
||||
|
||||
if (paint_mode == PaintMode::Sculpt) {
|
||||
BKE_brush_sculpt_reset(brush);
|
||||
}
|
||||
}
|
||||
}
|
||||
BKE_paint_brush_set(paint, brush);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -383,25 +346,6 @@ void WM_toolsystem_ref_sync_from_context(Main *bmain, WorkSpace *workspace, bToo
|
|||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
const PaintMode paint_mode = BKE_paintmode_get_from_tool(tref);
|
||||
Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode);
|
||||
const EnumPropertyItem *items = BKE_paint_get_tool_enum_from_paintmode(paint_mode);
|
||||
if (paint && paint->brush && items) {
|
||||
const ID *brush = (ID *)paint->brush;
|
||||
const char tool_type = BKE_brush_tool_get((Brush *)brush, paint);
|
||||
const int i = RNA_enum_from_value(items, tool_type);
|
||||
/* Possible when loading files from the future. */
|
||||
if (i != -1) {
|
||||
const char *name = items[i].name;
|
||||
const char *identifier = items[i].identifier;
|
||||
if (!STREQ(tref_rt->data_block, identifier)) {
|
||||
STRNCPY(tref_rt->data_block, identifier);
|
||||
SNPRINTF(tref->idname, "builtin_brush.%s", name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -711,26 +655,19 @@ static const char *toolsystem_default_tool(const bToolKey *tkey)
|
|||
switch (tkey->space_type) {
|
||||
case SPACE_VIEW3D:
|
||||
switch (tkey->mode) {
|
||||
/* Use the names of the enums for each brush tool. */
|
||||
case CTX_MODE_SCULPT:
|
||||
case CTX_MODE_PAINT_VERTEX:
|
||||
case CTX_MODE_PAINT_WEIGHT:
|
||||
case CTX_MODE_PAINT_TEXTURE:
|
||||
case CTX_MODE_PAINT_GPENCIL_LEGACY:
|
||||
case CTX_MODE_PAINT_GREASE_PENCIL:
|
||||
return "builtin_brush.Draw";
|
||||
case CTX_MODE_SCULPT_GPENCIL_LEGACY:
|
||||
case CTX_MODE_SCULPT_GREASE_PENCIL:
|
||||
return "builtin_brush.Push";
|
||||
case CTX_MODE_WEIGHT_GPENCIL_LEGACY:
|
||||
case CTX_MODE_WEIGHT_GREASE_PENCIL:
|
||||
return "builtin_brush.Weight";
|
||||
case CTX_MODE_VERTEX_GPENCIL_LEGACY:
|
||||
return "builtin_brush.Draw";
|
||||
case CTX_MODE_SCULPT_CURVES:
|
||||
return "builtin_brush.Density";
|
||||
/* End temporary hack. */
|
||||
|
||||
return "builtin.brush";
|
||||
case CTX_MODE_PARTICLE:
|
||||
return "builtin_brush.Comb";
|
||||
case CTX_MODE_EDIT_TEXT:
|
||||
|
@ -740,7 +677,7 @@ static const char *toolsystem_default_tool(const bToolKey *tkey)
|
|||
case SPACE_IMAGE:
|
||||
switch (tkey->mode) {
|
||||
case SI_MODE_PAINT:
|
||||
return "builtin_brush.Draw";
|
||||
return "builtin.brush";
|
||||
}
|
||||
break;
|
||||
case SPACE_NODE: {
|
||||
|
|
|
@ -1701,6 +1701,12 @@ install(
|
|||
|
||||
set(ASSET_BUNDLE_DIR ${CMAKE_SOURCE_DIR}/release/datafiles/assets/publish/)
|
||||
|
||||
# TODO temporary change for development only. Remove before merging.
|
||||
set(ASSET_BUNDLE_TESTING_DIR "${ASSET_BUNDLE_DIR}/../testing/")
|
||||
if(EXISTS "${ASSET_BUNDLE_TESTING_DIR}")
|
||||
set(ASSET_BUNDLE_DIR "${ASSET_BUNDLE_TESTING_DIR}")
|
||||
endif()
|
||||
|
||||
if(EXISTS "${ASSET_BUNDLE_DIR}")
|
||||
install(
|
||||
DIRECTORY ${ASSET_BUNDLE_DIR}
|
||||
|
|
Loading…
Reference in New Issue