LWJGL: Loading a texture, the imperative way.

Please ignore this post, if you really care about code design. This piece of code isn’t really cleaned up.


import java.awt.image.BufferedImage;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import javax.imageio.ImageIO;

//I only use LWJGL, nothing more.
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;

public class TextureLoader {
	
	public static class TempTexture {
		public ByteBuffer buffer;
		public int width;
		public int height;
		public int id;
	}
	
	public static TempTexture test = loadTexture("/icon.png");
	
	public static TempTexture loadTexture(String filename) {
		//Just to make sure I don't hog my laptop computer's memory and stick to an old Intel's graphics card.
		if (test != null) {
			GL11.glDeleteTextures(test.id);
			test.buffer.clear();
			test.buffer = null;
			test = null;
		}
		
		
		TempTexture result = null;
		try {
			BufferedImage img = ImageIO.read(TextureLoader.class.getResource(filename));
			int[] imgData = img.getRGB(0, 0, img.getWidth(), img.getHeight(), null, 0, img.getWidth());
			result = new TempTexture();
			result.width = img.getWidth();
			result.height = img.getHeight();
			int size = Math.max(result.width, result.height);
			result.buffer = BufferUtils.createByteBuffer(size * size * 4);
			result.buffer.order(ByteOrder.nativeOrder());
			result.buffer.position(0);
			for (int y = 0; y < result.height; y++) {
				for (int x = 0; x < result.width; x++) {
					int pixel = imgData[x + y * result.width];
					//RGBA is the preferred OpenGL format for loading texture.
					//Source:		http://www.opengl.org/wiki/Common_Mistakes#Texture_upload_and_pixel_reads
					
					result.buffer.put((byte) ((pixel >> 16) & 0xFF));
					result.buffer.put((byte) ((pixel >> 8) & 0xFF));
					result.buffer.put((byte) ((pixel) & 0xFF));
					result.buffer.put((byte) ((pixel >> 24) & 0xFF));
					
					//Do note the bitwise shift signs. Please do experiment and see for yourself.					
					//Green hue
					/*result.buffer.put((byte) ((pixel << 16) & 0xFF));
					result.buffer.put((byte) ((pixel) & 0xFF));
					result.buffer.put((byte) ((pixel << 8) & 0xFF));
					result.buffer.put((byte) ((pixel << 24) & 0xFF));*/
					
					//Blue hue
					/*result.buffer.put((byte) ((pixel << 16) & 0xFF));
					result.buffer.put((byte) ((pixel << 8) & 0xFF));
					result.buffer.put((byte) ((pixel) & 0xFF));
					result.buffer.put((byte) ((pixel << 24) & 0xFF));*/
					
					//Red hue
					/*result.buffer.put((byte) ((pixel) & 0xFF));
					result.buffer.put((byte) ((pixel << 16) & 0xFF));
					result.buffer.put((byte) ((pixel << 8) & 0xFF));
					result.buffer.put((byte) ((pixel << 24) & 0xFF));*/
					
					//Blackness
					/*result.buffer.put((byte) ((pixel << 24) & 0xFF));
					result.buffer.put((byte) ((pixel << 16) & 0xFF));
					result.buffer.put((byte) ((pixel << 8) & 0xFF));
					result.buffer.put((byte) ((pixel) & 0xFF));*/
					
					//Normal
					/*result.buffer.put((byte) ((pixel >> 16) & 0xFF));
					result.buffer.put((byte) ((pixel >> 8) & 0xFF));
					result.buffer.put((byte) ((pixel) & 0xFF));
					result.buffer.put((byte) ((pixel >> 24) & 0xFF));*/
				}
			}
			result.buffer.position(0);
		}
		catch (IOException e) {
			e.printStackTrace();
		}
		return result;
	}
}

So, to load a texture in a simple program:


import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;

public class Program {
	
	public Program() {
		try {
			Display.setDisplayMode(new DisplayMode(640, 480));
			Display.create();
		}
		catch (LWJGLException e) {
			e.printStackTrace();
			Display.destroy();
			System.exit(-1);
		}
		initialize();
	}
	
	public void initialize() {
		GL11.glMatrixMode(GL11.GL_PROJECTION);
		GL11.glLoadIdentity();
		GL11.glOrtho(0, 640, 480, 0, 1, -1);
		
		GL11.glMatrixMode(GL11.GL_MODELVIEW);
		GL11.glClearColor(1f, 1f, 1f, 1f);
		
	}
	
	public void start() {
		while (!Display.isCloseRequested()) {
			GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
			
			if (Keyboard.isKeyDown(Keyboard.KEY_A)) {
				TextureLoader.test = TextureLoader.loadTexture("/icon.png");
			}
			
			GL11.glEnable(GL11.GL_TEXTURE_2D);
			TextureLoader.test.id = GL11.glGenTextures();
			GL11.glBindTexture(GL11.GL_TEXTURE_2D, TextureLoader.test.id);
			GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
			GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
			GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA, TextureLoader.test.width, TextureLoader.test.height, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, TextureLoader.test.buffer);
			GL11.glBegin(GL11.GL_QUADS);
			{
				//Matching coordinate positive/negative signs is a must, in order to create directionally specific textures.
				//Texture coordinates (s, t) ranges from -1 to 1, with -1 being the lowest (analogous to 0 on the number line).
				GL11.glTexCoord2f(1f, -1f);
				GL11.glVertex2i(200, -200);
				GL11.glTexCoord2f(1f, 1f);
				GL11.glVertex2i(200, 200);
				GL11.glTexCoord2f(-1f, 1f);
				GL11.glVertex2i(-200, 200);
				GL11.glTexCoord2f(-1f, 1f);
				GL11.glVertex2i(-200, 200);
			}
			GL11.glEnd();
			GL11.glDisable(GL11.GL_TEXTURE_2D);
			Display.update();
			Display.sync(60);
		}
		Display.destroy();
		System.exit(0);
	}
	
	public static void main(String[] arg) {
		new Program().start();
	}
}

Created this piece of code using a laptop running Windows XP with an old Intel graphics card. And yes, it did not support shaders.

The loading of the texture looks similar to, but even messier than this (same naming conventions etc.). Note that [icode]result.buffer.position(0)[/icode] can be changed to [icode]result.buffer.rewind()[/icode]. The use of a nested class is not necessary and to be honest, neither is the use of a static method to load the pixel data. The design of this as a whole is terrible IMO. You could handle the texture as an instance which would be better OOP, e.g:


public class Texture {

	private int id;
	private int width;
	private int height;

	public Texture(String path) {
		// load the texture here...
	}

	public int getId() {
		return id;
	}

	// getters for height and width...

}

This would also reduce the number of OpenGL functions the user has to implicitly call. Making everything static is not the way to go.

@ Troubleshoots, it’s also no good design to load the texture in the constructor.

Why not?

Static design is actually quite fine, and I use it in my GLUtils library, which I’ve used to make multiple games, so I have no idea why you think a static design is terrible. Your code should go something like this:


public class Texture {

	public int id;
	public int width;
	public int height;
	
	private Texture(int id, int width, int height) {
		this.id = id;
		this.width = width;
		this.height = height;
	}

	public static Texture loadTexture(String name) {
		// Load the image
		BufferedImage img = //load pixels
		return new Texture(textureID, img.getWidth(), img.getHeight());
	}
	
	//other things like binding
}

I prefer it to be that way so that I don’t have to manually create new texture objects and litter my code with useless object creating calls.

Also, of course it looks like the one in the tutorial series, that’s the standard way of loading textures. Don’t try to tell someone else they’re wrong until you have more experience.

Java is an object orientated language. The use of statics should be avoided wherever possible.

I was referring to the similarity of using static methods and intermediate mode. The reason I was saying that it was messier is because there is no need to use a nested class for handling the texture data. That’s bad design. Also there’s no standard way of loading textures. That’s just the way most people do it using the Java API.

It’s not intermediate mode, its immediate mode, first off.

Second off, you say that the use of static should be avoided, I say a good programmer should use static when he/she needs to. Building your program entirely on a static model is probably not smart, but it’s also not smart to completely avoid static like its the plague. I think you need to re-evaluate your use of static and realize that you can use, just you have to be careful with it. Not to mention the fact that statically calling a method like I showed you above is actually easier than creating a new instance of Texture manually, and has the same outcome.

[quote]Also there’s no standard way of loading textures. That’s just the way most people do it using the Java API.
[/quote]
This is not true. Everywhere I’ve looked, this is the general way people have loaded textures in OpenGL.

I don’t see much difference between [icode]Texture texture = new Texture(“rgirn.png”)[/icode] and [icode]Texture texture = Texture.loadTexture(“rgirn.png”)[/icode]. When I say you should avoid the use of statics wherever possible I mean you should avoid the use of statics wherever possible. If you had a House class there is no reason to use a static method to create a House object, thus I don’t see a reason to use a static method that returns a Texture object.

Regarding loading the pixel data via the constructor; I don’t see a reason why:


public Texture(String path) {
	// load pixel data
	width = image.getWidth();
	height = image.getHeight();
	id = glGenTextures();
}

should become:


public static Texture loadTexture(String path) {
	// load pixel data
	return new Texture(glGenTextures(), image.getWidth(), image.getHeight(), pixelData);
}

EDIT: To add to that, the only good use of something being designed in this way is using the singleton pattern. This seems to be what the OP is doing, but the OP is using it in the wrong way. The singleton pattern is used to restrict having more than one instance of a class.

I never said using a nested class was a good idea, and I also do not support OP’s decision on using those. However, I still stand by my argument.

Seems like no one likes imperative Java programming. I will turn away.

Object orientated programming is a form of imperative programming. What you’re doing is structuring of an object orientated program. This is no more an imperative form of programming than OOP is; this is only a bad way of structuring object orientated code.

How is my article messy? Please elaborate.

You’re all missing the easiest way to load a BufferedImage: (source)

int size = width * height;
if (buffer == null || buffer.capacity() < size) {
    buffer = BufferUtils.createIntBuffer(size);
}
texture.bind();
buffer.rewind();
buffer.put(pixels, 0, size);
buffer.rewind();

GL11. glTexImage2D(GL11.GL_TEXTURE_2D,
     0,
     GL11.GL_RGBA,
     width,
     height,
     0,
     GL12.GL_BGRA,
     GL12.GL_UNSIGNED_INT_8_8_8_8_REV,
     buffer);

If you’re using PNG you might rather use Matthias’ decoder, see here:

:slight_smile:

The main downside to doing the texture loading in the constructor is that it’s hidden away, e.g. from a user who might want the same utility for a more advanced texture loader (say, for cube maps or texture arrays). IMHO it’s not a big deal if a simple GL utility library does this – you can’t be expected to support every possible need of the user.

LibGDX also loads textures in the constructor, although it’s abstracted out the image and texture loading to provide easier support for float texture and such.

I didn’t exactly mean it that way. I meant that the way the texture is loaded, i.e from a static method and using pretty much the same naming conventions, is similar. The reason I implied that that’s messy you can read above.

Please read the OpenGL spec on how texcoords are normalized, how they map to texels and how repeating/clamping works :slight_smile:

Every time you load in a new texture, you destroy the previous one… :clue:

Moved out of [shared code] due to being generally misinformed. :point:

I think they range from 0 to 1 and not -1 to 1.