diff --git a/Start_Run.py b/Start_Run.py index 8c6489d1..3e90d01d 100644 --- a/Start_Run.py +++ b/Start_Run.py @@ -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() \ No newline at end of file +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() \ No newline at end of file diff --git a/main.py b/main.py index d0e529b9..0c9a7c26 100644 --- a/main.py +++ b/main.py @@ -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')}") # ==================== 兼容性属性 ==================== diff --git a/project/project_manager.py b/project/project_manager.py index 4f386206..d10bf13b 100644 --- a/project/project_manager.py +++ b/project/project_manager.py @@ -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): diff --git a/scene/scene_manager.py b/scene/scene_manager.py index 1a2e9eae..9f89b417 100644 --- a/scene/scene_manager.py +++ b/scene/scene_manager.py @@ -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):