From d7a5f420c436270081c7e7e1c4e17651c8b33458 Mon Sep 17 00:00:00 2001 From: Rowland <975945824@qq.com> Date: Wed, 21 Jan 2026 16:12:33 +0800 Subject: [PATCH] =?UTF-8?q?ui=E6=9B=BF=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IFLOW.md | 488 ++++++++++++++++++++++++++++++++++-------------------- demo.py | 450 +++++++++++++++++++++++++++++++++++++++++++++---- imgui.ini | 16 +- 3 files changed, 737 insertions(+), 217 deletions(-) diff --git a/IFLOW.md b/IFLOW.md index fd4e1fe4..300862c1 100644 --- a/IFLOW.md +++ b/IFLOW.md @@ -1,179 +1,309 @@ -# iFlow 上下文文档 - -## 项目概述 - -这是一个基于 Panda3D 和 PyQt5 的 3D 虚拟现实 (VR) 应用程序框架。该项目旨在提供一个功能齐全、模块化的 3D 环境,支持 VR 设备(如 HTC Vive, Oculus Rift)的集成,包含场景管理、模型导入、GUI 系统、脚本系统、地形系统、碰撞检测以及完整的 VR 交互功能。 - -核心架构围绕 `MyWorld` 类构建,该类继承自 `CoreWorld`,并集成了各种管理器模块,如选择系统、工具管理器、脚本管理器、GUI 管理器、场景管理器、项目管理器、地形管理器、碰撞管理器和 VR 管理器。 - -## 核心技术栈 - -- **核心引擎**: Panda3D -- **图形渲染**: 可选择普通渲染或 RenderPipeline 高级渲染管线 -- **VR 支持**: OpenVR/SteamVR -- **用户界面**: PyQt5 -- **3D 模型格式**: 支持 glTF, FBX (需转换), BAM 等 -- **脚本语言**: Python (内嵌脚本系统) -- **物理/碰撞**: Panda3D 内置碰撞系统 - -## 项目结构 - -``` -. -├── core/ # 核心模块 -│ ├── world.py # CoreWorld 基础世界类 -│ ├── vr/ # VR 子模块 (完整模块化结构) -│ │ ├── __init__.py -│ │ ├── config/ # VR 配置管理 -│ │ ├── interaction/ # VR 交互系统 (动作、抓取、摇杆、传送) -│ │ ├── performance/ # VR 性能监控 -│ │ ├── rendering/ # VR 渲染相关 (RenderPipeline 集成) -│ │ ├── testing/ # VR 测试模式 -│ │ ├── tracking/ # VR 设备跟踪 -│ │ └── visualization/ # VR 可视化 (控制器模型) -│ ├── vr_manager.py # VR 管理器主文件 (待拆分) -│ ├── selection.py # 选择系统 -│ ├── tool_manager.py # 工具管理器 -│ ├── script_system.py # 脚本系统 -│ ├── gui_manager.py # GUI 管理器 -│ ├── terrain_manager.py # 地形管理器 -│ ├── collision_manager.py # 碰撞管理器 -│ ├── event_handler.py # 事件处理器 -│ ├── patrol_system.py # 巡检系统 -│ ├── Command_System.py # 命令系统 -│ └── InfoPanelManager.py # 信息面板管理器 -├── demo/ # 示例和测试文件 -├── gui/ # GUI 相关模块 -├── project/ # 项目管理模块 -├── scene/ # 场景管理模块 (部分代码在 core/) -├── scripts/ # 脚本文件目录 -├── ui/ # UI 组件和主窗口 -├── QPanda3D/ # Panda3D 与 PyQt 集成库 -├── Resources/ # 资源文件 (模型、纹理等) -├── config/ # 配置文件 -│ └── vr_settings.json # VR 配置文件 -├── main.py # 程序入口点 -└── Start_Run.py # 启动脚本 -``` - -## 核心功能模块 - -### 1. World (core/world.py, main.py) - -- `CoreWorld`: 基础 3D 世界设置,包括相机、光照、地面、资源路径。 -- `MyWorld`: 扩展的主世界类,整合所有管理器和功能模块。 - - **初始化**: 设置资源路径、相机、光照、地面,加载中文字体。 - - **兼容性**: 提供旧版属性访问接口。 - - **功能代理**: 将大量功能委托给专门的管理器。 - -### 2. VR 系统 (core/vr/) - -这是一个高度模块化的 VR 子系统,核心是 `VRManager` (core/vr_manager.py)。 - -- **VRManager**: - - **初始化与状态管理**: 检查 VR 可用性、初始化 OpenVR、管理启用/禁用状态。 - - **渲染系统**: - - 支持普通渲染和 RenderPipeline 高级渲染两种模式。 - - 创建和管理左右眼的渲染缓冲区和相机。 - - 实现高效的纹理提交到 OpenVR Compositor。 - - 支持动态分辨率缩放和质量预设。 - - **跟踪系统**: - - 通过 OpenVR 获取 HMD 和控制器的姿态。 - - 使用锚点层级系统 (`tracking_space`, `hmd_anchor` 等) 管理设备位置。 - - 坐标系转换 (OpenVR 到 Panda3D)。 - - **控制器**: - - `LeftController`, `RightController`: 管理具体的手柄输入和可视化。 - - 支持动作系统 (VRActionManager) 或直接输入读取。 - - **交互系统**: - - **VRInteractionManager**: 对象抓取和交互。 - - **VRTeleportSystem**: 传送功能。 - - **VRJoystickManager**: 摇杆移动控制。 - - **性能优化**: - - 对象池 (Mat4) 减少 GC 压力。 - - 纹理 ID 缓存避免重复 prepare。 - - 智能 GPU 同步策略。 - - 性能模式自动切换。 - - **配置管理**: - - `VRConfigManager`: 从 `config/vr_settings.json` 加载/保存配置。 - - **测试与调试**: - - `VRTestMode`: 提供不同的测试显示模式和功能开关。 - - `VRPerformanceMonitor`: 性能监控和报告。 - -### 3. GUI 系统 (core/gui_manager.py, gui/) - -- **GUIManager**: 管理 2D 和 3D GUI 元素的创建、编辑、删除。 -- **功能**: - - 创建按钮、标签、输入框、2D/3D 图像、视频屏幕等。 - - GUI 编辑模式,支持拖拽创建和属性编辑。 - - 与场景树和属性面板集成。 - - 独立的 GUI 预览窗口。 - -### 4. 场景与模型管理 (core/scene_manager.py) - -- **SceneManager**: 管理 3D 场景中的所有模型。 -- **功能**: - - 模型导入 (支持 glTF, FBX 转换)。 - - 材质和几何体处理。 - - 碰撞体设置。 - - 场景保存/加载 (BAM 格式)。 - - 场景树更新。 - -### 5. 脚本系统 (core/script_system.py) - -- **ScriptManager**: 嵌入式 Python 脚本系统。 -- **功能**: - - 脚本文件的创建、加载、重载。 - - 为游戏对象挂载/卸载脚本。 - - 热重载支持。 - - 脚本信息查询。 - -### 6. 地形系统 (core/terrain_manager.py) - -- **TerrainManager**: 管理 3D 地形。 -- **功能**: - - 从高度图或创建平面地形。 - - 地形 LOD 更新。 - - 地形高度查询和编辑。 - -### 7. 碰撞系统 (core/collision_manager.py) - -- **CollisionManager**: 处理场景中的碰撞检测。 -- **功能**: - - 模型间碰撞检测。 - - 碰撞历史和统计。 - -### 8. 工具与选择系统 (core/tool_manager.py, core/selection.py) - -- **ToolManager**: 管理当前使用的工具 (选择、移动、旋转、缩放)。 -- **SelectionSystem**: 处理对象选择逻辑和相机聚焦。 - -## 构建与运行 - -### 入口点 - -- `main.py`: 主程序入口,创建 `MyWorld` 实例并启动 PyQt5 主窗口。 -- `Start_Run.py`: 可能的启动脚本。 - -### 运行方式 - -1. 确保已安装所有依赖项(Panda3D, PyQt5, OpenVR 等)。 -2. 运行 `python main.py` 启动应用程序。 -3. 如果连接了 VR 设备并安装了 SteamVR,可以在应用内启用 VR 模式。 - -### 依赖项 - -项目未提供 `requirements.txt` 文件,但根据代码分析,主要依赖包括: - -- `panda3d` -- `PyQt5` -- `openvr` (用于 VR 功能) -- `numpy` (在 VR 模块中使用) - -## 开发约定 - -- **模块化设计**: 功能被分解到不同的管理器类中,`MyWorld` 主要起到集成和代理的作用。 -- **VR 子模块化**: VR 功能被组织在 `core/vr/` 目录下,具有清晰的子模块划分。 -- **配置驱动**: VR 设置通过 `config/vr_settings.json` 文件进行管理。 -- **性能意识**: VR 模块包含大量性能优化措施,如对象池、缓存、智能同步等。 -- **向后兼容**: `MyWorld` 通过属性代理保持与旧代码的兼容性。 -- **测试模式**: VR 系统包含专门的测试模式,便于调试和验证不同功能。 +# iFlow 上下文文档 + +## 项目概述 + +这是一个基于 Panda3D 和 PyQt5 的 3D 虚拟现实 (VR) 应用程序框架。该项目旨在提供一个功能齐全、模块化的 3D 环境,支持 VR 设备(如 HTC Vive, Oculus Rift)的集成,包含场景管理、模型导入、GUI 系统、脚本系统、地形系统、碰撞检测、视频播放、插件系统以及完整的 VR 交互功能。 + +核心架构围绕 `MyWorld` 类构建,该类继承自 `CoreWorld`,并集成了各种管理器模块,如选择系统、工具管理器、脚本管理器、GUI 管理器、场景管理器、项目管理器、地形管理器、碰撞管理器、视频管理器、资源管理器和 VR 管理器。 + +## 核心技术栈 + +- **核心引擎**: Panda3D 1.10.15 +- **图形渲染**: 可选择普通渲染或 RenderPipeline 高级渲染管线 +- **VR 支持**: OpenVR/SteamVR 2.2.0 +- **用户界面**: PyQt5 5.15.9 + ImGui 集成 +- **3D 模型格式**: 支持 glTF, FBX (需转换), BAM, EGG, OBJ 等 +- **脚本语言**: Python (内嵌脚本系统) +- **物理/碰撞**: Panda3D 内置碰撞系统 +- **视频支持**: MovieTexture (MP4, AVI, MOV, MKV, WebM) +- **插件架构**: 动态插件加载系统 +- **资源管理**: 文件浏览、拖拽、右键菜单集成 + +## 项目结构 + +``` +. +├── core/ # 核心模块 +│ ├── world.py # CoreWorld 基础世界类 +│ ├── vr/ # VR 子模块 (完整模块化结构) +│ │ ├── __init__.py +│ │ ├── config/ # VR 配置管理 +│ │ ├── interaction/ # VR 交互系统 (动作、抓取、摇杆、传送) +│ │ ├── performance/ # VR 性能监控 +│ │ ├── rendering/ # VR 渲染相关 (RenderPipeline 集成) +│ │ ├── testing/ # VR 测试模式 +│ │ ├── tracking/ # VR 设备跟踪 +│ │ └── visualization/ # VR 可视化 (控制器模型) +│ ├── drag_drop/ # 拖拽文件监控系统 +│ ├── vr_manager.py # VR 管理器主文件 +│ ├── selection.py # 选择系统 +│ ├── tool_manager.py # 工具管理器 +│ ├── script_system.py # 脚本系统 +│ ├── gui_manager.py # GUI 管理器 +│ ├── terrain_manager.py # 地形管理器 +│ ├── collision_manager.py # 碰撞管理器 +│ ├── event_handler.py # 事件处理器 +│ ├── patrol_system.py # 巡检系统 +│ ├── Command_System.py # 命令系统 +│ ├── InfoPanelManager.py # 信息面板管理器 +│ ├── resource_manager.py # 资源管理器 (ImGui版本) +│ ├── CustomMouseController.py # 自定义鼠标控制器 +│ ├── maintenance_gui.py # 维护GUI系统 +│ └── assembly_interaction.py # 装配交互系统 +├── demo/ # 示例和测试文件 (包含大量测试和演示) +├── gui/ # GUI 相关模块 +├── project/ # 项目管理模块 +├── scene/ # 场景管理模块 +├── scripts/ # 脚本文件目录 +├── ui/ # UI 组件和主窗口 +│ ├── widgets.py # 自定义UI组件 +│ ├── property_panel.py # 属性面板 +│ ├── interface_manager.py # 界面管理器 +│ ├── main_window.py # 主窗口 +│ ├── icon_manager.py # 图标管理器 +│ └── maintenance_system.py # 维护系统UI +├── plugins/ # 插件系统 +│ ├── core/ # 核心插件 +│ ├── third_party/ # 第三方插件 +│ ├── user/ # 用户插件 +│ ├── plugin_manager.py # 插件管理器 +│ └── plugin_interface_spec.py # 插件接口规范 +├── QPanda3D/ # Panda3D 与 PyQt 集成库 +├── QMeta3D/ # Meta3D 集成库 +├── RenderPipelineFile/ # RenderPipeline 高级渲染管线 +├── Resources/ # 资源文件 (模型、纹理等) +├── config/ # 配置文件 +│ └── vr_settings.json # VR 配置文件 +├── requirements/ # 依赖项文件 +│ ├── requirements.txt # 主要依赖项 +│ ├── conda-requirements.txt # Conda环境依赖 +│ └── clean-requirements.txt # 精简依赖项 +├── vr_actions/ # VR动作配置 +├── icons/ # 图标资源 +├── templates/ # 模板文件 +├── tools/ # 工具脚本 +├── tex/ # 纹理资源 +├── main.py # 程序入口点 +├── demo.py # ImGui演示程序 +├── gui_preview_window.py # GUI预览窗口 +├── Start_Run.py # 启动脚本 +└── imgui.ini # ImGui配置文件 +``` + +## 核心功能模块 + +### 1. World (core/world.py, main.py) + +- `CoreWorld`: 基础 3D 世界设置,包括相机、光照、地面、资源路径。 +- `MyWorld`: 扩展的主世界类,整合所有管理器和功能模块。 + - **初始化**: 设置资源路径、相机、光照、地面,加载中文字体。 + - **兼容性**: 提供旧版属性访问接口。 + - **功能代理**: 将大量功能委托给专门的管理器。 + - **新增集成**: 视频管理器、资源管理器、插件系统等。 + +### 2. VR 系统 (core/vr/) + +这是一个高度模块化的 VR 子系统,核心是 `VRManager` (core/vr_manager.py)。 + +- **VRManager**: + - **初始化与状态管理**: 检查 VR 可用性、初始化 OpenVR、管理启用/禁用状态。 + - **渲染系统**: + - 支持普通渲染和 RenderPipeline 高级渲染两种模式。 + - 创建和管理左右眼的渲染缓冲区和相机。 + - 实现高效的纹理提交到 OpenVR Compositor。 + - 支持动态分辨率缩放和质量预设。 + - **跟踪系统**: + - 通过 OpenVR 获取 HMD 和控制器的姿态。 + - 使用锚点层级系统 (`tracking_space`, `hmd_anchor` 等) 管理设备位置。 + - 坐标系转换 (OpenVR 到 Panda3D)。 + - **控制器**: + - `LeftController`, `RightController`: 管理具体的手柄输入和可视化。 + - 支持动作系统 (VRActionManager) 或直接输入读取。 + - **交互系统**: + - **VRInteractionManager**: 对象抓取和交互。 + - **VRTeleportSystem**: 传送功能。 + - **VRJoystickManager**: 摇杆移动控制。 + - **性能优化**: + - 对象池 (Mat4) 减少 GC 压力。 + - 纹理 ID 缓存避免重复 prepare。 + - 智能 GPU 同步策略。 + - 性能模式自动切换。 + - **配置管理**: + - `VRConfigManager`: 从 `config/vr_settings.json` 加载/保存配置。 + - **测试与调试**: + - `VRTestMode`: 提供不同的测试显示模式和功能开关。 + - `VRPerformanceMonitor`: 性能监控和报告。 + +### 3. GUI 系统 (core/gui_manager.py, gui/) + +- **GUIManager**: 管理 2D 和 3D GUI 元素的创建、编辑、删除。 +- **功能**: + - 创建按钮、标签、输入框、2D/3D 图像、视频屏幕等。 + - GUI 编辑模式,支持拖拽创建和属性编辑。 + - 与场景树和属性面板集成。 + - 独立的 GUI 预览窗口 (`gui_preview_window.py`)。 + - ImGui 集成支持现代化的界面设计。 + +### 4. 场景与模型管理 (scene/scene_manager.py) + +- **SceneManager**: 管理 3D 场景中的所有模型。 +- **功能**: + - 模型导入 (支持 glTF, FBX 转换, BAM, EGG, OBJ)。 + - 材质和几何体处理。 + - 碰撞体设置。 + - 场景保存/加载 (BAM 格式)。 + - 场景树更新。 + - Cesium 3D Tiles 支持 (`load_cesium_tileset`)。 + +### 5. 脚本系统 (core/script_system.py) + +- **ScriptManager**: 嵌入式 Python 脚本系统。 +- **功能**: + - 脚本文件的创建、加载、重载。 + - 为游戏对象挂载/卸载脚本。 + - 热重载支持。 + - 脚本信息查询。 + +### 6. 地形系统 (core/terrain_manager.py) + +- **TerrainManager**: 管理 3D 地形。 +- **功能**: + - 从高度图或创建平面地形。 + - 地形 LOD 更新。 + - 地形高度查询和编辑。 + +### 7. 碰撞系统 (core/collision_manager.py) + +- **CollisionManager**: 处理场景中的碰撞检测。 +- **功能**: + - 模型间碰撞检测。 + - 碰撞历史和统计。 + +### 8. 工具与选择系统 (core/tool_manager.py, core/selection.py) + +- **ToolManager**: 管理当前使用的工具 (选择、移动、旋转、缩放)。 +- **SelectionSystem**: 处理对象选择逻辑和相机聚焦。 +- **CustomMouseController**: 自定义鼠标控制器,增强交互体验。 + +### 9. 视频系统 (demo/video_integration.py) + +- **VideoManager**: 视频播放管理器。 +- **功能**: + - 支持多种视频格式 (MP4, AVI, MOV, MKV, WebM)。 + - 2D/3D 视频屏幕创建。 + - 360度球形视频播放。 + - 视频控制 (播放、暂停、时间定位)。 + +### 10. 资源管理系统 (core/resource_manager.py) + +- **ResourceManager**: ImGui风格的资源管理器。 +- **功能**: + - 文件浏览和导航。 + - 图标显示和文件类型识别。 + - 右键菜单操作。 + - 搜索和过滤功能。 + - 拖拽支持。 + - 文件系统监控和自动刷新。 + +### 11. 插件系统 (plugins/) + +- **PluginManager**: 动态插件加载和管理系统。 +- **功能**: + - 插件生命周期管理 (初始化、启用、禁用、清理)。 + - 核心插件、第三方插件和用户插件分类。 + - 插件接口规范 (`BasePlugin` 基类)。 + - 插件配置管理。 + +### 12. 信息面板系统 (core/InfoPanelManager.py) + +- **InfoPanelManager**: 信息面板管理器。 +- **功能**: + - 实时信息显示。 + - 系统状态监控。 + - 调试信息输出。 + +### 13. 巡检系统 (core/patrol_system.py) + +- **PatrolSystem**: 自动巡检系统。 +- **功能**: + - 巡检点管理。 + - 自动路径导航。 + - 巡检状态控制 (P键切换)。 + - 默认巡检路线创建。 + +### 14. 命令系统 (core/Command_System.py) + +- **CommandManager**: 命令管理模式。 +- **功能**: + - 撤销/重做支持。 + - 命令历史管理。 + - 操作封装和批处理。 + +### 15. 装配交互系统 (core/assembly_interaction.py) + +- **AssemblyInteraction**: 装配和拆卸交互系统。 +- **功能**: + - 装配流程管理。 + - 零部件识别和匹配。 + - 装配步骤指导。 + +### 16. 维护系统 (core/maintenance_gui.py, ui/maintenance_system.py) + +- **MaintenanceGUI**: 维护界面系统。 +- **功能**: + - 维护任务管理。 + - 故障诊断界面。 + - 维护记录和报告。 + +## 构建与运行 + +### 入口点 + +- `main.py`: 主程序入口,创建 `MyWorld` 实例并启动 PyQt5 主窗口。 +- `demo.py`: ImGui演示程序,展示现代化界面功能。 +- `Start_Run.py`: 启动脚本。 +- `gui_preview_window.py`: GUI预览窗口,用于独立测试GUI元素。 + +### 运行方式 + +1. 确保已安装所有依赖项(详见 requirements/requirements.txt)。 +2. 运行 `python main.py` 启动主应用程序。 +3. 运行 `python demo.py` 启动ImGui演示程序。 +4. 如果连接了 VR 设备并安装了 SteamVR,可以在应用内启用 VR 模式。 +5. VR配置可通过 `config/vr_settings.json` 进行调整。 + +### 依赖项 + +项目提供完整的依赖项文件在 `requirements/` 目录: + +- **requirements.txt**: 主要依赖项,包含: + - `Panda3D==1.10.15` (核心3D引擎) + - `PyQt5==5.15.9` (GUI框架) + - `openvr==2.2.0` (VR支持) + - `p3dimgui` (ImGui集成) + - `QPanda3D==0.2.10` (Panda3D与PyQt5集成) + - 以及其他辅助库 + +- **conda-requirements.txt**: Conda环境依赖项 +- **clean-requirements.txt**: 精简依赖项 + +安装命令: +```bash +pip install -r requirements/requirements.txt +``` + +## 开发约定 + +- **模块化设计**: 功能被分解到不同的管理器类中,`MyWorld` 主要起到集成和代理的作用。 +- **VR 子模块化**: VR 功能被组织在 `core/vr/` 目录下,具有清晰的子模块划分。 +- **配置驱动**: VR 设置通过 `config/vr_settings.json` 文件进行管理,ImGui配置通过 `imgui.ini` 管理。 +- **性能意识**: VR 模块包含大量性能优化措施,如对象池、缓存、智能同步等。 +- **向后兼容**: `MyWorld` 通过属性代理保持与旧代码的兼容性。 +- **测试模式**: VR 系统包含专门的测试模式,便于调试和验证不同功能。 +- **插件架构**: 支持动态插件加载,核心插件、第三方插件和用户插件分离管理。 +- **资源管理**: 使用ImGui风格的资源管理器,支持拖拽、右键菜单等现代交互。 +- **视频集成**: 完整的视频播放支持,包括2D/3D屏幕和360度球形视频。 +- **国际化支持**: 内置中文字体支持,适合中文环境开发。 +- **事件驱动**: 完善的事件处理系统,支持键盘快捷键(如F键聚焦、P键巡检)。 +- **命令模式**: 使用命令系统实现撤销/重做功能。 +- **演示系统**: demo/ 目录包含大量测试和演示文件,便于功能验证和学习。 diff --git a/demo.py b/demo.py index 17ce5ed1..c3217bed 100644 --- a/demo.py +++ b/demo.py @@ -537,6 +537,9 @@ class MyWorld(CoreWorld): self._draw_path_browser() self._draw_import_dialog() + # 绘制右键菜单 + self._draw_context_menus() + # 绘制拖拽界面 self._draw_drag_drop_interface() @@ -726,26 +729,121 @@ class MyWorld(CoreWorld): imgui.text("场景层级") imgui.separator() - # 模拟场景树结构 - if imgui.tree_node("渲染"): - if imgui.tree_node("环境光"): - imgui.text("环境光 #1") - imgui.tree_pop() - if imgui.tree_node("定向光"): - imgui.text("定向光 #1") - imgui.tree_pop() - if imgui.tree_node("地板"): - imgui.text("地板节点") - imgui.tree_pop() - imgui.tree_pop() + # 构建动态场景树 + self._build_scene_tree() + + def _build_scene_tree(self): + """构建动态场景树""" + # 渲染节点 + if imgui.tree_node("渲染"): + # 环境光 + if hasattr(self, 'ambient_light') and self.ambient_light: + self._draw_scene_node(self.ambient_light, "环境光", "light") - if imgui.tree_node("相机"): - imgui.text("主相机") - imgui.tree_pop() + # 定向光 + if hasattr(self, 'scene_manager') and self.scene_manager: + if hasattr(self.scene_manager, 'Spotlight') and self.scene_manager.Spotlight: + self._draw_scene_node(self.scene_manager.Spotlight, "定向光", "light") + if hasattr(self.scene_manager, 'Pointlight') and self.scene_manager.Pointlight: + self._draw_scene_node(self.scene_manager.Pointlight, "点光源", "light") - if imgui.tree_node("模型"): + # 地板 + if hasattr(self, 'ground') and self.ground: + self._draw_scene_node(self.ground, "地板", "geometry") + + imgui.tree_pop() + + # 相机节点 + if imgui.tree_node("相机"): + if hasattr(self, 'camera') and self.camera: + self._draw_scene_node(self.camera, "主相机", "camera") + imgui.tree_pop() + + # 3D模型节点 + if imgui.tree_node("模型"): + if hasattr(self, 'scene_manager') and self.scene_manager and hasattr(self.scene_manager, 'models'): + if self.scene_manager.models: + for i, model in enumerate(self.scene_manager.models): + if model and not model.isEmpty(): + self._draw_scene_node(model, model.getName() or f"模型_{i+1}", "model") + else: + imgui.text("(空)") + else: imgui.text("(空)") + imgui.tree_pop() + + # GUI元素节点 + if imgui.tree_node("GUI元素"): + if hasattr(self, 'gui_manager') and self.gui_manager and hasattr(self.gui_manager, 'gui_elements'): + if self.gui_manager.gui_elements: + for gui_element in self.gui_manager.gui_elements: + if gui_element and hasattr(gui_element, 'node'): + gui_type = getattr(gui_element, 'gui_type', 'GUI_UNKNOWN') + display_name = getattr(gui_element, 'name', gui_type) + self._draw_scene_node(gui_element.node, display_name, "gui", gui_type) + else: + imgui.text("(空)") + else: + imgui.text("(空)") + imgui.tree_pop() + + def _draw_scene_node(self, node, name, node_type, gui_subtype=None): + """绘制单个场景节点""" + if not node or node.isEmpty(): + return + + # 检查是否被选中 + is_selected = (hasattr(self, 'selection') and self.selection and + hasattr(self.selection, 'selectedNode') and + self.selection.selectedNode == node) + + # 节点可见性 + is_visible = node.is_hidden() == False + + # 设置选择颜色 + if is_selected: + imgui.push_style_color(imgui.Col_.text, (0.2, 0.6, 1.0, 1.0)) + + try: + # 显示节点 + node_open = imgui.tree_node(name) + + # 处理节点选择 + if imgui.is_item_clicked(): + if hasattr(self, 'selection') and self.selection: + self.selection.updateSelection(node) + + # 右键菜单 + if imgui.is_item_hovered() and imgui.is_mouse_clicked(1): + self._show_node_context_menu(node, name, node_type) + + # 显示节点属性 + imgui.same_line() + if is_visible: + imgui.text_colored((0.5, 1.0, 0.5, 1.0), "可见") + else: + imgui.text_colored((0.5, 0.5, 0.5, 1.0), "隐藏") + + if node_open: + # 如果有子节点,递归显示 + if node.getNumChildren() > 0: + for i in range(node.getNumChildren()): + child = node.getChild(i) + if child and not child.isEmpty(): + child_name = child.getName() or f"子节点_{i+1}" + self._draw_scene_node(child, child_name, node_type) imgui.tree_pop() + except Exception as e: + print(f"绘制场景节点时出错: {e}") + finally: + # 确保颜色状态被恢复 + if is_selected: + imgui.pop_style_color() + + def _show_node_context_menu(self, node, name, node_type): + """显示节点右键菜单""" + self._context_menu_node = True + self._context_menu_target = node def _draw_resource_manager(self): """绘制资源管理器面板""" @@ -1071,25 +1169,219 @@ class MyWorld(CoreWorld): imgui.text("对象属性") imgui.separator() - # 模拟属性设置 - changed, pos_x = imgui.drag_float("位置 X", 0.0, 0.1) - changed, pos_y = imgui.drag_float("位置 Y", 0.0, 0.1) - changed, pos_z = imgui.drag_float("位置 Z", 0.0, 0.1) + # 获取当前选中的节点 + selected_node = None + if hasattr(self, 'selection') and self.selection and hasattr(self.selection, 'selectedNode'): + selected_node = self.selection.selectedNode - imgui.separator() + if selected_node and not selected_node.isEmpty(): + self._draw_node_properties(selected_node) + else: + # 无选中对象时显示提示 + imgui.text_colored((0.5, 0.5, 0.5, 1.0), "未选择任何对象") + imgui.text("请从场景树中选择一个对象以查看其属性") + + def _draw_node_properties(self, node): + """绘制节点属性""" + if not node or node.isEmpty(): + return + + # 获取节点基本信息 + node_name = node.getName() or "未命名节点" + node_type = self._get_node_type_from_node(node) + + # 节点名称和类型 + imgui.text(f"名称: {node_name}") + imgui.text(f"类型: {node_type}") + + # 状态徽章 + self._draw_status_badges(node) + + imgui.separator() + + # 变换属性 + self._draw_transform_properties(node) + + imgui.separator() + + # 根据节点类型显示特定属性 + if node_type == "GUI元素": + self._draw_gui_properties(node) + elif node_type == "光源": + self._draw_light_properties(node) + elif node_type == "模型": + self._draw_model_properties(node) + + imgui.separator() + + # 操作按钮 + self._draw_property_actions(node) + + def _get_node_type_from_node(self, node): + """从节点判断其类型""" + # 检查是否为GUI元素 + if hasattr(node, 'getPythonTag') and node.getPythonTag('gui_element'): + return "GUI元素" + + # 检查是否为光源 + node_name = node.getName() or "" + if "light" in node_name.lower() or "Light" in node_name: + return "光源" + + # 检查是否为相机 + if "camera" in node_name.lower() or "Camera" in node_name: + return "相机" + + # 检查是否为模型 + if hasattr(self, 'scene_manager') and self.scene_manager: + if hasattr(self.scene_manager, 'models') and node in self.scene_manager.models: + return "模型" + + # 默认为几何体 + return "几何体" + + def _draw_status_badges(self, node): + """绘制状态徽章""" + # 可见性状态 + is_visible = not node.is_hidden() + visibility_color = (0.2, 0.8, 0.2, 1.0) if is_visible else (0.8, 0.2, 0.2, 1.0) + visibility_text = "可见" if is_visible else "隐藏" + + imgui.same_line() + imgui.text("状态: ") + imgui.same_line() + imgui.text_colored(visibility_color, f"● {visibility_text}") + + # 是否有碰撞体 + has_collision = hasattr(node, 'getChild') and any('Collision' in child.getName() for child in node.getChildren() if child.getName()) + if has_collision: + imgui.same_line() + imgui.text_colored((0.2, 0.4, 0.8, 1.0), "● 碰撞") + + def _draw_transform_properties(self, node): + """绘制变换属性""" + imgui.text("变换") + + # 位置 + pos = node.getPos() + changed, new_x = imgui.drag_float("位置 X##pos", pos.x, 0.1) + if changed: node.setX(new_x) + + changed, new_y = imgui.drag_float("位置 Y##pos", pos.y, 0.1) + if changed: node.setY(new_y) + + changed, new_z = imgui.drag_float("位置 Z##pos", pos.z, 0.1) + if changed: node.setZ(new_z) + + # 旋转 (度数) + hpr = node.getHpr() + changed, new_h = imgui.drag_float("旋转 H##rot", hpr.x, 1.0) + if changed: node.setH(new_h) + + changed, new_p = imgui.drag_float("旋转 P##rot", hpr.y, 1.0) + if changed: node.setP(new_p) + + changed, new_r = imgui.drag_float("旋转 R##rot", hpr.z, 1.0) + if changed: node.setR(new_r) + + # 缩放 + scale = node.getScale() + changed, new_sx = imgui.drag_float("缩放 X##scale", scale.x, 0.1) + if changed: node.setSx(new_sx) + + changed, new_sy = imgui.drag_float("缩放 Y##scale", scale.y, 0.1) + if changed: node.setSy(new_sy) + + changed, new_sz = imgui.drag_float("缩放 Z##scale", scale.z, 0.1) + if changed: node.setSz(new_sz) + + def _draw_gui_properties(self, node): + """绘制GUI元素属性""" + imgui.text("GUI属性") + + # 获取GUI元素 + gui_element = None + if hasattr(node, 'getPythonTag'): + gui_element = node.getPythonTag('gui_element') + + if gui_element: + # GUI类型 + gui_type = getattr(gui_element, 'gui_type', 'UNKNOWN') + imgui.text(f"GUI类型: {gui_type}") - changed, rot_x = imgui.drag_float("旋转 X", 0.0, 1.0) - changed, rot_y = imgui.drag_float("旋转 Y", 0.0, 1.0) - changed, rot_z = imgui.drag_float("旋转 Z", 0.0, 1.0) + # 文本内容 (适用于按钮、标签等) + if hasattr(gui_element, 'text'): + changed, new_text = imgui.input_text("文本内容", gui_element.text, 256) + if changed and hasattr(self, 'gui_manager'): + self.gui_manager.editGUIElement(gui_element, 'text', new_text) - imgui.separator() + # 大小 + if hasattr(gui_element, 'size'): + size = gui_element.size + changed, new_w = imgui.drag_float("宽度", size[0], 1.0) + if changed and hasattr(self, 'gui_manager'): + new_size = (new_w, size[1]) + self.gui_manager.editGUIElement(gui_element, 'size', new_size) + + changed, new_h = imgui.drag_float("高度", size[1], 1.0) + if changed and hasattr(self, 'gui_manager'): + new_size = (size[0], new_h) + self.gui_manager.editGUIElement(gui_element, 'size', new_size) + + def _draw_light_properties(self, node): + """绘制光源属性""" + imgui.text("光源属性") + + # 光源颜色 + if hasattr(node, 'getColor'): + color = node.getColor() + changed, new_r = imgui.drag_float("颜色 R", color.x, 0.01, 0.0, 1.0) + if changed: node.setColor(new_r, color.y, color.z, color.w) - changed, scale = imgui.drag_float("缩放", 1.0, 0.1) + changed, new_g = imgui.drag_float("颜色 G", color.y, 0.01, 0.0, 1.0) + if changed: node.setColor(color.x, new_g, color.z, color.w) - imgui.separator() - - if imgui.button("重置变换"): - print("重置变换") + changed, new_b = imgui.drag_float("颜色 B", color.z, 0.01, 0.0, 1.0) + if changed: node.setColor(color.x, color.y, new_b, color.w) + + # 光源强度 + imgui.text("光源强度: (暂不支持编辑)") + + def _draw_model_properties(self, node): + """绘制模型属性""" + imgui.text("模型属性") + + # 模型路径 + imgui.text("模型路径: (暂不支持显示)") + + # 材质数量 + imgui.text("材质数量: (暂不支持显示)") + + def _draw_property_actions(self, node): + """绘制属性操作按钮""" + # 重置变换 + if imgui.button("重置变换"): + node.setPos(0, 0, 0) + node.setHpr(0, 0, 0) + node.setScale(1, 1, 1) + + imgui.same_line() + + # 切换可见性 + 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() + + imgui.same_line() + + # 聚焦到对象 + if imgui.button("聚焦"): + if hasattr(self, 'selection') and self.selection: + self.selection.focusCameraOnSelectedNodeAdvanced() def _draw_console(self): """绘制控制台面板""" @@ -2424,6 +2716,104 @@ class MyWorld(CoreWorld): imgui.same_line() if imgui.button("取消"): self.clear_dragged_files() + + def _draw_context_menus(self): + """绘制右键菜单""" + # 节点右键菜单 + if hasattr(self, '_context_menu_node') and self._context_menu_node: + imgui.open_popup("节点右键菜单") + self._context_menu_node = None + + if imgui.begin_popup("节点右键菜单"): + if imgui.menu_item("删除节点")[0]: + if hasattr(self, '_context_menu_target') and self._context_menu_target: + self._delete_node(self._context_menu_target) + imgui.close_current_popup() + + if imgui.menu_item("重命名")[0]: + self._renaming_node = True + imgui.close_current_popup() + + imgui.separator() + + if imgui.menu_item("复制")[0]: + if hasattr(self, '_context_menu_target') and self._context_menu_target: + self._copy_node(self._context_menu_target) + imgui.close_current_popup() + + if imgui.menu_item("聚焦")[0]: + if hasattr(self, '_context_menu_target') and self._context_menu_target: + if hasattr(self, 'selection') and self.selection: + self.selection.updateSelection(self._context_menu_target) + self.selection.focusCameraOnSelectedNodeAdvanced() + imgui.close_current_popup() + + imgui.end_popup() + + # 重命名对话框 + 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) + if changed: + 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) + self._renaming_node = False + imgui.close_current_popup() + + imgui.same_line() + if imgui.button("取消"): + self._renaming_node = False + imgui.close_current_popup() + + imgui.end_popup() + + def _delete_node(self, node): + """删除节点""" + 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) + + # 删除节点本身 + node.removeNode() + + # 清除选择 + if hasattr(self, 'selection') and self.selection: + if self.selection.selectedNode == node: + self.selection.clearSelection() + + # 添加成功消息 + self.add_success_message(f"已删除节点: {node.getName() or '未命名节点'}") + + def _copy_node(self, node): + """复制节点""" + if not node or node.isEmpty(): + return + + # 这里可以实现节点复制逻辑 + # 暂时只显示消息 + self.add_info_message(f"复制功能暂未实现: {node.getName() or '未命名节点'}") demo = MyWorld() demo.run() diff --git a/imgui.ini b/imgui.ini index 8c491380..e7f9c7fa 100644 --- a/imgui.ini +++ b/imgui.ini @@ -31,7 +31,7 @@ DockId=0x00000007,0 [Window][场景树] Pos=0,20 -Size=285,739 +Size=285,776 Collapsed=0 DockId=0x00000001,0 @@ -42,8 +42,8 @@ Collapsed=0 DockId=0x00000005,0 [Window][控制台] -Pos=880,761 -Size=644,255 +Pos=880,798 +Size=644,218 Collapsed=0 DockId=0x0000000C,0 @@ -99,20 +99,20 @@ Size=600,500 Collapsed=0 [Window][资源管理器] -Pos=0,761 -Size=878,255 +Pos=0,798 +Size=878,218 Collapsed=0 DockId=0x0000000B,0 [Docking][Data] DockSpace ID=0x08BD597D Window=0x1BBC0F80 Pos=0,20 Size=1850,996 Split=X DockNode ID=0x00000003 Parent=0x08BD597D SizeRef=1524,996 Split=Y - DockNode ID=0x00000009 Parent=0x00000003 SizeRef=1380,739 Split=X + DockNode ID=0x00000009 Parent=0x00000003 SizeRef=1380,776 Split=X DockNode ID=0x00000001 Parent=0x00000009 SizeRef=285,730 HiddenTabBar=1 Selected=0xE0015051 DockNode ID=0x00000002 Parent=0x00000009 SizeRef=1237,730 Split=Y DockNode ID=0x00000007 Parent=0x00000002 SizeRef=1380,32 HiddenTabBar=1 Selected=0x43A39006 - DockNode ID=0x00000008 Parent=0x00000002 SizeRef=1380,705 CentralNode=1 Selected=0x5E5F7166 - DockNode ID=0x0000000A Parent=0x00000003 SizeRef=1380,255 Split=X Selected=0x5428E753 + DockNode ID=0x00000008 Parent=0x00000002 SizeRef=1380,742 CentralNode=1 Selected=0x5E5F7166 + DockNode ID=0x0000000A Parent=0x00000003 SizeRef=1380,218 Split=X Selected=0x5428E753 DockNode ID=0x0000000B Parent=0x0000000A SizeRef=878,111 HiddenTabBar=1 Selected=0x3A2E05C3 DockNode ID=0x0000000C Parent=0x0000000A SizeRef=644,111 HiddenTabBar=1 Selected=0x5428E753 DockNode ID=0x00000004 Parent=0x08BD597D SizeRef=324,996 Split=Y Selected=0x5DB6FF37