in development

a lazy cowboy culls the herd

Late Sunday’s a good time for an update. For a while I wasn’t certain what the new game was going to do, but it’s back on track.

Despite my peon to symmetry, I’ve ditched the tumbleweed approach I mentioned a few posts back in favor of something more chaotic-looking. They’re still space weeds, but they’re arranged in clumps, like clouds, which grabs me better. Clouds were, in fact, the visual inspiration for the game–those “artist’s renderings” of life bobbing around the atmosphere of Jupiter.

That one’s from Carl Sagan’s Cosmos, a personal favorite. It looks nothing like what I’ve created but that’s just as well. Copyright, you see.

So, I have thousands of weed clumps to render. It’s early days, so the objects are just sitting around in a display list. At some point I’ll whip up an octree or something that might pass muster in a CS course, but for now, they’re fine. Of course, I can’t display all of them at once. Drawing objects is expensive. What to do?

Well, I can start by only drawing stuff that’s close by. That cuts down the number in a big way, as they’re distributed more or less at random through the map space. Now, instead of drawing thousands, I’m drawing hundreds. It runs, but it’s slow. What else?

How about only drawing the stuff that’s right in front of the camera? That’s called frustum culling and it’s a great way of cutting down on what you have to draw. I don’t have a native culling object in my toolbox. I was going to hack one together today, but I decided to try something else first. It worked so well I thought I’d share it.

It’s lazy frustum culling. It’s so lazy it doesn’t even use a frustum. Instead, we use a little vector trick. Geometrically speaking, weed clumps are just a center point plus a radius. For now, drop the radius and just worry about the center.

// iterate through all the clumps
for (i = 0, il = this.node.length; i < il; i++) {
	n = this.node[i];
	// dir is a scratch vector object
	// derive a vector from the camera to the center of the clump
	dir.copy(n.center).sub(camera.position);
	// if it's very close, just draw it
	if (dir.length() < 100) {
 		n.object.draw();
 	} else {
 		// normalize the vector
 		dir.norm();
 		// take dot product between camera's front vector
 		// and the vector to the clump. less than 1-half?
 		// draw it
 		if (dir.dot(camera.front) > 0.5) {
			n.object.draw();
		}
	}
}

The dot product between two vectors is the cosine of the angle between them, so it’s equal to 1 if they face the same way, -1 if they face in opposite directions, and 0 if they’re perpendicular. Using 0.5 gives us an angle of 60 degrees, which is close enough to the view angle I’m using on my camera. Any vector that produces a larger angle doesn’t get drawn.

It works just fine. One caveat: if the camera is “inside” the clump (distance is less than the clump radius) we just draw it without a look at the dot product. Otherwise, the clump will vanish as the camera spins, because the center point will rotate behind the camera.

Sure, it’s not a proper frustum cull, but it’s simple, easy, and fun. I couldn’t ask for a better waste of an afternoon.