Kihz
Kihz

Reputation: 101

Render Issues Using Assimp and OpenGL

I am using Assimp to load models to render in OpenGL but am running into an issue where chunks/pieces of a mesh don't render.

Example:

What model is supposed to look like: Model

What I end up rendering:

As you can see, some of the model is rendering properly, but not all.

I have verified multiple times that the meshes being loaded from assimp are loading the correct vertices and indices into my "Mesh" class. Here is my code for loading a model:

This function will recursively call itself for all child nodes and load each mesh inside the node. Each mesh will then be transformed into my own "Mesh" class by creating a vector of vertices and faces.

void Model::LoadAssimpNode(aiNode* node, const aiScene* scene)
{
    // Process assimp meshes
    for (unsigned int i = 0; i < node->mNumMeshes; i++)
    {
        aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
        this->meshes.push_back(this->LoadAssimpMesh(mesh, scene));
    }

    // Recursivley processes child nodes
    for (unsigned int i = 0; i < node->mNumChildren; i++)
    {
        this->LoadAssimpNode(node->mChildren[i], scene);
    }
}

Mesh Model::LoadAssimpMesh(aiMesh* mesh, const aiScene* scene)
{
    std::vector<sVertex> vertices;
    for (unsigned int i = 0; i < mesh->mNumVertices; i++)
    {
        sVertex vertex;

        vertex.x = mesh->mVertices[i].x;
        vertex.y = mesh->mVertices[i].y;
        vertex.z = mesh->mVertices[i].z;

        vertex.nx = mesh->mNormals[i].x;
        vertex.ny = mesh->mNormals[i].y;
        vertex.nz = mesh->mNormals[i].z;

        if (mesh->mTextureCoords[0])
        {
            vertex.u0 = mesh->mTextureCoords[0][i].x;
            vertex.v0 = mesh->mTextureCoords[0][i].y;
        }

        vertices.push_back(vertex);
    }

    std::vector<sTriangle> faces;
    for (unsigned int i = 0; i < mesh->mNumFaces; i++)
    {
        sTriangle face;
        aiFace assimpFace = mesh->mFaces[i];
        if (assimpFace.mNumIndices != 3)
        {
            std::cout << "Face is not a triangle!" << std::endl;
        }

        for (unsigned int j = 0; j < assimpFace.mNumIndices; j++)
        {
            face.vertIndex[j] = assimpFace.mIndices[j];
        }

        faces.push_back(face);
    }

    std::vector<Texture> textures;
    if (mesh->mMaterialIndex >= 0)
    {
        aiMaterial* material = scene->mMaterials[mesh->mMaterialIndex];

        // Sampler names should adhere to the following convention:
        // Diffuse: texure_diffuseN
        // Specular: texture_specularN
        // Normal: texture_normalN
        // Where N = texture numbers

        for (Texture texture : this->LoadAssimpMaterialTextures(material, aiTextureType_DIFFUSE, "texture_diffuse"))
        {
            this->loadedTextures.insert(std::make_pair(texture.path.C_Str(), texture));
            textures.push_back(texture);
        }

        for (Texture texture : this->LoadAssimpMaterialTextures(material, aiTextureType_SPECULAR, "texture_specular"))
        {
            this->loadedTextures.insert(std::make_pair(texture.path.C_Str(), texture));
            textures.push_back(texture);
        }
    }

    return Mesh(vertices, faces, textures);
}

The sVertex and sTriangle structs are defined as:

struct sVertex
{
    float x, y, z; 
    float nx, ny, nz;
    float u0, v0;
};

struct sTriangle
{
    unsigned int vertIndex[3];
};

Now that the model is effectively loaded from assimp, we now call the SetupMesh() function which sets up the meshes' respective VAO, VBO and EBO:

void Mesh::SetupMesh()
{
    // Generate IDs for our VAO, VBO and EBO
    glGenVertexArrays(1, &this->VAO);
    glGenBuffers(1, &this->VBO);
    glGenBuffers(1, &this->EBO);

    glBindVertexArray(this->VAO);

    // Now ANY state that is related to vertex or index buffer
    //  and vertex attribute layout, is stored in the 'state' 
    //  of the VAO... 

    // Tell open GL where to look for for vertex data
    glBindBuffer(GL_ARRAY_BUFFER, this->VBO);
    glBufferData(GL_ARRAY_BUFFER, this->vertices.size() * sizeof(sVertex), &this->vertices[0], GL_STATIC_DRAW);

    // Tell open GL where our index buffer begins (AKA: where to look for faces)
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, this->faces.size() * sizeof(sTriangle), &this->faces[0], GL_STATIC_DRAW);

    // Set the vertex attributes for this shader
    // Layout information can be found in the vertex shader, currently:
    // 0 = position
    // 1 = normals
    // 2 = texture coordinates
    glEnableVertexAttribArray(0);       // position
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(sVertex), (void*) offsetof(sVertex, x));

    glEnableVertexAttribArray(1);       // normal
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(sVertex), (void*) offsetof(sVertex, nx));

    glEnableVertexAttribArray(2);       // textureCoordinates
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(sVertex), (void*)offsetof(sVertex, u0));

    // Now that all the parts are set up, unbind buffers
    glBindVertexArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);
    glDisableVertexAttribArray(2);
}

Once this is all setup, I will now call the Draw method for each mesh to render in my render loop:

void Mesh::Draw(const CompiledShader& shader)
{
    glm::mat4 matModel = glm::mat4(1.0f);
    glm::mat4 matTranslate = glm::translate(glm::mat4(1.0f), this->positionXYZ); // Translation matrix
    glm::mat4 rotateX = glm::rotate(glm::mat4(1.0f), this->orientationXYZ.x, glm::vec3(1.0f, 0.0f, 0.0f)); // X axis rotation
    glm::mat4 rotateY = glm::rotate(glm::mat4(1.0f), this->orientationXYZ.y, glm::vec3(0.0f, 1.0f, 0.0f)); // Y axis rotation
    glm::mat4 rotateZ = glm::rotate(glm::mat4(1.0f), this->orientationXYZ.z, glm::vec3(0.0f, 0.0f, 1.0f)); // Z axis rotation
    glm::mat4 matScale = glm::scale(glm::mat4(1.0f), glm::vec3(this->scale, this->scale, this->scale)); // Scale the mesh
    glm::mat4 matInvTransposeModel = glm::inverse(glm::transpose(matModel));

    // Apply all the transformations to our matrix
    matModel = matModel * matTranslate;
    matModel = matModel * rotateZ;
    matModel = matModel * rotateY;
    matModel = matModel * rotateX;
    matModel = matModel * matScale;

    glUseProgram(shader.ID);

    glUniformMatrix4fv(glGetUniformLocation(shader.ID, "matModel"), 1, GL_FALSE, glm::value_ptr(matModel)); // Tell shader the model matrix (AKA: Position orientation and scale)
    glUniformMatrix4fv(glGetUniformLocation(shader.ID, "matModelInverseTranspose"), 1, GL_FALSE, glm::value_ptr(matInvTransposeModel));

    // Draw the mesh
    glBindVertexArray(this->VAO);
    glDrawElements(GL_TRIANGLES, this->faces.size(), GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);

}

My shaders are very simple where the color of a pixel is equal to the vertex's normal:

Vertex Shader:

#version 420

layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
layout (location = 2) in vec2 textureCoordinates;

uniform mat4 matModel;
uniform mat4 matView;
uniform mat4 matProjection;
uniform mat4 matModelInverseTranspose;  // For normal calculation
    
out vec4 fVertWorldLocation;
out vec4 fNormal;
out vec2 TextureCoordinates;

void main()
{   
    mat4 MVP = matProjection * matView * matModel;
            
    gl_Position = MVP * vec4(position, 1.0f);   
    
    TextureCoordinates = textureCoordinates;
    
    // The location of the vertex in "world" space (not screen space)
    fVertWorldLocation = matModel * vec4(position, 1.0f);
    
    // Calculate the normal based on any rotation we've applied.
    // This inverse transpose removes scaling and tranlation (movement) 
    //  from the matrix.
    fNormal = matModelInverseTranspose * vec4(normal, 1.0f);
    
    
};

Fragment Shader:

#version 420

in vec2 TextureCoordinates;
in vec4 fNormal;

out vec4 Color;

uniform sampler2D texture_diffuse;

void main()
{
    //Color = vec4(texture(texture_diffuse, TextureCoordinates));
    //Color = vec4(TextureCoordinates, 1.0f, 1.0f);
    Color = fNormal;
    
}

Sorry for the insane length of this post, but I feel that all of it was necessary to get my point across.

If anyone could point out what I am doing wrong here it would be greatly appreciated! I feel like I need an extra pair of eyes here because I have read my code over countless times and can't seem to come up with anything.

Upvotes: 0

Views: 922

Answers (1)

Kihz
Kihz

Reputation: 101

Made a stupid mistake, I was under the impression that the "count" argument in the glDrawElements() function wanted the number of faces NOT the number of indices.

The problem was fixed by changing my glDrawElements call from:

   glDrawElements(GL_TRIANGLES, this->faces.size(), GL_UNSIGNED_INT, 0);

To this:

  glDrawElements(GL_TRIANGLES, this->faces.size() * 3, GL_UNSIGNED_INT, 0);

Upvotes: 1

Related Questions