OpenGL/JOGL: Multiple triangle fans in a vertex array
Asked Answered
S

1

13

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?

Salzman answered 8/12, 2010 at 11:24 Comment(3)
Plase don't ever forget to specify your target OpenGL version. :)Kalakalaazar
Fair enough - I believe we're using 2.1 if I remember correctly, but we'd like this to be as platform independent (and therefore OpenGL version independent?) as possible.Salzman
It's more of what GPUs you're targetting. The features described here should be available in GL 2.1, IIRC, so you're good to go :).Kalakalaazar
K
19

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.

Kalakalaazar answered 8/12, 2010 at 12:19 Comment(4)
Thanks very much, multiDrawElements does seem a little better. PRIMITIVE_RESTART or GL_PRIMITIVE_RESTART doesn't seem to be an option in JOGL. There is a GL_PRIMITIVE_RESTART_NV, but I'm hesitant to just use a different value as if it's the same. opengl.org/registry/specs/NV/primitive_restart.txt is what google gives me when I search for that parameter, and it seems like the same concept, but very different methods and structure.Salzman
PRIMITIVE_RESTART's just a variable name I introduced in my example code. :) Only glPrimitiveRestartIndex is the GL api related to that.Kalakalaazar
Ah, my mistake - I think I was assuming it needed to be enabled first, and therefore there would be a GL_ constant describing it.Salzman
If you decide to use Primitive Restart method don't forget to: - glEnable(GL_PRIMITIVE_RESTART); I don't know JOGL so I don't know if you need to do this in it but if there are any C programmers reading this, it can save them a LOT of time (It can cause segfaults without this line).Frenchy

© 2022 - 2024 — McMap. All rights reserved.