Return parsed material information in `parseObj` API..
Parse PBR extension in MTL
diff --git a/experimental/tinyobj_loader_opt.h b/experimental/tinyobj_loader_opt.h
index 8c745d3..91902fb 100644
--- a/experimental/tinyobj_loader_opt.h
+++ b/experimental/tinyobj_loader_opt.h
@@ -292,6 +292,22 @@
   std::string bump_texname;                // map_bump, bump
   std::string displacement_texname;        // disp
   std::string alpha_texname;               // map_d
+
+  // PBR extension
+  // http://exocortex.com/blog/extending_wavefront_mtl_to_support_pbr
+  float roughness;                // [0, 1] default 0
+  float metallic;                 // [0, 1] default 0
+  float sheen;                    // [0, 1] default 0
+  float clearcoat_thickness;      // [0, 1] default 0
+  float clearcoat_roughness;      // [0, 1] default 0
+  float anisotropy;               // aniso. [0, 1] default 0
+  float anisotropy_rotation;      // anisor. [0, 1] default 0
+  std::string roughness_texname;  // map_Pr
+  std::string metallic_texname;   // map_Pm
+  std::string sheen_texname;      // map_Ps
+  std::string emissive_texname;   // map_Ke
+  std::string normal_texname;     // norm. For normal mapping.
+
   std::map<std::string, std::string> unknown_parameter;
 } material_t;
 
@@ -304,7 +320,8 @@
 struct index_t {
   int vertex_index, texcoord_index, normal_index;
   index_t() : vertex_index(-1), texcoord_index(-1), normal_index(-1) {}
-  explicit index_t(int idx) : vertex_index(idx), texcoord_index(idx), normal_index(idx) {}
+  explicit index_t(int idx)
+      : vertex_index(idx), texcoord_index(idx), normal_index(idx) {}
   index_t(int vidx, int vtidx, int vnidx)
       : vertex_index(vidx), texcoord_index(vtidx), normal_index(vnidx) {}
 };
@@ -660,6 +677,11 @@
 
     std::string linebuf(&buf[0]);
 
+    // Trim trailing whitespace.
+    if (linebuf.size() > 0) {
+      linebuf = linebuf.substr(0, linebuf.find_last_not_of(" \t") + 1);
+    }
+
     // Trim newline '\r\n' or '\n'
     if (linebuf.size() > 0) {
       if (linebuf[linebuf.size() - 1] == '\n')
@@ -790,6 +812,7 @@
       material.dissolve = parseFloat(&token);
       continue;
     }
+
     if (token[0] == 'T' && token[1] == 'r' && IS_SPACE(token[2])) {
       token += 2;
       // Invert value of Tr(assume Tr is in range [0, 1])
@@ -797,6 +820,55 @@
       continue;
     }
 
+    // PBR: roughness
+    if (token[0] == 'P' && token[1] == 'r' && IS_SPACE(token[2])) {
+      token += 2;
+      material.roughness = parseFloat(&token);
+      continue;
+    }
+
+    // PBR: metallic
+    if (token[0] == 'P' && token[1] == 'm' && IS_SPACE(token[2])) {
+      token += 2;
+      material.metallic = parseFloat(&token);
+      continue;
+    }
+
+    // PBR: sheen
+    if (token[0] == 'P' && token[1] == 's' && IS_SPACE(token[2])) {
+      token += 2;
+      material.sheen = parseFloat(&token);
+      continue;
+    }
+
+    // PBR: clearcoat thickness
+    if (token[0] == 'P' && token[1] == 'c' && IS_SPACE(token[2])) {
+      token += 2;
+      material.clearcoat_thickness = parseFloat(&token);
+      continue;
+    }
+
+    // PBR: clearcoat roughness
+    if ((0 == strncmp(token, "Pcr", 3)) && IS_SPACE(token[3])) {
+      token += 4;
+      material.clearcoat_roughness = parseFloat(&token);
+      continue;
+    }
+
+    // PBR: anisotropy
+    if ((0 == strncmp(token, "aniso", 5)) && IS_SPACE(token[5])) {
+      token += 6;
+      material.anisotropy = parseFloat(&token);
+      continue;
+    }
+
+    // PBR: anisotropy rotation
+    if ((0 == strncmp(token, "anisor", 6)) && IS_SPACE(token[6])) {
+      token += 7;
+      material.anisotropy_rotation = parseFloat(&token);
+      continue;
+    }
+
     // ambient texture
     if ((0 == strncmp(token, "map_Ka", 6)) && IS_SPACE(token[6])) {
       token += 7;
@@ -853,6 +925,41 @@
       continue;
     }
 
+    // PBR: roughness texture
+    if ((0 == strncmp(token, "map_Pr", 6)) && IS_SPACE(token[6])) {
+      token += 7;
+      material.roughness_texname = token;
+      continue;
+    }
+
+    // PBR: metallic texture
+    if ((0 == strncmp(token, "map_Pm", 6)) && IS_SPACE(token[6])) {
+      token += 7;
+      material.metallic_texname = token;
+      continue;
+    }
+
+    // PBR: sheen texture
+    if ((0 == strncmp(token, "map_Ps", 6)) && IS_SPACE(token[6])) {
+      token += 7;
+      material.sheen_texname = token;
+      continue;
+    }
+
+    // PBR: emissive texture
+    if ((0 == strncmp(token, "map_Ke", 6)) && IS_SPACE(token[6])) {
+      token += 7;
+      material.emissive_texname = token;
+      continue;
+    }
+
+    // PBR: normal map texture
+    if ((0 == strncmp(token, "norm", 4)) && IS_SPACE(token[4])) {
+      token += 5;
+      material.normal_texname = token;
+      continue;
+    }
+
     // unknown parameter
     const char *_space = strchr(token, ' ');
     if (!_space) {
@@ -935,8 +1042,9 @@
 /// Parse wavefront .obj(.obj string data is expanded to linear char array
 /// `buf')
 /// -1 to req_num_threads use the number of HW threads in the running system.
-bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
-              size_t len, const LoadOption &option);
+bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
+              std::vector<material_t> *materials, const char *buf, size_t len,
+              const LoadOption &option);
 
 #ifdef TINYOBJ_LOADER_OPT_IMPLEMENTATION
 
@@ -1137,8 +1245,9 @@
   return false;
 }
 
-bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes, const char *buf,
-              size_t len, const LoadOption &option) {
+bool parseObj(attrib_t *attrib, std::vector<shape_t> *shapes,
+              std::vector<material_t> *materials, const char *buf, size_t len,
+              const LoadOption &option) {
   attrib->vertices.clear();
   attrib->normals.clear();
   attrib->texcoords.clear();
@@ -1313,7 +1422,6 @@
   }
 
   std::map<std::string, int> material_map;
-  std::vector<material_t> materials;
 
   // Load material(if exits)
   if (mtllib_i_index >= 0 && mtllib_t_index >= 0 &&
@@ -1328,7 +1436,7 @@
 
     std::ifstream ifs(material_filename);
     if (ifs.good()) {
-      LoadMtl(&material_map, &materials, &ifs);
+      LoadMtl(&material_map, materials, &ifs);
 
       // std::cout << "maetrials = " << materials.size() << std::endl;
 
@@ -1361,10 +1469,10 @@
     num_indices += command_count[t].num_indices;
   }
 
-  //std::cout << "# v " << num_v << std::endl;
-  //std::cout << "# vn " << num_vn << std::endl;
-  //std::cout << "# vt " << num_vt << std::endl;
-  //std::cout << "# f " << num_f << std::endl;
+  // std::cout << "# v " << num_v << std::endl;
+  // std::cout << "# vn " << num_vn << std::endl;
+  // std::cout << "# vt " << num_vt << std::endl;
+  // std::cout << "# f " << num_f << std::endl;
 
   // 4. merge
   // @todo { parallelize merge. }
@@ -1445,7 +1553,8 @@
               int vertex_index = fixIndex(vi.vertex_index, v_count);
               int texcoord_index = fixIndex(vi.texcoord_index, t_count);
               int normal_index = fixIndex(vi.normal_index, n_count);
-              attrib->indices[f_count + k] = index_t(vertex_index, texcoord_index, normal_index);
+              attrib->indices[f_count + k] =
+                  index_t(vertex_index, texcoord_index, normal_index);
             }
             attrib->material_ids[face_count] = material_id;
             attrib->face_num_verts[face_count] = commands[t][i].f.size();
diff --git a/experimental/viewer.cc b/experimental/viewer.cc
index f4ab5ed..be17c7a 100644
--- a/experimental/viewer.cc
+++ b/experimental/viewer.cc
@@ -218,6 +218,7 @@
 {
   tinyobj_opt::attrib_t attrib;
   std::vector<tinyobj_opt::shape_t> shapes;
+  std::vector<tinyobj_opt::material_t> materials;
 
   size_t data_len = 0;
   const char* data = get_file_data(&data_len, filename);
@@ -229,7 +230,7 @@
   tinyobj_opt::LoadOption option;
   option.req_num_threads = num_threads;
   option.verbose = verbose;
-  bool ret = parseObj(&attrib, &shapes, data, data_len, option);
+  bool ret = parseObj(&attrib, &shapes, &materials, data, data_len, option);
 
   bmin[0] = bmin[1] = bmin[2] = std::numeric_limits<float>::max();
   bmax[0] = bmax[1] = bmax[2] = -std::numeric_limits<float>::max();
@@ -528,6 +529,7 @@
 
     tinyobj_opt::attrib_t attrib;
     std::vector<tinyobj_opt::shape_t> shapes;
+    std::vector<tinyobj_opt::material_t> materials;
 
     size_t data_len = 0;
     const char* data = get_file_data(&data_len, argv[1]);
@@ -540,7 +542,7 @@
     option.req_num_threads = num_threads;
     option.verbose = true;
 
-    bool ret = parseObj(&attrib, &shapes, data, data_len, option);
+    bool ret = parseObj(&attrib, &shapes, &materials, data, data_len, option);
 
     return ret;
   }