Because I’ve not been able to find completely working examples that show the use of VBOs with vertices and normals, etc I decided to list some snippets of code here from I finally was able to get to work. I hope this is helpful to others.
I’ll try to explain what needs to occur where and what things mean as best as I could decipher them. BTW, I am trying to improve the renderer in the 3DS part of JOGLUTILS so this is related to the 3DS structures that I have. 3DS files contain ‘objects’ which each object has arrays of vertices and arrays of normals, textures and materials/colors.
So, for VBO the first thing is the generation of the VBO themselves. So say you have some ‘objects’ that have a size of ‘objects.size()’, well you want to create a VBO for each of the objects…actually since we want normals, vertices, colors, and textures we have to create 4 VBOs for each object. This code simply creates the necessary VBO ids and generates the necessary buffers. You would do this in the ‘init()’ method, typically. Note, there are routines to check if VBOs are available for use on the current machine but I’ve not listed this check here.
// Global variable
private int VBO[] = null;
// Note that I'm creating twice the VBOs since I want
// one VBO for vertices and one for normals. In our VBO
// array, the vertices will be first, and the normals second
// .. ie. vertices will be VBO[4*i]
// normals will be VBO[4*i+1]
// textures will be VBO[4*i+2]
// colors will be VBO[4*i+3]
VBO = new int[objects.size()*4];
// Generate the buffers for all of the VBOs
gl.glGenBuffers(objects.size()*4, VBO, 0);
Now we need to populate the VBOs in the init() method.
// Global variables
private FloatBuffer vertices, normals, textures, colors;
// Loop through all of the objects we have
for (int i=0; i<objects.size(); i++) {
// Get the current object from which we'll retrieve the vertices and normals
Obj tempObj = objects.get(i);
// First create the VERTICES buffer for each object
// ...corresponding to the VBO[4*i] ids
// Bind the buffer to represent which buffer we are currently working with
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, VBO[4*i]);
// Allocate the float buffer for the vertices. The size of the
// buffer of vertices corresponds to how many faces/triangles
// each object has times 3 vertices per triangle times 3 floats
// per vertex: #faces * 3 * 3
int totalBufferSize = tempObj.numOfFaces*3*3;
vertices = FloatBuffer.allocate(totalBufferSize);
// Create the float buffer for the vertices by looping over each
// face and adding the coordinates of each vertex to the buffer
// in the order that the vertices would normally be called in
// a direct draw method. Yes, you will be duplicating the vertices
// across the buffer because vertices are typically shared by
// faces/triangles, but that's how it is supposed to be done.
for (int j=0; j<tempObj.numOfFaces; j++) {
// Loop through the 3 vertices of each face
for (int whichVertex=0; whichVertex<3; whichVertex++) {
int index = tempObj.faces[j].vertIndex[whichVertex];
// add the X, Y, Z float values of the current vertex to
// the 'vertices' float buffer
vertices.put(tempObj.verts[index].x);
vertices.put(tempObj.verts[index].y);
vertices.put(tempObj.verts[index].z);
}
}
// Rewind buffer ... necessary step
vertices.rewind();
// Write out vertex buffer to the currently bound VBO.
// Use 'totalBufferSize*4' because we have 4 bytes for a float.
gl.glBufferData(GL.GL_ARRAY_BUFFER, totalBufferSize *4, vertices, GL.GL_STATIC_DRAW);
// Free the vertices buffer since we are done with them
vertices = null;
// Second create the NORMALS buffer for each object
// ...corresponding to the VBO[4*i+1] ids
// Bind the buffer to represent which buffer we are currently working with
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, VBO[4*i+1]);
// Allocate the float buffer for the normals. The size of the
// buffer of normals corresponds to the same number of floats
// as the vertices since each vertex has a normal and each normal
// also has 3 floats so the same buffer size is used.
totalBufferSize = tempObj.numOfFaces*3*3;
normals = FloatBuffer.allocate(totalBufferSize);
// Create the float buffer for the normals by looping over each
// face and each face's vertex and adding the coordinates of
// each corresponding normal to the buffer...same concept as for
// vertices
for (int j=0; j<tempObj.numOfFaces; j++) {
// Loop through the 3 normals of each face
for (int whichVertex=0; whichVertex<3; whichVertex++) {
int index = tempObj.faces[j].vertIndex[whichVertex];
// add the X, Y, Z float values of the current normal to
// the 'normals' float buffer
normals.put(tempObj.normals[index].x);
normals.put(tempObj.normals[index].y);
normals.put(tempObj.normals[index].z);
}
}
// Rewind buffer
normals.rewind();
// Write out the normals buffer to the currently bound VBO.
// Use 'totalBufferSize*4' because we have 4 bytes for a float.
gl.glBufferData(GL.GL_ARRAY_BUFFER, totalBufferSize *4, normals, GL.GL_STATIC_DRAW);
// Free the normals
normals = null;
// Third create the TEXTURES buffer for each object (if the object
// has a texture...skip it if it does not)
// ...corresponding to the VBO[4*i+2] ids
if(tempObj.hasTexture) {
// Bind the buffer to represent which buffer we are currently working with
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, VBO[4*i+2]);
// Textures have 2 coordinates (u,v) per vertex and 3 vertices per triangle/face
totalBuffer = (tempObj.numOfFaces*3*2);
// Allocate the float buffer for the textures (again, 4 bytes per float so multiply by 4)
textures = ByteBuffer.allocateDirect(totalBuffer*4).order(ByteOrder.nativeOrder()).asFloatBuffer();
for (int j=0; j<tempObj.numOfFaces; j++) {
for (int whichVertex=0; whichVertex<3; whichVertex++) {
int index = tempObj.faces[j].vertIndex[whichVertex];
// put the u,v coordinates of the texture vertices into the textures FloatBuffer
textures.put(tempObj.texVerts[index].x);
textures.put(tempObj.texVerts[index].y);
}
}
// Rewind buffer
textures.rewind();
// Write out the textures buffer to the currently bound VBO.
// Use 'totalBufferSize*4' because we have 4 bytes for a float.
gl.glBufferData(GL.GL_ARRAY_BUFFER, totalBuffer*4, textures, GL.GL_STATIC_DRAW);
// Free the textures
textures = null;
}
// Fourth create the COLORS buffer for each object (if the object has
// a texture then you don't need to set a color buffer for that object...but
// make sure not to use the empty buffer during rendering by accident...you
// could just put in fake colors for the buffer just in case...I don't do that
// here)
// ...corresponding to the VBO[4*i+3] ids
// Bind the buffer to represent which buffer we are currently working with
gl.glBindBuffer(GL.GL_ARRAY_BUFFER, VBO[4*i+3]);
// Allocate the float buffer for the colors. The size of the
// buffer of normals corresponds to the same number of floats
// as the vertices.
totalBufferSize = tempObj.numOfFaces*3*3;
colors = FloatBuffer.allocate(totalBufferSize);
// Create the float buffer for the colors by looping over each
// face and each face's vertex and adding the color of
// each corresponding vertex to the buffer
for (int j=0; j<tempObj.numOfFaces; j++) {
// if the material id is in the materials list
if (tempObj.faces[j].materialID < materials.size()) {
// Get the color (I'm getting the color from a material in my object)
byte aColor[] = materials.get(tempObj.faces[j].materialID).color;
// I convert the byte color to float color using my own methods...so
// inttofloat(), unsignedByteToInt() won't work for you.
// You basically need a way to get the float components of the color. (0.0f to 1.0f)
float R = inttofloat(unsignedByteToInt(aColor[0]));
float G = inttofloat(unsignedByteToInt(aColor[1]));
float B = inttofloat(unsignedByteToInt(aColor[2]));
// First vertex of face
colors.put(R);
colors.put(G);
colors.put(B);
//colors.put(1.0f); // if using a 4 float color then you need a fourth value
// Second vertex of face
colors.put(R);
colors.put(G);
colors.put(B);
//colors.put(1.0f); // only if using 4 floats
// Third vertex of face
colors.put(R);
colors.put(G);
colors.put(B);
//colors.put(1.0f); // only if using 4 floats
// else no material id exists and so just put in default color of white
} else {
for (int z=0; z<9; z++) {
colors.put(1.0f);
}
}
}
// Rewind buffer
colors.rewind();
// Write out the colors buffer to the currently bound VBO.
// Use 'totalBufferSize*4' because we have 4 bytes for a float.
gl.glBufferData(GL.GL_ARRAY_BUFFER, totalBufferSize*4, colors, GL.GL_STATIC_DRAW);
// Free colors
colors = null;
}