LWJGL OPENGL load non power of two texture error

I am using a library that loads some images from files, but they are not png or jpeg files. The library can return bytebuffer of the image. So my question is how do I load a texture in opengl using this bytebuffer?
Thanks in advance.

I solved the first problem which was the original purpose of this thread. Now I am facing the problem of loading non power of two textures.
I get error like these when loading those kinds of textures “Number of remaining buffer elements is 113844, must be at least 128164. Because at most 128164 elements can be returned, a buffer with at least 128164 elements is required, regardless of actual returned element count”
I am fine with power of two texture though.

Here is my entire program demo.

package Leo.Story.Game;
import java.io.FileNotFoundException;
import java.io.IOException;

import net.zepheus.nxjava.NXCanvasNode;
import net.zepheus.nxjava.NXFile;
import net.zepheus.nxjava.Texture;

import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GLContext;


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

public class Game {
	
	static NXFile charNX = null;
   
	
    public static void main(String args[]) {
        try {
            Display.setDisplayMode(new DisplayMode(640, 480));
            Display.setTitle("Texture Demo");
            Display.create();
            charNX = new NXFile("C:\\Users\\Leo\\Desktop\\map.nx");
        } catch (LWJGLException e) {
            e.printStackTrace();
            Display.destroy();
            System.exit(1);
        } catch (FileNotFoundException e) {
			
			e.printStackTrace();
		} catch (IOException e) {
			
			e.printStackTrace();
		}
        
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
        glPixelStorei(GL_PACK_ALIGNMENT, 1);
        
//        BufferedImage bi = ((NXCanvasNode)charNX.resolvePath("Back","Amoria.img","back","1")).getImage();
//        ByteBuffer bb = Game.convertImageData(bi);
        Texture texture = ((NXCanvasNode)charNX.resolvePath("Back","Amoria.img","back","0")).getTexture();
        glMatrixMode(GL_PROJECTION);
        glOrtho(0, 640, 480, 0, 1, -1);
        glMatrixMode(GL_MODELVIEW);
        glEnable(GL_TEXTURE_2D);
        System.out.println(texture.getWidth() + " " + texture.getHeight());
        int texid = glGenTextures();
        glBindTexture(GL_TEXTURE_2D, texid);
        
        // when texture area is small, bilinear filter the closest mipmap
        glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
        		GL_LINEAR );
        // when texture area is large, bilinear filter the original
        glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

        // the texture wraps over at the edges (repeat)
        glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
        glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture.getHeight(), texture.getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, texture.getBuffer());
        
        boolean ext = GLContext.getCapabilities().GL_EXT_bgra;
        System.out.println(ext);
        
        while (!Display.isCloseRequested()) {
            glClear(GL_COLOR_BUFFER_BIT);
            glEnable( GL_TEXTURE_2D );
            glBindTexture( GL_TEXTURE_2D, texid );

            glBegin(GL_QUADS);
            glTexCoord2f(0, 0);
            glVertex2i(100, 100); // Upper-left
            glTexCoord2f(1, 0);
            glVertex2i(100+texture.getWidth(), 100); // Upper-right
            glTexCoord2f(1, 1);
            glVertex2i(100+texture.getWidth(), 100+texture.getHeight()); // Bottom-right
            glTexCoord2f(0, 1);
            glVertex2i(100, 100+texture.getHeight()); // Bottom-left
            glEnd();
            Display.update();
            Display.sync(60);
        }
        // Release the resources of the wood texture
       
        Display.destroy();
        System.exit(0);
    }
}
    
   

OpenGL only expects raw pixel data. If this library decodes the file data into raw pixel data, then you can directly feed this ByteBuffer to the GPU.

Thanks for your reply. If it does, what functions can I use to load the texture with this raw data. Tutorials on the internet only load from image file.

glTexImage2D

If you didn’t know that, then I suggest you learn OpenGL properly. I recommend this tutorial, however it’s in C++ so here are the Java ports.

I am working on a game project. I have everything set up, that I mean the game data files: sound, images, strings. However they are compressed, and I am using a library to retrieve the data for me. The library returns a bytebuffer, or a java bufferedImage.
I don’t really want to get very technical with opengl, all I want is to load my image, display them and make my game. I have been googling a lot, but none of the results directly addresses my issue, so I hope someone here will help me.

public BufferedImage getBitmap(int id) {
		BufferedImage value;
		if(!low_memory && bmp_loaded != null && (value = bmp_loaded[id]) != null) {
			return value;
		} else {
			long offset = getBitmapOffset(id);
			if(offset == -1)
				throw new NXException("NX file does not this canvas.");
			
			lock();
			try {
				SeekableLittleEndianAccessor slea = getStreamAtOffset(offset);
				
				int width = slea.getUShort();
				int height = slea.getUShort();
				long length = slea.getUInt();
				
				ByteBuffer output = ByteBuffer.allocateDirect(width * height * 4);
				NXCompression.decompress(slea.getBuffer(), offset + 4, length + 4, output, 0);
				output.rewind();
				output.order(ByteOrder.LITTLE_ENDIAN);
				
				//TODO: optimize this without bitshifts.
				value = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
				for (int h = 0; h < height; h++) {
					for (int w = 0; w < width; w++) {
						int b = output.get() & 0xFF;
						int g = output.get() & 0xFF;
						int r = output.get() & 0xFF;
						int a = output.get() & 0xFF;
						value.setRGB(w, h, (a << 24) | (r << 16) | (g << 8) | b);
					}
				}
			} finally { unlock(); }
			
			if(!low_memory) bmp_loaded[id] = value;
			return value;
		}
	}

This code is to retrieve image from the file. It construct a bytebuffer and make a bufferedimage from it. Because opengl glteximage2d requires bytebuffer, I think i can just return the bytebuffer this method has. However, I don’t know how to use that bytebuffer, what format it is in… etc.

So yea, all I need is just a little function that makes me a texture to bind, map and display. Thanks for any help in advance.

Is that your code, or some from the library you’re using? For the sake of ease you could just convert the BufferedImage back to a ByteBuffer and pass that (see this thread).

It would be better to skip the BufferedImage completely, though. You could use glTexImage2D with GL_RGBA as the “internalFormat”, GL_BGRA as the “format” and GL_UNSIGNED_BYTE as the “type”.

Some pseudo-code:

				// ... NX lock/setup stuff ... // 
				
				int width = slea.getUShort();
				int height = slea.getUShort();
				long length = slea.getUInt();

				// ... NX decompresses into a ByteBuffer ... //
				ByteBuffer output = ByteBuffer.allocateDirect(width * height
						* 4);
				NXCompression.decompress(slea.getBuffer(), offset + 4,
						length + 4, output, 0);
				output.rewind();
				output.order(ByteOrder.LITTLE_ENDIAN);
				
				
				int tex = glGenTextures();
				glBindTexture(GL_TEXTURE_2D, tex);
				
				// ... setup wrap mode and filtering ... //
				
				glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, output);
				
				// ... NX unlock stuff ... //
				
				// ... return the texture, or cache ... // 
				return tex;

Hey, thanks for the reply. This is an external library for reading NX file as you can already tell. I did know that I can convert the bufferedimage back to a bytebuffer, but that would be redundant because the library construct the bufferedimage from the bytebuffer! And yes, I would like to find a way to just pass the bytebuffer in the library to opengl, that was my main question. I don’t really want to bother with the details of how this file reading library works, because I just want to make my game. I will try your method when I get back to my desktop, maybe tomorrow. Thanks again for you input.

edit: i don’t know really know the format of the bytebuffer in the method. So are you sure from reading the method, that your suggestions are right and they opengl would recognize the bytebuffer and make me a happy texture?

It’s all guesswork since I can’t test it on your end. But the decompression appears to leave the byte buffer in BGRA format, which you can work with.

Unless I’m mistaken this requires the GL_EXT_bgra extension, which it should be on most computers. If not, you may have to swizzle the bytes yourself like they do in the BufferedImage method (except ordering it RGBA instead).

//check if extension is present
boolean ext = GLContext.getCapabilities().GL_EXT_bgra;

That “extension” is supported by 100% of all implementations. No, literally. It’s even core in 1.2. No need to check it.

I was testing your suggestions and the parameter GL_BGRA gave me error in eclipse. “can’t resolve to a variable.”

package Leo.Story.Game;
import net.zepheus.nxjava.NXCanvasNode;
import net.zepheus.nxjava.NXFile;

import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GLContext;


import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;

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


public class Game {
	
	static NXFile charNX = null;
   
	
    public static void main(String args[]) {
        try {
            Display.setDisplayMode(new DisplayMode(640, 480));
            Display.setTitle("Texture Demo");
            Display.create();
            charNX = new NXFile("C:\\Users\\Leo\\Desktop\\nx\\character.nx");
        } catch (LWJGLException e) {
            e.printStackTrace();
            Display.destroy();
            System.exit(1);
        } catch (FileNotFoundException e) {
			
			e.printStackTrace();
		} catch (IOException e) {
			
			e.printStackTrace();
		}
        
        ByteBuffer bb = ((NXCanvasNode)charNX.resolvePath("00002000.img","alert","0", "body")).getImageByteBuffer();
        
        glMatrixMode(GL_PROJECTION);
        glOrtho(0, 640, 480, 0, 1, -1);
        glMatrixMode(GL_MODELVIEW);
        glEnable(GL_TEXTURE_2D);
        
        int texid = glGenTextures();
        glBindTexture(GL_TEXTURE_2D, texid);
        
        // when texture area is small, bilinear filter the closest mipmap
        glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
                         GL_LINEAR_MIPMAP_NEAREST );
        // when texture area is large, bilinear filter the original
        glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

        // the texture wraps over at the edges (repeat)
        glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
        glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 24, 29, 0, GL_RGBA, GL_UNSIGNED_BYTE, bb);
        
        boolean ext = GLContext.getCapabilities().GL_EXT_bgra;
        System.out.println(ext);
        
        while (!Display.isCloseRequested()) {
            glClear(GL_COLOR_BUFFER_BIT);
            glEnable( GL_TEXTURE_2D );
            glBindTexture( GL_TEXTURE_2D, texid );

            glBegin(GL_QUADS);
            glTexCoord2f(0, 0);
            glVertex2i(100, 100); // Upper-left
            glTexCoord2f(1, 0);
            glVertex2i(124, 100); // Upper-right
            glTexCoord2f(1, 1);
            glVertex2i(124, 129); // Bottom-right
            glTexCoord2f(0, 1);
            glVertex2i(100, 129); // Bottom-left
            glEnd();
            Display.update();
            Display.sync(60);
        }
        
       
        Display.destroy();
        System.exit(0);
    }
}

This is my file so far, I hope I did everything correctly. The result is just a white rectangle(the texture quad?) on a black background. I couldn’t compile with GL_BGRA as the second to the last parameter in glTexImage2D method, so I replaced it with GL_RBGA. It might be the problem.

Try adding this: “import static org.lwjgl.opengl.GL12.*;” to your imports, then GL_BGRA will work :wink:

Yeah, since GL_BGRA was promoted to core OpenGL with OpenGL 1.2, it’s in GL12.

On another note, GL_BGRA is in both GL12 and GL32, just like GL_RED was.

Damnit all, someone really needs to check for these pesky duplicates. I’ll report it… sigh

EDIT: Reported :slight_smile:

Ok, it compiles with GL_BGRA. But I still have the same result

http://s14.postimage.org/d2ykk0uoh/Untitled.png

I think the bytebuffer format is messed up in a way that opengl doesn’t recognize it. And sadly I don’t know how to modify the bytebuffer format to match opengl…

That texture resolution is a bit weird and most likely doesn’t work correctly since it’s not a multiple of 4, which OpenGL assumes. Try to add this before loading the texture (line 40 or so):

glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glPixelStorei(GL_PACK_ALIGNMENT, 1);

Alternatively try it with a simpler power-of-two texture? Draw a 32x32 pixel image in Paint or something.

I thought opengl doesn’t care if images are power of 2 anymore. The file I am loading from is made by other people, I can edit it but I don’t want to edit all of them. If I need to load an actual image file, I wouldn’t use bytebuffer in the first place. But I will try to find a power of 2 image from the file and try it.

OpenGL doesn’t require power of 2 textures anymore, but it still assumes that the pixels are aligned according to the GL_*PACK_ALIGNMENT, which defaults to 4 pixels. Just change it with the code I posted and your textures can be any size.

I tried glPixelStorei already, and I got the same result. It must be the format of the bytebuffer… I should just convert the bufferedimage to bytebuffer with prvoen method.

It’s probably invalid because you haven’t specified mipmaps. Use GL_LINEAR or GL_NEAREST as your filter modes.

Yes it was the mipmap! I changed it to linear and it worked! Thanks for all of you guys help!