Write a text in OpenGl

Hi,
I’m learning OpenGL using LWJGL, but i have a litte question, and i’ve found no answer on google (but i might not search very well…).
How can i write a text in my Display screen ?
I want to add the number of the frame (not the FPS) and as i’m in fullscreen mode, i can’t take it in the title of the Window…no way i want to know how to write a text in opengl.

PS : Is the Window class of LWJGL removed in the lasts versions ? Because i see many docs where they used the org.lwjgl.opengl.Window class, and i don’t have it ???
And maybe, an other question : what’s the origin point in opengl (and so lwjgl) ? i believed that was the left bottom corner , but when i do :

 glVertex2f(2,-2);
glVertex2f(-2,-2);
glVertex2f(-2,2);
glVertex2f(2,2 );

The quad is drown at the top right corner…

Thanks for answering and sorry for my bad english (i’m a french man…).

Drawing text with LWJGL is difficult. You either have to texture map the characters on to triangles, or use drawPixels.

GLU has an easy way of writing text, but I don’t think it’s been ported to LWJGL.

The functionality of the Window class was mearged into the Display class a couple of versions back, and some docs havn’t been updated yet. All the same stuff is still there though.

The origin depends on how you set up your projection and modelview matrices. By default LWJGL sets up the screen with the origin at the bottom left, with one pixel per unit. You can easily change this via glOrtho or glPerspective though.

For simple text, one of the NeHe tutorials does text with display lists and glDrawPixels, it should be ported to LWJGL somewhere.

Thanks for your answers.

I initialize projection and co like that :

		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluOrtho2D(0, 800, 600, 0);
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();

But the origin corner is the top left one…

For the text, i’ve found the nehe tutorial, but it looks a bit difficult, no portable as he said, and he seems use a special API for Windows (wgl) and Java and the Jogl ports used AWT and Canvas.
But maybe, with de AWTGLCanvas, i can do something ? But it make me use AWT, and i want to be independant of it (taht’s why i use lwjgl instead of jogl).

shame on me, i didn’t see the LWJGL port. But it looks very very complexe for a simple text…
Will be an easier method implemented in the nexts LWJGL versions ?

Thats because the params are gluOrtho2d(left, right, bottom, top). So if you want the origin to be bottom-left you want (0, width, 0, height);

Text is fundamentally complicated, especially if you want nice crisp fonts and variable-width characters. In fact text in OpenGL is always going to be tricky, as font-rendering is almost always tied to a platform-specific API. I doubt you’ll see anything ‘simple’ appear in LWJGL as it would be somewhat outside the aims.

If you want another method of drawing text you could try Cas’ SPGL lib (on sourceforge), but be warned that it’ll likely not be any simpler and doesn’t exactly come with much documentation (although you might be able to find more details in the forums here).

Thansk, but in all little games in java, they used text in their opengl display, didn’t they ?
So, it’s possible, and it’s difficult to make a correct game without using text in it…
I’m trying to make a little class, based on the NeHe tutorial, but for the moment, that just doesn’t work…

I have some horribly complicated font generating and rendering code in the Shaven Puppy Game Library, which is just an open view on my work so there’s no downloads and it’s generally unstable.

It’s at sourceforge: http://www.sourceforge.net/projects/spgl

I preprocess normal Java fonts and write them into .jgfont files, which contain a square luminance-alpha bitmap of all the glyphs in the font packed tightly, and a whole load of kerning information. The class in charge of the conversion is in the spgl-tools project, and it’s com.shavenpuppy.jglib.tools.FontConverter. It creates com.shavenpuppy.jglib.Font objects. Then I load these into com.shavenpuppy.jglib.opengl.GLFont objects, which creates a GLTexture out of the bitmap; and then I use GLTextAreas and GLStrings in the same package to do rendering.

Cas :slight_smile:

Some ideas (None of these are easy)

Method 1

  1. Use AWT to draw the desired text onto a BufferedImage.
  2. Get the bytes from the BufferedImage and use them as an OpenGL Texture
  3. Draw a Textured Quad using OpenGL

Method 2

  1. Use a paint program to draw all the letters into a .png graphics file. (A fixed width font like Courier is easiest)
  2. Read the graphics file into a BufferedImage
  3. Get the bytes from the BufferedImage and use them as an OpenGL Texture
  4. Draw a series of Quads, with the texture coordinates set so each Quad displays the correct letter

Method 3 (I’ve used this one)

  1. Use a paint program to draw all the letters into a .png graphics file. (A fixed width font like Courier is easiest)
  2. Write a java program to convert it into a series of NxN two colour bitmaps with the width a multiple of 8
  3. Use glBitmap() (easy) or glDrawPixels() (harder) to paint each letter on the screen as required.

Thanks, the last seems to be the best solution (for me, because i don’t want to use AWT) and makes Quads to draw letters, i suppose that’s consum lots of CPU for anything…

But how did oddlabs in tribal trouble to make text with LWJGL ?

i suppose that’s consum lots of CPU for anything…

No, it doesn’t. Its actually about as fast as it can get… that is as long as you dont use intermediate mode. One texture bind and then a call list per line of text.

If you do something similar in Java2D its also way faster than drawString.

Drawing a fart of polys with one texture isn’t much work for your graphics card.

selfquote

“The speed seems to be ok. I get around 100 fps if I fill the whole screen with 4368 characters (that’s 48lines with 91 characters each… all cramped together like the red/right stuff [line height 10]). Oh and my machine has only 500mhz and a gf2mx… so, yea… it should be fast enough ;)”

4368 characters is a lot of text. (Don’t forget that there are games which are <=4096 bytes.)

edit: Oh and I used vertex arrays instead of display lists because of rather odd circumstances. Well, with display lists its most likely faster.

Thanks for the reply, but , can i have your little code to display caracteres please ?

If you don’t want to use AWT, that means you can’t use BufferedImage. So… You need to make a separate program (which does use AWT) to read in the .png with the characterset. You then get the underlying bytes and save them to a file.

You AWTless program, then directly reads the bytes from the file and puts them into a texture or a bitmap. The textured Quad solution looks tempting, however I do have some code for glDrawPixels. It will slow your game down if you write lots of letters.

Here is part of my code to convert a .png file to a set of font characters. Note that this code misses out characters that aren’t used in the game to save space


    /** Include font support for all characters in this string */
    public void includeFont(String s) {
        for (int i=0; i< s.length(); i++) {
            char c = s.charAt(i);
            if (c>='.' && c<='9') c -='.';
            else if (c>='A' && c<='Z') c -='A'-12;
            else if (c>='a' && c<='z') c -='a'-38;
            else continue;
            font[(int)c] = true;
        }
    }

    /** Add 64 character, 16x22 font to the data file */
    public void convertFont(String filename) {
        try {
            // Get the font graphics file
            System.out.println(" "+filename);
            is = new FileInputStream(filename);
            BufferedImage img = ImageIO.read(is);
            // Construct 64 bit header
            // Bit 0 is set if char 0 is present in file etc.
            long f = 0;
            for (int i=63; i>=0; i--)
                if (font[i])
                    f=(f<<1) | 1;
                else
                    f<<=1;
            // Write header as a 'long'
            for (int i=7; i>=0; i--)
                os.write( (int)((f>>(i*8)) & 0xff) );
            for (int c=0; c<64; c++)
                if (font[c])
                    for (int y=0; y<22; y++)
                        for (int x=0; x<2; x++) {
                            int bits = 0;
                            for (int b=0; b<8; b++) {
                                int colour = img.getRGB(16*c+8*x+b, 21-y) & 0xffffff;
                                int mask = (colour==0)?1:0;
                                bits = bits*2 + mask;
                            }
                            os.write(bits);
                        }
            is.close();
        } catch (Exception e) {e.printStackTrace(); };
        
    }

Now we need to open the data file (not shown) & stuff the data into a series of ByteBuffers. Of course you could just stick it in one texture & use texture coordinates to select the letter for each Quad, which is probably faster (but I have no code for it)


    // Bitmap data
    public ByteBuffer[] fontData = new ByteBuffer[64]; // Font data


            // Load Font data
            long font = is.readLong(); // 64 bits indicating which chars
            for (int c=0; c<64;c++) {  // are defined
                fontData[c] = BufferUtils.createByteBuffer(44);
                for (int i=0; i<44; i++)
                    if ((font & 1) == 1)          // Define char
                        fontData[c].put(is.readByte());
                    else
                        fontData[c].put((byte)0); // Space if no definition
                fontData[c].rewind();
                font>>=1;
            }


    /** Print text string at x, y at specified size */
    public void print(int x, int y, int size, String s) {
        GL11.glPixelZoom(size, size);
        for (int i=0; i<s.length(); i++) {
            char c = s.charAt(i);
            if (c>='.' && c<='9') c -='.';
            else if (c>='A' && c<='Z') c -='A'-12;
            else if (c>='a' && c<='z') c -='a'-38;
            else continue;
            GL11.glRasterPos2i(x+size*16*i, y);            
            GL11.glDrawPixels(16, 22, GL11.GL_COLOR_INDEX,
                                      GL11.GL_BITMAP, fontData[(int)c]);
        }
    }

BTW. You need a fair bit of setup (not shown) to use glDrawPixels, so as to make the background transparent. glBitmap() is easier as the background is automatically transparent, but you can’t scale the letters, nor can you do multi-colour (or anti-aliased) letters.

http://cvs.sourceforge.net/viewcvs.py/fluffware/fluffware_ant/src/org/fluffware/game/VAFont.java?rev=1.3&view=log

See also:

http://cvs.sourceforge.net/viewcvs.py/fluffware/fluffware_ant/src/org/fluffware/media/OGLImage.java?rev=1.1&view=log

Its pretty much used like…

VAFont va;
[...]
va=new VAFont(someTexture,VAFont.NEHE,7);
va.setLineHeight(12);
[...]
va.print("foo",x,y);

But maybe you should start with a simpler version.