198 lines
6.5 KiB
Python
198 lines
6.5 KiB
Python
from pathlib import Path
|
|
import bpy
|
|
|
|
from .asset_library import has_bioxel_asset_library
|
|
from .node import get_layer_nodes, get_main_node_group
|
|
from .utils import load_icon
|
|
from .layer import get_layer_caches
|
|
from .operators.io import ImportAsColor, ImportAsLabel, ImportAsScalar, ImportData
|
|
from .operators.misc import AddAssetLibrary, Help, RenderSettingPreset
|
|
from .operators.layer import (
|
|
AddLayerNode,
|
|
DeleteLayer,
|
|
RelocatePath,
|
|
RenameLayer,
|
|
SaveLayer,
|
|
)
|
|
|
|
|
|
class ImportMenu(bpy.types.Menu):
|
|
bl_label = "Bioxel Import"
|
|
bl_idname = "BIOXEL_MT_import"
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
layout.operator(ImportAsScalar.bl_idname,
|
|
text="Import As Scalar").filepath = ""
|
|
layout.operator(ImportAsLabel.bl_idname,
|
|
text="Import As Label").filepath = ""
|
|
layout.operator(ImportAsColor.bl_idname,
|
|
text="Import As Color").filepath = ""
|
|
|
|
|
|
class RenderSettingMenu(bpy.types.Menu):
|
|
bl_label = "Render Setting Presets"
|
|
bl_idname = "BIOXEL_MT_render_setting"
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
for k, v in RenderSettingPreset.PRESETS.items():
|
|
op = layout.operator(RenderSettingPreset.bl_idname, text=v)
|
|
op.preset = k
|
|
|
|
|
|
class BioxelPanelBase:
|
|
"""
|
|
Abstract base panel for all Bioxel UI panels.
|
|
|
|
- Ensures the panel appears only when the active area is the Node Editor
|
|
and the Node Editor is editing a Geometry Nodes tree.
|
|
- Places the panel under the "Bioxel" sidebar tab.
|
|
- Provides an optional bl_order for panel ordering inside the category.
|
|
"""
|
|
|
|
bl_space_type = "NODE_EDITOR"
|
|
bl_region_type = "UI"
|
|
bl_context = "geometry_node"
|
|
bl_category = "Bioxel Nodes"
|
|
requires_asset_library = True
|
|
# try to make Bioxel panels order early (may be respected by Blender)
|
|
|
|
@classmethod
|
|
def poll(cls, context):
|
|
node_group = get_main_node_group(context)
|
|
if node_group is None:
|
|
return False
|
|
|
|
if not getattr(cls, "requires_asset_library", True):
|
|
return True
|
|
|
|
return has_bioxel_asset_library()
|
|
|
|
|
|
class HeaderPanel(bpy.types.Panel, BioxelPanelBase):
|
|
bl_label = "Bioxel Nodes"
|
|
bl_idname = "BIOXEL_PT_header_panel"
|
|
bl_order = 0
|
|
requires_asset_library = False
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
|
|
if not has_bioxel_asset_library():
|
|
layout.operator(
|
|
AddAssetLibrary.bl_idname,
|
|
text="Add Nodes Library",
|
|
icon="ASSET_MANAGER",
|
|
)
|
|
return
|
|
|
|
layout.operator(
|
|
Help.bl_idname, text="Help", icon=Help.bl_icon # 复用原操作的图标定义
|
|
)
|
|
|
|
layout.menu(
|
|
RenderSettingMenu.bl_idname, text="Render Preset", icon="RENDER_STILL"
|
|
)
|
|
|
|
|
|
class LayerNodePanel(BioxelPanelBase, bpy.types.Panel):
|
|
bl_label = "Layer Nodes"
|
|
bl_idname = "BIOXEL_PT_o_layer_nodes"
|
|
bl_order = 1
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
node_group = get_main_node_group(context)
|
|
|
|
layout.template_list(
|
|
"BIOXEL_UL_layer_nodes",
|
|
"",
|
|
node_group,
|
|
"nodes",
|
|
context.window_manager,
|
|
"bioxel_layer_list_index",
|
|
rows=3,
|
|
)
|
|
|
|
|
|
class LibraryPanel(BioxelPanelBase, bpy.types.Panel):
|
|
bl_label = "Layer Library"
|
|
bl_idname = "BIOXEL_PT_library_panel"
|
|
bl_order = 2
|
|
|
|
def draw(self, context):
|
|
layout = self.layout
|
|
wm = context.window_manager
|
|
caches = get_layer_caches()
|
|
|
|
row = layout.row()
|
|
row.scale_y = 2.0
|
|
row.operator(ImportData.bl_idname, icon="IMPORT")
|
|
# row.menu(ImportMenu.bl_idname, icon="IMPORT", text="Import")
|
|
layout.separator()
|
|
layout.template_icon_view(
|
|
bpy.context.window_manager,
|
|
"bioxel_layer_library",
|
|
show_labels=False,
|
|
scale=10.0,
|
|
scale_popup=6.0,
|
|
)
|
|
|
|
selected_id = getattr(wm, "bioxel_layer_library", None)
|
|
selected_id = None if selected_id == "nothing_found" else selected_id
|
|
entry = next((c for c in caches if c.get("id") == selected_id), None)
|
|
if entry:
|
|
cache_id = entry["id"]
|
|
path = entry["path"]
|
|
name = entry.get("name", "?")
|
|
kind = entry.get("kind", "?")
|
|
path_exists = Path(bpy.path.abspath(path)).exists()
|
|
|
|
layout.prop(wm, "bioxel_snapshot_z", text="Z Slice", slider=True)
|
|
layout.separator()
|
|
# operations and meta_box as before
|
|
ops_row = layout.row(align=True)
|
|
if path_exists:
|
|
ops_row.operator(
|
|
AddLayerNode.bl_idname, text="Add", icon="PLUS"
|
|
).cache_id = selected_id
|
|
ops_row.operator(
|
|
SaveLayer.bl_idname, text="Save", icon="FILE_TICK"
|
|
).cache_id = selected_id
|
|
ops_row.operator(
|
|
RenameLayer.bl_idname, text="Name", icon="FONT_DATA"
|
|
).cache_id = selected_id
|
|
ops_row.operator(
|
|
DeleteLayer.bl_idname, text="", icon="TRASH"
|
|
).cache_id = selected_id
|
|
else:
|
|
ops_row.operator(
|
|
RelocatePath.bl_idname, text="Relocate", icon="FILE_FOLDER"
|
|
).cache_id = selected_id
|
|
ops_row.operator(
|
|
DeleteLayer.bl_idname, text="", icon="TRASH"
|
|
).cache_id = selected_id
|
|
|
|
meta_box = layout.box()
|
|
if hasattr(meta_box, "alert"):
|
|
meta_box.alert = True
|
|
meta_box.label(text=f"Cache is missing...")
|
|
meta_box.label(text=f"Please relocate the cache path!")
|
|
return
|
|
|
|
meta_box = layout.box()
|
|
meta_box.label(text=f"Name: {name}")
|
|
meta_box.label(text=f"Path: {path}")
|
|
meta_box.label(text=f"Kind: {kind}")
|
|
meta_box.label(
|
|
text=f"Dims: [{entry.get('frame_count',1)},{tuple(entry.get('shape',''))},{entry.get('channel_count',1)}]"
|
|
)
|
|
if kind in ["scalar", "color", "vector"]:
|
|
path = Path(bpy.path.abspath(entry["path"]))
|
|
histogram = load_icon(
|
|
path / "histogram.png", f"{cache_id}_histogram")
|
|
meta_box.template_icon(histogram, scale=10.0)
|
|
else:
|
|
layout.label(text="No layer selected", icon="QUESTION")
|