//
you're reading...
Tech

Loading A .OBJ File in OpenGL

It is not impossible to write an .OBJ file reader. But it is quite difficult to write a GOOD one.

What is .OBJ file format?

This is a very comprehensive explanation of .OBJ file format. A .OBJ file is a bunch of data recording vertex coordinates, normal coordinates, texture coordinates, material, face topology, etc. It can be parsed and understood like any other plain-text files.

Basic process of parsing .OBJ files

Firstly, read in the .OBJ file like any other type of file. For example,

FILE *f;
if((f = fopen(fileName, "r"))==NULL)
{
    return ERROR;
}

// omitted ...

fclose(f);

Then try to parse the file line by line. For example,

char buffer[MAXNUM];
while(!feof(f))
{
    memset(buffer,0, MAXNUM-1);
    fgets(buffer, MAXNUM, f);
    // do the parsing here
}

We can use some basic string operations to parse each line. The nature of .OBJ file has made it quite easy – you just need to look at the head of each line, figure out whether it’s a vertex, a normal, a texture coord, etc.  and then parse the rest of it as numbers. For example,

char buffer[MAXNUM];
while(!feof(f))
{
    memset(buffer,0, MAXNUM-1);
    fgets(buffer, MAXNUM, f);

    if( strncmp("vn ", buffer, 3) == 0)
    {
        // parse the normal
    }
    else if(strncmp("vt ", buffer, 3) == 0)
    {
        // parse the texture coord
    }
    else if(strncmp("v ",buffer, 2) == 0)
    {
        // parse the vertex
    }
    else if(strncmp("f ", buffer, 2) == 0)
    {
        // parse the faces
    }
    else if(strncmp("mtllib ", buffer, 7) == 0)
    {
        // parse the mtl file name
    }
    else if(strncmp("usemtl ", buffer, 7) == 0)
    {
        // parse the material name
    }
    // more...
}

You might need to use some simple data structures to store the data. For example,

static struct Vertex{
     float x, y, z;
};

static struct Texcoor{
    float u, v;
};

static struct Normal{
    float i, j, k;
};

static struct Quad{
    int v[4], t[4], n[4];
    int mtlIndex;
};

static struct Texture{
    GLuint textIndex;
    char* textName;
    AUX_RGBImageRec* textureImage;
};

Just use  functions like sscanf to read the lines into the corresponding data structure. But the most tricky part is parsing the face lines. Because .OBJ files could be quite flexible, so flexible that there could be no normals or no textures (e.g. a flat square). So the face line is changeable. It could be

f 1/2/3 4/5/6 7/8/9

, which is complete. Or it could also be

f 1//3 4//6 7//9

, which is incomplete. And sometimes it could be

f 1/2/3 /4/5/6 7/8/9 10/11/12

which is a quad, not a triangle. My solution is treating all the faces as quads (for triangle, it’s a quad with the last two vertices identical). And I manually parse the line instead of using existing library (maybe I haven’t found a proper one).

Hopefully by so far we can read in a *skeleton* of an object. By the way, to show the *skeleton*, we could use the function

glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

What is .MTL file format?

This article gives a comprehensive explanation of .MTL file. When you are parsing .OBJ file, you would probably encounter something like mtllib and usemtl. What follows mtllib indicates the material file and usemtl precedes the kind of material to apply for the following faces.

Basic process of parsing .OBJ files

When you meet mtllib, what you need to do is quite similar to parsing .OBJ file (actually much easier): just parse the designated file and store the data in proper structures. For example,

static struct Material{
     char name[MAX_NUM_CHARS];
     float Ka[3], Kd[3], Ks[3];
     float d, Tr;
     float Ns;
     float illum;
     char* map_Kd;
};

When you meet usemtl, just mark the following faces with designated material – just keep some bits in each data unit for faces.

Creating textures for the object

One last thing is loading texture. In the .MTL file, the flag map_Kd indicates which image is used as the texture for that material. Once you know that, you can follow the routine for OpenGL texturing. What I did was creating textures upon parsing the .MTL file and indexing them. Then when applying material for each face, I also apply the texture, read from the created texture set. The following data is used to store texture.

static struct Texture{
    GLuint textIndex;
    char* textName;
    AUX_RGBImageRec* textureImage;
};

I have something to do before drawing the object, namely, initialize the texture,

bool OBJLoader::initObject()
{
  for(int i=0; i<textureCount; i++)
  {
     if(textureUnits[i].textureImage)
     {
        glGenTextures(1, &textureUnits[i].textIndex);
        glBindTexture(GL_TEXTURE_2D, textureUnits[i].textIndex);
        glTexImage2D(GL_TEXTURE_2D, 0, 3
                     , textureUnits[i].textureImage->sizeX
                     , textureUnits[i].textureImage->sizeY
                     , 0, GL_RGB, GL_UNSIGNED_BYTE
                     , textureUnits[i].textureImage->data);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
     }
  }
 return true;
}

And then when drawing the object, just do the following before going to each faces,

glEnable(GL_TEXTURE_2D);
int textIndex = findTextIndex(material[mtlIndex].map_Kd);
glBindTexture(GL_TEXTURE_2D, textIndex);

What now?

Now supposedly you already have a set of vertices, a set of normals, a set of texture coordinates, a set of faces, a set of material and a set of textures (indices and images). You can apply the OpenGL rendering routine, something like,

glBegin(GL_QUADS);
for(int i=0; i<facesCount; i++)
{
      // face 1
      // face 2
      // face 3
      // face 4
}
glEnd();

That’s more or less the process of loading a .OBJ file.

About Xiang 'Anthony' Chen

Making an Impact in Your Life

Discussion

8 thoughts on “Loading A .OBJ File in OpenGL

  1. thanks so much, man! you’ve just saved my life! now i can load my blender car with the proper material and textures without problems! 😀

    Posted by jjwade3 | May 15, 2012, 5:15 am
  2. Hi! I’m learning on this, tried to make my own code to read the obj, and it works. but i’m confuse how to draw it. could i have ur full code of this? i wanna see how ur code works, if u dont mind. waiting forward to hearing from u (u could send to my email). thanks in advance! 🙂

    Posted by Sylvi ani | September 6, 2012, 11:13 pm
    • Hi,

      This piece of code was written like 2+ years ago and I no longer work on graphics. So I couldn’t give you detailed instructions about how to use it for rendering objects. However I remember it was quite straightforward once you read the .obj file – it’s similar to rendering anything with a certain number of faces and vertices. What you need to do is assigning all vertices and faces from the .obj file to your rendered object.

      Hope that helps,
      ~Anthony

      Posted by Xiang 'Anthony' Chen | September 10, 2012, 4:28 pm
  3. Hi, do u have a simple-project to this project?

    Posted by alan | January 11, 2013, 7:49 pm

Leave a reply to jjwade3 Cancel reply