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 intersect
* simplex — a 4-point simplex containing the origin (valid only when hit == 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: 1. Start with an initial direction (typically vector between shape centers) 2. Add support vertices in directions that move the simplex toward the origin 3. Simplify the simplex to keep only points closest to the origin 4. 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