- 创建详细的README文档,介绍项目功能、架构、使用方法等 - 新增.pyc文件和目录,用于缓存编译后的Python代码 - 添加.lingma规则文件,用于代码格式检查 - 删除ALVR串流处理器代码,准备替换为OpenXR输入处理器 - 新增OpenXR输入处理器代码,处理VR控制器输入
279 lines
9.6 KiB
Python
279 lines
9.6 KiB
Python
#!/usr/bin/env python
|
||
# -*- coding: utf-8 -*-
|
||
|
||
"""
|
||
简单的OpenXR测试脚本
|
||
|
||
在主程序基础上直接添加OpenXR相关代码进行测试
|
||
"""
|
||
|
||
import sys
|
||
import os
|
||
|
||
# 添加项目根目录到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
|
||
|
||
# Panda3D imports
|
||
from panda3d.core import Texture, GraphicsOutput, ATSNone, GraphicsPipe, Vec3
|
||
|
||
|
||
def setup_openxr_simulation(world):
|
||
"""在主程序基础上设置简单的OpenXR模拟"""
|
||
print("🔧 设置OpenXR模拟环境...")
|
||
|
||
# 获取渲染尺寸
|
||
render_width = 1280
|
||
render_height = 720
|
||
|
||
# 创建左右眼渲染缓冲区
|
||
from panda3d.core import WindowProperties, FrameBufferProperties, GraphicsPipe
|
||
|
||
# 创建窗口属性
|
||
winprops = WindowProperties()
|
||
winprops.setSize(render_width//2, render_height)
|
||
|
||
# 创建帧缓冲区属性
|
||
fbprops = FrameBufferProperties()
|
||
fbprops.setRgbColor(True)
|
||
fbprops.setRgbaBits(8, 8, 8, 8)
|
||
fbprops.setDepthBits(24)
|
||
|
||
# 设置渲染标志和GSG
|
||
flags = GraphicsPipe.BFRefuseWindow
|
||
gsg = world.win.getGsg()
|
||
|
||
left_eye_buffer = world.graphicsEngine.makeOutput(
|
||
world.pipe,
|
||
"left_eye",
|
||
-1000,
|
||
fbprops,
|
||
winprops,
|
||
flags,
|
||
gsg,
|
||
world.win
|
||
)
|
||
|
||
right_eye_buffer = world.graphicsEngine.makeOutput(
|
||
world.pipe,
|
||
"right_eye",
|
||
-1000,
|
||
fbprops,
|
||
winprops,
|
||
flags,
|
||
gsg,
|
||
world.win
|
||
)
|
||
|
||
if not left_eye_buffer or not right_eye_buffer:
|
||
print("❌ 无法创建眼部渲染缓冲区")
|
||
return None, None
|
||
|
||
# 创建用于显示的左右眼纹理,使用与主程序相同的方式
|
||
left_eye_texture = Texture("left_eye_texture")
|
||
right_eye_texture = Texture("right_eye_texture")
|
||
left_eye_texture.set_format(Texture.F_rgba8) # 使用主程序的设置方式
|
||
right_eye_texture.set_format(Texture.F_rgba8)
|
||
left_eye_texture.set_component_type(Texture.T_unsigned_byte)
|
||
right_eye_texture.set_component_type(Texture.T_unsigned_byte)
|
||
|
||
# 将纹理附加到渲染缓冲区,使用与主程序相同的方式
|
||
left_eye_buffer.add_render_texture(left_eye_texture, GraphicsOutput.RTM_copy_ram)
|
||
right_eye_buffer.add_render_texture(right_eye_texture, GraphicsOutput.RTM_copy_ram)
|
||
|
||
# 创建左右眼相机
|
||
left_eye_camera = world.makeCamera(left_eye_buffer)
|
||
right_eye_camera = world.makeCamera(right_eye_buffer)
|
||
|
||
# 设置相机参数,确保与主相机一致
|
||
left_eye_camera.node().get_lens().set_fov(world.camLens.get_fov())
|
||
right_eye_camera.node().get_lens().set_fov(world.camLens.get_fov())
|
||
left_eye_camera.node().get_lens().set_near(world.camLens.get_near())
|
||
right_eye_camera.node().get_lens().set_near(world.camLens.get_near())
|
||
left_eye_camera.node().get_lens().set_far(world.camLens.get_far())
|
||
right_eye_camera.node().get_lens().set_far(world.camLens.get_far())
|
||
|
||
# 设置相机位置(模拟瞳距)
|
||
left_eye_camera.setPos(world.cam.getPos() + Vec3(-0.03, 0, 0)) # 瞳距的一半
|
||
right_eye_camera.setPos(world.cam.getPos() + Vec3(0.03, 0, 0)) # 瞳距的一半
|
||
|
||
# 设置相机朝向,确保与主相机一致
|
||
left_eye_camera.setHpr(world.cam.getHpr())
|
||
right_eye_camera.setHpr(world.cam.getHpr())
|
||
|
||
# 设置相机渲染场景
|
||
left_eye_camera.node().setScene(world.render)
|
||
right_eye_camera.node().setScene(world.render)
|
||
|
||
# 获取渲染管线的最终输出纹理
|
||
def get_final_render_texture():
|
||
"""获取渲染管线的最终输出纹理"""
|
||
# 修复:正确获取最终渲染纹理的方法
|
||
if world.win:
|
||
# 直接从主窗口获取最终渲染纹理
|
||
return world.win.get_texture()
|
||
return None
|
||
|
||
# 等待并获取渲染结果
|
||
def wait_for_render_texture():
|
||
"""等待并获取有效的渲染纹理"""
|
||
timeout = 100 # 超时帧数
|
||
for i in range(timeout):
|
||
final_texture = get_final_render_texture()
|
||
if final_texture and final_texture.hasRamImage():
|
||
return final_texture
|
||
world.graphicsEngine.renderFrame()
|
||
return None
|
||
|
||
# 获取最终渲染纹理作为左右眼纹理的基础
|
||
final_texture = wait_for_render_texture()
|
||
if final_texture:
|
||
# 使用与主渲染管线相同的纹理参数
|
||
left_eye_texture = final_texture.makeCopy()
|
||
right_eye_texture = final_texture.makeCopy()
|
||
|
||
# 确保纹理数据可用
|
||
world.graphicsEngine.extractTextureData(left_eye_texture, world.win.getGsg())
|
||
world.graphicsEngine.extractTextureData(right_eye_texture, world.win.getGsg())
|
||
|
||
print("✅ 成功获取渲染管线的最终输出纹理")
|
||
else:
|
||
print("⚠️ 无法获取渲染管线的最终输出纹理,使用默认设置")
|
||
|
||
print("✅ OpenXR模拟环境设置完成")
|
||
return left_eye_texture, right_eye_texture
|
||
|
||
|
||
def main():
|
||
"""主函数"""
|
||
print("🚀 启动OpenXR简单测试...")
|
||
|
||
# 创建应用程序和世界实例
|
||
app = QApplication(sys.argv)
|
||
world = MyWorld()
|
||
|
||
# 加载一些测试模型
|
||
print("📦 加载测试场景...")
|
||
from panda3d.core import CardMaker
|
||
|
||
# 创建地面
|
||
cm = CardMaker("ground")
|
||
cm.setFrame(-5, 5, -5, 5)
|
||
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)
|
||
|
||
# 创建测试立方体
|
||
cube = world.loader.loadModel("models/environment")
|
||
if cube:
|
||
cube.reparentTo(world.render)
|
||
cube.setScale(0.5)
|
||
cube.setPos(0, 0, 0)
|
||
else:
|
||
# 如果没有环境模型,创建简单立方体
|
||
cm_cube = CardMaker("cube")
|
||
cm_cube.setFrame(-0.5, 0.5, -0.5, 0.5)
|
||
cube = world.render.attachNewNode(cm_cube.generate())
|
||
cube.setPos(0, 0, 0)
|
||
|
||
# 设置OpenXR模拟
|
||
left_tex, right_tex = setup_openxr_simulation(world)
|
||
|
||
if not left_tex or not right_tex:
|
||
print("❌ OpenXR模拟设置失败")
|
||
return
|
||
|
||
# 创建显示窗口
|
||
window = QMainWindow()
|
||
window.setWindowTitle("OpenXR Simple Test")
|
||
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:
|
||
# 让RenderPipeline进行渲染
|
||
world.taskMgr.step()
|
||
|
||
# 渲染几帧以确保纹理更新
|
||
for _ in range(5):
|
||
world.graphicsEngine.renderFrame()
|
||
|
||
# 更新左眼纹理
|
||
if left_tex:
|
||
# 检查纹理是否具有RAM图像
|
||
if not left_tex.hasRamImage():
|
||
# 如果没有RAM图像,尝试提取
|
||
gsg = world.win.getGsg()
|
||
if gsg:
|
||
world.graphicsEngine.extractTextureData(left_tex, gsg)
|
||
|
||
# 现在检查是否有RAM图像和有效数据
|
||
if left_tex.hasRamImage():
|
||
data = left_tex.getRamImage().getData()
|
||
width = left_tex.getXSize()
|
||
height = left_tex.getYSize()
|
||
expected_len = width * height * 4
|
||
|
||
# 检查数据长度是否正确
|
||
if len(data) == expected_len:
|
||
# 使用与主程序相同的方式创建图像
|
||
img = QImage(data, width, height, QImage.Format_ARGB32).mirrored()
|
||
pixmap = QPixmap.fromImage(img)
|
||
left_label.setPixmap(pixmap.scaled(400, 300))
|
||
left_label.setText("")
|
||
|
||
# 更新右眼纹理
|
||
if right_tex:
|
||
# 检查纹理是否具有RAM图像
|
||
if not right_tex.hasRamImage():
|
||
# 如果没有RAM图像,尝试提取
|
||
gsg = world.win.getGsg()
|
||
if gsg:
|
||
world.graphicsEngine.extractTextureData(right_tex, gsg)
|
||
|
||
# 现在检查是否有RAM图像和有效数据
|
||
if right_tex.hasRamImage():
|
||
data = right_tex.getRamImage().getData()
|
||
width = right_tex.getXSize()
|
||
height = right_tex.getYSize()
|
||
expected_len = width * height * 4
|
||
|
||
# 检查数据长度是否正确
|
||
if len(data) == expected_len:
|
||
# 使用与主程序相同的方式创建图像
|
||
img = QImage(data, width, height, QImage.Format_ARGB32).mirrored()
|
||
pixmap = QPixmap.fromImage(img)
|
||
right_label.setPixmap(pixmap.scaled(400, 300))
|
||
right_label.setText("")
|
||
except Exception as e:
|
||
print(f"纹理更新错误: {str(e)}")
|
||
|
||
# 定时更新显示
|
||
timer = QTimer()
|
||
timer.timeout.connect(update_display)
|
||
timer.start(33) # 约30 FPS
|
||
|
||
# 运行应用程序
|
||
print("✅ OpenXR简单测试运行中...")
|
||
sys.exit(app.exec_())
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main() |