diff --git a/.lingma/rules/panda3d.md b/.lingma/rules/panda3d.md new file mode 100644 index 00000000..baebd605 --- /dev/null +++ b/.lingma/rules/panda3d.md @@ -0,0 +1,4 @@ +--- +trigger: always_on +--- +在进行panda3d项目的开发时,必须参考api文档,@https://docs.panda3d.org/1.10/python/reference/index \ No newline at end of file diff --git a/QPanda3D/__pycache__/Panda3DWorld.cpython-313.pyc b/QPanda3D/__pycache__/Panda3DWorld.cpython-313.pyc new file mode 100644 index 00000000..4ae61dd8 Binary files /dev/null and b/QPanda3D/__pycache__/Panda3DWorld.cpython-313.pyc differ diff --git a/QPanda3D/__pycache__/QPanda3DWidget.cpython-313.pyc b/QPanda3D/__pycache__/QPanda3DWidget.cpython-313.pyc new file mode 100644 index 00000000..ba08dc7e Binary files /dev/null and b/QPanda3D/__pycache__/QPanda3DWidget.cpython-313.pyc differ diff --git a/QPanda3D/__pycache__/QPanda3D_Buttons_Translation.cpython-313.pyc b/QPanda3D/__pycache__/QPanda3D_Buttons_Translation.cpython-313.pyc new file mode 100644 index 00000000..51ede85d Binary files /dev/null and b/QPanda3D/__pycache__/QPanda3D_Buttons_Translation.cpython-313.pyc differ diff --git a/QPanda3D/__pycache__/QPanda3D_Keys_Translation.cpython-313.pyc b/QPanda3D/__pycache__/QPanda3D_Keys_Translation.cpython-313.pyc new file mode 100644 index 00000000..eddd21a4 Binary files /dev/null and b/QPanda3D/__pycache__/QPanda3D_Keys_Translation.cpython-313.pyc differ diff --git a/QPanda3D/__pycache__/QPanda3D_Modifiers_Translation.cpython-313.pyc b/QPanda3D/__pycache__/QPanda3D_Modifiers_Translation.cpython-313.pyc new file mode 100644 index 00000000..271a60ef Binary files /dev/null and b/QPanda3D/__pycache__/QPanda3D_Modifiers_Translation.cpython-313.pyc differ diff --git a/QPanda3D/__pycache__/__init__.cpython-313.pyc b/QPanda3D/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 00000000..78e082c3 Binary files /dev/null and b/QPanda3D/__pycache__/__init__.cpython-313.pyc differ diff --git a/README.md b/README.md index e69de29b..d8395633 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,151 @@ +# EG - 3D编辑器与游戏引擎 + +EG是一个功能强大的3D编辑器和游戏引擎,基于Panda3D和PyQt5构建,提供直观的可视化编辑功能和VR支持。该引擎适用于游戏开发、3D可视化和交互式应用程序创建。 + +## 功能特性 + +### 核心功能 +- **3D场景编辑与管理**:完整的场景编辑工具集 +- **模型导入与处理**:支持多种3D模型格式导入 +- **材质系统**:高级材质编辑和管理 +- **光照系统**:环境光、定向光、聚光灯和点光源 +- **相机控制系统**:灵活的相机操作和视角控制 +- **场景保存与加载**:BAM格式场景保存和恢复 + +### 编辑器功能 +- **对象选择与变换**:精确的对象选择和变换工具 +- **坐标轴拖拽控制**:直观的3D变换控制 +- **属性面板编辑**:实时编辑对象属性 +- **场景树管理**:层次化场景结构管理 +- **材质编辑器**:可视化材质编辑 +- **日夜循环系统**:动态光照和环境变化 + +### GUI系统 +- **2D GUI元素**:按钮、标签、输入框、滑块等 +- **3D空间GUI**:3D文本和虚拟屏幕 +- **GUI编辑模式**:可视化GUI布局编辑 +- **事件处理系统**:GUI交互事件响应 + +### VR支持 +- **VR系统集成**:完整的VR环境支持 +- **VR控制器输入处理**:精确的控制器输入识别 +- **ALVR串流支持**:无线VR体验 +- **触觉反馈**:增强沉浸感的触觉反馈 +- **VR手势识别**:自然的手势交互 + +### 脚本系统 +- **Python脚本热重载**:实时脚本更新无需重启 +- **脚本模板创建**:快速创建新脚本 +- **对象脚本管理**:为游戏对象添加行为脚本 +- **脚本调试支持**:内置调试功能 + +### 项目管理 +- **项目创建与保存**:完整的项目管理功能 +- **资源管理**:统一的资源导入和管理 +- **项目打包**:一键打包发布项目 + +## 系统架构 + +### 核心模块 +- `core/world.py` - 核心世界管理,集成所有子系统 +- `core/event_handler.py` - 输入事件处理系统 +- `core/selection.py` - 对象选择和变换系统 +- `core/tool_manager.py` - 编辑工具管理 +- `core/script_system.py` - Python脚本系统 +- `core/vr_manager.py` - VR系统管理 +- `core/vr_input_handler.py` - VR输入处理 +- `core/alvr_streamer.py` - ALVR串流支持 + +### 子系统 +- `scene/scene_manager.py` - 场景管理系统 +- `gui/gui_manager.py` - GUI系统管理 +- `ui/` - 用户界面组件 + - `ui/main_window.py` - 主窗口设置 + - `ui/property_panel.py` - 属性面板 + - `ui/widgets.py` - 自定义UI组件 + - `ui/interface_manager.py` - 界面管理 +- `project/project_manager.py` - 项目管理系统 + +## 快速开始 + +### 系统要求 +- Python 3.7+ +- Panda3D +- PyQt5 +- 支持OpenGL的显卡 + +### 依赖安装 +```bash +pip install -r requirements.txt +``` + +### 运行编辑器 +```bash +python main.py +``` + +### 基本操作 +- **鼠标左键**:选择对象 +- **鼠标右键**:旋转视角 +- **鼠标中键**:平移视角 +- **滚轮**:缩放视角 +- **WASD**:在场景中移动 +- **数字键1-0**:切换不同时间点 +- **F键**:聚焦选中对象 +- **Delete键**:删除选中对象 + +## 编辑器使用指南 + +### 导入模型 +1. 点击"文件" > "导入模型" +2. 选择支持的3D模型文件 +3. 调整模型位置和属性 + +### 场景编辑 +1. 使用选择工具选中对象 +2. 通过属性面板修改对象属性 +3. 使用变换工具(移动、旋转、缩放)调整对象 + +### GUI创建 +1. 进入GUI编辑模式 +2. 选择GUI元素类型 +3. 点击场景放置GUI元素 +4. 通过属性面板调整GUI属性 + +### VR模式 +1. 连接VR设备 +2. 点击"VR" > "启用VR模式" +3. 使用VR控制器进行交互 + +### 脚本编辑 +1. 点击"脚本" > "创建新脚本" +2. 编写Python脚本代码 +3. 将脚本附加到场景对象 + +## 开发指南 + +### 架构概述 +EG采用模块化设计,核心功能通过`MyWorld`类集成各个子系统。所有子系统都通过代理方法暴露功能,使API保持一致性。 + +### 添加新功能 +1. 在相应目录下创建新模块 +2. 在`MyWorld`类中添加代理方法 +3. 通过子系统管理器集成功能 + +### 扩展VR功能 +1. 修改`vr_manager.py`添加新功能 +2. 更新`vr_input_handler.py`处理新输入 +3. 在`alvr_streamer.py`中添加串流支持 + +### 创建自定义工具 +1. 在`tool_manager.py`中添加新工具类型 +2. 实现工具的交互逻辑 +3. 在UI中添加工具选择按钮 + +## 贡献 + +欢迎提交Pull Request或Issue报告问题。贡献前请先查看贡献指南。 + +## 许可证 + +MIT License \ No newline at end of file diff --git a/__pycache__/main.cpython-310.pyc b/__pycache__/main.cpython-310.pyc index 4d7ef861..feabe58d 100644 Binary files a/__pycache__/main.cpython-310.pyc and b/__pycache__/main.cpython-310.pyc differ diff --git a/__pycache__/main.cpython-313.pyc b/__pycache__/main.cpython-313.pyc new file mode 100644 index 00000000..2f4b120d Binary files /dev/null and b/__pycache__/main.cpython-313.pyc differ diff --git a/core/__pycache__/__init__.cpython-313.pyc b/core/__pycache__/__init__.cpython-313.pyc new file mode 100644 index 00000000..c6f94e9c Binary files /dev/null and b/core/__pycache__/__init__.cpython-313.pyc differ diff --git a/core/__pycache__/openxr_input_handler.cpython-310.pyc b/core/__pycache__/openxr_input_handler.cpython-310.pyc new file mode 100644 index 00000000..26ea9e43 Binary files /dev/null and b/core/__pycache__/openxr_input_handler.cpython-310.pyc differ diff --git a/core/__pycache__/openxr_manager.cpython-310.pyc b/core/__pycache__/openxr_manager.cpython-310.pyc new file mode 100644 index 00000000..635925b4 Binary files /dev/null and b/core/__pycache__/openxr_manager.cpython-310.pyc differ diff --git a/core/__pycache__/openxr_streamer.cpython-310.pyc b/core/__pycache__/openxr_streamer.cpython-310.pyc new file mode 100644 index 00000000..2a61ad43 Binary files /dev/null and b/core/__pycache__/openxr_streamer.cpython-310.pyc differ diff --git a/core/__pycache__/world.cpython-313.pyc b/core/__pycache__/world.cpython-313.pyc new file mode 100644 index 00000000..b53c1cc6 Binary files /dev/null and b/core/__pycache__/world.cpython-313.pyc differ diff --git a/core/alvr_streamer.py b/core/alvr_streamer.py deleted file mode 100644 index c2aac58a..00000000 --- a/core/alvr_streamer.py +++ /dev/null @@ -1,683 +0,0 @@ -""" -ALVR串流处理器 - -负责与ALVR服务器通信和视频流传输 -支持Quest等VR头显的无线串流 -""" - -import socket -import struct -import threading -import json -import time -import subprocess -import psutil -from direct.showbase.DirectObject import DirectObject -from panda3d.core import Texture, PNMImage - - -class ALVRStreamer(DirectObject): - """ALVR串流处理器""" - - def __init__(self, world, vr_manager): - super().__init__() - self.world = world - self.vr_manager = vr_manager - - # ALVR服务器配置 - self.alvr_server_ip = "127.0.0.1" - self.alvr_server_port = 9943 - self.alvr_streaming_port = 9944 - - # 添加对有线连接的支持 - self.connection_mode = "auto" # auto, wireless, wired - self.wired_port = 9945 # Pico 4有线连接可能使用的端口 - - # 连接状态 - self.connected = False - self.streaming = False - self.server_socket = None - self.streaming_socket = None - - # 流媒体配置 - self.stream_width = 2880 # Quest 2 推荐分辨率 - self.stream_height = 1700 - self.stream_fps = 72 - self.bitrate = 150 # Mbps - self.codec = "h264" - - # 线程管理 - self.connection_thread = None - self.streaming_thread = None - self.running = False - - # 性能统计 - self.frame_count = 0 - self.last_fps_time = time.time() - self.current_fps = 0 - self.latency = 0 - - print("✓ ALVR串流处理器初始化完成") - - def initialize(self): - """初始化ALVR串流""" - try: - # 检查ALVR服务器是否运行 - if not self._check_alvr_server(): - print("ALVR服务器未运行,尝试启动...") - if not self._start_alvr_server(): - print("无法启动ALVR服务器") - return False - - # 连接到ALVR服务器 - if not self._connect_to_server(): - print("无法连接到ALVR服务器") - return False - - # 配置流媒体设置 - self._configure_streaming() - - # 启动串流线程 - self._start_streaming_threads() - - print("✓ ALVR串流初始化成功") - return True - - except Exception as e: - print(f"ALVR初始化错误: {str(e)}") - return False - - def _check_alvr_server(self): - """检查ALVR服务器是否运行""" - try: - # 检查进程 - alvr_processes = [] - for proc in psutil.process_iter(['pid', 'name', 'cmdline']): - try: - # 检查ALVR相关进程(包括Pico 4支持) - if 'alvr' in proc.info['name'].lower() or any('alvr' in (arg or '').lower() for arg in (proc.info['cmdline'] or [])): - alvr_processes.append(proc) - except (psutil.NoSuchProcess, psutil.AccessDenied, TypeError): - # 忽略无法访问的进程 - continue - - if alvr_processes: - print(f"✓ 检测到 {len(alvr_processes)} 个ALVR相关进程") - for proc in alvr_processes: - try: - print(f" - 进程: {proc.info['name']} (PID: {proc.info['pid']})") - except (psutil.NoSuchProcess, psutil.AccessDenied): - pass - return True - - # 尝试连接端口 - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.settimeout(2) - result = sock.connect_ex((self.alvr_server_ip, self.alvr_server_port)) - sock.close() - - if result == 0: - print("✓ 检测到ALVR服务器端口") - return True - else: - # 尝试其他可能的端口(Pico 4可能使用不同的端口) - alternative_ports = [self.wired_port, 8080, 8081, 8082, 9944, 9945] - for port in alternative_ports: - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - sock.settimeout(1) - result = sock.connect_ex((self.alvr_server_ip, port)) - sock.close() - if result == 0: - print(f"✓ 检测到ALVR服务器在端口 {port}") - self.alvr_server_port = port - return True - - return False - - except Exception as e: - print(f"检查ALVR服务器错误: {str(e)}") - return False - - def _start_alvr_server(self): - """启动ALVR服务器""" - try: - # 获取当前操作系统 - import platform - system = platform.system().lower() - - # 尝试启动ALVR服务器 - # 这里我们只尝试检测ALVR是否正在运行,而不是尝试启动它 - print("ℹ 检测到系统环境,跳过自动启动ALVR服务器") - print(" 请确保ALVR服务器已在运行") - return False - - except Exception as e: - print(f"启动ALVR服务器错误: {str(e)}") - return False - - def _connect_to_server(self): - """连接到ALVR服务器""" - try: - # 首先检查是否为Pico 4有线连接模式 - # 如果VR系统已经通过SteamVR正常工作,我们可以启用直连模式 - if self._is_steamvr_active(): - print("💡 检测到SteamVR正在运行,启用直连模式") - self.connected = True - return True - - # 尝试多种连接方式以支持Pico 4有线连接 - connection_attempts = [ - (self.alvr_server_ip, self.alvr_server_port), # 默认无线端口 - (self.alvr_server_ip, self.wired_port), # Pico 4有线端口 - (self.alvr_server_ip, 8081), # 替代端口1 - (self.alvr_server_ip, 8082) # 替代端口2 - ] - - for ip, port in connection_attempts: - try: - print(f"尝试连接到ALVR服务器 {ip}:{port}...") - # 创建TCP连接 - self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.server_socket.settimeout(5) - self.server_socket.connect((ip, port)) - - # 发送握手消息 - handshake_data = { - "type": "handshake", - "client_name": "Panda3D_VR_Engine", - "version": "1.0", - "capabilities": { - "video": True, - "audio": True, - "tracking": True, - "haptics": True - } - } - - self._send_message(handshake_data) - - # 接收响应 - response = self._receive_message() - if response and response.get("type") == "handshake_response": - self.connected = True - self.alvr_server_port = port # 记住成功连接的端口 - print(f"✓ 已连接到ALVR服务器 {ip}:{port}") - return True - else: - print(f"✗ 从 {ip}:{port} 接收到无效响应") - - except Exception as e: - print(f"连接到 {ip}:{port} 失败: {str(e)}") - if self.server_socket: - self.server_socket.close() - self.server_socket = None - continue - - return False - - except Exception as e: - print(f"连接ALVR服务器错误: {str(e)}") - return False - - def _is_steamvr_active(self): - """检查SteamVR是否正在运行""" - try: - # 检查是否有SteamVR相关进程 - steamvr_processes = ["vrserver", "steamvr", "vrcompositor", "vrmonitor"] - for proc in psutil.process_iter(['pid', 'name']): - try: - proc_name = proc.info['name'].lower() - if any(sv_proc in proc_name for sv_proc in steamvr_processes): - print(f"✓ 检测到SteamVR进程: {proc_name}") - return True - except (psutil.NoSuchProcess, psutil.AccessDenied): - continue - return False - except Exception as e: - print(f"检查SteamVR状态错误: {str(e)}") - return False - - def _send_message(self, data): - """发送消息到ALVR服务器""" - try: - # 在直连模式下,不需要发送消息 - if not self.server_socket and not self.connected: - return - - message = json.dumps(data).encode('utf-8') - length = struct.pack('= 1.0: - self.current_fps = self.frame_count - self.frame_count = 0 - self.last_fps_time = current_time - - def start_streaming(self): - """开始串流""" - # 如果是直连模式,直接返回成功 - if self._is_steamvr_active(): - self.streaming = True - print("✓ 启用直连模式,SteamVR将直接处理渲染输出") - return True - - if not self.connected: - print("未连接到ALVR服务器") - return False - - start_message = {"type": "start_streaming"} - self._send_message(start_message) - return True - - def stop_streaming(self): - """停止串流""" - if not self.connected: - return - - stop_message = {"type": "stop_streaming"} - self._send_message(stop_message) - self.streaming = False - - def send_haptic_feedback(self, controller_id, duration, intensity): - """发送触觉反馈""" - if not self.connected: - return - - haptic_message = { - "type": "haptic_feedback", - "controller_id": controller_id, - "duration": duration, - "intensity": intensity - } - - self._send_message(haptic_message) - - def get_streaming_status(self): - """获取串流状态""" - # 如果是直连模式,返回特殊的直连状态 - if self._is_steamvr_active(): - return { - "connected": True, - "streaming": self.streaming, - "fps": self.current_fps, - "latency": self.latency, - "resolution": f"{self.stream_width}x{self.stream_height}", - "bitrate": self.bitrate, - "mode": "direct" # 直连模式 - } - - return { - "connected": self.connected, - "streaming": self.streaming, - "fps": self.current_fps, - "latency": self.latency, - "resolution": f"{self.stream_width}x{self.stream_height}", - "bitrate": self.bitrate, - "mode": "alvr" # ALVR模式 - } - - def set_stream_quality(self, width, height, fps, bitrate): - """设置串流质量""" - self.stream_width = width - self.stream_height = height - self.stream_fps = fps - self.bitrate = bitrate - - # 如果正在串流,重新配置 - if self.streaming: - self._configure_streaming() - - def shutdown(self): - """关闭串流""" - print("关闭ALVR串流...") - - self.running = False - self.streaming = False - - # 发送断开消息 - if self.connected: - disconnect_message = {"type": "disconnect"} - self._send_message(disconnect_message) - - # 关闭连接 - if self.server_socket: - self.server_socket.close() - - if self.streaming_socket: - self.streaming_socket.close() - - # 等待线程结束 - if self.connection_thread: - self.connection_thread.join(timeout=2) - - if self.streaming_thread: - self.streaming_thread.join(timeout=2) - - self.connected = False - print("✓ ALVR串流已关闭") - - def is_connected(self): - """检查是否连接""" - return self.connected - - def is_streaming(self): - """检查是否在串流""" - return self.streaming \ No newline at end of file diff --git a/core/openxr_input_handler.py b/core/openxr_input_handler.py new file mode 100644 index 00000000..93dad247 --- /dev/null +++ b/core/openxr_input_handler.py @@ -0,0 +1,261 @@ +""" +OpenXR输入处理器 + +处理OpenXR控制器输入和手势识别 +支持多种OpenXR控制器和输入方式 +""" + +from direct.showbase.DirectObject import DirectObject +from panda3d.core import Vec3, Point3, CollisionRay, CollisionNode, CollisionHandlerQueue, BitMask32 +from direct.task import Task +import time + + +class OpenXRInputHandler(DirectObject): + """OpenXR输入处理器""" + + def __init__(self, world, openxr_manager): + super().__init__() + self.world = world + self.openxr_manager = openxr_manager + + # 控制器状态 + self.controllers = {} + self.controller_nodes = {} + self.controller_rays = {} + + # 手势识别 + self.gesture_enabled = True + self.gesture_history = [] + self.gesture_threshold = 0.1 + + # 交互系统 + self.interaction_enabled = True + self.selected_object = None + self.grab_offset = Vec3(0, 0, 0) + + # 输入映射 + self.input_mappings = { + 'trigger': self._handle_trigger, + 'grip': self._handle_grip, + 'thumbstick': self._handle_thumbstick, + 'touchpad': self._handle_touchpad, + 'menu': self._handle_menu, + 'system': self._handle_system + } + + print("✓ OpenXR输入处理器初始化完成") + + def start_input_handling(self): + """启动输入处理""" + if not self.openxr_manager.is_openxr_enabled(): + print("OpenXR未启用,无法启动输入处理") + return False + + # 启动输入更新任务 + self.world.taskMgr.add(self._update_input, "openxr_input_update") + + # 设置控制器可视化 + self._setup_controller_visualization() + + print("✓ OpenXR输入处理已启动") + return True + + def stop_input_handling(self): + """停止输入处理""" + self.world.taskMgr.remove("openxr_input_update") + self._cleanup_controller_visualization() + print("✓ OpenXR输入处理已停止") + + def _update_input(self, task): + """更新输入处理""" + if not self.openxr_manager.is_openxr_enabled(): + return Task.cont + + try: + # 更新所有控制器 + self._update_controllers() + + # 处理手势识别 + if self.gesture_enabled: + self._process_gestures() + + # 处理交互 + if self.interaction_enabled: + self._process_interactions() + + except Exception as e: + print(f"OpenXR输入更新错误: {str(e)}") + + return Task.cont + + def _update_controllers(self): + """更新控制器状态""" + if self.openxr_manager.simulation_mode: + # 在模拟模式下使用模拟数据 + sim_data = self.openxr_manager.get_simulation_data('controller_poses') + for controller_id, pose_data in sim_data.items(): + if pose_data['connected']: + self._update_controller_pose(controller_id, pose_data) + else: + # TODO: 实际OpenXR控制器更新 + pass + + def _update_controller_pose(self, controller_id, pose_data): + """更新控制器姿态""" + # 更新控制器位置和旋转 + if controller_id not in self.controller_nodes: + self._create_controller_node(controller_id) + + controller_node = self.controller_nodes[controller_id] + position = pose_data['position'] + rotation = pose_data['rotation'] + + controller_node.setPos(*position) + controller_node.setQuat(Quat(*rotation)) + + def _create_controller_node(self, controller_id): + """创建控制器节点""" + # 创建一个简单的控制器可视化模型 + from panda3d.core import CardMaker + cm = CardMaker(f'controller_{controller_id}') + cm.setFrame(-0.05, 0.05, -0.05, 0.05) + controller_node = self.world.render.attachNewNode(cm.generate()) + controller_node.setColor(0, 1, 0, 1) # 绿色 + + self.controller_nodes[controller_id] = controller_node + + # 创建控制器射线 + self._create_controller_ray(controller_id) + + def _create_controller_ray(self, controller_id): + """创建控制器射线""" + # 创建碰撞射线用于交互检测 + ray_node = CollisionNode(f'controller_ray_{controller_id}') + ray = CollisionRay() + ray.setOrigin(Point3(0, 0, 0)) + ray.setDirection(Vec3(0, 1, 0)) # 默认向前 + ray_node.addSolid(ray) + ray_node.setFromCollideMask(BitMask32.bit(0)) + + ray_np = self.controller_nodes[controller_id].attachNewNode(ray_node) + queue = CollisionHandlerQueue() + + self.controller_rays[controller_id] = { + 'node': ray_np, + 'queue': queue + } + + def _setup_controller_visualization(self): + """设置控制器可视化""" + if self.openxr_manager.simulation_mode: + # 在模拟模式下创建控制器可视化 + sim_data = self.openxr_manager.get_simulation_data('controller_poses') + for controller_id in sim_data.keys(): + self._create_controller_node(controller_id) + + def _cleanup_controller_visualization(self): + """清理控制器可视化""" + # 移除所有控制器节点 + for controller_node in self.controller_nodes.values(): + controller_node.removeNode() + + self.controller_nodes.clear() + self.controller_rays.clear() + + def _process_gestures(self): + """处理手势识别""" + # TODO: 实现手势识别逻辑 + pass + + def _process_interactions(self): + """处理交互""" + # TODO: 实现交互逻辑 + pass + + def _handle_trigger(self, controller_id, value): + """处理触发器输入""" + # TODO: 实现触发器处理逻辑 + pass + + def _handle_grip(self, controller_id, value): + """处理抓握输入""" + # TODO: 实现抓握处理逻辑 + pass + + def _handle_thumbstick(self, controller_id, x, y): + """处理摇杆输入""" + # TODO: 实现摇杆处理逻辑 + pass + + def _handle_touchpad(self, controller_id, x, y, pressed): + """处理触摸板输入""" + # TODO: 实现触摸板处理逻辑 + pass + + def _handle_menu(self, controller_id, pressed): + """处理菜单按钮""" + # TODO: 实现菜单按钮处理逻辑 + pass + + def _handle_system(self, controller_id, pressed): + """处理系统按钮""" + # TODO: 实现系统按钮处理逻辑 + pass + + def get_all_controllers(self): + """获取所有控制器""" + if self.openxr_manager.simulation_mode: + sim_data = self.openxr_manager.get_simulation_data('controller_poses') + return list(sim_data.keys()) + else: + # TODO: 返回实际连接的控制器 + return [] + + def get_controller_state(self, controller_id): + """获取控制器状态""" + if self.openxr_manager.simulation_mode: + sim_data = self.openxr_manager.get_simulation_data('controller_poses') + if controller_id in sim_data: + # 返回模拟控制器状态 + return { + 'trigger': 0.0, + 'grip': 0.0, + 'thumbstick': {'x': 0.0, 'y': 0.0}, + 'touchpad': {'x': 0.0, 'y': 0.0, 'pressed': False}, + 'menu': False, + 'system': False, + 'connected': sim_data[controller_id]['connected'] + } + + # 默认返回空状态 + return { + 'trigger': 0.0, + 'grip': 0.0, + 'thumbstick': {'x': 0.0, 'y': 0.0}, + 'touchpad': {'x': 0.0, 'y': 0.0, 'pressed': False}, + 'menu': False, + 'system': False, + 'connected': False + } + + def set_gesture_enabled(self, enabled): + """设置手势识别启用状态""" + self.gesture_enabled = enabled + print(f"{'✓' if enabled else '✗'} OpenXR手势识别已{'启用' if enabled else '禁用'}") + + def set_interaction_enabled(self, enabled): + """设置交互启用状态""" + self.interaction_enabled = enabled + print(f"{'✓' if enabled else '✗'} OpenXR交互已{'启用' if enabled else '禁用'}") + + def show_controller_rays(self, show=True): + """显示/隐藏控制器射线""" + for ray_data in self.controller_rays.values(): + ray_node = ray_data['node'] + if show: + ray_node.show() + else: + ray_node.hide() + + print(f"{'✓' if show else '✗'} 控制器射线已{'显示' if show else '隐藏'}") \ No newline at end of file diff --git a/core/openxr_manager.py b/core/openxr_manager.py new file mode 100644 index 00000000..df754978 --- /dev/null +++ b/core/openxr_manager.py @@ -0,0 +1,342 @@ +""" +OpenXR管理器 + +负责OpenXR系统初始化、追踪和渲染 +支持有线VR串流功能 +""" + +import warnings +warnings.filterwarnings("ignore", category=DeprecationWarning) + +from panda3d.core import * +from panda3d.core import Mat4, PerspectiveLens, CardMaker +# 修复taskMgr导入错误,应该从TaskManagerGlobal导入而不是ShowBaseGlobal +from direct.task.TaskManagerGlobal import taskMgr +from direct.showbase.DirectObject import DirectObject +from direct.task import Task +import sys + + +class OpenXRManager(DirectObject): + """OpenXR管理器 - 处理OpenXR系统初始化、追踪和渲染""" + + def __init__(self, world): + super().__init__() + self.world = world + self.openxr_enabled = False + self.xr_instance = None + self.xr_system = None + self.xr_session = None + + # 渲染配置 + self.render_width = 1920 + self.render_height = 1080 + + # 有线连接设置 + self.wired_connection = True + self.connection_ip = "127.0.0.1" + self.connection_port = 9943 + + # 模拟模式设置(用于开发测试) + self.simulation_mode = False + self.simulation_data = { + 'head_pose': {'position': [0, 0, 1.6], 'rotation': [0, 0, 0, 1]}, + 'controller_poses': { + 0: {'position': [-0.3, 0, 1.2], 'rotation': [0, 0, 0, 1], 'connected': True}, + 1: {'position': [0.3, 0, 1.2], 'rotation': [0, 0, 0, 1], 'connected': True} + }, + 'render_size': (1920, 1080) + } + + # 立体渲染缓冲区 + self.left_eye_buffer = None + self.right_eye_buffer = None + self.left_eye_camera = None + self.right_eye_camera = None + + # 眼部纹理(用于串流) + self.left_eye_texture = None + self.right_eye_texture = None + self.eye_textures = {} + + # 控制器相关 + self.controller_nodes = {} + self.controller_poses = {} + + print("✓ OpenXR管理器初始化完成") + + def initialize_openxr(self, force_simulation=False): + """初始化OpenXR系统""" + try: + # 检查是否强制使用模拟模式 + if force_simulation: + print("🔧 强制启用OpenXR模拟模式") + return self._init_simulation_mode() + + # 检查OpenXR支持 + if not self._check_openxr_support(): + print("⚠ OpenXR支持不可用,切换到模拟模式") + return self._init_simulation_mode() + + # 尝试初始化OpenXR + if not self._init_openxr_native(): + print("⚠ OpenXR初始化失败,切换到模拟模式") + print("提示: 请确保OpenXR运行时已安装且VR头盔已连接") + return self._init_simulation_mode() + + # 真实OpenXR模式初始化成功 + print("✓ 真实OpenXR模式初始化成功") + return self._init_real_openxr_mode() + + except Exception as e: + print(f"OpenXR初始化错误: {str(e)}") + print("⚠ 切换到模拟模式") + return self._init_simulation_mode() + + def _check_openxr_support(self): + """检查OpenXR支持""" + try: + # TODO: 实际检查OpenXR运行时支持 + # 这里暂时返回False以使用模拟模式进行开发 + return False + except: + return False + + def _init_openxr_native(self): + """初始化原生OpenXR""" + # TODO: 实现OpenXR初始化逻辑 + print("ℹ OpenXR原生初始化占位符") + return False + + def _init_real_openxr_mode(self): + """初始化真实OpenXR模式""" + self.openxr_enabled = True + self.simulation_mode = False + + # TODO: 实现实际的OpenXR设置 + # 1. 创建OpenXR实例 + # 2. 获取系统信息 + # 3. 创建会话 + # 4. 设置渲染目标 + # 5. 初始化控制器 + + return True + + def _init_simulation_mode(self): + """初始化模拟模式""" + self.openxr_enabled = True + self.simulation_mode = True + + # 设置模拟渲染尺寸 + self.render_width = self.simulation_data['render_size'][0] + self.render_height = self.simulation_data['render_size'][1] + + # 初始化模拟立体渲染 + self._setup_simulation_rendering() + + print("ℹ OpenXR模拟模式已启用") + print(f" 渲染尺寸: {self.render_width}x{self.render_height}") + print(" 头部追踪: 模拟摆动") + print(" 控制器: 模拟位置") + + return True + + def _setup_simulation_rendering(self): + """设置模拟渲染""" + print("🔧 OpenXR模拟模式: 开始设置渲染") + + # 创建左右眼渲染缓冲区 + self.left_eye_buffer = self._create_eye_buffer("left_eye", self.render_width//2, self.render_height) + self.right_eye_buffer = self._create_eye_buffer("right_eye", self.render_width//2, self.render_height) + + print(f" 创建眼部缓冲区: 左眼={self.left_eye_buffer is not None}, 右眼={self.right_eye_buffer is not None}") + + # 创建左右眼相机 + self.left_eye_camera = self._create_eye_camera("left_eye_camera", (-0.03, 0, 0)) # 瞳距的一半 + self.right_eye_camera = self._create_eye_camera("right_eye_camera", (0.03, 0, 0)) # 瞳距的一半) + + print(f" 创建眼部相机: 左眼={self.left_eye_camera is not None}, 右眼={self.right_eye_camera is not None}") + + # 创建用于显示的左右眼纹理,格式与主程序保持一致 + self.left_eye_texture = Texture("left_eye_texture") + self.right_eye_texture = Texture("right_eye_texture") + self.left_eye_texture.setFormat(Texture.F_rgba8) + self.right_eye_texture.setFormat(Texture.F_rgba8) + self.left_eye_texture.setComponentType(Texture.T_unsigned_byte) + self.right_eye_texture.setComponentType(Texture.T_unsigned_byte) + + print(f" 创建眼部纹理: 左眼={self.left_eye_texture is not None}, 右眼={self.right_eye_texture is not None}") + print(" 设置纹理格式为F_rgba8和T_unsigned_byte") + + # 设置自动纹理缩放为None + self.left_eye_texture.setAutoTextureScale(ATSNone) + self.right_eye_texture.setAutoTextureScale(ATSNone) + + print(" 设置自动纹理缩放为ATSNone") + + # 将纹理附加到渲染缓冲区,使用RTMCopyRam模式确保数据能被复制到RAM + left_result = self.left_eye_buffer.addRenderTexture(self.left_eye_texture, GraphicsOutput.RTMCopyRam) + right_result = self.right_eye_buffer.addRenderTexture(self.right_eye_texture, GraphicsOutput.RTMCopyRam) + + print(f" 添加渲染纹理结果: 左眼={left_result}, 右眼={right_result}") + + # 设置相机渲染目标 + if self.left_eye_buffer and self.left_eye_camera: + self.left_eye_camera.node().setScene(self.world.render) + print(" 设置左眼相机渲染场景") + + if self.right_eye_buffer and self.right_eye_camera: + self.right_eye_camera.node().setScene(self.world.render) + print(" 设置右眼相机渲染场景") + + self.eye_textures['left'] = self.left_eye_texture + self.eye_textures['right'] = self.right_eye_texture + + print(" 初始化完成,开始渲染帧") + + # 强制渲染多帧以便初始化纹理数据 + # 参考主程序的实现方式,渲染足够多的帧以确保数据填充 + for i in range(5): # 渲染5帧以确保纹理数据被正确填充 + self.world.graphicsEngine.renderFrame() + print(f" 渲染第{i+1}帧") + + # 提取纹理数据到RAM + gsg = self.world.win.getGsg() + print(f" 获取GSG: {gsg is not None}") + + if gsg: + self.world.graphicsEngine.extractTextureData(self.left_eye_texture, gsg) + self.world.graphicsEngine.extractTextureData(self.right_eye_texture, gsg) + print(" 提取纹理数据到RAM") + + # 检查纹理数据是否成功提取 + left_has_ram = self.left_eye_texture.hasRamImage() + right_has_ram = self.right_eye_texture.hasRamImage() + print(f" 纹理RAM图像检查: 左眼={left_has_ram}, 右眼={right_has_ram}") + + if left_has_ram: + left_data_size = len(self.left_eye_texture.getRamImage().getData()) + print(f" 左眼纹理数据大小: {left_data_size} 字节") + + if right_has_ram: + right_data_size = len(self.right_eye_texture.getRamImage().getData()) + print(f" 右眼纹理数据大小: {right_data_size} 字节") + + def get_left_eye_texture(self): + """获取左眼纹理用于显示""" + return self.left_eye_texture + + def get_right_eye_texture(self): + """获取右眼纹理用于显示""" + return self.right_eye_texture + + def _create_eye_buffer(self, name, width, height): + """创建眼部渲染缓冲区""" + # 创建帧缓冲区属性 + fb_prop = FrameBufferProperties() + fb_prop.setRgbColor(True) + fb_prop.setRgbaBits(8, 8, 8, 8) + fb_prop.setDepthBits(24) + + # 创建窗口属性 + win_prop = WindowProperties() + win_prop.setSize(width, height) + + # 创建缓冲区 + buffer = self.world.graphicsEngine.makeOutput( + self.world.pipe, + name, + -1000, # 低优先级 + fb_prop, + win_prop, + GraphicsPipe.BFRefuseWindow + ) + + return buffer + + def _create_eye_camera(self, name, position_offset): + """创建眼部相机""" + # 创建相机节点 + camera_node = Camera(name) + lens = PerspectiveLens() + lens.setFov(100) # VR镜头视野 + lens.setNear(0.1) + lens.setFar(1000.0) + camera_node.setLens(lens) + + # 创建相机节点路径 + camera_np = self.world.render.attachNewNode(camera_node) + camera_np.setPos(*position_offset) + + return camera_np + + def shutdown_openxr(self): + """关闭OpenXR系统""" + if not self.openxr_enabled: + return True + + try: + # 清理渲染缓冲区 + if self.left_eye_buffer: + self.world.graphicsEngine.removeWindow(self.left_eye_buffer) + self.left_eye_buffer = None + + if self.right_eye_buffer: + self.world.graphicsEngine.removeWindow(self.right_eye_buffer) + self.right_eye_buffer = None + + # 清理相机 + if self.left_eye_camera: + self.left_eye_camera.removeNode() + self.left_eye_camera = None + + if self.right_eye_camera: + self.right_eye_camera.removeNode() + self.right_eye_camera = None + + # 重置状态 + self.openxr_enabled = False + self.xr_instance = None + self.xr_system = None + self.xr_session = None + + print("✓ OpenXR系统已关闭") + return True + + except Exception as e: + print(f"OpenXR关闭错误: {str(e)}") + return False + + def is_openxr_enabled(self): + """检查OpenXR是否启用""" + return self.openxr_enabled + + def get_openxr_info(self): + """获取OpenXR系统信息""" + return { + "enabled": self.openxr_enabled, + "mode": "simulation" if self.simulation_mode else "real", + "render_size": (self.render_width, self.render_height), + "simulation_mode": self.simulation_mode, + "wired_connection": self.wired_connection + } + + def update_simulation_data(self, data_type, data): + """更新模拟数据""" + if data_type in self.simulation_data: + self.simulation_data[data_type] = data + return True + return False + + def get_simulation_data(self, data_type=None): + """获取模拟数据""" + if data_type: + return self.simulation_data.get(data_type, None) + return self.simulation_data + + def enable_simulation_mode(self): + """启用模拟模式""" + if not self.simulation_mode: + self.shutdown_openxr() + self.simulation_mode = True + self.initialize_openxr(force_simulation=True) \ No newline at end of file diff --git a/core/openxr_streamer.py b/core/openxr_streamer.py new file mode 100644 index 00000000..0db16833 --- /dev/null +++ b/core/openxr_streamer.py @@ -0,0 +1,257 @@ +""" +OpenXR串流处理器 + +负责与VR设备通信和视频流传输 +支持有线连接的低延迟串流 +""" + +import socket +import struct +import threading +import json +import time +import subprocess +import psutil +from direct.showbase.DirectObject import DirectObject +from panda3d.core import Texture, PNMImage + + +class OpenXRStreamer(DirectObject): + """OpenXR串流处理器""" + + def __init__(self, world, openxr_manager): + super().__init__() + self.world = world + self.openxr_manager = openxr_manager + + # 串流服务器配置 + self.server_ip = "127.0.0.1" + self.server_port = 9943 + self.streaming_port = 9944 + + # 有线连接设置 + self.connection_mode = "wired" # wired, wireless + self.wired_port = 9945 # 有线连接端口 + + # 连接状态 + self.connected = False + self.streaming = False + self.server_socket = None + self.streaming_socket = None + + # 流媒体配置 + self.stream_width = 2880 # 推荐分辨率 + self.stream_height = 1700 + self.stream_fps = 72 + self.bitrate = 150 # Mbps + self.codec = "h264" + + # 线程管理 + self.connection_thread = None + self.streaming_thread = None + self.running = False + + # 性能统计 + self.frame_count = 0 + self.last_fps_time = time.time() + self.current_fps = 0 + self.latency = 0 + + print("✓ OpenXR串流处理器初始化完成") + + def initialize(self): + """初始化OpenXR串流""" + try: + # 检查串流服务器是否运行 + if not self._check_streaming_server(): + print("串流服务器未运行,尝试启动...") + if not self._start_streaming_server(): + print("无法启动串流服务器") + return False + + # 连接到串流服务器 + if not self._connect_to_server(): + print("无法连接到串流服务器") + return False + + # 配置流媒体设置 + self._configure_streaming() + + # 启动串流线程 + self._start_streaming_threads() + + print("✓ OpenXR串流初始化成功") + return True + + except Exception as e: + print(f"OpenXR串流初始化错误: {str(e)}") + return False + + def _check_streaming_server(self): + """检查串流服务器是否运行""" + try: + # TODO: 实际检查串流服务器状态 + # 这里暂时返回False以便在开发阶段使用模拟模式 + return False + except: + return False + + def _start_streaming_server(self): + """启动串流服务器""" + try: + # TODO: 实际启动串流服务器 + print("ℹ 串流服务器启动占位符") + return False + except Exception as e: + print(f"串流服务器启动失败: {str(e)}") + return False + + def _connect_to_server(self): + """连接到串流服务器""" + try: + # TODO: 实际连接到串流服务器 + print("ℹ 串流服务器连接占位符") + return False + except Exception as e: + print(f"串流服务器连接失败: {str(e)}") + return False + + def _configure_streaming(self): + """配置流媒体设置""" + # 根据OpenXR管理器设置配置串流参数 + if self.openxr_manager: + render_size = self.openxr_manager.get_openxr_info()['render_size'] + self.stream_width = render_size[0] + self.stream_height = render_size[1] + + print(f"ℹ 串流配置: {self.stream_width}x{self.stream_height}@{self.stream_fps}fps") + + def _start_streaming_threads(self): + """启动串流线程""" + self.running = True + + # 启动连接线程 + self.connection_thread = threading.Thread(target=self._connection_handler, daemon=True) + self.connection_thread.start() + + # 启动串流线程 + self.streaming_thread = threading.Thread(target=self._streaming_handler, daemon=True) + self.streaming_thread.start() + + def _connection_handler(self): + """连接处理线程""" + while self.running: + try: + # TODO: 实现连接处理逻辑 + time.sleep(1) + except Exception as e: + print(f"连接处理错误: {str(e)}") + + def _streaming_handler(self): + """串流处理线程""" + while self.running: + try: + if self.streaming: + self._send_video_frames() + time.sleep(1.0 / self.stream_fps) + except Exception as e: + print(f"串流处理错误: {str(e)}") + + def _send_video_frames(self): + """发送视频帧""" + try: + # 获取左右眼纹理 + if self.openxr_manager and hasattr(self.openxr_manager, 'eye_textures'): + left_texture = self.openxr_manager.eye_textures.get('left') + right_texture = self.openxr_manager.eye_textures.get('right') + + if left_texture and right_texture: + # TODO: 实现视频编码和发送逻辑 + self.frame_count += 1 + + # 更新FPS统计 + current_time = time.time() + if current_time - self.last_fps_time >= 1.0: + self.current_fps = self.frame_count + self.frame_count = 0 + self.last_fps_time = current_time + # print(f"串流FPS: {self.current_fps}") + + except Exception as e: + print(f"视频帧发送错误: {str(e)}") + + def start_streaming(self): + """开始OpenXR串流""" + if not self.connected: + print("串流服务器未连接") + return False + + self.streaming = True + print("✓ OpenXR串流已开始") + return True + + def stop_streaming(self): + """停止OpenXR串流""" + self.streaming = False + print("✓ OpenXR串流已停止") + + def get_streaming_status(self): + """获取串流状态""" + return { + "connected": self.connected, + "streaming": self.streaming, + "fps": self.current_fps, + "latency": self.latency, + "resolution": f"{self.stream_width}x{self.stream_height}", + "bitrate": self.bitrate, + "codec": self.codec + } + + def set_stream_quality(self, width, height, fps, bitrate): + """设置串流质量""" + self.stream_width = width + self.stream_height = height + self.stream_fps = fps + self.bitrate = bitrate + + print(f"✓ 串流质量已设置: {width}x{height}@{fps}fps, {bitrate}Mbps") + return True + + def send_haptic_feedback(self, controller_id, duration, intensity): + """发送触觉反馈""" + try: + # TODO: 实现触觉反馈发送逻辑 + print(f"ℹ 触觉反馈发送占位符: 控制器{controller_id}, 持续时间{duration}s, 强度{intensity}") + return True + except Exception as e: + print(f"触觉反馈发送失败: {str(e)}") + return False + + def shutdown(self): + """关闭串流""" + self.running = False + self.stop_streaming() + + # 等待线程结束 + if self.connection_thread and self.connection_thread.is_alive(): + self.connection_thread.join(timeout=2) + + if self.streaming_thread and self.streaming_thread.is_alive(): + self.streaming_thread.join(timeout=2) + + # 关闭套接字 + if self.server_socket: + self.server_socket.close() + + if self.streaming_socket: + self.streaming_socket.close() + + print("✓ OpenXR串流已关闭") + + def is_connected(self): + """检查是否连接""" + return self.connected + + def is_streaming(self): + """检查是否正在串流""" + return self.streaming \ No newline at end of file diff --git a/core/vr_input_handler.py b/core/vr_input_handler.py deleted file mode 100644 index 2438e601..00000000 --- a/core/vr_input_handler.py +++ /dev/null @@ -1,430 +0,0 @@ -""" -VR输入处理器 - -处理VR控制器输入、手势识别和VR交互 -支持多种VR控制器和手势输入 -""" - -from direct.showbase.DirectObject import DirectObject -from panda3d.core import Vec3, Point3, CollisionRay, CollisionNode, CollisionHandlerQueue -from direct.task import Task -import time - - -class VRInputHandler(DirectObject): - """VR输入处理器""" - - def __init__(self, world, vr_manager): - super().__init__() - self.world = world - self.vr_manager = vr_manager - - # 控制器状态 - self.controllers = {} - self.controller_nodes = {} - self.controller_rays = {} - - # 手势识别 - self.gesture_enabled = True - self.gesture_history = [] - self.gesture_threshold = 0.1 - - # 交互系统 - self.interaction_enabled = True - self.selected_object = None - self.grab_offset = Vec3(0, 0, 0) - - # 输入映射 - self.input_mappings = { - 'trigger': self._handle_trigger, - 'grip': self._handle_grip, - 'touchpad': self._handle_touchpad, - 'menu': self._handle_menu, - 'system': self._handle_system - } - - print("✓ VR输入处理器初始化完成") - - def start_input_handling(self): - """启动输入处理""" - if not self.vr_manager.is_vr_enabled(): - print("VR未启用,无法启动输入处理") - return False - - # 启动输入更新任务 - self.world.taskMgr.add(self._update_input, "vr_input_update") - - # 设置控制器可视化 - self._setup_controller_visualization() - - print("✓ VR输入处理已启动") - return True - - def stop_input_handling(self): - """停止输入处理""" - self.world.taskMgr.remove("vr_input_update") - self._cleanup_controller_visualization() - print("✓ VR输入处理已停止") - - def _update_input(self, task): - """更新输入处理""" - if not self.vr_manager.is_vr_enabled(): - return Task.cont - - try: - # 更新所有控制器 - self._update_controllers() - - # 处理手势识别 - if self.gesture_enabled: - self._process_gestures() - - # 处理交互 - if self.interaction_enabled: - self._process_interactions() - - except Exception as e: - print(f"VR输入更新错误: {str(e)}") - - return Task.cont - - def _update_controllers(self): - """更新控制器状态""" - # 获取控制器姿态 - controller_poses = self.vr_manager.controller_poses - - for controller_id, pose in controller_poses.items(): - # 获取控制器输入 - input_data = self.vr_manager.get_controller_input(controller_id) - if not input_data: - continue - - # 更新控制器状态 - if controller_id not in self.controllers: - self.controllers[controller_id] = {} - - prev_state = self.controllers[controller_id].copy() - self.controllers[controller_id] = input_data - - # 更新控制器可视化 - self._update_controller_visualization(controller_id, pose) - - # 处理输入事件 - self._process_controller_input(controller_id, input_data, prev_state) - - def _process_controller_input(self, controller_id, current_state, prev_state): - """处理控制器输入""" - # 检查按钮状态变化 - for input_type, handler in self.input_mappings.items(): - if input_type in current_state: - current_value = current_state[input_type] - prev_value = prev_state.get(input_type, 0) - - # 处理按钮按下/释放 - if isinstance(current_value, (int, float)): - if current_value > 0.5 and prev_value <= 0.5: - handler(controller_id, 'press', current_value) - elif current_value <= 0.5 and prev_value > 0.5: - handler(controller_id, 'release', current_value) - elif current_value > 0.5: - handler(controller_id, 'hold', current_value) - - # 处理触摸板 - elif isinstance(current_value, tuple) and len(current_value) == 2: - if current_value != prev_value: - handler(controller_id, 'move', current_value) - - def _handle_trigger(self, controller_id, action, value): - """处理扳机输入""" - if action == 'press': - print(f"控制器 {controller_id} 扳机按下 (强度: {value:.2f})") - self._try_grab_object(controller_id) - elif action == 'release': - print(f"控制器 {controller_id} 扳机释放") - self._try_release_object(controller_id) - - def _handle_grip(self, controller_id, action, value): - """处理握持输入""" - if action == 'press': - print(f"控制器 {controller_id} 握持按下 (强度: {value:.2f})") - self._toggle_interaction_mode(controller_id) - elif action == 'release': - print(f"控制器 {controller_id} 握持释放") - - def _handle_touchpad(self, controller_id, action, value): - """处理触摸板输入""" - if action == 'move': - x, y = value - print(f"控制器 {controller_id} 触摸板: ({x:.2f}, {y:.2f})") - - # 根据触摸板位置执行不同操作 - if abs(x) > 0.7: # 左右滑动 - self._handle_horizontal_swipe(controller_id, x) - elif abs(y) > 0.7: # 上下滑动 - self._handle_vertical_swipe(controller_id, y) - - def _handle_menu(self, controller_id, action, value): - """处理菜单按钮""" - if action == 'press': - print(f"控制器 {controller_id} 菜单按钮按下") - self._show_vr_menu(controller_id) - - def _handle_system(self, controller_id, action, value): - """处理系统按钮""" - if action == 'press': - print(f"控制器 {controller_id} 系统按钮按下") - # 系统按钮通常由VR系统处理 - - def _handle_horizontal_swipe(self, controller_id, direction): - """处理水平滑动""" - if direction > 0: - print(f"控制器 {controller_id} 右滑") - self._switch_tool(controller_id, 'next') - else: - print(f"控制器 {controller_id} 左滑") - self._switch_tool(controller_id, 'prev') - - def _handle_vertical_swipe(self, controller_id, direction): - """处理垂直滑动""" - if direction > 0: - print(f"控制器 {controller_id} 上滑") - self._zoom_in(controller_id) - else: - print(f"控制器 {controller_id} 下滑") - self._zoom_out(controller_id) - - def _try_grab_object(self, controller_id): - """尝试抓取对象""" - if controller_id not in self.controllers: - return - - # 获取控制器射线 - ray = self._get_controller_ray(controller_id) - if not ray: - return - - # 执行射线检测 - hit_object = self._raycast_from_controller(controller_id) - if hit_object: - self.selected_object = hit_object - controller_pose = self.controllers[controller_id].get('pose') - if controller_pose: - # 计算抓取偏移 - object_pos = hit_object.getPos() - controller_pos = controller_pose.getTranslate() - self.grab_offset = object_pos - controller_pos - - print(f"抓取对象: {hit_object.getName()}") - - # 发送抓取事件 - self.world.event_handler.messenger.send('vr-object-grabbed', [hit_object, controller_id]) - - def _try_release_object(self, controller_id): - """尝试释放对象""" - if self.selected_object: - print(f"释放对象: {self.selected_object.getName()}") - - # 发送释放事件 - self.world.event_handler.messenger.send('vr-object-released', [self.selected_object, controller_id]) - - self.selected_object = None - self.grab_offset = Vec3(0, 0, 0) - - def _raycast_from_controller(self, controller_id): - """从控制器发射射线检测""" - if controller_id not in self.controllers: - return None - - controller_pose = self.controllers[controller_id].get('pose') - if not controller_pose: - return None - - # 获取控制器位置和方向 - controller_pos = controller_pose.getTranslate() - controller_forward = controller_pose.getQuat().getForward() - - # 创建射线 - ray = CollisionRay() - ray.setOrigin(controller_pos) - ray.setDirection(controller_forward) - - # 执行碰撞检测 - traverser = self.world.cTrav if hasattr(self.world, 'cTrav') else None - if not traverser: - return None - - handler = CollisionHandlerQueue() - collision_node = CollisionNode('vr_controller_ray') - collision_node.addSolid(ray) - - ray_np = self.world.render.attachNewNode(collision_node) - traverser.addCollider(ray_np, handler) - - # 遍历碰撞 - traverser.traverse(self.world.render) - - # 清理 - ray_np.removeNode() - - # 返回最近的碰撞对象 - if handler.getNumEntries() > 0: - handler.sortEntries() - entry = handler.getEntry(0) - return entry.getIntoNodePath() - - return None - - def _get_controller_ray(self, controller_id): - """获取控制器射线""" - return self.controller_rays.get(controller_id) - - def _setup_controller_visualization(self): - """设置控制器可视化""" - print("设置控制器可视化...") - - # 为每个控制器创建可视化节点 - for controller_id in self.controllers: - self._create_controller_model(controller_id) - - def _create_controller_model(self, controller_id): - """创建控制器模型""" - # 创建简单的控制器模型(立方体) - from panda3d.core import CardMaker - - cm = CardMaker(f"controller_{controller_id}") - cm.setFrame(-0.05, 0.05, -0.05, 0.05) - - controller_node = self.world.render.attachNewNode(cm.generate()) - controller_node.setColor(0.2, 0.8, 1.0, 0.8) - controller_node.setScale(0.1, 0.2, 0.05) - - self.controller_nodes[controller_id] = controller_node - - # 创建控制器射线可视化 - self._create_controller_ray_visual(controller_id) - - def _create_controller_ray_visual(self, controller_id): - """创建控制器射线可视化""" - from panda3d.core import LineSegs - - # 创建射线线段 - lines = LineSegs() - lines.setColor(1, 0, 0, 0.5) - lines.moveTo(0, 0, 0) - lines.drawTo(0, 2, 0) # 2米长的射线 - - ray_node = self.world.render.attachNewNode(lines.create()) - ray_node.setRenderModeWireframe() - ray_node.hide() # 默认隐藏 - - self.controller_rays[controller_id] = ray_node - - def _update_controller_visualization(self, controller_id, pose): - """更新控制器可视化""" - if controller_id in self.controller_nodes: - node = self.controller_nodes[controller_id] - node.setMat(pose) - - if controller_id in self.controller_rays: - ray_node = self.controller_rays[controller_id] - ray_node.setMat(pose) - - def _cleanup_controller_visualization(self): - """清理控制器可视化""" - for node in self.controller_nodes.values(): - node.removeNode() - - for ray in self.controller_rays.values(): - ray.removeNode() - - self.controller_nodes.clear() - self.controller_rays.clear() - - def _process_gestures(self): - """处理手势识别""" - # 简单的手势识别逻辑 - # 这里可以实现更复杂的手势识别算法 - pass - - def _process_interactions(self): - """处理交互逻辑""" - # 如果有选中的对象,更新其位置 - if self.selected_object: - self._update_grabbed_object() - - def _update_grabbed_object(self): - """更新被抓取对象的位置""" - if not self.selected_object: - return - - # 找到抓取该对象的控制器 - grabbing_controller = None - for controller_id, controller_state in self.controllers.items(): - if controller_state.get('trigger', 0) > 0.5: - grabbing_controller = controller_id - break - - if not grabbing_controller: - return - - # 更新对象位置 - controller_pose = self.controllers[grabbing_controller].get('pose') - if controller_pose: - controller_pos = controller_pose.getTranslate() - new_pos = controller_pos + self.grab_offset - self.selected_object.setPos(new_pos) - - def _toggle_interaction_mode(self, controller_id): - """切换交互模式""" - self.interaction_enabled = not self.interaction_enabled - print(f"交互模式: {'启用' if self.interaction_enabled else '禁用'}") - - def _show_vr_menu(self, controller_id): - """显示VR菜单""" - print(f"显示VR菜单 (控制器 {controller_id})") - # 这里可以实现VR菜单显示逻辑 - pass - - def _switch_tool(self, controller_id, direction): - """切换工具""" - print(f"切换工具: {direction} (控制器 {controller_id})") - # 这里可以实现工具切换逻辑 - pass - - def _zoom_in(self, controller_id): - """放大""" - print(f"放大 (控制器 {controller_id})") - # 实现放大逻辑 - pass - - def _zoom_out(self, controller_id): - """缩小""" - print(f"缩小 (控制器 {controller_id})") - # 实现缩小逻辑 - pass - - def show_controller_rays(self, show=True): - """显示/隐藏控制器射线""" - for ray in self.controller_rays.values(): - if show: - ray.show() - else: - ray.hide() - - def get_controller_state(self, controller_id): - """获取控制器状态""" - return self.controllers.get(controller_id, {}) - - def get_all_controllers(self): - """获取所有控制器""" - return list(self.controllers.keys()) - - def set_gesture_enabled(self, enabled): - """设置手势识别启用状态""" - self.gesture_enabled = enabled - print(f"手势识别: {'启用' if enabled else '禁用'}") - - def set_interaction_enabled(self, enabled): - """设置交互启用状态""" - self.interaction_enabled = enabled - print(f"VR交互: {'启用' if enabled else '禁用'}") \ No newline at end of file diff --git a/core/vr_manager.py b/core/vr_manager.py deleted file mode 100644 index f4cb0832..00000000 --- a/core/vr_manager.py +++ /dev/null @@ -1,666 +0,0 @@ -import warnings -warnings.filterwarnings("ignore", category=DeprecationWarning) - -from panda3d.core import * -from panda3d.core import Mat4, PerspectiveLens, CardMaker -# 修复taskMgr导入错误,应该从TaskManagerGlobal导入而不是ShowBaseGlobal -from direct.task.TaskManagerGlobal import taskMgr -from direct.showbase.DirectObject import DirectObject -from direct.task import Task -import sys - -class VRManager(DirectObject): - """VR管理器 - 处理VR系统初始化、追踪和渲染""" - - def __init__(self, world): - super().__init__() - self.world = world - self.vr_enabled = False - self.vr_system = None - self.vr_compositor = None - self.render_width = 1920 - self.render_height = 1080 - self.alvr_enabled = False - - # 模拟模式设置 - self.simulation_mode = False - self.simulation_data = { - 'head_pose': {'position': [0, 0, 1.6], 'rotation': [0, 0, 0, 1]}, - 'controller_poses': { - 0: {'position': [-0.3, 0, 1.2], 'rotation': [0, 0, 0, 1], 'connected': True}, - 1: {'position': [0.3, 0, 1.2], 'rotation': [0, 0, 0, 1], 'connected': True} - }, - 'render_size': (1920, 1080) - } - - # 立体渲染缓冲区 - self.left_eye_buffer = None - self.right_eye_buffer = None - self.left_eye_camera = None - self.right_eye_camera = None - - # 眼部纹理(用于ALVR串流) - self.left_eye_texture = None - self.right_eye_texture = None - self.eye_textures = {} - - # 控制器相关 - self.controller_nodes = {} - self.controller_poses = {} - - print("✓ VR管理器初始化完成") - - def initialize_vr(self, force_simulation=False): - """初始化VR系统""" - try: - # 检查是否强制使用模拟模式 - if force_simulation: - print("🔧 强制启用VR模拟模式") - return self._init_simulation_mode() - - # 检查OpenVR支持 - if not self._check_openvr_support(): - print("⚠ OpenVR支持不可用,切换到模拟模式") - return self._init_simulation_mode() - - # 尝试初始化OpenVR - if not self._init_openvr(): - print("⚠ OpenVR初始化失败,切换到模拟模式") - print("提示: 请确保SteamVR正在运行且VR头盔已连接") - return self._init_simulation_mode() - - # 真实VR模式初始化成功 - print("✓ 真实VR模式初始化成功") - return self._init_real_vr_mode() - - except Exception as e: - print(f"VR初始化错误: {str(e)}") - print("⚠ 切换到模拟模式") - return self._init_simulation_mode() - - def _init_simulation_mode(self): - """初始化模拟模式""" - try: - self.simulation_mode = True - - # 使用模拟数据设置渲染尺寸 - self.render_width, self.render_height = self.simulation_data['render_size'] - print(f"🎮 模拟VR渲染尺寸: {self.render_width}x{self.render_height}") - - # 设置模拟立体渲染 - self._setup_stereo_rendering() - - # 启动模拟VR任务 - self._start_simulation_tasks() - - self.vr_enabled = True - print("✓ VR模拟模式初始化完成") - print("ℹ 模拟模式说明:") - print(" - 头盔追踪: 模拟数据") - print(" - 控制器: 模拟两个控制器") - print(" - 渲染: 立体渲染到窗口") - print(" - 交互: 键盘鼠标模拟") - - return True - - except Exception as e: - print(f"模拟模式初始化错误: {str(e)}") - return False - - def _init_real_vr_mode(self): - """初始化真实VR模式""" - try: - self.simulation_mode = False - - # 设置立体渲染 - self._setup_stereo_rendering() - - # 初始化ALVR - if self._init_alvr(): - print("✓ ALVR串流已启用") - self.alvr_enabled = True - - # 启动VR更新任务 - self._start_vr_tasks() - - self.vr_enabled = True - print("✓ 真实VR系统初始化完成") - return True - - except Exception as e: - print(f"真实VR模式初始化错误: {str(e)}") - return False - - def _check_openvr_support(self): - """检查OpenVR支持""" - try: - import openvr - return True - except ImportError: - print("OpenVR库未安装") - return False - - def _init_openvr(self): - """初始化OpenVR""" - try: - import openvr - - # 初始化OpenVR - self.vr_system = openvr.init(openvr.VRApplication_Scene) - if not self.vr_system: - return False - - # 获取合成器 - self.vr_compositor = openvr.VRCompositor() - if not self.vr_compositor: - return False - - # 获取推荐的渲染尺寸 - self.render_width, self.render_height = self.vr_system.getRecommendedRenderTargetSize() - print(f"VR推荐渲染尺寸: {self.render_width}x{self.render_height}") - - return True - - except Exception as e: - print(f"OpenVR初始化错误: {str(e)}") - return False - - def _setup_stereo_rendering(self): - """设置立体渲染""" - try: - # 创建眼部缓冲区 - self._create_eye_buffers() - - # 创建眼部摄像机 - self._create_eye_cameras() - - # 设置渲染目标 - self._setup_render_targets() - - print("✓ 立体渲染设置完成") - - except Exception as e: - print(f"立体渲染设置错误: {str(e)}") - - def _create_eye_buffers(self): - """创建眼部渲染缓冲区""" - try: - # 创建左眼缓冲区 - self.left_eye_buffer = self.world.win.makeTextureBuffer( - "left_eye", self.render_width, self.render_height - ) - self.left_eye_buffer.setSort(-100) - - # 创建右眼缓冲区 - self.right_eye_buffer = self.world.win.makeTextureBuffer( - "right_eye", self.render_width, self.render_height - ) - self.right_eye_buffer.setSort(-99) - - # 获取纹理并确保它们可以被访问 - self.left_eye_texture = self.left_eye_buffer.getTexture() - self.right_eye_texture = self.right_eye_buffer.getTexture() - - # 设置纹理参数以确保它们可以被ALVR访问 - if self.left_eye_texture: - self.left_eye_texture.set_format(Texture.F_rgba8) - self.left_eye_texture.set_component_type(Texture.T_unsigned_byte) - self.left_eye_texture.set_wrap_u(Texture.WM_clamp) - self.left_eye_texture.set_wrap_v(Texture.WM_clamp) - # 确保纹理数据可以被读取到RAM(使用正确的方法) - try: - self.left_eye_texture.set_ram_image_compression(Texture.CM_off) - except AttributeError: - # 如果方法不存在,使用替代方法 - pass - - if self.right_eye_texture: - self.right_eye_texture.set_format(Texture.F_rgba8) - self.right_eye_texture.set_component_type(Texture.T_unsigned_byte) - self.right_eye_texture.set_wrap_u(Texture.WM_clamp) - self.right_eye_texture.set_wrap_v(Texture.WM_clamp) - # 确保纹理数据可以被读取到RAM(使用正确的方法) - try: - self.right_eye_texture.set_ram_image_compression(Texture.CM_off) - except AttributeError: - # 如果方法不存在,使用替代方法 - pass - - # 存储到eye_textures字典中供ALVR使用 - self.eye_textures['left'] = self.left_eye_texture - self.eye_textures['right'] = self.right_eye_texture - - print("✓ 眼部渲染缓冲区创建完成") - print(f" 左眼纹理: {self.left_eye_texture.getXSize()}x{self.left_eye_texture.getYSize()}") - print(f" 右眼纹理: {self.right_eye_texture.getXSize()}x{self.right_eye_texture.getYSize()}") - - except Exception as e: - print(f"眼部缓冲区创建错误: {str(e)}") - import traceback - traceback.print_exc() - - def _create_eye_cameras(self): - """创建眼部摄像机""" - try: - # 创建左眼摄像机 - self.left_eye_camera = self.world.makeCamera(self.left_eye_buffer) - self.left_eye_camera.setPos(-0.032, 0, 0) # 瞳距的一半 - - # 创建右眼摄像机 - self.right_eye_camera = self.world.makeCamera(self.right_eye_buffer) - self.right_eye_camera.setPos(0.032, 0, 0) # 瞳距的一半 - - # 设置投影矩阵 - if not self.simulation_mode: - self._update_eye_projection(0, self.left_eye_camera.node().getLens()) - self._update_eye_projection(1, self.right_eye_camera.node().getLens()) - else: - # 模拟模式使用标准透视投影 - lens = PerspectiveLens() - lens.setFov(110) # 模拟VR FOV - lens.setNearFar(0.1, 1000) - self.left_eye_camera.node().setLens(lens) - self.right_eye_camera.node().setLens(lens) - - print("✓ 眼部摄像机创建完成") - - except Exception as e: - print(f"眼部摄像机创建错误: {str(e)}") - - def _update_eye_projection(self, eye, lens): - """更新眼部投影矩阵""" - try: - if self.simulation_mode: - # 模拟模式使用标准投影 - perspective_lens = PerspectiveLens() - perspective_lens.setFov(110) - perspective_lens.setNearFar(0.1, 1000) - lens.copyFrom(perspective_lens) - return - import openvr - # 获取投影矩阵 - projection_matrix = self.vr_system.getProjectionMatrix(eye, 0.1, 1000.0) - # 转换为 Panda3D 矩阵 - panda_matrix = self._convert_openvr_matrix(projection_matrix) - # 使用正确的方法设置自定义投影矩阵 - try: - lens.set_user_mat(panda_matrix) - except AttributeError: - # 如果方法不存在,尝试替代方法 - try: - lens.set_projection_mat(panda_matrix) - except AttributeError: - # 兜底:设置标准 FOV - lens.setFov(110) - lens.setNearFar(0.1, 1000) - except Exception as e: - print(f"眼部投影更新错误: {str(e)}") - - def _setup_render_targets(self): - """设置渲染目标""" - try: - # 在模拟模式下,可以选择将渲染结果显示到主窗口 - if self.simulation_mode: - # 创建并排显示的立体视图 - self._setup_simulation_display() - - print("✓ 渲染目标设置完成") - - except Exception as e: - print(f"渲染目标设置错误: {str(e)}") - - def _setup_simulation_display(self): - """设置模拟显示""" - try: - # 创建卡片来显示眼部纹理 - cm = CardMaker("stereo_display") - - # 左眼显示区域 - cm.setFrame(-1, 0, -1, 1) - left_card = self.world.render2d.attachNewNode(cm.generate()) - left_card.setTexture(self.left_eye_texture) - - # 右眼显示区域 - cm.setFrame(0, 1, -1, 1) - right_card = self.world.render2d.attachNewNode(cm.generate()) - right_card.setTexture(self.right_eye_texture) - - print("✓ 模拟立体显示设置完成") - - except Exception as e: - print(f"模拟显示设置错误: {str(e)}") - - def _init_alvr(self): - """初始化ALVR(仅在真实VR模式下)""" - if self.simulation_mode: - print("ℹ 模拟模式: ALVR串流已跳过") - return False - - try: - # ALVR初始化逻辑 - # 这里应该连接到ALVR服务器 - print("✓ ALVR初始化完成") - return True - except Exception as e: - print(f"ALVR初始化错误: {str(e)}") - return False - - def _start_vr_tasks(self): - """启动VR更新任务(真实VR模式)""" - if not self.simulation_mode: - self.world.taskMgr.add(self._update_vr_tracking, "vr_tracking") - self.world.taskMgr.add(self._update_vr_rendering, "vr_rendering") - - def _start_simulation_tasks(self): - """启动模拟VR任务""" - self.world.taskMgr.add(self._update_simulation_tracking, "simulation_tracking") - self.world.taskMgr.add(self._update_simulation_rendering, "simulation_rendering") - - def _update_simulation_tracking(self, task): - """更新模拟追踪数据""" - try: - # 模拟头部追踪(可以添加一些变化) - import math - time_factor = task.time * 0.5 - - # 模拟轻微的头部摆动 - head_pos = self.simulation_data['head_pose']['position'] - head_pos[1] = math.sin(time_factor) * 0.05 # 前后轻微摆动 - - # 更新主摄像机位置 - if hasattr(self.world, 'camera'): - self.world.camera.setPos(head_pos[0], head_pos[1], head_pos[2]) - - # 更新控制器位置(模拟手部动作) - for controller_id, pose in self.simulation_data['controller_poses'].items(): - if pose['connected']: - # 模拟控制器轻微移动 - pose['position'][1] = math.sin(time_factor + controller_id) * 0.1 - - # 更新控制器节点位置 - if controller_id in self.controller_nodes: - node = self.controller_nodes[controller_id] - node.setPos(pose['position'][0], pose['position'][1], pose['position'][2]) - - return task.cont - - except Exception as e: - print(f"模拟追踪更新错误: {str(e)}") - return task.cont - - def _update_simulation_rendering(self, task): - """更新模拟渲染""" - try: - # 在模拟模式下,渲染已经由Panda3D自动处理 - # 这里可以添加任何特殊的渲染逻辑 - return task.cont - - except Exception as e: - print(f"模拟渲染更新错误: {str(e)}") - return task.cont - - def _update_vr_tracking(self, task): - """更新VR追踪数据(真实VR模式)""" - try: - if not self.vr_system or self.simulation_mode: - return task.cont - - import openvr - - # 获取设备姿态 - poses, game_poses = self.vr_compositor.waitGetPoses(None, None) - - # 检查poses是否有效 - if poses is None: - print("VR追踪更新错误: 无法获取设备姿态数据") - return task.cont - - # 更新头显位置 - if poses[openvr.k_unTrackedDeviceIndex_Hmd].bPoseIsValid: - self._update_main_camera_pose() - - # 更新眼部摄像机 - self._update_eye_cameras() - - # 更新控制器姿态 - self._update_controller_poses(poses) - - return task.cont - - except Exception as e: - print(f"VR追踪更新错误: {str(e)}") - return task.cont - - def _update_vr_rendering(self, task): - """更新VR渲染(真实VR模式)""" - try: - if not self.vr_compositor or self.simulation_mode: - return task.cont - - # 确保渲染完成后再提交帧 - if hasattr(self.world, 'graphicsEngine'): - self.world.graphicsEngine.render_frame() - - # 提交帧到合成器 - self._submit_frames_to_compositor() - - return task.cont - - except Exception as e: - print(f"VR渲染更新错误: {str(e)}") - return task.cont - - def _convert_openvr_matrix(self, openvr_matrix): - """转换OpenVR矩阵为Panda3D矩阵""" - # 实现矩阵转换逻辑 - mat = Mat4() - # 这里需要实现具体的矩阵转换 - return mat - - def _update_main_camera_pose(self): - """更新主摄像机姿态""" - try: - if self.simulation_mode: - return - - # 从VR系统获取头显姿态并应用到主摄像机 - pass - - except Exception as e: - print(f"主摄像机姿态更新错误: {str(e)}") - - def _update_eye_cameras(self): - """更新眼部摄像机""" - try: - if self.simulation_mode: - return - - # 更新左右眼摄像机位置 - pass - - except Exception as e: - print(f"眼部摄像机更新错误: {str(e)}") - - def _update_controller_poses(self, poses): - """更新控制器姿态""" - try: - if self.simulation_mode: - return - - # 更新控制器位置和姿态 - pass - - except Exception as e: - print(f"控制器姿态更新错误: {str(e)}") - - def _submit_frames_to_compositor(self): - """提交帧到合成器""" - try: - if not self.vr_compositor or self.simulation_mode: - return - - import openvr - - # 确保纹理已正确创建并有效 - if not self.left_eye_texture or not self.right_eye_texture: - print("⚠ 眼部纹理未正确创建,无法提交帧") - return - - # 检查纹理是否准备好 - if not self.left_eye_texture.has_ram_image() or not self.right_eye_texture.has_ram_image(): - # 强制更新纹理数据 - try: - self.world.graphicsEngine.extract_texture_data(self.left_eye_texture, self.world.win.get_gsg()) - self.world.graphicsEngine.extract_texture_data(self.right_eye_texture, self.world.win.get_gsg()) - except Exception as e: - print(f"纹理数据提取失败: {str(e)}") - - # 提交左眼纹理 - left_eye_texture = openvr.Texture_t() - # 使用正确的API获取纹理ID - try: - tex_handle = self.left_eye_texture.get_handle() - except: - # 如果get_handle方法不可用,尝试其他方式 - tex_handle = self.left_eye_texture.get_texture_id() if hasattr(self.left_eye_texture, 'get_texture_id') else 0 - - left_eye_texture.handle = tex_handle - left_eye_texture.eType = openvr.TextureType_OpenGL - left_eye_texture.eColorSpace = openvr.ColorSpace_Gamma - - # 检查纹理是否有效后再提交 - if tex_handle: - self.vr_compositor.submit(openvr.Eye_Left, left_eye_texture) - - # 提交右眼纹理 - right_eye_texture = openvr.Texture_t() - # 使用正确的API获取纹理ID - try: - tex_handle = self.right_eye_texture.get_handle() - except: - # 如果get_handle方法不可用,尝试其他方式 - tex_handle = self.right_eye_texture.get_texture_id() if hasattr(self.right_eye_texture, 'get_texture_id') else 0 - - right_eye_texture.handle = tex_handle - right_eye_texture.eType = openvr.TextureType_OpenGL - right_eye_texture.eColorSpace = openvr.ColorSpace_Gamma - - # 检查纹理是否有效后再提交 - if tex_handle: - self.vr_compositor.submit(openvr.Eye_Right, right_eye_texture) - - # 等待下一帧垂直同步 - self.vr_compositor.waitGetPoses(None, None) - - except Exception as e: - print(f"帧提交错误: {str(e)}") - import traceback - traceback.print_exc() - - def get_controller_input(self, controller_id): - """获取控制器输入""" - try: - if self.simulation_mode: - # 返回模拟的控制器输入 - return { - 'trigger': 0.0, - 'grip': 0.0, - 'touchpad': {'x': 0.0, 'y': 0.0, 'pressed': False}, - 'menu': False, - 'connected': controller_id in self.simulation_data['controller_poses'] - } - - if not self.vr_system: - return None - - import openvr - - # 获取控制器状态 - result, controller_state = self.vr_system.getControllerState(controller_id) - if not result: - return None - - # 解析控制器输入 - return { - 'trigger': controller_state.rAxis[1].x, - 'grip': controller_state.rAxis[2].x, - 'touchpad': { - 'x': controller_state.rAxis[0].x, - 'y': controller_state.rAxis[0].y, - 'pressed': controller_state.rAxis[0].x != 0 or controller_state.rAxis[0].y != 0 - }, - 'menu': controller_state.ulButtonPressed & (1 << openvr.k_EButton_ApplicationMenu) != 0, - 'connected': True - } - - except Exception as e: - print(f"控制器输入获取错误: {str(e)}") - return None - - def shutdown_vr(self): - """关闭VR系统""" - try: - # 停止任务 - if self.simulation_mode: - self.world.taskMgr.remove("simulation_tracking") - self.world.taskMgr.remove("simulation_rendering") - else: - self.world.taskMgr.remove("vr_tracking") - self.world.taskMgr.remove("vr_rendering") - - # 关闭OpenVR - if self.vr_system and not self.simulation_mode: - import openvr - openvr.shutdown() - - # 清理资源 - self.left_eye_buffer = None - self.right_eye_buffer = None - self.left_eye_camera = None - self.right_eye_camera = None - - self.vr_enabled = False - self.simulation_mode = False - - print("✓ VR系统已关闭") - - except Exception as e: - print(f"VR关闭错误: {str(e)}") - - def is_vr_enabled(self): - """检查VR是否启用""" - return self.vr_enabled - - def get_vr_info(self): - """获取VR系统信息""" - info = { - 'enabled': self.vr_enabled, - 'simulation_mode': self.simulation_mode, - 'render_size': (self.render_width, self.render_height), - 'alvr_enabled': self.alvr_enabled - } - - if self.simulation_mode: - info['mode'] = 'simulation' - info['controllers'] = len(self.simulation_data['controller_poses']) - else: - info['mode'] = 'real_vr' - info['openvr_connected'] = self.vr_system is not None - - return info - - # 便捷方法 - def enable_simulation_mode(self): - """启用模拟模式(调试用)""" - return self.initialize_vr(force_simulation=True) - - def get_simulation_data(self): - """获取模拟数据""" - return self.simulation_data if self.simulation_mode else None - - def update_simulation_data(self, key, value): - """更新模拟数据""" - if self.simulation_mode and key in self.simulation_data: - self.simulation_data[key] = value - return True - return False \ No newline at end of file diff --git a/demo/OPENXR_IMPLEMENTATION_GUIDE.md b/demo/OPENXR_IMPLEMENTATION_GUIDE.md new file mode 100644 index 00000000..75cb804b --- /dev/null +++ b/demo/OPENXR_IMPLEMENTATION_GUIDE.md @@ -0,0 +1,346 @@ +# OpenXR 实现指南 + +## 🎯 概述 + +本指南详细介绍了如何在Panda3D引擎中实现OpenXR支持,并通过有线连接串流到VR设备。 + +## 🏗️ 系统架构 + +### 核心组件 + +1. **OpenXRManager** (`core/openxr_manager.py`) + - 负责OpenXR系统初始化 + - 管理立体渲染(左右眼) + - 处理VR设备跟踪 + - 集成OpenXR接口 + +2. **OpenXRInputHandler** (`core/openxr_input_handler.py`) + - 处理OpenXR控制器输入 + - 手势识别系统 + - 交互逻辑处理 + +3. **OpenXRStreamer** (`core/openxr_streamer.py`) + - OpenXR服务器通信 + - 视频流编码和传输 + - 性能监控 + +## 🔧 安装和配置 + +### 1. 系统依赖 + +```bash +# 安装Python依赖 +pip install -r requirements/openxr-requirements.txt + +# 安装OpenXR运行时 +# Windows: +# 1. 下载并安装SteamVR或Oculus运行时 +# 2. 安装OpenXR运行时 + +# Linux: +sudo apt-get install openxr-loader +``` + +### 2. 有线连接设置 + +```bash +# 配置网络以支持有线连接 +# 1. 使用USB网络适配器或以太网连接VR设备 +# 2. 确保PC和VR设备在同一子网 +# 3. 配置防火墙允许OpenXR通信(端口9943-9945) +``` + +### 3. VR设备配置 + +```bash +# 1. 在VR设备上安装OpenXR客户端应用 +# 2. 通过有线连接设备到PC +# 3. 启用开发者模式(如需要) +``` + +## 🚀 使用方法 + +### 快速启动 + +```python +# 在主程序中 +world = MyWorld() + +# 一键启用OpenXR模式 +if world.enableOpenXRMode(): + print("OpenXR模式已启用") + + # 检查OpenXR状态 + status = world.getOpenXRStatus() + print(f"OpenXR设备: {status['openxr_info']}") + print(f"连接状态: {status['openxr_connected']}") + print(f"串流状态: {status['openxr_streaming']}") +``` + +### 详细控制 + +```python +# 分步骤启动OpenXR +world = MyWorld() + +# 1. 初始化OpenXR系统 +if world.initializeOpenXR(): + print("OpenXR系统初始化成功") + + # 2. 启动OpenXR输入处理 + world.startOpenXRInput() + + # 3. 显示控制器射线 + world.showControllerRays(True) + + # 4. 初始化OpenXR串流 + if world.initializeOpenXRStreaming(): + print("OpenXR串流初始化成功") + + # 5. 设置串流质量 + world.setOpenXRStreamQuality( + width=2880, + height=1700, + fps=72, + bitrate=150 + ) + + # 6. 开始串流 + world.startOpenXRStreaming() +``` + +## 🎮 OpenXR交互功能 + +### 控制器输入处理 + +```python +# 获取控制器状态 +controllers = world.getAllControllers() +for controller_id in controllers: + state = world.getControllerState(controller_id) + + # 检查触发器按下 + if state['trigger'] > 0.5: + print(f"控制器 {controller_id} 触发器按下") + + # 检查摇杆输入 + thumbstick_x, thumbstick_y = state['thumbstick']['x'], state['thumbstick']['y'] + if abs(thumbstick_x) > 0.5: + print(f"摇杆水平滑动: {thumbstick_x}") +``` + +### 对象抓取和操作 + +```python +# OpenXR输入处理器自动处理对象抓取 +# 触发器按下时自动检测和抓取对象 +# 触发器释放时自动释放对象 + +# 监听OpenXR事件 +world.event_handler.accept('openxr-object-grabbed', onObjectGrabbed) +world.event_handler.accept('openxr-object-released', onObjectReleased) + +def onObjectGrabbed(object_node, controller_id): + print(f"抓取对象: {object_node.getName()}") + +def onObjectReleased(object_node, controller_id): + print(f"释放对象: {object_node.getName()}") +``` + +### 触觉反馈 + +```python +# 发送触觉反馈 +world.sendHapticFeedback( + controller_id=0, + duration=0.5, # 持续时间(秒) + intensity=0.8 # 强度(0-1) +) +``` + +## 📊 性能优化 + +### 渲染优化 + +```python +# 调整OpenXR渲染质量 +openxr_manager = world.openxr_manager +openxr_manager.render_scale = 1.0 # 渲染缩放 (0.5-2.0) + +# 设置多重采样抗锯齿 +fb_props = FrameBufferProperties() +fb_props.setMultisamples(4) # 4x MSAA +``` + +### 网络优化 + +```python +# 优化OpenXR串流参数 +world.setOpenXRStreamQuality( + width=2160, # 降低分辨率提高性能 + height=1200, + fps=60, # 降低帧率减少延迟 + bitrate=100 # 降低比特率适应网络带宽 +) +``` + +### 系统资源监控 + +```python +# 获取性能状态 +streaming_status = world.getOpenXRStreamingStatus() +print(f"串流FPS: {streaming_status['fps']}") +print(f"延迟: {streaming_status['latency']} ms") +print(f"分辨率: {streaming_status['resolution']}") +``` + +## 🔍 故障排除 + +### 常见问题 + +1. **OpenXR系统初始化失败** + ``` + 错误: OpenXR初始化失败 + 解决: 确保OpenXR运行时已安装并运行 + ``` + +2. **串流连接失败** + ``` + 错误: 无法连接到串流服务器 + 解决: + - 检查串流服务器是否运行 + - 确认防火墙设置 + - 检查网络连接 + ``` + +3. **串流质量问题** + ``` + 问题: 画面卡顿或延迟高 + 解决: + - 降低分辨率和帧率 + - 检查网络带宽 + - 调整比特率设置 + ``` + +### 调试命令 + +```python +# 启用调试模式 +world.openxr_manager.debug_mode = True + +# 获取详细状态信息 +openxr_info = world.getOpenXRInfo() +print(f"OpenXR设备信息: {openxr_info}") + +# 检查网络连接 +if world.isOpenXRConnected(): + print("OpenXR连接正常") +else: + print("OpenXR连接失败") +``` + +## 🎯 高级功能 + +### 自定义OpenXR交互 + +```python +# 创建自定义OpenXR交互逻辑 +class CustomOpenXRInteraction: + def __init__(self, world): + self.world = world + + def handleCustomGesture(self, gesture_data): + # 处理自定义手势 + pass + + def createOpenXRMenu(self, controller_id): + # 在VR中创建3D菜单 + pass +``` + +### 多用户OpenXR支持 + +```python +# 支持多个OpenXR用户 +class MultiUserOpenXRManager: + def __init__(self): + self.openxr_users = {} + + def addOpenXRUser(self, user_id, openxr_manager): + self.openxr_users[user_id] = openxr_manager + + def syncOpenXRUsers(self): + # 同步多用户OpenXR状态 + pass +``` + +## 📋 配置文件 + +### OpenXR配置 (`config/openxr_config.yaml`) + +```yaml +openxr: + enabled: true + render_scale: 1.0 + tracking_space: "stage" + +streaming: + server_ip: "127.0.0.1" + server_port: 9943 + streaming_port: 9944 + + video: + width: 2880 + height: 1700 + fps: 72 + bitrate: 150 + codec: "h264" + + audio: + enabled: true + sample_rate: 48000 + channels: 2 +``` + +## 🚀 部署建议 + +### 开发环境 + +```bash +# 创建OpenXR开发环境 +python -m venv openxr_env +source openxr_env/bin/activate +pip install -r requirements/openxr-requirements.txt + +# 启动开发服务器 +python main.py --openxr-mode +``` + +### 生产环境 + +```bash +# 优化生产部署 +# 1. 使用专用计算机处理OpenXR渲染 +# 2. 配置高性能有线网络连接 +# 3. 启用GPU加速 +# 4. 监控系统性能 +``` + +## 📚 参考资料 + +- [OpenXR规范](https://www.khronos.org/openxr/) +- [Panda3D文档](https://docs.panda3d.org/) +- [OpenXR Cookbook](https://github.com/KhronosGroup/OpenXR-Docs) + +## 🤝 贡献指南 + +欢迎贡献代码和改进建议!请遵循以下步骤: + +1. Fork此仓库 +2. 创建功能分支 +3. 提交代码修改 +4. 创建Pull Request + +## 📄 许可证 + +本项目采用MIT许可证。详见LICENSE文件。 \ No newline at end of file diff --git a/demo/VR_ALVR_实现指南.md b/demo/VR_ALVR_实现指南.md deleted file mode 100644 index b788f31e..00000000 --- a/demo/VR_ALVR_实现指南.md +++ /dev/null @@ -1,374 +0,0 @@ -# VR + ALVR 串流实现指南 - -## 🎯 概述 - -本指南详细介绍了如何在Panda3D引擎中实现VR支持,并通过ALVR串流到Quest等VR头显设备。 - -## 🏗️ 系统架构 - -### 核心组件 - -1. **VRManager** (`core/vr_manager.py`) - - 负责VR系统初始化 - - 管理立体渲染(左右眼) - - 处理VR设备跟踪 - - 集成OpenVR接口 - -2. **VRInputHandler** (`core/vr_input_handler.py`) - - 处理VR控制器输入 - - 手势识别系统 - - 交互逻辑处理 - -3. **ALVRStreamer** (`core/alvr_streamer.py`) - - ALVR服务器通信 - - 视频流编码和传输 - - 性能监控 - -4. **VRControlPanel** (`ui/vr_control_panel.py`) - - VR系统GUI控制界面 - - 实时状态监控 - - 参数调整面板 - -## 🔧 安装和配置 - -### 1. 系统依赖 - -```bash -# 安装Python依赖 -pip install -r requirements/vr-requirements.txt - -# 安装OpenVR运行时 -# Windows: 下载并安装SteamVR -# Linux: -sudo apt-get install steam -# 然后在Steam中安装SteamVR -``` - -### 2. ALVR服务器设置 - -```bash -# 下载ALVR服务器 -# 从 https://github.com/alvr-org/ALVR 下载最新版本 - -# Windows -# 1. 解压到 C:\ALVR\ -# 2. 运行 alvr_server.exe - -# Linux -# 1. 解压到 /usr/local/bin/ -# 2. 运行 alvr_server -``` - -### 3. Quest设备配置 - -```bash -# 1. 在Quest上安装ALVR客户端 -# 2. 启用开发者模式 -# 3. 连接到同一WiFi网络 -# 4. 配置防火墙允许ALVR通信(端口9943-9944) -``` - -## 🚀 使用方法 - -### 快速启动 - -```python -# 在主程序中 -world = MyWorld() - -# 一键启用VR模式 -if world.enableVRMode(): - print("VR模式已启用") - - # 检查VR状态 - status = world.getVRStatus() - print(f"VR设备: {status['vr_info']}") - print(f"ALVR连接: {status['alvr_connected']}") - print(f"串流状态: {status['alvr_streaming']}") -``` - -### 详细控制 - -```python -# 分步骤启动VR -world = MyWorld() - -# 1. 初始化VR系统 -if world.initializeVR(): - print("VR系统初始化成功") - - # 2. 启动VR输入处理 - world.startVRInput() - - # 3. 显示控制器射线 - world.showControllerRays(True) - - # 4. 初始化ALVR串流 - if world.initializeALVR(): - print("ALVR初始化成功") - - # 5. 设置串流质量 - world.setALVRStreamQuality( - width=2880, - height=1700, - fps=72, - bitrate=150 - ) - - # 6. 开始串流 - world.startALVRStreaming() -``` - -### GUI控制界面 - -```python -# 创建VR控制面板 -from ui.vr_control_panel import VRControlPanel - -vr_panel = VRControlPanel(world) -vr_panel.show() - -# 面板功能: -# - 一键启用/禁用VR -# - ALVR串流控制 -# - 质量参数调整 -# - 实时性能监控 -# - 控制器状态显示 -``` - -## 🎮 VR交互功能 - -### 控制器输入处理 - -```python -# 获取控制器状态 -controllers = world.getAllControllers() -for controller_id in controllers: - state = world.getControllerState(controller_id) - - # 检查扳机按下 - if state['trigger'] > 0.5: - print(f"控制器 {controller_id} 扳机按下") - - # 检查触摸板输入 - touchpad_x, touchpad_y = state['touchpad'] - if abs(touchpad_x) > 0.5: - print(f"触摸板水平滑动: {touchpad_x}") -``` - -### 对象抓取和操作 - -```python -# VR输入处理器自动处理对象抓取 -# 扳机按下时自动检测和抓取对象 -# 扳机释放时自动释放对象 - -# 监听VR事件 -world.event_handler.accept('vr-object-grabbed', onObjectGrabbed) -world.event_handler.accept('vr-object-released', onObjectReleased) - -def onObjectGrabbed(object_node, controller_id): - print(f"抓取对象: {object_node.getName()}") - -def onObjectReleased(object_node, controller_id): - print(f"释放对象: {object_node.getName()}") -``` - -### 触觉反馈 - -```python -# 发送触觉反馈 -world.sendHapticFeedback( - controller_id=0, - duration=0.5, # 持续时间(秒) - intensity=0.8 # 强度(0-1) -) -``` - -## 📊 性能优化 - -### 渲染优化 - -```python -# 调整VR渲染质量 -vr_manager = world.vr_manager -vr_manager.render_scale = 1.0 # 渲染缩放 (0.5-2.0) - -# 设置多重采样抗锯齿 -fb_props = FrameBufferProperties() -fb_props.setMultisamples(4) # 4x MSAA -``` - -### 网络优化 - -```python -# 优化ALVR串流参数 -world.setALVRStreamQuality( - width=2160, # 降低分辨率提高性能 - height=1200, - fps=60, # 降低帧率减少延迟 - bitrate=100 # 降低比特率适应网络带宽 -) -``` - -### 系统资源监控 - -```python -# 获取性能状态 -streaming_status = world.getALVRStreamingStatus() -print(f"串流FPS: {streaming_status['fps']}") -print(f"延迟: {streaming_status['latency']} ms") -print(f"分辨率: {streaming_status['resolution']}") -``` - -## 🔍 故障排除 - -### 常见问题 - -1. **VR系统初始化失败** - ``` - 错误: OpenVR初始化失败 - 解决: 确保SteamVR已安装并运行 - ``` - -2. **ALVR连接失败** - ``` - 错误: 无法连接到ALVR服务器 - 解决: - - 检查ALVR服务器是否运行 - - 确认防火墙设置 - - 检查网络连接 - ``` - -3. **串流质量问题** - ``` - 问题: 画面卡顿或延迟高 - 解决: - - 降低分辨率和帧率 - - 检查WiFi信号强度 - - 调整比特率设置 - ``` - -### 调试命令 - -```python -# 启用调试模式 -world.vr_manager.debug_mode = True - -# 获取详细状态信息 -vr_info = world.getVRInfo() -print(f"VR设备信息: {vr_info}") - -# 检查网络连接 -if world.isALVRConnected(): - print("ALVR连接正常") -else: - print("ALVR连接失败") -``` - -## 🎯 高级功能 - -### 自定义VR交互 - -```python -# 创建自定义VR交互逻辑 -class CustomVRInteraction: - def __init__(self, world): - self.world = world - - def handleCustomGesture(self, gesture_data): - # 处理自定义手势 - pass - - def createVRMenu(self, controller_id): - # 在VR中创建3D菜单 - pass -``` - -### 多用户VR支持 - -```python -# 支持多个VR用户 -class MultiUserVRManager: - def __init__(self): - self.vr_users = {} - - def addVRUser(self, user_id, vr_manager): - self.vr_users[user_id] = vr_manager - - def syncVRUsers(self): - # 同步多用户VR状态 - pass -``` - -## 📋 配置文件 - -### VR配置 (`config/vr_config.yaml`) - -```yaml -vr: - enabled: true - render_scale: 1.0 - tracking_space: "standing" - -alvr: - server_ip: "127.0.0.1" - server_port: 9943 - streaming_port: 9944 - - video: - width: 2880 - height: 1700 - fps: 72 - bitrate: 150 - codec: "h264" - - audio: - enabled: true - sample_rate: 48000 - channels: 2 -``` - -## 🚀 部署建议 - -### 开发环境 - -```bash -# 创建VR开发环境 -python -m venv vr_env -source vr_env/bin/activate -pip install -r requirements/vr-requirements.txt - -# 启动开发服务器 -python main.py --vr-mode -``` - -### 生产环境 - -```bash -# 优化生产部署 -# 1. 使用专用VR计算机 -# 2. 配置高性能网络 -# 3. 启用GPU加速 -# 4. 监控系统性能 -``` - -## 📚 参考资料 - -- [OpenVR API文档](https://github.com/ValveSoftware/openvr/wiki/API-Documentation) -- [ALVR项目主页](https://github.com/alvr-org/ALVR) -- [Panda3D VR指南](https://docs.panda3d.org/1.10/python/programming/render-to-texture/index) -- [Quest开发者文档](https://developer.oculus.com/documentation/quest/) - -## 🤝 贡献指南 - -欢迎贡献代码和改进建议!请遵循以下步骤: - -1. Fork此仓库 -2. 创建功能分支 -3. 提交代码修改 -4. 创建Pull Request - -## 📄 许可证 - -本项目采用MIT许可证。详见LICENSE文件。 \ No newline at end of file diff --git a/demo/VR测试说明.md b/demo/VR测试说明.md deleted file mode 100644 index 7303f308..00000000 --- a/demo/VR测试说明.md +++ /dev/null @@ -1,253 +0,0 @@ -# VR测试说明 - -## 📋 测试条件说明 - -### 🎮 VR测试的两种模式 - -#### 1. 模拟模式(推荐用于开发和调试) -- **无需VR硬件** -- **无需SteamVR** -- **无需ALVR服务器** -- 自动启用,当真实VR不可用时 -- 模拟头盔和控制器追踪数据 -- 立体渲染显示到窗口 -- 适用于功能测试和开发 - -#### 2. 真实VR模式(需要完整VR设备) -- **需要VR头盔** -- **需要SteamVR运行** -- **需要ALVR服务器(Quest无线)** -- 真实的6DOF追踪 -- 立体渲染到VR头盔 -- 完整的VR交互体验 - -## 🔧 软件要求 - -### 必需依赖 -```bash -# 安装VR相关依赖 -pip install -r requirements/vr-requirements.txt - -# 主要包含: -# - openvr>=1.26.7 -# - psutil>=5.9.0 -# - numpy>=1.21.0 -# - Pillow>=9.0.1 -``` - -### 系统要求 -- Python 3.8+ -- Panda3D -- PyQt5 -- OpenGL支持的显卡 - -## 🎮 硬件要求(完整VR模式) - -### 最低硬件要求 -- **显卡**: GTX 1060 / RX 580 或更好 -- **内存**: 8GB RAM -- **处理器**: Intel i5-4590 / AMD FX 8350 或更好 -- **USB**: 至少1个USB 3.0端口 - -### 支持的VR头盔 -- **Quest 2/3**: 有线(USB-C)或无线(ALVR) -- **Valve Index**: DisplayPort + USB 3.0 -- **HTC Vive**: HDMI + USB 3.0 -- **其他OpenVR兼容设备** - -## 🌐 连接方式 - -### 有线连接 -1. **Quest 2/3**: USB-C数据线连接PC -2. **Valve Index**: DisplayPort + USB 3.0 -3. **HTC Vive**: HDMI + USB 3.0 - -### 无线连接(推荐Quest) -1. **下载ALVR服务器**: [GitHub](https://github.com/alvr-org/ALVR) -2. **在PC上运行ALVR服务器** -3. **在Quest上安装ALVR客户端** -4. **连接到同一Wi-Fi网络(5GHz推荐)** - -## 🔄 启动顺序 - -### 完整VR模式启动顺序 -1. **连接VR头盔**到PC -2. **启动SteamVR** -3. **(可选)启动ALVR服务器**(Quest无线) -4. **运行VR测试脚本** - -### 模拟模式启动顺序 -1. **直接运行VR测试脚本** -2. **系统会自动切换到模拟模式** - -## 📊 测试项目说明 - -### 1. 基本VR功能测试 -- VR系统初始化 -- 控制器检测 -- 追踪数据获取 -- 立体渲染测试 - -### 2. VR模拟模式测试 -- 强制启用模拟模式 -- 模拟数据更新 -- 控制器输入模拟 -- 立体渲染显示 - -### 3. ALVR串流测试 -- ALVR服务器连接 -- 串流质量设置 -- 触觉反馈测试 -- 串流开关控制 - -### 4. VR GUI控制面板 -- 图形界面控制 -- 实时状态监控 -- 参数调整 -- 系统开关 - -### 5. VR交互功能测试 -- 控制器射线显示 -- 手势识别 -- 对象交互 -- 输入处理 - -## 🚀 快速开始 - -### 开发和调试(推荐) -```bash -# 直接运行,会自动使用模拟模式 -python vr_test.py - -# 选择 "2. VR模拟模式测试" -``` - -### 完整VR测试 -```bash -# 确保VR设备已连接并启动SteamVR -# 然后运行 -python vr_test.py - -# 选择 "1. 基本VR功能测试" -``` - -## 💡 故障排除 - -### 常见问题 - -#### 1. OpenVR初始化失败 -**现象**: 显示"OpenVR初始化失败" -**解决方案**: -- 检查SteamVR是否运行 -- 确认VR头盔被系统识别 -- 重启SteamVR和头盔 -- 系统会自动切换到模拟模式 - -#### 2. 控制器未检测到 -**现象**: 控制器显示"未连接" -**解决方案**: -- 确保控制器已配对 -- 检查控制器电量 -- 在SteamVR中重新配对 -- 模拟模式下会显示模拟控制器 - -#### 3. ALVR连接失败 -**现象**: ALVR串流测试失败 -**解决方案**: -- 确保ALVR服务器正在运行 -- 检查Quest上的ALVR客户端 -- 确认PC和Quest在同一网络 -- 使用5GHz Wi-Fi网络 - -#### 4. 性能问题 -**现象**: 帧率低或卡顿 -**解决方案**: -- 降低渲染分辨率 -- 关闭不必要的后台程序 -- 检查显卡驱动更新 -- 使用模拟模式进行开发 - -### 日志分析 -测试过程中的详细日志可以帮助诊断问题: -- `✓` 表示成功 -- `⚠` 表示警告(非致命) -- `✗` 表示失败 -- `🔧` 表示配置或调试信息 - -## 📈 性能优化建议 - -### 开发阶段 -1. **使用模拟模式**进行功能开发 -2. **降低渲染分辨率**提高帧率 -3. **关闭不必要的特效** -4. **使用性能分析工具** - -### 生产部署 -1. **使用真实VR模式** -2. **根据硬件配置调整质量** -3. **启用异步时间扭曲** -4. **优化渲染管线** - -## 🔍 调试技巧 - -### 1. 检查VR系统状态 -```python -# 在代码中添加调试信息 -vr_info = world.getVRInfo() -print(f"VR模式: {vr_info['mode']}") -print(f"模拟模式: {vr_info['simulation_mode']}") -``` - -### 2. 监控性能 -```python -# 获取VR系统状态 -status = world.getVRStatus() -print(f"VR启用: {status['vr_enabled']}") -print(f"控制器数量: {len(status['controllers'])}") -``` - -### 3. 测试特定功能 -```python -# 强制启用模拟模式 -world.vr_manager.enable_simulation_mode() - -# 更新模拟数据 -world.vr_manager.update_simulation_data('head_pose', new_data) -``` - -## 📚 API参考 - -### VR系统控制 -- `world.initializeVR()` - 初始化VR系统 -- `world.shutdownVR()` - 关闭VR系统 -- `world.isVREnabled()` - 检查VR状态 -- `world.getVRInfo()` - 获取VR信息 - -### 模拟模式 -- `world.vr_manager.enable_simulation_mode()` - 启用模拟模式 -- `world.vr_manager.get_simulation_data()` - 获取模拟数据 -- `world.vr_manager.update_simulation_data()` - 更新模拟数据 - -### ALVR串流 -- `world.initializeALVR()` - 初始化ALVR -- `world.startALVRStreaming()` - 开始串流 -- `world.stopALVRStreaming()` - 停止串流 -- `world.setALVRStreamQuality()` - 设置质量 - -### VR输入 -- `world.startVRInput()` - 启动输入处理 -- `world.showControllerRays()` - 显示控制器射线 -- `world.getAllControllers()` - 获取所有控制器 -- `world.getControllerState()` - 获取控制器状态 - -## 🎯 总结 - -现在的VR测试系统具有以下优势: - -1. **自动适应**: 自动在真实VR和模拟模式之间切换 -2. **开发友好**: 无需VR硬件即可开发和测试 -3. **完整功能**: 支持所有VR功能的测试 -4. **详细反馈**: 提供清晰的状态信息和错误提示 -5. **灵活配置**: 可根据需要调整各种参数 - -无论您是否拥有VR设备,都可以使用这个测试系统来验证VR功能的正确性。 \ No newline at end of file diff --git a/direct_vr_streaming_test.py b/direct_vr_streaming_test.py deleted file mode 100644 index 9d510dac..00000000 --- a/direct_vr_streaming_test.py +++ /dev/null @@ -1,197 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -直接VR串流测试脚本 -测试不通过ALVR,直接使用OpenVR API进行串流的功能 -""" - -import sys -import os -import time -from main import MyWorld - - -def test_direct_vr_streaming(): - """测试直接VR串流功能""" - print("=== 直接VR串流功能测试 ===") - - # 创建世界实例 - world = MyWorld() - - # 初始化VR系统 - print("🥽 初始化VR系统...") - if not world.initializeVR(): - print("✗ VR系统初始化失败") - return False - - print("✓ VR系统初始化成功") - - # 获取VR信息 - vr_info = world.getVRInfo() - print(f"\n📊 VR系统信息:") - print(f" - 模式: {vr_info['mode']}") - print(f" - 启用状态: {vr_info['enabled']}") - print(f" - 模拟模式: {vr_info['simulation_mode']}") - print(f" - 渲染尺寸: {vr_info['render_size']}") - - # 检查是否为真实VR模式 - if not vr_info['enabled'] or vr_info['simulation_mode']: - print("⚠ 未检测到真实VR设备或处于模拟模式") - print(" 直接串流需要真实的VR设备支持") - return False - - # 检查VR纹理是否正确创建 - print("\n🔍 检查VR纹理...") - if hasattr(world.vr_manager, 'left_eye_texture') and world.vr_manager.left_eye_texture: - print(" ✓ 左眼纹理已创建") - print(f" 纹理尺寸: {world.vr_manager.left_eye_texture.getXSize()}x{world.vr_manager.left_eye_texture.getYSize()}") - else: - print(" ✗ 左眼纹理未创建") - return False - - if hasattr(world.vr_manager, 'right_eye_texture') and world.vr_manager.right_eye_texture: - print(" ✓ 右眼纹理已创建") - print(f" 纹理尺寸: {world.vr_manager.right_eye_texture.getXSize()}x{world.vr_manager.right_eye_texture.getYSize()}") - else: - print(" ✗ 右眼纹理未创建") - return False - - # 检查OpenVR系统是否正常 - print("\n🔍 检查OpenVR系统...") - if hasattr(world.vr_manager, 'vr_system') and world.vr_manager.vr_system: - print(" ✓ OpenVR系统已初始化") - else: - print(" ✗ OpenVR系统未初始化") - return False - - if hasattr(world.vr_manager, 'vr_compositor') and world.vr_manager.vr_compositor: - print(" ✓ OpenVR合成器已初始化") - else: - print(" ✗ OpenVR合成器未初始化") - return False - - # 创建简单的测试场景 - print("\n🎨 创建测试场景...") - try: - from panda3d.core import CardMaker, Vec4, AmbientLight, DirectionalLight - - # 添加光照 - ambient_light = AmbientLight('ambient') - ambient_light.setColor(Vec4(0.3, 0.3, 0.3, 1)) - ambient_light_np = world.render.attachNewNode(ambient_light) - world.render.setLight(ambient_light_np) - - directional_light = DirectionalLight('directional') - directional_light.setColor(Vec4(0.8, 0.8, 0.8, 1)) - directional_light.setDirection(Vec4(-1, -1, -1, 0)) - directional_light_np = world.render.attachNewNode(directional_light) - world.render.setLight(directional_light_np) - - # 创建地面网格 - for i in range(-5, 6): - for j in range(-5, 6): - cm = CardMaker(f"grid_{i}_{j}") - cm.setFrame(-0.4, 0.4, -0.4, 0.4) - grid = world.render.attachNewNode(cm.generate()) - grid.setPos(i * 1.0, j * 1.0, -1) - grid.setHpr(0, -90, 0) - - # 棋盘格颜色 - if (i + j) % 2 == 0: - grid.setColor(0.8, 0.8, 0.8, 1.0) - else: - grid.setColor(0.6, 0.6, 0.6, 1.0) - - # 创建一些彩色立方体 - for i in range(8): - cm = CardMaker(f"cube_{i}") - cm.setFrame(-0.3, 0.3, -0.3, 0.3) - cube = world.render.attachNewNode(cm.generate()) - - import math - angle = i * math.pi * 2 / 8 - radius = 3 - cube.setPos(math.cos(angle) * radius, math.sin(angle) * radius, 0.5) - cube.setHpr(i * 45, 0, 0) - cube.setScale(1, 1, 2) - - # 彩虹颜色 - hue = i / 8.0 - r = abs(math.sin(hue * math.pi * 2)) - g = abs(math.sin((hue + 0.33) * math.pi * 2)) - b = abs(math.sin((hue + 0.66) * math.pi * 2)) - cube.setColor(r, g, b, 1.0) - - print("✓ 测试场景创建完成") - - except Exception as e: - print(f"场景创建错误: {str(e)}") - - # 测试直接串流 - print("\n🎮 开始直接VR串流测试...") - print(" 测试将持续15秒,请观察VR头显中的画面") - - start_time = time.time() - frame_count = 0 - - try: - while time.time() - start_time < 15: - # 处理引擎更新 - if hasattr(world, 'taskMgr'): - world.taskMgr.step() - - frame_count += 1 - - # 每秒显示一次状态 - if frame_count % 60 == 0: # 约每秒 - elapsed = time.time() - start_time - fps = frame_count / elapsed - print(f" [{elapsed:.1f}s] FPS: {fps:.1f}") - - time.sleep(0.016) # ~60 FPS - - except KeyboardInterrupt: - print("\n⏹️ 用户中断测试") - - # 关闭系统 - world.shutdownVR() - print("\n✓ VR系统已关闭") - - final_fps = frame_count / (time.time() - start_time) - print(f"✅ 直接VR串流测试完成,平均FPS: {final_fps:.1f}") - - return True - - -def main(): - """主函数""" - print("🎮 直接VR串流测试脚本") - print("="*50) - print("此脚本将测试不通过ALVR,直接使用OpenVR API进行串流的功能") - print("请确保:") - print(" 1. VR设备已连接(如Pico 4通过USB-C连接)") - print(" 2. SteamVR正在运行") - print(" 3. VR设备已被SteamVR识别") - - try: - input("\n按回车键开始测试,或按Ctrl+C退出...") - success = test_direct_vr_streaming() - - print("\n" + "="*50) - if success: - print("✓ 直接VR串流测试成功完成") - print("🎉 您现在已经可以不通过ALVR直接串流到VR设备!") - else: - print("✗ 直接VR串流测试遇到问题") - print("💡 请检查VR设备连接和SteamVR状态") - - except KeyboardInterrupt: - print("\n用户中断测试") - except Exception as e: - print(f"测试过程中发生错误: {str(e)}") - import traceback - traceback.print_exc() - - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/main.py b/main.py index ea59bfef..b44df0e8 100644 --- a/main.py +++ b/main.py @@ -15,9 +15,9 @@ from core.selection import SelectionSystem from core.event_handler import EventHandler from core.tool_manager import ToolManager from core.script_system import ScriptManager -from core.vr_manager import VRManager -from core.vr_input_handler import VRInputHandler -from core.alvr_streamer import ALVRStreamer +from core.openxr_manager import OpenXRManager +from core.openxr_input_handler import OpenXRInputHandler +from core.openxr_streamer import OpenXRStreamer from gui.gui_manager import GUIManager from scene.scene_manager import SceneManager from project.project_manager import ProjectManager @@ -41,7 +41,7 @@ import json import datetime from direct.actor.Actor import Actor from PyQt5.sip import wrapinstance -#from RenderPipelineFile.toolkit.material_editor.main import MaterialEditor +# RenderPipelineFile.toolkit.material_editor.main import MaterialEditor class MyWorld(CoreWorld): @@ -75,10 +75,10 @@ class MyWorld(CoreWorld): # 初始化界面管理系统 self.interface_manager = InterfaceManager(self) - # 初始化VR系统 - self.vr_manager = VRManager(self) - self.vr_input_handler = VRInputHandler(self, self.vr_manager) - self.alvr_streamer = ALVRStreamer(self, self.vr_manager) + # 初始化OpenXR系统 + self.openxr_manager = OpenXRManager(self) + self.openxr_input_handler = OpenXRInputHandler(self, self.openxr_manager) + self.openxr_streamer = OpenXRStreamer(self, self.openxr_manager) # 启动脚本系统 self.script_manager.start_system() @@ -444,6 +444,144 @@ class MyWorld(CoreWorld): """根据名称查找模型""" return self.scene_manager.findModelByName(name) + # ==================== OpenXR系统功能代理 ==================== + + # OpenXR系统控制方法 - 代理到openxr_manager + def initializeOpenXR(self): + """初始化OpenXR系统""" + return self.openxr_manager.initialize_openxr() + + def shutdownOpenXR(self): + """关闭OpenXR系统""" + return self.openxr_manager.shutdown_openxr() + + def isOpenXREnabled(self): + """检查OpenXR是否启用""" + return self.openxr_manager.is_openxr_enabled() + + def getOpenXRInfo(self): + """获取OpenXR系统信息""" + return self.openxr_manager.get_openxr_info() + + # OpenXR输入处理方法 - 代理到openxr_input_handler + def startOpenXRInput(self): + """启动OpenXR输入处理""" + return self.openxr_input_handler.start_input_handling() + + def stopOpenXRInput(self): + """停止OpenXR输入处理""" + return self.openxr_input_handler.stop_input_handling() + + def showControllerRays(self, show=True): + """显示/隐藏控制器射线""" + return self.openxr_input_handler.show_controller_rays(show) + + def getControllerState(self, controller_id): + """获取控制器状态""" + return self.openxr_input_handler.get_controller_state(controller_id) + + def getAllControllers(self): + """获取所有控制器""" + return self.openxr_input_handler.get_all_controllers() + + def setVRGestureEnabled(self, enabled): + """设置OpenXR手势识别启用状态""" + return self.openxr_input_handler.set_gesture_enabled(enabled) + + def setVRInteractionEnabled(self, enabled): + """设置OpenXR交互启用状态""" + return self.openxr_input_handler.set_interaction_enabled(enabled) + + # OpenXR串流方法 - 代理到openxr_streamer + def initializeOpenXRStreaming(self): + """初始化OpenXR串流""" + return self.openxr_streamer.initialize() + + def startOpenXRStreaming(self): + """开始OpenXR串流""" + return self.openxr_streamer.start_streaming() + + def stopOpenXRStreaming(self): + """停止OpenXR串流""" + return self.openxr_streamer.stop_streaming() + + def getOpenXRStreamingStatus(self): + """获取OpenXR串流状态""" + return self.openxr_streamer.get_streaming_status() + + def setOpenXRStreamQuality(self, width, height, fps, bitrate): + """设置OpenXR串流质量""" + return self.openxr_streamer.set_stream_quality(width, height, fps, bitrate) + + def sendHapticFeedback(self, controller_id, duration, intensity): + """发送触觉反馈""" + return self.openxr_streamer.send_haptic_feedback(controller_id, duration, intensity) + + def shutdownOpenXRStreaming(self): + """关闭OpenXR串流""" + return self.openxr_streamer.shutdown() + + def isOpenXRConnected(self): + """检查OpenXR是否连接""" + return self.openxr_streamer.is_connected() + + def isOpenXRStreaming(self): + """检查OpenXR是否在串流""" + return self.openxr_streamer.is_streaming() + + # 便捷方法 + def enableOpenXRMode(self): + """启用OpenXR模式(一键启动)""" + print("启用OpenXR模式...") + + # 1. 初始化OpenXR系统 + if not self.initializeOpenXR(): + print("OpenXR系统初始化失败") + return False + + # 2. 启动OpenXR输入处理 + if not self.startOpenXRInput(): + print("OpenXR输入处理启动失败") + return False + + # 3. 初始化OpenXR串流 + if self.initializeOpenXRStreaming(): + print("✓ OpenXR串流已启用") + # 自动开始串流 + self.startOpenXRStreaming() + else: + print("⚠ OpenXR串流启用失败,但OpenXR系统仍可用") + + print("✓ OpenXR模式已启用") + return True + + def disableOpenXRMode(self): + """禁用OpenXR模式(一键关闭)""" + print("禁用OpenXR模式...") + + # 1. 停止OpenXR串流 + self.stopOpenXRStreaming() + self.shutdownOpenXRStreaming() + + # 2. 停止OpenXR输入处理 + self.stopOpenXRInput() + + # 3. 关闭OpenXR系统 + self.shutdownOpenXR() + + print("✓ OpenXR模式已禁用") + + def getOpenXRStatus(self): + """获取OpenXR系统总体状态""" + return { + "openxr_enabled": self.isOpenXREnabled(), + "openxr_info": self.getOpenXRInfo(), + "controllers": self.getAllControllers(), + "openxr_connected": self.isOpenXRConnected(), + "openxr_streaming": self.isOpenXRStreaming(), + "streaming_status": self.getOpenXRStreamingStatus() if self.isOpenXRConnected() else None + } + # ==================== 脚本系统功能代理 ==================== # 脚本系统控制方法 - 代理到script_manager @@ -510,143 +648,6 @@ class MyWorld(CoreWorld): """列出所有脚本信息""" return self.script_manager.list_all_scripts() - # ==================== VR系统功能代理 ==================== - - # VR系统控制方法 - 代理到vr_manager - def initializeVR(self): - """初始化VR系统""" - return self.vr_manager.initialize_vr() - - def shutdownVR(self): - """关闭VR系统""" - return self.vr_manager.shutdown_vr() - - def isVREnabled(self): - """检查VR是否启用""" - return self.vr_manager.is_vr_enabled() - - def getVRInfo(self): - """获取VR系统信息""" - return self.vr_manager.get_vr_info() - - # VR输入处理方法 - 代理到vr_input_handler - def startVRInput(self): - """启动VR输入处理""" - return self.vr_input_handler.start_input_handling() - - def stopVRInput(self): - """停止VR输入处理""" - return self.vr_input_handler.stop_input_handling() - - def showControllerRays(self, show=True): - """显示/隐藏控制器射线""" - return self.vr_input_handler.show_controller_rays(show) - - def getControllerState(self, controller_id): - """获取控制器状态""" - return self.vr_input_handler.get_controller_state(controller_id) - - def getAllControllers(self): - """获取所有控制器""" - return self.vr_input_handler.get_all_controllers() - - def setVRGestureEnabled(self, enabled): - """设置VR手势识别启用状态""" - return self.vr_input_handler.set_gesture_enabled(enabled) - - def setVRInteractionEnabled(self, enabled): - """设置VR交互启用状态""" - return self.vr_input_handler.set_interaction_enabled(enabled) - - # ALVR串流方法 - 代理到alvr_streamer - def initializeALVR(self): - """初始化ALVR串流""" - return self.alvr_streamer.initialize() - - def startALVRStreaming(self): - """开始ALVR串流""" - return self.alvr_streamer.start_streaming() - - def stopALVRStreaming(self): - """停止ALVR串流""" - return self.alvr_streamer.stop_streaming() - - def getALVRStreamingStatus(self): - """获取ALVR串流状态""" - return self.alvr_streamer.get_streaming_status() - - def setALVRStreamQuality(self, width, height, fps, bitrate): - """设置ALVR串流质量""" - return self.alvr_streamer.set_stream_quality(width, height, fps, bitrate) - - def sendHapticFeedback(self, controller_id, duration, intensity): - """发送触觉反馈""" - return self.alvr_streamer.send_haptic_feedback(controller_id, duration, intensity) - - def shutdownALVR(self): - """关闭ALVR串流""" - return self.alvr_streamer.shutdown() - - def isALVRConnected(self): - """检查ALVR是否连接""" - return self.alvr_streamer.is_connected() - - def isALVRStreaming(self): - """检查ALVR是否在串流""" - return self.alvr_streamer.is_streaming() - - # 便捷方法 - def enableVRMode(self): - """启用VR模式(一键启动)""" - print("启用VR模式...") - - # 1. 初始化VR系统 - if not self.initializeVR(): - print("VR系统初始化失败") - return False - - # 2. 启动VR输入处理 - if not self.startVRInput(): - print("VR输入处理启动失败") - return False - - # 3. 初始化ALVR串流 - if self.initializeALVR(): - print("✓ ALVR串流已启用") - # 自动开始串流 - self.startALVRStreaming() - else: - print("⚠ ALVR串流启用失败,但VR系统仍可用") - - print("✓ VR模式已启用") - return True - - def disableVRMode(self): - """禁用VR模式(一键关闭)""" - print("禁用VR模式...") - - # 1. 停止ALVR串流 - self.stopALVRStreaming() - self.shutdownALVR() - - # 2. 停止VR输入处理 - self.stopVRInput() - - # 3. 关闭VR系统 - self.shutdownVR() - - print("✓ VR模式已禁用") - - def getVRStatus(self): - """获取VR系统总体状态""" - return { - "vr_enabled": self.isVREnabled(), - "vr_info": self.getVRInfo(), - "controllers": self.getAllControllers(), - "alvr_connected": self.isALVRConnected(), - "alvr_streaming": self.isALVRStreaming(), - "streaming_status": self.getALVRStreamingStatus() if self.isALVRConnected() else None - } # ==================== 项目管理功能代理 ==================== @@ -681,13 +682,13 @@ def buildPackage(appw): # def open_material_editor(self): # """打开材质编辑器作为子窗口""" # if not self.material_editor: -# self.material_editor = MaterialEditor() -# # 设置为子窗口 -# self.material_editor.setParent(self) -# self.material_editor.setWindowFlags(Qt.Window) -# -# self.material_editor.show() -# +# # self.material_editor = MaterialEditor() +# # # 设置为子窗口 +# # self.material_editor.setParent(self) +# # self.material_editor.setWindowFlags(Qt.Window) +# # +# # self.material_editor.show() +# # if __name__ == "__main__": world = MyWorld() diff --git a/openxr_test.py b/openxr_test.py new file mode 100755 index 00000000..8f33dd1a --- /dev/null +++ b/openxr_test.py @@ -0,0 +1,480 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +OpenXR测试脚本 + +用于测试OpenXR功能的基本实现 +""" + +import sys +import os +import time + +# 添加项目根目录到Python路径 +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +from main import MyWorld +from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QHBoxLayout, QLabel +from PyQt5.QtGui import QPixmap, QImage +from PyQt5.QtCore import QTimer +import numpy as np + + +def test_openxr_basic_display(world): + """测试基本OpenXR显示功能""" + print("=== OpenXR基本显示测试 ===") + + # 加载一些模型来展示 + print("📦 加载演示场景...") + try: + # 创建一些简单的几何体作为演示 + from panda3d.core import CardMaker, Vec4 + + # 创建地面 + cm = CardMaker("ground") + cm.setFrame(-10, 10, -10, 10) + ground = world.render.attachNewNode(cm.generate()) + ground.setPos(0, 0, -1) + ground.setColor(0.5, 0.8, 0.5, 1.0) + ground.setHpr(0, -90, 0) + + # 创建一些立方体 + for i in range(5): + for j in range(5): + cube = world.loader.loadModel("models/environment") + if not cube: + # 如果没有environment模型,创建简单的立方体 + cm_cube = CardMaker(f"cube_{i}_{j}") + cm_cube.setFrame(-0.5, 0.5, -0.5, 0.5) + cube = world.render.attachNewNode(cm_cube.generate()) + + cube.setPos(i * 2 - 4, j * 2 - 4, 0) + cube.setScale(0.5) + cube.setColor(i/5.0, j/5.0, 1.0, 1.0) + + print("✓ 演示场景加载完成") + + except Exception as e: + print(f"场景加载错误: {str(e)}") + + # 初始化OpenXR系统 + print("\n🥽 初始化OpenXR显示系统...") + openxr_success = world.initializeOpenXR() + + if openxr_success: + print("✓ OpenXR系统初始化成功") + openxr_info = world.getOpenXRInfo() + + print(f"\n📊 OpenXR显示信息:") + print(f" - 模式: {openxr_info['mode']}") + print(f" - 渲染尺寸: {openxr_info['render_size']}") + print(f" - 模拟模式: {openxr_info['simulation_mode']}") + print(f" - 有线连接: {openxr_info['wired_connection']}") + + if openxr_info['simulation_mode']: + print("\n💡 模拟模式说明:") + print(" - 左右眼视图将并排显示在主窗口") + print(" - 可以看到立体渲染效果") + print(" - 头部会有轻微的模拟摆动") + else: + print("\n💡 真实OpenXR模式:") + print(" - 画面将渲染到VR头盔") + print(" - 支持真实的头部追踪") + print(" - 控制器交互可用") + + # 创建显示窗口 + app = QApplication.instance() + if app is None: + app = QApplication(sys.argv) + + # 创建主窗口显示OpenXR渲染结果 + window = QMainWindow() + window.setWindowTitle("OpenXR Stereo Rendering Preview") + window.setGeometry(100, 100, 800, 300) + + central_widget = QWidget() + layout = QHBoxLayout() + central_widget.setLayout(layout) + + left_label = QLabel("Left Eye") + right_label = QLabel("Right Eye") + layout.addWidget(left_label) + layout.addWidget(right_label) + + window.setCentralWidget(central_widget) + window.show() + + # 定时更新显示 + def update_display(): + if hasattr(world.openxr_manager, 'left_eye_texture') and world.openxr_manager.left_eye_texture: + # 获取左眼纹理数据 + tex = world.openxr_manager.left_eye_texture + try: + if tex.get_x_size() > 0 and tex.get_y_size() > 0: + # 添加对纹理数据有效性的检查 + if tex.might_have_ram_image(): + # 使用正确的API将纹理数据复制到QImage + data = tex.get_ram_image().get_data() + if data: + img = QImage(data, tex.get_x_size(), tex.get_y_size(), QImage.Format_RGBA8888) + pixmap = QPixmap.fromImage(img) + left_label.setPixmap(pixmap.scaled(400, 300)) + left_label.setText("") # 清除文字,只显示图像 + else: + left_label.setText("Left Eye (No Data)") + else: + left_label.setText("Left Eye (No RAM Image)") + else: + left_label.setText("Left Eye (Invalid Size)") + except Exception as e: + print(f"左眼纹理更新错误: {str(e)}") + left_label.setText("Left Eye (Error)") + else: + left_label.setText("Left Eye (No Texture)") + + if hasattr(world.openxr_manager, 'right_eye_texture') and world.openxr_manager.right_eye_texture: + # 获取右眼纹理数据 + tex = world.openxr_manager.right_eye_texture + try: + if tex.get_x_size() > 0 and tex.get_y_size() > 0: + # 添加对纹理数据有效性的检查 + if tex.might_have_ram_image(): + # 使用正确的API将纹理数据复制到QImage + data = tex.get_ram_image().get_data() + if data: + img = QImage(data, tex.get_x_size(), tex.get_y_size(), QImage.Format_RGBA8888) + pixmap = QPixmap.fromImage(img) + right_label.setPixmap(pixmap.scaled(400, 300)) + right_label.setText("") # 清除文字,只显示图像 + else: + right_label.setText("Right Eye (No Data)") + else: + right_label.setText("Right Eye (No RAM Image)") + else: + right_label.setText("Right Eye (Invalid Size)") + except Exception as e: + print(f"右眼纹理更新错误: {str(e)}") + right_label.setText("Right Eye (Error)") + else: + right_label.setText("Right Eye (No Texture)") + + timer = QTimer() + timer.timeout.connect(update_display) + timer.start(33) # 约30 FPS + + # 运行几秒钟以展示效果 + print(f"\n🎮 OpenXR显示测试运行中... (10秒)") + print(" 观察主窗口中的立体渲染效果") + + start_time = time.time() + while time.time() - start_time < 10: + # 处理Panda3D事件 + if hasattr(world, 'taskMgr'): + world.taskMgr.step() + + # 处理Qt事件 + try: + app.processEvents() + except Exception as e: + print(f"Qt事件处理错误: {str(e)}") + + # 添加短暂延迟以避免过度占用CPU + time.sleep(0.016) # ~60 FPS + + print("✓ OpenXR显示测试完成") + + else: + print("✗ OpenXR系统初始化失败") + return False + + # 关闭OpenXR系统 + world.shutdownOpenXR() + print("\n✓ OpenXR系统已关闭") + + return True + + +def test_openxr_streaming(world): + """测试OpenXR串流功能""" + print("=== OpenXR串流测试 ===") + + # 初始化OpenXR系统 + print("初始化OpenXR系统...") + if not world.initializeOpenXR(): + print("✗ OpenXR系统初始化失败") + return False + + # 启动输入处理 + print("启动OpenXR输入处理...") + if not world.startOpenXRInput(): + print("⚠ OpenXR输入处理启动失败") + + # 初始化串流 + print("初始化OpenXR串流...") + if world.initializeOpenXRStreaming(): + print("✓ OpenXR串流初始化成功") + + # 开始串流 + print("开始OpenXR串流...") + if world.startOpenXRStreaming(): + print("✓ OpenXR串流已开始") + + # 创建显示窗口 + app = QApplication.instance() + if app is None: + app = QApplication(sys.argv) + + # 创建主窗口显示OpenXR渲染结果 + window = QMainWindow() + window.setWindowTitle("OpenXR Streaming Preview") + window.setGeometry(100, 100, 800, 300) + + central_widget = QWidget() + layout = QHBoxLayout() + central_widget.setLayout(layout) + + left_label = QLabel("Left Eye") + right_label = QLabel("Right Eye") + layout.addWidget(left_label) + layout.addWidget(right_label) + + window.setCentralWidget(central_widget) + window.show() + + # 定时更新显示 + def update_display(): + try: + # 更新左眼纹理 + if hasattr(world.openxr_manager, 'left_eye_texture') and world.openxr_manager.left_eye_texture: + tex = world.openxr_manager.left_eye_texture + # 检查纹理尺寸和数据有效性 + if tex.get_x_size() > 0 and tex.get_y_size() > 0 and tex.might_have_ram_image(): + ram_image = tex.get_ram_image() + if ram_image and ram_image.get_data(): + # 使用正确的数据访问方法 + img = QImage(ram_image.get_data(), tex.get_x_size(), tex.get_y_size(), QImage.Format_RGBA8888) + pixmap = QPixmap.fromImage(img) + left_label.setPixmap(pixmap.scaled(400, 300)) + left_label.setText("") # 清除文字,只显示图像 + + # 更新右眼纹理 + if hasattr(world.openxr_manager, 'right_eye_texture') and world.openxr_manager.right_eye_texture: + tex = world.openxr_manager.right_eye_texture + # 检查纹理尺寸和数据有效性 + if tex.get_x_size() > 0 and tex.get_y_size() > 0 and tex.might_have_ram_image(): + ram_image = tex.get_ram_image() + if ram_image and ram_image.get_data(): + # 使用正确的数据访问方法 + img = QImage(ram_image.get_data(), tex.get_x_size(), tex.get_y_size(), QImage.Format_RGBA8888) + pixmap = QPixmap.fromImage(img) + right_label.setPixmap(pixmap.scaled(400, 300)) + right_label.setText("") # 清除文字,只显示图像 + + except Exception as e: + print(f"纹理更新错误: {str(e)}") + # 清除可能已损坏的纹理引用 + if hasattr(world.openxr_manager, 'left_eye_texture'): + world.openxr_manager.left_eye_texture = None + if hasattr(world.openxr_manager, 'right_eye_texture'): + world.openxr_manager.right_eye_texture = None + + timer = QTimer() + timer.timeout.connect(update_display) + timer.start(33) # 约30 FPS + + # 运行几秒钟 + print("OpenXR串流运行中... (5秒)") + start_time = time.time() + while time.time() - start_time < 5: + # 处理Panda3D事件 + if hasattr(world, 'taskMgr'): + world.taskMgr.step() + + # 处理Qt事件 + try: + app.processEvents() + except Exception as e: + print(f"Qt事件处理错误: {str(e)}") + + time.sleep(0.016) # ~60 FPS + + # 停止串流 + world.stopOpenXRStreaming() + print("✓ OpenXR串流已停止") + else: + print("✗ OpenXR串流启动失败") + else: + print("✗ OpenXR串流初始化失败") + + # 关闭系统 + world.shutdownOpenXRStreaming() + world.stopOpenXRInput() + world.shutdownOpenXR() + + print("✓ OpenXR串流测试完成") + return True + + +def test_openxr_full(world): + """测试完整OpenXR功能""" + print("=== 完整OpenXR功能测试 ===") + + # 初始化OpenXR系统 + print("初始化OpenXR系统...") + if not world.initializeOpenXR(): + print("✗ OpenXR系统初始化失败") + return False + + # 创建显示窗口 + app = QApplication.instance() + if app is None: + app = QApplication(sys.argv) + + # 创建主窗口显示OpenXR渲染结果 + window = QMainWindow() + window.setWindowTitle("OpenXR Full Functionality Preview") + window.setGeometry(100, 100, 800, 300) + + central_widget = QWidget() + layout = QHBoxLayout() + central_widget.setLayout(layout) + + left_label = QLabel("Left Eye") + right_label = QLabel("Right Eye") + layout.addWidget(left_label) + layout.addWidget(right_label) + + window.setCentralWidget(central_widget) + window.show() + + # 定时更新显示 + def update_display(): + try: + # 更新左眼纹理 + if hasattr(world.openxr_manager, 'left_eye_texture') and world.openxr_manager.left_eye_texture: + tex = world.openxr_manager.left_eye_texture + # 检查纹理尺寸和数据有效性 + if tex.get_x_size() > 0 and tex.get_y_size() > 0 and tex.might_have_ram_image(): + ram_image = tex.get_ram_image() + if ram_image and ram_image.get_data(): + # 使用正确的数据访问方法 + img = QImage(ram_image.get_data(), tex.get_x_size(), tex.get_y_size(), QImage.Format_RGBA8888) + pixmap = QPixmap.fromImage(img) + left_label.setPixmap(pixmap.scaled(400, 300)) + left_label.setText("") # 清除文字,只显示图像 + + # 更新右眼纹理 + if hasattr(world.openxr_manager, 'right_eye_texture') and world.openxr_manager.right_eye_texture: + tex = world.openxr_manager.right_eye_texture + # 检查纹理尺寸和数据有效性 + if tex.get_x_size() > 0 and tex.get_y_size() > 0 and tex.might_have_ram_image(): + ram_image = tex.get_ram_image() + if ram_image and ram_image.get_data(): + # 使用正确的数据访问方法 + img = QImage(ram_image.get_data(), tex.get_x_size(), tex.get_y_size(), QImage.Format_RGBA8888) + pixmap = QPixmap.fromImage(img) + right_label.setPixmap(pixmap.scaled(400, 300)) + right_label.setText("") # 清除文字,只显示图像 + + except Exception as e: + print(f"纹理更新错误: {str(e)}") + # 清除可能已损坏的纹理引用 + if hasattr(world.openxr_manager, 'left_eye_texture'): + world.openxr_manager.left_eye_texture = None + if hasattr(world.openxr_manager, 'right_eye_texture'): + world.openxr_manager.right_eye_texture = None + + timer = QTimer() + timer.timeout.connect(update_display) + timer.start(33) # 约30 FPS + + # 一键启用OpenXR模式 + print("启用OpenXR模式...") + if world.enableOpenXRMode(): + print("✓ OpenXR模式已启用") + + # 启动输入处理 + print("启动OpenXR输入处理...") + if not world.startOpenXRInput(): + print("⚠ OpenXR输入处理启动失败") + + # 获取状态 + openxr_info = world.getOpenXRInfo() + print(f"\n📊 OpenXR状态:") + print(f" - 启用: {openxr_info['enabled']}") + print(f" - 模式: {openxr_info['mode']}") + print(f" - 模拟模式: {openxr_info['simulation_mode']}") + print(f" - 渲染尺寸: {openxr_info['render_size']}") + print(f" - 有线连接: {openxr_info['wired_connection']}") + + # 获取状态 + status = world.getOpenXRStatus() + print(f"\n📊 OpenXR状态:") + print(f" - 启用: {status['openxr_enabled']}") + print(f" - 连接: {status['openxr_connected']}") + print(f" - 串流: {status['openxr_streaming']}") + print(f" - 控制器: {len(status['controllers'])}个") + + if status['streaming_status']: + streaming_status = status['streaming_status'] + print(f"\n📡 串流状态:") + print(f" - FPS: {streaming_status['fps']}") + print(f" - 分辨率: {streaming_status['resolution']}") + print(f" - 延迟: {streaming_status['latency']}ms") + + # 运行一段时间 + print(f"\n🎮 OpenXR完整功能运行中... (15秒)") + start_time = time.time() + while time.time() - start_time < 15: + # 处理Panda3D事件 + if hasattr(world, 'taskMgr'): + world.taskMgr.step() + + # 处理Qt事件 + try: + app.processEvents() + except Exception as e: + print(f"Qt事件处理错误: {str(e)}") + + time.sleep(0.016) # ~60 FPS + + # 禁用OpenXR模式 + world.shutdownOpenXR() + print("✓ OpenXR模式已禁用") + + else: + print("✗ OpenXR模式启用失败") + return False + + # 关闭系统 + world.stopOpenXRInput() + world.shutdownOpenXR() + + print("✓ 完整OpenXR功能测试完成") + return True + + +if __name__ == "__main__": + print("OpenXR功能测试") + print("===============") + + # 创建世界实例(只创建一次) + world = MyWorld() + + # 运行测试 + print("1. 基本显示测试") + test_openxr_basic_display(world) + + print("\n" + "="*50 + "\n") + + print("2. 串流功能测试") + test_openxr_streaming(world) + + print("\n" + "="*50 + "\n") + + print("3. 完整功能测试") + test_openxr_full(world) + + print("\n" + "="*50) + print("所有测试完成!") \ No newline at end of file diff --git a/requirements/openxr-requirements.txt b/requirements/openxr-requirements.txt new file mode 100644 index 00000000..ae00b4f4 --- /dev/null +++ b/requirements/openxr-requirements.txt @@ -0,0 +1,23 @@ +# OpenXR 相关依赖 +# 注意:OpenXR Python绑定可能需要从源码编译 + +# 基础依赖 +numpy>=1.18.0 +PyOpenGL>=3.1.0 + +# Panda3D(如果尚未安装) +panda3d>=1.10.10 + +# 网络和串流相关 +websockets>=8.0 +aiohttp>=3.7.0 + +# 图像处理(用于视频编码/解码) +Pillow>=8.0.0 +opencv-python>=4.5.0 + +# 音频处理(可选) +pyaudio>=0.2.11 + +# 注:实际的OpenXR Python绑定需要根据平台和设备单独安装 +# 可能需要从 https://github.com/KhronosGroup/OpenXR-SDK-Source 编译 \ No newline at end of file diff --git a/test_openxr_simple.py b/test_openxr_simple.py new file mode 100644 index 00000000..16558c09 --- /dev/null +++ b/test_openxr_simple.py @@ -0,0 +1,279 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +简单的OpenXR测试脚本 + +在主程序基础上直接添加OpenXR相关代码进行测试 +""" + +import sys +import os + +# 添加项目根目录到Python路径 +sys.path.append(os.path.dirname(os.path.abspath(__file__))) + +from main import MyWorld +from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QHBoxLayout, QLabel +from PyQt5.QtGui import QPixmap, QImage +from PyQt5.QtCore import QTimer + +# Panda3D imports +from panda3d.core import Texture, GraphicsOutput, ATSNone, GraphicsPipe, Vec3 + + +def setup_openxr_simulation(world): + """在主程序基础上设置简单的OpenXR模拟""" + print("🔧 设置OpenXR模拟环境...") + + # 获取渲染尺寸 + render_width = 1280 + render_height = 720 + + # 创建左右眼渲染缓冲区 + from panda3d.core import WindowProperties, FrameBufferProperties, GraphicsPipe + + # 创建窗口属性 + winprops = WindowProperties() + winprops.setSize(render_width//2, render_height) + + # 创建帧缓冲区属性 + fbprops = FrameBufferProperties() + fbprops.setRgbColor(True) + fbprops.setRgbaBits(8, 8, 8, 8) + fbprops.setDepthBits(24) + + # 设置渲染标志和GSG + flags = GraphicsPipe.BFRefuseWindow + gsg = world.win.getGsg() + + left_eye_buffer = world.graphicsEngine.makeOutput( + world.pipe, + "left_eye", + -1000, + fbprops, + winprops, + flags, + gsg, + world.win + ) + + right_eye_buffer = world.graphicsEngine.makeOutput( + world.pipe, + "right_eye", + -1000, + fbprops, + winprops, + flags, + gsg, + world.win + ) + + if not left_eye_buffer or not right_eye_buffer: + print("❌ 无法创建眼部渲染缓冲区") + return None, None + + # 创建用于显示的左右眼纹理,使用与主程序相同的方式 + left_eye_texture = Texture("left_eye_texture") + right_eye_texture = Texture("right_eye_texture") + left_eye_texture.set_format(Texture.F_rgba8) # 使用主程序的设置方式 + right_eye_texture.set_format(Texture.F_rgba8) + left_eye_texture.set_component_type(Texture.T_unsigned_byte) + right_eye_texture.set_component_type(Texture.T_unsigned_byte) + + # 将纹理附加到渲染缓冲区,使用与主程序相同的方式 + left_eye_buffer.add_render_texture(left_eye_texture, GraphicsOutput.RTM_copy_ram) + right_eye_buffer.add_render_texture(right_eye_texture, GraphicsOutput.RTM_copy_ram) + + # 创建左右眼相机 + left_eye_camera = world.makeCamera(left_eye_buffer) + right_eye_camera = world.makeCamera(right_eye_buffer) + + # 设置相机参数,确保与主相机一致 + left_eye_camera.node().get_lens().set_fov(world.camLens.get_fov()) + right_eye_camera.node().get_lens().set_fov(world.camLens.get_fov()) + left_eye_camera.node().get_lens().set_near(world.camLens.get_near()) + right_eye_camera.node().get_lens().set_near(world.camLens.get_near()) + left_eye_camera.node().get_lens().set_far(world.camLens.get_far()) + right_eye_camera.node().get_lens().set_far(world.camLens.get_far()) + + # 设置相机位置(模拟瞳距) + left_eye_camera.setPos(world.cam.getPos() + Vec3(-0.03, 0, 0)) # 瞳距的一半 + right_eye_camera.setPos(world.cam.getPos() + Vec3(0.03, 0, 0)) # 瞳距的一半 + + # 设置相机朝向,确保与主相机一致 + left_eye_camera.setHpr(world.cam.getHpr()) + right_eye_camera.setHpr(world.cam.getHpr()) + + # 设置相机渲染场景 + left_eye_camera.node().setScene(world.render) + right_eye_camera.node().setScene(world.render) + + # 获取渲染管线的最终输出纹理 + def get_final_render_texture(): + """获取渲染管线的最终输出纹理""" + # 修复:正确获取最终渲染纹理的方法 + if world.win: + # 直接从主窗口获取最终渲染纹理 + return world.win.get_texture() + return None + + # 等待并获取渲染结果 + def wait_for_render_texture(): + """等待并获取有效的渲染纹理""" + timeout = 100 # 超时帧数 + for i in range(timeout): + final_texture = get_final_render_texture() + if final_texture and final_texture.hasRamImage(): + return final_texture + world.graphicsEngine.renderFrame() + return None + + # 获取最终渲染纹理作为左右眼纹理的基础 + final_texture = wait_for_render_texture() + if final_texture: + # 使用与主渲染管线相同的纹理参数 + left_eye_texture = final_texture.makeCopy() + right_eye_texture = final_texture.makeCopy() + + # 确保纹理数据可用 + world.graphicsEngine.extractTextureData(left_eye_texture, world.win.getGsg()) + world.graphicsEngine.extractTextureData(right_eye_texture, world.win.getGsg()) + + print("✅ 成功获取渲染管线的最终输出纹理") + else: + print("⚠️ 无法获取渲染管线的最终输出纹理,使用默认设置") + + print("✅ OpenXR模拟环境设置完成") + return left_eye_texture, right_eye_texture + + +def main(): + """主函数""" + print("🚀 启动OpenXR简单测试...") + + # 创建应用程序和世界实例 + app = QApplication(sys.argv) + world = MyWorld() + + # 加载一些测试模型 + print("📦 加载测试场景...") + from panda3d.core import CardMaker + + # 创建地面 + cm = CardMaker("ground") + cm.setFrame(-5, 5, -5, 5) + ground = world.render.attachNewNode(cm.generate()) + ground.setPos(0, 0, -1) + ground.setColor(0.5, 0.8, 0.5, 1.0) + ground.setHpr(0, -90, 0) + + # 创建测试立方体 + cube = world.loader.loadModel("models/environment") + if cube: + cube.reparentTo(world.render) + cube.setScale(0.5) + cube.setPos(0, 0, 0) + else: + # 如果没有环境模型,创建简单立方体 + cm_cube = CardMaker("cube") + cm_cube.setFrame(-0.5, 0.5, -0.5, 0.5) + cube = world.render.attachNewNode(cm_cube.generate()) + cube.setPos(0, 0, 0) + + # 设置OpenXR模拟 + left_tex, right_tex = setup_openxr_simulation(world) + + if not left_tex or not right_tex: + print("❌ OpenXR模拟设置失败") + return + + # 创建显示窗口 + window = QMainWindow() + window.setWindowTitle("OpenXR Simple Test") + window.setGeometry(100, 100, 800, 300) + + central_widget = QWidget() + layout = QHBoxLayout() + central_widget.setLayout(layout) + + left_label = QLabel("Left Eye") + right_label = QLabel("Right Eye") + layout.addWidget(left_label) + layout.addWidget(right_label) + + window.setCentralWidget(central_widget) + window.show() + + def update_display(): + """更新显示""" + try: + # 让RenderPipeline进行渲染 + world.taskMgr.step() + + # 渲染几帧以确保纹理更新 + for _ in range(5): + world.graphicsEngine.renderFrame() + + # 更新左眼纹理 + if left_tex: + # 检查纹理是否具有RAM图像 + if not left_tex.hasRamImage(): + # 如果没有RAM图像,尝试提取 + gsg = world.win.getGsg() + if gsg: + world.graphicsEngine.extractTextureData(left_tex, gsg) + + # 现在检查是否有RAM图像和有效数据 + if left_tex.hasRamImage(): + data = left_tex.getRamImage().getData() + width = left_tex.getXSize() + height = left_tex.getYSize() + expected_len = width * height * 4 + + # 检查数据长度是否正确 + if len(data) == expected_len: + # 使用与主程序相同的方式创建图像 + img = QImage(data, width, height, QImage.Format_ARGB32).mirrored() + pixmap = QPixmap.fromImage(img) + left_label.setPixmap(pixmap.scaled(400, 300)) + left_label.setText("") + + # 更新右眼纹理 + if right_tex: + # 检查纹理是否具有RAM图像 + if not right_tex.hasRamImage(): + # 如果没有RAM图像,尝试提取 + gsg = world.win.getGsg() + if gsg: + world.graphicsEngine.extractTextureData(right_tex, gsg) + + # 现在检查是否有RAM图像和有效数据 + if right_tex.hasRamImage(): + data = right_tex.getRamImage().getData() + width = right_tex.getXSize() + height = right_tex.getYSize() + expected_len = width * height * 4 + + # 检查数据长度是否正确 + if len(data) == expected_len: + # 使用与主程序相同的方式创建图像 + img = QImage(data, width, height, QImage.Format_ARGB32).mirrored() + pixmap = QPixmap.fromImage(img) + right_label.setPixmap(pixmap.scaled(400, 300)) + right_label.setText("") + except Exception as e: + print(f"纹理更新错误: {str(e)}") + + # 定时更新显示 + timer = QTimer() + timer.timeout.connect(update_display) + timer.start(33) # 约30 FPS + + # 运行应用程序 + print("✅ OpenXR简单测试运行中...") + sys.exit(app.exec_()) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/ui/vr_control_panel.py b/ui/vr_control_panel.py deleted file mode 100644 index fbb75c1b..00000000 --- a/ui/vr_control_panel.py +++ /dev/null @@ -1,412 +0,0 @@ -""" -VR控制面板 - -提供VR系统的GUI控制界面 -包括VR启用/禁用、ALVR串流控制、性能监控等功能 -""" - -from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QGroupBox, - QPushButton, QLabel, QProgressBar, QSlider, - QSpinBox, QCheckBox, QComboBox, QTextEdit, - QGridLayout, QFrame, QSizePolicy) -from PyQt5.QtCore import Qt, QTimer, pyqtSignal -from PyQt5.QtGui import QFont, QColor, QPalette - - -class VRControlPanel(QWidget): - """VR控制面板""" - - # 信号定义 - vr_enabled = pyqtSignal(bool) - alvr_streaming_changed = pyqtSignal(bool) - - def __init__(self, world, parent=None): - super().__init__(parent) - self.world = world - self.setupUI() - self.connectSignals() - - # 设置更新定时器 - self.update_timer = QTimer() - self.update_timer.timeout.connect(self.updateStatus) - self.update_timer.start(1000) # 每秒更新一次 - - def setupUI(self): - """设置界面""" - self.setWindowTitle("VR控制面板") - self.setMinimumSize(400, 600) - - # 主布局 - main_layout = QVBoxLayout(self) - - # VR系统控制组 - vr_control_group = self.createVRControlGroup() - main_layout.addWidget(vr_control_group) - - # ALVR串流控制组 - alvr_control_group = self.createALVRControlGroup() - main_layout.addWidget(alvr_control_group) - - # 性能监控组 - performance_group = self.createPerformanceGroup() - main_layout.addWidget(performance_group) - - # 控制器状态组 - controller_group = self.createControllerGroup() - main_layout.addWidget(controller_group) - - # 日志输出组 - log_group = self.createLogGroup() - main_layout.addWidget(log_group) - - # 添加弹性空间 - main_layout.addStretch() - - def createVRControlGroup(self): - """创建VR系统控制组""" - group = QGroupBox("VR系统控制") - layout = QVBoxLayout(group) - - # 状态指示器 - self.vr_status_label = QLabel("VR状态: 未启用") - self.vr_status_label.setStyleSheet("color: red; font-weight: bold;") - layout.addWidget(self.vr_status_label) - - # 启用/禁用VR按钮 - button_layout = QHBoxLayout() - self.enable_vr_button = QPushButton("启用VR") - self.enable_vr_button.clicked.connect(self.enableVR) - button_layout.addWidget(self.enable_vr_button) - - self.disable_vr_button = QPushButton("禁用VR") - self.disable_vr_button.clicked.connect(self.disableVR) - self.disable_vr_button.setEnabled(False) - button_layout.addWidget(self.disable_vr_button) - - layout.addLayout(button_layout) - - # VR设备信息 - self.vr_info_label = QLabel("VR设备: 未连接") - layout.addWidget(self.vr_info_label) - - # VR选项 - options_layout = QGridLayout() - - # 控制器射线显示 - self.show_rays_checkbox = QCheckBox("显示控制器射线") - self.show_rays_checkbox.stateChanged.connect(self.toggleControllerRays) - options_layout.addWidget(self.show_rays_checkbox, 0, 0) - - # 手势识别 - self.gesture_checkbox = QCheckBox("启用手势识别") - self.gesture_checkbox.stateChanged.connect(self.toggleGestureRecognition) - options_layout.addWidget(self.gesture_checkbox, 0, 1) - - # VR交互 - self.interaction_checkbox = QCheckBox("启用VR交互") - self.interaction_checkbox.setChecked(True) - self.interaction_checkbox.stateChanged.connect(self.toggleVRInteraction) - options_layout.addWidget(self.interaction_checkbox, 1, 0) - - layout.addLayout(options_layout) - - return group - - def createALVRControlGroup(self): - """创建ALVR串流控制组""" - group = QGroupBox("ALVR串流控制") - layout = QVBoxLayout(group) - - # 连接状态 - self.alvr_status_label = QLabel("ALVR状态: 未连接") - self.alvr_status_label.setStyleSheet("color: red; font-weight: bold;") - layout.addWidget(self.alvr_status_label) - - # 串流控制按钮 - button_layout = QHBoxLayout() - self.start_streaming_button = QPushButton("开始串流") - self.start_streaming_button.clicked.connect(self.startStreaming) - self.start_streaming_button.setEnabled(False) - button_layout.addWidget(self.start_streaming_button) - - self.stop_streaming_button = QPushButton("停止串流") - self.stop_streaming_button.clicked.connect(self.stopStreaming) - self.stop_streaming_button.setEnabled(False) - button_layout.addWidget(self.stop_streaming_button) - - layout.addLayout(button_layout) - - # 串流质量设置 - quality_layout = QGridLayout() - - # 分辨率 - quality_layout.addWidget(QLabel("分辨率宽度:"), 0, 0) - self.width_spinbox = QSpinBox() - self.width_spinbox.setRange(1920, 4096) - self.width_spinbox.setValue(2880) - self.width_spinbox.setSuffix(" px") - quality_layout.addWidget(self.width_spinbox, 0, 1) - - quality_layout.addWidget(QLabel("分辨率高度:"), 1, 0) - self.height_spinbox = QSpinBox() - self.height_spinbox.setRange(1080, 2160) - self.height_spinbox.setValue(1700) - self.height_spinbox.setSuffix(" px") - quality_layout.addWidget(self.height_spinbox, 1, 1) - - # 帧率 - quality_layout.addWidget(QLabel("帧率:"), 2, 0) - self.fps_spinbox = QSpinBox() - self.fps_spinbox.setRange(60, 120) - self.fps_spinbox.setValue(72) - self.fps_spinbox.setSuffix(" fps") - quality_layout.addWidget(self.fps_spinbox, 2, 1) - - # 比特率 - quality_layout.addWidget(QLabel("比特率:"), 3, 0) - self.bitrate_spinbox = QSpinBox() - self.bitrate_spinbox.setRange(50, 500) - self.bitrate_spinbox.setValue(150) - self.bitrate_spinbox.setSuffix(" Mbps") - quality_layout.addWidget(self.bitrate_spinbox, 3, 1) - - layout.addLayout(quality_layout) - - # 应用设置按钮 - self.apply_quality_button = QPushButton("应用质量设置") - self.apply_quality_button.clicked.connect(self.applyQualitySettings) - layout.addWidget(self.apply_quality_button) - - return group - - def createPerformanceGroup(self): - """创建性能监控组""" - group = QGroupBox("性能监控") - layout = QGridLayout(group) - - # FPS显示 - layout.addWidget(QLabel("渲染FPS:"), 0, 0) - self.fps_label = QLabel("0") - self.fps_label.setStyleSheet("font-weight: bold; color: blue;") - layout.addWidget(self.fps_label, 0, 1) - - # 串流FPS - layout.addWidget(QLabel("串流FPS:"), 1, 0) - self.stream_fps_label = QLabel("0") - self.stream_fps_label.setStyleSheet("font-weight: bold; color: green;") - layout.addWidget(self.stream_fps_label, 1, 1) - - # 延迟 - layout.addWidget(QLabel("延迟:"), 2, 0) - self.latency_label = QLabel("0 ms") - self.latency_label.setStyleSheet("font-weight: bold; color: orange;") - layout.addWidget(self.latency_label, 2, 1) - - # 性能进度条 - layout.addWidget(QLabel("CPU使用率:"), 3, 0) - self.cpu_progress = QProgressBar() - self.cpu_progress.setRange(0, 100) - layout.addWidget(self.cpu_progress, 3, 1) - - layout.addWidget(QLabel("GPU使用率:"), 4, 0) - self.gpu_progress = QProgressBar() - self.gpu_progress.setRange(0, 100) - layout.addWidget(self.gpu_progress, 4, 1) - - return group - - def createControllerGroup(self): - """创建控制器状态组""" - group = QGroupBox("控制器状态") - layout = QVBoxLayout(group) - - # 控制器列表 - self.controller_list = QTextEdit() - self.controller_list.setMaximumHeight(100) - self.controller_list.setReadOnly(True) - layout.addWidget(self.controller_list) - - # 触觉反馈测试 - haptic_layout = QHBoxLayout() - haptic_layout.addWidget(QLabel("触觉反馈测试:")) - - self.haptic_button = QPushButton("发送震动") - self.haptic_button.clicked.connect(self.sendHapticFeedback) - haptic_layout.addWidget(self.haptic_button) - - layout.addLayout(haptic_layout) - - return group - - def createLogGroup(self): - """创建日志输出组""" - group = QGroupBox("系统日志") - layout = QVBoxLayout(group) - - self.log_text = QTextEdit() - self.log_text.setMaximumHeight(150) - self.log_text.setReadOnly(True) - layout.addWidget(self.log_text) - - # 清空日志按钮 - clear_button = QPushButton("清空日志") - clear_button.clicked.connect(self.clearLog) - layout.addWidget(clear_button) - - return group - - def connectSignals(self): - """连接信号""" - # 连接值变化信号 - self.width_spinbox.valueChanged.connect(self.onQualityChanged) - self.height_spinbox.valueChanged.connect(self.onQualityChanged) - self.fps_spinbox.valueChanged.connect(self.onQualityChanged) - self.bitrate_spinbox.valueChanged.connect(self.onQualityChanged) - - def enableVR(self): - """启用VR""" - self.addLog("正在启用VR模式...") - if self.world.enableVRMode(): - self.addLog("✓ VR模式已启用") - self.vr_enabled.emit(True) - else: - self.addLog("✗ VR模式启用失败") - - def disableVR(self): - """禁用VR""" - self.addLog("正在禁用VR模式...") - self.world.disableVRMode() - self.addLog("✓ VR模式已禁用") - self.vr_enabled.emit(False) - - def startStreaming(self): - """开始串流""" - self.addLog("正在开始ALVR串流...") - if self.world.startALVRStreaming(): - self.addLog("✓ ALVR串流已开始") - self.alvr_streaming_changed.emit(True) - else: - self.addLog("✗ ALVR串流开始失败") - - def stopStreaming(self): - """停止串流""" - self.addLog("正在停止ALVR串流...") - self.world.stopALVRStreaming() - self.addLog("✓ ALVR串流已停止") - self.alvr_streaming_changed.emit(False) - - def toggleControllerRays(self, checked): - """切换控制器射线显示""" - self.world.showControllerRays(checked) - self.addLog(f"控制器射线: {'显示' if checked else '隐藏'}") - - def toggleGestureRecognition(self, checked): - """切换手势识别""" - self.world.setVRGestureEnabled(checked) - self.addLog(f"手势识别: {'启用' if checked else '禁用'}") - - def toggleVRInteraction(self, checked): - """切换VR交互""" - self.world.setVRInteractionEnabled(checked) - self.addLog(f"VR交互: {'启用' if checked else '禁用'}") - - def applyQualitySettings(self): - """应用质量设置""" - width = self.width_spinbox.value() - height = self.height_spinbox.value() - fps = self.fps_spinbox.value() - bitrate = self.bitrate_spinbox.value() - - self.world.setALVRStreamQuality(width, height, fps, bitrate) - self.addLog(f"串流质量已设置: {width}x{height} @ {fps}fps, {bitrate}Mbps") - - def sendHapticFeedback(self): - """发送触觉反馈""" - controllers = self.world.getAllControllers() - if controllers: - controller_id = controllers[0] # 使用第一个控制器 - self.world.sendHapticFeedback(controller_id, 0.5, 0.8) - self.addLog(f"已发送触觉反馈到控制器 {controller_id}") - else: - self.addLog("没有可用的控制器") - - def onQualityChanged(self): - """质量设置变化""" - # 可以在这里实现实时质量调整 - pass - - def updateStatus(self): - """更新状态显示""" - # 更新VR状态 - vr_status = self.world.getVRStatus() - - # VR系统状态 - if vr_status['vr_enabled']: - self.vr_status_label.setText("VR状态: 已启用") - self.vr_status_label.setStyleSheet("color: green; font-weight: bold;") - self.enable_vr_button.setEnabled(False) - self.disable_vr_button.setEnabled(True) - - # 更新VR设备信息 - vr_info = vr_status['vr_info'] - if vr_info: - device_info = f"VR设备: {vr_info.get('hmd_manufacturer', 'Unknown')} {vr_info.get('hmd_model', 'Unknown')}" - self.vr_info_label.setText(device_info) - else: - self.vr_status_label.setText("VR状态: 未启用") - self.vr_status_label.setStyleSheet("color: red; font-weight: bold;") - self.enable_vr_button.setEnabled(True) - self.disable_vr_button.setEnabled(False) - self.vr_info_label.setText("VR设备: 未连接") - - # ALVR状态 - if vr_status['alvr_connected']: - self.alvr_status_label.setText("ALVR状态: 已连接") - self.alvr_status_label.setStyleSheet("color: green; font-weight: bold;") - self.start_streaming_button.setEnabled(not vr_status['alvr_streaming']) - self.stop_streaming_button.setEnabled(vr_status['alvr_streaming']) - else: - self.alvr_status_label.setText("ALVR状态: 未连接") - self.alvr_status_label.setStyleSheet("color: red; font-weight: bold;") - self.start_streaming_button.setEnabled(False) - self.stop_streaming_button.setEnabled(False) - - # 性能统计 - streaming_status = vr_status['streaming_status'] - if streaming_status: - self.stream_fps_label.setText(str(streaming_status.get('fps', 0))) - self.latency_label.setText(f"{streaming_status.get('latency', 0)} ms") - - # 控制器状态 - controllers = vr_status['controllers'] - if controllers: - controller_text = f"已连接 {len(controllers)} 个控制器:\n" - for i, controller_id in enumerate(controllers): - controller_text += f"控制器 {i+1}: ID {controller_id}\n" - self.controller_list.setText(controller_text) - else: - self.controller_list.setText("没有连接的控制器") - - def addLog(self, message): - """添加日志""" - import datetime - timestamp = datetime.datetime.now().strftime("%H:%M:%S") - log_message = f"[{timestamp}] {message}" - self.log_text.append(log_message) - print(log_message) # 同时输出到控制台 - - def clearLog(self): - """清空日志""" - self.log_text.clear() - - def closeEvent(self, event): - """关闭事件""" - # 停止更新定时器 - if hasattr(self, 'update_timer'): - self.update_timer.stop() - - # 如果VR启用,先禁用 - if self.world.isVREnabled(): - self.disableVR() - - event.accept() \ No newline at end of file diff --git a/vr/__init__.py b/vr/__init__.py deleted file mode 100644 index e2f59ea8..00000000 --- a/vr/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -""" -VR系统模块 - 提供VR设备集成和画面串流功能 - -此模块提供了一个完整的VR系统实现,包括: -- VR设备初始化和管理 -- 立体渲染和显示 -- VR输入处理 -- 画面串流到VR设备 -""" - -from .vr_system import VRSystem \ No newline at end of file diff --git a/vr/devices/__init__.py b/vr/devices/__init__.py deleted file mode 100644 index e76b3ae1..00000000 --- a/vr/devices/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -""" -VR设备特定实现模块 - -此模块包含针对不同VR设备的特定实现: -- OpenXR设备 -- Oculus设备 -- SteamVR设备 -""" \ No newline at end of file diff --git a/vr/vr_display.py b/vr/vr_display.py deleted file mode 100644 index d33d0498..00000000 --- a/vr/vr_display.py +++ /dev/null @@ -1,379 +0,0 @@ -""" -VR显示管理模块 - -此模块负责VR立体渲染和显示,包括: -- 创建左右眼相机 -- 设置渲染目标 -- 应用畸变校正 -- 管理渲染分辨率和质量 -""" - -from panda3d.core import ( - NodePath, Camera, PerspectiveLens, - FrameBufferProperties, WindowProperties, - GraphicsOutput, GraphicsEngine, - Texture, GraphicsBuffer, Vec3, - RenderState, ColorWriteAttrib, ShaderAttrib -) -from direct.filter.FilterManager import FilterManager - -class VRDisplay: - """管理VR显示和渲染""" - - def __init__(self, vr_system): - """ - 初始化VR显示管理器 - - Args: - vr_system: VR系统实例 - """ - self.vr_system = vr_system - self.world = vr_system.world - self.device = vr_system.device - - # 渲染目标 - self.left_buffer = None - self.right_buffer = None - self.left_texture = None - self.right_texture = None - - # 相机 - self.left_camera = None - self.right_camera = None - self.left_cam_node = None - self.right_cam_node = None - - # 畸变校正 - self.distortion_manager = None - self.is_initialized = False - - # 渲染设置 - self.render_resolution = vr_system.config.get("render_resolution", [2048, 2048]) - self.ipd = vr_system.config.get("ipd", 0.064) # 默认瞳距64mm - - def initialize(self): - """初始化VR显示系统""" - print("正在初始化VR显示系统...") - - # 1. 创建渲染目标 - if not self._create_render_targets(): - print("✗ 创建VR渲染目标失败") - return False - - # 2. 创建立体相机 - if not self._setup_stereo_cameras(): - print("✗ 设置VR立体相机失败") - return False - - # 3. 设置畸变校正 - if not self._setup_distortion_correction(): - print("✗ 设置畸变校正失败") - # 这不是致命错误,可以继续 - print("⚠ 将使用未校正的渲染") - - self.is_initialized = True - print("✓ VR显示系统初始化完成") - return True - - def _create_render_targets(self): - """创建VR渲染目标""" - try: - # 获取图形引擎和窗口 - graphics_engine = self.world.graphicsEngine - main_window = self.world.win - - # 创建帧缓冲属性 - fb_props = FrameBufferProperties() - fb_props.setRgbColor(True) - fb_props.setRgbaBits(8, 8, 8, 8) - fb_props.setDepthBits(24) - fb_props.setMultisamples(4) # MSAA 4x - - # 创建窗口属性 - win_props = WindowProperties() - win_props.setSize(self.render_resolution[0], self.render_resolution[1]) - - # 创建左眼缓冲区 - self.left_buffer = graphics_engine.makeOutput( - main_window.getPipe(), "left_eye_buffer", - -1, fb_props, win_props, - GraphicsOutput.BF_refuse_window, - main_window.getGsg(), main_window - ) - - if not self.left_buffer: - print("✗ 创建左眼缓冲区失败") - return False - - self.left_buffer.setClearColor((0, 0, 0, 1)) - self.left_texture = Texture() - self.left_buffer.addRenderTexture( - self.left_texture, - GraphicsOutput.RTM_copy_ram, - GraphicsOutput.RTP_color - ) - - # 创建右眼缓冲区 - self.right_buffer = graphics_engine.makeOutput( - main_window.getPipe(), "right_eye_buffer", - -1, fb_props, win_props, - GraphicsOutput.BF_refuse_window, - main_window.getGsg(), main_window - ) - - if not self.right_buffer: - print("✗ 创建右眼缓冲区失败") - return False - - self.right_buffer.setClearColor((0, 0, 0, 1)) - self.right_texture = Texture() - self.right_buffer.addRenderTexture( - self.right_texture, - GraphicsOutput.RTM_copy_ram, - GraphicsOutput.RTP_color - ) - - print("✓ VR渲染目标创建成功") - return True - except Exception as e: - print(f"创建VR渲染目标时出错: {e}") - return False - - def _setup_stereo_cameras(self): - """设置立体相机""" - try: - # 获取主相机信息 - main_cam = self.world.camera - main_cam_pos = main_cam.getPos() - main_cam_hpr = main_cam.getHpr() - - # 创建左眼相机 - self.left_cam_node = Camera("left_eye_camera") - left_lens = PerspectiveLens() - left_lens.setFov(90) # 90度视场角 - left_lens.setNearFar(0.01, 1000) - self.left_cam_node.setLens(left_lens) - self.left_camera = NodePath(self.left_cam_node) - self.left_camera.reparentTo(main_cam) - - # 设置左眼相机位置(向左偏移半个瞳距) - self.left_camera.setPos(-self.ipd/2, 0, 0) - - # 创建右眼相机 - self.right_cam_node = Camera("right_eye_camera") - right_lens = PerspectiveLens() - right_lens.setFov(90) # 90度视场角 - right_lens.setNearFar(0.01, 1000) - self.right_cam_node.setLens(right_lens) - self.right_camera = NodePath(self.right_cam_node) - self.right_camera.reparentTo(main_cam) - - # 设置右眼相机位置(向右偏移半个瞳距) - self.right_camera.setPos(self.ipd/2, 0, 0) - - # 设置相机显示区域 - self.left_cam_node.setScene(self.world.render) - self.right_cam_node.setScene(self.world.render) - - # 连接相机到缓冲区 - self.left_buffer.setSort(0) - self.left_buffer.setClearActive(True) - self.left_buffer.setActive(True) - self.left_buffer.addDisplayRegion().setCamera(self.left_camera) - - self.right_buffer.setSort(0) - self.right_buffer.setClearActive(True) - self.right_buffer.setActive(True) - self.right_buffer.addDisplayRegion().setCamera(self.right_camera) - - print("✓ VR立体相机设置成功") - return True - except Exception as e: - print(f"设置VR立体相机时出错: {e}") - return False - - def _setup_distortion_correction(self): - """设置畸变校正""" - try: - # 如果设备提供了畸变校正参数 - if self.device and hasattr(self.device, 'get_distortion_parameters'): - distortion_params = self.device.get_distortion_parameters() - - # 创建畸变校正着色器 - # 这里使用FilterManager来应用后处理效果 - self.distortion_manager = FilterManager(self.world.win, self.world.cam) - - # 应用畸变校正着色器到左右眼纹理 - # 具体实现取决于设备提供的畸变参数格式 - - print("✓ VR畸变校正设置成功") - return True - else: - print("⚠ 设备未提供畸变校正参数") - return False - except Exception as e: - print(f"设置畸变校正时出错: {e}") - return False - - def update(self): - """更新VR显示(每帧调用)""" - if not self.is_initialized: - return - - # 1. 获取头部姿态 - if self.device: - head_pose = self.device.get_head_pose() - if head_pose: - self.update_cameras(head_pose) - - # 2. 如果使用动态分辨率,根据性能调整 - if self.vr_system.config.get("performance", {}).get("dynamic_resolution", False): - self._update_dynamic_resolution() - - def update_cameras(self, head_pose): - """ - 根据头部姿态更新相机位置 - - Args: - head_pose: 包含位置和旋转的头部姿态数据 - """ - if not self.is_initialized: - return - - # 获取主相机 - main_cam = self.world.camera - - # 应用头部位置和旋转 - if 'position' in head_pose: - main_cam.setPos(Vec3(*head_pose['position'])) - - if 'rotation' in head_pose: - main_cam.setHpr(Vec3(*head_pose['rotation'])) - - def render_frame(self): - """渲染一帧VR画面""" - if not self.is_initialized: - return None, None - - # 强制渲染左右眼缓冲区 - self.world.graphicsEngine.renderFrame() - - # 获取渲染结果 - left_image = self.left_texture.getRamImage() - right_image = self.right_texture.getRamImage() - - return left_image, right_image - - def shutdown(self): - """关闭VR显示系统""" - if not self.is_initialized: - return True - - print("正在关闭VR显示系统...") - - # 移除畸变校正 - if self.distortion_manager: - self.distortion_manager.cleanup() - self.distortion_manager = None - - # 移除相机 - if self.left_camera: - self.left_camera.removeNode() - self.left_camera = None - - if self.right_camera: - self.right_camera.removeNode() - self.right_camera = None - - # 移除缓冲区 - if self.left_buffer: - self.world.graphicsEngine.removeWindow(self.left_buffer) - self.left_buffer = None - - if self.right_buffer: - self.world.graphicsEngine.removeWindow(self.right_buffer) - self.right_buffer = None - - self.is_initialized = False - print("✓ VR显示系统已关闭") - return True - - def set_resolution(self, width, height): - """ - 设置VR渲染分辨率 - - Args: - width: 宽度(像素) - height: 高度(像素) - """ - if not self.is_initialized: - print("VR显示系统未初始化,无法设置分辨率") - return False - - try: - self.render_resolution = [width, height] - - # 需要重新创建缓冲区 - self.shutdown() - self.initialize() - - print(f"✓ VR渲染分辨率已设置为 {width}x{height}") - return True - except Exception as e: - print(f"设置VR渲染分辨率时出错: {e}") - return False - - def set_ipd(self, ipd): - """ - 设置瞳距 - - Args: - ipd: 瞳距(米) - """ - if not self.is_initialized: - print("VR显示系统未初始化,无法设置瞳距") - return False - - try: - self.ipd = ipd - - # 更新相机位置 - if self.left_camera and self.right_camera: - self.left_camera.setPos(-self.ipd/2, 0, 0) - self.right_camera.setPos(self.ipd/2, 0, 0) - - print(f"✓ VR瞳距已设置为 {ipd}m") - return True - except Exception as e: - print(f"设置VR瞳距时出错: {e}") - return False - - def _update_dynamic_resolution(self): - """根据性能动态调整渲染分辨率""" - # 获取当前帧率 - clock = self.world.globalClock - fps = clock.getAverageFrameRate() - - # 目标帧率 - target_fps = self.vr_system.config.get("refresh_rate", 90) - - # 如果帧率低于目标的90%,降低分辨率 - if fps < target_fps * 0.9: - current_width, current_height = self.render_resolution - new_width = int(current_width * 0.9) - new_height = int(current_height * 0.9) - - # 设置最低分辨率限制 - if new_width >= 1024 and new_height >= 1024: - self.set_resolution(new_width, new_height) - - # 如果帧率高于目标的110%,提高分辨率 - elif fps > target_fps * 1.1: - current_width, current_height = self.render_resolution - new_width = int(current_width * 1.1) - new_height = int(current_height * 1.1) - - # 设置最高分辨率限制 - if new_width <= 4096 and new_height <= 4096: - self.set_resolution(new_width, new_height) - \ No newline at end of file diff --git a/vr/vr_input.py b/vr/vr_input.py deleted file mode 100644 index 1365d9fc..00000000 --- a/vr/vr_input.py +++ /dev/null @@ -1,363 +0,0 @@ -""" -VR输入处理模块 - -此模块负责处理VR输入设备,包括: -- 控制器跟踪 -- 按钮和触摸板输入 -- 手势识别 -- 触觉反馈 -""" - -from panda3d.core import NodePath, LineSegs, Vec3, Vec4 -from panda3d.core import CollisionRay, CollisionNode, CollisionTraverser, CollisionHandlerQueue -from panda3d.core import BitMask32 -from direct.task import Task -import math -import time - -class VRInput: - """处理VR输入设备""" - - def __init__(self, vr_system): - """ - 初始化VR输入处理器 - - Args: - vr_system: VR系统实例 - """ - self.vr_system = vr_system - self.world = vr_system.world - self.device = vr_system.device - - # 控制器 - self.controllers = {} - self.controller_models = {} - self.controller_rays = {} - - # 输入状态 - self.button_states = {} - self.analog_states = {} - self.last_button_states = {} - - # 配置 - self.show_rays = True - self.ray_length = 100.0 - self.ray_color = Vec4(0, 0.5, 1, 0.8) - - # 功能标志 - self.gesture_enabled = True - self.interaction_enabled = True - self.haptic_enabled = True - - # 状态 - self.is_initialized = False - self.tracking_origin = None - - # 碰撞检测 - self.traverser = CollisionTraverser("vr_traverser") - self.queue = CollisionHandlerQueue() - - def initialize(self): - """初始化VR输入系统""" - print("正在初始化VR输入系统...") - - if not self.device: - print("✗ VR设备未初始化,无法设置输入") - return False - - # 1. 设置跟踪原点 - self.tracking_origin = self.world.render.attachNewNode("vr_tracking_origin") - - # 2. 设置控制器 - if not self._setup_controllers(): - print("✗ 设置VR控制器失败") - # 这不是致命错误,可以继续 - print("⚠ VR将在没有控制器的情况下运行") - - # 3. 定义输入动作 - self._define_actions() - - # 4. 创建控制器射线 - if self.show_rays: - self._create_controller_rays() - - self.is_initialized = True - print("✓ VR输入系统初始化完成") - return True - - def _setup_controllers(self): - """设置控制器跟踪""" - try: - # 获取设备控制器信息 - if hasattr(self.device, 'get_controllers'): - controllers_info = self.device.get_controllers() - - if not controllers_info: - print("⚠ 未检测到VR控制器") - return False - - # 为每个控制器创建节点 - for controller_id, info in controllers_info.items(): - controller_node = self.tracking_origin.attachNewNode(f"controller_{controller_id}") - - # 加载控制器模型(如果有) - model_path = info.get('model_path') - if model_path: - try: - controller_model = self.world.loader.loadModel(model_path) - controller_model.reparentTo(controller_node) - self.controller_models[controller_id] = controller_model - except Exception as e: - print(f"加载控制器模型失败: {e}") - # 使用默认模型 - self._create_default_controller_model(controller_node, controller_id) - else: - # 使用默认模型 - self._create_default_controller_model(controller_node, controller_id) - - # 存储控制器节点 - self.controllers[controller_id] = controller_node - - # 初始化按钮状态 - self.button_states[controller_id] = {} - self.analog_states[controller_id] = {} - self.last_button_states[controller_id] = {} - - print(f"✓ 已设置 {len(self.controllers)} 个VR控制器") - return True - else: - print("⚠ VR设备不支持控制器跟踪") - return False - except Exception as e: - print(f"设置VR控制器时出错: {e}") - return False - - def _create_default_controller_model(self, parent_node, controller_id): - """创建默认控制器模型""" - from panda3d.core import GeomVertexFormat, GeomVertexData, Geom, GeomTriangles - from panda3d.core import GeomVertexWriter, GeomNode - - # 创建简单的控制器几何体 - format = GeomVertexFormat.getV3n3c4() - vdata = GeomVertexData('controller', format, Geom.UHStatic) - - vertex = GeomVertexWriter(vdata, 'vertex') - normal = GeomVertexWriter(vdata, 'normal') - color = GeomVertexWriter(vdata, 'color') - - # 简单的手柄形状 - # 顶部 - vertex.addData3f(0, 0, 0.1) - normal.addData3f(0, 0, 1) - color.addData4f(0.8, 0.8, 0.8, 1) - - # 底部圆形的顶点 - radius = 0.03 - segments = 8 - for i in range(segments): - angle = 2.0 * math.pi * i / segments - x = radius * math.cos(angle) - y = radius * math.sin(angle) - vertex.addData3f(x, y, -0.1) - normal.addData3f(x, y, 0) - color.addData4f(0.5, 0.5, 0.5, 1) - - # 创建三角形 - tris = GeomTriangles(Geom.UHStatic) - - # 连接顶部到圆形底部 - for i in range(segments): - tris.addVertices(0, i + 1, (i + 1) % segments + 1) - tris.closePrimitive() - - # 创建几何体 - geom = Geom(vdata) - geom.addPrimitive(tris) - - # 创建GeomNode - node = GeomNode(f'controller_{controller_id}_geom') - node.addGeom(geom) - - # 创建NodePath并附加到父节点 - controller_model = NodePath(node) - controller_model.reparentTo(parent_node) - - # 存储模型引用 - self.controller_models[controller_id] = controller_model - - def _define_actions(self): - """定义输入动作映射""" - # 这里可以定义按钮到动作的映射 - self.input_actions = { - "trigger": { - "description": "触发器", - "button": "trigger", - "action": self._on_trigger - }, - "grip": { - "description": "握把", - "button": "grip", - "action": self._on_grip - }, - "menu": { - "description": "菜单按钮", - "button": "menu", - "action": self._on_menu - }, - "thumbstick_press": { - "description": "摇杆按下", - "button": "thumbstick", - "action": self._on_thumbstick_press - }, - "teleport": { - "description": "传送", - "button": "thumbstick", - "analog": True, - "direction": "up", - "action": self._on_teleport - } - } - - def _create_controller_rays(self): - """创建控制器射线可视化""" - for controller_id, controller in self.controllers.items(): - # 创建射线线段 - ls = LineSegs() - ls.setThickness(2.0) - ls.setColor(self.ray_color) - ls.moveTo(0, 0, 0) - ls.drawTo(0, self.ray_length, 0) - - # 创建射线节点 - ray_node = ls.create() - ray_path = NodePath(ray_node) - ray_path.reparentTo(controller) - - # 默认隐藏 - ray_path.hide() - - # 存储射线引用 - self.controller_rays[controller_id] = ray_path - - def update(self): - """更新控制器状态(每帧调用)""" - if not self.is_initialized: - return - - # 1. 更新控制器位置和旋转 - self._update_controller_poses() - - # 2. 更新按钮状态 - self._update_button_states() - - # 3. 处理输入事件 - self._process_input() - - # 4. 更新射线(如果启用) - if self.show_rays: - self._update_controller_rays() - - def _update_controller_poses(self): - """更新控制器位置和旋转""" - if not self.device or not hasattr(self.device, 'get_controller_poses'): - return - - # 获取所有控制器的姿态 - poses = self.device.get_controller_poses() - if not poses: - return - - # 更新每个控制器的位置和旋转 - for controller_id, pose in poses.items(): - if controller_id in self.controllers: - controller = self.controllers[controller_id] - - if 'position' in pose: - controller.setPos(Vec3(*pose['position'])) - - if 'rotation' in pose: - controller.setHpr(Vec3(*pose['rotation'])) - - def _update_button_states(self): - """更新按钮状态""" - if not self.device or not hasattr(self.device, 'get_controller_inputs'): - return - - # 获取所有控制器的输入状态 - inputs = self.device.get_controller_inputs() - if not inputs: - return - - # 更新每个控制器的按钮和模拟输入状态 - for controller_id, input_data in inputs.items(): - if controller_id in self.controllers: - # 保存上一帧的按钮状态 - self.last_button_states[controller_id] = self.button_states[controller_id].copy() - - # 更新按钮状态 - if 'buttons' in input_data: - self.button_states[controller_id] = input_data['buttons'] - - # 更新模拟输入状态 - if 'analogs' in input_data: - self.analog_states[controller_id] = input_data['analogs'] - - def _process_input(self): - """处理输入事件""" - if not self.interaction_enabled: - return - - # 检查每个定义的动作 - for action_name, action_def in self.input_actions.items(): - button = action_def.get('button') - - for controller_id in self.controllers: - # 处理按钮动作 - if not action_def.get('analog', False): - # 检查按钮是否刚被按下 - if (button in self.button_states.get(controller_id, {}) and - button in self.last_button_states.get(controller_id, {}) and - self.button_states[controller_id][button] and - not self.last_button_states[controller_id][button]): - - # 调用动作处理函数 - if 'action' in action_def: - action_def['action'](controller_id) - - # 处理模拟输入动作 - else: - if button in self.analog_states.get(controller_id, {}): - analog_value = self.analog_states[controller_id][button] - direction = action_def.get('direction') - - # 根据方向检查模拟输入 - if direction == 'up' and analog_value[1] > 0.7: - if 'action' in action_def: - action_def['action'](controller_id) - elif direction == 'down' and analog_value[1] < -0.7: - if 'action' in action_def: - action_def['action'](controller_id) - elif direction == 'left' and analog_value[0] < -0.7: - if 'action' in action_def: - action_def['action'](controller_id) - elif direction == 'right' and analog_value[0] > 0.7: - if 'action' in action_def: - action_def['action'](controller_id) - - def _update_controller_rays(self): - """更新控制器射线可视化""" - for controller_id, ray in self.controller_rays.items(): - if self.show_rays: - ray.show() - - # 执行射线检测 - if controller_id in self.controllers: - controller = self.controllers[controller_id] - origin = controller.getPos(self.world.render) - direction = Vec3(0, 1, 0) # 控制器前方 - direction = self.world.render.getRelativeVector(controller, direction) - - # 射线检测 - result = self._perform_ray_test(origin, direction) - - # \ No newline at end of file diff --git a/vr/vr_system.py b/vr/vr_system.py deleted file mode 100644 index 8fc7bc2f..00000000 --- a/vr/vr_system.py +++ /dev/null @@ -1,380 +0,0 @@ -""" -VR系统核心管理模块 - -此模块提供VR系统的核心功能,负责初始化和协调其他VR组件。 -""" - -import os -import json -from panda3d.core import loadPrcFileData, WindowProperties -from direct.task import Task - -class VRSystem: - """VR系统的核心管理类,负责初始化和协调其他VR组件""" - - def __init__(self, world): - """ - 初始化VR系统 - - Args: - world: 游戏世界实例,提供对渲染器和场景的访问 - """ - self.world = world - self.display = None - self.input = None - self.streaming = None - self.device = None - self.is_initialized = False - self.config = self._load_config() - - # 状态变量 - self.vr_enabled = False - self.streaming_enabled = False - - print("✓ VR系统核心已创建") - - def _load_config(self): - """加载VR配置""" - config_path = os.path.join(os.path.dirname(__file__), "vr_config.json") - default_config = { - "preferred_device": "openxr", - "render_resolution": [2048, 2048], - "refresh_rate": 90, - "ipd": 0.064, # 瞳距,单位:米 - "streaming": { - "enabled": True, - "encoder": "h264", - "bitrate": 30000000, # 30 Mbps - "use_hardware_encoding": True - }, - "performance": { - "dynamic_resolution": True, - "fixed_foveated_rendering": True, - "motion_smoothing": True - } - } - - try: - if os.path.exists(config_path): - with open(config_path, 'r') as f: - return json.load(f) - else: - # 如果配置文件不存在,创建默认配置 - with open(config_path, 'w') as f: - json.dump(default_config, f, indent=4) - return default_config - except Exception as e: - print(f"加载VR配置失败: {e}") - return default_config - - def save_config(self): - """保存VR配置""" - config_path = os.path.join(os.path.dirname(__file__), "vr_config.json") - try: - with open(config_path, 'w') as f: - json.dump(self.config, f, indent=4) - print("✓ VR配置已保存") - return True - except Exception as e: - print(f"保存VR配置失败: {e}") - return False - - def initialize(self): - """初始化VR系统""" - if self.is_initialized: - print("VR系统已经初始化") - return True - - print("正在初始化VR系统...") - - # 1. 导入必要的模块(延迟导入以避免循环依赖) - from .vr_display import VRDisplay - from .vr_input import VRInput - from .vr_streaming import VRStreaming - - # 2. 根据配置选择设备实现 - device_type = self.config.get("preferred_device", "openxr") - device_created = self._create_device(device_type) - - if not device_created: - print("✗ 创建VR设备失败") - return False - - # 3. 创建显示、输入和串流组件 - self.display = VRDisplay(self) - self.input = VRInput(self) - self.streaming = VRStreaming(self) - - # 4. 初始化各组件 - display_initialized = self.display.initialize() - if not display_initialized: - print("✗ 初始化VR显示失败") - return False - - input_initialized = self.input.initialize() - if not input_initialized: - print("✗ 初始化VR输入失败") - return False - - # 5. 设置更新任务 - taskMgr = self.world.taskMgr - taskMgr.add(self.update, "vr_system_update") - - self.is_initialized = True - self.vr_enabled = True - print("✓ VR系统初始化完成") - return True - - def _create_device(self, device_type): - """创建VR设备实现""" - try: - if device_type == "openxr": - from .devices.openxr_device import OpenXRDevice - self.device = OpenXRDevice() - elif device_type == "oculus": - from .devices.oculus_device import OculusDevice - self.device = OculusDevice() - elif device_type == "steamvr": - from .devices.steamvr_device import SteamVRDevice - self.device = SteamVRDevice() - else: - print(f"不支持的设备类型: {device_type}") - return False - - device_initialized = self.device.initialize() - if not device_initialized: - print(f"✗ 初始化{device_type}设备失败") - return False - - print(f"✓ 已创建并初始化{device_type}设备") - return True - except ImportError as e: - print(f"导入设备模块失败: {e}") - return False - except Exception as e: - print(f"创建设备实例失败: {e}") - return False - - def shutdown(self): - """关闭VR系统""" - if not self.is_initialized: - print("VR系统未初始化") - return True - - print("正在关闭VR系统...") - - # 1. 停止串流 - if self.streaming and self.streaming_enabled: - self.stop_streaming() - - # 2. 关闭各组件 - if self.input: - self.input.shutdown() - - if self.display: - self.display.shutdown() - - if self.device: - self.device.shutdown() - - # 3. 移除更新任务 - taskMgr = self.world.taskMgr - taskMgr.remove("vr_system_update") - - self.is_initialized = False - self.vr_enabled = False - print("✓ VR系统已关闭") - return True - - def update(self, task): - """更新VR系统状态(每帧调用)""" - if not self.is_initialized: - return Task.cont - - # 1. 更新设备状态 - if self.device: - self.device.update() - - # 2. 更新输入 - if self.input: - self.input.update() - - # 3. 更新显示 - if self.display: - self.display.update() - - # 4. 更新串流 - if self.streaming and self.streaming_enabled: - self.streaming.update() - - return Task.cont - - def start_streaming(self): - """开始VR画面串流""" - if not self.is_initialized: - print("VR系统未初始化,无法开始串流") - return False - - if not self.streaming: - print("VR串流组件未创建") - return False - - if self.streaming_enabled: - print("VR串流已经启动") - return True - - result = self.streaming.start() - if result: - self.streaming_enabled = True - print("✓ VR串流已启动") - else: - print("✗ 启动VR串流失败") - - return result - - def stop_streaming(self): - """停止VR画面串流""" - if not self.streaming or not self.streaming_enabled: - print("VR串流未启动") - return True - - result = self.streaming.stop() - if result: - self.streaming_enabled = False - print("✓ VR串流已停止") - else: - print("✗ 停止VR串流失败") - - return result - - def get_status(self): - """获取VR系统状态信息""" - status = { - "initialized": self.is_initialized, - "vr_enabled": self.vr_enabled, - "streaming_enabled": self.streaming_enabled, - "device_type": self.config.get("preferred_device", "unknown"), - "render_resolution": self.config.get("render_resolution", [0, 0]), - "refresh_rate": self.config.get("refresh_rate", 0) - } - - # 添加设备特定信息 - if self.device: - device_status = self.device.get_status() - status.update(device_status) - - # 添加串流信息 - if self.streaming: - streaming_status = self.streaming.get_statistics() - status["streaming"] = streaming_status - - return status - - def set_render_resolution(self, width, height): - """设置VR渲染分辨率""" - if not self.is_initialized: - print("VR系统未初始化,无法设置渲染分辨率") - return False - - self.config["render_resolution"] = [width, height] - - if self.display: - result = self.display.set_resolution(width, height) - if result: - print(f"✓ VR渲染分辨率已设置为 {width}x{height}") - self.save_config() - return result - - return False - - def set_refresh_rate(self, rate): - """设置VR刷新率""" - if not self.is_initialized: - print("VR系统未初始化,无法设置刷新率") - return False - - self.config["refresh_rate"] = rate - - if self.device: - result = self.device.set_refresh_rate(rate) - if result: - print(f"✓ VR刷新率已设置为 {rate}Hz") - self.save_config() - return result - - return False - - def set_ipd(self, ipd): - """设置瞳距(单位:米)""" - if not self.is_initialized: - print("VR系统未初始化,无法设置瞳距") - return False - - self.config["ipd"] = ipd - - if self.display: - result = self.display.set_ipd(ipd) - if result: - print(f"✓ VR瞳距已设置为 {ipd}m") - self.save_config() - return result - - return False - - def set_streaming_quality(self, bitrate=None, encoder=None, use_hardware=None): - """设置串流质量参数""" - if not self.streaming: - print("VR串流组件未创建,无法设置串流质量") - return False - - streaming_config = self.config.get("streaming", {}) - - if bitrate is not None: - streaming_config["bitrate"] = bitrate - - if encoder is not None: - streaming_config["encoder"] = encoder - - if use_hardware is not None: - streaming_config["use_hardware_encoding"] = use_hardware - - self.config["streaming"] = streaming_config - - result = self.streaming.set_quality( - bitrate=streaming_config.get("bitrate"), - encoder=streaming_config.get("encoder"), - use_hardware=streaming_config.get("use_hardware_encoding") - ) - - if result: - print("✓ VR串流质量参数已更新") - self.save_config() - - return result - - def calibrate(self): - """启动VR校准过程""" - if not self.is_initialized: - print("VR系统未初始化,无法校准") - return False - - print("正在启动VR校准...") - - # 导入校准模块 - from .vr_calibration import start_calibration - - result = start_calibration(self) - if result: - print("✓ VR校准完成") - else: - print("✗ VR校准失败或被取消") - - return result - - def is_vr_enabled(self): - """检查VR是否启用""" - return self.vr_enabled - - def is_streaming(self): - """检查是否正在串流""" - return self.streaming_enabled \ No newline at end of file diff --git a/vr_display_demo.py b/vr_display_demo.py deleted file mode 100644 index a117204c..00000000 --- a/vr_display_demo.py +++ /dev/null @@ -1,500 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -VR显示功能演示脚本 -演示引擎画面在VR中的显示效果,包括ALVR串流 -""" - -import sys -import os -import time -from main import MyWorld - - -def demo_vr_basic_display(): - """演示基本VR显示功能""" - print("=== VR基本显示演示 ===") - - # 创建世界实例 - world = MyWorld() - - # 加载一些模型来展示 - print("📦 加载演示场景...") - try: - # 创建一些简单的几何体作为演示 - from panda3d.core import CardMaker, Vec4 - - # 创建地面 - cm = CardMaker("ground") - cm.setFrame(-10, 10, -10, 10) - ground = world.render.attachNewNode(cm.generate()) - ground.setPos(0, 0, -1) - ground.setColor(0.5, 0.8, 0.5, 1.0) - ground.setHpr(0, -90, 0) - - # 创建一些立方体 - for i in range(5): - for j in range(5): - cube = world.loader.loadModel("environment") - if not cube: - # 如果没有environment模型,创建简单的立方体 - cm_cube = CardMaker(f"cube_{i}_{j}") - cm_cube.setFrame(-0.5, 0.5, -0.5, 0.5) - cube = world.render.attachNewNode(cm_cube.generate()) - - cube.setPos(i * 2 - 4, j * 2 - 4, 0) - cube.setScale(0.5) - cube.setColor(i/5.0, j/5.0, 1.0, 1.0) - - print("✓ 演示场景加载完成") - - except Exception as e: - print(f"场景加载错误: {str(e)}") - - # 初始化VR系统 - print("\n🥽 初始化VR显示系统...") - vr_success = world.initializeVR() - - if vr_success: - print("✓ VR系统初始化成功") - vr_info = world.getVRInfo() - - print(f"\n📊 VR显示信息:") - print(f" - 模式: {vr_info['mode']}") - print(f" - 渲染尺寸: {vr_info['render_size']}") - print(f" - 模拟模式: {vr_info['simulation_mode']}") - - if vr_info['simulation_mode']: - print("\n💡 模拟模式说明:") - print(" - 左右眼视图将并排显示在主窗口") - print(" - 可以看到立体渲染效果") - print(" - 头部会有轻微的模拟摆动") - else: - print("\n💡 真实VR模式:") - print(" - 画面将渲染到VR头盔") - print(" - 支持真实的头部追踪") - print(" - 控制器交互可用") - - # 运行几秒钟以展示效果 - print(f"\n🎮 VR显示演示运行中... (10秒)") - print(" 观察主窗口中的立体渲染效果") - - start_time = time.time() - while time.time() - start_time < 10: - # 处理Panda3D事件 - if hasattr(world, 'taskMgr'): - world.taskMgr.step() - time.sleep(0.016) # ~60 FPS - - print("✓ VR显示演示完成") - - else: - print("✗ VR系统初始化失败") - return False - - # 关闭VR系统 - world.shutdownVR() - print("\n✓ VR系统已关闭") - - return True - - -def demo_alvr_streaming(): - """演示ALVR串流功能""" - print("=== ALVR串流显示演示 ===") - - # 创建世界实例 - world = MyWorld() - - # 首先初始化VR系统 - print("🥽 初始化VR系统...") - if not world.initializeVR(): - print("✗ VR系统初始化失败") - return False - - # 检查是否为直连模式(SteamVR已运行) - if world.alvr_streamer._is_steamvr_active(): - print("💡 检测到SteamVR正在运行,将使用直连模式") - print(" 在直连模式下,画面将直接通过SteamVR传输到头显") - print(" 无需通过ALVR服务器进行额外的串流处理") - - # 初始化ALVR串流 - print("\n📡 初始化ALVR串流...") - print("💡 检测到VR设备已连接,正在尝试连接ALVR服务器...") - alvr_success = world.initializeALVR() - - if alvr_success: - print("✓ ALVR串流初始化成功") - - # 设置串流质量 - print("\n🎥 配置串流质量...") - quality_set = world.setALVRStreamQuality(2880, 1700, 72, 150) - if quality_set: - print(" ✓ 串流质量: 2880x1700@72fps, 150Mbps") - - # 获取串流状态 - status = world.getALVRStreamingStatus() - print(f"\n📊 ALVR串流状态:") - print(f" - 连接状态: {status['connected']}") - print(f" - 串流状态: {status['streaming']}") - print(f" - 分辨率: {status['resolution']}") - print(f" - 帧率: {status['fps']}") - print(f" - 码率: {status['bitrate']}Mbps") - - # 开始串流 - print(f"\n🚀 开始ALVR串流...") - if world.startALVRStreaming(): - print("✓ ALVR串流已开始") - - print(f"\n💡 ALVR串流说明:") - print(" - 引擎画面正在串流到VR头盔") - print(" - 支持Quest 2/3/Pico 4等VR设备") - print(" - 确保ALVR客户端在头盔中运行") - print(" - 检查网络连接质量") - - # 运行串流演示 - print(f"\n📡 ALVR串流演示运行中... (15秒)") - start_time = time.time() - while time.time() - start_time < 15: - # 更新串流状态 - current_status = world.getALVRStreamingStatus() - if time.time() - start_time > 5: # 5秒后显示状态更新 - print(f" 当前FPS: {current_status['fps']}, 延迟: {current_status['latency']}ms") - - if hasattr(world, 'taskMgr'): - world.taskMgr.step() - time.sleep(0.016) - - # 停止串流 - print("\n🛑 停止ALVR串流...") - world.stopALVRStreaming() - print("✓ ALVR串流已停止") - - else: - print("⚠ ALVR串流启动失败") - print(" 可能原因:") - print(" - ALVR服务器未运行") - print(" - VR客户端未连接") - print(" - 网络连接问题") - print(" - 防火墙阻止连接") - - else: - print("⚠ ALVR串流初始化失败") - print("💡 这可能是因为:") - print(" 1. ALVR服务器未运行") - print(" 2. SteamVR正在使用独占模式") - print(" 3. 端口冲突") - print("\n🔧 Pico 4有线连接设置建议:") - print(" 1. 确保ALVR服务器正在运行") - print(" 2. 检查任务管理器中是否有alvr_server进程") - print(" 3. 尝试重启ALVR服务器") - print(" 4. 确认防火墙允许ALVR通信") - print(" 5. 检查ALVR服务器端口设置") - print(" 6. 如果SteamVR正在运行,尝试先关闭它") - print("\n💡 直连模式说明:") - print(" 如果您使用的是有线连接(如Pico 4通过USB-C连接)") - print(" 并且SteamVR已经可以正常显示画面,您可能不需要ALVR串流") - print(" SteamVR会直接处理渲染并将画面传输到头显") - - # 关闭系统 - world.shutdownALVR() - world.shutdownVR() - print("\n✓ 所有系统已关闭") - - return True - - -def demo_vr_interaction(): - """演示VR交互功能""" - print("=== VR交互显示演示 ===") - - # 创建世界实例 - world = MyWorld() - - # 初始化VR系统 - print("🥽 初始化VR系统...") - if not world.initializeVR(): - print("✗ VR系统初始化失败") - return False - - # 启动VR输入处理 - print("\n🎮 启动VR输入处理...") - if world.startVRInput(): - print("✓ VR输入处理已启动") - - # 显示控制器射线 - world.showControllerRays(True) - print("✓ 控制器射线已启用") - - # 启用VR交互 - world.setVRInteractionEnabled(True) - world.setVRGestureEnabled(True) - print("✓ VR交互和手势识别已启用") - - # 获取控制器状态 - print(f"\n🎮 控制器状态:") - controllers = world.getAllControllers() - for i, controller in enumerate(controllers): - if controller: - state = world.getControllerState(i) - print(f" 控制器 {i}: 可用") - if state: - print(f" - 连接: {state.get('connected', False)}") - print(f" - 扳机: {state.get('trigger', 0):.2f}") - print(f" - 握把: {state.get('grip', 0):.2f}") - - # 运行交互演示 - print(f"\n🤏 VR交互演示运行中... (10秒)") - print(" - 控制器射线可见") - print(" - 扳机:抓取物体") - print(" - 握把:切换交互模式") - print(" - 触摸板:导航控制") - - start_time = time.time() - while time.time() - start_time < 10: - if hasattr(world, 'taskMgr'): - world.taskMgr.step() - time.sleep(0.016) - - # 停止VR输入 - world.stopVRInput() - print("✓ VR输入处理已停止") - - else: - print("⚠ VR输入处理启动失败") - - # 关闭VR系统 - world.shutdownVR() - print("\n✓ VR系统已关闭") - - return True - - -def demo_comprehensive_vr(): - """综合VR显示演示""" - print("=== 综合VR显示演示 ===") - - # 创建世界实例 - world = MyWorld() - - # 创建丰富的演示场景 - print("🎨 创建VR演示场景...") - try: - from panda3d.core import CardMaker, Vec4, AmbientLight, DirectionalLight - - # 添加光照 - ambient_light = AmbientLight('ambient') - ambient_light.setColor(Vec4(0.3, 0.3, 0.3, 1)) - ambient_light_np = world.render.attachNewNode(ambient_light) - world.render.setLight(ambient_light_np) - - directional_light = DirectionalLight('directional') - directional_light.setColor(Vec4(0.8, 0.8, 0.8, 1)) - directional_light.setDirection(Vec4(-1, -1, -1, 0)) - directional_light_np = world.render.attachNewNode(directional_light) - world.render.setLight(directional_light_np) - - # 创建地面网格 - for i in range(-5, 6): - for j in range(-5, 6): - cm = CardMaker(f"grid_{i}_{j}") - cm.setFrame(-0.4, 0.4, -0.4, 0.4) - grid = world.render.attachNewNode(cm.generate()) - grid.setPos(i * 1.0, j * 1.0, -1) - grid.setHpr(0, -90, 0) - - # 棋盘格颜色 - if (i + j) % 2 == 0: - grid.setColor(0.8, 0.8, 0.8, 1.0) - else: - grid.setColor(0.6, 0.6, 0.6, 1.0) - - # 创建一些彩色立方体 - for i in range(8): - cm = CardMaker(f"cube_{i}") - cm.setFrame(-0.3, 0.3, -0.3, 0.3) - cube = world.render.attachNewNode(cm.generate()) - - import math - angle = i * math.pi * 2 / 8 - radius = 3 - cube.setPos(math.cos(angle) * radius, math.sin(angle) * radius, 0.5) - cube.setHpr(i * 45, 0, 0) - cube.setScale(1, 1, 2) - - # 彩虹颜色 - hue = i / 8.0 - r = abs(math.sin(hue * math.pi * 2)) - g = abs(math.sin((hue + 0.33) * math.pi * 2)) - b = abs(math.sin((hue + 0.66) * math.pi * 2)) - cube.setColor(r, g, b, 1.0) - - print("✓ VR演示场景创建完成") - - except Exception as e: - print(f"场景创建错误: {str(e)}") - - # 完整的VR系统演示 - print("\n🥽 启动完整VR系统...") - - # 1. 初始化VR - if not world.initializeVR(): - print("✗ VR系统初始化失败") - return False - - print("✓ VR显示系统已启动") - - # 2. 启动VR输入 - if world.startVRInput(): - world.showControllerRays(True) - world.setVRInteractionEnabled(True) - print("✓ VR交互系统已启动") - - # 3. 尝试启动ALVR串流 - if world.initializeALVR(): - world.setALVRStreamQuality(2880, 1700, 72, 100) - if world.startALVRStreaming(): - print("✓ ALVR串流已启动") - else: - print("⚠ ALVR串流启动失败") - else: - print("⚠ ALVR初始化失败(正常,如果没有ALVR服务器)") - - # 运行综合演示 - print(f"\n🎬 综合VR显示演示运行中... (20秒)") - print("💡 功能说明:") - print(" - 立体渲染显示") - print(" - 头部追踪(模拟或真实)") - print(" - 控制器可视化") - print(" - ALVR无线串流") - print(" - VR交互功能") - - start_time = time.time() - frame_count = 0 - - while time.time() - start_time < 20: - # 处理引擎更新 - if hasattr(world, 'taskMgr'): - world.taskMgr.step() - - frame_count += 1 - - # 每5秒显示一次状态 - elapsed = time.time() - start_time - if frame_count % 300 == 0: # 约每5秒 - fps = frame_count / elapsed - print(f" 运行状态: {elapsed:.1f}s, FPS: {fps:.1f}") - - # 显示VR状态 - vr_info = world.getVRInfo() - print(f" VR模式: {vr_info['mode']}, 分辨率: {vr_info['render_size']}") - - if world.isALVRStreaming(): - alvr_status = world.getALVRStreamingStatus() - print(f" ALVR: FPS {alvr_status['fps']}, 延迟 {alvr_status['latency']}ms") - - time.sleep(0.016) # ~60 FPS - - print("✓ 综合VR演示完成") - - # 关闭所有系统 - world.stopVRInput() - world.stopALVRStreaming() - world.shutdownALVR() - world.shutdownVR() - print("\n✓ 所有VR系统已关闭") - - return True - - -def print_vr_display_guide(): - """打印VR显示指南""" - print("📋 VR显示功能指南") - print("=" * 50) - - print("\n🎯 功能概述:") - print(" - 立体渲染:为左右眼生成不同视角的画面") - print(" - 头部追踪:根据头部运动调整视角") - print(" - ALVR串流:无线串流到Quest等VR设备") - print(" - 控制器交互:支持VR控制器输入") - - print("\n🔧 使用方法:") - print(" 1. 运行引擎:python3 main.py") - print(" 2. 初始化VR:world.initializeVR()") - print(" 3. 启动ALVR:world.initializeALVR()") - print(" 4. 开始串流:world.startALVRStreaming()") - - print("\n📱 ALVR设置步骤:") - print(" 1. 下载ALVR服务器到PC") - print(" 2. 在Quest中安装ALVR客户端") - print(" 3. 确保PC和Quest在同一WiFi网络") - print(" 4. 启动ALVR服务器") - print(" 5. 在Quest中连接到PC") - print(" 6. 运行引擎VR演示") - - print("\n🎮 VR模式说明:") - print(" 模拟模式:") - print(" - 无需VR硬件") - print(" - 立体视图显示在主窗口") - print(" - 模拟头部和控制器追踪") - print(" - 适合开发和调试") - print(" ") - print(" 真实VR模式:") - print(" - 需要VR头盔和SteamVR") - print(" - 真实的立体渲染和追踪") - print(" - 支持控制器交互") - print(" - 完整的VR体验") - - print("\n🛠 故障排除:") - print(" - VR初始化失败 → 自动切换到模拟模式") - print(" - ALVR连接失败 → 检查网络和服务器") - print(" - 画面不显示 → 检查VR头盔和SteamVR") - print(" - 延迟过高 → 降低串流质量或使用有线连接") - - -def main(): - """主函数""" - print("🎮 VR显示功能演示") - print("=" * 50) - - print("请选择演示类型:") - print("1. 基本VR显示演示") - print("2. ALVR串流演示") - print("3. VR交互演示") - print("4. 综合VR演示") - print("5. VR显示功能指南") - - try: - choice = input("请输入选择 (1-5): ") - - if choice == "1": - success = demo_vr_basic_display() - elif choice == "2": - success = demo_alvr_streaming() - elif choice == "3": - success = demo_vr_interaction() - elif choice == "4": - success = demo_comprehensive_vr() - elif choice == "5": - print_vr_display_guide() - return - else: - print("无效的选择") - return - - print("\n" + "=" * 50) - if success: - print("✓ VR显示演示成功完成") - print("🎉 引擎画面VR显示功能正常工作!") - else: - print("✗ VR显示演示遇到问题") - print("💡 请检查VR系统设置和连接") - - except KeyboardInterrupt: - print("\n用户中断演示") - except Exception as e: - print(f"演示过程中发生错误: {str(e)}") - - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/vr_display_optimizer.py b/vr_display_optimizer.py deleted file mode 100644 index 082b2a72..00000000 --- a/vr_display_optimizer.py +++ /dev/null @@ -1,316 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -VR显示优化工具 -优化VR渲染性能和显示质量 -""" - -from panda3d.core import RenderState, CullFaceAttrib, DepthTestAttrib, AlphaTestAttrib -from core.vr_manager import VRManager - - -class VRDisplayOptimizer: - """VR显示优化器""" - - def __init__(self, world): - self.world = world - self.vr_manager = world.vr_manager if hasattr(world, 'vr_manager') else None - self.optimization_enabled = True - - def optimize_vr_rendering(self): - """优化VR渲染设置""" - if not self.vr_manager or not self.vr_manager.is_vr_enabled(): - print("VR系统未启用,跳过优化") - return False - - print("🚀 开始VR渲染优化...") - - # 1. 优化眼部缓冲区设置 - self._optimize_eye_buffers() - - # 2. 优化渲染状态 - self._optimize_render_states() - - # 3. 设置VR特定的渲染选项 - self._setup_vr_render_options() - - # 4. 优化纹理设置 - self._optimize_textures() - - # 5. 设置LOD和裁剪 - self._setup_lod_and_culling() - - print("✓ VR渲染优化完成") - return True - - def _optimize_eye_buffers(self): - """优化眼部缓冲区""" - try: - if not self.vr_manager.left_eye_buffer or not self.vr_manager.right_eye_buffer: - return - - # 设置缓冲区优化选项 - for buffer in [self.vr_manager.left_eye_buffer, self.vr_manager.right_eye_buffer]: - # 启用多重采样抗锯齿 - buffer.setMultisample(4) - - # 设置深度缓冲精度 - buffer.addDepthRenderTexture() - - # 启用硬件加速 - buffer.setOneShot(False) - buffer.setActive(True) - - print(" ✓ 眼部缓冲区优化完成") - - except Exception as e: - print(f" ⚠ 眼部缓冲区优化失败: {str(e)}") - - def _optimize_render_states(self): - """优化渲染状态""" - try: - # 设置全局渲染状态 - render_state = RenderState.make( - CullFaceAttrib.make(CullFaceAttrib.MCullCounterClockwise), - DepthTestAttrib.make(DepthTestAttrib.MLess), - AlphaTestAttrib.make(AlphaTestAttrib.MGreater, 0.5) - ) - - # 应用到render节点 - self.world.render.setState(render_state) - - print(" ✓ 渲染状态优化完成") - - except Exception as e: - print(f" ⚠ 渲染状态优化失败: {str(e)}") - - def _setup_vr_render_options(self): - """设置VR特定的渲染选项""" - try: - # 启用VR优化 - if hasattr(self.world, 'win') and self.world.win: - # 设置VR特定的窗口属性 - properties = self.world.win.getProperties() - properties.setFrameBufferMode(properties.FBMHardware) - - # 启用垂直同步以减少延迟 - properties.setSync(True) - - # 优化摄像机设置 - if hasattr(self.world, 'camera'): - # 设置适合VR的FOV和近远裁剪面 - lens = self.world.camera.node().getLens() - lens.setNearFar(0.1, 1000.0) # VR推荐的近远裁剪面 - - print(" ✓ VR渲染选项设置完成") - - except Exception as e: - print(f" ⚠ VR渲染选项设置失败: {str(e)}") - - def _optimize_textures(self): - """优化纹理设置""" - try: - from panda3d.core import Texture - - # 设置全局纹理优化 - Texture.setTexturesPower2(Texture.ATPPad) # 填充到2的幂次方 - Texture.setKeepRAMImage(False) # 不保留RAM图像以节省内存 - - # 启用纹理压缩 - Texture.setCompressionMode(Texture.CMDefault) - - print(" ✓ 纹理优化完成") - - except Exception as e: - print(f" ⚠ 纹理优化失败: {str(e)}") - - def _setup_lod_and_culling(self): - """设置LOD和裁剪""" - try: - # 启用视锥裁剪 - from panda3d.core import CullTraverser - self.world.render.setTwoSided(False) - - # 设置距离裁剪 - if hasattr(self.world, 'camera'): - # 根据VR需求调整裁剪距离 - lens = self.world.camera.node().getLens() - lens.setFar(500.0) # 减少远裁剪面以提高性能 - - print(" ✓ LOD和裁剪设置完成") - - except Exception as e: - print(f" ⚠ LOD和裁剪设置失败: {str(e)}") - - def optimize_alvr_streaming(self): - """优化ALVR串流性能""" - if not hasattr(self.world, 'alvr_streamer'): - print("ALVR串流器不可用,跳过优化") - return False - - print("📡 开始ALVR串流优化...") - - alvr = self.world.alvr_streamer - - # 1. 优化串流质量设置 - self._optimize_stream_quality(alvr) - - # 2. 优化网络设置 - self._optimize_network_settings(alvr) - - # 3. 优化编码设置 - self._optimize_encoding_settings(alvr) - - print("✓ ALVR串流优化完成") - return True - - def _optimize_stream_quality(self, alvr): - """优化串流质量""" - try: - # 根据性能自动调整质量 - vr_info = self.vr_manager.get_vr_info() - render_width, render_height = vr_info['render_size'] - - # 针对不同设备优化 - if render_width >= 2880: # Quest 2/3高质量 - alvr.set_stream_quality(2880, 1700, 72, 150) - print(" ✓ 设置为高质量模式 (Quest 2/3)") - elif render_width >= 2160: # 中等质量 - alvr.set_stream_quality(2160, 1200, 60, 100) - print(" ✓ 设置为中等质量模式") - else: # 低质量模式 - alvr.set_stream_quality(1920, 1080, 60, 80) - print(" ✓ 设置为低质量模式") - - except Exception as e: - print(f" ⚠ 串流质量优化失败: {str(e)}") - - def _optimize_network_settings(self, alvr): - """优化网络设置""" - try: - # 这里可以添加网络优化逻辑 - # 例如调整缓冲区大小、连接超时等 - print(" ✓ 网络设置优化完成") - - except Exception as e: - print(f" ⚠ 网络设置优化失败: {str(e)}") - - def _optimize_encoding_settings(self, alvr): - """优化编码设置""" - try: - # 优化H.264编码参数 - alvr.codec = "h264" - - # 根据硬件能力调整编码设置 - print(" ✓ 编码设置优化完成") - - except Exception as e: - print(f" ⚠ 编码设置优化失败: {str(e)}") - - def monitor_performance(self, duration=10): - """监控VR性能""" - print(f"📊 开始VR性能监控 ({duration}秒)...") - - import time - start_time = time.time() - frame_count = 0 - last_report_time = start_time - - while time.time() - start_time < duration: - # 处理一帧 - if hasattr(self.world, 'taskMgr'): - self.world.taskMgr.step() - - frame_count += 1 - current_time = time.time() - - # 每2秒报告一次性能 - if current_time - last_report_time >= 2.0: - elapsed = current_time - last_report_time - fps = (frame_count / (current_time - start_time)) - - print(f" 性能报告: FPS {fps:.1f}") - - # 报告VR系统状态 - if self.vr_manager and self.vr_manager.is_vr_enabled(): - vr_info = self.vr_manager.get_vr_info() - print(f" VR模式: {vr_info['mode']}") - print(f" 渲染尺寸: {vr_info['render_size']}") - - # 报告ALVR状态 - if hasattr(self.world, 'alvr_streamer') and self.world.alvr_streamer.is_streaming(): - status = self.world.alvr_streamer.get_streaming_status() - print(f" ALVR FPS: {status['fps']}, 延迟: {status['latency']}ms") - - last_report_time = current_time - - time.sleep(0.016) # ~60 FPS目标 - - final_fps = frame_count / (time.time() - start_time) - print(f"✓ 性能监控完成,平均FPS: {final_fps:.1f}") - - return final_fps - - def auto_optimize(self): - """自动优化VR显示性能""" - print("🔧 开始自动VR优化...") - - # 1. 优化VR渲染 - if self.optimize_vr_rendering(): - print(" ✓ VR渲染优化完成") - - # 2. 优化ALVR串流 - if hasattr(self.world, 'alvr_streamer'): - if self.optimize_alvr_streaming(): - print(" ✓ ALVR串流优化完成") - - # 3. 监控性能 - print("\n📊 开始性能测试...") - fps = self.monitor_performance(5) - - # 4. 根据性能调整设置 - if fps < 45: # VR最低要求 - print("⚠ 性能不足,降低质量设置...") - self._apply_low_quality_settings() - elif fps > 80: - print("✓ 性能良好,可以提高质量设置") - self._apply_high_quality_settings() - - print("✓ 自动VR优化完成") - - def _apply_low_quality_settings(self): - """应用低质量设置""" - try: - # 降低渲染分辨率 - if self.vr_manager: - self.vr_manager.render_width = int(self.vr_manager.render_width * 0.8) - self.vr_manager.render_height = int(self.vr_manager.render_height * 0.8) - - # 降低ALVR质量 - if hasattr(self.world, 'alvr_streamer'): - self.world.alvr_streamer.set_stream_quality(1920, 1080, 60, 60) - - print(" ✓ 低质量设置已应用") - - except Exception as e: - print(f" ⚠ 低质量设置应用失败: {str(e)}") - - def _apply_high_quality_settings(self): - """应用高质量设置""" - try: - # 提高ALVR质量 - if hasattr(self.world, 'alvr_streamer'): - self.world.alvr_streamer.set_stream_quality(2880, 1700, 90, 200) - - print(" ✓ 高质量设置已应用") - - except Exception as e: - print(f" ⚠ 高质量设置应用失败: {str(e)}") - - -# 使用示例 -def optimize_vr_display(world): - """优化VR显示的便捷函数""" - optimizer = VRDisplayOptimizer(world) - optimizer.auto_optimize() - return optimizer \ No newline at end of file diff --git a/vr_quick_start.py b/vr_quick_start.py deleted file mode 100644 index c6635ac4..00000000 --- a/vr_quick_start.py +++ /dev/null @@ -1,263 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -VR快速启动脚本 -一键启动VR显示功能,包括自动优化和ALVR串流 -""" - -import sys -import os -import time -from main import MyWorld -from vr_display_optimizer import VRDisplayOptimizer - - -def quick_vr_setup(): - """快速VR设置""" - print("🚀 VR快速启动") - print("=" * 40) - - # 创建世界实例 - print("🌍 初始化引擎...") - world = MyWorld() - - # 创建优化器 - optimizer = VRDisplayOptimizer(world) - - # 设置基本场景 - print("🎨 设置VR演示场景...") - setup_demo_scene(world) - - # 初始化VR系统 - print("🥽 启动VR系统...") - vr_success = world.initializeVR() - - if not vr_success: - print("❌ VR初始化失败") - return None, None - - print("✅ VR系统已启动") - - # 优化VR性能 - print("⚡ 优化VR性能...") - optimizer.optimize_vr_rendering() - - # 尝试启动ALVR - print("📡 尝试启动ALVR串流...") - alvr_success = world.initializeALVR() - - if alvr_success: - optimizer.optimize_alvr_streaming() - if world.startALVRStreaming(): - print("✅ ALVR串流已启动") - else: - print("⚠️ ALVR串流启动失败") - else: - print("⚠️ ALVR初始化失败(正常,如果没有服务器)") - - # 启动VR输入 - print("🎮 启动VR交互...") - if world.startVRInput(): - world.showControllerRays(True) - world.setVRInteractionEnabled(True) - print("✅ VR交互已启用") - - # 显示状态信息 - print_vr_status(world) - - return world, optimizer - - -def setup_demo_scene(world): - """设置演示场景""" - try: - from panda3d.core import CardMaker, Vec4, AmbientLight, DirectionalLight - import math - - # 添加光照 - ambient = AmbientLight('ambient') - ambient.setColor(Vec4(0.4, 0.4, 0.4, 1)) - ambient_np = world.render.attachNewNode(ambient) - world.render.setLight(ambient_np) - - directional = DirectionalLight('sun') - directional.setColor(Vec4(1, 1, 0.9, 1)) - directional.setDirection(Vec4(-1, -1, -1, 0)) - sun_np = world.render.attachNewNode(directional) - world.render.setLight(sun_np) - - # 创建地面 - cm = CardMaker("ground") - cm.setFrame(-20, 20, -20, 20) - ground = world.render.attachNewNode(cm.generate()) - ground.setPos(0, 0, -2) - ground.setHpr(0, -90, 0) - ground.setColor(0.3, 0.7, 0.3, 1) - - # 创建彩色立方体环 - for i in range(12): - cm_cube = CardMaker(f"cube_{i}") - cm_cube.setFrame(-0.5, 0.5, -0.5, 0.5) - cube = world.render.attachNewNode(cm_cube.generate()) - - angle = i * math.pi * 2 / 12 - radius = 5 - height = math.sin(i * 0.5) * 2 - - cube.setPos(math.cos(angle) * radius, math.sin(angle) * radius, height) - cube.setHpr(i * 30, 0, 0) - cube.setScale(1, 1, 2 + height * 0.5) - - # HSV到RGB转换 - h = i / 12.0 * 360 - s = 0.8 - v = 1.0 - r, g, b = hsv_to_rgb(h, s, v) - cube.setColor(r, g, b, 1) - - print(" ✅ 演示场景创建完成") - - except Exception as e: - print(f" ⚠️ 场景创建失败: {str(e)}") - - -def hsv_to_rgb(h, s, v): - """HSV到RGB颜色转换""" - import math - h = h / 60.0 - c = v * s - x = c * (1 - abs((h % 2) - 1)) - m = v - c - - if 0 <= h < 1: - r, g, b = c, x, 0 - elif 1 <= h < 2: - r, g, b = x, c, 0 - elif 2 <= h < 3: - r, g, b = 0, c, x - elif 3 <= h < 4: - r, g, b = 0, x, c - elif 4 <= h < 5: - r, g, b = x, 0, c - else: - r, g, b = c, 0, x - - return r + m, g + m, b + m - - -def print_vr_status(world): - """显示VR状态信息""" - print("\n📊 VR系统状态") - print("-" * 30) - - if world.isVREnabled(): - vr_info = world.getVRInfo() - print(f"🥽 VR模式: {vr_info['mode']}") - print(f"📐 渲染尺寸: {vr_info['render_size']}") - print(f"🎮 模拟模式: {vr_info['simulation_mode']}") - - if world.isALVRConnected(): - status = world.getALVRStreamingStatus() - print(f"📡 ALVR状态: {'串流中' if world.isALVRStreaming() else '已连接'}") - print(f"🎥 分辨率: {status['resolution']}") - print(f"⚡ FPS: {status['fps']}") - else: - print("📡 ALVR状态: 未连接") - else: - print("🥽 VR状态: 未启用") - - -def run_vr_experience(world, optimizer, duration=30): - """运行VR体验""" - print(f"\n🎮 VR体验开始 ({duration}秒)") - print("💡 控制说明:") - print(" - 观察窗口中的立体视图") - print(" - VR头盔中可看到完整场景") - print(" - 控制器射线用于交互") - print(" - 按Ctrl+C退出") - - start_time = time.time() - frame_count = 0 - - try: - while time.time() - start_time < duration: - # 处理引擎更新 - if hasattr(world, 'taskMgr'): - world.taskMgr.step() - - frame_count += 1 - - # 每5秒显示状态 - elapsed = time.time() - start_time - if frame_count % 300 == 0: # ~5秒 - fps = frame_count / elapsed - print(f" 📈 运行状态: {elapsed:.1f}s, FPS: {fps:.1f}") - - if world.isALVRStreaming(): - alvr_status = world.getALVRStreamingStatus() - print(f" 📡 ALVR: {alvr_status['fps']} FPS, {alvr_status['latency']}ms延迟") - - time.sleep(0.016) # ~60 FPS - - except KeyboardInterrupt: - print("\n⏹️ 用户中断VR体验") - - final_fps = frame_count / (time.time() - start_time) - print(f"✅ VR体验完成,平均FPS: {final_fps:.1f}") - - -def cleanup_vr(world): - """清理VR资源""" - print("\n🧹 清理VR资源...") - - try: - if hasattr(world, 'stopVRInput'): - world.stopVRInput() - - if hasattr(world, 'stopALVRStreaming'): - world.stopALVRStreaming() - - if hasattr(world, 'shutdownALVR'): - world.shutdownALVR() - - if hasattr(world, 'shutdownVR'): - world.shutdownVR() - - print("✅ VR资源清理完成") - - except Exception as e: - print(f"⚠️ VR清理失败: {str(e)}") - - -def main(): - """主函数""" - print("🎮 VR引擎快速启动") - print("=" * 50) - - # 快速设置VR - world, optimizer = quick_vr_setup() - - if not world: - print("❌ VR启动失败") - return - - try: - # 运行VR体验 - run_vr_experience(world, optimizer, 60) # 60秒演示 - - except Exception as e: - print(f"❌ VR运行错误: {str(e)}") - - finally: - # 清理资源 - cleanup_vr(world) - - print("\n🎉 VR体验结束") - print("💡 要在Quest中体验:") - print(" 1. 安装ALVR服务器和客户端") - print(" 2. 连接同一WiFi网络") - print(" 3. 重新运行此脚本") - - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/vr_streaming_test.py b/vr_streaming_test.py deleted file mode 100644 index da9f5cf9..00000000 --- a/vr_streaming_test.py +++ /dev/null @@ -1,264 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -VR串流测试脚本 -专门用于测试和验证ALVR串流功能 -""" - -import sys -import os -import time -from main import MyWorld - - -def test_vr_streaming(): - """测试VR串流功能""" - print("=== VR串流功能测试 ===") - - # 创建世界实例 - world = MyWorld() - - # 初始化VR系统 - print("🥽 初始化VR系统...") - if not world.initializeVR(): - print("✗ VR系统初始化失败") - return False - - print("✓ VR系统初始化成功") - - # 获取VR信息 - vr_info = world.getVRInfo() - print(f"\n📊 VR系统信息:") - print(f" - 模式: {vr_info['mode']}") - print(f" - 启用状态: {vr_info['enabled']}") - print(f" - 模拟模式: {vr_info['simulation_mode']}") - print(f" - 渲染尺寸: {vr_info['render_size']}") - - # 检查是否为直连模式(SteamVR已运行) - if world.alvr_streamer._is_steamvr_active(): - print("\n💡 检测到SteamVR正在运行,将使用直连模式") - print(" 在直连模式下,画面将直接通过SteamVR传输到头显") - print(" 无需通过ALVR服务器进行额外的串流处理") - - # 初始化ALVR串流 - print("\n📡 初始化ALVR串流...") - print("💡 检测到您已连接VR设备,正在尝试连接ALVR服务器...") - alvr_success = world.initializeALVR() - - if alvr_success: - print("✓ ALVR串流初始化成功") - - # 设置串流质量 - print("\n🎥 配置串流质量...") - quality_set = world.setALVRStreamQuality(1920, 1080, 60, 50) - if quality_set: - print(" ✓ 串流质量设置成功: 1920x1080@60fps, 50Mbps") - - # 获取串流状态 - status = world.getALVRStreamingStatus() - print(f"\n📊 ALVR串流初始状态:") - print(f" - 连接状态: {status['connected']}") - print(f" - 串流状态: {status['streaming']}") - print(f" - 分辨率: {status['resolution']}") - print(f" - 帧率: {status['fps']}") - print(f" - 码率: {status['bitrate']}Mbps") - - # 开始串流 - print(f"\n🚀 开始ALVR串流...") - if world.startALVRStreaming(): - print("✓ ALVR串流已开始") - - # 运行串流测试 - print(f"\n📡 ALVR串流测试运行中... (10秒)") - start_time = time.time() - frame_count = 0 - - while time.time() - start_time < 10: - # 更新串流状态 - current_status = world.getALVRStreamingStatus() - - # 每2秒显示一次状态更新 - if frame_count % 120 == 0: # 约每2秒 - elapsed = time.time() - start_time - print(f" [{elapsed:.1f}s] FPS: {current_status['fps']}, 延迟: {current_status['latency']}ms") - - if hasattr(world, 'taskMgr'): - world.taskMgr.step() - frame_count += 1 - time.sleep(0.016) - - # 停止串流 - print("\n🛑 停止ALVR串流...") - world.stopALVRStreaming() - print("✓ ALVR串流已停止") - - else: - print("⚠ ALVR串流启动失败") - print(" 可能原因:") - print(" - ALVR服务器未运行") - print(" - VR客户端未连接") - print(" - 网络连接问题") - - else: - print("⚠ ALVR串流初始化失败") - print("💡 根据您提供的信息,您已经可以通过SteamVR看到画面,但串流测试无法连接到ALVR服务器") - print("\n💡 直连模式说明:") - print(" 如果您使用的是有线连接(如Pico 4通过USB-C连接)") - print(" 并且SteamVR已经可以正常显示画面,您实际上已经在使用直连模式") - print(" SteamVR会直接处理渲染并将画面传输到头显,无需额外的串流步骤") - print("\n🔧 建议操作:") - print(" 1. 确认SteamVR是否正常工作并显示画面") - print(" 2. 如果正常工作,您无需进行额外的ALVR串流设置") - print(" 3. 如果需要使用ALVR的额外功能,可以尝试:") - print(" - 关闭SteamVR") - print(" - 确保ALVR服务器正在运行") - print(" - 重新运行测试") - - # 提供替代方案 - print("\n🔄 替代方案:") - print(" 您可以尝试运行:") - print(" python3 vr_display_demo.py") - print(" 并选择'2. ALVR串流演示'来测试串流功能") - - # 关闭系统 - world.shutdownALVR() - world.shutdownVR() - print("\n✓ 所有系统已关闭") - - return True - - -def test_vr_streaming_detailed(): - """详细测试VR串流功能""" - print("=== VR串流详细功能测试 ===") - - # 创建世界实例 - world = MyWorld() - - # 初始化VR系统 - print("🥽 初始化VR系统...") - if not world.initializeVR(): - print("✗ VR系统初始化失败") - return False - - print("✓ VR系统初始化成功") - - # 检查VR纹理是否正确创建 - print("\n🔍 检查VR纹理...") - if hasattr(world.vr_manager, 'left_eye_texture') and world.vr_manager.left_eye_texture: - print(" ✓ 左眼纹理已创建") - print(f" 纹理尺寸: {world.vr_manager.left_eye_texture.getXSize()}x{world.vr_manager.left_eye_texture.getYSize()}") - else: - print(" ✗ 左眼纹理未创建") - - if hasattr(world.vr_manager, 'right_eye_texture') and world.vr_manager.right_eye_texture: - print(" ✓ 右眼纹理已创建") - print(f" 纹理尺寸: {world.vr_manager.right_eye_texture.getXSize()}x{world.vr_manager.right_eye_texture.getYSize()}") - else: - print(" ✗ 右眼纹理未创建") - - # 初始化ALVR串流 - print("\n📡 初始化ALVR串流...") - alvr_success = world.initializeALVR() - - if alvr_success: - print("✓ ALVR串流初始化成功") - - # 测试多种串流质量设置 - print("\n🎥 测试多种串流质量设置:") - quality_settings = [ - (1280, 720, 60, 25), - (1920, 1080, 60, 50), - (1920, 1080, 30, 25) - ] - - for width, height, fps, bitrate in quality_settings: - success = world.setALVRStreamQuality(width, height, fps, bitrate) - if success: - status = world.getALVRStreamingStatus() - print(f" ✓ 设置 {width}x{height}@{fps}fps, {bitrate}Mbps: 成功") - print(f" 实际分辨率: {status['resolution']}") - else: - print(f" ✗ 设置 {width}x{height}@{fps}fps, {bitrate}Mbps: 失败") - - # 测试串流控制 - print("\n🎮 测试串流控制:") - - # 开始串流 - if world.startALVRStreaming(): - print(" ✓ 串流开始成功") - - # 检查串流状态 - status = world.getALVRStreamingStatus() - print(f" 串流状态: {status['streaming']}") - print(f" 连接状态: {status['connected']}") - - # 等待2秒 - time.sleep(2) - - # 再次检查状态 - status = world.getALVRStreamingStatus() - print(f" 2秒后状态 - FPS: {status['fps']}, 延迟: {status['latency']}ms") - - # 停止串流 - world.stopALVRStreaming() - status = world.getALVRStreamingStatus() - print(f" ✓ 串流停止成功") - print(f" 串流状态: {status['streaming']}") - - else: - print(" ✗ 串流开始失败") - - else: - print("⚠ ALVR串流初始化失败") - print("\n🔧 Pico 4连接故障排除:") - print(" 1. 确认ALVR服务器正在运行") - print(" 2. 检查USB连接是否正常") - print(" 3. 确认Pico 4上已安装并启动ALVR客户端") - print(" 4. 检查防火墙设置") - print(" 5. 确认SteamVR可以识别到头显") - - # 关闭系统 - world.shutdownALVR() - world.shutdownVR() - print("\n✓ 所有系统已关闭") - - return True - - -def main(): - """主函数""" - print("🎮 VR串流测试脚本") - print("="*50) - - print("\n请选择测试类型:") - print("1. 基本串流测试") - print("2. 详细串流测试") - - try: - choice = input("请输入选择 (1-2): ") - - if choice == "1": - success = test_vr_streaming() - elif choice == "2": - success = test_vr_streaming_detailed() - else: - print("无效的选择") - return - - print("\n" + "="*50) - if success: - print("✓ 串流测试成功完成") - else: - print("✗ 串流测试遇到问题") - - except KeyboardInterrupt: - print("\n用户中断测试") - except Exception as e: - print(f"测试过程中发生错误: {str(e)}") - import traceback - traceback.print_exc() - - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/vr_test.py b/vr_test.py deleted file mode 100644 index b35a6ee3..00000000 --- a/vr_test.py +++ /dev/null @@ -1,468 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- -""" -VR功能测试脚本 -测试VR系统的各种功能,包括模拟模式和真实VR模式 -""" - -import sys -import os -from main import MyWorld - -def test_basic_vr_functionality(): - """测试基本VR功能""" - print("=== VR基本功能测试 ===") - - # 创建世界实例 - world = MyWorld() - - # 检查VR管理器是否正确初始化 - print("✓ VR管理器已创建") - - # 检查OpenVR库是否可用 - try: - import openvr - print("✓ OpenVR库已安装") - except ImportError: - print("⚠ OpenVR库未安装,将使用模拟模式") - - # 测试VR初始化(会自动回退到模拟模式) - print("\n正在测试VR初始化...") - vr_success = world.initializeVR() - - if vr_success: - print("✓ VR系统初始化成功") - - # 获取VR信息 - vr_info = world.getVRInfo() - print(f"\n📊 VR系统信息:") - print(f" - 模式: {vr_info['mode']}") - print(f" - 启用状态: {vr_info['enabled']}") - print(f" - 模拟模式: {vr_info['simulation_mode']}") - print(f" - 渲染尺寸: {vr_info['render_size']}") - print(f" - 控制器数量: {vr_info.get('controllers', 0)}") - - # 测试控制器输入 - print("\n🎮 测试控制器输入:") - for i in range(2): - controller_input = world.vr_manager.get_controller_input(i) - if controller_input and controller_input.get('connected'): - print(f" 控制器 {i}: 已连接") - print(f" - 扳机: {controller_input['trigger']:.2f}") - print(f" - 握把: {controller_input['grip']:.2f}") - print(f" - 触摸板: ({controller_input['touchpad']['x']:.2f}, {controller_input['touchpad']['y']:.2f})") - else: - print(f" 控制器 {i}: 未连接") - - # 测试VR状态 - print("\n📊 VR系统状态:") - status = world.getVRStatus() - print(f" - VR启用: {status['vr_enabled']}") - print(f" - 控制器总数: {len(status['controllers'])}") - - else: - print("✗ VR系统初始化失败") - return False - - # 关闭VR系统 - world.shutdownVR() - print("\n✓ VR系统已关闭") - - return True - -def test_simulation_mode(): - """测试VR模拟模式""" - print("=== VR模拟模式测试 ===") - - # 创建世界实例 - world = MyWorld() - - # 强制启用模拟模式 - print("强制启用VR模拟模式...") - vr_success = world.vr_manager.enable_simulation_mode() - - if vr_success: - print("✓ VR模拟模式启用成功") - - # 获取模拟数据 - sim_data = world.vr_manager.get_simulation_data() - if sim_data: - print(f"\n🎮 模拟数据:") - print(f" - 头部位置: {sim_data['head_pose']['position']}") - print(f" - 控制器 0 位置: {sim_data['controller_poses'][0]['position']}") - print(f" - 控制器 1 位置: {sim_data['controller_poses'][1]['position']}") - print(f" - 渲染尺寸: {sim_data['render_size']}") - - # 测试更新模拟数据 - print(f"\n🔧 测试模拟数据更新:") - new_head_pos = [0.1, 0.1, 1.7] - success = world.vr_manager.update_simulation_data('head_pose', { - 'position': new_head_pos, - 'rotation': [0, 0, 0, 1] - }) - if success: - print(f" ✓ 头部位置更新为: {new_head_pos}") - - # 测试控制器输入(模拟) - print(f"\n🎮 模拟控制器输入测试:") - for i in range(2): - controller_input = world.vr_manager.get_controller_input(i) - if controller_input: - print(f" 控制器 {i}:") - print(f" - 连接状态: {controller_input['connected']}") - print(f" - 扳机: {controller_input['trigger']}") - print(f" - 握把: {controller_input['grip']}") - print(f" - 菜单键: {controller_input['menu']}") - - else: - print("✗ VR模拟模式启用失败") - return False - - # 关闭VR系统 - world.vr_manager.shutdown_vr() - print("\n✓ VR模拟模式已关闭") - - return True - -def test_alvr_streaming(): - """测试ALVR串流功能""" - print("=== ALVR串流测试 ===") - - # 创建世界实例 - world = MyWorld() - - # 初始化VR系统 - print("初始化VR系统...") - if not world.initializeVR(): - print("✗ VR系统初始化失败") - return False - - # 测试ALVR初始化 - print("\n正在测试ALVR初始化...") - alvr_success = world.initializeALVR() - - if alvr_success: - print("✓ ALVR初始化成功") - - # 测试串流状态 - print("\n📊 ALVR串流状态:") - status = world.getALVRStreamingStatus() - print(f" - 连接状态: {world.isALVRConnected()}") - print(f" - 串流状态: {world.isALVRStreaming()}") - - # 测试串流质量设置 - print("\n🎥 测试串流质量设置:") - quality_success = world.setALVRStreamQuality(1920, 1080, 60, 100) - if quality_success: - print(" ✓ 串流质量设置成功: 1920x1080@60fps, 100Mbps") - else: - print(" ⚠ 串流质量设置失败") - - # 测试触觉反馈 - print("\n🔔 测试触觉反馈:") - for controller_id in range(2): - feedback_success = world.sendHapticFeedback(controller_id, 0.1, 0.5) - if feedback_success: - print(f" ✓ 控制器 {controller_id} 触觉反馈发送成功") - else: - print(f" ⚠ 控制器 {controller_id} 触觉反馈发送失败") - - # 测试串流控制 - print("\n🎮 测试串流控制:") - start_success = world.startALVRStreaming() - if start_success: - print(" ✓ 串流开始成功") - else: - print(" ⚠ 串流开始失败") - - # 等待一下然后停止 - import time - time.sleep(1) - - stop_success = world.stopALVRStreaming() - if stop_success: - print(" ✓ 串流停止成功") - else: - print(" ⚠ 串流停止失败") - - else: - print("⚠ ALVR初始化失败(这在没有ALVR服务器时是正常的)") - print(" 提示: 要使用ALVR功能,请:") - print(" 1. 下载并安装ALVR服务器") - print(" 2. 启动ALVR服务器") - print(" 3. 在Quest头盔上安装并启动ALVR客户端") - - # 关闭系统 - world.shutdownALVR() - world.shutdownVR() - print("\n✓ ALVR和VR系统已关闭") - - return True - -def test_vr_gui_control_panel(): - """测试VR GUI控制面板""" - print("=== VR GUI控制面板测试 ===") - - try: - from ui.vr_control_panel import VRControlPanel - from PyQt5.QtWidgets import QApplication - - # 创建Qt应用程序 - app = QApplication.instance() - if app is None: - app = QApplication(sys.argv) - - # 创建世界实例 - world = MyWorld() - - # 创建VR控制面板 - print("创建VR控制面板...") - control_panel = VRControlPanel(world) - - # 测试面板功能 - print("✓ VR控制面板创建成功") - print(" - 面板标题:", control_panel.windowTitle()) - print(" - 面板大小:", control_panel.size().width(), "x", control_panel.size().height()) - - # 显示面板(非阻塞) - control_panel.show() - print("✓ VR控制面板已显示") - - print("\n💡 GUI控制面板功能:") - print(" - VR系统开关控制") - print(" - ALVR串流控制") - print(" - 实时状态监控") - print(" - 质量设置调整") - print(" - 控制器状态显示") - print(" - 性能监控") - - # 短暂显示然后关闭 - import time - time.sleep(2) - control_panel.close() - - print("\n✓ VR控制面板测试完成") - - except Exception as e: - print(f"✗ VR控制面板测试失败: {str(e)}") - return False - - return True - -def test_vr_interaction(): - """测试VR交互功能""" - print("=== VR交互功能测试 ===") - - # 创建世界实例 - world = MyWorld() - - # 初始化VR系统 - print("初始化VR系统...") - if not world.initializeVR(): - print("✗ VR系统初始化失败") - return False - - # 测试VR输入处理 - print("\n🎮 测试VR输入处理:") - input_success = world.startVRInput() - if input_success: - print(" ✓ VR输入处理启动成功") - - # 测试控制器射线显示 - print("\n🌟 测试控制器射线:") - ray_success = world.showControllerRays(True) - if ray_success: - print(" ✓ 控制器射线显示启用") - else: - print(" ⚠ 控制器射线显示失败") - - # 测试手势识别 - print("\n✋ 测试手势识别:") - gesture_success = world.setVRGestureEnabled(True) - if gesture_success: - print(" ✓ VR手势识别启用") - else: - print(" ⚠ VR手势识别启用失败") - - # 测试VR交互 - print("\n🤏 测试VR交互:") - interaction_success = world.setVRInteractionEnabled(True) - if interaction_success: - print(" ✓ VR交互功能启用") - else: - print(" ⚠ VR交互功能启用失败") - - # 获取控制器状态 - print("\n🎮 控制器状态:") - controllers = world.getAllControllers() - for i, controller in enumerate(controllers): - if controller: - print(f" 控制器 {i}: 可用") - state = world.getControllerState(i) - if state: - print(f" - 位置: {state.get('position', 'N/A')}") - print(f" - 旋转: {state.get('rotation', 'N/A')}") - print(f" - 按钮: {state.get('buttons', 'N/A')}") - else: - print(f" 控制器 {i}: 不可用") - - # 停止VR输入 - world.stopVRInput() - print("\n✓ VR输入处理已停止") - - else: - print(" ⚠ VR输入处理启动失败") - - # 关闭VR系统 - world.shutdownVR() - print("\n✓ VR系统已关闭") - - return True - -def run_all_tests(): - """运行所有测试""" - print("=== 运行所有VR测试 ===") - - tests = [ - ("基本VR功能", test_basic_vr_functionality), - ("VR模拟模式", test_simulation_mode), - ("ALVR串流", test_alvr_streaming), - ("VR GUI控制面板", test_vr_gui_control_panel), - ("VR交互功能", test_vr_interaction) - ] - - results = [] - for test_name, test_func in tests: - print(f"\n{'='*50}") - print(f"正在运行: {test_name}") - print('='*50) - - try: - success = test_func() - results.append((test_name, success)) - if success: - print(f"✓ {test_name} 测试通过") - else: - print(f"✗ {test_name} 测试失败") - except Exception as e: - print(f"✗ {test_name} 测试出错: {str(e)}") - results.append((test_name, False)) - - # 打印总结 - print(f"\n{'='*50}") - print("测试总结") - print('='*50) - - passed = sum(1 for _, success in results if success) - total = len(results) - - for test_name, success in results: - status = "✓ 通过" if success else "✗ 失败" - print(f"{test_name}: {status}") - - print(f"\n总计: {passed}/{total} 个测试通过") - - if passed == total: - print("🎉 所有测试都通过了!") - else: - print("⚠ 部分测试失败,请检查上述输出") - - return passed == total - -def print_vr_requirements(): - """打印VR系统要求""" - print("📋 VR系统要求说明") - print("="*50) - print("🔧 软件要求:") - print(" - Python 3.8+") - print(" - Panda3D") - print(" - PyQt5") - print(" - OpenVR库 (pip install openvr)") - print(" - 其他依赖见 requirements/vr-requirements.txt") - - print("\n🎮 硬件要求(完整VR模式):") - print(" - 支持VR的显卡 (GTX 1060/RX 580 或更好)") - print(" - VR头盔 (Quest 2/3, Valve Index, HTC Vive等)") - print(" - 足够的USB端口或无线连接") - - print("\n🌐 连接方式:") - print(" 有线连接:") - print(" - Valve Index: DisplayPort + USB 3.0") - print(" - HTC Vive: HDMI + USB 3.0") - print(" - Quest 2/3: USB-C (Quest Link)") - print(" 无线连接:") - print(" - Quest 2/3: 通过ALVR串流") - print(" - 需要5GHz Wi-Fi网络") - print(" - 需要ALVR服务器运行") - - print("\n🔄 启动顺序(完整VR模式):") - print(" 1. 确保VR头盔已连接并识别") - print(" 2. 启动SteamVR") - print(" 3. (可选) 启动ALVR服务器(Quest无线)") - print(" 4. 运行VR测试脚本") - - print("\n🎮 模拟模式说明:") - print(" - 无需VR硬件") - print(" - 模拟头盔和控制器追踪") - print(" - 立体渲染到窗口") - print(" - 适用于开发和调试") - - print("\n💡 故障排除:") - print(" - 如果OpenVR初始化失败,系统会自动切换到模拟模式") - print(" - 检查SteamVR是否正在运行") - print(" - 确认VR头盔被系统识别") - print(" - 检查USB/DisplayPort连接") - print(" - 重启SteamVR和头盔") - -def main(): - """主函数""" - print("🎮 VR功能测试脚本") - print("="*50) - - # 显示要求说明 - print_vr_requirements() - - print("\n请选择测试类型:") - print("1. 基本VR功能测试") - print("2. VR模拟模式测试") - print("3. ALVR串流测试") - print("4. VR GUI控制面板") - print("5. VR交互功能测试") - print("6. 运行所有测试") - print("7. 查看VR系统要求") - - try: - choice = input("请输入选择 (1-7): ") - - if choice == "1": - success = test_basic_vr_functionality() - elif choice == "2": - success = test_simulation_mode() - elif choice == "3": - success = test_alvr_streaming() - elif choice == "4": - success = test_vr_gui_control_panel() - elif choice == "5": - success = test_vr_interaction() - elif choice == "6": - success = run_all_tests() - elif choice == "7": - print_vr_requirements() - return - else: - print("无效的选择") - return - - print("\n" + "="*50) - if success: - print("✓ 测试成功") - else: - print("✗ 测试失败") - - except KeyboardInterrupt: - print("\n用户中断测试") - except Exception as e: - print(f"测试过程中发生错误: {str(e)}") - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/参考文档.md b/参考文档.md new file mode 100644 index 00000000..6563da06 --- /dev/null +++ b/参考文档.md @@ -0,0 +1 @@ +API:https://docs.panda3d.org/1.10/python/reference/index