Brushstroke Tools: Initial Version #328

Merged
Simon Thommes merged 229 commits from SimonThommes/blender-studio-tools:brushstroke_tools-initial-version into main 2024-11-06 15:03:47 +01:00
5 changed files with 129 additions and 24 deletions
Showing only changes of commit 9debde1599 - Show all commits

View File

@ -128,6 +128,9 @@ class BSBST_OT_new_brushstrokes(bpy.types.Operator):
brushstrokes_object.modifiers[mod.name][f'{v.identifier}_use_attribute'] = False
brushstrokes_object.modifiers[mod.name][f'{v.identifier}'] = type(brushstrokes_object.modifiers[mod.name][f'{v.identifier}'])(val)
# transfer modifier info data from preset to brush strokes
utils.deep_copy_mod_info(settings.preset_object, brushstrokes_object)
# refresh UI
for mod in brushstrokes_object.modifiers:
mod.node_group.interface_update(context)
@ -209,8 +212,12 @@ class BSBST_OT_init_preset(bpy.types.Operator):
mod_info = settings.preset_object.modifier_info.add()
mod_info.name = mod.name
# context link settings
utils.mark_socket_context_type(mod_info, 'Socket_2', 'SURFACE_OBJECT')
# ui visibility settings
mod_info.hide_ui = True
## brushstrokes
mod = preset_object.modifiers.new('Brushstrokes', 'NODES')
mod.node_group = bpy.data.node_groups['.brushstroke_tools.surface_fill']
@ -220,9 +227,36 @@ class BSBST_OT_init_preset(bpy.types.Operator):
mod_info = settings.preset_object.modifier_info.add()
mod_info.name = mod.name
# context link settings
utils.mark_socket_context_type(mod_info, 'Socket_2', 'FLOW_OBJECT')
utils.mark_socket_context_type(mod_info, 'Socket_3', 'UVMAP')
utils.mark_socket_context_type(mod_info, 'Socket_9', 'RANDOM')
utils.mark_socket_context_type(mod_info, 'Socket_60', 'FLOW_OBJECT')
# ui visibility settings
hide_sockets =[
'Socket_3',
'Socket_8',
'Socket_9',
'Socket_12',
'Socket_15',
'Socket_27',
'Socket_35',
]
for s in hide_sockets:
utils.mark_socket_hidden(mod_info, s)
hide_panels = [
'Surface',
'Flow',
'Stroke Culling',
'Lighting Influence',
'Offset Override',
'Debug',
]
for p in hide_panels:
utils.mark_panel_hidden(mod_info, p)
def init_draw(self, context):
settings = context.scene.BSBST_settings

View File

@ -122,9 +122,11 @@ class BSBST_socket_info(bpy.types.PropertyGroup):
link_context: bpy.props.BoolProperty(default=False, name='Link to Context')
link_context_type: bpy.props.EnumProperty(default=1, name='Link to Context', update=update_link_context_type,
items=link_context_type_items)
hide_ui: bpy.props.BoolProperty(default=False)
class BSBST_modifier_info(bpy.types.PropertyGroup):
name: bpy.props.StringProperty(default='')
hide_ui: bpy.props.BoolProperty(default=False)
socket_info: bpy.props.CollectionProperty(type=BSBST_socket_info)
class BSBST_context_brushstrokes(bpy.types.PropertyGroup):
@ -164,6 +166,7 @@ class BSBST_Settings(bpy.types.PropertyGroup):
update=update_active_brushstrokes,
get=get_active_context_brushstrokes_index,
set=set_active_context_brushstrokes_index)
ui_options: bpy.props.BoolProperty(default=False, name='UI Options')
classes = [
BSBST_socket_info,

View File

@ -2,7 +2,7 @@ import bpy
from . import utils
from . import settings as settings_py
def draw_panel_ui_recursive(panel, panel_name, mod, items):
def draw_panel_ui_recursive(panel, panel_name, mod, items, display_mode):
scene = bpy.context.scene
settings = scene.BSBST_settings
@ -19,15 +19,40 @@ def draw_panel_ui_recursive(panel, panel_name, mod, items):
for k, v in items:
if type(v) == bpy.types.NodeTreeInterfacePanel:
v_id = f'Panel_{v.index}' # TODO: replace with panel identifier once that is exposed in Blender 4.3
if not mod_info:
continue
s = mod_info.socket_info.get(v_id)
if not s:
continue
if display_mode == 0:
if s.hide_ui:
continue
subpanel_header, subpanel = panel.panel(k, default_closed = v.default_closed)
subpanel_header.label(text=k)
draw_panel_ui_recursive(subpanel, k, mod, v.interface_items.items())
if display_mode != 0:
subpanel_header.prop(s, 'hide_ui', icon_only=True, icon='REMOVE')
draw_panel_ui_recursive(subpanel, k, mod, v.interface_items.items(), display_mode)
else:
if v.parent.name != panel_name:
continue
if f'{v.identifier}' not in mod.keys():
continue
if not mod_info:
continue
s = mod_info.socket_info.get(v.identifier)
if not s:
continue
if display_mode == 0:
if s.hide_ui:
continue
row = panel.row(align=True)
col = row.column()
input_row = col.row(align=True)
attribute_toggle = False
@ -51,21 +76,17 @@ def draw_panel_ui_recursive(panel, panel_name, mod, items):
toggle.input_name = v.identifier
else:
input_row.prop(mod, f'["{v.identifier}"]', text=k)
if not mod_info:
continue
if type(v) in utils.linkable_sockets:
s = mod_info.socket_info.get(v.identifier)
if not s:
continue
col.enabled = not s.link_context
if not s:
continue
icon = settings_py.icon_from_link_type(s.link_context_type)
row.alignment = 'EXPAND'
if s.link_context:
row.prop(s, 'link_context', text='', icon_value=icon)
else:
if display_mode == -1:
row.prop(s, 'link_context_type', text='', emboss=True, icon='LINKED', icon_only=True)
if display_mode != 0:
row.prop(s, 'hide_ui', icon_only=True, icon='REMOVE')
class BSBST_UL_brushstroke_objects(bpy.types.UIList):
def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
@ -121,7 +142,7 @@ class BSBST_PT_brushstroke_tools_panel(bpy.types.Panel):
new_advanced_panel.label(text='Curve mode does not support drawing on deformed geometry', icon='ERROR')
new_advanced_panel.prop(settings, 'assign_materials')
row = new_advanced_panel.row()
new_advanced_panel.prop(settings, 'ui_options', icon='OPTIONS')
# identify style context
style_object = context.object if settings.style_context=='BRUSHSTROKES' else settings.preset_object
@ -139,6 +160,10 @@ class BSBST_PT_brushstroke_tools_panel(bpy.types.Panel):
is_preset = style_object == settings.preset_object
display_mode = settings.ui_options
if is_preset:
display_mode = -1
style_header, style_panel = layout.panel("brushstrokes_style", default_closed=False)
if is_preset:
@ -161,15 +186,25 @@ class BSBST_PT_brushstroke_tools_panel(bpy.types.Panel):
if style_object:
for mod in style_object.modifiers:
mod_info = mod.id_data.modifier_info.get(mod.name)
if not mod_info:
continue
if display_mode == 0:
if mod_info.hide_ui:
continue
mod_header, mod_panel = style_panel.panel(mod.name, default_closed = False)
mod_header.label(text='', icon='GEOMETRY_NODES')
mod_header.prop(mod, 'name', text='')
row = mod_header.row(align=True)
row.label(text='', icon='GEOMETRY_NODES')
row.prop(mod, 'name', text='')
if is_preset:
op = mod_header.operator('brushstroke_tools.preset_remove_mod', text='', icon='X')
op = row.operator('brushstroke_tools.preset_remove_mod', text='', icon='X')
else:
op = mod_header.operator('object.modifier_remove', text='', icon='X')
op = row.operator('object.modifier_remove', text='', icon='X')
op.modifier = mod.name
if display_mode != 0:
mod_header.prop(mod_info, 'hide_ui', icon_only=True, icon='REMOVE')
if not mod_panel:
continue
@ -184,7 +219,11 @@ class BSBST_PT_brushstroke_tools_panel(bpy.types.Panel):
if not mod.node_group:
continue
draw_panel_ui_recursive(mod_panel, '', mod, mod.node_group.interface.items_tree.items())
draw_panel_ui_recursive(mod_panel,
'',
mod,
mod.node_group.interface.items_tree.items(),
display_mode)
# expose add modifier operator for preset context
if is_preset:

View File

@ -26,21 +26,19 @@ def refresh_preset(dummy):
if not settings.preset_object:
return
for mod in settings.preset_object.modifiers:
if not mod.type == 'NODES':
continue
if not mod.node_group:
continue
mod_info = settings.preset_object.modifier_info.get(mod.name)
if not mod_info:
mod_info = settings.preset_object.modifier_info.add()
mod_info.name = mod.name
for v in mod.node_group.interface.items_tree.values():
if not type(v) in linkable_sockets:
continue
if v.identifier in [s.name for s in mod_info.socket_info]:
if type(v) is bpy.types.NodeTreeInterfacePanel:
v_id = f'Panel_{v.index}' # TODO: replace with panel identifier once that is exposed in Blender 4.3
else:
v_id = v.identifier
if v_id in [s.name for s in mod_info.socket_info]:
continue
n = mod_info.socket_info.add()
n.name = v.identifier
n.name = v_id
# TODO: clean up old settings
def mark_socket_context_type(mod_info, socket_name, link_type):
@ -50,6 +48,37 @@ def mark_socket_context_type(mod_info, socket_name, link_type):
socket_info.name = socket_name
socket_info.link_context_type = link_type
def mark_socket_hidden(mod_info, socket_name, hide=True):
socket_info = mod_info.socket_info.get(socket_name)
if not socket_info:
socket_info = mod_info.socket_info.add()
socket_info.name = socket_name
socket_info.hide_ui = hide
def mark_panel_hidden(mod_info, panel_name, hide=True):
mod = mod_info.id_data.modifiers.get(mod_info.name)
if not mod:
return
if not mod.type == 'NODES':
return
ng = mod.node_group
if not ng:
return
v_id = ''
for k, v in ng.interface.items_tree.items():
if type(v) != bpy.types.NodeTreeInterfacePanel:
continue
if v.name == panel_name:
v_id = f'Panel_{v.index}'
break
if not v_id:
return
socket_info = mod_info.socket_info.get(v_id)
if not socket_info:
socket_info = mod_info.socket_info.add()
socket_info.name = v_id
socket_info.hide_ui = hide
def deep_copy_mod_info(source_object, target_object):
for mod_info in source_object.modifier_info:
mod_info_tgt = target_object.modifier_info.add()