docs(README): 添加项目README文档

- 创建详细的README文档,介绍项目功能、架构、使用方法等
- 新增.pyc文件和目录,用于缓存编译后的Python代码
- 添加.lingma规则文件,用于代码格式检查
- 删除ALVR串流处理器代码,准备替换为OpenXR输入处理器
- 新增OpenXR输入处理器代码,处理VR控制器输入
This commit is contained in:
Rowland 2025-07-29 17:46:08 +08:00
parent bb56813e44
commit 72819341ef
41 changed files with 2297 additions and 6119 deletions

4
.lingma/rules/panda3d.md Normal file
View File

@ -0,0 +1,4 @@
---
trigger: always_on
---
在进行panda3d项目的开发时必须参考api文档@https://docs.panda3d.org/1.10/python/reference/index

Binary file not shown.

Binary file not shown.

Binary file not shown.

151
README.md
View File

@ -0,0 +1,151 @@
# EG - 3D编辑器与游戏引擎
EG是一个功能强大的3D编辑器和游戏引擎基于Panda3D和PyQt5构建提供直观的可视化编辑功能和VR支持。该引擎适用于游戏开发、3D可视化和交互式应用程序创建。
## 功能特性
### 核心功能
- **3D场景编辑与管理**:完整的场景编辑工具集
- **模型导入与处理**支持多种3D模型格式导入
- **材质系统**:高级材质编辑和管理
- **光照系统**:环境光、定向光、聚光灯和点光源
- **相机控制系统**:灵活的相机操作和视角控制
- **场景保存与加载**BAM格式场景保存和恢复
### 编辑器功能
- **对象选择与变换**:精确的对象选择和变换工具
- **坐标轴拖拽控制**直观的3D变换控制
- **属性面板编辑**:实时编辑对象属性
- **场景树管理**:层次化场景结构管理
- **材质编辑器**:可视化材质编辑
- **日夜循环系统**:动态光照和环境变化
### GUI系统
- **2D GUI元素**:按钮、标签、输入框、滑块等
- **3D空间GUI**3D文本和虚拟屏幕
- **GUI编辑模式**可视化GUI布局编辑
- **事件处理系统**GUI交互事件响应
### VR支持
- **VR系统集成**完整的VR环境支持
- **VR控制器输入处理**:精确的控制器输入识别
- **ALVR串流支持**无线VR体验
- **触觉反馈**:增强沉浸感的触觉反馈
- **VR手势识别**:自然的手势交互
### 脚本系统
- **Python脚本热重载**:实时脚本更新无需重启
- **脚本模板创建**:快速创建新脚本
- **对象脚本管理**:为游戏对象添加行为脚本
- **脚本调试支持**:内置调试功能
### 项目管理
- **项目创建与保存**:完整的项目管理功能
- **资源管理**:统一的资源导入和管理
- **项目打包**:一键打包发布项目
## 系统架构
### 核心模块
- `core/world.py` - 核心世界管理,集成所有子系统
- `core/event_handler.py` - 输入事件处理系统
- `core/selection.py` - 对象选择和变换系统
- `core/tool_manager.py` - 编辑工具管理
- `core/script_system.py` - Python脚本系统
- `core/vr_manager.py` - VR系统管理
- `core/vr_input_handler.py` - VR输入处理
- `core/alvr_streamer.py` - ALVR串流支持
### 子系统
- `scene/scene_manager.py` - 场景管理系统
- `gui/gui_manager.py` - GUI系统管理
- `ui/` - 用户界面组件
- `ui/main_window.py` - 主窗口设置
- `ui/property_panel.py` - 属性面板
- `ui/widgets.py` - 自定义UI组件
- `ui/interface_manager.py` - 界面管理
- `project/project_manager.py` - 项目管理系统
## 快速开始
### 系统要求
- Python 3.7+
- Panda3D
- PyQt5
- 支持OpenGL的显卡
### 依赖安装
```bash
pip install -r requirements.txt
```
### 运行编辑器
```bash
python main.py
```
### 基本操作
- **鼠标左键**:选择对象
- **鼠标右键**:旋转视角
- **鼠标中键**:平移视角
- **滚轮**:缩放视角
- **WASD**:在场景中移动
- **数字键1-0**:切换不同时间点
- **F键**:聚焦选中对象
- **Delete键**:删除选中对象
## 编辑器使用指南
### 导入模型
1. 点击"文件" > "导入模型"
2. 选择支持的3D模型文件
3. 调整模型位置和属性
### 场景编辑
1. 使用选择工具选中对象
2. 通过属性面板修改对象属性
3. 使用变换工具(移动、旋转、缩放)调整对象
### GUI创建
1. 进入GUI编辑模式
2. 选择GUI元素类型
3. 点击场景放置GUI元素
4. 通过属性面板调整GUI属性
### VR模式
1. 连接VR设备
2. 点击"VR" > "启用VR模式"
3. 使用VR控制器进行交互
### 脚本编辑
1. 点击"脚本" > "创建新脚本"
2. 编写Python脚本代码
3. 将脚本附加到场景对象
## 开发指南
### 架构概述
EG采用模块化设计核心功能通过`MyWorld`类集成各个子系统。所有子系统都通过代理方法暴露功能使API保持一致性。
### 添加新功能
1. 在相应目录下创建新模块
2. 在`MyWorld`类中添加代理方法
3. 通过子系统管理器集成功能
### 扩展VR功能
1. 修改`vr_manager.py`添加新功能
2. 更新`vr_input_handler.py`处理新输入
3. 在`alvr_streamer.py`中添加串流支持
### 创建自定义工具
1. 在`tool_manager.py`中添加新工具类型
2. 实现工具的交互逻辑
3. 在UI中添加工具选择按钮
## 贡献
欢迎提交Pull Request或Issue报告问题。贡献前请先查看贡献指南。
## 许可证
MIT License

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,683 +0,0 @@
"""
ALVR串流处理器
负责与ALVR服务器通信和视频流传输
支持Quest等VR头显的无线串流
"""
import socket
import struct
import threading
import json
import time
import subprocess
import psutil
from direct.showbase.DirectObject import DirectObject
from panda3d.core import Texture, PNMImage
class ALVRStreamer(DirectObject):
"""ALVR串流处理器"""
def __init__(self, world, vr_manager):
super().__init__()
self.world = world
self.vr_manager = vr_manager
# ALVR服务器配置
self.alvr_server_ip = "127.0.0.1"
self.alvr_server_port = 9943
self.alvr_streaming_port = 9944
# 添加对有线连接的支持
self.connection_mode = "auto" # auto, wireless, wired
self.wired_port = 9945 # Pico 4有线连接可能使用的端口
# 连接状态
self.connected = False
self.streaming = False
self.server_socket = None
self.streaming_socket = None
# 流媒体配置
self.stream_width = 2880 # Quest 2 推荐分辨率
self.stream_height = 1700
self.stream_fps = 72
self.bitrate = 150 # Mbps
self.codec = "h264"
# 线程管理
self.connection_thread = None
self.streaming_thread = None
self.running = False
# 性能统计
self.frame_count = 0
self.last_fps_time = time.time()
self.current_fps = 0
self.latency = 0
print("✓ ALVR串流处理器初始化完成")
def initialize(self):
"""初始化ALVR串流"""
try:
# 检查ALVR服务器是否运行
if not self._check_alvr_server():
print("ALVR服务器未运行尝试启动...")
if not self._start_alvr_server():
print("无法启动ALVR服务器")
return False
# 连接到ALVR服务器
if not self._connect_to_server():
print("无法连接到ALVR服务器")
return False
# 配置流媒体设置
self._configure_streaming()
# 启动串流线程
self._start_streaming_threads()
print("✓ ALVR串流初始化成功")
return True
except Exception as e:
print(f"ALVR初始化错误: {str(e)}")
return False
def _check_alvr_server(self):
"""检查ALVR服务器是否运行"""
try:
# 检查进程
alvr_processes = []
for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
try:
# 检查ALVR相关进程包括Pico 4支持
if 'alvr' in proc.info['name'].lower() or any('alvr' in (arg or '').lower() for arg in (proc.info['cmdline'] or [])):
alvr_processes.append(proc)
except (psutil.NoSuchProcess, psutil.AccessDenied, TypeError):
# 忽略无法访问的进程
continue
if alvr_processes:
print(f"✓ 检测到 {len(alvr_processes)} 个ALVR相关进程")
for proc in alvr_processes:
try:
print(f" - 进程: {proc.info['name']} (PID: {proc.info['pid']})")
except (psutil.NoSuchProcess, psutil.AccessDenied):
pass
return True
# 尝试连接端口
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(2)
result = sock.connect_ex((self.alvr_server_ip, self.alvr_server_port))
sock.close()
if result == 0:
print("✓ 检测到ALVR服务器端口")
return True
else:
# 尝试其他可能的端口Pico 4可能使用不同的端口
alternative_ports = [self.wired_port, 8080, 8081, 8082, 9944, 9945]
for port in alternative_ports:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(1)
result = sock.connect_ex((self.alvr_server_ip, port))
sock.close()
if result == 0:
print(f"✓ 检测到ALVR服务器在端口 {port}")
self.alvr_server_port = port
return True
return False
except Exception as e:
print(f"检查ALVR服务器错误: {str(e)}")
return False
def _start_alvr_server(self):
"""启动ALVR服务器"""
try:
# 获取当前操作系统
import platform
system = platform.system().lower()
# 尝试启动ALVR服务器
# 这里我们只尝试检测ALVR是否正在运行而不是尝试启动它
print(" 检测到系统环境跳过自动启动ALVR服务器")
print(" 请确保ALVR服务器已在运行")
return False
except Exception as e:
print(f"启动ALVR服务器错误: {str(e)}")
return False
def _connect_to_server(self):
"""连接到ALVR服务器"""
try:
# 首先检查是否为Pico 4有线连接模式
# 如果VR系统已经通过SteamVR正常工作我们可以启用直连模式
if self._is_steamvr_active():
print("💡 检测到SteamVR正在运行启用直连模式")
self.connected = True
return True
# 尝试多种连接方式以支持Pico 4有线连接
connection_attempts = [
(self.alvr_server_ip, self.alvr_server_port), # 默认无线端口
(self.alvr_server_ip, self.wired_port), # Pico 4有线端口
(self.alvr_server_ip, 8081), # 替代端口1
(self.alvr_server_ip, 8082) # 替代端口2
]
for ip, port in connection_attempts:
try:
print(f"尝试连接到ALVR服务器 {ip}:{port}...")
# 创建TCP连接
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_socket.settimeout(5)
self.server_socket.connect((ip, port))
# 发送握手消息
handshake_data = {
"type": "handshake",
"client_name": "Panda3D_VR_Engine",
"version": "1.0",
"capabilities": {
"video": True,
"audio": True,
"tracking": True,
"haptics": True
}
}
self._send_message(handshake_data)
# 接收响应
response = self._receive_message()
if response and response.get("type") == "handshake_response":
self.connected = True
self.alvr_server_port = port # 记住成功连接的端口
print(f"✓ 已连接到ALVR服务器 {ip}:{port}")
return True
else:
print(f"✗ 从 {ip}:{port} 接收到无效响应")
except Exception as e:
print(f"连接到 {ip}:{port} 失败: {str(e)}")
if self.server_socket:
self.server_socket.close()
self.server_socket = None
continue
return False
except Exception as e:
print(f"连接ALVR服务器错误: {str(e)}")
return False
def _is_steamvr_active(self):
"""检查SteamVR是否正在运行"""
try:
# 检查是否有SteamVR相关进程
steamvr_processes = ["vrserver", "steamvr", "vrcompositor", "vrmonitor"]
for proc in psutil.process_iter(['pid', 'name']):
try:
proc_name = proc.info['name'].lower()
if any(sv_proc in proc_name for sv_proc in steamvr_processes):
print(f"✓ 检测到SteamVR进程: {proc_name}")
return True
except (psutil.NoSuchProcess, psutil.AccessDenied):
continue
return False
except Exception as e:
print(f"检查SteamVR状态错误: {str(e)}")
return False
def _send_message(self, data):
"""发送消息到ALVR服务器"""
try:
# 在直连模式下,不需要发送消息
if not self.server_socket and not self.connected:
return
message = json.dumps(data).encode('utf-8')
length = struct.pack('<I', len(message))
self.server_socket.send(length + message)
except Exception as e:
print(f"发送消息错误: {str(e)}")
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:
return None
length = struct.unpack('<I', length_data)[0]
# 接收消息内容
message_data = b''
while len(message_data) < length:
chunk = self.server_socket.recv(length - len(message_data))
if not chunk:
return None
message_data += chunk
return json.loads(message_data.decode('utf-8'))
except Exception as e:
print(f"接收消息错误: {str(e)}")
return None
def _configure_streaming(self):
"""配置流媒体设置"""
try:
# 发送流媒体配置
config_data = {
"type": "stream_config",
"video": {
"width": self.stream_width,
"height": self.stream_height,
"fps": self.stream_fps,
"bitrate": self.bitrate,
"codec": self.codec
},
"audio": {
"enabled": True,
"sample_rate": 48000,
"channels": 2
}
}
self._send_message(config_data)
# 接收配置响应
response = self._receive_message()
if response and response.get("type") == "config_response":
if response.get("status") == "success":
print("✓ 流媒体配置成功")
return True
return False
except Exception as e:
print(f"配置流媒体错误: {str(e)}")
return False
def _start_streaming_threads(self):
"""启动串流线程"""
self.running = True
# 启动连接管理线程
self.connection_thread = threading.Thread(target=self._connection_handler)
self.connection_thread.daemon = True
self.connection_thread.start()
# 启动流媒体线程
self.streaming_thread = threading.Thread(target=self._streaming_handler)
self.streaming_thread.daemon = True
self.streaming_thread.start()
def _connection_handler(self):
"""连接处理线程"""
while self.running:
try:
if self.connected:
# 发送心跳
heartbeat = {"type": "heartbeat", "timestamp": time.time()}
self._send_message(heartbeat)
# 接收消息
response = self._receive_message()
if response:
self._handle_server_message(response)
time.sleep(0.1)
except Exception as e:
print(f"连接处理错误: {str(e)}")
self.connected = False
time.sleep(1)
def _streaming_handler(self):
"""流媒体处理线程"""
while self.running:
try:
if self.connected and self.streaming:
# 获取VR渲染帧
frame_data = self._get_vr_frame()
if frame_data:
# 发送帧数据
self._send_frame(frame_data)
# 更新性能统计
self._update_performance_stats()
time.sleep(1.0 / self.stream_fps)
except Exception as e:
print(f"流媒体处理错误: {str(e)}")
time.sleep(0.1)
def _handle_server_message(self, message):
"""处理服务器消息"""
msg_type = message.get("type")
if msg_type == "start_streaming":
self.streaming = True
print("✓ 开始VR串流")
elif msg_type == "stop_streaming":
self.streaming = False
print("✓ 停止VR串流")
elif msg_type == "client_connected":
print(f"✓ VR客户端已连接: {message.get('client_info', {})}")
elif msg_type == "client_disconnected":
print("✓ VR客户端已断开")
elif msg_type == "tracking_data":
self._handle_tracking_data(message.get("data"))
elif msg_type == "haptic_feedback":
self._handle_haptic_feedback(message.get("data"))
def _handle_tracking_data(self, tracking_data):
"""处理跟踪数据"""
if not tracking_data:
return
# 更新VR管理器的跟踪数据
# 这里可以处理从ALVR客户端发送的跟踪数据
pass
def _handle_haptic_feedback(self, haptic_data):
"""处理触觉反馈"""
if not haptic_data:
return
# 处理触觉反馈请求
# 这里可以控制VR控制器的震动等
pass
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
# 获取左右眼纹理
left_texture = self.vr_manager.eye_textures.get('left')
right_texture = self.vr_manager.eye_textures.get('right')
if not left_texture or not right_texture:
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)
return frame_data
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):
"""合成立体帧"""
try:
# 创建组合图像
combined_image = PNMImage(self.stream_width, self.stream_height)
# 获取左右眼图像
left_image = PNMImage()
right_image = PNMImage()
if left_texture.store(left_image) and right_texture.store(right_image):
# 将左右眼图像合并Side-by-Side布局
left_width = self.stream_width // 2
# 缩放左眼图像到左半部分
left_scaled = PNMImage(left_width, self.stream_height)
left_scaled.quickFilterFrom(left_image)
combined_image.copySubImage(left_scaled, 0, 0)
# 缩放右眼图像到右半部分
right_scaled = PNMImage(left_width, self.stream_height)
right_scaled.quickFilterFrom(right_image)
combined_image.copySubImage(right_scaled, left_width, 0)
# 转换为字节数据
return combined_image.makeRamImage()
return None
except Exception as e:
print(f"合成立体帧错误: {str(e)}")
return None
def _send_frame(self, frame_data):
"""发送帧数据"""
try:
# 如果是直连模式,不需要发送帧数据
if self._is_steamvr_active():
return
if not self.server_socket:
return
# 创建帧消息
frame_message = {
"type": "video_frame",
"timestamp": time.time(),
"width": self.stream_width,
"height": self.stream_height,
"format": "rgb"
}
# 对于二进制数据,我们将其作为单独的消息发送
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)}")
def _update_performance_stats(self):
"""更新性能统计"""
self.frame_count += 1
current_time = time.time()
if current_time - self.last_fps_time >= 1.0:
self.current_fps = self.frame_count
self.frame_count = 0
self.last_fps_time = current_time
def start_streaming(self):
"""开始串流"""
# 如果是直连模式,直接返回成功
if self._is_steamvr_active():
self.streaming = True
print("✓ 启用直连模式SteamVR将直接处理渲染输出")
return True
if not self.connected:
print("未连接到ALVR服务器")
return False
start_message = {"type": "start_streaming"}
self._send_message(start_message)
return True
def stop_streaming(self):
"""停止串流"""
if not self.connected:
return
stop_message = {"type": "stop_streaming"}
self._send_message(stop_message)
self.streaming = False
def send_haptic_feedback(self, controller_id, duration, intensity):
"""发送触觉反馈"""
if not self.connected:
return
haptic_message = {
"type": "haptic_feedback",
"controller_id": controller_id,
"duration": duration,
"intensity": intensity
}
self._send_message(haptic_message)
def get_streaming_status(self):
"""获取串流状态"""
# 如果是直连模式,返回特殊的直连状态
if self._is_steamvr_active():
return {
"connected": True,
"streaming": self.streaming,
"fps": self.current_fps,
"latency": self.latency,
"resolution": f"{self.stream_width}x{self.stream_height}",
"bitrate": self.bitrate,
"mode": "direct" # 直连模式
}
return {
"connected": self.connected,
"streaming": self.streaming,
"fps": self.current_fps,
"latency": self.latency,
"resolution": f"{self.stream_width}x{self.stream_height}",
"bitrate": self.bitrate,
"mode": "alvr" # ALVR模式
}
def set_stream_quality(self, width, height, fps, bitrate):
"""设置串流质量"""
self.stream_width = width
self.stream_height = height
self.stream_fps = fps
self.bitrate = bitrate
# 如果正在串流,重新配置
if self.streaming:
self._configure_streaming()
def shutdown(self):
"""关闭串流"""
print("关闭ALVR串流...")
self.running = False
self.streaming = False
# 发送断开消息
if self.connected:
disconnect_message = {"type": "disconnect"}
self._send_message(disconnect_message)
# 关闭连接
if self.server_socket:
self.server_socket.close()
if self.streaming_socket:
self.streaming_socket.close()
# 等待线程结束
if self.connection_thread:
self.connection_thread.join(timeout=2)
if self.streaming_thread:
self.streaming_thread.join(timeout=2)
self.connected = False
print("✓ ALVR串流已关闭")
def is_connected(self):
"""检查是否连接"""
return self.connected
def is_streaming(self):
"""检查是否在串流"""
return self.streaming

View File

@ -0,0 +1,261 @@
"""
OpenXR输入处理器
处理OpenXR控制器输入和手势识别
支持多种OpenXR控制器和输入方式
"""
from direct.showbase.DirectObject import DirectObject
from panda3d.core import Vec3, Point3, CollisionRay, CollisionNode, CollisionHandlerQueue, BitMask32
from direct.task import Task
import time
class OpenXRInputHandler(DirectObject):
"""OpenXR输入处理器"""
def __init__(self, world, openxr_manager):
super().__init__()
self.world = world
self.openxr_manager = openxr_manager
# 控制器状态
self.controllers = {}
self.controller_nodes = {}
self.controller_rays = {}
# 手势识别
self.gesture_enabled = True
self.gesture_history = []
self.gesture_threshold = 0.1
# 交互系统
self.interaction_enabled = True
self.selected_object = None
self.grab_offset = Vec3(0, 0, 0)
# 输入映射
self.input_mappings = {
'trigger': self._handle_trigger,
'grip': self._handle_grip,
'thumbstick': self._handle_thumbstick,
'touchpad': self._handle_touchpad,
'menu': self._handle_menu,
'system': self._handle_system
}
print("✓ OpenXR输入处理器初始化完成")
def start_input_handling(self):
"""启动输入处理"""
if not self.openxr_manager.is_openxr_enabled():
print("OpenXR未启用无法启动输入处理")
return False
# 启动输入更新任务
self.world.taskMgr.add(self._update_input, "openxr_input_update")
# 设置控制器可视化
self._setup_controller_visualization()
print("✓ OpenXR输入处理已启动")
return True
def stop_input_handling(self):
"""停止输入处理"""
self.world.taskMgr.remove("openxr_input_update")
self._cleanup_controller_visualization()
print("✓ OpenXR输入处理已停止")
def _update_input(self, task):
"""更新输入处理"""
if not self.openxr_manager.is_openxr_enabled():
return Task.cont
try:
# 更新所有控制器
self._update_controllers()
# 处理手势识别
if self.gesture_enabled:
self._process_gestures()
# 处理交互
if self.interaction_enabled:
self._process_interactions()
except Exception as e:
print(f"OpenXR输入更新错误: {str(e)}")
return Task.cont
def _update_controllers(self):
"""更新控制器状态"""
if self.openxr_manager.simulation_mode:
# 在模拟模式下使用模拟数据
sim_data = self.openxr_manager.get_simulation_data('controller_poses')
for controller_id, pose_data in sim_data.items():
if pose_data['connected']:
self._update_controller_pose(controller_id, pose_data)
else:
# TODO: 实际OpenXR控制器更新
pass
def _update_controller_pose(self, controller_id, pose_data):
"""更新控制器姿态"""
# 更新控制器位置和旋转
if controller_id not in self.controller_nodes:
self._create_controller_node(controller_id)
controller_node = self.controller_nodes[controller_id]
position = pose_data['position']
rotation = pose_data['rotation']
controller_node.setPos(*position)
controller_node.setQuat(Quat(*rotation))
def _create_controller_node(self, controller_id):
"""创建控制器节点"""
# 创建一个简单的控制器可视化模型
from panda3d.core import CardMaker
cm = CardMaker(f'controller_{controller_id}')
cm.setFrame(-0.05, 0.05, -0.05, 0.05)
controller_node = self.world.render.attachNewNode(cm.generate())
controller_node.setColor(0, 1, 0, 1) # 绿色
self.controller_nodes[controller_id] = controller_node
# 创建控制器射线
self._create_controller_ray(controller_id)
def _create_controller_ray(self, controller_id):
"""创建控制器射线"""
# 创建碰撞射线用于交互检测
ray_node = CollisionNode(f'controller_ray_{controller_id}')
ray = CollisionRay()
ray.setOrigin(Point3(0, 0, 0))
ray.setDirection(Vec3(0, 1, 0)) # 默认向前
ray_node.addSolid(ray)
ray_node.setFromCollideMask(BitMask32.bit(0))
ray_np = self.controller_nodes[controller_id].attachNewNode(ray_node)
queue = CollisionHandlerQueue()
self.controller_rays[controller_id] = {
'node': ray_np,
'queue': queue
}
def _setup_controller_visualization(self):
"""设置控制器可视化"""
if self.openxr_manager.simulation_mode:
# 在模拟模式下创建控制器可视化
sim_data = self.openxr_manager.get_simulation_data('controller_poses')
for controller_id in sim_data.keys():
self._create_controller_node(controller_id)
def _cleanup_controller_visualization(self):
"""清理控制器可视化"""
# 移除所有控制器节点
for controller_node in self.controller_nodes.values():
controller_node.removeNode()
self.controller_nodes.clear()
self.controller_rays.clear()
def _process_gestures(self):
"""处理手势识别"""
# TODO: 实现手势识别逻辑
pass
def _process_interactions(self):
"""处理交互"""
# TODO: 实现交互逻辑
pass
def _handle_trigger(self, controller_id, value):
"""处理触发器输入"""
# TODO: 实现触发器处理逻辑
pass
def _handle_grip(self, controller_id, value):
"""处理抓握输入"""
# TODO: 实现抓握处理逻辑
pass
def _handle_thumbstick(self, controller_id, x, y):
"""处理摇杆输入"""
# TODO: 实现摇杆处理逻辑
pass
def _handle_touchpad(self, controller_id, x, y, pressed):
"""处理触摸板输入"""
# TODO: 实现触摸板处理逻辑
pass
def _handle_menu(self, controller_id, pressed):
"""处理菜单按钮"""
# TODO: 实现菜单按钮处理逻辑
pass
def _handle_system(self, controller_id, pressed):
"""处理系统按钮"""
# TODO: 实现系统按钮处理逻辑
pass
def get_all_controllers(self):
"""获取所有控制器"""
if self.openxr_manager.simulation_mode:
sim_data = self.openxr_manager.get_simulation_data('controller_poses')
return list(sim_data.keys())
else:
# TODO: 返回实际连接的控制器
return []
def get_controller_state(self, controller_id):
"""获取控制器状态"""
if self.openxr_manager.simulation_mode:
sim_data = self.openxr_manager.get_simulation_data('controller_poses')
if controller_id in sim_data:
# 返回模拟控制器状态
return {
'trigger': 0.0,
'grip': 0.0,
'thumbstick': {'x': 0.0, 'y': 0.0},
'touchpad': {'x': 0.0, 'y': 0.0, 'pressed': False},
'menu': False,
'system': False,
'connected': sim_data[controller_id]['connected']
}
# 默认返回空状态
return {
'trigger': 0.0,
'grip': 0.0,
'thumbstick': {'x': 0.0, 'y': 0.0},
'touchpad': {'x': 0.0, 'y': 0.0, 'pressed': False},
'menu': False,
'system': False,
'connected': False
}
def set_gesture_enabled(self, enabled):
"""设置手势识别启用状态"""
self.gesture_enabled = enabled
print(f"{'' if enabled else ''} OpenXR手势识别已{'启用' if enabled else '禁用'}")
def set_interaction_enabled(self, enabled):
"""设置交互启用状态"""
self.interaction_enabled = enabled
print(f"{'' if enabled else ''} OpenXR交互已{'启用' if enabled else '禁用'}")
def show_controller_rays(self, show=True):
"""显示/隐藏控制器射线"""
for ray_data in self.controller_rays.values():
ray_node = ray_data['node']
if show:
ray_node.show()
else:
ray_node.hide()
print(f"{'' if show else ''} 控制器射线已{'显示' if show else '隐藏'}")

342
core/openxr_manager.py Normal file
View File

@ -0,0 +1,342 @@
"""
OpenXR管理器
负责OpenXR系统初始化追踪和渲染
支持有线VR串流功能
"""
import warnings
warnings.filterwarnings("ignore", category=DeprecationWarning)
from panda3d.core import *
from panda3d.core import Mat4, PerspectiveLens, CardMaker
# 修复taskMgr导入错误应该从TaskManagerGlobal导入而不是ShowBaseGlobal
from direct.task.TaskManagerGlobal import taskMgr
from direct.showbase.DirectObject import DirectObject
from direct.task import Task
import sys
class OpenXRManager(DirectObject):
"""OpenXR管理器 - 处理OpenXR系统初始化、追踪和渲染"""
def __init__(self, world):
super().__init__()
self.world = world
self.openxr_enabled = False
self.xr_instance = None
self.xr_system = None
self.xr_session = None
# 渲染配置
self.render_width = 1920
self.render_height = 1080
# 有线连接设置
self.wired_connection = True
self.connection_ip = "127.0.0.1"
self.connection_port = 9943
# 模拟模式设置(用于开发测试)
self.simulation_mode = False
self.simulation_data = {
'head_pose': {'position': [0, 0, 1.6], 'rotation': [0, 0, 0, 1]},
'controller_poses': {
0: {'position': [-0.3, 0, 1.2], 'rotation': [0, 0, 0, 1], 'connected': True},
1: {'position': [0.3, 0, 1.2], 'rotation': [0, 0, 0, 1], 'connected': True}
},
'render_size': (1920, 1080)
}
# 立体渲染缓冲区
self.left_eye_buffer = None
self.right_eye_buffer = None
self.left_eye_camera = None
self.right_eye_camera = None
# 眼部纹理(用于串流)
self.left_eye_texture = None
self.right_eye_texture = None
self.eye_textures = {}
# 控制器相关
self.controller_nodes = {}
self.controller_poses = {}
print("✓ OpenXR管理器初始化完成")
def initialize_openxr(self, force_simulation=False):
"""初始化OpenXR系统"""
try:
# 检查是否强制使用模拟模式
if force_simulation:
print("🔧 强制启用OpenXR模拟模式")
return self._init_simulation_mode()
# 检查OpenXR支持
if not self._check_openxr_support():
print("⚠ OpenXR支持不可用切换到模拟模式")
return self._init_simulation_mode()
# 尝试初始化OpenXR
if not self._init_openxr_native():
print("⚠ OpenXR初始化失败切换到模拟模式")
print("提示: 请确保OpenXR运行时已安装且VR头盔已连接")
return self._init_simulation_mode()
# 真实OpenXR模式初始化成功
print("✓ 真实OpenXR模式初始化成功")
return self._init_real_openxr_mode()
except Exception as e:
print(f"OpenXR初始化错误: {str(e)}")
print("⚠ 切换到模拟模式")
return self._init_simulation_mode()
def _check_openxr_support(self):
"""检查OpenXR支持"""
try:
# TODO: 实际检查OpenXR运行时支持
# 这里暂时返回False以使用模拟模式进行开发
return False
except:
return False
def _init_openxr_native(self):
"""初始化原生OpenXR"""
# TODO: 实现OpenXR初始化逻辑
print(" OpenXR原生初始化占位符")
return False
def _init_real_openxr_mode(self):
"""初始化真实OpenXR模式"""
self.openxr_enabled = True
self.simulation_mode = False
# TODO: 实现实际的OpenXR设置
# 1. 创建OpenXR实例
# 2. 获取系统信息
# 3. 创建会话
# 4. 设置渲染目标
# 5. 初始化控制器
return True
def _init_simulation_mode(self):
"""初始化模拟模式"""
self.openxr_enabled = True
self.simulation_mode = True
# 设置模拟渲染尺寸
self.render_width = self.simulation_data['render_size'][0]
self.render_height = self.simulation_data['render_size'][1]
# 初始化模拟立体渲染
self._setup_simulation_rendering()
print(" OpenXR模拟模式已启用")
print(f" 渲染尺寸: {self.render_width}x{self.render_height}")
print(" 头部追踪: 模拟摆动")
print(" 控制器: 模拟位置")
return True
def _setup_simulation_rendering(self):
"""设置模拟渲染"""
print("🔧 OpenXR模拟模式: 开始设置渲染")
# 创建左右眼渲染缓冲区
self.left_eye_buffer = self._create_eye_buffer("left_eye", self.render_width//2, self.render_height)
self.right_eye_buffer = self._create_eye_buffer("right_eye", self.render_width//2, self.render_height)
print(f" 创建眼部缓冲区: 左眼={self.left_eye_buffer is not None}, 右眼={self.right_eye_buffer is not None}")
# 创建左右眼相机
self.left_eye_camera = self._create_eye_camera("left_eye_camera", (-0.03, 0, 0)) # 瞳距的一半
self.right_eye_camera = self._create_eye_camera("right_eye_camera", (0.03, 0, 0)) # 瞳距的一半)
print(f" 创建眼部相机: 左眼={self.left_eye_camera is not None}, 右眼={self.right_eye_camera is not None}")
# 创建用于显示的左右眼纹理,格式与主程序保持一致
self.left_eye_texture = Texture("left_eye_texture")
self.right_eye_texture = Texture("right_eye_texture")
self.left_eye_texture.setFormat(Texture.F_rgba8)
self.right_eye_texture.setFormat(Texture.F_rgba8)
self.left_eye_texture.setComponentType(Texture.T_unsigned_byte)
self.right_eye_texture.setComponentType(Texture.T_unsigned_byte)
print(f" 创建眼部纹理: 左眼={self.left_eye_texture is not None}, 右眼={self.right_eye_texture is not None}")
print(" 设置纹理格式为F_rgba8和T_unsigned_byte")
# 设置自动纹理缩放为None
self.left_eye_texture.setAutoTextureScale(ATSNone)
self.right_eye_texture.setAutoTextureScale(ATSNone)
print(" 设置自动纹理缩放为ATSNone")
# 将纹理附加到渲染缓冲区使用RTMCopyRam模式确保数据能被复制到RAM
left_result = self.left_eye_buffer.addRenderTexture(self.left_eye_texture, GraphicsOutput.RTMCopyRam)
right_result = self.right_eye_buffer.addRenderTexture(self.right_eye_texture, GraphicsOutput.RTMCopyRam)
print(f" 添加渲染纹理结果: 左眼={left_result}, 右眼={right_result}")
# 设置相机渲染目标
if self.left_eye_buffer and self.left_eye_camera:
self.left_eye_camera.node().setScene(self.world.render)
print(" 设置左眼相机渲染场景")
if self.right_eye_buffer and self.right_eye_camera:
self.right_eye_camera.node().setScene(self.world.render)
print(" 设置右眼相机渲染场景")
self.eye_textures['left'] = self.left_eye_texture
self.eye_textures['right'] = self.right_eye_texture
print(" 初始化完成,开始渲染帧")
# 强制渲染多帧以便初始化纹理数据
# 参考主程序的实现方式,渲染足够多的帧以确保数据填充
for i in range(5): # 渲染5帧以确保纹理数据被正确填充
self.world.graphicsEngine.renderFrame()
print(f" 渲染第{i+1}")
# 提取纹理数据到RAM
gsg = self.world.win.getGsg()
print(f" 获取GSG: {gsg is not None}")
if gsg:
self.world.graphicsEngine.extractTextureData(self.left_eye_texture, gsg)
self.world.graphicsEngine.extractTextureData(self.right_eye_texture, gsg)
print(" 提取纹理数据到RAM")
# 检查纹理数据是否成功提取
left_has_ram = self.left_eye_texture.hasRamImage()
right_has_ram = self.right_eye_texture.hasRamImage()
print(f" 纹理RAM图像检查: 左眼={left_has_ram}, 右眼={right_has_ram}")
if left_has_ram:
left_data_size = len(self.left_eye_texture.getRamImage().getData())
print(f" 左眼纹理数据大小: {left_data_size} 字节")
if right_has_ram:
right_data_size = len(self.right_eye_texture.getRamImage().getData())
print(f" 右眼纹理数据大小: {right_data_size} 字节")
def get_left_eye_texture(self):
"""获取左眼纹理用于显示"""
return self.left_eye_texture
def get_right_eye_texture(self):
"""获取右眼纹理用于显示"""
return self.right_eye_texture
def _create_eye_buffer(self, name, width, height):
"""创建眼部渲染缓冲区"""
# 创建帧缓冲区属性
fb_prop = FrameBufferProperties()
fb_prop.setRgbColor(True)
fb_prop.setRgbaBits(8, 8, 8, 8)
fb_prop.setDepthBits(24)
# 创建窗口属性
win_prop = WindowProperties()
win_prop.setSize(width, height)
# 创建缓冲区
buffer = self.world.graphicsEngine.makeOutput(
self.world.pipe,
name,
-1000, # 低优先级
fb_prop,
win_prop,
GraphicsPipe.BFRefuseWindow
)
return buffer
def _create_eye_camera(self, name, position_offset):
"""创建眼部相机"""
# 创建相机节点
camera_node = Camera(name)
lens = PerspectiveLens()
lens.setFov(100) # VR镜头视野
lens.setNear(0.1)
lens.setFar(1000.0)
camera_node.setLens(lens)
# 创建相机节点路径
camera_np = self.world.render.attachNewNode(camera_node)
camera_np.setPos(*position_offset)
return camera_np
def shutdown_openxr(self):
"""关闭OpenXR系统"""
if not self.openxr_enabled:
return True
try:
# 清理渲染缓冲区
if self.left_eye_buffer:
self.world.graphicsEngine.removeWindow(self.left_eye_buffer)
self.left_eye_buffer = None
if self.right_eye_buffer:
self.world.graphicsEngine.removeWindow(self.right_eye_buffer)
self.right_eye_buffer = None
# 清理相机
if self.left_eye_camera:
self.left_eye_camera.removeNode()
self.left_eye_camera = None
if self.right_eye_camera:
self.right_eye_camera.removeNode()
self.right_eye_camera = None
# 重置状态
self.openxr_enabled = False
self.xr_instance = None
self.xr_system = None
self.xr_session = None
print("✓ OpenXR系统已关闭")
return True
except Exception as e:
print(f"OpenXR关闭错误: {str(e)}")
return False
def is_openxr_enabled(self):
"""检查OpenXR是否启用"""
return self.openxr_enabled
def get_openxr_info(self):
"""获取OpenXR系统信息"""
return {
"enabled": self.openxr_enabled,
"mode": "simulation" if self.simulation_mode else "real",
"render_size": (self.render_width, self.render_height),
"simulation_mode": self.simulation_mode,
"wired_connection": self.wired_connection
}
def update_simulation_data(self, data_type, data):
"""更新模拟数据"""
if data_type in self.simulation_data:
self.simulation_data[data_type] = data
return True
return False
def get_simulation_data(self, data_type=None):
"""获取模拟数据"""
if data_type:
return self.simulation_data.get(data_type, None)
return self.simulation_data
def enable_simulation_mode(self):
"""启用模拟模式"""
if not self.simulation_mode:
self.shutdown_openxr()
self.simulation_mode = True
self.initialize_openxr(force_simulation=True)

257
core/openxr_streamer.py Normal file
View File

@ -0,0 +1,257 @@
"""
OpenXR串流处理器
负责与VR设备通信和视频流传输
支持有线连接的低延迟串流
"""
import socket
import struct
import threading
import json
import time
import subprocess
import psutil
from direct.showbase.DirectObject import DirectObject
from panda3d.core import Texture, PNMImage
class OpenXRStreamer(DirectObject):
"""OpenXR串流处理器"""
def __init__(self, world, openxr_manager):
super().__init__()
self.world = world
self.openxr_manager = openxr_manager
# 串流服务器配置
self.server_ip = "127.0.0.1"
self.server_port = 9943
self.streaming_port = 9944
# 有线连接设置
self.connection_mode = "wired" # wired, wireless
self.wired_port = 9945 # 有线连接端口
# 连接状态
self.connected = False
self.streaming = False
self.server_socket = None
self.streaming_socket = None
# 流媒体配置
self.stream_width = 2880 # 推荐分辨率
self.stream_height = 1700
self.stream_fps = 72
self.bitrate = 150 # Mbps
self.codec = "h264"
# 线程管理
self.connection_thread = None
self.streaming_thread = None
self.running = False
# 性能统计
self.frame_count = 0
self.last_fps_time = time.time()
self.current_fps = 0
self.latency = 0
print("✓ OpenXR串流处理器初始化完成")
def initialize(self):
"""初始化OpenXR串流"""
try:
# 检查串流服务器是否运行
if not self._check_streaming_server():
print("串流服务器未运行,尝试启动...")
if not self._start_streaming_server():
print("无法启动串流服务器")
return False
# 连接到串流服务器
if not self._connect_to_server():
print("无法连接到串流服务器")
return False
# 配置流媒体设置
self._configure_streaming()
# 启动串流线程
self._start_streaming_threads()
print("✓ OpenXR串流初始化成功")
return True
except Exception as e:
print(f"OpenXR串流初始化错误: {str(e)}")
return False
def _check_streaming_server(self):
"""检查串流服务器是否运行"""
try:
# TODO: 实际检查串流服务器状态
# 这里暂时返回False以便在开发阶段使用模拟模式
return False
except:
return False
def _start_streaming_server(self):
"""启动串流服务器"""
try:
# TODO: 实际启动串流服务器
print(" 串流服务器启动占位符")
return False
except Exception as e:
print(f"串流服务器启动失败: {str(e)}")
return False
def _connect_to_server(self):
"""连接到串流服务器"""
try:
# TODO: 实际连接到串流服务器
print(" 串流服务器连接占位符")
return False
except Exception as e:
print(f"串流服务器连接失败: {str(e)}")
return False
def _configure_streaming(self):
"""配置流媒体设置"""
# 根据OpenXR管理器设置配置串流参数
if self.openxr_manager:
render_size = self.openxr_manager.get_openxr_info()['render_size']
self.stream_width = render_size[0]
self.stream_height = render_size[1]
print(f" 串流配置: {self.stream_width}x{self.stream_height}@{self.stream_fps}fps")
def _start_streaming_threads(self):
"""启动串流线程"""
self.running = True
# 启动连接线程
self.connection_thread = threading.Thread(target=self._connection_handler, daemon=True)
self.connection_thread.start()
# 启动串流线程
self.streaming_thread = threading.Thread(target=self._streaming_handler, daemon=True)
self.streaming_thread.start()
def _connection_handler(self):
"""连接处理线程"""
while self.running:
try:
# TODO: 实现连接处理逻辑
time.sleep(1)
except Exception as e:
print(f"连接处理错误: {str(e)}")
def _streaming_handler(self):
"""串流处理线程"""
while self.running:
try:
if self.streaming:
self._send_video_frames()
time.sleep(1.0 / self.stream_fps)
except Exception as e:
print(f"串流处理错误: {str(e)}")
def _send_video_frames(self):
"""发送视频帧"""
try:
# 获取左右眼纹理
if self.openxr_manager and hasattr(self.openxr_manager, 'eye_textures'):
left_texture = self.openxr_manager.eye_textures.get('left')
right_texture = self.openxr_manager.eye_textures.get('right')
if left_texture and right_texture:
# TODO: 实现视频编码和发送逻辑
self.frame_count += 1
# 更新FPS统计
current_time = time.time()
if current_time - self.last_fps_time >= 1.0:
self.current_fps = self.frame_count
self.frame_count = 0
self.last_fps_time = current_time
# print(f"串流FPS: {self.current_fps}")
except Exception as e:
print(f"视频帧发送错误: {str(e)}")
def start_streaming(self):
"""开始OpenXR串流"""
if not self.connected:
print("串流服务器未连接")
return False
self.streaming = True
print("✓ OpenXR串流已开始")
return True
def stop_streaming(self):
"""停止OpenXR串流"""
self.streaming = False
print("✓ OpenXR串流已停止")
def get_streaming_status(self):
"""获取串流状态"""
return {
"connected": self.connected,
"streaming": self.streaming,
"fps": self.current_fps,
"latency": self.latency,
"resolution": f"{self.stream_width}x{self.stream_height}",
"bitrate": self.bitrate,
"codec": self.codec
}
def set_stream_quality(self, width, height, fps, bitrate):
"""设置串流质量"""
self.stream_width = width
self.stream_height = height
self.stream_fps = fps
self.bitrate = bitrate
print(f"✓ 串流质量已设置: {width}x{height}@{fps}fps, {bitrate}Mbps")
return True
def send_haptic_feedback(self, controller_id, duration, intensity):
"""发送触觉反馈"""
try:
# TODO: 实现触觉反馈发送逻辑
print(f" 触觉反馈发送占位符: 控制器{controller_id}, 持续时间{duration}s, 强度{intensity}")
return True
except Exception as e:
print(f"触觉反馈发送失败: {str(e)}")
return False
def shutdown(self):
"""关闭串流"""
self.running = False
self.stop_streaming()
# 等待线程结束
if self.connection_thread and self.connection_thread.is_alive():
self.connection_thread.join(timeout=2)
if self.streaming_thread and self.streaming_thread.is_alive():
self.streaming_thread.join(timeout=2)
# 关闭套接字
if self.server_socket:
self.server_socket.close()
if self.streaming_socket:
self.streaming_socket.close()
print("✓ OpenXR串流已关闭")
def is_connected(self):
"""检查是否连接"""
return self.connected
def is_streaming(self):
"""检查是否正在串流"""
return self.streaming

View File

@ -1,430 +0,0 @@
"""
VR输入处理器
处理VR控制器输入手势识别和VR交互
支持多种VR控制器和手势输入
"""
from direct.showbase.DirectObject import DirectObject
from panda3d.core import Vec3, Point3, CollisionRay, CollisionNode, CollisionHandlerQueue
from direct.task import Task
import time
class VRInputHandler(DirectObject):
"""VR输入处理器"""
def __init__(self, world, vr_manager):
super().__init__()
self.world = world
self.vr_manager = vr_manager
# 控制器状态
self.controllers = {}
self.controller_nodes = {}
self.controller_rays = {}
# 手势识别
self.gesture_enabled = True
self.gesture_history = []
self.gesture_threshold = 0.1
# 交互系统
self.interaction_enabled = True
self.selected_object = None
self.grab_offset = Vec3(0, 0, 0)
# 输入映射
self.input_mappings = {
'trigger': self._handle_trigger,
'grip': self._handle_grip,
'touchpad': self._handle_touchpad,
'menu': self._handle_menu,
'system': self._handle_system
}
print("✓ VR输入处理器初始化完成")
def start_input_handling(self):
"""启动输入处理"""
if not self.vr_manager.is_vr_enabled():
print("VR未启用无法启动输入处理")
return False
# 启动输入更新任务
self.world.taskMgr.add(self._update_input, "vr_input_update")
# 设置控制器可视化
self._setup_controller_visualization()
print("✓ VR输入处理已启动")
return True
def stop_input_handling(self):
"""停止输入处理"""
self.world.taskMgr.remove("vr_input_update")
self._cleanup_controller_visualization()
print("✓ VR输入处理已停止")
def _update_input(self, task):
"""更新输入处理"""
if not self.vr_manager.is_vr_enabled():
return Task.cont
try:
# 更新所有控制器
self._update_controllers()
# 处理手势识别
if self.gesture_enabled:
self._process_gestures()
# 处理交互
if self.interaction_enabled:
self._process_interactions()
except Exception as e:
print(f"VR输入更新错误: {str(e)}")
return Task.cont
def _update_controllers(self):
"""更新控制器状态"""
# 获取控制器姿态
controller_poses = self.vr_manager.controller_poses
for controller_id, pose in controller_poses.items():
# 获取控制器输入
input_data = self.vr_manager.get_controller_input(controller_id)
if not input_data:
continue
# 更新控制器状态
if controller_id not in self.controllers:
self.controllers[controller_id] = {}
prev_state = self.controllers[controller_id].copy()
self.controllers[controller_id] = input_data
# 更新控制器可视化
self._update_controller_visualization(controller_id, pose)
# 处理输入事件
self._process_controller_input(controller_id, input_data, prev_state)
def _process_controller_input(self, controller_id, current_state, prev_state):
"""处理控制器输入"""
# 检查按钮状态变化
for input_type, handler in self.input_mappings.items():
if input_type in current_state:
current_value = current_state[input_type]
prev_value = prev_state.get(input_type, 0)
# 处理按钮按下/释放
if isinstance(current_value, (int, float)):
if current_value > 0.5 and prev_value <= 0.5:
handler(controller_id, 'press', current_value)
elif current_value <= 0.5 and prev_value > 0.5:
handler(controller_id, 'release', current_value)
elif current_value > 0.5:
handler(controller_id, 'hold', current_value)
# 处理触摸板
elif isinstance(current_value, tuple) and len(current_value) == 2:
if current_value != prev_value:
handler(controller_id, 'move', current_value)
def _handle_trigger(self, controller_id, action, value):
"""处理扳机输入"""
if action == 'press':
print(f"控制器 {controller_id} 扳机按下 (强度: {value:.2f})")
self._try_grab_object(controller_id)
elif action == 'release':
print(f"控制器 {controller_id} 扳机释放")
self._try_release_object(controller_id)
def _handle_grip(self, controller_id, action, value):
"""处理握持输入"""
if action == 'press':
print(f"控制器 {controller_id} 握持按下 (强度: {value:.2f})")
self._toggle_interaction_mode(controller_id)
elif action == 'release':
print(f"控制器 {controller_id} 握持释放")
def _handle_touchpad(self, controller_id, action, value):
"""处理触摸板输入"""
if action == 'move':
x, y = value
print(f"控制器 {controller_id} 触摸板: ({x:.2f}, {y:.2f})")
# 根据触摸板位置执行不同操作
if abs(x) > 0.7: # 左右滑动
self._handle_horizontal_swipe(controller_id, x)
elif abs(y) > 0.7: # 上下滑动
self._handle_vertical_swipe(controller_id, y)
def _handle_menu(self, controller_id, action, value):
"""处理菜单按钮"""
if action == 'press':
print(f"控制器 {controller_id} 菜单按钮按下")
self._show_vr_menu(controller_id)
def _handle_system(self, controller_id, action, value):
"""处理系统按钮"""
if action == 'press':
print(f"控制器 {controller_id} 系统按钮按下")
# 系统按钮通常由VR系统处理
def _handle_horizontal_swipe(self, controller_id, direction):
"""处理水平滑动"""
if direction > 0:
print(f"控制器 {controller_id} 右滑")
self._switch_tool(controller_id, 'next')
else:
print(f"控制器 {controller_id} 左滑")
self._switch_tool(controller_id, 'prev')
def _handle_vertical_swipe(self, controller_id, direction):
"""处理垂直滑动"""
if direction > 0:
print(f"控制器 {controller_id} 上滑")
self._zoom_in(controller_id)
else:
print(f"控制器 {controller_id} 下滑")
self._zoom_out(controller_id)
def _try_grab_object(self, controller_id):
"""尝试抓取对象"""
if controller_id not in self.controllers:
return
# 获取控制器射线
ray = self._get_controller_ray(controller_id)
if not ray:
return
# 执行射线检测
hit_object = self._raycast_from_controller(controller_id)
if hit_object:
self.selected_object = hit_object
controller_pose = self.controllers[controller_id].get('pose')
if controller_pose:
# 计算抓取偏移
object_pos = hit_object.getPos()
controller_pos = controller_pose.getTranslate()
self.grab_offset = object_pos - controller_pos
print(f"抓取对象: {hit_object.getName()}")
# 发送抓取事件
self.world.event_handler.messenger.send('vr-object-grabbed', [hit_object, controller_id])
def _try_release_object(self, controller_id):
"""尝试释放对象"""
if self.selected_object:
print(f"释放对象: {self.selected_object.getName()}")
# 发送释放事件
self.world.event_handler.messenger.send('vr-object-released', [self.selected_object, controller_id])
self.selected_object = None
self.grab_offset = Vec3(0, 0, 0)
def _raycast_from_controller(self, controller_id):
"""从控制器发射射线检测"""
if controller_id not in self.controllers:
return None
controller_pose = self.controllers[controller_id].get('pose')
if not controller_pose:
return None
# 获取控制器位置和方向
controller_pos = controller_pose.getTranslate()
controller_forward = controller_pose.getQuat().getForward()
# 创建射线
ray = CollisionRay()
ray.setOrigin(controller_pos)
ray.setDirection(controller_forward)
# 执行碰撞检测
traverser = self.world.cTrav if hasattr(self.world, 'cTrav') else None
if not traverser:
return None
handler = CollisionHandlerQueue()
collision_node = CollisionNode('vr_controller_ray')
collision_node.addSolid(ray)
ray_np = self.world.render.attachNewNode(collision_node)
traverser.addCollider(ray_np, handler)
# 遍历碰撞
traverser.traverse(self.world.render)
# 清理
ray_np.removeNode()
# 返回最近的碰撞对象
if handler.getNumEntries() > 0:
handler.sortEntries()
entry = handler.getEntry(0)
return entry.getIntoNodePath()
return None
def _get_controller_ray(self, controller_id):
"""获取控制器射线"""
return self.controller_rays.get(controller_id)
def _setup_controller_visualization(self):
"""设置控制器可视化"""
print("设置控制器可视化...")
# 为每个控制器创建可视化节点
for controller_id in self.controllers:
self._create_controller_model(controller_id)
def _create_controller_model(self, controller_id):
"""创建控制器模型"""
# 创建简单的控制器模型(立方体)
from panda3d.core import CardMaker
cm = CardMaker(f"controller_{controller_id}")
cm.setFrame(-0.05, 0.05, -0.05, 0.05)
controller_node = self.world.render.attachNewNode(cm.generate())
controller_node.setColor(0.2, 0.8, 1.0, 0.8)
controller_node.setScale(0.1, 0.2, 0.05)
self.controller_nodes[controller_id] = controller_node
# 创建控制器射线可视化
self._create_controller_ray_visual(controller_id)
def _create_controller_ray_visual(self, controller_id):
"""创建控制器射线可视化"""
from panda3d.core import LineSegs
# 创建射线线段
lines = LineSegs()
lines.setColor(1, 0, 0, 0.5)
lines.moveTo(0, 0, 0)
lines.drawTo(0, 2, 0) # 2米长的射线
ray_node = self.world.render.attachNewNode(lines.create())
ray_node.setRenderModeWireframe()
ray_node.hide() # 默认隐藏
self.controller_rays[controller_id] = ray_node
def _update_controller_visualization(self, controller_id, pose):
"""更新控制器可视化"""
if controller_id in self.controller_nodes:
node = self.controller_nodes[controller_id]
node.setMat(pose)
if controller_id in self.controller_rays:
ray_node = self.controller_rays[controller_id]
ray_node.setMat(pose)
def _cleanup_controller_visualization(self):
"""清理控制器可视化"""
for node in self.controller_nodes.values():
node.removeNode()
for ray in self.controller_rays.values():
ray.removeNode()
self.controller_nodes.clear()
self.controller_rays.clear()
def _process_gestures(self):
"""处理手势识别"""
# 简单的手势识别逻辑
# 这里可以实现更复杂的手势识别算法
pass
def _process_interactions(self):
"""处理交互逻辑"""
# 如果有选中的对象,更新其位置
if self.selected_object:
self._update_grabbed_object()
def _update_grabbed_object(self):
"""更新被抓取对象的位置"""
if not self.selected_object:
return
# 找到抓取该对象的控制器
grabbing_controller = None
for controller_id, controller_state in self.controllers.items():
if controller_state.get('trigger', 0) > 0.5:
grabbing_controller = controller_id
break
if not grabbing_controller:
return
# 更新对象位置
controller_pose = self.controllers[grabbing_controller].get('pose')
if controller_pose:
controller_pos = controller_pose.getTranslate()
new_pos = controller_pos + self.grab_offset
self.selected_object.setPos(new_pos)
def _toggle_interaction_mode(self, controller_id):
"""切换交互模式"""
self.interaction_enabled = not self.interaction_enabled
print(f"交互模式: {'启用' if self.interaction_enabled else '禁用'}")
def _show_vr_menu(self, controller_id):
"""显示VR菜单"""
print(f"显示VR菜单 (控制器 {controller_id})")
# 这里可以实现VR菜单显示逻辑
pass
def _switch_tool(self, controller_id, direction):
"""切换工具"""
print(f"切换工具: {direction} (控制器 {controller_id})")
# 这里可以实现工具切换逻辑
pass
def _zoom_in(self, controller_id):
"""放大"""
print(f"放大 (控制器 {controller_id})")
# 实现放大逻辑
pass
def _zoom_out(self, controller_id):
"""缩小"""
print(f"缩小 (控制器 {controller_id})")
# 实现缩小逻辑
pass
def show_controller_rays(self, show=True):
"""显示/隐藏控制器射线"""
for ray in self.controller_rays.values():
if show:
ray.show()
else:
ray.hide()
def get_controller_state(self, controller_id):
"""获取控制器状态"""
return self.controllers.get(controller_id, {})
def get_all_controllers(self):
"""获取所有控制器"""
return list(self.controllers.keys())
def set_gesture_enabled(self, enabled):
"""设置手势识别启用状态"""
self.gesture_enabled = enabled
print(f"手势识别: {'启用' if enabled else '禁用'}")
def set_interaction_enabled(self, enabled):
"""设置交互启用状态"""
self.interaction_enabled = enabled
print(f"VR交互: {'启用' if enabled else '禁用'}")

View File

@ -1,666 +0,0 @@
import warnings
warnings.filterwarnings("ignore", category=DeprecationWarning)
from panda3d.core import *
from panda3d.core import Mat4, PerspectiveLens, CardMaker
# 修复taskMgr导入错误应该从TaskManagerGlobal导入而不是ShowBaseGlobal
from direct.task.TaskManagerGlobal import taskMgr
from direct.showbase.DirectObject import DirectObject
from direct.task import Task
import sys
class VRManager(DirectObject):
"""VR管理器 - 处理VR系统初始化、追踪和渲染"""
def __init__(self, world):
super().__init__()
self.world = world
self.vr_enabled = False
self.vr_system = None
self.vr_compositor = None
self.render_width = 1920
self.render_height = 1080
self.alvr_enabled = False
# 模拟模式设置
self.simulation_mode = False
self.simulation_data = {
'head_pose': {'position': [0, 0, 1.6], 'rotation': [0, 0, 0, 1]},
'controller_poses': {
0: {'position': [-0.3, 0, 1.2], 'rotation': [0, 0, 0, 1], 'connected': True},
1: {'position': [0.3, 0, 1.2], 'rotation': [0, 0, 0, 1], 'connected': True}
},
'render_size': (1920, 1080)
}
# 立体渲染缓冲区
self.left_eye_buffer = None
self.right_eye_buffer = None
self.left_eye_camera = None
self.right_eye_camera = None
# 眼部纹理用于ALVR串流
self.left_eye_texture = None
self.right_eye_texture = None
self.eye_textures = {}
# 控制器相关
self.controller_nodes = {}
self.controller_poses = {}
print("✓ VR管理器初始化完成")
def initialize_vr(self, force_simulation=False):
"""初始化VR系统"""
try:
# 检查是否强制使用模拟模式
if force_simulation:
print("🔧 强制启用VR模拟模式")
return self._init_simulation_mode()
# 检查OpenVR支持
if not self._check_openvr_support():
print("⚠ OpenVR支持不可用切换到模拟模式")
return self._init_simulation_mode()
# 尝试初始化OpenVR
if not self._init_openvr():
print("⚠ OpenVR初始化失败切换到模拟模式")
print("提示: 请确保SteamVR正在运行且VR头盔已连接")
return self._init_simulation_mode()
# 真实VR模式初始化成功
print("✓ 真实VR模式初始化成功")
return self._init_real_vr_mode()
except Exception as e:
print(f"VR初始化错误: {str(e)}")
print("⚠ 切换到模拟模式")
return self._init_simulation_mode()
def _init_simulation_mode(self):
"""初始化模拟模式"""
try:
self.simulation_mode = True
# 使用模拟数据设置渲染尺寸
self.render_width, self.render_height = self.simulation_data['render_size']
print(f"🎮 模拟VR渲染尺寸: {self.render_width}x{self.render_height}")
# 设置模拟立体渲染
self._setup_stereo_rendering()
# 启动模拟VR任务
self._start_simulation_tasks()
self.vr_enabled = True
print("✓ VR模拟模式初始化完成")
print(" 模拟模式说明:")
print(" - 头盔追踪: 模拟数据")
print(" - 控制器: 模拟两个控制器")
print(" - 渲染: 立体渲染到窗口")
print(" - 交互: 键盘鼠标模拟")
return True
except Exception as e:
print(f"模拟模式初始化错误: {str(e)}")
return False
def _init_real_vr_mode(self):
"""初始化真实VR模式"""
try:
self.simulation_mode = False
# 设置立体渲染
self._setup_stereo_rendering()
# 初始化ALVR
if self._init_alvr():
print("✓ ALVR串流已启用")
self.alvr_enabled = True
# 启动VR更新任务
self._start_vr_tasks()
self.vr_enabled = True
print("✓ 真实VR系统初始化完成")
return True
except Exception as e:
print(f"真实VR模式初始化错误: {str(e)}")
return False
def _check_openvr_support(self):
"""检查OpenVR支持"""
try:
import openvr
return True
except ImportError:
print("OpenVR库未安装")
return False
def _init_openvr(self):
"""初始化OpenVR"""
try:
import openvr
# 初始化OpenVR
self.vr_system = openvr.init(openvr.VRApplication_Scene)
if not self.vr_system:
return False
# 获取合成器
self.vr_compositor = openvr.VRCompositor()
if not self.vr_compositor:
return False
# 获取推荐的渲染尺寸
self.render_width, self.render_height = self.vr_system.getRecommendedRenderTargetSize()
print(f"VR推荐渲染尺寸: {self.render_width}x{self.render_height}")
return True
except Exception as e:
print(f"OpenVR初始化错误: {str(e)}")
return False
def _setup_stereo_rendering(self):
"""设置立体渲染"""
try:
# 创建眼部缓冲区
self._create_eye_buffers()
# 创建眼部摄像机
self._create_eye_cameras()
# 设置渲染目标
self._setup_render_targets()
print("✓ 立体渲染设置完成")
except Exception as e:
print(f"立体渲染设置错误: {str(e)}")
def _create_eye_buffers(self):
"""创建眼部渲染缓冲区"""
try:
# 创建左眼缓冲区
self.left_eye_buffer = self.world.win.makeTextureBuffer(
"left_eye", self.render_width, self.render_height
)
self.left_eye_buffer.setSort(-100)
# 创建右眼缓冲区
self.right_eye_buffer = self.world.win.makeTextureBuffer(
"right_eye", self.render_width, self.render_height
)
self.right_eye_buffer.setSort(-99)
# 获取纹理并确保它们可以被访问
self.left_eye_texture = self.left_eye_buffer.getTexture()
self.right_eye_texture = self.right_eye_buffer.getTexture()
# 设置纹理参数以确保它们可以被ALVR访问
if self.left_eye_texture:
self.left_eye_texture.set_format(Texture.F_rgba8)
self.left_eye_texture.set_component_type(Texture.T_unsigned_byte)
self.left_eye_texture.set_wrap_u(Texture.WM_clamp)
self.left_eye_texture.set_wrap_v(Texture.WM_clamp)
# 确保纹理数据可以被读取到RAM使用正确的方法
try:
self.left_eye_texture.set_ram_image_compression(Texture.CM_off)
except AttributeError:
# 如果方法不存在,使用替代方法
pass
if self.right_eye_texture:
self.right_eye_texture.set_format(Texture.F_rgba8)
self.right_eye_texture.set_component_type(Texture.T_unsigned_byte)
self.right_eye_texture.set_wrap_u(Texture.WM_clamp)
self.right_eye_texture.set_wrap_v(Texture.WM_clamp)
# 确保纹理数据可以被读取到RAM使用正确的方法
try:
self.right_eye_texture.set_ram_image_compression(Texture.CM_off)
except AttributeError:
# 如果方法不存在,使用替代方法
pass
# 存储到eye_textures字典中供ALVR使用
self.eye_textures['left'] = self.left_eye_texture
self.eye_textures['right'] = self.right_eye_texture
print("✓ 眼部渲染缓冲区创建完成")
print(f" 左眼纹理: {self.left_eye_texture.getXSize()}x{self.left_eye_texture.getYSize()}")
print(f" 右眼纹理: {self.right_eye_texture.getXSize()}x{self.right_eye_texture.getYSize()}")
except Exception as e:
print(f"眼部缓冲区创建错误: {str(e)}")
import traceback
traceback.print_exc()
def _create_eye_cameras(self):
"""创建眼部摄像机"""
try:
# 创建左眼摄像机
self.left_eye_camera = self.world.makeCamera(self.left_eye_buffer)
self.left_eye_camera.setPos(-0.032, 0, 0) # 瞳距的一半
# 创建右眼摄像机
self.right_eye_camera = self.world.makeCamera(self.right_eye_buffer)
self.right_eye_camera.setPos(0.032, 0, 0) # 瞳距的一半
# 设置投影矩阵
if not self.simulation_mode:
self._update_eye_projection(0, self.left_eye_camera.node().getLens())
self._update_eye_projection(1, self.right_eye_camera.node().getLens())
else:
# 模拟模式使用标准透视投影
lens = PerspectiveLens()
lens.setFov(110) # 模拟VR FOV
lens.setNearFar(0.1, 1000)
self.left_eye_camera.node().setLens(lens)
self.right_eye_camera.node().setLens(lens)
print("✓ 眼部摄像机创建完成")
except Exception as e:
print(f"眼部摄像机创建错误: {str(e)}")
def _update_eye_projection(self, eye, lens):
"""更新眼部投影矩阵"""
try:
if self.simulation_mode:
# 模拟模式使用标准投影
perspective_lens = PerspectiveLens()
perspective_lens.setFov(110)
perspective_lens.setNearFar(0.1, 1000)
lens.copyFrom(perspective_lens)
return
import openvr
# 获取投影矩阵
projection_matrix = self.vr_system.getProjectionMatrix(eye, 0.1, 1000.0)
# 转换为 Panda3D 矩阵
panda_matrix = self._convert_openvr_matrix(projection_matrix)
# 使用正确的方法设置自定义投影矩阵
try:
lens.set_user_mat(panda_matrix)
except AttributeError:
# 如果方法不存在,尝试替代方法
try:
lens.set_projection_mat(panda_matrix)
except AttributeError:
# 兜底:设置标准 FOV
lens.setFov(110)
lens.setNearFar(0.1, 1000)
except Exception as e:
print(f"眼部投影更新错误: {str(e)}")
def _setup_render_targets(self):
"""设置渲染目标"""
try:
# 在模拟模式下,可以选择将渲染结果显示到主窗口
if self.simulation_mode:
# 创建并排显示的立体视图
self._setup_simulation_display()
print("✓ 渲染目标设置完成")
except Exception as e:
print(f"渲染目标设置错误: {str(e)}")
def _setup_simulation_display(self):
"""设置模拟显示"""
try:
# 创建卡片来显示眼部纹理
cm = CardMaker("stereo_display")
# 左眼显示区域
cm.setFrame(-1, 0, -1, 1)
left_card = self.world.render2d.attachNewNode(cm.generate())
left_card.setTexture(self.left_eye_texture)
# 右眼显示区域
cm.setFrame(0, 1, -1, 1)
right_card = self.world.render2d.attachNewNode(cm.generate())
right_card.setTexture(self.right_eye_texture)
print("✓ 模拟立体显示设置完成")
except Exception as e:
print(f"模拟显示设置错误: {str(e)}")
def _init_alvr(self):
"""初始化ALVR仅在真实VR模式下"""
if self.simulation_mode:
print(" 模拟模式: ALVR串流已跳过")
return False
try:
# ALVR初始化逻辑
# 这里应该连接到ALVR服务器
print("✓ ALVR初始化完成")
return True
except Exception as e:
print(f"ALVR初始化错误: {str(e)}")
return False
def _start_vr_tasks(self):
"""启动VR更新任务真实VR模式"""
if not self.simulation_mode:
self.world.taskMgr.add(self._update_vr_tracking, "vr_tracking")
self.world.taskMgr.add(self._update_vr_rendering, "vr_rendering")
def _start_simulation_tasks(self):
"""启动模拟VR任务"""
self.world.taskMgr.add(self._update_simulation_tracking, "simulation_tracking")
self.world.taskMgr.add(self._update_simulation_rendering, "simulation_rendering")
def _update_simulation_tracking(self, task):
"""更新模拟追踪数据"""
try:
# 模拟头部追踪(可以添加一些变化)
import math
time_factor = task.time * 0.5
# 模拟轻微的头部摆动
head_pos = self.simulation_data['head_pose']['position']
head_pos[1] = math.sin(time_factor) * 0.05 # 前后轻微摆动
# 更新主摄像机位置
if hasattr(self.world, 'camera'):
self.world.camera.setPos(head_pos[0], head_pos[1], head_pos[2])
# 更新控制器位置(模拟手部动作)
for controller_id, pose in self.simulation_data['controller_poses'].items():
if pose['connected']:
# 模拟控制器轻微移动
pose['position'][1] = math.sin(time_factor + controller_id) * 0.1
# 更新控制器节点位置
if controller_id in self.controller_nodes:
node = self.controller_nodes[controller_id]
node.setPos(pose['position'][0], pose['position'][1], pose['position'][2])
return task.cont
except Exception as e:
print(f"模拟追踪更新错误: {str(e)}")
return task.cont
def _update_simulation_rendering(self, task):
"""更新模拟渲染"""
try:
# 在模拟模式下渲染已经由Panda3D自动处理
# 这里可以添加任何特殊的渲染逻辑
return task.cont
except Exception as e:
print(f"模拟渲染更新错误: {str(e)}")
return task.cont
def _update_vr_tracking(self, task):
"""更新VR追踪数据真实VR模式"""
try:
if not self.vr_system or self.simulation_mode:
return task.cont
import openvr
# 获取设备姿态
poses, game_poses = self.vr_compositor.waitGetPoses(None, None)
# 检查poses是否有效
if poses is None:
print("VR追踪更新错误: 无法获取设备姿态数据")
return task.cont
# 更新头显位置
if poses[openvr.k_unTrackedDeviceIndex_Hmd].bPoseIsValid:
self._update_main_camera_pose()
# 更新眼部摄像机
self._update_eye_cameras()
# 更新控制器姿态
self._update_controller_poses(poses)
return task.cont
except Exception as e:
print(f"VR追踪更新错误: {str(e)}")
return task.cont
def _update_vr_rendering(self, task):
"""更新VR渲染真实VR模式"""
try:
if not self.vr_compositor or self.simulation_mode:
return task.cont
# 确保渲染完成后再提交帧
if hasattr(self.world, 'graphicsEngine'):
self.world.graphicsEngine.render_frame()
# 提交帧到合成器
self._submit_frames_to_compositor()
return task.cont
except Exception as e:
print(f"VR渲染更新错误: {str(e)}")
return task.cont
def _convert_openvr_matrix(self, openvr_matrix):
"""转换OpenVR矩阵为Panda3D矩阵"""
# 实现矩阵转换逻辑
mat = Mat4()
# 这里需要实现具体的矩阵转换
return mat
def _update_main_camera_pose(self):
"""更新主摄像机姿态"""
try:
if self.simulation_mode:
return
# 从VR系统获取头显姿态并应用到主摄像机
pass
except Exception as e:
print(f"主摄像机姿态更新错误: {str(e)}")
def _update_eye_cameras(self):
"""更新眼部摄像机"""
try:
if self.simulation_mode:
return
# 更新左右眼摄像机位置
pass
except Exception as e:
print(f"眼部摄像机更新错误: {str(e)}")
def _update_controller_poses(self, poses):
"""更新控制器姿态"""
try:
if self.simulation_mode:
return
# 更新控制器位置和姿态
pass
except Exception as e:
print(f"控制器姿态更新错误: {str(e)}")
def _submit_frames_to_compositor(self):
"""提交帧到合成器"""
try:
if not self.vr_compositor or self.simulation_mode:
return
import openvr
# 确保纹理已正确创建并有效
if not self.left_eye_texture or not self.right_eye_texture:
print("⚠ 眼部纹理未正确创建,无法提交帧")
return
# 检查纹理是否准备好
if not self.left_eye_texture.has_ram_image() or not self.right_eye_texture.has_ram_image():
# 强制更新纹理数据
try:
self.world.graphicsEngine.extract_texture_data(self.left_eye_texture, self.world.win.get_gsg())
self.world.graphicsEngine.extract_texture_data(self.right_eye_texture, self.world.win.get_gsg())
except Exception as e:
print(f"纹理数据提取失败: {str(e)}")
# 提交左眼纹理
left_eye_texture = openvr.Texture_t()
# 使用正确的API获取纹理ID
try:
tex_handle = self.left_eye_texture.get_handle()
except:
# 如果get_handle方法不可用尝试其他方式
tex_handle = self.left_eye_texture.get_texture_id() if hasattr(self.left_eye_texture, 'get_texture_id') else 0
left_eye_texture.handle = tex_handle
left_eye_texture.eType = openvr.TextureType_OpenGL
left_eye_texture.eColorSpace = openvr.ColorSpace_Gamma
# 检查纹理是否有效后再提交
if tex_handle:
self.vr_compositor.submit(openvr.Eye_Left, left_eye_texture)
# 提交右眼纹理
right_eye_texture = openvr.Texture_t()
# 使用正确的API获取纹理ID
try:
tex_handle = self.right_eye_texture.get_handle()
except:
# 如果get_handle方法不可用尝试其他方式
tex_handle = self.right_eye_texture.get_texture_id() if hasattr(self.right_eye_texture, 'get_texture_id') else 0
right_eye_texture.handle = tex_handle
right_eye_texture.eType = openvr.TextureType_OpenGL
right_eye_texture.eColorSpace = openvr.ColorSpace_Gamma
# 检查纹理是否有效后再提交
if tex_handle:
self.vr_compositor.submit(openvr.Eye_Right, right_eye_texture)
# 等待下一帧垂直同步
self.vr_compositor.waitGetPoses(None, None)
except Exception as e:
print(f"帧提交错误: {str(e)}")
import traceback
traceback.print_exc()
def get_controller_input(self, controller_id):
"""获取控制器输入"""
try:
if self.simulation_mode:
# 返回模拟的控制器输入
return {
'trigger': 0.0,
'grip': 0.0,
'touchpad': {'x': 0.0, 'y': 0.0, 'pressed': False},
'menu': False,
'connected': controller_id in self.simulation_data['controller_poses']
}
if not self.vr_system:
return None
import openvr
# 获取控制器状态
result, controller_state = self.vr_system.getControllerState(controller_id)
if not result:
return None
# 解析控制器输入
return {
'trigger': controller_state.rAxis[1].x,
'grip': controller_state.rAxis[2].x,
'touchpad': {
'x': controller_state.rAxis[0].x,
'y': controller_state.rAxis[0].y,
'pressed': controller_state.rAxis[0].x != 0 or controller_state.rAxis[0].y != 0
},
'menu': controller_state.ulButtonPressed & (1 << openvr.k_EButton_ApplicationMenu) != 0,
'connected': True
}
except Exception as e:
print(f"控制器输入获取错误: {str(e)}")
return None
def shutdown_vr(self):
"""关闭VR系统"""
try:
# 停止任务
if self.simulation_mode:
self.world.taskMgr.remove("simulation_tracking")
self.world.taskMgr.remove("simulation_rendering")
else:
self.world.taskMgr.remove("vr_tracking")
self.world.taskMgr.remove("vr_rendering")
# 关闭OpenVR
if self.vr_system and not self.simulation_mode:
import openvr
openvr.shutdown()
# 清理资源
self.left_eye_buffer = None
self.right_eye_buffer = None
self.left_eye_camera = None
self.right_eye_camera = None
self.vr_enabled = False
self.simulation_mode = False
print("✓ VR系统已关闭")
except Exception as e:
print(f"VR关闭错误: {str(e)}")
def is_vr_enabled(self):
"""检查VR是否启用"""
return self.vr_enabled
def get_vr_info(self):
"""获取VR系统信息"""
info = {
'enabled': self.vr_enabled,
'simulation_mode': self.simulation_mode,
'render_size': (self.render_width, self.render_height),
'alvr_enabled': self.alvr_enabled
}
if self.simulation_mode:
info['mode'] = 'simulation'
info['controllers'] = len(self.simulation_data['controller_poses'])
else:
info['mode'] = 'real_vr'
info['openvr_connected'] = self.vr_system is not None
return info
# 便捷方法
def enable_simulation_mode(self):
"""启用模拟模式(调试用)"""
return self.initialize_vr(force_simulation=True)
def get_simulation_data(self):
"""获取模拟数据"""
return self.simulation_data if self.simulation_mode else None
def update_simulation_data(self, key, value):
"""更新模拟数据"""
if self.simulation_mode and key in self.simulation_data:
self.simulation_data[key] = value
return True
return False

View File

@ -0,0 +1,346 @@
# OpenXR 实现指南
## 🎯 概述
本指南详细介绍了如何在Panda3D引擎中实现OpenXR支持并通过有线连接串流到VR设备。
## 🏗️ 系统架构
### 核心组件
1. **OpenXRManager** (`core/openxr_manager.py`)
- 负责OpenXR系统初始化
- 管理立体渲染(左右眼)
- 处理VR设备跟踪
- 集成OpenXR接口
2. **OpenXRInputHandler** (`core/openxr_input_handler.py`)
- 处理OpenXR控制器输入
- 手势识别系统
- 交互逻辑处理
3. **OpenXRStreamer** (`core/openxr_streamer.py`)
- OpenXR服务器通信
- 视频流编码和传输
- 性能监控
## 🔧 安装和配置
### 1. 系统依赖
```bash
# 安装Python依赖
pip install -r requirements/openxr-requirements.txt
# 安装OpenXR运行时
# Windows:
# 1. 下载并安装SteamVR或Oculus运行时
# 2. 安装OpenXR运行时
# Linux:
sudo apt-get install openxr-loader
```
### 2. 有线连接设置
```bash
# 配置网络以支持有线连接
# 1. 使用USB网络适配器或以太网连接VR设备
# 2. 确保PC和VR设备在同一子网
# 3. 配置防火墙允许OpenXR通信端口9943-9945
```
### 3. VR设备配置
```bash
# 1. 在VR设备上安装OpenXR客户端应用
# 2. 通过有线连接设备到PC
# 3. 启用开发者模式(如需要)
```
## 🚀 使用方法
### 快速启动
```python
# 在主程序中
world = MyWorld()
# 一键启用OpenXR模式
if world.enableOpenXRMode():
print("OpenXR模式已启用")
# 检查OpenXR状态
status = world.getOpenXRStatus()
print(f"OpenXR设备: {status['openxr_info']}")
print(f"连接状态: {status['openxr_connected']}")
print(f"串流状态: {status['openxr_streaming']}")
```
### 详细控制
```python
# 分步骤启动OpenXR
world = MyWorld()
# 1. 初始化OpenXR系统
if world.initializeOpenXR():
print("OpenXR系统初始化成功")
# 2. 启动OpenXR输入处理
world.startOpenXRInput()
# 3. 显示控制器射线
world.showControllerRays(True)
# 4. 初始化OpenXR串流
if world.initializeOpenXRStreaming():
print("OpenXR串流初始化成功")
# 5. 设置串流质量
world.setOpenXRStreamQuality(
width=2880,
height=1700,
fps=72,
bitrate=150
)
# 6. 开始串流
world.startOpenXRStreaming()
```
## 🎮 OpenXR交互功能
### 控制器输入处理
```python
# 获取控制器状态
controllers = world.getAllControllers()
for controller_id in controllers:
state = world.getControllerState(controller_id)
# 检查触发器按下
if state['trigger'] > 0.5:
print(f"控制器 {controller_id} 触发器按下")
# 检查摇杆输入
thumbstick_x, thumbstick_y = state['thumbstick']['x'], state['thumbstick']['y']
if abs(thumbstick_x) > 0.5:
print(f"摇杆水平滑动: {thumbstick_x}")
```
### 对象抓取和操作
```python
# OpenXR输入处理器自动处理对象抓取
# 触发器按下时自动检测和抓取对象
# 触发器释放时自动释放对象
# 监听OpenXR事件
world.event_handler.accept('openxr-object-grabbed', onObjectGrabbed)
world.event_handler.accept('openxr-object-released', onObjectReleased)
def onObjectGrabbed(object_node, controller_id):
print(f"抓取对象: {object_node.getName()}")
def onObjectReleased(object_node, controller_id):
print(f"释放对象: {object_node.getName()}")
```
### 触觉反馈
```python
# 发送触觉反馈
world.sendHapticFeedback(
controller_id=0,
duration=0.5, # 持续时间(秒)
intensity=0.8 # 强度0-1
)
```
## 📊 性能优化
### 渲染优化
```python
# 调整OpenXR渲染质量
openxr_manager = world.openxr_manager
openxr_manager.render_scale = 1.0 # 渲染缩放 (0.5-2.0)
# 设置多重采样抗锯齿
fb_props = FrameBufferProperties()
fb_props.setMultisamples(4) # 4x MSAA
```
### 网络优化
```python
# 优化OpenXR串流参数
world.setOpenXRStreamQuality(
width=2160, # 降低分辨率提高性能
height=1200,
fps=60, # 降低帧率减少延迟
bitrate=100 # 降低比特率适应网络带宽
)
```
### 系统资源监控
```python
# 获取性能状态
streaming_status = world.getOpenXRStreamingStatus()
print(f"串流FPS: {streaming_status['fps']}")
print(f"延迟: {streaming_status['latency']} ms")
print(f"分辨率: {streaming_status['resolution']}")
```
## 🔍 故障排除
### 常见问题
1. **OpenXR系统初始化失败**
```
错误: OpenXR初始化失败
解决: 确保OpenXR运行时已安装并运行
```
2. **串流连接失败**
```
错误: 无法连接到串流服务器
解决:
- 检查串流服务器是否运行
- 确认防火墙设置
- 检查网络连接
```
3. **串流质量问题**
```
问题: 画面卡顿或延迟高
解决:
- 降低分辨率和帧率
- 检查网络带宽
- 调整比特率设置
```
### 调试命令
```python
# 启用调试模式
world.openxr_manager.debug_mode = True
# 获取详细状态信息
openxr_info = world.getOpenXRInfo()
print(f"OpenXR设备信息: {openxr_info}")
# 检查网络连接
if world.isOpenXRConnected():
print("OpenXR连接正常")
else:
print("OpenXR连接失败")
```
## 🎯 高级功能
### 自定义OpenXR交互
```python
# 创建自定义OpenXR交互逻辑
class CustomOpenXRInteraction:
def __init__(self, world):
self.world = world
def handleCustomGesture(self, gesture_data):
# 处理自定义手势
pass
def createOpenXRMenu(self, controller_id):
# 在VR中创建3D菜单
pass
```
### 多用户OpenXR支持
```python
# 支持多个OpenXR用户
class MultiUserOpenXRManager:
def __init__(self):
self.openxr_users = {}
def addOpenXRUser(self, user_id, openxr_manager):
self.openxr_users[user_id] = openxr_manager
def syncOpenXRUsers(self):
# 同步多用户OpenXR状态
pass
```
## 📋 配置文件
### OpenXR配置 (`config/openxr_config.yaml`)
```yaml
openxr:
enabled: true
render_scale: 1.0
tracking_space: "stage"
streaming:
server_ip: "127.0.0.1"
server_port: 9943
streaming_port: 9944
video:
width: 2880
height: 1700
fps: 72
bitrate: 150
codec: "h264"
audio:
enabled: true
sample_rate: 48000
channels: 2
```
## 🚀 部署建议
### 开发环境
```bash
# 创建OpenXR开发环境
python -m venv openxr_env
source openxr_env/bin/activate
pip install -r requirements/openxr-requirements.txt
# 启动开发服务器
python main.py --openxr-mode
```
### 生产环境
```bash
# 优化生产部署
# 1. 使用专用计算机处理OpenXR渲染
# 2. 配置高性能有线网络连接
# 3. 启用GPU加速
# 4. 监控系统性能
```
## 📚 参考资料
- [OpenXR规范](https://www.khronos.org/openxr/)
- [Panda3D文档](https://docs.panda3d.org/)
- [OpenXR Cookbook](https://github.com/KhronosGroup/OpenXR-Docs)
## 🤝 贡献指南
欢迎贡献代码和改进建议!请遵循以下步骤:
1. Fork此仓库
2. 创建功能分支
3. 提交代码修改
4. 创建Pull Request
## 📄 许可证
本项目采用MIT许可证。详见LICENSE文件。

View File

@ -1,374 +0,0 @@
# VR + ALVR 串流实现指南
## 🎯 概述
本指南详细介绍了如何在Panda3D引擎中实现VR支持并通过ALVR串流到Quest等VR头显设备。
## 🏗️ 系统架构
### 核心组件
1. **VRManager** (`core/vr_manager.py`)
- 负责VR系统初始化
- 管理立体渲染(左右眼)
- 处理VR设备跟踪
- 集成OpenVR接口
2. **VRInputHandler** (`core/vr_input_handler.py`)
- 处理VR控制器输入
- 手势识别系统
- 交互逻辑处理
3. **ALVRStreamer** (`core/alvr_streamer.py`)
- ALVR服务器通信
- 视频流编码和传输
- 性能监控
4. **VRControlPanel** (`ui/vr_control_panel.py`)
- VR系统GUI控制界面
- 实时状态监控
- 参数调整面板
## 🔧 安装和配置
### 1. 系统依赖
```bash
# 安装Python依赖
pip install -r requirements/vr-requirements.txt
# 安装OpenVR运行时
# Windows: 下载并安装SteamVR
# Linux:
sudo apt-get install steam
# 然后在Steam中安装SteamVR
```
### 2. ALVR服务器设置
```bash
# 下载ALVR服务器
# 从 https://github.com/alvr-org/ALVR 下载最新版本
# Windows
# 1. 解压到 C:\ALVR\
# 2. 运行 alvr_server.exe
# Linux
# 1. 解压到 /usr/local/bin/
# 2. 运行 alvr_server
```
### 3. Quest设备配置
```bash
# 1. 在Quest上安装ALVR客户端
# 2. 启用开发者模式
# 3. 连接到同一WiFi网络
# 4. 配置防火墙允许ALVR通信端口9943-9944
```
## 🚀 使用方法
### 快速启动
```python
# 在主程序中
world = MyWorld()
# 一键启用VR模式
if world.enableVRMode():
print("VR模式已启用")
# 检查VR状态
status = world.getVRStatus()
print(f"VR设备: {status['vr_info']}")
print(f"ALVR连接: {status['alvr_connected']}")
print(f"串流状态: {status['alvr_streaming']}")
```
### 详细控制
```python
# 分步骤启动VR
world = MyWorld()
# 1. 初始化VR系统
if world.initializeVR():
print("VR系统初始化成功")
# 2. 启动VR输入处理
world.startVRInput()
# 3. 显示控制器射线
world.showControllerRays(True)
# 4. 初始化ALVR串流
if world.initializeALVR():
print("ALVR初始化成功")
# 5. 设置串流质量
world.setALVRStreamQuality(
width=2880,
height=1700,
fps=72,
bitrate=150
)
# 6. 开始串流
world.startALVRStreaming()
```
### GUI控制界面
```python
# 创建VR控制面板
from ui.vr_control_panel import VRControlPanel
vr_panel = VRControlPanel(world)
vr_panel.show()
# 面板功能:
# - 一键启用/禁用VR
# - ALVR串流控制
# - 质量参数调整
# - 实时性能监控
# - 控制器状态显示
```
## 🎮 VR交互功能
### 控制器输入处理
```python
# 获取控制器状态
controllers = world.getAllControllers()
for controller_id in controllers:
state = world.getControllerState(controller_id)
# 检查扳机按下
if state['trigger'] > 0.5:
print(f"控制器 {controller_id} 扳机按下")
# 检查触摸板输入
touchpad_x, touchpad_y = state['touchpad']
if abs(touchpad_x) > 0.5:
print(f"触摸板水平滑动: {touchpad_x}")
```
### 对象抓取和操作
```python
# VR输入处理器自动处理对象抓取
# 扳机按下时自动检测和抓取对象
# 扳机释放时自动释放对象
# 监听VR事件
world.event_handler.accept('vr-object-grabbed', onObjectGrabbed)
world.event_handler.accept('vr-object-released', onObjectReleased)
def onObjectGrabbed(object_node, controller_id):
print(f"抓取对象: {object_node.getName()}")
def onObjectReleased(object_node, controller_id):
print(f"释放对象: {object_node.getName()}")
```
### 触觉反馈
```python
# 发送触觉反馈
world.sendHapticFeedback(
controller_id=0,
duration=0.5, # 持续时间(秒)
intensity=0.8 # 强度0-1
)
```
## 📊 性能优化
### 渲染优化
```python
# 调整VR渲染质量
vr_manager = world.vr_manager
vr_manager.render_scale = 1.0 # 渲染缩放 (0.5-2.0)
# 设置多重采样抗锯齿
fb_props = FrameBufferProperties()
fb_props.setMultisamples(4) # 4x MSAA
```
### 网络优化
```python
# 优化ALVR串流参数
world.setALVRStreamQuality(
width=2160, # 降低分辨率提高性能
height=1200,
fps=60, # 降低帧率减少延迟
bitrate=100 # 降低比特率适应网络带宽
)
```
### 系统资源监控
```python
# 获取性能状态
streaming_status = world.getALVRStreamingStatus()
print(f"串流FPS: {streaming_status['fps']}")
print(f"延迟: {streaming_status['latency']} ms")
print(f"分辨率: {streaming_status['resolution']}")
```
## 🔍 故障排除
### 常见问题
1. **VR系统初始化失败**
```
错误: OpenVR初始化失败
解决: 确保SteamVR已安装并运行
```
2. **ALVR连接失败**
```
错误: 无法连接到ALVR服务器
解决:
- 检查ALVR服务器是否运行
- 确认防火墙设置
- 检查网络连接
```
3. **串流质量问题**
```
问题: 画面卡顿或延迟高
解决:
- 降低分辨率和帧率
- 检查WiFi信号强度
- 调整比特率设置
```
### 调试命令
```python
# 启用调试模式
world.vr_manager.debug_mode = True
# 获取详细状态信息
vr_info = world.getVRInfo()
print(f"VR设备信息: {vr_info}")
# 检查网络连接
if world.isALVRConnected():
print("ALVR连接正常")
else:
print("ALVR连接失败")
```
## 🎯 高级功能
### 自定义VR交互
```python
# 创建自定义VR交互逻辑
class CustomVRInteraction:
def __init__(self, world):
self.world = world
def handleCustomGesture(self, gesture_data):
# 处理自定义手势
pass
def createVRMenu(self, controller_id):
# 在VR中创建3D菜单
pass
```
### 多用户VR支持
```python
# 支持多个VR用户
class MultiUserVRManager:
def __init__(self):
self.vr_users = {}
def addVRUser(self, user_id, vr_manager):
self.vr_users[user_id] = vr_manager
def syncVRUsers(self):
# 同步多用户VR状态
pass
```
## 📋 配置文件
### VR配置 (`config/vr_config.yaml`)
```yaml
vr:
enabled: true
render_scale: 1.0
tracking_space: "standing"
alvr:
server_ip: "127.0.0.1"
server_port: 9943
streaming_port: 9944
video:
width: 2880
height: 1700
fps: 72
bitrate: 150
codec: "h264"
audio:
enabled: true
sample_rate: 48000
channels: 2
```
## 🚀 部署建议
### 开发环境
```bash
# 创建VR开发环境
python -m venv vr_env
source vr_env/bin/activate
pip install -r requirements/vr-requirements.txt
# 启动开发服务器
python main.py --vr-mode
```
### 生产环境
```bash
# 优化生产部署
# 1. 使用专用VR计算机
# 2. 配置高性能网络
# 3. 启用GPU加速
# 4. 监控系统性能
```
## 📚 参考资料
- [OpenVR API文档](https://github.com/ValveSoftware/openvr/wiki/API-Documentation)
- [ALVR项目主页](https://github.com/alvr-org/ALVR)
- [Panda3D VR指南](https://docs.panda3d.org/1.10/python/programming/render-to-texture/index)
- [Quest开发者文档](https://developer.oculus.com/documentation/quest/)
## 🤝 贡献指南
欢迎贡献代码和改进建议!请遵循以下步骤:
1. Fork此仓库
2. 创建功能分支
3. 提交代码修改
4. 创建Pull Request
## 📄 许可证
本项目采用MIT许可证。详见LICENSE文件。

View File

@ -1,253 +0,0 @@
# VR测试说明
## 📋 测试条件说明
### 🎮 VR测试的两种模式
#### 1. 模拟模式(推荐用于开发和调试)
- **无需VR硬件**
- **无需SteamVR**
- **无需ALVR服务器**
- 自动启用当真实VR不可用时
- 模拟头盔和控制器追踪数据
- 立体渲染显示到窗口
- 适用于功能测试和开发
#### 2. 真实VR模式需要完整VR设备
- **需要VR头盔**
- **需要SteamVR运行**
- **需要ALVR服务器Quest无线**
- 真实的6DOF追踪
- 立体渲染到VR头盔
- 完整的VR交互体验
## 🔧 软件要求
### 必需依赖
```bash
# 安装VR相关依赖
pip install -r requirements/vr-requirements.txt
# 主要包含:
# - openvr>=1.26.7
# - psutil>=5.9.0
# - numpy>=1.21.0
# - Pillow>=9.0.1
```
### 系统要求
- Python 3.8+
- Panda3D
- PyQt5
- OpenGL支持的显卡
## 🎮 硬件要求完整VR模式
### 最低硬件要求
- **显卡**: GTX 1060 / RX 580 或更好
- **内存**: 8GB RAM
- **处理器**: Intel i5-4590 / AMD FX 8350 或更好
- **USB**: 至少1个USB 3.0端口
### 支持的VR头盔
- **Quest 2/3**: 有线USB-C或无线ALVR
- **Valve Index**: DisplayPort + USB 3.0
- **HTC Vive**: HDMI + USB 3.0
- **其他OpenVR兼容设备**
## 🌐 连接方式
### 有线连接
1. **Quest 2/3**: USB-C数据线连接PC
2. **Valve Index**: DisplayPort + USB 3.0
3. **HTC Vive**: HDMI + USB 3.0
### 无线连接推荐Quest
1. **下载ALVR服务器**: [GitHub](https://github.com/alvr-org/ALVR)
2. **在PC上运行ALVR服务器**
3. **在Quest上安装ALVR客户端**
4. **连接到同一Wi-Fi网络5GHz推荐**
## 🔄 启动顺序
### 完整VR模式启动顺序
1. **连接VR头盔**到PC
2. **启动SteamVR**
3. **可选启动ALVR服务器**Quest无线
4. **运行VR测试脚本**
### 模拟模式启动顺序
1. **直接运行VR测试脚本**
2. **系统会自动切换到模拟模式**
## 📊 测试项目说明
### 1. 基本VR功能测试
- VR系统初始化
- 控制器检测
- 追踪数据获取
- 立体渲染测试
### 2. VR模拟模式测试
- 强制启用模拟模式
- 模拟数据更新
- 控制器输入模拟
- 立体渲染显示
### 3. ALVR串流测试
- ALVR服务器连接
- 串流质量设置
- 触觉反馈测试
- 串流开关控制
### 4. VR GUI控制面板
- 图形界面控制
- 实时状态监控
- 参数调整
- 系统开关
### 5. VR交互功能测试
- 控制器射线显示
- 手势识别
- 对象交互
- 输入处理
## 🚀 快速开始
### 开发和调试(推荐)
```bash
# 直接运行,会自动使用模拟模式
python vr_test.py
# 选择 "2. VR模拟模式测试"
```
### 完整VR测试
```bash
# 确保VR设备已连接并启动SteamVR
# 然后运行
python vr_test.py
# 选择 "1. 基本VR功能测试"
```
## 💡 故障排除
### 常见问题
#### 1. OpenVR初始化失败
**现象**: 显示"OpenVR初始化失败"
**解决方案**:
- 检查SteamVR是否运行
- 确认VR头盔被系统识别
- 重启SteamVR和头盔
- 系统会自动切换到模拟模式
#### 2. 控制器未检测到
**现象**: 控制器显示"未连接"
**解决方案**:
- 确保控制器已配对
- 检查控制器电量
- 在SteamVR中重新配对
- 模拟模式下会显示模拟控制器
#### 3. ALVR连接失败
**现象**: ALVR串流测试失败
**解决方案**:
- 确保ALVR服务器正在运行
- 检查Quest上的ALVR客户端
- 确认PC和Quest在同一网络
- 使用5GHz Wi-Fi网络
#### 4. 性能问题
**现象**: 帧率低或卡顿
**解决方案**:
- 降低渲染分辨率
- 关闭不必要的后台程序
- 检查显卡驱动更新
- 使用模拟模式进行开发
### 日志分析
测试过程中的详细日志可以帮助诊断问题:
- `✓` 表示成功
- `⚠` 表示警告(非致命)
- `✗` 表示失败
- `🔧` 表示配置或调试信息
## 📈 性能优化建议
### 开发阶段
1. **使用模拟模式**进行功能开发
2. **降低渲染分辨率**提高帧率
3. **关闭不必要的特效**
4. **使用性能分析工具**
### 生产部署
1. **使用真实VR模式**
2. **根据硬件配置调整质量**
3. **启用异步时间扭曲**
4. **优化渲染管线**
## 🔍 调试技巧
### 1. 检查VR系统状态
```python
# 在代码中添加调试信息
vr_info = world.getVRInfo()
print(f"VR模式: {vr_info['mode']}")
print(f"模拟模式: {vr_info['simulation_mode']}")
```
### 2. 监控性能
```python
# 获取VR系统状态
status = world.getVRStatus()
print(f"VR启用: {status['vr_enabled']}")
print(f"控制器数量: {len(status['controllers'])}")
```
### 3. 测试特定功能
```python
# 强制启用模拟模式
world.vr_manager.enable_simulation_mode()
# 更新模拟数据
world.vr_manager.update_simulation_data('head_pose', new_data)
```
## 📚 API参考
### VR系统控制
- `world.initializeVR()` - 初始化VR系统
- `world.shutdownVR()` - 关闭VR系统
- `world.isVREnabled()` - 检查VR状态
- `world.getVRInfo()` - 获取VR信息
### 模拟模式
- `world.vr_manager.enable_simulation_mode()` - 启用模拟模式
- `world.vr_manager.get_simulation_data()` - 获取模拟数据
- `world.vr_manager.update_simulation_data()` - 更新模拟数据
### ALVR串流
- `world.initializeALVR()` - 初始化ALVR
- `world.startALVRStreaming()` - 开始串流
- `world.stopALVRStreaming()` - 停止串流
- `world.setALVRStreamQuality()` - 设置质量
### VR输入
- `world.startVRInput()` - 启动输入处理
- `world.showControllerRays()` - 显示控制器射线
- `world.getAllControllers()` - 获取所有控制器
- `world.getControllerState()` - 获取控制器状态
## 🎯 总结
现在的VR测试系统具有以下优势
1. **自动适应**: 自动在真实VR和模拟模式之间切换
2. **开发友好**: 无需VR硬件即可开发和测试
3. **完整功能**: 支持所有VR功能的测试
4. **详细反馈**: 提供清晰的状态信息和错误提示
5. **灵活配置**: 可根据需要调整各种参数
无论您是否拥有VR设备都可以使用这个测试系统来验证VR功能的正确性。

View File

@ -1,197 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
直接VR串流测试脚本
测试不通过ALVR直接使用OpenVR API进行串流的功能
"""
import sys
import os
import time
from main import MyWorld
def test_direct_vr_streaming():
"""测试直接VR串流功能"""
print("=== 直接VR串流功能测试 ===")
# 创建世界实例
world = MyWorld()
# 初始化VR系统
print("🥽 初始化VR系统...")
if not world.initializeVR():
print("✗ VR系统初始化失败")
return False
print("✓ VR系统初始化成功")
# 获取VR信息
vr_info = world.getVRInfo()
print(f"\n📊 VR系统信息:")
print(f" - 模式: {vr_info['mode']}")
print(f" - 启用状态: {vr_info['enabled']}")
print(f" - 模拟模式: {vr_info['simulation_mode']}")
print(f" - 渲染尺寸: {vr_info['render_size']}")
# 检查是否为真实VR模式
if not vr_info['enabled'] or vr_info['simulation_mode']:
print("⚠ 未检测到真实VR设备或处于模拟模式")
print(" 直接串流需要真实的VR设备支持")
return False
# 检查VR纹理是否正确创建
print("\n🔍 检查VR纹理...")
if hasattr(world.vr_manager, 'left_eye_texture') and world.vr_manager.left_eye_texture:
print(" ✓ 左眼纹理已创建")
print(f" 纹理尺寸: {world.vr_manager.left_eye_texture.getXSize()}x{world.vr_manager.left_eye_texture.getYSize()}")
else:
print(" ✗ 左眼纹理未创建")
return False
if hasattr(world.vr_manager, 'right_eye_texture') and world.vr_manager.right_eye_texture:
print(" ✓ 右眼纹理已创建")
print(f" 纹理尺寸: {world.vr_manager.right_eye_texture.getXSize()}x{world.vr_manager.right_eye_texture.getYSize()}")
else:
print(" ✗ 右眼纹理未创建")
return False
# 检查OpenVR系统是否正常
print("\n🔍 检查OpenVR系统...")
if hasattr(world.vr_manager, 'vr_system') and world.vr_manager.vr_system:
print(" ✓ OpenVR系统已初始化")
else:
print(" ✗ OpenVR系统未初始化")
return False
if hasattr(world.vr_manager, 'vr_compositor') and world.vr_manager.vr_compositor:
print(" ✓ OpenVR合成器已初始化")
else:
print(" ✗ OpenVR合成器未初始化")
return False
# 创建简单的测试场景
print("\n🎨 创建测试场景...")
try:
from panda3d.core import CardMaker, Vec4, AmbientLight, DirectionalLight
# 添加光照
ambient_light = AmbientLight('ambient')
ambient_light.setColor(Vec4(0.3, 0.3, 0.3, 1))
ambient_light_np = world.render.attachNewNode(ambient_light)
world.render.setLight(ambient_light_np)
directional_light = DirectionalLight('directional')
directional_light.setColor(Vec4(0.8, 0.8, 0.8, 1))
directional_light.setDirection(Vec4(-1, -1, -1, 0))
directional_light_np = world.render.attachNewNode(directional_light)
world.render.setLight(directional_light_np)
# 创建地面网格
for i in range(-5, 6):
for j in range(-5, 6):
cm = CardMaker(f"grid_{i}_{j}")
cm.setFrame(-0.4, 0.4, -0.4, 0.4)
grid = world.render.attachNewNode(cm.generate())
grid.setPos(i * 1.0, j * 1.0, -1)
grid.setHpr(0, -90, 0)
# 棋盘格颜色
if (i + j) % 2 == 0:
grid.setColor(0.8, 0.8, 0.8, 1.0)
else:
grid.setColor(0.6, 0.6, 0.6, 1.0)
# 创建一些彩色立方体
for i in range(8):
cm = CardMaker(f"cube_{i}")
cm.setFrame(-0.3, 0.3, -0.3, 0.3)
cube = world.render.attachNewNode(cm.generate())
import math
angle = i * math.pi * 2 / 8
radius = 3
cube.setPos(math.cos(angle) * radius, math.sin(angle) * radius, 0.5)
cube.setHpr(i * 45, 0, 0)
cube.setScale(1, 1, 2)
# 彩虹颜色
hue = i / 8.0
r = abs(math.sin(hue * math.pi * 2))
g = abs(math.sin((hue + 0.33) * math.pi * 2))
b = abs(math.sin((hue + 0.66) * math.pi * 2))
cube.setColor(r, g, b, 1.0)
print("✓ 测试场景创建完成")
except Exception as e:
print(f"场景创建错误: {str(e)}")
# 测试直接串流
print("\n🎮 开始直接VR串流测试...")
print(" 测试将持续15秒请观察VR头显中的画面")
start_time = time.time()
frame_count = 0
try:
while time.time() - start_time < 15:
# 处理引擎更新
if hasattr(world, 'taskMgr'):
world.taskMgr.step()
frame_count += 1
# 每秒显示一次状态
if frame_count % 60 == 0: # 约每秒
elapsed = time.time() - start_time
fps = frame_count / elapsed
print(f" [{elapsed:.1f}s] FPS: {fps:.1f}")
time.sleep(0.016) # ~60 FPS
except KeyboardInterrupt:
print("\n⏹️ 用户中断测试")
# 关闭系统
world.shutdownVR()
print("\n✓ VR系统已关闭")
final_fps = frame_count / (time.time() - start_time)
print(f"✅ 直接VR串流测试完成平均FPS: {final_fps:.1f}")
return True
def main():
"""主函数"""
print("🎮 直接VR串流测试脚本")
print("="*50)
print("此脚本将测试不通过ALVR直接使用OpenVR API进行串流的功能")
print("请确保:")
print(" 1. VR设备已连接如Pico 4通过USB-C连接")
print(" 2. SteamVR正在运行")
print(" 3. VR设备已被SteamVR识别")
try:
input("\n按回车键开始测试或按Ctrl+C退出...")
success = test_direct_vr_streaming()
print("\n" + "="*50)
if success:
print("✓ 直接VR串流测试成功完成")
print("🎉 您现在已经可以不通过ALVR直接串流到VR设备")
else:
print("✗ 直接VR串流测试遇到问题")
print("💡 请检查VR设备连接和SteamVR状态")
except KeyboardInterrupt:
print("\n用户中断测试")
except Exception as e:
print(f"测试过程中发生错误: {str(e)}")
import traceback
traceback.print_exc()
if __name__ == "__main__":
main()

305
main.py
View File

@ -15,9 +15,9 @@ from core.selection import SelectionSystem
from core.event_handler import EventHandler
from core.tool_manager import ToolManager
from core.script_system import ScriptManager
from core.vr_manager import VRManager
from core.vr_input_handler import VRInputHandler
from core.alvr_streamer import ALVRStreamer
from core.openxr_manager import OpenXRManager
from core.openxr_input_handler import OpenXRInputHandler
from core.openxr_streamer import OpenXRStreamer
from gui.gui_manager import GUIManager
from scene.scene_manager import SceneManager
from project.project_manager import ProjectManager
@ -41,7 +41,7 @@ import json
import datetime
from direct.actor.Actor import Actor
from PyQt5.sip import wrapinstance
#from RenderPipelineFile.toolkit.material_editor.main import MaterialEditor
# RenderPipelineFile.toolkit.material_editor.main import MaterialEditor
class MyWorld(CoreWorld):
@ -75,10 +75,10 @@ class MyWorld(CoreWorld):
# 初始化界面管理系统
self.interface_manager = InterfaceManager(self)
# 初始化VR系统
self.vr_manager = VRManager(self)
self.vr_input_handler = VRInputHandler(self, self.vr_manager)
self.alvr_streamer = ALVRStreamer(self, self.vr_manager)
# 初始化OpenXR系统
self.openxr_manager = OpenXRManager(self)
self.openxr_input_handler = OpenXRInputHandler(self, self.openxr_manager)
self.openxr_streamer = OpenXRStreamer(self, self.openxr_manager)
# 启动脚本系统
self.script_manager.start_system()
@ -444,6 +444,144 @@ class MyWorld(CoreWorld):
"""根据名称查找模型"""
return self.scene_manager.findModelByName(name)
# ==================== OpenXR系统功能代理 ====================
# OpenXR系统控制方法 - 代理到openxr_manager
def initializeOpenXR(self):
"""初始化OpenXR系统"""
return self.openxr_manager.initialize_openxr()
def shutdownOpenXR(self):
"""关闭OpenXR系统"""
return self.openxr_manager.shutdown_openxr()
def isOpenXREnabled(self):
"""检查OpenXR是否启用"""
return self.openxr_manager.is_openxr_enabled()
def getOpenXRInfo(self):
"""获取OpenXR系统信息"""
return self.openxr_manager.get_openxr_info()
# OpenXR输入处理方法 - 代理到openxr_input_handler
def startOpenXRInput(self):
"""启动OpenXR输入处理"""
return self.openxr_input_handler.start_input_handling()
def stopOpenXRInput(self):
"""停止OpenXR输入处理"""
return self.openxr_input_handler.stop_input_handling()
def showControllerRays(self, show=True):
"""显示/隐藏控制器射线"""
return self.openxr_input_handler.show_controller_rays(show)
def getControllerState(self, controller_id):
"""获取控制器状态"""
return self.openxr_input_handler.get_controller_state(controller_id)
def getAllControllers(self):
"""获取所有控制器"""
return self.openxr_input_handler.get_all_controllers()
def setVRGestureEnabled(self, enabled):
"""设置OpenXR手势识别启用状态"""
return self.openxr_input_handler.set_gesture_enabled(enabled)
def setVRInteractionEnabled(self, enabled):
"""设置OpenXR交互启用状态"""
return self.openxr_input_handler.set_interaction_enabled(enabled)
# OpenXR串流方法 - 代理到openxr_streamer
def initializeOpenXRStreaming(self):
"""初始化OpenXR串流"""
return self.openxr_streamer.initialize()
def startOpenXRStreaming(self):
"""开始OpenXR串流"""
return self.openxr_streamer.start_streaming()
def stopOpenXRStreaming(self):
"""停止OpenXR串流"""
return self.openxr_streamer.stop_streaming()
def getOpenXRStreamingStatus(self):
"""获取OpenXR串流状态"""
return self.openxr_streamer.get_streaming_status()
def setOpenXRStreamQuality(self, width, height, fps, bitrate):
"""设置OpenXR串流质量"""
return self.openxr_streamer.set_stream_quality(width, height, fps, bitrate)
def sendHapticFeedback(self, controller_id, duration, intensity):
"""发送触觉反馈"""
return self.openxr_streamer.send_haptic_feedback(controller_id, duration, intensity)
def shutdownOpenXRStreaming(self):
"""关闭OpenXR串流"""
return self.openxr_streamer.shutdown()
def isOpenXRConnected(self):
"""检查OpenXR是否连接"""
return self.openxr_streamer.is_connected()
def isOpenXRStreaming(self):
"""检查OpenXR是否在串流"""
return self.openxr_streamer.is_streaming()
# 便捷方法
def enableOpenXRMode(self):
"""启用OpenXR模式一键启动"""
print("启用OpenXR模式...")
# 1. 初始化OpenXR系统
if not self.initializeOpenXR():
print("OpenXR系统初始化失败")
return False
# 2. 启动OpenXR输入处理
if not self.startOpenXRInput():
print("OpenXR输入处理启动失败")
return False
# 3. 初始化OpenXR串流
if self.initializeOpenXRStreaming():
print("✓ OpenXR串流已启用")
# 自动开始串流
self.startOpenXRStreaming()
else:
print("⚠ OpenXR串流启用失败但OpenXR系统仍可用")
print("✓ OpenXR模式已启用")
return True
def disableOpenXRMode(self):
"""禁用OpenXR模式一键关闭"""
print("禁用OpenXR模式...")
# 1. 停止OpenXR串流
self.stopOpenXRStreaming()
self.shutdownOpenXRStreaming()
# 2. 停止OpenXR输入处理
self.stopOpenXRInput()
# 3. 关闭OpenXR系统
self.shutdownOpenXR()
print("✓ OpenXR模式已禁用")
def getOpenXRStatus(self):
"""获取OpenXR系统总体状态"""
return {
"openxr_enabled": self.isOpenXREnabled(),
"openxr_info": self.getOpenXRInfo(),
"controllers": self.getAllControllers(),
"openxr_connected": self.isOpenXRConnected(),
"openxr_streaming": self.isOpenXRStreaming(),
"streaming_status": self.getOpenXRStreamingStatus() if self.isOpenXRConnected() else None
}
# ==================== 脚本系统功能代理 ====================
# 脚本系统控制方法 - 代理到script_manager
@ -510,143 +648,6 @@ class MyWorld(CoreWorld):
"""列出所有脚本信息"""
return self.script_manager.list_all_scripts()
# ==================== VR系统功能代理 ====================
# VR系统控制方法 - 代理到vr_manager
def initializeVR(self):
"""初始化VR系统"""
return self.vr_manager.initialize_vr()
def shutdownVR(self):
"""关闭VR系统"""
return self.vr_manager.shutdown_vr()
def isVREnabled(self):
"""检查VR是否启用"""
return self.vr_manager.is_vr_enabled()
def getVRInfo(self):
"""获取VR系统信息"""
return self.vr_manager.get_vr_info()
# VR输入处理方法 - 代理到vr_input_handler
def startVRInput(self):
"""启动VR输入处理"""
return self.vr_input_handler.start_input_handling()
def stopVRInput(self):
"""停止VR输入处理"""
return self.vr_input_handler.stop_input_handling()
def showControllerRays(self, show=True):
"""显示/隐藏控制器射线"""
return self.vr_input_handler.show_controller_rays(show)
def getControllerState(self, controller_id):
"""获取控制器状态"""
return self.vr_input_handler.get_controller_state(controller_id)
def getAllControllers(self):
"""获取所有控制器"""
return self.vr_input_handler.get_all_controllers()
def setVRGestureEnabled(self, enabled):
"""设置VR手势识别启用状态"""
return self.vr_input_handler.set_gesture_enabled(enabled)
def setVRInteractionEnabled(self, enabled):
"""设置VR交互启用状态"""
return self.vr_input_handler.set_interaction_enabled(enabled)
# ALVR串流方法 - 代理到alvr_streamer
def initializeALVR(self):
"""初始化ALVR串流"""
return self.alvr_streamer.initialize()
def startALVRStreaming(self):
"""开始ALVR串流"""
return self.alvr_streamer.start_streaming()
def stopALVRStreaming(self):
"""停止ALVR串流"""
return self.alvr_streamer.stop_streaming()
def getALVRStreamingStatus(self):
"""获取ALVR串流状态"""
return self.alvr_streamer.get_streaming_status()
def setALVRStreamQuality(self, width, height, fps, bitrate):
"""设置ALVR串流质量"""
return self.alvr_streamer.set_stream_quality(width, height, fps, bitrate)
def sendHapticFeedback(self, controller_id, duration, intensity):
"""发送触觉反馈"""
return self.alvr_streamer.send_haptic_feedback(controller_id, duration, intensity)
def shutdownALVR(self):
"""关闭ALVR串流"""
return self.alvr_streamer.shutdown()
def isALVRConnected(self):
"""检查ALVR是否连接"""
return self.alvr_streamer.is_connected()
def isALVRStreaming(self):
"""检查ALVR是否在串流"""
return self.alvr_streamer.is_streaming()
# 便捷方法
def enableVRMode(self):
"""启用VR模式一键启动"""
print("启用VR模式...")
# 1. 初始化VR系统
if not self.initializeVR():
print("VR系统初始化失败")
return False
# 2. 启动VR输入处理
if not self.startVRInput():
print("VR输入处理启动失败")
return False
# 3. 初始化ALVR串流
if self.initializeALVR():
print("✓ ALVR串流已启用")
# 自动开始串流
self.startALVRStreaming()
else:
print("⚠ ALVR串流启用失败但VR系统仍可用")
print("✓ VR模式已启用")
return True
def disableVRMode(self):
"""禁用VR模式一键关闭"""
print("禁用VR模式...")
# 1. 停止ALVR串流
self.stopALVRStreaming()
self.shutdownALVR()
# 2. 停止VR输入处理
self.stopVRInput()
# 3. 关闭VR系统
self.shutdownVR()
print("✓ VR模式已禁用")
def getVRStatus(self):
"""获取VR系统总体状态"""
return {
"vr_enabled": self.isVREnabled(),
"vr_info": self.getVRInfo(),
"controllers": self.getAllControllers(),
"alvr_connected": self.isALVRConnected(),
"alvr_streaming": self.isALVRStreaming(),
"streaming_status": self.getALVRStreamingStatus() if self.isALVRConnected() else None
}
# ==================== 项目管理功能代理 ====================
@ -681,13 +682,13 @@ def buildPackage(appw):
# def open_material_editor(self):
# """打开材质编辑器作为子窗口"""
# if not self.material_editor:
# self.material_editor = MaterialEditor()
# # 设置为子窗口
# self.material_editor.setParent(self)
# self.material_editor.setWindowFlags(Qt.Window)
#
# self.material_editor.show()
#
# # self.material_editor = MaterialEditor()
# # # 设置为子窗口
# # self.material_editor.setParent(self)
# # self.material_editor.setWindowFlags(Qt.Window)
# #
# # self.material_editor.show()
# #
if __name__ == "__main__":
world = MyWorld()

480
openxr_test.py Executable file
View File

@ -0,0 +1,480 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
OpenXR测试脚本
用于测试OpenXR功能的基本实现
"""
import sys
import os
import time
# 添加项目根目录到Python路径
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from main import MyWorld
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QHBoxLayout, QLabel
from PyQt5.QtGui import QPixmap, QImage
from PyQt5.QtCore import QTimer
import numpy as np
def test_openxr_basic_display(world):
"""测试基本OpenXR显示功能"""
print("=== OpenXR基本显示测试 ===")
# 加载一些模型来展示
print("📦 加载演示场景...")
try:
# 创建一些简单的几何体作为演示
from panda3d.core import CardMaker, Vec4
# 创建地面
cm = CardMaker("ground")
cm.setFrame(-10, 10, -10, 10)
ground = world.render.attachNewNode(cm.generate())
ground.setPos(0, 0, -1)
ground.setColor(0.5, 0.8, 0.5, 1.0)
ground.setHpr(0, -90, 0)
# 创建一些立方体
for i in range(5):
for j in range(5):
cube = world.loader.loadModel("models/environment")
if not cube:
# 如果没有environment模型创建简单的立方体
cm_cube = CardMaker(f"cube_{i}_{j}")
cm_cube.setFrame(-0.5, 0.5, -0.5, 0.5)
cube = world.render.attachNewNode(cm_cube.generate())
cube.setPos(i * 2 - 4, j * 2 - 4, 0)
cube.setScale(0.5)
cube.setColor(i/5.0, j/5.0, 1.0, 1.0)
print("✓ 演示场景加载完成")
except Exception as e:
print(f"场景加载错误: {str(e)}")
# 初始化OpenXR系统
print("\n🥽 初始化OpenXR显示系统...")
openxr_success = world.initializeOpenXR()
if openxr_success:
print("✓ OpenXR系统初始化成功")
openxr_info = world.getOpenXRInfo()
print(f"\n📊 OpenXR显示信息:")
print(f" - 模式: {openxr_info['mode']}")
print(f" - 渲染尺寸: {openxr_info['render_size']}")
print(f" - 模拟模式: {openxr_info['simulation_mode']}")
print(f" - 有线连接: {openxr_info['wired_connection']}")
if openxr_info['simulation_mode']:
print("\n💡 模拟模式说明:")
print(" - 左右眼视图将并排显示在主窗口")
print(" - 可以看到立体渲染效果")
print(" - 头部会有轻微的模拟摆动")
else:
print("\n💡 真实OpenXR模式:")
print(" - 画面将渲染到VR头盔")
print(" - 支持真实的头部追踪")
print(" - 控制器交互可用")
# 创建显示窗口
app = QApplication.instance()
if app is None:
app = QApplication(sys.argv)
# 创建主窗口显示OpenXR渲染结果
window = QMainWindow()
window.setWindowTitle("OpenXR Stereo Rendering Preview")
window.setGeometry(100, 100, 800, 300)
central_widget = QWidget()
layout = QHBoxLayout()
central_widget.setLayout(layout)
left_label = QLabel("Left Eye")
right_label = QLabel("Right Eye")
layout.addWidget(left_label)
layout.addWidget(right_label)
window.setCentralWidget(central_widget)
window.show()
# 定时更新显示
def update_display():
if hasattr(world.openxr_manager, 'left_eye_texture') and world.openxr_manager.left_eye_texture:
# 获取左眼纹理数据
tex = world.openxr_manager.left_eye_texture
try:
if tex.get_x_size() > 0 and tex.get_y_size() > 0:
# 添加对纹理数据有效性的检查
if tex.might_have_ram_image():
# 使用正确的API将纹理数据复制到QImage
data = tex.get_ram_image().get_data()
if data:
img = QImage(data, tex.get_x_size(), tex.get_y_size(), QImage.Format_RGBA8888)
pixmap = QPixmap.fromImage(img)
left_label.setPixmap(pixmap.scaled(400, 300))
left_label.setText("") # 清除文字,只显示图像
else:
left_label.setText("Left Eye (No Data)")
else:
left_label.setText("Left Eye (No RAM Image)")
else:
left_label.setText("Left Eye (Invalid Size)")
except Exception as e:
print(f"左眼纹理更新错误: {str(e)}")
left_label.setText("Left Eye (Error)")
else:
left_label.setText("Left Eye (No Texture)")
if hasattr(world.openxr_manager, 'right_eye_texture') and world.openxr_manager.right_eye_texture:
# 获取右眼纹理数据
tex = world.openxr_manager.right_eye_texture
try:
if tex.get_x_size() > 0 and tex.get_y_size() > 0:
# 添加对纹理数据有效性的检查
if tex.might_have_ram_image():
# 使用正确的API将纹理数据复制到QImage
data = tex.get_ram_image().get_data()
if data:
img = QImage(data, tex.get_x_size(), tex.get_y_size(), QImage.Format_RGBA8888)
pixmap = QPixmap.fromImage(img)
right_label.setPixmap(pixmap.scaled(400, 300))
right_label.setText("") # 清除文字,只显示图像
else:
right_label.setText("Right Eye (No Data)")
else:
right_label.setText("Right Eye (No RAM Image)")
else:
right_label.setText("Right Eye (Invalid Size)")
except Exception as e:
print(f"右眼纹理更新错误: {str(e)}")
right_label.setText("Right Eye (Error)")
else:
right_label.setText("Right Eye (No Texture)")
timer = QTimer()
timer.timeout.connect(update_display)
timer.start(33) # 约30 FPS
# 运行几秒钟以展示效果
print(f"\n🎮 OpenXR显示测试运行中... (10秒)")
print(" 观察主窗口中的立体渲染效果")
start_time = time.time()
while time.time() - start_time < 10:
# 处理Panda3D事件
if hasattr(world, 'taskMgr'):
world.taskMgr.step()
# 处理Qt事件
try:
app.processEvents()
except Exception as e:
print(f"Qt事件处理错误: {str(e)}")
# 添加短暂延迟以避免过度占用CPU
time.sleep(0.016) # ~60 FPS
print("✓ OpenXR显示测试完成")
else:
print("✗ OpenXR系统初始化失败")
return False
# 关闭OpenXR系统
world.shutdownOpenXR()
print("\n✓ OpenXR系统已关闭")
return True
def test_openxr_streaming(world):
"""测试OpenXR串流功能"""
print("=== OpenXR串流测试 ===")
# 初始化OpenXR系统
print("初始化OpenXR系统...")
if not world.initializeOpenXR():
print("✗ OpenXR系统初始化失败")
return False
# 启动输入处理
print("启动OpenXR输入处理...")
if not world.startOpenXRInput():
print("⚠ OpenXR输入处理启动失败")
# 初始化串流
print("初始化OpenXR串流...")
if world.initializeOpenXRStreaming():
print("✓ OpenXR串流初始化成功")
# 开始串流
print("开始OpenXR串流...")
if world.startOpenXRStreaming():
print("✓ OpenXR串流已开始")
# 创建显示窗口
app = QApplication.instance()
if app is None:
app = QApplication(sys.argv)
# 创建主窗口显示OpenXR渲染结果
window = QMainWindow()
window.setWindowTitle("OpenXR Streaming Preview")
window.setGeometry(100, 100, 800, 300)
central_widget = QWidget()
layout = QHBoxLayout()
central_widget.setLayout(layout)
left_label = QLabel("Left Eye")
right_label = QLabel("Right Eye")
layout.addWidget(left_label)
layout.addWidget(right_label)
window.setCentralWidget(central_widget)
window.show()
# 定时更新显示
def update_display():
try:
# 更新左眼纹理
if hasattr(world.openxr_manager, 'left_eye_texture') and world.openxr_manager.left_eye_texture:
tex = world.openxr_manager.left_eye_texture
# 检查纹理尺寸和数据有效性
if tex.get_x_size() > 0 and tex.get_y_size() > 0 and tex.might_have_ram_image():
ram_image = tex.get_ram_image()
if ram_image and ram_image.get_data():
# 使用正确的数据访问方法
img = QImage(ram_image.get_data(), tex.get_x_size(), tex.get_y_size(), QImage.Format_RGBA8888)
pixmap = QPixmap.fromImage(img)
left_label.setPixmap(pixmap.scaled(400, 300))
left_label.setText("") # 清除文字,只显示图像
# 更新右眼纹理
if hasattr(world.openxr_manager, 'right_eye_texture') and world.openxr_manager.right_eye_texture:
tex = world.openxr_manager.right_eye_texture
# 检查纹理尺寸和数据有效性
if tex.get_x_size() > 0 and tex.get_y_size() > 0 and tex.might_have_ram_image():
ram_image = tex.get_ram_image()
if ram_image and ram_image.get_data():
# 使用正确的数据访问方法
img = QImage(ram_image.get_data(), tex.get_x_size(), tex.get_y_size(), QImage.Format_RGBA8888)
pixmap = QPixmap.fromImage(img)
right_label.setPixmap(pixmap.scaled(400, 300))
right_label.setText("") # 清除文字,只显示图像
except Exception as e:
print(f"纹理更新错误: {str(e)}")
# 清除可能已损坏的纹理引用
if hasattr(world.openxr_manager, 'left_eye_texture'):
world.openxr_manager.left_eye_texture = None
if hasattr(world.openxr_manager, 'right_eye_texture'):
world.openxr_manager.right_eye_texture = None
timer = QTimer()
timer.timeout.connect(update_display)
timer.start(33) # 约30 FPS
# 运行几秒钟
print("OpenXR串流运行中... (5秒)")
start_time = time.time()
while time.time() - start_time < 5:
# 处理Panda3D事件
if hasattr(world, 'taskMgr'):
world.taskMgr.step()
# 处理Qt事件
try:
app.processEvents()
except Exception as e:
print(f"Qt事件处理错误: {str(e)}")
time.sleep(0.016) # ~60 FPS
# 停止串流
world.stopOpenXRStreaming()
print("✓ OpenXR串流已停止")
else:
print("✗ OpenXR串流启动失败")
else:
print("✗ OpenXR串流初始化失败")
# 关闭系统
world.shutdownOpenXRStreaming()
world.stopOpenXRInput()
world.shutdownOpenXR()
print("✓ OpenXR串流测试完成")
return True
def test_openxr_full(world):
"""测试完整OpenXR功能"""
print("=== 完整OpenXR功能测试 ===")
# 初始化OpenXR系统
print("初始化OpenXR系统...")
if not world.initializeOpenXR():
print("✗ OpenXR系统初始化失败")
return False
# 创建显示窗口
app = QApplication.instance()
if app is None:
app = QApplication(sys.argv)
# 创建主窗口显示OpenXR渲染结果
window = QMainWindow()
window.setWindowTitle("OpenXR Full Functionality Preview")
window.setGeometry(100, 100, 800, 300)
central_widget = QWidget()
layout = QHBoxLayout()
central_widget.setLayout(layout)
left_label = QLabel("Left Eye")
right_label = QLabel("Right Eye")
layout.addWidget(left_label)
layout.addWidget(right_label)
window.setCentralWidget(central_widget)
window.show()
# 定时更新显示
def update_display():
try:
# 更新左眼纹理
if hasattr(world.openxr_manager, 'left_eye_texture') and world.openxr_manager.left_eye_texture:
tex = world.openxr_manager.left_eye_texture
# 检查纹理尺寸和数据有效性
if tex.get_x_size() > 0 and tex.get_y_size() > 0 and tex.might_have_ram_image():
ram_image = tex.get_ram_image()
if ram_image and ram_image.get_data():
# 使用正确的数据访问方法
img = QImage(ram_image.get_data(), tex.get_x_size(), tex.get_y_size(), QImage.Format_RGBA8888)
pixmap = QPixmap.fromImage(img)
left_label.setPixmap(pixmap.scaled(400, 300))
left_label.setText("") # 清除文字,只显示图像
# 更新右眼纹理
if hasattr(world.openxr_manager, 'right_eye_texture') and world.openxr_manager.right_eye_texture:
tex = world.openxr_manager.right_eye_texture
# 检查纹理尺寸和数据有效性
if tex.get_x_size() > 0 and tex.get_y_size() > 0 and tex.might_have_ram_image():
ram_image = tex.get_ram_image()
if ram_image and ram_image.get_data():
# 使用正确的数据访问方法
img = QImage(ram_image.get_data(), tex.get_x_size(), tex.get_y_size(), QImage.Format_RGBA8888)
pixmap = QPixmap.fromImage(img)
right_label.setPixmap(pixmap.scaled(400, 300))
right_label.setText("") # 清除文字,只显示图像
except Exception as e:
print(f"纹理更新错误: {str(e)}")
# 清除可能已损坏的纹理引用
if hasattr(world.openxr_manager, 'left_eye_texture'):
world.openxr_manager.left_eye_texture = None
if hasattr(world.openxr_manager, 'right_eye_texture'):
world.openxr_manager.right_eye_texture = None
timer = QTimer()
timer.timeout.connect(update_display)
timer.start(33) # 约30 FPS
# 一键启用OpenXR模式
print("启用OpenXR模式...")
if world.enableOpenXRMode():
print("✓ OpenXR模式已启用")
# 启动输入处理
print("启动OpenXR输入处理...")
if not world.startOpenXRInput():
print("⚠ OpenXR输入处理启动失败")
# 获取状态
openxr_info = world.getOpenXRInfo()
print(f"\n📊 OpenXR状态:")
print(f" - 启用: {openxr_info['enabled']}")
print(f" - 模式: {openxr_info['mode']}")
print(f" - 模拟模式: {openxr_info['simulation_mode']}")
print(f" - 渲染尺寸: {openxr_info['render_size']}")
print(f" - 有线连接: {openxr_info['wired_connection']}")
# 获取状态
status = world.getOpenXRStatus()
print(f"\n📊 OpenXR状态:")
print(f" - 启用: {status['openxr_enabled']}")
print(f" - 连接: {status['openxr_connected']}")
print(f" - 串流: {status['openxr_streaming']}")
print(f" - 控制器: {len(status['controllers'])}")
if status['streaming_status']:
streaming_status = status['streaming_status']
print(f"\n📡 串流状态:")
print(f" - FPS: {streaming_status['fps']}")
print(f" - 分辨率: {streaming_status['resolution']}")
print(f" - 延迟: {streaming_status['latency']}ms")
# 运行一段时间
print(f"\n🎮 OpenXR完整功能运行中... (15秒)")
start_time = time.time()
while time.time() - start_time < 15:
# 处理Panda3D事件
if hasattr(world, 'taskMgr'):
world.taskMgr.step()
# 处理Qt事件
try:
app.processEvents()
except Exception as e:
print(f"Qt事件处理错误: {str(e)}")
time.sleep(0.016) # ~60 FPS
# 禁用OpenXR模式
world.shutdownOpenXR()
print("✓ OpenXR模式已禁用")
else:
print("✗ OpenXR模式启用失败")
return False
# 关闭系统
world.stopOpenXRInput()
world.shutdownOpenXR()
print("✓ 完整OpenXR功能测试完成")
return True
if __name__ == "__main__":
print("OpenXR功能测试")
print("===============")
# 创建世界实例(只创建一次)
world = MyWorld()
# 运行测试
print("1. 基本显示测试")
test_openxr_basic_display(world)
print("\n" + "="*50 + "\n")
print("2. 串流功能测试")
test_openxr_streaming(world)
print("\n" + "="*50 + "\n")
print("3. 完整功能测试")
test_openxr_full(world)
print("\n" + "="*50)
print("所有测试完成!")

View File

@ -0,0 +1,23 @@
# OpenXR 相关依赖
# 注意OpenXR Python绑定可能需要从源码编译
# 基础依赖
numpy>=1.18.0
PyOpenGL>=3.1.0
# Panda3D如果尚未安装
panda3d>=1.10.10
# 网络和串流相关
websockets>=8.0
aiohttp>=3.7.0
# 图像处理(用于视频编码/解码)
Pillow>=8.0.0
opencv-python>=4.5.0
# 音频处理(可选)
pyaudio>=0.2.11
# 注实际的OpenXR Python绑定需要根据平台和设备单独安装
# 可能需要从 https://github.com/KhronosGroup/OpenXR-SDK-Source 编译

279
test_openxr_simple.py Normal file
View File

@ -0,0 +1,279 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
简单的OpenXR测试脚本
在主程序基础上直接添加OpenXR相关代码进行测试
"""
import sys
import os
# 添加项目根目录到Python路径
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from main import MyWorld
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QHBoxLayout, QLabel
from PyQt5.QtGui import QPixmap, QImage
from PyQt5.QtCore import QTimer
# Panda3D imports
from panda3d.core import Texture, GraphicsOutput, ATSNone, GraphicsPipe, Vec3
def setup_openxr_simulation(world):
"""在主程序基础上设置简单的OpenXR模拟"""
print("🔧 设置OpenXR模拟环境...")
# 获取渲染尺寸
render_width = 1280
render_height = 720
# 创建左右眼渲染缓冲区
from panda3d.core import WindowProperties, FrameBufferProperties, GraphicsPipe
# 创建窗口属性
winprops = WindowProperties()
winprops.setSize(render_width//2, render_height)
# 创建帧缓冲区属性
fbprops = FrameBufferProperties()
fbprops.setRgbColor(True)
fbprops.setRgbaBits(8, 8, 8, 8)
fbprops.setDepthBits(24)
# 设置渲染标志和GSG
flags = GraphicsPipe.BFRefuseWindow
gsg = world.win.getGsg()
left_eye_buffer = world.graphicsEngine.makeOutput(
world.pipe,
"left_eye",
-1000,
fbprops,
winprops,
flags,
gsg,
world.win
)
right_eye_buffer = world.graphicsEngine.makeOutput(
world.pipe,
"right_eye",
-1000,
fbprops,
winprops,
flags,
gsg,
world.win
)
if not left_eye_buffer or not right_eye_buffer:
print("❌ 无法创建眼部渲染缓冲区")
return None, None
# 创建用于显示的左右眼纹理,使用与主程序相同的方式
left_eye_texture = Texture("left_eye_texture")
right_eye_texture = Texture("right_eye_texture")
left_eye_texture.set_format(Texture.F_rgba8) # 使用主程序的设置方式
right_eye_texture.set_format(Texture.F_rgba8)
left_eye_texture.set_component_type(Texture.T_unsigned_byte)
right_eye_texture.set_component_type(Texture.T_unsigned_byte)
# 将纹理附加到渲染缓冲区,使用与主程序相同的方式
left_eye_buffer.add_render_texture(left_eye_texture, GraphicsOutput.RTM_copy_ram)
right_eye_buffer.add_render_texture(right_eye_texture, GraphicsOutput.RTM_copy_ram)
# 创建左右眼相机
left_eye_camera = world.makeCamera(left_eye_buffer)
right_eye_camera = world.makeCamera(right_eye_buffer)
# 设置相机参数,确保与主相机一致
left_eye_camera.node().get_lens().set_fov(world.camLens.get_fov())
right_eye_camera.node().get_lens().set_fov(world.camLens.get_fov())
left_eye_camera.node().get_lens().set_near(world.camLens.get_near())
right_eye_camera.node().get_lens().set_near(world.camLens.get_near())
left_eye_camera.node().get_lens().set_far(world.camLens.get_far())
right_eye_camera.node().get_lens().set_far(world.camLens.get_far())
# 设置相机位置(模拟瞳距)
left_eye_camera.setPos(world.cam.getPos() + Vec3(-0.03, 0, 0)) # 瞳距的一半
right_eye_camera.setPos(world.cam.getPos() + Vec3(0.03, 0, 0)) # 瞳距的一半
# 设置相机朝向,确保与主相机一致
left_eye_camera.setHpr(world.cam.getHpr())
right_eye_camera.setHpr(world.cam.getHpr())
# 设置相机渲染场景
left_eye_camera.node().setScene(world.render)
right_eye_camera.node().setScene(world.render)
# 获取渲染管线的最终输出纹理
def get_final_render_texture():
"""获取渲染管线的最终输出纹理"""
# 修复:正确获取最终渲染纹理的方法
if world.win:
# 直接从主窗口获取最终渲染纹理
return world.win.get_texture()
return None
# 等待并获取渲染结果
def wait_for_render_texture():
"""等待并获取有效的渲染纹理"""
timeout = 100 # 超时帧数
for i in range(timeout):
final_texture = get_final_render_texture()
if final_texture and final_texture.hasRamImage():
return final_texture
world.graphicsEngine.renderFrame()
return None
# 获取最终渲染纹理作为左右眼纹理的基础
final_texture = wait_for_render_texture()
if final_texture:
# 使用与主渲染管线相同的纹理参数
left_eye_texture = final_texture.makeCopy()
right_eye_texture = final_texture.makeCopy()
# 确保纹理数据可用
world.graphicsEngine.extractTextureData(left_eye_texture, world.win.getGsg())
world.graphicsEngine.extractTextureData(right_eye_texture, world.win.getGsg())
print("✅ 成功获取渲染管线的最终输出纹理")
else:
print("⚠️ 无法获取渲染管线的最终输出纹理,使用默认设置")
print("✅ OpenXR模拟环境设置完成")
return left_eye_texture, right_eye_texture
def main():
"""主函数"""
print("🚀 启动OpenXR简单测试...")
# 创建应用程序和世界实例
app = QApplication(sys.argv)
world = MyWorld()
# 加载一些测试模型
print("📦 加载测试场景...")
from panda3d.core import CardMaker
# 创建地面
cm = CardMaker("ground")
cm.setFrame(-5, 5, -5, 5)
ground = world.render.attachNewNode(cm.generate())
ground.setPos(0, 0, -1)
ground.setColor(0.5, 0.8, 0.5, 1.0)
ground.setHpr(0, -90, 0)
# 创建测试立方体
cube = world.loader.loadModel("models/environment")
if cube:
cube.reparentTo(world.render)
cube.setScale(0.5)
cube.setPos(0, 0, 0)
else:
# 如果没有环境模型,创建简单立方体
cm_cube = CardMaker("cube")
cm_cube.setFrame(-0.5, 0.5, -0.5, 0.5)
cube = world.render.attachNewNode(cm_cube.generate())
cube.setPos(0, 0, 0)
# 设置OpenXR模拟
left_tex, right_tex = setup_openxr_simulation(world)
if not left_tex or not right_tex:
print("❌ OpenXR模拟设置失败")
return
# 创建显示窗口
window = QMainWindow()
window.setWindowTitle("OpenXR Simple Test")
window.setGeometry(100, 100, 800, 300)
central_widget = QWidget()
layout = QHBoxLayout()
central_widget.setLayout(layout)
left_label = QLabel("Left Eye")
right_label = QLabel("Right Eye")
layout.addWidget(left_label)
layout.addWidget(right_label)
window.setCentralWidget(central_widget)
window.show()
def update_display():
"""更新显示"""
try:
# 让RenderPipeline进行渲染
world.taskMgr.step()
# 渲染几帧以确保纹理更新
for _ in range(5):
world.graphicsEngine.renderFrame()
# 更新左眼纹理
if left_tex:
# 检查纹理是否具有RAM图像
if not left_tex.hasRamImage():
# 如果没有RAM图像尝试提取
gsg = world.win.getGsg()
if gsg:
world.graphicsEngine.extractTextureData(left_tex, gsg)
# 现在检查是否有RAM图像和有效数据
if left_tex.hasRamImage():
data = left_tex.getRamImage().getData()
width = left_tex.getXSize()
height = left_tex.getYSize()
expected_len = width * height * 4
# 检查数据长度是否正确
if len(data) == expected_len:
# 使用与主程序相同的方式创建图像
img = QImage(data, width, height, QImage.Format_ARGB32).mirrored()
pixmap = QPixmap.fromImage(img)
left_label.setPixmap(pixmap.scaled(400, 300))
left_label.setText("")
# 更新右眼纹理
if right_tex:
# 检查纹理是否具有RAM图像
if not right_tex.hasRamImage():
# 如果没有RAM图像尝试提取
gsg = world.win.getGsg()
if gsg:
world.graphicsEngine.extractTextureData(right_tex, gsg)
# 现在检查是否有RAM图像和有效数据
if right_tex.hasRamImage():
data = right_tex.getRamImage().getData()
width = right_tex.getXSize()
height = right_tex.getYSize()
expected_len = width * height * 4
# 检查数据长度是否正确
if len(data) == expected_len:
# 使用与主程序相同的方式创建图像
img = QImage(data, width, height, QImage.Format_ARGB32).mirrored()
pixmap = QPixmap.fromImage(img)
right_label.setPixmap(pixmap.scaled(400, 300))
right_label.setText("")
except Exception as e:
print(f"纹理更新错误: {str(e)}")
# 定时更新显示
timer = QTimer()
timer.timeout.connect(update_display)
timer.start(33) # 约30 FPS
# 运行应用程序
print("✅ OpenXR简单测试运行中...")
sys.exit(app.exec_())
if __name__ == "__main__":
main()

View File

@ -1,412 +0,0 @@
"""
VR控制面板
提供VR系统的GUI控制界面
包括VR启用/禁用ALVR串流控制性能监控等功能
"""
from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QGroupBox,
QPushButton, QLabel, QProgressBar, QSlider,
QSpinBox, QCheckBox, QComboBox, QTextEdit,
QGridLayout, QFrame, QSizePolicy)
from PyQt5.QtCore import Qt, QTimer, pyqtSignal
from PyQt5.QtGui import QFont, QColor, QPalette
class VRControlPanel(QWidget):
"""VR控制面板"""
# 信号定义
vr_enabled = pyqtSignal(bool)
alvr_streaming_changed = pyqtSignal(bool)
def __init__(self, world, parent=None):
super().__init__(parent)
self.world = world
self.setupUI()
self.connectSignals()
# 设置更新定时器
self.update_timer = QTimer()
self.update_timer.timeout.connect(self.updateStatus)
self.update_timer.start(1000) # 每秒更新一次
def setupUI(self):
"""设置界面"""
self.setWindowTitle("VR控制面板")
self.setMinimumSize(400, 600)
# 主布局
main_layout = QVBoxLayout(self)
# VR系统控制组
vr_control_group = self.createVRControlGroup()
main_layout.addWidget(vr_control_group)
# ALVR串流控制组
alvr_control_group = self.createALVRControlGroup()
main_layout.addWidget(alvr_control_group)
# 性能监控组
performance_group = self.createPerformanceGroup()
main_layout.addWidget(performance_group)
# 控制器状态组
controller_group = self.createControllerGroup()
main_layout.addWidget(controller_group)
# 日志输出组
log_group = self.createLogGroup()
main_layout.addWidget(log_group)
# 添加弹性空间
main_layout.addStretch()
def createVRControlGroup(self):
"""创建VR系统控制组"""
group = QGroupBox("VR系统控制")
layout = QVBoxLayout(group)
# 状态指示器
self.vr_status_label = QLabel("VR状态: 未启用")
self.vr_status_label.setStyleSheet("color: red; font-weight: bold;")
layout.addWidget(self.vr_status_label)
# 启用/禁用VR按钮
button_layout = QHBoxLayout()
self.enable_vr_button = QPushButton("启用VR")
self.enable_vr_button.clicked.connect(self.enableVR)
button_layout.addWidget(self.enable_vr_button)
self.disable_vr_button = QPushButton("禁用VR")
self.disable_vr_button.clicked.connect(self.disableVR)
self.disable_vr_button.setEnabled(False)
button_layout.addWidget(self.disable_vr_button)
layout.addLayout(button_layout)
# VR设备信息
self.vr_info_label = QLabel("VR设备: 未连接")
layout.addWidget(self.vr_info_label)
# VR选项
options_layout = QGridLayout()
# 控制器射线显示
self.show_rays_checkbox = QCheckBox("显示控制器射线")
self.show_rays_checkbox.stateChanged.connect(self.toggleControllerRays)
options_layout.addWidget(self.show_rays_checkbox, 0, 0)
# 手势识别
self.gesture_checkbox = QCheckBox("启用手势识别")
self.gesture_checkbox.stateChanged.connect(self.toggleGestureRecognition)
options_layout.addWidget(self.gesture_checkbox, 0, 1)
# VR交互
self.interaction_checkbox = QCheckBox("启用VR交互")
self.interaction_checkbox.setChecked(True)
self.interaction_checkbox.stateChanged.connect(self.toggleVRInteraction)
options_layout.addWidget(self.interaction_checkbox, 1, 0)
layout.addLayout(options_layout)
return group
def createALVRControlGroup(self):
"""创建ALVR串流控制组"""
group = QGroupBox("ALVR串流控制")
layout = QVBoxLayout(group)
# 连接状态
self.alvr_status_label = QLabel("ALVR状态: 未连接")
self.alvr_status_label.setStyleSheet("color: red; font-weight: bold;")
layout.addWidget(self.alvr_status_label)
# 串流控制按钮
button_layout = QHBoxLayout()
self.start_streaming_button = QPushButton("开始串流")
self.start_streaming_button.clicked.connect(self.startStreaming)
self.start_streaming_button.setEnabled(False)
button_layout.addWidget(self.start_streaming_button)
self.stop_streaming_button = QPushButton("停止串流")
self.stop_streaming_button.clicked.connect(self.stopStreaming)
self.stop_streaming_button.setEnabled(False)
button_layout.addWidget(self.stop_streaming_button)
layout.addLayout(button_layout)
# 串流质量设置
quality_layout = QGridLayout()
# 分辨率
quality_layout.addWidget(QLabel("分辨率宽度:"), 0, 0)
self.width_spinbox = QSpinBox()
self.width_spinbox.setRange(1920, 4096)
self.width_spinbox.setValue(2880)
self.width_spinbox.setSuffix(" px")
quality_layout.addWidget(self.width_spinbox, 0, 1)
quality_layout.addWidget(QLabel("分辨率高度:"), 1, 0)
self.height_spinbox = QSpinBox()
self.height_spinbox.setRange(1080, 2160)
self.height_spinbox.setValue(1700)
self.height_spinbox.setSuffix(" px")
quality_layout.addWidget(self.height_spinbox, 1, 1)
# 帧率
quality_layout.addWidget(QLabel("帧率:"), 2, 0)
self.fps_spinbox = QSpinBox()
self.fps_spinbox.setRange(60, 120)
self.fps_spinbox.setValue(72)
self.fps_spinbox.setSuffix(" fps")
quality_layout.addWidget(self.fps_spinbox, 2, 1)
# 比特率
quality_layout.addWidget(QLabel("比特率:"), 3, 0)
self.bitrate_spinbox = QSpinBox()
self.bitrate_spinbox.setRange(50, 500)
self.bitrate_spinbox.setValue(150)
self.bitrate_spinbox.setSuffix(" Mbps")
quality_layout.addWidget(self.bitrate_spinbox, 3, 1)
layout.addLayout(quality_layout)
# 应用设置按钮
self.apply_quality_button = QPushButton("应用质量设置")
self.apply_quality_button.clicked.connect(self.applyQualitySettings)
layout.addWidget(self.apply_quality_button)
return group
def createPerformanceGroup(self):
"""创建性能监控组"""
group = QGroupBox("性能监控")
layout = QGridLayout(group)
# FPS显示
layout.addWidget(QLabel("渲染FPS:"), 0, 0)
self.fps_label = QLabel("0")
self.fps_label.setStyleSheet("font-weight: bold; color: blue;")
layout.addWidget(self.fps_label, 0, 1)
# 串流FPS
layout.addWidget(QLabel("串流FPS:"), 1, 0)
self.stream_fps_label = QLabel("0")
self.stream_fps_label.setStyleSheet("font-weight: bold; color: green;")
layout.addWidget(self.stream_fps_label, 1, 1)
# 延迟
layout.addWidget(QLabel("延迟:"), 2, 0)
self.latency_label = QLabel("0 ms")
self.latency_label.setStyleSheet("font-weight: bold; color: orange;")
layout.addWidget(self.latency_label, 2, 1)
# 性能进度条
layout.addWidget(QLabel("CPU使用率:"), 3, 0)
self.cpu_progress = QProgressBar()
self.cpu_progress.setRange(0, 100)
layout.addWidget(self.cpu_progress, 3, 1)
layout.addWidget(QLabel("GPU使用率:"), 4, 0)
self.gpu_progress = QProgressBar()
self.gpu_progress.setRange(0, 100)
layout.addWidget(self.gpu_progress, 4, 1)
return group
def createControllerGroup(self):
"""创建控制器状态组"""
group = QGroupBox("控制器状态")
layout = QVBoxLayout(group)
# 控制器列表
self.controller_list = QTextEdit()
self.controller_list.setMaximumHeight(100)
self.controller_list.setReadOnly(True)
layout.addWidget(self.controller_list)
# 触觉反馈测试
haptic_layout = QHBoxLayout()
haptic_layout.addWidget(QLabel("触觉反馈测试:"))
self.haptic_button = QPushButton("发送震动")
self.haptic_button.clicked.connect(self.sendHapticFeedback)
haptic_layout.addWidget(self.haptic_button)
layout.addLayout(haptic_layout)
return group
def createLogGroup(self):
"""创建日志输出组"""
group = QGroupBox("系统日志")
layout = QVBoxLayout(group)
self.log_text = QTextEdit()
self.log_text.setMaximumHeight(150)
self.log_text.setReadOnly(True)
layout.addWidget(self.log_text)
# 清空日志按钮
clear_button = QPushButton("清空日志")
clear_button.clicked.connect(self.clearLog)
layout.addWidget(clear_button)
return group
def connectSignals(self):
"""连接信号"""
# 连接值变化信号
self.width_spinbox.valueChanged.connect(self.onQualityChanged)
self.height_spinbox.valueChanged.connect(self.onQualityChanged)
self.fps_spinbox.valueChanged.connect(self.onQualityChanged)
self.bitrate_spinbox.valueChanged.connect(self.onQualityChanged)
def enableVR(self):
"""启用VR"""
self.addLog("正在启用VR模式...")
if self.world.enableVRMode():
self.addLog("✓ VR模式已启用")
self.vr_enabled.emit(True)
else:
self.addLog("✗ VR模式启用失败")
def disableVR(self):
"""禁用VR"""
self.addLog("正在禁用VR模式...")
self.world.disableVRMode()
self.addLog("✓ VR模式已禁用")
self.vr_enabled.emit(False)
def startStreaming(self):
"""开始串流"""
self.addLog("正在开始ALVR串流...")
if self.world.startALVRStreaming():
self.addLog("✓ ALVR串流已开始")
self.alvr_streaming_changed.emit(True)
else:
self.addLog("✗ ALVR串流开始失败")
def stopStreaming(self):
"""停止串流"""
self.addLog("正在停止ALVR串流...")
self.world.stopALVRStreaming()
self.addLog("✓ ALVR串流已停止")
self.alvr_streaming_changed.emit(False)
def toggleControllerRays(self, checked):
"""切换控制器射线显示"""
self.world.showControllerRays(checked)
self.addLog(f"控制器射线: {'显示' if checked else '隐藏'}")
def toggleGestureRecognition(self, checked):
"""切换手势识别"""
self.world.setVRGestureEnabled(checked)
self.addLog(f"手势识别: {'启用' if checked else '禁用'}")
def toggleVRInteraction(self, checked):
"""切换VR交互"""
self.world.setVRInteractionEnabled(checked)
self.addLog(f"VR交互: {'启用' if checked else '禁用'}")
def applyQualitySettings(self):
"""应用质量设置"""
width = self.width_spinbox.value()
height = self.height_spinbox.value()
fps = self.fps_spinbox.value()
bitrate = self.bitrate_spinbox.value()
self.world.setALVRStreamQuality(width, height, fps, bitrate)
self.addLog(f"串流质量已设置: {width}x{height} @ {fps}fps, {bitrate}Mbps")
def sendHapticFeedback(self):
"""发送触觉反馈"""
controllers = self.world.getAllControllers()
if controllers:
controller_id = controllers[0] # 使用第一个控制器
self.world.sendHapticFeedback(controller_id, 0.5, 0.8)
self.addLog(f"已发送触觉反馈到控制器 {controller_id}")
else:
self.addLog("没有可用的控制器")
def onQualityChanged(self):
"""质量设置变化"""
# 可以在这里实现实时质量调整
pass
def updateStatus(self):
"""更新状态显示"""
# 更新VR状态
vr_status = self.world.getVRStatus()
# VR系统状态
if vr_status['vr_enabled']:
self.vr_status_label.setText("VR状态: 已启用")
self.vr_status_label.setStyleSheet("color: green; font-weight: bold;")
self.enable_vr_button.setEnabled(False)
self.disable_vr_button.setEnabled(True)
# 更新VR设备信息
vr_info = vr_status['vr_info']
if vr_info:
device_info = f"VR设备: {vr_info.get('hmd_manufacturer', 'Unknown')} {vr_info.get('hmd_model', 'Unknown')}"
self.vr_info_label.setText(device_info)
else:
self.vr_status_label.setText("VR状态: 未启用")
self.vr_status_label.setStyleSheet("color: red; font-weight: bold;")
self.enable_vr_button.setEnabled(True)
self.disable_vr_button.setEnabled(False)
self.vr_info_label.setText("VR设备: 未连接")
# ALVR状态
if vr_status['alvr_connected']:
self.alvr_status_label.setText("ALVR状态: 已连接")
self.alvr_status_label.setStyleSheet("color: green; font-weight: bold;")
self.start_streaming_button.setEnabled(not vr_status['alvr_streaming'])
self.stop_streaming_button.setEnabled(vr_status['alvr_streaming'])
else:
self.alvr_status_label.setText("ALVR状态: 未连接")
self.alvr_status_label.setStyleSheet("color: red; font-weight: bold;")
self.start_streaming_button.setEnabled(False)
self.stop_streaming_button.setEnabled(False)
# 性能统计
streaming_status = vr_status['streaming_status']
if streaming_status:
self.stream_fps_label.setText(str(streaming_status.get('fps', 0)))
self.latency_label.setText(f"{streaming_status.get('latency', 0)} ms")
# 控制器状态
controllers = vr_status['controllers']
if controllers:
controller_text = f"已连接 {len(controllers)} 个控制器:\n"
for i, controller_id in enumerate(controllers):
controller_text += f"控制器 {i+1}: ID {controller_id}\n"
self.controller_list.setText(controller_text)
else:
self.controller_list.setText("没有连接的控制器")
def addLog(self, message):
"""添加日志"""
import datetime
timestamp = datetime.datetime.now().strftime("%H:%M:%S")
log_message = f"[{timestamp}] {message}"
self.log_text.append(log_message)
print(log_message) # 同时输出到控制台
def clearLog(self):
"""清空日志"""
self.log_text.clear()
def closeEvent(self, event):
"""关闭事件"""
# 停止更新定时器
if hasattr(self, 'update_timer'):
self.update_timer.stop()
# 如果VR启用先禁用
if self.world.isVREnabled():
self.disableVR()
event.accept()

View File

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

View File

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

View File

@ -1,379 +0,0 @@
"""
VR显示管理模块
此模块负责VR立体渲染和显示包括
- 创建左右眼相机
- 设置渲染目标
- 应用畸变校正
- 管理渲染分辨率和质量
"""
from panda3d.core import (
NodePath, Camera, PerspectiveLens,
FrameBufferProperties, WindowProperties,
GraphicsOutput, GraphicsEngine,
Texture, GraphicsBuffer, Vec3,
RenderState, ColorWriteAttrib, ShaderAttrib
)
from direct.filter.FilterManager import FilterManager
class VRDisplay:
"""管理VR显示和渲染"""
def __init__(self, vr_system):
"""
初始化VR显示管理器
Args:
vr_system: VR系统实例
"""
self.vr_system = vr_system
self.world = vr_system.world
self.device = vr_system.device
# 渲染目标
self.left_buffer = None
self.right_buffer = None
self.left_texture = None
self.right_texture = None
# 相机
self.left_camera = None
self.right_camera = None
self.left_cam_node = None
self.right_cam_node = None
# 畸变校正
self.distortion_manager = None
self.is_initialized = False
# 渲染设置
self.render_resolution = vr_system.config.get("render_resolution", [2048, 2048])
self.ipd = vr_system.config.get("ipd", 0.064) # 默认瞳距64mm
def initialize(self):
"""初始化VR显示系统"""
print("正在初始化VR显示系统...")
# 1. 创建渲染目标
if not self._create_render_targets():
print("✗ 创建VR渲染目标失败")
return False
# 2. 创建立体相机
if not self._setup_stereo_cameras():
print("✗ 设置VR立体相机失败")
return False
# 3. 设置畸变校正
if not self._setup_distortion_correction():
print("✗ 设置畸变校正失败")
# 这不是致命错误,可以继续
print("⚠ 将使用未校正的渲染")
self.is_initialized = True
print("✓ VR显示系统初始化完成")
return True
def _create_render_targets(self):
"""创建VR渲染目标"""
try:
# 获取图形引擎和窗口
graphics_engine = self.world.graphicsEngine
main_window = self.world.win
# 创建帧缓冲属性
fb_props = FrameBufferProperties()
fb_props.setRgbColor(True)
fb_props.setRgbaBits(8, 8, 8, 8)
fb_props.setDepthBits(24)
fb_props.setMultisamples(4) # MSAA 4x
# 创建窗口属性
win_props = WindowProperties()
win_props.setSize(self.render_resolution[0], self.render_resolution[1])
# 创建左眼缓冲区
self.left_buffer = graphics_engine.makeOutput(
main_window.getPipe(), "left_eye_buffer",
-1, fb_props, win_props,
GraphicsOutput.BF_refuse_window,
main_window.getGsg(), main_window
)
if not self.left_buffer:
print("✗ 创建左眼缓冲区失败")
return False
self.left_buffer.setClearColor((0, 0, 0, 1))
self.left_texture = Texture()
self.left_buffer.addRenderTexture(
self.left_texture,
GraphicsOutput.RTM_copy_ram,
GraphicsOutput.RTP_color
)
# 创建右眼缓冲区
self.right_buffer = graphics_engine.makeOutput(
main_window.getPipe(), "right_eye_buffer",
-1, fb_props, win_props,
GraphicsOutput.BF_refuse_window,
main_window.getGsg(), main_window
)
if not self.right_buffer:
print("✗ 创建右眼缓冲区失败")
return False
self.right_buffer.setClearColor((0, 0, 0, 1))
self.right_texture = Texture()
self.right_buffer.addRenderTexture(
self.right_texture,
GraphicsOutput.RTM_copy_ram,
GraphicsOutput.RTP_color
)
print("✓ VR渲染目标创建成功")
return True
except Exception as e:
print(f"创建VR渲染目标时出错: {e}")
return False
def _setup_stereo_cameras(self):
"""设置立体相机"""
try:
# 获取主相机信息
main_cam = self.world.camera
main_cam_pos = main_cam.getPos()
main_cam_hpr = main_cam.getHpr()
# 创建左眼相机
self.left_cam_node = Camera("left_eye_camera")
left_lens = PerspectiveLens()
left_lens.setFov(90) # 90度视场角
left_lens.setNearFar(0.01, 1000)
self.left_cam_node.setLens(left_lens)
self.left_camera = NodePath(self.left_cam_node)
self.left_camera.reparentTo(main_cam)
# 设置左眼相机位置(向左偏移半个瞳距)
self.left_camera.setPos(-self.ipd/2, 0, 0)
# 创建右眼相机
self.right_cam_node = Camera("right_eye_camera")
right_lens = PerspectiveLens()
right_lens.setFov(90) # 90度视场角
right_lens.setNearFar(0.01, 1000)
self.right_cam_node.setLens(right_lens)
self.right_camera = NodePath(self.right_cam_node)
self.right_camera.reparentTo(main_cam)
# 设置右眼相机位置(向右偏移半个瞳距)
self.right_camera.setPos(self.ipd/2, 0, 0)
# 设置相机显示区域
self.left_cam_node.setScene(self.world.render)
self.right_cam_node.setScene(self.world.render)
# 连接相机到缓冲区
self.left_buffer.setSort(0)
self.left_buffer.setClearActive(True)
self.left_buffer.setActive(True)
self.left_buffer.addDisplayRegion().setCamera(self.left_camera)
self.right_buffer.setSort(0)
self.right_buffer.setClearActive(True)
self.right_buffer.setActive(True)
self.right_buffer.addDisplayRegion().setCamera(self.right_camera)
print("✓ VR立体相机设置成功")
return True
except Exception as e:
print(f"设置VR立体相机时出错: {e}")
return False
def _setup_distortion_correction(self):
"""设置畸变校正"""
try:
# 如果设备提供了畸变校正参数
if self.device and hasattr(self.device, 'get_distortion_parameters'):
distortion_params = self.device.get_distortion_parameters()
# 创建畸变校正着色器
# 这里使用FilterManager来应用后处理效果
self.distortion_manager = FilterManager(self.world.win, self.world.cam)
# 应用畸变校正着色器到左右眼纹理
# 具体实现取决于设备提供的畸变参数格式
print("✓ VR畸变校正设置成功")
return True
else:
print("⚠ 设备未提供畸变校正参数")
return False
except Exception as e:
print(f"设置畸变校正时出错: {e}")
return False
def update(self):
"""更新VR显示每帧调用"""
if not self.is_initialized:
return
# 1. 获取头部姿态
if self.device:
head_pose = self.device.get_head_pose()
if head_pose:
self.update_cameras(head_pose)
# 2. 如果使用动态分辨率,根据性能调整
if self.vr_system.config.get("performance", {}).get("dynamic_resolution", False):
self._update_dynamic_resolution()
def update_cameras(self, head_pose):
"""
根据头部姿态更新相机位置
Args:
head_pose: 包含位置和旋转的头部姿态数据
"""
if not self.is_initialized:
return
# 获取主相机
main_cam = self.world.camera
# 应用头部位置和旋转
if 'position' in head_pose:
main_cam.setPos(Vec3(*head_pose['position']))
if 'rotation' in head_pose:
main_cam.setHpr(Vec3(*head_pose['rotation']))
def render_frame(self):
"""渲染一帧VR画面"""
if not self.is_initialized:
return None, None
# 强制渲染左右眼缓冲区
self.world.graphicsEngine.renderFrame()
# 获取渲染结果
left_image = self.left_texture.getRamImage()
right_image = self.right_texture.getRamImage()
return left_image, right_image
def shutdown(self):
"""关闭VR显示系统"""
if not self.is_initialized:
return True
print("正在关闭VR显示系统...")
# 移除畸变校正
if self.distortion_manager:
self.distortion_manager.cleanup()
self.distortion_manager = None
# 移除相机
if self.left_camera:
self.left_camera.removeNode()
self.left_camera = None
if self.right_camera:
self.right_camera.removeNode()
self.right_camera = None
# 移除缓冲区
if self.left_buffer:
self.world.graphicsEngine.removeWindow(self.left_buffer)
self.left_buffer = None
if self.right_buffer:
self.world.graphicsEngine.removeWindow(self.right_buffer)
self.right_buffer = None
self.is_initialized = False
print("✓ VR显示系统已关闭")
return True
def set_resolution(self, width, height):
"""
设置VR渲染分辨率
Args:
width: 宽度像素
height: 高度像素
"""
if not self.is_initialized:
print("VR显示系统未初始化无法设置分辨率")
return False
try:
self.render_resolution = [width, height]
# 需要重新创建缓冲区
self.shutdown()
self.initialize()
print(f"✓ VR渲染分辨率已设置为 {width}x{height}")
return True
except Exception as e:
print(f"设置VR渲染分辨率时出错: {e}")
return False
def set_ipd(self, ipd):
"""
设置瞳距
Args:
ipd: 瞳距
"""
if not self.is_initialized:
print("VR显示系统未初始化无法设置瞳距")
return False
try:
self.ipd = ipd
# 更新相机位置
if self.left_camera and self.right_camera:
self.left_camera.setPos(-self.ipd/2, 0, 0)
self.right_camera.setPos(self.ipd/2, 0, 0)
print(f"✓ VR瞳距已设置为 {ipd}m")
return True
except Exception as e:
print(f"设置VR瞳距时出错: {e}")
return False
def _update_dynamic_resolution(self):
"""根据性能动态调整渲染分辨率"""
# 获取当前帧率
clock = self.world.globalClock
fps = clock.getAverageFrameRate()
# 目标帧率
target_fps = self.vr_system.config.get("refresh_rate", 90)
# 如果帧率低于目标的90%,降低分辨率
if fps < target_fps * 0.9:
current_width, current_height = self.render_resolution
new_width = int(current_width * 0.9)
new_height = int(current_height * 0.9)
# 设置最低分辨率限制
if new_width >= 1024 and new_height >= 1024:
self.set_resolution(new_width, new_height)
# 如果帧率高于目标的110%,提高分辨率
elif fps > target_fps * 1.1:
current_width, current_height = self.render_resolution
new_width = int(current_width * 1.1)
new_height = int(current_height * 1.1)
# 设置最高分辨率限制
if new_width <= 4096 and new_height <= 4096:
self.set_resolution(new_width, new_height)

View File

@ -1,363 +0,0 @@
"""
VR输入处理模块
此模块负责处理VR输入设备包括
- 控制器跟踪
- 按钮和触摸板输入
- 手势识别
- 触觉反馈
"""
from panda3d.core import NodePath, LineSegs, Vec3, Vec4
from panda3d.core import CollisionRay, CollisionNode, CollisionTraverser, CollisionHandlerQueue
from panda3d.core import BitMask32
from direct.task import Task
import math
import time
class VRInput:
"""处理VR输入设备"""
def __init__(self, vr_system):
"""
初始化VR输入处理器
Args:
vr_system: VR系统实例
"""
self.vr_system = vr_system
self.world = vr_system.world
self.device = vr_system.device
# 控制器
self.controllers = {}
self.controller_models = {}
self.controller_rays = {}
# 输入状态
self.button_states = {}
self.analog_states = {}
self.last_button_states = {}
# 配置
self.show_rays = True
self.ray_length = 100.0
self.ray_color = Vec4(0, 0.5, 1, 0.8)
# 功能标志
self.gesture_enabled = True
self.interaction_enabled = True
self.haptic_enabled = True
# 状态
self.is_initialized = False
self.tracking_origin = None
# 碰撞检测
self.traverser = CollisionTraverser("vr_traverser")
self.queue = CollisionHandlerQueue()
def initialize(self):
"""初始化VR输入系统"""
print("正在初始化VR输入系统...")
if not self.device:
print("✗ VR设备未初始化无法设置输入")
return False
# 1. 设置跟踪原点
self.tracking_origin = self.world.render.attachNewNode("vr_tracking_origin")
# 2. 设置控制器
if not self._setup_controllers():
print("✗ 设置VR控制器失败")
# 这不是致命错误,可以继续
print("⚠ VR将在没有控制器的情况下运行")
# 3. 定义输入动作
self._define_actions()
# 4. 创建控制器射线
if self.show_rays:
self._create_controller_rays()
self.is_initialized = True
print("✓ VR输入系统初始化完成")
return True
def _setup_controllers(self):
"""设置控制器跟踪"""
try:
# 获取设备控制器信息
if hasattr(self.device, 'get_controllers'):
controllers_info = self.device.get_controllers()
if not controllers_info:
print("⚠ 未检测到VR控制器")
return False
# 为每个控制器创建节点
for controller_id, info in controllers_info.items():
controller_node = self.tracking_origin.attachNewNode(f"controller_{controller_id}")
# 加载控制器模型(如果有)
model_path = info.get('model_path')
if model_path:
try:
controller_model = self.world.loader.loadModel(model_path)
controller_model.reparentTo(controller_node)
self.controller_models[controller_id] = controller_model
except Exception as e:
print(f"加载控制器模型失败: {e}")
# 使用默认模型
self._create_default_controller_model(controller_node, controller_id)
else:
# 使用默认模型
self._create_default_controller_model(controller_node, controller_id)
# 存储控制器节点
self.controllers[controller_id] = controller_node
# 初始化按钮状态
self.button_states[controller_id] = {}
self.analog_states[controller_id] = {}
self.last_button_states[controller_id] = {}
print(f"✓ 已设置 {len(self.controllers)} 个VR控制器")
return True
else:
print("⚠ VR设备不支持控制器跟踪")
return False
except Exception as e:
print(f"设置VR控制器时出错: {e}")
return False
def _create_default_controller_model(self, parent_node, controller_id):
"""创建默认控制器模型"""
from panda3d.core import GeomVertexFormat, GeomVertexData, Geom, GeomTriangles
from panda3d.core import GeomVertexWriter, GeomNode
# 创建简单的控制器几何体
format = GeomVertexFormat.getV3n3c4()
vdata = GeomVertexData('controller', format, Geom.UHStatic)
vertex = GeomVertexWriter(vdata, 'vertex')
normal = GeomVertexWriter(vdata, 'normal')
color = GeomVertexWriter(vdata, 'color')
# 简单的手柄形状
# 顶部
vertex.addData3f(0, 0, 0.1)
normal.addData3f(0, 0, 1)
color.addData4f(0.8, 0.8, 0.8, 1)
# 底部圆形的顶点
radius = 0.03
segments = 8
for i in range(segments):
angle = 2.0 * math.pi * i / segments
x = radius * math.cos(angle)
y = radius * math.sin(angle)
vertex.addData3f(x, y, -0.1)
normal.addData3f(x, y, 0)
color.addData4f(0.5, 0.5, 0.5, 1)
# 创建三角形
tris = GeomTriangles(Geom.UHStatic)
# 连接顶部到圆形底部
for i in range(segments):
tris.addVertices(0, i + 1, (i + 1) % segments + 1)
tris.closePrimitive()
# 创建几何体
geom = Geom(vdata)
geom.addPrimitive(tris)
# 创建GeomNode
node = GeomNode(f'controller_{controller_id}_geom')
node.addGeom(geom)
# 创建NodePath并附加到父节点
controller_model = NodePath(node)
controller_model.reparentTo(parent_node)
# 存储模型引用
self.controller_models[controller_id] = controller_model
def _define_actions(self):
"""定义输入动作映射"""
# 这里可以定义按钮到动作的映射
self.input_actions = {
"trigger": {
"description": "触发器",
"button": "trigger",
"action": self._on_trigger
},
"grip": {
"description": "握把",
"button": "grip",
"action": self._on_grip
},
"menu": {
"description": "菜单按钮",
"button": "menu",
"action": self._on_menu
},
"thumbstick_press": {
"description": "摇杆按下",
"button": "thumbstick",
"action": self._on_thumbstick_press
},
"teleport": {
"description": "传送",
"button": "thumbstick",
"analog": True,
"direction": "up",
"action": self._on_teleport
}
}
def _create_controller_rays(self):
"""创建控制器射线可视化"""
for controller_id, controller in self.controllers.items():
# 创建射线线段
ls = LineSegs()
ls.setThickness(2.0)
ls.setColor(self.ray_color)
ls.moveTo(0, 0, 0)
ls.drawTo(0, self.ray_length, 0)
# 创建射线节点
ray_node = ls.create()
ray_path = NodePath(ray_node)
ray_path.reparentTo(controller)
# 默认隐藏
ray_path.hide()
# 存储射线引用
self.controller_rays[controller_id] = ray_path
def update(self):
"""更新控制器状态(每帧调用)"""
if not self.is_initialized:
return
# 1. 更新控制器位置和旋转
self._update_controller_poses()
# 2. 更新按钮状态
self._update_button_states()
# 3. 处理输入事件
self._process_input()
# 4. 更新射线(如果启用)
if self.show_rays:
self._update_controller_rays()
def _update_controller_poses(self):
"""更新控制器位置和旋转"""
if not self.device or not hasattr(self.device, 'get_controller_poses'):
return
# 获取所有控制器的姿态
poses = self.device.get_controller_poses()
if not poses:
return
# 更新每个控制器的位置和旋转
for controller_id, pose in poses.items():
if controller_id in self.controllers:
controller = self.controllers[controller_id]
if 'position' in pose:
controller.setPos(Vec3(*pose['position']))
if 'rotation' in pose:
controller.setHpr(Vec3(*pose['rotation']))
def _update_button_states(self):
"""更新按钮状态"""
if not self.device or not hasattr(self.device, 'get_controller_inputs'):
return
# 获取所有控制器的输入状态
inputs = self.device.get_controller_inputs()
if not inputs:
return
# 更新每个控制器的按钮和模拟输入状态
for controller_id, input_data in inputs.items():
if controller_id in self.controllers:
# 保存上一帧的按钮状态
self.last_button_states[controller_id] = self.button_states[controller_id].copy()
# 更新按钮状态
if 'buttons' in input_data:
self.button_states[controller_id] = input_data['buttons']
# 更新模拟输入状态
if 'analogs' in input_data:
self.analog_states[controller_id] = input_data['analogs']
def _process_input(self):
"""处理输入事件"""
if not self.interaction_enabled:
return
# 检查每个定义的动作
for action_name, action_def in self.input_actions.items():
button = action_def.get('button')
for controller_id in self.controllers:
# 处理按钮动作
if not action_def.get('analog', False):
# 检查按钮是否刚被按下
if (button in self.button_states.get(controller_id, {}) and
button in self.last_button_states.get(controller_id, {}) and
self.button_states[controller_id][button] and
not self.last_button_states[controller_id][button]):
# 调用动作处理函数
if 'action' in action_def:
action_def['action'](controller_id)
# 处理模拟输入动作
else:
if button in self.analog_states.get(controller_id, {}):
analog_value = self.analog_states[controller_id][button]
direction = action_def.get('direction')
# 根据方向检查模拟输入
if direction == 'up' and analog_value[1] > 0.7:
if 'action' in action_def:
action_def['action'](controller_id)
elif direction == 'down' and analog_value[1] < -0.7:
if 'action' in action_def:
action_def['action'](controller_id)
elif direction == 'left' and analog_value[0] < -0.7:
if 'action' in action_def:
action_def['action'](controller_id)
elif direction == 'right' and analog_value[0] > 0.7:
if 'action' in action_def:
action_def['action'](controller_id)
def _update_controller_rays(self):
"""更新控制器射线可视化"""
for controller_id, ray in self.controller_rays.items():
if self.show_rays:
ray.show()
# 执行射线检测
if controller_id in self.controllers:
controller = self.controllers[controller_id]
origin = controller.getPos(self.world.render)
direction = Vec3(0, 1, 0) # 控制器前方
direction = self.world.render.getRelativeVector(controller, direction)
# 射线检测
result = self._perform_ray_test(origin, direction)
#

View File

@ -1,380 +0,0 @@
"""
VR系统核心管理模块
此模块提供VR系统的核心功能负责初始化和协调其他VR组件
"""
import os
import json
from panda3d.core import loadPrcFileData, WindowProperties
from direct.task import Task
class VRSystem:
"""VR系统的核心管理类负责初始化和协调其他VR组件"""
def __init__(self, world):
"""
初始化VR系统
Args:
world: 游戏世界实例提供对渲染器和场景的访问
"""
self.world = world
self.display = None
self.input = None
self.streaming = None
self.device = None
self.is_initialized = False
self.config = self._load_config()
# 状态变量
self.vr_enabled = False
self.streaming_enabled = False
print("✓ VR系统核心已创建")
def _load_config(self):
"""加载VR配置"""
config_path = os.path.join(os.path.dirname(__file__), "vr_config.json")
default_config = {
"preferred_device": "openxr",
"render_resolution": [2048, 2048],
"refresh_rate": 90,
"ipd": 0.064, # 瞳距,单位:米
"streaming": {
"enabled": True,
"encoder": "h264",
"bitrate": 30000000, # 30 Mbps
"use_hardware_encoding": True
},
"performance": {
"dynamic_resolution": True,
"fixed_foveated_rendering": True,
"motion_smoothing": True
}
}
try:
if os.path.exists(config_path):
with open(config_path, 'r') as f:
return json.load(f)
else:
# 如果配置文件不存在,创建默认配置
with open(config_path, 'w') as f:
json.dump(default_config, f, indent=4)
return default_config
except Exception as e:
print(f"加载VR配置失败: {e}")
return default_config
def save_config(self):
"""保存VR配置"""
config_path = os.path.join(os.path.dirname(__file__), "vr_config.json")
try:
with open(config_path, 'w') as f:
json.dump(self.config, f, indent=4)
print("✓ VR配置已保存")
return True
except Exception as e:
print(f"保存VR配置失败: {e}")
return False
def initialize(self):
"""初始化VR系统"""
if self.is_initialized:
print("VR系统已经初始化")
return True
print("正在初始化VR系统...")
# 1. 导入必要的模块(延迟导入以避免循环依赖)
from .vr_display import VRDisplay
from .vr_input import VRInput
from .vr_streaming import VRStreaming
# 2. 根据配置选择设备实现
device_type = self.config.get("preferred_device", "openxr")
device_created = self._create_device(device_type)
if not device_created:
print("✗ 创建VR设备失败")
return False
# 3. 创建显示、输入和串流组件
self.display = VRDisplay(self)
self.input = VRInput(self)
self.streaming = VRStreaming(self)
# 4. 初始化各组件
display_initialized = self.display.initialize()
if not display_initialized:
print("✗ 初始化VR显示失败")
return False
input_initialized = self.input.initialize()
if not input_initialized:
print("✗ 初始化VR输入失败")
return False
# 5. 设置更新任务
taskMgr = self.world.taskMgr
taskMgr.add(self.update, "vr_system_update")
self.is_initialized = True
self.vr_enabled = True
print("✓ VR系统初始化完成")
return True
def _create_device(self, device_type):
"""创建VR设备实现"""
try:
if device_type == "openxr":
from .devices.openxr_device import OpenXRDevice
self.device = OpenXRDevice()
elif device_type == "oculus":
from .devices.oculus_device import OculusDevice
self.device = OculusDevice()
elif device_type == "steamvr":
from .devices.steamvr_device import SteamVRDevice
self.device = SteamVRDevice()
else:
print(f"不支持的设备类型: {device_type}")
return False
device_initialized = self.device.initialize()
if not device_initialized:
print(f"✗ 初始化{device_type}设备失败")
return False
print(f"✓ 已创建并初始化{device_type}设备")
return True
except ImportError as e:
print(f"导入设备模块失败: {e}")
return False
except Exception as e:
print(f"创建设备实例失败: {e}")
return False
def shutdown(self):
"""关闭VR系统"""
if not self.is_initialized:
print("VR系统未初始化")
return True
print("正在关闭VR系统...")
# 1. 停止串流
if self.streaming and self.streaming_enabled:
self.stop_streaming()
# 2. 关闭各组件
if self.input:
self.input.shutdown()
if self.display:
self.display.shutdown()
if self.device:
self.device.shutdown()
# 3. 移除更新任务
taskMgr = self.world.taskMgr
taskMgr.remove("vr_system_update")
self.is_initialized = False
self.vr_enabled = False
print("✓ VR系统已关闭")
return True
def update(self, task):
"""更新VR系统状态每帧调用"""
if not self.is_initialized:
return Task.cont
# 1. 更新设备状态
if self.device:
self.device.update()
# 2. 更新输入
if self.input:
self.input.update()
# 3. 更新显示
if self.display:
self.display.update()
# 4. 更新串流
if self.streaming and self.streaming_enabled:
self.streaming.update()
return Task.cont
def start_streaming(self):
"""开始VR画面串流"""
if not self.is_initialized:
print("VR系统未初始化无法开始串流")
return False
if not self.streaming:
print("VR串流组件未创建")
return False
if self.streaming_enabled:
print("VR串流已经启动")
return True
result = self.streaming.start()
if result:
self.streaming_enabled = True
print("✓ VR串流已启动")
else:
print("✗ 启动VR串流失败")
return result
def stop_streaming(self):
"""停止VR画面串流"""
if not self.streaming or not self.streaming_enabled:
print("VR串流未启动")
return True
result = self.streaming.stop()
if result:
self.streaming_enabled = False
print("✓ VR串流已停止")
else:
print("✗ 停止VR串流失败")
return result
def get_status(self):
"""获取VR系统状态信息"""
status = {
"initialized": self.is_initialized,
"vr_enabled": self.vr_enabled,
"streaming_enabled": self.streaming_enabled,
"device_type": self.config.get("preferred_device", "unknown"),
"render_resolution": self.config.get("render_resolution", [0, 0]),
"refresh_rate": self.config.get("refresh_rate", 0)
}
# 添加设备特定信息
if self.device:
device_status = self.device.get_status()
status.update(device_status)
# 添加串流信息
if self.streaming:
streaming_status = self.streaming.get_statistics()
status["streaming"] = streaming_status
return status
def set_render_resolution(self, width, height):
"""设置VR渲染分辨率"""
if not self.is_initialized:
print("VR系统未初始化无法设置渲染分辨率")
return False
self.config["render_resolution"] = [width, height]
if self.display:
result = self.display.set_resolution(width, height)
if result:
print(f"✓ VR渲染分辨率已设置为 {width}x{height}")
self.save_config()
return result
return False
def set_refresh_rate(self, rate):
"""设置VR刷新率"""
if not self.is_initialized:
print("VR系统未初始化无法设置刷新率")
return False
self.config["refresh_rate"] = rate
if self.device:
result = self.device.set_refresh_rate(rate)
if result:
print(f"✓ VR刷新率已设置为 {rate}Hz")
self.save_config()
return result
return False
def set_ipd(self, ipd):
"""设置瞳距(单位:米)"""
if not self.is_initialized:
print("VR系统未初始化无法设置瞳距")
return False
self.config["ipd"] = ipd
if self.display:
result = self.display.set_ipd(ipd)
if result:
print(f"✓ VR瞳距已设置为 {ipd}m")
self.save_config()
return result
return False
def set_streaming_quality(self, bitrate=None, encoder=None, use_hardware=None):
"""设置串流质量参数"""
if not self.streaming:
print("VR串流组件未创建无法设置串流质量")
return False
streaming_config = self.config.get("streaming", {})
if bitrate is not None:
streaming_config["bitrate"] = bitrate
if encoder is not None:
streaming_config["encoder"] = encoder
if use_hardware is not None:
streaming_config["use_hardware_encoding"] = use_hardware
self.config["streaming"] = streaming_config
result = self.streaming.set_quality(
bitrate=streaming_config.get("bitrate"),
encoder=streaming_config.get("encoder"),
use_hardware=streaming_config.get("use_hardware_encoding")
)
if result:
print("✓ VR串流质量参数已更新")
self.save_config()
return result
def calibrate(self):
"""启动VR校准过程"""
if not self.is_initialized:
print("VR系统未初始化无法校准")
return False
print("正在启动VR校准...")
# 导入校准模块
from .vr_calibration import start_calibration
result = start_calibration(self)
if result:
print("✓ VR校准完成")
else:
print("✗ VR校准失败或被取消")
return result
def is_vr_enabled(self):
"""检查VR是否启用"""
return self.vr_enabled
def is_streaming(self):
"""检查是否正在串流"""
return self.streaming_enabled

View File

@ -1,500 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
VR显示功能演示脚本
演示引擎画面在VR中的显示效果包括ALVR串流
"""
import sys
import os
import time
from main import MyWorld
def demo_vr_basic_display():
"""演示基本VR显示功能"""
print("=== VR基本显示演示 ===")
# 创建世界实例
world = MyWorld()
# 加载一些模型来展示
print("📦 加载演示场景...")
try:
# 创建一些简单的几何体作为演示
from panda3d.core import CardMaker, Vec4
# 创建地面
cm = CardMaker("ground")
cm.setFrame(-10, 10, -10, 10)
ground = world.render.attachNewNode(cm.generate())
ground.setPos(0, 0, -1)
ground.setColor(0.5, 0.8, 0.5, 1.0)
ground.setHpr(0, -90, 0)
# 创建一些立方体
for i in range(5):
for j in range(5):
cube = world.loader.loadModel("environment")
if not cube:
# 如果没有environment模型创建简单的立方体
cm_cube = CardMaker(f"cube_{i}_{j}")
cm_cube.setFrame(-0.5, 0.5, -0.5, 0.5)
cube = world.render.attachNewNode(cm_cube.generate())
cube.setPos(i * 2 - 4, j * 2 - 4, 0)
cube.setScale(0.5)
cube.setColor(i/5.0, j/5.0, 1.0, 1.0)
print("✓ 演示场景加载完成")
except Exception as e:
print(f"场景加载错误: {str(e)}")
# 初始化VR系统
print("\n🥽 初始化VR显示系统...")
vr_success = world.initializeVR()
if vr_success:
print("✓ VR系统初始化成功")
vr_info = world.getVRInfo()
print(f"\n📊 VR显示信息:")
print(f" - 模式: {vr_info['mode']}")
print(f" - 渲染尺寸: {vr_info['render_size']}")
print(f" - 模拟模式: {vr_info['simulation_mode']}")
if vr_info['simulation_mode']:
print("\n💡 模拟模式说明:")
print(" - 左右眼视图将并排显示在主窗口")
print(" - 可以看到立体渲染效果")
print(" - 头部会有轻微的模拟摆动")
else:
print("\n💡 真实VR模式:")
print(" - 画面将渲染到VR头盔")
print(" - 支持真实的头部追踪")
print(" - 控制器交互可用")
# 运行几秒钟以展示效果
print(f"\n🎮 VR显示演示运行中... (10秒)")
print(" 观察主窗口中的立体渲染效果")
start_time = time.time()
while time.time() - start_time < 10:
# 处理Panda3D事件
if hasattr(world, 'taskMgr'):
world.taskMgr.step()
time.sleep(0.016) # ~60 FPS
print("✓ VR显示演示完成")
else:
print("✗ VR系统初始化失败")
return False
# 关闭VR系统
world.shutdownVR()
print("\n✓ VR系统已关闭")
return True
def demo_alvr_streaming():
"""演示ALVR串流功能"""
print("=== ALVR串流显示演示 ===")
# 创建世界实例
world = MyWorld()
# 首先初始化VR系统
print("🥽 初始化VR系统...")
if not world.initializeVR():
print("✗ VR系统初始化失败")
return False
# 检查是否为直连模式SteamVR已运行
if world.alvr_streamer._is_steamvr_active():
print("💡 检测到SteamVR正在运行将使用直连模式")
print(" 在直连模式下画面将直接通过SteamVR传输到头显")
print(" 无需通过ALVR服务器进行额外的串流处理")
# 初始化ALVR串流
print("\n📡 初始化ALVR串流...")
print("💡 检测到VR设备已连接正在尝试连接ALVR服务器...")
alvr_success = world.initializeALVR()
if alvr_success:
print("✓ ALVR串流初始化成功")
# 设置串流质量
print("\n🎥 配置串流质量...")
quality_set = world.setALVRStreamQuality(2880, 1700, 72, 150)
if quality_set:
print(" ✓ 串流质量: 2880x1700@72fps, 150Mbps")
# 获取串流状态
status = world.getALVRStreamingStatus()
print(f"\n📊 ALVR串流状态:")
print(f" - 连接状态: {status['connected']}")
print(f" - 串流状态: {status['streaming']}")
print(f" - 分辨率: {status['resolution']}")
print(f" - 帧率: {status['fps']}")
print(f" - 码率: {status['bitrate']}Mbps")
# 开始串流
print(f"\n🚀 开始ALVR串流...")
if world.startALVRStreaming():
print("✓ ALVR串流已开始")
print(f"\n💡 ALVR串流说明:")
print(" - 引擎画面正在串流到VR头盔")
print(" - 支持Quest 2/3/Pico 4等VR设备")
print(" - 确保ALVR客户端在头盔中运行")
print(" - 检查网络连接质量")
# 运行串流演示
print(f"\n📡 ALVR串流演示运行中... (15秒)")
start_time = time.time()
while time.time() - start_time < 15:
# 更新串流状态
current_status = world.getALVRStreamingStatus()
if time.time() - start_time > 5: # 5秒后显示状态更新
print(f" 当前FPS: {current_status['fps']}, 延迟: {current_status['latency']}ms")
if hasattr(world, 'taskMgr'):
world.taskMgr.step()
time.sleep(0.016)
# 停止串流
print("\n🛑 停止ALVR串流...")
world.stopALVRStreaming()
print("✓ ALVR串流已停止")
else:
print("⚠ ALVR串流启动失败")
print(" 可能原因:")
print(" - ALVR服务器未运行")
print(" - VR客户端未连接")
print(" - 网络连接问题")
print(" - 防火墙阻止连接")
else:
print("⚠ ALVR串流初始化失败")
print("💡 这可能是因为:")
print(" 1. ALVR服务器未运行")
print(" 2. SteamVR正在使用独占模式")
print(" 3. 端口冲突")
print("\n🔧 Pico 4有线连接设置建议:")
print(" 1. 确保ALVR服务器正在运行")
print(" 2. 检查任务管理器中是否有alvr_server进程")
print(" 3. 尝试重启ALVR服务器")
print(" 4. 确认防火墙允许ALVR通信")
print(" 5. 检查ALVR服务器端口设置")
print(" 6. 如果SteamVR正在运行尝试先关闭它")
print("\n💡 直连模式说明:")
print(" 如果您使用的是有线连接如Pico 4通过USB-C连接")
print(" 并且SteamVR已经可以正常显示画面您可能不需要ALVR串流")
print(" SteamVR会直接处理渲染并将画面传输到头显")
# 关闭系统
world.shutdownALVR()
world.shutdownVR()
print("\n✓ 所有系统已关闭")
return True
def demo_vr_interaction():
"""演示VR交互功能"""
print("=== VR交互显示演示 ===")
# 创建世界实例
world = MyWorld()
# 初始化VR系统
print("🥽 初始化VR系统...")
if not world.initializeVR():
print("✗ VR系统初始化失败")
return False
# 启动VR输入处理
print("\n🎮 启动VR输入处理...")
if world.startVRInput():
print("✓ VR输入处理已启动")
# 显示控制器射线
world.showControllerRays(True)
print("✓ 控制器射线已启用")
# 启用VR交互
world.setVRInteractionEnabled(True)
world.setVRGestureEnabled(True)
print("✓ VR交互和手势识别已启用")
# 获取控制器状态
print(f"\n🎮 控制器状态:")
controllers = world.getAllControllers()
for i, controller in enumerate(controllers):
if controller:
state = world.getControllerState(i)
print(f" 控制器 {i}: 可用")
if state:
print(f" - 连接: {state.get('connected', False)}")
print(f" - 扳机: {state.get('trigger', 0):.2f}")
print(f" - 握把: {state.get('grip', 0):.2f}")
# 运行交互演示
print(f"\n🤏 VR交互演示运行中... (10秒)")
print(" - 控制器射线可见")
print(" - 扳机:抓取物体")
print(" - 握把:切换交互模式")
print(" - 触摸板:导航控制")
start_time = time.time()
while time.time() - start_time < 10:
if hasattr(world, 'taskMgr'):
world.taskMgr.step()
time.sleep(0.016)
# 停止VR输入
world.stopVRInput()
print("✓ VR输入处理已停止")
else:
print("⚠ VR输入处理启动失败")
# 关闭VR系统
world.shutdownVR()
print("\n✓ VR系统已关闭")
return True
def demo_comprehensive_vr():
"""综合VR显示演示"""
print("=== 综合VR显示演示 ===")
# 创建世界实例
world = MyWorld()
# 创建丰富的演示场景
print("🎨 创建VR演示场景...")
try:
from panda3d.core import CardMaker, Vec4, AmbientLight, DirectionalLight
# 添加光照
ambient_light = AmbientLight('ambient')
ambient_light.setColor(Vec4(0.3, 0.3, 0.3, 1))
ambient_light_np = world.render.attachNewNode(ambient_light)
world.render.setLight(ambient_light_np)
directional_light = DirectionalLight('directional')
directional_light.setColor(Vec4(0.8, 0.8, 0.8, 1))
directional_light.setDirection(Vec4(-1, -1, -1, 0))
directional_light_np = world.render.attachNewNode(directional_light)
world.render.setLight(directional_light_np)
# 创建地面网格
for i in range(-5, 6):
for j in range(-5, 6):
cm = CardMaker(f"grid_{i}_{j}")
cm.setFrame(-0.4, 0.4, -0.4, 0.4)
grid = world.render.attachNewNode(cm.generate())
grid.setPos(i * 1.0, j * 1.0, -1)
grid.setHpr(0, -90, 0)
# 棋盘格颜色
if (i + j) % 2 == 0:
grid.setColor(0.8, 0.8, 0.8, 1.0)
else:
grid.setColor(0.6, 0.6, 0.6, 1.0)
# 创建一些彩色立方体
for i in range(8):
cm = CardMaker(f"cube_{i}")
cm.setFrame(-0.3, 0.3, -0.3, 0.3)
cube = world.render.attachNewNode(cm.generate())
import math
angle = i * math.pi * 2 / 8
radius = 3
cube.setPos(math.cos(angle) * radius, math.sin(angle) * radius, 0.5)
cube.setHpr(i * 45, 0, 0)
cube.setScale(1, 1, 2)
# 彩虹颜色
hue = i / 8.0
r = abs(math.sin(hue * math.pi * 2))
g = abs(math.sin((hue + 0.33) * math.pi * 2))
b = abs(math.sin((hue + 0.66) * math.pi * 2))
cube.setColor(r, g, b, 1.0)
print("✓ VR演示场景创建完成")
except Exception as e:
print(f"场景创建错误: {str(e)}")
# 完整的VR系统演示
print("\n🥽 启动完整VR系统...")
# 1. 初始化VR
if not world.initializeVR():
print("✗ VR系统初始化失败")
return False
print("✓ VR显示系统已启动")
# 2. 启动VR输入
if world.startVRInput():
world.showControllerRays(True)
world.setVRInteractionEnabled(True)
print("✓ VR交互系统已启动")
# 3. 尝试启动ALVR串流
if world.initializeALVR():
world.setALVRStreamQuality(2880, 1700, 72, 100)
if world.startALVRStreaming():
print("✓ ALVR串流已启动")
else:
print("⚠ ALVR串流启动失败")
else:
print("⚠ ALVR初始化失败正常如果没有ALVR服务器")
# 运行综合演示
print(f"\n🎬 综合VR显示演示运行中... (20秒)")
print("💡 功能说明:")
print(" - 立体渲染显示")
print(" - 头部追踪(模拟或真实)")
print(" - 控制器可视化")
print(" - ALVR无线串流")
print(" - VR交互功能")
start_time = time.time()
frame_count = 0
while time.time() - start_time < 20:
# 处理引擎更新
if hasattr(world, 'taskMgr'):
world.taskMgr.step()
frame_count += 1
# 每5秒显示一次状态
elapsed = time.time() - start_time
if frame_count % 300 == 0: # 约每5秒
fps = frame_count / elapsed
print(f" 运行状态: {elapsed:.1f}s, FPS: {fps:.1f}")
# 显示VR状态
vr_info = world.getVRInfo()
print(f" VR模式: {vr_info['mode']}, 分辨率: {vr_info['render_size']}")
if world.isALVRStreaming():
alvr_status = world.getALVRStreamingStatus()
print(f" ALVR: FPS {alvr_status['fps']}, 延迟 {alvr_status['latency']}ms")
time.sleep(0.016) # ~60 FPS
print("✓ 综合VR演示完成")
# 关闭所有系统
world.stopVRInput()
world.stopALVRStreaming()
world.shutdownALVR()
world.shutdownVR()
print("\n✓ 所有VR系统已关闭")
return True
def print_vr_display_guide():
"""打印VR显示指南"""
print("📋 VR显示功能指南")
print("=" * 50)
print("\n🎯 功能概述:")
print(" - 立体渲染:为左右眼生成不同视角的画面")
print(" - 头部追踪:根据头部运动调整视角")
print(" - ALVR串流无线串流到Quest等VR设备")
print(" - 控制器交互支持VR控制器输入")
print("\n🔧 使用方法:")
print(" 1. 运行引擎python3 main.py")
print(" 2. 初始化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()

View File

@ -1,316 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
VR显示优化工具
优化VR渲染性能和显示质量
"""
from panda3d.core import RenderState, CullFaceAttrib, DepthTestAttrib, AlphaTestAttrib
from core.vr_manager import VRManager
class VRDisplayOptimizer:
"""VR显示优化器"""
def __init__(self, world):
self.world = world
self.vr_manager = world.vr_manager if hasattr(world, 'vr_manager') else None
self.optimization_enabled = True
def optimize_vr_rendering(self):
"""优化VR渲染设置"""
if not self.vr_manager or not self.vr_manager.is_vr_enabled():
print("VR系统未启用跳过优化")
return False
print("🚀 开始VR渲染优化...")
# 1. 优化眼部缓冲区设置
self._optimize_eye_buffers()
# 2. 优化渲染状态
self._optimize_render_states()
# 3. 设置VR特定的渲染选项
self._setup_vr_render_options()
# 4. 优化纹理设置
self._optimize_textures()
# 5. 设置LOD和裁剪
self._setup_lod_and_culling()
print("✓ VR渲染优化完成")
return True
def _optimize_eye_buffers(self):
"""优化眼部缓冲区"""
try:
if not self.vr_manager.left_eye_buffer or not self.vr_manager.right_eye_buffer:
return
# 设置缓冲区优化选项
for buffer in [self.vr_manager.left_eye_buffer, self.vr_manager.right_eye_buffer]:
# 启用多重采样抗锯齿
buffer.setMultisample(4)
# 设置深度缓冲精度
buffer.addDepthRenderTexture()
# 启用硬件加速
buffer.setOneShot(False)
buffer.setActive(True)
print(" ✓ 眼部缓冲区优化完成")
except Exception as e:
print(f" ⚠ 眼部缓冲区优化失败: {str(e)}")
def _optimize_render_states(self):
"""优化渲染状态"""
try:
# 设置全局渲染状态
render_state = RenderState.make(
CullFaceAttrib.make(CullFaceAttrib.MCullCounterClockwise),
DepthTestAttrib.make(DepthTestAttrib.MLess),
AlphaTestAttrib.make(AlphaTestAttrib.MGreater, 0.5)
)
# 应用到render节点
self.world.render.setState(render_state)
print(" ✓ 渲染状态优化完成")
except Exception as e:
print(f" ⚠ 渲染状态优化失败: {str(e)}")
def _setup_vr_render_options(self):
"""设置VR特定的渲染选项"""
try:
# 启用VR优化
if hasattr(self.world, 'win') and self.world.win:
# 设置VR特定的窗口属性
properties = self.world.win.getProperties()
properties.setFrameBufferMode(properties.FBMHardware)
# 启用垂直同步以减少延迟
properties.setSync(True)
# 优化摄像机设置
if hasattr(self.world, 'camera'):
# 设置适合VR的FOV和近远裁剪面
lens = self.world.camera.node().getLens()
lens.setNearFar(0.1, 1000.0) # VR推荐的近远裁剪面
print(" ✓ VR渲染选项设置完成")
except Exception as e:
print(f" ⚠ VR渲染选项设置失败: {str(e)}")
def _optimize_textures(self):
"""优化纹理设置"""
try:
from panda3d.core import Texture
# 设置全局纹理优化
Texture.setTexturesPower2(Texture.ATPPad) # 填充到2的幂次方
Texture.setKeepRAMImage(False) # 不保留RAM图像以节省内存
# 启用纹理压缩
Texture.setCompressionMode(Texture.CMDefault)
print(" ✓ 纹理优化完成")
except Exception as e:
print(f" ⚠ 纹理优化失败: {str(e)}")
def _setup_lod_and_culling(self):
"""设置LOD和裁剪"""
try:
# 启用视锥裁剪
from panda3d.core import CullTraverser
self.world.render.setTwoSided(False)
# 设置距离裁剪
if hasattr(self.world, 'camera'):
# 根据VR需求调整裁剪距离
lens = self.world.camera.node().getLens()
lens.setFar(500.0) # 减少远裁剪面以提高性能
print(" ✓ LOD和裁剪设置完成")
except Exception as e:
print(f" ⚠ LOD和裁剪设置失败: {str(e)}")
def optimize_alvr_streaming(self):
"""优化ALVR串流性能"""
if not hasattr(self.world, 'alvr_streamer'):
print("ALVR串流器不可用跳过优化")
return False
print("📡 开始ALVR串流优化...")
alvr = self.world.alvr_streamer
# 1. 优化串流质量设置
self._optimize_stream_quality(alvr)
# 2. 优化网络设置
self._optimize_network_settings(alvr)
# 3. 优化编码设置
self._optimize_encoding_settings(alvr)
print("✓ ALVR串流优化完成")
return True
def _optimize_stream_quality(self, alvr):
"""优化串流质量"""
try:
# 根据性能自动调整质量
vr_info = self.vr_manager.get_vr_info()
render_width, render_height = vr_info['render_size']
# 针对不同设备优化
if render_width >= 2880: # Quest 2/3高质量
alvr.set_stream_quality(2880, 1700, 72, 150)
print(" ✓ 设置为高质量模式 (Quest 2/3)")
elif render_width >= 2160: # 中等质量
alvr.set_stream_quality(2160, 1200, 60, 100)
print(" ✓ 设置为中等质量模式")
else: # 低质量模式
alvr.set_stream_quality(1920, 1080, 60, 80)
print(" ✓ 设置为低质量模式")
except Exception as e:
print(f" ⚠ 串流质量优化失败: {str(e)}")
def _optimize_network_settings(self, alvr):
"""优化网络设置"""
try:
# 这里可以添加网络优化逻辑
# 例如调整缓冲区大小、连接超时等
print(" ✓ 网络设置优化完成")
except Exception as e:
print(f" ⚠ 网络设置优化失败: {str(e)}")
def _optimize_encoding_settings(self, alvr):
"""优化编码设置"""
try:
# 优化H.264编码参数
alvr.codec = "h264"
# 根据硬件能力调整编码设置
print(" ✓ 编码设置优化完成")
except Exception as e:
print(f" ⚠ 编码设置优化失败: {str(e)}")
def monitor_performance(self, duration=10):
"""监控VR性能"""
print(f"📊 开始VR性能监控 ({duration}秒)...")
import time
start_time = time.time()
frame_count = 0
last_report_time = start_time
while time.time() - start_time < duration:
# 处理一帧
if hasattr(self.world, 'taskMgr'):
self.world.taskMgr.step()
frame_count += 1
current_time = time.time()
# 每2秒报告一次性能
if current_time - last_report_time >= 2.0:
elapsed = current_time - last_report_time
fps = (frame_count / (current_time - start_time))
print(f" 性能报告: FPS {fps:.1f}")
# 报告VR系统状态
if self.vr_manager and self.vr_manager.is_vr_enabled():
vr_info = self.vr_manager.get_vr_info()
print(f" VR模式: {vr_info['mode']}")
print(f" 渲染尺寸: {vr_info['render_size']}")
# 报告ALVR状态
if hasattr(self.world, 'alvr_streamer') and self.world.alvr_streamer.is_streaming():
status = self.world.alvr_streamer.get_streaming_status()
print(f" ALVR FPS: {status['fps']}, 延迟: {status['latency']}ms")
last_report_time = current_time
time.sleep(0.016) # ~60 FPS目标
final_fps = frame_count / (time.time() - start_time)
print(f"✓ 性能监控完成平均FPS: {final_fps:.1f}")
return final_fps
def auto_optimize(self):
"""自动优化VR显示性能"""
print("🔧 开始自动VR优化...")
# 1. 优化VR渲染
if self.optimize_vr_rendering():
print(" ✓ VR渲染优化完成")
# 2. 优化ALVR串流
if hasattr(self.world, 'alvr_streamer'):
if self.optimize_alvr_streaming():
print(" ✓ ALVR串流优化完成")
# 3. 监控性能
print("\n📊 开始性能测试...")
fps = self.monitor_performance(5)
# 4. 根据性能调整设置
if fps < 45: # VR最低要求
print("⚠ 性能不足,降低质量设置...")
self._apply_low_quality_settings()
elif fps > 80:
print("✓ 性能良好,可以提高质量设置")
self._apply_high_quality_settings()
print("✓ 自动VR优化完成")
def _apply_low_quality_settings(self):
"""应用低质量设置"""
try:
# 降低渲染分辨率
if self.vr_manager:
self.vr_manager.render_width = int(self.vr_manager.render_width * 0.8)
self.vr_manager.render_height = int(self.vr_manager.render_height * 0.8)
# 降低ALVR质量
if hasattr(self.world, 'alvr_streamer'):
self.world.alvr_streamer.set_stream_quality(1920, 1080, 60, 60)
print(" ✓ 低质量设置已应用")
except Exception as e:
print(f" ⚠ 低质量设置应用失败: {str(e)}")
def _apply_high_quality_settings(self):
"""应用高质量设置"""
try:
# 提高ALVR质量
if hasattr(self.world, 'alvr_streamer'):
self.world.alvr_streamer.set_stream_quality(2880, 1700, 90, 200)
print(" ✓ 高质量设置已应用")
except Exception as e:
print(f" ⚠ 高质量设置应用失败: {str(e)}")
# 使用示例
def optimize_vr_display(world):
"""优化VR显示的便捷函数"""
optimizer = VRDisplayOptimizer(world)
optimizer.auto_optimize()
return optimizer

View File

@ -1,263 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
VR快速启动脚本
一键启动VR显示功能包括自动优化和ALVR串流
"""
import sys
import os
import time
from main import MyWorld
from vr_display_optimizer import VRDisplayOptimizer
def quick_vr_setup():
"""快速VR设置"""
print("🚀 VR快速启动")
print("=" * 40)
# 创建世界实例
print("🌍 初始化引擎...")
world = MyWorld()
# 创建优化器
optimizer = VRDisplayOptimizer(world)
# 设置基本场景
print("🎨 设置VR演示场景...")
setup_demo_scene(world)
# 初始化VR系统
print("🥽 启动VR系统...")
vr_success = world.initializeVR()
if not vr_success:
print("❌ VR初始化失败")
return None, None
print("✅ VR系统已启动")
# 优化VR性能
print("⚡ 优化VR性能...")
optimizer.optimize_vr_rendering()
# 尝试启动ALVR
print("📡 尝试启动ALVR串流...")
alvr_success = world.initializeALVR()
if alvr_success:
optimizer.optimize_alvr_streaming()
if world.startALVRStreaming():
print("✅ ALVR串流已启动")
else:
print("⚠️ ALVR串流启动失败")
else:
print("⚠️ ALVR初始化失败正常如果没有服务器")
# 启动VR输入
print("🎮 启动VR交互...")
if world.startVRInput():
world.showControllerRays(True)
world.setVRInteractionEnabled(True)
print("✅ VR交互已启用")
# 显示状态信息
print_vr_status(world)
return world, optimizer
def setup_demo_scene(world):
"""设置演示场景"""
try:
from panda3d.core import CardMaker, Vec4, AmbientLight, DirectionalLight
import math
# 添加光照
ambient = AmbientLight('ambient')
ambient.setColor(Vec4(0.4, 0.4, 0.4, 1))
ambient_np = world.render.attachNewNode(ambient)
world.render.setLight(ambient_np)
directional = DirectionalLight('sun')
directional.setColor(Vec4(1, 1, 0.9, 1))
directional.setDirection(Vec4(-1, -1, -1, 0))
sun_np = world.render.attachNewNode(directional)
world.render.setLight(sun_np)
# 创建地面
cm = CardMaker("ground")
cm.setFrame(-20, 20, -20, 20)
ground = world.render.attachNewNode(cm.generate())
ground.setPos(0, 0, -2)
ground.setHpr(0, -90, 0)
ground.setColor(0.3, 0.7, 0.3, 1)
# 创建彩色立方体环
for i in range(12):
cm_cube = CardMaker(f"cube_{i}")
cm_cube.setFrame(-0.5, 0.5, -0.5, 0.5)
cube = world.render.attachNewNode(cm_cube.generate())
angle = i * math.pi * 2 / 12
radius = 5
height = math.sin(i * 0.5) * 2
cube.setPos(math.cos(angle) * radius, math.sin(angle) * radius, height)
cube.setHpr(i * 30, 0, 0)
cube.setScale(1, 1, 2 + height * 0.5)
# HSV到RGB转换
h = i / 12.0 * 360
s = 0.8
v = 1.0
r, g, b = hsv_to_rgb(h, s, v)
cube.setColor(r, g, b, 1)
print(" ✅ 演示场景创建完成")
except Exception as e:
print(f" ⚠️ 场景创建失败: {str(e)}")
def hsv_to_rgb(h, s, v):
"""HSV到RGB颜色转换"""
import math
h = h / 60.0
c = v * s
x = c * (1 - abs((h % 2) - 1))
m = v - c
if 0 <= h < 1:
r, g, b = c, x, 0
elif 1 <= h < 2:
r, g, b = x, c, 0
elif 2 <= h < 3:
r, g, b = 0, c, x
elif 3 <= h < 4:
r, g, b = 0, x, c
elif 4 <= h < 5:
r, g, b = x, 0, c
else:
r, g, b = c, 0, x
return r + m, g + m, b + m
def print_vr_status(world):
"""显示VR状态信息"""
print("\n📊 VR系统状态")
print("-" * 30)
if world.isVREnabled():
vr_info = world.getVRInfo()
print(f"🥽 VR模式: {vr_info['mode']}")
print(f"📐 渲染尺寸: {vr_info['render_size']}")
print(f"🎮 模拟模式: {vr_info['simulation_mode']}")
if world.isALVRConnected():
status = world.getALVRStreamingStatus()
print(f"📡 ALVR状态: {'串流中' if world.isALVRStreaming() else '已连接'}")
print(f"🎥 分辨率: {status['resolution']}")
print(f"⚡ FPS: {status['fps']}")
else:
print("📡 ALVR状态: 未连接")
else:
print("🥽 VR状态: 未启用")
def run_vr_experience(world, optimizer, duration=30):
"""运行VR体验"""
print(f"\n🎮 VR体验开始 ({duration}秒)")
print("💡 控制说明:")
print(" - 观察窗口中的立体视图")
print(" - VR头盔中可看到完整场景")
print(" - 控制器射线用于交互")
print(" - 按Ctrl+C退出")
start_time = time.time()
frame_count = 0
try:
while time.time() - start_time < duration:
# 处理引擎更新
if hasattr(world, 'taskMgr'):
world.taskMgr.step()
frame_count += 1
# 每5秒显示状态
elapsed = time.time() - start_time
if frame_count % 300 == 0: # ~5秒
fps = frame_count / elapsed
print(f" 📈 运行状态: {elapsed:.1f}s, FPS: {fps:.1f}")
if world.isALVRStreaming():
alvr_status = world.getALVRStreamingStatus()
print(f" 📡 ALVR: {alvr_status['fps']} FPS, {alvr_status['latency']}ms延迟")
time.sleep(0.016) # ~60 FPS
except KeyboardInterrupt:
print("\n⏹️ 用户中断VR体验")
final_fps = frame_count / (time.time() - start_time)
print(f"✅ VR体验完成平均FPS: {final_fps:.1f}")
def cleanup_vr(world):
"""清理VR资源"""
print("\n🧹 清理VR资源...")
try:
if hasattr(world, 'stopVRInput'):
world.stopVRInput()
if hasattr(world, 'stopALVRStreaming'):
world.stopALVRStreaming()
if hasattr(world, 'shutdownALVR'):
world.shutdownALVR()
if hasattr(world, 'shutdownVR'):
world.shutdownVR()
print("✅ VR资源清理完成")
except Exception as e:
print(f"⚠️ VR清理失败: {str(e)}")
def main():
"""主函数"""
print("🎮 VR引擎快速启动")
print("=" * 50)
# 快速设置VR
world, optimizer = quick_vr_setup()
if not world:
print("❌ VR启动失败")
return
try:
# 运行VR体验
run_vr_experience(world, optimizer, 60) # 60秒演示
except Exception as e:
print(f"❌ VR运行错误: {str(e)}")
finally:
# 清理资源
cleanup_vr(world)
print("\n🎉 VR体验结束")
print("💡 要在Quest中体验:")
print(" 1. 安装ALVR服务器和客户端")
print(" 2. 连接同一WiFi网络")
print(" 3. 重新运行此脚本")
if __name__ == "__main__":
main()

View File

@ -1,264 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
VR串流测试脚本
专门用于测试和验证ALVR串流功能
"""
import sys
import os
import time
from main import MyWorld
def test_vr_streaming():
"""测试VR串流功能"""
print("=== VR串流功能测试 ===")
# 创建世界实例
world = MyWorld()
# 初始化VR系统
print("🥽 初始化VR系统...")
if not world.initializeVR():
print("✗ VR系统初始化失败")
return False
print("✓ VR系统初始化成功")
# 获取VR信息
vr_info = world.getVRInfo()
print(f"\n📊 VR系统信息:")
print(f" - 模式: {vr_info['mode']}")
print(f" - 启用状态: {vr_info['enabled']}")
print(f" - 模拟模式: {vr_info['simulation_mode']}")
print(f" - 渲染尺寸: {vr_info['render_size']}")
# 检查是否为直连模式SteamVR已运行
if world.alvr_streamer._is_steamvr_active():
print("\n💡 检测到SteamVR正在运行将使用直连模式")
print(" 在直连模式下画面将直接通过SteamVR传输到头显")
print(" 无需通过ALVR服务器进行额外的串流处理")
# 初始化ALVR串流
print("\n📡 初始化ALVR串流...")
print("💡 检测到您已连接VR设备正在尝试连接ALVR服务器...")
alvr_success = world.initializeALVR()
if alvr_success:
print("✓ ALVR串流初始化成功")
# 设置串流质量
print("\n🎥 配置串流质量...")
quality_set = world.setALVRStreamQuality(1920, 1080, 60, 50)
if quality_set:
print(" ✓ 串流质量设置成功: 1920x1080@60fps, 50Mbps")
# 获取串流状态
status = world.getALVRStreamingStatus()
print(f"\n📊 ALVR串流初始状态:")
print(f" - 连接状态: {status['connected']}")
print(f" - 串流状态: {status['streaming']}")
print(f" - 分辨率: {status['resolution']}")
print(f" - 帧率: {status['fps']}")
print(f" - 码率: {status['bitrate']}Mbps")
# 开始串流
print(f"\n🚀 开始ALVR串流...")
if world.startALVRStreaming():
print("✓ ALVR串流已开始")
# 运行串流测试
print(f"\n📡 ALVR串流测试运行中... (10秒)")
start_time = time.time()
frame_count = 0
while time.time() - start_time < 10:
# 更新串流状态
current_status = world.getALVRStreamingStatus()
# 每2秒显示一次状态更新
if frame_count % 120 == 0: # 约每2秒
elapsed = time.time() - start_time
print(f" [{elapsed:.1f}s] FPS: {current_status['fps']}, 延迟: {current_status['latency']}ms")
if hasattr(world, 'taskMgr'):
world.taskMgr.step()
frame_count += 1
time.sleep(0.016)
# 停止串流
print("\n🛑 停止ALVR串流...")
world.stopALVRStreaming()
print("✓ ALVR串流已停止")
else:
print("⚠ ALVR串流启动失败")
print(" 可能原因:")
print(" - ALVR服务器未运行")
print(" - VR客户端未连接")
print(" - 网络连接问题")
else:
print("⚠ ALVR串流初始化失败")
print("💡 根据您提供的信息您已经可以通过SteamVR看到画面但串流测试无法连接到ALVR服务器")
print("\n💡 直连模式说明:")
print(" 如果您使用的是有线连接如Pico 4通过USB-C连接")
print(" 并且SteamVR已经可以正常显示画面您实际上已经在使用直连模式")
print(" SteamVR会直接处理渲染并将画面传输到头显无需额外的串流步骤")
print("\n🔧 建议操作:")
print(" 1. 确认SteamVR是否正常工作并显示画面")
print(" 2. 如果正常工作您无需进行额外的ALVR串流设置")
print(" 3. 如果需要使用ALVR的额外功能可以尝试:")
print(" - 关闭SteamVR")
print(" - 确保ALVR服务器正在运行")
print(" - 重新运行测试")
# 提供替代方案
print("\n🔄 替代方案:")
print(" 您可以尝试运行:")
print(" python3 vr_display_demo.py")
print(" 并选择'2. ALVR串流演示'来测试串流功能")
# 关闭系统
world.shutdownALVR()
world.shutdownVR()
print("\n✓ 所有系统已关闭")
return True
def test_vr_streaming_detailed():
"""详细测试VR串流功能"""
print("=== VR串流详细功能测试 ===")
# 创建世界实例
world = MyWorld()
# 初始化VR系统
print("🥽 初始化VR系统...")
if not world.initializeVR():
print("✗ VR系统初始化失败")
return False
print("✓ VR系统初始化成功")
# 检查VR纹理是否正确创建
print("\n🔍 检查VR纹理...")
if hasattr(world.vr_manager, 'left_eye_texture') and world.vr_manager.left_eye_texture:
print(" ✓ 左眼纹理已创建")
print(f" 纹理尺寸: {world.vr_manager.left_eye_texture.getXSize()}x{world.vr_manager.left_eye_texture.getYSize()}")
else:
print(" ✗ 左眼纹理未创建")
if hasattr(world.vr_manager, 'right_eye_texture') and world.vr_manager.right_eye_texture:
print(" ✓ 右眼纹理已创建")
print(f" 纹理尺寸: {world.vr_manager.right_eye_texture.getXSize()}x{world.vr_manager.right_eye_texture.getYSize()}")
else:
print(" ✗ 右眼纹理未创建")
# 初始化ALVR串流
print("\n📡 初始化ALVR串流...")
alvr_success = world.initializeALVR()
if alvr_success:
print("✓ ALVR串流初始化成功")
# 测试多种串流质量设置
print("\n🎥 测试多种串流质量设置:")
quality_settings = [
(1280, 720, 60, 25),
(1920, 1080, 60, 50),
(1920, 1080, 30, 25)
]
for width, height, fps, bitrate in quality_settings:
success = world.setALVRStreamQuality(width, height, fps, bitrate)
if success:
status = world.getALVRStreamingStatus()
print(f" ✓ 设置 {width}x{height}@{fps}fps, {bitrate}Mbps: 成功")
print(f" 实际分辨率: {status['resolution']}")
else:
print(f" ✗ 设置 {width}x{height}@{fps}fps, {bitrate}Mbps: 失败")
# 测试串流控制
print("\n🎮 测试串流控制:")
# 开始串流
if world.startALVRStreaming():
print(" ✓ 串流开始成功")
# 检查串流状态
status = world.getALVRStreamingStatus()
print(f" 串流状态: {status['streaming']}")
print(f" 连接状态: {status['connected']}")
# 等待2秒
time.sleep(2)
# 再次检查状态
status = world.getALVRStreamingStatus()
print(f" 2秒后状态 - FPS: {status['fps']}, 延迟: {status['latency']}ms")
# 停止串流
world.stopALVRStreaming()
status = world.getALVRStreamingStatus()
print(f" ✓ 串流停止成功")
print(f" 串流状态: {status['streaming']}")
else:
print(" ✗ 串流开始失败")
else:
print("⚠ ALVR串流初始化失败")
print("\n🔧 Pico 4连接故障排除:")
print(" 1. 确认ALVR服务器正在运行")
print(" 2. 检查USB连接是否正常")
print(" 3. 确认Pico 4上已安装并启动ALVR客户端")
print(" 4. 检查防火墙设置")
print(" 5. 确认SteamVR可以识别到头显")
# 关闭系统
world.shutdownALVR()
world.shutdownVR()
print("\n✓ 所有系统已关闭")
return True
def main():
"""主函数"""
print("🎮 VR串流测试脚本")
print("="*50)
print("\n请选择测试类型:")
print("1. 基本串流测试")
print("2. 详细串流测试")
try:
choice = input("请输入选择 (1-2): ")
if choice == "1":
success = test_vr_streaming()
elif choice == "2":
success = test_vr_streaming_detailed()
else:
print("无效的选择")
return
print("\n" + "="*50)
if success:
print("✓ 串流测试成功完成")
else:
print("✗ 串流测试遇到问题")
except KeyboardInterrupt:
print("\n用户中断测试")
except Exception as e:
print(f"测试过程中发生错误: {str(e)}")
import traceback
traceback.print_exc()
if __name__ == "__main__":
main()

View File

@ -1,468 +0,0 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
VR功能测试脚本
测试VR系统的各种功能包括模拟模式和真实VR模式
"""
import sys
import os
from main import MyWorld
def test_basic_vr_functionality():
"""测试基本VR功能"""
print("=== VR基本功能测试 ===")
# 创建世界实例
world = MyWorld()
# 检查VR管理器是否正确初始化
print("✓ VR管理器已创建")
# 检查OpenVR库是否可用
try:
import openvr
print("✓ OpenVR库已安装")
except ImportError:
print("⚠ OpenVR库未安装将使用模拟模式")
# 测试VR初始化会自动回退到模拟模式
print("\n正在测试VR初始化...")
vr_success = world.initializeVR()
if vr_success:
print("✓ VR系统初始化成功")
# 获取VR信息
vr_info = world.getVRInfo()
print(f"\n📊 VR系统信息:")
print(f" - 模式: {vr_info['mode']}")
print(f" - 启用状态: {vr_info['enabled']}")
print(f" - 模拟模式: {vr_info['simulation_mode']}")
print(f" - 渲染尺寸: {vr_info['render_size']}")
print(f" - 控制器数量: {vr_info.get('controllers', 0)}")
# 测试控制器输入
print("\n🎮 测试控制器输入:")
for i in range(2):
controller_input = world.vr_manager.get_controller_input(i)
if controller_input and controller_input.get('connected'):
print(f" 控制器 {i}: 已连接")
print(f" - 扳机: {controller_input['trigger']:.2f}")
print(f" - 握把: {controller_input['grip']:.2f}")
print(f" - 触摸板: ({controller_input['touchpad']['x']:.2f}, {controller_input['touchpad']['y']:.2f})")
else:
print(f" 控制器 {i}: 未连接")
# 测试VR状态
print("\n📊 VR系统状态:")
status = world.getVRStatus()
print(f" - VR启用: {status['vr_enabled']}")
print(f" - 控制器总数: {len(status['controllers'])}")
else:
print("✗ VR系统初始化失败")
return False
# 关闭VR系统
world.shutdownVR()
print("\n✓ VR系统已关闭")
return True
def test_simulation_mode():
"""测试VR模拟模式"""
print("=== VR模拟模式测试 ===")
# 创建世界实例
world = MyWorld()
# 强制启用模拟模式
print("强制启用VR模拟模式...")
vr_success = world.vr_manager.enable_simulation_mode()
if vr_success:
print("✓ VR模拟模式启用成功")
# 获取模拟数据
sim_data = world.vr_manager.get_simulation_data()
if sim_data:
print(f"\n🎮 模拟数据:")
print(f" - 头部位置: {sim_data['head_pose']['position']}")
print(f" - 控制器 0 位置: {sim_data['controller_poses'][0]['position']}")
print(f" - 控制器 1 位置: {sim_data['controller_poses'][1]['position']}")
print(f" - 渲染尺寸: {sim_data['render_size']}")
# 测试更新模拟数据
print(f"\n🔧 测试模拟数据更新:")
new_head_pos = [0.1, 0.1, 1.7]
success = world.vr_manager.update_simulation_data('head_pose', {
'position': new_head_pos,
'rotation': [0, 0, 0, 1]
})
if success:
print(f" ✓ 头部位置更新为: {new_head_pos}")
# 测试控制器输入(模拟)
print(f"\n🎮 模拟控制器输入测试:")
for i in range(2):
controller_input = world.vr_manager.get_controller_input(i)
if controller_input:
print(f" 控制器 {i}:")
print(f" - 连接状态: {controller_input['connected']}")
print(f" - 扳机: {controller_input['trigger']}")
print(f" - 握把: {controller_input['grip']}")
print(f" - 菜单键: {controller_input['menu']}")
else:
print("✗ VR模拟模式启用失败")
return False
# 关闭VR系统
world.vr_manager.shutdown_vr()
print("\n✓ VR模拟模式已关闭")
return True
def test_alvr_streaming():
"""测试ALVR串流功能"""
print("=== ALVR串流测试 ===")
# 创建世界实例
world = MyWorld()
# 初始化VR系统
print("初始化VR系统...")
if not world.initializeVR():
print("✗ VR系统初始化失败")
return False
# 测试ALVR初始化
print("\n正在测试ALVR初始化...")
alvr_success = world.initializeALVR()
if alvr_success:
print("✓ ALVR初始化成功")
# 测试串流状态
print("\n📊 ALVR串流状态:")
status = world.getALVRStreamingStatus()
print(f" - 连接状态: {world.isALVRConnected()}")
print(f" - 串流状态: {world.isALVRStreaming()}")
# 测试串流质量设置
print("\n🎥 测试串流质量设置:")
quality_success = world.setALVRStreamQuality(1920, 1080, 60, 100)
if quality_success:
print(" ✓ 串流质量设置成功: 1920x1080@60fps, 100Mbps")
else:
print(" ⚠ 串流质量设置失败")
# 测试触觉反馈
print("\n🔔 测试触觉反馈:")
for controller_id in range(2):
feedback_success = world.sendHapticFeedback(controller_id, 0.1, 0.5)
if feedback_success:
print(f" ✓ 控制器 {controller_id} 触觉反馈发送成功")
else:
print(f" ⚠ 控制器 {controller_id} 触觉反馈发送失败")
# 测试串流控制
print("\n🎮 测试串流控制:")
start_success = world.startALVRStreaming()
if start_success:
print(" ✓ 串流开始成功")
else:
print(" ⚠ 串流开始失败")
# 等待一下然后停止
import time
time.sleep(1)
stop_success = world.stopALVRStreaming()
if stop_success:
print(" ✓ 串流停止成功")
else:
print(" ⚠ 串流停止失败")
else:
print("⚠ ALVR初始化失败这在没有ALVR服务器时是正常的")
print(" 提示: 要使用ALVR功能请:")
print(" 1. 下载并安装ALVR服务器")
print(" 2. 启动ALVR服务器")
print(" 3. 在Quest头盔上安装并启动ALVR客户端")
# 关闭系统
world.shutdownALVR()
world.shutdownVR()
print("\n✓ ALVR和VR系统已关闭")
return True
def test_vr_gui_control_panel():
"""测试VR GUI控制面板"""
print("=== VR GUI控制面板测试 ===")
try:
from ui.vr_control_panel import VRControlPanel
from PyQt5.QtWidgets import QApplication
# 创建Qt应用程序
app = QApplication.instance()
if app is None:
app = QApplication(sys.argv)
# 创建世界实例
world = MyWorld()
# 创建VR控制面板
print("创建VR控制面板...")
control_panel = VRControlPanel(world)
# 测试面板功能
print("✓ VR控制面板创建成功")
print(" - 面板标题:", control_panel.windowTitle())
print(" - 面板大小:", control_panel.size().width(), "x", control_panel.size().height())
# 显示面板(非阻塞)
control_panel.show()
print("✓ VR控制面板已显示")
print("\n💡 GUI控制面板功能:")
print(" - VR系统开关控制")
print(" - ALVR串流控制")
print(" - 实时状态监控")
print(" - 质量设置调整")
print(" - 控制器状态显示")
print(" - 性能监控")
# 短暂显示然后关闭
import time
time.sleep(2)
control_panel.close()
print("\n✓ VR控制面板测试完成")
except Exception as e:
print(f"✗ VR控制面板测试失败: {str(e)}")
return False
return True
def test_vr_interaction():
"""测试VR交互功能"""
print("=== VR交互功能测试 ===")
# 创建世界实例
world = MyWorld()
# 初始化VR系统
print("初始化VR系统...")
if not world.initializeVR():
print("✗ VR系统初始化失败")
return False
# 测试VR输入处理
print("\n🎮 测试VR输入处理:")
input_success = world.startVRInput()
if input_success:
print(" ✓ VR输入处理启动成功")
# 测试控制器射线显示
print("\n🌟 测试控制器射线:")
ray_success = world.showControllerRays(True)
if ray_success:
print(" ✓ 控制器射线显示启用")
else:
print(" ⚠ 控制器射线显示失败")
# 测试手势识别
print("\n✋ 测试手势识别:")
gesture_success = world.setVRGestureEnabled(True)
if gesture_success:
print(" ✓ VR手势识别启用")
else:
print(" ⚠ VR手势识别启用失败")
# 测试VR交互
print("\n🤏 测试VR交互:")
interaction_success = world.setVRInteractionEnabled(True)
if interaction_success:
print(" ✓ VR交互功能启用")
else:
print(" ⚠ VR交互功能启用失败")
# 获取控制器状态
print("\n🎮 控制器状态:")
controllers = world.getAllControllers()
for i, controller in enumerate(controllers):
if controller:
print(f" 控制器 {i}: 可用")
state = world.getControllerState(i)
if state:
print(f" - 位置: {state.get('position', 'N/A')}")
print(f" - 旋转: {state.get('rotation', 'N/A')}")
print(f" - 按钮: {state.get('buttons', 'N/A')}")
else:
print(f" 控制器 {i}: 不可用")
# 停止VR输入
world.stopVRInput()
print("\n✓ VR输入处理已停止")
else:
print(" ⚠ VR输入处理启动失败")
# 关闭VR系统
world.shutdownVR()
print("\n✓ VR系统已关闭")
return True
def run_all_tests():
"""运行所有测试"""
print("=== 运行所有VR测试 ===")
tests = [
("基本VR功能", test_basic_vr_functionality),
("VR模拟模式", test_simulation_mode),
("ALVR串流", test_alvr_streaming),
("VR GUI控制面板", test_vr_gui_control_panel),
("VR交互功能", test_vr_interaction)
]
results = []
for test_name, test_func in tests:
print(f"\n{'='*50}")
print(f"正在运行: {test_name}")
print('='*50)
try:
success = test_func()
results.append((test_name, success))
if success:
print(f"{test_name} 测试通过")
else:
print(f"{test_name} 测试失败")
except Exception as e:
print(f"{test_name} 测试出错: {str(e)}")
results.append((test_name, False))
# 打印总结
print(f"\n{'='*50}")
print("测试总结")
print('='*50)
passed = sum(1 for _, success in results if success)
total = len(results)
for test_name, success in results:
status = "✓ 通过" if success else "✗ 失败"
print(f"{test_name}: {status}")
print(f"\n总计: {passed}/{total} 个测试通过")
if passed == total:
print("🎉 所有测试都通过了!")
else:
print("⚠ 部分测试失败,请检查上述输出")
return passed == total
def print_vr_requirements():
"""打印VR系统要求"""
print("📋 VR系统要求说明")
print("="*50)
print("🔧 软件要求:")
print(" - Python 3.8+")
print(" - Panda3D")
print(" - PyQt5")
print(" - OpenVR库 (pip install openvr)")
print(" - 其他依赖见 requirements/vr-requirements.txt")
print("\n🎮 硬件要求完整VR模式:")
print(" - 支持VR的显卡 (GTX 1060/RX 580 或更好)")
print(" - VR头盔 (Quest 2/3, Valve Index, HTC Vive等)")
print(" - 足够的USB端口或无线连接")
print("\n🌐 连接方式:")
print(" 有线连接:")
print(" - Valve Index: DisplayPort + USB 3.0")
print(" - HTC Vive: HDMI + USB 3.0")
print(" - Quest 2/3: USB-C (Quest Link)")
print(" 无线连接:")
print(" - Quest 2/3: 通过ALVR串流")
print(" - 需要5GHz Wi-Fi网络")
print(" - 需要ALVR服务器运行")
print("\n🔄 启动顺序完整VR模式:")
print(" 1. 确保VR头盔已连接并识别")
print(" 2. 启动SteamVR")
print(" 3. (可选) 启动ALVR服务器Quest无线")
print(" 4. 运行VR测试脚本")
print("\n🎮 模拟模式说明:")
print(" - 无需VR硬件")
print(" - 模拟头盔和控制器追踪")
print(" - 立体渲染到窗口")
print(" - 适用于开发和调试")
print("\n💡 故障排除:")
print(" - 如果OpenVR初始化失败系统会自动切换到模拟模式")
print(" - 检查SteamVR是否正在运行")
print(" - 确认VR头盔被系统识别")
print(" - 检查USB/DisplayPort连接")
print(" - 重启SteamVR和头盔")
def main():
"""主函数"""
print("🎮 VR功能测试脚本")
print("="*50)
# 显示要求说明
print_vr_requirements()
print("\n请选择测试类型:")
print("1. 基本VR功能测试")
print("2. VR模拟模式测试")
print("3. ALVR串流测试")
print("4. VR GUI控制面板")
print("5. VR交互功能测试")
print("6. 运行所有测试")
print("7. 查看VR系统要求")
try:
choice = input("请输入选择 (1-7): ")
if choice == "1":
success = test_basic_vr_functionality()
elif choice == "2":
success = test_simulation_mode()
elif choice == "3":
success = test_alvr_streaming()
elif choice == "4":
success = test_vr_gui_control_panel()
elif choice == "5":
success = test_vr_interaction()
elif choice == "6":
success = run_all_tests()
elif choice == "7":
print_vr_requirements()
return
else:
print("无效的选择")
return
print("\n" + "="*50)
if success:
print("✓ 测试成功")
else:
print("✗ 测试失败")
except KeyboardInterrupt:
print("\n用户中断测试")
except Exception as e:
print(f"测试过程中发生错误: {str(e)}")
if __name__ == "__main__":
main()

1
参考文档.md Normal file
View File

@ -0,0 +1 @@
API:https://docs.panda3d.org/1.10/python/reference/index