Graphic Delphi

Title: OpenGL VII: use of Bitmap fonts
Question: I've been writing articles on how to draw stuff on the screen
but we were missing something very important... text!;
this time I show you how to show text using bitmap fonts
so, finally here it is
here's a screen shot of this demo
Answer:
Previous article on OpenGL :
OpenGL VI: using Alpha Blending (Transparency) - updated
Finally! you get to show some text on the screen (First actual "Hello World" OpenGL application)
I have shown you how to create basic shapes on the screen, move them around, color them, use Textures, alpha blending, etc
now, you are ready to create that cool game (yeah, right) which of course will have some text to display stats
There are a few ways to show text on the screen, here I will show you a simple way of displaying any font on your
OpenGL application
Along with the use of bitmap fonts, this also includes the use of Display Lists, which basically are a way
to pre-draw stuff that you can use later (optimization =o) ), think of it like an "array" where instead of values you
have pre-drawn images you can use anytime.
I will write one separated article specifically for Display Lists, they are pretty cool,
after you learn them you will want to do everything using them.
In this case, each item of the display list will hold one character and we will create a function that just calls
the list for each character we want to print.
ok, for this demo I'm going to read the strings off of an array, but nothing should stop you from loading a text file
(like in a TStringList) and displaying the entire file, or any way of getting text
for this Demo I have this array of strings that I will show:
first we declare a variable which will serve as base for our Display List
var
base:GLuint; // Base Display List For The Font Set

These other variables are to change the text that will be displayed
CurrIndex:Byte=0; // To Navigate trough the array
FCount:Integer; // To change the array item being displayed
PosX, PosY:GLfloat; // for positioning

then the array of strings
const
myStrings:array[0..4] of string=('Hello World!', 'Delphi3000', 'bitmap fonts', 'Demo', 'Delphi3000 bitmap fonts demo');
we then proceed to the routine that will create our font
procedure BuildFont(Const FontName:String; Const Height:Integer=-24; Width:Integer=0;
Italic:Integer=0; Underline:Integer=0; Strikeout:Integer=0);
var font, oldfont: HFONT; // Windows Font ID
begin
base := glGenLists(96); // Storage For 96 Characters
//the rest is all WINDOWS calls
font := CreateFont(Height, // Height Of Font
Width, // Width Of Font
0, // Angle Of Escapement
0, // Orientation Angle
FW_BOLD, // Font Weight
Italic, // Italic
Underline, // Underline
Strikeout, // Strikeout
ANSI_CHARSET, // Character Set Identifier
OUT_TT_PRECIS, // Output Precision
CLIP_DEFAULT_PRECIS, // Clipping Precision
ANTIALIASED_QUALITY, // Output Quality
FF_DONTCARE or DEFAULT_PITCH,// Family And Pitch
PChar(FontName)); // Font Name
oldfont:=SelectObject(h_DC, font); // Selects The previous font
wglUseFontBitmaps(h_DC, 32, 96, base);// Builds 96 Characters Starting At Character 32
SelectObject(h_DC, oldfont); // Put back the old font
DeleteObject(font) // Delete the font we just created (we already have it in the Display list)
end;
basically, we create the List with glGenLists, we then call the CreateFont, this is a Windows function
this is not OpenGL, but we use it here to create our font
then the next lines are kinda tricky, the line
oldfont:=SelectObject(h_DC, font); // Selects The previous font
would seem to return a pointer (or assign or something) to the font we just created, but what this really does is
it selects an object (our font) into a specified device context, if the function succeeds it returns the handle
of the object being replaced so now it makes sense, it does 2 things at once, we set the new and save the old font
wglUseFontBitmaps(h_DC, 32, 96, base);// Builds 96 Characters Starting At Character 32
populates our list with all the characters and the specified font, note the "starting at character 32"
SelectObject(h_DC, oldfont); // Put back the old font
now we just set the old font, we don't care about the handle of the replaced object
DeleteObject(font) // Delete the font we just created (we already have it in the Display list)
This deletes the font, we already have it in our Display List, remember that a Display list keeps an "array of images"
so now all the characters were basically converted to images we can now use
note: Please see the Delphi Help for all the functions from CreateFont to DeleteObject
We now need a function to do some cleanup and delete the Display List, the function is pretty simple
procedure KillFont; // Delete The Font
begin
glDeleteLists(base, 96); // Delete the 96 Characters
end;
...pretty self explanatory I think, we will call this function in our "standard" KillGLWindow... you have been reading the other
articles, right?
procedure KillGLWindow;
{ Properly Kill the Window }
begin
KillFont;
...blabla, restore everything

Ok, we're almost there, we have the font, we have the cleanup code... oh yes, we now need to use the font =o)
Now we will create a routine you can just call and print some text
procedure glPrint(text : pchar); // Custom GL "Print" Routine
begin
if (text = '') then // If There's No Text
Exit; // just exit
glPushAttrib(GL_LIST_BIT); // Pushes The Display List Bits
glListBase(base - 32); // Sets The Base Character to 32
glCallLists(length(text), GL_UNSIGNED_BYTE, text); // Draws The Display List Text
glPopAttrib() // Pops The Display List Bits
end;
The function only has one parameter, our text.
In the function,
- we check if the parameter is empty, we just exit the function
- We then push the GL_LIST_BIT attribute, this prevents glListBase from affecting any other display lists we may be using in our program
- The glListBase is kinda saying "change the starting point", because we didn't create a list with the 256 characters, instead we started
at character 32, so we have to tell it that the character 32 is actually 0 (that's why the -32)
basically our table begins at 32, not at 0 as the ASCII code, if we had the characters from char 0
we wouldn't have to call this function, so without this OpenGL would look for 'A' at position 65, but we want it to look at
position 65-32... hope I didn't confuse you here
- OpenGL knows now where to look for the characters, so now we call glCallLists, this function does a lot of things
it renders all the characters to the screen at "once" (you don't have to call a for loop to do the job)
it also knows how to draw each character next to each other, instead of every char on top of each other
We pass the number of characters we will send: length(text)
We then tell it what the largest list number would be, in this case in won't be any larger than 255, and finally
We pass a pointer to the string we want to display (yes a pointer... we're using PChars, and that's what they are, pointers)
- The final step is to pop the GL_LIST_BIT back to what it was before this
our InitGL simply adds the BuildFont routine
{ All Setup For OpenGL Goes Here }
function InitGL(Const Width, Height: Glsizei): Bool;
var
fWidth, fHeight: GLfloat;
begin
glClearColor(0.0, 0.0, 0.5, 0.0);//Blue Background
glClearDepth(1.0); //Depth Buffer Setup
glEnable(GL_DEPTH_TEST); //Enables Depth Testing
glDepthFunc(GL_LEQUAL); //Text
glShadeModel(GL_SMOOTH); //Enables Smooth Color Shading
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
BuildFont('Times New Roman', 16, 0); //another
FCount:=0;
PosX:=0.0;
PosY:=1.0
end;
and now that we have everything, we just need to use it
function DrawGLScene(): Bool; { All Rendering Done Here }
begin
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); //Clear Screen and Depth Buffer
glLoadIdentity(); //Reset The View (move to 0, 0, 0)
glTranslatef(0, 0, -3); //move 3 units into the screen
glRasterPos2f(1.0, 1.0); //This function is to position the text!!!
glColor3f(0.85, 0.85, 0.85); //change the color
glPrint(PChar(FormatDateTime('MM/DD/YYYY HH:NN:SS', Now))); //print date-time
glRasterPos2f(-0.45+cos(PosX), sin(PosY)); //Position the text (bounce around)
glColor3f(1.0, 1.0, 0.0); //change color
glPrint(PChar(myStrings[CurrIndex])); //print our text
//The rest is to change the string that is being displayed and it's position
inc(FCount);
if (FCount80) then //every 80 calls to this function, change the index
begin
FCount:=0;
CurrIndex:=(CurrIndex+1) mod 4;
end;
if ((FCount mod 5)=0) then //change the position of the text every 5 frames
begin
PosX:=PosX+0.003;
PosY:=PosY+0.004;
end;
Result:=True
end;
The only new function here is glRasterPos2f which will position our text on the screen, The center of the screen
is still 0, 0, but this time there is no Z position, Bitmap fonts are only positioned in X, Y axis
glPrint is the function we created above to print our text, so we just call it with whatever text and voila!
one important thing to note here is the use of glTranslatef before the glRasterPos2f, doesn't the glRasterPos2f
do the job by itself?... kinda... what happens when you call the glTranslatef function is kinda defines how near the
screen is (with the Z value) and therefore if the screen is really close to you, you'll have to use the glRasterPos2f function
with small numbers to move around, if the screen is farther you could use larger numbers
more specifically, if you move 1 unit into the screen glTranslatef(0.0, 0.0, -1.0) you could place the text within -0.5 to 0.5
on the X axis, but if you move 10 units into the screen glTranslatef(0.0, 0.0, -10.0) you could place the text within -5.0 to 5.0
make sense?
That's it for now
you can download the code for the article here:
I strongly encourage you to play with the positions, colors, etc
you can find the GLAux unit and dll here:
keep checking here for future articles on OpenGL
keep up coding salu2 EberSys