WIP: Brush assets project #106303
|
@ -1 +1 @@
|
|||
Subproject commit 7181d6dccb9fe4184340f9f5b1c381f8089fe4ec
|
||||
Subproject commit 1c9b964b58948b369a5d5c84b35db809fc843d45
|
|
@ -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.
|
||||
|
|
|
@ -279,6 +279,10 @@ def op_panel(menu, kmi_args, kmi_data=()):
|
|||
return ("wm.call_panel", kmi_args, {"properties": [("name", menu), *kmi_data]})
|
||||
|
||||
|
||||
def op_asset_shelf_popup(asset_shelf, kmi_args):
|
||||
return ("wm.call_asset_shelf_popover", kmi_args, {"properties": [("name", asset_shelf)]})
|
||||
|
||||
|
||||
def op_tool(tool, kmi_args):
|
||||
return ("wm.tool_set_by_id", kmi_args, {"properties": [("name", tool)]})
|
||||
|
||||
|
@ -3906,6 +3910,10 @@ def km_gpencil_legacy_stroke_paint_mode(params):
|
|||
("gpencil.select_box", {"type": 'B', "value": 'PRESS'}, None),
|
||||
# Lasso erase
|
||||
("gpencil.select_lasso", {"type": params.action_mouse, "value": 'CLICK_DRAG', "ctrl": True, "alt": True}, None),
|
||||
op_asset_shelf_popup(
|
||||
"VIEW3D_AST_brush_gpencil_paint",
|
||||
{"type": 'SPACE', "value": 'PRESS', "shift": True}
|
||||
),
|
||||
])
|
||||
|
||||
return keymap
|
||||
|
@ -4072,6 +4080,10 @@ def km_gpencil_legacy_stroke_sculpt_mode(params):
|
|||
"VIEW3D_MT_sculpt_gpencil_automasking_pie",
|
||||
{"type": 'A', "shift": True, "alt": True, "value": 'PRESS'},
|
||||
),
|
||||
op_asset_shelf_popup(
|
||||
"VIEW3D_AST_brush_gpencil_sculpt",
|
||||
{"type": 'SPACE', "value": 'PRESS', "shift": True}
|
||||
),
|
||||
])
|
||||
|
||||
return keymap
|
||||
|
@ -4299,6 +4311,10 @@ def km_gpencil_legacy_stroke_weight_mode(params):
|
|||
("gpencil.weight_toggle_direction", {"type": 'D', "value": 'PRESS'}, None),
|
||||
# Weight sample
|
||||
("gpencil.weight_sample", {"type": 'X', "value": 'PRESS', "shift": True}, None),
|
||||
op_asset_shelf_popup(
|
||||
"VIEW3D_AST_brush_gpencil_weight",
|
||||
{"type": 'SPACE', "value": 'PRESS', "shift": True}
|
||||
),
|
||||
])
|
||||
|
||||
if params.select_mouse == 'LEFTMOUSE':
|
||||
|
@ -4428,6 +4444,10 @@ def km_gpencil_legacy_stroke_vertex_mode(params):
|
|||
|
||||
# Vertex Paint context menu
|
||||
op_panel("VIEW3D_PT_gpencil_vertex_context_menu", params.context_menu_event),
|
||||
op_asset_shelf_popup(
|
||||
"VIEW3D_AST_brush_gpencil_vertex",
|
||||
{"type": 'SPACE', "value": 'PRESS', "shift": True}
|
||||
),
|
||||
])
|
||||
|
||||
return keymap
|
||||
|
@ -4579,6 +4599,11 @@ def km_grease_pencil_paint_mode(_params):
|
|||
|
||||
# Isolate Layer
|
||||
("grease_pencil.layer_isolate", {"type": 'NUMPAD_ASTERIX', "value": 'PRESS'}, None),
|
||||
|
||||
op_asset_shelf_popup(
|
||||
"VIEW3D_AST_brush_gpencil_paint",
|
||||
{"type": 'SPACE', "value": 'PRESS', "shift": True}
|
||||
),
|
||||
])
|
||||
|
||||
return keymap
|
||||
|
@ -4721,6 +4746,10 @@ def km_grease_pencil_sculpt_mode(params):
|
|||
("grease_pencil.sculpt_paint", {"type": 'LEFTMOUSE', "value": 'PRESS',
|
||||
"shift": True}, {"properties": [("mode", 'SMOOTH')]}),
|
||||
*_template_paint_radial_control("gpencil_sculpt_paint"),
|
||||
op_asset_shelf_popup(
|
||||
"VIEW3D_AST_brush_gpencil_sculpt",
|
||||
{"type": 'SPACE', "value": 'PRESS', "shift": True}
|
||||
),
|
||||
])
|
||||
|
||||
return keymap
|
||||
|
@ -4760,6 +4789,11 @@ def km_grease_pencil_weight_paint(params):
|
|||
|
||||
# Show/hide layer
|
||||
*_template_items_hide_reveal_actions("grease_pencil.layer_hide", "grease_pencil.layer_reveal"),
|
||||
|
||||
op_asset_shelf_popup(
|
||||
"VIEW3D_AST_brush_gpencil_weight",
|
||||
{"type": 'SPACE', "value": 'PRESS', "shift": True}
|
||||
),
|
||||
])
|
||||
|
||||
if params.select_mouse == 'LEFTMOUSE':
|
||||
|
@ -5087,6 +5121,10 @@ def km_paint_curve(params):
|
|||
("transform.translate", {"type": params.select_mouse, "value": 'CLICK_DRAG'}, None),
|
||||
("transform.rotate", {"type": 'R', "value": 'PRESS'}, None),
|
||||
("transform.resize", {"type": 'S', "value": 'PRESS'}, None),
|
||||
op_asset_shelf_popup(
|
||||
"VIEW3D_AST_brush_sculpt_curves",
|
||||
{"type": 'SPACE', "value": 'PRESS', "shift": True}
|
||||
),
|
||||
])
|
||||
|
||||
return keymap
|
||||
|
@ -5364,6 +5402,14 @@ def km_image_paint(params):
|
|||
("wm.context_menu_enum", {"type": 'E', "value": 'PRESS', "alt": True},
|
||||
{"properties": [("data_path", 'tool_settings.image_paint.brush.stroke_method')]}),
|
||||
*_template_items_context_panel("VIEW3D_PT_paint_texture_context_menu", params.context_menu_event),
|
||||
op_asset_shelf_popup(
|
||||
"VIEW3D_AST_brush_texture_paint",
|
||||
{"type": 'SPACE', "value": 'PRESS', "shift": True}
|
||||
),
|
||||
op_asset_shelf_popup(
|
||||
"IMAGE_AST_brush_paint",
|
||||
{"type": 'SPACE', "value": 'PRESS', "shift": True}
|
||||
),
|
||||
])
|
||||
|
||||
if params.legacy:
|
||||
|
@ -5414,6 +5460,10 @@ def km_vertex_paint(params):
|
|||
{"properties": [("data_path", 'tool_settings.vertex_paint.brush.stroke_method')]}),
|
||||
("paint.face_vert_reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None),
|
||||
*_template_items_context_panel("VIEW3D_PT_paint_vertex_context_menu", params.context_menu_event),
|
||||
op_asset_shelf_popup(
|
||||
"VIEW3D_AST_brush_vertex_paint",
|
||||
{"type": 'SPACE', "value": 'PRESS', "shift": True}
|
||||
),
|
||||
])
|
||||
|
||||
if params.legacy:
|
||||
|
@ -5471,6 +5521,10 @@ def km_weight_paint(params):
|
|||
op_menu_pie("VIEW3D_MT_wpaint_vgroup_lock_pie", {"type": 'K', "value": 'PRESS'}),
|
||||
("paint.face_vert_reveal", {"type": 'H', "value": 'PRESS', "alt": True}, None),
|
||||
*_template_items_context_panel("VIEW3D_PT_paint_weight_context_menu", params.context_menu_event),
|
||||
op_asset_shelf_popup(
|
||||
"VIEW3D_AST_brush_weight_paint",
|
||||
{"type": 'SPACE', "value": 'PRESS', "shift": True}
|
||||
),
|
||||
])
|
||||
|
||||
if params.select_mouse == 'LEFTMOUSE':
|
||||
|
@ -5666,27 +5720,41 @@ def km_sculpt(params):
|
|||
op_menu_pie("VIEW3D_MT_sculpt_automasking_pie", {"type": 'A', "alt": True, "value": 'PRESS'}),
|
||||
op_menu_pie("VIEW3D_MT_sculpt_face_sets_edit_pie", {"type": 'W', "value": 'PRESS', "alt": True}),
|
||||
*_template_items_context_panel("VIEW3D_PT_sculpt_context_menu", params.context_menu_event),
|
||||
# Tools
|
||||
("paint.brush_select", {"type": 'V', "value": 'PRESS'},
|
||||
{"properties": [("sculpt_tool", 'DRAW')]}),
|
||||
("paint.brush_select", {"type": 'S', "value": 'PRESS'},
|
||||
{"properties": [("sculpt_tool", 'SMOOTH')]}),
|
||||
("paint.brush_select", {"type": 'P', "value": 'PRESS'},
|
||||
{"properties": [("sculpt_tool", 'PINCH')]}),
|
||||
("paint.brush_select", {"type": 'I', "value": 'PRESS'},
|
||||
{"properties": [("sculpt_tool", 'INFLATE')]}),
|
||||
("paint.brush_select", {"type": 'G', "value": 'PRESS'},
|
||||
{"properties": [("sculpt_tool", 'GRAB')]}),
|
||||
("paint.brush_select", {"type": 'T', "value": 'PRESS', "shift": True},
|
||||
{"properties": [("sculpt_tool", 'SCRAPE')]}),
|
||||
("paint.brush_select", {"type": 'C', "value": 'PRESS'},
|
||||
{"properties": [("sculpt_tool", 'CLAY_STRIPS')]}),
|
||||
("paint.brush_select", {"type": 'C', "value": 'PRESS', "shift": True},
|
||||
{"properties": [("sculpt_tool", 'CREASE')]}),
|
||||
("paint.brush_select", {"type": 'K', "value": 'PRESS'},
|
||||
{"properties": [("sculpt_tool", 'SNAKE_HOOK')]}),
|
||||
("paint.brush_select", {"type": 'M', "value": 'PRESS'},
|
||||
{"properties": [("sculpt_tool", 'MASK'), ("toggle", True), ("create_missing", True)]}),
|
||||
# Brushes
|
||||
("brush.asset_select", {"type": 'V', "value": 'PRESS'},
|
||||
{"properties": [("asset_library_type", 'ESSENTIALS'),
|
||||
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Draw")]}),
|
||||
("brush.asset_select", {"type": 'S', "value": 'PRESS'},
|
||||
{"properties": [("asset_library_type", 'ESSENTIALS'),
|
||||
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Smooth")]}),
|
||||
("brush.asset_select", {"type": 'P', "value": 'PRESS'},
|
||||
{"properties": [("asset_library_type", 'ESSENTIALS'),
|
||||
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Pinch/Magnify")]}),
|
||||
("brush.asset_select", {"type": 'I', "value": 'PRESS'},
|
||||
{"properties": [("asset_library_type", 'ESSENTIALS'),
|
||||
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Inflate/Deflate")]}),
|
||||
("brush.asset_select", {"type": 'G', "value": 'PRESS'},
|
||||
{"properties": [("asset_library_type", 'ESSENTIALS'),
|
||||
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Grab")]}),
|
||||
("brush.asset_select", {"type": 'T', "value": 'PRESS', "shift": True},
|
||||
{"properties": [("asset_library_type", 'ESSENTIALS'),
|
||||
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Scrape/Fill")]}),
|
||||
("brush.asset_select", {"type": 'C', "value": 'PRESS'},
|
||||
{"properties": [("asset_library_type", 'ESSENTIALS'),
|
||||
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Clay Strips")]}),
|
||||
("brush.asset_select", {"type": 'C', "value": 'PRESS', "shift": True},
|
||||
{"properties": [("asset_library_type", 'ESSENTIALS'),
|
||||
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Crease")]}),
|
||||
("brush.asset_select", {"type": 'K', "value": 'PRESS'},
|
||||
{"properties": [("asset_library_type", 'ESSENTIALS'),
|
||||
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Snake Hook")]}),
|
||||
("brush.asset_select", {"type": 'M', "value": 'PRESS'},
|
||||
{"properties": [("asset_library_type", 'ESSENTIALS'),
|
||||
("relative_asset_identifier", "brushes/essentials_brushes.blend/Brush/Mask")]}),
|
||||
op_asset_shelf_popup(
|
||||
"VIEW3D_AST_brush_sculpt",
|
||||
{"type": 'SPACE', "value": 'PRESS', "shift": True}
|
||||
),
|
||||
])
|
||||
|
||||
# Lasso Masking.
|
||||
|
|
|
@ -2,24 +2,76 @@
|
|||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
import bpy
|
||||
from bpy.types import Menu
|
||||
|
||||
|
||||
class BrushAssetShelf:
|
||||
bl_options = {'DEFAULT_VISIBLE', 'NO_ASSET_DRAG', 'STORE_ENABLED_CATALOGS_IN_PREFERENCES'}
|
||||
bl_activate_operator = "BRUSH_OT_asset_select"
|
||||
bl_default_preview_size = 48
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
prefs = context.preferences
|
||||
if not prefs.experimental.use_extended_asset_browser:
|
||||
return False
|
||||
|
||||
return context.mode == 'SCULPT'
|
||||
return context.object and context.object.mode == cls.mode
|
||||
|
||||
@classmethod
|
||||
def asset_poll(cls, asset):
|
||||
return asset.id_type == 'BRUSH'
|
||||
if asset.id_type != 'BRUSH':
|
||||
return False
|
||||
if hasattr(cls, "mode_prop"):
|
||||
return asset.metadata.get(cls.mode_prop, False)
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
def get_active_asset(cls):
|
||||
# Only show active highlight when using the brush tool.
|
||||
from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
|
||||
tool = ToolSelectPanelHelper.tool_active_from_context(bpy.context)
|
||||
if not tool or tool.idname != "builtin.brush":
|
||||
return None
|
||||
|
||||
paint_settings = UnifiedPaintPanel.paint_settings(bpy.context)
|
||||
return paint_settings.brush_asset_reference if paint_settings else None
|
||||
|
||||
@classmethod
|
||||
def draw_context_menu(self, context, asset, layout):
|
||||
# Currently this menu adds operators that deal with the affected brush and don't take the
|
||||
# asset into account. Luckily that is okay for now, since right clicking in the grid view
|
||||
# also activates the item.
|
||||
layout.menu_contents("VIEW3D_MT_brush_context_menu")
|
||||
|
||||
@staticmethod
|
||||
def get_shelf_name_from_context(context):
|
||||
mode_map = {
|
||||
'SCULPT': "VIEW3D_AST_brush_sculpt",
|
||||
'PAINT_VERTEX': "VIEW3D_AST_brush_vertex_paint",
|
||||
'PAINT_WEIGHT': "VIEW3D_AST_brush_weight_paint",
|
||||
'PAINT_TEXTURE': "VIEW3D_AST_brush_texture_paint",
|
||||
'PAINT_2D': "IMAGE_AST_brush_paint",
|
||||
'PAINT_GPENCIL': "VIEW3D_AST_brush_gpencil_paint",
|
||||
'SCULPT_GPENCIL': "VIEW3D_AST_brush_gpencil_sculpt",
|
||||
'WEIGHT_GPENCIL': "VIEW3D_AST_brush_gpencil_weight",
|
||||
'VERTEX_GPENCIL': "VIEW3D_AST_brush_gpencil_vertex",
|
||||
'PAINT_GREASE_PENCIL': "VIEW3D_AST_brush_gpencil_paint",
|
||||
'SCULPT_GREASE_PENCIL': "VIEW3D_AST_brush_gpencil_sculpt",
|
||||
'WEIGHT_GREASE_PENCIL': "VIEW3D_AST_brush_gpencil_weight",
|
||||
'VERTEX_GREASE_PENCIL': "VIEW3D_AST_brush_gpencil_vertex",
|
||||
'SCULPT_CURVES': "VIEW3D_AST_brush_sculpt_curves",
|
||||
}
|
||||
mode = UnifiedPaintPanel.get_brush_mode(context)
|
||||
return mode_map[mode]
|
||||
|
||||
@staticmethod
|
||||
def draw_popup_selector(layout, context, brush, show_name=True):
|
||||
preview_icon_id = brush.preview.icon_id if brush and brush.preview else 0
|
||||
|
||||
layout.template_asset_shelf_popover(
|
||||
BrushAssetShelf.get_shelf_name_from_context(context),
|
||||
name=brush.name if (brush and show_name) else None,
|
||||
icon='BRUSH_DATA' if not preview_icon_id else 'NONE',
|
||||
icon_value=preview_icon_id,
|
||||
)
|
||||
|
||||
|
||||
class UnifiedPaintPanel:
|
||||
|
@ -156,28 +208,29 @@ class BrushPanel(UnifiedPaintPanel):
|
|||
|
||||
|
||||
class BrushSelectPanel(BrushPanel):
|
||||
bl_label = "Brushes"
|
||||
bl_label = "Brush Asset"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
settings = self.paint_settings(context)
|
||||
if settings is None:
|
||||
return
|
||||
|
||||
brush = settings.brush
|
||||
|
||||
row = layout.row()
|
||||
large_preview = True
|
||||
if large_preview:
|
||||
row.column().template_ID_preview(settings, "brush", new="brush.add", rows=3, cols=8, hide_buttons=False)
|
||||
else:
|
||||
row.column().template_ID(settings, "brush", new="brush.add")
|
||||
|
||||
col = row.column(align=True)
|
||||
BrushAssetShelf.draw_popup_selector(col, context, brush, show_name=False)
|
||||
if brush:
|
||||
col.prop(brush, "name", text="")
|
||||
|
||||
if brush is None:
|
||||
return
|
||||
|
||||
col = row.column()
|
||||
col.menu("VIEW3D_MT_brush_context_menu", icon='DOWNARROW_HLT', text="")
|
||||
|
||||
if brush is not None:
|
||||
col.prop(brush, "use_custom_icon", toggle=True, icon='FILE_IMAGE', text="")
|
||||
|
||||
if brush.use_custom_icon:
|
||||
layout.prop(brush, "icon_filepath", text="")
|
||||
|
||||
|
||||
class ColorPalettePanel(BrushPanel):
|
||||
bl_label = "Color Palette"
|
||||
|
@ -994,6 +1047,9 @@ def brush_settings_advanced(layout, context, brush, popover=False):
|
|||
use_frontface = False
|
||||
|
||||
if mode == 'SCULPT':
|
||||
layout.prop(brush, "sculpt_tool")
|
||||
layout.separator()
|
||||
|
||||
capabilities = brush.sculpt_capabilities
|
||||
use_accumulate = capabilities.has_accumulate
|
||||
use_frontface = True
|
||||
|
@ -1084,6 +1140,9 @@ def brush_settings_advanced(layout, context, brush, popover=False):
|
|||
|
||||
# 3D and 2D Texture Paint.
|
||||
elif mode in {'PAINT_TEXTURE', 'PAINT_2D'}:
|
||||
layout.prop(brush, "image_tool")
|
||||
layout.separator()
|
||||
|
||||
capabilities = brush.image_paint_capabilities
|
||||
use_accumulate = capabilities.has_accumulate
|
||||
|
||||
|
@ -1111,6 +1170,9 @@ def brush_settings_advanced(layout, context, brush, popover=False):
|
|||
|
||||
# Vertex Paint #
|
||||
elif mode == 'PAINT_VERTEX':
|
||||
layout.prop(brush, "vertex_tool")
|
||||
layout.separator()
|
||||
|
||||
layout.prop(brush, "use_alpha")
|
||||
if brush.vertex_tool != 'SMEAR':
|
||||
use_accumulate = True
|
||||
|
@ -1118,10 +1180,17 @@ def brush_settings_advanced(layout, context, brush, popover=False):
|
|||
|
||||
# Weight Paint
|
||||
elif mode == 'PAINT_WEIGHT':
|
||||
layout.prop(brush, "weight_tool")
|
||||
layout.separator()
|
||||
|
||||
if brush.weight_tool != 'SMEAR':
|
||||
use_accumulate = True
|
||||
use_frontface = True
|
||||
|
||||
# Sculpt Curves
|
||||
elif mode == 'SCULPT_CURVES':
|
||||
layout.prop(brush, "curves_sculpt_tool")
|
||||
|
||||
# Draw shared settings.
|
||||
if use_accumulate:
|
||||
layout.prop(brush, "use_accumulate")
|
||||
|
@ -1129,6 +1198,29 @@ def brush_settings_advanced(layout, context, brush, popover=False):
|
|||
if use_frontface:
|
||||
layout.prop(brush, "use_frontface", text="Front Faces Only")
|
||||
|
||||
# Brush modes
|
||||
header, panel = layout.panel("modes", default_closed=True)
|
||||
header.label(text="Modes")
|
||||
if panel:
|
||||
panel.use_property_split = True
|
||||
panel.use_property_decorate = False
|
||||
|
||||
col = panel.column(align=True)
|
||||
col.prop(brush, "use_paint_sculpt", text="Sculpt")
|
||||
col.prop(brush, "use_paint_uv_sculpt", text="UV Sculpt")
|
||||
col.prop(brush, "use_paint_vertex", text="Vertex Paint")
|
||||
col.prop(brush, "use_paint_weight", text="Weight Paint")
|
||||
col.prop(brush, "use_paint_image", text="Texture Paint")
|
||||
col.prop(brush, "use_paint_sculpt_curves", text="Sculpt Curves")
|
||||
|
||||
if len(brush.icon_filepath) > 0:
|
||||
header, panel = layout.panel("legacy", default_closed=True)
|
||||
header.label(text="Legacy Icon")
|
||||
if panel:
|
||||
panel.label(text="Brush icons have moved to the asset preview image", icon='ERROR')
|
||||
panel.prop(brush, "use_custom_icon")
|
||||
panel.prop(brush, "icon_filepath")
|
||||
|
||||
|
||||
def draw_color_settings(context, layout, brush, color_type=False):
|
||||
"""Draw color wheel and gradient settings."""
|
||||
|
@ -1354,7 +1446,6 @@ def brush_basic_gpencil_paint_settings(layout, context, brush, *, compact=False)
|
|||
row.prop(brush, "size", text="Radius")
|
||||
row.prop(gp_settings, "use_pressure", text="", icon='STYLUS_PRESSURE')
|
||||
row.prop(gp_settings, "use_occlude_eraser", text="", icon='XRAY')
|
||||
row.prop(gp_settings, "use_default_eraser", text="")
|
||||
|
||||
row = layout.row(align=True)
|
||||
row.prop(gp_settings, "eraser_mode", expand=True)
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
from bpy.types import (
|
||||
AssetShelf,
|
||||
Header,
|
||||
Menu,
|
||||
Panel,
|
||||
|
@ -23,6 +24,7 @@ from bl_ui.properties_paint_common import (
|
|||
SmoothStrokePanel,
|
||||
FalloffPanel,
|
||||
DisplayPanel,
|
||||
BrushAssetShelf,
|
||||
)
|
||||
from bl_ui.properties_grease_pencil_common import (
|
||||
AnnotationDataPanel,
|
||||
|
@ -764,9 +766,10 @@ class _draw_tool_settings_context_mode:
|
|||
return
|
||||
|
||||
paint = context.tool_settings.image_paint
|
||||
layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
|
||||
|
||||
brush = paint.brush
|
||||
|
||||
BrushAssetShelf.draw_popup_selector(layout, context, brush)
|
||||
|
||||
if brush is None:
|
||||
return
|
||||
|
||||
|
@ -1182,7 +1185,7 @@ class IMAGE_PT_udim_tiles(Panel):
|
|||
|
||||
|
||||
class IMAGE_PT_paint_select(Panel, ImagePaintPanel, BrushSelectPanel):
|
||||
bl_label = "Brushes"
|
||||
bl_label = "Brush Asset"
|
||||
bl_context = ".paint_common_2d"
|
||||
bl_category = "Tool"
|
||||
|
||||
|
@ -1220,7 +1223,7 @@ class IMAGE_PT_paint_settings_advanced(Panel, ImagePaintPanel):
|
|||
|
||||
settings = context.tool_settings.image_paint
|
||||
brush = settings.brush
|
||||
|
||||
if brush:
|
||||
brush_settings_advanced(layout.column(), context, brush, self.is_popover)
|
||||
|
||||
|
||||
|
@ -1234,15 +1237,16 @@ class IMAGE_PT_paint_color(Panel, ImagePaintPanel):
|
|||
def poll(cls, context):
|
||||
settings = context.tool_settings.image_paint
|
||||
brush = settings.brush
|
||||
if not brush:
|
||||
return False
|
||||
capabilities = brush.image_paint_capabilities
|
||||
|
||||
return capabilities.has_color
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
settings = context.tool_settings.image_paint
|
||||
brush = settings.brush
|
||||
|
||||
if brush:
|
||||
draw_color_settings(context, layout, brush, color_type=True)
|
||||
|
||||
|
||||
|
@ -1688,6 +1692,18 @@ class IMAGE_PT_annotation(AnnotationDataPanel, Panel):
|
|||
# Grease Pencil drawing tools.
|
||||
|
||||
|
||||
class ImageAssetShelf(BrushAssetShelf):
|
||||
bl_space_type = "IMAGE_EDITOR"
|
||||
|
||||
|
||||
class IMAGE_AST_brush_paint(ImageAssetShelf, AssetShelf):
|
||||
mode_prop = "use_paint_image"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return context.space_data and context.space_data.ui_mode == 'PAINT'
|
||||
|
||||
|
||||
classes = (
|
||||
IMAGE_MT_view,
|
||||
IMAGE_MT_view_zoom,
|
||||
|
@ -1757,6 +1773,7 @@ classes = (
|
|||
IMAGE_PT_overlay_uv_edit_geometry,
|
||||
IMAGE_PT_overlay_texture_paint,
|
||||
IMAGE_PT_overlay_image,
|
||||
IMAGE_AST_brush_paint,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -18,7 +18,9 @@ from bl_ui.space_toolsystem_common import (
|
|||
ToolSelectPanelHelper,
|
||||
ToolDef,
|
||||
)
|
||||
|
||||
from bl_ui.properties_paint_common import (
|
||||
BrushAssetShelf,
|
||||
)
|
||||
from bpy.app.translations import pgettext_tip as tip_
|
||||
|
||||
|
||||
|
@ -1396,16 +1398,23 @@ class _defs_sculpt:
|
|||
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
return generate_from_enum_ex(
|
||||
context,
|
||||
idname_prefix="builtin_brush.",
|
||||
icon_prefix="brush.sculpt.",
|
||||
type=bpy.types.Brush,
|
||||
attr="sculpt_tool",
|
||||
# TODO(@ideasman42): we may want to enable this,
|
||||
# it causes awkward grouping with 2x column button layout.
|
||||
use_separators=False,
|
||||
# Though `data_block` is conceptually unnecessary with a single brush tool,
|
||||
# it's still used because many areas assume that brush tools have it set #bToolRef.
|
||||
tool = None
|
||||
if context:
|
||||
brush = context.tool_settings.sculpt.brush
|
||||
if brush:
|
||||
tool = brush.sculpt_tool
|
||||
return [
|
||||
ToolDef.from_dict(
|
||||
dict(
|
||||
idname="builtin.brush",
|
||||
label="Brush",
|
||||
icon="brush.sculpt.paint",
|
||||
data_block=tool
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
@ToolDef.from_fn
|
||||
def hide_border():
|
||||
|
@ -1726,13 +1735,23 @@ class _defs_vertex_paint:
|
|||
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
return generate_from_enum_ex(
|
||||
context,
|
||||
idname_prefix="builtin_brush.",
|
||||
icon_prefix="brush.paint_vertex.",
|
||||
type=bpy.types.Brush,
|
||||
attr="vertex_tool",
|
||||
# Though `data_block` is conceptually unnecessary with a single brush tool,
|
||||
# it's still used because many areas assume that brush tools have it set #bToolRef.
|
||||
tool = None
|
||||
if context:
|
||||
brush = context.tool_settings.vertex_paint.brush
|
||||
if brush:
|
||||
tool = brush.vertex_tool
|
||||
return [
|
||||
ToolDef.from_dict(
|
||||
dict(
|
||||
idname="builtin.brush",
|
||||
label="Brush",
|
||||
icon="brush.sculpt.paint",
|
||||
data_block=tool
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class _defs_texture_paint:
|
||||
|
@ -1747,14 +1766,24 @@ class _defs_texture_paint:
|
|||
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
return generate_from_enum_ex(
|
||||
context,
|
||||
idname_prefix="builtin_brush.",
|
||||
icon_prefix="brush.paint_texture.",
|
||||
type=bpy.types.Brush,
|
||||
attr="image_tool",
|
||||
# Though `data_block` is conceptually unnecessary with a single brush tool,
|
||||
# it's still used because many areas assume that brush tools have it set #bToolRef.
|
||||
tool = None
|
||||
if context:
|
||||
brush = context.tool_settings.image_paint.brush
|
||||
if brush:
|
||||
tool = brush.image_tool
|
||||
return [
|
||||
ToolDef.from_dict(
|
||||
dict(
|
||||
idname="builtin.brush",
|
||||
label="Brush",
|
||||
icon="brush.sculpt.paint",
|
||||
cursor='PAINT_CROSS',
|
||||
data_block=tool
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class _defs_weight_paint:
|
||||
|
@ -1774,13 +1803,23 @@ class _defs_weight_paint:
|
|||
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
return generate_from_enum_ex(
|
||||
context,
|
||||
idname_prefix="builtin_brush.",
|
||||
icon_prefix="brush.paint_weight.",
|
||||
type=bpy.types.Brush,
|
||||
attr="weight_tool",
|
||||
# Though `data_block` is conceptually unnecessary with a single brush tool,
|
||||
# it's still used because many areas assume that brush tools have it set #bToolRef.
|
||||
tool = None
|
||||
if context:
|
||||
brush = context.tool_settings.weight_paint.brush
|
||||
if brush:
|
||||
tool = brush.weight_tool
|
||||
return [
|
||||
ToolDef.from_dict(
|
||||
dict(
|
||||
idname="builtin.brush",
|
||||
label="Brush",
|
||||
icon="brush.sculpt.paint",
|
||||
data_block=tool
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
@ToolDef.from_fn
|
||||
def sample_weight():
|
||||
|
@ -1853,45 +1892,25 @@ class _defs_weight_paint:
|
|||
|
||||
class _defs_grease_pencil_paint:
|
||||
|
||||
# FIXME: Replace brush tools with code below once they are all implemented:
|
||||
#
|
||||
# @staticmethod
|
||||
# def generate_from_brushes(context):
|
||||
# return generate_from_enum_ex(
|
||||
# context,
|
||||
# idname_prefix="builtin_brush.",
|
||||
# icon_prefix="brush.gpencil_draw.",
|
||||
# type=bpy.types.Brush,
|
||||
# attr="gpencil_tool",
|
||||
# cursor='DOT',
|
||||
# )
|
||||
|
||||
@ToolDef.from_fn
|
||||
def draw():
|
||||
return dict(
|
||||
idname="builtin_brush.Draw",
|
||||
label="Draw",
|
||||
icon="brush.gpencil_draw.draw",
|
||||
data_block='DRAW',
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
# Though `data_block` is conceptually unnecessary with a single brush tool,
|
||||
# it's still used because many areas assume that brush tools have it set #bToolRef.
|
||||
tool = None
|
||||
if context:
|
||||
brush = context.tool_settings.gpencil_paint.brush
|
||||
if brush:
|
||||
tool = brush.gpencil_tool
|
||||
return [
|
||||
ToolDef.from_dict(
|
||||
dict(
|
||||
idname="builtin.brush",
|
||||
label="Brush",
|
||||
icon="brush.sculpt.paint",
|
||||
data_block=tool
|
||||
)
|
||||
|
||||
@ToolDef.from_fn
|
||||
def fill():
|
||||
return dict(
|
||||
idname="builtin_brush.Fill",
|
||||
label="Fill",
|
||||
icon="brush.gpencil_draw.fill",
|
||||
data_block='FILL',
|
||||
)
|
||||
|
||||
@ToolDef.from_fn
|
||||
def erase():
|
||||
return dict(
|
||||
idname="builtin_brush.Erase",
|
||||
label="Erase",
|
||||
icon="brush.gpencil_draw.erase",
|
||||
data_block='ERASE',
|
||||
)
|
||||
]
|
||||
|
||||
@ToolDef.from_fn
|
||||
def cutter():
|
||||
|
@ -1934,7 +1953,8 @@ class _defs_grease_pencil_paint:
|
|||
row = layout.row(align=True)
|
||||
tool_settings = context.scene.tool_settings
|
||||
settings = tool_settings.gpencil_paint
|
||||
row.template_ID_preview(settings, "brush", rows=3, cols=8, hide_buttons=True)
|
||||
|
||||
BrushAssetShelf.draw_popup_selector(row, context, brush)
|
||||
|
||||
from bl_ui.properties_paint_common import (
|
||||
brush_basic_grease_pencil_paint_settings,
|
||||
|
@ -2316,7 +2336,8 @@ class _defs_gpencil_paint:
|
|||
row = layout.row(align=True)
|
||||
tool_settings = context.scene.tool_settings
|
||||
settings = tool_settings.gpencil_paint
|
||||
row.template_ID_preview(settings, "brush", rows=3, cols=8, hide_buttons=True)
|
||||
|
||||
BrushAssetShelf.draw_popup_selector(layout, context, brush)
|
||||
|
||||
from bl_ui.properties_paint_common import (
|
||||
brush_basic_gpencil_paint_settings,
|
||||
|
@ -2329,17 +2350,23 @@ class _defs_gpencil_paint:
|
|||
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
return generate_from_enum_ex(
|
||||
context,
|
||||
idname_prefix="builtin_brush.",
|
||||
icon_prefix="brush.gpencil_draw.",
|
||||
type=bpy.types.Brush,
|
||||
attr="gpencil_tool",
|
||||
cursor='DOT',
|
||||
tooldef_keywords=dict(
|
||||
operator="gpencil.draw",
|
||||
),
|
||||
# Though `data_block` is conceptually unnecessary with a single brush tool,
|
||||
# it's still used because many areas assume that brush tools have it set #bToolRef.
|
||||
tool = None
|
||||
if context:
|
||||
brush = context.tool_settings.gpencil_paint.brush
|
||||
if brush:
|
||||
tool = brush.gpencil_tool
|
||||
return [
|
||||
ToolDef.from_dict(
|
||||
dict(
|
||||
idname="builtin.brush",
|
||||
label="Brush",
|
||||
icon="brush.sculpt.paint",
|
||||
data_block=tool
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
@ToolDef.from_fn
|
||||
def cutter():
|
||||
|
@ -2695,16 +2722,23 @@ class _defs_gpencil_sculpt:
|
|||
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
return generate_from_enum_ex(
|
||||
context,
|
||||
idname_prefix="builtin_brush.",
|
||||
icon_prefix="ops.gpencil.sculpt_",
|
||||
type=bpy.types.Brush,
|
||||
attr="gpencil_sculpt_tool",
|
||||
tooldef_keywords=dict(
|
||||
operator="gpencil.sculpt_paint",
|
||||
),
|
||||
# Though `data_block` is conceptually unnecessary with a single brush tool,
|
||||
# it's still used because many areas assume that brush tools have it set #bToolRef.
|
||||
tool = None
|
||||
if context:
|
||||
brush = context.tool_settings.gpencil_sculpt_paint.brush
|
||||
if brush:
|
||||
tool = brush.gpencil_sculpt_tool
|
||||
return [
|
||||
ToolDef.from_dict(
|
||||
dict(
|
||||
idname="builtin.brush",
|
||||
label="Brush",
|
||||
icon="brush.sculpt.paint",
|
||||
data_block=tool
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class _defs_grease_pencil_sculpt:
|
||||
|
@ -2725,33 +2759,46 @@ class _defs_grease_pencil_sculpt:
|
|||
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
return generate_from_enum_ex(
|
||||
context,
|
||||
idname_prefix="builtin_brush.",
|
||||
icon_prefix="ops.gpencil.sculpt_",
|
||||
type=bpy.types.Brush,
|
||||
# Uses GPv2 tool settings
|
||||
attr="gpencil_sculpt_tool",
|
||||
tooldef_keywords=dict(
|
||||
operator="grease_pencil.sculpt_paint",
|
||||
),
|
||||
# Though `data_block` is conceptually unnecessary with a single brush tool,
|
||||
# it's still used because many areas assume that brush tools have it set #bToolRef.
|
||||
tool = None
|
||||
if context:
|
||||
brush = context.tool_settings.gpencil_sculpt_paint.brush
|
||||
if brush:
|
||||
tool = brush.gpencil_sculpt_tool
|
||||
return [
|
||||
ToolDef.from_dict(
|
||||
dict(
|
||||
idname="builtin.brush",
|
||||
label="Brush",
|
||||
icon="brush.sculpt.paint",
|
||||
data_block=tool
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class _defs_gpencil_weight:
|
||||
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
return generate_from_enum_ex(
|
||||
context,
|
||||
idname_prefix="builtin_brush.",
|
||||
icon_prefix="ops.gpencil.sculpt_",
|
||||
type=bpy.types.Brush,
|
||||
attr="gpencil_weight_tool",
|
||||
tooldef_keywords=dict(
|
||||
operator="gpencil.weight_paint",
|
||||
),
|
||||
# Though `data_block` is conceptually unnecessary with a single brush tool,
|
||||
# it's still used because many areas assume that brush tools have it set #bToolRef.
|
||||
tool = None
|
||||
if context:
|
||||
brush = context.tool_settings.gpencil_weight_paint.brush
|
||||
if brush:
|
||||
tool = brush.gpencil_weight_tool
|
||||
return [
|
||||
ToolDef.from_dict(
|
||||
dict(
|
||||
idname="builtin.brush",
|
||||
label="Brush",
|
||||
icon="brush.sculpt.paint",
|
||||
data_block=tool
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class _defs_grease_pencil_weight:
|
||||
|
@ -2775,17 +2822,23 @@ class _defs_curves_sculpt:
|
|||
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
return generate_from_enum_ex(
|
||||
context,
|
||||
idname_prefix="builtin_brush.",
|
||||
icon_prefix="ops.curves.sculpt_",
|
||||
type=bpy.types.Brush,
|
||||
attr="curves_sculpt_tool",
|
||||
icon_map={
|
||||
# Use the generic icon for selection painting.
|
||||
"ops.curves.sculpt_selection_paint": "ops.generic.select_paint",
|
||||
},
|
||||
# Though `data_block` is conceptually unnecessary with a single brush tool,
|
||||
# it's still used because many areas assume that brush tools have it set #bToolRef.
|
||||
tool = None
|
||||
if context:
|
||||
brush = context.tool_settings.curves_sculpt.brush
|
||||
if brush:
|
||||
tool = brush.curves_sculpt_tool
|
||||
return [
|
||||
ToolDef.from_dict(
|
||||
dict(
|
||||
idname="builtin.brush",
|
||||
label="Brush",
|
||||
icon="brush.sculpt.paint",
|
||||
data_block=tool
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class _defs_gpencil_vertex:
|
||||
|
@ -2807,17 +2860,23 @@ class _defs_gpencil_vertex:
|
|||
|
||||
@staticmethod
|
||||
def generate_from_brushes(context):
|
||||
return generate_from_enum_ex(
|
||||
context,
|
||||
idname_prefix="builtin_brush.",
|
||||
icon_prefix="brush.paint_vertex.",
|
||||
type=bpy.types.Brush,
|
||||
attr="gpencil_vertex_tool",
|
||||
cursor='DOT',
|
||||
tooldef_keywords=dict(
|
||||
operator="gpencil.vertex_paint",
|
||||
),
|
||||
# Though `data_block` is conceptually unnecessary with a single brush tool,
|
||||
# it's still used because many areas assume that brush tools have it set #bToolRef.
|
||||
tool = None
|
||||
if context:
|
||||
brush = context.tool_settings.gpencil_vertex_paint.brush
|
||||
if brush:
|
||||
tool = brush.gpencil_vertex_tool
|
||||
return [
|
||||
ToolDef.from_dict(
|
||||
dict(
|
||||
idname="builtin.brush",
|
||||
label="Brush",
|
||||
icon="brush.sculpt.paint",
|
||||
data_block=tool
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
class _defs_node_select:
|
||||
|
@ -3535,9 +3594,7 @@ class VIEW3D_PT_tools_active(ToolSelectPanelHelper, Panel):
|
|||
'PAINT_GREASE_PENCIL': [
|
||||
_defs_view3d_generic.cursor,
|
||||
None,
|
||||
_defs_grease_pencil_paint.draw,
|
||||
_defs_grease_pencil_paint.fill,
|
||||
_defs_grease_pencil_paint.erase,
|
||||
_defs_grease_pencil_paint.generate_from_brushes,
|
||||
_defs_grease_pencil_paint.cutter,
|
||||
_defs_grease_pencil_paint.tint,
|
||||
None,
|
||||
|
|
|
@ -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 42
|
||||
#define BLENDER_FILE_SUBVERSION 43
|
||||
|
||||
/* Minimum Blender version that supports reading file written with the current
|
||||
* version. Older Blender versions will test this and cancel loading the file, showing a warning to
|
||||
|
|
|
@ -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,
|
||||
const Brush &brush,
|
||||
const float pos[2],
|
||||
|
@ -185,16 +163,5 @@ void BKE_brush_scale_size(int *r_brush_size,
|
|||
*/
|
||||
bool BKE_brush_has_cube_tip(const Brush *brush, PaintMode paint_mode);
|
||||
|
||||
/* Accessors */
|
||||
#define BKE_brush_tool_get(brush, p) \
|
||||
(CHECK_TYPE_ANY(brush, Brush *, const Brush *), \
|
||||
*(const char *)POINTER_OFFSET(brush, (p)->runtime.tool_offset))
|
||||
#define BKE_brush_tool_set(brush, p, tool) \
|
||||
{ \
|
||||
CHECK_TYPE_ANY(brush, Brush *); \
|
||||
*(char *)POINTER_OFFSET(brush, (p)->runtime.tool_offset) = tool; \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
/* debugging only */
|
||||
void BKE_brush_debug_print_state(Brush *br);
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#include <variant>
|
||||
|
||||
/* XXX temporary, until AssetHandle is designed properly and queries can return a pointer to it. */
|
||||
#include "DNA_asset_types.h"
|
||||
|
||||
|
@ -102,7 +104,7 @@ using bContextDataCallback = int /*eContextResult*/ (*)(const bContext *C,
|
|||
|
||||
struct bContextStoreEntry {
|
||||
std::string name;
|
||||
PointerRNA ptr;
|
||||
std::variant<PointerRNA, std::string> value;
|
||||
};
|
||||
|
||||
struct bContextStore {
|
||||
|
@ -158,6 +160,9 @@ bContext *CTX_copy(const bContext *C);
|
|||
bContextStore *CTX_store_add(blender::Vector<std::unique_ptr<bContextStore>> &contexts,
|
||||
blender::StringRefNull name,
|
||||
const PointerRNA *ptr);
|
||||
bContextStore *CTX_store_add(blender::Vector<std::unique_ptr<bContextStore>> &contexts,
|
||||
blender::StringRefNull name,
|
||||
blender::StringRef str);
|
||||
bContextStore *CTX_store_add_all(blender::Vector<std::unique_ptr<bContextStore>> &contexts,
|
||||
const bContextStore *context);
|
||||
const bContextStore *CTX_store_get(const bContext *C);
|
||||
|
@ -165,6 +170,8 @@ void CTX_store_set(bContext *C, const bContextStore *store);
|
|||
const PointerRNA *CTX_store_ptr_lookup(const bContextStore *store,
|
||||
blender::StringRefNull name,
|
||||
const StructRNA *type = nullptr);
|
||||
std::optional<blender::StringRefNull> CTX_store_string_lookup(const bContextStore *store,
|
||||
blender::StringRefNull name);
|
||||
|
||||
/* need to store if python is initialized or not */
|
||||
bool CTX_py_init_get(bContext *C);
|
||||
|
@ -262,6 +269,7 @@ enum {
|
|||
CTX_DATA_TYPE_POINTER = 0,
|
||||
CTX_DATA_TYPE_COLLECTION,
|
||||
CTX_DATA_TYPE_PROPERTY,
|
||||
CTX_DATA_TYPE_STRING,
|
||||
};
|
||||
|
||||
PointerRNA CTX_data_pointer_get(const bContext *C, const char *member);
|
||||
|
@ -282,6 +290,8 @@ blender::Vector<PointerRNA> CTX_data_collection_get(const bContext *C, const cha
|
|||
void CTX_data_collection_remap_property(blender::MutableSpan<PointerRNA> collection_pointers,
|
||||
const char *propname);
|
||||
|
||||
std::optional<blender::StringRefNull> CTX_data_string_get(const bContext *C, const char *member);
|
||||
|
||||
/**
|
||||
* \param C: Context.
|
||||
* \param use_store: Use 'C->wm.store'.
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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);
|
||||
|
@ -255,19 +276,6 @@ void paint_update_brush_rake_rotation(UnifiedPaintSettings &ups,
|
|||
|
||||
void BKE_paint_stroke_get_average(const Scene *scene, const Object *ob, float stroke[3]);
|
||||
|
||||
/* Tool slot API. */
|
||||
|
||||
void BKE_paint_toolslots_init_from_main(Main *bmain);
|
||||
void BKE_paint_toolslots_len_ensure(Paint *paint, int len);
|
||||
void BKE_paint_toolslots_brush_update_ex(Paint *paint, Brush *brush);
|
||||
void BKE_paint_toolslots_brush_update(Paint *paint);
|
||||
/**
|
||||
* Run this to ensure brush types are set for each slot on entering modes
|
||||
* (for new scenes for example).
|
||||
*/
|
||||
void BKE_paint_brush_validate(Main *bmain, Paint *paint);
|
||||
Brush *BKE_paint_toolslots_brush_get(Paint *paint, int slot_index);
|
||||
|
||||
/* .blend I/O */
|
||||
|
||||
void BKE_paint_blend_write(BlendWriter *writer, Paint *paint);
|
||||
|
|
|
@ -64,6 +64,7 @@ set(SRC
|
|||
intern/armature_update.cc
|
||||
intern/asset.cc
|
||||
intern/asset_weak_reference.cc
|
||||
intern/asset_edit.cc
|
||||
intern/attribute.cc
|
||||
intern/attribute_access.cc
|
||||
intern/attribute_math.cc
|
||||
|
@ -253,7 +254,6 @@ set(SRC
|
|||
intern/packedFile.cc
|
||||
intern/paint.cc
|
||||
intern/paint_canvas.cc
|
||||
intern/paint_toolslots.cc
|
||||
intern/particle.cc
|
||||
intern/particle_child.cc
|
||||
intern/particle_distribute.cc
|
||||
|
@ -332,6 +332,7 @@ set(SRC
|
|||
BKE_appdir.hh
|
||||
BKE_armature.hh
|
||||
BKE_asset.hh
|
||||
BKE_asset_edit.hh
|
||||
BKE_attribute.h
|
||||
BKE_attribute.hh
|
||||
BKE_attribute_math.hh
|
||||
|
|
|
@ -0,0 +1,453 @@
|
|||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bke
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "BLI_fileops.h"
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "DNA_asset_types.h"
|
||||
#include "DNA_space_types.h"
|
||||
|
||||
#include "AS_asset_identifier.hh"
|
||||
#include "AS_asset_library.hh"
|
||||
|
||||
#include "BKE_asset.hh"
|
||||
#include "BKE_asset_edit.hh"
|
||||
#include "BKE_blendfile.hh"
|
||||
#include "BKE_blendfile_link_append.hh"
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_idtype.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_lib_remap.hh"
|
||||
#include "BKE_library.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_packedFile.h"
|
||||
#include "BKE_preferences.h"
|
||||
#include "BKE_report.hh"
|
||||
|
||||
#include "BLO_read_write.hh"
|
||||
#include "BLO_readfile.hh"
|
||||
#include "BLO_writefile.hh"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
#include "DEG_depsgraph_build.hh"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
namespace blender::bke {
|
||||
|
||||
static ID *asset_link_id(Main &global_main,
|
||||
const ID_Type id_type,
|
||||
const char *filepath,
|
||||
const char *asset_name)
|
||||
{
|
||||
/* Load asset from asset library. */
|
||||
LibraryLink_Params lapp_params{};
|
||||
lapp_params.bmain = &global_main;
|
||||
BlendfileLinkAppendContext *lapp_context = BKE_blendfile_link_append_context_new(&lapp_params);
|
||||
BKE_blendfile_link_append_context_flag_set(lapp_context, BLO_LIBLINK_FORCE_INDIRECT, true);
|
||||
|
||||
BKE_blendfile_link_append_context_library_add(lapp_context, filepath, nullptr);
|
||||
|
||||
BlendfileLinkAppendContextItem *lapp_item = BKE_blendfile_link_append_context_item_add(
|
||||
lapp_context, asset_name, id_type, nullptr);
|
||||
BKE_blendfile_link_append_context_item_library_index_enable(lapp_context, lapp_item, 0);
|
||||
|
||||
BKE_blendfile_link(lapp_context, nullptr);
|
||||
|
||||
ID *local_asset = BKE_blendfile_link_append_context_item_newid_get(lapp_context, lapp_item);
|
||||
|
||||
BKE_blendfile_link_append_context_free(lapp_context);
|
||||
|
||||
/* Verify that the name matches. It must for referencing the same asset again to work. */
|
||||
BLI_assert(local_asset == nullptr || STREQ(local_asset->name + 2, asset_name));
|
||||
|
||||
/* Tag library as being editable. */
|
||||
if (local_asset && local_asset->lib) {
|
||||
local_asset->lib->runtime.tag |= LIBRARY_ASSET_EDITABLE;
|
||||
|
||||
/* Simple check, based on being a writable .asset.blend file in a user asset library. */
|
||||
if (StringRef(filepath).endswith(BLENDER_ASSET_FILE_SUFFIX) &&
|
||||
BKE_preferences_asset_library_containing_path(&U, filepath) &&
|
||||
BLI_file_is_writable(filepath))
|
||||
{
|
||||
local_asset->lib->runtime.tag |= LIBRARY_ASSET_FILE_WRITABLE;
|
||||
}
|
||||
}
|
||||
|
||||
return local_asset;
|
||||
}
|
||||
|
||||
static std::string asset_root_path_for_save(const bUserAssetLibrary &user_library,
|
||||
const ID_Type id_type)
|
||||
{
|
||||
BLI_assert(user_library.dirpath[0] != '\0');
|
||||
|
||||
char libpath[FILE_MAX];
|
||||
BLI_strncpy(libpath, user_library.dirpath, sizeof(libpath));
|
||||
BLI_path_slash_native(libpath);
|
||||
BLI_path_normalize(libpath);
|
||||
|
||||
/* Capitalize folder name. Ideally this would already available in
|
||||
* the type info to work correctly with multiple words. */
|
||||
const IDTypeInfo *id_type_info = BKE_idtype_get_info_from_idcode(id_type);
|
||||
std::string name = id_type_info->name_plural;
|
||||
name[0] = BLI_toupper_ascii(name[0]);
|
||||
|
||||
return std::string(libpath) + SEP + "Saved" + SEP + name;
|
||||
}
|
||||
|
||||
static std::string asset_blendfile_path_for_save(const bUserAssetLibrary &user_library,
|
||||
const StringRef base_name,
|
||||
const ID_Type id_type,
|
||||
ReportList &reports)
|
||||
{
|
||||
std::string root_path = asset_root_path_for_save(user_library, id_type);
|
||||
BLI_assert(!root_path.empty());
|
||||
|
||||
if (!BLI_dir_create_recursive(root_path.c_str())) {
|
||||
BKE_report(&reports, RPT_ERROR, "Failed to create asset library directory to save asset");
|
||||
return "";
|
||||
}
|
||||
|
||||
/* Make sure filename only contains valid characters for filesystem. */
|
||||
char base_name_filesafe[FILE_MAXFILE];
|
||||
BLI_strncpy(base_name_filesafe,
|
||||
base_name.data(),
|
||||
std::min(sizeof(base_name_filesafe), size_t(base_name.size() + 1)));
|
||||
BLI_path_make_safe_filename(base_name_filesafe);
|
||||
|
||||
const std::string filepath = root_path + SEP + base_name_filesafe + BLENDER_ASSET_FILE_SUFFIX;
|
||||
|
||||
if (!BLI_is_file(filepath.c_str())) {
|
||||
return filepath;
|
||||
}
|
||||
|
||||
/* Avoid overwriting existing file by adding number suffix. */
|
||||
for (int i = 1;; i++) {
|
||||
const std::string filepath = root_path + SEP + base_name_filesafe + "_" + std::to_string(i++) +
|
||||
BLENDER_ASSET_FILE_SUFFIX;
|
||||
if (!BLI_is_file((filepath.c_str()))) {
|
||||
return filepath;
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
static void asset_main_create_expander(void * /*handle*/, Main * /*bmain*/, void *vid)
|
||||
{
|
||||
ID *id = static_cast<ID *>(vid);
|
||||
|
||||
if (id && (id->tag & LIB_TAG_DOIT) == 0) {
|
||||
id->tag |= LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT;
|
||||
}
|
||||
}
|
||||
|
||||
static Main *asset_main_create_from_ID(Main &bmain_src, ID &id_asset, ID **id_asset_new)
|
||||
{
|
||||
/* Tag asset ID and its dependencies. */
|
||||
ID *id_src;
|
||||
FOREACH_MAIN_ID_BEGIN (&bmain_src, id_src) {
|
||||
id_src->tag &= ~(LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT);
|
||||
}
|
||||
FOREACH_MAIN_ID_END;
|
||||
|
||||
id_asset.tag |= LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT;
|
||||
|
||||
BLO_expand_main(nullptr, &bmain_src, asset_main_create_expander);
|
||||
|
||||
/* Create main and copy all tagged datablocks. */
|
||||
Main *bmain_dst = BKE_main_new();
|
||||
STRNCPY(bmain_dst->filepath, bmain_src.filepath);
|
||||
|
||||
blender::bke::id::IDRemapper id_remapper;
|
||||
|
||||
FOREACH_MAIN_ID_BEGIN (&bmain_src, id_src) {
|
||||
if (id_src->tag & LIB_TAG_DOIT) {
|
||||
/* Note that this will not copy Library datablocks, and all copied
|
||||
* datablocks will become local as a result. */
|
||||
ID *id_dst = BKE_id_copy_ex(bmain_dst,
|
||||
id_src,
|
||||
nullptr,
|
||||
LIB_ID_CREATE_NO_USER_REFCOUNT | LIB_ID_CREATE_NO_DEG_TAG |
|
||||
((id_src == &id_asset) ? LIB_ID_COPY_ASSET_METADATA : 0));
|
||||
id_remapper.add(id_src, id_dst);
|
||||
if (id_src == &id_asset) {
|
||||
*id_asset_new = id_dst;
|
||||
}
|
||||
}
|
||||
else {
|
||||
id_remapper.add(id_src, nullptr);
|
||||
}
|
||||
|
||||
id_src->tag &= ~(LIB_TAG_NEED_EXPAND | LIB_TAG_DOIT);
|
||||
}
|
||||
FOREACH_MAIN_ID_END;
|
||||
|
||||
/* Remap datablock pointers. */
|
||||
BKE_libblock_remap_multiple_raw(bmain_dst, id_remapper, ID_REMAP_SKIP_USER_CLEAR);
|
||||
|
||||
/* Compute reference counts. */
|
||||
ID *id_dst;
|
||||
FOREACH_MAIN_ID_BEGIN (bmain_dst, id_dst) {
|
||||
id_dst->tag &= ~LIB_TAG_NO_USER_REFCOUNT;
|
||||
}
|
||||
FOREACH_MAIN_ID_END;
|
||||
BKE_main_id_refcount_recompute(bmain_dst, false);
|
||||
|
||||
return bmain_dst;
|
||||
}
|
||||
|
||||
static bool asset_write_in_library(Main &bmain,
|
||||
const ID &id_const,
|
||||
const StringRef name,
|
||||
const StringRefNull filepath,
|
||||
std::string &final_full_file_path,
|
||||
ReportList &reports)
|
||||
{
|
||||
ID &id = const_cast<ID &>(id_const);
|
||||
|
||||
ID *new_id = nullptr;
|
||||
Main *new_main = asset_main_create_from_ID(bmain, id, &new_id);
|
||||
|
||||
std::string new_name = name;
|
||||
BKE_libblock_rename(new_main, new_id, new_name.c_str());
|
||||
id_fake_user_set(new_id);
|
||||
|
||||
BlendFileWriteParams blend_file_write_params{};
|
||||
blend_file_write_params.remap_mode = BLO_WRITE_PATH_REMAP_RELATIVE;
|
||||
|
||||
BKE_packedfile_pack_all(new_main, nullptr, false);
|
||||
|
||||
const int write_flags = G_FILE_COMPRESS;
|
||||
const bool success = BLO_write_file(
|
||||
new_main, filepath.c_str(), write_flags, &blend_file_write_params, &reports);
|
||||
|
||||
if (success) {
|
||||
const IDTypeInfo *idtype = BKE_idtype_get_info_from_id(&id);
|
||||
final_full_file_path = std::string(filepath) + SEP + std::string(idtype->name) + SEP + name;
|
||||
}
|
||||
|
||||
BKE_main_free(new_main);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static void asset_reload(Main &global_main, Library *lib, ReportList &reports)
|
||||
{
|
||||
/* Fill fresh main database with same datablock as before. */
|
||||
LibraryLink_Params lapp_params{};
|
||||
lapp_params.bmain = &global_main;
|
||||
BlendfileLinkAppendContext *lapp_context = BKE_blendfile_link_append_context_new(&lapp_params);
|
||||
BKE_blendfile_link_append_context_flag_set(
|
||||
lapp_context, BLO_LIBLINK_FORCE_INDIRECT | BLO_LIBLINK_USE_PLACEHOLDERS, true);
|
||||
|
||||
BKE_blendfile_link_append_context_library_add(lapp_context, lib->runtime.filepath_abs, nullptr);
|
||||
BKE_blendfile_library_relocate(lapp_context, &reports, lib, true);
|
||||
BKE_blendfile_link_append_context_free(lapp_context);
|
||||
|
||||
/* Clear temporary tag from reloaction. */
|
||||
BKE_main_id_tag_all(&global_main, LIB_TAG_PRE_EXISTING, false);
|
||||
|
||||
/* Recreate dependency graph to include new IDs. */
|
||||
DEG_relations_tag_update(&global_main);
|
||||
}
|
||||
|
||||
static AssetWeakReference asset_weak_reference_for_user_library(
|
||||
const bUserAssetLibrary &user_library,
|
||||
const short idcode,
|
||||
const char *idname,
|
||||
const char *filepath)
|
||||
{
|
||||
AssetWeakReference weak_ref;
|
||||
weak_ref.asset_library_type = ASSET_LIBRARY_CUSTOM;
|
||||
weak_ref.asset_library_identifier = BLI_strdup(user_library.name);
|
||||
|
||||
/* BLI_path_rel requires a trailing slash. */
|
||||
char user_library_dirpath[FILE_MAX];
|
||||
STRNCPY(user_library_dirpath, user_library.dirpath);
|
||||
BLI_path_slash_ensure(user_library_dirpath, sizeof(user_library_dirpath));
|
||||
|
||||
char relative_filepath[FILE_MAX];
|
||||
STRNCPY(relative_filepath, filepath);
|
||||
BLI_path_rel(relative_filepath, user_library_dirpath);
|
||||
const char *asset_blend_path = relative_filepath + 2; /* Strip out // prefix. */
|
||||
|
||||
weak_ref.relative_asset_identifier = BLI_sprintfN(
|
||||
"%s/%s/%s", asset_blend_path, BKE_idtype_idcode_to_name(idcode), idname);
|
||||
|
||||
return weak_ref;
|
||||
}
|
||||
|
||||
static AssetWeakReference asset_weak_reference_for_essentials(const short idcode,
|
||||
const char *idname,
|
||||
const char *filepath)
|
||||
{
|
||||
AssetWeakReference weak_ref;
|
||||
weak_ref.asset_library_type = ASSET_LIBRARY_ESSENTIALS;
|
||||
weak_ref.relative_asset_identifier = BLI_sprintfN("%s/%s/%s/%s",
|
||||
BKE_idtype_idcode_to_name_plural(idcode),
|
||||
BLI_path_basename(filepath),
|
||||
BKE_idtype_idcode_to_name(idcode),
|
||||
idname);
|
||||
|
||||
return weak_ref;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public API
|
||||
*/
|
||||
std::optional<std::string> asset_edit_id_save_as(Main &global_main,
|
||||
const ID &id,
|
||||
const StringRef name,
|
||||
const bUserAssetLibrary &user_library,
|
||||
AssetWeakReference &new_weak_ref,
|
||||
ReportList &reports)
|
||||
{
|
||||
const std::string filepath = asset_blendfile_path_for_save(
|
||||
user_library, name, GS(id.name), reports);
|
||||
|
||||
std::string final_full_asset_filepath;
|
||||
const bool success = asset_write_in_library(
|
||||
global_main, id, name, filepath, final_full_asset_filepath, reports);
|
||||
if (!success) {
|
||||
BKE_report(&reports, RPT_ERROR, "Failed to write to asset library");
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
new_weak_ref = asset_weak_reference_for_user_library(
|
||||
user_library, GS(id.name), id.name + 2, filepath.c_str());
|
||||
|
||||
BKE_reportf(&reports, RPT_INFO, "Saved \"%s\"", filepath.c_str());
|
||||
|
||||
return final_full_asset_filepath;
|
||||
}
|
||||
|
||||
bool asset_edit_id_save(Main &global_main, const ID &id, ReportList &reports)
|
||||
{
|
||||
if (!asset_edit_id_is_editable(id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string final_full_asset_filepath;
|
||||
const bool success = asset_write_in_library(global_main,
|
||||
id,
|
||||
id.name + 2,
|
||||
id.lib->runtime.filepath_abs,
|
||||
final_full_asset_filepath,
|
||||
reports);
|
||||
|
||||
if (!success) {
|
||||
BKE_report(&reports, RPT_ERROR, "Failed to write to asset library");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool asset_edit_id_revert(Main &global_main, ID &id, ReportList &reports)
|
||||
{
|
||||
if (!asset_edit_id_is_editable(id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Reload entire main, including texture dependencies. This relies on there
|
||||
* being only a single asset per blend file. */
|
||||
asset_reload(global_main, id.lib, reports);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool asset_edit_id_delete(Main &global_main, ID &id, ReportList &reports)
|
||||
{
|
||||
if (asset_edit_id_is_editable(id)) {
|
||||
if (BLI_delete(id.lib->runtime.filepath_abs, false, false) != 0) {
|
||||
BKE_report(&reports, RPT_ERROR, "Failed to delete asset library file");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
BKE_id_delete(&global_main, &id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ID *asset_edit_id_from_weak_reference(Main &global_main,
|
||||
const ID_Type id_type,
|
||||
const AssetWeakReference &weak_ref)
|
||||
{
|
||||
/* Don't do this in file load. */
|
||||
BLI_assert(!global_main.is_locked_for_linking);
|
||||
|
||||
char asset_full_path_buffer[FILE_MAX_LIBEXTRA];
|
||||
char *asset_lib_path, *asset_group, *asset_name;
|
||||
|
||||
AS_asset_full_path_explode_from_weak_ref(
|
||||
&weak_ref, asset_full_path_buffer, &asset_lib_path, &asset_group, &asset_name);
|
||||
if (asset_lib_path == nullptr && asset_group == nullptr && asset_name == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* If this is the same file as we have open, use local datablock. */
|
||||
if (asset_lib_path && STREQ(asset_lib_path, global_main.filepath)) {
|
||||
asset_lib_path = nullptr;
|
||||
}
|
||||
|
||||
BLI_assert(asset_name != nullptr);
|
||||
|
||||
/* Test if asset has been loaded already. */
|
||||
ID *local_asset = BKE_libblock_find_name_and_library_filepath(
|
||||
&global_main, id_type, asset_name, asset_lib_path);
|
||||
if (local_asset) {
|
||||
return local_asset;
|
||||
}
|
||||
|
||||
/* Try linking in the required file. */
|
||||
if (asset_lib_path == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return asset_link_id(global_main, id_type, asset_lib_path, asset_name);
|
||||
}
|
||||
|
||||
std::optional<AssetWeakReference> asset_edit_weak_reference_from_id(ID &id)
|
||||
{
|
||||
if (!asset_edit_id_is_editable(id)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bUserAssetLibrary *user_library = BKE_preferences_asset_library_containing_path(
|
||||
&U, id.lib->runtime.filepath_abs);
|
||||
|
||||
const short idcode = GS(id.name);
|
||||
|
||||
if (user_library && user_library->dirpath[0]) {
|
||||
return asset_weak_reference_for_user_library(
|
||||
*user_library, idcode, id.name + 2, id.lib->runtime.filepath_abs);
|
||||
}
|
||||
else {
|
||||
return asset_weak_reference_for_essentials(idcode, id.name + 2, id.lib->runtime.filepath_abs);
|
||||
}
|
||||
}
|
||||
|
||||
bool asset_edit_id_is_editable(const ID &id)
|
||||
{
|
||||
return (id.lib && (id.lib->runtime.tag & LIBRARY_ASSET_EDITABLE));
|
||||
}
|
||||
|
||||
bool asset_edit_id_is_writable(const ID &id)
|
||||
{
|
||||
return asset_edit_id_is_editable(id) && (id.lib->runtime.tag & LIBRARY_ASSET_FILE_WRITABLE);
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
|
@ -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 */
|
||||
|
|
|
@ -874,6 +874,9 @@ static void setup_app_data(bContext *C,
|
|||
* (but may have a null `curscreen`). */
|
||||
else if (ELEM(nullptr, bfd->curscreen, bfd->curscene)) {
|
||||
BKE_report(reports->reports, RPT_WARNING, "Library file, loading empty scene");
|
||||
if (blender::StringRefNull(bfd->main->filepath).endswith(BLENDER_ASSET_FILE_SUFFIX)) {
|
||||
bfd->main->is_asset_repository = true;
|
||||
}
|
||||
mode = LOAD_UI_OFF;
|
||||
}
|
||||
else if (G.fileflags & G_FILE_NO_UI) {
|
||||
|
@ -1468,6 +1471,15 @@ UserDef *BKE_blendfile_userdef_from_defaults()
|
|||
BKE_preferences_extension_repo_add_default(userdef);
|
||||
BKE_preferences_extension_repo_add_default_user(userdef);
|
||||
|
||||
{
|
||||
BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(
|
||||
userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh/Sculpt/Cloth");
|
||||
BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(
|
||||
userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh/Sculpt/General");
|
||||
BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(
|
||||
userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh/Sculpt/Painting");
|
||||
}
|
||||
|
||||
return userdef;
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -131,9 +131,10 @@ void CTX_free(bContext *C)
|
|||
|
||||
/* store */
|
||||
|
||||
bContextStore *CTX_store_add(Vector<std::unique_ptr<bContextStore>> &contexts,
|
||||
const blender::StringRefNull name,
|
||||
const PointerRNA *ptr)
|
||||
/**
|
||||
* Append a new context store to \a contexts, copying entries from the previous one if any.
|
||||
*/
|
||||
static bContextStore *ctx_store_extend(Vector<std::unique_ptr<bContextStore>> &contexts)
|
||||
{
|
||||
/* ensure we have a context to put the entry in, if it was already used
|
||||
* we have to copy the context to ensure */
|
||||
|
@ -145,25 +146,31 @@ bContextStore *CTX_store_add(Vector<std::unique_ptr<bContextStore>> &contexts,
|
|||
contexts.append(std::move(new_ctx));
|
||||
}
|
||||
|
||||
bContextStore *ctx = contexts.last().get();
|
||||
return contexts.last().get();
|
||||
}
|
||||
|
||||
bContextStore *CTX_store_add(Vector<std::unique_ptr<bContextStore>> &contexts,
|
||||
const blender::StringRefNull name,
|
||||
const PointerRNA *ptr)
|
||||
{
|
||||
bContextStore *ctx = ctx_store_extend(contexts);
|
||||
ctx->entries.append(bContextStoreEntry{name, *ptr});
|
||||
return ctx;
|
||||
}
|
||||
|
||||
bContextStore *CTX_store_add(Vector<std::unique_ptr<bContextStore>> &contexts,
|
||||
const blender::StringRefNull name,
|
||||
const blender::StringRef str)
|
||||
{
|
||||
bContextStore *ctx = ctx_store_extend(contexts);
|
||||
ctx->entries.append(bContextStoreEntry{name, std::string{str}});
|
||||
return ctx;
|
||||
}
|
||||
|
||||
bContextStore *CTX_store_add_all(Vector<std::unique_ptr<bContextStore>> &contexts,
|
||||
const bContextStore *context)
|
||||
{
|
||||
/* ensure we have a context to put the entry in, if it was already used
|
||||
* we have to copy the context to ensure */
|
||||
if (contexts.is_empty()) {
|
||||
contexts.append(std::make_unique<bContextStore>());
|
||||
}
|
||||
else if (contexts.last()->used) {
|
||||
auto new_ctx = std::make_unique<bContextStore>(bContextStore{contexts.last()->entries, false});
|
||||
contexts.append(std::move(new_ctx));
|
||||
}
|
||||
|
||||
bContextStore *ctx = contexts.last().get();
|
||||
bContextStore *ctx = ctx_store_extend(contexts);
|
||||
for (const bContextStoreEntry &src_entry : context->entries) {
|
||||
ctx->entries.append(src_entry);
|
||||
}
|
||||
|
@ -185,15 +192,27 @@ const PointerRNA *CTX_store_ptr_lookup(const bContextStore *store,
|
|||
const StructRNA *type)
|
||||
{
|
||||
for (auto entry = store->entries.rbegin(); entry != store->entries.rend(); ++entry) {
|
||||
if (entry->name == name) {
|
||||
if (!type || RNA_struct_is_a(entry->ptr.type, type)) {
|
||||
return &entry->ptr;
|
||||
if (entry->name == name && std::holds_alternative<PointerRNA>(entry->value)) {
|
||||
const PointerRNA &ptr = std::get<PointerRNA>(entry->value);
|
||||
if (!type || RNA_struct_is_a(ptr.type, type)) {
|
||||
return &ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::optional<blender::StringRefNull> CTX_store_string_lookup(const bContextStore *store,
|
||||
const blender::StringRefNull name)
|
||||
{
|
||||
for (auto entry = store->entries.rbegin(); entry != store->entries.rend(); ++entry) {
|
||||
if (entry->name == name && std::holds_alternative<std::string>(entry->value)) {
|
||||
return std::get<std::string>(entry->value);
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
/* is python initialized? */
|
||||
|
||||
bool CTX_py_init_get(bContext *C)
|
||||
|
@ -235,6 +254,7 @@ struct bContextDataResult {
|
|||
Vector<PointerRNA> list;
|
||||
PropertyRNA *prop;
|
||||
int index;
|
||||
blender::StringRefNull str;
|
||||
const char **dir;
|
||||
short type; /* 0: normal, 1: seq */
|
||||
};
|
||||
|
@ -313,6 +333,15 @@ static eContextResult ctx_data_get(bContext *C, const char *member, bContextData
|
|||
result->ptr = *ptr;
|
||||
done = 1;
|
||||
}
|
||||
else {
|
||||
std::optional<blender::StringRefNull> str = CTX_store_string_lookup(C->wm.store, member);
|
||||
|
||||
if (str) {
|
||||
result->str = *str;
|
||||
result->type = CTX_DATA_TYPE_STRING;
|
||||
done = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (done != 1 && recursion < 2 && (region = CTX_wm_region(C))) {
|
||||
C->data.recursion = 2;
|
||||
|
@ -487,6 +516,17 @@ void CTX_data_collection_remap_property(blender::MutableSpan<PointerRNA> collect
|
|||
}
|
||||
}
|
||||
|
||||
std::optional<blender::StringRefNull> CTX_data_string_get(const bContext *C, const char *member)
|
||||
{
|
||||
bContextDataResult result;
|
||||
if (ctx_data_get((bContext *)C, member, &result) == CTX_RESULT_OK) {
|
||||
BLI_assert(result.type == CTX_DATA_TYPE_STRING);
|
||||
return result.str;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
int /*eContextResult*/ CTX_data_get(const bContext *C,
|
||||
const char *member,
|
||||
PointerRNA *r_ptr,
|
||||
|
|
|
@ -1572,6 +1572,12 @@ void BKE_libblock_copy_in_lib(Main *bmain,
|
|||
}
|
||||
}
|
||||
|
||||
if (flag & LIB_ID_COPY_ASSET_METADATA) {
|
||||
if (id->asset_data) {
|
||||
new_id->asset_data = BKE_asset_metadata_copy(id->asset_data);
|
||||
}
|
||||
}
|
||||
|
||||
if ((flag & LIB_ID_CREATE_NO_DEG_TAG) == 0 && (flag & LIB_ID_CREATE_NO_MAIN) == 0) {
|
||||
DEG_id_type_tag(bmain, GS(new_id->name));
|
||||
}
|
||||
|
|
|
@ -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,337 @@ PaintMode BKE_paintmode_get_from_tool(const bToolRef *tref)
|
|||
return PaintMode::Invalid;
|
||||
}
|
||||
|
||||
Brush *BKE_paint_brush(Paint *p)
|
||||
static bool paint_brush_set_from_asset_reference(Main *bmain, Paint *paint)
|
||||
{
|
||||
return (Brush *)BKE_paint_brush_for_read((const Paint *)p);
|
||||
/* Don't resolve this during file read, it will be done after. */
|
||||
if (bmain->is_locked_for_linking) {
|
||||
return false;
|
||||
}
|
||||
/* Attempt to restore a valid active brush from brush asset information. */
|
||||
if (paint->brush != nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (paint->brush_asset_reference == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const Brush *BKE_paint_brush_for_read(const Paint *p)
|
||||
Brush *brush = reinterpret_cast<Brush *>(blender::bke::asset_edit_id_from_weak_reference(
|
||||
*bmain, ID_BR, *paint->brush_asset_reference));
|
||||
BLI_assert(brush == nullptr || blender::bke::asset_edit_id_is_editable(brush->id));
|
||||
|
||||
/* Ensure we have a brush with appropriate mode to assign.
|
||||
* Could happen if contents of asset blend was manually changed. */
|
||||
if (brush == nullptr || (paint->runtime.ob_mode & brush->ob_mode) == 0) {
|
||||
MEM_delete(paint->brush_asset_reference);
|
||||
paint->brush_asset_reference = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
paint->brush = brush;
|
||||
return true;
|
||||
}
|
||||
|
||||
Brush *BKE_paint_brush(Paint *paint)
|
||||
{
|
||||
return p ? p->brush : nullptr;
|
||||
return (Brush *)BKE_paint_brush_for_read((const Paint *)paint);
|
||||
}
|
||||
|
||||
void BKE_paint_brush_set(Paint *p, Brush *br)
|
||||
const Brush *BKE_paint_brush_for_read(const Paint *paint)
|
||||
{
|
||||
if (p) {
|
||||
id_us_min((ID *)p->brush);
|
||||
id_us_plus((ID *)br);
|
||||
p->brush = br;
|
||||
return paint ? paint->brush : nullptr;
|
||||
}
|
||||
|
||||
BKE_paint_toolslots_brush_update(p);
|
||||
bool BKE_paint_brush_set(Paint *paint, Brush *brush)
|
||||
{
|
||||
if (paint == nullptr || paint->brush == brush) {
|
||||
return false;
|
||||
}
|
||||
if (brush && (paint->runtime.ob_mode & brush->ob_mode) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
paint->brush = brush;
|
||||
|
||||
MEM_delete(paint->brush_asset_reference);
|
||||
paint->brush_asset_reference = nullptr;
|
||||
|
||||
if (brush != nullptr) {
|
||||
std::optional<AssetWeakReference> weak_ref = blender::bke::asset_edit_weak_reference_from_id(
|
||||
brush->id);
|
||||
if (weak_ref.has_value()) {
|
||||
paint->brush_asset_reference = MEM_new<AssetWeakReference>(__func__, *weak_ref);
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_paint_runtime_init(const ToolSettings *ts, Paint *paint)
|
||||
return true;
|
||||
}
|
||||
|
||||
Brush *BKE_paint_brush_from_essentials(Main *bmain, const char *name)
|
||||
{
|
||||
AssetWeakReference weak_ref;
|
||||
weak_ref.asset_library_type = eAssetLibraryType::ASSET_LIBRARY_ESSENTIALS;
|
||||
weak_ref.relative_asset_identifier = BLI_sprintfN("brushes/essentials_brushes.blend/Brush/%s",
|
||||
name);
|
||||
|
||||
return reinterpret_cast<Brush *>(
|
||||
blender::bke::asset_edit_id_from_weak_reference(*bmain, ID_BR, weak_ref));
|
||||
}
|
||||
|
||||
static void paint_brush_set_essentials_reference(Paint *paint, const char *name)
|
||||
{
|
||||
/* Set brush asset reference to a named brush in the essentials asset library. */
|
||||
MEM_delete(paint->brush_asset_reference);
|
||||
|
||||
AssetWeakReference *weak_ref = MEM_new<AssetWeakReference>(__func__);
|
||||
weak_ref->asset_library_type = eAssetLibraryType::ASSET_LIBRARY_ESSENTIALS;
|
||||
weak_ref->relative_asset_identifier = BLI_sprintfN("brushes/essentials_brushes.blend/Brush/%s",
|
||||
name);
|
||||
paint->brush_asset_reference = weak_ref;
|
||||
paint->brush = nullptr;
|
||||
}
|
||||
|
||||
static void paint_eraser_brush_set_essentials_reference(Paint *paint, const char *name)
|
||||
{
|
||||
/* Set brush asset reference to a named brush in the essentials asset library. */
|
||||
MEM_delete(paint->eraser_brush_asset_reference);
|
||||
|
||||
AssetWeakReference *weak_ref = MEM_new<AssetWeakReference>(__func__);
|
||||
weak_ref->asset_library_type = eAssetLibraryType::ASSET_LIBRARY_ESSENTIALS;
|
||||
weak_ref->relative_asset_identifier = BLI_sprintfN("brushes/essentials_brushes.blend/Brush/%s",
|
||||
name);
|
||||
paint->eraser_brush_asset_reference = weak_ref;
|
||||
paint->eraser_brush = nullptr;
|
||||
}
|
||||
|
||||
static void paint_brush_set_default_reference(Paint *paint,
|
||||
const bool do_regular = true,
|
||||
const bool do_eraser = true)
|
||||
{
|
||||
const char *name = nullptr;
|
||||
const char *eraser_name = nullptr;
|
||||
|
||||
switch (paint->runtime.ob_mode) {
|
||||
case OB_MODE_SCULPT:
|
||||
name = "Draw";
|
||||
break;
|
||||
case OB_MODE_VERTEX_PAINT:
|
||||
name = "Paint Vertex";
|
||||
break;
|
||||
case OB_MODE_WEIGHT_PAINT:
|
||||
name = "Paint Weight";
|
||||
break;
|
||||
case OB_MODE_TEXTURE_PAINT:
|
||||
name = "Paint Texture";
|
||||
break;
|
||||
case OB_MODE_SCULPT_CURVES:
|
||||
name = "Comb Curves";
|
||||
break;
|
||||
case OB_MODE_PAINT_GPENCIL_LEGACY:
|
||||
name = "Pencil";
|
||||
eraser_name = "Eraser Soft";
|
||||
break;
|
||||
case OB_MODE_VERTEX_GPENCIL_LEGACY:
|
||||
name = "Paint Point Color";
|
||||
break;
|
||||
case OB_MODE_SCULPT_GPENCIL_LEGACY:
|
||||
name = "Smooth Stroke";
|
||||
break;
|
||||
case OB_MODE_WEIGHT_GPENCIL_LEGACY:
|
||||
name = "Paint Point Weight";
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return;
|
||||
}
|
||||
|
||||
if (do_regular && name) {
|
||||
paint_brush_set_essentials_reference(paint, name);
|
||||
}
|
||||
if (do_eraser && eraser_name) {
|
||||
paint_eraser_brush_set_essentials_reference(paint, eraser_name);
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_paint_brushes_set_default_references(ToolSettings *ts)
|
||||
{
|
||||
if (ts->sculpt) {
|
||||
paint_brush_set_default_reference(&ts->sculpt->paint);
|
||||
}
|
||||
if (ts->curves_sculpt) {
|
||||
paint_brush_set_default_reference(&ts->curves_sculpt->paint);
|
||||
}
|
||||
if (ts->wpaint) {
|
||||
paint_brush_set_default_reference(&ts->wpaint->paint);
|
||||
}
|
||||
if (ts->vpaint) {
|
||||
paint_brush_set_default_reference(&ts->vpaint->paint);
|
||||
}
|
||||
if (ts->gp_paint) {
|
||||
paint_brush_set_default_reference(&ts->gp_paint->paint);
|
||||
}
|
||||
if (ts->gp_vertexpaint) {
|
||||
paint_brush_set_default_reference(&ts->gp_vertexpaint->paint);
|
||||
}
|
||||
if (ts->gp_sculptpaint) {
|
||||
paint_brush_set_default_reference(&ts->gp_sculptpaint->paint);
|
||||
}
|
||||
if (ts->gp_weightpaint) {
|
||||
paint_brush_set_default_reference(&ts->gp_weightpaint->paint);
|
||||
}
|
||||
paint_brush_set_default_reference(&ts->imapaint.paint);
|
||||
}
|
||||
|
||||
bool BKE_paint_brush_set_default(Main *bmain, Paint *paint)
|
||||
{
|
||||
paint_brush_set_default_reference(paint, true, false);
|
||||
return paint_brush_set_from_asset_reference(bmain, paint);
|
||||
}
|
||||
|
||||
bool BKE_paint_brush_set_essentials(Main *bmain, Paint *paint, const char *name)
|
||||
{
|
||||
paint_brush_set_essentials_reference(paint, name);
|
||||
return paint_brush_set_from_asset_reference(bmain, paint);
|
||||
}
|
||||
|
||||
void BKE_paint_brushes_validate(Main *bmain, Paint *paint)
|
||||
{
|
||||
/* Clear brush with invalid mode. Unclear if this can still happen,
|
||||
* but kept from old paint toolslots code. */
|
||||
Brush *brush = BKE_paint_brush(paint);
|
||||
if (brush && (paint->runtime.ob_mode & brush->ob_mode) == 0) {
|
||||
BKE_paint_brush_set(paint, nullptr);
|
||||
BKE_paint_brush_set_default(bmain, paint);
|
||||
}
|
||||
|
||||
Brush *eraser_brush = BKE_paint_eraser_brush(paint);
|
||||
if (eraser_brush && (paint->runtime.ob_mode & eraser_brush->ob_mode) == 0) {
|
||||
BKE_paint_eraser_brush_set(paint, nullptr);
|
||||
BKE_paint_eraser_brush_set_default(bmain, paint);
|
||||
}
|
||||
}
|
||||
|
||||
static bool paint_eraser_brush_set_from_asset_reference(Main *bmain, Paint *paint)
|
||||
{
|
||||
/* Don't resolve this during file read, it will be done after. */
|
||||
if (bmain->is_locked_for_linking) {
|
||||
return false;
|
||||
}
|
||||
/* Attempt to restore a valid active brush from brush asset information. */
|
||||
if (paint->eraser_brush != nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (paint->eraser_brush_asset_reference == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Brush *brush = reinterpret_cast<Brush *>(blender::bke::asset_edit_id_from_weak_reference(
|
||||
*bmain, ID_BR, *paint->eraser_brush_asset_reference));
|
||||
BLI_assert(brush == nullptr || blender::bke::asset_edit_id_is_editable(brush->id));
|
||||
|
||||
/* Ensure we have a brush with appropriate mode to assign.
|
||||
* Could happen if contents of asset blend was manually changed. */
|
||||
if (brush == nullptr || (paint->runtime.ob_mode & brush->ob_mode) == 0) {
|
||||
MEM_delete(paint->eraser_brush_asset_reference);
|
||||
paint->eraser_brush_asset_reference = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
paint->eraser_brush = brush;
|
||||
return true;
|
||||
}
|
||||
|
||||
Brush *BKE_paint_eraser_brush(Paint *paint)
|
||||
{
|
||||
return (Brush *)BKE_paint_eraser_brush_for_read((const Paint *)paint);
|
||||
}
|
||||
|
||||
const Brush *BKE_paint_eraser_brush_for_read(const Paint *paint)
|
||||
{
|
||||
return paint ? paint->eraser_brush : nullptr;
|
||||
}
|
||||
|
||||
bool BKE_paint_eraser_brush_set(Paint *paint, Brush *brush)
|
||||
{
|
||||
if (paint == nullptr || paint->eraser_brush == brush) {
|
||||
return false;
|
||||
}
|
||||
if (brush && (paint->runtime.ob_mode & brush->ob_mode) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
paint->eraser_brush = brush;
|
||||
|
||||
MEM_delete(paint->eraser_brush_asset_reference);
|
||||
paint->eraser_brush_asset_reference = nullptr;
|
||||
|
||||
if (brush != nullptr) {
|
||||
std::optional<AssetWeakReference> weak_ref = blender::bke::asset_edit_weak_reference_from_id(
|
||||
brush->id);
|
||||
if (weak_ref.has_value()) {
|
||||
paint->eraser_brush_asset_reference = MEM_new<AssetWeakReference>(__func__, *weak_ref);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Brush *BKE_paint_eraser_brush_from_essentials(Main *bmain, const char *name)
|
||||
{
|
||||
AssetWeakReference weak_ref;
|
||||
weak_ref.asset_library_type = eAssetLibraryType::ASSET_LIBRARY_ESSENTIALS;
|
||||
weak_ref.relative_asset_identifier = BLI_sprintfN("brushes/essentials_brushes.blend/Brush/%s",
|
||||
name);
|
||||
|
||||
return reinterpret_cast<Brush *>(
|
||||
blender::bke::asset_edit_id_from_weak_reference(*bmain, ID_BR, weak_ref));
|
||||
}
|
||||
|
||||
bool BKE_paint_eraser_brush_set_default(Main *bmain, Paint *paint)
|
||||
{
|
||||
paint_brush_set_default_reference(paint, false, true);
|
||||
return paint_eraser_brush_set_from_asset_reference(bmain, paint);
|
||||
}
|
||||
|
||||
bool BKE_paint_eraser_brush_set_essentials(Main *bmain, Paint *paint, const char *name)
|
||||
{
|
||||
paint_eraser_brush_set_essentials_reference(paint, name);
|
||||
return paint_eraser_brush_set_from_asset_reference(bmain, paint);
|
||||
}
|
||||
|
||||
static void paint_runtime_init(const ToolSettings *ts, Paint *paint)
|
||||
{
|
||||
if (paint == &ts->imapaint.paint) {
|
||||
paint->runtime.tool_offset = offsetof(Brush, imagepaint_tool);
|
||||
paint->runtime.ob_mode = OB_MODE_TEXTURE_PAINT;
|
||||
}
|
||||
else if (ts->sculpt && paint == &ts->sculpt->paint) {
|
||||
paint->runtime.tool_offset = offsetof(Brush, sculpt_tool);
|
||||
paint->runtime.ob_mode = OB_MODE_SCULPT;
|
||||
}
|
||||
else if (ts->vpaint && paint == &ts->vpaint->paint) {
|
||||
paint->runtime.tool_offset = offsetof(Brush, vertexpaint_tool);
|
||||
paint->runtime.ob_mode = OB_MODE_VERTEX_PAINT;
|
||||
}
|
||||
else if (ts->wpaint && paint == &ts->wpaint->paint) {
|
||||
paint->runtime.tool_offset = offsetof(Brush, weightpaint_tool);
|
||||
paint->runtime.ob_mode = OB_MODE_WEIGHT_PAINT;
|
||||
}
|
||||
else if (ts->gp_paint && paint == &ts->gp_paint->paint) {
|
||||
paint->runtime.tool_offset = offsetof(Brush, gpencil_tool);
|
||||
paint->runtime.ob_mode = OB_MODE_PAINT_GPENCIL_LEGACY;
|
||||
}
|
||||
else if (ts->gp_vertexpaint && paint == &ts->gp_vertexpaint->paint) {
|
||||
paint->runtime.tool_offset = offsetof(Brush, gpencil_vertex_tool);
|
||||
paint->runtime.ob_mode = OB_MODE_VERTEX_GPENCIL_LEGACY;
|
||||
}
|
||||
else if (ts->gp_sculptpaint && paint == &ts->gp_sculptpaint->paint) {
|
||||
paint->runtime.tool_offset = offsetof(Brush, gpencil_sculpt_tool);
|
||||
paint->runtime.ob_mode = OB_MODE_SCULPT_GPENCIL_LEGACY;
|
||||
}
|
||||
else if (ts->gp_weightpaint && paint == &ts->gp_weightpaint->paint) {
|
||||
paint->runtime.tool_offset = offsetof(Brush, gpencil_weight_tool);
|
||||
paint->runtime.ob_mode = OB_MODE_WEIGHT_GPENCIL_LEGACY;
|
||||
}
|
||||
else if (ts->curves_sculpt && paint == &ts->curves_sculpt->paint) {
|
||||
paint->runtime.tool_offset = offsetof(Brush, curves_sculpt_tool);
|
||||
paint->runtime.ob_mode = OB_MODE_SCULPT_CURVES;
|
||||
}
|
||||
else {
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
|
||||
paint->runtime.initialized = true;
|
||||
}
|
||||
|
||||
uint BKE_paint_get_brush_tool_offset_from_paintmode(const PaintMode mode)
|
||||
|
@ -1091,17 +1309,15 @@ eObjectMode BKE_paint_object_mode_from_paintmode(const PaintMode mode)
|
|||
}
|
||||
}
|
||||
|
||||
bool BKE_paint_ensure(Main * /*bmain*/, ToolSettings *ts, Paint **r_paint)
|
||||
bool BKE_paint_ensure(Main *bmain, ToolSettings *ts, Paint **r_paint)
|
||||
{
|
||||
Paint *paint = nullptr;
|
||||
if (*r_paint) {
|
||||
/* Tool offset should never be 0 for initialized paint settings, so it's a reliable way to
|
||||
* check if already initialized. */
|
||||
if ((*r_paint)->runtime.tool_offset == 0) {
|
||||
if (!(*r_paint)->runtime.initialized) {
|
||||
/* Currently only image painting is initialized this way, others have to be allocated. */
|
||||
BLI_assert(ELEM(*r_paint, (Paint *)&ts->imapaint));
|
||||
|
||||
BKE_paint_runtime_init(ts, *r_paint);
|
||||
paint_runtime_init(ts, *r_paint);
|
||||
}
|
||||
else {
|
||||
BLI_assert(ELEM(*r_paint,
|
||||
|
@ -1117,13 +1333,14 @@ bool BKE_paint_ensure(Main * /*bmain*/, ToolSettings *ts, Paint **r_paint)
|
|||
(Paint *)&ts->imapaint));
|
||||
#ifndef NDEBUG
|
||||
Paint paint_test = **r_paint;
|
||||
BKE_paint_runtime_init(ts, *r_paint);
|
||||
paint_runtime_init(ts, *r_paint);
|
||||
/* Swap so debug doesn't hide errors when release fails. */
|
||||
std::swap(**r_paint, paint_test);
|
||||
BLI_assert(paint_test.runtime.ob_mode == (*r_paint)->runtime.ob_mode);
|
||||
BLI_assert(paint_test.runtime.tool_offset == (*r_paint)->runtime.tool_offset);
|
||||
#endif
|
||||
}
|
||||
paint_brush_set_from_asset_reference(bmain, *r_paint);
|
||||
paint_eraser_brush_set_from_asset_reference(bmain, *r_paint);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1166,7 +1383,9 @@ bool BKE_paint_ensure(Main * /*bmain*/, ToolSettings *ts, Paint **r_paint)
|
|||
|
||||
*r_paint = paint;
|
||||
|
||||
BKE_paint_runtime_init(ts, paint);
|
||||
paint_runtime_init(ts, paint);
|
||||
BKE_paint_brush_set_default(bmain, paint);
|
||||
BKE_paint_eraser_brush_set_default(bmain, paint);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -1178,18 +1397,6 @@ void BKE_paint_init(Main *bmain, Scene *sce, PaintMode mode, const uchar col[3])
|
|||
|
||||
BKE_paint_ensure_from_paintmode(bmain, sce, mode);
|
||||
|
||||
/* If there's no brush, create one */
|
||||
Brush *brush = BKE_paint_brush(paint);
|
||||
if (brush == nullptr) {
|
||||
eObjectMode ob_mode = BKE_paint_object_mode_from_paintmode(mode);
|
||||
brush = BKE_brush_first_search(bmain, ob_mode);
|
||||
if (!brush) {
|
||||
brush = BKE_brush_add(bmain, "Brush", ob_mode);
|
||||
id_us_min(&brush->id); /* Fake user only. */
|
||||
}
|
||||
BKE_paint_brush_set(paint, brush);
|
||||
}
|
||||
|
||||
copy_v3_v3_uchar(paint->paint_cursor_col, col);
|
||||
paint->paint_cursor_col[3] = 128;
|
||||
ups->last_stroke_valid = false;
|
||||
|
@ -1203,23 +1410,26 @@ void BKE_paint_init(Main *bmain, Scene *sce, PaintMode mode, const uchar col[3])
|
|||
void BKE_paint_free(Paint *paint)
|
||||
{
|
||||
BKE_curvemapping_free(paint->cavity_curve);
|
||||
MEM_SAFE_FREE(paint->tool_slots);
|
||||
MEM_delete(paint->brush_asset_reference);
|
||||
MEM_delete(paint->eraser_brush_asset_reference);
|
||||
}
|
||||
|
||||
void BKE_paint_copy(const Paint *src, Paint *tar, const int flag)
|
||||
void BKE_paint_copy(const Paint *src, Paint *dst, const int flag)
|
||||
{
|
||||
tar->brush = src->brush;
|
||||
tar->cavity_curve = BKE_curvemapping_copy(src->cavity_curve);
|
||||
tar->tool_slots = static_cast<PaintToolSlot *>(MEM_dupallocN(src->tool_slots));
|
||||
dst->brush = src->brush;
|
||||
dst->cavity_curve = BKE_curvemapping_copy(src->cavity_curve);
|
||||
|
||||
if (src->brush_asset_reference) {
|
||||
dst->brush_asset_reference = MEM_new<AssetWeakReference>(__func__,
|
||||
*src->brush_asset_reference);
|
||||
}
|
||||
if (src->eraser_brush_asset_reference) {
|
||||
dst->eraser_brush_asset_reference = MEM_new<AssetWeakReference>(
|
||||
__func__, *src->eraser_brush_asset_reference);
|
||||
}
|
||||
|
||||
if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
|
||||
id_us_plus((ID *)tar->brush);
|
||||
id_us_plus((ID *)tar->palette);
|
||||
if (src->tool_slots != nullptr) {
|
||||
for (int i = 0; i < tar->tool_slots_len; i++) {
|
||||
id_us_plus((ID *)tar->tool_slots[i].brush);
|
||||
}
|
||||
}
|
||||
id_us_plus((ID *)dst->palette);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1240,7 +1450,12 @@ void BKE_paint_blend_write(BlendWriter *writer, Paint *p)
|
|||
if (p->cavity_curve) {
|
||||
BKE_curvemapping_blend_write(writer, p->cavity_curve);
|
||||
}
|
||||
BLO_write_struct_array(writer, PaintToolSlot, p->tool_slots_len, p->tool_slots);
|
||||
if (p->brush_asset_reference) {
|
||||
BKE_asset_weak_reference_write(writer, p->brush_asset_reference);
|
||||
}
|
||||
if (p->eraser_brush_asset_reference) {
|
||||
BKE_asset_weak_reference_write(writer, p->eraser_brush_asset_reference);
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_paint_blend_read_data(BlendDataReader *reader, const Scene *scene, Paint *p)
|
||||
|
@ -1253,17 +1468,18 @@ void BKE_paint_blend_read_data(BlendDataReader *reader, const Scene *scene, Pain
|
|||
BKE_paint_cavity_curve_preset(p, CURVE_PRESET_LINE);
|
||||
}
|
||||
|
||||
BLO_read_struct_array(reader, PaintToolSlot, p->tool_slots_len, &p->tool_slots);
|
||||
BLO_read_struct(reader, AssetWeakReference, &p->brush_asset_reference);
|
||||
if (p->brush_asset_reference) {
|
||||
BKE_asset_weak_reference_read(reader, p->brush_asset_reference);
|
||||
}
|
||||
|
||||
/* Workaround for invalid data written in older versions. */
|
||||
const size_t expected_size = sizeof(PaintToolSlot) * p->tool_slots_len;
|
||||
if (p->tool_slots && MEM_allocN_len(p->tool_slots) < expected_size) {
|
||||
MEM_freeN(p->tool_slots);
|
||||
p->tool_slots = static_cast<PaintToolSlot *>(MEM_callocN(expected_size, "PaintToolSlot"));
|
||||
BLO_read_struct(reader, AssetWeakReference, &p->eraser_brush_asset_reference);
|
||||
if (p->eraser_brush_asset_reference) {
|
||||
BKE_asset_weak_reference_read(reader, p->eraser_brush_asset_reference);
|
||||
}
|
||||
|
||||
p->paint_cursor = nullptr;
|
||||
BKE_paint_runtime_init(scene->toolsettings, p);
|
||||
paint_runtime_init(scene->toolsettings, p);
|
||||
}
|
||||
|
||||
bool paint_is_grid_face_hidden(const blender::BoundedBitSpan grid_hidden,
|
||||
|
|
|
@ -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;
|
||||
Brush *eraser_brush_tmp = nullptr;
|
||||
Brush **eraser_brush_p = paint ? &paint->eraser_brush : &eraser_brush_tmp;
|
||||
BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER_P(data,
|
||||
brush_p,
|
||||
eraser_brush_p,
|
||||
do_undo_restore,
|
||||
SCENE_FOREACH_UNDO_RESTORE,
|
||||
reader,
|
||||
&paint_old->tool_slots[i].brush,
|
||||
IDWALK_CB_USER);
|
||||
}
|
||||
&paint_old->eraser_brush,
|
||||
IDWALK_CB_NOP);
|
||||
|
||||
Palette *palette_tmp = nullptr;
|
||||
Palette **palette_p = paint ? &paint->palette : &palette_tmp;
|
||||
|
|
|
@ -2583,7 +2583,6 @@ void do_versions_after_linking_280(FileData *fd, Main *bmain)
|
|||
brush->gpencil_tool = brush->gpencil_settings->brush_type;
|
||||
}
|
||||
}
|
||||
BKE_paint_toolslots_init_from_main(bmain);
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 280, 38)) {
|
||||
|
@ -2862,13 +2861,9 @@ void do_versions_after_linking_280(FileData *fd, Main *bmain)
|
|||
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 282, 2)) {
|
||||
/* Init all Vertex/Sculpt and Weight Paint brushes. */
|
||||
Brush *brush;
|
||||
Material *ma;
|
||||
/* Pen Soft brush. */
|
||||
brush = (Brush *)do_versions_rename_id(bmain, ID_BR, "Draw Soft", "Pencil Soft");
|
||||
if (brush) {
|
||||
brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PEN;
|
||||
}
|
||||
do_versions_rename_id(bmain, ID_BR, "Draw Soft", "Pencil Soft");
|
||||
do_versions_rename_id(bmain, ID_BR, "Draw Pencil", "Pencil");
|
||||
do_versions_rename_id(bmain, ID_BR, "Draw Pen", "Pen");
|
||||
do_versions_rename_id(bmain, ID_BR, "Draw Ink", "Ink Pen");
|
||||
|
@ -2896,9 +2891,6 @@ void do_versions_after_linking_280(FileData *fd, Main *bmain)
|
|||
do_versions_rename_id(bmain, ID_MA, "Black Dots", "Dots Stroke");
|
||||
}
|
||||
|
||||
brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, "Pencil", offsetof(ID, name) + 2));
|
||||
|
||||
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
|
||||
ToolSettings *ts = scene->toolsettings;
|
||||
|
||||
|
@ -2908,15 +2900,11 @@ void do_versions_after_linking_280(FileData *fd, Main *bmain)
|
|||
BKE_paint_ensure_from_paintmode(bmain, scene, PaintMode::SculptGPencil);
|
||||
BKE_paint_ensure_from_paintmode(bmain, scene, PaintMode::WeightGPencil);
|
||||
|
||||
/* Set default Draw brush. */
|
||||
if (brush != nullptr) {
|
||||
Paint *paint = &ts->gp_paint->paint;
|
||||
BKE_paint_brush_set(paint, brush);
|
||||
/* Enable cursor by default. */
|
||||
Paint *paint = &ts->gp_paint->paint;
|
||||
paint->flags |= PAINT_SHOW_BRUSH;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 282, 4)) {
|
||||
FOREACH_NODETREE_BEGIN (bmain, ntree, id) {
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "DNA_movieclip_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_sequence_types.h"
|
||||
#include "DNA_workspace_types.h"
|
||||
#include "DNA_world_types.h"
|
||||
|
||||
#include "DNA_defaults.h"
|
||||
|
@ -50,6 +51,7 @@
|
|||
#include "BKE_armature.hh"
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_colortools.hh"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_curve.hh"
|
||||
#include "BKE_customdata.hh"
|
||||
#include "BKE_effect.h"
|
||||
|
@ -60,6 +62,7 @@
|
|||
#include "BKE_mesh_legacy_convert.hh"
|
||||
#include "BKE_nla.h"
|
||||
#include "BKE_node_runtime.hh"
|
||||
#include "BKE_paint.hh"
|
||||
#include "BKE_scene.hh"
|
||||
#include "BKE_tracking.h"
|
||||
|
||||
|
@ -2114,6 +2117,40 @@ static bool seq_filter_bilinear_to_auto(Sequence *seq, void * /*user_data*/)
|
|||
return true;
|
||||
}
|
||||
|
||||
static void update_paint_modes_for_brush_assets(Main &bmain)
|
||||
{
|
||||
/* Replace paint brushes with a reference to the default brush asset for that mode. */
|
||||
LISTBASE_FOREACH (Scene *, scene, &bmain.scenes) {
|
||||
BKE_paint_brushes_set_default_references(scene->toolsettings);
|
||||
}
|
||||
|
||||
/* Replace persistent tool references with the new single builtin brush tool. */
|
||||
LISTBASE_FOREACH (WorkSpace *, workspace, &bmain.workspaces) {
|
||||
LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) {
|
||||
if (tref->space_type != SPACE_VIEW3D) {
|
||||
continue;
|
||||
}
|
||||
if (!ELEM(tref->mode,
|
||||
CTX_MODE_SCULPT,
|
||||
CTX_MODE_PAINT_VERTEX,
|
||||
CTX_MODE_PAINT_WEIGHT,
|
||||
CTX_MODE_PAINT_TEXTURE,
|
||||
CTX_MODE_PAINT_GPENCIL_LEGACY,
|
||||
CTX_MODE_PAINT_GREASE_PENCIL,
|
||||
CTX_MODE_SCULPT_GPENCIL_LEGACY,
|
||||
CTX_MODE_SCULPT_GREASE_PENCIL,
|
||||
CTX_MODE_WEIGHT_GPENCIL_LEGACY,
|
||||
CTX_MODE_WEIGHT_GREASE_PENCIL,
|
||||
CTX_MODE_VERTEX_GPENCIL_LEGACY,
|
||||
CTX_MODE_SCULPT_CURVES))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
STRNCPY(tref->idname, "builtin.brush");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void image_settings_avi_to_ffmpeg(Scene *scene)
|
||||
{
|
||||
if (ELEM(scene->r.im_format.imtype, R_IMF_IMTYPE_AVIRAW, R_IMF_IMTYPE_AVIJPEG)) {
|
||||
|
@ -3640,6 +3677,10 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
|
|||
}
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 402, 43)) {
|
||||
update_paint_modes_for_brush_assets(*bmain);
|
||||
}
|
||||
|
||||
/**
|
||||
* Always bump subversion in BKE_blender_version.h when adding versioning
|
||||
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "BLI_math_rotation.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_math_vector_types.hh"
|
||||
#include "BLI_mempool.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
|
@ -451,43 +452,8 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template)
|
|||
BLO_update_defaults_workspace(workspace, app_template);
|
||||
}
|
||||
|
||||
/* New grease pencil brushes and vertex paint setup. */
|
||||
/* Grease pencil materials and paint modes setup. */
|
||||
{
|
||||
/* Update Grease Pencil brushes. */
|
||||
Brush *brush;
|
||||
|
||||
/* Pencil brush. */
|
||||
do_versions_rename_id(bmain, ID_BR, "Draw Pencil", "Pencil");
|
||||
|
||||
/* Pen brush. */
|
||||
do_versions_rename_id(bmain, ID_BR, "Draw Pen", "Pen");
|
||||
|
||||
/* Pen Soft brush. */
|
||||
brush = reinterpret_cast<Brush *>(
|
||||
do_versions_rename_id(bmain, ID_BR, "Draw Soft", "Pencil Soft"));
|
||||
if (brush) {
|
||||
brush->gpencil_settings->icon_id = GP_BRUSH_ICON_PEN;
|
||||
}
|
||||
|
||||
/* Ink Pen brush. */
|
||||
do_versions_rename_id(bmain, ID_BR, "Draw Ink", "Ink Pen");
|
||||
|
||||
/* Ink Pen Rough brush. */
|
||||
do_versions_rename_id(bmain, ID_BR, "Draw Noise", "Ink Pen Rough");
|
||||
|
||||
/* Marker Bold brush. */
|
||||
do_versions_rename_id(bmain, ID_BR, "Draw Marker", "Marker Bold");
|
||||
|
||||
/* Marker Chisel brush. */
|
||||
do_versions_rename_id(bmain, ID_BR, "Draw Block", "Marker Chisel");
|
||||
|
||||
/* Remove useless Fill Area.001 brush. */
|
||||
brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, "Fill Area.001", offsetof(ID, name) + 2));
|
||||
if (brush) {
|
||||
BKE_id_delete(bmain, brush);
|
||||
}
|
||||
|
||||
/* Rename and fix materials and enable default object lights on. */
|
||||
if (app_template && STREQ(app_template, "2D_Animation")) {
|
||||
Material *ma = nullptr;
|
||||
|
@ -538,23 +504,10 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template)
|
|||
}
|
||||
}
|
||||
|
||||
/* Reset all grease pencil brushes. */
|
||||
/* Reset grease pencil paint modes. */
|
||||
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
|
||||
ToolSettings *ts = scene->toolsettings;
|
||||
|
||||
if (ts->gp_paint) {
|
||||
BKE_brush_gpencil_paint_presets(bmain, ts, true);
|
||||
}
|
||||
if (ts->gp_sculptpaint) {
|
||||
BKE_brush_gpencil_sculpt_presets(bmain, ts, true);
|
||||
}
|
||||
if (ts->gp_vertexpaint) {
|
||||
BKE_brush_gpencil_vertex_presets(bmain, ts, true);
|
||||
}
|
||||
if (ts->gp_weightpaint) {
|
||||
BKE_brush_gpencil_weight_presets(bmain, ts, true);
|
||||
}
|
||||
|
||||
/* Ensure new Paint modes. */
|
||||
BKE_paint_ensure_from_paintmode(bmain, scene, PaintMode::VertexGPencil);
|
||||
BKE_paint_ensure_from_paintmode(bmain, scene, PaintMode::SculptGPencil);
|
||||
|
@ -711,203 +664,24 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template)
|
|||
|
||||
/* Brushes */
|
||||
{
|
||||
/* Enable for UV sculpt (other brush types will be created as needed),
|
||||
* without this the grab brush will be active but not selectable from the list. */
|
||||
const char *brush_name = "Grab";
|
||||
Brush *brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
|
||||
if (brush) {
|
||||
brush->ob_mode |= OB_MODE_EDIT;
|
||||
/* Remove default brushes replaced by assets. Also remove outliner `treestore` that may point
|
||||
* to brushes. Normally the treestore is updated properly but it doesn't seem to update during
|
||||
* versioning code. It's not helpful anyway. */
|
||||
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
|
||||
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
|
||||
LISTBASE_FOREACH (SpaceLink *, space_link, &area->spacedata) {
|
||||
if (space_link->spacetype == SPACE_OUTLINER) {
|
||||
SpaceOutliner *space_outliner = reinterpret_cast<SpaceOutliner *>(space_link);
|
||||
if (space_outliner->treestore) {
|
||||
BLI_mempool_destroy(space_outliner->treestore);
|
||||
space_outliner->treestore = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
|
||||
brush->blur_kernel_radius = 2;
|
||||
|
||||
/* Use full strength for all non-sculpt brushes,
|
||||
* when painting we want to use full color/weight always.
|
||||
*
|
||||
* Note that sculpt is an exception,
|
||||
* its values are overwritten by #BKE_brush_sculpt_reset below. */
|
||||
brush->alpha = 1.0;
|
||||
|
||||
/* Enable anti-aliasing by default. */
|
||||
brush->sampling_flag |= BRUSH_PAINT_ANTIALIASING;
|
||||
|
||||
/* By default, each brush should use a single input sample. */
|
||||
brush->input_samples = 1;
|
||||
}
|
||||
|
||||
{
|
||||
/* Change the spacing of the Smear brush to 3.0% */
|
||||
const char *brush_name;
|
||||
Brush *brush;
|
||||
|
||||
brush_name = "Smear";
|
||||
brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
|
||||
if (brush) {
|
||||
brush->spacing = 3.0;
|
||||
}
|
||||
|
||||
brush_name = "Draw Sharp";
|
||||
brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
|
||||
if (!brush) {
|
||||
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
|
||||
id_us_min(&brush->id);
|
||||
brush->sculpt_tool = SCULPT_TOOL_DRAW_SHARP;
|
||||
}
|
||||
|
||||
brush_name = "Elastic Deform";
|
||||
brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
|
||||
if (!brush) {
|
||||
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
|
||||
id_us_min(&brush->id);
|
||||
brush->sculpt_tool = SCULPT_TOOL_ELASTIC_DEFORM;
|
||||
}
|
||||
|
||||
brush_name = "Pose";
|
||||
brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
|
||||
if (!brush) {
|
||||
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
|
||||
id_us_min(&brush->id);
|
||||
brush->sculpt_tool = SCULPT_TOOL_POSE;
|
||||
}
|
||||
|
||||
brush_name = "Multi-plane Scrape";
|
||||
brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
|
||||
if (!brush) {
|
||||
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
|
||||
id_us_min(&brush->id);
|
||||
brush->sculpt_tool = SCULPT_TOOL_MULTIPLANE_SCRAPE;
|
||||
}
|
||||
|
||||
brush_name = "Clay Thumb";
|
||||
brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
|
||||
if (!brush) {
|
||||
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
|
||||
id_us_min(&brush->id);
|
||||
brush->sculpt_tool = SCULPT_TOOL_CLAY_THUMB;
|
||||
}
|
||||
|
||||
brush_name = "Cloth";
|
||||
brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
|
||||
if (!brush) {
|
||||
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
|
||||
id_us_min(&brush->id);
|
||||
brush->sculpt_tool = SCULPT_TOOL_CLOTH;
|
||||
}
|
||||
|
||||
brush_name = "Slide Relax";
|
||||
brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
|
||||
if (!brush) {
|
||||
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
|
||||
id_us_min(&brush->id);
|
||||
brush->sculpt_tool = SCULPT_TOOL_SLIDE_RELAX;
|
||||
}
|
||||
|
||||
brush_name = "Paint";
|
||||
brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
|
||||
if (!brush) {
|
||||
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
|
||||
id_us_min(&brush->id);
|
||||
brush->sculpt_tool = SCULPT_TOOL_PAINT;
|
||||
}
|
||||
|
||||
brush_name = "Smear";
|
||||
brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
|
||||
if (!brush) {
|
||||
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
|
||||
id_us_min(&brush->id);
|
||||
brush->sculpt_tool = SCULPT_TOOL_SMEAR;
|
||||
}
|
||||
|
||||
brush_name = "Boundary";
|
||||
brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
|
||||
if (!brush) {
|
||||
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
|
||||
id_us_min(&brush->id);
|
||||
brush->sculpt_tool = SCULPT_TOOL_BOUNDARY;
|
||||
}
|
||||
|
||||
brush_name = "Simplify";
|
||||
brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
|
||||
if (!brush) {
|
||||
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
|
||||
id_us_min(&brush->id);
|
||||
brush->sculpt_tool = SCULPT_TOOL_SIMPLIFY;
|
||||
}
|
||||
|
||||
brush_name = "Draw Face Sets";
|
||||
brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
|
||||
if (!brush) {
|
||||
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
|
||||
id_us_min(&brush->id);
|
||||
brush->sculpt_tool = SCULPT_TOOL_DRAW_FACE_SETS;
|
||||
}
|
||||
|
||||
brush_name = "Multires Displacement Eraser";
|
||||
brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
|
||||
if (!brush) {
|
||||
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
|
||||
id_us_min(&brush->id);
|
||||
brush->sculpt_tool = SCULPT_TOOL_DISPLACEMENT_ERASER;
|
||||
}
|
||||
|
||||
brush_name = "Multires Displacement Smear";
|
||||
brush = static_cast<Brush *>(
|
||||
BLI_findstring(&bmain->brushes, brush_name, offsetof(ID, name) + 2));
|
||||
if (!brush) {
|
||||
brush = BKE_brush_add(bmain, brush_name, OB_MODE_SCULPT);
|
||||
id_us_min(&brush->id);
|
||||
brush->sculpt_tool = SCULPT_TOOL_DISPLACEMENT_SMEAR;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
|
||||
/* Use the same tool icon color in the brush cursor */
|
||||
if (brush->ob_mode & OB_MODE_SCULPT) {
|
||||
BLI_assert(brush->sculpt_tool != 0);
|
||||
BKE_brush_sculpt_reset(brush);
|
||||
}
|
||||
|
||||
/* Set the default texture mapping.
|
||||
* Do it for all brushes, since some of them might be coming from the startup file. */
|
||||
brush->mtex.brush_map_mode = MTEX_MAP_MODE_VIEW;
|
||||
brush->mask_mtex.brush_map_mode = MTEX_MAP_MODE_VIEW;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const Brush *default_brush = DNA_struct_default_get(Brush);
|
||||
LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
|
||||
brush->automasking_start_normal_limit = default_brush->automasking_start_normal_limit;
|
||||
brush->automasking_start_normal_falloff = default_brush->automasking_start_normal_falloff;
|
||||
|
||||
brush->automasking_view_normal_limit = default_brush->automasking_view_normal_limit;
|
||||
brush->automasking_view_normal_falloff = default_brush->automasking_view_normal_falloff;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
|
||||
if (!brush->automasking_cavity_curve) {
|
||||
brush->automasking_cavity_curve = BKE_sculpt_default_cavity_curve();
|
||||
}
|
||||
LISTBASE_FOREACH_MUTABLE (Brush *, brush, &bmain->brushes) {
|
||||
BKE_id_delete(bmain, brush);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -961,6 +961,15 @@ void blo_do_versions_userdef(UserDef *userdef)
|
|||
}
|
||||
}
|
||||
|
||||
if (!USER_VERSION_ATLEAST(402, 43)) {
|
||||
BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(
|
||||
userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh Sculpt/Cloth");
|
||||
BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(
|
||||
userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh Sculpt/General");
|
||||
BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(
|
||||
userdef, "VIEW3D_AST_brush_sculpt", "Brushes/Mesh Sculpt/Paint");
|
||||
}
|
||||
|
||||
/**
|
||||
* Always bump subversion in BKE_blender_version.h when adding versioning
|
||||
* code here, and wrap it inside a USER_VERSION_ATLEAST check.
|
||||
|
|
|
@ -34,7 +34,7 @@ set(SRC
|
|||
intern/asset_shelf.cc
|
||||
intern/asset_shelf_asset_view.cc
|
||||
intern/asset_shelf_catalog_selector.cc
|
||||
intern/asset_shelf_popup.cc
|
||||
intern/asset_shelf_popover.cc
|
||||
intern/asset_shelf_regiondata.cc
|
||||
intern/asset_shelf_settings.cc
|
||||
intern/asset_temp_id_consumer.cc
|
||||
|
|
|
@ -23,6 +23,7 @@ struct Main;
|
|||
struct SpaceType;
|
||||
struct uiBlock;
|
||||
struct RegionPollParams;
|
||||
struct wmRegionMessageSubscribeParams;
|
||||
struct wmWindowManager;
|
||||
|
||||
namespace blender {
|
||||
|
@ -52,6 +53,7 @@ void region_init(wmWindowManager *wm, ARegion *region);
|
|||
int region_snap(const ARegion *region, int size, int axis);
|
||||
void region_on_user_resize(const ARegion *region);
|
||||
void region_listen(const wmRegionListenerParams *params);
|
||||
void region_message_subscribe(const wmRegionMessageSubscribeParams *params);
|
||||
void region_layout(const bContext *C, ARegion *region);
|
||||
void region_draw(const bContext *C, ARegion *region);
|
||||
void region_on_poll_success(const bContext *C, ARegion *region);
|
||||
|
@ -63,7 +65,7 @@ void header_region_init(wmWindowManager *wm, ARegion *region);
|
|||
void header_region(const bContext *C, ARegion *region);
|
||||
void header_region_listen(const wmRegionListenerParams *params);
|
||||
int header_region_size();
|
||||
void header_regiontype_register(ARegionType *region_type, const int space_type);
|
||||
void types_register(ARegionType *region_type, const int space_type);
|
||||
|
||||
/** \} */
|
||||
|
||||
|
@ -88,7 +90,6 @@ AssetShelfType *type_find_from_idname(const StringRef idname);
|
|||
/** \name Asset Shelf Popup
|
||||
* \{ */
|
||||
|
||||
uiBlock *popup_block_create(const bContext *C, ARegion *region, AssetShelfType *shelf_type);
|
||||
void type_popup_unlink(const AssetShelfType &shelf_type);
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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(). */
|
||||
|
@ -815,7 +832,7 @@ static void asset_shelf_header_draw(const bContext *C, Header *header)
|
|||
uiItemR(sub, &shelf_ptr, "search_filter", UI_ITEM_NONE, "", ICON_VIEWZOOM);
|
||||
}
|
||||
|
||||
void header_regiontype_register(ARegionType *region_type, const int space_type)
|
||||
static void header_regiontype_register(ARegionType *region_type, const int space_type)
|
||||
{
|
||||
HeaderType *ht = MEM_cnew<HeaderType>(__func__);
|
||||
STRNCPY(ht->idname, "ASSETSHELF_HT_settings");
|
||||
|
@ -827,8 +844,13 @@ void header_regiontype_register(ARegionType *region_type, const int space_type)
|
|||
};
|
||||
|
||||
BLI_addtail(®ion_type->headertypes, ht);
|
||||
}
|
||||
|
||||
void types_register(ARegionType *region_type, const int space_type)
|
||||
{
|
||||
header_regiontype_register(region_type, space_type);
|
||||
catalog_selector_panel_register(region_type);
|
||||
popover_panel_register(region_type);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -38,6 +38,7 @@ void build_asset_view(uiLayout &layout,
|
|||
const ARegion ®ion);
|
||||
|
||||
void catalog_selector_panel_register(ARegionType *region_type);
|
||||
void popover_panel_register(ARegionType *region_type);
|
||||
|
||||
AssetShelf *active_shelf_from_context(const bContext *C);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
#include "BKE_screen.hh"
|
||||
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "UI_interface_c.hh"
|
||||
|
@ -22,6 +24,8 @@
|
|||
#include "RNA_access.hh"
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
#include "WM_api.hh"
|
||||
|
||||
namespace blender::ed::asset::shelf {
|
||||
|
||||
class StaticPopupShelves {
|
||||
|
@ -51,20 +55,20 @@ void type_popup_unlink(const AssetShelfType &shelf_type)
|
|||
}
|
||||
}
|
||||
|
||||
static AssetShelf *get_shelf_for_popup(const bContext *C, AssetShelfType &shelf_type)
|
||||
static AssetShelf *get_shelf_for_popup(const bContext &C, AssetShelfType &shelf_type)
|
||||
{
|
||||
Vector<AssetShelf *> &popup_shelves = StaticPopupShelves::shelves();
|
||||
|
||||
for (AssetShelf *shelf : popup_shelves) {
|
||||
if (STREQ(shelf->idname, shelf_type.idname)) {
|
||||
if (type_poll_for_popup(*C, ensure_shelf_has_type(*shelf))) {
|
||||
if (type_poll_for_popup(C, ensure_shelf_has_type(*shelf))) {
|
||||
return shelf;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (type_poll_for_popup(*C, &shelf_type)) {
|
||||
if (type_poll_for_popup(C, &shelf_type)) {
|
||||
AssetShelf *new_shelf = create_shelf_from_type(shelf_type);
|
||||
new_shelf->settings.display_flag |= ASSETSHELF_SHOW_NAMES;
|
||||
popup_shelves.append(new_shelf);
|
||||
|
@ -157,36 +161,43 @@ static void catalog_tree_draw(uiLayout &layout, AssetShelf &shelf)
|
|||
ui::TreeViewBuilder::build_tree_view(*tree_view, layout);
|
||||
}
|
||||
|
||||
uiBlock *popup_block_create(const bContext *C, ARegion *region, AssetShelfType *shelf_type)
|
||||
static AssetShelfType *lookup_type_from_idname_in_context(const bContext *C)
|
||||
{
|
||||
bScreen *screen = CTX_wm_screen(C);
|
||||
uiBlock *block = UI_block_begin(C, region, "_popup", UI_EMBOSS);
|
||||
UI_block_flag_enable(block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_POPOVER);
|
||||
UI_block_theme_style_set(block, UI_BLOCK_THEME_STYLE_POPUP);
|
||||
UI_block_bounds_set_normal(block, 0.3f * U.widget_unit);
|
||||
UI_block_direction_set(block, UI_DIR_DOWN);
|
||||
|
||||
AssetShelf *shelf = get_shelf_for_popup(C, *shelf_type);
|
||||
if (!shelf) {
|
||||
BLI_assert_unreachable();
|
||||
return block;
|
||||
const std::optional<StringRefNull> idname = CTX_data_string_get(C, "asset_shelf_idname");
|
||||
if (!idname) {
|
||||
return nullptr;
|
||||
}
|
||||
return type_find_from_idname(*idname);
|
||||
}
|
||||
|
||||
const uiStyle *style = UI_style_get_dpi();
|
||||
static void popover_panel_draw(const bContext *C, Panel *panel)
|
||||
{
|
||||
AssetShelfType *shelf_type = lookup_type_from_idname_in_context(C);
|
||||
BLI_assert_msg(shelf_type != nullptr, "couldn't find asset shelf type from context");
|
||||
|
||||
const int layout_width = UI_UNIT_X * 40;
|
||||
const int left_col_width = 10 * UI_UNIT_X;
|
||||
const int right_col_width = layout_width - left_col_width;
|
||||
uiLayout *layout = UI_block_layout(
|
||||
block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, 0, 0, layout_width, 0, 0, style);
|
||||
const ARegion *region = CTX_wm_region_popup(C) ? CTX_wm_region_popup(C) : CTX_wm_region(C);
|
||||
|
||||
const int left_col_width_units = 10;
|
||||
const int right_col_width_units = 30;
|
||||
const int layout_width_units = left_col_width_units + right_col_width_units;
|
||||
|
||||
uiLayout *layout = panel->layout;
|
||||
uiLayoutSetUnitsX(layout, layout_width_units);
|
||||
|
||||
AssetShelf *shelf = get_shelf_for_popup(*C, *shelf_type);
|
||||
if (!shelf) {
|
||||
BLI_assert_unreachable();
|
||||
return;
|
||||
}
|
||||
|
||||
bScreen *screen = CTX_wm_screen(C);
|
||||
PointerRNA library_ref_ptr = RNA_pointer_create(
|
||||
&screen->id, &RNA_AssetLibraryReference, &shelf->settings.asset_library_reference);
|
||||
uiLayoutSetContextPointer(layout, "asset_library_reference", &library_ref_ptr);
|
||||
|
||||
uiLayout *row = uiLayoutRow(layout, false);
|
||||
uiLayout *catalogs_col = uiLayoutColumn(row, false);
|
||||
uiLayoutSetUnitsX(catalogs_col, left_col_width / UI_UNIT_X);
|
||||
uiLayoutSetUnitsX(catalogs_col, left_col_width_units);
|
||||
uiLayoutSetFixedSize(catalogs_col, true);
|
||||
library_selector_draw(C, catalogs_col, *shelf);
|
||||
catalog_tree_draw(*catalogs_col, *shelf);
|
||||
|
@ -198,11 +209,40 @@ uiBlock *popup_block_create(const bContext *C, ARegion *region, AssetShelfType *
|
|||
uiItemR(sub, &shelf_ptr, "search_filter", UI_ITEM_R_IMMEDIATE, "", ICON_VIEWZOOM);
|
||||
|
||||
uiLayout *asset_view_col = uiLayoutColumn(right_col, false);
|
||||
uiLayoutSetUnitsX(asset_view_col, right_col_width / UI_UNIT_X);
|
||||
uiLayoutSetUnitsX(asset_view_col, right_col_width_units);
|
||||
uiLayoutSetFixedSize(asset_view_col, true);
|
||||
build_asset_view(*asset_view_col, shelf->settings.asset_library_reference, *shelf, *C, *region);
|
||||
|
||||
return block;
|
||||
build_asset_view(*asset_view_col, shelf->settings.asset_library_reference, *shelf, *C, *region);
|
||||
}
|
||||
|
||||
static bool popover_panel_poll(const bContext *C, PanelType * /*panel_type*/)
|
||||
{
|
||||
const AssetShelfType *shelf_type = lookup_type_from_idname_in_context(C);
|
||||
if (!shelf_type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return type_poll_for_popup(*C, shelf_type);
|
||||
}
|
||||
|
||||
void popover_panel_register(ARegionType *region_type)
|
||||
{
|
||||
/* Uses global paneltype registry to allow usage as popover. So only register this once (may be
|
||||
* called from multiple spaces). */
|
||||
if (WM_paneltype_find("ASSETSHELF_PT_popover_panel", true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
PanelType *pt = MEM_cnew<PanelType>(__func__);
|
||||
STRNCPY(pt->idname, "ASSETSHELF_PT_popover_panel");
|
||||
STRNCPY(pt->label, N_("Asset Shelf Panel"));
|
||||
STRNCPY(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
|
||||
pt->description = N_("Display an asset shelf in a popover panel");
|
||||
pt->draw = popover_panel_draw;
|
||||
pt->poll = popover_panel_poll;
|
||||
pt->listener = asset::list::asset_reading_region_listen_fn;
|
||||
BLI_addtail(®ion_type->paneltypes, pt);
|
||||
WM_paneltype_add(pt);
|
||||
}
|
||||
|
||||
} // namespace blender::ed::asset::shelf
|
|
@ -20,7 +20,7 @@ bool id_type_is_non_experimental(const ID *id)
|
|||
{
|
||||
/* Remember to update #ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING and
|
||||
* #ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_FLAGS() with this! */
|
||||
return ELEM(GS(id->name), ID_MA, ID_GR, ID_OB, ID_AC, ID_WO, ID_NT);
|
||||
return ELEM(GS(id->name), ID_BR, ID_MA, ID_GR, ID_OB, ID_AC, ID_WO, ID_NT);
|
||||
}
|
||||
|
||||
bool id_type_is_supported(const ID *id)
|
||||
|
|
|
@ -1952,283 +1952,6 @@ void GPENCIL_OT_material_lock_unused(wmOperatorType *ot)
|
|||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/* ************************************************ */
|
||||
/* Drawing Brushes Operators */
|
||||
|
||||
/* ******************* Brush resets ************************** */
|
||||
static int gpencil_brush_reset_exec(bContext *C, wmOperator * /*op*/)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
ToolSettings *ts = CTX_data_tool_settings(C);
|
||||
const enum eContextObjectMode mode = CTX_data_mode_enum(C);
|
||||
|
||||
switch (mode) {
|
||||
case CTX_MODE_PAINT_GPENCIL_LEGACY: {
|
||||
Paint *paint = &ts->gp_paint->paint;
|
||||
Brush *brush = BKE_paint_brush(paint);
|
||||
if (brush && brush->gpencil_settings) {
|
||||
BKE_gpencil_brush_preset_set(bmain, brush, brush->gpencil_settings->preset_type);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CTX_MODE_SCULPT_GPENCIL_LEGACY: {
|
||||
Paint *paint = &ts->gp_sculptpaint->paint;
|
||||
Brush *brush = BKE_paint_brush(paint);
|
||||
if (brush && brush->gpencil_settings) {
|
||||
BKE_gpencil_brush_preset_set(bmain, brush, brush->gpencil_settings->preset_type);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CTX_MODE_WEIGHT_GPENCIL_LEGACY: {
|
||||
Paint *paint = &ts->gp_weightpaint->paint;
|
||||
Brush *brush = BKE_paint_brush(paint);
|
||||
if (brush && brush->gpencil_settings) {
|
||||
BKE_gpencil_brush_preset_set(bmain, brush, brush->gpencil_settings->preset_type);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CTX_MODE_VERTEX_GPENCIL_LEGACY: {
|
||||
Paint *paint = &ts->gp_vertexpaint->paint;
|
||||
Brush *brush = BKE_paint_brush(paint);
|
||||
if (brush && brush->gpencil_settings) {
|
||||
BKE_gpencil_brush_preset_set(bmain, brush, brush->gpencil_settings->preset_type);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* notifiers */
|
||||
WM_main_add_notifier(NC_BRUSH | NA_EDITED, nullptr);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void GPENCIL_OT_brush_reset(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Reset Brush";
|
||||
ot->idname = "GPENCIL_OT_brush_reset";
|
||||
ot->description = "Reset brush to default parameters";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = gpencil_brush_reset_exec;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
static Brush *gpencil_brush_get_first_by_mode(Main *bmain,
|
||||
Paint * /*paint*/,
|
||||
const enum eContextObjectMode mode,
|
||||
char tool)
|
||||
{
|
||||
Brush *brush_next = nullptr;
|
||||
for (Brush *brush = static_cast<Brush *>(bmain->brushes.first); brush; brush = brush_next) {
|
||||
brush_next = static_cast<Brush *>(brush->id.next);
|
||||
|
||||
if (brush->gpencil_settings == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((mode == CTX_MODE_PAINT_GPENCIL_LEGACY) && (brush->gpencil_tool == tool)) {
|
||||
return brush;
|
||||
}
|
||||
|
||||
if ((mode == CTX_MODE_SCULPT_GPENCIL_LEGACY) && (brush->gpencil_sculpt_tool == tool)) {
|
||||
return brush;
|
||||
}
|
||||
|
||||
if ((mode == CTX_MODE_WEIGHT_GPENCIL_LEGACY) && (brush->gpencil_weight_tool == tool)) {
|
||||
return brush;
|
||||
}
|
||||
|
||||
if ((mode == CTX_MODE_VERTEX_GPENCIL_LEGACY) && (brush->gpencil_vertex_tool == tool)) {
|
||||
return brush;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static void gpencil_brush_delete_mode_brushes(Main *bmain,
|
||||
Paint *paint,
|
||||
const enum eContextObjectMode mode)
|
||||
{
|
||||
Brush *brush_active = BKE_paint_brush(paint);
|
||||
Brush *brush_next = nullptr;
|
||||
for (Brush *brush = static_cast<Brush *>(bmain->brushes.first); brush; brush = brush_next) {
|
||||
brush_next = static_cast<Brush *>(brush->id.next);
|
||||
|
||||
if ((brush->gpencil_settings == nullptr) && (brush->ob_mode != OB_MODE_PAINT_GPENCIL_LEGACY)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
short preset = (brush->gpencil_settings) ? brush->gpencil_settings->preset_type :
|
||||
short(GP_BRUSH_PRESET_UNKNOWN);
|
||||
|
||||
if (preset != GP_BRUSH_PRESET_UNKNOWN) {
|
||||
/* Verify to delete only the brushes of the current mode. */
|
||||
if (mode == CTX_MODE_PAINT_GPENCIL_LEGACY) {
|
||||
if ((preset < GP_BRUSH_PRESET_AIRBRUSH) || (preset > GP_BRUSH_PRESET_TINT)) {
|
||||
continue;
|
||||
}
|
||||
if ((brush_active) && (brush_active->gpencil_tool != brush->gpencil_tool)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == CTX_MODE_SCULPT_GPENCIL_LEGACY) {
|
||||
if ((preset < GP_BRUSH_PRESET_SMOOTH_STROKE) || (preset > GP_BRUSH_PRESET_CLONE_STROKE)) {
|
||||
continue;
|
||||
}
|
||||
if ((brush_active) && (brush_active->gpencil_sculpt_tool != brush->gpencil_sculpt_tool)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == CTX_MODE_WEIGHT_GPENCIL_LEGACY) {
|
||||
if ((preset < GP_BRUSH_PRESET_WEIGHT_DRAW) || (preset > GP_BRUSH_PRESET_WEIGHT_SMEAR)) {
|
||||
continue;
|
||||
}
|
||||
if ((brush_active) && (brush_active->gpencil_weight_tool != brush->gpencil_weight_tool)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == CTX_MODE_VERTEX_GPENCIL_LEGACY) {
|
||||
if ((preset < GP_BRUSH_PRESET_VERTEX_DRAW) || (preset > GP_BRUSH_PRESET_VERTEX_REPLACE)) {
|
||||
continue;
|
||||
}
|
||||
if ((brush_active) && (brush_active->gpencil_vertex_tool != brush->gpencil_vertex_tool)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Before delete, un-pin any material of the brush. */
|
||||
if ((brush->gpencil_settings) && (brush->gpencil_settings->material != nullptr)) {
|
||||
brush->gpencil_settings->material = nullptr;
|
||||
brush->gpencil_settings->flag &= ~GP_BRUSH_MATERIAL_PINNED;
|
||||
}
|
||||
|
||||
BKE_brush_delete(bmain, brush);
|
||||
if (brush == brush_active) {
|
||||
brush_active = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int gpencil_brush_reset_all_exec(bContext *C, wmOperator * /*op*/)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
ToolSettings *ts = CTX_data_tool_settings(C);
|
||||
const enum eContextObjectMode mode = CTX_data_mode_enum(C);
|
||||
Paint *paint = nullptr;
|
||||
|
||||
switch (mode) {
|
||||
case CTX_MODE_PAINT_GPENCIL_LEGACY: {
|
||||
paint = &ts->gp_paint->paint;
|
||||
break;
|
||||
}
|
||||
case CTX_MODE_SCULPT_GPENCIL_LEGACY: {
|
||||
paint = &ts->gp_sculptpaint->paint;
|
||||
break;
|
||||
}
|
||||
case CTX_MODE_WEIGHT_GPENCIL_LEGACY: {
|
||||
paint = &ts->gp_weightpaint->paint;
|
||||
break;
|
||||
}
|
||||
case CTX_MODE_VERTEX_GPENCIL_LEGACY: {
|
||||
paint = &ts->gp_vertexpaint->paint;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
char tool = '0';
|
||||
if (paint) {
|
||||
Brush *brush_active = BKE_paint_brush(paint);
|
||||
if (brush_active) {
|
||||
switch (mode) {
|
||||
case CTX_MODE_PAINT_GPENCIL_LEGACY: {
|
||||
tool = brush_active->gpencil_tool;
|
||||
break;
|
||||
}
|
||||
case CTX_MODE_SCULPT_GPENCIL_LEGACY: {
|
||||
tool = brush_active->gpencil_sculpt_tool;
|
||||
break;
|
||||
}
|
||||
case CTX_MODE_WEIGHT_GPENCIL_LEGACY: {
|
||||
tool = brush_active->gpencil_weight_tool;
|
||||
break;
|
||||
}
|
||||
case CTX_MODE_VERTEX_GPENCIL_LEGACY: {
|
||||
tool = brush_active->gpencil_vertex_tool;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
tool = brush_active->gpencil_tool;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gpencil_brush_delete_mode_brushes(bmain, paint, mode);
|
||||
|
||||
switch (mode) {
|
||||
case CTX_MODE_PAINT_GPENCIL_LEGACY: {
|
||||
BKE_brush_gpencil_paint_presets(bmain, ts, true);
|
||||
break;
|
||||
}
|
||||
case CTX_MODE_SCULPT_GPENCIL_LEGACY: {
|
||||
BKE_brush_gpencil_sculpt_presets(bmain, ts, true);
|
||||
break;
|
||||
}
|
||||
case CTX_MODE_WEIGHT_GPENCIL_LEGACY: {
|
||||
BKE_brush_gpencil_weight_presets(bmain, ts, true);
|
||||
break;
|
||||
}
|
||||
case CTX_MODE_VERTEX_GPENCIL_LEGACY: {
|
||||
BKE_brush_gpencil_vertex_presets(bmain, ts, true);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
BKE_paint_brush_validate(bmain, paint);
|
||||
|
||||
/* Set Again the first brush of the mode. */
|
||||
Brush *deft_brush = gpencil_brush_get_first_by_mode(bmain, paint, mode, tool);
|
||||
if (deft_brush) {
|
||||
BKE_paint_brush_set(paint, deft_brush);
|
||||
}
|
||||
/* notifiers */
|
||||
DEG_relations_tag_update(bmain);
|
||||
WM_main_add_notifier(NC_BRUSH | NA_EDITED, nullptr);
|
||||
}
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void GPENCIL_OT_brush_reset_all(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Reset All Brushes";
|
||||
ot->idname = "GPENCIL_OT_brush_reset_all";
|
||||
ot->description = "Delete all mode brushes and recreate a default set";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = gpencil_brush_reset_all_exec;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
/*********************** Vertex Groups ***********************************/
|
||||
|
||||
static bool gpencil_vertex_group_poll(bContext *C)
|
||||
|
|
|
@ -423,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;
|
||||
}
|
||||
|
||||
|
|
|
@ -1430,13 +1430,6 @@ void ED_gpencil_add_defaults(bContext *C, Object *ob)
|
|||
ToolSettings *ts = CTX_data_tool_settings(C);
|
||||
|
||||
BKE_paint_ensure(bmain, ts, (Paint **)&ts->gp_paint);
|
||||
Paint *paint = &ts->gp_paint->paint;
|
||||
Brush *brush = BKE_paint_brush(paint);
|
||||
/* if not exist, create a new one */
|
||||
if ((brush == nullptr) || (brush->gpencil_settings == nullptr)) {
|
||||
/* create new brushes */
|
||||
BKE_brush_gpencil_paint_presets(bmain, ts, true);
|
||||
}
|
||||
|
||||
/* ensure a color exists and is assigned to object */
|
||||
BKE_gpencil_object_material_ensure_from_active_input_toolsettings(bmain, ob, ts);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -73,6 +73,9 @@ void attribute_search_add_items(StringRefNull str,
|
|||
uiSearchItems *items,
|
||||
bool is_first);
|
||||
|
||||
bool asset_shelf_popover_invoke(bContext &C,
|
||||
blender::StringRef asset_shelf_idname,
|
||||
ReportList &reports);
|
||||
/**
|
||||
* Some drop targets simply allow dropping onto/into them, others support dragging in-between them.
|
||||
* Classes implementing the drop-target interface can use this type to control the behavior by
|
||||
|
|
|
@ -1385,6 +1385,8 @@ void UI_but_context_ptr_set(uiBlock *block, uiBut *but, const char *name, const
|
|||
const PointerRNA *UI_but_context_ptr_get(const uiBut *but,
|
||||
const char *name,
|
||||
const StructRNA *type = nullptr);
|
||||
std::optional<blender::StringRefNull> UI_but_context_string_get(const uiBut *but,
|
||||
const char *name);
|
||||
const bContextStore *UI_but_context_get(const uiBut *but);
|
||||
|
||||
void UI_but_unit_type_set(uiBut *but, int unit_type);
|
||||
|
@ -2174,6 +2176,7 @@ uiBlock *uiLayoutGetBlock(uiLayout *layout);
|
|||
|
||||
void uiLayoutSetFunc(uiLayout *layout, uiMenuHandleFunc handlefunc, void *argv);
|
||||
void uiLayoutSetContextPointer(uiLayout *layout, const char *name, PointerRNA *ptr);
|
||||
void uiLayoutSetContextString(uiLayout *layout, const char *name, blender::StringRef value);
|
||||
bContextStore *uiLayoutGetContextStore(uiLayout *layout);
|
||||
void uiLayoutContextCopy(uiLayout *layout, const bContextStore *context);
|
||||
|
||||
|
@ -2206,6 +2209,10 @@ MenuType *UI_but_menutype_get(const uiBut *but);
|
|||
* This is a bit of a hack but best keep it in one place at least.
|
||||
*/
|
||||
PanelType *UI_but_paneltype_get(const uiBut *but);
|
||||
/**
|
||||
* This is a bit of a hack but best keep it in one place at least.
|
||||
*/
|
||||
std::optional<blender::StringRefNull> UI_but_asset_shelf_type_idname_get(const uiBut *but);
|
||||
void UI_menutype_draw(bContext *C, MenuType *mt, uiLayout *layout);
|
||||
/**
|
||||
* Used for popup panels only.
|
||||
|
@ -2725,8 +2732,11 @@ void uiTemplateAssetView(uiLayout *layout,
|
|||
|
||||
namespace blender::ui {
|
||||
|
||||
void template_asset_shelf_popover(
|
||||
uiLayout &layout, const bContext &C, StringRefNull asset_shelf_id, StringRef name, int icon);
|
||||
void template_asset_shelf_popover(uiLayout &layout,
|
||||
const bContext &C,
|
||||
StringRefNull asset_shelf_id,
|
||||
StringRefNull name,
|
||||
int icon);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -5769,6 +5769,14 @@ const PointerRNA *UI_but_context_ptr_get(const uiBut *but, const char *name, con
|
|||
return CTX_store_ptr_lookup(but->context, name, type);
|
||||
}
|
||||
|
||||
std::optional<blender::StringRefNull> UI_but_context_string_get(const uiBut *but, const char *name)
|
||||
{
|
||||
if (!but->context) {
|
||||
return {};
|
||||
}
|
||||
return CTX_store_string_lookup(but->context, name);
|
||||
}
|
||||
|
||||
const bContextStore *UI_but_context_get(const uiBut *but)
|
||||
{
|
||||
return but->context;
|
||||
|
|
|
@ -114,6 +114,13 @@ static const char *shortcut_get_operator_property(bContext *C, uiBut *but, IDPro
|
|||
return "WM_OT_call_menu";
|
||||
}
|
||||
|
||||
if (std::optional asset_shelf_idname = UI_but_asset_shelf_type_idname_get(but)) {
|
||||
IDProperty *prop = blender::bke::idprop::create_group(__func__).release();
|
||||
IDP_AddToGroup(prop, bke::idprop::create("name", *asset_shelf_idname).release());
|
||||
*r_prop = prop;
|
||||
return "WM_OT_call_asset_shelf_popover";
|
||||
}
|
||||
|
||||
if (PanelType *pt = UI_but_paneltype_get(but)) {
|
||||
IDProperty *prop = blender::bke::idprop::create_group(__func__).release();
|
||||
IDP_AddToGroup(prop, bke::idprop::create("name", pt->idname).release());
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -997,10 +997,12 @@ uiPopupBlockHandle *ui_popup_menu_create(
|
|||
|
||||
/* `interface_region_popover.cc` */
|
||||
|
||||
using uiPopoverCreateFunc = std::function<void(bContext *, uiLayout *, PanelType *)>;
|
||||
|
||||
uiPopupBlockHandle *ui_popover_panel_create(bContext *C,
|
||||
ARegion *butregion,
|
||||
uiBut *but,
|
||||
uiMenuCreateFunc menu_func,
|
||||
uiPopoverCreateFunc popover_func,
|
||||
const PanelType *panel_type);
|
||||
|
||||
/* `interface_region_menu_pie.cc` */
|
||||
|
@ -1523,6 +1525,9 @@ void UI_OT_eyedropper_driver(wmOperatorType *ot);
|
|||
|
||||
void UI_OT_eyedropper_gpencil_color(wmOperatorType *ot);
|
||||
|
||||
/* interface_template_asset_shelf_popover.cc */
|
||||
std::optional<blender::StringRefNull> UI_asset_shelf_idname_from_button_context(const uiBut *but);
|
||||
|
||||
/* interface_template_asset_view.cc */
|
||||
|
||||
uiListType *UI_UL_asset_view();
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "BLI_memory_utils.hh"
|
||||
#include "BLI_rect.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
@ -3187,6 +3188,10 @@ void uiItemPopoverPanel_ptr(
|
|||
icon = ICON_BLANK1;
|
||||
}
|
||||
|
||||
const bContextStore *previous_ctx = CTX_store_get(C);
|
||||
/* Set context for polling (and panel header drawing). */
|
||||
CTX_store_set(const_cast<bContext *>(C), layout->context);
|
||||
|
||||
const bool ok = (pt->poll == nullptr) || pt->poll(C, pt);
|
||||
if (ok && (pt->draw_header != nullptr)) {
|
||||
layout = uiLayoutRow(layout, true);
|
||||
|
@ -3198,6 +3203,9 @@ void uiItemPopoverPanel_ptr(
|
|||
panel.flag = PNL_POPOVER;
|
||||
pt->draw_header(C, &panel);
|
||||
}
|
||||
|
||||
CTX_store_set(const_cast<bContext *>(C), previous_ctx);
|
||||
|
||||
uiBut *but = ui_item_menu(
|
||||
layout, name, icon, ui_item_paneltype_func, pt, nullptr, TIP_(pt->description), true);
|
||||
but->type = UI_BTYPE_POPOVER;
|
||||
|
@ -5999,6 +6007,12 @@ void uiLayoutSetContextPointer(uiLayout *layout, const char *name, PointerRNA *p
|
|||
layout->context = CTX_store_add(block->contexts, name, ptr);
|
||||
}
|
||||
|
||||
void uiLayoutSetContextString(uiLayout *layout, const char *name, blender::StringRef value)
|
||||
{
|
||||
uiBlock *block = layout->root->block;
|
||||
layout->context = CTX_store_add(block->contexts, name, value);
|
||||
}
|
||||
|
||||
bContextStore *uiLayoutGetContextStore(uiLayout *layout)
|
||||
{
|
||||
return layout->context;
|
||||
|
@ -6092,6 +6106,11 @@ PanelType *UI_but_paneltype_get(const uiBut *but)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
std::optional<blender::StringRefNull> UI_but_asset_shelf_type_idname_get(const uiBut *but)
|
||||
{
|
||||
return UI_asset_shelf_idname_from_button_context(but);
|
||||
}
|
||||
|
||||
void UI_menutype_draw(bContext *C, MenuType *mt, uiLayout *layout)
|
||||
{
|
||||
Menu menu{};
|
||||
|
|
|
@ -61,8 +61,8 @@ struct uiPopover {
|
|||
wmKeyMap *keymap;
|
||||
wmEventHandler_Keymap *keymap_handler;
|
||||
|
||||
uiMenuCreateFunc menu_func;
|
||||
const PanelType *menu_arg;
|
||||
uiPopoverCreateFunc popover_func;
|
||||
const PanelType *panel_type;
|
||||
|
||||
/* Size in pixels (ui scale applied). */
|
||||
int ui_size_x;
|
||||
|
@ -114,9 +114,9 @@ static uiBlock *ui_block_func_POPOVER(bContext *C, uiPopupBlockHandle *handle, v
|
|||
if (!pup->layout) {
|
||||
ui_popover_create_block(C, handle->region, pup, WM_OP_INVOKE_REGION_WIN);
|
||||
|
||||
if (pup->menu_func) {
|
||||
if (pup->popover_func) {
|
||||
pup->block->handle = handle;
|
||||
pup->menu_func(C, pup->layout, const_cast<PanelType *>(pup->menu_arg));
|
||||
pup->popover_func(C, pup->layout, const_cast<PanelType *>(pup->panel_type));
|
||||
pup->block->handle = nullptr;
|
||||
}
|
||||
|
||||
|
@ -233,20 +233,20 @@ static void ui_block_free_func_POPOVER(void *arg_pup)
|
|||
wmWindow *window = pup->window;
|
||||
WM_event_remove_keymap_handler(&window->modalhandlers, pup->keymap);
|
||||
}
|
||||
MEM_freeN(pup);
|
||||
MEM_delete(pup);
|
||||
}
|
||||
|
||||
uiPopupBlockHandle *ui_popover_panel_create(bContext *C,
|
||||
ARegion *butregion,
|
||||
uiBut *but,
|
||||
uiMenuCreateFunc menu_func,
|
||||
uiPopoverCreateFunc popover_func,
|
||||
const PanelType *panel_type)
|
||||
{
|
||||
wmWindow *window = CTX_wm_window(C);
|
||||
const uiStyle *style = UI_style_get_dpi();
|
||||
|
||||
/* Create popover, buttons are created from callback. */
|
||||
uiPopover *pup = MEM_cnew<uiPopover>(__func__);
|
||||
uiPopover *pup = MEM_new<uiPopover>(__func__);
|
||||
pup->but = but;
|
||||
|
||||
/* FIXME: maybe one day we want non panel popovers? */
|
||||
|
@ -259,8 +259,8 @@ uiPopupBlockHandle *ui_popover_panel_create(bContext *C,
|
|||
(text_points_max / float(UI_DEFAULT_TEXT_POINTS));
|
||||
}
|
||||
|
||||
pup->menu_func = menu_func;
|
||||
pup->menu_arg = panel_type;
|
||||
pup->popover_func = popover_func;
|
||||
pup->panel_type = panel_type;
|
||||
|
||||
#ifdef USE_UI_POPOVER_ONCE
|
||||
{
|
||||
|
@ -335,7 +335,7 @@ int UI_popover_panel_invoke(bContext *C, const char *idname, bool keep_open, Rep
|
|||
|
||||
uiPopover *UI_popover_begin(bContext *C, int ui_menu_width, bool from_active_button)
|
||||
{
|
||||
uiPopover *pup = MEM_cnew<uiPopover>(__func__);
|
||||
uiPopover *pup = MEM_new<uiPopover>(__func__);
|
||||
if (ui_menu_width == 0) {
|
||||
ui_menu_width = U.widget_unit * UI_POPOVER_WIDTH_UNITS;
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -7,28 +7,28 @@
|
|||
*/
|
||||
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_report.hh"
|
||||
#include "BKE_screen.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_define.hh"
|
||||
#include "RNA_prototypes.h"
|
||||
|
||||
#include "UI_interface.hh"
|
||||
#include "UI_interface_c.hh"
|
||||
#include "UI_resources.hh"
|
||||
#include "interface_intern.hh"
|
||||
|
||||
#include "ED_asset_shelf.hh"
|
||||
|
||||
namespace blender::ui {
|
||||
#include "WM_api.hh"
|
||||
|
||||
static uiBlock *asset_shelf_block_fn(bContext *C, ARegion *region, void *arg_shelf_type)
|
||||
{
|
||||
AssetShelfType *shelf_type = reinterpret_cast<AssetShelfType *>(arg_shelf_type);
|
||||
return ed::asset::shelf::popup_block_create(C, region, shelf_type);
|
||||
}
|
||||
namespace blender::ui {
|
||||
|
||||
void template_asset_shelf_popover(uiLayout &layout,
|
||||
const bContext &C,
|
||||
const StringRefNull asset_shelf_id,
|
||||
const StringRef name,
|
||||
const StringRefNull name,
|
||||
const BIFIconID icon)
|
||||
{
|
||||
AssetShelfType *shelf_type = ed::asset::shelf::type_find_from_idname(asset_shelf_id);
|
||||
|
@ -38,30 +38,64 @@ void template_asset_shelf_popover(uiLayout &layout,
|
|||
}
|
||||
|
||||
const ARegion *region = CTX_wm_region(&C);
|
||||
uiBlock *block = uiLayoutGetBlock(&layout);
|
||||
|
||||
uiLayout *row = uiLayoutRow(&layout, true);
|
||||
const bool use_big_size = !RGN_TYPE_IS_HEADER_ANY(region->regiontype);
|
||||
const bool use_preview_icon = use_big_size;
|
||||
const short width = [&]() -> short {
|
||||
if (use_big_size) {
|
||||
return UI_UNIT_X * 6;
|
||||
}
|
||||
return UI_UNIT_X * (name.is_empty() ? 1.6f : 7);
|
||||
}();
|
||||
const short height = UI_UNIT_Y * (use_big_size ? 6 : 1);
|
||||
|
||||
uiBlock *block = uiLayoutGetBlock(&layout);
|
||||
uiBut *but = uiDefBlockBut(
|
||||
block, asset_shelf_block_fn, shelf_type, name, 0, 0, width, height, "Select an asset");
|
||||
uiLayoutSetContextString(row, "asset_shelf_idname", asset_shelf_id);
|
||||
if (use_big_size) {
|
||||
uiLayoutSetScaleX(row, 6);
|
||||
uiLayoutSetScaleY(row, 6);
|
||||
}
|
||||
else {
|
||||
uiLayoutSetUnitsX(row, name.is_empty() ? 1.6f : 7);
|
||||
}
|
||||
|
||||
uiItemPopoverPanel(row, &C, "ASSETSHELF_PT_popover_panel", name.c_str(), icon);
|
||||
uiBut *but = static_cast<uiBut *>(block->buttons.last);
|
||||
if (use_preview_icon) {
|
||||
ui_def_but_icon(but, icon, UI_HAS_ICON | UI_BUT_ICON_PREVIEW);
|
||||
}
|
||||
else {
|
||||
ui_def_but_icon(but, icon, UI_HAS_ICON);
|
||||
UI_but_drawflag_enable(but, UI_BUT_ICON_LEFT);
|
||||
}
|
||||
|
||||
bool asset_shelf_popover_invoke(bContext &C, StringRef asset_shelf_idname, ReportList &reports)
|
||||
{
|
||||
AssetShelfType *shelf_type = ed::asset::shelf::type_find_from_idname(asset_shelf_idname);
|
||||
if (ed::asset::shelf::type_poll_for_popup(C, shelf_type) == false) {
|
||||
UI_but_flag_enable(but, UI_BUT_DISABLED);
|
||||
return false;
|
||||
}
|
||||
|
||||
PanelType *pt = WM_paneltype_find("ASSETSHELF_PT_popover_panel", true);
|
||||
if (pt == nullptr) {
|
||||
BKE_reportf(&reports, RPT_ERROR, "Asset shelf popover panel type not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Skip panel poll check here. Should usually be done, but requires passing the asset shelf type
|
||||
* name via some context-store, but there's nothing to provide that here. Asset shelf type is
|
||||
* polled above, so it's okay. */
|
||||
|
||||
std::string asset_shelf_id_str = asset_shelf_idname;
|
||||
ui_popover_panel_create(
|
||||
&C,
|
||||
nullptr,
|
||||
nullptr,
|
||||
[asset_shelf_id_str](bContext *C, uiLayout *layout, void *arg_pt) {
|
||||
uiLayoutSetContextString(layout, "asset_shelf_idname", asset_shelf_id_str);
|
||||
ui_item_paneltype_func(C, layout, arg_pt);
|
||||
},
|
||||
pt);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace blender::ui
|
||||
|
||||
using namespace blender;
|
||||
|
||||
std::optional<StringRefNull> UI_asset_shelf_idname_from_button_context(const uiBut *but)
|
||||
{
|
||||
return UI_but_context_string_get(but, "asset_shelf_idname");
|
||||
}
|
||||
|
|
|
@ -6423,13 +6423,55 @@ void uiTemplateInputStatus(uiLayout *layout, bContext *C)
|
|||
}
|
||||
}
|
||||
|
||||
static void ui_template_status_info_warnings_messages(Main *bmain,
|
||||
Scene *scene,
|
||||
ViewLayer *view_layer,
|
||||
std::string &warning_message,
|
||||
std::string ®ular_message)
|
||||
{
|
||||
char statusbar_info_flag = U.statusbar_flag;
|
||||
|
||||
if (bmain->has_forward_compatibility_issues) {
|
||||
warning_message = ED_info_statusbar_string_ex(
|
||||
bmain, scene, view_layer, STATUSBAR_SHOW_VERSION);
|
||||
statusbar_info_flag &= ~STATUSBAR_SHOW_VERSION;
|
||||
}
|
||||
|
||||
regular_message = ED_info_statusbar_string_ex(bmain, scene, view_layer, statusbar_info_flag);
|
||||
}
|
||||
|
||||
static std::string ui_template_status_tooltip(bContext *C, void * /*argN*/, const char * /*tip*/)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
std::string tooltip_message = "";
|
||||
|
||||
if (bmain->has_forward_compatibility_issues) {
|
||||
char writer_ver_str[12];
|
||||
BKE_blender_version_blendfile_string_from_values(
|
||||
writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, -1);
|
||||
tooltip_message += fmt::format(RPT_("File saved by newer Blender\n({}), expect loss of data"),
|
||||
writer_ver_str);
|
||||
}
|
||||
if (bmain->is_asset_repository) {
|
||||
if (!tooltip_message.empty()) {
|
||||
tooltip_message += "\n\n";
|
||||
}
|
||||
tooltip_message += RPT_(
|
||||
"This file is managed by the Blender asset system\n"
|
||||
"and is expected to contain a single asset data-block.\n"
|
||||
"Take care to avoid data loss when editing assets.");
|
||||
}
|
||||
|
||||
return tooltip_message;
|
||||
}
|
||||
|
||||
void uiTemplateStatusInfo(uiLayout *layout, bContext *C)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
|
||||
if (!bmain->has_forward_compatibility_issues) {
|
||||
if (!BKE_main_has_issues(bmain)) {
|
||||
const char *status_info_txt = ED_info_statusbar_string(bmain, scene, view_layer);
|
||||
uiItemL(layout, status_info_txt, ICON_NONE);
|
||||
return;
|
||||
|
@ -6438,13 +6480,12 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C)
|
|||
/* Blender version part is shown as warning area when there are forward compatibility issues with
|
||||
* currently loaded .blend file. */
|
||||
|
||||
const char *status_info_txt = ED_info_statusbar_string_ex(
|
||||
bmain, scene, view_layer, (U.statusbar_flag & ~STATUSBAR_SHOW_VERSION));
|
||||
uiItemL(layout, status_info_txt, ICON_NONE);
|
||||
std::string warning_message;
|
||||
std::string regular_message;
|
||||
ui_template_status_info_warnings_messages(
|
||||
bmain, scene, view_layer, warning_message, regular_message);
|
||||
|
||||
status_info_txt = ED_info_statusbar_string_ex(bmain, scene, view_layer, STATUSBAR_SHOW_VERSION);
|
||||
|
||||
uiBut *but;
|
||||
uiItemL(layout, regular_message.c_str(), ICON_NONE);
|
||||
|
||||
const uiStyle *style = UI_style_get();
|
||||
uiLayout *ui_abs = uiLayoutAbsolute(layout, false);
|
||||
|
@ -6452,14 +6493,15 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C)
|
|||
eUIEmbossType previous_emboss = UI_block_emboss_get(block);
|
||||
|
||||
UI_fontstyle_set(&style->widgetlabel);
|
||||
int width = int(
|
||||
BLF_width(style->widgetlabel.uifont_id, status_info_txt, strlen(status_info_txt)));
|
||||
width = max_ii(width, int(10 * UI_SCALE_FAC));
|
||||
const int width = max_ii(int(BLF_width(style->widgetlabel.uifont_id,
|
||||
warning_message.c_str(),
|
||||
warning_message.length())),
|
||||
int(10 * UI_SCALE_FAC));
|
||||
|
||||
UI_block_align_begin(block);
|
||||
|
||||
/* Background for icon. */
|
||||
but = uiDefBut(block,
|
||||
uiBut *but = uiDefBut(block,
|
||||
UI_BTYPE_ROUNDBOX,
|
||||
0,
|
||||
"",
|
||||
|
@ -6495,14 +6537,7 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C)
|
|||
UI_block_align_end(block);
|
||||
UI_block_emboss_set(block, UI_EMBOSS_NONE);
|
||||
|
||||
/* The report icon itself. */
|
||||
static char compat_error_msg[256];
|
||||
char writer_ver_str[12];
|
||||
BKE_blender_version_blendfile_string_from_values(
|
||||
writer_ver_str, sizeof(writer_ver_str), bmain->versionfile, -1);
|
||||
SNPRINTF(compat_error_msg,
|
||||
RPT_("File saved by newer Blender\n(%s), expect loss of data"),
|
||||
writer_ver_str);
|
||||
/* The warning icon itself. */
|
||||
but = uiDefIconBut(block,
|
||||
UI_BTYPE_BUT,
|
||||
0,
|
||||
|
@ -6514,15 +6549,17 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C)
|
|||
nullptr,
|
||||
0.0f,
|
||||
0.0f,
|
||||
compat_error_msg);
|
||||
nullptr);
|
||||
UI_but_func_tooltip_set(but, ui_template_status_tooltip, nullptr, nullptr);
|
||||
UI_GetThemeColorType4ubv(TH_INFO_WARNING_TEXT, SPACE_INFO, but->col);
|
||||
but->col[3] = 255; /* This theme color is RBG only, so have to set alpha here. */
|
||||
|
||||
/* The report message. */
|
||||
/* The warning message, if any. */
|
||||
if (!warning_message.empty()) {
|
||||
but = uiDefBut(block,
|
||||
UI_BTYPE_BUT,
|
||||
0,
|
||||
status_info_txt,
|
||||
warning_message.c_str(),
|
||||
UI_UNIT_X,
|
||||
0,
|
||||
short(width + UI_UNIT_X),
|
||||
|
@ -6530,7 +6567,9 @@ void uiTemplateStatusInfo(uiLayout *layout, bContext *C)
|
|||
nullptr,
|
||||
0.0f,
|
||||
0.0f,
|
||||
compat_error_msg);
|
||||
nullptr);
|
||||
UI_but_func_tooltip_set(but, ui_template_status_tooltip, nullptr, nullptr);
|
||||
}
|
||||
|
||||
UI_block_emboss_set(block, previous_emboss);
|
||||
}
|
||||
|
|
|
@ -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,795 @@
|
|||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BLI_fileops.h"
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "DNA_brush_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_space_types.h"
|
||||
|
||||
#include "BKE_asset.hh"
|
||||
#include "BKE_asset_edit.hh"
|
||||
#include "BKE_blendfile.hh"
|
||||
#include "BKE_brush.hh"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_paint.hh"
|
||||
#include "BKE_preferences.h"
|
||||
#include "BKE_preview_image.hh"
|
||||
#include "BKE_report.hh"
|
||||
|
||||
#include "AS_asset_catalog_path.hh"
|
||||
#include "AS_asset_catalog_tree.hh"
|
||||
#include "AS_asset_library.hh"
|
||||
#include "AS_asset_representation.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_define.hh"
|
||||
|
||||
#include "ED_asset_handle.hh"
|
||||
#include "ED_asset_library.hh"
|
||||
#include "ED_asset_list.hh"
|
||||
#include "ED_asset_mark_clear.hh"
|
||||
#include "ED_asset_menu_utils.hh"
|
||||
#include "ED_asset_shelf.hh"
|
||||
|
||||
#include "UI_interface_icons.hh"
|
||||
#include "UI_resources.hh"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "WM_api.hh"
|
||||
#include "WM_toolsystem.hh"
|
||||
|
||||
#include "paint_intern.hh"
|
||||
|
||||
namespace blender::ed::sculpt_paint {
|
||||
|
||||
static int brush_asset_select_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
/* This operator currently covers both cases: the file/asset browser file list and the asset list
|
||||
* used for the asset-view template. Once the asset list design is used by the Asset Browser,
|
||||
* this can be simplified to just that case. */
|
||||
Main *bmain = CTX_data_main(C);
|
||||
const asset_system::AssetRepresentation *asset =
|
||||
asset::operator_asset_reference_props_get_asset_from_all_library(*C, *op->ptr, op->reports);
|
||||
if (!asset) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
AssetWeakReference brush_asset_reference = asset->make_weak_reference();
|
||||
Brush *brush = reinterpret_cast<Brush *>(
|
||||
bke::asset_edit_id_from_weak_reference(*bmain, ID_BR, brush_asset_reference));
|
||||
|
||||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
|
||||
if (!BKE_paint_brush_set(paint, brush)) {
|
||||
/* Note brush datablock was still added, so was not a no-op. */
|
||||
BKE_report(op->reports, RPT_WARNING, "Unable to select brush, wrong object mode");
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
WM_main_add_notifier(NC_ASSET | NA_ACTIVATED, nullptr);
|
||||
WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, nullptr);
|
||||
WM_toolsystem_ref_set_by_id(C, "builtin.brush");
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void BRUSH_OT_asset_select(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Select Brush Asset";
|
||||
ot->description = "Select a brush asset as current sculpt and paint tool";
|
||||
ot->idname = "BRUSH_OT_asset_select";
|
||||
|
||||
ot->exec = brush_asset_select_exec;
|
||||
|
||||
asset::operator_asset_reference_props_register(*ot->srna);
|
||||
}
|
||||
|
||||
static std::optional<AssetLibraryReference> library_to_library_ref(
|
||||
const asset_system::AssetLibrary &library)
|
||||
{
|
||||
for (const AssetLibraryReference &ref : asset_system::all_valid_asset_library_refs()) {
|
||||
const std::string root_path = AS_asset_library_root_path_from_library_ref(ref);
|
||||
/* Use #BLI_path_cmp_normalized because `library.root_path()` ends with a slash while
|
||||
* `root_path` doesn't. */
|
||||
if (BLI_path_cmp_normalized(root_path.c_str(), library.root_path().c_str()) == 0) {
|
||||
return ref;
|
||||
}
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
static AssetLibraryReference user_library_to_library_ref(const bUserAssetLibrary &user_library)
|
||||
{
|
||||
AssetLibraryReference library_ref{};
|
||||
library_ref.custom_library_index = BLI_findindex(&U.asset_libraries, &user_library);
|
||||
library_ref.type = ASSET_LIBRARY_CUSTOM;
|
||||
return library_ref;
|
||||
}
|
||||
|
||||
static const bUserAssetLibrary *library_ref_to_user_library(
|
||||
const AssetLibraryReference &library_ref)
|
||||
{
|
||||
if (library_ref.type != ASSET_LIBRARY_CUSTOM) {
|
||||
return nullptr;
|
||||
}
|
||||
return static_cast<const bUserAssetLibrary *>(
|
||||
BLI_findlink(&U.asset_libraries, library_ref.custom_library_index));
|
||||
}
|
||||
|
||||
static void refresh_asset_library(const bContext *C, const AssetLibraryReference &library_ref)
|
||||
{
|
||||
asset::list::clear(&library_ref, C);
|
||||
/* TODO: Should the all library reference be automatically cleared? */
|
||||
AssetLibraryReference all_lib_ref = asset_system::all_library_reference();
|
||||
asset::list::clear(&all_lib_ref, C);
|
||||
}
|
||||
|
||||
static void refresh_asset_library(const bContext *C, const bUserAssetLibrary &user_library)
|
||||
{
|
||||
refresh_asset_library(C, user_library_to_library_ref(user_library));
|
||||
}
|
||||
|
||||
static bool brush_asset_save_as_poll(bContext *C)
|
||||
{
|
||||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr;
|
||||
if (paint == nullptr || brush == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (!paint->brush_asset_reference) {
|
||||
/* The brush should always be an imported asset. We use this asset reference to find
|
||||
* which library and catalog the brush came from, as defaults for the popup. */
|
||||
return false;
|
||||
}
|
||||
|
||||
if (BLI_listbase_is_empty(&U.asset_libraries)) {
|
||||
CTX_wm_operator_poll_msg_set(C, "No asset library available to save to");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const bUserAssetLibrary *get_asset_library_from_prop(PointerRNA &ptr)
|
||||
{
|
||||
const int enum_value = RNA_enum_get(&ptr, "asset_library_reference");
|
||||
const AssetLibraryReference lib_ref = asset::library_reference_from_enum_value(enum_value);
|
||||
return BKE_preferences_asset_library_find_index(&U, lib_ref.custom_library_index);
|
||||
}
|
||||
|
||||
static asset_system::AssetCatalog &asset_library_ensure_catalog(
|
||||
asset_system::AssetLibrary &library, const asset_system::AssetCatalogPath &path)
|
||||
{
|
||||
if (asset_system::AssetCatalog *catalog = library.catalog_service().find_catalog_by_path(path)) {
|
||||
return *catalog;
|
||||
}
|
||||
return *library.catalog_service().create_catalog(path);
|
||||
}
|
||||
|
||||
static asset_system::AssetCatalog &asset_library_ensure_catalogs_in_path(
|
||||
asset_system::AssetLibrary &library, const asset_system::AssetCatalogPath &path)
|
||||
{
|
||||
/* Adding multiple catalogs in a path at a time with #AssetCatalogService::create_catalog()
|
||||
* doesn't work; add each potentially new catalog in the hierarchy manually here. */
|
||||
asset_system::AssetCatalogPath parent = "";
|
||||
path.iterate_components([&](StringRef component_name, bool /*is_last_component*/) {
|
||||
asset_library_ensure_catalog(library, parent / component_name);
|
||||
parent = parent / component_name;
|
||||
});
|
||||
return *library.catalog_service().find_catalog_by_path(path);
|
||||
}
|
||||
|
||||
static void show_catalog_in_asset_shelf(const bContext &C, const StringRefNull catalog_path)
|
||||
{
|
||||
/* Enable catalog in all visible asset shelves. */
|
||||
wmWindowManager *wm = CTX_wm_manager(&C);
|
||||
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
|
||||
const bScreen *screen = WM_window_get_active_screen(win);
|
||||
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
|
||||
const AssetShelf *shelf = asset::shelf::active_shelf_from_area(area);
|
||||
if (shelf && BKE_preferences_asset_shelf_settings_ensure_catalog_path_enabled(
|
||||
&U, shelf->idname, catalog_path.c_str()))
|
||||
{
|
||||
U.runtime.is_dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int brush_asset_save_as_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr;
|
||||
|
||||
/* Determine file path to save to. */
|
||||
PropertyRNA *name_prop = RNA_struct_find_property(op->ptr, "name");
|
||||
char name[MAX_NAME] = "";
|
||||
if (RNA_property_is_set(op->ptr, name_prop)) {
|
||||
RNA_property_string_get(op->ptr, name_prop, name);
|
||||
}
|
||||
if (name[0] == '\0') {
|
||||
STRNCPY(name, brush->id.name + 2);
|
||||
}
|
||||
|
||||
const bUserAssetLibrary *user_library = get_asset_library_from_prop(*op->ptr);
|
||||
if (!user_library) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
asset_system::AssetLibrary *library = AS_asset_library_load(
|
||||
bmain, user_library_to_library_ref(*user_library));
|
||||
if (!library) {
|
||||
BKE_report(op->reports, RPT_ERROR, "Failed to load asset library");
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* Turn brush into asset if it isn't yet. */
|
||||
if (!ID_IS_ASSET(&brush->id)) {
|
||||
asset::mark_id(&brush->id);
|
||||
asset::generate_preview(C, &brush->id);
|
||||
}
|
||||
BLI_assert(ID_IS_ASSET(&brush->id));
|
||||
|
||||
/* Add asset to catalog. */
|
||||
char catalog_path[MAX_NAME];
|
||||
RNA_string_get(op->ptr, "catalog_path", catalog_path);
|
||||
|
||||
AssetMetaData &meta_data = *brush->id.asset_data;
|
||||
if (catalog_path[0]) {
|
||||
const asset_system::AssetCatalog &catalog = asset_library_ensure_catalogs_in_path(
|
||||
*library, catalog_path);
|
||||
BKE_asset_metadata_catalog_id_set(&meta_data, catalog.catalog_id, catalog.simple_name.c_str());
|
||||
}
|
||||
|
||||
AssetWeakReference brush_asset_reference;
|
||||
const std::optional<std::string> final_full_asset_filepath = bke::asset_edit_id_save_as(
|
||||
*bmain, brush->id, name, *user_library, brush_asset_reference, *op->reports);
|
||||
if (!final_full_asset_filepath) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
library->catalog_service().write_to_disk(*final_full_asset_filepath);
|
||||
show_catalog_in_asset_shelf(*C, catalog_path);
|
||||
|
||||
brush = reinterpret_cast<Brush *>(
|
||||
bke::asset_edit_id_from_weak_reference(*bmain, ID_BR, brush_asset_reference));
|
||||
|
||||
if (!BKE_paint_brush_set(paint, brush)) {
|
||||
/* Note brush sset was still saved in editable asset library, so was not a no-op. */
|
||||
BKE_report(op->reports, RPT_WARNING, "Unable to activate just-saved brush asset");
|
||||
}
|
||||
|
||||
refresh_asset_library(C, *user_library);
|
||||
WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_ADDED, nullptr);
|
||||
WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static bool library_is_editable(const AssetLibraryReference &library)
|
||||
{
|
||||
if (library.type == ASSET_LIBRARY_ESSENTIALS) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int brush_asset_save_as_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
|
||||
{
|
||||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
const AssetWeakReference &brush_weak_ref = *paint->brush_asset_reference;
|
||||
const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref(
|
||||
*C, brush_weak_ref, op->reports);
|
||||
if (!asset) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
const asset_system::AssetLibrary &library = asset->owner_asset_library();
|
||||
const std::optional<AssetLibraryReference> library_ref = library_to_library_ref(library);
|
||||
if (!library_ref) {
|
||||
BLI_assert_unreachable();
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
RNA_string_set(op->ptr, "name", asset->get_name().c_str());
|
||||
|
||||
/* If the library isn't saved from the operator's last execution, find the current library or the
|
||||
* first library if the current library isn't editable. */
|
||||
if (!RNA_struct_property_is_set_ex(op->ptr, "asset_library_reference", false)) {
|
||||
if (library_is_editable(*library_ref)) {
|
||||
RNA_enum_set(op->ptr,
|
||||
"asset_library_reference",
|
||||
asset::library_reference_to_enum_value(&*library_ref));
|
||||
}
|
||||
else {
|
||||
const AssetLibraryReference first_library = user_library_to_library_ref(
|
||||
*static_cast<const bUserAssetLibrary *>(U.asset_libraries.first));
|
||||
RNA_enum_set(op->ptr,
|
||||
"asset_library_reference",
|
||||
asset::library_reference_to_enum_value(&first_library));
|
||||
}
|
||||
}
|
||||
|
||||
/* By default, put the new asset in the same catalog as the existing asset. */
|
||||
if (!RNA_struct_property_is_set(op->ptr, "catalog_path")) {
|
||||
const asset_system::CatalogID &id = asset->get_metadata().catalog_id;
|
||||
if (const asset_system::AssetCatalog *catalog = library.catalog_service().find_catalog(id)) {
|
||||
RNA_string_set(op->ptr, "catalog_path", catalog->path.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return WM_operator_props_dialog_popup(C, op, 400, std::nullopt, IFACE_("Save"));
|
||||
}
|
||||
|
||||
static const EnumPropertyItem *rna_asset_library_reference_itemf(bContext * /*C*/,
|
||||
PointerRNA * /*ptr*/,
|
||||
PropertyRNA * /*prop*/,
|
||||
bool *r_free)
|
||||
{
|
||||
const EnumPropertyItem *items = asset::library_reference_to_rna_enum_itemf(false);
|
||||
if (!items) {
|
||||
*r_free = false;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
*r_free = true;
|
||||
return items;
|
||||
}
|
||||
|
||||
static void visit_library_catalogs_catalog_for_search(
|
||||
const Main &bmain,
|
||||
const bUserAssetLibrary &user_library,
|
||||
const StringRef edit_text,
|
||||
const FunctionRef<void(StringPropertySearchVisitParams)> visit_fn)
|
||||
{
|
||||
const asset_system::AssetLibrary *library = AS_asset_library_load(
|
||||
&bmain, user_library_to_library_ref(user_library));
|
||||
if (!library) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!edit_text.is_empty()) {
|
||||
const asset_system::AssetCatalogPath edit_path = edit_text;
|
||||
if (!library->catalog_service().find_catalog_by_path(edit_path)) {
|
||||
visit_fn(StringPropertySearchVisitParams{edit_path.str(), std::nullopt, ICON_ADD});
|
||||
}
|
||||
}
|
||||
|
||||
const asset_system::AssetCatalogTree &full_tree = library->catalog_service().catalog_tree();
|
||||
full_tree.foreach_item([&](const asset_system::AssetCatalogTreeItem &item) {
|
||||
visit_fn(StringPropertySearchVisitParams{item.catalog_path().str(), std::nullopt});
|
||||
});
|
||||
}
|
||||
|
||||
static void visit_library_prop_catalogs_catalog_for_search_fn(
|
||||
const bContext *C,
|
||||
PointerRNA *ptr,
|
||||
PropertyRNA * /*prop*/,
|
||||
const char *edit_text,
|
||||
FunctionRef<void(StringPropertySearchVisitParams)> visit_fn)
|
||||
{
|
||||
/* NOTE: Using the all library would also be a valid choice. */
|
||||
if (const bUserAssetLibrary *user_library = get_asset_library_from_prop(*ptr)) {
|
||||
visit_library_catalogs_catalog_for_search(
|
||||
*CTX_data_main(C), *user_library, edit_text, visit_fn);
|
||||
}
|
||||
}
|
||||
|
||||
void BRUSH_OT_asset_save_as(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Save as Brush Asset";
|
||||
ot->description =
|
||||
"Save a copy of the active brush asset into the default asset library, and make it the "
|
||||
"active brush";
|
||||
ot->idname = "BRUSH_OT_asset_save_as";
|
||||
|
||||
ot->exec = brush_asset_save_as_exec;
|
||||
ot->invoke = brush_asset_save_as_invoke;
|
||||
ot->poll = brush_asset_save_as_poll;
|
||||
|
||||
ot->prop = RNA_def_string(
|
||||
ot->srna, "name", nullptr, MAX_NAME, "Name", "Name for the new brush asset");
|
||||
|
||||
PropertyRNA *prop = RNA_def_property(ot->srna, "asset_library_reference", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_enum_funcs(prop, rna_asset_library_reference_itemf);
|
||||
RNA_def_property_ui_text(prop, "Library", "Asset library used to store the new brush");
|
||||
|
||||
prop = RNA_def_string(
|
||||
ot->srna, "catalog_path", nullptr, MAX_NAME, "Catalog", "Catalog to use for the new asset");
|
||||
RNA_def_property_string_search_func_runtime(
|
||||
prop, visit_library_prop_catalogs_catalog_for_search_fn, PROP_STRING_SEARCH_SUGGESTION);
|
||||
}
|
||||
|
||||
static int brush_asset_edit_metadata_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
Brush *brush = BKE_paint_brush(paint);
|
||||
BLI_assert(ID_IS_ASSET(&brush->id));
|
||||
const AssetWeakReference &brush_weak_ref = *paint->brush_asset_reference;
|
||||
const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref(
|
||||
*C, brush_weak_ref, op->reports);
|
||||
if (!asset) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
const asset_system::AssetLibrary &library_const = asset->owner_asset_library();
|
||||
const AssetLibraryReference library_ref = *library_to_library_ref(library_const);
|
||||
asset_system::AssetLibrary *library = AS_asset_library_load(bmain, library_ref);
|
||||
|
||||
char catalog_path[MAX_NAME];
|
||||
RNA_string_get(op->ptr, "catalog_path", catalog_path);
|
||||
|
||||
AssetMetaData &meta_data = *brush->id.asset_data;
|
||||
MEM_SAFE_FREE(meta_data.author);
|
||||
meta_data.author = RNA_string_get_alloc(op->ptr, "author", nullptr, 0, nullptr);
|
||||
MEM_SAFE_FREE(meta_data.description);
|
||||
meta_data.description = RNA_string_get_alloc(op->ptr, "description", nullptr, 0, nullptr);
|
||||
|
||||
if (catalog_path[0]) {
|
||||
const asset_system::AssetCatalog &catalog = asset_library_ensure_catalogs_in_path(
|
||||
*library, catalog_path);
|
||||
BKE_asset_metadata_catalog_id_set(&meta_data, catalog.catalog_id, catalog.simple_name.c_str());
|
||||
}
|
||||
|
||||
if (!bke::asset_edit_id_save(*bmain, brush->id, *op->reports)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
char asset_full_path_buffer[FILE_MAX_LIBEXTRA];
|
||||
char *file_path = nullptr;
|
||||
AS_asset_full_path_explode_from_weak_ref(
|
||||
&brush_weak_ref, asset_full_path_buffer, &file_path, nullptr, nullptr);
|
||||
if (!file_path) {
|
||||
BLI_assert_unreachable();
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
library->catalog_service().write_to_disk(file_path);
|
||||
|
||||
refresh_asset_library(C, library_ref);
|
||||
WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_EDITED, nullptr);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static int brush_asset_edit_metadata_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
|
||||
{
|
||||
const Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
const AssetWeakReference &brush_weak_ref = *paint->brush_asset_reference;
|
||||
const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref(
|
||||
*C, brush_weak_ref, op->reports);
|
||||
if (!asset) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
const asset_system::AssetLibrary &library = asset->owner_asset_library();
|
||||
const AssetMetaData &meta_data = asset->get_metadata();
|
||||
|
||||
if (!RNA_struct_property_is_set(op->ptr, "catalog_path")) {
|
||||
const asset_system::CatalogID &id = meta_data.catalog_id;
|
||||
if (const asset_system::AssetCatalog *catalog = library.catalog_service().find_catalog(id)) {
|
||||
RNA_string_set(op->ptr, "catalog_path", catalog->path.c_str());
|
||||
}
|
||||
}
|
||||
if (!RNA_struct_property_is_set(op->ptr, "author")) {
|
||||
RNA_string_set(op->ptr, "author", meta_data.author ? meta_data.author : "");
|
||||
}
|
||||
if (!RNA_struct_property_is_set(op->ptr, "description")) {
|
||||
RNA_string_set(op->ptr, "description", meta_data.description ? meta_data.description : "");
|
||||
}
|
||||
|
||||
return WM_operator_props_dialog_popup(C, op, 400, std::nullopt, IFACE_("Edit Metadata"));
|
||||
}
|
||||
|
||||
static void visit_active_library_catalogs_catalog_for_search_fn(
|
||||
const bContext *C,
|
||||
PointerRNA * /*ptr*/,
|
||||
PropertyRNA * /*prop*/,
|
||||
const char *edit_text,
|
||||
FunctionRef<void(StringPropertySearchVisitParams)> visit_fn)
|
||||
{
|
||||
const Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
const AssetWeakReference &brush_weak_ref = *paint->brush_asset_reference;
|
||||
const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref(
|
||||
*C, brush_weak_ref, nullptr);
|
||||
if (!asset) {
|
||||
return;
|
||||
}
|
||||
const asset_system::AssetLibrary &library = asset->owner_asset_library();
|
||||
|
||||
/* NOTE: Using the all library would also be a valid choice. */
|
||||
visit_library_catalogs_catalog_for_search(
|
||||
*CTX_data_main(C),
|
||||
*library_ref_to_user_library(*library_to_library_ref(library)),
|
||||
edit_text,
|
||||
visit_fn);
|
||||
}
|
||||
|
||||
static bool brush_asset_edit_metadata_poll(bContext *C)
|
||||
{
|
||||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr;
|
||||
if (paint == nullptr || brush == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (!ID_IS_ASSET(&brush->id)) {
|
||||
BLI_assert_unreachable();
|
||||
return false;
|
||||
}
|
||||
const AssetWeakReference *brush_weak_ref = paint->brush_asset_reference;
|
||||
if (!brush_weak_ref) {
|
||||
BLI_assert_unreachable();
|
||||
return false;
|
||||
}
|
||||
const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref(
|
||||
*C, *brush_weak_ref, nullptr);
|
||||
if (!asset) {
|
||||
BLI_assert_unreachable();
|
||||
return false;
|
||||
}
|
||||
const std::optional<AssetLibraryReference> library_ref = library_to_library_ref(
|
||||
asset->owner_asset_library());
|
||||
if (!library_ref) {
|
||||
BLI_assert_unreachable();
|
||||
return false;
|
||||
}
|
||||
if (!library_is_editable(*library_ref)) {
|
||||
CTX_wm_operator_poll_msg_set(C, "Asset library is not editable");
|
||||
return false;
|
||||
}
|
||||
if (!bke::asset_edit_id_is_writable(brush->id)) {
|
||||
CTX_wm_operator_poll_msg_set(C, "Asset file is not editable");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void BRUSH_OT_asset_edit_metadata(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Edit Metadata";
|
||||
ot->description = "Edit asset information like the catalog, preview image, tags, or author";
|
||||
ot->idname = "BRUSH_OT_asset_edit_metadata";
|
||||
|
||||
ot->exec = brush_asset_edit_metadata_exec;
|
||||
ot->invoke = brush_asset_edit_metadata_invoke;
|
||||
ot->poll = brush_asset_edit_metadata_poll;
|
||||
|
||||
PropertyRNA *prop = RNA_def_string(
|
||||
ot->srna, "catalog_path", nullptr, MAX_NAME, "Catalog", "The asset's catalog path");
|
||||
RNA_def_property_string_search_func_runtime(
|
||||
prop, visit_active_library_catalogs_catalog_for_search_fn, PROP_STRING_SEARCH_SUGGESTION);
|
||||
RNA_def_string(ot->srna, "author", nullptr, MAX_NAME, "Author", "");
|
||||
RNA_def_string(ot->srna, "description", nullptr, MAX_NAME, "Description", "");
|
||||
}
|
||||
|
||||
static int brush_asset_load_preview_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
Brush *brush = BKE_paint_brush(paint);
|
||||
BLI_assert(ID_IS_ASSET(&brush->id));
|
||||
const AssetWeakReference &brush_weak_ref = *paint->brush_asset_reference;
|
||||
const asset_system::AssetRepresentation *asset = asset::find_asset_from_weak_ref(
|
||||
*C, brush_weak_ref, op->reports);
|
||||
if (!asset) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
const AssetLibraryReference library_ref = *library_to_library_ref(asset->owner_asset_library());
|
||||
|
||||
char filepath[FILE_MAX];
|
||||
RNA_string_get(op->ptr, "filepath", filepath);
|
||||
if (!BLI_is_file(filepath)) {
|
||||
BKE_reportf(op->reports, RPT_ERROR, "File not found '%s'", filepath);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
BKE_previewimg_id_custom_set(&brush->id, filepath);
|
||||
|
||||
if (!bke::asset_edit_id_save(*bmain, brush->id, *op->reports)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
refresh_asset_library(C, library_ref);
|
||||
WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_EDITED, nullptr);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static int brush_asset_load_preview_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
if (RNA_struct_property_is_set(op->ptr, "filepath")) {
|
||||
return brush_asset_load_preview_exec(C, op);
|
||||
}
|
||||
return WM_operator_filesel(C, op, event);
|
||||
}
|
||||
|
||||
void BRUSH_OT_asset_load_preview(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Load Preview Image";
|
||||
ot->description = "Choose a preview image for the brush";
|
||||
ot->idname = "BRUSH_OT_asset_load_preview";
|
||||
|
||||
ot->exec = brush_asset_load_preview_exec;
|
||||
ot->invoke = brush_asset_load_preview_invoke;
|
||||
ot->poll = brush_asset_edit_metadata_poll;
|
||||
|
||||
WM_operator_properties_filesel(ot,
|
||||
FILE_TYPE_FOLDER | FILE_TYPE_IMAGE,
|
||||
FILE_SPECIAL,
|
||||
FILE_OPENFILE,
|
||||
WM_FILESEL_FILEPATH,
|
||||
FILE_DEFAULTDISPLAY,
|
||||
FILE_SORT_DEFAULT);
|
||||
}
|
||||
|
||||
static bool brush_asset_delete_poll(bContext *C)
|
||||
{
|
||||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr;
|
||||
if (paint == nullptr || brush == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Linked brush, check if belongs to an editable blend file. */
|
||||
if (ID_IS_LINKED(brush)) {
|
||||
if (!bke::asset_edit_id_is_writable(brush->id)) {
|
||||
CTX_wm_operator_poll_msg_set(C, "Asset blend file is not editable");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int brush_asset_delete_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
Brush *brush = BKE_paint_brush(paint);
|
||||
Main *bmain = CTX_data_main(C);
|
||||
bUserAssetLibrary *library = (paint->brush_asset_reference) ?
|
||||
BKE_preferences_asset_library_find_by_name(
|
||||
&U,
|
||||
paint->brush_asset_reference->asset_library_identifier) :
|
||||
nullptr;
|
||||
|
||||
bke::asset_edit_id_delete(*bmain, brush->id, *op->reports);
|
||||
|
||||
BKE_paint_brush_set_default(bmain, paint);
|
||||
|
||||
if (library) {
|
||||
refresh_asset_library(C, *library);
|
||||
}
|
||||
|
||||
WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_REMOVED, nullptr);
|
||||
WM_main_add_notifier(NC_BRUSH | NA_EDITED, nullptr);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static int brush_asset_delete_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
|
||||
{
|
||||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
Brush *brush = BKE_paint_brush(paint);
|
||||
|
||||
return WM_operator_confirm_ex(
|
||||
C,
|
||||
op,
|
||||
IFACE_("Delete Brush Asset"),
|
||||
ID_IS_LINKED(brush) ?
|
||||
IFACE_("Permanently delete brush asset blend file. This can't be undone.") :
|
||||
IFACE_("Premanently delete brush. This can't be undo."),
|
||||
IFACE_("Delete"),
|
||||
ALERT_ICON_WARNING,
|
||||
false);
|
||||
}
|
||||
|
||||
void BRUSH_OT_asset_delete(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Delete Brush Asset";
|
||||
ot->description = "Delete the active brush asset";
|
||||
ot->idname = "BRUSH_OT_asset_delete";
|
||||
|
||||
ot->exec = brush_asset_delete_exec;
|
||||
ot->invoke = brush_asset_delete_invoke;
|
||||
ot->poll = brush_asset_delete_poll;
|
||||
}
|
||||
|
||||
static bool brush_asset_update_poll(bContext *C)
|
||||
{
|
||||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr;
|
||||
if (paint == nullptr || brush == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bke::asset_edit_id_is_editable(brush->id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(paint->brush_asset_reference && ID_IS_ASSET(brush))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bke::asset_edit_id_is_writable(brush->id)) {
|
||||
CTX_wm_operator_poll_msg_set(C, "Asset blend file is not editable");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int brush_asset_update_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
Brush *brush = BKE_paint_brush(paint);
|
||||
const AssetWeakReference *asset_weak_ref = paint->brush_asset_reference;
|
||||
|
||||
const bUserAssetLibrary *user_library = BKE_preferences_asset_library_find_by_name(
|
||||
&U, asset_weak_ref->asset_library_identifier);
|
||||
if (!user_library) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
BLI_assert(ID_IS_ASSET(brush));
|
||||
|
||||
bke::asset_edit_id_save(*bmain, brush->id, *op->reports);
|
||||
|
||||
refresh_asset_library(C, *user_library);
|
||||
WM_main_add_notifier(NC_ASSET | ND_ASSET_LIST | NA_EDITED, nullptr);
|
||||
WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void BRUSH_OT_asset_update(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Update Brush Asset";
|
||||
ot->description = "Update the active brush asset in the asset library with current settings";
|
||||
ot->idname = "BRUSH_OT_asset_update";
|
||||
|
||||
ot->exec = brush_asset_update_exec;
|
||||
ot->poll = brush_asset_update_poll;
|
||||
}
|
||||
|
||||
static bool brush_asset_revert_poll(bContext *C)
|
||||
{
|
||||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
Brush *brush = (paint) ? BKE_paint_brush(paint) : nullptr;
|
||||
if (paint == nullptr || brush == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return paint->brush_asset_reference && bke::asset_edit_id_is_editable(brush->id);
|
||||
}
|
||||
|
||||
static int brush_asset_revert_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
Brush *brush = BKE_paint_brush(paint);
|
||||
|
||||
bke::asset_edit_id_revert(*bmain, brush->id, *op->reports);
|
||||
|
||||
WM_main_add_notifier(NC_BRUSH | NA_EDITED, nullptr);
|
||||
WM_main_add_notifier(NC_TEXTURE | ND_NODES, nullptr);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
void BRUSH_OT_asset_revert(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Revert Brush Asset";
|
||||
ot->description =
|
||||
"Revert the active brush settings to the default values from the asset library";
|
||||
ot->idname = "BRUSH_OT_asset_revert";
|
||||
|
||||
ot->exec = brush_asset_revert_exec;
|
||||
ot->poll = brush_asset_revert_poll;
|
||||
}
|
||||
|
||||
} // namespace blender::ed::sculpt_paint
|
|
@ -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);
|
||||
|
|
|
@ -422,19 +422,17 @@ bool mode_toggle_poll_test(bContext *C)
|
|||
|
||||
void smooth_brush_toggle_off(const bContext *C, Paint *paint, StrokeCache *cache)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Brush *brush = BKE_paint_brush(paint);
|
||||
/* The current brush should match with what we have stored in the cache. */
|
||||
BLI_assert(brush == cache->brush);
|
||||
|
||||
/* If saved_active_brush_name is not set, brush was not switched/affected in
|
||||
/* If saved_active_brush is not set, brush was not switched/affected in
|
||||
* smooth_brush_toggle_on(). */
|
||||
Brush *saved_active_brush = (Brush *)BKE_libblock_find_name(
|
||||
bmain, ID_BR, cache->saved_active_brush_name);
|
||||
if (saved_active_brush) {
|
||||
if (cache->saved_active_brush) {
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
BKE_brush_size_set(scene, brush, cache->saved_smooth_size);
|
||||
BKE_paint_brush_set(paint, saved_active_brush);
|
||||
BKE_paint_brush_set(paint, cache->saved_active_brush);
|
||||
cache->saved_active_brush = nullptr;
|
||||
}
|
||||
}
|
||||
/* Initialize the stroke cache invariants from operator properties */
|
||||
|
@ -579,24 +577,27 @@ void last_stroke_update(Scene &scene, const float location[3])
|
|||
/* -------------------------------------------------------------------- */
|
||||
void smooth_brush_toggle_on(const bContext *C, Paint *paint, StrokeCache *cache)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Brush *cur_brush = BKE_paint_brush(paint);
|
||||
|
||||
/* Switch to the blur (smooth) brush if possible. */
|
||||
/* NOTE: used for both vertexpaint and weightpaint, VPAINT_TOOL_BLUR & WPAINT_TOOL_BLUR are the
|
||||
* same, see comments for eBrushVertexPaintTool & eBrushWeightPaintTool. */
|
||||
Brush *smooth_brush = BKE_paint_toolslots_brush_get(paint, WPAINT_TOOL_BLUR);
|
||||
BKE_paint_brush_set_essentials(bmain,
|
||||
paint,
|
||||
(paint->runtime.ob_mode = OB_MODE_WEIGHT_PAINT) ? "Blur Weight" :
|
||||
"Blur Vertex");
|
||||
Brush *smooth_brush = BKE_paint_brush(paint);
|
||||
|
||||
if (!smooth_brush) {
|
||||
BKE_paint_brush_set(paint, cur_brush);
|
||||
CLOG_WARN(&LOG, "Switching to the blur (smooth) brush not possible, corresponding brush not");
|
||||
cache->saved_active_brush_name[0] = '\0';
|
||||
cache->saved_active_brush = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
Brush *cur_brush = paint->brush;
|
||||
int cur_brush_size = BKE_brush_size_get(scene, cur_brush);
|
||||
|
||||
STRNCPY(cache->saved_active_brush_name, cur_brush->id.name + 2);
|
||||
|
||||
BKE_paint_brush_set(paint, smooth_brush);
|
||||
cache->saved_active_brush = cur_brush;
|
||||
cache->saved_smooth_size = BKE_brush_size_get(scene, smooth_brush);
|
||||
BKE_brush_size_set(scene, smooth_brush, cur_brush_size);
|
||||
BKE_curvemapping_init(smooth_brush->curve);
|
||||
|
@ -842,7 +843,7 @@ static int vpaint_mode_toggle_exec(bContext *C, wmOperator *op)
|
|||
depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
||||
}
|
||||
ED_object_vpaintmode_enter_ex(bmain, *depsgraph, scene, ob);
|
||||
BKE_paint_brush_validate(&bmain, &ts.vpaint->paint);
|
||||
BKE_paint_brushes_validate(&bmain, &ts.vpaint->paint);
|
||||
}
|
||||
|
||||
BKE_mesh_batch_cache_dirty_tag((Mesh *)ob.data, BKE_MESH_BATCH_DIRTY_ALL);
|
||||
|
|
|
@ -1631,7 +1631,7 @@ static int wpaint_mode_toggle_exec(bContext *C, wmOperator *op)
|
|||
depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
||||
}
|
||||
ED_object_wpaintmode_enter_ex(bmain, *depsgraph, scene, ob);
|
||||
BKE_paint_brush_validate(&bmain, &ts.wpaint->paint);
|
||||
BKE_paint_brushes_validate(&bmain, &ts.wpaint->paint);
|
||||
}
|
||||
|
||||
/* Prepare armature posemode. */
|
||||
|
|
|
@ -4227,8 +4227,9 @@ static void sculpt_init_mirror_clipping(Object &ob, SculptSession &ss)
|
|||
|
||||
static void smooth_brush_toggle_on(const bContext *C, Paint *paint, StrokeCache *cache)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Brush *cur_brush = paint->brush;
|
||||
Brush *cur_brush = BKE_paint_brush(paint);
|
||||
|
||||
if (cur_brush->sculpt_tool == SCULPT_TOOL_MASK) {
|
||||
cache->saved_mask_brush_tool = cur_brush->mask_tool;
|
||||
|
@ -4247,18 +4248,20 @@ static void smooth_brush_toggle_on(const bContext *C, Paint *paint, StrokeCache
|
|||
}
|
||||
|
||||
/* Switch to the smooth brush if possible. */
|
||||
Brush *smooth_brush = BKE_paint_toolslots_brush_get(paint, SCULPT_TOOL_SMOOTH);
|
||||
BKE_paint_brush_set_essentials(bmain, paint, "Smooth");
|
||||
Brush *smooth_brush = BKE_paint_brush(paint);
|
||||
|
||||
if (!smooth_brush) {
|
||||
BKE_paint_brush_set(paint, cur_brush);
|
||||
CLOG_WARN(&LOG, "Switching to the smooth brush not possible, corresponding brush not");
|
||||
cache->saved_active_brush_name[0] = '\0';
|
||||
cache->saved_active_brush = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
int cur_brush_size = BKE_brush_size_get(scene, cur_brush);
|
||||
|
||||
STRNCPY(cache->saved_active_brush_name, cur_brush->id.name + 2);
|
||||
cache->saved_active_brush = cur_brush;
|
||||
|
||||
BKE_paint_brush_set(paint, smooth_brush);
|
||||
cache->saved_smooth_size = BKE_brush_size_get(scene, smooth_brush);
|
||||
BKE_brush_size_set(scene, smooth_brush, cur_brush_size);
|
||||
BKE_curvemapping_init(smooth_brush->curve);
|
||||
|
@ -4266,7 +4269,6 @@ static void smooth_brush_toggle_on(const bContext *C, Paint *paint, StrokeCache
|
|||
|
||||
static void smooth_brush_toggle_off(const bContext *C, Paint *paint, StrokeCache *cache)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Brush &brush = *BKE_paint_brush(paint);
|
||||
|
||||
if (brush.sculpt_tool == SCULPT_TOOL_MASK) {
|
||||
|
@ -4284,14 +4286,13 @@ static void smooth_brush_toggle_off(const bContext *C, Paint *paint, StrokeCache
|
|||
return;
|
||||
}
|
||||
|
||||
/* If saved_active_brush_name is not set, brush was not switched/affected in
|
||||
/* If saved_active_brush is not set, brush was not switched/affected in
|
||||
* smooth_brush_toggle_on(). */
|
||||
Brush *saved_active_brush = (Brush *)BKE_libblock_find_name(
|
||||
bmain, ID_BR, cache->saved_active_brush_name);
|
||||
if (saved_active_brush) {
|
||||
if (cache->saved_active_brush) {
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
BKE_brush_size_set(scene, &brush, cache->saved_smooth_size);
|
||||
BKE_paint_brush_set(paint, saved_active_brush);
|
||||
BKE_paint_brush_set(paint, cache->saved_active_brush);
|
||||
cache->saved_active_brush = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -526,7 +526,7 @@ static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op)
|
|||
depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
||||
}
|
||||
ED_object_sculptmode_enter_ex(bmain, *depsgraph, scene, ob, false, op->reports);
|
||||
BKE_paint_brush_validate(&bmain, &ts.sculpt->paint);
|
||||
BKE_paint_brushes_validate(&bmain, &ts.sculpt->paint);
|
||||
|
||||
if (ob.mode & mode_flag) {
|
||||
Mesh *mesh = static_cast<Mesh *>(ob.data);
|
||||
|
|
|
@ -1202,6 +1202,7 @@ void ED_spacetype_image()
|
|||
art->free = asset::shelf::region_free;
|
||||
art->on_poll_success = asset::shelf::region_on_poll_success;
|
||||
art->listener = asset::shelf::region_listen;
|
||||
art->message_subscribe = asset::shelf::region_message_subscribe;
|
||||
art->poll = asset::shelf::regions_poll;
|
||||
art->snap_size = asset::shelf::region_snap;
|
||||
art->on_user_resize = asset::shelf::region_on_user_resize;
|
||||
|
@ -1221,7 +1222,7 @@ void ED_spacetype_image()
|
|||
art->listener = asset::shelf::header_region_listen;
|
||||
art->context = asset::shelf::context;
|
||||
BLI_addhead(&st->regiontypes, art);
|
||||
asset::shelf::header_regiontype_register(art, SPACE_IMAGE);
|
||||
asset::shelf::types_register(art, SPACE_IMAGE);
|
||||
|
||||
/* regions: hud */
|
||||
art = ED_area_type_hud(st->spaceid);
|
||||
|
|
|
@ -2219,6 +2219,7 @@ void ED_spacetype_view3d()
|
|||
art->free = asset::shelf::region_free;
|
||||
art->on_poll_success = asset::shelf::region_on_poll_success;
|
||||
art->listener = asset::shelf::region_listen;
|
||||
art->message_subscribe = asset::shelf::region_message_subscribe;
|
||||
art->poll = asset::shelf::regions_poll;
|
||||
art->snap_size = asset::shelf::region_snap;
|
||||
art->on_user_resize = asset::shelf::region_on_user_resize;
|
||||
|
@ -2238,7 +2239,7 @@ void ED_spacetype_view3d()
|
|||
art->listener = asset::shelf::header_region_listen;
|
||||
art->context = asset::shelf::context;
|
||||
BLI_addhead(&st->regiontypes, art);
|
||||
asset::shelf::header_regiontype_register(art, SPACE_VIEW3D);
|
||||
asset::shelf::types_register(art, SPACE_VIEW3D);
|
||||
|
||||
/* regions: hud */
|
||||
art = ED_area_type_hud(st->spaceid);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -399,50 +399,6 @@ static EnumPropertyItem rna_enum_gpencil_brush_modes_items[] = {
|
|||
{GP_BRUSH_MODE_VERTEXCOLOR, "VERTEXCOLOR", 0, "Vertex Color", "Use always Vertex Color mode"},
|
||||
{0, nullptr, 0, nullptr, nullptr}};
|
||||
|
||||
static EnumPropertyItem rna_enum_gpencil_brush_paint_icons_items[] = {
|
||||
{GP_BRUSH_ICON_PENCIL, "PENCIL", ICON_GPBRUSH_PENCIL, "Pencil", ""},
|
||||
{GP_BRUSH_ICON_PEN, "PEN", ICON_GPBRUSH_PEN, "Pen", ""},
|
||||
{GP_BRUSH_ICON_INK, "INK", ICON_GPBRUSH_INK, "Ink", ""},
|
||||
{GP_BRUSH_ICON_INKNOISE, "INKNOISE", ICON_GPBRUSH_INKNOISE, "Ink Noise", ""},
|
||||
{GP_BRUSH_ICON_BLOCK, "BLOCK", ICON_GPBRUSH_BLOCK, "Block", ""},
|
||||
{GP_BRUSH_ICON_MARKER, "MARKER", ICON_GPBRUSH_MARKER, "Marker", ""},
|
||||
{GP_BRUSH_ICON_AIRBRUSH, "AIRBRUSH", ICON_GPBRUSH_AIRBRUSH, "Airbrush", ""},
|
||||
{GP_BRUSH_ICON_CHISEL, "CHISEL", ICON_GPBRUSH_CHISEL, "Chisel", ""},
|
||||
{GP_BRUSH_ICON_FILL, "FILL", ICON_GPBRUSH_FILL, "Fill", ""},
|
||||
{GP_BRUSH_ICON_ERASE_SOFT, "SOFT", ICON_GPBRUSH_ERASE_SOFT, "Eraser Soft", ""},
|
||||
{GP_BRUSH_ICON_ERASE_HARD, "HARD", ICON_GPBRUSH_ERASE_HARD, "Eraser Hard", ""},
|
||||
{GP_BRUSH_ICON_ERASE_STROKE, "STROKE", ICON_GPBRUSH_ERASE_STROKE, "Eraser Stroke", ""},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
static EnumPropertyItem rna_enum_gpencil_brush_sculpt_icons_items[] = {
|
||||
{GP_BRUSH_ICON_GPBRUSH_SMOOTH, "SMOOTH", ICON_GPBRUSH_SMOOTH, "Smooth", ""},
|
||||
{GP_BRUSH_ICON_GPBRUSH_THICKNESS, "THICKNESS", ICON_GPBRUSH_THICKNESS, "Thickness", ""},
|
||||
{GP_BRUSH_ICON_GPBRUSH_STRENGTH, "STRENGTH", ICON_GPBRUSH_STRENGTH, "Strength", ""},
|
||||
{GP_BRUSH_ICON_GPBRUSH_RANDOMIZE, "RANDOMIZE", ICON_GPBRUSH_RANDOMIZE, "Randomize", ""},
|
||||
{GP_BRUSH_ICON_GPBRUSH_GRAB, "GRAB", ICON_GPBRUSH_GRAB, "Grab", ""},
|
||||
{GP_BRUSH_ICON_GPBRUSH_PUSH, "PUSH", ICON_GPBRUSH_PUSH, "Push", ""},
|
||||
{GP_BRUSH_ICON_GPBRUSH_TWIST, "TWIST", ICON_GPBRUSH_TWIST, "Twist", ""},
|
||||
{GP_BRUSH_ICON_GPBRUSH_PINCH, "PINCH", ICON_GPBRUSH_PINCH, "Pinch", ""},
|
||||
{GP_BRUSH_ICON_GPBRUSH_CLONE, "CLONE", ICON_GPBRUSH_CLONE, "Clone", ""},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
|
||||
static EnumPropertyItem rna_enum_gpencil_brush_weight_icons_items[] = {
|
||||
{GP_BRUSH_ICON_GPBRUSH_WEIGHT, "DRAW", ICON_GPBRUSH_WEIGHT, "Draw", ""},
|
||||
{GP_BRUSH_ICON_GPBRUSH_BLUR, "BLUR", ICON_BRUSH_BLUR, "Blur", ""},
|
||||
{GP_BRUSH_ICON_GPBRUSH_AVERAGE, "AVERAGE", ICON_BRUSH_BLUR, "Average", ""},
|
||||
{GP_BRUSH_ICON_GPBRUSH_SMEAR, "SMEAR", ICON_BRUSH_BLUR, "Smear", ""},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
static EnumPropertyItem rna_enum_gpencil_brush_vertex_icons_items[] = {
|
||||
{GP_BRUSH_ICON_VERTEX_DRAW, "DRAW", ICON_BRUSH_MIX, "Draw", ""},
|
||||
{GP_BRUSH_ICON_VERTEX_BLUR, "BLUR", ICON_BRUSH_BLUR, "Blur", ""},
|
||||
{GP_BRUSH_ICON_VERTEX_AVERAGE, "AVERAGE", ICON_BRUSH_BLUR, "Average", ""},
|
||||
{GP_BRUSH_ICON_VERTEX_SMEAR, "SMEAR", ICON_BRUSH_BLUR, "Smear", ""},
|
||||
{GP_BRUSH_ICON_VERTEX_REPLACE, "REPLACE", ICON_BRUSH_MIX, "Replace", ""},
|
||||
{0, nullptr, 0, nullptr, nullptr},
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef RNA_RUNTIME
|
||||
|
@ -1081,26 +1037,6 @@ static std::optional<std::string> rna_BrushGpencilSettings_path(const PointerRNA
|
|||
return "gpencil_settings";
|
||||
}
|
||||
|
||||
static void rna_BrushGpencilSettings_default_eraser_update(Main *bmain,
|
||||
Scene *scene,
|
||||
PointerRNA * /*ptr*/)
|
||||
{
|
||||
ToolSettings *ts = scene->toolsettings;
|
||||
Paint *paint = &ts->gp_paint->paint;
|
||||
Brush *brush_cur = BKE_paint_brush(paint);
|
||||
|
||||
/* disable default eraser in all brushes */
|
||||
for (Brush *brush = static_cast<Brush *>(bmain->brushes.first); brush;
|
||||
brush = static_cast<Brush *>(brush->id.next))
|
||||
{
|
||||
if ((brush != brush_cur) && (brush->ob_mode == OB_MODE_PAINT_GPENCIL_LEGACY) &&
|
||||
(brush->gpencil_tool == GPAINT_TOOL_ERASE))
|
||||
{
|
||||
brush->gpencil_settings->flag &= ~GP_BRUSH_DEFAULT_ERASER;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void rna_BrushGpencilSettings_use_material_pin_update(bContext *C, PointerRNA *ptr)
|
||||
{
|
||||
const Scene *scene = CTX_data_scene(C);
|
||||
|
@ -1121,33 +1057,6 @@ static void rna_BrushGpencilSettings_use_material_pin_update(bContext *C, Pointe
|
|||
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_PROPERTIES, nullptr);
|
||||
}
|
||||
|
||||
static void rna_BrushGpencilSettings_eraser_mode_update(Main * /*bmain*/,
|
||||
Scene *scene,
|
||||
PointerRNA * /*ptr*/)
|
||||
{
|
||||
ToolSettings *ts = scene->toolsettings;
|
||||
Paint *paint = &ts->gp_paint->paint;
|
||||
Brush *brush = BKE_paint_brush(paint);
|
||||
|
||||
/* set eraser icon */
|
||||
if ((brush) && (brush->gpencil_tool == GPAINT_TOOL_ERASE)) {
|
||||
switch (brush->gpencil_settings->eraser_mode) {
|
||||
case GP_BRUSH_ERASER_SOFT:
|
||||
brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_SOFT;
|
||||
break;
|
||||
case GP_BRUSH_ERASER_HARD:
|
||||
brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_HARD;
|
||||
break;
|
||||
case GP_BRUSH_ERASER_STROKE:
|
||||
brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_STROKE;
|
||||
break;
|
||||
default:
|
||||
brush->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_SOFT;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool rna_BrushGpencilSettings_material_poll(PointerRNA * /*ptr*/, PointerRNA value)
|
||||
{
|
||||
Material *ma = (Material *)value.data;
|
||||
|
@ -1730,32 +1639,6 @@ static void rna_def_gpencil_options(BlenderRNA *brna)
|
|||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, nullptr);
|
||||
|
||||
/* brush standard icon */
|
||||
prop = RNA_def_property(srna, "gpencil_paint_icon", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, nullptr, "icon_id");
|
||||
RNA_def_property_enum_items(prop, rna_enum_gpencil_brush_paint_icons_items);
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_ui_text(prop, "Grease Pencil Icon", "");
|
||||
|
||||
prop = RNA_def_property(srna, "gpencil_sculpt_icon", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, nullptr, "icon_id");
|
||||
RNA_def_property_enum_items(prop, rna_enum_gpencil_brush_sculpt_icons_items);
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_GPENCIL);
|
||||
RNA_def_property_ui_text(prop, "Grease Pencil Icon", "");
|
||||
|
||||
prop = RNA_def_property(srna, "gpencil_weight_icon", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, nullptr, "icon_id");
|
||||
RNA_def_property_enum_items(prop, rna_enum_gpencil_brush_weight_icons_items);
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_ui_text(prop, "Grease Pencil Icon", "");
|
||||
|
||||
prop = RNA_def_property(srna, "gpencil_vertex_icon", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, nullptr, "icon_id");
|
||||
RNA_def_property_enum_items(prop, rna_enum_gpencil_brush_vertex_icons_items);
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_ui_text(prop, "Grease Pencil Icon", "");
|
||||
|
||||
/* Mode type. */
|
||||
prop = RNA_def_property(srna, "vertex_mode", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_bitflag_sdna(prop, nullptr, "vertex_mode");
|
||||
|
@ -1952,8 +1835,7 @@ static void rna_def_gpencil_options(BlenderRNA *brna)
|
|||
RNA_def_property_ui_text(prop, "Mode", "Eraser Mode");
|
||||
RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_GPENCIL);
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_update(
|
||||
prop, NC_GPENCIL | ND_DATA, "rna_BrushGpencilSettings_eraser_mode_update");
|
||||
RNA_def_property_update(prop, NC_GPENCIL | ND_DATA, nullptr);
|
||||
|
||||
prop = RNA_def_property(srna, "caps_type", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_sdna(prop, nullptr, "caps_type");
|
||||
|
@ -2088,16 +1970,6 @@ static void rna_def_gpencil_options(BlenderRNA *brna)
|
|||
RNA_def_property_ui_text(prop, "Limit to Viewport", "Fill only visible areas in viewport");
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
|
||||
prop = RNA_def_property(srna, "use_default_eraser", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "flag", GP_BRUSH_DEFAULT_ERASER);
|
||||
RNA_def_property_boolean_default(prop, true);
|
||||
RNA_def_property_ui_icon(prop, ICON_UNPINNED, 1);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Default Eraser", "Use this brush when enable eraser with fast switch key");
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_update(
|
||||
prop, NC_GPENCIL | ND_DATA, "rna_BrushGpencilSettings_default_eraser_update");
|
||||
|
||||
prop = RNA_def_property(srna, "use_settings_postprocess", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "flag", GP_BRUSH_GROUP_SETTINGS);
|
||||
RNA_def_property_ui_text(
|
||||
|
|
|
@ -271,103 +271,56 @@ static std::optional<std::string> rna_ParticleEdit_path(const PointerRNA * /*ptr
|
|||
return "tool_settings.particle_edit";
|
||||
}
|
||||
|
||||
static bool rna_Brush_mode_poll(PointerRNA *ptr, PointerRNA value)
|
||||
static PointerRNA rna_Paint_brush_get(PointerRNA *ptr)
|
||||
{
|
||||
Paint *paint = static_cast<Paint *>(ptr->data);
|
||||
Brush *brush = BKE_paint_brush(paint);
|
||||
if (!brush) {
|
||||
return PointerRNA_NULL;
|
||||
}
|
||||
return RNA_id_pointer_create(&brush->id);
|
||||
}
|
||||
|
||||
static void rna_Paint_brush_set(PointerRNA *ptr, PointerRNA value, ReportList * /*reports*/)
|
||||
{
|
||||
Paint *paint = static_cast<Paint *>(ptr->data);
|
||||
Brush *brush = static_cast<Brush *>(value.data);
|
||||
BKE_paint_brush_set(paint, brush);
|
||||
BKE_paint_invalidate_overlay_all();
|
||||
}
|
||||
|
||||
static bool rna_Paint_brush_poll(PointerRNA *ptr, PointerRNA value)
|
||||
{
|
||||
const Paint *paint = static_cast<Paint *>(ptr->data);
|
||||
Brush *brush = (Brush *)value.owner_id;
|
||||
const uint tool_offset = paint->runtime.tool_offset;
|
||||
const eObjectMode ob_mode = eObjectMode(paint->runtime.ob_mode);
|
||||
UNUSED_VARS_NDEBUG(tool_offset);
|
||||
BLI_assert(tool_offset && ob_mode);
|
||||
const Brush *brush = static_cast<Brush *>(value.data);
|
||||
|
||||
if (brush->ob_mode & ob_mode) {
|
||||
if (paint->brush) {
|
||||
if (BKE_brush_tool_get(paint->brush, paint) == BKE_brush_tool_get(brush, paint)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
return (brush == nullptr) || (paint->runtime.ob_mode & brush->ob_mode) != 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool paint_contains_brush_slot(const Paint *paint, const PaintToolSlot *tslot, int *r_index)
|
||||
static PointerRNA rna_Paint_eraser_brush_get(PointerRNA *ptr)
|
||||
{
|
||||
if ((tslot >= paint->tool_slots) && (tslot < (paint->tool_slots + paint->tool_slots_len))) {
|
||||
*r_index = int(tslot - paint->tool_slots);
|
||||
return true;
|
||||
Paint *paint = static_cast<Paint *>(ptr->data);
|
||||
Brush *brush = BKE_paint_eraser_brush(paint);
|
||||
if (!brush) {
|
||||
return PointerRNA_NULL;
|
||||
}
|
||||
return false;
|
||||
return RNA_id_pointer_create(&brush->id);
|
||||
}
|
||||
|
||||
static bool rna_Brush_mode_with_tool_poll(PointerRNA *ptr, PointerRNA value)
|
||||
static void rna_Paint_eraser_brush_set(PointerRNA *ptr, PointerRNA value, ReportList * /*reports*/)
|
||||
{
|
||||
Scene *scene = (Scene *)ptr->owner_id;
|
||||
const PaintToolSlot *tslot = static_cast<PaintToolSlot *>(ptr->data);
|
||||
ToolSettings *ts = scene->toolsettings;
|
||||
Brush *brush = (Brush *)value.owner_id;
|
||||
int mode = 0;
|
||||
int slot_index = 0;
|
||||
|
||||
if (paint_contains_brush_slot(&ts->imapaint.paint, tslot, &slot_index)) {
|
||||
if (slot_index != brush->imagepaint_tool) {
|
||||
return false;
|
||||
}
|
||||
mode = OB_MODE_TEXTURE_PAINT;
|
||||
}
|
||||
else if (paint_contains_brush_slot(&ts->sculpt->paint, tslot, &slot_index)) {
|
||||
if (slot_index != brush->sculpt_tool) {
|
||||
return false;
|
||||
}
|
||||
mode = OB_MODE_SCULPT;
|
||||
}
|
||||
else if (paint_contains_brush_slot(&ts->vpaint->paint, tslot, &slot_index)) {
|
||||
if (slot_index != brush->vertexpaint_tool) {
|
||||
return false;
|
||||
}
|
||||
mode = OB_MODE_VERTEX_PAINT;
|
||||
}
|
||||
else if (paint_contains_brush_slot(&ts->wpaint->paint, tslot, &slot_index)) {
|
||||
if (slot_index != brush->weightpaint_tool) {
|
||||
return false;
|
||||
}
|
||||
mode = OB_MODE_WEIGHT_PAINT;
|
||||
}
|
||||
else if (paint_contains_brush_slot(&ts->gp_paint->paint, tslot, &slot_index)) {
|
||||
if (slot_index != brush->gpencil_tool) {
|
||||
return false;
|
||||
}
|
||||
mode = OB_MODE_PAINT_GPENCIL_LEGACY;
|
||||
}
|
||||
else if (paint_contains_brush_slot(&ts->gp_vertexpaint->paint, tslot, &slot_index)) {
|
||||
if (slot_index != brush->gpencil_vertex_tool) {
|
||||
return false;
|
||||
}
|
||||
mode = OB_MODE_VERTEX_GPENCIL_LEGACY;
|
||||
}
|
||||
else if (paint_contains_brush_slot(&ts->gp_sculptpaint->paint, tslot, &slot_index)) {
|
||||
if (slot_index != brush->gpencil_sculpt_tool) {
|
||||
return false;
|
||||
}
|
||||
mode = OB_MODE_SCULPT_GPENCIL_LEGACY;
|
||||
}
|
||||
else if (paint_contains_brush_slot(&ts->gp_weightpaint->paint, tslot, &slot_index)) {
|
||||
if (slot_index != brush->gpencil_weight_tool) {
|
||||
return false;
|
||||
}
|
||||
mode = OB_MODE_WEIGHT_GPENCIL_LEGACY;
|
||||
}
|
||||
else if (paint_contains_brush_slot(&ts->curves_sculpt->paint, tslot, &slot_index)) {
|
||||
if (slot_index != brush->curves_sculpt_tool) {
|
||||
return false;
|
||||
}
|
||||
mode = OB_MODE_SCULPT_CURVES;
|
||||
Paint *paint = static_cast<Paint *>(ptr->data);
|
||||
Brush *brush = static_cast<Brush *>(value.data);
|
||||
BKE_paint_eraser_brush_set(paint, brush);
|
||||
BKE_paint_invalidate_overlay_all();
|
||||
}
|
||||
|
||||
return brush->ob_mode & mode;
|
||||
static bool rna_Paint_eraser_brush_poll(PointerRNA *ptr, PointerRNA value)
|
||||
{
|
||||
const Paint *paint = static_cast<Paint *>(ptr->data);
|
||||
const Brush *brush = static_cast<Brush *>(value.data);
|
||||
|
||||
return (brush == nullptr) || (paint->runtime.ob_mode & brush->ob_mode) != 0;
|
||||
}
|
||||
|
||||
static void rna_Sculpt_update(bContext *C, PointerRNA * /*ptr*/)
|
||||
|
@ -443,16 +396,6 @@ static std::optional<std::string> rna_ParticleBrush_path(const PointerRNA * /*pt
|
|||
return "tool_settings.particle_edit.brush";
|
||||
}
|
||||
|
||||
static void rna_Paint_brush_update(Main * /*bmain*/, Scene * /*scene*/, PointerRNA *ptr)
|
||||
{
|
||||
Paint *paint = static_cast<Paint *>(ptr->data);
|
||||
Brush *br = paint->brush;
|
||||
BKE_paint_invalidate_overlay_all();
|
||||
/* Needed because we're not calling 'BKE_paint_brush_set' which handles this. */
|
||||
BKE_paint_toolslots_brush_update(paint);
|
||||
WM_main_add_notifier(NC_BRUSH | NA_SELECTED, br);
|
||||
}
|
||||
|
||||
static void rna_ImaPaint_viewport_update(Main * /*bmain*/, Scene * /*scene*/, PointerRNA * /*ptr*/)
|
||||
{
|
||||
/* not the best solution maybe, but will refresh the 3D viewport */
|
||||
|
@ -600,20 +543,6 @@ static void rna_def_paint_curve(BlenderRNA *brna)
|
|||
RNA_def_struct_ui_icon(srna, ICON_CURVE_BEZCURVE);
|
||||
}
|
||||
|
||||
static void rna_def_paint_tool_slot(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
PropertyRNA *prop;
|
||||
|
||||
srna = RNA_def_struct(brna, "PaintToolSlot", nullptr);
|
||||
RNA_def_struct_ui_text(srna, "Paint Tool Slot", "");
|
||||
|
||||
prop = RNA_def_property(srna, "brush", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_pointer_funcs(prop, nullptr, nullptr, nullptr, "rna_Brush_mode_with_tool_poll");
|
||||
RNA_def_property_ui_text(prop, "Brush", "");
|
||||
}
|
||||
|
||||
static void rna_def_paint(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
|
@ -625,25 +554,38 @@ static void rna_def_paint(BlenderRNA *brna)
|
|||
/* Global Settings */
|
||||
prop = RNA_def_property(srna, "brush", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_pointer_funcs(prop, nullptr, nullptr, nullptr, "rna_Brush_mode_poll");
|
||||
RNA_def_property_ui_text(prop, "Brush", "Active Brush");
|
||||
RNA_def_property_update(prop, 0, "rna_Paint_brush_update");
|
||||
RNA_def_property_struct_type(prop, "Brush");
|
||||
RNA_def_property_pointer_funcs(
|
||||
prop, "rna_Paint_brush_get", "rna_Paint_brush_set", nullptr, "rna_Paint_brush_poll");
|
||||
RNA_def_property_ui_text(prop, "Brush", "Active brush");
|
||||
RNA_def_property_update(prop, NC_BRUSH | NA_SELECTED, nullptr);
|
||||
|
||||
/* paint_tool_slots */
|
||||
prop = RNA_def_property(srna, "tool_slots", PROP_COLLECTION, PROP_NONE);
|
||||
RNA_def_property_collection_sdna(prop, nullptr, "tool_slots", "tool_slots_len");
|
||||
RNA_def_property_struct_type(prop, "PaintToolSlot");
|
||||
/* don't dereference pointer! */
|
||||
RNA_def_property_collection_funcs(prop,
|
||||
prop = RNA_def_property(srna, "brush_asset_reference", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Brush Asset Reference",
|
||||
"A weak reference to the matching brush asset, used e.g. to restore "
|
||||
"the last used brush on file load");
|
||||
|
||||
prop = RNA_def_property(srna, "eraser_brush", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_struct_type(prop, "Brush");
|
||||
RNA_def_property_pointer_funcs(prop,
|
||||
"rna_Paint_eraser_brush_get",
|
||||
"rna_Paint_eraser_brush_set",
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
"rna_iterator_array_get",
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr);
|
||||
RNA_def_property_ui_text(prop, "Paint Tool Slots", "");
|
||||
"rna_Paint_eraser_brush_poll");
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Default Eraser Brush",
|
||||
"Default eraser bnush for quickly alternating with the main brush");
|
||||
RNA_def_property_update(prop, NC_BRUSH | NA_SELECTED, nullptr);
|
||||
|
||||
prop = RNA_def_property(srna, "eraser_brush_asset_reference", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Eraser Brush Asset Reference",
|
||||
"A weak reference to the matching brush asset, used e.g. to restore "
|
||||
"the last used brush on file load");
|
||||
|
||||
prop = RNA_def_property(srna, "palette", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_flag(prop, PROP_EDITABLE);
|
||||
|
@ -1706,7 +1648,6 @@ void RNA_def_sculpt_paint(BlenderRNA *brna)
|
|||
/* *** Non-Animated *** */
|
||||
RNA_define_animate_sdna(false);
|
||||
rna_def_paint_curve(brna);
|
||||
rna_def_paint_tool_slot(brna);
|
||||
rna_def_paint(brna);
|
||||
rna_def_sculpt(brna);
|
||||
rna_def_uv_sculpt(brna);
|
||||
|
|
|
@ -974,7 +974,7 @@ void rna_uiTemplateAssetShelfPopover(uiLayout *layout,
|
|||
icon = icon_value;
|
||||
}
|
||||
|
||||
blender::ui::template_asset_shelf_popover(*layout, *C, asset_shelf_id, name, icon);
|
||||
blender::ui::template_asset_shelf_popover(*layout, *C, asset_shelf_id, name ? name : "", icon);
|
||||
}
|
||||
|
||||
#else
|
||||
|
|
|
@ -3639,7 +3639,7 @@ static int wm_save_mainfile_invoke(bContext *C, wmOperator *op, const wmEvent *
|
|||
}
|
||||
|
||||
if (blendfile_path[0] != '\0') {
|
||||
if (CTX_data_main(C)->has_forward_compatibility_issues) {
|
||||
if (BKE_main_needs_overwrite_confirm(CTX_data_main(C))) {
|
||||
wm_save_file_overwrite_dialog(C, op);
|
||||
ret = OPERATOR_INTERFACE;
|
||||
}
|
||||
|
@ -4001,6 +4001,7 @@ static void file_overwrite_detailed_info_show(uiLayout *parent_layout, Main *bma
|
|||
* block. */
|
||||
uiLayoutSetScaleY(layout, 0.70f);
|
||||
|
||||
if (bmain->has_forward_compatibility_issues) {
|
||||
char writer_ver_str[16];
|
||||
char current_ver_str[16];
|
||||
if (bmain->versionfile == BLENDER_VERSION) {
|
||||
|
@ -4028,6 +4029,17 @@ static void file_overwrite_detailed_info_show(uiLayout *parent_layout, Main *bma
|
|||
uiItemL(layout, message_line2, ICON_NONE);
|
||||
}
|
||||
|
||||
if (bmain->is_asset_repository) {
|
||||
if (bmain->has_forward_compatibility_issues) {
|
||||
uiItemS_ex(layout, 1.4f);
|
||||
}
|
||||
|
||||
uiItemL(layout, RPT_("This file is managed by the Blender asset system."), ICON_NONE);
|
||||
uiItemL(layout, RPT_("and is expected to contain a single asset data-block."), ICON_NONE);
|
||||
uiItemL(layout, RPT_("Take care to avoid data loss when editing assets."), ICON_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
static void save_file_overwrite_cancel(bContext *C, void *arg_block, void * /*arg_data*/)
|
||||
{
|
||||
wmWindow *win = CTX_wm_window(C);
|
||||
|
@ -4090,7 +4102,12 @@ static void save_file_overwrite_saveas(bContext *C, void *arg_block, void * /*ar
|
|||
wmWindow *win = CTX_wm_window(C);
|
||||
UI_popup_block_close(C, win, static_cast<uiBlock *>(arg_block));
|
||||
|
||||
WM_operator_name_call(C, "WM_OT_save_as_mainfile", WM_OP_INVOKE_DEFAULT, nullptr, nullptr);
|
||||
PointerRNA props_ptr;
|
||||
wmOperatorType *ot = WM_operatortype_find("WM_OT_save_as_mainfile", false);
|
||||
WM_operator_properties_create_ptr(&props_ptr, ot);
|
||||
|
||||
WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr, nullptr);
|
||||
WM_operator_properties_free(&props_ptr);
|
||||
}
|
||||
|
||||
static void save_file_overwrite_saveas_button(uiBlock *block, wmGenericCallback *post_action)
|
||||
|
@ -4126,8 +4143,27 @@ static uiBlock *block_create_save_file_overwrite_dialog(bContext *C, ARegion *re
|
|||
uiLayout *layout = uiItemsAlertBox(block, 34, ALERT_ICON_WARNING);
|
||||
|
||||
/* Title. */
|
||||
if (bmain->has_forward_compatibility_issues) {
|
||||
if (bmain->is_asset_repository) {
|
||||
uiItemL_ex(
|
||||
layout,
|
||||
RPT_("Convert asset blend file to regular blend file with an older Blender version?"),
|
||||
ICON_NONE,
|
||||
true,
|
||||
false);
|
||||
}
|
||||
else {
|
||||
uiItemL_ex(
|
||||
layout, RPT_("Overwrite file with an older Blender version?"), ICON_NONE, true, false);
|
||||
}
|
||||
}
|
||||
else if (bmain->is_asset_repository) {
|
||||
uiItemL_ex(
|
||||
layout, RPT_("Convert asset blend file to regular blend file?"), ICON_NONE, true, false);
|
||||
}
|
||||
else {
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
|
||||
/* Filename. */
|
||||
const char *blendfile_path = BKE_main_blendfile_path(CTX_data_main(C));
|
||||
|
@ -4339,7 +4375,7 @@ static uiBlock *block_create__close_file_dialog(bContext *C, ARegion *region, vo
|
|||
|
||||
uiLayout *layout = uiItemsAlertBox(block, 34, ALERT_ICON_QUESTION);
|
||||
|
||||
const bool needs_overwrite_confirm = bmain->has_forward_compatibility_issues;
|
||||
const bool needs_overwrite_confirm = BKE_main_needs_overwrite_confirm(bmain);
|
||||
|
||||
/* Title. */
|
||||
uiItemL_ex(layout, RPT_("Save changes before closing?"), ICON_NONE, true, false);
|
||||
|
|
|
@ -210,7 +210,6 @@ wmKeyMap *WM_keymap_guess_from_context(const bContext *C)
|
|||
wmKeyMap *WM_keymap_guess_opname(const bContext *C, const char *opname)
|
||||
{
|
||||
/* Op types purposely skipped for now:
|
||||
* BRUSH_OT
|
||||
* BOID_OT
|
||||
* BUTTONS_OT
|
||||
* CONSTRAINT_OT
|
||||
|
@ -226,7 +225,7 @@ wmKeyMap *WM_keymap_guess_opname(const bContext *C, const char *opname)
|
|||
|
||||
/* Window. */
|
||||
if (STRPREFIX(opname, "WM_OT") || STRPREFIX(opname, "ED_OT_undo")) {
|
||||
if (STREQ(opname, "WM_OT_tool_set_by_id")) {
|
||||
if (STREQ(opname, "WM_OT_tool_set_by_id") || STREQ(opname, "WM_OT_call_asset_shelf_popover")) {
|
||||
km = WM_keymap_guess_from_context(C);
|
||||
}
|
||||
|
||||
|
@ -336,7 +335,7 @@ wmKeyMap *WM_keymap_guess_opname(const bContext *C, const char *opname)
|
|||
km = WM_keymap_find_all(
|
||||
wm, "Paint Face Mask (Weight, Vertex, Texture)", SPACE_EMPTY, RGN_TYPE_WINDOW);
|
||||
}
|
||||
else if (STRPREFIX(opname, "PAINT_OT")) {
|
||||
else if (STRPREFIX(opname, "PAINT_OT") || STRPREFIX(opname, "BRUSH_OT")) {
|
||||
/* Check for relevant mode. */
|
||||
switch (CTX_data_mode_enum(C)) {
|
||||
case CTX_MODE_PAINT_WEIGHT:
|
||||
|
|
|
@ -2212,6 +2212,39 @@ static void WM_OT_call_panel(wmOperatorType *ot)
|
|||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
}
|
||||
|
||||
static int asset_shelf_popover_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
|
||||
{
|
||||
char *asset_shelf_id = RNA_string_get_alloc(op->ptr, "name", nullptr, 0, nullptr);
|
||||
BLI_SCOPED_DEFER([&]() { MEM_freeN(asset_shelf_id); });
|
||||
|
||||
if (!blender::ui::asset_shelf_popover_invoke(*C, asset_shelf_id, *op->reports)) {
|
||||
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
|
||||
}
|
||||
|
||||
return OPERATOR_INTERFACE;
|
||||
}
|
||||
|
||||
/* Needs to be defined at WM level to be globally accessible. */
|
||||
static void WM_OT_call_asset_shelf_popover(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Call Asset Shelf Popover";
|
||||
ot->idname = "WM_OT_call_asset_shelf_popover";
|
||||
ot->description = "Open a predefined asset shelf in a popup";
|
||||
|
||||
/* api callbacks */
|
||||
ot->invoke = asset_shelf_popover_invoke;
|
||||
|
||||
ot->flag = OPTYPE_INTERNAL;
|
||||
|
||||
RNA_def_string(ot->srna,
|
||||
"name",
|
||||
nullptr,
|
||||
0,
|
||||
"Asset Shelf Name",
|
||||
"Identifier of the asset shelf to display");
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
@ -4065,6 +4098,7 @@ void wm_operatortypes_register()
|
|||
WM_operatortype_append(WM_OT_call_menu);
|
||||
WM_operatortype_append(WM_OT_call_menu_pie);
|
||||
WM_operatortype_append(WM_OT_call_panel);
|
||||
WM_operatortype_append(WM_OT_call_asset_shelf_popover);
|
||||
WM_operatortype_append(WM_OT_radial_control);
|
||||
WM_operatortype_append(WM_OT_stereo3d_set);
|
||||
#if defined(WIN32)
|
||||
|
|
|
@ -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