Font rendering with BMFont

Hey guys. I’m trying to use BMFont to render some bitmap fonts with my engine and it almost works fine, however, I got stuck with a minor issue that I don’t know how to overcome:


:point:

As you can see some of the fonts are in line but some of them are not.
I know for a fact that this has to do something with the characters’ Y offset, however, if I don’t add that value to my cursor position then 90% of the characters will be in line but some characters such as y, g, and p (so basically those that would need minus offsetting) will be “in line” too, so their descender won’t be placed lower.
I don’t know what I’m messing up so please, if someone has experience with parsing and rendering BMFont bitmaps help me out here. :slight_smile:

Here’s my source code (I can provide the other classes as well, however I think this is the only important part):

package com.pandadev.ogame.fonts;

import java.nio.FloatBuffer;
import java.nio.ShortBuffer;

import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL20;
import org.lwjgl.opengl.GL30;
import org.lwjgl.util.vector.Matrix4f;
import org.lwjgl.util.vector.Vector2f;

import com.pandadev.ogame.MatrixStack;
import com.pandadev.ogame.textures.Texture;

public class FontText {
	public FontText(Font font, String text, Vector2f position){
		this.fontTexture = font.getTexture();
		//Generate text using the font information
		Vector2f cursor = new Vector2f(position);
		FontCharacter lastCharacter = null;
		char[] textCharacters = text.toCharArray();
		FloatBuffer vertexData = BufferUtils.createFloatBuffer(textCharacters.length*16);
		for(char textCharacter : textCharacters){
			//Convert the character from ASCII value to FontCharacter object
			FontCharacter character = font.getCharacter(Character.toString(textCharacter));
			if(character == null){
				character = font.getCharacter(Character.toString('?'));
			}
			Vector2f charPos = new Vector2f(cursor.x+character.getOffsetX(), cursor.y+character.getOffsetY());
			//Check and apply kerning if possible
			if(lastCharacter != null){
				FontKerning kerning = font.getKerning(lastCharacter.getId(), character.getId());
				if(kerning != null)
					charPos.x += kerning.getValue();
			}
			//Calculating vertices and uploading to the buffer
			float[] vertices = {
				charPos.x, charPos.y, //Vertex #0
				character.getTextureX(), character.getTextureEndY(), //TexCoord #0
				charPos.x+character.getWidth(), charPos.y, //Vertex #1
				character.getTextureEndX(), character.getTextureEndY(), //TexCoord #1
				charPos.x+character.getWidth(), charPos.y+character.getHeight(), //Vertex #2
				character.getTextureEndX(), character.getTextureY(), //TexCoord #2
				charPos.x, charPos.y+character.getHeight(),  //Vertex #3
				character.getTextureX(), character.getTextureY() //TexCoord #3
			};
			vertexData.put(vertices);
			cursor.x += character.getAdvanceX();
			lastCharacter = character;
		}
		//Generate indices
		this.index_count = textCharacters.length*6;
		ShortBuffer indexData = BufferUtils.createShortBuffer(index_count);
		short counter = 0;
		for(int i = 0; i < textCharacters.length; i++){
			short[] indices = {
				counter++, counter++, counter++,
				((short)(counter-3)), ((short)(counter-1)), counter++
			};
			indexData.put(indices);
		}
		vertexData.flip();
		indexData.flip();
		//Upload data to the GPU
		this.vao = GL30.glGenVertexArrays();
		this.vbo = GL15.glGenBuffers();
		this.ibo = GL15.glGenBuffers();
		GL30.glBindVertexArray(vao);
			GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
			GL15.glBufferData(GL15.GL_ARRAY_BUFFER, vertexData, GL15.GL_STATIC_DRAW);
			GL20.glEnableVertexAttribArray(0);
			GL20.glEnableVertexAttribArray(1);
			GL20.glVertexAttribPointer(0, 2, GL11.GL_FLOAT, false, 16, 0L);
			GL20.glVertexAttribPointer(1, 2, GL11.GL_FLOAT, false, 16, 8L);
			GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ibo);
			GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, indexData, GL15.GL_STATIC_DRAW);
		GL30.glBindVertexArray(0);
	}
	private final int vao;
	private final int vbo, ibo;
	private final int index_count;
	private final Matrix4f textureMatrix = new Matrix4f();
	private final Texture fontTexture;
	
	public Texture getFontTexture(){
		return fontTexture;
	}
	
	public void render(){
		MatrixStack.getTextureMatrix().load(textureMatrix);
		fontTexture.bind();
		GL30.glBindVertexArray(vao);
			GL11.glDrawElements(GL11.GL_TRIANGLES, index_count, GL11.GL_UNSIGNED_SHORT, 0L);
		GL30.glBindVertexArray(0);
		fontTexture.unbind();
	}
	
	public void destroy(){
		GL30.glDeleteVertexArrays(vao);
		GL15.glDeleteBuffers(vbo);
		GL15.glDeleteBuffers(ibo);
	}
	
}