NeHe Productions: OpenGL Lesson #14Date de publication : 31/03/2006 , Date de mise à jour : 31/03/2006
Par
Jeff Molofee ( NeHe ) (Autres articles)
Lesson: 14 This tutorial is a sequel to the last tutorial. In tutorial 13 I taught you how to use Bitmap
Fonts. In this tutorial I'll teach you how to use Outline Fonts.
The way we create Outline fonts is fairly similar to the way we made the Bitmap font in lesson 13.
However... Outline fonts are about 100 times more cool! You can size Outline fonts. Outline
font's can move around the screen in 3D, and outline fonts can have thickness! No more flat 2D
characters. With Outline fonts, you can turn any font installed on your computer into a 3D
font for OpenGL, complete with proper normals so the characters light up really nice when light
shines on them.
A small note, this code is Windows specific. It uses the wgl functions of Windows to build the
font. Apparently Apple has agl support that should do the same thing, and X has glx.
Unfortunately I can't guarantee this code is portable. If anyone has platform independant code
to draw fonts to the screen, send it my way and I'll write another font tutorial.
We start off with the typical code from lesson 1. We'll be adding the stdio.h header file for standard
input/output operations; the stdarg.h header file to parse the text and convert variables to text,
and finally the math.h header file so we can move the text around the screen using SIN and COS.
We're going to add 2 new variables. base will hold the number of the first display
list we create. Each character requires it's own display list. The character 'A' is 65 in
the display list, 'B' is 66, 'C' is 67, etc. So 'A' would be stored in display list base+65.
Next we add a variable called rot. rot will be used to spin the text around on the screen
using both SIN and COS. It will also be used to pulse the colors.
GLYPHMETRICSFLOAT gmf[256] will hold information about the placement and orientation for each of
our 256 outline font display lists. We select a letter by using gmf[num]. num is
the number of the display list we want to know something about. Later in the code I'll show you
how to find out the width of each character so that you can automatically center the text on the
screen. Keep in mind that each character can be a different width. glyphmetrics will make our
lives a whole lot easier.
The following section of code builds the actual font similar to the way we made our Bitmap font.
Just like in lesson 13, this section of code was the hardest part for me to figure out.
'HFONT font' will hold our Windows font ID.
Next we define base. We do this by creating a group of 256 display lists using glGenLists(256).
After the display lists are created, the variable base will hold the number of the first list.
More fun stuff. We're going to create our Outline font. We start off by specifying the size
of the font. You'll notice it's a negative number. By putting a minus, we're telling windows
to find us a font based on the CHARACTER height. If we use a positive number we match the font
based on the CELL height.
Then we specify the cell width. You'll notice I have it set to 0. By setting values to 0,
windows will use the default value. You can play around with this value if you want. Make
the font wide, etc.
Angle of Escapement will rotate the font. Orientation Angle quoted from MSDN help Specifies
the angle, in tenths of degrees, between each character's base line and the x-axis of the
device. Unfortunately I have no idea what that means :(
Font weight is a great parameter. You can put a number from 0 - 1000 or you can use one of
the predefined values. FW_DONTCARE is 0, FW_NORMAL is 400, FW_BOLD is 700 and FW_BLACK is 900.
There are alot more predefined values, but those 4 give some good variety. The higher the
value, the thicker the font (more bold).
Italic, Underline and Strikeout can be either TRUE or FALSE. Basically if underline is TRUE,
the font will be underlined. If it's FALSE it wont be. Pretty simple :)
Character set Identifier describes the type of Character set you wish to use. There are too
many types to explain. CHINESEBIG5_CHARSET, GREEK_CHARSET, RUSSIAN_CHARSET, DEFAULT_CHARSET,
etc. ANSI is the one I use, although DEFAULT would probably work just as well.
If you're interested in using a font such as Webdings or Wingdings, you need to use
SYMBOL_CHARSET instead of ANSI_CHARSET.
Output Precision is very important. It tells Windows what type of character set to use if
there is more than one type available. OUT_TT_PRECIS tells Windows that if there is more than
one type of font to choose from with the same name, select the TRUETYPE version of the font.
Truetype fonts always look better, especially when you make them large. You can also use
OUT_TT_ONLY_PRECIS, which ALWAYS trys to use a TRUETYPE Font.
Clipping Precision is the type of clipping to do on the font if it goes outside the clipping
region. Not much to say about this, just leave it set to default.
Output Quality is very important.you can have PROOF, DRAFT, NONANTIALIASED, DEFAULT or
ANTIALIASED. We all know that ANTIALIASED fonts look good :) Antialiasing a font is the
same effect you get when you turn on font smoothing in Windows. It makes everything look less
jagged.
Next we have the Family and Pitch settings. For pitch you can have DEFAULT_PITCH,
FIXED_PITCH and VARIABLE_PITCH, and for family you can have FF_DECORATIVE, FF_MODERN,
FF_ROMAN, FF_SCRIPT, FF_SWISS, FF_DONTCARE. Play around with them to find out what they do.
I just set them both to default.
Finally... We have the actual name of the font. Boot up Microsoft Word or some other text
editor. Click on the font drop down menu, and find a font you like. To use the font,
replace 'Comic Sans MS' with the name of the font you'd rather use.
Now we select the font by relating it to our DC.
Now for the new code. We build our Outline font using a new command wglUseFontOutlines. We
select our DC, the starting character, the number of characters to create and the 'base'
display list value. All very similar to the way we built our Bitmap font.
That's not all however. We then set the deviation level. The closer to 0.0f, the smooth the
font will look. After we set the deviation, we get to set the font thickness. This describes
how thick the font is on the Z axis. 0.0f will produce a flat 2D looking font and 1.0f will
produce a font with some depth.
The parameter WGL_FONT_POLYGONS tells OpenGL to create a solid font using polygons. If we use
WGL_FONT_LINES instead, the font will be wireframe (made of lines). It's also important to note
that if you use GL_FONT_LINES, normals will not be generated so lighting will not work properly.
The last parameter gmf points to the address buffer for the display list data.
The following code is pretty simple. It deletes the 256 display lists from memory starting at
the first list specified by base. I'm not sure if Windows would do this for you, but it's
better to be safe than sorry :)
Now for my handy dandy GL text routine. You call this section of code with the command
glPrint("message goes here"). Exactly the same way you drew Bitmap fonts to the screen in
lesson 13. The text is stored in the char string fmt.
The first line below sets up a variable called length. We'll use this variable to find out
how our string of text is. The second line creates storage space for a 256 character string.
text is the string we will end up printing to the screen. The third line creates a pointer
that points to the list of arguments we pass along with the string. If we send any variables
along with the text, this pointer will point to them.
The next two lines of code check to see if there's anything to display? If there's no text, fmt
will equal nothing (NULL), and nothing will be drawn to the screen.
The following three lines of code convert any symbols in the text to the actual numbers the symbols represent.
The final text and any converted symbols are then stored in the character string called "text". I'll explain
symbols in more detail down below.
Thanks to Jim Williams for suggesting the code below. I was centering the text manually. His method
works alot better :)
We start off by making a loop that goes through all the text character by character.
strlen(text) gives us the length of our text. After we've set up the loop, we will
increase the value of length by the width of each
character. When we are done the value stored in length will be the width of our entire
string. So if we were printing "hello" and by some fluke each character was exactly 10 units wide,
we'd increase the value of length by the width of the first letter 10. Then we'd check the
width of the second letter. The width would also be 10, so length would become 10+10 (20).
By the time we were done checking all 5 letters length would equal 50 (5*10).
The code that gives us the width of each character is gmf[text[loop]].gmfCellIncX. Remember that
gmf stores information out each display list. If loop is equal to 0 text[loop] will
be the first character in our string. If loop is equal to 1 text[loop] will be the
second character in our string. gmfCellIncX tells us how wide the selected character is.
gmfCellIncX is actually the distance that our display moves to the right after the character has
been drawn so that each character isn't drawn on top of eachother. Just so happens that distance
is our width :) You can also find out the character height with the command gmfCellIncY. This
might come in handy if you're drawing text vertically on the screen instead of horizontally.
NOTE: Curt Werline adds - When you calculate the dimensions of the text string, you are using gmfCellIncX for
the width and you suggest using gmfCellIncY for the height. These values are offsets and not true dimensions.
To get the true dimensions you should use gmfBlackBoxX and gmfBlackBoxY.
Finally we take the length that we calculate and make it a negative number (because we have to
move left of center to center our text). We then divide the length by 2. We don't want all the
text to move left of center, just half the text!
We then push the GL_LIST_BIT, this prevents glListBase from affecting any other display lists we may be using
in our program.
The command glListBase(base) tells OpenGL where to find the proper display list for each
character.
Now that OpenGL knows where the characters are located, we can tell it to write the text to the
screen. glCallLists writes the entire string of text to the screen at once by making multiple
display list calls for you.
The line below does the following. First it tells OpenGL we're going to be displaying lists
to the screen. strlen(text) finds out how many letters we're going to send to the screen.
Next it needs to know what the largest list number were sending to it is going to be. We're
still not sending any more than 255 characters. So we can use an UNSIGNED_BYTE. (a byte
represents a number from 0 - 255 which is exactly what we need). Finally we tell it what to
display by passing the string text.
In case you're wondering why the letters don't pile on top of eachother. Each display list
for each character knows where the right side of the character is. After the letter is drawn
to the screen, OpenGL translates to the right side of the drawn letter. The next letter or
object drawn will be drawn starting at the last location GL translated to, which is to the
right of the last letter.
Finally we pop the GL_LIST_BIT setting GL back to how it was before we set our base setting
using glListBase(base).
Resizing code is exactly the same as the code in Lesson 1 so we'll skip over it.
There are a few new lines at the end of the InitGL code. The line BuildFont() from lesson 13
is still there, along with new code to do quick and dirty lighting. Light0 is predefined on most
video cards and will light up the scene nicely with no effort on my part :)
I've also added the command glEnable(GL_Color_Material). Because the characters are 3D objects
you need to enable Material Coloring, otherwise changing the color with glColor3f(r,g,b) will
not change the color of the text. If you're drawing shapes of your own to the screen while you
write text enable material coloring before you write the text, and disable it after you've
drawn the text, otherwise all the object on your screen will be colored.
Now for the drawing code. We start off by clearing the screen and the depth buffer. We call
glLoadIdentity() to reset everything. Then we translate ten units into the screen. Outline
fonts look great in perspective mode. The further into the screen you translate, the smaller
the font becomes. The closer you translate, the larger the font becomes.
Outline fonts can also be manipulated by using the glScalef(x,y,z) command. If you want the
font 2 times taller, use glScalef(1.0f,2.0f,1.0f). the 2.0f is on the y axis, which tells OpenGL
to draw the list twice as tall. If the 2.0f was on the x axis, the character would be twice
as wide.
After we've translated into the screen, we want the text to spin. The next 3 lines rotate the
screen on all three axes. I multiply rot by different numbers to make each rotation happen at
a different speed.
Now for the crazy color cycling. As usual, I make use of the only variable that counts up (rot).
The colors pulse up and down using COS and SIN. I divide the value of rot by different numbers
so that each color isn't increasing at the same speed. The final results are nice.
My favorite part... Writing the text to the screen. I've used the same command we used to write
Bitmap fonts to the screen. All you have to do to write the text to the screen is glPrint("{any
text you want}"). It's that easy!
In the code below we'll print NeHe, a space, a dash, a space, and then whatever number is stored
in rot divided by 50 (to slow down the counter a bit). If the number is larger that 999.99
the 4th digit to the left will be cut off (we're requesting only 3 digits to the left of the
decimal place). Only 2 digits will be displayed after the decimal place.
Then we increase the rotation variable so the colors pulse and the text spins.
The last thing to do is add KillFont() to the end of KillGLWindow() just like I'm showing below. It's important to
add this line. It cleans things up before we exit our program.
At the end of this tutorial you should be able to use Outline Fonts in your own OpenGL projects.
Just like lesson 13, I've searched the net looking for a tutorial similar to this one, and have
found nothing. Could my site be the first to cover this topic in great detail while explaining
everything in easy to understand C code? Enjoy the tutorial, and happy coding!
Jeff Molofee ( NeHe )
|
Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2006 Nehe Gamedev.net. Aucune reproduction, même partielle, ne peut être faite de ce site ni de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.