修复项目加载

This commit is contained in:
Rowland 2026-01-28 11:31:04 +08:00
parent 8b6b8daf9b
commit 3b1b547ed8
4 changed files with 273 additions and 46 deletions

View File

@ -1,37 +1,49 @@
import os
import sys
# 添加项目根目录到 Python 路径
project_root = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, project_root)
# 设置工作目录为项目根目录
os.chdir(project_root)
# 添加 RenderPipeline 到路径(注意路径名称应与实际目录一致)
render_pipeline_path = os.path.join(project_root, "RenderPipeline")
sys.path.insert(0, render_pipeline_path)
# 添加 RenderPipelineFile 路径
render_pipeline_file_path = os.path.join(project_root, "RenderPipelineFile")
sys.path.insert(0, render_pipeline_file_path)
# 添加 icons 目录到路径
icons_path = os.path.join(project_root, "icons")
sys.path.insert(0, icons_path)
# 现在可以导入并运行主程序
if __name__ == "__main__":
args = sys.argv[1:]
# args = "/home/tiger/桌面/Test1"
# args = "C:/Users/29381/Desktop/1"
print(f'Path is {args}')
# 将整个列表转换为字符串(包括方括号)
args_str = ''.join(args)
from main import run
if args:
run(args_str)
# run(args)
else:
run()
import os
import sys
# 添加项目根目录到 Python 路径
project_root = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, project_root)
# 设置工作目录为项目根目录
os.chdir(project_root)
# 添加 RenderPipeline 到路径(注意路径名称应与实际目录一致)
render_pipeline_path = os.path.join(project_root, "RenderPipeline")
sys.path.insert(0, render_pipeline_path)
# 添加 RenderPipelineFile 路径
render_pipeline_file_path = os.path.join(project_root, "RenderPipelineFile")
sys.path.insert(0, render_pipeline_file_path)
# 添加 icons 目录到路径
icons_path = os.path.join(project_root, "icons")
sys.path.insert(0, icons_path)
# 现在可以导入并运行主程序
if __name__ == "__main__":
args = sys.argv[1:]
# args = "/home/tiger/桌面/Test1"
# args = "C:/Users/29381/Desktop/1"
print(f'Path is {args}')
# 将整个列表转换为字符串(包括方括号)
args_str = ''.join(args)
from main import MyWorld
if args:
# print(f"[DEBUG] 启动时传入项目路径: {args_str}")
# 创建应用实例
app = MyWorld()
# 如果传入了项目路径,尝试打开项目
if hasattr(app, 'project_manager'):
# print(f"[DEBUG] 尝试打开项目: {args_str}")
success = app.project_manager.openProject(args_str)
# print(f"[DEBUG] 项目打开结果: {success}")
else:
# print(f"[DEBUG] 项目管理器未初始化")
pass
app.run()
else:
# print(f"[DEBUG] 无项目路径,正常启动")
app = MyWorld()
app.run()

View File

@ -148,10 +148,14 @@ class MyWorld(CoreWorld):
self.video_manager = None
# 初始化场景管理系统
# print(f"[DEBUG] 初始化场景管理系统...")
self.scene_manager = SceneManager(self)
# print(f"[DEBUG] 场景管理系统初始化完成")
# 初始化项目管理系统
# print(f"[DEBUG] 初始化项目管理系统...")
self.project_manager = ProjectManager(self)
# print(f"[DEBUG] 项目管理系统初始化完成")
self.info_panel_manager = InfoPanelManager(self)
@ -424,6 +428,11 @@ class MyWorld(CoreWorld):
self.add_info_message("拖拽导入功能已启用 - 可将3D文件拖拽到窗口中导入")
print("✓ MyWorld 初始化完成")
# print(f"[DEBUG] MyWorld 初始化完成,所有管理器已就绪")
# print(f"[DEBUG] 场景管理器: {hasattr(self, 'scene_manager')}")
# print(f"[DEBUG] 项目管理器: {hasattr(self, 'project_manager')}")
# print(f"[DEBUG] GUI管理器: {hasattr(self, 'gui_manager')}")
# print(f"[DEBUG] 脚本管理器: {hasattr(self, 'script_manager')}")
# ==================== 兼容性属性 ====================

View File

@ -92,6 +92,7 @@ class ProjectManager:
Returns:
bool: 打开是否成功
"""
# print(f"\n[DEBUG] ===== 开始打开项目: {project_path} =====")
try:
if not project_path:
print("错误: 项目路径不能为空")
@ -99,21 +100,83 @@ class ProjectManager:
# 检查是否是有效的项目文件夹
config_file = os.path.join(project_path, "project.json")
# print(f"[DEBUG] 项目配置文件路径: {config_file}")
# print(f"[DEBUG] 配置文件是否存在: {os.path.exists(config_file)}")
if not os.path.exists(config_file):
print("错误: 选择的不是有效的项目文件夹!")
return False
# 读取项目配置
# print(f"[DEBUG] 开始读取项目配置文件...")
with open(config_file, "r", encoding="utf-8") as f:
project_config = json.load(f)
# print(f"[DEBUG] 项目配置读取成功: {project_config.get('name', 'Unknown')}")
# 检查场景文件
scene_file = os.path.join(project_path, "scenes", "scene.bam")
# print(f"[DEBUG] 场景文件路径: {scene_file}")
# print(f"[DEBUG] 场景文件是否存在: {os.path.exists(scene_file)}")
if os.path.exists(scene_file):
# 加载场景
# 加载场景前的安全检查
# print(f"[DEBUG] 开始加载场景文件...")
# print(f"[DEBUG] 当前world状态检查:")
# print(f"[DEBUG] - render节点存在: {hasattr(self.world, 'render') and self.world.render is not None}")
# print(f"[DEBUG] - loader存在: {hasattr(self.world, 'loader') and self.world.loader is not None}")
# print(f"[DEBUG] - scene_manager存在: {hasattr(self.world, 'scene_manager') and self.world.scene_manager is not None}")
# 检查world的基本状态
if not hasattr(self.world, 'render') or self.world.render is None:
# print(f"[DEBUG] 错误: world.render不存在或为None")
return False
if not hasattr(self.world, 'loader') or self.world.loader is None:
# print(f"[DEBUG] 错误: world.loader不存在或为None")
return False
# 清理可能的残留状态
try:
# 清理选择状态
if hasattr(self.world, 'selection') and self.world.selection:
self.world.selection.clearSelection()
# print(f"[DEBUG] 选择状态已清理")
# 清理GUI状态
if hasattr(self.world, 'gui_elements'):
for gui_elem in self.world.gui_elements:
if gui_elem and not gui_elem.isEmpty():
gui_elem.detachNode()
self.world.gui_elements.clear()
# print(f"[DEBUG] GUI元素已清理")
# 验证BAM文件
# print(f"[DEBUG] 验证BAM文件完整性...")
if os.path.getsize(scene_file) == 0:
# print(f"[DEBUG] 错误: BAM文件为空")
return False
# 检查文件权限
if not os.access(scene_file, os.R_OK):
# print(f"[DEBUG] 错误: BAM文件不可读")
return False
# print(f"[DEBUG] BAM文件验证通过")
except Exception as e:
# print(f"[DEBUG] 清理状态时发生异常: {e}")
return False
if self.world.scene_manager.loadScene(scene_file):
# print(f"[DEBUG] 场景加载成功")
# 更新项目配置
project_config["scene_file"] = os.path.relpath(scene_file, project_path)
else:
# print(f"[DEBUG] 场景加载失败")
return False
else:
# print(f"[DEBUG] 场景文件不存在,跳过场景加载")
pass
project_config["last_modified"] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
with open(config_file, "w", encoding="utf-8") as f:
@ -123,11 +186,15 @@ class ProjectManager:
self.current_project_path = project_path
self.project_config = project_config
# print(f"[DEBUG] ===== 项目打开成功: {project_path} =====")
print("项目加载成功!")
return True
except Exception as e:
# print(f"[DEBUG] ===== 项目打开失败 =====")
print(f"加载项目时发生错误:{str(e)}")
import traceback
traceback.print_exc()
return False
def openProjectForPath(self, project_path):

View File

@ -1433,29 +1433,83 @@ class SceneManager:
traceback.print_exc()
return False
def loadScene(self, filename):
def loadScene(self, filename, retry_count=0):
"""从BAM文件加载场景"""
max_retries = 3
try:
print(f"\n=== 开始加载场景: {filename} ===")
print(f"\n=== 开始加载场景: {filename} (尝试 {retry_count + 1}/{max_retries + 1}) ===")
# print(f"[DEBUG] loadScene 被调用,参数: {filename}")
# print(f"[DEBUG] 当前场景中的模型数量: {len(self.models)}")
# print(f"[DEBUG] 当前场景中的灯光数量: {len(self.Spotlight) + len(self.Pointlight)}")
# print(f"[DEBUG] 当前GUI元素数量: {len(self.world.gui_elements) if hasattr(self.world, 'gui_elements') else 0}")
# 确保文件路径是规范化的
filename = os.path.normpath(filename)
# print(f"[DEBUG] 规范化后的文件路径: {filename}")
# 检查文件是否存在
if not os.path.exists(filename):
print(f"场景文件不存在: {filename}")
return False
# print(f"[DEBUG] 文件大小: {os.path.getsize(filename)} bytes")
# 检查文件是否损坏
if os.path.getsize(filename) == 0:
# print(f"[DEBUG] 错误: 场景文件为空")
return False
# 预防性清理确保Panda3D处于稳定状态
if retry_count > 0:
# print(f"[DEBUG] 执行预防性清理...")
try:
# 清理所有可能的残留状态
from panda3d.core import ModelPool
ModelPool.releaseAllModels()
# 强制垃圾回收
import gc
gc.collect()
# 清理渲染状态
if hasattr(self.world, 'render') and self.world.render:
# 清理渲染节点的子节点(保留基本节点)
children_to_remove = []
for i in range(self.world.render.getNumChildren()):
child = self.world.render.getChild(i)
if child.getName() not in ['camera', 'alight', 'dlight', 'ambient', 'render']:
children_to_remove.append(child)
for child in children_to_remove:
if not child.isEmpty():
child.removeNode()
# print(f"[DEBUG] 清理了 {len(children_to_remove)} 个渲染子节点")
except Exception as cleanup_error:
# print(f"[DEBUG] 预防性清理时发生异常: {cleanup_error}")
pass
# print(f"[DEBUG] 预加载检查完成,开始正式加载...")
tree_widget = self._get_tree_widget()
# print(f"[DEBUG] 获取树形控件: {tree_widget is not None}")
# 清除当前场景
print("\n清除当前场景...")
for model in self.models:
# print(f"[DEBUG] 需要清理的模型数量: {len(self.models)}")
for i, model in enumerate(self.models):
# print(f"[DEBUG] 清理模型 {i+1}/{len(self.models)}: {model.getName() if model else 'None'}")
if tree_widget:
tree_widget.delete_item(model)
else:
# 直接移除节点
if not model.isEmpty():
model.removeNode()
self.models.clear()
# print(f"[DEBUG] 模型清理完成")
# 清除灯光
for light_node in self.Spotlight:
@ -1524,13 +1578,73 @@ class SceneManager:
self._cleanupAuxiliaryNodes()
# 加载场景
scene = self.world.loader.loadModel(Filename.fromOsSpecific(filename))
if not scene:
print("场景加载失败")
# print(f"[DEBUG] 开始加载BAM文件...")
# 安全清理措施
try:
# 1. 清理Panda3D模型缓存
from panda3d.core import ModelPool
ModelPool.releaseAllModels()
# print(f"[DEBUG] 模型缓存已清理")
# 2. 强制垃圾回收
import gc
gc.collect()
# print(f"[DEBUG] 垃圾回收完成")
# 3. 检查文件状态
if not os.access(filename, os.R_OK):
# print(f"[DEBUG] 文件不可读: {filename}")
return False
# 4. 使用更安全的加载方式
# print(f"[DEBUG] 尝试加载BAM文件...")
panda_filename = Filename.fromOsSpecific(filename)
# print(f"[DEBUG] Panda3D文件名: {panda_filename}")
# 设置加载选项
from panda3d.core import LoaderOptions
loader_options = LoaderOptions()
loader_options.setFlags(loader_options.LFNoCache) # 禁用缓存
scene = self.world.loader.loadModel(panda_filename, loader_options)
if not scene:
print("场景加载失败")
return False
# print(f"[DEBUG] BAM文件加载成功")
except Exception as e:
# print(f"[DEBUG] BAM加载过程中发生异常: {e}")
import traceback
traceback.print_exc()
return False
# print(f"[DEBUG] BAM文件加载成功场景根节点: {scene.getName()}")
# print(f"[DEBUG] 场景子节点数量: {scene.getNumChildren()}")
# 验证场景节点的有效性
if scene.isEmpty():
# print(f"[DEBUG] 警告: 加载的场景节点为空")
return False
if not scene.hasParent():
# print(f"[DEBUG] 将场景节点临时挂载到render")
scene.reparentTo(self.world.render)
if tree_widget:
tree_widget.create_model_items(scene)
# print(f"[DEBUG] 创建模型项...")
try:
tree_widget.create_model_items(scene)
# print(f"[DEBUG] 模型项创建完成")
except Exception as e:
# print(f"[DEBUG] 创建模型项失败: {e}")
# 继续执行,不影响场景加载
pass
else:
# print(f"[DEBUG] 树形控件为空,跳过创建模型项")
pass
# 遍历场景中的所有模型节点
# 用于存储处理后的灯光节点,避免重复处理
processed_lights = []
@ -1542,7 +1656,12 @@ class SceneManager:
# 遍历场景中的所有节点
def processNode(nodePath, depth=0):
indent = " " * depth
print(f"{indent}处理节点: {nodePath.getName()} (类型: {type(nodePath.node()).__name__})")
# print(f"{indent}处理节点: {nodePath.getName()} (类型: {type(nodePath.node()).__name__})")
# 检查节点是否有效
if nodePath.isEmpty():
# print(f"{indent}[DEBUG] 节点为空,跳过处理")
return
#存储节点以便后续处理父子关系
loaded_nodes[nodePath.getName()] = nodePath
@ -1870,9 +1989,29 @@ class SceneManager:
return True
except Exception as e:
print(f"=== 场景加载失败 ===")
print(f"加载场景时发生错误: {str(e)}")
import traceback
traceback.print_exc()
# 重试机制
if retry_count < 3:
print(f"[DEBUG] 准备重试... ({retry_count + 1}/{3})")
try:
# 清理状态
import gc
gc.collect()
from panda3d.core import ModelPool
ModelPool.releaseAllModels()
# 等待一小段时间后重试
import time
time.sleep(0.5)
return self.loadScene(filename, retry_count + 1)
except Exception as retry_error:
print(f"[DEBUG] 重试失败: {retry_error}")
return False
def _rebuildParentChildRelationships(self, loaded_nodes):