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:
While in Blender it looks like this:
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");
getchar();
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 );
temp_vertices.push_back(vertex);
}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.
temp_uvs.push_back(uv);
}else if ( strcmp( lineHeader, "vn" ) == 0 ){
glm::vec3 normal;
fscanf(file, "%f %f %f\n", &normal.x, &normal.y, &normal.z );
temp_normals.push_back(normal);
}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");
fclose(file);
return false;
}
vertexIndices.push_back(vertexIndex[0]);
vertexIndices.push_back(vertexIndex[1]);
vertexIndices.push_back(vertexIndex[2]);
uvIndices .push_back(uvIndex[0]);
uvIndices .push_back(uvIndex[1]);
uvIndices .push_back(uvIndex[2]);
normalIndices.push_back(normalIndex[0]);
normalIndices.push_back(normalIndex[1]);
normalIndices.push_back(normalIndex[2]);
}else{
// 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_vertices.push_back(vertex);
out_uvs .push_back(uv);
out_normals .push_back(normal);
}
fclose(file);
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;
}else{
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
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Render video background
[appRenderer renderVideoBackground];
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
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]));
glEnableVertexAttribArray(vertexHandle);
glEnableVertexAttribArray(normalHandle);
glEnableVertexAttribArray(textureCoordHandle);
glActiveTexture(GL_TEXTURE0);
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]));
glDisableVertexAttribArray(vertexHandle);
glDisableVertexAttribArray(normalHandle);
glDisableVertexAttribArray(textureCoordHandle);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
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);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
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]);
}
V = 1 - V
. It is very common thing that UV gets vertically flipped in different programs/engines. \$\endgroup\$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\$