Designing a Physics Engine
blog.winter.dev
blog.winter.dev
For a game dev class in uni, I wrote air hockey. This is straightforward because you only have circles and lines, and there's no gravity so objects can't rest on each other. I wrote an event-based physics engine, which exploits the fact that the equations to find the time t of a future collision are closed-form and you can just solve for t. If t is greater than the time to the next frame, just step everything forward; if it's less, step to that collision, then find the next one. Collision response is easy too: just choose a reference frame where one of the pucks is stationary, then solve conservation of momentum and energy.
This excludes some kinds of friction models, but if you choose one where deceleration due to friction is independent of speed (perfectly valid for lowish speeds), you're fine.
Another cool thing about an event-based system is that it makes it trivial to write an AI: just have the system try N moves toward the nearest puck, and run the simulation forward a few collisions to see if any of those moves results in a puck going in the opponent's goal.
But it's black magic to bring if from there to a proper game-ready physics engine which can handle 30 stacked boxes and complex penetration problems.
There is a reason that there are very few physics engines compared to game engines.
Not to discount the effort involved here though; it's pretty impressive, I know I couldn't write this without a lot of mental effort.
Maybe it's just using a really tiny physics time step. That allows forces to propagate through the whole pile over many time steps.
Objects might sink into each other a bit, but that is generally not very noticeable unless you make huge piles of spheres. However, it might quickly become a problem in 2D, since you only need a quadratic number of objects to get a "critical" pile, compared to a cubic number of spheres in 3D.
I think of a Jacobian as a way to transform the velocities from however they're stored for the interacting objects into linear world-space velocities.
For example, in 2D rigid body physics, rotational velocity is usually just a single number representing how fast something is spinning. Given a radius from the center of a spinning object (e.g. object origin -> a contact point in world space), the instantaneous linear velocity is the rotation speed times the tangent to that radius. Therefore, the Jacobian is just that tangent vector - multiplication by that vector transforms scalar rotational velocities into 2D linear velocities. The Jacobian for the object's linear velocity is just the identity matrix.
The rotation Jacobian is more complicated in 3D and 4D, but the principal is the same - find a matrix (at a particular point, usually the contact point or the joint location) to multiply with whatever represents rotational velocity to get the linear velocity.
[1]https://box2d.org/files/ErinCatto_ModelingAndSolvingConstrai... [2]http://allenchou.net/2013/12/game-physics-constraints-sequen...
Then I understood that even doing proper collision detection would mean having to implement some kind of physics in order to do proper frame independent linear interpolation of objects moving in 3D space, in order for the objects not to go through each other if the movement step is too big and also to do proper moving of objects anyway in 3D space, as you can't just expect to linearly transform an object from one place to another, as framerates might wildly differ and the object might go through another if the frametime would be too big for this particular frame we are rendering.
That is where I just stopped, and decided that it was too much work to do anything proper by myself, and gave up on writing my own 3D engine.
I see many people are going through this process, writing their own engines and libraries. Somebody told me when I started I should use an existing engine, but I didn't listen. And that is a good thing, and at the same time, wasting time to do something that could be done using existing libraries or engines.
This is what I feel anytime I see somebody write about them re-inventing the wheels, but I guess it's a process that many choose to go through for learning and fun purposes.
And since the obvious next question is: Why don't all physics engines implement a "proper" continuous collision system? I guess because checking collision in discrete steps is a lot cheaper, so that it may be an acceptable tradeoff if one knows the tunneling problem exists and how to work around it.
Btw Bullet has continuous collision detection. It's not fundamentally magical - you extrude a hull of the shapes to do CCD on along their velocity tangents and collide the hulls instead of the shapes. This effectively solves the problem of bullets or other fast moving objects going through walls etc. (this is why the engine is called Bullet btw)
Actually the most difficult problem I had with the physics was the player physics, as its usually not handled by letting the player be a normal physics body (for gameplay reasons). The player shape can easily get caught up on infinitesimal seams between polygons and edges in the world around you. :)
Reminds me of another thing - back when I was trying to write my own 3D engine, I've never heard of the concept that you interpolate physics state between rendering frames. Something that's considered table stakes today.
When and how did that arise?
(Thinking about it now, I suppose this trick lets you update physics less frequently than drawing while faking fluid movement, and free up CPU time for other tasks.)
Given that I failed my 'A' level maths in spectacular fashion (I honestly don't get integration - it just doesn't compute for me) I'm obviously attempting the impossible here. But I live in hope that one day I will somehow stumble on a solution that looks like its working on screen ... which will be enough for me.
I've tried combining my library with third party physics engines - but that, too, is hard work. The best I've ever managed is with the Matter.js[1] engine but the amount, and complexity, of code needed to create a scene is too much for my tastes[2].
Maybe I'll go back for a third attempt if/when the second Covid wave hits.
[1] Matter.js home page - https://brm.io/matter-js/
[2] Codepen demo - https://codepen.io/kaliedarik/pen/zYvbwBy
One idea comes from quake: for collision response, call a function recursively until there's no more collision.
Basically, it pushes you away from a given plane. So the function looks like pushAway(plane, you). If that pushes you into something else, you get pushed away again, recursively.
If you imagine a V shape, the result is that you end up "standing" on the V, because you're getting pushed away from both sides of the V equally.
I'm not sure my explanation is very good, so to simplify: just add recursion, lol. (You'll need to limit the max recursion depth, or else you'll get a frozen game.)
I used to try to solve things from first principles – and it's more fun to do that – but there's no shame in copying the answer. Carmack's solutions tended to be copied from the medical rendering field anyway. :)
I've tried that. Open various libraries, go through the code, work out how/where they perform the collision response calculations, trace back the vars passed into those calculations to see where their values get calculated, etc, etc ... and clearly I keep missing some key step or other along the way. I'm either misunderstanding the collision normal, or misinterpreting the collision depth, or my understanding of torque is deeply flawed (very possible, that one) ... frustration builds. Then I blame the engine coders for being too clever with their code and try a different library.
I'll get there one day. Failure is not a disaster!
Based on the access patterns in the code (iterating through every physics object every step), keeping the physics objects behind a pointer is actively cache hostile! If at all possible try to have these objects in contiguous memory.
There is still that indirection though :(
Are any used in physics engines? I assume yes.
The simplest integrator that "preserves" energy is the Symplectic Euler [2] integrator, which reads:
v <- v + F/m * dt
x <- x + v * dt
The key is that you use the updated velocity to update the position in the same time step. In this scheme, energy isn't exactly preserved, it varies a little bit from one time step to the next. However, you can guarantee that the energy fluctuations will remain bounded, if your time step is below a (fairly large) critical value.
Reading the code of this blog post, the first version of the Step (at the beginning of the blogpost) uses the Symplectic Euler scheme, and the second version (towards the end) uses the naive Euler scheme. Bullet, a popular physics engine, uses the Symplectic Euler method [3].
A popular, more sophisticated, scheme is the Runge-Kutta integrator, which comes with variants that preserve the energy and ones that don't. The MuJoCo simulator, used in robotics research, uses the Runge-Kutta method [4]
[1] https://en.wikipedia.org/wiki/Euler_method
[2] https://en.wikipedia.org/wiki/Semi-implicit_Euler_method
I was looking into the Runge-Kutta method, but it looked too complex for what I was doing.
Seems like that is the default for almost all robot focused sims too, see some benchmarks here: https://leggedrobotics.github.io/SimBenchmark/
Each displayed object was backed by a simplified "physics 3D model" (small number of points of mass, fully connected with edges), and after applying forces and moving points, we checked the length of each connecting link and made sure that they didn't stretch or compress too much. Rigs of Rods has a similar model: https://en.wikipedia.org/wiki/Rigs_of_Rods
``` obj->Force += obj->Mass * m_gravity; // apply a force
obj->Velocity += obj->Force / obj->Mass * dt; obj->Position += obj->Velocity * dt;
obj->Force = vector3(0, 0, 0); // reset net force at the end ```
As in
``` void Object::Step(double dt_s, const vector3& g_mps2) { Force += Mass * g_mps2; // apply a force
Velocity += Force / Mass * dt_s;
Position += Velocity * dt_s;
Force = vector3(0, 0, 0); // reset net force at the end
}
```I think with ECS, you can avoid a lot of this branching.
I'm also not sure it's worth it. If you have a small number of bodies, it doesn't make sense to attempt the collision detection on the GPU, the benefit of the GPU is really that it scales. If you _do_ have a large number of bodies, youre likely going to be using lots of your GPU for rendering anyway....
There's also the gameplay side of it - moving the bulk of the physics engine to the GPU will make things like contact filtering, scene querying difficult to do. Gameplay code is usually very straightforward, and adding in complex or async callbacks likely would be met with resistance..
But why don't we want plane v. plane? Can't a flat surface hit another flat surface?
I plan to make a part 2 that covers more complex constraint based methods. I'll put rotation in with that.