OBJ model drawing

joellevin
Unregistered
 
Post: #1
I wrote a parser in C++ that can correctly and successfully parse an obj model, but I'm having issues drawing it. I have parsed all the vertex, texcoord, normal, and face data into stl vectors (because they automatically expand and I don't know the number of each before I start parsing), but obviously OpenGL cannot do anything with the vectors themselves. How can I pass my parsed data to OpenGL to have it drawn?

Thanks!
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #2
OBJ data is not in a format that makes sense for OpenGL, so if you don't restructure the data, you have no choice but glBegin/glNormal/glTexCoord/glEnd.

If you restructure the data so it makes sense for OpenGL, you can use vertex arrays. Search the forums for akb825's approach to doing this.
Quote this message in a reply
joellevin
Unregistered
 
Post: #3
Hi, searched the forums but I'm not exactly sure what I'm looking for. I found some related stuff but I'm still not really sure where to look.
Quote this message in a reply
Moderator
Posts: 1,140
Joined: 2005.07
Post: #4
I discussed the basic algorithm on the first post of this page.

Since writing that post, to avoid some ordering problems, I have made my comparison more robust. I have 5 elements which I can use to differentiate a vertex, some of which are optional: the position, normal, color, and 2 sets of texture coordinates. I found that I can use 3 sets of sums to have each combination be distinct, at least with the examples I used: 1 has the normal sum of every component, 1 has the sum of each element's components times a constant, and one multiplies each component of an element by a constant and sums them together. I also throw in a constant saying if I need that element in case it happened to be whatever default value I would have chosen. Since code speaks louder than words, here's how I create the 3 sums:
Code:
float sum[3];
sum[0] = sum[1] = vertex[0] + vertex[1] + vertex[2];
sum[2] = vertex[0] + 2*vertex[1] + 3*vertex[2];
sum[0] += normal[0] + normal[1] + normal[2];
sum[1] += 2*(normal[0] + normal[1] + normal[2]);
sum[2] += normal[0] + 2*normal[1] + 3*normal[2];
sum[0] += color[0] + color[1] + color[2] + color[3] + needsColor;
sum[1] += 3*(color[0] + color[1] + color[2] + color[3] + needsColor);
sum[2] += color[0] + 2*color[1] + 3*color[2] + 4*color[3] + needsColor;
sum[0] += textureCoord[0] + textureCoord[1] + needsTextureCoord;
sum[1] += 4*(textureCoord[0] + textureCoord[1] + needsTextureCoord);
sum[2] += textureCoord[0] + 2*textureCoord[1] + needsTextureCoord;
sum[0] += textureCoord2[0] + textureCoord2[1] + needsTextureCoord2;
sum[1] += 5*(textureCoord2[0] + textureCoord2[1] + needsTextureCoord2);
sum[2] += textureCoord2[0] + 2*textureCoord2[1] + needsTextureCoord2;
If one of the sums are equal, I simply move to the next.
Quote this message in a reply
Sage
Posts: 1,403
Joined: 2005.07
Post: #5
What is the point of this?

Sir, e^iπ + 1 = 0, hence God exists; reply!
Quote this message in a reply
Moderator
Posts: 1,140
Joined: 2005.07
Post: #6
This is to organize the data that's given by an obj into a format that is better suited for OpenGL vertex arrays or VBOs. (the way it is, the arrays you would have aren't concurrent, and you'd need an index for each element rather than just 1 master index) Since you can have vertices with the same position, but with different normals, colors, or texture coordinates, you have to be sure to split them up properly as well, hence the sums for the ordering property. I have the 3 different sums because if I only had 1, there can be multiple combinations that result in the same sum.
Quote this message in a reply
Sage
Posts: 1,403
Joined: 2005.07
Post: #7
Cant you just take your std::vector of vertices, normals, and texture coordinates, and make an interleaved array shove them in and then pass that in the right stride values when you are calling glVertex/Normal/TexCoordPointer?

Sir, e^iπ + 1 = 0, hence God exists; reply!
Quote this message in a reply
Sage
Posts: 1,199
Joined: 2004.10
Post: #8
Not only that, but if you take the address of vector.front(), you get the first element. I do this for vertex arrays all the time. std::vector is -- supposedly -- guaranteed to use contiguous storage, so this is safe.

Code:
int nPoints = _points.size();
vec3 *points = &(_points.front());

Boom. You can pass that to gl's vertex pointers, VBO data, etc.
Quote this message in a reply
Sage
Posts: 1,403
Joined: 2005.07
Post: #9
yes this is why I am confused about all this mess with binary trees or w/e.

Sir, e^iπ + 1 = 0, hence God exists; reply!
Quote this message in a reply
DoG
Moderator
Posts: 869
Joined: 2003.01
Post: #10
Actually, OBJ may contain indices which are unsuitable for OpenGL's vertex array style stuff, as the indices of vertex/texture/normal coordinates might not match. This would be the case with per-triangle colors, for example. In that case you have to create new duplicate vertices with the matching colors, etc.
Quote this message in a reply
Moderator
Posts: 1,140
Joined: 2005.07
Post: #11
This is exactly the kind of case that I was talking about. The texture coordinates also often don't line up, and sometimes there's also per-triangle normals. (even if the editor smoothes the normals, there are often parts that they won't smooth due to the sharpness of the angles) In those cases, you have to create completely new indices.
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #12
To clarify (hopefully)

The issue is, OBJ has three arrays: texcoords, normals, vertices. These arrays can be different lengths (lt, ln, lv).

OBJ describes vertices by three indices, one into each of the three arrays.

OpenGL requires that all arrays be the same length and describes vertices by a single index into that array.

In order to reconcile those world views, you need one entry in your OpenGL array for every OBJ index triple. That gives you a potential OpenGL array size of lt * ln * lv, but obviously most models won't be anywhere near that.

In order to create the OpenGL array, you go through all the combinations actually used in the OBJ "f" lines, create an OpenGL vertex for each used combination, and build a new index array with the new indexes.

Doing that is annoying, but not difficult -- make a std::vector for your new vertex data, for each OBJ vertex, search the vector for a matching one and use its index, or append a new vertex and use the new index.

That algorithm is O(n^2), though, so will take a long time for larger models. A better solution is to use a more efficient data structure (a tree or a hashtable) to avoid searching the entire array for a matching vertex. This is where akb825 is coming from.
Quote this message in a reply
Sage
Posts: 1,403
Joined: 2005.07
Post: #13
This is really not somthing you should do at load time in a computer game, you should always store files in a format thats at least somewhat close to what you _want_.

Sir, e^iπ + 1 = 0, hence God exists; reply!
Quote this message in a reply
Moderator
Posts: 1,140
Joined: 2005.07
Post: #14
You always have to do it a first time. Besides, you may still want to do it at load time if you want to decrease the size of your models. It's also useful if you are loading things from heightmaps, such as for terrain, which take up much less space than saving the resulting mesh.
Quote this message in a reply
Sage
Posts: 1,403
Joined: 2005.07
Post: #15
you dont have to do it at all.

Sir, e^iπ + 1 = 0, hence God exists; reply!
Quote this message in a reply
Post Reply