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:
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.
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);
}
}