- 添加对有线连接的支持,特别是 Pico 4 的有线连接 - 改进 ALVR 服务器检测和连接逻辑 - 优化 VR 帧获取和处理流程 - 增加备用帧获取功能(主摄像机视图) - 修复 VR 眼部纹理相关问题 - 优化 VR 任务管理,确保正确渲染和提交帧
363 lines
13 KiB
Python
363 lines
13 KiB
Python
"""
|
||
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)
|
||
|
||
# |