This is going to be by far the easiest part in this tutorial. In order to display our model we just need to parse through the structures so that we get every vertex in the order that it is referenced. We will be using modern OpenGL in order to render a model with color based on vertex position.

In order to follow along you will have to setup some code in order to open a window. If you are using SDL you can download the starter files that I have included which will open a window, load the GLSL shaders, and rotate the model, but does not have the code to actually compile the OpenGL buffers.

Even if you have avoided GLSL up to this day, what better way to apply it then through loading a model. Even without bones a MS3D file can still store static geometry that you can use in you applications. For the most part GLSL is really similar in syntax to the C language. The most important part to our shaders is this chunk of code which can be found in “vertex.glsl”

#version 330 core precision highp float; layout(location = 0) in vec3 vertex_Position; layout(location = 1) in vec2 veretex_UV; layout(location = 2) in vec3 vertex_Normal;

This tells OpenGL the version that we are coding for as well as what data we are going to send to our shaders. We are going to be sending the positions of the vertices, the UV coordinates, and the normals.

If you have set up your own code to open a window, but do not know how to load the shaders you can look through my code to see how to do it.

Equally important are the two variable declerations.

uniform mat4 projection; uniform mat4 modelview;

This is what will position the model and project it onto the screen. If you find the your model is not being draw, I want you to make sure that you are uploading the matrices with **glUniformMatrix4fv**. Now let’s see how to actually get our model on the screen.

## A few additions to our class

We are going to be adding a few functions and variables to our class.

void draw(int modelview_Location); //Draws the model void setPosition(glm::vec3 newPos); void setRotation(glm::vec3 newRot); ... private: ... glm::vec3 pos; glm::vec3 rot; void genBuffers(); //Compiles OpenGL drawing buffers unsigned totalVertices; //Calculated from numTriangles*3 //OpenGL buffers unsigned vertArray; unsigned posBuffer; unsigned uvBuffer; unsigned normBuffer; };

The most obvious one is **draw** which will have the code to draw our model in the correct position and orientation. The class is not responsible for fetching the shader location for the modelview matrix, so we have to make sure that we give it to the function.

The **genBuffers** will compile all of the OpenGL buffers so that we can quickly draw our model as many times as we need. Variables for the position and rotatation have also been added as well as functions so we can actually modify them.

We will store the precomputed value for the TRUE number of vertices in our model in **totalVertices**. If you remember from the previous tutorial, that a flat square plane with four countable vertices actually had two triangles with 3 vertices each.

Finally, we have variables for the OpenGL buffers that we will need in order to draw our model.

## Onto the functions!

Let’s start off with the new **genBuffers** function. For the most part, the bulk of it is a for-loop which we collect all of the data that we need. However, we need to generate some information before the loop begins.

void ms3dModel::genBuffers(){ unsigned totalVerts = numTriangles * 3; totalVertices = totalVerts; //These are temporary buffer to copy the vertex data to float *vertData = (float*) malloc(totalVerts * 3 * sizeof(float) ); float *uvData = (float*) malloc(totalVerts * 2 * sizeof(float) ); float *normData = (float*) malloc(totalVerts * 3 * sizeof(float) ); //Pointers to whichever index we are currently using for each structure ms3dMesh *pMesh; ms3dTriangle *pTri; ms3dVertex *pVert; //Number of vertices that have been parsed int processed = 0;

The first thing that is done is figuring out the total number of vertices that makes up a model. All we need to do is take the number of triangles and multiply by three. After that we need to allocate the memory for some temporary buffers so we can send the data to OpenGL.

I want you to pay close attention to the amounts being allocated. There are three position axis for every vertex (x, y, z) so we multiply the total number of vertices by three. Similarly, there are two texture coordinates for every vertex so in that case we multiply by two.

If you want, you can replace these with vectors so that you just push the data to the array. I personally like to only handle the exact amount of memory that I will be using, but the choice is yours.

I also want you to take special notice of the **processed** variable. As the models that we load become increasingly complex, there may be several meshes that make up the entire thing. The catch is that there can be a variable number of triangles within any particular mesh, so we need to make sure that we add the amount that was in the mesh that was just “processed.”

Now we can look at the for-loop that we use to copy the data to our temporary buffers.

//All vertices are parsed in the order that they are referenced, i.e. Mesh->Triangle->Vertex for(int indMesh = 0; indMesh < numMeshes; indMesh++){ pMesh = &meshes[indMesh]; for(int indTri = 0; indTri < pMesh->numberTriangles; indTri++){ pTri = &triangles[pMesh->triangleIndices[indTri]]; //Normals and UVs are stored in the triangle, NOT the vertices memcpy(normData + indTri*9 + processed*3, pTri->vertexNormals, 9 * sizeof(float) ); for(int indVert = 0; indVert < 3; indVert++){ pVert = &vertices[pTri->vertexIndices[indVert]]; //Triangles have three vertices which in turn have three position vectors //So, all operations are powers of 3 memcpy(vertData + indVert*3 + indTri*9 + processed*3, pVert->position, 3 * sizeof(float) ); //U and V data is stored in seperate buffers so we will copy it in the vertex loop uvData[indVert*2 + indTri*4 + processed*2] = pTri->vertexTexture_U[indVert]; uvData[1 + indVert*2 + indTri*4 + processed*2] = pTri->vertexTexture_V[indVert]; } } //Every triangle has 3 vertices processed += pMesh->numberTriangles * 3; }

The code is fairly straightforward. We start at the top with the meshes, and we make our way down until we get to vertices. This is when we can start copying the data to our buffers.

Even though the UV coordinates are stored in the triangles, we have to make sure that they are stored in the proper order so that it looks like, “U1, V1, U2, V2…” We can not just copy the two arrays one on top of the other. The normals on the other hand, are already in an OpenGL friendly format and we can just copy the whole chunk into our buffer.

Finally, we add the number of vertices that was in the mesh to the **processed** variable. Just like with **totalVertices** we multiply the number of triangles by three.

## Uploading the OpenGL buffers

Now that we have organized all of the data into our temporary buffers, it is time to give it to OpenGL so we can draw our model.

//Allocate the buffers glGenVertexArrays(1, &vertArray); glGenBuffers(1, &posBuffer); glGenBuffers(1, &uvBuffer); glGenBuffers(1, &normBuffer);

We start by actually creating the buffers that we will use. These are the same buffers that we added to our class in the beginning of the tutorial.

We will start by looking at how to upload the vertex positions to OpenGL.

//Now we give all the data to OpenGL glBindVertexArray(vertArray); glBindBuffer(GL_ARRAY_BUFFER, posBuffer); glBufferData(GL_ARRAY_BUFFER, totalVerts * 3 * sizeof(float), vertData, GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);

Before you start uploading any data you have to make sure you bind the vertex array, and then bind the buffer that you will be giving the data to. In this case we bind **posBuffer**. After that we can send the data using **glBufferData**, and send the same size that we used to allocate **posData**. After that we call **glEnableVertexAttribArray**, and if you go back to the top where we looked at the beginning of “vertex.glsl” you will see that it cooresponds to the number of **vertex_Position**. Finally, we tell OpenGL the number of elements that make up a single vertex which is three (x, y, z) as well as the type that it is.

We can do the same with the other two buffers.

glBindBuffer(GL_ARRAY_BUFFER, uvBuffer); glBufferData(GL_ARRAY_BUFFER, totalVerts * 2 * sizeof(float), uvData, GL_STATIC_DRAW); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, normBuffer); glBufferData(GL_ARRAY_BUFFER, totalVerts * 3 * sizeof(float), normData, GL_STATIC_DRAW); glEnableVertexAttribArray(2); glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0);

Once again, notice that for all of the calls for the UV data, we use two instead of three. Since the data for these buffers will never change, we use GL_STATIC_DRAW when sending the data.

The last thing we need to do is free the temporary buffers that we allocated at the beginning of the function.

free(vertData); free(uvData); free(normData); }

and that brings us to the end of the **genBuffers** function.

## I can haz draw?

We are now at the final section of this tutorial. Drawing the model once the buffers are all uploaded is extremely simple. All we have to do is calculate the transformation matrix and send it to our shaders, set our class’s vertex array, and then call the draw command.

//Positions and draws the model void ms3dModel::draw(int modelview_Location){ //Make sure that there is something that can actually be drawn if(totalVertices > 0){ //Compile the modelview matrix glm::mat4 tranMat = glm::translate(glm::mat4(1.f), pos); glm::mat4 rotMat (1.f); rotMat = glm::rotate(rotMat, rot.z, glm::vec3(0.f, 0.f, 1.f)); rotMat = glm::rotate(rotMat, rot.y, glm::vec3(0.f, 1.f, 0.f)); rotMat = glm::rotate(rotMat, rot.x, glm::vec3(1.f, 0.f, 0.f)); //Send the modelview matrix to our shaders glm::mat4 transforms = tranMat * rotMat; glUniformMatrix4fv(modelview_Location, 1, GL_FALSE, &transforms[0][0]); //Draws the model glBindVertexArray(vertArray); glDrawArrays(GL_TRIANGLES, 0, totalVertices); } }

Most of this is self explanatory. The function is sent the location of the modelview matrix which can be obtained with a quick call to **glGetUniformLocation**. It is our responsibility to provide this so that we avoid hard coding it directly into the class. Next, it checks that the total number of vertices is greater than zero. After the transformation matrix has been calculated and uploaded, we bind our models vertex array which was generated by **genBuffers**. Then we call **glDrawArrays** and supply the total number of vertices.

Of course, if you try to run the code right now as it is, you won’t be able to see anything since we have note coded the functions to position and rotate the model. So let’s get those out of the way.

void ms3dModel::setPosition(glm::vec3 newPosition){ pos = newPosition; } void ms3dModel::setRotation(glm::vec3 newRotation){ rot = newRotation; }

Finally, we have to make some additions to our class constructor, and **clearBuffers** since we are allocating new information.

//Set everything to NULL ms3dModel::ms3dModel(){ ... totalVertices = 0; } ... //Clear all of the buffers if they are being used void ms3dModel::clearBuffers(){ totalVertices = 0; glDeleteVertexArrays(1, &vertArray); glDeleteBuffers(1, &posBuffer); glDeleteBuffers(1, &uvBuffer); glDeleteBuffers(1, &normBuffer); ... }

If you are using SDL to open your window and have downloaded the starter files then you can compile and see your results. Even if you are not, you may still want to use the glsl shader files that I have provided.

Finally, in your main code you can set the position and rotation with the new functions that we created. Once you are ready to draw it, you can call our brand new draw function. This is what my code looks like in order to draw that handsome little set of triangles.

//Set projection matrix glm::mat4 projection = glm::perspective(45.f, (float)width/(float)height, 0.1f, 10.f); glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "projection"), 1, GL_FALSE, &projection[0][0]); int modelview = glGetUniformLocation(shaderProgram, "modelview"); //Load model ms3dModel myModel; if(myModel.loadModel("ms3d-test1.ms3d") == 0){ myModel.printModel(); myModel.setPosition(glm::vec3(0.f, 0.f, -5.f)); unsigned int currtime; unsigned int oldtime = SDL_GetTicks(); //Main loop while(running){ static float theta = 0.f; //We will rotate the model with this //Event loop SDL_Event e; while(SDL_PollEvent(&e) != 0){ switch(e.type){ case SDL_QUIT: running = false; break; default: break; } } //All of the drawing stuff glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); //Rotate and draw model //If your model is rotating really slowly then remove the radians function myModel.setRotation(glm::vec3(0.f, glm::radians(theta), 0.f)); myModel.draw(modelview); //Swap the drawing buffers (double buffered rendering) SDL_GL_SwapWindow(window); //Calculate delta and convert to metric system millisecond, i.e. 1000 ms == 1 s currtime = SDL_GetTicks(); float delta = float(currtime - oldtime) * .001f; oldtime = currtime; theta += 45.f * delta; //45 degrees every second if(theta >= 360.f) theta -= 360.f; }

I have highlighted the important lines of code that will be used no matter what you use to open and handle your window. The first thing I do is send the projection matrix to the shaders as well as get the location for the modelview matrix so we can send it to our drawing function. I set the position of the model, and in the main loop I set the rotation and finally call our draw function that we made.

If you are using the same sample file that I provided in the previous tutorial, You should get something that looks like this.

This brings us to the end of the second tutorial for loading and animating a MilkShape 3D file! In the next tutorial, we will get to actually have a true skeletal animation system.

Here are a few things you can try on your own.

- Add a function to apply scaling to the model (Be careful with your normals!)
- Try drawing the normals of the triangles
- Load and display a more complex model. You may have to adjust the position and/or scale in order to see it
- Modify line 10 in “fragment.glsl” to see the model with double sided lighting (remove “1.0;”)