Dominic Szablewski, @phoboslab
— Saturday, August 16th 2008

Yuckfu Dev Diary #5 – OpenGL, Fonts and You

After my last post I read a bit more about displaying text with OpenGL. One of the more popular solutions is to have a single texture containing all (ASCII) characters of a font. As it turned out, there are several applications to build such a texture for you. Bitmap Font Builder is one of them. It looks a bit clunky, but gets the job done nicely – and it’s free!

Helvetica Font-Map

Here’s a quick test I did with the Helvetica typeface. I exported the texture as a transparent PNG file, cropped it and added a simple drop shadow effect in Photoshop. For now, I only used the upper case letters. I’ll probably come back later and export the full set of Latin-1 characters.

Bitmap Font Builder can also export an INI file with the pixel width of each character. I used a simple Regular Expression to convert this into a comma separated list, so I can use it directly in C.

const unsigned char charWidthMap[] = { 
//    ! " # $ %  & ' ( ) * + , - . /
    3,4,6,6,6,10,8,3,4,4,5,8,3,5,3,4,
//  0 1 2 3 4 5 6 7 8 9 : ; < = > ?
    6,6,6,6,6,6,6,6,6,6,3,3,8,8,8,6,
//  @  A B C D E F G H I J K L M  N O
    10,7,7,7,7,6,6,7,7,3,6,7,6,10,7,7,
//  P Q R S T U V W  X Y Z
    7,7,7,7,6,7,7,10,7,7,6 
};

To prepare displaying text on top of my 3D scene, I push the current projection matrix to the stack, load an orthographic (2D) view matrix and disable depth testing and lighting. I can then render my previously computed arrays of vertices and texture coordinates for a particular string in screen space. The vertex coordinates for each character are computed based on the previous characters width.

int offset = 0;
for( int i = 0; text[i] != 0; i++ ) {
    vertices[0].x = offset; // top left
    vertices[0].y = 0;
    …
    vertices[3].x = offset + charSizeInTexture.x; // bottom right
    vertices[3].y = charSizeInTexture.y;

    // Increase the offset for the next character.
    // My charWidthMap starts at ASCII character 32 (space)
    offset += charWidthMap[ text[i] - 32 ];
}

The normalized texture coordinates for each character are computed based on its position in the texture. E.g. for the top left corner:

int x = ((text[i] - 32) % charsPerRow) * charSizeInTexture.x;
int y = ((text[i] - 32) / charsPerRow) * charSizeInTexture.y;
texCoords[0].x = (float)x / textureSize.x;
texCoords[0].y = (float)y / textureSize.y;

The Result:

OpenGL Text on iPhone

As you can see, the result looks pretty good. I’ve got a proportional, anti aliased font with a nice shadow effect that blends in perfectly with the 3D scene. Still, no kerning (look at the spacing between the W and A in “WAR”), but I can live with that – after all, it’s an arcade game not an E-Book reader.

Update: Bitmap Font Builder is only free for non-commercial projects, but there are alternatives. On the other hand, with the current dollar course, I could also spend some 20 bucks.

Update 2: But then again, why pay for something that can be easily implemented with Javascript/HTML?

© 2024 Dominic Szablewski – Imprint – powered by Pagenode (2ms) – made with <3