优化项目资源结构和保存项目

This commit is contained in:
ayuan9957 2026-03-19 10:16:03 +08:00
parent 776d3c01aa
commit 4ef6ce97d4
9 changed files with 135 additions and 12 deletions

View File

@ -10,7 +10,7 @@
"importer": "model_importer",
"import_settings": {},
"dependency_guids": [],
"updated_at": "2026-03-18 18:31:18",
"updated_at": "2026-03-19 10:14:56",
"imported_cache": {
"root": "Library/Imported/90ece77e67b54ccda38d2de71cb4694d",
"model_bam": "Library/Imported/90ece77e67b54ccda38d2de71cb4694d/model.bam",

View File

@ -3,5 +3,5 @@
"asset_path": "Assets/Models/box1.glb",
"asset_type": "model",
"source_hash": "fc694905c47a9b0005d77b701cc41852b56ef08c7406829a306e98f3ce158a64",
"generated_at": "2026-03-18 18:31:18"
"generated_at": "2026-03-19 10:14:56"
}

View File

@ -14,13 +14,22 @@ import time
class ResourceManager:
"""ImGui资源管理器类"""
DEFAULT_ASSET_SUBDIRS = (
"Models",
"Textures",
"Audio",
"Video",
"UI",
"Scripts",
)
def __init__(self, world):
self.world = world
self.project_root = Path(__file__).resolve().parent.parent
self.project_path: Optional[Path] = None
# 当前浏览路径默认从Resources目录开始
# 当前浏览路径,默认从统一的 Assets 结构开始
self.current_path = self._get_default_root_path()
# 历史记录,用于前进后退导航
@ -63,15 +72,29 @@ class ResourceManager:
assets_root = Path(current_project_path) / "Assets"
elif self.project_path:
assets_root = self.project_path / "Assets"
else:
assets_root = self.project_root / "Assets"
if assets_root and assets_root.exists():
return assets_root.resolve()
if assets_root:
self._ensure_assets_structure(assets_root)
if assets_root.exists():
return assets_root.resolve()
legacy_resources = self.project_root / "Resources"
if legacy_resources.exists():
return legacy_resources.resolve()
return self.project_root.resolve()
def _ensure_assets_structure(self, assets_root: Path):
"""确保资源浏览器默认使用统一的 Assets 目录结构。"""
try:
assets_root.mkdir(parents=True, exist_ok=True)
for subdir in self.DEFAULT_ASSET_SUBDIRS:
(assets_root / subdir).mkdir(parents=True, exist_ok=True)
except OSError:
# 目录创建失败时,保留后续兼容回退逻辑。
pass
def set_project_path(self, project_path: str):
self.project_path = Path(project_path).resolve() if project_path else None
self.current_path = self._get_default_root_path()

View File

@ -189,6 +189,9 @@ class SelectionSystem:
node = ssbo_editor.get_selection_scene_node()
if self._is_valid_node(node, require_attached=True):
return node
fallback_model = getattr(ssbo_editor, "model", None)
if self._is_valid_node(fallback_model, require_attached=True):
return fallback_model
return None
except Exception:
pass

View File

@ -84,12 +84,12 @@ Size=400,300
Collapsed=0
[Window][选择路径]
Pos=724,302
Pos=980,446
Size=600,500
Collapsed=0
[Window][打开项目]
Pos=774,352
Pos=1030,496
Size=500,400
Collapsed=0
@ -206,7 +206,7 @@ Collapsed=0
DockId=0x00000005,2
[Window][项目另存为]
Pos=794,432
Pos=1050,576
Size=460,240
Collapsed=0
@ -220,7 +220,7 @@ DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=0,20 Size=2048,1084 Split=
DockNode ID=0x00000001 Parent=0x08BD597D SizeRef=1525,1012 Split=X
DockNode ID=0x00000007 Parent=0x00000001 SizeRef=274,1084 Selected=0xE0015051
DockNode ID=0x00000008 Parent=0x00000001 SizeRef=1249,1084 Split=Y
DockNode ID=0x00000005 Parent=0x00000008 SizeRef=2048,610 Split=Y
DockNode ID=0x00000005 Parent=0x00000008 SizeRef=2048,970 Split=Y
DockNode ID=0x0000000D Parent=0x00000005 SizeRef=1318,383 HiddenTabBar=1 Selected=0x43A39006
DockNode ID=0x0000000E Parent=0x00000005 SizeRef=1318,363 CentralNode=1 Selected=0xE0015051
DockNode ID=0x00000006 Parent=0x00000008 SizeRef=2048,400 Selected=0x3A2E05C3

View File

@ -899,6 +899,50 @@ class SSBOEditor:
self._rebuild_or_clear_runtime_from_current_source()
return target
def delete_selected_source_node(self):
"""Delete the current SSBO selection from source_model_root and rebuild runtime."""
if not self.controller or self.selected_name is None:
return False
target = self._resolve_source_node_by_tree_key(self.selected_name)
if not self._node_is_valid(target):
return False
source_root = self._ensure_source_model_root()
if target == source_root:
children = self._get_source_root_children()
if not children:
return False
if self.controller and self.model:
self._snapshot_top_level_transforms_to_source_root()
self._clear_runtime_state(preserve_source_models=True)
for child in children:
try:
child.detach_node()
except Exception:
try:
child.detachNode()
except Exception:
continue
self.clear_selection(sync_world_selection=False)
self._rebuild_or_clear_runtime_from_current_source()
return True
if self.controller and self.model:
self._snapshot_top_level_transforms_to_source_root()
self._clear_runtime_state(preserve_source_models=True)
try:
target.detach_node()
except Exception:
try:
target.detachNode()
except Exception:
return False
self.clear_selection(sync_world_selection=False)
self._rebuild_or_clear_runtime_from_current_source()
return True
def attach_source_child(self, child_np, highlight_root_name=None):
if not self._node_is_valid(child_np):
return None

View File

@ -1003,6 +1003,42 @@ class AppActions:
ssbo_editor = getattr(self, "ssbo_editor", None)
ssbo_model = getattr(ssbo_editor, "model", None) if ssbo_editor else None
deleting_ssbo_root = bool(ssbo_model and (node == ssbo_model))
deleting_ssbo_selection = bool(
ssbo_editor and
hasattr(ssbo_editor, "has_active_selection") and
ssbo_editor.has_active_selection()
)
if deleting_ssbo_selection and hasattr(ssbo_editor, "delete_selected_source_node"):
deleted = bool(ssbo_editor.delete_selected_source_node())
if deleted:
scene_manager = getattr(self, "scene_manager", None)
if scene_manager and hasattr(scene_manager, "models"):
rebuilt_model = getattr(ssbo_editor, "model", None)
scene_manager.models = []
if rebuilt_model and not rebuilt_model.isEmpty():
scene_manager.models.append(rebuilt_model)
print(f"[SSBO] 已从源场景树删除当前选择并重建: {node_name}")
return True
if deleting_ssbo_root and ssbo_editor and hasattr(ssbo_editor, "detach_source_child"):
detached = ssbo_editor.detach_source_child(child_name=node_name)
scene_manager = getattr(self, "scene_manager", None)
if scene_manager and hasattr(scene_manager, "models"):
scene_manager.models = [
model for model in scene_manager.models
if model and not model.isEmpty() and model != node
]
rebuilt_model = getattr(ssbo_editor, "model", None)
if rebuilt_model and not rebuilt_model.isEmpty() and rebuilt_model not in scene_manager.models:
scene_manager.models.append(rebuilt_model)
if detached is not None:
try:
ssbo_editor.clear_selection(sync_world_selection=False)
except Exception:
pass
print(f"[SSBO] 已从源模型树移除并重建运行时: {node_name}")
return True
# 创建删除命令
if hasattr(self, 'command_manager') and self.command_manager:

View File

@ -212,9 +212,11 @@ class EditorPanelsLeftMixin:
for i, model in enumerate(models):
if ssbo_model and controller and model == ssbo_model:
for child_key in self._get_ssbo_top_level_model_keys():
self._draw_ssbo_virtual_tree_node(ssbo_editor, controller, child_key)
continue
top_level_keys = self._get_ssbo_top_level_model_keys()
if top_level_keys:
for child_key in top_level_keys:
self._draw_ssbo_virtual_tree_node(ssbo_editor, controller, child_key)
continue
self._draw_scene_node(model, model.getName() or f"模型_{i + 1}", "model")
imgui.tree_pop()
elif not render_entries and not camera_entries and not model_display_count and self._get_scene_tree_filter():
@ -403,6 +405,12 @@ class EditorPanelsLeftMixin:
ssbo_editor.select_node(key)
if hasattr(self.app, "lui_manager"):
self.app.lui_manager.selected_index = -1
if imgui.is_item_hovered() and imgui.is_mouse_clicked(1):
try:
ssbo_editor.select_node(key)
except Exception:
pass
self._show_node_context_menu(getattr(ssbo_editor, "model", None), display, "model")
else:
# Branch node: tree node
flags = imgui.TreeNodeFlags_.open_on_arrow
@ -414,6 +422,12 @@ class EditorPanelsLeftMixin:
ssbo_editor.select_node(key)
if hasattr(self.app, "lui_manager"):
self.app.lui_manager.selected_index = -1
if imgui.is_item_hovered() and imgui.is_mouse_clicked(1):
try:
ssbo_editor.select_node(key)
except Exception:
pass
self._show_node_context_menu(getattr(ssbo_editor, "model", None), display, "model")
if opened:
for child_key in children:
self._draw_ssbo_virtual_tree_node(ssbo_editor, controller, child_key)

View File

@ -30,6 +30,9 @@ class PanelDelegates:
ssbo_node = ssbo_editor.get_selection_scene_node()
if ssbo_node and self._node_is_valid(ssbo_node, require_attached=False):
return ssbo_node
fallback_model = getattr(ssbo_editor, "model", None)
if fallback_model and self._node_is_valid(fallback_model, require_attached=False):
return fallback_model
return None
selection = getattr(self, "selection", None)