This commit is contained in:
Rowland 2026-01-21 16:12:33 +08:00
parent ae6395b88e
commit d7a5f420c4
3 changed files with 737 additions and 217 deletions

488
IFLOW.md
View File

@ -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/ 目录包含大量测试和演示文件,便于功能验证和学习。

450
demo.py
View File

@ -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()

View File

@ -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