In the last chapter, we built our font importer. We can now read in a graphics file containing letters and symbols in a particular format with black letters and a white background, and generate an array of coordinates that will allow an OpenGL program to look up the individual characters on the sheet (as a texture).
Well, almost. OpenGL texture coordinates run from (0,0) to (1,1), and our font coordinates could be anything, depending on the size of the font sheet we used. In the case of the Cooper Std sheet, which is 512 x 256, the character coordinates could run anywhere from (0,0) to (512,256). Not only that, but OpenGL starts (0,0) in the lower left-hand corner and our font sheet starts (0,0) in the upper left-hand corner.
This looks like a mess. What we need to do is create a coordinate conversion system, and we need to make a very important decision. Should the class using the font importer be responsible for the conversion, or should we do the conversion in the font importer?
Since the font importer’s only job is to log the coordinates of the characters in the font sheet, it’s really not tied directly to any OpenGL processing. In fact, this class could be used for any number of purposes outside of OpenGL, and if used outside of OpenGL, we would be expecting pixel coordinates, not texture coordinates.
Populating our array with texture coordinates would limit the uses of this class, but we could always go ahead and create a second array of OpenGL texture coordinates as well. Unfortunately, that would nearly double our memory requirements for array storage. The array is pretty small, though, so what it might not be so bad. What would the alternative be?
If we make the calling process responsible for conversion, that means that every text string using this data would have to perform coordinate conversion at some point, and then again if the contents of that text string changed.
So it boils down to increased memory usage versus increased processing requirements. I won’t pretend that there’s a single right answer here, and will even guarantee that whatever recommendation I make, someone will disagree with very good reasons either way.
For this tutorial, I’m going to make coordinate conversion the responsibility of the caller, and take the extra processing over the extra memory usage. One of the reasons for this is I happen to know that we actually have a second coordinate conversion requirement: converting these pixel coordinates to OpenGL render space to create the backing triangles for rendering the textures on to. OpenGL space runs from (-1,-1) to (1,1), also starting in the lower left-hand corner. I think it will be easier to see what’s going on in the text string class if we keep these two conversion together. Moving both of these conversions into the importer class will make some of the text string class processing too abstract.
With that issue resolved, the next thing we need to worry about is what our requirements for the text string class will be. What are some of the things we want to do with this text? We know we want to display messages like “Game Over” and “Touch to Start”. We also want to be able to update the text so a score counter will be possible.
Those are the basics, but some nice features would be allowing the text to drift and fade, for when we touch a target (or miss). The color should be configurable, too. We’ll need different sizes, and we’ll need to be able to place the text where we want on the screen.
Finally, we don’t want the program using the text class to have to manage all of these text strings individually, so we’ll plan on creating a text string manager that will take care of rendering and tracking all of these text strings. Since we know there will be a manager class, we’ll plan on putting the rendering logic in there instead of the text string class. The only thing the text string class will need to do for rendering is provide the manager with a vertex array, texture coordinate array, and number of vertices. A few other pieces of information will be provided for the shaders, like color and alpha (opacity), but the manager will make sure that the shaders get the information properly.
Speaking of shaders, the text string manager will need custom shaders since the text string rendering will have different requirements than other rendering jobs in our TouchTargets game. This looks like a good opportunity to reuse that EDOpenGLProgram class!
Now that we have a pretty good idea of what we need to do, we’ll start coding our new text string classes and shaders in the next chapter.