3D collision detection
convex hull
computational geometry
physics simulation
vector mathematics

3D collision detection convex hull vs convex hull , need Position and Normal

Data Structures & Algorithms practice on Codemia

Step through 300+ coding interview problems with animated visualisers that show the data structure changing as the code runs. The Blind 75 are free.

Practice algorithms

Introduction

For convex hull versus convex hull collision, detecting overlap is only the first step. A physics solver usually also needs a contact normal and a contact position so it can push the shapes apart and apply impulses in a stable direction.

Many collision algorithms answer slightly different questions:

  • "Do these hulls intersect?"
  • "Along which direction should I separate them?"
  • "Where is the contact point or contact manifold?"

Those are connected, but they are not the same computation. A lot of confusion comes from using a boolean overlap test and expecting it to magically produce high-quality contact data.

Common Approaches for Convex Hulls

For convex shapes, the usual choices are:

  • SAT, or Separating Axis Theorem
  • GJK for intersection or closest distance
  • EPA after GJK to get penetration depth and a separating normal

SAT is conceptually straightforward for polyhedra. You test face normals from both hulls and edge-cross-edge axes. If all projected intervals overlap, the hulls intersect. The axis with the smallest overlap is often used as the contact normal candidate.

That gives you a usable penetration direction, but not automatically a robust contact position.

SAT Gives a Normal from the Minimum Penetration Axis

With SAT, you project each hull onto every candidate axis:

cpp
1struct Projection {
2    float min;
3    float max;
4};
5
6Projection project(const std::vector<Vec3>& vertices, const Vec3& axis) {
7    float p = dot(vertices[0], axis);
8    Projection result{p, p};
9
10    for (const Vec3& v : vertices) {
11        float d = dot(v, axis);
12        result.min = std::min(result.min, d);
13        result.max = std::max(result.max, d);
14    }
15    return result;
16}
17
18float overlap(const Projection& a, const Projection& b) {
19    return std::min(a.max, b.max) - std::max(a.min, b.min);
20}

If one axis gives a negative overlap, there is no collision. If all overlaps are positive, choose the smallest positive overlap. That axis is your penetration normal candidate. You still need to orient it from hull A toward hull B, usually by checking the vector from center A to center B.

GJK Plus EPA Is Often Better for General Convex Shapes

GJK is excellent for answering whether two convex sets intersect. When they do, EPA can expand the simplex to find the penetration depth and contact normal.

The useful mental model is:

  • GJK says whether the Minkowski difference contains the origin
  • EPA finds the closest face of that Minkowski difference to the origin
  • that face normal becomes the separation direction

This often produces a more stable normal than ad hoc guessing after a boolean test.

Getting the Contact Position

The contact position is harder than the normal. For SAT-based solvers, the common approach is feature clipping:

  1. Identify the reference face from the hull whose separating axis produced the minimum overlap
  2. Identify the incident face on the other hull that is most opposed to that normal
  3. Clip the incident polygon against the side planes of the reference face
  4. The remaining points are contact candidates

Then you can average the surviving points or keep a small manifold. A single "position" is often just the centroid of those clipped points.

For simpler engines, a practical approximation is to use support points along the normal:

cpp
1Vec3 contactPointApprox(const ConvexHull& a, const ConvexHull& b, const Vec3& normal) {
2    Vec3 pa = a.support(-normal);
3    Vec3 pb = b.support(normal);
4    return (pa + pb) * 0.5f;
5}

This midpoint approximation is not as accurate as proper face clipping, but it is often good enough for debugging or simple simulations.

Position Versus Manifold

Real solvers usually prefer a contact manifold, not just one position. A single point can cause boxes to jitter or rotate oddly when the actual contact patch is a face touching a face. Even if your current API asks for one position and one normal, keep in mind that the stable long-term design is often a small list of contact points plus one normal.

Common Pitfalls

  • Expecting a plain overlap test to provide high-quality contact data leads to unstable physics.
  • Using the minimum-overlap SAT axis as a normal without orienting it consistently can make the solver push objects deeper into each other.
  • Returning the midpoint between object centers is a bad contact position for most hull pairs.
  • Ignoring face clipping produces poor contact points for box-like shapes and causes jitter.
  • Treating contact as always a single point makes edge-edge and face-face cases much harder to stabilize.

Summary

  • For convex hulls, SAT or GJK can detect collision, but contact generation is a separate step.
  • The collision normal usually comes from the minimum-penetration SAT axis or from EPA after GJK.
  • A good contact position often comes from reference-face and incident-face clipping, not from object centers.
  • If possible, design for a contact manifold instead of one point, because stable physics usually needs more than a single approximate position.

Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300+ coding interview problems with animated visualisers that show the data structure changing as the code runs. The Blind 75 are free.

Practice algorithms