PDA

View Full Version : Text with OpenGL



IlovePascal
10-02-2007, 08:37 AM
Hello, me again with OpenGL questions...

I have learned about drawing objects but not text.
-How do we 'write' with OpenGL?
-Is it always in front of everything else, or can we give it a depth?
-Is it possible to make text become a shape (with 3D properties), so that it can be part of the environment?

-Got any samples? (it's always the easiest way to learn!)

Cheers :wink:

WILL
10-02-2007, 08:51 AM
Well do you know how to draw textured quads in Ortho mode? if so then you can make a rather simple raster font for use in your programs.

I use this technique and it works quite well. If it wasn't so late I'd post some code. :? Perhaps if noone does better tomorrow I'll do just that. ;)

The concept is simple though. Just define a string with all the ASCII/text characters you require to write with then create your texture with those characters in the SAME ORDER.

Then create your DrawText() function and in each character of this string match it up with your font's 'character set' string and use that index to raw from your font's texture image.

Thats basically the gist of it. It's late or I'd probably have explained it a bit nicer. :P

I'll post code for my method sometime tomorrow if you are interested still.

JSoftware
10-02-2007, 12:06 PM
Will is right. The easiest way is to use a bitmap font generator. I use Nitrogen's which you can read about here: http://www.pascalgamedevelopment.com/viewtopic.php?t=3982&postdays=0&postorder=asc&start=0

If you want to have outlines, eg real 3d text, (and if you use windows...) then go here:
http://support.microsoft.com/kb/131024

grudzio
10-02-2007, 12:21 PM
If you look for samples, check tutorials on NeHe (http://nehe.gamedev.net) site.

IlovePascal
11-02-2007, 12:22 AM
>Wow! Nehe's site is fantastic! So many tutorials and so many explanations! It's a shame it's C++! lol
I know they have the Delphi versions to download, but I don't have Delphi...
Would they work with Dev-Pascal or Lazarus?

>I downloaded Nitrogen's FontStudio, but as I said, I don't know if i'll manage to get it to work with Dev-Pascal or Lazarus.

>And Will, I will always be interested in a few samples! if you've got anything handy that shows like how to change fonts, colours, size, position, etc, post it!
Oh, and I have always had difficulties with understanding texturing so, no, I don't know how to do that, but Im gonna read Nehe's tutorials and see if I get the hang of it!

Thanks a lot, ;)

cairnswm
12-02-2007, 10:25 AM
The idea of bitmap fonts is as follows.

Create an image that contains all the characters you want. Typically I use an image that has 16 rows of 16 characters.

Load the image as though it was a standard 2D texture you were going to use to draw on the screen.

In memory list all the characters in the image in an array - or use the full 256 ASCII chars. When you want to draw/write a character to the screen, search for it in the array and just draw that section of the image.

Within my S2DL library is a font class called TS2DLNeheFont which is a conversion of the Nehe font method (and uses the same set of chars). Download my S2DL libraries from here http://www.cairnsgames.co.za/files/s2dlv1-10.zip

WILL
12-02-2007, 03:59 PM
Hey sorry for the delay, but I've had quite the busy week last week and really needed the R&R so...

Here is the entire function that I use for a simple raster (http://en.wikipedia.org/wiki/Raster_graphics) font.

procedure TFont.DrawText(X, Y: Integer; Text: String);
var
i: Integer;
xChar: Integer;
SrcRect, DestRect: TSDL_Rect;
UVRateX, UVRateY: Real;
begin
if (Length(Text) > 0) then
begin
for i := 0 to Length(Text) - 1 do
begin
// Find Character tile
xChar := GetCharNum(Text[i + 1]) - 1;
if (Text[i + 1] <> ' ') then
begin
glEnable(GL_TEXTURE_2D); //Enable Texturing
glBindTexture(GL_TEXTURE_2D, FontData.ID); //Bind Texture

glDisable(GL_BLEND);

glColor3f(1, 1, 1); //Make sure color is white (Normal)

BeginOrtho; //Switch to 2D mode
glPushMatrix; //Save Current Matrix
glTranslatef(X + (i * CharWidth), Y, 0); //Move to Objects location

UVRateX := CharWidth / FontData.Width;
UVRateY := CharHeight / FontData.Height;

glBegin(GL_QUADS);
//Top-Left
glTexCoord2f((UVRateX * xChar), UVRateY * 0);
glVertex2f(0, 0);
//Top-Right
glTexCoord2f((UVRateX * xChar) + UVRateX, UVRateY * 0);
glVertex2f(CharWidth, 0);
//Bottom-Right
glTexCoord2f((UVRateX * xChar) + UVRateX, (UVRateY * 0) + UVRateY);
glVertex2f(CharWidth, CharHeight);
//Bottom-Left
glTexCoord2f((UVRateX * xChar), (UVRateY * 0) + UVRateY);
glVertex2f(0, CharHeight);
glEnd;
glPopMatrix; //Reload Old Matrix
EndOrtho;
end;
end;
end;
end;

As you can see it's just a series of textured quads being drawn, once per text character. Spaces are skipped and if you decided to add it, other forms of white space could also be treated in the same way.

FontData is just the container I made for all my textures which the Width, Height is stored at the time of loading into texture memory and ID is the GLUint reference handler.

function GetCharNum(ch: Char): Integer;
...is another function I made that returns the index of the character specified (ch) based on the order of the characters in your texture.

ie...

const
CharSet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!?.,:;"' + #39 + #92 + '/|+-=_@#$%^&*()' + #123 + #125 + '[]<>';

The rest I'm sure you can put together as it would just be a simple matter of creating your own TFont object structure and listing the needed characters into an array and returning the proper index of that character in the texture.

IlovePascal
12-02-2007, 11:55 PM
>Well, M. Cairns, I will certainly have a look at your libraries, as I read a lot about them in other forums and you seem to specialize in 2D!

>Will, thanks a lot for the sample. :wink: I find it very frustrating when I read a whole bunch of theory but then when I try it out myself I get stuck! A sample has the advantage of the certainty it works! :lol: Now I jst need to understand it fully and play around with it, and i'll be that much more knowledgeable! lol

There are just a few things I am not FULLY sure about:
-Why do you use 'glPushMatrix' after changing to ortho mode? If i'm using perspective, shouldn't I save the current matrix before switching modes? :scratch:
- What does 'glTexCoord2f' do? and why do you write 'UVRateY * 0'? Isn't that always 0? :D
- How do u assign fontdata.id?
And not as important:
-CharWidth and CharHeight are constants depending on the size of the image you use, right? :)
-Don't you use the variables 'SrcRect, DestRect: TSDL_Rect;' that you define? :roll:

Well, thanks a lot, and don't worry about the last few questions if you don't have much time!
Cheers :wink:

IlovePascal
14-02-2007, 01:08 AM
Oh, and also, I just tried your code, it seems like the compiler can't find BeginOrtho and EndOrtho... are they your own procedures?

WILL
14-02-2007, 04:39 PM
Well first, the Push/Pop is to store your current matrix so that after drawing your text you can return to drawing whatever else is on your screen in whatever perspective you were drawing it in.

BeginOrtho and EndOrtho are just little procedures I made up to switch Ortho mode on and off. This is the mode that allows you to draw in 2D.

Don't worry about (UVRateY * 0) so much. It does come out to 0, but if you wanted to draw text in different ways or go multi-line then I'd replace the 0 with another value. For single line text purposes however, you can substitute it with 0 if you're worried about the extra calculation.

TFont is my own font class I made, I recommend making your own as well. Within the object that I made, I use a record that I made for all my textures, TTexture. It has 3 simple values that it holds...

type
TTexture = Record
Width,
Height : Integer;
ID: GluInt;
end;

So I store this texture data within TFont.FontData. TFont.FontData.ID contains the handler for the texture index you get back from creating your texture with glGenTextures().

Since I use SDL to assist with loading of textures from PNG files and the like, the way I grab width and height values will differ so I'll not get into that.

The other font related thing is the character's height and width. If you are using a raster font you have to know the width and height of each of your characters. The easiest way is to have ALL your characters have the same dimensions. Easy to calculate and easy to work with. Though if you need to have different widths to make the font look good, this is not an absolute rule to follow, just makes the function more complicated. :)

If you use my fixed character size method I use here then know the size for each character and store this value as I have in CharWidth and CharHeight.


To get a better and further understanding of OpenGL I strongly recommend you go and check out the 'Red Book' over at OpenGL.org: http://www.opengl.org/documentation/red_book/