Simple object (.obj) and material (.mtl) file parser. 
Tags: android, opengles2, vrgame, devlog
Published: 2020-08-23 11:40:33

# Why object files and material files together. 
Object files and material files are both wavefront technology formats which are designed to work in combination. 
I have described the basics of object files as a standalone file [in part 8 of this series] (https://dev.to/longevitysoftware/simple-obj-object-file-parser-in-kotlin-4okb). 
When combined with material files the object file contains a line detailing the name of the material file which starts with “mtllib” 

![mtllib example](https://dev-to-uploads.s3.amazonaws.com/i/ltaqsnmm3rnd28gsf2ut.PNG)

and the faces are grouped by material, with each group starting with “usemtl” and the name of the material to use. 

![usemtl example](https://dev-to-uploads.s3.amazonaws.com/i/5rl9gx25j9vsmn4lv4wq.PNG)

# Modifications to the existing object file parser. 
The first change will be to update the <code>faceMap</code> to include a material name as well as the vertex, UV and normal indices.

![new Facemap](https://dev-to-uploads.s3.amazonaws.com/i/bxvon7vpwvi1943wv1z4.PNG)

Next, we need something to hold all the material data, for this, I generated a data class <code>MaterialData</code>.

![Material Data Class](https://dev-to-uploads.s3.amazonaws.com/i/jqsw5u9nyjf5limpcdpx.PNG)

I then generated a function <code>parseMtlFile</code> which takes in a <code>BufferedReader</code> and parses the file to populate a hash map with the material names as the keys and a <code>MaterialData</code> classes as the value. 

![mMaterial hash map definition](https://dev-to-uploads.s3.amazonaws.com/i/uz6wfphyhap6cexhqz71.PNG)

When the object file specifies a material file to use, I take the file name and generate an <code>InputStream</code> which I use to create a <code>BufferedReader</code> and pass it to the <code>parseMtlFile</code> function.

![Calling ParseMtlFile function](https://dev-to-uploads.s3.amazonaws.com/i/30cqvt0a2xvrk0eqi7vy.PNG)

Taking a step back to detail the .mtl file format, each Material file can detail multiple materials, each material definition starts with a line with the word "newmtl" and the material name.
  
![Material Definition](https://dev-to-uploads.s3.amazonaws.com/i/4m6hyl5b3zqaha5gf93k.PNG)

When we see a "newmtl" line we can generate a new <code>MaterialData</code> class and add it to the <code>mMaterials</code> hash map, I also store the material name so I can use it to populate the classes variables later.

![new material](https://dev-to-uploads.s3.amazonaws.com/i/gx9pgzk1laot08m61zo0.PNG)

Following the "newmtl" line there are the following lines which are of interest to us.
* Ns - This value defines the Specular exponent of the material.
* Ka - This value defines the Ambient colour of the material.
* Kd - This value defines the diffuse colour of the material.
* Ks - This value defines the specular colour of the material.
* d - This value defined the dissolve (transparency) of the material.

Parsing each of these lines, we can populate the <code>MaterialData</code> variables similar to this:

![Parsing colour data](https://dev-to-uploads.s3.amazonaws.com/i/rg8b0rqi8l09kk8jdpcr.PNG)

## Parsing the faces with this new material data
Now that we have the material data we continue parsing the object file as before until we get to the faces.
Before each section of faces there is a "usemtl" line which we parse to determine which material name to use for the following faces.

![getting the name of the material to use](https://dev-to-uploads.s3.amazonaws.com/i/0thqfyjpe6fj698x6ywc.PNG)

When I parse a face, I take this <code>materialName</code> and use it to get the material from the <code>mMaterials</code> hash map.

![getting material from hash map](https://dev-to-uploads.s3.amazonaws.com/i/3wxdvv2oo3lugw799fom.PNG)

The remainder of the face parsing is the same as before except that for each new index, I also populate new <code>FloatArray</code>s for:
* <code>mFinalAmbientColour</code>
* <code>mFinalDiffuseColour</code>
* <code>mFinalSpecularColour</code>
* <code>mFinalSpecularExponent</code>
* <code>mFinalDissolve</code>

![Populating final colours](https://dev-to-uploads.s3.amazonaws.com/i/es63q7rvrkyzkb6jpztt.PNG)

If the material is not in the <code>mMaterials</code> hash map for any reason then default values are used to populate the arrays.

# The new <code>ModelData</code>
To use all this new data I modified the <code>ModelData</code> class to be a Kotlin data class and include the new colour information.

![Model Data Class updates](https://dev-to-uploads.s3.amazonaws.com/i/0whoyov0k5axa9dy8rkn.PNG)

# Using the new data
Once we have the data, we need to update our shader code to use the values, this was pretty straight forward but did involve adding an attribute for each new value 
![New attributes](https://dev-to-uploads.s3.amazonaws.com/i/68zy1n97pcs6kck6u1z8.PNG)
and then passing it to the Fragment shader to be used in the lighting calculations
![Varying values](https://dev-to-uploads.s3.amazonaws.com/i/kr71dgnxdcv66ssm25ku.PNG)
I also added a uniform variable to the fragment shader to pass the light colour in rather than having it hard coded.
Finally, For each new attribute I generated a <code>FloatBuffer</code> which is populated with the new material data in the same way as the Vertices and normal data was populated and then passed to the shaders in the <code>draw</code> function in the same way as the vertices and normals.

# Conclusions
I now have different coloured models in my game which looks better than the monocolour models that I had before.

![Screenshot](https://dev-to-uploads.s3.amazonaws.com/i/o95ijc0upoelg11lbimw.png)

The final code for this update can be found [here](https://github.com/longevity-software/AndroidVrGameDemo/releases/tag/B1.8).
Until next time....