Until now, we embedded any text that we needed inside of an existing texture. However, there are times when we may want to have the code decide what text to display. For example, on our credits screen, we don't want to make a graphic for each person's name who took part in creating the game.
We need a way to render text directly to the screen, and this means that we also need a way to define the font that we want to use when rendering the text. First, we need to add a global variable that services as a handle to our fonts. Add the following line to the variable declarations in the code:
GLuint fontBase;
Now, we need to add the following code to create the font:
GLvoid BuildFont(GLvoid) { HFONT newFont; HFONT tempFont; fontBase = glGenLists(96); tempFont = CreateFont(-26, // Height 0, // Width 0, // Escapement 0, // Orientation FW_BOLD, // Weight FALSE, // Italic FALSE, // Underline FALSE, // Strikeout ANSI_CHARSET, // Character Set OUT_TT_PRECIS, // Output Precision CLIP_DEFAULT_PRECIS, // Clipping Precision ANTIALIASED_QUALITY,// Output Quality FF_DONTCARE | DEFAULT_PITCH, // Family/Pitch "Courier New"); // Font Name newFont = (HFONT)SelectObject(hDC, tempFont); wglUseFontBitmaps(hDC, 32, 96, fontBase); SelectObject(hDC, newFont); DeleteObject(tempFont); }
This code creates a font using three main elements.
First, we use glGenLists
to create 96 display lists to hold each letter of our font. A display list is basically a buffer that can hold rendering data. Next, we call CreateFont
to create a Windows font. The parameters of the CreateFont
function specify the type of font that we want to create. Finally, we use wglUseFontBitmaps
to assign our new font to the font handle that we created earlier.
One little twist is that we have to create a temporary HFONT
object called tempFont
with all the properties, then we assign tempFont
to newFont
and delete tempFont
.
We will want to delete the display lists when the program closes down, so add the following utility function:
GLvoid KillFont(GLvoid) { glDeleteLists(fontBase, 96); }
This code simply uses glDeleteLists
to delete the display lists that we created to hold our font.
Now that we have a font, we need to have a function that will render text to the screen. Add the following function to the code:
void DrawText(const char* p_text, const float p_x, const float p_y, const float r, const float g, const float b) { glBindTexture(GL_TEXTURE_2D, 0); glColor3f(r, g, b); glRasterPos2f(p_x, p_y); if (p_text != NULL) { glPushAttrib(GL_LIST_BIT); glListBase(fontBase - 32); glCallLists(strlen(p_text), GL_UNSIGNED_BYTE, p_text); glPopAttrib(); } glColor3f(1.0f, 1.0f, 1.0f); }
This code takes a string and an x and y position, and draws the text at that position. It also takes r
, g
, and b
parameters to define the text color:
glBindTexture
(GL_TEXTURE_2D
, 0
): This tells OpenGL that we are going to be working with 2D textures (i.e. the fonts) glColor3f(r, g, b)
: This sets the color of the font.glRasterPos2f
: This is used to set the current draw position on the screen.glPushAttrib(GL_LIST_BIT)
: This tells OpenGL that we are going to render using display lists.glListBase
: This sets the current start of the list. We subtract 32 because the ASCII value for a space is 32, and we don't use any characters with lower ASCII values.glCallLists
: This is used to retrieve the lists for each character in the text.glPopAttrib
: This returns the OpenGL attribute to its previous value.Now, we are ready to draw our credits text:
void DrawCredits() { float startX = 325.0f; float startY = 250.0f; float spaceY = 30.0f; DrawText("Robert Madsen", startX, startY, 0.0f, 0.0f, 1.0f); DrawText("Author", startX, startY + spaceY, 0.0f, 0.0f, 1.0f); }
First, we set the position on the screen where we want to draw, then we use the DrawText
function to actually perform the drawing. The first line adds me (a subtle indulgence), and the second line is for you!
We have a few more book keeping tasks to perform to get the font support to work. First, modify the GameLoop
code, adding the highlighted line:
if (m_gameState == GameState::GS_Splash)
{
BuildFont();
LoadTextures();
m_gameState = GameState::GS_Loading;
}
This will create our fonts when the game starts up.
Next, fill out the GS_Credits
case of the m_gameState
switch in the Render
function:
case GameState::GS_Credits: { creditsScreen->Update(p_deltaTime); menuButton->IsActive(true); menuButton->Update(p_deltaTime); inputManager->Update(p_deltaTime); ProcessInput(p_deltaTime); } break;
This draws the credits text when the game state changes to GS_Credits
. Congratulations! You can finally get the credit that you deserve!
18.117.119.206