EG/core/vr_manager.py
2025-07-16 10:19:34 +08:00

573 lines
20 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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