The Ablative Air debris-removing weapons are all derived from scientific proposals for how to remove orbital debris: lasers, aerogel, and physical collection via automated satellites. (Electrical tethers are another proposal, but they seemed too boring from a gameplay standpoint. Sorry tether fans.)
In my original game design, I had always planned on implementing the big three: lasers, aerogel, and satellites. Lasers were easy–I did that first, and was the only weapon for a while. Hunter/Seeker – a little satellite that goes around collecting up debris – was written almost completely on a particularly long layover in the Frankfurt airport. But Aerogel I left to the bitter end, only implementing it about two weeks before my gold candidate. Why?
Because the math sucks. Or should I say, I wasn’t thinking about things properly. You see, I was still thinking in ordinary 3D cartesian coordinates. Pooh on that! My breakthrough came by thinking like a quaternion.
If you’re not familiar with quaternions, they’re 4 dimensional complex numbers, and they’re extraordinarily good for representing rotations. Plenty of other 3D game authoring sites talk about them in length. All my orbital mechanics, view manipulation, and tracking is done with quaternion math, and I’ve needed them from the very beginning of the game so I had to learn about them pretty early on.
Quaternions are not terrifically natural to my mind, so I don’t always think of them even when they’re the perfect tool for the problem at hand. What problem was I was trying to solve?
Quite simply, I needed circular decals placed on the atmospheric surface. They spread and become more diffuse until they evaporate. The screen shot shows two blobs of it: a younger one near the center, and an older, bigger (and dimmer) one to the left of the station.
Simple, no?
Seemed so–all I needed to do was calculate all the points on a sphere that were a certain radius from a given point. So I got out paper & pencil, and started drawing out the math. And realized that my simple 2D circle calculations wouldn’t quite cut it. So then I figured–ah hah! It’s as simple as having a plane intersecting a sphere! So I looked up great (and lesser) circle calculations, and implemented some of those equations. But I still didn’t get it quite right. So then I thought–wait! I know–it’s just a cylinder intersecting a sphere!
You see the pattern here. I couldn’t find exactly the formula I needed on the Internet, and although I could create a system of equations to represent the problem, I realized I was too lazy (or too rusty, or both) to do all the math.
Maybe I could have slogged through all the math eventually, but stepping away from the problem gave me the solution. I was already using quaternions for spherical rotations, can’t I use them here?
Most certainly I could.
What was a page full of intimidating coordinate transforms and trig boiled down to two very simple quaternion operations:
1) Rotate a point from the center of the decal to the edge.
2) Rotate the edge point completely around, tracing the outline of the circle (like you would with a drafting compass).
That’s it. Easy peasy!
After having that Eureka moment, I almost jumped naked out of my bath and ran through the streets of Athens. (Well, not actually, but I was pretty stoked to have solved a bugger of a problem with no real “math” to speak of.)
The actual function is almost as simple; after that mental breakthrough it took me all of 5 minutes to get working. You’ll note that I save the center point and the first edge point twice since I’m creating a GL FAN to draw the graphic. With my Aerogel implmentation I don’t bother with an spherical 3D patch that conforms to the surface of the sphere. It’s good enough for my purposes that I create just a circle. Since the decal sits on top of the transparent atmosphere, it won’t ever intersect the earth surface. Creating a spherical patch with quaternions should be almost as simple–you just need to trace out each ray as well as the outer edge. I leave that as an exercise for the reader.
void Aerogel::setVerticesFromRadius(float radius) { // Save the center point as the first point of the fan vertexArray[0] = location; // Create the normal to the location, based // on any arbitrary vector (here we use "up") Vector3 norm = crossProduct(location, Vector3(1,0,0)); // Create a quaternion that can rotate our // center point to the edge of the circle Quaternion q; q.setToRotateAboutAxis(norm, radius); // Rotate our center point out to the edge: // this is the start of tracing our circle Vector3 p = rotate(location, q); // Create a new rotation that rotates around // the axis (the original point), scribing a circle q.setToRotateAboutAxis(location, (2.0*M_PI)/AerogelSegments); // Now it's easy: Save our points by tracing // around the edge and rotating to next point for (int i=0; i<AerogelSegments; i++) { vertexArray[i+1] = p; p = rotate(p,q); } // Save the first edge point again to close the circle vertexArray[AerogelVertices-1] = vertexArray[1]; }