@Override
 public void draw() {
   checkCreated();
   // Bind the vao and enable all attributes
   GL30.glBindVertexArray(id);
   for (int i = 0; i < attributeBufferIDs.length; i++) {
     GL20.glEnableVertexAttribArray(i);
   }
   // Bind the indices buffer
   GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, indicesBufferID);
   // Draw all indices with the provided mode
   GL11.glDrawElements(
       drawingMode.getGLConstant(),
       indicesCount,
       GL11.GL_UNSIGNED_INT,
       indicesOffset * DataType.INT.getByteSize());
   // Unbind the indices buffer
   GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
   // Disable all attributes and unbind the vao
   for (int i = 0; i < attributeBufferIDs.length; i++) {
     GL20.glDisableVertexAttribArray(i);
   }
   GL30.glBindVertexArray(0);
   // Check for errors
   LWJGLUtil.checkForGLError();
 }
 @Override
 public void create() {
   if (isCreated()) {
     throw new IllegalStateException("Vertex array has already been created");
   }
   if (vertexData == null) {
     throw new IllegalStateException("Vertex data has not been set");
   }
   // Generate and bind the vao
   id = GL30.glGenVertexArrays();
   GL30.glBindVertexArray(id);
   // Generate, bind and fill the indices vbo then unbind
   indicesBufferID = GL15.glGenBuffers();
   GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, indicesBufferID);
   GL15.glBufferData(
       GL15.GL_ELEMENT_ARRAY_BUFFER, vertexData.getIndicesBuffer(), GL15.GL_STATIC_DRAW);
   GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
   // Save the count of indices to draw
   indicesCountCache = vertexData.getIndicesCount();
   resetIndicesCountAndOffset();
   // Create the map for attribute index to buffer ID
   attributeBufferIDs = new int[vertexData.getAttributeCount()];
   // For each attribute, generate, bind and fill the vbo, then setup the attribute in the vao and
   // save the buffer ID for the index
   for (int i = 0; i < vertexData.getAttributeCount(); i++) {
     final VertexAttribute attribute = vertexData.getAttribute(i);
     final int bufferID = GL15.glGenBuffers();
     GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, bufferID);
     GL15.glBufferData(GL15.GL_ARRAY_BUFFER, attribute.getData(), GL15.GL_STATIC_DRAW);
     attributeBufferIDs[i] = bufferID;
     // Three ways to interpret integer data
     if (attribute.getType().isInteger() && attribute.getUploadMode() == UploadMode.KEEP_INT) {
       // Directly as an int
       GL30.glVertexAttribIPointer(
           i, attribute.getSize(), attribute.getType().getGLConstant(), 0, 0);
     } else {
       // Or as a float, normalized or not
       GL20.glVertexAttribPointer(
           i,
           attribute.getSize(),
           attribute.getType().getGLConstant(),
           attribute.getUploadMode().normalize(),
           0,
           0);
     }
   }
   // Unbind the vbo and vao
   GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
   GL30.glBindVertexArray(0);
   // Update state
   super.create();
   // Check for errors
   LWJGLUtil.checkForGLError();
 }
 @Override
 public void destroy() {
   checkCreated();
   // Unbind any bound buffer
   GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
   // Unbind and delete the indices buffer
   GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, 0);
   GL15.glDeleteBuffers(indicesBufferID);
   // Bind the vao for deletion
   GL30.glBindVertexArray(id);
   // Disable each attribute and delete its buffer
   for (int i = 0; i < attributeBufferIDs.length; i++) {
     GL20.glDisableVertexAttribArray(i);
     GL15.glDeleteBuffers(attributeBufferIDs[i]);
   }
   // Unbind the vao and delete it
   GL30.glBindVertexArray(0);
   GL30.glDeleteVertexArrays(id);
   super.destroy();
   // Check for errors
   LWJGLUtil.checkForGLError();
 }