forked from Rowland/EG
430 lines
15 KiB
Python
430 lines
15 KiB
Python
"""
|
||
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 '禁用'}") |