Displaying text

speed

Computer programs are based on a two way communication with the user. The user 'talks' to the program through the input devices, such as keyboard, mouse and joystick. In games sound is primarily used to create effects that will make the environment seem more realistic as in the movies. The other alternative is text display. How do we display text in games? Our first ever program consisted of the line 'printf("hello world\n");', but that was only a simple text output on a console. In 3D environments like OpenGL we have to try a little harder to display text. This tutorial explains how it can be done and gives you a simple class that can hide away all the details and let you concentrate on the development of your game.


3D font

Text like anything else in OpenGL is actually a 3D object. Its basic attribute is the geometry of the faces and vertices within it. So all we have to do is generate the geometry for every letter. I know this sounds rather intimidating. I was kind of scared the first time I stumbled in it. But when you read a little more about how it is done you realize that the inner workings of the graphics libraries take care of everything. All you have to do is guide them to what you want.

First you have to decide about the font you want to use. Let's assume you want to use "Bookman Old Style". The next thing is the font size to use. This parameter influences the quality of 3D font generated. Use higher values if you plan to use the font for big size display. Finally you specify the third dimension of the font the depth of the letters.

Here is the class method that creates a 3D font

void clf_font::build_3d(const char *name, int size, dFloat depth)
{
	HFONT hFont;
	HDC hDC = wglGetCurrentDC();
	listBase = glGenLists(256); // create storage for 256 characters
	// ask windows to create the outline of the font
	if (strcmp(name, "symbol") == 0)
	{
		hFont = CreateFont(-size, 0,0,0,FW_BOLD, FALSE, FALSE, FALSE,
				SYMBOL_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS,
				ANTIALIASED_QUALITY,FF_DONTCARE | DEFAULT_PITCH, name);
	}
	else
	{
		hFont = CreateFont(size, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, 
				ANSI_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY,
				FF_DONTCARE | DEFAULT_PITCH, name);
	}
	// on faillure return
	if (!hFont)
		return;
	// and now ask the system to generate the geometry and store it in the draw lists
	SelectObject(hDC, hFont);
	wglUseFontOutlines(hDC, 0, 255, listBase, 0.0f, depth, WGL_FONT_POLYGONS, gmf);
}

To speed things up in drawing we use draw lists. You can see the draw list article for more details on draw lists. As you can see all the dirty job is done by the system.

Now when you want to display some text all you have to do is treat it like any other object. Use 'translate' to move it around, 'rotate' to set the orientation and 'scale' for the size. Then use 'glCallLists' to draw the text.

glListBase(listBase);
glCallLists((GLsizei)strlen(text), GL_UNSIGNED_BYTE,text);

It was not hard after all, was it?


2D font

There are times when we want to display some simple text on the screen without any effects. Just a nice typeface to display guiding information and the current game status taking up very little screen space. In cases like these the text is not actually part of the 3D environment so we do not need full 3D geometry. The solution is similar to the one above. We just ask the system to generate the draw lists for the letters and we draw them.

However there are some differences in the way the two methods behave. When drawing in 3D we place the text IN THE SCENE, while when we draw in 2D we place the text ON THE SCREEN. The position is only specified in X and Y coordinates within the current viewport. You can also specify the position in screen pixel coordinates with the (0,0) point being at the bottom left corner of the window. In the later case viewports and other 3D scene manipulation objects have no effect in the behavior of the 2D text.

Here is the code that generates the draw list for the font:

void clf_font::build_2d(const char *name, int size)
{
	HFONT hFont;
	HDC hDC = wglGetCurrentDC();

	listBase = glGenLists(256);

	if (strcmp(name, "symbol") == 0)
	{
		hFont = CreateFont(-size, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, SYMBOL_CHARSET,
				OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY,
				FF_DONTCARE | DEFAULT_PITCH, name);
	}
	else
	{
		hFont = CreateFont(-size, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
				OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, ANTIALIASED_QUALITY,
				FF_DONTCARE | DEFAULT_PITCH, name);
	}
	SelectObject(hDC, hFont);
	wglUseFontBitmaps(hDC, 0, 256, listBase);
}

Almost identical with the font generation in 3D, only this time we ask the system to generate bitmap font at the last line. The code to display the text is the same as in 3D. The code to set the text position has two different options.

First we can position the text in the 3D scene using the call :

glRasterPos2f( xpos, ypos );

or we can use screen coordinates with the call :

glWindowPos2i(screenX, screenY);

You can use either positioning method according to your needs.

Download the font sample here.

GusOnGames is member of Obscure Horizons group
Increase your website traffic with Attracta.com