obj.[ch]

obj.[ch] loads, manipulates, renders, and stores 3D geometry using the Wavefront OBJ file format.

Geometry is represented as a set of files. A file consists of sets of materials, vertices, and surfaces. A surface consists of sets of polygons and lines with a reference to a material in the same file to be used when rendering them. A polygon is a triplet of references to vertices in the file, and a line is a pair of references to vertices.

Each of these objects may be referenced by application code via an integer index. This works much like a file or socket descriptor. The internal geometry representation is not accessible to the application. All operations on geometry are performed using the API documented here.

Compilation

To use this module, simply link it with your own code. It requires OpenGL, GLU, JPEG, and PNG libraries to be present:

cc -o program program.c obj.c -ljpeg -lpng -lz -lGL -lGLU -lm

If JPEG or PNG support is not required, the dependancies may be eliminated by defining the symbols CONF_NO_JPG or CONF_NO_PNG:

cc -DCONF_NO_PNG -DCONF_NO_JPG -o program program.c obj.c -lGL -lGLU -lm

Alternatively, this module may be stored in a library for easy reuse:

cc -c obj.c
ar rc libobj.a obj.o

Then when compiling your own code, specify the location of libobj.a using the compiler's -L option, and the location of obj.h using the -I option.

Quickstart

These code fragments implement the common case of loading and displaying a model stored in an OBJ file. First, an integer descriptor is declared.

int fi;

During initialization, the OBJ file and all of its MTL files and texture images are read and the resulting descriptor is stored.

fi = obj_add_file("model.obj");

During rendering, the model is drawn.

obj_draw_file(fi);

The OBJ API

Object Creation

int obj_add_file(const char *filename)

Create a new file object, load the OBJ file named by filename, and return a file index. If filename is NULL then an empty file is returned.

int obj_add_mtrl(int fi)

Add a new material to file fi, return a material index. The new material is initialized with a diffuse color of (0.8, 0.8, 0.8, 1.0), an ambient color of (0.2, 0.2, 0.2, 1.0), an emissive color of (0.0, 0.0, 0.0, 0.0), a specular color of (0.0, 0.0, 0.0, 0.0), and a specular exponent of 8.0.

int obj_add_vert(int fi)

Add a new vertex to file fi, returning a vertex index. The new vertex position, normal, and texture coordinate are initialized to zero.

int obj_add_surf(int fi)

Add a new surface to file fi, returning a surface index. The new surface is initialized to reference material zero. If no materials are defined when this surface is rendered, a default material is applied.

int obj_add_poly(int fi, int si)

Add a new 3-sided polygon to surface si of file fi, returning a polygon index. The new polygon's vertex indices are initialized to zero.

int obj_add_line(int fi, int si)

Add a new line to surface si of file fi, returning a line index. The new line's vertex indices are initialized to zero.

Object Counters

int obj_num_file(void)

Return the number of existing file objects. All indices less than this number are valid file indices.

int obj_num_mtrl(int fi)
int obj_num_vert(int fi)
int obj_num_surf(int fi)

Return the number of materials, vertices, and surfaces contained by file fi. All indices less than this number are valid indices into that file.

int obj_num_poly(int fi, int si)
int obj_num_line(int fi, int si)

Return the number of polygons and lines contained by surface si of file fi. All indices less than this number are valid indices into that surface.

Object deletion

void obj_del_mtrl(int fi, int mi)

Remove material mi from file fi. Any surfaces in file fi that reference this material will also be removed. All higher-indexed materials are shifted down. Surfaces that refer to higher-indexed materials have their material indices decremented.

void obj_del_vert(int fi, int vi)

Remove vertex vi from file fi. Any polygons or lines referencing this vertex in any surface of file fi are also removed. Higher-indexed vertices are shifted down. Polygons and lines that refer to higher-index materials have their vertex indices decremented.

void obj_del_poly(int fi, int si, int pi)

Remove polygon pi from surface si of file fi. Higher-indexed polygons are shifted down. Vertices referenced by polygon pi are not removed.

void obj_del_line(int fi, int si, int li)

Remove line li from surface si of file fi. Higher-indexed lines are shifted down. Vertices referenced by line li are not removed.

void obj_del_surf(int fi, int si)

Remove surface si from file fi. All polygons and lines contained in this surface are also removed. Vertices in file fi referenced by these polygons and lines are not removed. Higher-indexed surfaces are shifted down.

void obj_del_file(int fi)

Remove file fi. Higher-indexed files are shifted down.

Object Manipulators

void obj_set_mtrl_name(int fi, int mi, const char *name)

Set the name of material mi in file fi. This name is the means by which materials defined in an MTL file are referenced from within an OBJ file. Materials without assigned names are referenced as “default.” If materials are not assigned unique names, then then the surface-material mapping is not guaranteed to be preserved when a file is written.

void obj_set_mtrl_map(int fi, int mi, int ki, const char *image)

Set the image map to be used for diffuse color, ambient color, emissive color, specular color, or specular exponent of material mi of file fi. The named file must be of one of the supported image formats, usually PNG or JPEG depending on the configuration options specified at compile time.

The ki argument selects the property to be set. It must be one of the following:

void obj_set_mtrl_opt(int fi, int mi, int ki, unsigned int opt)

Set the options on property ki of material mi in file fi.

The opt argument gives a bitmap of options to be enabled. The following options are defined. Yeah, there's only one right now.

  • OBJ_OPT_CLAMP

    Clamp the corresponding property map. The default is off, and the image map is wrapped.

void obj_set_mtrl_c(int fi, int mi, int ki, const float c[4])

Set the diffuse color, ambient color, emissive color, specular color, or specular exponent of material mi of file fi. The ki argument selects the property to be set. Note that the MTL file format supports RGBA diffuse color, but only RGB ambient, and specular colors. So while ambient and specular alpha values will be rendered normally, they cannot be stored in an MTL file, and will always default to 1.0 when a material is loaded. The specular exponent is stored and applied as a scalar.

void obj_set_mtrl_o(int fi, int mi, int ki, const float o[3])

Set the texture coordinate offset for property map ki of material mi in file fi.

void obj_set_mtrl_s(int fi, int mi, int ki, const float s[3])

Set the texture coordinate scale for property map ki of material mi in file fi.

void obj_set_vert_v(int fi, int vi, const float v[3])
void obj_set_vert_t(int fi, int vi, const float t[2])
void obj_set_vert_n(int fi, int vi, const float n[3])

Set the position, texture coordinate, or normal vector of vertex vi of file fi.

void obj_set_poly(int fi, int si, int pi, const int vi[3])

Set the triplet of vertex indices defining polygon pi in surface si of file fi.

void obj_set_line(int fi, int si, int li, const int vi[2])

Set the pair of vertex indices defining line li in surface si of file fi.

void obj_set_surf(int fi, int si, int mi)

Set the material index of surface si of file fi

Object Query

const char *obj_get_mtrl_name(int fi, int mi)

Return the name of material mi of file fi.

unsigned int obj_get_mtrl_map(int fi, int mi, int ki)

Return the property map ki of material mi of file fi. The returned value is an OpenGL texture object that may be manipulated normally using the OpenGL API.

unsigned int obj_get_mtrl_opt(int fi, int mi, int ki)

Return the bitmap of options set on the property map ki of material mi of file fi.

void obj_get_mtrl_c(int fi, int mi, int ki, float c[4])

Return the color of property ki of material mi in file fi.

void obj_get_mtrl_o(int fi, int mi, int ki, float o[3])

Return the texture coordinate offset of property map ki of material mi in file fi.

void obj_get_mtrl_s(int fi, int mi, int ki, float s[3])

Return the texture coordinate scale of property map ki of material mi in file fi.

void obj_get_vert_v(int fi, int vi, float v[3])
void obj_get_vert_t(int fi, int vi, float t[2])
void obj_get_vert_n(int fi, int vi, float n[3])

Return the position, texture coordinate, or normal vector of vertex vi of file fi.

void obj_get_poly(int fi, int si, int pi, int vi[3])

Return the triplet of indices defining polygon pi in surface si of file fi.

void obj_get_line(int fi, int si, int li, int vi[2])

Return the pair of indices defining line li in surface si of file fi.

int obj_get_surf(int fi, int si)

Return the material index of surface si of file fi.

Object I/O

Processing

void obj_proc_file(int fi)

Process file fi for rendering. All normal vectors are normalized and a tangent vector is computed for each vertex using its normal vector and texture coordinate. Surfaces are sorted in order of increasing transparency in order to ensure correct blend order.

float obj_acmr_file(int fi, int qc)

Compute the average cache miss ratio for file fi using a cache size of qc. The ACMR gives a measure of the effective geometry complexity of a model. It is the average number of vertices processed per triangle, taking into account post-transform vertex caching. In the worst case scenario, an unoptimized model will have an ACMR of 3. A well-optimized, well-behaved model can have an ACMR as low as 0.6.

void obj_sort_file(int fi, int qc)

Sort the triangles of file fi in an attempt to reduce the model's average cache miss ratio, as rendered using a vertex cache of size qc. A sorted model may be written to a file and will remain optimized when subsequently read.

Proper selection of qc is crucial. Overestimating the cache size will result in bad performance. It is safe to assume a cache size of 16. Recent video hardware provides cache sizes up to 32. Average-case analysis indicates thet future video hardware is unlikely to increase cache size far beyond 32.

Optimal sorting is NP-complete. This implementation is fast (linear in the number of triangles) but not optimal. There is no guarantee that a sorted model will have a lower ACMR than the original unsorted model. Paranoid applications should confirm that sorting reduces the ACMR and reload the model if it does not.

Rendering

void obj_draw_file(int fi)

Draw file fi. All polygons and lines of all surfaces are rendered using their assigned materials. Aside from materials and textures, no OpenGL state is modified. In particular, bound vertex and fragment shaders execute as expected.

Tangent vectors are bound to vertex attribute location 6. Applications may take advantage of them by binding this location to a GLSL attribute like so:

glBindAttribLocationARB(program, 6, "tangent");
void obj_draw_vert(int fi)

Bind the vertex buffer of file fi. If the local OpenGL implementation supports the GL_ARB_vertex_buffer_object extension then it is used, otherwise the buffer is mapped as a vertex array. The application may then render selected vertices using glDrawElements, glDrawArrays, etc.

void obj_draw_mtrl(int fi, int mi)

Apply material mi of file fi. Any rendering subsequently performed by the application will use this material.

void obj_draw_surf(int fi, int si)

Draw all polygons and lines in surface si of file fi. Rendering will use any material and vertex buffer previously set by the application. It is recommended that applications bind the file's own vertex buffer using obj_draw_vert prior to rendering a surface in that file.

void obj_draw_axes(int fi, float length)

Draw the tangent-space basis vectors (normal, tangent, and bitangent) of all vertices of file fi.

Exporting

void obj_write_file(int fi, const char *obj, const char *mtl)

Write all geometry of file fi to a file named by obj. Write all materials of file fi to a file named by mtl. If either file name argument is NULL then no file is written.

Note: if geometry is read from one file and written to another then there is no guarantee that the source and destination files be identical. Shared normals and texture coordinates are duplicated per vertex. The number of position, normal, and texture coordinate specifications equal the number of vertices in the file.

All face specifications are of the form “f i/i/i j/j/j k/k/k” for some vertex indices i, j, k. All line specifications are of the form “l i/i j/j” for some vertex indices i, j. All vertex indices are positive, counting from the beginning of the file.

Any groups specified in the source OBJ are discarded, and the output OBJ is organized by material. Unused specifications in OBJ and MTL files (curves, merging groups, etc) are omitted. Smoothing groups are omitted, and the computed normals for each vertex are inserted instead. All comments are stripped.

rlk (at) evl.uic.edu