EG/vr/vr_input.py
Rowland bb56813e44 feat(core): 优化 ALVR 串流功能并添加对 Pico 4 的支持
- 添加对有线连接的支持,特别是 Pico 4 的有线连接
- 改进 ALVR 服务器检测和连接逻辑
- 优化 VR 帧获取和处理流程
- 增加备用帧获取功能(主摄像机视图)
- 修复 VR 眼部纹理相关问题
- 优化 VR 任务管理,确保正确渲染和提交帧
2025-07-29 10:17:13 +08:00

363 lines
13 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.

"""
VR输入处理模块
此模块负责处理VR输入设备包括
- 控制器跟踪
- 按钮和触摸板输入
- 手势识别
- 触觉反馈
"""
from panda3d.core import NodePath, LineSegs, Vec3, Vec4
from panda3d.core import CollisionRay, CollisionNode, CollisionTraverser, CollisionHandlerQueue
from panda3d.core import BitMask32
from direct.task import Task
import math
import time
class VRInput:
"""处理VR输入设备"""
def __init__(self, vr_system):
"""
初始化VR输入处理器
Args:
vr_system: VR系统实例
"""
self.vr_system = vr_system
self.world = vr_system.world
self.device = vr_system.device
# 控制器
self.controllers = {}
self.controller_models = {}
self.controller_rays = {}
# 输入状态
self.button_states = {}
self.analog_states = {}
self.last_button_states = {}
# 配置
self.show_rays = True
self.ray_length = 100.0
self.ray_color = Vec4(0, 0.5, 1, 0.8)
# 功能标志
self.gesture_enabled = True
self.interaction_enabled = True
self.haptic_enabled = True
# 状态
self.is_initialized = False
self.tracking_origin = None
# 碰撞检测
self.traverser = CollisionTraverser("vr_traverser")
self.queue = CollisionHandlerQueue()
def initialize(self):
"""初始化VR输入系统"""
print("正在初始化VR输入系统...")
if not self.device:
print("✗ VR设备未初始化无法设置输入")
return False
# 1. 设置跟踪原点
self.tracking_origin = self.world.render.attachNewNode("vr_tracking_origin")
# 2. 设置控制器
if not self._setup_controllers():
print("✗ 设置VR控制器失败")
# 这不是致命错误,可以继续
print("⚠ VR将在没有控制器的情况下运行")
# 3. 定义输入动作
self._define_actions()
# 4. 创建控制器射线
if self.show_rays:
self._create_controller_rays()
self.is_initialized = True
print("✓ VR输入系统初始化完成")
return True
def _setup_controllers(self):
"""设置控制器跟踪"""
try:
# 获取设备控制器信息
if hasattr(self.device, 'get_controllers'):
controllers_info = self.device.get_controllers()
if not controllers_info:
print("⚠ 未检测到VR控制器")
return False
# 为每个控制器创建节点
for controller_id, info in controllers_info.items():
controller_node = self.tracking_origin.attachNewNode(f"controller_{controller_id}")
# 加载控制器模型(如果有)
model_path = info.get('model_path')
if model_path:
try:
controller_model = self.world.loader.loadModel(model_path)
controller_model.reparentTo(controller_node)
self.controller_models[controller_id] = controller_model
except Exception as e:
print(f"加载控制器模型失败: {e}")
# 使用默认模型
self._create_default_controller_model(controller_node, controller_id)
else:
# 使用默认模型
self._create_default_controller_model(controller_node, controller_id)
# 存储控制器节点
self.controllers[controller_id] = controller_node
# 初始化按钮状态
self.button_states[controller_id] = {}
self.analog_states[controller_id] = {}
self.last_button_states[controller_id] = {}
print(f"✓ 已设置 {len(self.controllers)} 个VR控制器")
return True
else:
print("⚠ VR设备不支持控制器跟踪")
return False
except Exception as e:
print(f"设置VR控制器时出错: {e}")
return False
def _create_default_controller_model(self, parent_node, controller_id):
"""创建默认控制器模型"""
from panda3d.core import GeomVertexFormat, GeomVertexData, Geom, GeomTriangles
from panda3d.core import GeomVertexWriter, GeomNode
# 创建简单的控制器几何体
format = GeomVertexFormat.getV3n3c4()
vdata = GeomVertexData('controller', format, Geom.UHStatic)
vertex = GeomVertexWriter(vdata, 'vertex')
normal = GeomVertexWriter(vdata, 'normal')
color = GeomVertexWriter(vdata, 'color')
# 简单的手柄形状
# 顶部
vertex.addData3f(0, 0, 0.1)
normal.addData3f(0, 0, 1)
color.addData4f(0.8, 0.8, 0.8, 1)
# 底部圆形的顶点
radius = 0.03
segments = 8
for i in range(segments):
angle = 2.0 * math.pi * i / segments
x = radius * math.cos(angle)
y = radius * math.sin(angle)
vertex.addData3f(x, y, -0.1)
normal.addData3f(x, y, 0)
color.addData4f(0.5, 0.5, 0.5, 1)
# 创建三角形
tris = GeomTriangles(Geom.UHStatic)
# 连接顶部到圆形底部
for i in range(segments):
tris.addVertices(0, i + 1, (i + 1) % segments + 1)
tris.closePrimitive()
# 创建几何体
geom = Geom(vdata)
geom.addPrimitive(tris)
# 创建GeomNode
node = GeomNode(f'controller_{controller_id}_geom')
node.addGeom(geom)
# 创建NodePath并附加到父节点
controller_model = NodePath(node)
controller_model.reparentTo(parent_node)
# 存储模型引用
self.controller_models[controller_id] = controller_model
def _define_actions(self):
"""定义输入动作映射"""
# 这里可以定义按钮到动作的映射
self.input_actions = {
"trigger": {
"description": "触发器",
"button": "trigger",
"action": self._on_trigger
},
"grip": {
"description": "握把",
"button": "grip",
"action": self._on_grip
},
"menu": {
"description": "菜单按钮",
"button": "menu",
"action": self._on_menu
},
"thumbstick_press": {
"description": "摇杆按下",
"button": "thumbstick",
"action": self._on_thumbstick_press
},
"teleport": {
"description": "传送",
"button": "thumbstick",
"analog": True,
"direction": "up",
"action": self._on_teleport
}
}
def _create_controller_rays(self):
"""创建控制器射线可视化"""
for controller_id, controller in self.controllers.items():
# 创建射线线段
ls = LineSegs()
ls.setThickness(2.0)
ls.setColor(self.ray_color)
ls.moveTo(0, 0, 0)
ls.drawTo(0, self.ray_length, 0)
# 创建射线节点
ray_node = ls.create()
ray_path = NodePath(ray_node)
ray_path.reparentTo(controller)
# 默认隐藏
ray_path.hide()
# 存储射线引用
self.controller_rays[controller_id] = ray_path
def update(self):
"""更新控制器状态(每帧调用)"""
if not self.is_initialized:
return
# 1. 更新控制器位置和旋转
self._update_controller_poses()
# 2. 更新按钮状态
self._update_button_states()
# 3. 处理输入事件
self._process_input()
# 4. 更新射线(如果启用)
if self.show_rays:
self._update_controller_rays()
def _update_controller_poses(self):
"""更新控制器位置和旋转"""
if not self.device or not hasattr(self.device, 'get_controller_poses'):
return
# 获取所有控制器的姿态
poses = self.device.get_controller_poses()
if not poses:
return
# 更新每个控制器的位置和旋转
for controller_id, pose in poses.items():
if controller_id in self.controllers:
controller = self.controllers[controller_id]
if 'position' in pose:
controller.setPos(Vec3(*pose['position']))
if 'rotation' in pose:
controller.setHpr(Vec3(*pose['rotation']))
def _update_button_states(self):
"""更新按钮状态"""
if not self.device or not hasattr(self.device, 'get_controller_inputs'):
return
# 获取所有控制器的输入状态
inputs = self.device.get_controller_inputs()
if not inputs:
return
# 更新每个控制器的按钮和模拟输入状态
for controller_id, input_data in inputs.items():
if controller_id in self.controllers:
# 保存上一帧的按钮状态
self.last_button_states[controller_id] = self.button_states[controller_id].copy()
# 更新按钮状态
if 'buttons' in input_data:
self.button_states[controller_id] = input_data['buttons']
# 更新模拟输入状态
if 'analogs' in input_data:
self.analog_states[controller_id] = input_data['analogs']
def _process_input(self):
"""处理输入事件"""
if not self.interaction_enabled:
return
# 检查每个定义的动作
for action_name, action_def in self.input_actions.items():
button = action_def.get('button')
for controller_id in self.controllers:
# 处理按钮动作
if not action_def.get('analog', False):
# 检查按钮是否刚被按下
if (button in self.button_states.get(controller_id, {}) and
button in self.last_button_states.get(controller_id, {}) and
self.button_states[controller_id][button] and
not self.last_button_states[controller_id][button]):
# 调用动作处理函数
if 'action' in action_def:
action_def['action'](controller_id)
# 处理模拟输入动作
else:
if button in self.analog_states.get(controller_id, {}):
analog_value = self.analog_states[controller_id][button]
direction = action_def.get('direction')
# 根据方向检查模拟输入
if direction == 'up' and analog_value[1] > 0.7:
if 'action' in action_def:
action_def['action'](controller_id)
elif direction == 'down' and analog_value[1] < -0.7:
if 'action' in action_def:
action_def['action'](controller_id)
elif direction == 'left' and analog_value[0] < -0.7:
if 'action' in action_def:
action_def['action'](controller_id)
elif direction == 'right' and analog_value[0] > 0.7:
if 'action' in action_def:
action_def['action'](controller_id)
def _update_controller_rays(self):
"""更新控制器射线可视化"""
for controller_id, ray in self.controller_rays.items():
if self.show_rays:
ray.show()
# 执行射线检测
if controller_id in self.controllers:
controller = self.controllers[controller_id]
origin = controller.getPos(self.world.render)
direction = Vec3(0, 1, 0) # 控制器前方
direction = self.world.render.getRelativeVector(controller, direction)
# 射线检测
result = self._perform_ray_test(origin, direction)
#