- 创建详细的README文档,介绍项目功能、架构、使用方法等 - 新增.pyc文件和目录,用于缓存编译后的Python代码 - 添加.lingma规则文件,用于代码格式检查 - 删除ALVR串流处理器代码,准备替换为OpenXR输入处理器 - 新增OpenXR输入处理器代码,处理VR控制器输入
480 lines
18 KiB
Python
Executable File
480 lines
18 KiB
Python
Executable File
#!/usr/bin/env python
|
||
# -*- coding: utf-8 -*-
|
||
|
||
"""
|
||
OpenXR测试脚本
|
||
|
||
用于测试OpenXR功能的基本实现
|
||
"""
|
||
|
||
import sys
|
||
import os
|
||
import time
|
||
|
||
# 添加项目根目录到Python路径
|
||
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
|
||
|
||
from main import MyWorld
|
||
from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget, QHBoxLayout, QLabel
|
||
from PyQt5.QtGui import QPixmap, QImage
|
||
from PyQt5.QtCore import QTimer
|
||
import numpy as np
|
||
|
||
|
||
def test_openxr_basic_display(world):
|
||
"""测试基本OpenXR显示功能"""
|
||
print("=== OpenXR基本显示测试 ===")
|
||
|
||
# 加载一些模型来展示
|
||
print("📦 加载演示场景...")
|
||
try:
|
||
# 创建一些简单的几何体作为演示
|
||
from panda3d.core import CardMaker, Vec4
|
||
|
||
# 创建地面
|
||
cm = CardMaker("ground")
|
||
cm.setFrame(-10, 10, -10, 10)
|
||
ground = world.render.attachNewNode(cm.generate())
|
||
ground.setPos(0, 0, -1)
|
||
ground.setColor(0.5, 0.8, 0.5, 1.0)
|
||
ground.setHpr(0, -90, 0)
|
||
|
||
# 创建一些立方体
|
||
for i in range(5):
|
||
for j in range(5):
|
||
cube = world.loader.loadModel("models/environment")
|
||
if not cube:
|
||
# 如果没有environment模型,创建简单的立方体
|
||
cm_cube = CardMaker(f"cube_{i}_{j}")
|
||
cm_cube.setFrame(-0.5, 0.5, -0.5, 0.5)
|
||
cube = world.render.attachNewNode(cm_cube.generate())
|
||
|
||
cube.setPos(i * 2 - 4, j * 2 - 4, 0)
|
||
cube.setScale(0.5)
|
||
cube.setColor(i/5.0, j/5.0, 1.0, 1.0)
|
||
|
||
print("✓ 演示场景加载完成")
|
||
|
||
except Exception as e:
|
||
print(f"场景加载错误: {str(e)}")
|
||
|
||
# 初始化OpenXR系统
|
||
print("\n🥽 初始化OpenXR显示系统...")
|
||
openxr_success = world.initializeOpenXR()
|
||
|
||
if openxr_success:
|
||
print("✓ OpenXR系统初始化成功")
|
||
openxr_info = world.getOpenXRInfo()
|
||
|
||
print(f"\n📊 OpenXR显示信息:")
|
||
print(f" - 模式: {openxr_info['mode']}")
|
||
print(f" - 渲染尺寸: {openxr_info['render_size']}")
|
||
print(f" - 模拟模式: {openxr_info['simulation_mode']}")
|
||
print(f" - 有线连接: {openxr_info['wired_connection']}")
|
||
|
||
if openxr_info['simulation_mode']:
|
||
print("\n💡 模拟模式说明:")
|
||
print(" - 左右眼视图将并排显示在主窗口")
|
||
print(" - 可以看到立体渲染效果")
|
||
print(" - 头部会有轻微的模拟摆动")
|
||
else:
|
||
print("\n💡 真实OpenXR模式:")
|
||
print(" - 画面将渲染到VR头盔")
|
||
print(" - 支持真实的头部追踪")
|
||
print(" - 控制器交互可用")
|
||
|
||
# 创建显示窗口
|
||
app = QApplication.instance()
|
||
if app is None:
|
||
app = QApplication(sys.argv)
|
||
|
||
# 创建主窗口显示OpenXR渲染结果
|
||
window = QMainWindow()
|
||
window.setWindowTitle("OpenXR Stereo Rendering Preview")
|
||
window.setGeometry(100, 100, 800, 300)
|
||
|
||
central_widget = QWidget()
|
||
layout = QHBoxLayout()
|
||
central_widget.setLayout(layout)
|
||
|
||
left_label = QLabel("Left Eye")
|
||
right_label = QLabel("Right Eye")
|
||
layout.addWidget(left_label)
|
||
layout.addWidget(right_label)
|
||
|
||
window.setCentralWidget(central_widget)
|
||
window.show()
|
||
|
||
# 定时更新显示
|
||
def update_display():
|
||
if hasattr(world.openxr_manager, 'left_eye_texture') and world.openxr_manager.left_eye_texture:
|
||
# 获取左眼纹理数据
|
||
tex = world.openxr_manager.left_eye_texture
|
||
try:
|
||
if tex.get_x_size() > 0 and tex.get_y_size() > 0:
|
||
# 添加对纹理数据有效性的检查
|
||
if tex.might_have_ram_image():
|
||
# 使用正确的API将纹理数据复制到QImage
|
||
data = tex.get_ram_image().get_data()
|
||
if data:
|
||
img = QImage(data, tex.get_x_size(), tex.get_y_size(), QImage.Format_RGBA8888)
|
||
pixmap = QPixmap.fromImage(img)
|
||
left_label.setPixmap(pixmap.scaled(400, 300))
|
||
left_label.setText("") # 清除文字,只显示图像
|
||
else:
|
||
left_label.setText("Left Eye (No Data)")
|
||
else:
|
||
left_label.setText("Left Eye (No RAM Image)")
|
||
else:
|
||
left_label.setText("Left Eye (Invalid Size)")
|
||
except Exception as e:
|
||
print(f"左眼纹理更新错误: {str(e)}")
|
||
left_label.setText("Left Eye (Error)")
|
||
else:
|
||
left_label.setText("Left Eye (No Texture)")
|
||
|
||
if hasattr(world.openxr_manager, 'right_eye_texture') and world.openxr_manager.right_eye_texture:
|
||
# 获取右眼纹理数据
|
||
tex = world.openxr_manager.right_eye_texture
|
||
try:
|
||
if tex.get_x_size() > 0 and tex.get_y_size() > 0:
|
||
# 添加对纹理数据有效性的检查
|
||
if tex.might_have_ram_image():
|
||
# 使用正确的API将纹理数据复制到QImage
|
||
data = tex.get_ram_image().get_data()
|
||
if data:
|
||
img = QImage(data, tex.get_x_size(), tex.get_y_size(), QImage.Format_RGBA8888)
|
||
pixmap = QPixmap.fromImage(img)
|
||
right_label.setPixmap(pixmap.scaled(400, 300))
|
||
right_label.setText("") # 清除文字,只显示图像
|
||
else:
|
||
right_label.setText("Right Eye (No Data)")
|
||
else:
|
||
right_label.setText("Right Eye (No RAM Image)")
|
||
else:
|
||
right_label.setText("Right Eye (Invalid Size)")
|
||
except Exception as e:
|
||
print(f"右眼纹理更新错误: {str(e)}")
|
||
right_label.setText("Right Eye (Error)")
|
||
else:
|
||
right_label.setText("Right Eye (No Texture)")
|
||
|
||
timer = QTimer()
|
||
timer.timeout.connect(update_display)
|
||
timer.start(33) # 约30 FPS
|
||
|
||
# 运行几秒钟以展示效果
|
||
print(f"\n🎮 OpenXR显示测试运行中... (10秒)")
|
||
print(" 观察主窗口中的立体渲染效果")
|
||
|
||
start_time = time.time()
|
||
while time.time() - start_time < 10:
|
||
# 处理Panda3D事件
|
||
if hasattr(world, 'taskMgr'):
|
||
world.taskMgr.step()
|
||
|
||
# 处理Qt事件
|
||
try:
|
||
app.processEvents()
|
||
except Exception as e:
|
||
print(f"Qt事件处理错误: {str(e)}")
|
||
|
||
# 添加短暂延迟以避免过度占用CPU
|
||
time.sleep(0.016) # ~60 FPS
|
||
|
||
print("✓ OpenXR显示测试完成")
|
||
|
||
else:
|
||
print("✗ OpenXR系统初始化失败")
|
||
return False
|
||
|
||
# 关闭OpenXR系统
|
||
world.shutdownOpenXR()
|
||
print("\n✓ OpenXR系统已关闭")
|
||
|
||
return True
|
||
|
||
|
||
def test_openxr_streaming(world):
|
||
"""测试OpenXR串流功能"""
|
||
print("=== OpenXR串流测试 ===")
|
||
|
||
# 初始化OpenXR系统
|
||
print("初始化OpenXR系统...")
|
||
if not world.initializeOpenXR():
|
||
print("✗ OpenXR系统初始化失败")
|
||
return False
|
||
|
||
# 启动输入处理
|
||
print("启动OpenXR输入处理...")
|
||
if not world.startOpenXRInput():
|
||
print("⚠ OpenXR输入处理启动失败")
|
||
|
||
# 初始化串流
|
||
print("初始化OpenXR串流...")
|
||
if world.initializeOpenXRStreaming():
|
||
print("✓ OpenXR串流初始化成功")
|
||
|
||
# 开始串流
|
||
print("开始OpenXR串流...")
|
||
if world.startOpenXRStreaming():
|
||
print("✓ OpenXR串流已开始")
|
||
|
||
# 创建显示窗口
|
||
app = QApplication.instance()
|
||
if app is None:
|
||
app = QApplication(sys.argv)
|
||
|
||
# 创建主窗口显示OpenXR渲染结果
|
||
window = QMainWindow()
|
||
window.setWindowTitle("OpenXR Streaming Preview")
|
||
window.setGeometry(100, 100, 800, 300)
|
||
|
||
central_widget = QWidget()
|
||
layout = QHBoxLayout()
|
||
central_widget.setLayout(layout)
|
||
|
||
left_label = QLabel("Left Eye")
|
||
right_label = QLabel("Right Eye")
|
||
layout.addWidget(left_label)
|
||
layout.addWidget(right_label)
|
||
|
||
window.setCentralWidget(central_widget)
|
||
window.show()
|
||
|
||
# 定时更新显示
|
||
def update_display():
|
||
try:
|
||
# 更新左眼纹理
|
||
if hasattr(world.openxr_manager, 'left_eye_texture') and world.openxr_manager.left_eye_texture:
|
||
tex = world.openxr_manager.left_eye_texture
|
||
# 检查纹理尺寸和数据有效性
|
||
if tex.get_x_size() > 0 and tex.get_y_size() > 0 and tex.might_have_ram_image():
|
||
ram_image = tex.get_ram_image()
|
||
if ram_image and ram_image.get_data():
|
||
# 使用正确的数据访问方法
|
||
img = QImage(ram_image.get_data(), tex.get_x_size(), tex.get_y_size(), QImage.Format_RGBA8888)
|
||
pixmap = QPixmap.fromImage(img)
|
||
left_label.setPixmap(pixmap.scaled(400, 300))
|
||
left_label.setText("") # 清除文字,只显示图像
|
||
|
||
# 更新右眼纹理
|
||
if hasattr(world.openxr_manager, 'right_eye_texture') and world.openxr_manager.right_eye_texture:
|
||
tex = world.openxr_manager.right_eye_texture
|
||
# 检查纹理尺寸和数据有效性
|
||
if tex.get_x_size() > 0 and tex.get_y_size() > 0 and tex.might_have_ram_image():
|
||
ram_image = tex.get_ram_image()
|
||
if ram_image and ram_image.get_data():
|
||
# 使用正确的数据访问方法
|
||
img = QImage(ram_image.get_data(), tex.get_x_size(), tex.get_y_size(), QImage.Format_RGBA8888)
|
||
pixmap = QPixmap.fromImage(img)
|
||
right_label.setPixmap(pixmap.scaled(400, 300))
|
||
right_label.setText("") # 清除文字,只显示图像
|
||
|
||
except Exception as e:
|
||
print(f"纹理更新错误: {str(e)}")
|
||
# 清除可能已损坏的纹理引用
|
||
if hasattr(world.openxr_manager, 'left_eye_texture'):
|
||
world.openxr_manager.left_eye_texture = None
|
||
if hasattr(world.openxr_manager, 'right_eye_texture'):
|
||
world.openxr_manager.right_eye_texture = None
|
||
|
||
timer = QTimer()
|
||
timer.timeout.connect(update_display)
|
||
timer.start(33) # 约30 FPS
|
||
|
||
# 运行几秒钟
|
||
print("OpenXR串流运行中... (5秒)")
|
||
start_time = time.time()
|
||
while time.time() - start_time < 5:
|
||
# 处理Panda3D事件
|
||
if hasattr(world, 'taskMgr'):
|
||
world.taskMgr.step()
|
||
|
||
# 处理Qt事件
|
||
try:
|
||
app.processEvents()
|
||
except Exception as e:
|
||
print(f"Qt事件处理错误: {str(e)}")
|
||
|
||
time.sleep(0.016) # ~60 FPS
|
||
|
||
# 停止串流
|
||
world.stopOpenXRStreaming()
|
||
print("✓ OpenXR串流已停止")
|
||
else:
|
||
print("✗ OpenXR串流启动失败")
|
||
else:
|
||
print("✗ OpenXR串流初始化失败")
|
||
|
||
# 关闭系统
|
||
world.shutdownOpenXRStreaming()
|
||
world.stopOpenXRInput()
|
||
world.shutdownOpenXR()
|
||
|
||
print("✓ OpenXR串流测试完成")
|
||
return True
|
||
|
||
|
||
def test_openxr_full(world):
|
||
"""测试完整OpenXR功能"""
|
||
print("=== 完整OpenXR功能测试 ===")
|
||
|
||
# 初始化OpenXR系统
|
||
print("初始化OpenXR系统...")
|
||
if not world.initializeOpenXR():
|
||
print("✗ OpenXR系统初始化失败")
|
||
return False
|
||
|
||
# 创建显示窗口
|
||
app = QApplication.instance()
|
||
if app is None:
|
||
app = QApplication(sys.argv)
|
||
|
||
# 创建主窗口显示OpenXR渲染结果
|
||
window = QMainWindow()
|
||
window.setWindowTitle("OpenXR Full Functionality Preview")
|
||
window.setGeometry(100, 100, 800, 300)
|
||
|
||
central_widget = QWidget()
|
||
layout = QHBoxLayout()
|
||
central_widget.setLayout(layout)
|
||
|
||
left_label = QLabel("Left Eye")
|
||
right_label = QLabel("Right Eye")
|
||
layout.addWidget(left_label)
|
||
layout.addWidget(right_label)
|
||
|
||
window.setCentralWidget(central_widget)
|
||
window.show()
|
||
|
||
# 定时更新显示
|
||
def update_display():
|
||
try:
|
||
# 更新左眼纹理
|
||
if hasattr(world.openxr_manager, 'left_eye_texture') and world.openxr_manager.left_eye_texture:
|
||
tex = world.openxr_manager.left_eye_texture
|
||
# 检查纹理尺寸和数据有效性
|
||
if tex.get_x_size() > 0 and tex.get_y_size() > 0 and tex.might_have_ram_image():
|
||
ram_image = tex.get_ram_image()
|
||
if ram_image and ram_image.get_data():
|
||
# 使用正确的数据访问方法
|
||
img = QImage(ram_image.get_data(), tex.get_x_size(), tex.get_y_size(), QImage.Format_RGBA8888)
|
||
pixmap = QPixmap.fromImage(img)
|
||
left_label.setPixmap(pixmap.scaled(400, 300))
|
||
left_label.setText("") # 清除文字,只显示图像
|
||
|
||
# 更新右眼纹理
|
||
if hasattr(world.openxr_manager, 'right_eye_texture') and world.openxr_manager.right_eye_texture:
|
||
tex = world.openxr_manager.right_eye_texture
|
||
# 检查纹理尺寸和数据有效性
|
||
if tex.get_x_size() > 0 and tex.get_y_size() > 0 and tex.might_have_ram_image():
|
||
ram_image = tex.get_ram_image()
|
||
if ram_image and ram_image.get_data():
|
||
# 使用正确的数据访问方法
|
||
img = QImage(ram_image.get_data(), tex.get_x_size(), tex.get_y_size(), QImage.Format_RGBA8888)
|
||
pixmap = QPixmap.fromImage(img)
|
||
right_label.setPixmap(pixmap.scaled(400, 300))
|
||
right_label.setText("") # 清除文字,只显示图像
|
||
|
||
except Exception as e:
|
||
print(f"纹理更新错误: {str(e)}")
|
||
# 清除可能已损坏的纹理引用
|
||
if hasattr(world.openxr_manager, 'left_eye_texture'):
|
||
world.openxr_manager.left_eye_texture = None
|
||
if hasattr(world.openxr_manager, 'right_eye_texture'):
|
||
world.openxr_manager.right_eye_texture = None
|
||
|
||
timer = QTimer()
|
||
timer.timeout.connect(update_display)
|
||
timer.start(33) # 约30 FPS
|
||
|
||
# 一键启用OpenXR模式
|
||
print("启用OpenXR模式...")
|
||
if world.enableOpenXRMode():
|
||
print("✓ OpenXR模式已启用")
|
||
|
||
# 启动输入处理
|
||
print("启动OpenXR输入处理...")
|
||
if not world.startOpenXRInput():
|
||
print("⚠ OpenXR输入处理启动失败")
|
||
|
||
# 获取状态
|
||
openxr_info = world.getOpenXRInfo()
|
||
print(f"\n📊 OpenXR状态:")
|
||
print(f" - 启用: {openxr_info['enabled']}")
|
||
print(f" - 模式: {openxr_info['mode']}")
|
||
print(f" - 模拟模式: {openxr_info['simulation_mode']}")
|
||
print(f" - 渲染尺寸: {openxr_info['render_size']}")
|
||
print(f" - 有线连接: {openxr_info['wired_connection']}")
|
||
|
||
# 获取状态
|
||
status = world.getOpenXRStatus()
|
||
print(f"\n📊 OpenXR状态:")
|
||
print(f" - 启用: {status['openxr_enabled']}")
|
||
print(f" - 连接: {status['openxr_connected']}")
|
||
print(f" - 串流: {status['openxr_streaming']}")
|
||
print(f" - 控制器: {len(status['controllers'])}个")
|
||
|
||
if status['streaming_status']:
|
||
streaming_status = status['streaming_status']
|
||
print(f"\n📡 串流状态:")
|
||
print(f" - FPS: {streaming_status['fps']}")
|
||
print(f" - 分辨率: {streaming_status['resolution']}")
|
||
print(f" - 延迟: {streaming_status['latency']}ms")
|
||
|
||
# 运行一段时间
|
||
print(f"\n🎮 OpenXR完整功能运行中... (15秒)")
|
||
start_time = time.time()
|
||
while time.time() - start_time < 15:
|
||
# 处理Panda3D事件
|
||
if hasattr(world, 'taskMgr'):
|
||
world.taskMgr.step()
|
||
|
||
# 处理Qt事件
|
||
try:
|
||
app.processEvents()
|
||
except Exception as e:
|
||
print(f"Qt事件处理错误: {str(e)}")
|
||
|
||
time.sleep(0.016) # ~60 FPS
|
||
|
||
# 禁用OpenXR模式
|
||
world.shutdownOpenXR()
|
||
print("✓ OpenXR模式已禁用")
|
||
|
||
else:
|
||
print("✗ OpenXR模式启用失败")
|
||
return False
|
||
|
||
# 关闭系统
|
||
world.stopOpenXRInput()
|
||
world.shutdownOpenXR()
|
||
|
||
print("✓ 完整OpenXR功能测试完成")
|
||
return True
|
||
|
||
|
||
if __name__ == "__main__":
|
||
print("OpenXR功能测试")
|
||
print("===============")
|
||
|
||
# 创建世界实例(只创建一次)
|
||
world = MyWorld()
|
||
|
||
# 运行测试
|
||
print("1. 基本显示测试")
|
||
test_openxr_basic_display(world)
|
||
|
||
print("\n" + "="*50 + "\n")
|
||
|
||
print("2. 串流功能测试")
|
||
test_openxr_streaming(world)
|
||
|
||
print("\n" + "="*50 + "\n")
|
||
|
||
print("3. 完整功能测试")
|
||
test_openxr_full(world)
|
||
|
||
print("\n" + "="*50)
|
||
print("所有测试完成!") |