0
votes

I've been attempting to solve this problem without any luck. I have four classes, the main class, an Object class, a sprite class and an image loader class.

I'm attempting to load a png using the image loader and using the method found here: http://www.java-gaming.org/topics/bufferedimage-to-lwjgl-texture/25516/msg/220280/view.html#msg220280 to convert to a ByteBuffer, and then bind to OpenGL.

The Image is in it's own seperate resource folder. What should be being drawn is this: (http://i.imgur.com/j8GBU4c.png) (32x32), but what I'm seeing is a white box that has the dimensions of the image, but not the actual texture.

If anyone has any idea of where I could go to fix this I'd appreciate it. I'm new to OpenGL and looking to avoid using an external library in order to learn how the actual code works. Thanks.

Updated!

To anyone following, I implemented the suggestion provided by Vallentin, however the quad is now taking the color of the first pixel in the image. I've attempted implementing an image loader provided by C.S here. However now I'm receiving this error:

javax.imageio.IIOException: Error reading PNG image data
at com.sun.imageio.plugins.png.PNGImageReader.readImage(PNGImageReader.java:1291)
at com.sun.imageio.plugins.png.PNGImageReader.read(PNGImageReader.java:1560)
at pImageLoader.loadBIn(pImageLoader.java:60)
at Monkeybars.main(Monkeybars.java:35)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)

Caused by: javax.imageio.IIOException: Destination type from ImageReadParam does not match!
at javax.imageio.ImageReader.getDestination(ImageReader.java:2862)
at com.sun.imageio.plugins.png.PNGImageReader.readImage(PNGImageReader.java:1263)
... 8 more

Exception in thread "main" java.lang.NullPointerException
at pImageLoader.loadpSprite(pImageLoader.java:75)
at Monkeybars.main(Monkeybars.java:35)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at     sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)

I'm using IntelliJ 12 on Windows 7, using Java 6. The Image I'm trying to load is linked above. If anyone has any ideas, I'm open to suggestions.

Main Block:

   public static void main(String[] args){
    System.out.println("Sup!");
    int width = 800 ;
    int height = 600;
    try{
        Display.setDisplayMode(new DisplayMode(width,height));
        Display.setTitle("The Playground");
        Display.create();
    }catch (LWJGLException e){
        e.printStackTrace();
        Display.destroy();
        System.exit(1);
    }


    //Initialize OpenGL
    GL11.glMatrixMode(GL11.GL_PROJECTION);
    GL11.glLoadIdentity();
    GL11.glOrtho(0, width, height, 0f, 1f, -1f);

    GL11.glMatrixMode(GL11.GL_MODELVIEW);
    GL11.glLoadIdentity();
    GL11.glEnable(GL11.GL_TEXTURE_2D);
    GL11.glDisable(GL11.GL_DEPTH_TEST);
    GL11.glEnable(GL11.GL_BLEND);



    pObject MrRedSquare = new pObject(300f,300f,pImageLoader.loadpSprite(pImageLoader.loadBIn("test.png")));

    while(!Display.isCloseRequested()){

        GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
        GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
        MrRedSquare.drawMe();
        Display.sync(60);
        Display.update();
    }
}
}

Texture Loader, new method.

public static BufferedImage loadBIn(String filepath){

    BufferedImage image;
    // get the reader
    ImageReader ir = ImageIO.getImageReadersByFormatName("png").next();

    // get the default param
    ImageReadParam p = ir.getDefaultReadParam();
    p.setDestinationType(
            // define the image type to return if supported
            ImageTypeSpecifier.createInterleaved(
                    ColorSpace.getInstance(ColorSpace.CS_sRGB),
                    new int[] {0, 1, 2, 3},    // <-- the order of the color bands to return so the bytes are in the desired order
                    DataBuffer.TYPE_BYTE,
                    true, false)
    );

    try{
        InputStream stream = pImageLoader.class.getClassLoader().getResourceAsStream(filepath);
        ImageInputStream imageStream = ImageIO.createImageInputStream(stream);
        ir.setInput(imageStream);
        image = ir.read(0, p);
    }catch(Exception e){
        System.out.print("IMAGELOADER CANNOT OBTAIN ASSET");
        e.printStackTrace();
        return null;

    }
    return image;

}

Older code, kept for archival purposes

Main:

public static void main(String[] args){
    System.out.println("Sup!");
    try{
        Display.setDisplayMode(new DisplayMode(800,600));
        Display.setTitle("The Playground");
        Display.create();
    }catch (LWJGLException e){
        e.printStackTrace();
        Display.destroy();
        System.exit(1);
    }


    //Initialize OpenGL
    GL11.glMatrixMode(GL11.GL_PROJECTION);
    GL11.glLoadIdentity();
    GL11.glOrtho(0, 800, 0, 600, 1, -1);
    GL11.glMatrixMode(GL11.GL_MODELVIEW);


    pObject MrRedSquare = new pObject(300,300,pImageLoader.loadpSprite(pImageLoader.loadBI("square.png")));

    while(!Display.isCloseRequested()){
        GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
        MrRedSquare.drawMe();
        Display.sync(60);
        Display.update();
    }
}

Object (draw code):

    public void drawMe(){

    // store the current model matrix
    GL11.glPushMatrix();

    // bind to the appropriate texture for this sprite
    texture.bind();

    // translate to the right location and prepare to draw
    GL11.glTranslatef(x, y, 0);

    // draw a quad textured to match the sprite
    GL11.glBegin(GL11.GL_QUADS);
    {
        GL11.glTexCoord2f(0, 0);
        GL11.glVertex2f(0, 0);

        GL11.glTexCoord2f(0, texture.getHeight());
        GL11.glVertex2f(0, height);

        GL11.glTexCoord2f(texture.getWidth(), texture.getHeight());
        GL11.glVertex2f(width,height);

        GL11.glTexCoord2f(texture.getWidth(), 0);
        GL11.glVertex2f(width,0);
    }
    GL11.glEnd();

    // restore the model view matrix to prevent contamination
    GL11.glPopMatrix();

} 

Texture:

public class pSprite {

protected ByteBuffer spriteData;
protected BufferedImage spriteImage;
protected int id;

protected int width;
protected int height;

//---------------------------------------------------------------------------------------
//Constructors:

pSprite(BufferedImage sI, ByteBuffer s, int i){
    spriteImage = sI;
    spriteData = s;
    id = i;

    width = spriteImage.getWidth();
    height = spriteImage.getHeight();
}
//---------------------------------------------------------------------------------------
//Methods:
public void bind(){
    GL11.glBindTexture(GL11.GL_TEXTURE_2D, id);
} 

ImageLoader:

public class pImageLoader {

public static BufferedImage loadBI(String filepath){
    BufferedImage image;
    try{
        InputStream input = pImageLoader.class.getResourceAsStream(filepath);
        image = ImageIO.read(input);
    }catch (Exception e){
        System.out.print("IMAGELOADER: Cannot obtain asset.");
        e.printStackTrace();
        return null;
    }
    return image;
}

public static pSprite loadpSprite(BufferedImage image){

    //http://www.java-gaming.org/topics/bufferedimage-to-lwjgl-texture/25516/msg/220280/view.html#msg220280

    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() * 4); // <-- 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();

    int textureID = glGenTextures(); //Generate texture ID
    glBindTexture(GL_TEXTURE_2D, textureID);

    // 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 new pSprite(image, buffer, textureID);
    }
}
3
Not sure what the right output would be (the image you link to is the texture you expect to see or what you are actually seeing?), but the code for loading the image assumes too much for the byte order. Take a look at a question of mine. It might helpc.s.
Should have clarified, that's what I'm aiming to see. All I am viewing is a white box. For assuming too much, by that do you mean that it shouldn't assume it's loading an RGBA file?Ooo
First try what the answer below suggested. Try to enable glEnable(GL_TEXTURE_2D) before binding the texture for the first time and before uploading to OpenGL. This might be enough depending on your image (is it RGBA? RGB?). If you still have problems try to read the image in the way suggested in the answer I have provided to the link I gave, you should not make assumptions about the byte order returned when reading png files. But you can request to ImageIO to use the order you expect.c.s.
@Vallentin, The below answer seems to have helped, however now instead of a white quad it's a quad the color of the first pixel of the image. I attempted loading the image in the way you(@c.s) mentioned in your answer, however I'm getting an error "javax.imageio.IIOException: Destination type from ImageReadParam does not match!" and the only thing I changed from your code were the references to your class and test image. I tried substituting in your test image to see if that would help but to no avail. I'm using Java 6 on IntelliJ 12 on Windows if it makes any difference.Ooo
1) If you changed the code you are using please update it in the question and 2) can you add the image you are trying to use? (if it is not allowed to you yet, upload it somewhere and add a link)c.s.

3 Answers

2
votes

There are a lot of problems with your code. The most important issues I have found are:

1) The problems of yours, start from the fact that you are not using a 32-bit image (i.e. RGBA) but you are using a 24-bit image (i.e. RGB).

So the code you have used for specifying the image type is wrong. You should use the following:

p.setDestinationType(
    // define the image type to return if supported
    ImageTypeSpecifier.createInterleaved(
                    ColorSpace.getInstance(ColorSpace.CS_sRGB),
                    new int[] {0, 1, 2}, // <-- you are expecting 3 color bands since your image is RGB 
                    DataBuffer.TYPE_BYTE,
                    false,   //<-- this is alpha, your image doesn't have any
                    false)
);

2) As per the comments instrunctions of your image converting process, you are supposed to use a ByteBuffer of size width * height * 3. In general this is true. However it seems that LWJGL will not let you do that (it forces rectangular dimensions?) so you should leave this value to 4 and modify your alpha like:

buffer.put((byte) 0xFF);    // alpha always 1.0 i.e. opaque

This should load your image correctly. Please note that an additional problem in the original code (other than forgetting to use glEnable(GL_TEXTURE_2D)) was that you were not setting your blend function (btw you can set this once you do not need to set it in every frame). So even if you have used a 32-bit texture your transparent areas would appear black.

3) Your third problem is that when you are giving the texture coordinates you use glTexCoord2f(texture.getWidth(), texture.getHeight()); This is wrong because texture coordinates can range from [0, 1]. The only way this would work, would be if you had setup your texture parameters for GL_TEXTURE_WRAP_S, GL_TEXTURE_WRAP_T so they both use GL_REPEAT (the default). However you are setting it to GL_CLAMP_TO_EDGE. So if you give texture values greater than 1 the final image will have the color of your image border.

So you should modify your code like below:

GL11.glBegin(GL11.GL_QUADS);
{
    GL11.glTexCoord2f(0, 0);   // <--
    GL11.glVertex2f(0, 0);

    GL11.glTexCoord2f(0, 1);   // <--
    GL11.glVertex2f(0, height);

    GL11.glTexCoord2f(1, 1);   // <--
    GL11.glVertex2f(width, height);

    GL11.glTexCoord2f(1, 0);   // <--
    GL11.glVertex2f(width,0);
}

My advice is that when you are experimenting with textures, use a large simple image so you can see what's happenning (correct alpha, upside-down etc) e.g. a red rectangle with an inner blue rectangle or something like that, so you are able to identify what the problem with your texture is other than "it does not work".

I hope that helps

0
votes

You seem to be missing glEnable(GL_TEXTURE_2D); you need to put that into where you initialize OpenGL, by enabling that you allow OpenGL to render using textures/materials.

If you don't enable it the faces will just be rendered plain white as you where describing.

0
votes

In applet its very simple to do this

BufferedImage originalImage = ImageIO.read(new File("c:\\image\\mypic.jpg"));

ByteArrayOutputStream baos = new ByteArrayOutputStream();

ImageIO.write( originalImage, "jpg", baos );

baos.flush();

byte[] imageInByte = baos.toByteArray();

baos.close()

ByteBuffer buf = ByteBuffer.wrap(imageInByte);