Simple .obj (Object) file parser in Kotlin
Tags: opengles2, devlog, android, vrgame
Published: 2020-07-30 20:53:58

#.obj file basic
An .obj file is one of many ways to represent the data for a 3D model.
Full details of the file contents can be found elsewhere but for our purposes we are looking for lines which start with:
* “v “ - these lines contain the data for the vertices. 
* “vn“ - these lines contain the data for the normals (we haven’t used these yet but they are required for applying lighting and shading)
* “vt” - these lines contain the uv coordinates used to map textures to the model
* “f “ - these lines define the faces, each line links the vertices, uv coordinates and normals for the 3 point of one triangle. 

# Parsing the file
I Opened Blender and generated a simple square to demonstrate the parser
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/a48vd9cz494ubmmat8o3.PNG)
I then selected File->Export->Wavefront (.obj).
I named the file square.obj and ensured that the export settings were as follows:
![Blender Export Settings](https://dev-to-uploads.s3.amazonaws.com/i/1mviq9ya1zxgyc4gc8ys.PNG)
Opening the generated file in notepad++ shows the contents of the file, you can see the 4 different line prefixes which I mentioned above v, vn, vt and f.
![Sample Object File](https://dev-to-uploads.s3.amazonaws.com/i/umnficnx4dijql454oi9.PNG)
To parse this file I used a <code>BufferedReader</code> to read each line with the <code>forEachLine</code> function, this allows us to access each line in turn using the <code>it</code> reference.

# “v “ - Vertices
Parsing the vertices is done by splitting the line each time a space is encountered, Kotlin makes this easy for us by providing the <code>split</code> function.
This function returns a <code>List<String></code> of 4 strings, the first being the "v " prefix and the other 3 being the x, y and z components of the vertex.
I generate a <code>Triple</code> from the x, y, z Float values and add them to an <code>ArrayList</code> of vertices.

![Code for parsing Vertices](https://dev-to-uploads.s3.amazonaws.com/i/ltb0dkwpqgh3op7ha405.PNG)

# “vn“ - Normals
These lines are optional and when present are parsed in much the same way as the vertices.

![Code for parsing Normals](https://dev-to-uploads.s3.amazonaws.com/i/x2b3khqilv00xiar1a6x.PNG)

# “vt” - UV Coordinates
These lines are also optional and again are parsed in much the same way as the others except the UV coordinates are stored in an <code>ArrayList</code> of <code>Pair</code>s rather than a <code>Triple</code>s.

![Code for parsing UVs](https://dev-to-uploads.s3.amazonaws.com/i/mh3gysg6b7a90jl0qyr1.PNG)

# “f “ - Faces
These lines are different from the rest, they are the glue that sticks the whole file together.
First I split the string using " " as a delimiter, each of the 3 data strings in the returned <code>List<String></code> represent a single point in the a triangle.
Then I split the string using "/" this generates a <code>List<String></code> of up to 3 integer values indexing the vertex, the optional uv and the optional normal.
It should be noted at this point that the indices start at 1 not 0.
The resulting 3 sets of 3 indices are then used to generate 3 <code>Triple</code>s each representing a point of the triangle.

![Generating the vertices, uvs and normals](https://dev-to-uploads.s3.amazonaws.com/i/sxg199tod8d7q8w60mhh.PNG)

The final step is to generate our final list of vertices, uvs, normals and indices from this information, to do this I use a variable <code>nextIndex</code> and a <code>HashMap<Triple<Int, Int?, Int?>, Short></code> called <code>faceMap</code>, HashMaps have a look up time complexity of O(1) which is crucial to parsing larger object files in a reasonable time.
* <code>nextIndex</code> tracks the next index to be placed in the final list of indices.
* <code>faceMap</code> holds all the vertex/uv/normal <code>Triple</code>s that have already been added to the final lists of vertices, UVs and normals as the keys and the associated index as the value.

If any of the vertex/uv/normal <code>Triple</code>s are already in the <code>faceMap</code> then the index value is added to the indices final list.

![checking duplicate](https://dev-to-uploads.s3.amazonaws.com/i/em62mdzjwt6nj0sk7qdy.PNG)

If not, the <code>faceMap</code> is updated with the vertex/uv/normal <code>Triple</code> and <code>nextIndex</code>, the relevant vertex, uv and normal values are added to the final lists and finally the <code>nextIndex</code> is added to the indices final list and incremented.

![Adding a new index](https://dev-to-uploads.s3.amazonaws.com/i/902unenij5ic2ew2g32s.PNG)

# Running through the process by hand
Using our square.obj file as an example lets go through the process.
Once the vertices, normals and uvs have been processed we will have the following 3 lists:
* Vertices: (0.000000, 1.000000, 1.000000), (-0.000000, -1.000000, 1.000000), (0.000000, 1.000000, -1.000000), (-0.000000, -1.000000, -1.000000)
* UVs: (1.0000, 1.0000), (0.0000, 0.0000), (1.0000, 0.0000), (0.0000, 1.0000)
* Normals: (1.0000, -0.0000, 0.0000)

Going through the faces:
The First vertex/uv/normal triple would be (2, 1, 1), this is not in the <code>faceMap</code> as the <code>faceMap</code> is empty so we add ((2, 1, 1), 0) to the <code>faceMap</code>, then add the 2nd vertex to the final vertices list, the 1st UV to the final UVs list and the 1st Normal to the final normals list and the value of <code>nextIndex</code> to the final indices list before incrementing it, giving the variables at this stage as:
* Final Vertices: **(-0.000000, -1.000000, 1.000000)**
* Final UVs: **(1.0000, 1.0000)**
* Final Normals: **(1.0000, -0.0000, 0.0000)**
* Final Indices: **0**
* <code>faceMap</code>: **((2, 1, 1), 0)**
* <code>nextIndex</code>: **1**

The Second vertex/uv/normal triple would be (3, 2, 1), this is not in the <code>faceMap</code> so we add ((3, 2, 1), 1) to the <code>faceMap</code>, then add the 3rd vertex to the final vertices list, the 2nd UV to the final UVs list and the 1st Normal to the final normals list and the value of <code>nextIndex</code> to the final indices list before incrementing it, giving the variables at this stage as:
* Final Vertices: (-0.000000, -1.000000, 1.000000), **(-0.000000, 1.000000, -1.000000)**
* Final UVs: (1.0000, 1.0000), **(0.0000, 0.0000)**
* Final Normals: (1.0000, -0.0000, 0.0000), **(1.0000, -0.0000, 0.0000)**
* Final Indices: 0, **1**
* <code>faceMap</code>: ((2, 1, 1), 0), **((3, 2, 1), 1)**
* <code>nextIndex</code>: **2**

The Third vertex/uv/normal triple would be (1, 3, 1), this is not in the <code>faceMap</code> so we add ((1, 3, 1), 2) to the <code>faceMap</code>, then add the 1st vertex to the final vertices list, the 3rd UV to the final UVs list and the 1st Normal to the final normals list and the value of <code>nextIndex</code> to the final indices list before incrementing it, giving the variables at this stage as:
* Final Vertices: (-0.000000, -1.000000, 1.000000), (-0.000000, 1.000000, -1.000000), **(-0.000000, 1.000000, 1.000000)**
* Final UVs: (1.0000, 1.0000), (0.0000, 0.0000), **(1.0000, 0.0000)**
* Final Normals: (1.0000, -0.0000, 0.0000), (1.0000, -0.0000, 0.0000), **(1.0000, -0.0000, 0.0000)**
* Final Indices: 0, 1, **2**
* <code>faceMap</code>: ((2, 1, 1), 0), ((3, 2, 1), 1), **((1, 3, 1), 2)**
* <code>nextIndex</code>: **3**

The Fourth vertex/uv/normal triple would be (2, 1, 1), this is in the <code>faceMap</code> so we add just add the value associated with (2, 1, 1) to the final indices which is 0:
* Final Vertices: (-0.000000, -1.000000, 1.000000), (-0.000000, 1.000000, -1.000000), (-0.000000, 1.000000, 1.000000)
* Final UVs: (1.0000, 1.0000), (0.0000, 0.0000), (1.0000, 0.0000)
* Final Normals: (1.0000, -0.0000, 0.0000), (1.0000, -0.0000, 0.0000), (1.0000, -0.0000, 0.0000)
* Final Indices: 0, 1, 2, **0**
* <code>faceMap</code>: ((2, 1, 1), 0), ((3, 2, 1), 1), ((1, 3, 1), 2)
* <code>nextIndex</code>: 3

The Fifth vertex/uv/normal triple would be (4, 4, 1), this is not in the <code>faceMap</code> so we add ((4, 4, 1), 3) to the <code>faceMap</code>, then add the 4th vertex to the final vertices list, the 4th UV to the final UVs list and the 1st Normal to the final normals list and the value of <code>nextIndex</code> to the final indices list before incrementing it, giving the variables at this stage as:
* Final Vertices: (-0.000000, -1.000000, 1.000000), (-0.000000, 1.000000, -1.000000), (-0.000000, 1.000000, 1.000000), **(-0.000000, -1.000000, -1.000000)**
* Final UVs: (1.0000, 1.0000), (0.0000, 0.0000), (1.0000, 0.0000), **(0.0000, 1.0000)**
* Final Normals: (1.0000, -0.0000, 0.0000), (1.0000, -0.0000, 0.0000), (1.0000, -0.0000, 0.0000), **(1.0000, -0.0000, 0.0000)**
* Final Indices: 0, 1, 2, 0, **3**
* <code>faceMap</code>: ((2, 1, 1), 0), ((3, 2, 1), 1), ((1, 3, 1), 2), **((4, 4, 1), 3)**
* <code>nextIndex</code>: 4

The Sixth vertex/uv/normal triple would be (3, 2, 1), this is in the <code>faceMap</code> so we add just add the value associated with (3, 2, 1) to the final indices which is 1:
* Final Vertices: (-0.000000, -1.000000, 1.000000), (-0.000000, 1.000000, -1.000000), (-0.000000, 1.000000, 1.000000), (-0.000000, -1.000000, -1.000000)
* Final UVs: (1.0000, 1.0000), (0.0000, 0.0000), (1.0000, 0.0000), (0.0000, 1.0000)
* Final Normals: (1.0000, -0.0000, 0.0000), (1.0000, -0.0000, 0.0000), (1.0000, -0.0000, 0.0000), (1.0000, -0.0000, 0.0000)
* Final Indices: 0, 1, 2, 0, 3, **1**
* <code>faceMap</code>: ((2, 1, 1), 0), ((3, 2, 1), 1), ((1, 3, 1), 2), ((4, 4, 1), 3)
* <code>nextIndex</code>: 4

This is all of the faces described in the square.obj file therefore our final vertices, UVs, normals and indices are:
* Final Vertices: (-0.000000, -1.000000, 1.000000), (-0.000000, 1.000000, -1.000000), (-0.000000, 1.000000, 1.000000), (-0.000000, -1.000000, -1.000000)
* Final UVs: (1.0000, 1.0000), (0.0000, 0.0000), (1.0000, 0.0000), (0.0000, 1.0000)
* Final Normals: (1.0000, -0.0000, 0.0000), (1.0000, -0.0000, 0.0000), (1.0000, -0.0000, 0.0000), (1.0000, -0.0000, 0.0000)
* Final Indices: 0, 1, 2, 0, 3, 1

# Conclusion
I hope this blogpost has explained the basics of an object file and how one is parsed, this has been a fun class to work on and the source code can be found [here](https://github.com/longevity-software/AndroidVrGameDemo/blob/master/app/src/main/java/longevity/software/vrgamedemo/ObjectFileParser.kt).
With this class and a <code>GenericGameObject</code> class I can now generate 3D models in Blender and easily import them into my game as assets, until next time.