1
0
forked from Rowland/EG
EG/core/vr_input_handler.py
2025-08-13 09:30:16 +08:00

430 lines
15 KiB
Python
Raw Permalink 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控制器输入、手势识别和VR交互
支持多种VR控制器和手势输入
"""
from direct.showbase.DirectObject import DirectObject
from panda3d.core import Vec3, Point3, CollisionRay, CollisionNode, CollisionHandlerQueue
from direct.task import Task
import time
class VRInputHandler(DirectObject):
"""VR输入处理器"""
def __init__(self, world, vr_manager):
super().__init__()
self.world = world
self.vr_manager = vr_manager
# 控制器状态
self.controllers = {}
self.controller_nodes = {}
self.controller_rays = {}
# 手势识别
self.gesture_enabled = True
self.gesture_history = []
self.gesture_threshold = 0.1
# 交互系统
self.interaction_enabled = True
self.selected_object = None
self.grab_offset = Vec3(0, 0, 0)
# 输入映射
self.input_mappings = {
'trigger': self._handle_trigger,
'grip': self._handle_grip,
'touchpad': self._handle_touchpad,
'menu': self._handle_menu,
'system': self._handle_system
}
print("✓ VR输入处理器初始化完成")
def start_input_handling(self):
"""启动输入处理"""
if not self.vr_manager.is_vr_enabled():
print("VR未启用无法启动输入处理")
return False
# 启动输入更新任务
self.world.taskMgr.add(self._update_input, "vr_input_update")
# 设置控制器可视化
self._setup_controller_visualization()
print("✓ VR输入处理已启动")
return True
def stop_input_handling(self):
"""停止输入处理"""
self.world.taskMgr.remove("vr_input_update")
self._cleanup_controller_visualization()
print("✓ VR输入处理已停止")
def _update_input(self, task):
"""更新输入处理"""
if not self.vr_manager.is_vr_enabled():
return Task.cont
try:
# 更新所有控制器
self._update_controllers()
# 处理手势识别
if self.gesture_enabled:
self._process_gestures()
# 处理交互
if self.interaction_enabled:
self._process_interactions()
except Exception as e:
print(f"VR输入更新错误: {str(e)}")
return Task.cont
def _update_controllers(self):
"""更新控制器状态"""
# 获取控制器姿态
controller_poses = self.vr_manager.controller_poses
for controller_id, pose in controller_poses.items():
# 获取控制器输入
input_data = self.vr_manager.get_controller_input(controller_id)
if not input_data:
continue
# 更新控制器状态
if controller_id not in self.controllers:
self.controllers[controller_id] = {}
prev_state = self.controllers[controller_id].copy()
self.controllers[controller_id] = input_data
# 更新控制器可视化
self._update_controller_visualization(controller_id, pose)
# 处理输入事件
self._process_controller_input(controller_id, input_data, prev_state)
def _process_controller_input(self, controller_id, current_state, prev_state):
"""处理控制器输入"""
# 检查按钮状态变化
for input_type, handler in self.input_mappings.items():
if input_type in current_state:
current_value = current_state[input_type]
prev_value = prev_state.get(input_type, 0)
# 处理按钮按下/释放
if isinstance(current_value, (int, float)):
if current_value > 0.5 and prev_value <= 0.5:
handler(controller_id, 'press', current_value)
elif current_value <= 0.5 and prev_value > 0.5:
handler(controller_id, 'release', current_value)
elif current_value > 0.5:
handler(controller_id, 'hold', current_value)
# 处理触摸板
elif isinstance(current_value, tuple) and len(current_value) == 2:
if current_value != prev_value:
handler(controller_id, 'move', current_value)
def _handle_trigger(self, controller_id, action, value):
"""处理扳机输入"""
if action == 'press':
print(f"控制器 {controller_id} 扳机按下 (强度: {value:.2f})")
self._try_grab_object(controller_id)
elif action == 'release':
print(f"控制器 {controller_id} 扳机释放")
self._try_release_object(controller_id)
def _handle_grip(self, controller_id, action, value):
"""处理握持输入"""
if action == 'press':
print(f"控制器 {controller_id} 握持按下 (强度: {value:.2f})")
self._toggle_interaction_mode(controller_id)
elif action == 'release':
print(f"控制器 {controller_id} 握持释放")
def _handle_touchpad(self, controller_id, action, value):
"""处理触摸板输入"""
if action == 'move':
x, y = value
print(f"控制器 {controller_id} 触摸板: ({x:.2f}, {y:.2f})")
# 根据触摸板位置执行不同操作
if abs(x) > 0.7: # 左右滑动
self._handle_horizontal_swipe(controller_id, x)
elif abs(y) > 0.7: # 上下滑动
self._handle_vertical_swipe(controller_id, y)
def _handle_menu(self, controller_id, action, value):
"""处理菜单按钮"""
if action == 'press':
print(f"控制器 {controller_id} 菜单按钮按下")
self._show_vr_menu(controller_id)
def _handle_system(self, controller_id, action, value):
"""处理系统按钮"""
if action == 'press':
print(f"控制器 {controller_id} 系统按钮按下")
# 系统按钮通常由VR系统处理
def _handle_horizontal_swipe(self, controller_id, direction):
"""处理水平滑动"""
if direction > 0:
print(f"控制器 {controller_id} 右滑")
self._switch_tool(controller_id, 'next')
else:
print(f"控制器 {controller_id} 左滑")
self._switch_tool(controller_id, 'prev')
def _handle_vertical_swipe(self, controller_id, direction):
"""处理垂直滑动"""
if direction > 0:
print(f"控制器 {controller_id} 上滑")
self._zoom_in(controller_id)
else:
print(f"控制器 {controller_id} 下滑")
self._zoom_out(controller_id)
def _try_grab_object(self, controller_id):
"""尝试抓取对象"""
if controller_id not in self.controllers:
return
# 获取控制器射线
ray = self._get_controller_ray(controller_id)
if not ray:
return
# 执行射线检测
hit_object = self._raycast_from_controller(controller_id)
if hit_object:
self.selected_object = hit_object
controller_pose = self.controllers[controller_id].get('pose')
if controller_pose:
# 计算抓取偏移
object_pos = hit_object.getPos()
controller_pos = controller_pose.getTranslate()
self.grab_offset = object_pos - controller_pos
print(f"抓取对象: {hit_object.getName()}")
# 发送抓取事件
self.world.event_handler.messenger.send('vr-object-grabbed', [hit_object, controller_id])
def _try_release_object(self, controller_id):
"""尝试释放对象"""
if self.selected_object:
print(f"释放对象: {self.selected_object.getName()}")
# 发送释放事件
self.world.event_handler.messenger.send('vr-object-released', [self.selected_object, controller_id])
self.selected_object = None
self.grab_offset = Vec3(0, 0, 0)
def _raycast_from_controller(self, controller_id):
"""从控制器发射射线检测"""
if controller_id not in self.controllers:
return None
controller_pose = self.controllers[controller_id].get('pose')
if not controller_pose:
return None
# 获取控制器位置和方向
controller_pos = controller_pose.getTranslate()
controller_forward = controller_pose.getQuat().getForward()
# 创建射线
ray = CollisionRay()
ray.setOrigin(controller_pos)
ray.setDirection(controller_forward)
# 执行碰撞检测
traverser = self.world.cTrav if hasattr(self.world, 'cTrav') else None
if not traverser:
return None
handler = CollisionHandlerQueue()
collision_node = CollisionNode('vr_controller_ray')
collision_node.addSolid(ray)
ray_np = self.world.render.attachNewNode(collision_node)
traverser.addCollider(ray_np, handler)
# 遍历碰撞
traverser.traverse(self.world.render)
# 清理
ray_np.removeNode()
# 返回最近的碰撞对象
if handler.getNumEntries() > 0:
handler.sortEntries()
entry = handler.getEntry(0)
return entry.getIntoNodePath()
return None
def _get_controller_ray(self, controller_id):
"""获取控制器射线"""
return self.controller_rays.get(controller_id)
def _setup_controller_visualization(self):
"""设置控制器可视化"""
print("设置控制器可视化...")
# 为每个控制器创建可视化节点
for controller_id in self.controllers:
self._create_controller_model(controller_id)
def _create_controller_model(self, controller_id):
"""创建控制器模型"""
# 创建简单的控制器模型(立方体)
from panda3d.core import CardMaker
cm = CardMaker(f"controller_{controller_id}")
cm.setFrame(-0.05, 0.05, -0.05, 0.05)
controller_node = self.world.render.attachNewNode(cm.generate())
controller_node.setColor(0.2, 0.8, 1.0, 0.8)
controller_node.setScale(0.1, 0.2, 0.05)
self.controller_nodes[controller_id] = controller_node
# 创建控制器射线可视化
self._create_controller_ray_visual(controller_id)
def _create_controller_ray_visual(self, controller_id):
"""创建控制器射线可视化"""
from panda3d.core import LineSegs
# 创建射线线段
lines = LineSegs()
lines.setColor(1, 0, 0, 0.5)
lines.moveTo(0, 0, 0)
lines.drawTo(0, 2, 0) # 2米长的射线
ray_node = self.world.render.attachNewNode(lines.create())
ray_node.setRenderModeWireframe()
ray_node.hide() # 默认隐藏
self.controller_rays[controller_id] = ray_node
def _update_controller_visualization(self, controller_id, pose):
"""更新控制器可视化"""
if controller_id in self.controller_nodes:
node = self.controller_nodes[controller_id]
node.setMat(pose)
if controller_id in self.controller_rays:
ray_node = self.controller_rays[controller_id]
ray_node.setMat(pose)
def _cleanup_controller_visualization(self):
"""清理控制器可视化"""
for node in self.controller_nodes.values():
node.removeNode()
for ray in self.controller_rays.values():
ray.removeNode()
self.controller_nodes.clear()
self.controller_rays.clear()
def _process_gestures(self):
"""处理手势识别"""
# 简单的手势识别逻辑
# 这里可以实现更复杂的手势识别算法
pass
def _process_interactions(self):
"""处理交互逻辑"""
# 如果有选中的对象,更新其位置
if self.selected_object:
self._update_grabbed_object()
def _update_grabbed_object(self):
"""更新被抓取对象的位置"""
if not self.selected_object:
return
# 找到抓取该对象的控制器
grabbing_controller = None
for controller_id, controller_state in self.controllers.items():
if controller_state.get('trigger', 0) > 0.5:
grabbing_controller = controller_id
break
if not grabbing_controller:
return
# 更新对象位置
controller_pose = self.controllers[grabbing_controller].get('pose')
if controller_pose:
controller_pos = controller_pose.getTranslate()
new_pos = controller_pos + self.grab_offset
self.selected_object.setPos(new_pos)
def _toggle_interaction_mode(self, controller_id):
"""切换交互模式"""
self.interaction_enabled = not self.interaction_enabled
print(f"交互模式: {'启用' if self.interaction_enabled else '禁用'}")
def _show_vr_menu(self, controller_id):
"""显示VR菜单"""
print(f"显示VR菜单 (控制器 {controller_id})")
# 这里可以实现VR菜单显示逻辑
pass
def _switch_tool(self, controller_id, direction):
"""切换工具"""
print(f"切换工具: {direction} (控制器 {controller_id})")
# 这里可以实现工具切换逻辑
pass
def _zoom_in(self, controller_id):
"""放大"""
print(f"放大 (控制器 {controller_id})")
# 实现放大逻辑
pass
def _zoom_out(self, controller_id):
"""缩小"""
print(f"缩小 (控制器 {controller_id})")
# 实现缩小逻辑
pass
def show_controller_rays(self, show=True):
"""显示/隐藏控制器射线"""
for ray in self.controller_rays.values():
if show:
ray.show()
else:
ray.hide()
def get_controller_state(self, controller_id):
"""获取控制器状态"""
return self.controllers.get(controller_id, {})
def get_all_controllers(self):
"""获取所有控制器"""
return list(self.controllers.keys())
def set_gesture_enabled(self, enabled):
"""设置手势识别启用状态"""
self.gesture_enabled = enabled
print(f"手势识别: {'启用' if enabled else '禁用'}")
def set_interaction_enabled(self, enabled):
"""设置交互启用状态"""
self.interaction_enabled = enabled
print(f"VR交互: {'启用' if enabled else '禁用'}")