Complete TrueTypeFont class that only requires LWJGL

Started by bobjob, June 28, 2009, 00:54:50

Previous topic - Next topic

Evil-Devil

Quote from: bobjob on August 20, 2009, 19:44:32
Quote from: Evil-Devil on August 20, 2009, 08:00:31
The main thing i am missing while reading the source is the possibility to save/load into any given texture format or at least saving it into any reusable format and just calling the load/draw functions afterall.
My intention for this class was to just give a simple self containted text implementation that doesnt require other resources (mainly for new LWJGL users).

I remember when i was first started to learn LWJGL and found a texture loader class for devIL somewere, it made a world of difference having a self contained class. It was extremly easy for me to get through alot of opengl lessons after that.
Yay, i know what you mean. I remember the time investing how to load DDS files just to came up reading some papers at nvidia/ati with the c++ implementation...

Guess it will be ok to credit the creators and use your class as basecode for a custom implementation.

Still need time to test it anyway....some holidays would be great....gonna ask my boss... ;)

//edit: Found the time to test it. Great stuff. Hopefully java will support opentype fonts someday too =)

Ciardhubh

Nice lightweight font class.

I just tried it and came across a few minor issues:
* - artifacts, e.g. bottom right on 'i' in an italic font
* - unclean background, slight discolouration (fixed by g.setColor(new Color(0,0,0,0)); in createSet(...))
* - black border around chars (probably due to AA when drawing on BufferedImage in TrueTypeFont), e.g. red chars on green background
* - font sizes above around 40 produce garbage or nothing at all
* - manipulates global glTexEnvf state

Most of these are in Slick's TrueTypeFont class, too.

CommanderKeith

This is great, thanks bobjob. I couldn't believe how hard it is to render text in openGL. I was so glad to find this code.

Just a few things that I noticed, for some reason I see some feint black lines in the TTFTest class demo - there's a 5 pixel high feint vertical gray line a small distance under the question mark in the second line of text. There's also the same feint vertical lines under the t and the u's in the first line of text.

Also the 'f', 'j' and 'y' have their lower tails cut off.

bobjob

Quote from: CommanderKeith on December 07, 2009, 01:45:13
This is great, thanks bobjob. I couldn't believe how hard it is to render text in openGL. I was so glad to find this code.

Just a few things that I noticed, for some reason I see some feint black lines in the TTFTest class demo - there's a 5 pixel high feint vertical gray line a small distance under the question mark in the second line of text. There's also the same feint vertical lines under the t and the u's in the first line of text.

Also the 'f', 'j' and 'y' have their lower tails cut off.
I didnt really do much on this. All I did is remove the need for slick.
if you want to fix faint lines you may want to change the tex paremeters in loadImage() to:
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
			GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);

CommanderKeith

Thanks, that worked. But the large-scaled text at the top now looks pixelated/blocky. The other lines of text still look fine.

Cheers,
Keith

EDIT: interestingly, when italics is off the text renders without those feint lines (using the original textute rendering options).

bobjob

if you plan to have large scale text in your program, its probably best to select a large font when building the truetypefont. the reason i added the scale is just for the sake animation and effects.

if you still want scaled fonts without artifacts, you may want to try increase the value of
GL11.glAlphaFunc(GL11.GL_GREATER, 0.25f) // or even 0.5f
in initGL() in the test case class.

but first remember to put back the texture perameters to the way they were.

bobjob

for those interested. I have updated my version of the TrueTypeFont, to save and load, so that if you use a saved version it will look the same on all OS's.

Ill update the post as soon as I port it over to the stand alone version. It Also means you can minipulate the saved texture to fix up any artificats or other specific problems relating to a single character.

charstar

Seriously rad!  Thanks for publishing your code!  It's nice to be able to just get some text on screen and not have to write yet-another-HUD system.

zovirl

I'm having trouble getting this to work along side the rest of my code.  Specifically, using TrueTypeFont makes the rest of my polygons disappear.

I have a simple example that draws a triangle to the screen.  If I start using TrueTypeFont, I can get text to draw to the window but the triangle no longer appears. I can't get both the triangle and the text, it is strictly either/or.

I see the same behavior in the test case from the first post on this thread...it draws text but I can't get it to also draw polygons at the same time.

Strangely, simply constructing a TrueTypeObject is enough to hide the triangle. I don't even have to call drawString().  I think I've narrowed it down to the gluBuild2DMipmaps call inside TrueTypeFont.  If I comment that out, then my triangle shows up (though obviously the text doesn't).  I'm at a loss as to why, though.  Anyone have any ideas?

I assume I must be missing something fairly elementary, since I don't see anyone else having trouble.


Here's my code:
import java.awt.Font;

import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;


public class Game {

	public static void main(String[] args) throws Exception {
		Game game = new Game();
		game.Init();
		game.Run();
	}

	private void Init() throws LWJGLException {
		Display.setDisplayMode(new DisplayMode(800, 600));
		Display.create();
		
		// Switch to ortho view
	    GL11.glMatrixMode(GL11.GL_PROJECTION);
	    GL11.glLoadIdentity();
	    GL11.glOrtho(0.0, Display.getDisplayMode().getWidth(), 0.0, Display.getDisplayMode().getHeight(), -1.0, 1.0);
	    GL11.glMatrixMode(GL11.GL_MODELVIEW);
	    GL11.glLoadIdentity();
	    GL11.glViewport(0, 0, Display.getDisplayMode().getWidth(), Display.getDisplayMode().getHeight());

	    // Set up for text
		GL11.glEnable(GL11.GL_TEXTURE_2D); // exture Mapping, needed for text
		GL11.glEnable(GL11.GL_BLEND); // Enabled blending for text
		GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
	}

	private void Run() throws LWJGLException {
		Font font = new Font("Serif", Font.BOLD, 30);
		
		/*
		 * If I remove this next line, the triangle is drawn.  
		 * With the line, the triangle isn't drawn 
		 */
		TrueTypeFont trueTypeFont = new TrueTypeFont(font, true);

		while (true) {
			if (Display.isCloseRequested() || Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)) {
				break;
			}
			
			GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT);
			GL11.glLoadIdentity();
			
			GL11.glBegin(GL11.GL_TRIANGLES);
			GL11.glVertex2f(100, 100);
			GL11.glVertex2f(500, 100);
			GL11.glVertex2f(300, 500);
			GL11.glEnd();

//			trueTypeFont.drawString(0, 0, "Hello", 1, 1);
			Display.update();
		}
	}
}

ryanm

I would imagine that constructing the truetype font enables textures. Since you're not specifying texture coordinates, they'll be (0,0) for the triangle vertices and you won't see anything. Stick a GL11.glDisable( GL11.GL_TEXTURE_2D ) in after the font constructor.


Klems

Hi !
Some necroposting here, but anyway, it's for the greater good !

I've made some improvement on the code.

//Klems : in case the current font can't draw a char, we look in the system for the first capable font
private Font getFontFromChar(char ch) {
	if (font.canDisplay(ch)) {
		return font;
	} else {
		for (Font currentFont : getFonts()) {
			if (currentFont.canDisplay(ch)) {
				//Don't forget to derive the new font to match this.font size
				return currentFont.deriveFont(font.getSize2D());
			}
		}
	}
	//Worst case scenario :
	return font;
}

This code may run slowly if there is a lot of custom char. Using a static Set could be useful.
Use this method on "private BufferedImage getFontImage(char ch)". It allow the text renderer to display virtually ANY character by searching in the system fonts if the current font is unable to display this char.
Very useful when you want to display chinese, thai and stuff.


You may also want to change this around line 400 :

/* Klems : only white is needed
newI[newIndex] = b[1];
newI[newIndex+1] = b[2];
newI[newIndex+2] = b[3];
newI[newIndex+3] = b[0]; */
newI[newIndex] = -1;
newI[newIndex+1] = -1;
newI[newIndex+2] = -1;
newI[newIndex+3] = b[0];

Only white is needed : let OpenGL handle the color tainting. The old code could result in visual artifact.

mick1114

Sorry that i reply on this old topic, but I have a question depending on your code and i don't want to start a new one.
At first i want to thank you for this nice work, but can it be that the getWidht() method doesn't work correctly, or is it my fault?

I have tried to render text in d middle of the screen with this line
this.font.drawString(Display.getWidth()/2 - font.getWidth("sampleText")/2, 20, "sampleText"), 1,1);


but it is not in the center.
It seems getWitdh return wrong values (I think they are too big). Contrary to this the original ttf code from slick works:
font.drawString(Display.getWidth()/2 - font.getWidth("sampleText"), Display.getHeight()/2, "sampleText");


Your and the original getWidth method are the same, so i don't have any clue what is wrong.

Can anybody help me?
Thank you in advance.