From 7e6b1d54d99338e1a8f93c2438ea36bea9b95bb3 Mon Sep 17 00:00:00 2001 From: ayuan9957 <107920784+ayuan9957@users.noreply.github.com> Date: Fri, 13 Mar 2026 14:44:00 +0800 Subject: [PATCH] Use commands for reset transform --- imgui.ini | 20 ++--- ui/panels/editor_panels_right.py | 32 ++++++-- ui/panels/editor_panels_right_material.py | 21 +++-- ui/panels/interaction_panels.py | 65 ++++++++-------- ui/panels/property_helpers.py | 93 ++++++++++++++++++++--- 5 files changed, 161 insertions(+), 70 deletions(-) diff --git a/imgui.ini b/imgui.ini index e089d092..4deebaaf 100644 --- a/imgui.ini +++ b/imgui.ini @@ -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 diff --git a/ui/panels/editor_panels_right.py b/ui/panels/editor_panels_right.py index 0114ec68..75397757 100644 --- a/ui/panels/editor_panels_right.py +++ b/ui/panels/editor_panels_right.py @@ -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() diff --git a/ui/panels/editor_panels_right_material.py b/ui/panels/editor_panels_right_material.py index 0a8df3aa..03b5a1d4 100644 --- a/ui/panels/editor_panels_right_material.py +++ b/ui/panels/editor_panels_right_material.py @@ -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) diff --git a/ui/panels/interaction_panels.py b/ui/panels/interaction_panels.py index 0580ff84..ee784bc2 100644 --- a/ui/panels/interaction_panels.py +++ b/ui/panels/interaction_panels.py @@ -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): diff --git a/ui/panels/property_helpers.py b/ui/panels/property_helpers.py index d3ec08c4..870d4e8e 100644 --- a/ui/panels/property_helpers.py +++ b/ui/panels/property_helpers.py @@ -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):