Author: Movania Muhammad Mobeen
In this article, we will learn how to port the 4th tutorial on loading 3ds mesh file in the new OpenGL 3.3. Ok let's have a look at tutorial 4. In this tutorial, we see a rotating textured 3ds spaceship mesh displayed on screen. We saw in the last tutorial how to handle textue mapping in OpenGL 3.3 and above. With that knowledge in our hands we can now explore on how to handle multiple meshes. Since the first tutorial, we know that handling of geometry in OpenGL 3.0 and above requires the use of vertex buffer objects VBO along with their state management using the vertex array object (VAO). Every mesh contains an array of vertices, normals, and optionally texture coordinates (also called uv coordinates). In order to handle these arrays, we have two options:
- Store all of these arrays in a large array and then use a single VBO to handle it.
- Store each array into a separate vbo and then assign offset to the VBO.
While both of these approaches work. We adopted the second approach of storing each array into a separate VBO. This keeps our code clean and also makes our code consistent with the original 3ds loading tutorial.
OK as before first we generate ids for all our VBOs and VAO:
glGenVertexArrays(1, &vaoID); glGenBuffers (1, &vboVerticesID); glGenBuffers (1, &vboTexCoordID); glGenBuffers (1, &vboIndicesID);
Similar to the previous tutorial, we pass the data into these buffer objects using glBufferData calls:
glBindVertexArray(vaoID); glBindBuffer (GL_ARRAY_BUFFER, vboVerticesID); glBufferData (GL_ARRAY_BUFFER, sizeof(GLfloat)*3*object.vertices_qty, &object.vertex, GL_STATIC_DRAW); GL_CHECK_ERRORS glEnableVertexAttribArray(shader["vVertex"]); glVertexAttribPointer (shader["vVertex"], 3, GL_FLOAT, GL_FALSE,stride,0); GL_CHECK_ERRORS glBindBuffer (GL_ARRAY_BUFFER, vboTexCoordID); glBufferData (GL_ARRAY_BUFFER, sizeof(GLfloat)*2*object.vertices_qty, &object.mapcoord, GL_STATIC_DRAW); GL_CHECK_ERRORS glEnableVertexAttribArray(shader["vUV"]); glVertexAttribPointer (shader["vUV"], 2, GL_FLOAT, GL_FALSE,sizeof(GLfloat)*2,0); GL_CHECK_ERRORS glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboIndicesID); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLushort)*3*object.polygons_qty, &object.polygon, GL_STATIC_DRAW); glBindVertexArray(0);
All of these lines should be easy to understand now. We first bind the vertices. Since each vertex has 3 float values, the total size of the vertices (the second parameter to glBufferData) is sizeof(GLfloat)*3*object.vertices_qty. Similarly, we assign the size for the texture coordinates and finally, we bind out element array buffer (the indices array). Since each object id is an unsinged short type and one triangle has 3 such numbers, the total size of the index buffer is sizeof(GLushort)*3*object.polygons_qty. One thing to note here is that we could have used the approach in the last tutorial to get the total size in bytes of our vertex buffer by issuing a call like this:
However, this is wrong since the sizeof operator returns the size of the array. Since our array has been statically allocated with 8000 elements, sizeof would give us 8000*3*sizeof(float) = 96000 bytes when the actual size of our vertex array is 649 elements so we would have allocated more space then required by our vertices. We have inserted a macro in between every two lines called CHECK_GL_ERRORS. This macro tells us if the OpenGL state is error free. If there is an error, an assertion is thrown so we immediately know that we have done something wrong. Our vertex and fragment shaders are exactly the same as in the last tutorial and the handling of the attributes and uniforms is also identical. The display function is also similar. As before, we bind our VAO. Then we use our shader and pass our combined modelview projection matrix (MVP) to it (see the previous tutorial if u forgot how to do this). The only difference is that now we use the object's polygon count (object.polygons_qty) to determine the number of triangles to draw. Since every triangle has 3 indices, the total indices are (object.polygons_qty*3). After issuing the draw element call, we un use our shader and then unbind our VAO:
glBindVertexArray(vaoID); shader.Use(); glUniformMatrix4fv(shader("MVP"), 1, GL_FALSE, glm::value_ptr(MVP)); glDrawElements(GL_TRIANGLES, object.polygons_qty*3, GL_UNSIGNED_SHORT, 0); shader.UnUse(); glBindVertexArray(0);
After these calls, we issue a call to glutSwapBuffers to make sure the the back buffer is flipped to the screen:
Our vertex and fragment shaders are exactly the same as the last tutorial so we wont discuss them here again.
Thats it, running the code gives us the following output:
The Source Code of this lesson can be downloaded from the Tutorials Main Page