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