Part I A Simple Game of Air Hockey 2. Defining Vertices and Shaders
2.3 Defining the Structure of Our Air Hockey Table 22
Before we can draw our table to the screen, we need to tell OpenGL what to draw. The first step in that chain is to define the structure of our table in a form that OpenGL understands. In OpenGL, the structure of everything begins with a vertex.
Introducing Vertices
A vertex is simply a point representing one corner of a geometric object, with various attributes associated with that point. The most important attribute is the position, which represents where this vertex is located in space.
Building Our Table with Vertices
We said we would keep things easy for now, so what’s the most basic shape we could use to represent the structure of our air hockey table? We could use a rectangle. Since a rectangle has four corners, we would need four ver- tices. A rectangle is a two-dimensional object, so each vertex would need a position, with a coordinate for each dimension.
If we were to draw this out on a sheet of graph paper, we might end up with something similar to the following figure:
Figure 6—Drawing a table on graph paper
Chapter 2. Defining Vertices and Shaders • 22
Defining Vertices in Code
Let’s go ahead and write some code to store these vertices. We’ll represent the vertices as a list of floating point numbers; and since we’re working in two dimensions, we’ll use two floating point numbers per vertex: one for the x position and one for the y position.
Since we have two components per vertex, let’s first create a constant to contain that fact. Open up AirHockeyRenderer and add the following constant to the top of the class:
AirHockey1/src/com/airhockey/android/AirHockeyRenderer.java
private static final int POSITION_COMPONENT_COUNT = 2;
Now add the following constructor before onSurfaceCreated():
AirHockey1/src/com/airhockey/android/AirHockeyRenderer.java public AirHockeyRenderer() {
float[] tableVertices = { 0f, 0f,
0f, 14f, 9f, 14f, 9f, 0f };
}
We define our vertex data using a sequential list of floating point numbers so that we can store positions with decimal points. We’ll refer to this array as our vertex attribute array. We’ve only stored the position for now, but later on we’ll also store the color and other attributes using the same concept seen here.
Points, Lines, and Triangles
Remember when I said that the easiest way to represent our hockey table would be as a rectangle? Well, I’m about to throw a wrench in the works: in OpenGL, we can only draw points, lines, and triangles.
The triangle is the most basic geometric shape around. We see it everywhere in the world, such as in the structural components of a bridge, because it is such a strong shape. It has three sides connected to three vertices. If we took away one vertex, we’d end up with a line, and if we took away one more, we’d have a point.
Points and lines can be used for certain effects, but only triangles can be used to construct an entire scene of complex objects and textures. We build trian- gles in OpenGL by grouping individual vertices together, and then we tell OpenGL literally how to connect the dots. Everything we want to build needs Defining the Structure of Our Air Hockey Table • 23
to be defined in terms of these points, lines, and triangles, and if we want to build more complex shapes, such as an arch, then we need to use enough points to approximate the curve.
So how can we define our air hockey table if we can’t use rectangles? Well, it turns out that we can think of the table as two triangles joined together, as seen in the next figure:
Figure 7—Drawing a table on graph paper: two triangles joined together
Let’s change the code to reflect the fact that we’ll now use two triangles instead of one rectangle:
AirHockey1/src/com/airhockey/android/AirHockeyRenderer.java float[] tableVerticesWithTriangles = {
// Triangle 1 0f, 0f, 9f, 14f, 0f, 14f, // Triangle 2 0f, 0f, 9f, 0f, 9f, 14f };
Our array now holds six vertices, which will be used to represent two triangles.
The first triangle is bounded by the points at (0, 0), (9, 14), and (0, 14). The second triangle shares two of these positions and is bounded by (0, 0), (9, 0), and (9, 14).
Whenever we want to represent an object in OpenGL, we need to think about how we can compose it in terms of points, lines, and triangles.
Chapter 2. Defining Vertices and Shaders • 24
The Winding Order of a Triangle
You might notice that when we define our triangles we order the vertices in counter- clockwise order; this is known as the winding order. When we’re consistent in using the same winding order everywhere, we can often optimize performance by using the winding order to figure out if a triangle belongs to the front or to the back of any given object, and then we can ask OpenGL to skip the back triangles since we won’t be able to see them anyway.
We’ll learn more about this later in Culling, on page 249.
Adding the Center Line and Two Mallets
We’re almost done defining our vertices. We just need to add a few more ver- tices for the center line and our two mallets. We want to end up with some- thing like the following figure:
Figure 8—Drawing a table on graph paper: with a line and two mallets
We’ll use a line for the center line and a point for each mallet. Add a comma to the end of the array, and then add the following new vertices:
AirHockey1/src/com/airhockey/android/AirHockeyRenderer.java // Line 1
0f, 7f, 9f, 7f, // Mallets 4.5f, 2f, 4.5f, 12f
Defining the Structure of Our Air Hockey Table • 25
As you can see, we can also use decimal coordinates since our array is com- posed of floating point values. In order to keep the Java compiler happy, we need to add a small f after the number to inform the compiler that this number should be interpreted as a float and not as a double. Doubles have about double the precision (hence the name), so if we don’t add the f, Java will see it as a precision-losing conversion and ask us to add an explicit cast.