573 lines
20 KiB
Python
573 lines
20 KiB
Python
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 |