[FIXED] Yet more VBO silent failure

Started by euclid, January 17, 2012, 22:05:32

Previous topic - Next topic

euclid

I'm reasonably familar with vertex buffer objects. I've checked this over and over, rewritten it multiple times and can't find a thing wrong with it. Still, no output from glDrawElements.

package shaders0.shaders3;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;

import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL15;
import org.lwjgl.util.glu.GLU;

public final class Main implements Runnable
{
  private static final int  SCREEN_WIDTH  = 640;
  private static final int  SCREEN_HEIGHT = 480;

  private final FloatBuffer vertices;
  private final int         vertices_name;
  private int               indices_name;
  private ShortBuffer       indices;

  /**
   * Abort if an OpenGL error occurred.
   */

  private static void checkGL()
  {
    final int code = GL11.glGetError();
    if (code != 0) {
      final String x = GLU.gluErrorString(code);
      final String message = "OpenGL error (" + code + "): " + x;
      System.err.println(message);
      System.exit(1);
    }
  }

  public Main()
  {
    // Initialize a vertex buffer object with vertex data.
    {
      // Allocate buffer name.
      this.vertices_name = GL15.glGenBuffers();

      // Bind buffer name.
      GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, this.vertices_name);

      // Create and upload vertex data.
      final ByteBuffer vertices_bytes =
        ByteBuffer.allocateDirect(3 * 3 * 4).order(ByteOrder.nativeOrder());
      this.vertices = vertices_bytes.asFloatBuffer();
      this.vertices.put(new float[] { 0.0f, 0.0f, 0.0f });
      this.vertices.put(new float[] { 200.0f, 0.0f, 0.0f });
      this.vertices.put(new float[] { 200.0f, 200.0f, 0.0f });
      assert this.vertices.remaining() == 0;
      assert this.vertices.order() == ByteOrder.LITTLE_ENDIAN;

      GL15.glBufferData(
        GL15.GL_ARRAY_BUFFER,
        this.vertices,
        GL15.GL_STATIC_DRAW);

      // Unbind buffer name.
      GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
      Main.checkGL();
    }

    // Initialize an index buffer to reference the above vertices.
    {
      // Allocate buffer name.
      this.indices_name = GL15.glGenBuffers();

      // Bind buffer name.
      GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, this.indices_name);

      // Allocate and upload indices.
      final ByteBuffer indices_bytes =
        ByteBuffer.allocateDirect(3 * 2).order(ByteOrder.nativeOrder());
      this.indices = indices_bytes.asShortBuffer();
      this.indices.put(new short[] { 0, 1, 2 });
      assert this.indices.remaining() == 0;
      assert this.indices.order() == ByteOrder.LITTLE_ENDIAN;

      GL15.glBufferData(
        GL15.GL_ELEMENT_ARRAY_BUFFER,
        this.indices,
        GL15.GL_STATIC_DRAW);

      // Unbind buffer name.
      GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
      Main.checkGL();
    }
  }

  private static void setupCamera()
  {
    GL11.glViewport(0, 0, Main.SCREEN_WIDTH, Main.SCREEN_HEIGHT);
    GL11.glClearColor(0.3f, 0.3f, 0.3f, 1.0f);
    GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);

    GL11.glMatrixMode(GL11.GL_PROJECTION);
    GL11.glLoadIdentity();
    GL11.glOrtho(0, Main.SCREEN_WIDTH, 0, Main.SCREEN_HEIGHT, -1.0, 100.0);

    GL11.glMatrixMode(GL11.GL_MODELVIEW);
    GL11.glLoadIdentity();
  }

  private void draw()
  {
    // Draw a blue triangle that should be completely overwritten
    // by the triangle drawn by the VBO.

    GL11.glColor3d(0.0, 0.0, 1.0);
    GL11.glBegin(GL11.GL_TRIANGLES);
    {
      GL11.glVertex3f(0.0f, 0.0f, 0.0f);
      GL11.glVertex3f(200.0f, 0.0f, 0.0f);
      GL11.glVertex3f(200.0f, 200.0f, 0.0f);
    }
    GL11.glEnd();

    // Reset colour to white.
    GL11.glColor3d(1.0, 1.0, 1.0);

    // Bind vertex array and index buffer.
    GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, this.vertices_name);
    GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, this.indices_name);

    // Enable vertex array.
    GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY);

    // Tell GL where vertex data can be found.
    GL11.glVertexPointer(3, GL11.GL_FLOAT, 0, 0L);

    // Draw triangle.
    GL11.glDrawElements(GL11.GL_TRIANGLES, 3, GL11.GL_UNSIGNED_SHORT, 0L);

    // Disable vertex array.
    GL11.glDisableClientState(GL11.GL_VERTEX_ARRAY);

    // Unbind buffers.
    GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
    GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);

    Main.checkGL();
  }

  private void render()
  {
    Main.setupCamera();
    this.draw();
  }

  @Override public void run()
  {
    while (!Display.isCloseRequested()) {
      this.render();
      Display.update();
      Display.sync(60);
    }
    Display.destroy();
  }

  public static void main(
    final String args[])
  {
    try {
      Display.setDisplayMode(new DisplayMode(
        Main.SCREEN_WIDTH,
        Main.SCREEN_HEIGHT));
      Display.setTitle("shaders0");
      Display.create();
    } catch (final LWJGLException e) {
      e.printStackTrace();
      System.exit(0);
    }

    final Main m = new Main();
    m.run();
  }
}

euclid

For future reference: buffers need rewinding before passing to OpenGL. I wish the lwjgl bindings had raised an error, as this would've saved an entire day.

      this.vertices.put(new float[] { 200.0f, 200.0f, 0.0f });
      assert this.vertices.remaining() == 0;
      assert this.vertices.order() == ByteOrder.LITTLE_ENDIAN;
      this.vertices.rewind();


      this.indices.put(new short[] { 0, 1, 2 });
      assert this.indices.remaining() == 0;
      assert this.indices.order() == ByteOrder.LITTLE_ENDIAN;
      this.indices.rewind();

spasi

The next nightly build will be checking all buffer object data parameters, to make sure that .remaining() is greater than zero.

euclid

Nice. Thank you!

I recommend checking endianness, too (buffer.order() == ByteOrder.nativeOrder()). I've forgotten to set the byte order for buffers more than once (also manifests as silent failure on the OpenGL side).

spasi

The best way to avoid that is to always use the org.lwjgl.BufferUtils class to allocate buffers. Then you only have to be careful when .slice() is used, you need to set the order again on the resulting buffer.