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);
}
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?
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:
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.
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.