1

I have written a simple obj parser in c++ that loads the vertices, indices and texture coordinates (that's all the data I need).

Here is the function:

Model* ModelLoader::loadFromOBJ(string objFile, ShaderProgram *shader, GLuint texture)
    {
        fstream file;

        file.open(objFile);
        if (!file.is_open())
        {
            cout << "ModelLoader: " << objFile << " was not found";
            return NULL;
        }

        int vertexCount = 0;
        int indexCount = 0;
        vector<Vector3> vertices;
        vector<Vector2> textureCoordinates;
        vector<Vector2> textureCoordinatesFinal;
        vector<unsigned int> vertexIndices;
        vector<unsigned int> textureIndices;

        string line;
        while (getline(file, line))
        {
            vector<string> splitLine = Common::splitString(line, ' ');

            // v - vertex
            if (splitLine[0] == "v")
            {
                Vector3 vertex(stof(splitLine[1]), stof(splitLine[2]), stof(splitLine[3]));
                vertices.push_back(vertex);
                vertexCount++;
            }
            // vt - texture coordinate
            else if (splitLine[0] == "vt")
            {
                Vector2 textureCoordinate(stof(splitLine[1]), 1 - stof(splitLine[2]));
                textureCoordinates.push_back(textureCoordinate);
            }
            // f - face
            else if (splitLine[0] == "f")
            {
                vector<string> faceSplit1 = Common::splitString(splitLine[1], '/');
                vector<string> faceSplit2 = Common::splitString(splitLine[2], '/');
                vector<string> faceSplit3 = Common::splitString(splitLine[3], '/');

                unsigned int vi1 = stoi(faceSplit1[0]) - 1;
                unsigned int vi2 = stoi(faceSplit2[0]) - 1;
                unsigned int vi3 = stoi(faceSplit3[0]) - 1;
                unsigned int ti1 = stoi(faceSplit1[1]) - 1;
                unsigned int ti2 = stoi(faceSplit2[1]) - 1;
                unsigned int ti3 = stoi(faceSplit3[1]) - 1;

                vertexIndices.push_back(vi1);
                vertexIndices.push_back(vi2);
                vertexIndices.push_back(vi3);
                textureIndices.push_back(ti1);
                textureIndices.push_back(ti2);
                textureIndices.push_back(ti3);

                indexCount += 3;
            }
        }

        // rearanging textureCoordinates into textureCoordinatesFinal based on textureIndices
        for (int i = 0; i < indexCount; i++)
            textureCoordinatesFinal.push_back(textureCoordinates[textureIndices[i]]);

        Model *result = new Model(shader, vertexCount, &vertices[0], NULL, texture, indexCount, &textureCoordinatesFinal[0], &vertexIndices[0]);
        models.push_back(result);

        return result;
    }

As you can see, I take into account the 1 - texCoord.y (because blender and opengl use a different coordinate system for textures). I also arrange the texture coordinates based on the texture indices after the while loop.

However, the models I try to render have their textures messed up. Here is an example:

Texture messed up
Texture messed up

I even tried it with a single cube which I unwrapped myself in blender and applied a very simple brick texture. In 1 or 2 faces, the texture was fine and working, then in some other faces, 1 of the tringles had a correct texture and the others appeared streched out (same as in the picture above).

genpfault
  • 51,148
  • 11
  • 85
  • 139
456panos
  • 55
  • 2

1 Answers1

0

To define a mesh, there is only one index list that indexes the vertex attributes. The vertex attributes (in your case the vertices and the texture coordinate) form a record set, which is referred by these indices.

This causes, that each vertex coordinate may occur several times in the list and each texture coordinate may occur several times in the list. But each combination of vertices and texture coordinates is unique.

Take the vertexIndices and textureIndices an create unique pairs of vertices and texture coordinates (verticesFinal, textureCoordinatesFinal).
Create new attribute_indices, which indexes the pairs.
Use the a temporary container attribute_pairs to manage the unique pairs and to identify their indices:

#include <vector>
#include <map>

// input
std::vector<Vector3> vertices;
std::vector<Vector2> textureCoordinates;
std::vector<unsigned int> vertexIndices;
std::vector<unsigned int> textureIndices;

std::vector<unsigned int> attribute_indices;  // final indices
std::vector<Vector3> verticesFinal;           // final vertices buffer
std::vector<Vector2> textureCoordinatesFinal; // final texture coordinate buffer 

// map a pair of indices to the final attribute index
std::map<std::pair<unsigned int, unsigned int>, unsigned int> attribute_pairs; 

// vertexIndices.size() == textureIndices.size()
for ( size_t i = 0; i < vertexIndices.size(); ++ i )
{
    // pair of vertex index an texture index
    auto attr = std::make_pair( vertexIndices[i], textureIndices[i] );

    // check if the pair aready is a member of "attribute_pairs"
    auto attr_it = attribute_pairs.find( attr );

    if ( attr_it == attribute_pairs.end() )
    {
        // "attr" is a new pair

        // add the attributes to the final buffers
        verticesFinal.push_back( vertices[attr.first] );
        textureCoordinatesFinal.push_back( textureCoordinates[attr.first] );

        // the new final index is the next index
        unsigned int new_index = (unsigned int)attribute_pairs.size();
        attribute_indices.push_back( new_index );

        // add the new map entry 
        attribute_pairs[attr] = new_index;
    }
    else
    {
        // the pair "attr" already exists: add the index which was found in the map
        attribute_indices.push_back( attr_it->second );
    }
} 

Note the number of the vertex coordinates (verticesFinal.size()) is equal the number of the texture coordiantes (textureCoordinatesFinal.size()). But the number of the indices (attribute_indices.size()) is something completely different.

// verticesFinal.size() == textureCoordinatesFinal.size()
Model *result = new Model(
    shader, 
    verticesFinal.size(),
    verticesFinal.data(),
    NULL, texture,
    attribute_indices.size(),
    textureCoordinatesFinal.data(),
    attribute_indices.data() );
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • Thank you so much, I don't know how I mixed it up so much in my head that I couldn't understand that I'm not re-ordering the textureCoordinates array correctly. – 456panos Feb 24 '18 at 19:19