Mega Code Archive

 
Categories / Delphi / Graphic
 

OpenGL VII use of Bitmap fonts

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