732 lines
26 KiB
Python
732 lines
26 KiB
Python
"""
|
|
物理引擎集成模块
|
|
负责将物理引擎中的碰撞和力反馈转换为触觉效果
|
|
"""
|
|
|
|
import time
|
|
from typing import Dict, Any, List, Optional
|
|
import math
|
|
|
|
class PhysicsIntegration:
|
|
"""
|
|
物理引擎集成
|
|
负责将物理引擎中的碰撞和力反馈转换为触觉效果
|
|
"""
|
|
|
|
def __init__(self, plugin):
|
|
"""
|
|
初始化物理引擎集成
|
|
|
|
Args:
|
|
plugin: 触觉反馈系统插件实例
|
|
"""
|
|
self.plugin = plugin
|
|
self.enabled = False
|
|
self.initialized = False
|
|
|
|
# 物理事件监听器
|
|
self.physics_listeners = {}
|
|
|
|
# 碰撞效果配置
|
|
self.collision_config = {
|
|
'enable_collision_feedback': True,
|
|
'min_impulse_threshold': 0.1,
|
|
'max_impulse_threshold': 100.0,
|
|
'intensity_scale': 1.0,
|
|
'frequency_scale': 1.0,
|
|
'duration_scale': 1.0,
|
|
'effect_type': 'impulse'
|
|
}
|
|
|
|
# 力反馈配置
|
|
self.force_config = {
|
|
'enable_force_feedback': True,
|
|
'min_force_threshold': 0.01,
|
|
'max_force_threshold': 10.0,
|
|
'intensity_scale': 1.0,
|
|
'frequency_scale': 1.0,
|
|
'smoothing_factor': 0.1
|
|
}
|
|
|
|
# 摩擦效果配置
|
|
self.friction_config = {
|
|
'enable_friction_feedback': True,
|
|
'min_velocity_threshold': 0.1,
|
|
'texture_sensitivity': 1.0,
|
|
'vibration_frequency': 20.0
|
|
}
|
|
|
|
# 材质反馈配置
|
|
self.material_config = {
|
|
'enable_material_feedback': True,
|
|
'material_effects': {
|
|
'metal': {'intensity': 0.8, 'frequency': 60.0, 'type': 'buzz'},
|
|
'wood': {'intensity': 0.6, 'frequency': 30.0, 'type': 'rumble'},
|
|
'stone': {'intensity': 0.9, 'frequency': 80.0, 'type': 'click'},
|
|
'fabric': {'intensity': 0.3, 'frequency': 15.0, 'type': 'soft_rumble'},
|
|
'rubber': {'intensity': 0.5, 'frequency': 25.0, 'type': 'bounce'}
|
|
}
|
|
}
|
|
|
|
# 状态跟踪
|
|
self.contact_points = {}
|
|
self.force_feedback_state = {}
|
|
self.friction_state = {}
|
|
|
|
# 统计信息
|
|
self.stats = {
|
|
'collisions_detected': 0,
|
|
'forces_processed': 0,
|
|
'friction_effects': 0,
|
|
'material_effects': 0,
|
|
'haptic_effects_generated': 0
|
|
}
|
|
|
|
# 回调函数
|
|
self.physics_callbacks = {
|
|
'collision_detected': [],
|
|
'force_applied': [],
|
|
'friction_updated': [],
|
|
'haptic_effect_generated': []
|
|
}
|
|
|
|
print("✓ 物理引擎集成已创建")
|
|
|
|
def initialize(self) -> bool:
|
|
"""
|
|
初始化物理引擎集成
|
|
|
|
Returns:
|
|
是否初始化成功
|
|
"""
|
|
try:
|
|
self.initialized = True
|
|
print("✓ 物理引擎集成初始化完成")
|
|
return True
|
|
|
|
except Exception as e:
|
|
print(f"✗ 物理引擎集成初始化失败: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
return False
|
|
|
|
def enable(self) -> bool:
|
|
"""
|
|
启用物理引擎集成
|
|
|
|
Returns:
|
|
是否启用成功
|
|
"""
|
|
try:
|
|
if not self.initialized:
|
|
print("✗ 物理引擎集成未初始化")
|
|
return False
|
|
|
|
self.enabled = True
|
|
print("✓ 物理引擎集成已启用")
|
|
return True
|
|
|
|
except Exception as e:
|
|
print(f"✗ 物理引擎集成启用失败: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
return False
|
|
|
|
def disable(self):
|
|
"""禁用物理引擎集成"""
|
|
try:
|
|
self.enabled = False
|
|
print("✓ 物理引擎集成已禁用")
|
|
|
|
except Exception as e:
|
|
print(f"✗ 物理引擎集成禁用失败: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
|
|
def finalize(self):
|
|
"""清理物理引擎集成资源"""
|
|
try:
|
|
self.disable()
|
|
self.physics_listeners.clear()
|
|
self.contact_points.clear()
|
|
self.force_feedback_state.clear()
|
|
self.friction_state.clear()
|
|
self.physics_callbacks.clear()
|
|
self.initialized = False
|
|
print("✓ 物理引擎集成资源已清理")
|
|
|
|
except Exception as e:
|
|
print(f"✗ 物理引擎集成资源清理失败: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
|
|
def update(self, dt: float):
|
|
"""
|
|
更新物理引擎集成状态
|
|
|
|
Args:
|
|
dt: 时间增量
|
|
"""
|
|
try:
|
|
if not self.enabled:
|
|
return
|
|
|
|
# 更新摩擦状态
|
|
self._update_friction_state(dt)
|
|
|
|
# 更新力反馈状态
|
|
self._update_force_feedback_state(dt)
|
|
|
|
except Exception as e:
|
|
print(f"✗ 物理引擎集成更新失败: {e}")
|
|
import traceback
|
|
traceback.print_exc()
|
|
|
|
def _update_friction_state(self, dt: float):
|
|
"""更新摩擦状态"""
|
|
try:
|
|
# 减少摩擦状态值(衰减)
|
|
decay_rate = 0.95
|
|
keys_to_remove = []
|
|
|
|
for key, state in self.friction_state.items():
|
|
state['intensity'] *= decay_rate
|
|
if state['intensity'] < 0.01:
|
|
keys_to_remove.append(key)
|
|
|
|
# 移除已衰减完毕的状态
|
|
for key in keys_to_remove:
|
|
del self.friction_state[key]
|
|
|
|
except Exception as e:
|
|
print(f"✗ 摩擦状态更新失败: {e}")
|
|
|
|
def _update_force_feedback_state(self, dt: float):
|
|
"""更新力反馈状态"""
|
|
try:
|
|
# 减少力反馈状态值(平滑)
|
|
smoothing = self.force_config['smoothing_factor']
|
|
keys_to_remove = []
|
|
|
|
for key, state in self.force_feedback_state.items():
|
|
state['current_force'] = (
|
|
state['current_force'] * (1 - smoothing) +
|
|
state['target_force'] * smoothing
|
|
)
|
|
if abs(state['current_force']) < 0.001:
|
|
keys_to_remove.append(key)
|
|
|
|
# 移除已平滑完毕的状态
|
|
for key in keys_to_remove:
|
|
del self.force_feedback_state[key]
|
|
|
|
except Exception as e:
|
|
print(f"✗ 力反馈状态更新失败: {e}")
|
|
|
|
def process_collision(self, collision_data: Dict[str, Any]) -> bool:
|
|
"""
|
|
处理碰撞事件并生成触觉效果
|
|
|
|
Args:
|
|
collision_data: 碰撞数据
|
|
|
|
Returns:
|
|
是否处理成功
|
|
"""
|
|
try:
|
|
if not self.enabled or not self.collision_config['enable_collision_feedback']:
|
|
return False
|
|
|
|
# 提取碰撞信息
|
|
impulse = collision_data.get('impulse', 0.0)
|
|
contact_point = collision_data.get('contact_point', (0, 0, 0))
|
|
object_a = collision_data.get('object_a', 'unknown')
|
|
object_b = collision_data.get('object_b', 'unknown')
|
|
material_a = collision_data.get('material_a', 'default')
|
|
material_b = collision_data.get('material_b', 'default')
|
|
device_id = collision_data.get('device_id')
|
|
|
|
# 检查冲量阈值
|
|
if impulse < self.collision_config['min_impulse_threshold']:
|
|
return False
|
|
|
|
if impulse > self.collision_config['max_impulse_threshold']:
|
|
impulse = self.collision_config['max_impulse_threshold']
|
|
|
|
# 计算触觉效果强度
|
|
intensity = (
|
|
impulse / self.collision_config['max_impulse_threshold'] *
|
|
self.collision_config['intensity_scale']
|
|
)
|
|
intensity = max(0.0, min(1.0, intensity))
|
|
|
|
# 计算频率
|
|
frequency = 20.0 + (impulse * self.collision_config['frequency_scale'])
|
|
frequency = max(1.0, min(100.0, frequency))
|
|
|
|
# 计算持续时间
|
|
duration = 0.05 + (impulse * 0.01 * self.collision_config['duration_scale'])
|
|
duration = max(0.01, min(1.0, duration))
|
|
|
|
# 获取材质效果
|
|
material_effect = self._get_material_effect(material_a, material_b)
|
|
|
|
# 生成触觉效果
|
|
effect_type = self.collision_config['effect_type']
|
|
if material_effect:
|
|
effect_type = material_effect.get('type', effect_type)
|
|
intensity *= material_effect.get('intensity', 1.0)
|
|
frequency = material_effect.get('frequency', frequency)
|
|
|
|
# 播放触觉效果
|
|
if self.plugin.haptic_manager:
|
|
effect_id = self.plugin.haptic_manager.play_effect(
|
|
effect_type=effect_type,
|
|
device_id=device_id,
|
|
intensity=intensity,
|
|
duration=duration,
|
|
frequency=frequency,
|
|
parameters={
|
|
'collision_impulse': impulse,
|
|
'contact_point': contact_point,
|
|
'objects': [object_a, object_b],
|
|
'materials': [material_a, material_b]
|
|
}
|
|
)
|
|
|
|
if effect_id >= 0:
|
|
# 触发效果生成回调
|
|
self._trigger_physics_callback('haptic_effect_generated', {
|
|
'effect_id': effect_id,
|
|
'effect_type': effect_type,
|
|
'source': 'collision',
|
|
'intensity': intensity,
|
|
'frequency': frequency,
|
|
'duration': duration
|
|
})
|
|
|
|
self.stats['haptic_effects_generated'] += 1
|
|
self.stats['collisions_detected'] += 1
|
|
|
|
print(f"✓ 碰撞触觉效果已生成: {effect_type}")
|
|
return True
|
|
|
|
return False
|
|
|
|
except Exception as e:
|
|
print(f"✗ 碰撞处理失败: {e}")
|
|
return False
|
|
|
|
def process_force_feedback(self, force_data: Dict[str, Any]) -> bool:
|
|
"""
|
|
处理力反馈并生成连续触觉效果
|
|
|
|
Args:
|
|
force_data: 力反馈数据
|
|
|
|
Returns:
|
|
是否处理成功
|
|
"""
|
|
try:
|
|
if not self.enabled or not self.force_config['enable_force_feedback']:
|
|
return False
|
|
|
|
# 提取力反馈信息
|
|
force = force_data.get('force', 0.0)
|
|
force_direction = force_data.get('direction', (0, 0, 0))
|
|
object_id = force_data.get('object_id', 'unknown')
|
|
device_id = force_data.get('device_id')
|
|
|
|
# 检查力阈值
|
|
if abs(force) < self.force_config['min_force_threshold']:
|
|
return False
|
|
|
|
if abs(force) > self.force_config['max_force_threshold']:
|
|
force = math.copysign(self.force_config['max_force_threshold'], force)
|
|
|
|
# 计算触觉效果强度
|
|
intensity = (
|
|
abs(force) / self.force_config['max_force_threshold'] *
|
|
self.force_config['intensity_scale']
|
|
)
|
|
intensity = max(0.0, min(1.0, intensity))
|
|
|
|
# 计算频率
|
|
frequency = 10.0 + (abs(force) * self.force_config['frequency_scale'])
|
|
frequency = max(1.0, min(100.0, frequency))
|
|
|
|
# 更新力反馈状态
|
|
state_key = f"force_{object_id}"
|
|
if state_key not in self.force_feedback_state:
|
|
self.force_feedback_state[state_key] = {
|
|
'current_force': 0.0,
|
|
'target_force': force
|
|
}
|
|
else:
|
|
self.force_feedback_state[state_key]['target_force'] = force
|
|
|
|
# 生成连续触觉效果
|
|
if self.plugin.haptic_manager:
|
|
# 播放连续效果
|
|
effect_id = self.plugin.haptic_manager.play_effect(
|
|
effect_type='periodic',
|
|
device_id=device_id,
|
|
intensity=intensity,
|
|
duration=0.1, # 短持续时间,循环播放
|
|
frequency=frequency,
|
|
parameters={
|
|
'force': force,
|
|
'direction': force_direction,
|
|
'object_id': object_id
|
|
}
|
|
)
|
|
|
|
if effect_id >= 0:
|
|
# 触发效果生成回调
|
|
self._trigger_physics_callback('haptic_effect_generated', {
|
|
'effect_id': effect_id,
|
|
'effect_type': 'periodic',
|
|
'source': 'force',
|
|
'intensity': intensity,
|
|
'frequency': frequency
|
|
})
|
|
|
|
self.stats['haptic_effects_generated'] += 1
|
|
self.stats['forces_processed'] += 1
|
|
|
|
print(f"✓ 力反馈触觉效果已生成: 强度 {intensity:.2f}")
|
|
return True
|
|
|
|
return False
|
|
|
|
except Exception as e:
|
|
print(f"✗ 力反馈处理失败: {e}")
|
|
return False
|
|
|
|
def process_friction(self, friction_data: Dict[str, Any]) -> bool:
|
|
"""
|
|
处理摩擦并生成纹理触觉效果
|
|
|
|
Args:
|
|
friction_data: 摩擦数据
|
|
|
|
Returns:
|
|
是否处理成功
|
|
"""
|
|
try:
|
|
if not self.enabled or not self.friction_config['enable_friction_feedback']:
|
|
return False
|
|
|
|
# 提取摩擦信息
|
|
velocity = friction_data.get('velocity', 0.0)
|
|
surface_normal = friction_data.get('normal', (0, 0, 1))
|
|
object_id = friction_data.get('object_id', 'unknown')
|
|
material = friction_data.get('material', 'default')
|
|
device_id = friction_data.get('device_id')
|
|
|
|
# 检查速度阈值
|
|
if abs(velocity) < self.friction_config['min_velocity_threshold']:
|
|
return False
|
|
|
|
# 计算触觉效果强度
|
|
intensity = min(1.0, abs(velocity) * 0.1 * self.friction_config['texture_sensitivity'])
|
|
|
|
# 获取材质效果
|
|
material_effect = self._get_material_effect(material)
|
|
|
|
# 计算频率
|
|
frequency = self.friction_config['vibration_frequency']
|
|
if material_effect:
|
|
frequency = material_effect.get('frequency', frequency)
|
|
|
|
# 更新摩擦状态
|
|
state_key = f"friction_{object_id}"
|
|
if state_key not in self.friction_state:
|
|
self.friction_state[state_key] = {
|
|
'intensity': intensity,
|
|
'velocity': velocity
|
|
}
|
|
else:
|
|
self.friction_state[state_key]['intensity'] = max(
|
|
self.friction_state[state_key]['intensity'],
|
|
intensity
|
|
)
|
|
|
|
# 生成摩擦触觉效果
|
|
if self.plugin.haptic_manager:
|
|
effect_id = self.plugin.haptic_manager.play_effect(
|
|
effect_type='noise',
|
|
device_id=device_id,
|
|
intensity=intensity,
|
|
duration=0.05,
|
|
frequency=frequency,
|
|
parameters={
|
|
'velocity': velocity,
|
|
'material': material,
|
|
'object_id': object_id
|
|
}
|
|
)
|
|
|
|
if effect_id >= 0:
|
|
# 触发效果生成回调
|
|
self._trigger_physics_callback('haptic_effect_generated', {
|
|
'effect_id': effect_id,
|
|
'effect_type': 'noise',
|
|
'source': 'friction',
|
|
'intensity': intensity,
|
|
'frequency': frequency
|
|
})
|
|
|
|
self.stats['haptic_effects_generated'] += 1
|
|
self.stats['friction_effects'] += 1
|
|
|
|
print(f"✓ 摩擦触觉效果已生成: 强度 {intensity:.2f}")
|
|
return True
|
|
|
|
return False
|
|
|
|
except Exception as e:
|
|
print(f"✗ 摩擦处理失败: {e}")
|
|
return False
|
|
|
|
def _get_material_effect(self, *materials) -> Optional[Dict[str, Any]]:
|
|
"""
|
|
获取材质效果配置
|
|
|
|
Args:
|
|
*materials: 材质名称
|
|
|
|
Returns:
|
|
材质效果配置或None
|
|
"""
|
|
try:
|
|
if not self.material_config['enable_material_feedback']:
|
|
return None
|
|
|
|
material_effects = self.material_config['material_effects']
|
|
|
|
# 尝试匹配每个材质
|
|
for material in materials:
|
|
if material in material_effects:
|
|
return material_effects[material]
|
|
|
|
return None
|
|
except Exception as e:
|
|
print(f"✗ 材质效果获取失败: {e}")
|
|
return None
|
|
|
|
def register_physics_listener(self, event_type: str, callback: callable):
|
|
"""
|
|
注册物理事件监听器
|
|
|
|
Args:
|
|
event_type: 事件类型
|
|
callback: 回调函数
|
|
"""
|
|
try:
|
|
if event_type in self.physics_listeners:
|
|
self.physics_listeners[event_type].append(callback)
|
|
else:
|
|
self.physics_listeners[event_type] = [callback]
|
|
|
|
print(f"✓ 物理事件监听器已注册: {event_type}")
|
|
except Exception as e:
|
|
print(f"✗ 物理事件监听器注册失败: {e}")
|
|
|
|
def unregister_physics_listener(self, event_type: str, callback: callable):
|
|
"""
|
|
注销物理事件监听器
|
|
|
|
Args:
|
|
event_type: 事件类型
|
|
callback: 回调函数
|
|
"""
|
|
try:
|
|
if event_type in self.physics_listeners:
|
|
if callback in self.physics_listeners[event_type]:
|
|
self.physics_listeners[event_type].remove(callback)
|
|
print(f"✓ 物理事件监听器已注销: {event_type}")
|
|
except Exception as e:
|
|
print(f"✗ 物理事件监听器注销失败: {e}")
|
|
|
|
def _trigger_physics_callback(self, callback_type: str, data: Dict[str, Any]):
|
|
"""
|
|
触发物理回调
|
|
|
|
Args:
|
|
callback_type: 回调类型
|
|
data: 回调数据
|
|
"""
|
|
try:
|
|
if callback_type in self.physics_callbacks:
|
|
for callback in self.physics_callbacks[callback_type]:
|
|
try:
|
|
callback(data)
|
|
except Exception as e:
|
|
print(f"✗ 物理回调执行失败: {e}")
|
|
except Exception as e:
|
|
print(f"✗ 物理回调触发失败: {e}")
|
|
|
|
def register_physics_callback(self, callback_type: str, callback: callable):
|
|
"""
|
|
注册物理回调
|
|
|
|
Args:
|
|
callback_type: 回调类型
|
|
callback: 回调函数
|
|
"""
|
|
try:
|
|
if callback_type in self.physics_callbacks:
|
|
self.physics_callbacks[callback_type].append(callback)
|
|
print(f"✓ 物理回调已注册: {callback_type}")
|
|
else:
|
|
print(f"✗ 无效的回调类型: {callback_type}")
|
|
except Exception as e:
|
|
print(f"✗ 物理回调注册失败: {e}")
|
|
|
|
def unregister_physics_callback(self, callback_type: str, callback: callable):
|
|
"""
|
|
注销物理回调
|
|
|
|
Args:
|
|
callback_type: 回调类型
|
|
callback: 回调函数
|
|
"""
|
|
try:
|
|
if callback_type in self.physics_callbacks:
|
|
if callback in self.physics_callbacks[callback_type]:
|
|
self.physics_callbacks[callback_type].remove(callback)
|
|
print(f"✓ 物理回调已注销: {callback_type}")
|
|
except Exception as e:
|
|
print(f"✗ 物理回调注销失败: {e}")
|
|
|
|
def set_collision_config(self, config: Dict[str, Any]) -> bool:
|
|
"""
|
|
设置碰撞配置
|
|
|
|
Args:
|
|
config: 配置字典
|
|
|
|
Returns:
|
|
是否设置成功
|
|
"""
|
|
try:
|
|
self.collision_config.update(config)
|
|
print(f"✓ 碰撞配置已更新: {self.collision_config}")
|
|
return True
|
|
except Exception as e:
|
|
print(f"✗ 碰撞配置设置失败: {e}")
|
|
return False
|
|
|
|
def set_force_config(self, config: Dict[str, Any]) -> bool:
|
|
"""
|
|
设置力反馈配置
|
|
|
|
Args:
|
|
config: 配置字典
|
|
|
|
Returns:
|
|
是否设置成功
|
|
"""
|
|
try:
|
|
self.force_config.update(config)
|
|
print(f"✓ 力反馈配置已更新: {self.force_config}")
|
|
return True
|
|
except Exception as e:
|
|
print(f"✗ 力反馈配置设置失败: {e}")
|
|
return False
|
|
|
|
def set_friction_config(self, config: Dict[str, Any]) -> bool:
|
|
"""
|
|
设置摩擦配置
|
|
|
|
Args:
|
|
config: 配置字典
|
|
|
|
Returns:
|
|
是否设置成功
|
|
"""
|
|
try:
|
|
self.friction_config.update(config)
|
|
print(f"✓ 摩擦配置已更新: {self.friction_config}")
|
|
return True
|
|
except Exception as e:
|
|
print(f"✗ 摩擦配置设置失败: {e}")
|
|
return False
|
|
|
|
def set_material_config(self, config: Dict[str, Any]) -> bool:
|
|
"""
|
|
设置材质配置
|
|
|
|
Args:
|
|
config: 配置字典
|
|
|
|
Returns:
|
|
是否设置成功
|
|
"""
|
|
try:
|
|
self.material_config.update(config)
|
|
print(f"✓ 材质配置已更新: {self.material_config}")
|
|
return True
|
|
except Exception as e:
|
|
print(f"✗ 材质配置设置失败: {e}")
|
|
return False
|
|
|
|
def get_collision_config(self) -> Dict[str, Any]:
|
|
"""
|
|
获取碰撞配置
|
|
|
|
Returns:
|
|
配置字典
|
|
"""
|
|
return self.collision_config.copy()
|
|
|
|
def get_force_config(self) -> Dict[str, Any]:
|
|
"""
|
|
获取力反馈配置
|
|
|
|
Returns:
|
|
配置字典
|
|
"""
|
|
return self.force_config.copy()
|
|
|
|
def get_friction_config(self) -> Dict[str, Any]:
|
|
"""
|
|
获取摩擦配置
|
|
|
|
Returns:
|
|
配置字典
|
|
"""
|
|
return self.friction_config.copy()
|
|
|
|
def get_material_config(self) -> Dict[str, Any]:
|
|
"""
|
|
获取材质配置
|
|
|
|
Returns:
|
|
配置字典
|
|
"""
|
|
return self.material_config.copy()
|
|
|
|
def get_stats(self) -> Dict[str, int]:
|
|
"""
|
|
获取统计信息
|
|
|
|
Returns:
|
|
统计信息字典
|
|
"""
|
|
return self.stats.copy()
|
|
|
|
def reset_stats(self):
|
|
"""重置统计信息"""
|
|
try:
|
|
self.stats = {
|
|
'collisions_detected': 0,
|
|
'forces_processed': 0,
|
|
'friction_effects': 0,
|
|
'material_effects': 0,
|
|
'haptic_effects_generated': 0
|
|
}
|
|
print("✓ 物理引擎集成统计信息已重置")
|
|
except Exception as e:
|
|
print(f"✗ 物理引擎集成统计信息重置失败: {e}") |