Search code examples
javaimagelwjgl

LWJGL Textures and Strings


Is it possible to load PNG Textures and draw Strings in LWJGL WITHOUT using the Slick Framework?

Everytime I google "how to load png images in lwjgl" I get answers like this -> "hey just use the textureloader from the slick framework".
Same for "how to draw strings in lwjgl" -> "just use the TTFFont Class from the slick framework"

But I don't want to use this half-way-crossframework design. Because I don't think that this is the best way.

Are there any Libraries or Extensions for LWJGL that are only made for Textures or Strings?


Solution

  • Basically, you take a BufferedImage, use getRGB() to get the RGB of each pixel, take that data and put it into a ByteBuffer (the data type used to input image data to OpenGL), set some texture data, and create the GL_TEXTURE_2D.

    This code by Krythic does it:

    import java.awt.image.BufferedImage;
    import java.io.IOException;
    import java.nio.ByteBuffer;
    
    import javax.imageio.ImageIO;
    
    import org.lwjgl.BufferUtils;
    import org.lwjgl.opengl.GL12;
    
    import static org.lwjgl.opengl.GL11.*;
    
    public class TextureLoader {
        private static final int BYTES_PER_PIXEL = 4;//3 for RGB, 4 for RGBA
           public static int loadTexture(BufferedImage image){
    
              int[] pixels = new int[image.getWidth() * image.getHeight()];
                image.getRGB(0, 0, image.getWidth(), image.getHeight(), pixels, 0, image.getWidth());
    
                ByteBuffer buffer = BufferUtils.createByteBuffer(image.getWidth() * image.getHeight() * BYTES_PER_PIXEL); //4 for RGBA, 3 for RGB
    
                for(int y = 0; y < image.getHeight(); y++){
                    for(int x = 0; x < image.getWidth(); x++){
                        int pixel = pixels[y * image.getWidth() + x];
                        buffer.put((byte) ((pixel >> 16) & 0xFF));     // Red component
                        buffer.put((byte) ((pixel >> 8) & 0xFF));      // Green component
                        buffer.put((byte) (pixel & 0xFF));               // Blue component
                        buffer.put((byte) ((pixel >> 24) & 0xFF));    // Alpha component. Only for RGBA
                    }
                }
    
                buffer.flip(); //FOR THE LOVE OF GOD DO NOT FORGET THIS
    
                // You now have a ByteBuffer filled with the color data of each pixel.
                // Now just create a texture ID and bind it. Then you can load it using 
                // whatever OpenGL method you want, for example:
    
              int textureID = glGenTextures(); //Generate texture ID
                glBindTexture(GL_TEXTURE_2D, textureID); //Bind texture ID
    
                //Setup wrap mode
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
    
                //Setup texture scaling filtering
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    
                //Send texel data to OpenGL
                glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, image.getWidth(), image.getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
    
                //Return the texture ID so we can bind it later again
              return textureID;
           }
    
           public static BufferedImage loadImage(String loc)
           {
                try {
                   return ImageIO.read(MainClass.class.getResource(loc));
                } catch (IOException e) {
                    //Error Handling Here
                }
               return null;
           }
    }
    

    To use this code, do something like this:

    BufferedImage image = TextureLoader.loadImage("/res/test.png");//The path is inside the jar file
    int textureID = TextureLoader.loadTexture(image);
    

    You can either save the textureID as a final variable(if the texture never changes), or unload the texture after each render using GL11.glDeleteTextures(textureID);

    To do text, just create a BufferedImage manually, and use createGraphics() to get a graphics2D() instance for the image. Then, use drawString() to draw onto the BufferedImage, load it into the TextureLoader, render it onscreen, and unload the texture using the method above.