123 lines
3.6 KiB
C++
123 lines
3.6 KiB
C++
/**
|
||
* @file gjk.h
|
||
* @brief GJK (Gilbert–Johnson–Keerthi) 碰撞检测算法
|
||
*
|
||
* 检测任意凸形状之间的碰撞与最近距离。
|
||
* 通过 Minkowski 差和支撑函数迭代逼近最近点对。
|
||
*
|
||
* @ingroup collision
|
||
*/
|
||
|
||
#pragma once
|
||
#include "vde/core/point.h"
|
||
#include <functional>
|
||
#include <optional>
|
||
|
||
namespace vde::collision {
|
||
using core::Point3D;
|
||
using core::Vector3D;
|
||
|
||
/**
|
||
* @brief 支撑函数类型
|
||
*
|
||
* 给定方向向量 d,返回形状在该方向上最远点的坐标:
|
||
* @code{.cpp}
|
||
* Point3D support(const Vector3D& d) {
|
||
* return argmax_{v ∈ shape} dot(v, d);
|
||
* }
|
||
* @endcode
|
||
*
|
||
* 支撑函数是 GJK / EPA 算法的核心抽象。
|
||
* 只要实现了支撑函数,即可用于任意凸形状的碰撞检测。
|
||
*/
|
||
using SupportFunc = std::function<Point3D(const Vector3D&)>;
|
||
|
||
/**
|
||
* @brief GJK 完整检测结果
|
||
*/
|
||
struct GJKResult {
|
||
bool intersect; /**< 两凸体是否相交 */
|
||
double distance; /**< 最近距离(不相交时 >0,相交时为 0) */
|
||
Point3D point_a; /**< 形状 A 上的最近点 */
|
||
Point3D point_b; /**< 形状 B 上的最近点 */
|
||
};
|
||
|
||
/**
|
||
* @brief GJK 相交检测(仅判断是否碰撞)
|
||
*
|
||
* 构造 Minkowski 差 A − B,在原点附近迭代构建单纯形。
|
||
* 若原点在 Minkowski 差内 → 碰撞。
|
||
*
|
||
* 算法流程:
|
||
* 1. 取初始方向(如 a.center − b.center)
|
||
* 2. 计算支撑点并加入单纯形
|
||
* 3. 判断单纯形是否包含原点
|
||
* 4. 若不包含,更新搜索方向并迭代
|
||
* 5. 若方向无法收敛 → 不相交
|
||
*
|
||
* 时间复杂度 O(n),其中 n 为迭代次数(通常 < 20 对于紧凑形状)。
|
||
*
|
||
* @param shape_a 形状 A 的支撑函数
|
||
* @param shape_b 形状 B 的支撑函数
|
||
* @return 相交返回 true
|
||
*
|
||
* @code{.cpp}
|
||
* auto sphere_support = [&](const Vector3D& d) {
|
||
* return center + radius * d.normalized();
|
||
* };
|
||
* auto box_support = [&](const Vector3D& d) {
|
||
* return center + Vector3D{
|
||
* half_extent.x * (d.x > 0 ? 1 : -1),
|
||
* half_extent.y * (d.y > 0 ? 1 : -1),
|
||
* half_extent.z * (d.z > 0 ? 1 : -1)
|
||
* };
|
||
* };
|
||
* if (gjk_intersect(sphere_support, box_support)) { ... }
|
||
* @endcode
|
||
*
|
||
* @see gjk_distance, gjk_full, SupportFunc
|
||
*/
|
||
bool gjk_intersect(const SupportFunc& shape_a, const SupportFunc& shape_b);
|
||
|
||
/**
|
||
* @brief GJK 最近距离查询
|
||
*
|
||
* 在 gjk_intersect 基础上,不相交时额外调用 EPA (Expanding Polytope Algorithm)
|
||
* 计算两形状之间的精确最近距离。
|
||
*
|
||
* @param shape_a 形状 A 的支撑函数
|
||
* @param shape_b 形状 B 的支撑函数
|
||
* @return 最近距离(相交时返回 0),始终 ≥ 0
|
||
*
|
||
* @see gjk_intersect, gjk_full
|
||
*/
|
||
double gjk_distance(const SupportFunc& shape_a, const SupportFunc& shape_b);
|
||
|
||
/**
|
||
* @brief GJK 完整检测:碰撞状态、距离、最近点对
|
||
*
|
||
* 融合 gjk_intersect 和 gjk_distance,一次调用返回
|
||
* 碰撞状态、最近距离及两形状上的最近点。
|
||
*
|
||
* @param shape_a 形状 A 的支撑函数
|
||
* @param shape_b 形状 B 的支撑函数
|
||
* @return GJKResult 包含碰撞状态、距离、最近点对
|
||
*
|
||
* @note 对于连续碰撞检测 (CCD) 或需要分离向量的场景,
|
||
* 使用此函数可同时获取碰撞信息和穿透深度方向。
|
||
*
|
||
* @code{.cpp}
|
||
* auto result = gjk_full(a_support, b_support);
|
||
* if (result.intersect) {
|
||
* // 穿透,分离方向 = (point_b - point_a).normalized()
|
||
* } else {
|
||
* double gap = result.distance; // 间隙
|
||
* }
|
||
* @endcode
|
||
*
|
||
* @see gjk_intersect, gjk_distance
|
||
*/
|
||
GJKResult gjk_full(const SupportFunc& shape_a, const SupportFunc& shape_b);
|
||
|
||
} // namespace vde::collision
|