glDrawElements question

Falcor
Unregistered
 
Post: #1
I am probably way off because everyone seems to think this is a non-issue or obvious, but here goes\:
I've never really bothered to use drawelements before but now I've been trying and am really confused. when I draw by indices using glDrawElements (VBOs) not only do the vertices seem to be indexed, but also the texture coordinates and normals? When I'm drawing a cube I obviously don't want the same vertex to have the same texturecoordinate and normal every time. Is there some way to specify index = {index of vertex, index of texcoord, index of normal} instead of just one index for each glVertex call it replaces which it seems to be doing?
In other word is it doing this:
i = index;
glNormal3f(normals[i].x, normals[i].y, normals[i].z);
glTexCoord2f(tex[i].u, tex[i].v);
glVertex3f(vert[i].x, vert[i].y);

if that's true how is that possibly useful asside from drawing untextured unlit objects?

-thanks
Quote this message in a reply
Moderator
Posts: 1,140
Joined: 2005.07
Post: #2
You need to structure your arrays so that they are parallel. If you don't want the same texture coordinate or normal for another time you use the vertex, you're going to need to have another vertex to specify it. It may take up more memory, but believe me: it will be faster, since OpenGL will have more freedom with the data, and will also be able to reduce calls for repeated vertices.
Quote this message in a reply
Falcor
Unregistered
 
Post: #3
thanks, looks like I'll have to restructure how I specify my data. It seems like that greatly reduces the number of reusable vertices though.
Quote this message in a reply
Moderator
Posts: 1,140
Joined: 2005.07
Post: #4
This is because it isn't really the same vertex. The vertices in this context aren't just the xyz coordinates, but also the color, normal, texture coord, etc. This way you only have one set of indices, and it is also probably a lot easier to optimize, since you're traversing all the datasets once and in parallel, rather than having to jump all around for all of them with different duplicates. I believe OSC has more knowledge of the internals of OpenGL and will be able to give you a more specific answer of why it's better than I can if you want a better explanation.
Quote this message in a reply
Roady
Unregistered
 
Post: #5
I develop on Windows x86, not Mac, but since I'm having problems with OpenGL, maybe someone here can help answer my question, it fits in with the OP.

With the following structures and a model loaded from a .3ds file
  • INDEXED ARRAY DRAWING draws the test model geometry properly, the normals improperly (I think because I must calculate them and Blender's .3ds exporter doesn't save all triangles counter-clockwise), and the texture coordinates are fubar.
  • MULTI-GL PROC CALL draws the geometry and texture coordinates properly, and the normals still have issues.
Can anyone explain why the texture coordinates aren't working for with the INDEXED ARRAY DRAWING code??
I can google many sites that quote the oh-so-useless OpenGL API info for indexed arrays, but so far nothing explains the data relationship between polygon indices and the various arrays.

struct type_vertex { float x, y, z; };
struct type_triangle { unsigned short a, b, c; };
struct type_uvmap { float u, v; };
struct type_object { type_vertex * vertices, normals; type_triangle * triangles; type_uvmap * uvmap; };

type_object object;

/*************************
* vertices = new type_vertex[VERTEX_COUNT];
* triangles = new type_triangle[TRI_COUNT];
* uvmap = new type_uvmap[TRI_COUNT * 3]; 1 tex coord for each triangle vertex, therefore, vertices[0] may have 3 tex coords if part of 3 triangles.
* normals = new type_vertex[TRI_COUNT * 3]; 1 normal for each triangle vertex, therefore, vertices[0] may have 3 normals if part of 3 triangles.
*/

void draw ()
{
glColor3f(1.0f, 1.0f, 1.0f);
glBindTexture(GL_TEXTURE_2D, object.texture->texID);
glEnable(GL_TEXTURE_2D);

/*** START INDEXED ARRAY DRAWING ***
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, object.vertices);
glNormalPointer(GL_FLOAT, 0, object.normals);
glTexCoordPointer(2, GL_FLOAT, 0, object.uvmap);
glDrawElements(GL_TRIANGLES, object.triangle_count*3, GL_UNSIGNED_SHORT, object.triangles);
*** STOP INDEXED ARRAY DRAWING ***/

/*** START MULTI-GL PROC CALL ***/
glBegin(GL_TRIANGLES);
int i0, i1, i2;
for (int i=0; i < object.triangle_count; i++)
{
i0 = i*3;
i1 = i0 + 1;
i2 = i0 + 2;
glNormal3fv( (GLfloat *)&object.normals[i0] );
glTexCoord2fv( (GLfloat *)&object.uvmap[i0] );
glVertex3fv( (GLfloat *)&object.vertices[object.triangles[i].a] );
glTexCoord2fv( (GLfloat *)&object.uvmap[i1] );
glVertex3fv( (GLfloat *)&object.vertices[object.triangles[i].b] );
glTexCoord2fv( (GLfloat *)&object.uvmap[i2] );
glVertex3fv( (GLfloat *)&object.vertices[object.triangles[i].c] );
}
glEnd();
/*** STOP MULTI-GL PROC CALL ***/
glDisable(GL_TEXTURE_2D);
}

Thanks for any info regarding my question.
I hope Falcor gains a bit more insight from my inquiry and post. I'd rather not start another thread when the info falls under the same topic.
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #6
The element index always indexes *all* the arrays -- color, texture coordinates, normals, vertices, and any special extras you might have available.

That means that all those arrays must always be exactly the same size, and there is no opportunity to re-use a position with a different normal, for example -- you have to duplicate the position for each normal it uses.

This is not a major issue. Most surfaces are smooth, meaning that all adjacent triangles share the position, normal, texture coordinates, etc. It just feels wrong when you're starting out, since you tend to use polygonal shapes like cubes and tetrahedrons.
Quote this message in a reply
Roady
Unregistered
 
Post: #7
nerts... guess I'm stuck with the MULTI-GL PROC CALL. Thanks for the info ^^
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #8
that method is *horribly* slow. I suggest switching to indexed arrays, and living with the tiny limitation.
Quote this message in a reply
Moderator
Posts: 1,140
Joined: 2005.07
Post: #9
Indexed arrays are really the way to go. It will greatly reduce the calls that OpenGL will make, and it also gives you a chance to put them in VBOs, which will make your arrays be resident on the video card. Both of those will give you a large speed advantage.
Quote this message in a reply
Roady
Unregistered
 
Post: #10
Thanks for suggestions. I'm amateur at texture mapping and don't want to spend hours/days trying to wrap a texture around my model with 1 tex_coord per vertex.

However, I've figured out that I can expand my vertices array so that vertices = new type_vertex[TRI_COUNT*3];. The vertex array is full of duplicates, though.
The index array contains no duplicate values; the values increment from 0 to TRICOUNT*3 - 1.
Now my geometry, normals, and texture coordinates work.

I'm sure there's another way to draw this, but atm I have the performance optimization of indexed array rendering, so I'm happy Smile

Here's a photo ^^
[Image: screen_dump_success.png]
Quote this message in a reply
Moderator
Posts: 1,140
Joined: 2005.07
Post: #11
When I wrote my obj loader, this is how I got rid of duplicates: I had a linked list that, when you insert an item, it compares the values for each of the currently inserted objects. If it encounters one that's the same, it just stops. If it doesn't, it inserts it at the end and increments a counter. It then also returns the index that the value is at, whether it was a pre-existing index or a new index. When that's done, I allocate the arrays for the vertices, colors, texture coordinates, normals, etc. and copy the values from the linked list into the arrays. It's somewhat expensive in terms of processor time, but if you then save those arrays to a file, when you read in your model, it will basically all be in memory the way you want it.
Quote this message in a reply
Member
Posts: 469
Joined: 2002.10
Post: #12
akb825 Wrote:When I wrote my obj loader, this is how I got rid of duplicates: I had a linked list that, when you insert an item, it compares the values for each of the currently inserted objects.
Um... this is a joke... right? You really used a hash table... right?

---Kelvin--
15.4" MacBook Pro revA
1.83GHz/2GB/250GB
Quote this message in a reply
Moderator
Posts: 1,140
Joined: 2005.07
Post: #13
Sneaky Well, I didn't know how large it would be in the end, so I wanted something that would be dynamic. It was my first stab at it, anyway. I'm not even sure how I'd implement a hash table with that, especially since it's with relatively small floating point numbers that would also be somewhat similar. I suppose you could also use a tree to help speed things up, but that would make it a lot difficult with keeping the indices intact. (mainly with traversing the tree at the end and putting them in the array in the correct order) I could probably redo it easily enough, since all I'd have to do on the higher end is change 1 or 2 function calls. If you have any suggestions, I'm certainly willing to listen (er... read), since I'm pretty new to this still. (hell, I didn't even start programming until last school year) Fortunately, I'm not going to use obj for my games (this is for an intermediate program that will end up eventually exporting the result to a binary format where it will be pretty much in disk the same way it's in memory), so speed isn't of utmost importance.

Edit: for hash tables, don't the work best with the array size being a prime number? If so, I could also see it difficult for choosing a size that would work well...
Quote this message in a reply
Moderator
Posts: 3,579
Joined: 2003.06
Post: #14
I'm sure there's nothing really wrong with your approach akb825. Brute force is perfectly acceptable in this scenario since it's only done once. I create a nice fat array and start filling in indices there one at a time and look for dupes each index. I make a check to see if the normals and texture coords are within a small arbitrary tolerance [edit] of each other [/edit] as well as the vertices. You have to loop through the whole works each time any way you look at it. A linked list would be a little more elegant, but again, since it's only done once on export and memory is cheap, why bother? I wouldn't prefer a hash table for this situation in any case.
Quote this message in a reply
Moderator
Posts: 1,140
Joined: 2005.07
Post: #15
With the idea I mentioned with the tree, I think that would be a very good approach. To fix the indexing problem, I can just store the index in the node, and when I traverse the tree I can just put the objects right in the array. I'd imagine I'll be able to choose which direction to take by the sum of all the parts. I'll try it out and report back on the results. (bruit force is a little slower than I'd like, but as you said, since it's only being done once it isn't that big of a deal)
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  glDrawElements and Face indices Ashford 8 13,381 Nov 11, 2009 03:03 PM
Last Post: Ashford
  Agh! glDrawElements kills my artwork ferum 2 3,672 Nov 23, 2006 09:05 AM
Last Post: ferum
  glDrawElements() some questions .. NYGhost 2 2,926 Nov 15, 2004 06:15 PM
Last Post: NYGhost
  glDrawElements question Jake 9 4,614 Jun 24, 2004 08:13 PM
Last Post: Jake
  glDrawElements vs. glDrawArrays - The numbers are in! inio 22 21,010 Jul 19, 2003 10:00 AM
Last Post: Josh