0

I need to update my mesh with new vertices. I create the VBO as such (initially it gets created with only one vertex in it):

public Mesh(float[] vertex, int size)
{
    texture = null;
    meshType = 1;           //will draw lines

    FloatBuffer verticesBuffer = null;
    IntBuffer indicesBuffer = null;
    int vboID;

    try
    {
        vertexCount = size;

        vaoID = glGenVertexArrays();
        glBindVertexArray(vaoID);

        vboIDList = new ArrayList<>();

        // Vertices VBO generation
        vboID = glGenBuffers();
        vboIDList.add(vboID);
        verticesBuffer = MemoryUtil.memAllocFloat(size * 3);        // !!! Must Be manually freed!
        verticesBuffer.put(vertex).flip();
        glBindBuffer(GL_ARRAY_BUFFER, vboID);
        glBufferData(GL_ARRAY_BUFFER, verticesBuffer, GL_STATIC_DRAW);
        glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
        vertexAttrArrCount += 1;

        // Indices VBO generation
        vboID = glGenBuffers();
        vboIDList.add(vboID);
        indicesBuffer = MemoryUtil.memAllocInt(size);             // !!! Must be manually freed!
        indicesBuffer.put(new int[]{0}).flip();
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboID);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicesBuffer, GL_STATIC_DRAW);

        // unbinding
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glBindVertexArray(0);
    }
    finally
    {
        if (verticesBuffer != null)
        {
            MemoryUtil.memFree(verticesBuffer);                             // Freeing vertex buffer
        }

        if (indicesBuffer != null)
        {
            MemoryUtil.memFree(indicesBuffer);                              // Freeing indices buffer
        }
    }

}

then I want to update the VBO buffer and write new vertices into it. Note that I do create VBO to have enough space for my new vertices, and I do control that it doesn't get overfilled. I also control how many elements I draw with each render call, so I don't draw the empty 0/0/0 vertices.

My problem is, this code WORKS:

public void updateVBO(float[] vertices, int[] indices, int size)
{
    if (meshType == 1)
    {
        lineCount = size;

        FloatBuffer subDataF = null;
        IntBuffer subDataI = null;
        int vboID;

        try
        {
            //System.out.printf("Adding vertex (%f, %f, %f) to position %d\n",vertex.x,vertex.y,vertex.z,position);
            vboID = vboIDList.get(0);
            //float[] vert = new float[]{vertex.x, vertex.y, vertex.z};
            subDataF = MemoryUtil.memAllocFloat(vertices.length);        // !!! Must Be manually freed!
            subDataF.put(vertices).flip();
            glBindBuffer(GL_ARRAY_BUFFER, vboID);
            glBufferData(GL_ARRAY_BUFFER, subDataF, GL_STATIC_DRAW);
            glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);

            vboID = vboIDList.get(1);
            //int[] index = new int[]{ position };
            subDataI = MemoryUtil.memAllocInt(indices.length);        // !!! Must Be manually freed!
            subDataI.put(indices).flip();
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboID);
            glBufferData(GL_ELEMENT_ARRAY_BUFFER, subDataI, GL_STATIC_DRAW);

            //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
        }
        finally
        {
            if (subDataF != null)
            {
                MemoryUtil.memFree(subDataF);
            }
            if (subDataI != null)
            {
                MemoryUtil.memFree(subDataI);
            }
        }
    }
}

so when I pass the entire vertices array, and re-allocate VBO memory from scratch - it draws exactly what I need it to. However I would like to use glBufferSubData, so that I don't re-allocate the momory each time I add new vertex. And this code DOESN'T WORK:

public void addVertex(Vector3f vertex, int position)
{
    if (meshType == 1)
    {
        FloatBuffer subDataF = null;
        IntBuffer subDataI = null;
        int vboID;

        lineCount = position+1;

        try
        {
            System.out.printf("Adding vertex (%f, %f, %f) to position %d\n",vertex.x,vertex.y,vertex.z,position);
            vboID = vboIDList.get(0);
            float[] vert = new float[]{vertex.x, vertex.y, vertex.z};
            subDataF = MemoryUtil.memAllocFloat(3);        // !!! Must Be manually freed!
            subDataF.put(vert).flip();
            glBindBuffer(GL_ARRAY_BUFFER, vboID);
            glBufferSubData(GL_ARRAY_BUFFER, position * 3 * 4, subDataF);
            glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);

            vboID = vboIDList.get(1);
            int[] index = new int[]{ position };
            subDataI = MemoryUtil.memAllocInt(1);        // !!! Must Be manually freed!
            subDataI.put(index).flip();
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboID);
            glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, position * 4, subDataI);

            glBindBuffer(GL_ARRAY_BUFFER, 0);
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
        }
        finally
        {
            if (subDataF != null)
            {
                MemoryUtil.memFree(subDataF);
            }
            if (subDataI != null)
            {
                MemoryUtil.memFree(subDataI);
            }
        }
    }
}

Also I'm aware it's not optimized the way I create the floatbuffer and intbuffer, I just want to get it to work before I clean this up. I was trying a bunch of things, so the last piece of code is weird because of that.

Still, I don't understand what I'm doing wrong. I did check that I pass all the data correctly, and that the position (and offset) seem to be calculated how they should be. And it just doesn't draw anything, while when I use glBufferData it does.

Could someone explain where I'm making a mistake?

After all suggestions, here's what I end up with, but it still doesn't work at all:

public void addVertex(Vector3f vertex, int position)
{
    if (meshType == 1)
    {
        FloatBuffer subDataF = null;
        IntBuffer subDataI = null;
        int vboID;

        lineCount = position+1;

        try
        {
            System.out.printf("Adding vertex (%f, %f, %f) to position %d\n",vertex.x,vertex.y,vertex.z,position);
            vboID = vboIDList.get(0);
            float[] vert = new float[]{vertex.x, vertex.y, vertex.z};
            subDataF = MemoryUtil.memAllocFloat(3);        // !!! Must Be manually freed!
            subDataF.put(vert).flip();
            glBindBuffer(GL_ARRAY_BUFFER, vboID);
            glBufferSubData(GL_ARRAY_BUFFER, (long)(position * 3 * 4), (FloatBuffer)subDataF);
            glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);

            vboID = vboIDList.get(1);
            int[] index = new int[]{ position };
            subDataI = MemoryUtil.memAllocInt(1);        // !!! Must Be manually freed!
            subDataI.put(index).flip();

            glBindVertexArray(vaoID);
            glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, (long)(position * 4), (IntBuffer)subDataI);
        }
        finally
        {
            if (subDataF != null)
            {
                MemoryUtil.memFree(subDataF);
            }
            if (subDataI != null)
            {
                MemoryUtil.memFree(subDataI);
            }
        }
    }
}

I did check that VAO ID is correct.

Vlad Vyatkin
  • 544
  • 5
  • 16
  • What is the parameter `position`? Why is it used for the vertex array and index array? Why do you change the index buffer at all, when you only want to change a single vertex coordinate? – Rabbid76 Sep 16 '19 at 11:08
  • position is basically the number of vertices currently created in a mesh. It's used for vertex VBO to provide an offset - if position is 0, we write starting from 0th byte in the VBO, if it's one we write to the 12th, which is 2nd vert, etc. The same with indices array - for each vertex an index gets created. And it just so happens that right now for my purpose the index is equal position, so indices array will look like { 0, 1, 2, 3, ....}. This is because right now I basically want to draw lines, where the first line goes from 0 to 1, the second from 1 to 2 etc - that's why they're ordered. – Vlad Vyatkin Sep 16 '19 at 11:15

1 Answers1

1

Just as I thought, it was something stupid, and not at all connected to VAO binding and such.

The thing is, when I create VBO initially, I do it like this:

// Vertices VBO generation
        vboID = glGenBuffers();
        vboIDList.add(vboID);
        verticesBuffer = MemoryUtil.memAllocFloat(size * 3);        // !!! Must Be manually freed!
        verticesBuffer.put(vertex).flip();
        glBindBuffer(GL_ARRAY_BUFFER, vboID);
        glBufferData(GL_ARRAY_BUFFER, verticesBuffer, GL_STATIC_DRAW);
        glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
        vertexAttrArrCount += 1;

I assumed that because I allocate buffer for size*3 floats, it will be of that size, and when I put it in the VBO - it will allocate size*3*4 bytes, i.e. enough for size*3 floats.

Turns out Nope! Because I put only one vertex (3 floats) into the buffer - it will allocate only that amount of space. So when I later try to use glBufferSubData - it only has spaces for 3 floats on the GPU, and naturally doesn't put the values where I need them. I'm actually surprised it doesn't flat-out crash on me.

To fix this, at the moment I did this instead:

// Vertices VBO generation
        ...
        verticesBuffer.put(vertex).put(new float[size*3 - 3]).flip();
        ...

So basically I'm manually putting an empty array into the FloatBuffer, and that ensures that the buffer is the right size.

Here's the result: Constructor:

public Mesh(float[] vertex, int size)
{
    texture = null;
    meshType = 1;           //will draw lines

    FloatBuffer verticesBuffer = null;
    IntBuffer indicesBuffer = null;
    int vboID;

    try
    {
        vertexCount = size;

        vaoID = glGenVertexArrays();
        glBindVertexArray(vaoID);

        vboIDList = new ArrayList<>();

        // Vertices VBO generation
        vboID = glGenBuffers();
        vboIDList.add(vboID);
        verticesBuffer = MemoryUtil.memAllocFloat(size * 3);        // !!! Must Be manually freed!
        verticesBuffer.put(vertex).put(new float[size*3 - 3]).flip();
        glBindBuffer(GL_ARRAY_BUFFER, vboID);
        glBufferData(GL_ARRAY_BUFFER, verticesBuffer, GL_STATIC_DRAW);
        glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);
        vertexAttrArrCount += 1;

        // Indices VBO generation
        vboID = glGenBuffers();
        vboIDList.add(vboID);
        indicesBuffer = MemoryUtil.memAllocInt(size);             // !!! Must be manually freed!
        indicesBuffer.put(new int[size]).flip();                  // I need the first element 0 anyway
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboID);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, indicesBuffer, GL_STATIC_DRAW);

        // unbinding
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glBindVertexArray(0);
    }
    finally
    {
        if (verticesBuffer != null)
        {
            MemoryUtil.memFree(verticesBuffer);                             // Freeing vertex buffer
        }

        if (indicesBuffer != null)
        {
            MemoryUtil.memFree(indicesBuffer);                              // Freeing indices buffer
        }
    }

}

And then updating:

public void addVertex(Vector3f vertex, int position)
{
    if (meshType == 1)
    {
        FloatBuffer subDataF = null;
        IntBuffer subDataI = null;
        int vboID;

        lineCount = position+1;

        try
        {
            vboID = vboIDList.get(0);
            float[] vert = new float[]{vertex.x, vertex.y, vertex.z};
            subDataF = MemoryUtil.memAllocFloat(vert.length);        // !!! Must Be manually freed!
            subDataF.put(vert).flip();
            glBindBuffer(GL_ARRAY_BUFFER, vboID);
            glBufferSubData(GL_ARRAY_BUFFER, (long)(position * 3 * 4), (FloatBuffer)subDataF);
            glVertexAttribPointer(0, 3, GL_FLOAT, false, 0, 0);

            vboID = vboIDList.get(1);
            int[] index = new int[]{ position };
            subDataI = MemoryUtil.memAllocInt(index.length);        // !!! Must Be manually freed!
            subDataI.put(index).flip();
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboID);
            glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, (long)(position * 4), (IntBuffer)subDataI);

            glBindBuffer(GL_ARRAY_BUFFER, 0);
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
        }
        finally
        {
            if (subDataF != null)
            {
                MemoryUtil.memFree(subDataF);
            }
            if (subDataI != null)
            {
                MemoryUtil.memFree(subDataI);
            }
        }
    }
}

And it works. Do note that the code is a bit dirty, I didn't clean it up before posting an answer.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Vlad Vyatkin
  • 544
  • 5
  • 16
  • 1
    I assume that after the VAO is bound (`glBindVertexArray(vaoID);`) it is persistent. This means this state is never changed. That would explain the GL_ELEMENT_ARRAY_BUFFER behavior. There is no need to bind the VAO, because it is still bound (an so the element buffer. But don't be stubborn, believe me, it is the VAO which has to be bound to change the data of the `GL_ELEMENT_ARRAY_BUFFER`! This will cause an issue if you've more than 1 VAO object. Never never do `glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);`! – Rabbid76 Sep 17 '19 at 04:51
  • You don't need to allocate an array and put it into the VBO. You can just pass `null` instead of the vertex array to your `glBufferData` and it will allocate the buffer according to your `size`. – Reaper Sep 17 '19 at 06:21
  • Rabbid76, If you didn't notice, VAO gets unbound at the end of constructor glBindVertexArray(0); The thing is that inside OGL API both VAO and VBO's presist - they don't go anywhere. Certain VBO are associated with certain VAO's. You need them when you render, but you do not need to bind VAO in order to manipulate VBO associated with it. You can work directly with the VBO. _____ Reaper but I need to have the first vertex there already. I wonder what's faster - binding it to null and then immediately calling glBufferSubData(), or populating an array like I do and calling glBufferData only once. – Vlad Vyatkin Sep 17 '19 at 06:42
  • >>>> This will cause an issue if you've more than 1 VAO object. Never never do glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);..... Ermmm..., I have like 20 meshes on my scene, each with it's own VAO and a different number of VBO's. I use different shader programs too with them, and pass different data depending on the object. And so far I didn't notice any issues - even though I unbind VBO just like I should. You are clearly missing some point - or maybe I do. But so far, I don't see anything amiss, all works as intended. – Vlad Vyatkin Sep 17 '19 at 06:50
  • @VladKozmyuk Read the specification. I mention that because I want to help you and I've experience with that for decades. Don't confuse `GL_ELEMENT_ARRAY_BUFFER` and `GL_ARRAY_BUFFER`. This buffer targets behave completely different. What you actually do is to bind the element buffer to the default vertex array object 0. This only works in a compatibility profile but it will cause an **error in core profile** context!!!! The `GL_ELEMENT_ARRAY_BUFFER` is stated in the VAO, in compare to the `GL_ARRAY_BUFFER` which is a global state. – Rabbid76 Sep 17 '19 at 14:12
  • @VladKozmyuk ... when you call [`glVertexAttribPointer`](https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glVertexAttribPointer.xhtml) then the VAO has to be bound, too. When `glVertexAttribPointer` is called, then the buffer which is currently bound to the target `GL_ARRAY_BUFFER` (global state) is associated to the attribute and the name (value) of the object is stored in the state vector of the VAO. When `glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboID);` is called the `vboID` is associated to the VAO and the name (value) `vboID` is stored in the VAO. – Rabbid76 Sep 17 '19 at 14:14