openglpolygonjoglvertex-array

OpenGL/JOGL: Multiple triangle fans in a vertex array


I'm working on making some moderately simple shapes with vertex arrays, and I'm making some good headway, but now I want to draw 2 (or more) triangle fan objects. Is there any way to only make one call to gl.glDrawArrays(GL.GL_TRIANGLE_FAN,... or do I need to make a separate call for each fan?

Wikipedia's Triangle strip article describes something called primitive restart, but OpenGL's Vertex Specification makes me think this doesn't work with vertex arrays.

What is the correct way to draw multiple triangle fans? Here is my current draw method:

public void draw(GL gl){
if(vertices.length == 0)
    return;

gl.glEnableClientState(GL.GL_VERTEX_ARRAY);
    gl.glEnableClientState(GL.GL_COLOR_ARRAY);
    gl.glEnableClientState(GL.GL_NORMAL_ARRAY);

    gl.glVertexPointer(3, GL.GL_FLOAT, 0, vertBuff);
    gl.glColorPointer(3, GL.GL_FLOAT, 0, colorBuff);
    gl.glNormalPointer(GL.GL_FLOAT,0, normalBuff);

    // drawArrays count is num of points, not indices.
    gl.glDrawArrays(GL.GL_TRIANGLES, 0, triangleCount);
    gl.glDrawArrays(GL.GL_QUADS, triangleCount, quadCount);
    gl.glDrawArrays(GL.GL_TRIANGLE_FAN, triangleCount+quadCount, fanCount);

    gl.glDisableClientState(GL.GL_VERTEX_ARRAY);
    gl.glDisableClientState(GL.GL_COLOR_ARRAY);
    gl.glDisableClientState(GL.GL_NORMAL_ARRAY);
}

Edit

I updated the relevant section of draw like so:

    for(int i = 0; i < fanLength.length; i++){
        gl.glDrawArrays(GL.GL_TRIANGLE_FAN, 
            triangleCount+quadCount+fanDist[i], fanLength[i]);
    }

Where fanDist is the offset (from the start of the fans) of the start of this fan, and fanLength is the length of this fan.

This does seem to work, which is nice, but still, is this the right way to do this? Is there a better way?


Solution

  • Primitive restart does work with vertex arrays and vertex buffers - it wouldn't be really useful if it didn't.

    However, it's true that it's not useful in conjunction with glDrawArrays.

    Let's have a look on two techniques:

    Primitive Restart


    Let me introduce glDrawElements to you: A call like

    glDrawArrays(mode, 0, 5);
    

    is analogous to

    GLuint idxs[] = {0, 1, 2, 3, 4}; // C code, but idea's the same in Java
    glDrawElements(mode, 5, GL_UNSIGNED_INT, idxs);
    

    So instead of specifying a range of elements to draw from an array, you specify exact indices of those elements.

    And then you can introduce primitive restarting by using such array:

    GLuint PRIMITIVE_RESTART = 12345; // magic value
    
    glEnable(GL_PRIMITIVE_RESTART);
    glPrimitiveRestartIndex(PRIMITIVE_RESTART);
    GLuint idxs[] = {0, 1, 2, 3, PRIMITIVE_RESTART, 4, 5, 6, 7};
    glDrawElements(mode, 9, GL_UNSIGNED_INT, idxs);
    

    This will draw a fan from the first 4 vertices, then will encounter the "sign" to restart the primitive, and then it will draw another fan of the last 4 vertices.

    The indices passed to DrawElements don't have to be a consecutive range! They can be in any order and can be repeated as you desire - that's the whole best part of this function. In fact, it's best to reuse one index as often as possible, as it will get processed by vertex shader only once if the result's cached. So you're free to make buffers like:

    GLuint idxs[] = {0, 6, 3, 4, 6, 2, PRIMITIVE_RESTART, 2, 6, 3, 3, 5, 2}; // etc.
    

    MultiDrawElements


    In your case, you might want to use glMultiDrawElements instead.

    If you have, say, 20 vertices and you want to draw one fan of 8 vertices starting from the first and one fan of 10 vertices starting from the 10th, you can do something like this:

    // assuming glVertexPointer is already set
    GLuint startingElements[] = {0, 9};
    GLuint counts[] = {8, 10};
    glMultiDrawArrays(GL_TRIANGLE_FAN, startingElements, counts, 2); // 2 fans
    

    Thus you have a bit less work to do.


    Pick the technique you find more useful. I'll leave it to you to rewrite that in Java/JOGL, the principle is the same but you'll have to use the Buffer class for all those, I guess.