Sunday, December 1, 2013

Sponza рэндэрлэх оролдлого 4, Mesh ачаалах, рэндэрлэх

Өмнөх постуудад материалуудыг нь ачаалж байсан бол одоо мэш буюу геометрийн өгөгдлүүдийг нь ачаалая.



Геометр өгөгдлийг хадгалах бүтцийн хувьд
typedef struct {
    GLuint vertexID;
    GLuint normalID;
    GLuint texcoordID;
    GLuint vaoID;
    int numFaces;
    int *faces;
    int material;
} Mesh;

Энд vertexID нь бүх геометрийн оройнуудыг агуулсан буфферийн ID-г төлөөлнө.
normalID нь геометрийн оройн нормалуудыг хадгалсан буфферийн ID-г төлөөлнө.
texcoordID нь текстур буулгалтын UV координатуудын буфферийн ID-г төлөөлнө.
vaoID нь vertex array object буюу эдгээр буфферүүдээ бүлэглэн авч явна.
faces нь зурагдах интексийн дарааллыг агуулна.
material нь энэ mesh-д хэрэглэгдэх өмнө тодорхойлсон материалын дугаарыг хадгална.

Одоогийн байдлаар зөвхөн diffuse текстуруудыг ашиглахаар байгаа. Shader ачаалах shader management талын функцүүдийг өмнө бичиж байсан RRC-TANK прожектоосоо авч ашиглалаа.

Vertex Shader
#version 330
layout(location=0) in vec4 in_position;
layout(location=1) in vec3 in_normal;
layout(location=2) in vec2 in_texcoord;
out vec2 varyingTexcoord;
uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
void main(void)
{
    gl_Position = (projectionMatrix * viewMatrix * modelMatrix) * in_position;
    varyingTexcoord = in_texcoord;
}


Fragment Shader
#version 330
uniform sampler2D material_diffuse_texture;
in vec2 varyingTexcoord;
out vec4 out_color;
void main(void)
{
    out_color = texture2D(material_diffuse_texture, vec2(varyingTexcoord.x,varyingTexcoord.y));
}




Main.cpp
#include <cstdlib>
#include <string>
#include <iostream>
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtx/constants.hpp>
#include "MaterialManager.hpp"
#include "Material.hpp"
#include "ShaderManager.hpp"
#include "Shader.hpp"
#include "SceneObject.hpp"

using namespace std;


GLFWwindow *window;
int windowWidth = 1000;
int windowHeight = 800;

bool wKeyPressed;
bool sKeyPressed;
bool aKeyPressed;
bool dKeyPressed;
double mouseX, mouseY;
float speed = 150.09f;
float mouseSpeed = 0.04f;
glm::vec3 position = glm::vec3(0, 0, 5);
glm::vec3 direction;
//glm::vec3 right;
glm::vec3 up;
float horizontalAngle = 3.14159f;
float verticalAngle = 0.0f;
float fov = 60.0f;

glm::mat4 modelMatrix;
glm::mat4 viewMatrix;
glm::mat4 projectionMatrix;

void initWindow();
void disposeWindow();
void handleKeyboardInput(GLFWwindow *window, int key, int scancode, int action, int mods);
void processInput(float dt);

void processMaterial(aiMaterial* material, int index);
void processMesh(aiMesh* mesh, SceneObject *object);

/*
 * experiment with ASSIMP
 */
int main(int argc, char** argv) {
    initWindow();
    glfwSetKeyCallback(window, handleKeyboardInput);
    MaterialManager::getSingleton();
    ShaderManager* sm = ShaderManager::getSingleton();
    sm->CreateShaderProgram("phong");
    sm->AttachShader("phongVertex", VERTEX);
    sm->AttachShader("phongFragment", FRAGMENT);
    sm->LoadShaderSource("phongVertex", "shaders/phong.vert.glsl");
    sm->LoadShaderSource("phongFragment", "shaders/phong.frag.glsl");
    sm->CompileShader("phongVertex");
    sm->CompileShader("phongFragment");
    sm->AttachShaderToProgram("phong", "phongVertex");
    sm->AttachShaderToProgram("phong", "phongFragment");
    sm->LinkProgramObject("phong");
    std::cout << "ShaderProgram ID is " << (*sm)["phong"]->GetID() << std::endl;

    SceneObject *sceneObject = new SceneObject();

    std::string filePath = "sponza.obj";
    Assimp::Importer importer;
    const aiScene *scene = importer.ReadFile(
            filePath.c_str(),
            aiProcess_OptimizeGraph |
            aiProcess_OptimizeMeshes |
            aiProcess_ImproveCacheLocality |
            aiProcess_SplitLargeMeshes |
            aiProcess_Triangulate |
            aiProcess_JoinIdenticalVertices |
            aiProcess_SortByPType
            );
    if (!scene) {
        std::cout << "error in loading file : " << importer.GetErrorString() << std::endl;
        return 1;
    }
    std::cout << "number of meshes " << scene->mNumMeshes << std::endl;
    std::cout << "number of materials " << scene->mNumMaterials << std::endl;
    for (unsigned int i = 0; i < scene->mNumMaterials; i++) {
        processMaterial(scene->mMaterials[i], i);
    }
    for (unsigned int i = 0; i < scene->mNumMeshes; i++) {
        processMesh(scene->mMeshes[i], sceneObject);
    }

    projectionMatrix = glm::mat4(1.0f);
    viewMatrix = glm::mat4(1.0f);
    viewMatrix = glm::translate(viewMatrix, glm::vec3(0, 0, -2));
    glViewport(0, 0, (int) windowWidth, (int) windowHeight);
    projectionMatrix = glm::perspective(60.0f, (float) windowWidth / windowHeight, 1.0f, 10000.0f);

    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);
    glEnable(GL_CULL_FACE);
    glCullFace(GL_BACK);
    glFrontFace(GL_CCW);

    GLuint programID = (*sm)["phong"]->GetID();
    float deltaTime = 0.0f;
    float startTime = glfwGetTime();
    float buffTime = 0.0;
    int counter = 0;
    while (!glfwWindowShouldClose(window)) {
        processInput(deltaTime);
        glClearColor(0.0f, 0.5f, 1.0f, 0.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        glUseProgram(programID);
        glUniformMatrix4fv(glGetUniformLocation(programID, "viewMatrix"), 1, GL_FALSE, glm::value_ptr(viewMatrix));
        glUniformMatrix4fv(glGetUniformLocation(programID, "projectionMatrix"), 1, GL_FALSE, glm::value_ptr(projectionMatrix));
        sceneObject->render();
        glUseProgram(0);

        glfwSwapBuffers(window);
        glfwPollEvents();
        double lastTime = glfwGetTime();
        deltaTime = lastTime - startTime;
        startTime = lastTime;
        counter++;
        buffTime += deltaTime;
        if (buffTime > 1.0f) {
            buffTime = 0.0f;
            cout << "fps " << counter << endl;
            counter = 0;
        }
    }

    delete sceneObject;
    delete MaterialManager::getSingleton();
    delete ShaderManager::getSingleton();
    disposeWindow();
    return 0;
}

void processMesh(aiMesh* mesh, SceneObject *object) {
    if (mesh->mMaterialIndex == 5)
        return;
    float *normals, *textureCoordinates;
    float *vertices = (float *) malloc(4 * mesh->mNumVertices * sizeof (float));
    if (mesh->HasNormals())
        normals = (float *) malloc(3 * mesh->mNumVertices * sizeof (float));
    if (mesh->HasTextureCoords(0))
        textureCoordinates = (float *) malloc(2 * mesh->mNumVertices * sizeof (float));
    for (unsigned int i = 0; i < mesh->mNumVertices; i++) {
        vertices[4 * i + 0] = mesh->mVertices[i].x;
        vertices[4 * i + 1] = mesh->mVertices[i].y;
        vertices[4 * i + 2] = mesh->mVertices[i].z;
        vertices[4 * i + 3] = 1.0f;
        if (mesh->HasNormals()) {
            normals[3 * i + 0] = mesh->mNormals[i].x;
            normals[3 * i + 1] = mesh->mNormals[i].y;
            normals[3 * i + 2] = mesh->mNormals[i].z;
        }
        if (mesh->HasTextureCoords(0)) {
            textureCoordinates[2 * i + 0] = mesh->mTextureCoords[0][i].x;
            textureCoordinates[2 * i + 1] = mesh->mTextureCoords[0][i].y;
        }
    }
    int *faces = (int *) malloc(3 * mesh->mNumFaces * sizeof (int));
    for (unsigned int i = 0; i < mesh->mNumFaces; i++) {
        faces[3 * i + 0] = mesh->mFaces[i].mIndices[0];
        faces[3 * i + 1] = mesh->mFaces[i].mIndices[1];
        faces[3 * i + 2] = mesh->mFaces[i].mIndices[2];
    }
    object->addMesh(vertices, mesh->mNumVertices, normals, mesh->mNumVertices, textureCoordinates, mesh->mNumVertices, faces, mesh->mNumFaces, mesh->mMaterialIndex);
}

void processMaterial(aiMaterial* material, int index) {
    MaterialManager *mm = MaterialManager::getSingleton();
    std::cout << "================ ID " << index << "====================" << std::endl;

    aiString materialName;

    // Get the name for managing purpose
    if (material->Get(AI_MATKEY_NAME, materialName) != AI_SUCCESS) {
        std::cout << "ERROR: while getting name of material" << std::endl;
    }

    aiString diffuseTexturePath, normalMapTexturePath, specularTexturePath;
    aiColor3D diffuseColor, ambientColor, specularColor, emissiveColor;
    float opacity, shininess;

    // Get diffuse color
    if (material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuseColor) != AI_SUCCESS) {
        std::cout << "ERROR: Could not get diffuse color for Material " << materialName.data << std::endl;
    }

    // Get ambient color
    if (material->Get(AI_MATKEY_COLOR_AMBIENT, ambientColor) != AI_SUCCESS) {
        std::cout << "ERROR: Could not get ambient color for Material " << materialName.data << std::endl;
    }

    // Get diffuse color
    if (material->Get(AI_MATKEY_COLOR_SPECULAR, specularColor) != AI_SUCCESS) {
        std::cout << "ERROR: Could not get specular color for Material " << materialName.data << std::endl;
    }

    // Get opacity
    if (material->Get(AI_MATKEY_OPACITY, opacity) != AI_SUCCESS) {
        std::cout << "ERROR: Could not get opacity for Material " << materialName.data << std::endl;
    }

    // Get emissive color
    if (material->Get(AI_MATKEY_COLOR_EMISSIVE, emissiveColor) != AI_SUCCESS)
        emissiveColor = aiColor3D(0);

    // Get shininess
    if (material->Get(AI_MATKEY_SHININESS, shininess) != AI_SUCCESS) {
        std::cout << "ERROR: Could not get Shininess for Material " << materialName.data << std::endl;
    }


    // material color parameters
    std::cout << "diffuse color [" << diffuseColor.r << "," << diffuseColor.g << "," << diffuseColor.b << "] opacity=" << opacity << std::endl;
    std::cout << "ambient color [" << ambientColor.r << "," << ambientColor.g << "," << ambientColor.b << "]" << std::endl;
    std::cout << "specular color [" << specularColor.r << "," << specularColor.g << "," << specularColor.b << "]" << std::endl;
    std::cout << "emissive color [" << emissiveColor.r << "," << emissiveColor.g << "," << emissiveColor.b << "]" << std::endl;


    Material *m = mm->createMaterial(std::string(materialName.data), index);
    m->setDiffuseColor(glm::vec4(diffuseColor.r, diffuseColor.g, diffuseColor.b, opacity));
    m->setAmbientColor(glm::vec3(ambientColor.r, ambientColor.g, ambientColor.b));
    m->setSpecularColor(glm::vec3(specularColor.r, specularColor.g, specularColor.b));
    m->setEmissiveColor(glm::vec3(emissiveColor.r, emissiveColor.g, emissiveColor.b));

    // Load diffuse texture if there is one
    if (material->GetTextureCount(aiTextureType_DIFFUSE) > 0) {
        if (material->Get(AI_MATKEY_TEXTURE(aiTextureType_DIFFUSE, 0), diffuseTexturePath) != AI_SUCCESS) {
            std::cout << "ERROR: Could not get Diffuse Texture for Material " << materialName.data << std::endl;
            return;
        }
        m->loadDiffuseTexture(diffuseTexturePath.data);
        std::cout << "diffuse texture path " << diffuseTexturePath.data << std::endl;
    }

    // Load height map texture if there is one
    if (material->GetTextureCount(aiTextureType_HEIGHT) > 0) {
        if (material->Get(AI_MATKEY_TEXTURE(aiTextureType_HEIGHT, 0), normalMapTexturePath) != AI_SUCCESS) {
            std::cout << "ERROR: Could not get Height Map Texture for Material " << materialName.data << std::endl;
            return;
        }
        m->loadNormalTexture(normalMapTexturePath.data);
        std::cout << "height map texture path " << normalMapTexturePath.data << std::endl;
    }

    // Load mask texture if there is one
    if (material->GetTextureCount(aiTextureType_SPECULAR) > 0) {
        if (material->Get(AI_MATKEY_TEXTURE(aiTextureType_SPECULAR, 0), specularTexturePath) != AI_SUCCESS) {
            std::cout << "ERROR: Could not get Mask Texture for Material " << materialName.data << std::endl;
            return;
        }
        m->loadSpecularTexture(specularTexturePath.data);
        std::cout << "specular texture path " << specularTexturePath.data << std::endl;
    }
    std::cout << "==========================================" << std::endl;
}

void initWindow() {
    if (!glfwInit()) {
        std::cout << "Error: GLFW failed to initialize.\n";
        return;
    }

    window = glfwCreateWindow(windowWidth, windowHeight, "OpenGL ASSIMP", NULL, NULL);
    if (!window) {
        std::cout << "Error: Failed to create window.\n";
        glfwTerminate();
        return;
    }
    glfwMakeContextCurrent(window);

    if (GLEW_OK != glewInit()) {
        std::cout << "Error: Failed to load OpenGL functions.\n";
        glfwTerminate();
        return;
    }
    glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
}

void disposeWindow() {
    glfwDestroyWindow(window);
    glfwTerminate();
}

void handleKeyboardInput(GLFWwindow *window, int key, int scancode, int action, int mods) {
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, 1);
    else if (key == GLFW_KEY_W && action == GLFW_PRESS)
        wKeyPressed = true;
    else if (key == GLFW_KEY_S && action == GLFW_PRESS)
        sKeyPressed = true;
    else if (key == GLFW_KEY_A && action == GLFW_PRESS)
        aKeyPressed = true;
    else if (key == GLFW_KEY_D && action == GLFW_PRESS)
        dKeyPressed = true;
    else if (key == GLFW_KEY_W && action == GLFW_RELEASE)
        wKeyPressed = false;
    else if (key == GLFW_KEY_S && action == GLFW_RELEASE)
        sKeyPressed = false;
    else if (key == GLFW_KEY_A && action == GLFW_RELEASE)
        aKeyPressed = false;
    else if (key == GLFW_KEY_D && action == GLFW_RELEASE)
        dKeyPressed = false;
}

void processInput(float dt) {
    static glm::vec3 right;
    if (wKeyPressed)
        position += direction * speed * dt;
    if (sKeyPressed)
        position -= direction * speed * dt;
    if (aKeyPressed)
        position -= right * speed * dt;
    if (dKeyPressed)
        position += right * speed * dt;

    direction = glm::vec3
            (
            std::cos(verticalAngle) * std::sin(horizontalAngle),
            std::sin(verticalAngle),
            std::cos(verticalAngle) * std::cos(horizontalAngle)
            );
    right = glm::vec3
            (
            std::sin(horizontalAngle - 3.14159f / 2.0f),
            0.0f,
            std::cos(horizontalAngle - 3.14159f / 2.0f)
            );
    up = glm::cross(right, direction);

    glfwGetCursorPos(window, &mouseX, &mouseY);
    glfwSetCursorPos(window, windowWidth / 2, windowHeight / 2);
    horizontalAngle += mouseSpeed * dt * float(windowWidth / 2 - mouseX);
    if (!up.y >= -0.5f)
        verticalAngle += mouseSpeed * dt * float(windowHeight / 2 - mouseY);
    else if (up.y == -1.0f)
        up = glm::vec3(up.x, 1, up.z);

    viewMatrix = glm::lookAt(position, position + direction, up);
}


Шинээр нэмсэн классууд
SceneObject.hpp
#ifndef SCENEOBJECT_HPP
#define SCENEOBJECT_HPP

#include <GL/glew.h>
#include <glm/glm.hpp>
#include <string>
#include <vector>

typedef struct {
    GLuint vertexID;
    GLuint normalID;
    GLuint texcoordID;
    GLuint vaoID;
    int numFaces;
    int *faces;
    int material;
    glm::vec3 boundingBoxMin;
    glm::vec3 boundingBoxMax;
} Mesh;

class SceneObject {
public:
    SceneObject();
    virtual ~SceneObject();
    void render();
    void update(float dt);
    void addMesh(float *vertices, int numVertices, float *normals, int numNormals, float *textureCoordinates, int numTextureCoordinates, int *faces, int numFaces, int materialIndex);
    void addMesh(float *vertices, int numVertices, float *normals, int numNormals, float *textureCoordinates, int numTextureCoordinates, int *faces, int numFaces, const std::string &materialName);
    glm::vec3 getBoundingBoxMin();
    glm::vec3 getBoundingBoxMax();
protected:
    std::vector<Mesh> meshes;
    glm::mat4 modelMatrix;
    glm::mat4 parentMatrix;
    glm::vec3 boundingBoxMin;
    glm::vec3 boundingBoxMax;
};

#endif /* SCENEOBJECT_HPP */

SceneObject.cpp
#include "SceneObject.hpp"
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_inverse.hpp>
#include "MaterialManager.hpp"
#include "ShaderManager.hpp"

SceneObject::SceneObject() {
    this->modelMatrix = glm::mat4(1.0f);
    this->boundingBoxMin = glm::vec3(FLT_MAX);
    this->boundingBoxMax = glm::vec3(FLT_MIN);
}

SceneObject::~SceneObject() {
    std::cout << "desctructor function of the SceneObject" << std::endl;
    for (std::vector<Mesh>::iterator it = this->meshes.begin(); it != this->meshes.end(); it++) {
        Mesh mesh = (*it);
        glDeleteBuffers(1, &mesh.vertexID);
        glDeleteBuffers(1, &mesh.normalID);
        glDeleteBuffers(1, &mesh.texcoordID);
        glDeleteVertexArrays(1, &mesh.vaoID);
    }
}

void SceneObject::addMesh(float *vertices, int numVertices, float *normals, int numNormals, float *textureCoordinates, int numTextureCoordinates, int *faces, int numFaces, const std::string &materialName) {
    MaterialManager *mm = MaterialManager::getSingleton();
    int idx = mm->getMaterialIndex(materialName);

    this->addMesh(vertices, numVertices, normals, numNormals, textureCoordinates, numTextureCoordinates, faces, numFaces, idx);
}

void SceneObject::addMesh(float *vertices, int numVertices, float *normals, int numNormals, float *textureCoordinates, int numTextureCoordinates, int *faces, int numFaces, int materialIndex) {
    MaterialManager* mm = MaterialManager::getSingleton();
    GLuint programID = (*ShaderManager::getSingleton())["phong"]->GetID();

    GLuint positionAttrib = glGetAttribLocation(programID, "in_position");
    GLuint normalAttrib = glGetAttribLocation(programID, "in_normal");
    GLuint texcoordAttrib = glGetAttribLocation(programID, "in_texcoord");

    Mesh m;

    m.material = materialIndex;
    m.boundingBoxMin = glm::vec3(FLT_MAX);
    m.boundingBoxMax = glm::vec3(FLT_MIN);

    Material *mat = mm->getMaterial(materialIndex);

    glGenVertexArrays(1, &m.vaoID);
    glBindVertexArray(m.vaoID);

    if (numVertices > 0) {
        for (int i = 0; i < numVertices; i++) {
            if (vertices[4 * i + 0] < this->boundingBoxMin.x)
                this->boundingBoxMin.x = vertices[4 * i + 0];
            if (vertices[4 * i + 0] > this->boundingBoxMax.x)
                this->boundingBoxMax.x = vertices[4 * i + 0];

            if (vertices[4 * i + 1] < this->boundingBoxMin.y)
                this->boundingBoxMin.y = vertices[4 * i + 1];
            if (vertices[4 * i + 1] > this->boundingBoxMax.y)
                this->boundingBoxMax.y = vertices[4 * i + 1];

            if (vertices[4 * i + 2] < this->boundingBoxMin.z)
                this->boundingBoxMin.z = vertices[4 * i + 2];
            if (vertices[4 * i + 2] > this->boundingBoxMax.z)
                this->boundingBoxMax.z = vertices[4 * i + 2];

            if (vertices[4 * i + 0] < m.boundingBoxMin.x)
                m.boundingBoxMin.x = vertices[4 * i + 0];
            if (vertices[4 * i + 0] > m.boundingBoxMax.x)
                m.boundingBoxMax.x = vertices[4 * i + 0];

            if (vertices[4 * i + 1] < m.boundingBoxMin.y)
                m.boundingBoxMin.y = vertices[4 * i + 1];
            if (vertices[4 * i + 1] > m.boundingBoxMax.y)
                m.boundingBoxMax.y = vertices[4 * i + 1];

            if (vertices[4 * i + 2] < m.boundingBoxMin.z)
                m.boundingBoxMin.z = vertices[4 * i + 2];
            if (vertices[4 * i + 2] > m.boundingBoxMax.z)
                m.boundingBoxMax.z = vertices[4 * i + 2];

        }

        glGenBuffers(1, &m.vertexID);
        glBindBuffer(GL_ARRAY_BUFFER, m.vertexID);
        glBufferData(GL_ARRAY_BUFFER, numVertices * 4 * sizeof (GLfloat), vertices, GL_STATIC_DRAW);
        glEnableVertexAttribArray(positionAttrib);
        glVertexAttribPointer(positionAttrib, 4, GL_FLOAT, GL_FALSE, 0, 0);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
    }

    if (numNormals > 0) {
        glGenBuffers(1, &m.normalID);
        glBindBuffer(GL_ARRAY_BUFFER, m.normalID);
        glBufferData(GL_ARRAY_BUFFER, numNormals * 3 * sizeof (GLfloat), normals, GL_STATIC_DRAW);
        glEnableVertexAttribArray(normalAttrib);
        glVertexAttribPointer(normalAttrib, 3, GL_FLOAT, GL_FALSE, 0, 0);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
    }

    if (numTextureCoordinates > 0) {
        glGenBuffers(1, &m.texcoordID);
        glBindBuffer(GL_ARRAY_BUFFER, m.texcoordID);
        glBufferData(GL_ARRAY_BUFFER, numTextureCoordinates * 2 * sizeof (GLfloat), textureCoordinates, GL_STATIC_DRAW);
        glEnableVertexAttribArray(texcoordAttrib);
        glVertexAttribPointer(texcoordAttrib, 2, GL_FLOAT, GL_FALSE, 0, 0);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
    }

    m.faces = faces;
    m.numFaces = numFaces;

    glBindVertexArray(0);

    this->meshes.push_back(m);
}

void SceneObject::update(float dt) {

}

glm::vec3 SceneObject::getBoundingBoxMin() {
    return this->boundingBoxMin;
}

glm::vec3 SceneObject::getBoundingBoxMax() {
    return this->boundingBoxMax;
}

void SceneObject::render() {
    GLuint programID = (*ShaderManager::getSingleton())["phong"]->GetID();
    glUniformMatrix4fv(glGetUniformLocation(programID, "modelMatrix"), 1, GL_FALSE, glm::value_ptr(this->modelMatrix));
    MaterialManager *mm = MaterialManager::getSingleton();
    for (std::vector<Mesh>::iterator it = this->meshes.begin(); it != this->meshes.end(); it++) {
        Mesh m = (*it);
        Material *mat = mm->getMaterial(m.material);
        mat->bindMaterial(programID);
        glBindVertexArray(m.vaoID);
        glDrawElements(GL_TRIANGLES, 3 * m.numFaces, GL_UNSIGNED_INT, m.faces);
        glBindVertexArray(0);
        mat->unbindMaterial();
    }
}