""" 实用工具模块 提供物理系统相关的实用函数和工具类 """ from panda3d.core import Vec3, Point3, LVector3f from panda3d.core import TransformState import math import json class PhysicsUtils: """ 物理实用工具类 提供常用的物理计算和转换函数 """ @staticmethod def calculate_mass_from_density(shape, density): """ 根据密度计算质量 Args: shape: 碰撞形状对象 density (float): 密度(kg/m³) Returns: float: 质量(kg) """ # 获取形状体积(简化实现) volume = PhysicsUtils.calculate_volume(shape) return volume * density @staticmethod def calculate_volume(shape): """ 计算形状体积(简化实现) Args: shape: 碰撞形状对象 Returns: float: 体积(m³) """ # 这里只是一个简化的实现,实际应该根据形状类型计算 try: # 尝试获取形状的AABB aabb = shape.getAabb() min_point = aabb.getMin() max_point = aabb.getMax() # 计算包围盒体积 dimensions = max_point - min_point volume = dimensions.getX() * dimensions.getY() * dimensions.getZ() # 简化:假设实际体积是包围盒体积的一半 return volume * 0.5 except: # 默认返回1立方米 return 1.0 @staticmethod def apply_impulse_at_point(body, impulse, point, relative_to_world=True): """ 在指定点应用冲量 Args: body: 刚体对象 impulse (Vec3): 冲量向量 point (Point3): 作用点 relative_to_world (bool): 点坐标是否相对于世界坐标系 """ if relative_to_world: # 转换到局部坐标系 local_point = body.physics_node.getTransform().getInverse().xformPoint(point) else: local_point = point body.bullet_body.applyImpulse(impulse, local_point) @staticmethod def calculate_torque_from_force(force, point, center_of_mass=Point3(0, 0, 0)): """ 根据力和作用点计算扭矩 Args: force (Vec3): 力向量 point (Point3): 作用点 center_of_mass (Point3): 质心位置 Returns: Vec3: 扭矩向量 """ r = point - center_of_mass return r.cross(force) @staticmethod def interpolate_transforms(transform_a, transform_b, t): """ 插值两个变换 Args: transform_a (TransformState): 起始变换 transform_b (TransformState): 结束变换 t (float): 插值因子 (0.0-1.0) Returns: TransformState: 插值后的变换 """ # 插值位置 pos_a = transform_a.getPos() pos_b = transform_b.getPos() interpolated_pos = pos_a + (pos_b - pos_a) * t # 插值旋转(简化实现) hpr_a = transform_a.getHpr() hpr_b = transform_b.getHpr() interpolated_hpr = hpr_a + (hpr_b - hpr_a) * t # 插值缩放 scale_a = transform_a.getScale() scale_b = transform_b.getScale() interpolated_scale = scale_a + (scale_b - scale_a) * t return TransformState.makePosHprScale(interpolated_pos, interpolated_hpr, interpolated_scale) @staticmethod def clamp_vector(vector, max_length): """ 限制向量长度 Args: vector (Vec3): 输入向量 max_length (float): 最大长度 Returns: Vec3: 限制后的向量 """ length = vector.length() if length > max_length and length > 0: return vector * (max_length / length) return vector @staticmethod def reflect_vector(vector, normal): """ 计算向量在法线上的反射 Args: vector (Vec3): 入射向量 normal (Vec3): 法线向量(应为单位向量) Returns: Vec3: 反射向量 """ # R = V - 2 * (V · N) * N dot_product = vector.dot(normal) reflection = vector - normal * (2.0 * dot_product) return reflection @staticmethod def calculate_angular_velocity_from_rotation(start_rotation, end_rotation, time_delta): """ 根据旋转变化计算角速度 Args: start_rotation (Vec3): 起始旋转 end_rotation (Vec3): 结束旋转 time_delta (float): 时间差 Returns: Vec3: 角速度 """ if time_delta <= 0: return Vec3(0, 0, 0) # 计算旋转差 rotation_delta = end_rotation - start_rotation # 处理角度环绕 for i in range(3): # H, P, R while rotation_delta[i] > 180: rotation_delta[i] -= 360 while rotation_delta[i] < -180: rotation_delta[i] += 360 # 计算角速度 return rotation_delta / time_delta @staticmethod def create_transform_from_position_rotation(position, rotation): """ 根据位置和旋转创建变换 Args: position (Point3): 位置 rotation (Vec3): 旋转(HPR) Returns: TransformState: 变换状态 """ return TransformState.makePosHpr(position, rotation) @staticmethod def get_transform_matrix(transform): """ 获取变换的矩阵表示 Args: transform (TransformState): 变换状态 Returns: LMatrix4f: 变换矩阵 """ return transform.getMat() @staticmethod def transform_point(transform, point): """ 变换点 Args: transform (TransformState): 变换状态 point (Point3): 点 Returns: Point3: 变换后的点 """ return transform.getMat().xformPoint(point) @staticmethod def transform_vector(transform, vector): """ 变换向量 Args: transform (TransformState): 变换状态 vector (Vec3): 向量 Returns: Vec3: 变换后的向量 """ return transform.getMat().xformVec(vector) class PhysicsConfigManager: """ 物理配置管理器 管理物理系统的配置文件 """ def __init__(self, config_file=None): """ 初始化配置管理器 Args: config_file (str): 配置文件路径 """ self.config_file = config_file self.config_data = {} if config_file: self.load_config(config_file) def load_config(self, config_file): """ 加载配置文件 Args: config_file (str): 配置文件路径 """ try: with open(config_file, 'r', encoding='utf-8') as f: self.config_data = json.load(f) self.config_file = config_file print(f"配置文件加载成功: {config_file}") except Exception as e: print(f"加载配置文件失败: {e}") def save_config(self, config_file=None): """ 保存配置文件 Args: config_file (str): 配置文件路径(可选) """ save_path = config_file or self.config_file if not save_path: print("未指定配置文件路径") return try: with open(save_path, 'w', encoding='utf-8') as f: json.dump(self.config_data, f, indent=2, ensure_ascii=False) print(f"配置文件保存成功: {save_path}") except Exception as e: print(f"保存配置文件失败: {e}") def get_setting(self, key, default=None): """ 获取配置项 Args: key (str): 配置项键名 default: 默认值 Returns: 配置项值 """ return self.config_data.get(key, default) def set_setting(self, key, value): """ 设置配置项 Args: key (str): 配置项键名 value: 配置项值 """ self.config_data[key] = value def get_physics_settings(self): """ 获取物理设置 Returns: dict: 物理设置 """ return self.config_data.get('physics', {}) def set_physics_settings(self, settings): """ 设置物理设置 Args: settings (dict): 物理设置 """ self.config_data['physics'] = settings def get_material_settings(self, material_name): """ 获取材质设置 Args: material_name (str): 材质名称 Returns: dict: 材质设置 """ materials = self.config_data.get('materials', {}) return materials.get(material_name, {}) def set_material_settings(self, material_name, settings): """ 设置材质设置 Args: material_name (str): 材质名称 settings (dict): 材质设置 """ if 'materials' not in self.config_data: self.config_data['materials'] = {} self.config_data['materials'][material_name] = settings class PhysicsCollisionGroups: """ 物理碰撞组定义 定义常用的碰撞组和掩码 """ # 基础碰撞组 GROUP_STATIC = 1 # 静态物体 GROUP_DYNAMIC = 2 # 动态物体 GROUP_KINEMATIC = 4 # 运动物体 GROUP_SENSOR = 8 # 传感器 GROUP_PARTICLE = 16 # 粒子 GROUP_VEHICLE = 32 # 车辆 GROUP_CHARACTER = 64 # 角色 GROUP_TRIGGER = 128 # 触发器 # 常用掩码组合 MASK_ALL = 0xFFFFFFFF # 所有组 MASK_STATIC_DYNAMIC = GROUP_STATIC | GROUP_DYNAMIC # 静态与动态 MASK_DYNAMIC_ONLY = GROUP_DYNAMIC # 仅动态 MASK_VEHICLE_WORLD = GROUP_STATIC | GROUP_DYNAMIC | GROUP_VEHICLE # 车辆与世界 MASK_CHARACTER_WORLD = GROUP_STATIC | GROUP_DYNAMIC | GROUP_CHARACTER # 角色与世界 MASK_PARTICLE_WORLD = GROUP_STATIC | GROUP_DYNAMIC | GROUP_PARTICLE # 粒子与世界 @staticmethod def create_mask(*groups): """ 创建掩码 Args: *groups: 碰撞组 Returns: int: 掩码值 """ mask = 0 for group in groups: mask |= group return mask @staticmethod def add_to_mask(mask, *groups): """ 添加组到掩码 Args: mask (int): 原始掩码 *groups: 要添加的碰撞组 Returns: int: 新掩码 """ for group in groups: mask |= group return mask @staticmethod def remove_from_mask(mask, *groups): """ 从掩码移除组 Args: mask (int): 原始掩码 *groups: 要移除的碰撞组 Returns: int: 新掩码 """ for group in groups: mask &= ~group return mask # 预定义的物理常量 PHYSICS_CONSTANTS = { 'GRAVITY_EARTH': Vec3(0, 0, -9.81), # 地球重力 'GRAVITY_MOON': Vec3(0, 0, -1.62), # 月球重力 'GRAVITY_MARS': Vec3(0, 0, -3.71), # 火星重力 'AIR_DENSITY_SEA_LEVEL': 1.225, # 海平面空气密度 'WATER_DENSITY': 1000.0, # 水密度 'SPEED_OF_SOUND_AIR': 343.0, # 空气中声速 'SPEED_OF_LIGHT': 299792458.0 # 光速 }