A detailed look at implementing graphically demanding games in OpenGL with Java, using the JOGL library

Wednesday, February 11, 2009

Quick hint - Texture matrix

Just a quick note stemming from a week long bug hunt. The GL_TEXTURE matrix is per texture unit, so if you are using multiple texture units, be sure to get the right one active before pushing, popping or loading a new matrix! e.g.


gl.glMatrixMode(GL.GL_TEXTURE);
gl.glActiveTexture(GL.GL_TEXTURE0);
gl.glPushMatrix();
gl.glLoadIdentity();

...

gl.glPopMatrix();


Misunderstanding this feature can also easily lead to stack under flow if you change the active texture in between push and pop pairs.
Of course in retrospect, it's obvious that each texture unit needs its own matrix, but it's just one of those little details that's not well described in the man pages.

Text rendering

I can't think of many (or any!) games that don't have any need for text. From displaying your vital stats to pointing the player in the right direction to providing subtitles for those with hearing difficulties, it's hard to escape at least 5000 years of written tradition.

Rendering text in OpenGL poses some issues. There's no built in text rendering facility, though there are several utility libraries that give you varying degrees of control. But to go old-school and implement your own basic text functions will give you the ultimate control and help you earn serious respect for font designers.

There are two approaches to rendering text - vector and bitmap. Modern computing has sidelined the bitmap font in favor of structured vector fonts, but bitmaps have several advantages in OpenGL - they can be conveniently represented in a texture, and they're fast. And, you can easily customise them, adding colour, embossing, or even pixel shader effects such as normal bump mapping that literally make your text stand up off the page.

I've chosen a small-caps font. This means I have to draw fewer glyphs and kern fewer character pairs (more later on kerning). Here's my font texture, fresh out of photoshop:
Notice the classy embossed faux-gold effect ;o)

The characters are in an 8 by 8 grid, giving me 64 characters to play with. Now its a simple matter to load this texture, and render texture mapped quads for each character in a string:

private void drawChar(GL gl, int index, float x, float y, float size) {
float tx, ty;
tx = (index % 8) / 8.0f;
ty = (index / 8) / 8.0f;
gl.glBegin(GL.GL_QUADS);
{
gl.glTexCoord2f(tx, ty);
gl.glVertex2f(x, y + 0.8f * size);

gl.glTexCoord2f(tx, ty + 1.0f / 8.0f);
gl.glVertex2f(x, y - 0.2f * size);

gl.glTexCoord2f(tx + 1.0f / 8.0f, ty + 1.0f / 8.0f);
gl.glVertex2f(x + size, y - 0.2f * size);

gl.glTexCoord2f(tx + 1.0f / 8.0f, ty);
gl.glVertex2f(x + size, y + 0.8f * size);
}
gl.glEnd();
}

But hang on a second, we can do better than that. Used naively, we'd get ugly monospaced text, where consecutive i's look stranded and the W's are almost overlapping. We need kerning.

To get the kerning right is pretty hard. It's a subjective matter that requires an artful eye, but I thought I'd give it a go anyway. I wrote a quick app that displays grids of letter pairs, and allows the user to adjust the kerning by left and right clicking:


So - nearly 2000 kerning pairs later, we have a variable width font that can display whatever strings, in whatever size is needed.

The GLText class can be found here in the JavaPop project (though it is pretty standalone if you want to re-use it; you'll just have to pick somewhere in your project to keep the kerning2.dat file) and the kerning tool is in the JavaPopTools project. Let me know if you have any similar bitmap fonts that need kerning and maybe I'll see what I can do.