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
1v(x, y, z, [w])
w is optional, and default value is 1.0f. - vt introduces texture coordinates (UV coordinates) of one vertex. vt is defined as
1vt(x, y, z, [w])
w is optional, and default value is 0.f. - vn introduces a normal. vn is defined as
1vn(x,y,z)
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
1f v1 v2 v3
format - Vertex Texture Coordinate Indices contain vertices, and corresponding texture coordinates.
1f v1/vt1 v2/vt2 v3/vt3
format is used - Vertex Normal Indices can contain texture coordinates or not. If texture coordinates are used faces are defined as
1f v1/vt1/vn1 v2/vt2/vn2 v3/vt3/vn3
If they are not used,
1f v1//vn1 v2//vn2 v3//vn3
format is used.
- Vertex Indices contain only vertices, and defined in
- 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.
1mtllib filename
format is used for mtllib, and specifies the material library for the material definitions set with the
1usemtl materialName
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
- Principia Mathematica, Inc., 7 OBJ files including Stanford Bunny, Stanford Armadillo, and Blender’s Suzanne
- Stanford Bunny with just vertices, and faces
References
- Wiki Wavefront .obj file
- Object Files (.obj) -paulbourke.net
- OpenGL Programming/Modern OpenGL Tutorial Load OBJ
- Tutorial 7 : Model loading -opengl-tutorial.org
- Rodrigo Silveira, Parsing OBJ File From Blender
- Braynzar Soft, Lesson 21: Direct3D 11 Loading Static 3D Models (.obj format)
- Ohio State, Guidance to write a parser for .Obj and mtl file