Java-Gaming.org    
Featured games (79)
games approved by the League of Dukes
Games in Showcase (477)
Games in Android Showcase (109)
games submitted by our members
Games in WIP (536)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Font rendering with BMFont  (Read 1078 times)
0 Members and 1 Guest are viewing this topic.
Offline PandaMoniumHUN
« Posted 2014-01-14 12:24:31 »

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:

 Pointing

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. Smiley

Here's my source code (I can provide the other classes as well, however I think this is the only important part):
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
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);
   }
   
}

Offline Screem
« Reply #1 - Posted 2014-01-14 13:30:48 »

I can't find anything wrong in your code above, but I think this has to do with how you render the font. Characters that should be aligned with the baseline but ascend above it (t, f, l, any capital letter, etc) are rendered as though they're characters that descend below it (y, g, p, q, etc) and vice versa. So you just need to "flip" the y-offset for those characters. Hopefully this makes sense. Cheesy

Offline PandaMoniumHUN
« Reply #2 - Posted 2014-01-14 15:58:06 »

I can't find anything wrong in your code above, but I think this has to do with how you render the font. Characters that should be aligned with the baseline but ascend above it (t, f, l, any capital letter, etc) are rendered as though they're characters that descend below it (y, g, p, q, etc) and vice versa. So you just need to "flip" the y-offset for those characters. Hopefully this makes sense. Cheesy
Thank you, you got me started in the right way but that wasn't quite the solution.

So what the problem was that all the tutorials I found on the subject used (0,0) as their top-left coordinate (but they never really mentioned it), while (0,0) for me is my bottom-left coordinate so all I had to do is instead of rendering from the bottom to the top (BL, BR, UR, UL order) I'm now rendering from top to bottom (TL, BL, BR, TR (these are CCW orders btw)) and instead of adding height to the cursor position I negate it. It's a bit hard to explain, but you'll find a picture below from the result and the working source code, feel free to use it for your own projects if needed.

  Pointing

Source code:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
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 - UL
           character.getTextureX(), character.getTextureY(), //TexCoord #0 - UL
           charPos.x, charPos.y-character.getHeight(), //Vertex #1 - BL
           character.getTextureX(), character.getTextureEndY(), //TexCoord #1 - BL
           charPos.x+character.getWidth(), charPos.y-character.getHeight(), //Vertex #2 - BR
           character.getTextureEndX(), character.getTextureEndY(), //TexCoord #2 - BR
           charPos.x+character.getWidth(), charPos.y, //Vertex #3 - UR
           character.getTextureEndX(), character.getTextureY() //TexCoord #3 - UR
        };
         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);
   }
   
}


I will probably write a complete tutorial on the subject soon, so those who don't know how to do this or don't understand it, stay tuned. Wink

Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

CogWheelz (18 views)
2014-07-30 21:08:39

Riven (25 views)
2014-07-29 18:09:19

Riven (15 views)
2014-07-29 18:08:52

Dwinin (12 views)
2014-07-29 10:59:34

E.R. Fleming (33 views)
2014-07-29 03:07:13

E.R. Fleming (12 views)
2014-07-29 03:06:25

pw (43 views)
2014-07-24 01:59:36

Riven (43 views)
2014-07-23 21:16:32

Riven (30 views)
2014-07-23 21:07:15

Riven (31 views)
2014-07-23 20:56:16
List of Learning Resources
by SilverTiger
2014-07-31 18:29:50

List of Learning Resources
by SilverTiger
2014-07-31 18:26:06

List of Learning Resources
by SilverTiger
2014-07-31 13:54:12

HotSpot Options
by dleskov
2014-07-08 03:59:08

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:58:24

Java and Game Development Tutorials
by SwordsMiner
2014-06-14 00:47:22

How do I start Java Game Development?
by ra4king
2014-05-17 11:13:37

HotSpot Options
by Roquen
2014-05-15 09:59:54
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!