Bullet Physics is an open source physics engine that is sometimes used for games and movies.
It supports many near phase collision types such as spheres, boxes, capsules, triangle meshes and convex hulls.
None of these were a good match for my signed distance fields(SDF).
Triangle meshes might seem like a solution, but they have two obvious issues.
- No volume which leads to penetration issues and bad collision detection
- Uses lots of memory to store the mesh
Convex hulls might also seem like a solution, but also have a downsides
- They only work with convex data. To work with concave data you must generate multiple hulls and stick them together.
- Not very accurate representation of the original shape unless you stitch many of them together, this would be infeasible for the world/terrain shape which is many kilometres in size
So I decided to extend bullet to directly support SDF vs SDF collision.
As everything in my game world is represented by an SDF, I do not need any of bullets built in collision types and only use SDF vs SDF collision.
Collision Algorithm
The solution I went with is based on generating a point hull for the SDF, a point hull is a list of points that lie within the shapes negative space. In my implementation all points in a given hull uses the same radius.
To perform collision detection between two SDFs, you treat one of them as a point hull and the other as an SDF. You transform each point in the point hull into the space of the SDF and sample the SDF. If the distance to the negative space is <= the point hulls radius, you have a collision.
I decide which object will act as the point hull based on who has the smaller point hull radius, this ensures consistent collision, and allows for the smaller object to collide against the larger objects full SDF.
For the world/terrain shape I disable the point hull generation pass, it is always treated as the SDF when colliding.
For points that are found to be colliding, a second pass is run to generate the normal and collision depth, and to reduce the number of impact points down to four(this is the number of points bullet wants fed to it).
Result
SDF vs SDF supports both convex and concave shapes.
It also has fewer issues with penetration than triangle meshes since SDFs have proper volume, even if a small object penetrates into a larger one, it will still be pushed out in the correct direction.
Here is a shape for which we will generate a point hull

And here is a visualization of the point hull.
Optimizations sometimes known as midphase
There are numerous ways to optimize this, but here are some that I use. The underlying SDF algorithms are already all SIMD, so this is focused on algorithmic optimizations.
Surface Approximate
Instead of sampling the full SDF against all of the points, I first run a pass that only samples eight points on the point hulls AABB within the SDF. It then uses those eight points to perform a quick rejection of points that incapable of colliding because they are too far away. This often eliminates >90% of the points, so we can skip full SDF evaluation.
Temporal Collision Frame
This is a frame that is specific to a given object vs object collision, it records a rough approximation of the previous collision attempt between the two shapes. If not enough movement or rotation has occurred it can early out and skip performing the full near phase–the previous contact points are reused.
Handling SDFs with broken distance formulas
Otherwise known as Lipschitz continuous– we want gradients whose magnitude is as close to 1 as possible.
Some SDFs do not have euclidean correct distances, for these cases I run a fix up step which calculates a correction factor based on the rate of change in the local space of the SDF.
My correction algorithm takes the distances calculated for the AABB of the point hull projected into the SDFs space, and compares the true distance between the points against the distance returned by the SDF. It compares all of the points against each other, and uses the largest ratio of (sdf distance/true distance) as the correction factor.
This allows for colliding against fractals and other complex formulas, which often have incorrect distance formulas.
Performance Notes
In scenes with many moving & colliding objects:
Initially my SDF near phase and Bullets solver were about equal in cycle usage.
After adding various optimizations to reduce the time spent in the SDF near phase, the solver is now the primary waster of cycles.
Bullets solver has a few SSE based implementations, but they are AoS not SoA based, so the performance gain is minimal.
Bullet has a few inefficiencies in how it works, it loves to iterate over every single CollisionObject just to access a single bool, while this is not a problem for small scenes, it does not scale to the number of physics objects I plan to use.
I plan to rework this part of Bullet in my local copy, and have already rewritten/removed a few passes Bullet was performing that I did not need.
Pseudo Continuous Collision Detection
Bullet has some support for CCD(Continuous Collision Detection), but I’m using my own pseudo CCD instead.
My CCD solution is very simple:
Calculate the maximum distance an object can travel in a given physics tick based on its current velocity, and expand the point hulls radius to incorporate it.
At high speeds this causes expansion, but I cannot visually tell that this is happening.
I run physics at 120 hertz, but I did test it at 60 hertz and it seemed to work fine there also.
I’m not currently incorporating angular velocity, but I will probably add that at some point.
Future Work
- The algorithm for placing points within the hull could always use more work.
- It might also be worth looking into storing a per point radius, this will allow for less points in some situations
- Angular Velocity for CCD