Working with OBJ Files

Object files define the geometry and other properties for objects. They are simple, common, and a good starting point for doing complex stuff in the future. Working with a basic OBJ loader may be enough for simple projects, but in real life, well written libraries are needed.

When an OBJ file is read, it is read line by line, and looked for the elements such as v, vt, vn. These elements are put into different arrays for using during the rendering.

OBJ files can be in both ASCII (with .obj extension), and binary (with .mod extension) formats, and support both polygonal objects, and free-form objects. Polygonal geometry uses points, lines, faces to define an object. Free-form geometry uses curves, and surfaces.

For referencing the materials of the objects, MTL files are used. Material information is stored in .mtl files. More than one external MTL file can be referenced from within the OBJ file. MTL material file may contain one or more named material definition. Things like Kd, Ks, Ka, ambient color map, diffuse color map come with MTL file.

Basic Elements of an OBJ File

  • # is used for comments.
  • o introduces a new object. Objects are a group of groups (g). For example, a town, where we may have a couple of houses, would be considered as objects (o), and each house contains groups (g) like windows, doors, walls, roof etc.
  • g specifies groups. Group is a set of faces that all use the same attributes like the same material.
  • v introduces a vertex. v is defined as

    w is optional, and default value is 1.0f.
  • vt introduces texture coordinates (UV coordinates) of one vertex. vt is defined as

    w is optional, and default value is 0.f.
  • vn introduces a normal. vn is defined as

    vn may not always be unit vector.
  • f introduces  a face. Faces are defined using list of vertex, texture, and normal indices in v/vt/vn format.

Indices always start from 1, and match the corresponding vertex element of a previously defined vertex/texture/normal list. Each face can contain 3 or more elements. White space could not be used before or after the slash.

There are 3 different ways to define a face:

    • Vertex Indices contain only vertices, and defined in

      format
    • Vertex Texture Coordinate Indices contain vertices, and corresponding texture coordinates.

      format is used
    • Vertex Normal Indices can contain texture coordinates or not. If texture coordinates are used faces are defined as

      If they are not used,

      format is used.
  • vp element is used for parameter space vertices  which define free-form geometry. Details can be found on Wiki.
  • usemtl and mtllib are used for referencing to materials.

    format is used for mtllib, and specifies the material library for the material definitions set with the

All these elements should be handled within a complete OBJ file reader.

OBJ Loader

Writing an obj parser is not that hard, but there are so many cases to handle, and lots of stuff to write that takes some time.

Before starting to write, we need some basic classes. First of all, we need a 3D vector class for saving the components of vertices, normals, and UV coordinates. Classes for saving the faces, and materials in .mtl file. And finally, a class to save whole scene that saves a list of vertex, face, materials etc.

Algorith:

  • Open the file
  • Read the file line by line until reach EOF
    • Get the first token (element of OBJ)
    • Compare the token with OBJ elements such as #, v, vt, vn, f, and parse with related code
    • Add the parsing result to the related list
  • Save the completed lists into the scene

The memory needed cannot be known beforehand. So, for the lists we keep, dynamically growing buffer should be used or a large buffer should be allocated before parsing.

Implementation Notes

Writing OBJ reader is a struggle because, you have to write a simple piece of code for every element of both OBJ and MTL files. It may take your whole day not because it is hard to code but, you have to code around 500 lines with lots of cases.

Dividing OBJ, and MTL reading into two different methods will increase the readability.

Handling face (f) element may be the hardest part of coding because you should handle different kind of notions, but it has a simple logic.

After you read the whole vertex, normal, texture, and material information, you should put  vertex, normal, and texture information into a list with respect to their index order, and indices, and materials into another one.

OBJ Files on the net

References