-Font Class-
I wrote an OpenGL demo a while ago, for which I wanted a lot of text effects and a customisable font class. It's horrifying to go into the problems I had when I tried to implement one. After a LOT of problems, I finally managed to write one that fulfilled all my requirements. In this tutorial, I will explain why a particular functionality is present with examples of problems that could arise if they are'nt there. Enough about the article, lets talk about how best to draw text with OpenGL.
There is, of course wglUseFontBitmaps, but I have found that it is kinda slow, and gives text that is not antialiased. One of the fastest ways to draw text in OpenGL is to map the letters onto small quads, and draw the quads. This uses the hardware rasterizer, so it as fast as drawing a textured polygon. The minification filter and magnification filter control the aliasing of the text, so the text looks really smooth. Also, this method allows a lot more effects than is possible with wglUseFontBitmaps.
One of the main problems with texturemapped fonts is that, it looks alright for one particular resolution, say 640x480. When the resolution is switched to 320x240 the font size remains the same and it looks really huge. Another problem is placement. When you draw text at co ordinates (10,10) in 640x480 resolution you mean you want to text to appear at those relative co ordinates whatever the resolution. Just think of the consequences when you place text at co ordinates (500,400) in 320x240 resolution! The class we are going to develop will take care of all these possibilites and scale/position perfectly irrespective of the resolution. We will now take a look at some of the declarations of this class
const
     RESOLUTION_REFX=640;
     RESOLUTION_REFY=480;
This is the resolution the screen is assumed to be. That is, all co ordinates and sizes will be specified assuming the resolution to be these values. The specified values will be scaled to fit the current resolution. Don't worry if you dont get it, we'll be seeing more of this later.
    TGLFont=class
                 private
                 fCharWidth:TFloat;
                 fCharHeight:TFloat;
                 fImageWidth:integer;
                 fImageHeight:integer;
                 fTexture:TTexture;
                 fBase:GLuint;
                 fColor:TColorf;
                 fXRes,fYRes:integer;
                 public
                 constructor Create;
                 //Delete the currently created lists
                 procedure DeleteLists;
                 //Destroy everything
                 destructor Destroy;override;
                 //Load the texture
                 function LoadFont(aFilename:string):boolean;
                 //Build the font, which is based on current resolution
                 procedure BuildFont(aXRes,aYRes:integer);
                 //This prints the specified string at the given co ordinates
                 procedure Print(X,Y:integer;Text:PChar);
                 //This is the character width on the texture
                 property CharWidth:TFloat
                          read fCharWidth
                          write fCharWidth;
                 //This is the character height on the texture
                 property CharHeight:TFloat
                          read fCharHeight
                          write fCharHeight;
                 //The width of the texture image supplied
                 property ImageWidth:integer
                          read fImageWidth;
                 //The height of the texture image supplied
                 property ImageHeight:integer
                          read fImageHeight;
                 //The color with which these characters will be drawn
                 property Color:TColorf
                          read fColor
                          write fColor;
                 property XRes:integer
                          read fXRes;
                 property YRes:integer
                          read fYRes;
    end;

At this point, note that the BuildFont routine takes the current resolution as its parameters. So, if the resolution is switched, then we will have to call DeleteLists and BuildFont, to remake the font for that resolution. The LoadFont method is simply a call to fTexture.LoadTexture. The methods that we will really need to understand are BuildFont and Print. Let us go over BuildFont first.
     //If this is a display list, then delete it all.....
     DeleteLists;

     //Save this resolution for future Print calls...
     fXRes:=aXRes;
     fYRes:=aYRes;

     //Assume that the texture has been loaded.... :)
     //Generate enough display list IDs to cover all characters on the texture
     numCols:=(fImageWidth div Trunc(fCharWidth));
     numRows:=(fImageHeight div Trunc(fCharHeight));
     fBase:=glGenLists(numCols * numRows);
     //Enable the texture
     fTexture.Enable;
     //This is a TGA, so we use the Alpha channel
     fTexture.Transparency;

     ht:=HowMuch(RESOLUTION_REFX,fCharWidth,aXRes);
     wd:=HowMuch(RESOLUTION_REFY,fCharHeight,aYRes);
     space:=HowMuch(RESOLUTION_REFX,(fCharWidth/2),aXRes);
     charCount:=0;
     for j:=numCols-1 downto 0 do
     begin
          for i:=0 to numCols-1 do
          begin
               //Top left u,v of the quad
               u0:=HowMuch(fImageWidth,i*fCharWidth,1);
               v0:=HowMuch(fImageHeight,j*fCharHeight,1);

               //Bottom left u,v of the quad
               u1:=HowMuch(fImageWidth,i*fCharWidth,1);
               v1:=HowMuch(fImageHeight,j*fCharHeight+fCharHeight,1);

               //Bottom right u,v of the quad
               u2:=HowMuch(fImageWidth,i*fCharWidth+fCharWidth,1);
               v2:=HowMuch(fImageHeight,j*fCharHeight+fCharHeight,1);

               //Top right u,v of the quad
               u3:=HowMuch(fImageWidth,i*fCharWidth+fCharWidth,1);
               v3:=HowMuch(fImageHeight,j*fCharHeight,1);

               glNewList(fBase+charCount,GL_COMPILE);
                        glBegin(GL_QUADS);
                               glTexCoord2f(u0,v0);
                               glVertex2d(0,0+ht);

                               glTexCoord2f(u1,v1);
                               glVertex2d(0,0);

                               glTexCoord2f(u2,v2);
                               glVertex2d(0+wd,0);

                               glTexCoord2f(u3,v3);
                               glVertex2d(0+wd,0+ht);
                        glEnd;
                        glTranslated(space,0,0);
               glEndList;
               Inc(charCount);
          end;
     end;
We first calculate the number of rows and columns this is done by dividing the Width (or Height) by the char Width (or Height). Then we allocate numCols*numRows display list IDs. This is because display lists provide fantastic performance and.....well, you'll see when we get to the Print procedure :). Now comes the size independent part we were talking about. We have to calculate the dimensions of the font for this resolution (aXRes, aYRes). This is done by the call to the function HowMuch.
HowMuch accepts three parameters, and returns a TFloat. The parameter names will seem weird at first, but once you get what I'm talking about, you'll find these names easy to use, believe me. Remember ratio and proportion from school?
Get the idea? This will give us co ordinates/sizes that scale perfectly. The loop itself is pretty simple. To calculate the u-v coords between 0 and 1, I have again made use of the HowMuch function like so......(I'm explaining this for u0. All the rest are almost the same)
For ImageWidth units, the current coordinate is i*fCharWidth. So, I get the result for 1 unit. This will give me a value between 0 and 1. Simple, isnt it? The rest of it is making the quad, and then translating it right by "space" units. We now have as many display lists as we have characters pn the texture. Let's take a look at the Print method.
     //Save the current drawing color...
     glPushAttrib(GL_ALL_ATTRIB_BITS);

     //Set the color we want to draw the font in
     glColor4fv(@fColor);

     pX:=HowMuch(RESOLUTION_REFX,X,fXRes);
     pY:=HowMuch(RESOLUTION_REFY,Y,fYRes);
     glTranslatef(pX,pY,0);

     glListBase(fbase-32);
     glCallLists(strlen(Text),GL_UNSIGNED_BYTE,Text);

     glPopAttrib;
For all the hype we made about it, IS kinda small, aint it? Now you see why display lists were a better choice. glCallLists takes and treats the entire string as a set of display list numbers, so that each character's ASCII value (minus our starting ASCII value which is 32) is a call to the corresponding display list. PHEW! That was easy! But there's another catch. Its not just enough if the size scales. So must the position. You DONT want text appearing at 500,400 in 320x240 resolution. So we make another couple of calls to HowMuch. Voila! Perfectly scaled and positioned text.
The example doesnt go into fullscreen mode. It does, however change the window size. The controls are 1, 2, 3, 4 for 320x240, 640x480, 800x600, 1024x768 respectively. Notice that the text stays at the same size with respect to the size of the window.

>>Download the tutorial source
You'll also need, in case you dont have them already
>>Download the OpenGL 1.2 headers