Reading an .obj file in Processing

One of the most use­ful skills in cre­ative cod­ing is read­ing all kinds of data and files. And read­ing Wave­front .obj files in Pro­cess­ing is def­i­nite­ly one of the more fun appli­ca­tions. There are sev­er­al libraries that will read .obj files but writ­ing one for our­selves is a good exer­cise. Espe­cial­ly since we will have more con­trol over what we want to do.

File format

Wave­front .obj files are plain text files describ­ing poly­gon mesh­es. The link above describes the entire for­mat com­pre­hen­sive­ly. But that’s not what we need right now. Let’s start with an exam­ple, a sim­ple pyra­mid (down­load).

The exam­ple file con­tains every­thing need­ed to draw the pyra­mid: the posi­tions of the 5 cor­ners, the ver­tices, and how the cor­ners are con­nect­ed, the faces. There are three dif­fer­ent kinds of lines in this exam­ple file. The first type is a com­ment line, start­ing with #. The oth­er two types describe the essen­tial ele­ments of a mesh: ver­tices and faces.

Lines start­ing with v give the posi­tion of a point on the mesh. The order in which the ver­tices are giv­en defines an index. The first ver­tex index is “1”. Faces, the lines start­ing with f, are defined by the indices of the ver­tices they con­nect in some con­sis­tent order. Sev­er­al faces can share the same ver­tex. Obj files sup­port faces with 3 ver­tices, tri­an­gles, and faces with 4 ver­tices, quads. Most files will be tri­an­gle-based.

The pyra­mid file con­tains five ver­tices, one top ver­tex 1, and four base ver­tices, 2 to 5. The four sides of the pyra­mid con­nect the top “1” with each of the lines at the base, “2 3”, “3 4”, “4 5” and “5 1”. The bot­tom of the pyra­mid is a square “5 4 3 2”, in the file giv­en as two tri­an­gles “5 4 3” and “5 3 2”. Don’t wor­ry about the order, we’re read­ing files, not writ­ing them (yet).

Reading .obj files in Processing

Since these files are text files, Pro­cess­ing has lit­tle prob­lems read­ing them, all at once (ref­er­ence) or line by line (ref­er­ence). This is my pre­ferred way (down­load):

This reads the file but does noth­ing with it except println(line). Han­dling each line requires us to do three things:

  1. Deter­mine the type of the line
  2. If not a com­ment line, read the ver­tex or face data
  3. Add the ver­tex or face to our col­lec­tion

Deter­min­ing the type is a mat­ter of look­ing at the first part of the line and see if it’s a ver­tex, a face or anoth­er type. Look­ing at the first char­ac­ter only would­n’t be suf­fi­cient for arbi­trary .obj files. The full spec includes the types “vn” and “vt”, which would be indis­tin­guish­able from “v”, but we’re not going to use those. Instead, we’re using Pro­cess­ing’s splitTokens() func­tion­al­i­ty, cut­ting each line in parts sep­a­rat­ed by white­space (ref­er­ence).

How to store the ver­tices and the faces? Each ver­tex has three coor­di­nates and a face has three or four indices. We cre­ate two small class­es to hold the data.

Also, we don’t know before­hand how many there will be. We’ll be using two ArrayList to store the ver­tices and faces while the file is read. Read­ing the ver­tex infor­ma­tion is straight­for­ward. Each line “v x y z” has been split into 4 parts. We ignore the first part, “v”, and use float() to con­vert the text string to a float­ing point num­ber (ref­er­ence).

The face data requires an addi­tion­al step. In our exam­ple file, all faces are defined as “f index1 index2 index3”. In gen­er­al, a face line in an .obj file can hold three indices, “vertexIndex1/​textureIndex1/​normalIndex1”. We only need the first one. splitTokens() to the res­cue, this time using “/​” to split each part of the string into fur­ther chunks.

Addi­tion­al­ly, we need to cor­rect the ver­tex index we read from the file. The first index in a Wave­front .obj file is 1. This intu­itive, dai­ly life way of count­ing is called one-based index­ing. That ver­tex is the first that will be added to the ArrayList. Pro­cess­ing uses zero-based index­ing, the first ele­ment is at index 0. This is very sen­si­ble from a com­put­er sci­ence point-of-view. To retrieve that first ele­ment, we write vertices.get(0). As a con­se­quence, if the file refers to index 1254, that ver­tex is stored at ArrayList index 1253. This invites such a com­mon type of error that it has it’s own Wikipedia entry. To com­pen­sate for the dif­fer­ent index­ing schemes, we sub­tract 1 from every index we read from the file.

Putting it all together

Last task: get Pro­cess­ing to draw the faces of the mesh we loaded. For con­ve­nience, we add draw­ing func­tions to Ver­tex en Face. Putting every­thing togeth­er (down­load):

Processing sketch drawing a pyramid