Part of the Khronos Group
OpenGL.org

The Industry's Foundation for High Performance Graphics

from games to virtual reality, mobile phones to supercomputers

Results 1 to 8 of 8

Thread: .OBJ texture coordinates aren't mapped correctly

  1. #1
    Junior Member Newbie
    Join Date
    May 2013
    Posts
    4

    Unhappy .OBJ texture coordinates aren't mapped correctly

    This is a cross-post from game-dev because it seems more general to graphical programming than game development.

    I have written a simple .obj parser for my OpenGL game engine that reads vertices, uv's and normals; but when I get to drawing the model in the texture isn't mapped correctly.

    I have tried
    Code :
    uv.y = 1.0f-uv.y
    to get inverted Y axis coordinates as suggested by a few people but my textures are still skewed.

    I know the texture and my rendering are correct because when I render a plane, with hand written texture coordinates and vertices using the same texture, it renders fine. I can render a textured cube by hand fine too.

    I am using an index buffer to store vertex elements and I use the face elements provided by the obj to fill texture coordinate and normal buffers with the correct vectors.

    What is wrong with my parser?


    PARSER CODE:

    Code :
    Model::Model(const string &givenName, const string &filePath){
        name = givenName;
     
        std::ifstream in(MODEL_PATH+filePath, std::ios::in);
        if(!in){
            error("Model \"" + filePath + "\" does not exist");
            return;
        }
     
     
        ModelMeshIndex tempindex;
     
        vector<vec3> normals;
        vector<vec2> uvs;
     
        string line;
        while(getline(in, line)){
            if(line.substr(0,2) == "v "){
                        std::istringstream s(line.substr(2));
                        vec3 v(0.0f);
                        s >> v.x >> v.y >> v.z;
                        data.vertices.push_back(v);
            }
            else if(line.substr(0,2) == "vt"){
                        std::istringstream s(line.substr(2));
                        vec2 v(0.0f);
                        s >> v.x >> v.y;
                        //v.y = 1.0f-v.y;
                        uvs.push_back(v);
            }
            else if(line.substr(0,2) == "vn"){
                        std::istringstream s(line.substr(2));
                        vec3 v(0.0f);
                        s >> v.x >> v.y >> v.z;
                        normals.push_back(v);
            }
            else if(line.substr(0,2) == "f "){
                        std::istringstream s(line.substr(1));
                        char tmp; // For the slashes between elements
                        unsigned short a,b,c, d,e,f, g,h,i;
                        s >> a >> tmp >> d >> tmp >>g;
                        s >> b >> tmp >> e >> tmp >>h;
                        s >> c >> tmp >> f >> tmp >>i;
                        a--; b--; c--; d--; e--; f--; g--; h--; i--;
                        data.index.vertexElements.push_back(a); data.index.vertexElements.push_back(b); data.index.vertexElements.push_back(c);
                        data.index.uvElements.push_back(d);     data.index.uvElements.push_back(e);     data.index.uvElements.push_back(f);
                        data.index.normalElements.push_back(g); data.index.normalElements.push_back(h); data.index.normalElements.push_back(i);
            }
            else if(line[0] == '#') continue;
            else continue;
        }
     
        for(unsigned int i = 0; i < data.index.normalElements.size(); ++i)
            data.normals.push_back(normals[data.index.normalElements[i]]);
     
        for(unsigned int i = 0; i < data.index.uvElements.size(); ++i)
            data.uvs.push_back(uvs[data.index.uvElements[i]]);
     
        //data.uvs.push_back(vec2( 0.0f, 0.0f)); // bottom left
        //data.uvs.push_back(vec2( 0.0f, 1.0f)); // top left
        //data.uvs.push_back(vec2( 1.0f, 0.0f)); // bottom right
        //data.uvs.push_back(vec2( 1.0f, 1.0f)); // top right
     
        good = true;
    }

    Last edited by CoffeeandCode; 05-07-2013 at 10:19 PM.

  2. #2
    Advanced Member Frequent Contributor
    Join Date
    Apr 2010
    Posts
    645
    .OBJ is multi-indexed; different vertex attributes have their own index (i.e. there is an index for position, one for normals, ...). OpenGL only supports a single index that is used for all vertex attributes. I suppose that is why you do this:

    Code :
    for(unsigned int i = 0; i < data.index.normalElements.size(); ++i)
        data.normals.push_back(normals[data.index.normalElements[i]]);
     
    for(unsigned int i = 0; i < data.index.uvElements.size(); ++i)
        data.uvs.push_back(uvs[data.index.uvElements[i]]);

    However, you are not doing this for positions (presumably because you are still using indexed drawing?), but for that to work you need to arrange for the normals/texcoords to be in an order that is "compatible" with the position index. The usual way to deal with the multiple indices in OBJ is to go through the indices and look at each tuple (position_index, normal_index, texcoord_index); if the tuple has not occurred previously (i.e. it's a new unique combination) write the referenced position, normal, texcoord to the vertex data buffer(s), write the number of unique tuples so far to the index buffer and remember that this tuple is associated with this new index. If the tuple occurred before, look up what index it was associated with and write that to the index buffer.

  3. #3
    Junior Member Newbie
    Join Date
    May 2013
    Posts
    4
    Could you give me a pseudo code example ? I'm not really sure how you suggest I store the vertices

  4. #4
    Senior Member OpenGL Pro
    Join Date
    Jan 2012
    Location
    Australia
    Posts
    1,098
    Code :
    vertices.clear
    next_vertex = 0;
     
    for each face
      for each vertex in face
          key = fn(position_index,normal_index,texcoord_index)
          if vertices.find(key)
            new_index = vertex(key).index
          else
            create vertex from position/normal/texcorrd
            store new vertex in vertices with key and next_vertex
            new_index = next_vertex
            increment next_index
          end
          save new_index in index buffer
      loop

    Please note this is simplified code. It assumes .obj only has triangles (the specs allow for polygon faces and often the .obj will be quads not triangles.

  5. #5
    Junior Member Newbie
    Join Date
    May 2013
    Posts
    4
    I'm having a little trouble understanding your example; what is key? and what is the function used to define it?
    Quote Originally Posted by tonyo_au View Post
    Code :
    vertices.clear
    next_vertex = 0;
     
    for each face
      for each vertex in face
          key = fn(position_index,normal_index,texcoord_index)
          if vertices.find(key)
            new_index = vertex(key).index
          else
            create vertex from position/normal/texcorrd
            store new vertex in vertices with key and next_vertex
            new_index = next_vertex
            increment next_index
          end
          save new_index in index buffer
      loop

    Please note this is simplified code. It assumes .obj only has triangles (the specs allow for polygon faces and often the .obj will be quads not triangles.

  6. #6
    Senior Member OpenGL Pro
    Join Date
    Jan 2012
    Location
    Australia
    Posts
    1,098
    Do you know how to use std::map or any other hashing function?

  7. #7
    Junior Member Newbie
    Join Date
    May 2013
    Posts
    4
    I don't know very much about them but I understand the basics. I have been meaning to do some practical work with them to get better with them. In your example do you suggest that I map unsigned integers to vertices and in the function iterate through the map to check for pairs?

  8. #8
    Senior Member OpenGL Pro
    Join Date
    Jan 2012
    Location
    Australia
    Posts
    1,098
    Basically yes.

    The key is the key for the hash. std::map has an insert with a key and a data structure pair and a retrieve with a find with key as input.

    Here your data will be vertex and the position in the vertex buffer to put this vertex.

    Once you have passed all the faces, you build the vertex buffer by iterating through the vertex list and copy the vertex into its correct position.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •