Search code examples
openglglfwglm-math

OpenGL - First mesh disappeared after second mesh was added


I am trying to load 2 different meshes (a taurus and a monkey head) in my program. Both using the same load_mesh function.

I used load_mesh for the taurus in the init function. The vertice data for this mesh was stored in g_pMeshVertices. It looked like this.

Taurus mesh added

Then I loaded a second one and it looked like this (below). The second mesh (monkey head) successfully appeared but half of the previous mesh disappeared. What I did was I used the load_mesh function again but for the "monkey head" inside init. The vertices for the new mesh was also stored in g_pMeshVertices. However, I created a new VBO and VAO to store the new vertices of g_pMeshVertices but it seems to be affecting my previous mesh. Can someone tell me why?

Monkey head added but half of taurus missing

Here is my code

#define MAX_CUBES 6
#define MAX_PLANES 6

// struct for lighting properties
struct LightProperties
{
    vec4 position;
    vec4 ambient;
    vec4 diffuse;
    vec4 specular;
    float shininess;
    vec3 attenuation;
    float cutoffAngle;
    vec3 direction;
};

// struct for material properties
struct MaterialProperties
{
    vec4 ambient;
    vec4 diffuse;
    vec4 specular;
};

LightProperties g_lightProperties;
MaterialProperties g_materialProperties;

// struct for vertex attributes
struct Vertex
{
    GLfloat position[3];
    GLfloat normal[3];
};

// Wall Vertices
Vertex g_vertices_plane[] = {
    -5.0f, -1.0f, 5.0f, // position
    0.0f, 1.0f, 0.0f,   // normal
    5.0f, -1.0f, 5.0f,  // position
    0.0f, 1.0f, 0.0f,   // normal
    -5.0f, -1.0f, -5.0f,// position
    0.0f, 1.0f, 0.0f,   // normal
    -5.0f, -1.0f, -5.0f,// position
    0.0f, 1.0f, 0.0f,   // normal
    5.0f, -1.0f, 5.0f,  // position
    0.0f, 1.0f, 0.0f,   // normal
    5.0f, -1.0f, -5.0f, // position
    0.0f, 1.0f, 0.0f,   // normal
};

Vertex g_vertices_cube[] = {
    // vertex 1
    -0.5f, 0.5f, 0.5f,  // position
    1.0f, 1.0f, 1.0f,   // normal
    // vertex 2
    -0.5f, -0.5f, 0.5f, // position
    1.0f, 1.0f, 1.0f,   // normal
    // vertex 3
    0.5f, 0.5f, 0.5f,   // position
    1.0f, 1.0f, 1.0f,   // normal
    // vertex 4
    0.5f, -0.5f, 0.5f,  // position
    1.0f, 1.0f, 1.0f,   // normal
    // vertex 5
    -0.5f, 0.5f, -0.5f, // position
    1.0f, 1.0f, 1.0f,   // normal
    // vertex 6
    -0.5f, -0.5f, -0.5f,// position
    1.0f, 1.0f, 1.0f,   // normal
    // vertex 7
    0.5f, 0.5f, -0.5f,  // position
    1.0f, 1.0f, 1.0f,   // normal
    // vertex 8
    0.5f, -0.5f, -0.5f, // position
    1.0f, 1.0f, 1.0f,   // normal
};

GLuint g_indices_cube[] = {
    0, 1, 2,    // triangle 1
    2, 1, 3,    // triangle 2
    4, 5, 0,    // triangle 3
    0, 5, 1,    // ...
    2, 3, 6,
    6, 3, 7,
    4, 0, 6,
    6, 0, 2,
    1, 5, 3,
    3, 5, 7,
    5, 4, 7,
    7, 4, 6,    // triangle 12
};

// Meshes
Vertex* g_pMeshVertices = NULL; // pointer to mesh vertices
GLint g_numberOfVertices = 0;   // number of vertices in the mesh
GLint* g_pMeshIndices = NULL;   // pointer to mesh indices
GLint g_numberOfFaces = 0;      // number of faces in the mesh

/*
    g_VBO[0] - Planes ie. walls, ceiling
    g_VBO[1] - Cubes ie. table, stools
    g_VBO[2] - Meshes (Taurus)
*/
GLuint g_IBO[3];                // index buffer object identifier
GLuint g_VBO[4];                // vertex buffer object identifier
GLuint g_VAO[4];                // vertex array object identifier
GLuint g_shaderProgramID = 0;   // shader program identifier

// locations in shader
GLuint g_MVP_Index;
GLuint g_M_Index = 0;
GLuint g_viewPointIndex = 0;
GLuint g_lightPositionIndex = 0;
GLuint g_lightAmbientIndex = 0;
GLuint g_lightDiffuseIndex = 0;
GLuint g_lightSpecularIndex = 0;
GLuint g_lightShininessIndex = 0;
GLuint g_lightAttenuationIndex = 0;
GLuint g_lightCutoffAngleIndex = 0;
GLuint g_lightDirectionIndex = 0;
GLuint g_materialAmbientIndex = 0;
GLuint g_materialDiffuseIndex = 0;
GLuint g_materialSpecularIndex = 0;

glm::mat4 g_modelMatrix_plane[MAX_PLANES];  // object's model matrix (4 walls + 1 ceiling + 1 floor)
glm::mat4 g_modelMatrix_cube[MAX_CUBES];// cube for table
glm::mat4 g_modelMatrix_mesh[2];        // for meshes
glm::mat4 g_viewMatrix;             // view matrix
glm::mat4 g_projectionMatrix;       // projection matrix
glm::vec3 g_viewPoint;              // view point

Camera g_camera;            // camera

GLuint g_windowWidth = 1600;        // window dimensions
GLuint g_windowHeight = 1000;
bool g_wireFrame = false;       // wireframe on or off

bool load_mesh(const char* fileName)
{
    // load file with assimp 
    const aiScene* pScene = aiImportFile(fileName, aiProcess_Triangulate
        | aiProcess_GenSmoothNormals | aiProcess_JoinIdenticalVertices);

    // check whether scene was loaded
    if (!pScene)
    {
        cout << "Could not load mesh." << endl;
        return false;
    }

    // get pointer to mesh 0
    const aiMesh* pMesh = pScene->mMeshes[0];

    // store number of mesh vertices
    g_numberOfVertices = pMesh->mNumVertices;

    // if mesh contains vertex coordinates
    if (pMesh->HasPositions())
    {
        // allocate memory for vertices
        g_pMeshVertices = new Vertex[pMesh->mNumVertices];

        // read vertex coordinates and store in the array
        for (int i = 0; i < pMesh->mNumVertices; i++)
        {
            const aiVector3D* pVertexPos = &(pMesh->mVertices[i]);

            g_pMeshVertices[i].position[0] = (GLfloat)pVertexPos->x;
            g_pMeshVertices[i].position[1] = (GLfloat)pVertexPos->y;
            g_pMeshVertices[i].position[2] = (GLfloat)pVertexPos->z;
        }
    }

    // if mesh contains normals
    if (pMesh->HasNormals())
    {
        // read normals and store in the array
        for (int i = 0; i < pMesh->mNumVertices; i++)
        {
            const aiVector3D* pVertexNormal = &(pMesh->mNormals[i]);

            g_pMeshVertices[i].normal[0] = (GLfloat)pVertexNormal->x;
            g_pMeshVertices[i].normal[1] = (GLfloat)pVertexNormal->y;
            g_pMeshVertices[i].normal[2] = (GLfloat)pVertexNormal->z;
        }
    }

    // if mesh contains faces
    if (pMesh->HasFaces())
    {
        // store number of mesh faces
        g_numberOfFaces = pMesh->mNumFaces;

        // allocate memory for vertices
        g_pMeshIndices = new GLint[pMesh->mNumFaces * 3];

        // read normals and store in the array
        for (int i = 0; i < pMesh->mNumFaces; i++)
        {
            const aiFace* pFace = &(pMesh->mFaces[i]);

            g_pMeshIndices[i * 3] = (GLint)pFace->mIndices[0];
            g_pMeshIndices[i * 3 + 1] = (GLint)pFace->mIndices[1];
            g_pMeshIndices[i * 3 + 2] = (GLint)pFace->mIndices[2];
        }
    }

    // release the scene
    aiReleaseImport(pScene);

    return true;
}

static void init(GLFWwindow* window)
{
    glEnable(GL_DEPTH_TEST);    // enable depth buffer test

    // create and compile our GLSL program from the shader files
    g_shaderProgramID = loadShaders("PerFragLightingVS.vert", "PerFragLightingFS.frag");

    // find the location of shader variables
    GLuint positionIndex = glGetAttribLocation(g_shaderProgramID, "aPosition");
    GLuint normalIndex = glGetAttribLocation(g_shaderProgramID, "aNormal");
    g_MVP_Index = glGetUniformLocation(g_shaderProgramID, "uModelViewProjectionMatrix");
    g_M_Index = glGetUniformLocation(g_shaderProgramID, "uModelMatrix");
    g_viewPointIndex = glGetUniformLocation(g_shaderProgramID, "uViewPoint");

    g_lightPositionIndex = glGetUniformLocation(g_shaderProgramID, "uLightingProperties.position");
    g_lightAmbientIndex = glGetUniformLocation(g_shaderProgramID, "uLightingProperties.ambient");
    g_lightDiffuseIndex = glGetUniformLocation(g_shaderProgramID, "uLightingProperties.diffuse");
    g_lightSpecularIndex = glGetUniformLocation(g_shaderProgramID, "uLightingProperties.specular");
    g_lightShininessIndex = glGetUniformLocation(g_shaderProgramID, "uLightingProperties.shininess");
    g_lightAttenuationIndex = glGetUniformLocation(g_shaderProgramID, "uLightingProperties.attenuation");
    g_lightCutoffAngleIndex = glGetUniformLocation(g_shaderProgramID, "uLightingProperties.cutoffAngle");
    g_lightDirectionIndex = glGetUniformLocation(g_shaderProgramID, "uLightingProperties.direction");

    g_materialAmbientIndex = glGetUniformLocation(g_shaderProgramID, "uMaterialProperties.ambient");
    g_materialDiffuseIndex = glGetUniformLocation(g_shaderProgramID, "uMaterialProperties.diffuse");
    g_materialSpecularIndex = glGetUniformLocation(g_shaderProgramID, "uMaterialProperties.specular");

    // initialise model matrix to the identity matrix
    for (int i = 0; i < MAX_PLANES; i++) { g_modelMatrix_plane[i] = glm::mat4(1.0f); }
    for (int i = 0; i < MAX_CUBES; i++) { g_modelMatrix_cube[i] = glm::mat4(1.0f); }
    for (int i = 0; i < 2; i++) { g_modelMatrix_mesh[i] = glm::mat4(1.0f); }

...

// Model Matrices - Mesh
    g_modelMatrix_mesh[0] = glm::scale(glm::vec3(0.3f, 0.3f, 0.3f));
    g_modelMatrix_mesh[1] = glm::translate(glm::vec3(0.0f, 1.0f, 0.0f)) * glm::scale(glm::vec3(0.3f, 0.3f, 0.3f));

    // set camera's view matrix
    g_camera.setViewMatrix(glm::vec3(0, 0, 3), glm::vec3(0, 0, 2), glm::vec3(0, 1, 0));

    int width, height;
    glfwGetFramebufferSize(window, &width, &height);
    float aspectRatio = static_cast<float>(width) / height;

    // set camera's projection matrix
    g_camera.setProjectionMatrix(glm::perspective(45.0f, aspectRatio, 0.1f, 100.0f));

    // load mesh
    load_mesh("models/WusonOBJ.obj");

// initialise light and material properties
    g_lightProperties.position = glm::vec4(0.0f, 2.0f, 0.0f, 1.0f);
    g_lightProperties.ambient = glm::vec4(0.2f, 0.2f, 0.2f, 1.0f);
    g_lightProperties.diffuse = glm::vec4(0.0f, 0.5f, 1.0f, 1.0f);
    g_lightProperties.specular = glm::vec4(0.0f, 0.5f, 1.0f, 1.0f);
    g_lightProperties.shininess = 10.0f;
    g_lightProperties.attenuation = glm::vec3(1.0f, 0.0f, 0.0f);
    //g_lightProperties.cutoffAngle = 45.0f;
    g_lightProperties.cutoffAngle = 180.0f;
    g_lightProperties.direction = glm::vec3(0.0f, -1.0f, 0.0f);

// Material Properties - Planes
    // Floor
    g_materialProperties.ambient = glm::vec4(1.0f, 1.0f, 1.0f, 1.0f);
    g_materialProperties.diffuse = glm::vec4(0.2f, 0.7f, 1.0f, 1.0f);
    g_materialProperties.specular = glm::vec4(0.2f, 0.7f, 1.0f, 1.0f);

    // generate identifier for VBOs and copy data to GPU
// Planes
    glGenBuffers(1, &g_VBO[0]);
    glBindBuffer(GL_ARRAY_BUFFER, g_VBO[0]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertices_plane), g_vertices_plane, GL_STATIC_DRAW);

    // generate identifiers for VAO
    glGenVertexArrays(1, &g_VAO[0]);

    // create VAO and specify VBO data
    glBindVertexArray(g_VAO[0]);
    glBindBuffer(GL_ARRAY_BUFFER, g_VBO[0]);
    glVertexAttribPointer(positionIndex, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void*>(offsetof(Vertex, position)));
    glVertexAttribPointer(normalIndex, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void*>(offsetof(Vertex, normal)));

    glEnableVertexAttribArray(positionIndex);   // enable vertex attributes
    glEnableVertexAttribArray(normalIndex);

// Cube
    // generate identifier for VBOs and copy data to GPU
    glGenBuffers(1, &g_VBO[1]);
    glBindBuffer(GL_ARRAY_BUFFER, g_VBO[1]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertices_cube), g_vertices_cube, GL_STATIC_DRAW);

    // generate identifier for IBO and copy data to GPU
    glGenBuffers(1, &g_IBO[0]);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_IBO[0]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(g_indices_cube), g_indices_cube, GL_STATIC_DRAW);

    // generate identifiers for VAO
    glGenVertexArrays(1, &g_VAO[1]);

    // create VAO and specify VBO data
    glBindVertexArray(g_VAO[1]);
    glBindBuffer(GL_ARRAY_BUFFER, g_VBO[1]);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_IBO[0]);
    glVertexAttribPointer(positionIndex, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void*>(offsetof(Vertex, position)));
    glVertexAttribPointer(normalIndex, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void*>(offsetof(Vertex, normal)));

    glEnableVertexAttribArray(positionIndex);   // enable vertex attributes
    glEnableVertexAttribArray(normalIndex);

// Meshes
    // Taurus Mesh
    // generate identifier for VBOs and copy data to GPU
    glGenBuffers(1, &g_VBO[2]);
    glBindBuffer(GL_ARRAY_BUFFER, g_VBO[2]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex)*g_numberOfVertices, g_pMeshVertices, GL_STATIC_DRAW);

    // generate identifier for IBO and copy data to GPU
    glGenBuffers(1, &g_IBO[1]);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_IBO[1]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLint) * 3 * g_numberOfFaces, g_pMeshIndices, GL_STATIC_DRAW);

    // generate identifiers for VAO
    glGenVertexArrays(1, &g_VAO[2]);

    // create VAO and specify VBO data
    glBindVertexArray(g_VAO[2]);
    glBindBuffer(GL_ARRAY_BUFFER, g_VBO[2]);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_IBO[1]);
    glVertexAttribPointer(positionIndex, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void*>(offsetof(Vertex, position)));
    glVertexAttribPointer(normalIndex, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void*>(offsetof(Vertex, normal)));

    glEnableVertexAttribArray(positionIndex);   // enable vertex attributes
    glEnableVertexAttribArray(normalIndex);

    // Suzanne Mesh
    load_mesh("models/suzanne.obj");

    // generate identifier for VBOs and copy data to GPU
    glGenBuffers(1, &g_VBO[3]);
    glBindBuffer(GL_ARRAY_BUFFER, g_VBO[3]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex)*g_numberOfVertices, g_pMeshVertices, GL_STATIC_DRAW);

    // generate identifier for IBO and copy data to GPU
    glGenBuffers(1, &g_IBO[2]);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_IBO[2]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLint) * 3 * g_numberOfFaces, g_pMeshIndices, GL_STATIC_DRAW);

    // generate identifiers for VAO
    glGenVertexArrays(1, &g_VAO[3]);

    // create VAO and specify VBO data
    glBindVertexArray(g_VAO[3]);
    glBindBuffer(GL_ARRAY_BUFFER, g_VBO[3]);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_IBO[2]);
    glVertexAttribPointer(positionIndex, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void*>(offsetof(Vertex, position)));
    glVertexAttribPointer(normalIndex, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), reinterpret_cast<void*>(offsetof(Vertex, normal)));

    glEnableVertexAttribArray(positionIndex);   // enable vertex attributes
    glEnableVertexAttribArray(normalIndex);
}

// function used to render the scene
static void render_scene()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear colour buffer and depth buffer

    glUseProgram(g_shaderProgramID);    // use the shaders associated with the shader program

    glBindVertexArray(g_VAO[0]);        // make VAO active

// Material Properties - Planes
    glUniform4fv(g_materialAmbientIndex, 1, &g_materialProperties.ambient[0]);
    glUniform4fv(g_materialDiffuseIndex, 1, &g_materialProperties.diffuse[0]);
    glUniform4fv(g_materialSpecularIndex, 1, &g_materialProperties.specular[0]);

    glUniform4fv(g_lightPositionIndex, 1, &g_lightProperties.position[0]);
    glUniform4fv(g_lightAmbientIndex, 1, &g_lightProperties.ambient[0]);
    glUniform4fv(g_lightDiffuseIndex, 1, &g_lightProperties.diffuse[0]);
    glUniform4fv(g_lightSpecularIndex, 1, &g_lightProperties.specular[0]);
    glUniform1fv(g_lightShininessIndex, 1, &g_lightProperties.shininess);
    glUniform3fv(g_lightAttenuationIndex, 1, &g_lightProperties.attenuation[0]);
    glUniform1fv(g_lightCutoffAngleIndex, 1, &g_lightProperties.cutoffAngle);
    glUniform3fv(g_lightDirectionIndex, 1, &g_lightProperties.direction[0]);

    // set uniform shader variables
    glm::mat4 MVP = glm::mat4(1.0f);

...

// Draw Cubes
    // Table top + 4 Table legs
    for (int i = 0; i < (MAX_CUBES - 1); i++)
    {
        MVP = g_camera.getProjectionMatrix() * g_camera.getViewMatrix() * g_modelMatrix_cube[i];
        glUniformMatrix4fv(g_MVP_Index, 1, GL_FALSE, &MVP[0][0]);
        glUniformMatrix4fv(g_M_Index, 1, GL_FALSE, &g_modelMatrix_cube[i][0][0]);
        glUniform3fv(g_viewPointIndex, 1, &g_viewPoint[0]);
        glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);   // display the vertices based on their indices and primitive type
    }

    // Chair (Right)
    for (int i = 0; i < MAX_CUBES; i++)
    {
        MVP = g_camera.getProjectionMatrix() * g_camera.getViewMatrix() 
            * glm::translate(glm::vec3(1.5f, -0.2f, 0.0f)) * glm::scale(glm::vec3(0.7f, 0.7f, 0.7f)) * g_modelMatrix_cube[i];
        glUniformMatrix4fv(g_MVP_Index, 1, GL_FALSE, &MVP[0][0]);
        glUniformMatrix4fv(g_M_Index, 1, GL_FALSE, &g_modelMatrix_cube[i][0][0]);
        glUniform3fv(g_viewPointIndex, 1, &g_viewPoint[0]);
        glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);   // display the vertices based on their indices and primitive type
    }
    // Chair (Left)
    for (int i = 0; i < MAX_CUBES; i++)
    {
        MVP = g_camera.getProjectionMatrix() * g_camera.getViewMatrix()
            * glm::rotate(glm::radians(180.0f), glm::vec3(0.0f, 1.0f, 0.0f))
            * glm::translate(glm::vec3(1.5f, -0.2f, 0.0f)) * glm::scale(glm::vec3(0.7f, 0.7f, 0.7f)) * g_modelMatrix_cube[i];
        glUniformMatrix4fv(g_MVP_Index, 1, GL_FALSE, &MVP[0][0]);
        glUniformMatrix4fv(g_M_Index, 1, GL_FALSE, &g_modelMatrix_cube[i][0][0]);
        glUniform3fv(g_viewPointIndex, 1, &g_viewPoint[0]);
        glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_INT, 0);   // display the vertices based on their indices and primitive type
    }

    glBindVertexArray(g_VAO[2]);        // make VAO active

// Draw Meshes
    // Taurus
    MVP = g_camera.getProjectionMatrix() * g_camera.getViewMatrix() * g_modelMatrix_mesh[0];
    glUniformMatrix4fv(g_MVP_Index, 1, GL_FALSE, &MVP[0][0]);
    glUniformMatrix4fv(g_M_Index, 1, GL_FALSE, &g_modelMatrix_mesh[0][0][0]);
    glUniform3fv(g_viewPointIndex, 1, &g_viewPoint[0]);
    glDrawElements(GL_TRIANGLES, g_numberOfFaces * 3, GL_UNSIGNED_INT, 0);  // display the vertices based on their indices and primitive type

    glBindVertexArray(g_VAO[3]);        // make VAO active

    MVP = g_camera.getProjectionMatrix() * g_camera.getViewMatrix() * g_modelMatrix_mesh[1];
    glUniformMatrix4fv(g_MVP_Index, 1, GL_FALSE, &MVP[0][0]);
    glUniformMatrix4fv(g_M_Index, 1, GL_FALSE, &g_modelMatrix_mesh[1][0][0]);
    glUniform3fv(g_viewPointIndex, 1, &g_viewPoint[0]);
    glDrawElements(GL_TRIANGLES, g_numberOfFaces * 3, GL_UNSIGNED_INT, 0);  // display the vertices based on their indices and primitive type

    glFlush();  // flush the pipeline
}

int main(void)
{
    ...

    // create spotlight entries
    TwAddVarRW(TweakBar, "Cutoff", TW_TYPE_FLOAT, &g_lightProperties.cutoffAngle, " group='Spotlight' min=-180.0 max=180.0 step=1.0 ");
    TwAddVarRW(TweakBar, "Direction: x", TW_TYPE_FLOAT, &g_lightProperties.direction[0], " group='Spotlight' min=-1.0 max=1.0 step=0.1");
    TwAddVarRW(TweakBar, "Direction: y", TW_TYPE_FLOAT, &g_lightProperties.direction[1], " group='Spotlight' min=-1.0 max=1.0 step=0.1");
    TwAddVarRW(TweakBar, "Direction: z", TW_TYPE_FLOAT, &g_lightProperties.direction[2], " group='Spotlight' min=-1.0 max=1.0 step=0.1");

    // initialise rendering states
    init(window);

    // the rendering loop
    while (!glfwWindowShouldClose(window))
    {
        g_camera.update(window);    // update camera

        if (g_wireFrame)
            glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

        render_scene();     // render the scene

        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

        TwDraw();           // draw tweak bar(s)

        glfwSwapBuffers(window);    // swap buffers
        glfwPollEvents();           // poll for events
    }

...

    exit(EXIT_SUCCESS);
}

Vertex shader

#version 330 core

// input data (different for all executions of this shader)
in vec3 aPosition;
in vec3 aNormal;

// uniform input data
uniform mat4 uModelViewProjectionMatrix;
uniform mat4 uModelMatrix;

// output data (will be interpolated for each fragment)
out vec3 vNormal;
out vec3 vPosition;

void main()
{
    // set vertex position
    gl_Position = uModelViewProjectionMatrix * vec4(aPosition, 1.0);

    // world space
    vPosition = (uModelMatrix * vec4(aPosition, 1.0)).xyz;
    vNormal = (uModelMatrix * vec4(aNormal, 0.0)).xyz;
}

Fragment shader

#version 330 core

// interpolated values from the vertex shaders
in vec3 vNormal;
in vec3 vPosition;

// uniform input data
struct LightProperties
{
    vec4 position;
    vec4 ambient;
    vec4 diffuse;
    vec4 specular;
    float shininess;
    vec3 attenuation;
    float cutoffAngle;
    vec3 direction;
};

struct MaterialProperties
{
    vec4 ambient;
    vec4 diffuse;
    vec4 specular;
};

uniform LightProperties uLightingProperties;
uniform MaterialProperties uMaterialProperties;
uniform vec3 uViewPoint;

// output data
out vec3 fColor;

void main()
{
    // calculate vectors for lighting
    vec3 N = normalize(vNormal);
    vec3 L;
    float attenuation = 1.0f;

    // calculate the attenuation based on distance
    L = (uLightingProperties.position).xyz - vPosition;
    float distance = length(L);
    L = normalize(L);
    attenuation = 1/(uLightingProperties.attenuation.x 
        + uLightingProperties.attenuation.y * distance 
        + uLightingProperties.attenuation.z * distance * distance);

    vec3 V = normalize(uViewPoint - vPosition);
    vec3 R = reflect(-L, N);

    // the direction of the spotlight
    vec3 direction = normalize(uLightingProperties.direction);
    // the angle between the vector from the light to the fragment’s position and the spotlight’s direction
    float angle = degrees(acos(dot(-L, direction)));

    vec3 colour = vec3(0.0f, 0.0f, 0.0f);

    // only compute if angle is less than the cutoff angle
    if(angle <= uLightingProperties.cutoffAngle)
    {
        // calculate Phong lighting
        vec4 ambient  = uLightingProperties.ambient * uMaterialProperties.ambient;
        vec4 diffuse  = uLightingProperties.diffuse * uMaterialProperties.diffuse * max(dot(L, N), 0.0);
        vec4 specular = vec4(0.0f, 0.0f, 0.0f, 1.0f);

        if(dot(L, N) > 0.0f)
        {
            specular = uLightingProperties.specular * uMaterialProperties.specular 
                * pow(max(dot(V, R), 0.0), uLightingProperties.shininess);
        }

        colour = (attenuation * (diffuse + specular)).rgb + ambient.rgb;
        // fade the spotlight's intensity linearly with angle
        colour *= 1.0f - angle/uLightingProperties.cutoffAngle;
    }

    // set output color
    fColor = colour;    
}

Solution

  • The function load_mesh reads data from a file and allocate dynamic memory and stores the read data in the following global variables:

    Vertex* g_pMeshVertices = NULL;
    GLint   g_numberOfVertices = 0;
    GLint*  g_pMeshIndices = NULL;
    GLint   g_numberOfFaces = 0;
    

    If you use the function load_mesh a second time the data of the first using are overwritten. Apart from this, the allocated memory for g_pMeshVertices and g_pMeshIndices is not freed, what causes a memory leak.

    In your code this does not cause any problem, since you immediately create an array buffer and an element array buffer where you bind the data, with the exception of g_numberOfFaces which you need for drawing the mesh. g_numberOfFaces is to low for the whole cow mesh, because you first read the cow mesh and you 2nd read the monky mesh, and the monky mesh has less than indices, than the cow mesh (g_numberOfFaces is overwirtten when reading the monky mesh).

    I recommend to create a class for the mesh data with a methode load_mesh:

    class CMesh
    {
    public:
    
        Vertex* m_pMeshVertices = nullptr;
        GLint   m_numberOfVertices = 0;
        GLint*  m_pMeshIndices = nullptr;
        GLint   m_numberOfFaces = 0;
    
        CMesh(void) {}
        virtual ~CMesh()
        {
          delete m_pMeshVertices;
          delete m_pMeshIndices;
        }
    
        bool load_mesh( const char* fileName )
    };  
    

    This allows you to instantiate a separate object for each mesh

    CMesh monkey;
    CMesh cow;
    
    monkey.load_mesh("models/WusonOBJ.obj");
    cow.load_mesh("models/suzanne.obj");
    

    Apart from this, you should think about using a std::vector instead of the dynamically allocated arrays:

    #include <vector>
    
    std::vector<Vertex> m_pMeshVertices;
    std::vector<GLint>  m_pMeshIndices;
    

    Extension to the answer

    Of course you can use an arrays instead:

    const int c_noOfMesh = 2;
    Vertex* g_pMeshVertices[c_noOfMesh]    = {nullptr}; 
    GLint   g_numberOfVertices[c_noOfMesh] = {0};
    GLint*  g_pMeshIndices[c_noOfMesh]     = {nullptr};   
    GLint   g_numberOfFaces[c_noOfMesh]    = {0};
    

    If you do so, you should add a new input parameter to the function load_mesh which indicates the index of the mesh which is read.

    bool load_mesh( int iMesh, const char* fileName )
    {
        .....
        g_pMeshVertices[iMesh] = .....;
        g_numberOfVertices[iMesh] = .....;
        g_pMeshIndices[iMesh] = .....;
        g_numberOfFaces[iMesh] = .....;
        .....
    }
    
    load_mesh( 0, "models/WusonOBJ.obj" );
    load_mesh( 1, "models/suzanne.obj" );