Slick utility with lwjgl 3

Started by TheBoneJarmer, November 26, 2014, 23:51:55

Previous topic - Next topic

TheBoneJarmer

Hey

I couldn't find any stuff about this, but the slick utility wont work with LWJGL3. I haven't found any info about this. I don't know who the developer is and if he or she is active on this forum. I'm not busy with OpenAL at the moment, but I need to be able to draw fonts. That is a piece of cake with slick's utility but I have no clue what to do without it. So, any info/news/updates?

Thanks in advance!

lightbringer

For now you'd need to roll your own. You could take a look at gdx-freetype, it's quite possible that you can integrate that (without the rest of libgdx).

Cornix

Slick is outdated and not continued. Its very unlikely there will ever be any support for it.
It would be a much better choice to work on your own solution or to look for a different library.

TheBoneJarmer

I feared so. Besides gdx-freetype (which I will take a look at later), are there any other libraries to use for drawing text? Slick used AWT, although I'm sure there should be other solutions. I read a lot about bitmap fonts but the tutorials I find (which are written in c++) are hard to translate to Java. And I do not even know where to go with TTF.

Cornix

You can simply use a texture atlas or spritesheet with single letters and then draw textured quads for each letter in your text.
What you can do is use AWT to create a BufferedImage, then use the native OS specific font rendering mechanism to render a text to the buffered image and then use the buffered image to create an OpenGL texture. I did this myself once, it isnt too hard to do.

abcdef

Here is a basic framework you need for rendering TTF using awt

1) Create an awt Font

        Font font = Font.createFont(java.awt.Font.TRUETYPE_FONT, "locationToMyFont");


2) Create a graphics object using this font

        GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
        Graphics2D graphics = gc.createCompatibleImage(1, 1, Transparency.TRANSLUCENT).createGraphics();
        graphics.setFont(font);


3) Extract the font metrics and find out where in a bitmap you want to draw the glyphs

         FontMetrics fontMetrics = graphics.getFontMetrics();


Specifics of doing this is left up to you

4) Create a buffered image of the size you want to draw it

        BufferedImage bufferedImage = graphics.getDeviceConfiguration().createCompatibleImage(textureWidth, textureHeight, Transparency.TRANSLUCENT);

        Graphics2D imageGraphics = (Graphics2D) bufferedImage.getGraphics();
        imageGraphics.setFont(font);
        imageGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
        imageGraphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);


5) Draw each character you want to use (this is normally all visible characters) at the location you worked out above

        imageGraphics.drawChars('glyphChar', 0, 1,glyphLocationX, glyphLocationY);


6) Convert this BufferedImage to a ByteBuffer
        int[] pixels = new int[bufferedImage.getWidth() * bufferedImage.getHeight()];
        bufferedImage.getRGB(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight(), pixels, 0, bufferedImage.getWidth());

        ByteBuffer data = ByteBuffer.allocateDirect((bufferedImage.getWidth() * bufferedImage.getHeight() * 4));

        for (int y = 0; y < bufferedImage.getHeight(); y++)
        {
            for (int x = 0; x < bufferedImage.getWidth(); x++)
            {
                int pixel = pixels[y * bufferedImage.getWidth() + x];
                data.put((byte) ((pixel >> 16) & 0xFF));     // Red component
                data.put((byte) ((pixel >> 8) & 0xFF));      // Green component
                data.put((byte) (pixel & 0xFF));               // Blue component
                data.put((byte) ((pixel >> 24) & 0xFF));    // Alpha component. Only for RGBA                
            }
        }


You now have your texture for opengl which is in the RGBA format, you can now use your glyph coordinates to string together a set of characters to draw in your opengl app.

There is obviously more to it but it should help you out.

Hopefully a freetype bindings of some sort would be added in the future, lots still to do on LWJGL3 though so it might be a while.

TheBoneJarmer

@Cornix

Funny, I was thinking the same thing while I was walking with my dogs lol. Thanks for the idea though!

@abcdef

Amazing! But can you please explain the purpose of textureWidth, textureHeight, glyphLocationX,glyphLocationY? I do not know what values I have to provide there.

abcdef

The texture width and height are the size of the texture you want to draw all your glyphs on, it is this texture you will pass to opengl

The glyphX and glyphY are the coordinates in that texture that you want to draw the glyph in. Example below

ABCDEabcdeYPyp

If all I wanted to do was to draw the characters above I would create a texture big enough to draw them all, i would then draw A at the far left on the texture and then B. A might have coordinate (0,0), B might have (10,0) and so on.

If I then wanted to use this to draw the word Bed, I would give opengl the overall texture then I would create three quads (created from triangles) with the texture coordinates pointing the locations of B e d in the texture.

Hope that makes sense

TheBoneJarmer

Hey and sorry for the late reply!

I have been busy for 3 hours now and I still cannot get it to work. This is my font class:
import org.jarmer.*;
import java.awt.*;
import java.awt.image.*;
import java.io.*;
import java.nio.*;
import org.lwjgl.*;
import org.lwjgl.opengl.*;
import org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL11.*;

public class TrueTypeFont {
	private String path;
	private Font font;
	private ByteBuffer imageData;
	private BufferedImage imageBuffer;
	private Graphics2D imageGraphics;
	private int image = 0;
	
	public TrueTypeFont(String path) throws JarmerException {
		this.path = path;
		
		try {
			Font font = Font.createFont(java.awt.Font.TRUETYPE_FONT, new File(path));
			
			char[] chars = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
			generateTexture(chars);
			
		} catch (FontFormatException ex) {
			throw new JarmerException("Font is not a TTF!");
		} catch (IOException ex) {
			throw new JarmerException(ex);
		}
	}
	
	//Draws the bitmap generated by the class
	public void drawBitmap(float x,float y) {
		glBindTexture(GL_TEXTURE_2D, image);
		glBegin(GL_QUADS);
			glVertex2f(x,y);
			glVertex2f(x+512,y);
			glVertex2f(x+512,y+512);
			glVertex2f(x,y+512);
			
			glTexCoord2f(0,0);
			glTexCoord2f(1,0);
			glTexCoord2f(1,1);
			glTexCoord2f(0,1);
		glEnd();
	}
	
	public void drawText(char[] text,float x,float y) {
		
	}
	
	private void generateTexture(char[] text) {		
		
		//Configure
		GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
	    Graphics2D graphics = gc.createCompatibleImage(1, 1, Transparency.TRANSLUCENT).createGraphics();
        graphics.setFont(font);
        
        //Create the image buffer
		FontMetrics fontMetrics = graphics.getFontMetrics();
		BufferedImage imageBuffer = graphics.getDeviceConfiguration().createCompatibleImage(512, 512, Transparency.TRANSLUCENT);
		
		//Draw the characters on our image
		imageGraphics = (Graphics2D) imageBuffer.getGraphics();
		imageGraphics.setFont(font);
		imageGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
		imageGraphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
		imageGraphics.drawChars(text,0,text.length,0,0);
		
		//Generate texture data
		int[] pixels = new int[imageBuffer.getWidth() * imageBuffer.getHeight()];
	    imageBuffer.getRGB(0, 0, imageBuffer.getWidth(), imageBuffer.getHeight(), pixels, 0, imageBuffer.getWidth());
	    imageData = ByteBuffer.allocateDirect((imageBuffer.getWidth() * imageBuffer.getHeight() * 4));
 
	    for (int y = 0; y < imageBuffer.getHeight(); y++)
	    {
	        for (int x = 0; x < imageBuffer.getWidth(); x++)
	        {
	            int pixel = pixels[y * imageBuffer.getWidth() + x];
	            imageData.put((byte) ((pixel >> 16) & 0xFF));     // Red component
	            imageData.put((byte) ((pixel >> 8) & 0xFF));      // Green component
	            imageData.put((byte) (pixel & 0xFF));               // Blue component
	            imageData.put((byte) ((pixel >> 24) & 0xFF));    // Alpha component. Only for RGBA               
	        }
	    }
	    
	    imageData.flip();
	    
	    //Generate texture
	    image = glGenTextures();
	    
	    glEnable(GL_TEXTURE_2D);
	    glBindTexture(GL_TEXTURE_2D,image);
		glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,512,512,0,GL_RGBA,GL_UNSIGNED_BYTE,imageData);
		
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	}
}


I'm obviously missing something but I can't figure out what. The texture I try to draw remains entirely blank and nothing is being rendered. Also, which lines are required and which not? I'd like to learn about it by playing around but I don't know which lines are required and which aren't. I haven't used AWT but I'm very familiar with the HTML5 canvas and it's graphics context. I think I understand what you wanted to let me do here. It's like creating a canvas with a context, render the font's glyps 1 by 1 and create an image from it isn't it?

SHC

This is your error:

glBegin(GL_QUADS);
    glVertex2f(x,y);
    glVertex2f(x+512,y);
    glVertex2f(x+512,y+512);
    glVertex2f(x,y+512);
             
    glTexCoord2f(0,0);
    glTexCoord2f(1,0);
    glTexCoord2f(1,1);
    glTexCoord2f(0,1);
glEnd();

You have to add texture-coordinates just after adding the vertices. Add the vertex, then the corresponding texture coordinate. Again, add the vertex and it's texture coordinate and follow the order.

TheBoneJarmer

Oh god I feel like an idiot, thanks! I've been using VAO's and VBO's lately so I completely got used their vertex orders. Things are still screwed up though (I don't see any glyphs, just some random pixels at the end of the image or something) but at least I do see something. Now I got at least some visual results I can go on with.

Leycarno

Hi BoneJarmer,

I'm just at this Point for my app now - do you have completed your class?


Leycarno

I modify your code just a LITTLE...
Now it works usefull, but there ist still lot more more to do... but I need sleep  ;D


import java
.awt.*;
import java.awt.image.*;
import java.io.*;
import java.nio.*;
import java.util.HashMap;
import java.util.Map;

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

public class 
TrueTypeFont {
    private 
String path;
    private 
Font font;
    private 
ByteBuffer imageData;
    private 
BufferedImage imageBuffer;
    private 
Graphics2D imageGraphics;
    private 
int image 0;

    private static final 
Map<IntegerStringCHARS = new HashMap<IntegerString>() {{
        
put(1"ABCDEFGHIJKLMNOPQRSTUVWXYZ");
        
put(2"abcdefghijklmnopqrstuvwxyz");
        
put(3"0123456789");
        
put(4"ÄÃâ€"ÃÅ"äöüß");   // Yep - I am from Germany ^^
        
put(5"$+-*/=%\"'#@&_(),.;:?!\\|<>[]Ã,§`^~");
    }};

    public 
TrueTypeFont(String paththrows Exception {
        
this.path path;

        try {
            
font Font.createFont(java.awt.Font.TRUETYPE_FONT, new File(path)).deriveFont(32f);

            
generateTexture();

        } catch (
FontFormatException ex) {
            throw new 
Exception("Font is not a TTF!");
        } catch (
IOException ex) {
            throw new 
Exception(ex);
        }
    }

    
//Draws the bitmap generated by the class
    
public void drawBitmap(float xfloat y) {
        
glBindTexture(GL_TEXTURE_2Dimage);
        
glBegin(GL_QUADS);

        
glTexCoord2f(00);
        
glVertex3f(xy0);

        
glTexCoord2f(10);
        
glVertex3f(512y0);

        
glTexCoord2f(11);
        
glVertex3f(5125120);

        
glTexCoord2f(01);
        
glVertex3f(x5120);

        
glEnd();
    }

    public 
void drawText(String textfloat xfloat y) {
        
glBindTexture(GL_TEXTURE_2Dimage);
        
glBegin(GL_QUADS);
        for (
char c text.toCharArray()) {
            for (
int index CHARS.keySet()) {
                
float p = (float) CHARS.get(index).indexOf(c);
                if (
>= 0) {
                    
float recY 1f 512f 32f;
                    
float recX 1f 512f 16f;
                    
float xStart recX p;
                    
float yStart recY * (index 1);

                    
glTexCoord2f(xStartyStart);
                    
glVertex3f(xy0);

                    
glTexCoord2f(xStart recXyStart);
                    
glVertex3f(16y0);

                    
glTexCoord2f(xStart recXyStart recY);
                    
glVertex3f(16320);

                    
glTexCoord2f(xStartyStart recY);
                    
glVertex3f(x320);

                    break;
                }
            }
            
+= 16;
        }

        
glEnd();
    }

    private 
void generateTexture() {

//Configure
        
GraphicsConfiguration gc GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
        
Graphics2D graphics gc.createCompatibleImage(11Transparency.TRANSLUCENT).createGraphics();
        
graphics.setFont(font);

        
//Create the image buffer
        
FontMetrics fontMetrics graphics.getFontMetrics();
        
BufferedImage imageBuffer graphics.getDeviceConfiguration().createCompatibleImage(512512Transparency.TRANSLUCENT);

//Draw the characters on our image
        
imageGraphics = (Graphics2DimageBuffer.getGraphics();
        
imageGraphics.setFont(font);
        
imageGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASINGRenderingHints.VALUE_ANTIALIAS_OFF);
        
imageGraphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASINGRenderingHints.VALUE_TEXT_ANTIALIAS_ON);

        
// draw every CHAR by line...
        
imageGraphics.setColor(Color.WHITE);
        
CHARS.keySet().stream().forEach(-> {
            
imageGraphics.drawString(CHARS.get(i), 0fontMetrics.getMaxAscent() * i);
        });


//Generate texture data
        
int[] pixels = new int[imageBuffer.getWidth() * imageBuffer.getHeight()];
        
imageBuffer.getRGB(00imageBuffer.getWidth(), imageBuffer.getHeight(), pixels0imageBuffer.getWidth());
        
imageData ByteBuffer.allocateDirect((imageBuffer.getWidth() * imageBuffer.getHeight() * 4));

        for (
int y 0imageBuffer.getHeight(); y++) {
            for (
int x 0imageBuffer.getWidth(); x++) {
                
int pixel pixels[imageBuffer.getWidth() + x];
                
imageData.put((byte) ((pixel >> 16) & 0xFF));     // Red component
                
imageData.put((byte) ((pixel >> 8) & 0xFF));      // Green component
                
imageData.put((byte) (pixel 0xFF));               // Blue component
                
imageData.put((byte) ((pixel >> 24) & 0xFF));    // Alpha component. Only for RGBA
            
}
        }

        
imageData.flip();

        
//Generate texture
        
image glGenTextures();

        
glEnable(GL_TEXTURE_2D);
        
glBindTexture(GL_TEXTURE_2Dimage);
        
glTexImage2D(GL_TEXTURE_2D0GL_RGBA5125120GL_RGBAGL_UNSIGNED_BYTEimageData);

        
glTexParameteri(GL_TEXTURE_2DGL_TEXTURE_MIN_FILTERGL_LINEAR);
        
glTexParameteri(GL_TEXTURE_2DGL_TEXTURE_MAG_FILTERGL_LINEAR);
    }
}



IMPORTANT -> you need a MONOSPACE (i use this: http://www.dafont.com/de/monofonto.font)
I will improve this part later...

um.. and you need java 8...

Here my (working) test:

TrueTypeFont font = new TrueTypeFont("font\\monofonto.ttf");
font.drawBitmap(00);
font.drawText("HALLO World ! ;-)"600100);



PS: Sorry for ugly format, but code dont work for me at the moment, so i use the BB-Code php-tag...

TheBoneJarmer

Oh hey and sorry! Haven't been online for a while but no, I didn't complete my class to be honest. I got stuck after a couple of tries, and duties required me to use my time for other business so I didn't spent much more efforts into it. In fact, I have to thank you for your code. I haven't tried it yet but you did what I could not.

Leycarno

Hi :)

here an all-purpose version for any TTF with any width for the Chars...

First class is just a utility for the AWT-stuff:

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Map;

public class Ley2dFontUtilAwt {

    private static final float DEFAULT_FONT_SIZE = 32f; // ok THIS is specific... you can give it as param of construct...
    private static final Map< Integer, String> CHARS = new HashMap< Integer, String>() {{
        put(0, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
        put(1, "abcdefghijklmnopqrstuvwxyz");
        put(2, "0123456789");
        put(3, "ÄÃâ€"ÃÅ"äöüß");
        put(4, "$+-*/=%\"'#@&_(),.;:?!\\|<>[]Ã,§`^~");
    }};

    private Font font;
    private FontMetrics fontMetrics;

    public Ley2dFontUtilAwt(String ttfFilename) throws Throwable {
        this.loadFont(ttfFilename);
    }

    public ByteBuffer getFontAsByteBuffer() {

        BufferedImage bufferedImage = this.generateBufferedImage();

        this.drawFontChars(bufferedImage);

        ByteBuffer byteBuffer = this.generateByteBuffer(bufferedImage);

        return byteBuffer;
    }

    public float getFontImageWidth() {
        return (float) CHARS.values().stream()
                .mapToDouble(e -> fontMetrics.getStringBounds(e, null).getWidth())
                .max().getAsDouble();
    }

    public float getFontImageHeight() {
        return (float) CHARS.keySet().size() * (this.getCharHeight());
    }

    /**
     * @return the "start"-PositionX of the Char on the FontImage
     */
    public float getCharX(char c) {
        String originStr = CHARS.values().stream()
                .filter(e -> e.contains("" + c))
                .findFirst()
                .orElse("" + c);
        return (float) fontMetrics.getStringBounds(originStr.substring(0, originStr.indexOf(c)), null).getWidth();
    }

    /**
     * @return the "start"-PositionY of the Char on the FontImage
     */
    public float getCharY(char c) {
        float lineId = (float) CHARS.keySet().stream()
                .filter(i -> CHARS.get(i).contains("" + c))
                .findFirst()
                .orElse(0);
        return this.getCharHeight() * lineId;
    }

    /**
     * @return the width of the specific Character
     */
    public float getCharWidth(char c) {
        return fontMetrics.charWidth(c);
    }

    /**
     * Hint:
     *      getMaxAscent is the max Height above the baseline
     *      getMaxDescent is the max Height under the baseline
     *
     * @returns the max Height of every possible Char
     */
    public float getCharHeight() {
        return (float) (fontMetrics.getMaxAscent() + fontMetrics.getMaxDescent());
    }

    private BufferedImage generateBufferedImage() {
        //Configure
        GraphicsConfiguration gc = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration();
        Graphics2D graphics = gc.createCompatibleImage(1, 1, Transparency.TRANSLUCENT).createGraphics();
        graphics.setFont(font);
        fontMetrics = graphics.getFontMetrics();
        return graphics
                .getDeviceConfiguration()
                .createCompatibleImage(
                        (int) getFontImageWidth(),
                        (int) getFontImageHeight(),
                        Transparency.TRANSLUCENT);
    }

    private void drawFontChars(BufferedImage imageBuffer) {
        //Draw the characters on our image
        Graphics2D imageGraphics = (Graphics2D) imageBuffer.getGraphics();
        imageGraphics.setFont(font);
        imageGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
        imageGraphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);

        // draw every CHAR by line...
        imageGraphics.setColor(Color.WHITE);
        CHARS.keySet().stream().forEach(i -> imageGraphics.drawString(CHARS.get(i), 0, fontMetrics.getMaxAscent() + (this.getCharHeight() * i)));
    }

    private ByteBuffer generateByteBuffer(BufferedImage imageBuffer) {
        //Generate texture data
        int[] pixels = new int[imageBuffer.getWidth() * imageBuffer.getHeight()];
        imageBuffer.getRGB(0, 0, imageBuffer.getWidth(), imageBuffer.getHeight(), pixels, 0, imageBuffer.getWidth());
        ByteBuffer imageData = ByteBuffer.allocateDirect((imageBuffer.getWidth() * imageBuffer.getHeight() * 4));

        for (int y = 0; y < imageBuffer.getHeight(); y++) {
            for (int x = 0; x < imageBuffer.getWidth(); x++) {
                int pixel = pixels[y * imageBuffer.getWidth() + x];
                imageData.put((byte) ((pixel >> 16) & 0xFF));   // Red component
                imageData.put((byte) ((pixel >> 8) & 0xFF));    // Green component
                imageData.put((byte) (pixel & 0xFF));           // Blue component
                imageData.put((byte) ((pixel >> 24) & 0xFF));   // Alpha component. Only for RGBA
            }
        }
        imageData.flip();
        return imageData;
    }

    private void loadFont(String ttfFilename) throws Throwable {
        try {
            font = Font.createFont(java.awt.Font.TRUETYPE_FONT, new File(ttfFilename))
                    .deriveFont(DEFAULT_FONT_SIZE);
        } catch (FontFormatException ex) {
            throw new Exception("Font is not a TTF!");
        } catch (IOException ex) {
            throw new Exception(ex);
        }
    }
}


And the second Class with the LWJGL-Stuff using the AwtUtil:

import java.nio.ByteBuffer;

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

class Ley2dFontLWJGL3 {

    private Ley2dFontUtilAwt fontUtil;
    private int fontTextureId;

    Ley2dFontLWJGL3(String ttfFilename) throws Throwable {
        fontUtil = new Ley2dFontUtilAwt(ttfFilename);
        generateTexture(fontUtil.getFontAsByteBuffer());
    }

    private void generateTexture(ByteBuffer bb) {
        this.fontTextureId = glGenTextures();

        glEnable(GL_TEXTURE_2D);
        glBindTexture(GL_TEXTURE_2D, this.fontTextureId);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
                (int) fontUtil.getFontImageWidth(),
                (int) fontUtil.getFontImageHeight(),
                0, GL_RGBA, GL_UNSIGNED_BYTE, bb);

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    }

    public void drawFontTexture(int x, int y) {
        glBindTexture(GL_TEXTURE_2D, this.fontTextureId);
        glBegin(GL_QUADS);

        glTexCoord2f(0, 0);
        glVertex3f(x, y, 0);

        glTexCoord2f(1, 0);
        glVertex3f(x + fontUtil.getFontImageWidth(), y, 0);

        glTexCoord2f(1, 1);
        glVertex3f(x + fontUtil.getFontImageWidth(), y + fontUtil.getFontImageHeight(), 0);

        glTexCoord2f(0, 1);
        glVertex3f(x, y + fontUtil.getFontImageHeight(), 0);

        glEnd();
    }

    public void drawText(String text, int xPosition, int yPosition) {
        glBindTexture(GL_TEXTURE_2D, this.fontTextureId);
        glBegin(GL_QUADS);
        int xTmp = xPosition;
        for (char c : text.toCharArray()) {
            float width = fontUtil.getCharWidth(c);
            float height = fontUtil.getCharHeight();
            float w = 1f / fontUtil.getFontImageWidth() * width;
            float h = 1f / fontUtil.getFontImageHeight() * height;
            float x = 1f / fontUtil.getFontImageWidth() * fontUtil.getCharX(c);
            float y = 1f / fontUtil.getFontImageHeight() * fontUtil.getCharY(c);

            glTexCoord2f(x, y);
            glVertex3f(xTmp, yPosition, 0);

            glTexCoord2f(x + w, y);
            glVertex3f(xTmp + width, yPosition, 0);

            glTexCoord2f(x + w, y + h);
            glVertex3f(xTmp + width, yPosition + height, 0);

            glTexCoord2f(x, y + h);
            glVertex3f(xTmp, yPosition + height, 0);

            xTmp += width;
        }
        glEnd();
    }
}


PS: the Problem with the code-Tag is:
<IntegerString>
<Integer, String>

Solution: Space before the Integer!
IntegerString>
< Integer, String>


Best Regards
Ley