This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/release/scripts/startup/bl_ui/sculpt_ui.py
2021-10-02 00:15:48 -07:00

543 lines
15 KiB
Python

import bpy
import os
import sys
from bpy.props import *
from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
class WM_MT_button_context(bpy.types.Menu):
bl_label = "Unused"
def draw(self, context):
pass
def getToolSlotName(brush):
slot = brush.rna_type.properties["sculpt_tool"].enum_items
return "builtin_brush." + slot[brush.sculpt_tool].name
def getToolSlotIndex(brush):
for i, enum in enumerate(brush.rna_type.properties["sculpt_tool"].enum_items):
if enum.identifier == brush.sculpt_tool:
return i
def getPalette(autocreate=False):
key = "SCULPT_PALETTE"
if key not in bpy.data.palettes:
if autocreate:
pal = bpy.data.palettes.new(key)
for i in range(9):
ref = pal.brush_palette.brushes.add()
ref.type = "EMPTY"
else:
return None
return bpy.data.palettes[key].brush_palette
def menu_func(self, context):
layout = self.layout
layout.separator()
if hasattr(context, "button_operator"):
opname = context.button_operator.rna_type.identifier
if opname == "SCULPT_OT_select_brush_tool":
props = layout.operator("sculpt.remove_brush_tool")
props.slot = context.button_operator.ref
layout.operator("sculpt.add_to_palette")
layout.operator("sculpt.call_brush_palette")
#layout.operator(WM_MT_button_context_sculpt_palette.bl_idname)
class BrushRef(bpy.types.PropertyGroup):
type_items = [("BRUSH", "Brush","", 0),
("TOOL", "Tool", "",1),
("EMPTY", "Empty", "", 2)]
type : EnumProperty(items=type_items)
brush : PointerProperty(type=bpy.types.Brush)
tool : StringProperty()
class BrushPalette(bpy.types.PropertyGroup):
brushes : CollectionProperty(type=BrushRef)
def brushCount(self):
count = 0
for i, ref in enumerate(self.brushes):
if ref.type != "EMPTY" and (ref.type == "TOOL" or (ref.type == "BRUSH" and ref.brush)):
count += 1
return count
def addBrush(self, brush):
for ref in self.brushes:
if ref.brush == brush:
print("Brush already in palette!")
return None
if ref.type == "EMPTY" or (ref.type == "BRUSH" and not ref.brush):
ref.type = "BRUSH"
ref.brush = brush
return ref
ref = self.brushes.add()
ref.type = "BRUSH"
ref.brush = brush
return ref
class BrushPaletteSet(bpy.types.PropertyGroup):
palettes : CollectionProperty(type=BrushPalette)
active : IntProperty()
def getOrCreateActive(self):
if len(self.palettes) == 0:
self.palettes.add()
return self.palettes[self.active]
def getActive(self):
if len(self.palettes) == 0:
return None
return self.palettes[self.active]
def sculpt_poll(cls, context):
return context.mode == "SCULPT" and context.active_object
whitelist = ["mesh_filter", "cloth_filter", "line_project"
"box_face_set", "box_mask", "box_hide",
"ipmask_filter", "color_filter", "mask_by_color",
"face_set_edit", "move", "rotate", "scale",
"transform", "annotate"]
whitelist = set(map(lambda item: "builtin." + item, whitelist))
class AddToPalette(bpy.types.Operator):
"Add brush/tool to palette"
bl_idname = "sculpt.add_to_palette"
bl_label = "Add To Palette"
@classmethod
def poll(cls, context):
if not sculpt_poll(cls, context):
return False
ok = False
for k in ["button_pointer", "button_prop", "button_operator"]:
if not hasattr(context, k):
continue
v = getattr(context, k)
print(v, dir(v))
if hasattr(v, "name"):
print(v.name)
if k == "button_operator":
ok = v.name.startswith("builtin_brush") or v.name in whitelist
break
print("=====" + k + "=====")
"""
for k in dir(context):
print(k)
#"""
return ok
def execute(self, context):
pal = getPalette(True)
ok = False
key = None
for k in ["button_pointer", "button_prop", "button_operator"]:
if not hasattr(context, k):
continue
v = getattr(context, k)
print(v, dir(v))
if hasattr(v, "name"):
print(v.name)
if k == "button_operator":
ok = v.name.startswith("builtin_brush") or v.name in whitelist
if ok:
key = v.name
break
if not ok:
return {'CANCELLED'}
print("found brush or tool", key)
if key.startswith("builtin_brush"):
self.do_brush(context, key)
else:
self.do_tool(context, key)
return {'FINISHED'}
def do_brush(self, context, key):
key = key.split(".")
type = key[1].upper()
print(type)
slots = context.tool_settings.sculpt.tool_slots
slot = None
for slot2 in slots:
if not slot2.brush: continue
if slot2.brush.sculpt_tool == type:
slot = slot2
break
if slot is None:
print("error!", type)
return
print("found", type)
pal = getPalette(True)
pal.addBrush(slot.brush)
def do_tool(self, context, key):
pass
class SelectBrushTool(bpy.types.Operator):
"Select brush/tool"
bl_idname = "sculpt.select_brush_tool"
bl_label = "Select Brush/Tool"
ref : IntProperty()
@classmethod
def poll(cls, context):
return sculpt_poll(cls, context)
def execute(self, context):
pal = getPalette(True)
ref = pal.brushes[self.ref]
if ref.type == "TOOL":
bpy.ops.wm.tool_set_by_id(name="builtin." + ref.tool)
elif ref.type == "BRUSH":
print("REF", ref.brush, ref.type, ref)
if not ref.brush:
return {'CANCELLED'}
tool = getToolSlotName(ref.brush)
#bpy.ops.wm.tool_set_by_id(name=tool)
bpy.ops.paint.brush_select(sculpt_tool=ref.brush.sculpt_tool)
sloti = getToolSlotIndex(ref.brush)
slots = bpy.context.tool_settings.sculpt.tool_slots
slots[sloti].brush = ref.brush
return {'FINISHED'}
class SetBrushTool(bpy.types.Operator):
"Set brush/tool"
bl_idname = "sculpt.set_brush_tool"
bl_label = "Set Palette Entry"
ref : IntProperty()
slot : IntProperty()
@classmethod
def poll(cls, context):
return sculpt_poll(cls, context)
def execute(self, context):
pal = getPalette(True)
slot = self.slot
while len(pal.brushes) <= slot:
ref = pal.brushes.add()
ref.type = "EMPTY"
ref = pal.brushes[slot]
ref.type = "BRUSH"
ref.brush = context.tool_settings.sculpt.brush
return {'FINISHED'}
class RemoveBrushTool(bpy.types.Operator):
"Remove brush/tool"
bl_idname = "sculpt.remove_brush_tool"
bl_label = "Remove Brush/Tool From Palette"
ref : IntProperty()
slot : IntProperty()
@classmethod
def poll(cls, context):
return sculpt_poll(cls, context)
def execute(self, context):
pal = getPalette(True)
slot = self.slot
pal.brushes[slot].type = "EMPTY"
pal.brushes[slot].brush = None
return {'FINISHED'}
class CallBrushMenu(bpy.types.Operator):
"Select brush/tool"
bl_idname = "sculpt.call_brush_palette"
bl_label = "Open Brush Palette"
@classmethod
def poll(cls, context):
ok = context.active_object is not None
ok = ok and context.mode == "SCULPT"
ok = ok and context.tool_settings is not None
ok = ok and context.tool_settings.sculpt is not None
return ok
def invoke(cls, context, b):
getPalette(True)
bpy.ops.wm.call_panel(name="VIEW3D_PT_SculptPalette", keep_open=False)
#bpy.ops.wm.call_menu(name="VIEW3D_MT_SculptPalette")
return {'CANCELLED'}
def execute(self, context):
return {'CANCELLED'}
class VIEW3D_PT_SculptPalette(bpy.types.Panel):
bl_label = "Sculpt Palette"
bl_idname = "VIEW3D_PT_SculptPalette"
bl_space_type = "VIEW_3D"
bl_region_type = "WINDOW"
def draw(self, context):
layout = self.layout #.menu_pie()#.column(align=True)
row = layout.column(align=1)
col = None #row.column(align=True)
row.alignment = 'LEFT'
scale = 2.0
pal = getPalette(False)
if pal is None:
print("no palettes")
return
#rows and cols as identifiers became logically swapped during
#development, unwap them
j = 0
rows = min(int(len(pal.brushes) / 3) + 1, 3)
cols = 3
if rows * cols == pal.brushCount():
rows += 1
for i in range(cols * rows):
if i % cols == 0:
col = row.row(align=1)
col.scale_x = scale
col.scale_y = scale
col.alignment = 'LEFT'
j += 1
if j == rows:
row = layout.column(align=1)
j = 0
id = ((i + 0) % cols)
id = ((j + 2) % cols) * cols + id
n = 0
icon = 0
if id < len(pal.brushes) and pal.brushes[id].type != "EMPTY" and pal.brushes[id].brush:
n = getToolSlotIndex(pal.brushes[id].brush)
brush = pal.brushes[id].brush
vname = brush.sculpt_tool.lower()
vname = "brush.sculpt." + vname
print("ICON", vname)
icon = ToolSelectPanelHelper._icon_value_from_icon_handle(vname)
if id > 10:
keyt = ''
else:
keyt = str(id)
keyt = ''
empty = id >= len(pal.brushes)
empty = empty or pal.brushes[id].type == "EMPTY"
empty = empty or pal.brushes[id].type == "BRUSH" and not pal.brushes[id]
if empty:
icon = 9
props = col.operator("sculpt.set_brush_tool", text='', icon_value=icon)
props.slot = id
else:
props = col.operator("sculpt.select_brush_tool", text=keyt, icon_value=icon)
props.ref = id
#col.label(text="")
#col.template_icon(i, scale=1)
"""
preview_collections = {}
my_icons_dir = os.path.dirname(bpy.data.filepath)
my_icons_path = ""
my_icon_name = "my_icon"
class SubObj (dict):
pass
def savedata(obj, subkeys={}, is_sub=False):
props = obj.bl_rna.properties
ret = {} if not is_sub else SubObj()
for p in props:
if p.is_readonly:
continue
if p.type in ["ENUM", "FLOAT", "INT", "BOOLEAN", "STRING"]:
ret[p.identifier] = getattr(obj, p.identifier)
return ret
def loaddata(obj, data):
for k in data:
v = data[k]
if type(v) == SubObj:
loaddata(getattr(obj), v)
continue
try:
setattr(obj, k, v)
except:
print("failed to set property", k, "on", obj);
def pushRender():
render = bpy.context.scene.render
return savedata(render)
def popRender(state):
render = bpy.context.scene.render
loaddata(render, state)
state = pushRender()
popRender(state)
class RenderPreview (bpy.types.Operator):
"Render preview icon"
bl_idname = "render.my_render_preview"
bl_label = "Render Icon"
filepath: bpy.props.StringProperty()
use_viewport: bpy.props.BoolProperty()
@classmethod
def poll(cls, context):
return context.active_object is not None
def execute(self, context):
state = pushRender()
render = bpy.context.scene.render
print("Render!")
print("file", self.filepath)
render.filepath = self.filepath
res = 256
render.resolution_x = res
render.resolution_y = res
render.film_transparent = True
render.filter_size = 0.75
if self.use_viewport:
bpy.ops.render.opengl(write_still=True, view_context=True)
else:
bpy.ops.render.render(write_still=True)
popRender(state)
pcoll = preview_collections["main"]
print(pcoll) #.update.__doc__, pcoll.load.__doc__)
#pcoll.clear(m
if my_icon_name in pcoll:
pcoll[my_icon_name].reload()
else:
pcoll.load(my_icon_name, my_icon_path, 'IMAGE', force_reload=True)
return {'FINISHED'}
class PreviewsExamplePanel(bpy.types.Panel):
"Creates a Panel in the Object properties window"
bl_label = "Previews Example Panel"
bl_idname = "OBJECT_PT_previews"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "object"
def draw(self, context):
layout = self.layout
pcoll = preview_collections["main"]
row = layout.row()
my_icon = pcoll[my_icon_name]
print(my_icon_name, my_icon.icon_id)
row.template_icon(my_icon.icon_id, scale=2)
props = row.operator("render.my_render_preview", icon_value=my_icon.icon_id)
props["filepath"] = my_icon_path
props["use_viewport"] = True
# my_icon.icon_id can be used in any UI function that accepts
# icon_value # try also setting text=""
# to get an icon only operator button
#"""
classes = [VIEW3D_PT_SculptPalette,
#RenderPreview,
WM_MT_button_context,
SelectBrushTool,
CallBrushMenu,
BrushRef,
SetBrushTool,
AddToPalette,
RemoveBrushTool,
BrushPalette,
BrushPaletteSet]
def post_register():
bpy.types.Palette.brush_palette = PointerProperty(type=BrushPalette)
bpy.types.WM_MT_button_context.append(menu_func)
if __name__ == "__main__":
if not hasattr(bpy, "sculpt_global"):
bpy.sculpt_global = []
for cls in bpy.sculpt_global:
bpy.utils.unregister_class(cls)
bpy.sculpt_global = []
for cls in classes:
bpy.utils.register_class(cls)
bpy.sculpt_global.append(cls)
post_register()