Hey! We're in orbit!

As cbRenderScene() was registered as both the Idle and Display callbacks, that is where we next receive control back. Here we draw the next frame of our display, and if motion is enabled, request our "physics engine" to update the position of each particle.

Heads Up status display turned on.

First cbRenderScene() clears the screen (including the depth buffer), ensures we're working with the MODELVIEW, and loads the "identity" matrix. Note that the DEPTH_TEST and Depth Mask are both enabled. Next, gluLookAt() is called to look at (0,0,0) from either the location of the observer, or from the root particle, depending on the global variable On_Orbit. This, and many other variables, are controlled from the keyboard, and are detailed below.

Texturing is disabled, and then a truly black sphere is drawn by way of a call to gluSphere(). The stars, particle vectors, and axis are drawn next, as appropriate, and then the depth mask is set to false, which disables writing any new depth information to the depth buffer. This is in preparation for the drawing of the particles themselves, which shouldn't change the depth buffer because they're completely transparent. Note that depth test is still enabled so particles behind the black hole will be obscured.

For the particles themselves, texturing is re-enabled if appropriate, and then the array of particles is iterated through. For each particle that isn't yet "running," there's a 0.03% chance it will be introduced into the system with a call to ourFireParticleGun(). This is a way of slowly introducing particles to the system at the start.

For particles that are already running, we simply set the color as defined by the particle (which is yellow for the root particle), and then draw two intersecting quads centered on the particles position (a "poor man's" billboard). If motion is enabled, we call ourMoveParticle(), the entry point for our physics engine, which will calculate the next position for the particle.

After the particle loop, cbRenderScene() calls ourRenderHeadsUp() to draw the heads-up display, if desired. At this point all drawing is complete, so it swaps the OpenGL display buffers so our just-drawn scene is on-screen. Single-stepping is handled next, then frame-per-second statistics are collected, and finally control is returned to GLU.

"Scotty, I need impulse power or we're all dead!"

Two of the functions registered as callbacks are cbKeyPressed() and cbSpecialKeyPressed(). These are used to adjust various variables for the simulation, and include "impulse engines" to adjust the velocity (and thus the orbit) of the root particle.

Some place you really don't want to go today.

The controls for the root particle are 'O' for toggling between the observer's position and being "on-orbit" with the root particle. The 'x,' 'y' (or 'c'), and 'z' keys provide the root particle with a small impulse in the positive direction of the respective axis; the shifted capital keys provide a negative impulse. The velocity impulses are too small to directly escape the black hole's gravity field, but over several orbits, escape is possible with carefully planned periods of impulse.

It's also possible to move the root particle into a closer orbit. In fact, playing with different impulse directions at different points in the root particle's orbit can be an entertaining way of learning about orbital mechanics. The short form is -- everything's backwards. You slow down to go closer (and thus faster), and any impulses done on one side of the orbit have an effect on the opposite side.

While changing the root particle's orbit, it's often handy to have the axis lines displayed. This is done with the 'A' key. The 'S' key toggles the stars display, and the 'H' key toggles the "Heads Up" display, which gives several current values including our particle gun's location and injection velocity and the root particle's position and velocity. Such information can be useful when changing the orbit. If you get into trouble, the 'R' key will re-fire the root particle out of the particle gun.

When not on-orbit, the observation point is controlled with the arrow keys to change the angle around the Y axis and the height (above/below the XZ plane). These let you spin around the black hole, getting different views of the orbits. The Page Up and Page Down keys control how close the observation point is from the Y axis. The function ourCalcObs() converts these values into XYZ position coordinates for our drawing routine.

Other controls include the 'B' key for particle brightness, and the '+' and '-' keys to introduce or remove a hundred new particles to the system. Using these keys, you can wipe out all the particles in orbit and then restart, without having to stop the program. Lastly, the numeric keypad (when enabled) controls the position and injection velocities for the particle gun. Changing these values can result in very different and interesting orbits.

The particle gun itself is simply a conceptual point in space from which particles are injected into the system to begin their orbits around the black hole. There is some randomness given to the particles fired from the gun, to avoid "follow-the-leader" type effects. Occasionally the randomness is set to be extreme or to be backwards. This is done to add to the complexity of the scene, and it's always entertaining when a particle on an unusual orbit happens to come whipping by our point of observation.

The gun is fired whenever a particle is eaten by the black hole or escapes, occasionally randomly, and also whenever a particle is not yet running and the 'i' key is pressed. The 'I' key injects all non-running particles at one time, and can be quite amusing to watch. One last key of some importance: 'm' will start single-stepping through the simulation, with 'M' re-starting normal motion.