6.6 KiB
omath::collision::GjkAlgorithm — Gilbert-Johnson-Keerthi collision detection
Header:
omath/collision/gjk_algorithm.hppNamespace:omath::collisionDepends on:Simplex<VertexType>, collider types withfind_abs_furthest_vertexmethod Algorithm: GJK (Gilbert-Johnson-Keerthi) for convex shape collision detection
Overview
The GJK algorithm determines whether two convex shapes intersect by iteratively constructing a simplex in Minkowski difference space. The algorithm is widely used in physics engines and collision detection systems due to its efficiency and robustness.
GjkAlgorithm is a template class that works with any collider type implementing the required support function interface:
find_abs_furthest_vertex(direction)— returns the farthest point in the collider along the given direction.
The algorithm returns a GjkHitInfo containing:
hit— boolean indicating whether the shapes intersectsimplex— a 4-point simplex containing the origin (valid only whenhit == true)
GjkHitInfo
template<class VertexType>
struct GjkHitInfo final {
bool hit{false}; // true if collision detected
Simplex<VertexType> simplex; // 4-point simplex (valid only if hit == true)
};
The simplex field is only meaningful when hit == true and contains 4 points. This simplex can be passed to the EPA algorithm for penetration depth calculation.
GjkAlgorithm
template<class ColliderType>
class GjkAlgorithm final {
using VertexType = typename ColliderType::VertexType;
public:
// Find support vertex in Minkowski difference
[[nodiscard]]
static VertexType find_support_vertex(
const ColliderType& collider_a,
const ColliderType& collider_b,
const VertexType& direction
);
// Check if two convex shapes intersect
[[nodiscard]]
static GjkHitInfo<VertexType> check_collision(
const ColliderType& collider_a,
const ColliderType& collider_b
);
};
Collider Requirements
Any type used as ColliderType must provide:
// Type alias for vertex type (typically Vector3<float>)
using VertexType = /* ... */;
// Find the farthest point in world space along the given direction
[[nodiscard]]
VertexType find_abs_furthest_vertex(const VertexType& direction) const;
Common collider types:
MeshCollider<MeshType>— for arbitrary triangle meshes- Custom colliders for spheres, boxes, capsules, etc.
Algorithm Details
Minkowski Difference
GJK operates in the Minkowski difference space A - B, where a point in this space represents the difference between points in shapes A and B. The shapes intersect if and only if the origin lies within this Minkowski difference.
Support Function
The support function finds the point in the Minkowski difference farthest along a given direction:
support(A, B, dir) = A.furthest(dir) - B.furthest(-dir)
This is computed by find_support_vertex.
Simplex Iteration
The algorithm builds a simplex incrementally:
- Start with an initial direction (typically vector between shape centers)
- Add support vertices in directions that move the simplex toward the origin
- Simplify the simplex to keep only points closest to the origin
- Repeat until either:
- Origin is contained (collision detected, returns 4-point simplex)
- No progress can be made (no collision)
Maximum 64 iterations are performed to prevent infinite loops in edge cases.
Usage Examples
Basic Collision Check
using namespace omath::collision;
using namespace omath::source_engine;
// Create mesh colliders
Mesh mesh_a = /* ... */;
Mesh mesh_b = /* ... */;
MeshCollider collider_a(mesh_a);
MeshCollider collider_b(mesh_b);
// Check for collision
auto result = GjkAlgorithm<MeshCollider<Mesh>>::check_collision(
collider_a,
collider_b
);
if (result.hit) {
std::cout << "Collision detected!\n";
// Can pass result.simplex to EPA for penetration depth
}
Combined with EPA
auto gjk_result = GjkAlgorithm<Collider>::check_collision(a, b);
if (gjk_result.hit) {
// Get penetration depth and normal using EPA
auto epa_result = Epa<Collider>::solve(
a, b, gjk_result.simplex
);
if (epa_result.success) {
std::cout << "Penetration depth: " << epa_result.depth << "\n";
std::cout << "Separation normal: " << epa_result.normal << "\n";
}
}
Performance Characteristics
- Time complexity: O(k) where k is the number of iterations (typically < 20 for most cases)
- Space complexity: O(1) — only stores a 4-point simplex
- Best case: 4-8 iterations for well-separated objects
- Worst case: 64 iterations (hard limit)
- Cache efficient: operates on small fixed-size data structures
Optimization Tips
- Initial direction: Use vector between shape centers for faster convergence
- Early exit: GJK quickly rejects non-intersecting shapes
- Warm starting: Reuse previous simplex for continuous collision detection
- Broad phase: Use spatial partitioning before GJK (AABB trees, grids)
Limitations & Edge Cases
- Convex shapes only: GJK only works with convex colliders. For concave shapes, decompose into convex parts or use a mesh collider wrapper.
- Degenerate simplices: The algorithm handles degenerate cases, but numerical precision can cause issues with very thin or flat shapes.
- Iteration limit: Hard limit of 64 iterations prevents infinite loops but may miss collisions in extreme cases.
- Zero-length directions: The simplex update logic guards against zero-length vectors, returning safe fallbacks.
Vertex Type Requirements
The VertexType must satisfy the GjkVector concept (defined in simplex.hpp):
template<class V>
concept GjkVector = requires(const V& a, const V& b) {
{ -a } -> std::same_as<V>;
{ a - b } -> std::same_as<V>;
{ a.cross(b) } -> std::same_as<V>;
{ a.point_to_same_direction(b) } -> std::same_as<bool>;
};
omath::Vector3<float> satisfies this concept.
See Also
- EPA Algorithm Documentation - Penetration depth calculation
- Simplex Documentation - Simplex data structure
- MeshCollider Documentation - Mesh-based collider
- Mesh Documentation - Mesh primitive
- LineTracer Documentation - Ray-triangle intersection
- Tutorials - Collision Detection - Complete collision tutorial
Last updated: 13 Nov 2025