Home > OS >  Why is my Index Generation Function not correctly building the triangle primitives?
Why is my Index Generation Function not correctly building the triangle primitives?

Time:01-03

I am trying to code a function which automatically populates a mesh's index vector container. The function should work without issue in theory as it generates the proper indices in their correct order; however, the triangles do not form! Instead, I am left with a single line.

My mesh generation code is supposed to build an octahedron and then render it in the main game loop. The mesh class is shown below in its entirety:

struct vertex
{
    glm::vec3 position;
    glm::vec3 color;
};

class Mesh
{
public:
    GLuint VAO, VBO, EBO;
    std::vector <vertex> vtx;
    std::vector <glm::vec3> idx;
    glm::mat4 modelMatrix = glm::mat4(1.f); 

    Mesh(glm::vec3 position, glm::vec3 scale)
    {
        vertexGen(6);
        idx = indexGen(6);
        modelMatrix = glm::scale(glm::translate(modelMatrix, position), scale);
        initMesh();
    };
    void Render(Shader shaderProgram, Camera camera, bool wireframe)
    {
        glUseProgram(shaderProgram.ID);
        glPatchParameteri(GL_PATCH_VERTICES, 3); // Indicates to the VAO that each group of three vertices is one patch (triangles)
        glProgramUniformMatrix4fv(shaderProgram.ID, 0, 1, GL_FALSE, glm::value_ptr(modelMatrix));
        glProgramUniformMatrix4fv(shaderProgram.ID, 1, 1, GL_FALSE, glm::value_ptr(camera.camMatrix));
        glProgramUniform3fv(shaderProgram.ID, 2, 1, glm::value_ptr(camera.Position));

        glBindVertexArray(VAO); // Binds the VAO to the shader program
        if (wireframe)
        {
            glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
            glDisable(GL_CULL_FACE);
        }
        else
        {
            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
            //glEnable(GL_CULL_FACE);
        }
        glDrawElements(GL_PATCHES, idx.size(), GL_UNSIGNED_INT, 0); // Tells the shader program how to draw the primitives
    }
private:
    void vertexGen(int n) {
        // Populate the base six vertices
        vtx.push_back(vertex{ glm::vec3( 0.0f, 0.5f,  0.0f), glm::vec3(0.f, 1.f, 0.f) });
        vtx.push_back(vertex{ glm::vec3(-0.5f, 0.0f,  0.0f), glm::vec3(0.f, 1.f, 0.f) });
        vtx.push_back(vertex{ glm::vec3( 0.0f, 0.0f, -0.5f), glm::vec3(0.f, 1.f, 0.f) });
        vtx.push_back(vertex{ glm::vec3( 0.5f, 0.0f,  0.0f), glm::vec3(0.f, 1.f, 0.f) });
        vtx.push_back(vertex{ glm::vec3( 0.0f, 0.0f,  0.5f), glm::vec3(0.f, 1.f, 0.f) });
        vtx.push_back(vertex{ glm::vec3( 0.0f,-0.5f,  0.0f), glm::vec3(0.f, 1.f, 0.f) });
    }
    std::vector<glm::vec3> indexGen(int n) {
        std::vector<glm::vec3> indices;

        // Calculate the indices for the top 4 triangles
        indices.push_back(glm::vec3( 0, n - 5, n - 4 ));
        indices.push_back(glm::vec3( 0, n - 4, n - 3 ));
        indices.push_back(glm::vec3( 0, n - 3, n - 2 ));
        indices.push_back(glm::vec3( 0, n - 2, n - 5 ));

        // Calculate the indices for the bottom 4 triangles
        indices.push_back(glm::vec3( 5, n - 5, n - 4)); 
        indices.push_back(glm::vec3( 5, n - 4, n - 3));
        indices.push_back(glm::vec3( 5, n - 3, n - 2));
        indices.push_back(glm::vec3( 5, n - 2, n - 5));

        return indices;
    }
    void initMesh()
    {
        glCreateVertexArrays(1, &VAO); // Sets the address of the uint VAO as the location of a gl vertex array object
        glCreateBuffers(1, &VBO);      // Sets the address of the uint VBO as the location of a gl buffer object
        glCreateBuffers(1, &EBO);      // Sets the address of the uint EBO as the location of a gl buffer object

        glNamedBufferData(VBO, vtx.size() * sizeof(vtx[0]), vtx.data(), GL_STATIC_DRAW); // Sets the data of the buffer named VBO
        glNamedBufferData(EBO, idx.size() * sizeof(idx[0]), idx.data(), GL_STATIC_DRAW); // Sets the data of the buffer named EBO

        glEnableVertexArrayAttrib(VAO, 0); // Enables an attribute of the VAO in location 0
        glEnableVertexArrayAttrib(VAO, 1); // Enables an attribute of the VAO in location 1

        glVertexArrayAttribBinding(VAO, 0, 0); // Layout Location of Position Vectors
        glVertexArrayAttribBinding(VAO, 1, 0); // Layout Location of Color Values

        glVertexArrayAttribFormat(VAO, 0, 3, GL_FLOAT, GL_FALSE, 0); // Size, and Type of Position Vectors
        glVertexArrayAttribFormat(VAO, 1, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat)); // For the Color Values

        glVertexArrayVertexBuffer(VAO, 0, VBO, 0, 6 * sizeof(GLfloat)); // Sets the VBO to indicate the start, offset, and stride of vertex data in the VAO

        glVertexArrayElementBuffer(VAO, EBO); // Sets the EBO to index the VAO vertex connections
    }
};

I took this problem step by step and did all of the basic math on paper. The index generation function returns the expected indices in their correct order as just having the indices written out, but it differs in that the written-out indices generate the desired result whereas the generation function only produces a single line when rendered:

Two opposing vertical lines which should have rendered as two octahedra.

I suspect that the issue lies in my mesh initialization function (initMesh), specifically in the glNamedBufferData or glVertexArrayVertexBuffer, but my knowledge of the functions is very limited. I tried changing the parameter of the glNamedBufferData function to different variations of idx.size()*sizeof(idx[0].x), but that yielded the same results, so I am at a loss. Could someone help me fix this, please?

CodePudding user response:

glm::vec3 is a vector of floats (I think) but you are telling OpenGL to read them as unsigned ints.

Float 0.0 is 0x00000000 (i.e. same as int 0), but float 1.0 is 0x3f800000 (same as int 1065353216). They aren't compatible ways to store numbers. You could try glm::ivec3 which is a vector of ints, but I think most people would use std::vector<int> (or unsigned int) and use 3 entries per triangle.

I think it's okay in this case, but I don't like to use types like ivec3 when I mean to have 3 separate ints isn't always a good practice, because the compiler can insert padding in unexpected places. It's possible that on some platforms, ivec3 could be 3 ints and an extra 4 bytes of padding, making 16 bytes in total, and the extra padding bytes throw off the layout you're relying on. glDrawArrays wouldn't skip over padding after every 3 indices and there would be no way to tell it to do that. It's okay for vertices, since you can tell OpenGL exactly where the data is.

  • Related