A description of the MTL format can be found here.
The other references I’ve found appear to be copies of that but with some of the formatting lost.
For rendering, I suggest that you “unweld” the OBJ structure first. By that, I mean identify the distinct combinations of (vertex position, texture coordinate, normal) indices used in the face definitions and create a separate vertex for each one, so that you can draw many faces with a single glDrawElements() call.
You may run into trouble here. Texture and normal info is optional in OBJ format. For example you can have statements like -
f 15 47 29, or f 15//22 24//21 29//16.
You may also encounter faces with more than 3 vertices, such as f 45 12 24 67 29 30 44
It’s also possible, though rare, to encounter negative face indices, such as f -2 -7 -3 -27
But your code looks good for triangulated OBJs with textures and normals specified.
If you end up writing your own MTL code, here’s a couple of tips: 1) Do not call glMaterial for every polygon rendered. It will be painfully slow. Talked to someone who did that. It took about 8 hours to render a model with a few thousand polys. 2) As you read in polys, put them in bins by material, and render them that way (i.e. all blue polys in one display list, all red polys …). Material bins can be avoided if you generate the models yourself, in which case you can write OBJ files that already have polys grouped by material.
Sorry, my employer does not allow us to give out code developed in house. Our OBJ reader is sort of klunky anyway. It does not do material binning. You could add material binning to your code in a day or two. Good luck.