
The powerpoint presentation slides are now available on the download page
-
Recent Posts
Archives
Tags
Meta

The powerpoint presentation slides are now available on the download page
The second edition is finally back from the printer! Buy it from your local bookstore, or online from amazon.com.


The very scary Halloween update has been released. A new terrifying boss, MONOCULUS, roams the new map, the Eyeaduct! Collectible Halloween loot. Read the new comic, the bombinomicon!!
Control point enabled. Get going!
This post is the first in a series on a group of related tricks we can file under the category of “interpolation” or “control systems.” This can seem very abstract, but there’s a reason: it’s applicable in so many different areas, as this first post will attempt to show. In this post, I want to discuss some general principles of signals, systems, and interpolation, and give practical examples of where these things come up in everyday video game programming.
Many of the data we deal with in video games can be viewed as a “signal,” or to use an equivalent term, a “function.” Quite often we deal with signals in the time domain, or more simply, we have some data that changes over time. Here are some examples:
You will notice that some of the examples signals take on values from a continuous domain, such as the positions of characters and other objects in the world, or mouse movement rates. Those are the signals that we store using floating point data types, whether they be 1D scalar values or 2D or 3D vectors. Other signals may only assume values from a discrete set, for example, menu selection, discrete camera modes, lock-on targets, whether or not the ‘W’ key is currently depressed, and so forth. We use bools, ints, and enums for these.

Just because a signal comes from a continuous domain does not mean it is continuous function. For example, a character’s health may be constant and then take a sudden jump up or down if they get shot or pick up a potion, etc. An AI entity may suddenly switch targets, so that even if each individual target moves around in a continuous fashion, the signal representing “the position that the AI wants to be aiming at” may exhibit discontinuities. Characters can warp and spawn. A client depending upon a game server for the authoritative value of some continuous variable is only going to receive that variable in bursts. One of the main reasons to understand control systems theory is to know how best to mask these discontinuities, making a smooth output signal out of a jumpy input one.
Of course, since we are working in a digital computer, in reality all of our signals are not truly continuous in time, because we only know (or care about) its value for particular time values, for example, perhaps at 30 different times per second. But we still consider the underlying signal to be continuous, even if we are only sampling an input signal or updating an output signal once per frame.
All of the examples we’ll look at are time-domain signals, but before moving one, it’s worth mentioning that there are certainly signals from other domains. For example, a visual image can be thought of as a function of the continuous screen space coordinates, defined at any (x,y) coordinates, not just at the pixel “centers.” (We could use raytracing to determine the value.) Practical images are represented as a discrete grid of pixel values (a bitmap); how to best sample the continuous signal to determine the pixel colors is a very important topic, one that can only be fully understood using the tools of signal processing theory. (It is NOT simply the value of the continuous signal at the “center” of the pixel!)
Video games contain lots of code that, when viewed from the standpoint of signal processing theory, is a system. A system is basically any process, function, algorithm — or block of video game code — that takes one or more input signals, and produces one or more output signals. The list of example signals given previously was hopefully highly suggestive, and you already thought of these examples of systems:

As you can see, many of these examples can be put into the category of “making things feel right.” Different types of systems will “feel” differently and have different performance characteristics. It’s very helpful to have a library of systems to choose from.
In the next post, I will consider some very special signals that are able to capture common behaviour in signals: they are the impulse, step, ramp, and sinosoid. By analyzing how our system responds to these special signals, we will learn a lot about how our system behaves in general.

The Manniversary update has been released for Team Fortress 2! The TF2 store is one year old! (Which is about six months longer than I’ve been working on the game.) Read about it on the TF2 blog.
I’m going to be out of town on September 27-30, 2011 presenting two papers at GAMEON-NA, so here are videos of the lectures that I would have given to my Game Math and Physics class at the University of North Texas. A big shout-out to my students who are supposed to be watching them in my absence.
In a previous post I described how to tell whether two axially aligned bounding boxes (AABB’s) intersected. The solution was presented as a process of elimination: if box A is to the left of B, to the the right of B, above B, or below B, then they cannot intersect.
This rule is a special case application of the separating axis theorem. This theorem says that if two 2D convex polygons don’t intersect, then there exists some line (known as a “separating axis”), such that when the polygons are projected onto that line, the projections of the two polygons will not overlap.
Here are two non-overlapping polygons. The black line is a separating axis for these two polygons, because the projections of the polygons onto that axis (the thick green and blue blues) do not overlap.

A separating axis is really just a direction (we would describe the axis as a vector). Translating the axis does not affect the polygon projections.
This immediately suggests an algorithm to determine if two convex polygons intersect: check all the potential separating axes; if the polygon projections overlap on all such axes, then they polygons intersect. Otherwise, as soon as a separating axis is found, we can declare the polygons to be non-intersecting.
Unfortunately, there are an infinite number of directions. Fortunately, as it turns out, we must only try a limited subset of those directions. In 2D, we need only test the directions that are perpendicular a polygon edge. For example, in the example above, the separating axis is perpendicular to the leftmost edge of the polygon on the right. For 2D AABB’s, all of the polygon edges are horizontal or vertical, so there are only two possible separating axis, one vertical and horizontal. (There were four cases to test, two per axis, because nonoverlapping polygons may be on either side of each other.)
We now have a strategy for detecting the overlap of 2D convex polygons. Scan all the edges of both polygons. For each edge, we form a candidate separating axis that is perpendicular to that edge. We project the two polygons onto the axis, and test whether their projections overlap. If not, the polygons are non-intersecting, and we are done. If they overlap, keep searching. If the polygons are overlapping on all candidate separating axes, then we can declare them to be overlapping — that is what the separating axis theorem guarantees us.
But what exactly does it mean to project the polygons onto an axis? How do we represent that projection, in such a way that we can tell if the projections overlap? The dot product is the answer. Let’s translate our axis so that it contains the origin. (We said that this does not change whether the projections overlap.) Now we can describe the the projection of any given polygon vertex onto this axis by giving the signed distance from the projected vertex to the origin. This distance just so happens to be precisely what we get when we dot the vertex with a unit vector parallel to the axis, which we’ll call v.

The dot product gives us a tool to describe the projection of a polygon onto each candidate separating axis v. For each polygon vertex, we take the dot product of the vertex with v, which gives us a signed distance from the origin. We collect the minimum and maximum of these distance, which gives us the extents of the projection, labeled amin and amax in the figure above. Note that we we don’t know or care ahead of time which points will be a1 and a2 as the figure above might lead you to believe; all we need are amin and amin. A simple one dimensional overlap test of amin and amax against the bmin and bmax tells us if the projections overlap.
In fact, v need not be a unit vector at all. If we scale v by some factor k, then that will scale the numeric values of amin, amax, bmin, and bmax by this same factor, and they will no longer measure the distance to the origin, but it will not affect whether the intervals overlap.
The following code snippet illustrates these ideas.
// Very simple vector class struct Vec2D { float x,y; }; // Dot product operator float dot(const Vec2D &a, const Vec2D &b) { return a.x*b.x + a.y*b.y; } // Here is our high level entry point. It tests whether two polygons intersect. The // polygons must be convex, and they must not be degenerate. bool convexPolygonOverlap( int aVertCount, const Vec2D *aVertList, int bVertCount, const Vec2D *bVertList ) { // First, use all of A's edges to get candidate separating axes if (findSeparatingAxis(aVertCount, aVertList, bVertCount, bVertList)) return false; // Now swap roles, and use B's edges if (findSeparatingAxis(bVertCount, bVertList, aVertCount, aVertList)) return false; // No separating axis found. They must overlap return true; } // Helper routine: test if two convex polygons overlap, using only the edges of // the first polygon (polygon "a") to build the list of candidate separating axes. bool findSeparatingAxis( int aVertCount, const Vec2D *aVertList, int bVertCount, const Vec2D *bVertList ) { // Iterate over all the edges int prev = aVertCount-1; for (int cur = 0 ; cur < aVertCount ; ++cur) { // Get edge vector. (Assume operator- is overloaded) Vec2D edge = aVertList[cur] - aVertList[prev]; // Rotate vector 90 degrees (doesn't matter which way) to get // candidate separating axis. Vec2D v; v.x = edge.y; v.y = -edge.x; // Gather extents of both polygons projected onto this axis float aMin, aMax, bMin, bMax; gatherPolygonProjectionExtents(aVertCount, aVertlist, v, aMin, aMax); gatherPolygonProjectionExtents(bVertCount, bVertlist, v, bMin, bMax); // Is this a separating axis? if (aMax < bMin) return true; if (bMax < aMin) return true; // Next edge, please prev = cur; } // Failed to find a separating axis return false; } // Gather up one-dimensional extents of the projection of the polygon // onto this axis. void gatherPolygonProjectionExtents( int vertCount, const Vec2D *vertList, // input polygon verts const Vec2D &v, // axis to project onto float &outMin, float &outMax // 1D extents are output here ) { // Initialize extents to a single point, the first vertex outMin = outMax = dot(v, vertList[0]); // Now scan all the rest, growing extents to include them for (int i = 1 ; i < vertCount ; ++i) { float d = dot(v, vertList[i]); if (d < outMin) outMin = d; else if (d > outMax) outMax = d; } }
Let’s hasten to add a few words of warning. The first is a warning concerning robustness: this code assumes valid convex polygons. If any of the edges are degenerate, or if the polygon is concave, this routine can produce unexpected results. Of course, the separating axis theorem applies only to convex polygons. Detecting the intersection of concave polygons is a trickier business. The second warning is concerning performance. The algorithm above is clearly O(N2). For large values of N, different algorithms can be faster. A standard approach is to sort the vertices vertically, and then sweep a horizontal line through the polygons, stopping at “event points” to see if the polygons have horizontal overlap at the given location. With a small bit of extra work, this approach can also be made to work for concave polygons.
The separating axis extends naturally into 3D, to test whether convex polyhedra overlap. It is this use of the theorem that gets the most attention in video games, since this test is needed in collision detection. The set of potential separating axes starts with vectors perpendicular to the polygon faces, which is analogous to what we did here in 2D. Unfortunately, in 3D things are a bit trickier and this list is not sufficient; there are cases where the polyhedra to not intersect, but a separating axis cannot be found among this simple set. We must also include certain cross products in the candidate set.
Questions asked on job interviews also make for interesting blog post topics. (And very rarely, they are also practical!) This post discusses one of my favorite interview questions: “How can you tell if two axially-aligned 2D boxes intersect?” The adjective “axially-aligned” means that the sides of the box are parallel to the x- and y-axes; the box is not arbitrarily oriented. The acronym AABB is often used for axially-aligned bounding box.
I like this question because it is both a test of experience and problem solving ability. Entry-level applicants aren’t necessarily expected to know the answer, but they should be able to suggest an approach (with very high frequency they suggest the incorrect solution discussed below), work through some examples, and arrive at the correct one. Most experienced web/GUI programmers have worked with 2D boxes and have encountered the problem, and should know the proper solution. However, they often do not immediately perceive the principle that extends the idea beyond AABB’s to arbitrarily-oriented boxes. In summary, it’s a problem with a deceptively simple starting point and many branching points depending on the skill of the applicant, which is precisely why it’s a great interview question.
Let’s say that we have basic 2D vector and bounding box classes such as:
// Simple 2D vector class struct Vec2D { float x,y; }; // 2D axially-aligned bounding box. struct Box2D { Vec2D min, max; };

So the goal of the question is a function with a prototype such as
bool BoxesIntersect(const Box2D &a, const Box2D &b);
When faced with this problem, inexperienced programmers produce a particular solution that doesn’t work with such regularity, that it is worth mentioning. They suggest to check the four corners of box A, to see if any are containing within box B, and also check B‘s corners to see if they are contained within A. But this approach fails in the following example.

The correct approach is to work by process of elimination. What are some situations when it is clear that the two AABB’s do not intersect? We can immediately think of an obvious case: if box A is completely to the left of B, then the boxes cannot intersect. Likewise if A is completely to the right of B, or above or below it. Are there any other cases to consider? No. If A is completely to the left or right of B, then the vertical positions of the boxes do not matter. If two boxes do not intersect, they will fit into at at least one of the four cases just mentioned. (If you are not convinced of this, a useful exercise is to try to categorize all the possible possible cases.) So the only remaining task is to express “A is completely to the left of B,” and so forth, in C.
This is the correct answer:
bool BoxesIntersect(const Box2D &a, const Box2D &b) { if (a.max.x < b.min.x) return false; // a is left of b if (a.min.x > b.max.x) return false; // a is right of b if (a.max.y < b.min.y) return false; // a is above b if (a.min.y > b.max.y) return false; // a is below b return true; // boxes overlap }
(By the way, whether “<” and “>” should be “<=” and “>=” depends on exactly what you mean by boxes “intersecting.” If you are using floating point coordinates and this distinction matters, you are probably doing something wrong.)
Most everybody understands the solution to this problem, and it extends in a straightforward way into 3D. So that’s pretty much all there is to say about AABB’s.
But what about boxes that are arbitrarily oriented? How about convex polygons (or polyhedra in 3D) with an arbitrary number of sides? To solve these problems requires that we distill from our ad-hoc solution a more fundamental principle, known as the separating axis theorem. It can be used to detect whether two convex polygons overlap.