EG/ui/panels/editor_panels_right_material.py

364 lines
16 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from imgui_bundle import imgui, imgui_ctx
class EditorPanelsRightMaterialMixin:
"""Auto-split mixin from editor_panels_right.py."""
def _ensure_material_edit_sessions(self):
if not hasattr(self, "_material_edit_sessions"):
self._material_edit_sessions = {}
return self._material_edit_sessions
def _sync_material_panel_target(self, node):
"""Reset per-node material UI state when selection changes."""
target_key = None
try:
target_key = getattr(node, "this", None) or id(node)
except Exception:
target_key = id(node)
if getattr(self, "_material_panel_target_key", None) == target_key:
return
self._material_panel_target_key = target_key
self._material_edit_sessions = {}
property_helpers = getattr(self.app, "property_helpers", None)
ensure_unique_fn = getattr(property_helpers, "_ensure_unique_materials_for_node", None)
if callable(ensure_unique_fn):
ensure_unique_fn(node)
def _ensure_node_materials_are_editable(self, node):
self._sync_material_panel_target(node)
ensure_unique_fn = getattr(self.app, "_ensure_unique_materials_for_node", None)
if callable(ensure_unique_fn):
try:
ensure_unique_fn(node)
except Exception:
pass
panel_materials_fn = getattr(self.app, "_get_panel_edit_materials_for_node", None)
if callable(panel_materials_fn):
try:
materials = panel_materials_fn(node)
if materials:
return materials
except Exception:
pass
return self.app._get_node_materials(node)
def _refresh_ssbo_runtime_for_material_node(self, node, preserve_selection=True):
ssbo_editor = getattr(self.app, "ssbo_editor", None)
if not ssbo_editor:
return
if not hasattr(ssbo_editor, "is_source_tree_node") or not ssbo_editor.is_source_tree_node(node):
return
if hasattr(ssbo_editor, "refresh_runtime_from_source"):
try:
ssbo_editor.refresh_runtime_from_source(preserve_selection=preserve_selection)
except Exception:
pass
def _begin_material_edit_session(self, node, session_key):
self._ensure_node_materials_are_editable(node)
sessions = self._ensure_material_edit_sessions()
sessions.setdefault(session_key, self.app._capture_node_material_snapshot(node))
def _record_material_snapshot_command(self, node, before_snapshot, after_snapshot):
if not hasattr(self.app, "command_manager") or not self.app.command_manager:
return
if self.app._material_snapshots_equal(before_snapshot, after_snapshot):
return
try:
from core.Command_System import MaterialStateCommand
self.app.command_manager.record_command(
MaterialStateCommand(
lambda state, target_node=node: self.app._apply_node_material_snapshot(target_node, state),
before_snapshot,
after_snapshot,
)
)
except Exception:
pass
def _finish_material_edit_session(self, node, session_key):
if not imgui.is_item_deactivated_after_edit():
return
sessions = self._ensure_material_edit_sessions()
before_snapshot = sessions.pop(session_key, None)
if before_snapshot is None:
return
after_snapshot = self.app._capture_node_material_snapshot(node)
self._record_material_snapshot_command(node, before_snapshot, after_snapshot)
self._refresh_ssbo_runtime_for_material_node(node)
def _apply_material_change_with_history(self, node, apply_callback):
self._ensure_node_materials_are_editable(node)
before_snapshot = self.app._capture_node_material_snapshot(node)
apply_callback()
after_snapshot = self.app._capture_node_material_snapshot(node)
self._record_material_snapshot_command(node, before_snapshot, after_snapshot)
self._refresh_ssbo_runtime_for_material_node(node)
def _select_texture_for_material_with_history(self, node, material, texture_type):
self._ensure_node_materials_are_editable(node)
before_snapshot = self.app._capture_node_material_snapshot(node)
changed = self._select_texture_for_material(node, material, texture_type)
if not changed:
return False
after_snapshot = self.app._capture_node_material_snapshot(node)
self._record_material_snapshot_command(node, before_snapshot, after_snapshot)
self._refresh_ssbo_runtime_for_material_node(node)
return True
def _draw_appearance_properties(self, node):
"""绘制材质属性Unity风格主材质入口"""
materials = self._ensure_node_materials_are_editable(node)
if not materials:
fallback_material = self.app._ensure_material_for_node(node)
materials = [fallback_material] if fallback_material else []
if not materials:
imgui.text_colored((1.0, 0.5, 0.5, 1.0), "无法获取材质")
return
material = materials[0]
# 历史上可能通过 node.setColor 留下了额外染色,先清掉避免与材质主颜色打架
try:
if node.hasColor():
node.clearColor()
if hasattr(node, "clearColorScale"):
node.clearColorScale()
except Exception:
pass
base_color = self.app._get_material_base_color(material)
def apply_primary_color(color):
for current_material in materials:
self.app._set_material_base_color(current_material, color)
if self.app._get_material_surface_type(current_material) == 3:
self.app._set_material_opacity(node, current_material, color[3])
else:
self.app._sync_material_node_runtime(node, current_material, refresh_ssbo_runtime=False)
self._refresh_ssbo_runtime_for_material_node(node)
def apply_surface_type(surface_type):
for current_material in materials:
self.app._set_material_surface_type(
node,
current_material,
surface_type,
refresh_pipeline=False,
)
if materials:
self.app._apply_material_surface_state(node, materials[0])
self.app._refresh_pipeline_material_mode(node, materials[0])
def apply_opacity(opacity):
for current_material in materials:
self.app._set_material_opacity(node, current_material, opacity)
imgui.text("主颜色")
changed, new_color = imgui.color_edit4(
"##material_base_color",
base_color,
imgui.ColorEditFlags_.display_rgb.value,
)
if imgui.is_item_activated():
self._begin_material_edit_session(node, "appearance_primary_color")
if changed:
apply_primary_color(new_color)
self._finish_material_edit_session(node, "appearance_primary_color")
imgui.same_line()
if imgui.button("颜色选择器##material_color_picker"):
self.show_color_picker(
target_object=None,
property_name=None,
initial_color=base_color,
callback=lambda color: self._apply_material_change_with_history(
node,
lambda: apply_primary_color(color),
),
)
surface_options = [
("不透明", 0),
("自发光", 1),
("透明", 3),
]
current_surface = self.app._get_material_surface_type(material)
current_surface_index = next(
(index for index, (_, value) in enumerate(surface_options) if value == current_surface),
0,
)
imgui.text("表面类型")
changed, selected_index = imgui.combo(
"##material_surface_type",
current_surface_index,
[label for label, _ in surface_options],
)
if changed:
self._apply_material_change_with_history(
node,
lambda: apply_surface_type(surface_options[selected_index][1]),
)
current_surface = surface_options[selected_index][1]
if self.app._get_material_surface_type(material) == 3:
opacity = self.app._get_material_opacity(material)
transparency = 1.0 - opacity
changed, new_transparency = imgui.slider_float("透明度", transparency, 0.0, 1.0)
if imgui.is_item_activated():
self._begin_material_edit_session(node, "appearance_opacity")
if changed:
apply_opacity(1.0 - new_transparency)
self._finish_material_edit_session(node, "appearance_opacity")
imgui.separator()
# 详细材质属性
self._draw_material_properties(node)
def _draw_material_properties(self, node):
"""绘制材质属性"""
materials = self._ensure_node_materials_are_editable(node)
if not materials:
imgui.text_colored((0.5, 0.5, 0.5, 1.0), "无材质")
return
for i, material in enumerate(materials):
material_name = material.get_name() if hasattr(material, 'get_name') and material.get_name() else f"材质{i + 1}"
if imgui.collapsing_header(f"材质: {material_name}"):
# PBR属性
imgui.text("PBR")
if hasattr(material, 'roughness') and material.roughness is not None:
try:
roughness_value = float(material.roughness)
changed, new_roughness = imgui.slider_float(f"粗糙度##rough_{i}", roughness_value, 0.0, 1.0)
if imgui.is_item_activated():
self._begin_material_edit_session(node, f"material_{i}_roughness")
if changed:
self._update_material_roughness(material, new_roughness, node)
self._finish_material_edit_session(node, f"material_{i}_roughness")
except:
imgui.text_colored((0.7, 0.7, 0.7, 1.0), "粗糙度: 不可用")
if hasattr(material, 'metallic') and material.metallic is not None:
try:
metallic_value = float(material.metallic)
changed, new_metallic = imgui.slider_float(f"金属性##metal_{i}", metallic_value, 0.0, 1.0)
if imgui.is_item_activated():
self._begin_material_edit_session(node, f"material_{i}_metallic")
if changed:
self._update_material_metallic(material, new_metallic, node)
self._finish_material_edit_session(node, f"material_{i}_metallic")
except:
imgui.text_colored((0.7, 0.7, 0.7, 1.0), "金属性: 不可用")
if hasattr(material, 'refractive_index') and material.refractive_index is not None:
try:
ior_value = float(material.refractive_index)
changed, new_ior = imgui.slider_float(f"折射率##ior_{i}", ior_value, 1.0, 3.0)
if imgui.is_item_activated():
self._begin_material_edit_session(node, f"material_{i}_ior")
if changed:
self._update_material_ior(material, new_ior, node)
self._finish_material_edit_session(node, f"material_{i}_ior")
except:
imgui.text_colored((0.7, 0.7, 0.7, 1.0), "折射率: 不可用")
# 材质预设
imgui.text("材质预设")
presets = ["默认", "金属", "塑料", "玻璃", "木材", "混凝土"]
current_preset = 0 # 默认选择
if imgui.begin_combo(f"预设##preset_{i}", presets[current_preset]):
for j, preset_name in enumerate(presets):
if imgui.selectable(preset_name, j == current_preset):
self._apply_material_change_with_history(
node,
lambda selected_preset=preset_name: (
self._apply_material_preset(material, selected_preset, node),
self._apply_material_surface_state(node, material),
self.app._refresh_pipeline_material_mode(node, material),
),
)
imgui.end_combo()
# 纹理信息
imgui.text("纹理贴图")
if imgui.button(f"选择漫反射贴图##diffuse_{i}"):
self._select_texture_for_material_with_history(node, material, "diffuse")
imgui.same_line()
if imgui.button(f"选择法线贴图##normal_{i}"):
self._select_texture_for_material_with_history(node, material, "normal")
imgui.same_line()
if imgui.button(f"选择粗糙度贴图##roughness_{i}"):
self._select_texture_for_material_with_history(node, material, "roughness")
if imgui.button(f"选择金属性贴图##metallic_{i}"):
self._select_texture_for_material_with_history(node, material, "metallic")
imgui.same_line()
if imgui.button(f"选择自发光贴图##emission_{i}"):
self._select_texture_for_material_with_history(node, material, "emission")
imgui.same_line()
if imgui.button(f"清除所有贴图##clear_{i}"):
self._apply_material_change_with_history(node, lambda: self._clear_all_textures(node))
# 显示当前纹理信息
self._display_current_textures(node, material)
imgui.separator()
if imgui.button("应用材质"):
self._apply_material_to_node(node)
self._refresh_ssbo_runtime_for_material_node(node)
imgui.same_line()
if imgui.button("重置材质"):
self._apply_material_change_with_history(node, lambda: self._reset_material(node))
def _draw_shading_model_panel(self, node, material, material_index):
"""绘制着色模型选择面板"""
try:
imgui.text("着色模型")
shading_models = [
("默认", 0),
("自发光", 1),
("透明", 3),
]
current_model = self.app._get_material_surface_type(material)
current_index = next((idx for idx, (_, value) in enumerate(shading_models) if value == current_model), 0)
if imgui.begin_combo(f"着色模型##shading_{material_index}", shading_models[current_index][0]):
for index, (model_name, model_value) in enumerate(shading_models):
if imgui.selectable(model_name, index == current_index):
self.app._set_material_surface_type(node, material, model_value)
imgui.end_combo()
if self.app._get_material_surface_type(material) == 3:
imgui.text("透明度设置")
try:
current_opacity = self.app._get_material_opacity(material)
current_transparency = 1.0 - current_opacity
changed, new_transparency = imgui.slider_float(
f"透明度##opacity_{material_index}",
current_transparency,
0.0,
1.0,
)
if changed:
self.app._set_material_opacity(node, material, 1.0 - new_transparency)
except:
imgui.text_colored((0.7, 0.7, 0.7, 1.0), "透明度控制不可用")
except Exception as e:
print(f"绘制着色模型面板失败: {e}")