Physics 101 #2: Rigid bodies
In the previous post we talked about models, integrators, and solvers. Now we will try to get our hands dirtier with an actual example: the typical rigidbody engine used in most games today. Be warned that I will glance over or downright skip a lot of the details, as my goal is to give a very brief overview of rigidbody physics.
Suppose we are making an adventure game, and need to write our own physics engine for it. First, we´ll need to find an appropriate model to represent the physics in our world. An average adventure game has things like characters, walls, floors, maybe irregular terrain with plants, grass, a nice pond, stuff like that.
If we want our game to run smoothly, we need to be able to paint a result in the screen at least 60 times per second, that means we have 1/60th of a second (0.016 seconds, or 16 milliseconds) to process input, simulate physics, update the artificial intelligence, and render everything for a single frame -not necessarily in this exact order-. A typical time budget for game physics is 5 milliseconds/frame.
With such a tight budget, calculating collisions between blades of grass should unquestionably be left out of our model. Simulating character clothing at the yarn level (if at all) is also completely out of scope. Fluid dynamics (oceans, rivers) must also be left out, unless they’re extremely simplistic and/or are central to our gameplay. A simple plane with no dynamics at all would make for a lake a few years ago… and often still does.
Pruning overly complex things like these we could arrive at the typical model used for game physics. We will model characters as capsules, terrain as height maps, and walls/floors will be simple boxes. More complex objects could be modeled as arbitrary convex shapes.(convex shapes are faster and easier to simulate than concave ones, for reasons we will not discuss here).
We will also assume these simplified shapes can move and rotate but aren´t able to deform in any way, unlike all things in real life. They won´t squash, shear, twist, or bend, not even under a lot of stress. This is why we call them rigid bodies.
All the information we need to simulate a point in space is:
- Linear velocity.
Since we probably also want to support rotational motion, we need to add some extra stuff:
- Orientation (analogous to position)
- Angular velocity (analogous to linear velocity)
- Inertia tensor (sort of analogous to mass)
Orientation is usually expressed using a quaternion. At this point you don´t really need to know what a quaternion is or how it works. Just remember that it encodes an orientation.
Also our object is no longer a point in space, it has some shape and size. But we can still treat it as such if we consider all of its mass to be concentrated in a single point: its center of mass. Pure rotation will always happen around the center of mass, regardless of the body’s shape. Complex compound motion -such as rotation around a different point- will naturally emerge as a combination of translation and rotation around the center of mass. Check out this for more info on this subject.
If we use Euler’s integrator to advance each rigidbody’s position, we can also use it to advance its rotation:
new orientation = current orientation + current angular vel. * time between frames
new angular vel. = current angular vel. + (torque / inertia tensor) * time between frames
You might have noticed that when compared to the integration formula for linear velocity, the above formula has replaced force with torque, and mass with inertia tensor. Torque is a twisting force that causes rotation. To calculate torque from linear force, we do:
torque = force x (point – center of mass)
(Note that the “x” operator represents vector cross product). As you can see, the amount of torque induced by a force applied on our rigidbody depends on the position of the point we are applying the force to relative to the body’s center of mass. The further away from the center of mass we apply the force, the higher the torque we´ll generate.
At this point there’s only one obvious question left: what’s the inertia tensor?
Cutting a few corners we could say the inertia tensor is to angular velocity as mass is to linear velocity. It tells us how much force is needed to rotate the rigidbody. However, unlike mass, the inertia tensor is not a scalar value (a single number): it is a matrix, more specifically a 3×3 matrix when working in 3D. Why? because objects -most notably asymmetrical ones- are usually harder to rotate on a certain axis.
We can always force the inertia tensor to be a diagonal matrix. Because of this sometimes you´ll find inertia tensors that are stored as just 3 values instead of 9 (a full 3×3 matrix). Explaning how and why you can diagonalize inertia tensors is out of the scope of this post.
Now that we know how to simulate individual rigidbodies, it would be nice to make them interact with each other: collisions!
For the sake of focus, we will assume that we already have a way to determine contact points between pairs of bodies. All that is left then is respond to the collisions.
Given a single contact point between two rigid bodies that overlap, we can calculate and apply a force at the contact point. This force will translate and/or rotate both rigidbodies so that they are barely touching at the start of the next frame. It should be applied instantaneously, during the current frame, not over a long time. That’s why we call it an impulse. This method is known as impulse-based collision resolution.
Note: We could also calculate the distance between both bodies and apply a repulsion force over time to them, which should be stronger the closer they are. This is called penalty-based collision resolution, and is another valid alternative.
By applying carefully measured impulses to pairs of rigidbodies at each contact point, we can get a fairly realistic simulation running. Oh well… not exactly. There’s a lot of stuff I´ve blatantly ignored to get to this point, but I hope you get the overall picture.
Now, what happens when we have not a single pair of rigidbodies involved in a collision, but three or more of them? Like, a box sandwiched between two other boxes…
Do we still calculate and apply impulses pair by pair, in an iterative manner, and reconcile the results somehow? do we treat all of them as a single entity and try to solve all contacts at once?
The answer is only one post away from you. 🙂