I’ve been fiddling around with font rendering again this weekend. For my upcoming game, I wanted
a cool intro effect like in some movies, where the displayed text is very very slowly expanding.
But I couldn’t quite replicate that effect with the XNA
SpriteFont class. Even with
antialiasing turned on, the borders of the text were flickering. Using very large font sizes
resulted in hopelessly oversized .xnb files with only a slight improvement for
the unsteady borders.
So my new solution was to write a component that renders vector-based text instead of bitmaps. Extracting the vector data from a font was a bit harder than I had expected because fonts consist mostly of complicated second and third order bezier curves. I managed to break these down into plain straight line segments and am now in the possession of a content pipeline importer that converts .ttf fonts into arrays of line segments. Behold:
In the upper left is my replacement sprite font importer which can take the place of
Microsoft’s own importer by the flip of a combobox. It produces identical .xnb files
that can be used with the
SpriteFont class, so you can toggle forth and back
at will. The upper line is from a font imported with my own importer (using FreeType) and
the lower line is from the same font imported with the XNA SpriteFont importer (using
Windows Vista .ttf rendering).
The big outlined vector text highlights the separate outlines the characters consist of in the upper line and shows the normal rendered result below. Both outputs have been generated by writing a series of line strips into a vertex buffer and rendering it.
Now filling the characters requires the outlines to be tessellated into a
triangle mesh, which is another unexpected complexity I hadn’t thought about
when I set out to create a vector font rendering component. But I already took
the hurdle by cheating with OpenGL’s
gluTess...() routines. Of course, these
will only be used in the content pipeline assembly, the final game .exe has
zero references to unmanaged code and/or OpenGL.
Update: I completed the GLU tessellator interfacing code and am now proud to announce the near completion of the Nuclex.Fonts VectorFont importer. Once I’m done with the runtime side of things, you can have smooth-zooming text without any blurry pixels or loss of quality, rotating 3D text or just a means of converting strings into vertices for whatever purpose you want.