Slick Utils and the Modern OpenGL 3+

FYI: I have finally broken through the OpenGL 3+ barrier with the help of a series of tutorials by ThinMatrix


BTW: These tutorials are amazing! Need a little code cleanup/organization when done, but they explain Many concepts wonderfully

Now, to my Question:

I am trying to use Slick Utils to render fonts to the screen.

Problem is, Slick Utils uses older OpenGL methods like Immediate Mode to draw them.

I found a source code for the Slick Utils TrueTypeFont parser here:
https://raw.githubusercontent.com/ariejan/slick2d/master/src/org/newdawn/slick/TrueTypeFont.java

I then rewrote the code to use textured quad VBOs to render the fonts, as is the newer way to do fonts.

once I spent about 3 hours rewriting it, it all came down to 1 line of code that won’t work still. The line in question is the one that converts a BufferedImage to a Slick Utils Texture.

I keep getting this error:


Exception in thread "main" org.lwjgl.opengl.OpenGLException: Invalid enum (1280)
	at org.lwjgl.opengl.Util.checkGLError(Util.java:59)
	at org.lwjgl.opengl.GL11.glEnable(GL11.java:1061)
	at org.newdawn.slick.opengl.renderer.ImmediateModeOGLRenderer.glEnable(ImmediateModeOGLRenderer.java:159)
	at org.newdawn.slick.util.BufferedImageUtil.getTexture(BufferedImageUtil.java:99)
	at org.newdawn.slick.util.BufferedImageUtil.getTexture(BufferedImageUtil.java:39)

my code call is as follows:


BufferedImageUtil.getTexture("",fontImage);

Here is the whole class for those who want it:
[spoiler]
EDIT: Guess spoilers aren’t working, sorry


package text;

//<editor-fold defaultstate="collapsed" desc="Imports">

import java.awt.*;
import java.awt.image.*;
import java.io.*;
import java.util.*;
import org.newdawn.slick.opengl.*;
import org.newdawn.slick.util.*;



//</editor-fold>

/**
 * 
 * @since Jul 31, 2015
 * @update Jul 31, 2015
 * @version 0.07
 */
public class Letter{

    // Variable Declarations
    //<editor-fold defaultstate="!collapsed" desc="Variable Declarations">
    private int width;
        public int getWidth(){
            return width;
        }
        public void setWidth(int width){
            this.width = width;
        }
    private int height;
        public int getHeight(){
            return height;
        }
        public void setHeight(int height){
            this.height = height;
        }
    private Texture texture;
        public Texture getTexture(){
            return texture;
        }
        public void setTexture(Texture texture){
            this.texture = texture;
        }
    private String fontName;
        public String getFontName(){
            return fontName;
        }
        public void setFontName(String fontName){
            this.fontName = fontName;
        }
    private String letterName;
        public String getLetterName(){
            return letterName;
        }
        public void setLetterName(String letterName){
            this.letterName = letterName;
        }
	 private static FontMetrics fontMetrics;
    private static boolean antiAlias;
    
    private static HashMap<String, HashMap<Character, Letter>> fonts = new HashMap<>(10);
        public static Letter[] getMessageLetters(String fontName, String message){
            Letter[] messageLetters = new Letter[message.length()];
            
            int spacing = 0;
            for(int i = 0; i < message.length(); i++){
                messageLetters[i] = fonts.get(fontName).get(message.charAt(i));
                spacing += messageLetters[i].getWidth();
                messageLetters[i].setLetterOffsetInPixels(spacing);
            }
            
            return messageLetters;
        }
        
    private int letterOffsetInPixels;
        public void setLetterOffsetInPixels(int offset){
            this.letterOffsetInPixels = offset;
        }
        public int getLetterOffsetInPixels(){
            return letterOffsetInPixels;
        }
    private float positionX; // used later for 2 or 3D positioning
        public float getPositionX(){
            return positionX;
        }
        public void setPositionX(float positionX){
            this.positionX = positionX;
        }
    private float positionY;
        public float getPositionY(){
            return positionY;
        }
        public void setPositionY(float positionY){
            this.positionY = positionY;
        }
    private float positionZ;
        public float getPositionZ(){
            return positionZ;
        }
        public void setPositionZ(float positionZ){
            this.positionZ = positionZ;
        }
        
    //</editor-fold>

    // Constructors
    //<editor-fold defaultstate="!collapsed" desc="Constructors">
    /**
     * @param width
     * @param height
     * @param texture
     * @param fontName
     */
    public Letter(int width, int height, Texture texture, String fontName){
        this.width = width;
        this.height = height;
        this.texture = texture;
        this.fontName = fontName;
    }
    //</editor-fold>

    // Publicly accessable Methods to control functions of this class
    //<editor-fold defaultstate="!collapsed" desc="Public Methods">
    public static void initializeFont(Font font, int fontSize, char[] customCharsArray, Color color){
        int fontCount = 256;
        if(customCharsArray != null){
            fontCount += customCharsArray.length;
        }
        HashMap<Character, Letter> letters = new HashMap<>(fontCount);
        try {
            for(int i = 0; i < fontCount; i++){
                if(i < 256){
                    // send letter i
                    letters.put((char)i,createLetter((char) i, fontSize, font, color));
                }else{
                    // send custom char i
                    letters.put((char)i,createLetter(customCharsArray[i-256], fontSize, font, color));
                }
            }
        } catch (IOException ex){
            System.out.println("Error loading font: " + font.getFontName());
            ex.printStackTrace();
            System.exit(-1);
        }
        fonts.put(font.getFontName(), letters);
    }
    public static void initializeFont(String fontName, int fontSize){
        Font font = new Font(fontName, Font.PLAIN, fontSize);
        initializeFont(font, fontSize, null, Color.WHITE);
    }
    public static void initializeFont(String fontName, int fontStyle, int fontSize){
        Font font = new Font(fontName, fontStyle, fontSize);
        initializeFont(font, fontSize, null, Color.WHITE);
    }
    public static void initializeFont(String fontName, int fontSize, Color color){
        Font font = new Font(fontName, Font.PLAIN, fontSize);
        initializeFont(font, fontSize, null, color);
    }
    public static void initializeFont(String fontName, int fontStyle, int fontSize, Color color){
        Font font = new Font(fontName, fontStyle, fontSize);
        initializeFont(font, fontSize, null, color);
    }
    //</editor-fold>

    // Internal Methods used within this class only
    //<editor-fold defaultstate="!collapsed" desc="Internal Methods">
    private static Letter createLetter(char character, int fontSize, Font font, Color color) throws IOException{
		// Create a temporary image Just to extract char sizes from
		BufferedImage tempfontImage = new BufferedImage(1, 1,
				BufferedImage.TYPE_INT_ARGB);
        // Create a Graphics context for calculations
		Graphics2D g1 = tempfontImage.createGraphics();
        
        // If Anti-aliasing is desired, set option
		if (antiAlias == true) {
			g1.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
					RenderingHints.VALUE_ANTIALIAS_ON);
		}
        
        // Set Graphics Context Font to desired font and generate metrics
		g1.setFont(font);
		fontMetrics = g1.getFontMetrics();
        
        // Get Character Width
		int charWidth = fontMetrics.charWidth(character);
		if (charWidth <= 0) {
			charWidth = 1;
		}
        // Get Character Height
		int charHeight = fontMetrics.getMaxAscent() + fontMetrics.getMaxDescent();
		if (charHeight <= 0) {
			charHeight = fontSize;
		}

        
        
		// Create another image to hold the character we are creating
		BufferedImage fontImage = new BufferedImage(charWidth, charHeight, BufferedImage.TYPE_INT_ARGB);
        // Create a Graphics context for drawing the character to
		Graphics2D g2 = fontImage.createGraphics();
        // If Anti-aliasing is desired, set option
		if (antiAlias == true) {
			g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
					RenderingHints.VALUE_ANTIALIAS_ON);
		}
        
        // Set Graphics Context Font to desired font
		g2.setFont(font);

        // Set drawing color to white - leaving background transparent
		g2.setColor(color);
		g2.drawString(String.valueOf(character), 0, 0 + fontMetrics.getMaxAscent());

        
        Letter letter = new Letter(charWidth, 
                                   charHeight, 
                                   BufferedImageUtil
                                       .getTexture(
                                           String.valueOf(character),
                                           fontImage), 
                                   font.getFontName());
        
        return letter;
    }
    //</editor-fold>
}


End Code.
[/spoiler]

Is there a Modern OpenGL 3+ way to convert ImageBuffers to Textures?
Is there a different command?
is there a sourceCode to that util that I can rewrite?
or Some other way to do it?

I don’t know much about Slick, but I seem to remember reading that it’s not being updated anymore. Assuming that’s actually the case, have you considered just writing the needed functionality yourself? It’d be additional work of course, but it might be easier than trying to combine your own code with whatever Slick is doing.

In any case, I assume Slick is open-source and that you can take a look at the problematic function. Can you see what’s going on in the glEnable() call that seems to be causing the error? (You also might double-check to make sure the error is actually occurring there, rather than occurring in your own code somewhere and just being reported there.)

Slick was the Go-To library for pre-OpenGL 3.0. If it isn’t being updated anymore, is there a better/newer library that uses the newer OpenGL 3+ features?

I need one that plays well with LWJGL, OpenGL 3+ and Java in general.

As you can see above, the reading in of the font is not the issue. I end up with a BufferedImage that contains everything I want. However, I don’t know how to make the leap from BufferedImage to Slick Texture.

I chose slick Textures because the retrieving of the external png, storing it, accessing it, and binding it to a face is all done in a very code-easy way.

Is there a better way for modern setups?

(and Yes, I am sure the line mentioned is the problematic code and it is pre-compiled so I can’t see the code - Slick uses Immediate Mode to render text, that’s why I had to rewrite this class to begin with)

I remember reading that Slick is no longer maintained, but I could be wrong (I’m not sure where I read it). It does sound though like it doesn’t use modern techniques consistently under the hood. As for alternatives, the only thing I can think of is the oft-mentioned LibGDX, but maybe you’ve already considered that and decided not to use it for one reason or another.

[quote](and Yes, I am sure the line mentioned is the problematic code and it is pre-compiled so I can’t see the code - Slick uses Immediate Mode to render text, that’s why I had to rewrite this class to begin with)
[/quote]
Isn’t Slick open source? It seems like you should be able to find out what’s going on in that line of code. Also, are you sure the ‘invalid enum’ error isn’t being generated by your own code somewhere and just happens to be being caught by the Slick code? (Apologies if I’m misunderstanding anything.)

Lastly, it might be worth considering whether whatever functionality Slick is providing is something you could just replicate yourself, which would free you from the dependency and might make debugging easier. Maybe you have a reason for not wanting to do that though.