Text trouble

Started by jam007, January 16, 2005, 15:12:44

Previous topic - Next topic

jam007

Hi IÃ,´m rather new to OpenGL and LWJGL and trying to make a 3D-iceboat game. I want to have some text displayed (speed and similar things) HUD-like.
I have managed to make a working 3d representation of the iceboat and sourroundings. I have now made a version of Jeff Molofee et al font builder and can print a "floating" text in the 3D world but...
As soon as I builds the font durini initialation (not even displays it) the rest of my world disappears. The display only shows the text and the clear color.
I guess there is some problem with blending but as IÃ,´m new to OpenGl I do not know where to begin...
What OpenGL settings must I have to have both text and the rest of the 3d-world rendered?

Thankful for any advice

Anders

Here is the code of "my" font class:
/*
 * OpenGLFont.java
 * Created on den 16 januari 2005, 11:09
 * This Code is adapted from code created By Jeff Molofee 2000
 * Modified by Shawn T. to handle (%3.2f, num) parameters and
 * Fredric Echols. nehe.gamedev.net
 *  Use
 *  GL11.glEnable(GL11.GL_TEXTURE_2D); // Enable Texture Mapping
 *  to enable text and turn off lighing during text rendering
 *  
 */

package iceboatgame;

import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.glu.GLU;

/* Creates an Font for use in OpenGL and a routine to print it.
 */

public class OpenGLFont {
    
        public int texture, base;
        private float size;
        private String fontname;

    //build colours for font with alpha transparency
    private static final Color OPAQUE_WHITE = new Color(0xFFFFFFFF, true);
    private static final Color TRANSPARENT_BLACK = new Color(0x00000000, true);
    
    /** Creates a new instance of OpenGLFont 
     *  fontname="Courier New" and size =0.045 is a good choice
     */
    public OpenGLFont(String fontname, float size) {
        this.size=size;
        this.fontname=fontname;
        buildFont();
    }
    
    // Prints a single line of text
     public static void glPrint(OpenGLFont font, String msg) {                                      // Custom GL "Print" Routine
        if(msg != null) {
            GL11.glBindTexture(GL11.GL_TEXTURE_2D, font.texture);
            for(int i=0;i<msg.length();i++) {
                GL11.glCallList(font.base + msg.charAt(i));
                GL11.glTranslatef(font.size*1.1f, 0.0f, 0.0f);
            }
        }
    }
     
     private void buildFont() {          // Build Our Bitmap Font
        Font font;                                      // Font object
        
        /* Note that this will work well with monospace fonts, but does not look as good with
         * proportional fonts.
         */
        
        BufferedImage fontImage;                   // image for creating the bitmap
        int bitmapSize = 512;                      // set the size for the bitmap texture
        boolean sizeFound = false;
        boolean directionSet = false;
        int delta = 0;
        int fontSize = 24;

        /* To find out how much space a Font takes, you need to use a the FontMetrics class. 
         * To get the FontMetrics, you need to get it from a Graphics context.  A Graphics context is
         * only available from a displayable surface, ie any class that subclasses Component or any Image.
         * First the font is set on a Graphics object.  Then get the FontMetrics and find out the width
         * and height of the widest character (W).  Then take the largest of the 2 values and find the 
         * maximum size font that will fit in the size allocated.
         */
        while(!sizeFound) {
            font = new Font(fontname, Font.PLAIN, fontSize);  // Font Name
            // use BufferedImage.TYPE_4BYTE_ABGR to allow alpha blending
            fontImage = new BufferedImage(bitmapSize, bitmapSize, BufferedImage.TYPE_4BYTE_ABGR);
            Graphics2D g = (Graphics2D)fontImage.getGraphics();
            g.setFont(font);
            FontMetrics fm = g.getFontMetrics();
            int width = fm.stringWidth("W");
            int height = fm.getHeight();
            int lineWidth = (width > height) ? width * 16 : height * 16;
            if(!directionSet) {
                if(lineWidth > bitmapSize) {
                    delta = -2;
                }
                else {
                    delta = 2;
                }
                directionSet = true;
            }
            if(delta > 0) {
                if(lineWidth < bitmapSize) {
                    fontSize += delta;
                }
                else {
                    sizeFound = true;
                    fontSize -= delta;
                }
            }
            else if(delta < 0) {
                if(lineWidth > bitmapSize) {
                    fontSize += delta;
                }
                else {
                    sizeFound = true;
                    fontSize -= delta;
                }
            }
        }

        /* Now that a font size has been determined, create the final image, set the font and draw the
         * standard/extended ASCII character set for that font.
         */
        font = new Font(fontname, Font.BOLD, fontSize);  // Font Name
        // use BufferedImage.TYPE_4BYTE_ABGR to allow alpha blending
        fontImage = new BufferedImage(bitmapSize, bitmapSize, BufferedImage.TYPE_4BYTE_ABGR);
        Graphics2D g = (Graphics2D)fontImage.getGraphics();
        g.setFont(font);
        g.setColor(OPAQUE_WHITE);
        g.setBackground(TRANSPARENT_BLACK);
        FontMetrics fm = g.getFontMetrics();
        for(int i=0;i<256;i++) {
            int x = i % 16;
            int y = i / 16;
            char ch[] = {(char)i};
            String temp = new String(ch);
            g.drawString(temp, (x * 32) + 1, (y * 32) + fm.getAscent());
        }

        /* The following code is taken directly for the LWJGL example code.
         * It takes a Java Image and converts it into an OpenGL texture.
         * This is a very powerful feature as you can use this to generate textures on the fly out
         * of anything.
         */
        //      Flip Image
        AffineTransform tx = AffineTransform.getScaleInstance(1, -1);
        tx.translate(0, -fontImage.getHeight(null));
        AffineTransformOp op =
            new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
        fontImage = op.filter(fontImage, null);

        // Put Image In Memory
        ByteBuffer scratch =
            ByteBuffer.allocateDirect(
                4 * fontImage.getWidth() * fontImage.getHeight());

        byte data[] =
            (byte[])fontImage.getRaster().getDataElements(
                0,
                0,
                fontImage.getWidth(),
                fontImage.getHeight(),
                null);
        scratch.clear();
        scratch.put(data);
        scratch.rewind();

        // Create A IntBuffer For Image Address In Memory   
        IntBuffer buf =
            ByteBuffer
                .allocateDirect(4)
                .order(ByteOrder.nativeOrder())
                .asIntBuffer();
        GL11.glGenTextures(buf); // Create Texture In OpenGL   

        GL11.glBindTexture(GL11.GL_TEXTURE_2D, buf.get(0));
        // Typical Texture Generation Using Data From The Image

        // Linear Filtering
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
        // Linear Filtering
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
        // Generate The Texture
        GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, fontImage.getWidth(), fontImage.getHeight(), 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, scratch);


        texture = buf.get(0);                           // Return Image Address In Memory

        base = GL11.glGenLists(256);                    // Storage For 256 Characters

        /* Generate the display lists.  One for each character in the standard/extended ASCII chart.
         */
        float textureDelta = 1.0f / 16.0f;        
        for(int i=0;i<256;i++) {
            float u = ((float)(i % 16)) / 16.0f;
            float v = 1.f - (((float)(i / 16)) / 16.0f);
            GL11.glNewList(base + i, GL11.GL_COMPILE);
            GL11.glBindTexture(GL11.GL_TEXTURE_2D, texture);
            GL11.glBegin(GL11.GL_QUADS); 
                GL11.glTexCoord2f(u, v);
                GL11.glVertex3f(-size, size, 0.0f);
                GL11.glTexCoord2f((u + textureDelta), v);
                GL11.glVertex3f(size, size, 0.0f);
                GL11.glTexCoord2f((u + textureDelta), v - textureDelta);
                GL11.glVertex3f(size, -size, 0.0f);
                GL11.glTexCoord2f(u, v - textureDelta);
                GL11.glVertex3f(-size, -size, 0.0f);
            GL11.glEnd();
            GL11.glEndList();
        }
    }
     
    public float getSize(){
        return size;
    }
    
    public String getFontName(){
        return fontname;
    }
    
}

jam007

Still wondering. Do I have to set some property for the materials/polygones i use in the 3D picture?

Here is a new method I just added for the OpenGLFont class above:
// Prints a number of lines of text
    public static void glPrintLines(OpenGLFont font, String[] msg) {                                      // Custom GL "Print" Routine
       if(msg != null) {
           GL11.glBindTexture(GL11.GL_TEXTURE_2D, font.texture);
           for (int j=0; j<msg.length; j++) {
               for(int i=0;i<msg[j].length();i++) {
                   GL11.glCallList(font.base + msg[j].charAt(i));
                   GL11.glTranslatef(-font.size*1.1f, 0.0f, 0.0f);
               }
               GL11.glTranslatef(font.size*1.1f*msg[j].length(), -font.size*1.1f, 0.0f);
           }
       }
    }

jam007

Problem solved.
I hade to turn on and off textures between rendering of my textured text and my, yet, untextured world and switch between blendingmodes.

In the above method you might want to change the signs for font.size depending on how you view the text.

Anders

napier

Could be a few things.  It may help to see your render function.

Might be a blend setting is off.  Depth buffer also can affect this sort of thing.  Also are you switching to ortho mode (2D) before drawing the text?  

I have a demo of text rendering online here:

http://potatoland.com/code/gl/lwjgl_demoText.zip

Some snips from that code:

       // Enable alpha blend (so text will have transparent background)
        GL11.glEnable(GL11.GL_BLEND);
        GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);

        .....

        // Turn off lighting so text will draw as-is, not lit
        GL11.glDisable(GL11.GL_LIGHTING);

	// Draw the text 

        // enable the charset texture
        GL11.glBindTexture(GL11.GL_TEXTURE_2D, fontTextureHandle);
        // prepare to render in 2D
        setOrthoOn();
        // draw the text
        GL11.glTranslatef(x, y, 0);        // Position The Text (in pixels coords)
        for(int i=0; i<msg.length(); i++) {
             GL11.glCallList(offset + msg.charAt(i));
        }
        // restore the original positions and views
        setOrthoOff();



        .....


     // functions to turn on 2D rendering

    public static void setOrthoOn()
    {
        // prepare to render in 2D
        GL11.glDisable(GL11.GL_DEPTH_TEST);             // so text stays on top of scene
        GL11.glMatrixMode(GL11.GL_PROJECTION);
        GL11.glPushMatrix();                            // preserve perspective view
        GL11.glLoadIdentity();                          // clear the perspective matrix
        GL11.glOrtho(viewportX,viewportX+viewportW,viewportY,viewportY+viewportH,-1,1);  // turn on 2D mode
        GL11.glMatrixMode(GL11.GL_MODELVIEW);
        GL11.glPushMatrix();				// Preserve the Modelview Matrix
        GL11.glLoadIdentity();				// clear the Modelview Matrix
    }

    public static void setOrthoOff()
    {
        // restore the original positions and views
        GL11.glMatrixMode(GL11.GL_PROJECTION);
        GL11.glPopMatrix();
        GL11.glMatrixMode(GL11.GL_MODELVIEW);
        GL11.glPopMatrix();
        GL11.glEnable(GL11.GL_DEPTH_TEST);		// turn Depth Testing back on
    }
penGL/Java/LWJGL demos and code: http://potatoland.org/code/gl

napier

Good to see that you got it.  You must have posted just before me!

napier
penGL/Java/LWJGL demos and code: http://potatoland.org/code/gl

jam007

Thanks for you taking the trouble of replying. I have checked out your site and it is really helpful!

Anders

jam007

Is there any large advantage in switching between projection modes (ortho)?
I just draw the "HUD" text in 3d-space in front of the viewing position. Turning it as the viewer turns..

Anders

PS maybe that is why I got troubles with the text being mirrored and having to change signs as mentioned above....
Learning every minute...I hope...

napier

I suppose you can place a HUD display in the scene in 3D space, but then you have to take into account things like navigation and lighting in your 3D scene and make sure those things don't affect your HUD display.  Seems easier to switch to 2D and draw the HUD (or text)  to exactly match the viewport.   Also in ortho mode x and y map directly to screen pixels, which is useful at times.
penGL/Java/LWJGL demos and code: http://potatoland.org/code/gl

jam007

Yes of course. I see the benefits for that. I will use your example and switch between modes. That way I can add other texts like "Ooops collision" without having to twist and turn it to suit the position and direction of the view. I had some fun seeing my "GPS-display" in all different angles before I got the trans and rot right....

Anders

PS I was daunted by all the GL11-commands for swiching as I tried it the first time using another example code for reference but you have put them in two nice reusable methods, thank you:)

rainforest

napier

Thank you for your wonderful text demo. That was really helpful in displaying text in my project too.

Can you please tell me how to add more font styles?

rainforest.
.:: A journey of thousand miles starts with a single step ::.

napier

Hi rainforest,

Been busy and didn't check for forum for a week...

Good to hear that the text demo helped.  

You can get different font styles by using a different texture image in place of the one I included in the demo.  The texture image contains all the printable characters, in a given font style.  There are utility programs that will do this for you easily.  I used this one:

Bitmap Font Builder
http://www.lmnopc.com/bitmapfontbuilder

Use this app to produce a texture map for the font style you want, then pass that image name to the buildFont() function in my demo.
penGL/Java/LWJGL demos and code: http://potatoland.org/code/gl