Main Menu

LWJGL3 Texture

Started by n0mad, November 22, 2014, 12:09:56

Previous topic - Next topic

n0mad

Hello guys! Are there any standard means lwjgl3, which suitable to replace the org.newdawn.slick.opengl.Texture? I apologize if this question has already been, but search not give any result for me. If there is no replacement, it be planned in the future with the possibility of converting from texture of java.awt.image.BufferedImage and back?

chris_c

I used the PNG decoder from the TWL gui library (http://hg.l33tlabs.org/twl you can isolate the single java file very easily - it stands alone) - which basically allowed me to load a png into a buffer...

            FileInputStream in = new FileInputStream("data/wall.png");
            PNGDecoder decoder = new PNGDecoder(in);
            // assuming RGB here but should allow for RGB and RGBA (changing wall.png to RGBA will crash this!)
            ByteBuffer buf = ByteBuffer.allocateDirect(3*decoder.getWidth()*decoder.getHeight());
            decoder.decode(buf, decoder.getWidth()*3, PNGDecoder.Format.RGB);
            buf.flip();

            texture=glGenTextures();
            glBindTexture(GL_TEXTURE_2D, texture);
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, decoder.getWidth(), decoder.getHeight(), 0,
                    GL_RGB, GL_UNSIGNED_BYTE, buf);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
            in.close();

n0mad

this is no problem to get ByteBuffer or ByteArray from file, for java.awt.image.Image  / BufferedImage i do so:
try {
InputStream IS = Main.class.getResourceAsStream("res/texture.png");
ByteArrayOutputStream BAOS = new ByteArrayOutputStream();
int read1 = IS.read(); while (read1 != -1) {BAOS.write(read1); read1 = IS.read();}
byte[] imageBA = BAOS.toByteArray();
BAOS.close();
BufferedImage imageBI = ImageIO.read(new ByteArrayInputStream(imageBA));
} catch (IOException e) {e.printStackTrace();}
// and then add new Object with Texture:
Texture image = org.newdawn.slick.util.BufferedImageUtil.getTexture("", imageBI);
new Object(image);

the problem is that i previously (before LWJGL3) used Slick Texture for store texture of the object in its class, and then i just use object.getTexture().bind(); before glBegin...glEnd and this worked fine!
but now Slick is not ready for LWJGL3, and i thought that it is strange that LWJGL haven't independent Texture class, - because LWJGL to work with opengl, which are always used textures

chris_c

The advantage of using the PNG decoder is you don't have to drag in awt...

Opengl has no need of a texture class, you simply show it where the data is and it returns a "handle" to that texture...

n0mad

yes, thanks! but how about OGL applications with awt frames when awt images needed?
PNG Loader needed for load the texture image to opengl, but for some reason this is a third-party library also
awt this is standart in java, texture loading in awt/OGL apps - too. just, me seems, that need some support for awt without another libs and need own picture loader for all major texture formats (PNG, BMP, GIF, JPG)  in based lwjgl library

n0mad

in the end i did something similar to this advice, and now my objects have OGL texture and BufferedImage cache for awt (if it will needed).
http://www.java-gaming.org/index.php?topic=25516.0
         private final int BYTES_PER_PIXEL = 4;
         private 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_LINEAR);
         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        
         //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;

lightbringer

AWT is so ancient, we might as well consider it deprecated functionality. It spawns its own threads, too. With GLFW and tools like PNGDecoder you get to bypass all that. IMHO it's better to stay away from all AWT libraries (I don't use BufferedImage anymore) and also to dispose of the local texture data after uploading it to OpenGL - makes no sense to me to waste RAM on that unless you have some very specific requirements that demand it. You can get away with it for smaller use cases but once you have a significant amount of texture data loaded at once, it's not really an option to keep it around twice. Also, loading image data is a bit orthogonal to the core of LWJGL (providing bindings to native libraries).

You can roll your own Texture class easily though. It can be as simple as a wrapper around an int (texture object) with some syntactic sugar (glGenTextures, maybe binding and glActiveTexture, maybe format loading and OpenGL upload though I put that in separate loader/factory classes). Once you want to do async loading and proxy textures or support pregenerated mipmaps or cubemaps or volumetric textures or texture arrays, it gets a bit more complicated, but essentially you're still dealing with a glorified integer.

chris_c

Most I've done in the past is an int loadTexture(String url) in my utilties class....

n0mad

Yes, chris_c, lightbringer - thank you for the wise philosophy, you right!
i may want to completely abandon the awt, but, for example this code - is not ideal for loading a texture without third-party libraries? but there use BufferedImage. probably, PNGLoader has meaning only if it read bytes faster than the awt.... or am i wrong?
of course, not saving awt, only load here..
public class TextureLoad {
    
    private static final int BYTES_PER_PIXEL = 4;

    public static int newTexture(final String path){
        int texture = 0;
        try {
        InputStream IS = Main.class.getResourceAsStream(path);
        ByteArrayOutputStream BAOS = new ByteArrayOutputStream();
        int read1 = IS.read(); while (read1 != -1) { BAOS.write(read1); read1 = IS.read(); }
        byte[] textureBA = BAOS.toByteArray();
        BAOS.close();
        BufferedImage textureBI = ImageIO.read(new ByteArrayInputStream(textureBA));
        texture = loadTexture(textureBI);
        Main.log("Texture load > Buffered image: " + textureBI.getWidth() + "x" + textureBI.getHeight() + " / Texture ID: " + texture);
        } catch (IOException e) {e.printStackTrace();}    
        return texture;
    }

    private 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 newTexture it using 
        // whatever OpenGL method you want, for example:
        int textureID = GL11.glGenTextures(); //Generate texture ID
        GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureID); //Bind texture ID
        //Setup wrap mode
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
        //Setup texture scaling filtering
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
        //Send texel data to OpenGL
        GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, image.getWidth(), image.getHeight(), 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer);
        //Return the texture ID so we can bind it later again
        return textureID;
    }
    
}

lightbringer

PNGDecoder is only one class, and it's open sourced. You can just copy paste it into your project, and then you will have no dependency on 3rd party libraries and no dependency on AWT either.

I have not tested PNGDecoder speed vs ImageIO, although I'd be very much surprised if PNGDecoder is slower. PNGDecoder is a specialized solution - it only deals with PNG files, that's it - while ImageIO is a generalized solution that needs to do a lot more checks and find an appropriate decoder algorithm.

I prefer to have my own loaders for each texture format but that's definitely not the only possible approach. If ImageIO works for you then you can certainly keep using it :) I just wanted to get rid of all AWT code so I took out the ImageIO stuff as well :)

In ancient versions of LWJGL we had a DevIL binding for loading images. Admittedly, I kind of miss that :)

spasi

I'm open to suggestions for bindings to one or more image loading libraries. OpenEXR is already under consideration.

lightbringer

ImageMagick is the one that immediately comes to mind. But it seems they already have a JNI wrapper over their C API. I have not used it though.

Zeroni

Thanks for the reference. I will be using JMagick(ImageMagick) from now on. Mad props bro. OpenEXR is seems like a good library as well. I would be ecstatic if LWJGL had bindings for image and sound writing and reading.
- Zeroni