feat(core): 优化 ALVR 串流功能并添加对 Pico 4 的支持

- 添加对有线连接的支持,特别是 Pico 4 的有线连接
- 改进 ALVR 服务器检测和连接逻辑
- 优化 VR 帧获取和处理流程
- 增加备用帧获取功能(主摄像机视图)
- 修复 VR 眼部纹理相关问题
- 优化 VR 任务管理,确保正确渲染和提交帧
This commit is contained in:
Rowland 2025-07-29 10:17:13 +08:00
parent 5b7a1505cf
commit bb56813e44
24 changed files with 3131 additions and 76 deletions

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"Lingma.aI Chat.webToolsInAsk/AgentMode": "Auto-execute"
}

102
CLAUDE.md Normal file
View File

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

View File

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

View File

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

View File

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

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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('<I', len(message))
self.server_socket.send(length + message)
@ -178,6 +253,10 @@ class ALVRStreamer(DirectObject):
def _receive_message(self):
"""从ALVR服务器接收消息"""
try:
# 在直连模式下,不需要接收消息
if not self.server_socket and self.connected:
return None
# 接收消息长度
length_data = self.server_socket.recv(4)
if not length_data:
@ -334,6 +413,11 @@ class ALVRStreamer(DirectObject):
def _get_vr_frame(self):
"""获取VR渲染帧"""
try:
# 如果启用了直连模式通过SteamVR直接返回None
# 表示不需要通过ALVR传输帧SteamVR会直接处理
if self._is_steamvr_active():
return None
if not self.vr_manager.is_vr_enabled():
return None
@ -342,7 +426,22 @@ class ALVRStreamer(DirectObject):
right_texture = self.vr_manager.eye_textures.get('right')
if not left_texture or not right_texture:
return None
print("⚠ VR眼部纹理不可用使用主摄像机视图")
# 如果VR纹理不可用使用主摄像机视图作为备选
return self._get_fallback_frame()
# 检查纹理是否有效
if not left_texture.has_ram_image() or not right_texture.has_ram_image():
# 强制将纹理数据复制到RAM
left_texture.set_auto_texture_scale(Texture.AT_scaleNone)
right_texture.set_auto_texture_scale(Texture.AT_scaleNone)
# 尝试重新获取纹理数据
try:
self.world.graphicsEngine.extract_texture_data(left_texture, self.world.win.get_gsg())
self.world.graphicsEngine.extract_texture_data(right_texture, self.world.win.get_gsg())
except Exception as e:
print(f"提取纹理数据失败: {str(e)}")
# 合成立体帧
frame_data = self._compose_stereo_frame(left_texture, right_texture)
@ -350,6 +449,40 @@ class ALVRStreamer(DirectObject):
except Exception as e:
print(f"获取VR帧错误: {str(e)}")
import traceback
traceback.print_exc()
return None
def _get_fallback_frame(self):
"""获取备用帧(主摄像机视图)"""
try:
# 使用主窗口的纹理作为备选
if hasattr(self.world, 'win') and self.world.win:
# 创建临时缓冲区来捕获主视图
temp_buffer = self.world.win.makeTextureBuffer(
"temp_vr_frame", self.stream_width, self.stream_height
)
temp_texture = temp_buffer.getTexture()
temp_camera = self.world.makeCamera(temp_buffer)
# 渲染一帧
self.world.graphicsEngine.render_frame()
# 获取图像数据
image = PNMImage()
if temp_texture.store(image):
frame_data = image.makeRamImage()
# 清理临时资源
temp_buffer.remove()
return frame_data
# 清理临时资源
temp_buffer.remove()
return None
except Exception as e:
print(f"获取备用帧错误: {str(e)}")
return None
def _compose_stereo_frame(self, left_texture, right_texture):
@ -388,6 +521,10 @@ class ALVRStreamer(DirectObject):
def _send_frame(self, frame_data):
"""发送帧数据"""
try:
# 如果是直连模式,不需要发送帧数据
if self._is_steamvr_active():
return
if not self.server_socket:
return
@ -397,11 +534,29 @@ class ALVRStreamer(DirectObject):
"timestamp": time.time(),
"width": self.stream_width,
"height": self.stream_height,
"format": "rgb",
"data": frame_data.hex() # 转换为十六进制字符串
"format": "rgb"
}
self._send_message(frame_message)
# 对于二进制数据,我们将其作为单独的消息发送
if frame_data:
# 先发送元数据
self._send_message(frame_message)
# 然后发送二进制数据
try:
# 发送数据长度
data_length = len(frame_data)
length_header = struct.pack('<I', data_length)
self.server_socket.send(length_header)
# 发送实际数据
self.server_socket.send(frame_data)
except Exception as e:
print(f"发送帧数据错误: {str(e)}")
else:
# 如果没有帧数据,发送空帧消息
frame_message["empty"] = True
self._send_message(frame_message)
except Exception as e:
print(f"发送帧错误: {str(e)}")
@ -418,6 +573,12 @@ class ALVRStreamer(DirectObject):
def start_streaming(self):
"""开始串流"""
# 如果是直连模式,直接返回成功
if self._is_steamvr_active():
self.streaming = True
print("✓ 启用直连模式SteamVR将直接处理渲染输出")
return True
if not self.connected:
print("未连接到ALVR服务器")
return False
@ -451,13 +612,26 @@ class ALVRStreamer(DirectObject):
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
"bitrate": self.bitrate,
"mode": "alvr" # ALVR模式
}
def set_stream_quality(self, width, height, fps, bitrate):

View File

@ -2,6 +2,9 @@ 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
@ -36,6 +39,11 @@ class VRManager(DirectObject):
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 = {}
@ -189,14 +197,47 @@ class VRManager(DirectObject):
)
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):
"""创建眼部摄像机"""
@ -236,18 +277,22 @@ class VRManager(DirectObject):
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矩阵
# 转换为 Panda3D 矩阵
panda_matrix = self._convert_openvr_matrix(projection_matrix)
# 设置自定义投影矩阵
lens.setCustomProjectionMatrix(panda_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)}")
@ -303,13 +348,13 @@ class VRManager(DirectObject):
def _start_vr_tasks(self):
"""启动VR更新任务真实VR模式"""
if not self.simulation_mode:
taskMgr.add(self._update_vr_tracking, "vr_tracking")
taskMgr.add(self._update_vr_rendering, "vr_rendering")
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任务"""
taskMgr.add(self._update_simulation_tracking, "simulation_tracking")
taskMgr.add(self._update_simulation_rendering, "simulation_rendering")
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):
"""更新模拟追踪数据"""
@ -365,6 +410,11 @@ class VRManager(DirectObject):
# 获取设备姿态
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()
@ -387,6 +437,10 @@ class VRManager(DirectObject):
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()
@ -447,22 +501,61 @@ class VRManager(DirectObject):
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()
left_eye_texture.handle = self.left_eye_texture.getTextureId()
# 使用正确的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
self.vr_compositor.submit(openvr.Eye_Left, left_eye_texture)
# 检查纹理是否有效后再提交
if tex_handle:
self.vr_compositor.submit(openvr.Eye_Left, left_eye_texture)
# 提交右眼纹理
right_eye_texture = openvr.Texture_t()
right_eye_texture.handle = self.right_eye_texture.getTextureId()
# 使用正确的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
self.vr_compositor.submit(openvr.Eye_Right, right_eye_texture)
# 检查纹理是否有效后再提交
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):
"""获取控制器输入"""
@ -509,11 +602,11 @@ class VRManager(DirectObject):
try:
# 停止任务
if self.simulation_mode:
taskMgr.remove("simulation_tracking")
taskMgr.remove("simulation_rendering")
self.world.taskMgr.remove("simulation_tracking")
self.world.taskMgr.remove("simulation_rendering")
else:
taskMgr.remove("vr_tracking")
taskMgr.remove("vr_rendering")
self.world.taskMgr.remove("vr_tracking")
self.world.taskMgr.remove("vr_rendering")
# 关闭OpenVR
if self.vr_system and not self.simulation_mode:

View File

@ -46,7 +46,7 @@ class CoreWorld(Panda3DWorld):
self.accept("9", lambda: self.set_daytime("20:00")) # 深夜
self.accept("0", lambda: self.set_daytime("22:00")) # 深夜
self.launch_day_time_editor()
# self.launch_day_time_editor()
#self.createDirectionalLight()
print("✓ 核心世界初始化完成")

197
direct_vr_streaming_test.py Normal file
View File

@ -0,0 +1,197 @@
#!/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()

11
vr/__init__.py Normal file
View File

@ -0,0 +1,11 @@
"""
VR系统模块 - 提供VR设备集成和画面串流功能
此模块提供了一个完整的VR系统实现包括
- VR设备初始化和管理
- 立体渲染和显示
- VR输入处理
- 画面串流到VR设备
"""
from .vr_system import VRSystem

8
vr/devices/__init__.py Normal file
View File

@ -0,0 +1,8 @@
"""
VR设备特定实现模块
此模块包含针对不同VR设备的特定实现
- OpenXR设备
- Oculus设备
- SteamVR设备
"""

379
vr/vr_display.py Normal file
View File

@ -0,0 +1,379 @@
"""
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)

363
vr/vr_input.py Normal file
View File

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

380
vr/vr_system.py Normal file
View File

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

500
vr_display_demo.py Normal file
View File

@ -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. 初始化VRworld.initializeVR()")
print(" 3. 启动ALVRworld.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()

316
vr_display_optimizer.py Normal file
View File

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

263
vr_quick_start.py Normal file
View File

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

264
vr_streaming_test.py Normal file
View File

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