Bitmap fonts
Well - this took me a lot longer than I thought it would. The goal was to enable the ability to draw strings of a certain font and colour within my game. I could have just made an image of each of the words I want to appear in the game (since there aren’t many), but for a larger game, or a game that needs to be translated into lots of languages, this would mean a lot of content development. Also, the goal of this project is to learn things, and I would learn nothing from using images for all my words.
I had used bitmap fonts in OpenGL once before, but using tinyXML and an OpenGL framework/engine that I did not write. So I wanted to see if I could do it on my own (though heavily referencing what I did before).
I created a Text class that is responsible for drawing text. I should really have called this the font class, since I am not actually storing the text to be drawn, rather the information I need about the font. The Text class inherits from the SPrite class.
The first thing the Text class must do is load in the font image and the xml. The image was easy enough, since I already had the ability to load images. Loading the xml took me a lot longer.
I decided to use RapidXML after a bit of research because it was called the “fastest” implementation with the least overhead. With my lofty goal of being an engine programmer at some point, and the idea being to practice making my projects as fast as they can be, this sounded like a good idea.
Well, you can see my frustrations in my previous post. I was stuck on that problem for quite some time. Just as I was ready to give up and try a different api, I discovered my error. I had tried to read from the document tag twice, instead of once from the doc tag, and once from the child of the doc tag.
The error is on line 2 of the following code - the program kept segfaulting on line 3. And RapidXML does not throw any errors outside of its parse function.
xml_node<> *font = doc.first_node("font"); font = doc.first_node("chars"); xml_node<> *character = font->first_node("char");
The fix seems so small - properly reading from the doc’s child tag (”chars”):
xml_node<> *font = doc.first_node("font"); font = font->first_node("chars"); xml_node<> *character = font->first_node("char");
Anyway, once I fixed that, the rest of the code worked completely perfectly. Onto the drawString method. The idea here is to modify the underlying sprite variable, draw a character and repeat for each character before the function ends.
This involves modifying the x,y coordinates of each corner of the sprite, as well as the centre position of the sprite and the UV coordinates. This part of the code I had from the last time I tried this - which I believe was written by my coworker at the time: Sam Cartwright.
My current version of the code is as follows. Pretty sure I can make this faster by getting rid of that vector.
glm::vec3 centrePos = m_oCentrePos; float curX = 0; const char* currentLetter = a_szToDraw; //not sure if better to put NULL or 0 here instead of the null terminator character. probably no difference. while(*currentLetter != '\0') { //loop through all letters to find current. Got to be a faster way. A map? for(std::vector<Character>::iterator it = m_characters.begin(); it != m_characters.end(); it++) { if((*it).value == *currentLetter) { Character& thisChar = *it; //move the position of the sprite m_oCentrePos.x = centrePos.x + curX + thisChar.width*0.5; //update the width and height and align bottom of the letters UpdateCornersGrounded(thisChar.width, thisChar.height); //change the UVs setSpriteUVCoords(thisChar.x/(float)m_uiTextureWidth, 1.0 - (thisChar.y+ thisChar.height)/(float)m_uiTextureHeight, (thisChar.x + thisChar.width)/(float)m_uiTextureWidth, 1.0 - (thisChar.y )/(float)m_uiTextureHeight); Sprite::Update(0); Sprite::Draw(VBO, IBO, shader); curX += thisChar.width; break; } } currentLetter++; } m_oCentrePos = centrePos;
Originally I was not taking into account the width of the current letter when setting the centre position (and I also had the y coordinates the wrong way around), which gave me this delightful result:
I also wasn’t placing the bottom of the letters at 0 on the y co-ordinate. These were all relatively minor fixes though!
(Right, putting code in here just does not work. I think it’s time I set up a proper blog so I can format everything correctly).












