diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..95bf679f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "Lingma.aI Chat.webToolsInAsk/AgentMode": "Auto-execute" +} \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..1489fa62 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,102 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Overview + +This is a Python-based 3D engine editor built with Panda3D and PyQt5. It integrates a comprehensive render pipeline (RenderPipelineFile) and provides VR support through ALVR streaming. The application follows a modular architecture with separate systems for world management, GUI, VR, scripting, and project management. + +## Development Commands + +This project is a Python application without npm/package.json build commands. Key development commands: + +```bash +# Run the main application +python3 main.py + +# Run specific demo/test files +python3 demo/basic_gui_demo.py +python3 demo/script_system_demo.py +python3 vr_test.py + +# Install dependencies +pip install -r requirements/requirements.txt +``` + +## Core Architecture + +### Main Application Structure +- **main.py**: Entry point - initializes MyWorld class which extends CoreWorld +- **MyWorld class**: Central coordinator that initializes all major systems: + - SelectionSystem, EventHandler, ToolManager, ScriptManager + - GUIManager, SceneManager, ProjectManager, PropertyPanelManager + - InterfaceManager, VRManager, VRInputHandler, ALVRStreamer + +### Key Modules + +1. **core/**: Core engine functionality + - `world.py`: CoreWorld base class extending Panda3DWorld + - `selection.py`: Object selection and transformation system + - `event_handler.py`: Event processing and input handling + - `tool_manager.py`: Tool system management + - `script_system.py`: Dynamic script loading and execution + - `vr_manager.py`, `vr_input_handler.py`, `alvr_streamer.py`: VR support + +2. **ui/**: User interface components + - `main_window.py`: MainWindow class using PyQt5 + - `widgets.py`: Custom PyQt5/Panda3D integration widgets + - `property_panel.py`: Object property editing interface + - `interface_manager.py`: UI state management + +3. **QPanda3D/**: Custom Panda3D-Qt integration + - `QPanda3DWidget.py`: Core Qt widget for embedding Panda3D + - `Panda3DWorld.py`: Base world class + - Translation modules for Qt/Panda3D event mapping + +4. **RenderPipelineFile/**: Advanced rendering pipeline + - Complete PBR rendering system with plugins + - Shader management, lighting, post-processing effects + - Material system and environment probes + +5. **scripts/**: User scripts directory + - Contains example scripts like BouncerScript.py, RotatorScript.py + - Scripts are dynamically loaded by the script system + +### VR Integration +- ALVR streaming support for wireless VR +- VR input handling with hand tracking +- VR-specific UI and interaction systems + +### Demo and Testing +- **demo/**: Extensive collection of test files and examples + - GUI testing examples (basic_gui_demo.py, etc.) + - VR testing and integration demos + - Picking and selection system tests + - Script system demonstrations + +## Key Dependencies +- **Panda3D 1.10.15**: Core 3D engine +- **PyQt5**: GUI framework +- **QPanda3D 0.2.10**: Qt-Panda3D integration library +- Various supporting libraries for VR, rendering, and utilities + +## Development Notes + +### Camera Controls +- Default camera speed: 20.0 units +- Default rotation speed: 10.0 degrees +- Mouse right-click for camera rotation + +### Coordinate System +The engine uses Panda3D's coordinate system with specific camera and world setups defined in CoreWorld. + +### Script System +Scripts in the `scripts/` directory are automatically discovered and can be loaded dynamically. The script system supports hot-reloading and provides a management interface. + +### Render Pipeline Integration +The project integrates a sophisticated render pipeline with: +- PBR materials and lighting +- Post-processing effects (bloom, SSAO, motion blur, FXAA, etc.) +- Environment mapping and IBL +- Shadow mapping (PSSM) +- Day/night cycle simulation \ No newline at end of file diff --git a/RenderPipelineFile/rpcore/render_pipeline.py b/RenderPipelineFile/rpcore/render_pipeline.py index f5fa7e34..46728df3 100644 --- a/RenderPipelineFile/rpcore/render_pipeline.py +++ b/RenderPipelineFile/rpcore/render_pipeline.py @@ -38,7 +38,8 @@ from direct.showbase.ShowBase import ShowBase from direct.stdpy.file import isfile from rplibs.yaml import load_yaml_file_flat -from six.moves import range # pylint: disable=import-error +from rplibs.six import moves # pylint: disable=import-error +range = moves.range from rpcore.globals import Globals from rpcore.effect import Effect diff --git a/RenderPipelineFile/toolkit/day_time_editor/curve_widget.py b/RenderPipelineFile/toolkit/day_time_editor/curve_widget.py index 249030af..6352c96e 100644 --- a/RenderPipelineFile/toolkit/day_time_editor/curve_widget.py +++ b/RenderPipelineFile/toolkit/day_time_editor/curve_widget.py @@ -29,11 +29,12 @@ from __future__ import print_function # from PyQt5 import Qt # from PyQt5.QtGui import QPainter, QColor, QPen # from PyQt5.QtWidgets import QWidget -from six.moves import range # pylint: disable=import-error +from rplibs.six import moves # pylint: disable=import-error +range = moves.range import math -from RenderPipelineFile.rplibs.pyqt_imports import * # noqa +from rplibs.pyqt_imports import * # noqa class CurveWidget(QWidget): diff --git a/RenderPipelineFile/toolkit/day_time_editor/main.py b/RenderPipelineFile/toolkit/day_time_editor/main.py index 3031b478..ff4d71ea 100644 --- a/RenderPipelineFile/toolkit/day_time_editor/main.py +++ b/RenderPipelineFile/toolkit/day_time_editor/main.py @@ -40,8 +40,8 @@ os.chdir(os.path.join(os.path.dirname(os.path.realpath(__file__)))) # Add the render pipeline to the path sys.path.insert(0, "../../") -from RenderPipelineFile.rplibs.six import iteritems # noqa -from RenderPipelineFile.rplibs.pyqt_imports import * # noqa +from rplibs.six import iteritems # noqa +from rplibs.pyqt_imports import * # noqa from curve_widget import CurveWidget # noqa diff --git a/__pycache__/main.cpython-310.pyc b/__pycache__/main.cpython-310.pyc index c8e668c0..4d7ef861 100644 Binary files a/__pycache__/main.cpython-310.pyc and b/__pycache__/main.cpython-310.pyc differ diff --git a/__pycache__/vr_display_optimizer.cpython-310.pyc b/__pycache__/vr_display_optimizer.cpython-310.pyc new file mode 100644 index 00000000..220c9d60 Binary files /dev/null and b/__pycache__/vr_display_optimizer.cpython-310.pyc differ diff --git a/core/__pycache__/alvr_streamer.cpython-310.pyc b/core/__pycache__/alvr_streamer.cpython-310.pyc index 4ef3703e..d1b34ea0 100644 Binary files a/core/__pycache__/alvr_streamer.cpython-310.pyc and b/core/__pycache__/alvr_streamer.cpython-310.pyc differ diff --git a/core/__pycache__/simple_vr.cpython-310.pyc b/core/__pycache__/simple_vr.cpython-310.pyc new file mode 100644 index 00000000..3400df32 Binary files /dev/null and b/core/__pycache__/simple_vr.cpython-310.pyc differ diff --git a/core/__pycache__/vr_manager.cpython-310.pyc b/core/__pycache__/vr_manager.cpython-310.pyc index 25a3ceba..d1fccd6f 100644 Binary files a/core/__pycache__/vr_manager.cpython-310.pyc and b/core/__pycache__/vr_manager.cpython-310.pyc differ diff --git a/core/__pycache__/world.cpython-310.pyc b/core/__pycache__/world.cpython-310.pyc index 485744ac..f7fd572e 100644 Binary files a/core/__pycache__/world.cpython-310.pyc and b/core/__pycache__/world.cpython-310.pyc differ diff --git a/core/alvr_streamer.py b/core/alvr_streamer.py index 2783c5bd..c2aac58a 100644 --- a/core/alvr_streamer.py +++ b/core/alvr_streamer.py @@ -29,6 +29,10 @@ class ALVRStreamer(DirectObject): 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 @@ -87,9 +91,24 @@ class ALVRStreamer(DirectObject): """检查ALVR服务器是否运行""" try: # 检查进程 + alvr_processes = [] for proc in psutil.process_iter(['pid', 'name', 'cmdline']): - if 'alvr' in proc.info['name'].lower(): - return True + 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) @@ -97,7 +116,23 @@ class ALVRStreamer(DirectObject): result = sock.connect_ex((self.alvr_server_ip, self.alvr_server_port)) sock.close() - return result == 0 + 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)}") @@ -106,24 +141,14 @@ class ALVRStreamer(DirectObject): def _start_alvr_server(self): """启动ALVR服务器""" try: + # 获取当前操作系统 + import platform + system = platform.system().lower() + # 尝试启动ALVR服务器 - # 这里需要根据实际的ALVR安装路径调整 - alvr_paths = [ - "/usr/local/bin/alvr_server", - "/usr/bin/alvr_server", - "C:/Program Files/ALVR/alvr_server.exe", - "C:/ALVR/alvr_server.exe" - ] - - for path in alvr_paths: - try: - subprocess.Popen([path], shell=True) - time.sleep(3) # 等待服务器启动 - if self._check_alvr_server(): - return True - except FileNotFoundError: - continue - + # 这里我们只尝试检测ALVR是否正在运行,而不是尝试启动它 + print("ℹ 检测到系统环境,跳过自动启动ALVR服务器") + print(" 请确保ALVR服务器已在运行") return False except Exception as e: @@ -133,42 +158,92 @@ class ALVRStreamer(DirectObject): def _connect_to_server(self): """连接到ALVR服务器""" try: - # 创建TCP连接 - self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.server_socket.settimeout(5) - self.server_socket.connect((self.alvr_server_ip, self.alvr_server_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": + # 首先检查是否为Pico 4有线连接模式 + # 如果VR系统已经通过SteamVR正常工作,我们可以启用直连模式 + if self._is_steamvr_active(): + print("💡 检测到SteamVR正在运行,启用直连模式") self.connected = True - print("✓ 已连接到ALVR服务器") 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('= 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 new file mode 100644 index 00000000..1365d9fc --- /dev/null +++ b/vr/vr_input.py @@ -0,0 +1,363 @@ +""" +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 new file mode 100644 index 00000000..8fc7bc2f --- /dev/null +++ b/vr/vr_system.py @@ -0,0 +1,380 @@ +""" +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 new file mode 100644 index 00000000..a117204c --- /dev/null +++ b/vr_display_demo.py @@ -0,0 +1,500 @@ +#!/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 new file mode 100644 index 00000000..082b2a72 --- /dev/null +++ b/vr_display_optimizer.py @@ -0,0 +1,316 @@ +#!/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 new file mode 100644 index 00000000..c6635ac4 --- /dev/null +++ b/vr_quick_start.py @@ -0,0 +1,263 @@ +#!/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 new file mode 100644 index 00000000..da9f5cf9 --- /dev/null +++ b/vr_streaming_test.py @@ -0,0 +1,264 @@ +#!/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