Use commands for reset transform

This commit is contained in:
ayuan9957 2026-03-13 14:44:00 +08:00
parent 8a80af9825
commit 7e6b1d54d9
5 changed files with 161 additions and 70 deletions

View File

@ -25,25 +25,25 @@ Collapsed=0
[Window][工具栏]
Pos=354,20
Size=1846,32
Size=1334,32
Collapsed=0
DockId=0x0000000D,0
[Window][场景树]
Pos=0,20
Size=352,995
Size=352,748
Collapsed=0
DockId=0x00000007,0
[Window][属性面板]
Pos=2202,20
Size=358,995
Pos=1690,20
Size=358,748
Collapsed=0
DockId=0x00000002,0
[Window][控制台]
Pos=2202,20
Size=358,995
Pos=1690,20
Size=358,748
Collapsed=0
DockId=0x00000002,1
@ -60,7 +60,7 @@ Collapsed=0
[Window][WindowOverViewport_11111111]
Pos=0,20
Size=2560,1331
Size=2048,1084
Collapsed=0
[Window][测试窗口1]
@ -99,8 +99,8 @@ Size=600,500
Collapsed=0
[Window][资源管理器]
Pos=0,1017
Size=2560,334
Pos=0,770
Size=2048,334
Collapsed=0
DockId=0x00000006,0
@ -207,7 +207,7 @@ Collapsed=0
DockId=0x00000002,2
[Docking][Data]
DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=0,20 Size=2560,1331 Split=Y
DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=0,20 Size=2048,1084 Split=Y
DockNode ID=0x00000005 Parent=0x08BD597D SizeRef=2560,995 Split=X
DockNode ID=0x00000007 Parent=0x00000005 SizeRef=352,1084 Selected=0xE0015051
DockNode ID=0x00000008 Parent=0x00000005 SizeRef=2206,1084 Split=X

View File

@ -726,9 +726,30 @@ class EditorPanelsRightMixin(
"""绘制属性操作按钮"""
# 重置变换
if imgui.button("重置变换"):
node.setPos(0, 0, 0)
node.setHpr(0, 0, 0)
node.setScale(1, 1, 1)
if hasattr(self.app, "command_manager") and self.app.command_manager:
from core.Command_System import (
CompositeCommand,
MoveNodeCommand,
RotateNodeCommand,
ScaleNodeCommand,
)
current_pos = tuple(float(v) for v in node.getPos())
current_hpr = tuple(float(v) for v in node.getHpr())
current_scale = tuple(float(v) for v in node.getScale())
self.app.command_manager.execute_command(
CompositeCommand(
[
MoveNodeCommand(node, current_pos, (0.0, 0.0, 0.0)),
RotateNodeCommand(node, current_hpr, (0.0, 0.0, 0.0)),
ScaleNodeCommand(node, current_scale, (1.0, 1.0, 1.0)),
]
)
)
else:
node.setPos(0, 0, 0)
node.setHpr(0, 0, 0)
node.setScale(1, 1, 1)
imgui.same_line()
@ -736,10 +757,7 @@ class EditorPanelsRightMixin(
is_visible = not node.is_hidden()
visibility_text = "隐藏" if is_visible else "显示"
if imgui.button(visibility_text):
if is_visible:
node.hide()
else:
node.show()
self._record_visibility_change(node, is_visible, not is_visible)
imgui.same_line()

View File

@ -47,6 +47,15 @@ class EditorPanelsRightMaterialMixin:
after_snapshot = self.app._capture_node_material_snapshot(node)
self._record_material_snapshot_command(node, before_snapshot, after_snapshot)
def _select_texture_for_material_with_history(self, node, material, texture_type):
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)
return True
def _draw_appearance_properties(self, node):
"""绘制材质属性Unity风格主材质入口"""
materials = self.app._get_node_materials(node)
@ -228,26 +237,26 @@ class EditorPanelsRightMaterialMixin:
# 纹理信息
imgui.text("纹理贴图")
if imgui.button(f"选择漫反射贴图##diffuse_{i}"):
self._select_texture_for_material(node, material, "diffuse")
self._select_texture_for_material_with_history(node, material, "diffuse")
imgui.same_line()
if imgui.button(f"选择法线贴图##normal_{i}"):
self._select_texture_for_material(node, material, "normal")
self._select_texture_for_material_with_history(node, material, "normal")
imgui.same_line()
if imgui.button(f"选择粗糙度贴图##roughness_{i}"):
self._select_texture_for_material(node, material, "roughness")
self._select_texture_for_material_with_history(node, material, "roughness")
if imgui.button(f"选择金属性贴图##metallic_{i}"):
self._select_texture_for_material(node, material, "metallic")
self._select_texture_for_material_with_history(node, material, "metallic")
imgui.same_line()
if imgui.button(f"选择自发光贴图##emission_{i}"):
self._select_texture_for_material(node, material, "emission")
self._select_texture_for_material_with_history(node, material, "emission")
imgui.same_line()
if imgui.button(f"清除所有贴图##clear_{i}"):
self._clear_all_textures(node)
self._apply_material_change_with_history(node, lambda: self._clear_all_textures(node))
# 显示当前纹理信息
self._display_current_textures(node, material)

View File

@ -50,6 +50,12 @@ class InteractionPanels:
if imgui.menu_item("重命名", "", False, True)[1]:
self._renaming_node = True
if hasattr(self, "_context_menu_target") and self._context_menu_target:
self._rename_target = self._context_menu_target
self._rename_buffer = self._context_menu_target.getName() or ""
else:
self._rename_target = None
self._rename_buffer = ""
imgui.close_current_popup()
imgui.separator()
@ -71,10 +77,6 @@ class InteractionPanels:
# 重命名对话框
if hasattr(self, '_renaming_node') and self._renaming_node:
imgui.open_popup("重命名节点")
if not hasattr(self, '_rename_buffer'):
self._rename_buffer = ""
if hasattr(self, '_context_menu_target') and self._context_menu_target:
self._rename_buffer = self._context_menu_target.getName() or ""
if imgui.begin_popup("重命名节点"):
changed, new_name = imgui.input_text("新名称", self._rename_buffer, 256)
@ -82,14 +84,28 @@ class InteractionPanels:
self._rename_buffer = new_name
if imgui.button("确定"):
if hasattr(self, '_context_menu_target') and self._context_menu_target:
self._context_menu_target.setName(self._rename_buffer)
target = getattr(self, "_rename_target", None) or getattr(self, "_context_menu_target", None)
if target and not target.isEmpty():
old_name = target.getName() or ""
new_name = (self._rename_buffer or "").strip()
if not new_name:
new_name = old_name
if hasattr(self, "editor_panels") and self.editor_panels:
self.editor_panels._record_name_change(target, old_name, new_name)
elif hasattr(self, "_update_node_name"):
self._update_node_name(target, new_name)
else:
target.setName(new_name)
self._renaming_node = False
self._rename_target = None
self._rename_buffer = ""
imgui.close_current_popup()
imgui.same_line()
if imgui.button("取消"):
self._renaming_node = False
self._rename_target = None
self._rename_buffer = ""
imgui.close_current_popup()
imgui.end_popup()
@ -99,38 +115,17 @@ class InteractionPanels:
"""删除节点 - 简化版本"""
if not node or node.isEmpty():
return
# 从场景管理器中删除
if hasattr(self, 'scene_manager') and self.scene_manager:
if hasattr(self.scene_manager, 'models') and node in self.scene_manager.models:
self.scene_manager.models.remove(node)
# 从GUI管理器中删除
if hasattr(self, 'gui_manager') and self.gui_manager:
gui_element = None
if hasattr(node, 'getPythonTag'):
gui_element = node.getPythonTag('gui_element')
if gui_element and hasattr(self.gui_manager, 'gui_elements'):
if gui_element in self.gui_manager.gui_elements:
self.gui_manager.gui_elements.remove(gui_element)
# 使用主删除方法
self._delete_node(node)
# 获取节点名称(在删除之前)
node_name = node.getName() if not node.isEmpty() else '未命名节点'
# 删除节点本身
node.removeNode()
# 清除选择
if hasattr(self, 'selection') and self.selection:
node_name = node.getName() or "未命名节点"
deleted = self._delete_node(node)
if deleted and hasattr(self, 'selection') and self.selection:
current_node = self.selection.getSelectedNode() if hasattr(self.selection, "getSelectedNode") else self.selection.selectedNode
if current_node == node:
self.selection.clearSelection()
# 添加成功消息
self.add_success_message(f"已删除节点: {node_name}")
if deleted:
self.add_success_message(f"已删除节点: {node_name}")
def _copy_node(self, node):

View File

@ -850,7 +850,7 @@ class PropertyHelpers:
fallback_material = self._ensure_material_for_node(node)
materials = [fallback_material] if fallback_material else []
snapshot = []
material_entries = []
for material in materials:
emission = None
try:
@ -872,14 +872,47 @@ class PropertyHelpers:
"ior": float(material.refractive_index) if hasattr(material, "refractive_index") and material.refractive_index is not None else None,
"emission": emission,
}
snapshot.append(entry)
return snapshot
material_entries.append(entry)
texture_tags = {}
for texture_type in self._get_material_texture_slots().keys():
tag_name = f"material_texture_{texture_type}"
if node and hasattr(node, "hasTag") and node.hasTag(tag_name):
texture_tags[texture_type] = node.getTag(tag_name)
effect_tags = {}
for tag_name in (
"material_effect_metallic_enabled",
"material_effect_default_texture_enabled",
"material_effect_parallax_enabled",
):
effect_tags[tag_name] = bool(node and hasattr(node, "hasTag") and node.hasTag(tag_name))
return {
"materials": material_entries,
"node_state": {
"textures": texture_tags,
"effect_tags": effect_tags,
},
}
def _normalize_material_snapshot(self, snapshot):
if isinstance(snapshot, list):
return {"materials": snapshot, "node_state": {"textures": {}, "effect_tags": {}}}
if isinstance(snapshot, dict):
return {
"materials": snapshot.get("materials", []) or [],
"node_state": snapshot.get("node_state", {}) or {"textures": {}, "effect_tags": {}},
}
return {"materials": [], "node_state": {"textures": {}, "effect_tags": {}}}
def _material_snapshots_equal(self, before_snapshot, after_snapshot):
"""Compare two material snapshots with a small float tolerance."""
before_snapshot = before_snapshot or []
after_snapshot = after_snapshot or []
if len(before_snapshot) != len(after_snapshot):
before_snapshot = self._normalize_material_snapshot(before_snapshot)
after_snapshot = self._normalize_material_snapshot(after_snapshot)
before_materials = before_snapshot.get("materials", [])
after_materials = after_snapshot.get("materials", [])
if len(before_materials) != len(after_materials):
return False
def _rounded_tuple(values):
@ -887,7 +920,7 @@ class PropertyHelpers:
return None
return tuple(round(float(value), 6) for value in values)
for before_entry, after_entry in zip(before_snapshot, after_snapshot):
for before_entry, after_entry in zip(before_materials, after_materials):
before_material = before_entry.get("material")
after_material = after_entry.get("material")
before_key = getattr(before_material, "this", None) or id(before_material)
@ -909,6 +942,13 @@ class PropertyHelpers:
continue
if round(float(before_value), 6) != round(float(after_value), 6):
return False
before_node_state = before_snapshot.get("node_state", {}) or {}
after_node_state = after_snapshot.get("node_state", {}) or {}
if (before_node_state.get("textures", {}) or {}) != (after_node_state.get("textures", {}) or {}):
return False
if (before_node_state.get("effect_tags", {}) or {}) != (after_node_state.get("effect_tags", {}) or {}):
return False
return True
def _apply_node_material_snapshot(self, node, snapshot):
@ -919,8 +959,11 @@ class PropertyHelpers:
if not node or node.isEmpty():
return
snapshot = snapshot or []
for entry in snapshot:
snapshot = self._normalize_material_snapshot(snapshot)
materials_snapshot = snapshot.get("materials", [])
node_state = snapshot.get("node_state", {}) or {}
for entry in materials_snapshot:
material = entry.get("material")
if material is None:
continue
@ -948,7 +991,26 @@ class PropertyHelpers:
self._apply_material_to_geom_states(node, material)
self._apply_material_surface_state(node, material)
for entry in snapshot:
self._clear_all_textures(node)
texture_tags = node_state.get("textures", {}) or {}
primary_material = None
if materials_snapshot:
primary_material = materials_snapshot[0].get("material")
if primary_material is None:
primary_material = self._ensure_material_for_node(node)
texture_slots = self._get_material_texture_slots()
for texture_type, texture_path in sorted(texture_tags.items(), key=lambda item: texture_slots.get(item[0], 999)):
if texture_path:
self._apply_texture_to_material(node, primary_material, texture_type, texture_path)
for tag_name, enabled in (node_state.get("effect_tags", {}) or {}).items():
if enabled:
node.setTag(tag_name, "1")
elif node.hasTag(tag_name):
node.clearTag(tag_name)
for entry in materials_snapshot:
material = entry.get("material")
if material is not None:
self._refresh_pipeline_material_mode(node, material)
@ -1706,12 +1768,14 @@ class PropertyHelpers:
if selected_path and os.path.exists(selected_path):
self._texture_dialog_path = os.path.dirname(selected_path)
self._apply_texture_to_material(node, material, texture_type, selected_path)
return bool(self._apply_texture_to_material(node, material, texture_type, selected_path))
else:
print(f"已取消选择{texture_type}贴图")
return False
except Exception as e:
print(f"选择纹理失败: {e}")
return False
def _get_material_texture_slots(self):
@ -2040,12 +2104,15 @@ class PropertyHelpers:
# 记录路径,便于 UI 展示和后续恢复
if texture_path:
node.setTag(f"material_texture_{texture_type}", os.path.normpath(normalized_path or texture_path))
stable_texture_path = os.path.normpath(os.path.abspath(texture_path))
node.setTag(f"material_texture_{texture_type}", stable_texture_path)
print(f"已应用{texture_type}纹理到槽位 p3d_Texture{slot}: {texture_path}")
return True
except Exception as e:
print(f"应用纹理失败: {e}")
return False
def _clear_all_textures(self, node):
@ -2072,8 +2139,10 @@ class PropertyHelpers:
node.clearTag("material_effect_parallax_enabled")
print("已清除所有纹理")
return True
except Exception as e:
print(f"清除纹理失败: {e}")
return False
def _display_current_textures(self, node, material):