1

Topic: Bitmap Fonts

Bitmap Fonts: Using Prerendered Graphics for Text

NOTE: This tutorial assumes you can handle the basic setup of a D3D/DX project. The code included here can be added to such a project for use. You will also have to work with the code a bit, as it is very much designed to work within our specific project.  Also, accompanying source is available in our downloads section.

During the initial development of WordWars, we were using the D3DXFont object; it's very simple and takes very little to set up. As we neared the beta release, the performance became a serious issue. DirectX actually creates multiple polygons per letter to do the text, and does so very inefficiently. This is fine for small amounts of text, but not for long messages (or interactive chat text). DrawText isn't much better.

The only real answer was a prerendered font, or bitmap font. Bitmap fonts have the advantage of being very fast, and they can be painted with whatever texture or mix of colors you like. The WordWars metal set uses a "liquid metal" effect for its fonts, as an example. Unfortunately, it can be tricky to draw them where their baselines align correctly, and manually calculating the width of each letter (much less applying that information when drawing them) can be a very trying experience. What finally made us decide to take the plunge into bitmap fonts was the discovery of an excellent free tool: Bitmap Font Builder.

This tutorial roughly covers the process we use for WordWars' bitmap fonts. Code is also available for download, although it will need a little modification to work in your own program. Our bitmap font engine supports standard D3DXFont alignments (left, top, center, etc.) and a few formatting tags (/COLOR, /NEWLINE). You are welcome to use the provided code for any purpose.

http://www.atomicmonks.com/images/fontbuilder.jpg

Bitmap Font Builder I stumbled across this on a C++ site, and it can be found at http://www.lmnopc.com/. The creator asks for an email telling him what you're doing with it...and that's the only cost. Seems fair.

Bitmap Font Builder gives you many options, but the only one truly important to this tutorial is the texture size of 512x512.

There is an option to create a bitmap with a full ASCII character set, but the default gives you all the characters you should need. WordWars sets use the latter, with Font 2 set to precisely half the size of Font 1. Create a font bitmap and save the graphic to a tutorial project folder. Record the font sizes you used for later use. Finally, save the font widths to an INI file (in the File menu. Put this file inside your project folder also.

Next, a look at the ATOMFont code module.

Are you playing?
http://www.atomicmonks.com/sig.php?a=av&pname=Brother%20Erryn

2

Re: Bitmap Fonts

Keep in mind that this module is actually functions from several different WordWars modules. They were combined for this tutorial, and were designed strictly for the game...you will most likely want to modify parts of it, but this tutorial will explain how it's done in WordWars.

First, let's take a look at a couple of UDTs needed:

Private Type tFont
    InUse As Boolean ' is this font in use?
    LettersBig(127) As Long ' reference to letter sprites,
    LettersSmall(127) As Long ' characters 32 to 159
    SizeLarge As Single ' original font size
    SizeSmall As Single ' ditto
    WidthBig(127) As Long ' reference to character width
    WidthSmall(127) As Long ' ditto
End Type

This type stores the actual font information. In WordWars, sets have a large font, and a small font. The comments explain the parts of the type fairly well, so we won't go into them too much here. One important thing to note is that the characters in the bitmap font are ASC 32 to 159...we'll use that later. And here you can see why we said to record the font sizes we used in Bitmap Font Builder.

The next UDT stores information about individual letters:

Public Type tLetter
    Color As Long ' color of this letter
    Scaling As Single ' scale of this letter
    SpriteRef As Long ' reference to sprite object
    x As Single ' x coordinate of this letter
    y As Single ' y coordinate of this letter
End Type

An array of tLetters will store all the display information for a given word, and will be passed to a function later to actually display the text.

Public bFonts() As tFont
Public PLAYERFONT As Long
Public ENEMYFONT As Long

bFonts is an array of loaded fonts, stored in a custom font pool. PLAYERFONT and ENEMYFONT are just what they say, and are the only fonts used in WordWars.

Next, a look at the InitbFont function.

Are you playing?
http://www.atomicmonks.com/sig.php?a=av&pname=Brother%20Erryn

3

Re: Bitmap Fonts

The InitbFont function loads a bitmap font into the font pool for future use. It finds an available spot in the pool, then prepares it for use. The first lines that really need any explanation are these:

Dim TEMPTEX() As Long
Carve TEMPTEX, 32, "your font bitmap.bmp", 32, 512, 512, False

The Carve function is a function that I wrote, and that gets a lot of use. It slices a larger image into multiple smaller images. (Alternatively, you can use a paint program and carve the font bitmap into letter-sized pieces manually.) The complete Carve function is included, as-is, along with the font functions. It makes several calls to other unique functions that are not included. Also, some of parameters don't mean anything outside of WordWars. Just accept that "32" is the end dimension of each of the pieces, and "512" is the original height and width of the font bitmap. The end result is that TEMPTEX() contains an array of sprite objects containing all the letters of your font bitmap.

Bottom line: you need a collection of sprite objects that contain the letters, in ASCII sequence.

The next bit loads the font information into the bFonts pool:

With bFonts(FontNum)
    For lngCount = 0 To 127
        .LettersBig(lngCount) = TEMPTEX(lngCount)
        .LettersSmall(lngCount) = TEMPTEX(lngCount + 128)
        .SizeLarge = CSng(GetGameSetting("BigFontSize", "mysettings.ini"))
        .SizeSmall = CSng(GetGameSetting("SmallFontSize", "mysettings.ini"))
        .WidthBig(lngCount) = CLng(GetGameSetting(CStr(lngCount), _
             "BFB Font Metrics.ini"))
        .WidthSmall(lngCount) = CLng(GetGameSetting(CStr(lngCount + 128), _
             "BFB Font Metrics.ini"))
        Next
End With

This part assigns the sprites to the big and small letter sections, and stores the letter widths and original font sizes. The function GetGameSetting (also included in the source code) retrieves an INI file setting as a string. If "BigFontSize=15" is inside "mysettings.ini", the function will return "15". This function is used instead of API calls or anything else "standard" because of how Windows 2000 handles INI files (it looks in the registry instead).

Finally, the function returns a reference number to the loaded font (which in this case is actually TWO fonts).

Next, a look at the hideous SetUpbFontWord function.

Are you playing?
http://www.atomicmonks.com/sig.php?a=av&pname=Brother%20Erryn

4

Re: Bitmap Fonts

SetUpbFontWord is a long and painful function, but it does the real work. Let's start by taking a look at the Function call header.

Public Sub SetUpbFontWord(LetterArray() As tLetter, _
    FontNum As Long, _
    Text As String, _
    FontSize As Single, _
    Optional BigFont As Boolean = True, _
    Optional VertAlign As String = "CENTER", _
    Optional HorizAlign As String = "CENTER", _
    Optional rLeft As Single = 0, _
    Optional rTop As Single = 0, _
    Optional rRight As Single = 800, _
    Optional rBottom As Single = 600)

Wow! Let's go over these parameters:

LetterArray - A dynamic array of letters that will be loaded with texture and position information.
FontNum - A reference to a font previously created by InitbFont
Text - The actual text to be displayed
FontSize - The actual font size (not pixels)
BigFont - Specifies whether to use the top (larger) font, or the smaller one.

The rest are alignment settings, set for a screen with a default resolution of 800x600 pixels.

Next is a Do loop, that divides the words of Text into multiple rows, based on the dimensions passed to the subroutine. This loop repeatedly uses the WordSize function (also included in the source) to evaluate the pixel width of text as it builds the rows. Inside this loop is a Select Case statement that watches for formatting tags. All tags and words must be separated by a space to be properly parsed, and as they are reassembled spaces are actually added to keep the words from running together. This is why there is a /NOSPACE tag. It allows multiple /COLOR tags to be used within the same word without spacing it apart.

After the row building loop, the subroutine calculates the total height of the rows, so that it can properly perform any specified vertical alignment.

Without confusing everybody, suffice it to say that the rest of the function simply works its way through the rows, creating tLetter objects and setting their x and y coordinates. At the start of a new row, the x position is reset to zero, and the row height is added to the y position.

When finished, the passed LetterArray dynamic array contains a collection of sprites to the needed letters, all set with the proper position and the proper scale for the specified font size.

Next, a look at the hideous DrawbWord function.

Are you playing?
http://www.atomicmonks.com/sig.php?a=av&pname=Brother%20Erryn

5

Re: Bitmap Fonts

There's really not much to the DrawbWord function. It loops through the tLetter array it is given and draws the sprites. It accepts an x and y offset (effectively moving the "box" in which you set up the font), and a color setting that can override any /COLOR tag setting. Sprites is a pool of custom sprite objects that contain the information needed by the D3DSprite object for rendering (the D3DSprite is a good subject for a completely different tutorial). The following lines:

ATOMEngine.MoveSprite .SpriteRef, .x + OffsetX, .y + OffsetY
ATOMEngine.RenderSprite .SpriteRef

These simply reposition a sprite and render it in the WordWars engine. Consider writing your own DrawbWord function...just remember that the tLetter array you pass to it will contain color, position, and scale information. Pass this to your own sprite rendering routine as needed.

Now for an example of use, although it might not be truly necessary. First you declare a dynamic array for the letters you'll need:

Private btWord() As tLetter

Then you create the text:

ATOMFont.SetUpbFontWord btWord(), PLAYERFONT, _
    "Here is my message", 40, False, "BOTTOM"

Then in your main render loop:

DrawbWord btWord()

To be honest, this "tutorial" is more of a collection of code samples with a little explanation and some overview. The original code is so integrated into its project that it's difficult to extract specifically for a tutorial project.

If you need specific help or answers concerning this tutorial, please post in the Visual Basic section of the forums. Others may benefit from your questions, and the answers to them.

Are you playing?
http://www.atomicmonks.com/sig.php?a=av&pname=Brother%20Erryn