VBOs in LWJGL

AAAAAAH! I spent hours looking up tutorials trying to understand vbos, ibos, and textures, and I can’t even display a simple textured vbo! I DID manage to get rendering working in immediate mode, but only because it is infinitely more simplistic.

I should probably tell you what I am trying to make. I am making a class called TexturedMesh that will store a VBO for coordinates, another VBO for texture coordinates, and an IBO for vertex data.

Here is my class:


package render;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import core.Disposable;
import core.Renderable;
import core.Texture;


/**
 * 2-Dimensional Mesh object that can be rendered.  Uses VBOs to render data.
 * @author William Andrew Cahill
 */
public class TexturedMesh2D implements Renderable, Disposable
{
	// Buffered objects
	private Texture tex;			// Texture to use
	private int vbo;				// Verticies to draw
	private int ibo;				// Index buffer to use
	private int tvbo;				// Texture vbo to use
	
	// Vertex buffers
	private FloatBuffer vertices, texCoords;		// Vertex and Texture coordinates
	private IntBuffer indices;						// Indices
	
	
	/**
	 * Constructs the mesh
	 * @param vertices List of vertices to connect to
	 * @param texCoords Texture coordinates for mapping
	 * @param indices Order in which to connect vertices
	 * @param tex Texture to map to Mesh2D
	 */
	public TexturedMesh2D(FloatBuffer vertices, FloatBuffer texCoords, IntBuffer indices, Texture tex)
	{
		create(vertices, texCoords, indices, tex);
	}
	
	
	/**
	 * Constructs a TexturedMesh with the mesh simply wrapping the entire texture.
	 * @param tex Texture to use
	 */
	public TexturedMesh2D(Texture tex)
	{
		// Creates vertices
		FloatBuffer vertices = BufferUtils.createFloatBuffer(8).put(new float[]
		{
			0, 0,
			tex.getWidth(), 0,
			tex.getWidth(), tex.getHeight(),
			0, tex.getHeight()
		});
		
		// Creates texture coordinates
		FloatBuffer texCoords = BufferUtils.createFloatBuffer(8).put(new float[]
		{
			0, 0,
			1, 0,
			1, 1,
			0, 1
		});
		
		// Creates indices
		IntBuffer indices = BufferUtils.createIntBuffer(4).put(new int[]{0, 1, 2, 3});
		
		// Creates mesh
		create(vertices, texCoords, indices, tex);
	}
	
	
	/**
	 * Creates the mesh
	 */
	private void create(FloatBuffer vertices, FloatBuffer texCoords, IntBuffer indices, Texture tex)
	{
		// Checks arguments
		if(vertices == null || texCoords == null || indices == null || tex == null)
			throw new IllegalArgumentException("Arguments cannot be null");
		
		// Stores arguments
		this.vertices = vertices;
		this.texCoords = texCoords;
		this.indices = indices;
		this.tex = tex;
		
		// Creates coordinate vbo
		vbo = GL15.glGenBuffers();
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
		GL15.glBufferData(GL15.GL_ARRAY_BUFFER, vertices, GL15.GL_STATIC_DRAW);
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
		
		// Creates texture vbo
		tvbo = GL15.glGenBuffers();
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, tvbo);
		GL15.glBufferData(GL15.GL_ARRAY_BUFFER, texCoords, GL15.GL_STATIC_DRAW);
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
		
		// Creates ibo
		ibo = GL15.glGenBuffers();
		GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ibo);
		GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, indices, GL15.GL_STATIC_DRAW);
		GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
	}
	
	
	/**
	 * Sets up object pointers before using them
	 */
	private void setupPointers()
	{
		// Points to vertices
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
		GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY);
		GL11.glVertexPointer(2, GL11.GL_FLOAT, 0, 0);
		
		// Points to texture vertices
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, tvbo);
		GL11.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
		GL11.glVertexPointer(2, GL11.GL_FLOAT, 0, 0);
		
		// Points to indices
		GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ibo);
	}

	
	@Override
	public void render()
	{
		render(0, 0);
	}

	
	@Override
	public void render(float x, float y)
	{
		// Translates
		GL11.glTranslatef(x,  y,  0);
		
		// Sets up buffer pointers
		setupPointers();
		
		// Binds texture
		tex.bind();
		
		// Draws elements
		GL11.glDrawElements(GL11.GL_TRIANGLE_STRIP, indices.capacity(), GL11.GL_UNSIGNED_INT, 0);
		
		// Disables states
		GL11.glDisableClientState(GL11.GL_VERTEX_ARRAY);
		GL11.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
		
		// Translates back
		GL11.glTranslatef(-x, -y, 0);
	}
	
	
	@Override
	public boolean isDisposed()
	{
		// Later, later...
		return false;
	}

	
	@Override
	public void dispose()
	{
		// Later, later...
	}
	
	
	/**
	 * Disposes of the TextureMesh2D's underlying resources
	 * @param disposeChildren Flag that determines if underlying resources besides the Texture coordinates
	 * should be disposed of
	 */
	public void dispose(boolean disposeChildren)
	{
		// I'll get to this later...
	}
}

I had to implement a lot of code before I could even think of testing it, so I’m not surprised that nothing shows up on my screen. I don’t even know where to look in order start fixing it, though. Help?

Edit: When testing, I use the constructor in which only the Texture is supplied. I made a Texture class from scratch, and there haven’t been any problems with it yet. Oh, and a shout out to theagentd for helping in this post regarding binding textures: http://www.java-gaming.org/index.php?topic=25516.0 Helped me out a lot.

Edit2: When I call GL11.glGetError(), I get an error code of 1285 which means out of memory. Hrm… It happens after GL11.glDrawElements(GL11.GL_TRIANGLE_STRIP, indices.capacity(), GL11.GL_UNSIGNED_INT, 0);

I maaaaaaay have forgotten to flip the buffers. I shall see if I can fix this…

Edit: Ok, so now instead of giving an error message, it just crashes. Windows forces it to close, and I am given no indication as to why this happens. Ugh… Here is my updated code:


package render;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import core.Disposable;
import core.Renderable;
import core.Texture;


/**
 * 2-Dimensional Mesh object that can be rendered.  Uses VBOs to render data.
 * @author William Andrew Cahill
 */
public class TexturedMesh2D implements Renderable, Disposable
{
	// Buffered objects
	private Texture tex;			// Texture to use
	private int vbo;				// Verticies to draw
	private int ibo;				// Index buffer to use
	private int tvbo;				// Texture vbo to use
	
	// Vertex buffers
	private FloatBuffer vertices, texCoords;		// Vertex and Texture coordinates
	private IntBuffer indices;						// Indices
	
	
	/**
	 * Constructs the mesh
	 * @param vertices List of vertices to connect to
	 * @param texCoords Texture coordinates for mapping
	 * @param indices Order in which to connect vertices
	 * @param tex Texture to map to Mesh2D
	 */
	public TexturedMesh2D(FloatBuffer vertices, FloatBuffer texCoords, IntBuffer indices, Texture tex)
	{
		create(vertices, texCoords, indices, tex);
	}
	
	
	/**
	 * Constructs a TexturedMesh with the mesh simply wrapping the entire texture.
	 * @param tex Texture to use
	 */
	public TexturedMesh2D(Texture tex)
	{
		// Creates vertices
		FloatBuffer vertices = BufferUtils.createFloatBuffer(8).put(new float[]
		{
			0, 0,
			tex.getWidth(), 0,
			tex.getWidth(), tex.getHeight(),
			0, tex.getHeight()
		});
		vertices.flip();
		
		// Creates texture coordinates
		FloatBuffer texCoords = BufferUtils.createFloatBuffer(8).put(new float[]
		{
			0, 0,
			1, 0,
			1, 1,
			0, 1
		});
		texCoords.flip();
		
		// Creates indices
		IntBuffer indices = BufferUtils.createIntBuffer(4).put(new int[]{0, 1, 2, 3});
		indices.flip();
		
		// Creates mesh
		create(vertices, texCoords, indices, tex);
	}
	
	
	/**
	 * Creates the mesh
	 */
	private void create(FloatBuffer vertices, FloatBuffer texCoords, IntBuffer indices, Texture tex)
	{
		// Checks arguments
		if(vertices == null || texCoords == null || indices == null || tex == null)
			throw new IllegalArgumentException("Arguments cannot be null");
		
		// Stores arguments
		this.vertices = vertices;
		this.texCoords = texCoords;
		this.indices = indices;
		this.tex = tex;
		
		// Creates coordinate vbo
		vbo = GL15.glGenBuffers();
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
		GL15.glBufferData(GL15.GL_ARRAY_BUFFER, vertices, GL15.GL_STATIC_DRAW);
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
		
		// Creates texture vbo
		tvbo = GL15.glGenBuffers();
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, tvbo);
		GL15.glBufferData(GL15.GL_ARRAY_BUFFER, texCoords, GL15.GL_STATIC_DRAW);
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
		System.out.println(texCoords.capacity());
		
		// Creates ibo
		ibo = GL15.glGenBuffers();
		GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ibo);
		GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, indices, GL15.GL_STATIC_DRAW);
		GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
	}
	
	
	/**
	 * Sets up object pointers before using them
	 */
	private void setupPointers()
	{
		// Points to vertices
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo);
		GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY);
		GL11.glVertexPointer(2, GL11.GL_FLOAT, 0, 0);
		
		// Points to texture vertices
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, tvbo);
		GL11.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
		GL11.glVertexPointer(2, GL11.GL_FLOAT, 0, 0);
		
		// Points to indices
		GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, ibo);
	}

	
	@Override
	public void render()
	{
		render(0, 0);
	}

	
	@Override
	public void render(float x, float y)
	{
		// Translates
		GL11.glTranslatef(x,  y,  0);
		
		// Sets up buffer pointers
		setupPointers();
		
		// Binds texture
		tex.bind();
		
		// Draws elements
		GL11.glDrawElements(GL11.GL_TRIANGLE_STRIP, indices.capacity(), GL11.GL_UNSIGNED_INT, 0);
		
		
		// Disables states
		GL11.glDisableClientState(GL11.GL_VERTEX_ARRAY);
		GL11.glDisableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
		
		// Translates back
		GL11.glTranslatef(-x, -y, 0);
	}
	
	
	@Override
	public boolean isDisposed()
	{
		// Later, later...
		return false;
	}

	
	@Override
	public void dispose()
	{
		// Later, later...
	}
	
	
	/**
	 * Disposes of the TextureMesh2D's underlying resources
	 * @param disposeChildren Flag that determines if underlying resources besides the Texture coordinates
	 * should be disposed of
	 */
	public void dispose(boolean disposeChildren)
	{
		// I'll get to this later...
	}
}

Buffers are now flipped.

Try replacing


//    (line 124-126)
      GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, tvbo);
      GL11.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
      GL11.glVertexPointer(2, GL11.GL_FLOAT, 0, 0);

with


      GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, tvbo);
      GL11.glEnableClientState(GL11.GL_TEXTURE_COORD_ARRAY);
      GL11.glTexCoordPointer(2, GL11.GL_FLOAT, 0, 0);

I could be totally wrong, I don’t have this totally memorized.

Woah! That! Almost worked! It actually displayed something! Three of the verticies of my texture, in fact! Ah, well, now I have to figure out why THAT’S happening, but it works! Man, this all reminds me of my assembly class, where type information does not exist, and the program can exit inexplicably due to limited runtime checks. Can’t believe I missed that. I understand what I did wrong. Thanks for your help! I’ll make another post once I get this class working… Or if I fail and need help again :slight_smile:

I have the successfuls! My class now appears to function without any quirky behavior. I had to replace

GL11.glDrawElements(GL11.GL_TRIANGLE_STRIPS, indices.capacity(), GL11.GL_UNSIGNED_INT, 0);

with

GL11.glDrawElements(GL11.GL_TRIANGLE_FAN, indices.capacity(), GL11.GL_UNSIGNED_INT, 0);

to make it draw a quad, and not just a triangle. Probably going to have to look into how those draw methods work. Now, to dispose of the Mesh, I just need to delete all of the buffers, and destroy the underlying texture. Then life should be good forever!

Awesome, I’m glad to see you got it working :slight_smile: