Let's Get Physics 3D (LGP3)

A 3D Physics Engine with a focus on education

The Setting

In early 2024 as a part of an assignment for class we were tasked with creating a 2D physics engine, complete with polygon, plane, and circle collisions, and rigid bodies. (be that with Euler or Verlet integration)
As an extended challenge for myself, I set the task of also adding rotational collision resolution and gravitational forces.

With that securely under my belt, when then given a new assignment on creating a complex games system, I decided to try and make a 3D physics engine, except this time I wanted more than just rotation, I set my sights on friction and contact forces too!

Why?

3D physics had always seemed like a pipe dream, but physics has always been a passion of mine, both as a student and teacher. A big struggle of the research for this project was that there is nearly no open source discourse on solving 3D collisions, this lack of content though instilled me with a new goal, if I was going to crack 3D collision, I would also need to make the work I had done readable, and accessible for people who also want to take these steps themselves.

How Hard Could it be?

This assignment instead spanned for 5 weeks, and also needed to be a C++ library. This was actually the hardest programming task I had ever set myself and I was yet to find out.
I began by taking some basic ECS code to unity to help myself emulate my C++ workspace but with some useful tools I could use for visualising.
Approaching the physics I decided to tackle each step of the collision phase one learning step at a time.

1. Collision Detection:
In 2D I had originally done collision detection using Separating Axis Theorem (SAT), SAT stepped through each edge, and each vert on the two colliding polygon's and determines the axis of separation, however for 3D you also need to add faces to the cycle, and I wasn't looking forward to an additional pass and then also determining the point of collision for rotation, so instead I decided to use Gilbert-Johnson-Keerthi algorithm(GJK), GJK instead uses a point cloud of vertices and subtracts every vert from the other collider to determine whether 0,0,0 is within its bounds. This process is similar to comparing two numbers, if you subtract one from the other they are the same if the result is 0. GJK was rather fun and I was able to learn what I needed from resources like these from Kenton Hamluik and Iggmonclar. GJK also allowed me to use a radius with my colliders, here is my testing my work on unity.


2. Normals:
With two bodies colliding we now need to be sure of the faces/edges/vert responsible so that we can get a normal and push the two objects away from each other. In an act of sheer blessing GJK does NOT calculate normals as a part of its algorithm (unlike SAT), however there GJK does leave you with a 3D polytope, amd using an algorithm called Expanding Polytope Algorithm we can inflate the polytope to the closest bounds of the collision and determine the resulting normal.
I put it rather simply but learning about and finding EPA was one of the harder parts of this process, thankfully I was able to read dyn4j's article on the topic and add it to the engine, to these results:

3. Contact Points:
This part was a very slow and independent one, after determining the normals with EPA it became pretty obvious that there was no next standard way to determine a contact manifold/ points of contact from the collision. Contact points are important for resolving rotation and torque so to get the engine to a state I was happy with it was going to be important.
My approach was to determine all faces responsible for a collision, and then map them into a 2D space and iterate over the shapes to determine the points that their edges intersect. Its a bit of a slow course, and I'm not happy with it, but it works!

4. Inertia tensors:
Anyone who has stepped into 3D collisions is probably aware of this beast ahead, an Inertia Tensor is the part of a physical body that we use to determine their ability to allow torque to rotate them. Thats a bit of a mouthful, but its essentially the mass component of impulse but for torque. Learning about this step was sitting down, watching university lectures on engineering courses and getting to taking notes. Its a big endeavour into matrices, and is a big reason why I am eager to do my own write up, but to anyone interested Id recommend taking your time and enjoying the ride.
Here is a fun demo of me showing off its effects:

5. Taking it to C++:
With all of this under my looking good I took my work to my at the time WIP engine to see if we could get it working on home grounds. and here it is! A thank you to Andrew Maximov for their very shiny Cerberus model. Check out the library on my GitHub and always feel free to talk shop with me about physics!

Looking ahead

The engine isn't perfect, but for setting aside this idea for a 5 week task I am so happy with the results. I will get back to it soon, but for now I will bath in the glory of its shiny bloomy self.

This next Part Isn't Ready Yet

I'm working on a presentation about the step by step breakdown of 3D physics, however with the ending of university its been harder to find the time to get it in a good state. It'll be out soon.

3D Collisions and Resolution

This isn't ready yet, but when its done it'll be a brief rundown on computing 3D collisions with code examples.

Learn more