0

I am writing a simple OBJ parser. Now I don't know how to deal with the case when #texture_coordinates > #vertices. My OBJ file looks like this:

# Blender3D v249 OBJ File: untitled.blend
# www.blender3d.org
mtllib cube.mtl
v 1.000000 -1.000000 -1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 -1.000000 1.000000
v -1.000000 -1.000000 -1.000000
v 1.000000 1.000000 -1.000000
v 0.999999 1.000000 1.000001
v -1.000000 1.000000 1.000000
v -1.000000 1.000000 -1.000000
vt 0.748573 0.750412
vt 0.749279 0.501284
vt 0.999110 0.501077
vt 0.999455 0.750380
vt 0.250471 0.500702
vt 0.249682 0.749677
vt 0.001085 0.750380
vt 0.001517 0.499994
vt 0.499422 0.500239
vt 0.500149 0.750166
vt 0.748355 0.998230
vt 0.500193 0.998728
vt 0.498993 0.250415
vt 0.748953 0.250920
vn 0.000000 0.000000 -1.000000
vn -1.000000 -0.000000 -0.000000
vn -0.000000 -0.000000 1.000000
vn -0.000001 0.000000 1.000000
vn 1.000000 -0.000000 0.000000
vn 1.000000 0.000000 0.000001
vn 0.000000 1.000000 -0.000000
vn -0.000000 -1.000000 0.000000
usemtl Material_ray.png
s off
f 5/1/1 1/2/1 4/3/1
f 5/1/1 4/3/1 8/4/1
f 3/5/2 7/6/2 8/7/2
f 3/5/2 8/7/2 4/8/2
f 2/9/3 6/10/3 3/5/3
f 6/10/4 7/6/4 3/5/4
f 1/2/5 5/1/5 2/9/5
f 5/1/6 6/10/6 2/9/6
f 5/1/7 8/11/7 6/10/7
f 8/11/7 7/12/7 6/10/7
f 1/2/8 2/9/8 3/13/8
f 1/2/8 3/13/8 4/14/8

I know why we can have more texture coordinates than vertices, I just don't know what to do with them especially when rendering with OpenGL (which I do). My Index Buffer looks like this when I parse the obj:

4 0 3
4 3 7
2 6 7
2 7 3
1 5 2
5 6 2
0 4 1
4 5 1
4 7 5
7 6 5
0 1 2
0 2 3

note that I subtracted 1 from all of the indices, since OpenGL wants them in that way. The object draws fine but of course the vertex data is just wrong. I store the vertex data in a dedicated VBO by the way. So in total I have my VAO with 2 VBOs (vertices, textures) and one EBO storing the information which vertecies form a triangle. How to address the texture coordinates now?

EDIT after suggestions:

#

So I unpacked the vertices (that means I do have 36 vertices in total) and have the vertex, texture and normal data in one consecutive array. The outcome is this: enter image description here

My VBO looks like this:

1.000000 1.000000 -1.000000 0.748573 0.750412 0.000000 0.000000 -1.000000
1.000000 -1.000000 -1.000000 0.749279 0.501284 0.000000 0.000000 -1.000000
-1.000000 -1.000000 -1.000000 0.999110 0.501077 0.000000 0.000000 -1.000000
1.000000 1.000000 -1.000000 0.748573 0.750412 0.000000 0.000000 -1.000000
-1.000000 -1.000000 -1.000000 0.999110 0.501077 0.000000 0.000000 -1.000000
-1.000000 1.000000 -1.000000 0.999455 0.750380 0.000000 0.000000 -1.000000

and so on... one row is exactly one vertex with: vertex pos, texture coordinates, normal vector.

So three rows represent one triangle.

I set up by attribute pointers like this:

    glBufferData(GL_ARRAY_BUFFER, sizeof(float) * objModel2.getObjData().size(), &objModel2.getObjData()[0], GL_STATIC_DRAW);
// vertex positions
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), 0);
glEnableVertexAttribArray(0);
// texture positions
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(sizeof(GLfloat) * 3));
glEnableVertexAttribArray(1);
// normals positions
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(sizeof(GLfloat) * 5));
glEnableVertexAttribArray(2);

and finally draw the cube like so:

    glBindVertexArray(VAO_cube_model_2);
    glDrawArrays(GL_TRIANGLES, 0, objModel2.getObjData().size() / 8);
    glBindVertexArray(0);

The texture file is the following png file: enter image description here

Only 3 and 5 gets drawn. I cannot figure out why...

kimsay
  • 311
  • 3
  • 15
  • 1
    This comes up frequently. See for example here for a related question where I posted some pseudo-code in the answer: http://stackoverflow.com/questions/23349080/opengl-index-buffers-difficulties. – Reto Koradi Oct 19 '16 at 05:54
  • OK, I found out that OpenGL flips the texture coordinates for some reason. When I flip my texture vertically, it works. – kimsay Oct 21 '16 at 10:51

2 Answers2

2

OpenGL uses a different indexing schema than used by the obj format. In obj, each attribute (position, color, texture coordinate) can have its own indexing while in OpenGL there is just one index possible. In order to load the obj correctly you will have to duplicate some of the data and find a new indexing.

The simplest thing is to work without any duplication check. In this case, you would simply duplicate the data for each vertex. The process could look like this:

load all v lines into v[]
load all vt lines into vt[]
load all vn lines into vn[]

float[] verts;
int[] idx;

foreach f line consisting of (vi[0]/vti[0]/vni[0] vi[1]/vti[1]/vni[1] vi[2]/vti[2]/vni[2])
    for i = 1:3
        verts.append(v[vi[i]], vt[vti[i]], vn[vni[i]]);
        idx.append(verts.size() - 1);

In this case you could even leave away the index buffer. As one might notice, this is not a optimal way since a lot of duplicate data is saved. One optimization would be to identify whether the combination of vi, vti, vni has already be added to the vertex list and just add the correct index if this is already the case.

BDL
  • 21,052
  • 22
  • 49
  • 55
  • thank you! I did exactly that and left the idx array away and drew it with glDrawArrays. The model renders correctly but only 2 of the numbers of the texture appear on the model. The thing is that with this implementation there are 36 entries in the array, correct? – kimsay Oct 21 '16 at 09:29
0

the lines at the bottom:

f 5/1/1 1/2/1 4/3/1

Tell you how to combine the vertices, texture coordinates, and normals respecitvely to create the object.

They are 1 indexed.

They are "face" elements and described in this document:

http://www.martinreddy.net/gfx/3d/OBJ.spec

xaxxon
  • 19,189
  • 5
  • 50
  • 80
  • Yes, that would mean that texture number one has to go to vertex number 5 etc... so I would do a reordering in my list of all the texture coordinates. But since there are more texture coordinates than vertices there is no one to one match but a one to n match. How to tell OpenGL to use more texture coordinates than just one for a vertex; thus, how has the data to be arrange to be useful for OpenGL? – kimsay Oct 18 '16 at 12:19
  • I don't understand. The number of vertices to send to opengl is determined by the number of "f" elements. There is exactly 1 vertex, 1 texture coordinate, and 1 normal for each face. – xaxxon Oct 18 '16 at 12:25
  • The way you build up the data for opengl is only determined by the order and values (which are then looked up into the V/VT/VN data) in the F elements. Basically you'd make a v, vt, and vn vector in your "normal" code and then when you read the F's, that's when you start grabbing the data from those vectors (not necessarily in the order they were put in). – xaxxon Oct 18 '16 at 12:34
  • 1
    @kimsay you'll need to unpack vertices and recalculate new indices. Or just use `glDrawArrays` without indices. – keltar Oct 18 '16 at 12:36
  • 1
    @keltar that's a great way to put it. – xaxxon Oct 18 '16 at 12:39