1

I'm trying to setup a simple OpenGL framework for a project and I'm running into a problem displaying my .obj model object. From what I can tell, the object is being loaded in fine using assimp, and all it's meshes should be drawn onto the screen but I'm not seeing anything..

I'll walk you through the whole process from loading the .obj all the way to where I draw it. First lets get some need to know files out of the way:

main.cxx

int main() 
{

    Display display(800, 600, "project2");

    Model model("./models/nanosuit/nanosuit.obj");
    Shader shader("./shaders/shader");  
    Texture texture("./textures/wall.jpg");

    int width, height;
    glfwGetWindowSize(display.GetWindow(), &width, &height);
    Camera camera(glm::vec3(0, 0, -5.0f), 45.0f, (float)width/height, 0.1f, 1000.0f); 

    Transform transform;

    while (!display.IsClosed())
    {
        display.ProcessInput();
        display.Clear(0.0f, 0.15f, 0.3f, 1.0f);

        shader.Bind();
        texture.Bind(0);

        shader.Update(transform, camera);
        model.Draw();

        display.Update();
    } 

    return 0;
}

Shader::Update()

void Shader::Update(const Transform& transform, const Camera& camera)
{
    glm::mat4 trans = camera.GetViewProjection() * transform.GetModel();
    glUniformMatrix4fv(uniforms[TRANSFORM_U], 1, GL_FALSE, &trans[0][0]);
}

vertex shader

#version 330 core
layout (location=0) in vec3 position;
layout (location=1) in vec2 texCoord;
layout (location=2) in vec3 normal;

out vec2 texCoord0;
out vec3 normal0;

uniform mat4 transform;

void main() {
    gl_Position = transform * vec4(position, 1.0);
    texCoord0 = texCoord;
    normal0 = (transform * vec4(normal, 0.0)).xyz; 
}

fragment shader

#version 330 core

out vec4 FragColor;
uniform sampler2D diffuse;
in vec2 texCoord0;
in vec3 normal0;

void main() {
    FragColor =     texture(diffuse, texCoord0);
}

I start by loading the model in my Model class's constructor

Model model("./models/nanosuit/nanosuit.obj");

model.cxx

Model::Model(const std::string& filePath)
{
    Assimp::Importer import;
    const aiScene *scene = import.ReadFile(filePath, aiProcess_Triangulate | aiProcess_FlipUVs);

    if(!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) 
    {
        std::cout << "ERROR::ASSIMP::" << import.GetErrorString() << std::endl;
        return;
    }
    //directory = filePath.substr(0, filePath.find_last_of('/'));

    processNode(scene->mRootNode, scene);
}


void Model::Draw()
{
    for (unsigned int i = 0; i < meshes.size(); ++i)
        meshes[i].Draw();
}

void Model::processNode(aiNode *node, const aiScene *scene)
{
    for (unsigned int i = 0; i < node->mNumMeshes; ++i)
    {
        aiMesh *mesh = scene->mMeshes[node->mMeshes[i]];
        meshes.push_back(processMesh(mesh, scene));
    }
    for (unsigned int i = 0; i < node->mNumChildren; i++)
    {
        processNode(node->mChildren[i], scene);
    }
}

Mesh Model::processMesh(aiMesh *mesh, const aiScene *scene)
{
    std::vector<Vertex> vertices;
    std::vector<unsigned int> indices;

    for (unsigned int i = 0; i < mesh->mNumVertices; ++i)
    {
        glm::vec3 position = glm::vec3(mesh->mVertices[i].x, mesh->mVertices[i].y, mesh->mVertices[i].z);
        glm::vec2 texCoords = glm::vec2(0.0f);
        if(mesh->mTextureCoords[0])
            texCoords = glm::vec2(mesh->mTextureCoords[0][i].x, mesh->mTextureCoords[0][i].y);
        glm::vec3 normal = glm::vec3(mesh->mNormals[i].x, mesh->mNormals[i].y, mesh->mNormals[i].z);

        Vertex vertex(position, texCoords, normal);

        vertices.push_back(vertex);
    }

    // process indices
    for(unsigned int i = 0; i < mesh->mNumFaces; i++)
    {
        aiFace face = mesh->mFaces[i];
        for(unsigned int j = 0; j < face.mNumIndices; j++)
            indices.push_back(face.mIndices[j]);
    }  

    return Mesh(&vertices[0], vertices.size(), &indices[0], indices.size());
}

This in turn creates a list of meshes, which are then drawn on screen by calling

Model::Draw()

For your reference here is my mesh class

mesh.cxx

Mesh::Mesh(Vertex *vertices, unsigned int numVertices, unsigned int* indices, unsigned int numIndices)
{
    std::vector<glm::vec3> positions;
    std::vector<glm::vec2> texCoords;
    std::vector<glm::vec3> normals;

    for (unsigned int i = 0; i < numVertices; ++i)
    {
        positions.push_back(vertices[i].GetPos());
        texCoords.push_back(vertices[i].GetTexCoord());
        normals.push_back(vertices[i].GetNormal());
    }

    drawCount = numIndices;

    glGenVertexArrays(1, &VAO);
    glBindVertexArray(VAO);

    glGenBuffers(NUM_BUFFERS, VBO);

    // positions
    glBindBuffer(GL_ARRAY_BUFFER, VBO[POSITION_VB]);
    glBufferData(GL_ARRAY_BUFFER, positions.size() * sizeof(positions[0]), &positions[0], GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(0);

    // texCoords
    glBindBuffer(GL_ARRAY_BUFFER, VBO[TEXCOORD_VB]);
    glBufferData(GL_ARRAY_BUFFER, positions.size() * sizeof(texCoords[0]), &texCoords[0], GL_STATIC_DRAW);

    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(1);

    // normals
    glBindBuffer(GL_ARRAY_BUFFER, VBO[NORMAL_VB]);
    glBufferData(GL_ARRAY_BUFFER, normals.size()  * sizeof(normals[0]), &normals[0], GL_STATIC_DRAW);

    glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glEnableVertexAttribArray(2);

    // indices
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, VBO[INDEX_VB]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, numIndices * sizeof(indices[0]), &indices[0], GL_STATIC_DRAW);

    glBindVertexArray(0);
}

Mesh::~Mesh()
{
    glDeleteVertexArrays(1, &VAO);
}

void Mesh::Draw()
{
    glBindVertexArray(VAO);

    glDrawElements(GL_TRIANGLES, drawCount, GL_UNSIGNED_INT, 0);

    glBindVertexArray(0);
}

I've verified that the model vertices are in fact received by the Mesh::Draw() method and they should be drawn.. But again no output in the window :(

  • 2
    The field of view angle is set in degrees (`45.0f`). The angle for [`glm::perspective`](https://glm.g-truc.net/0.9.9/api/a00665.html#ga747c8cf99458663dd7ad1bb3a2f07787) has to be in radians (`glm::radians(45.0f)`). – Rabbid76 Apr 03 '19 at 19:44
  • @Rabbid76 Unfortunately that doesn't fix the issue, is there any other kind of information I can provide that would help you help me with the problem? – Mostapha Rammo Apr 03 '19 at 19:48
  • Possibly the issue is related to [OpenGL object in C++ RAII class no longer works](https://stackoverflow.com/questions/46839586/opengl-object-in-c-raii-class-no-longer-works). Does the class `Mesh` have a "move" constructor? Put a breakpoint to `Mesh::~Mesh()` and see what happens. – Rabbid76 Apr 03 '19 at 19:50
  • No it doesn't, all the Mesh class methods are defined in mesh.cxx so only constructor, destructor and Mesh::Draw() – Mostapha Rammo Apr 03 '19 at 19:54
  • The attribute `meshes` int eh class `Model` is a `std::vector` isn't it? The `Mesh` object is add to the container by `push_back` isn't it? – Rabbid76 Apr 03 '19 at 19:57
  • Exactly, in `Model::processNode()` I `push_back` the newly created mesh to `Model`'s meshes vector. Then once `Model::Draw()` is called I iteratively draw every mesh in meshes – Mostapha Rammo Apr 03 '19 at 20:01
  • 2
    Yes, when the `Mesh` object is pushed to the `std::vector` a new object in the dynamic memory of the `std::vector` is allocated. The original object is copied to the new object and the original object is destructed -> `glDeleteVertexArrays(1, &VAO);`. So the `VAO` is destroyed. This is C++ basic and exactly this issue is pointed out in the question which is linked above. – Rabbid76 Apr 03 '19 at 20:06

0 Answers0