So I implemented this tutorial from my favourite place. While the results were good, I started to hack around them in order to create A Better Font Management System™. It went on for days …
This first thing I did was dealing with the colour. This is an old trick I learned with OpenGL: it’s easy to colour white fonts to any other colour simply by playing with the colour calls. Say you have, like most games today, a chat system with different colours for highlighting various functions: local chat, world chat, whisper, system messages … Well, you’re not going to create a strip of coloured fonts for each of these functions. Previously, it was all about using
glColor() before each draw call. Now with shaders, it’s just a matter of adding these 2 lines in a simple fragment shader for textures.
uniform vec3 Filter;
color = texture2D( myTextureSampler, UV ) - (vec4(1.0, 1.0, 1.0, 0.0) - vec4(Filter, 0.0));
This may not look like much, but this is actually the first time I modified a shader by myself for an original solution. There are other ways to do it, but this one is mine. 🙂
The second thing to consider was the usual warnings about using foreign languages. The tutorial repeats this fact but for the scope of the tutorial, it still uses the old ASCII-based grid trick for building the font map, which mean that it’s tailored for an anglo-saxon public. To create another mapping (and there are plenty out there), you must create another map of those fonts. Another problem I find with a fixed ASCII grid is that you may not want to write text at all but simply use numbers with commas, dots and colons and a few unusual dingbats for good humour. In that case, all the other characters are wasted. So instead of automagically mapping your UVs as mathematical offsets to your grid based on the ASCII code of your characters, it’s better to have a single strip of random characters, each isolated by a grid that remembers the Unicode number for each one.
Now Unicode isn’t that hard to learn. I found this article very useful to hack my own UTF-8 based system.
So then it’s just a matter of scanning the strip to isolate the rectangles surrounding each character, packing them (using a solution similar to what I presented on my last post), and, finally, keeping a separate file for the UVs and any additional info necessary in a manageable format like XML or JSON.
In this “game” (together with Oh, so beautiful Suzanne), I stored each rectangle positions in an STL map with the Unicode number as key. Each time I scan a character, I search the key and it returns the correct rectangle in the map.
I stumbled against a few blocks along the way. I panicked a bit when I saw that the rendering was not quite pixel-perfect … Did something go wrong in the packing ? Are the characters packed too close ? I found that for a successful 2D rendering, you must turn off filtering. The fact that the tutorial used a TGA file instead of the usual PNG or DDS format may have been for the practical reason of isolating the filtering and mip-mapping routines that are so common in the other tutorials. And I naïvely assume that I should write them as part of the loading process for any map.
So that was fun. I’m far from a complete solution, as there are many tricks to master: scaling, rotating, alignment, spacing … but there are other times when fonts are not necessary and rendered text on a quad is the best way to display an original message.
Also, my current system renders fonts with FreeType (they provide an excellent tutorial here) so you can see a bit of no-so-smooth contours. It provides a nice quick solution but creating beautiful fonts is also an art – and my knowledge of FreeType is limited. So retouching them with The Gimp improved things dramatically as you can see in the “18.018” image above.
I’ll be taking a break from the standard OpenGL tutorials and work on some new exciting stuff in terms of game engine.
See ya soon and Happy Hacking.