Physics

Okay, now that you have an idea of the classes that make up the Physics part of the REngine, let's take a look at how to move these objects around. Below is a list of functions you will find in the REnginePhysics project in the Integrator.cpp file:

RIntegrator::Update

This is the main entry point for all of the physics stuff. You simply pass the integrator a container of rigid bodies, and an elapsed time. In terms of how this fits into a typical game loop, you spend some amount of time checking I/O, you spend time painting your scene on the screen, you spend time updating your AI, etc etc. When it comes time to update the physics you simply add up how long you have been doing other things, and pass that elapsed time into the RIntegrator::Update function. The integrator then looks for any collisions that may have happened over that time, and then applies all of the forces and motions to move the objects to where they should be at the end of that time. When it's complete all of your objects are in the right place and you're free to go back to all the other things you need to get done.

One thing to note is the increment of the cache stamp. Remember that the cache stamp is used by rigid bodies to know whether or not they need to apply all the math to figure out their current convex polyhedra. This value is incremented once each physics step because this is the only time an object moves. Within a physics step, everything is stationary, so if you find a convex polyhedra for a rigid body, it's not going to move until the next physics step.

RIntegrator::CollisionDetection

Check for collisions and any resultant impulse forces. This function accepts the same list of rigid bodies and elapsed time as the Update function did. The first part of it is just to unravel the container so you end up with loops where every object in the container is checked against every other object in the container. My rigid bodies support a ShouldCheckCollision function to give them the choice to ignore certain collision possibilities (ie a missle coming out of my launch tube will definitely collide with me, but that shouldn't trigger an explosion and damage). If it's decided that two objects should be checked for a collision I go ahead and call RCollisionSolver::CheckCollision (which I will explain in the next section). If that function returns true then we have two colliding objects and we need to do something with them.

The first step is to find the impulse. The impulse is an instantaneous force that will move the objects without using the usual force over time approach of classical physics. Put another way, this impulse steps around the whole F=ma thing and directly applies itself to the velocity. Once I find this force I simply set the length of the collision normal. This means the bigger the force, the longer the vector, the larger the change in velocity. Now I ask each object if they should respond to a collision. Objects like laser beams and missles don't need to respond as they don't bounce off of things. They simply need to be removed from the game and blow up or evaporate or whatever. However if an object does want to respond then now's the time to figure out what the response should be.

The linear reaction to the impulse force is pretty straight forward. You take that impulse force, set the length to 1 over the mass of the object, and add that to the velocity. This means that the larger the mass, the less effect the impulse will have. This makes sense. If you have a rubber ball bouncing off a truck there is no noticable change to the position of the truck. However a bowling ball bouncing off a milk carton will move the milk carton significantly. The change to the angular velocity (or rotational velocity as I've called it earlier) is sort of the same idea applied to spin. Instead of mass we worry about the inertial tensor (in this case the inverse inertial tensor, just as 1/Mass is the inverse of the mass). We also need to consider the position of the impact. Much like the heavy door we where pushing earlier, if the impact is close to the handle it will have a bigger effect then if the impact is close to the hinge. One oddity you will notice here is that I'm rotating the angular impulse velocity by the inverse of my rotation. This is because my angular velocity is stored in object coordinates whereas the impulse we've found at this point is in world coords. This rotation simply converts the motion into my object's coordinate system.

You will also notice that I don't actually change the position or velocity of the objects at this point, rather I save the change for later. This is to keep all of the positional changes in one place. It also means that I don't have to re-figure the convex polyhedras for an object that has collided and looks like it may be in another collision right away.

RIntegrator::FindImpulse

This is mostly just the standard impulse function. You can find the impulse function online all over the place. I was introduced to it while reading Chris Hecker's "Behind the Screen" articles. The value of e is fun to play with. If you set it to 1.0 then you have perfectly elastic collisions (meaning an object will bounce off of an object exactly as hard as it hits it, like a super bouncy rubber ball). If you set it to zero then you will have inelastic collisions, meaning the objects don't bounce. What's fun is if you set it to something greater then 1.0. In that case objects speed up when they bounce off each other. Put a bunch of objects in a small room and turn this number up and it gets nutty quick.

There is one extremely important and subtle thing to notice in this function. That's the part where if Numer is greater then 0.0 I return 0. This tiny little check is what makes imperfect collision detection algorithms like mine workable. Do you remember the penetrating case? The problem comes from objects that somehow manage to intersect. This usually happens when objects collide due to high rotational velocities. In this case the collision detection system can miss the collision and happily let the objects move into each other. Then on the next collision check it will find that the objects are intersection and it will respond by trying to bounce them apart. However now comes the trouble. If by the next collision check the two objects have not bounced far enough to be outside of each other there will be another collision, resulting in even more force to push them apart. By the time they actually manage to get away from each other they have a lot of velocity. When you see it on the screen it looks like two slow objects that hit and then fly apart very fast. Obviously this behavior could be considered somewhat unrealistic. So to fix it we have this simple check. Basically what it says is that if the impulse force is going to be pushing the object in the direction it's already moving in, don't bother. So if our two objects intersect and are pushed apart, on the next collision check don't push them again, just let them continue on their way. Obviously none of this stuff is truly correct physics, but it looks realistic and it's fast, which is what you need for a game.

RIntegrator::UpdateObjects

This function is the one that actually moves the objects around. You can find this kind of function in many physics papers. Again I saw my first one in Chris Hecker's stuff. Unfortunately as I've mentioned before everyone seems to work with different quantities to get the job done. Some people mess with momentum or acceleration or whatever. I like to work with force, mass, and velocity. These things just make sense to me.

The first step is to apply the impulse velocities I found during the collision detection step. Simply add these to your current velocities. Don't forget to set them back to zero as they are always added to during collision response.

Next you have to apply the classical physics stuff. If you've had physics in school then F=ma, a=V/t, and V=d/t should look pretty familiar to you. If you move the variables around a bit you'll get d=Vt and V=F*t/m. I simply apply those last two to change the position and velocity. And of course there are rotational forms of the same which can be applied to the rotation and angular velocity.

So by now you should feel comfortable with moving objects around in space. The only question left is how to tell whether or not two objects have collided. so it's on to the next section, Collision Detection...