1
0
forked from Rowland/EG

vr第一次提交

This commit is contained in:
Rowland 2025-09-15 10:03:47 +08:00
parent c53f1d3ae5
commit af0a999191
17 changed files with 113 additions and 3187 deletions

112
CLAUDE.md Normal file
View 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支持
- 某些功能可能需要特定的系统配置

View File

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

View File

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

View File

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

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

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功能的正确性。

144
main.py
View File

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

View File

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

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