forked from Rowland/EG
vr第一次提交
This commit is contained in:
parent
c53f1d3ae5
commit
af0a999191
112
CLAUDE.md
Normal file
112
CLAUDE.md
Normal file
@ -0,0 +1,112 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## 项目概述
|
||||
|
||||
这是一个基于Panda3D的3D渲染引擎和场景编辑器,集成了PyQt5界面和多种高级功能:
|
||||
|
||||
- 3D场景编辑器(模型导入、材质系统、碰撞检测)
|
||||
- GUI元素管理(2D/3D GUI组件)
|
||||
- 项目管理系统(场景保存/加载、项目打包)
|
||||
- Cesium地图集成
|
||||
- 渲染管线增强(RenderPipelineFile)
|
||||
|
||||
## 运行和构建命令
|
||||
|
||||
### 启动应用程序
|
||||
```bash
|
||||
python Start_Run.py [project_path]
|
||||
```
|
||||
或者直接:
|
||||
```bash
|
||||
python main.py
|
||||
```
|
||||
|
||||
### 依赖安装
|
||||
```bash
|
||||
# 主要依赖
|
||||
pip install -r requirements/requirements.txt
|
||||
|
||||
# Conda环境依赖
|
||||
pip install -r requirements/conda-requirements.txt
|
||||
```
|
||||
|
||||
### 工具脚本
|
||||
```bash
|
||||
# 安装FBX到GLTF转换工具
|
||||
./install_fbx2gltf.sh
|
||||
```
|
||||
|
||||
## 核心架构
|
||||
|
||||
### 主要模块结构
|
||||
```
|
||||
EG/
|
||||
├── main.py # 应用程序入口点
|
||||
├── Start_Run.py # 启动脚本(路径配置)
|
||||
├── core/ # 核心功能模块
|
||||
│ ├── world.py # 3D世界核心(继承Panda3DWorld)
|
||||
│ ├── scene_manager.py # 场景和模型管理
|
||||
│ ├── selection.py # 对象选择系统
|
||||
│ ├── event_handler.py # 事件处理
|
||||
│ └── tool_manager.py # 工具系统
|
||||
├── gui/ # GUI元素管理
|
||||
│ └── gui_manager.py # 2D/3D GUI组件
|
||||
├── ui/ # 用户界面
|
||||
│ ├── widgets.py # 自定义Qt组件
|
||||
│ ├── property_panel.py # 属性面板
|
||||
│ └── interface_manager.py # 界面管理
|
||||
├── scene/ # 场景相关
|
||||
│ └── scene_manager.py # 场景管理器
|
||||
├── project/ # 项目管理
|
||||
│ └── project_manager.py # 项目生命周期
|
||||
├── RenderPipelineFile/ # 渲染管线扩展
|
||||
└── QPanda3D/ # Panda3D Qt集成
|
||||
```
|
||||
|
||||
### 核心设计模式
|
||||
|
||||
1. **模块化架构**: 每个功能模块独立,通过管理器类协调
|
||||
2. **事件驱动**: EventHandler统一处理用户交互和系统事件
|
||||
3. **组件系统**: SelectionSystem、ToolManager等可插拔组件
|
||||
4. **MVC分离**: UI组件、核心逻辑和数据管理分离
|
||||
|
||||
### 主要依赖集成
|
||||
|
||||
- **Panda3D 1.10.15**: 3D渲染引擎
|
||||
- **PyQt5**: GUI框架
|
||||
- **QPanda3D**: Panda3D的Qt集成
|
||||
- **RenderPipeline**: 高级渲染功能
|
||||
|
||||
## 开发指南
|
||||
|
||||
### 添加新功能模块
|
||||
1. 在对应目录下创建新的Python文件
|
||||
2. 继承相应的基类(如Panda3DWorld用于3D功能)
|
||||
3. 在main.py中集成新模块
|
||||
4. 更新界面管理器以添加UI控制
|
||||
|
||||
### 材质和渲染
|
||||
- 材质系统集成在scene/scene_manager.py
|
||||
- 支持PBR材质和自定义着色器
|
||||
- RenderPipelineFile提供高级渲染特性
|
||||
|
||||
|
||||
### GUI开发
|
||||
- 使用PyQt5构建主界面
|
||||
- 3D GUI元素通过gui/gui_manager.py管理
|
||||
- 自定义组件在ui/widgets.py中定义
|
||||
|
||||
## 文件约定
|
||||
|
||||
- Python文件使用UTF-8编码
|
||||
- 中文注释和文档字符串
|
||||
- 模块顶部包含功能描述注释
|
||||
- 类和方法使用描述性命名
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 项目依赖多个大型库(Panda3D、PyQt5、RenderPipeline)
|
||||
- Cesium集成需要WebEngine支持
|
||||
- 某些功能可能需要特定的系统配置
|
||||
@ -53,7 +53,7 @@ class Application(ShowBase):
|
||||
# ------ End of render pipeline code, thats it! ------
|
||||
|
||||
# Set time of day
|
||||
self.render_pipeline.daytime_mgr.time = "12:43"
|
||||
self.render_pipeline.daytime_mgr.time = "6:43"
|
||||
|
||||
# Load the scene
|
||||
model = loader.loadModel("scene/Scene.bam")
|
||||
|
||||
@ -52,11 +52,6 @@ class MainApp(ShowBase):
|
||||
# Load the scene
|
||||
model = loader.loadModel("scene/scene.bam")
|
||||
# model = loader.loadModel("scene2/Scene.bam")
|
||||
model_0 = self.loader.loadModel("/home/tiger/下载/Benci/source/s65/s65/s65.fbx")
|
||||
model_0.reparentTo(self.render)
|
||||
model_0.setScale(0.01)
|
||||
model_0.setPos(-8, 42, 0)
|
||||
model_0.setHpr(0, 90, 0)
|
||||
|
||||
model.reparent_to(render)
|
||||
self.render_pipeline.prepare_scene(model)
|
||||
|
||||
@ -1,509 +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.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:
|
||||
# 检查进程
|
||||
for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
|
||||
if 'alvr' in proc.info['name'].lower():
|
||||
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()
|
||||
|
||||
return result == 0
|
||||
|
||||
except Exception as e:
|
||||
print(f"检查ALVR服务器错误: {str(e)}")
|
||||
return False
|
||||
|
||||
def _start_alvr_server(self):
|
||||
"""启动ALVR服务器"""
|
||||
try:
|
||||
# 尝试启动ALVR服务器
|
||||
# 这里需要根据实际的ALVR安装路径调整
|
||||
alvr_paths = [
|
||||
"/usr/local/bin/alvr_server",
|
||||
"/usr/bin/alvr_server",
|
||||
"C:/Program Files/ALVR/alvr_server.exe",
|
||||
"C:/ALVR/alvr_server.exe"
|
||||
]
|
||||
|
||||
for path in alvr_paths:
|
||||
try:
|
||||
subprocess.Popen([path], shell=True)
|
||||
time.sleep(3) # 等待服务器启动
|
||||
if self._check_alvr_server():
|
||||
return True
|
||||
except FileNotFoundError:
|
||||
continue
|
||||
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"启动ALVR服务器错误: {str(e)}")
|
||||
return False
|
||||
|
||||
def _connect_to_server(self):
|
||||
"""连接到ALVR服务器"""
|
||||
try:
|
||||
# 创建TCP连接
|
||||
self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.server_socket.settimeout(5)
|
||||
self.server_socket.connect((self.alvr_server_ip, self.alvr_server_port))
|
||||
|
||||
# 发送握手消息
|
||||
handshake_data = {
|
||||
"type": "handshake",
|
||||
"client_name": "Panda3D_VR_Engine",
|
||||
"version": "1.0",
|
||||
"capabilities": {
|
||||
"video": True,
|
||||
"audio": True,
|
||||
"tracking": True,
|
||||
"haptics": True
|
||||
}
|
||||
}
|
||||
|
||||
self._send_message(handshake_data)
|
||||
|
||||
# 接收响应
|
||||
response = self._receive_message()
|
||||
if response and response.get("type") == "handshake_response":
|
||||
self.connected = True
|
||||
print("✓ 已连接到ALVR服务器")
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"连接ALVR服务器错误: {str(e)}")
|
||||
return False
|
||||
|
||||
def _send_message(self, data):
|
||||
"""发送消息到ALVR服务器"""
|
||||
try:
|
||||
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:
|
||||
# 接收消息长度
|
||||
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:
|
||||
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:
|
||||
return None
|
||||
|
||||
# 合成立体帧
|
||||
frame_data = self._compose_stereo_frame(left_texture, right_texture)
|
||||
return frame_data
|
||||
|
||||
except Exception as e:
|
||||
print(f"获取VR帧错误: {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 not self.server_socket:
|
||||
return
|
||||
|
||||
# 创建帧消息
|
||||
frame_message = {
|
||||
"type": "video_frame",
|
||||
"timestamp": time.time(),
|
||||
"width": self.stream_width,
|
||||
"height": self.stream_height,
|
||||
"format": "rgb",
|
||||
"data": frame_data.hex() # 转换为十六进制字符串
|
||||
}
|
||||
|
||||
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 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):
|
||||
"""获取串流状态"""
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
@ -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 '禁用'}")
|
||||
@ -1,573 +0,0 @@
|
||||
import warnings
|
||||
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
||||
|
||||
from panda3d.core import *
|
||||
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
|
||||
|
||||
# 控制器相关
|
||||
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()
|
||||
|
||||
print("✓ 眼部渲染缓冲区创建完成")
|
||||
|
||||
except Exception as e:
|
||||
print(f"眼部缓冲区创建错误: {str(e)}")
|
||||
|
||||
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)
|
||||
|
||||
# 设置自定义投影矩阵
|
||||
lens.setCustomProjectionMatrix(panda_matrix)
|
||||
|
||||
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:
|
||||
taskMgr.add(self._update_vr_tracking, "vr_tracking")
|
||||
taskMgr.add(self._update_vr_rendering, "vr_rendering")
|
||||
|
||||
def _start_simulation_tasks(self):
|
||||
"""启动模拟VR任务"""
|
||||
taskMgr.add(self._update_simulation_tracking, "simulation_tracking")
|
||||
taskMgr.add(self._update_simulation_rendering, "simulation_rendering")
|
||||
|
||||
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)
|
||||
|
||||
# 更新头显位置
|
||||
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
|
||||
|
||||
# 提交帧到合成器
|
||||
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
|
||||
|
||||
# 提交左眼纹理
|
||||
left_eye_texture = openvr.Texture_t()
|
||||
left_eye_texture.handle = self.left_eye_texture.getTextureId()
|
||||
left_eye_texture.eType = openvr.TextureType_OpenGL
|
||||
left_eye_texture.eColorSpace = openvr.ColorSpace_Gamma
|
||||
self.vr_compositor.submit(openvr.Eye_Left, left_eye_texture)
|
||||
|
||||
# 提交右眼纹理
|
||||
right_eye_texture = openvr.Texture_t()
|
||||
right_eye_texture.handle = self.right_eye_texture.getTextureId()
|
||||
right_eye_texture.eType = openvr.TextureType_OpenGL
|
||||
right_eye_texture.eColorSpace = openvr.ColorSpace_Gamma
|
||||
self.vr_compositor.submit(openvr.Eye_Right, right_eye_texture)
|
||||
|
||||
except Exception as e:
|
||||
print(f"帧提交错误: {str(e)}")
|
||||
|
||||
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:
|
||||
taskMgr.remove("simulation_tracking")
|
||||
taskMgr.remove("simulation_rendering")
|
||||
else:
|
||||
taskMgr.remove("vr_tracking")
|
||||
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
|
||||
@ -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文件。
|
||||
253
demo/VR测试说明.md
253
demo/VR测试说明.md
@ -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功能的正确性。
|
||||
144
main.py
144
main.py
@ -19,9 +19,6 @@ 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 gui.gui_manager import GUIManager
|
||||
from core.terrain_manager import TerrainManager
|
||||
from scene.scene_manager import SceneManager
|
||||
@ -83,10 +80,6 @@ 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)
|
||||
|
||||
# 启动脚本系统
|
||||
self.script_manager.start_system()
|
||||
@ -606,143 +599,6 @@ class MyWorld(CoreWorld):
|
||||
def listAllScripts(self):
|
||||
"""列出所有脚本信息"""
|
||||
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
|
||||
}
|
||||
|
||||
def loadCesiumTileset(self,tileset_url,position=(0,0,0)):
|
||||
return self.scene_manager.load_cesium_tileset(tileset_url,position)
|
||||
|
||||
@ -1,18 +0,0 @@
|
||||
# VR功能依赖包
|
||||
# 用于支持VR显示和ALVR串流功能
|
||||
|
||||
# OpenVR Python库 - VR系统核心
|
||||
openvr>=1.26.7
|
||||
|
||||
# 网络和通信
|
||||
psutil>=5.9.0
|
||||
|
||||
# 数学和科学计算
|
||||
numpy>=1.21.0
|
||||
|
||||
# 图像处理
|
||||
Pillow>=9.0.1
|
||||
|
||||
# 现有核心依赖
|
||||
Panda3D>=1.10.15
|
||||
PyQt5>=5.15.9
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 19 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.3 KiB |
@ -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()
|
||||
468
vr_test.py
468
vr_test.py
@ -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()
|
||||
Loading…
Reference in New Issue
Block a user