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;
}
}
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);
}
}
}
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
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
}
Good to see that you got it. You must have posted just before me!
napier
Thanks for you taking the trouble of replying. I have checked out your site and it is really helpful!
Anders
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...
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.
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:)
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.
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.