So I am creating an AR app using Vuforia, and I have found a way to read obj files and index them. But now that I finally got my model to show up, there must be something wrong with my UVs/texture coordinates, as I am getting this:

enter image description here

While in Blender it looks like this:

enter image description here

I think there is something wrong with the way I am indexing it, but I can't seem to figure it out. I am pretty new to all this. It could also be that I am using the texture data in the wrong way somehow.

I am using this code to load the .obj:

bool loadOBJ(
    const char * path, 
    std::vector<glm::vec3> & out_vertices, 
    std::vector<glm::vec2> & out_uvs,
    std::vector<glm::vec3> & out_normals
    printf("Loading OBJ file %s...\n", path);

    std::vector<unsigned int> vertexIndices, uvIndices, normalIndices;
    std::vector<glm::vec3> temp_vertices; 
    std::vector<glm::vec2> temp_uvs;
    std::vector<glm::vec3> temp_normals;

    FILE * file = fopen(path, "r");
    if( file == NULL ){
        printf("Impossible to open the file ! Are you in the right path ? See Tutorial 1 for details\n");
        return false;

    while( 1 ){

        char lineHeader[128];
        // read the first word of the line
        int res = fscanf(file, "%s", lineHeader);
        if (res == EOF)
            break; // EOF = End Of File. Quit the loop.

        // else : parse lineHeader

        if ( strcmp( lineHeader, "v" ) == 0 ){
            glm::vec3 vertex;
            fscanf(file, "%f %f %f\n", &vertex.x, &vertex.y, &vertex.z );
        }else if ( strcmp( lineHeader, "vt" ) == 0 ){
            glm::vec2 uv;
            fscanf(file, "%f %f\n", &uv.x, &uv.y );
            uv.y = -uv.y; // Invert V coordinate since we will only use DDS texture, which are inverted. Remove if you want to use TGA or BMP loaders.
        }else if ( strcmp( lineHeader, "vn" ) == 0 ){
            glm::vec3 normal;
            fscanf(file, "%f %f %f\n", &normal.x, &normal.y, &normal.z );
        }else if ( strcmp( lineHeader, "f" ) == 0 ){
            std::string vertex1, vertex2, vertex3;
            unsigned int vertexIndex[3], uvIndex[3], normalIndex[3];
            int matches = fscanf(file, "%d/%d/%d %d/%d/%d %d/%d/%d\n", &vertexIndex[0], &uvIndex[0], &normalIndex[0], &vertexIndex[1], &uvIndex[1], &normalIndex[1], &vertexIndex[2], &uvIndex[2], &normalIndex[2] );
            if (matches != 9){
                printf("File can't be read by our simple parser :-( Try exporting with other options\n");
                return false;
            uvIndices    .push_back(uvIndex[0]);
            uvIndices    .push_back(uvIndex[1]);
            uvIndices    .push_back(uvIndex[2]);
            // Probably a comment, eat up the rest of the line
            char stupidBuffer[1000];
            fgets(stupidBuffer, 1000, file);


    // For each vertex of each triangle
    for( unsigned int i=0; i<vertexIndices.size(); i++ ){

        // Get the indices of its attributes
        unsigned int vertexIndex = vertexIndices[i];
        unsigned int uvIndex = uvIndices[i];
        unsigned int normalIndex = normalIndices[i];

        // Get the attributes thanks to the index
        glm::vec3 vertex = temp_vertices[ vertexIndex-1 ];
        glm::vec2 uv = temp_uvs[ uvIndex-1 ];
        glm::vec3 normal = temp_normals[ normalIndex-1 ];

        // Put the attributes in buffers
        out_uvs     .push_back(uv);
        out_normals .push_back(normal);

    printf("File can be read by our simple parser :-)\n");
    return true;

And this code to index the .obj:

void indexVBO(
    std::vector<glm::vec3> & in_vertices,
    std::vector<glm::vec2> & in_uvs,
    std::vector<glm::vec3> & in_normals,

    std::vector<unsigned int> & out_indices,
    std::vector<glm::vec3> & out_vertices,
    std::vector<glm::vec2> & out_uvs,
    std::vector<glm::vec3> & out_normals
    std::map<PackedVertex,unsigned int> VertexToOutIndex;

    // For each input vertex
    for ( unsigned int i=0; i<in_vertices.size(); i++ ){

        PackedVertex packed = {in_vertices[i], in_uvs[i], in_normals[i]};

        std::map<PackedVertex,unsigned int>::iterator it = VertexToOutIndex.find(packed);
        if ( it == VertexToOutIndex.end() ){
            out_vertices.push_back( in_vertices[i]);
            out_uvs     .push_back( in_uvs[i]);
            out_normals .push_back( in_normals[i]);
            unsigned int newindex = (unsigned int)out_vertices.size() - 1;
            out_indices .push_back( newindex );
            VertexToOutIndex[ packed ] = newindex;
            unsigned int index = it->second;
            out_indices.push_back( index );

Here is my rendering code for the model (Warning: this contains Objective-C):

// Clear colour and depth buffers

// Render video background
[appRenderer renderVideoBackground];


glVertexAttribPointer(vertexHandle, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)(&(object->vertices)[0]));
glVertexAttribPointer(normalHandle, 3, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)(&(object->normals)[0]));
glVertexAttribPointer(textureCoordHandle, 2, GL_FLOAT, GL_FALSE, 0, (const GLvoid*)(&(object->uvs)[0]));



int targetIndex = [[self.vuforiaTrackCaller textureIndexForTarget:name] intValue];

glBindTexture(GL_TEXTURE_2D, [augmentationTexture objectAtIndex:targetIndex].textureID);
glUniformMatrix4fv(mvpMatrixHandle, 1, GL_FALSE, (const GLfloat*)&modelViewProjection.data[0]);
glUniform1i(texSampler2DHandle, 0 /*GL_TEXTURE0*/);

glDrawElements(GL_TRIANGLES, object->indices.size(), GL_UNSIGNED_INT, (const GLvoid*)(&(object->indices)[0]));



And how I load the textures (I don't think the problem is here):

// Generate the OpenGL ES texture and upload the texture data for use
// when rendering the augmentation
for (int i = 0; i < [augmentationTexture count]; ++i) {
    GLuint textureID;
    glGenTextures(1, &textureID);
    [[augmentationTexture objectAtIndex:i] setTextureID:textureID];
    glBindTexture(GL_TEXTURE_2D, textureID);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, [[augmentationTexture objectAtIndex:i] width], [[augmentationTexture objectAtIndex:i] height], 0, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid*)[[augmentationTexture objectAtIndex:i] pngData]);
  • \$\begingroup\$ See if you can fix this by flipping V coordinate of UV map - V = 1 - V. It is very common thing that UV gets vertically flipped in different programs/engines. \$\endgroup\$ – Kromster says support Monica Dec 30 '16 at 10:08
  • \$\begingroup\$ Oh wow it even states that in the obj loader. I commented the line uv.y = -uv.y; // Invert V coordinate since we will only use DDS texture, which are inverted. Remove if you want to use TGA or BMP loaders. and it works. Thank you for giving me a pointer. If you want, you can answer this question for that sweet, sweet reputation :) \$\endgroup\$ – user1009013 Dec 30 '16 at 10:18
  • \$\begingroup\$ Happy to help. Posted this as answer \$\endgroup\$ – Kromster says support Monica Dec 30 '16 at 10:51

See if you can fix this by flipping V coordinate of the UV map:

 V = 1 - V

It is very common that UV gets vertically flipped in different programs/engines. Also note that different image formats (and loaders) could also store/flip images vertically.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.