0

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?

1 Answers1

1

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.

user253751
  • 57,427
  • 7
  • 48
  • 90
  • Thank you, friend! Using an ivec3 gets me a quarter of the way literally. Now instead of a line, I am rendering the upper back fourth of the octahedra. I tried switching up the type to an std::vector> as an alternative to the vector of ivec3s, but I get the same fourth of the shape. Would I really need to use std::vector? I'm not a big fan of the idea of tripling the number of pushbacks that I would need to call. – ModernEraCaveman Jan 02 '23 at 19:11
  • 1
    @ModernEraCaveman if you are worried about performance, just tell the vector to reserve the number of items you need, then push_back is basically free. Maybe your system *does* have the 4 bytes of padding (idk) - what number is `sizeof(glm::ivec3)` and `alignof(glm::ivec3)`? – user253751 Jan 02 '23 at 19:20
  • `sizeof(glm::ivec3)` is 12 and `alignof(glm::ivec3)` is 4. I am pretty new to coding in c++ so I'm not sure how I would go about telling the vector to reserve the number of items that I would. Is it some variation of an address/pointer call? – ModernEraCaveman Jan 02 '23 at 19:31
  • @ModernEraCaveman hmm that's what I expected so I don't know why it only works once. – user253751 Jan 02 '23 at 19:34
  • 1
    @ModernEraCaveman Actually I do know - I just noticed it. You are telling OpenGL that the number of indices is `idx.size()` but that's actually the number of triangles. The number of indices is 3 times that. – user253751 Jan 02 '23 at 19:34
  • 1
    @ModernEraCaveman As for reserve, you can just do `indices.reserve(24);` and then indices allocates space for 24 items all at once, so it doesn't have to allocate any more space when you call push_back, as long as there are 24 items or less. – user253751 Jan 02 '23 at 19:34
  • you are amazing! Thank you so much for helping me out. I replaced it with `3*idx.size()` and it's working perfectly now! I appreciate you teaching me how to reserve memory for vectors too, I'm sure that will come in handy sometime on my coding adventures. – ModernEraCaveman Jan 02 '23 at 19:40