OpenGL text renderer

I got bored yesterday and had an idea. So, i decided to write myself a small text renderer. It’s far from finished or perfect, but it works.
You need to initialize it with a font file that is alpha based. I got the idea from the coke and code one after i tried using that and it didn’t work. But it should be pretty straight forward. I would also appreciate feedback!
You can download the font’s i used, be aware one is from the coke and code tutorial but only the top font and the other is and 8-bit i made:http://lonecoder.heliohost.org/download/


import static org.lwjgl.opengl.GL11.*;

import java.io.IOException;
import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.opengl.TextureLoader;
import org.newdawn.slick.util.ResourceLoader;

import net.lonecoder.reachfortheskies.*;

public class TextRenderer {
	
	public static final int CENTER = 0; //Alignment (Optional but it overides the x value if either of these three a passed)
	public static final int LEFT = 1;
	public static final int RIGHT = 2;
	
	private static int charWidth;
	private static int charHeight;
	private static float charWidthInTex;
	private static float charHeightInTex;
	
	private static int charsAcross;
	private static int charsDown;
	private static int charSpacing;
	
	private static Texture texture;
	
	public TextRenderer(String texFileName, int charWidth, int charHeight) {
		TextRenderer.charWidth = charWidth;
		TextRenderer.charHeight = charHeight;
		
		try {
			texture = TextureLoader.getTexture("PNG", ResourceLoader.getResourceAsStream(texFileName));
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		charsAcross = texture.getImageWidth() / charWidth;
		charsDown = texture.getImageHeight() / charHeight;
		System.out.println(texture.getImageWidth() + " " + texture.getWidth());
		
		charWidthInTex = texture.getWidth() / charsAcross;
		charHeightInTex = texture.getHeight() / charsDown;
		charSpacing = charWidth - 5;
		
	}
	
	public static void drawString(String text ,double x, double y, double size, float red, float green, float blue, double orientation) {
		double charSize;
		double charSpace;
		
		//If no size value passed, use default size
		if (size == 0) {
			charSize = charWidth;
			charSpace = charSpacing;
		}
		else {
			charSize = charSpacing * size;
			charSpace = charSize - 4;
		}
		
		if (orientation == CENTER) {
			x = (Game.WIDTH - (text.length() * charSpace))/2;
		}
		if (orientation == LEFT) {
			x = 0;
		}
		if (orientation == RIGHT) {
			x = (Game.WIDTH - (text.length() * charSpace + 10));
		}
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glEnable(GL_BLEND);
		glEnable(GL_TEXTURE_2D);
		glPushMatrix();
		texture.bind();
		
		glBegin(GL_QUADS);
		
		for (int i = 0; i < text.length(); i++) {
			int c = (text.charAt(i)) - 32;
			
			float u = (c % charsAcross) * charWidthInTex;
			float v = (c / charsAcross) * charHeightInTex;

			glColor3f(red, green, blue); //Color can be (must be) set when function is called.
			glTexCoord2f(u, v);
			glVertex2d(x + (i * charSpace), y);
			glTexCoord2f(u + charWidthInTex, v);
			glVertex2d(x + (i * charSpace) + charSize, y);
			glTexCoord2f(u + charWidthInTex, v + charHeightInTex);
			glVertex2d(x + (i * charSpace) + charSize, y + charSize);
			glTexCoord2f(u, v + charHeightInTex);
			glVertex2d(x + (i * charSpace), y + charSize);
		}
		glEnd();
		glPopMatrix();
		glDisable(GL_TEXTURE_2D);
		glDisable(GL_BLEND);
	}
}


This is just how i use it:


TextRenderer textRenderer;
textRenderer = new TextRenderer("res/8-bit_font1.png", 32, 32);

As you can access it statically, i make calls like this without creating any objects:


TextRenderer.drawString("HELLO", 0, 0, 1, 1f, 1f, 1f, TextRenderer.LEFT);

Isn’t it a little strange that a constructor initializes the static texture object? Calling drawString() without ever calling the constructor will break, and calling the constructor multiple times will also cause strange behavior.

I think in this case, better design would be to just use an instance method to draw a string. If you’re worried about creating too many objects, just make sure to pass the TextRenderer in as a dependency to your code that needs to render text.

It’s best if you make a public static Hashmap of <String,TextRenderer> inside that TextRenderer class :slight_smile: