Robotran C Documentation
tiny_obj_loader.h
Go to the documentation of this file.
1 /*
2 The MIT License (MIT)
3 
4 Copyright (c) 2012-2016 Syoyo Fujita and many contributors.
5 
6 Permission is hereby granted, free of charge, to any person obtaining a copy
7 of this software and associated documentation files (the "Software"), to deal
8 in the Software without restriction, including without limitation the rights
9 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 copies of the Software, and to permit persons to whom the Software is
11 furnished to do so, subject to the following conditions:
12 
13 The above copyright notice and this permission notice shall be included in
14 all copies or substantial portions of the Software.
15 
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 THE SOFTWARE.
23 */
24 #ifdef OPEN_GL
25 
26 //
27 // version 1.0.0 : Change data structure. Change license from BSD to MIT.
28 //
29 
30 //
31 // Use this in *one* .cc
32 // #define TINYOBJLOADER_IMPLEMENTATION
33 // #include "tiny_obj_loader.h"
34 //
35 
36 #ifndef TINY_OBJ_LOADER_H_
37 #define TINY_OBJ_LOADER_H_
38 
39 #include <map>
40 #include <string>
41 #include <vector>
42 
43 namespace tinyobj {
44 
45 typedef struct {
46  std::string name;
47 
48  float ambient[3];
49  float diffuse[3];
50  float specular[3];
51  float transmittance[3];
52  float emission[3];
53  float shininess;
54  float ior; // index of refraction
55  float dissolve; // 1 == opaque; 0 == fully transparent
56  // illumination model (see http://www.fileformat.info/format/material/)
57  int illum;
58 
59  int dummy; // Suppress padding warning.
60 
61  std::string ambient_texname; // map_Ka
62  std::string diffuse_texname; // map_Kd
63  std::string specular_texname; // map_Ks
64  std::string specular_highlight_texname; // map_Ns
65  std::string bump_texname; // map_bump, bump
66  std::string displacement_texname; // disp
67  std::string alpha_texname; // map_d
68 
69  // PBR extension
70  // http://exocortex.com/blog/extending_wavefront_mtl_to_support_pbr
71  float roughness; // [0, 1] default 0
72  float metallic; // [0, 1] default 0
73  float sheen; // [0, 1] default 0
74  float clearcoat_thickness; // [0, 1] default 0
75  float clearcoat_roughness; // [0, 1] default 0
76  float anisotropy; // aniso. [0, 1] default 0
77  float anisotropy_rotation; // anisor. [0, 1] default 0
78  std::string roughness_texname; // map_Pr
79  std::string metallic_texname; // map_Pm
80  std::string sheen_texname; // map_Ps
81  std::string emissive_texname; // map_Ke
82  std::string normal_texname; // norm. For normal mapping.
83 
84  std::map<std::string, std::string> unknown_parameter;
85 } material_t;
86 
87 typedef struct {
88  std::string name;
89 
90  std::vector<int> intValues;
91  std::vector<float> floatValues;
92  std::vector<std::string> stringValues;
93 } tag_t;
94 
95 // Index struct to support different indices for vtx/normal/texcoord.
96 // -1 means not used.
97 typedef struct {
98  int vertex_index;
99  int normal_index;
100  int texcoord_index;
101 } index_t;
102 
103 typedef struct {
104  std::vector<index_t> indices;
105  std::vector<unsigned char> num_face_vertices; // The number of vertices per
106  // face. 3 = polygon, 4 = quad,
107  // ... Up to 255.
108  std::vector<int> material_ids; // per-face material ID
109  std::vector<tag_t> tags; // SubD tag
110 } mesh_t;
111 
112 typedef struct {
113  std::string name;
114  mesh_t mesh;
115 } shape_t;
116 
117 // Vertex attributes
118 typedef struct {
119  std::vector<float> vertices; // 'v'
120  std::vector<float> normals; // 'vn'
121  std::vector<float> texcoords; // 'vt'
122 } attrib_t;
123 
124 typedef struct callback_t_ {
125  // W is optional and set to 1 if there is no `w` item in `v` line
126  void (*vertex_cb)(void *user_data, float x, float y, float z, float w);
127  void (*normal_cb)(void *user_data, float x, float y, float z);
128 
129  // y and z are optional and set to 0 if there is no `y` and/or `z` item(s) in
130  // `vt` line.
131  void (*texcoord_cb)(void *user_data, float x, float y, float z);
132 
133  // called per 'f' line. num_indices is the number of face indices(e.g. 3 for
134  // triangle, 4 for quad)
135  // 0 will be passed for undefined index in index_t members.
136  void (*index_cb)(void *user_data, index_t *indices, int num_indices);
137  // `name` material name, `material_id` = the array index of material_t[]. -1
138  // if
139  // a material not found in .mtl
140  void (*usemtl_cb)(void *user_data, const char *name, int material_id);
141  // `materials` = parsed material data.
142  void (*mtllib_cb)(void *user_data, const material_t *materials,
143  int num_materials);
144  // There may be multiple group names
145  void (*group_cb)(void *user_data, const char **names, int num_names);
146  void (*object_cb)(void *user_data, const char *name);
147 
148  callback_t_()
149  : vertex_cb(NULL),
150  normal_cb(NULL),
151  texcoord_cb(NULL),
152  index_cb(NULL),
153  usemtl_cb(NULL),
154  mtllib_cb(NULL),
155  group_cb(NULL),
156  object_cb(NULL) {}
157 } callback_t;
158 
159 class MaterialReader {
160  public:
161  MaterialReader() {}
162  virtual ~MaterialReader();
163 
164  virtual bool operator()(const std::string &matId,
165  std::vector<material_t> *materials,
166  std::map<std::string, int> *matMap,
167  std::string *err) = 0;
168 };
169 
170 class MaterialFileReader : public MaterialReader {
171  public:
172  explicit MaterialFileReader(const std::string &mtl_basepath)
173  : m_mtlBasePath(mtl_basepath) {}
174  virtual ~MaterialFileReader() {}
175  virtual bool operator()(const std::string &matId,
176  std::vector<material_t> *materials,
177  std::map<std::string, int> *matMap, std::string *err);
178 
179  private:
180  std::string m_mtlBasePath;
181 };
182 
183 class MaterialStreamReader : public MaterialReader {
184  public:
185  explicit MaterialStreamReader(std::istream &inStream)
186  : m_inStream(inStream) {}
187  virtual ~MaterialStreamReader() {}
188  virtual bool operator()(const std::string &matId,
189  std::vector<material_t> *materials,
190  std::map<std::string, int> *matMap, std::string *err);
191 
192  private:
193  std::istream &m_inStream;
194 };
195 
204 bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
205  std::vector<material_t> *materials, std::string *err,
206  const char *filename, const char *mtl_basepath = NULL,
207  bool triangulate = true);
208 
215 bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
216  void *user_data = NULL,
217  MaterialReader *readMatFn = NULL,
218  std::string *err = NULL);
219 
224 bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
225  std::vector<material_t> *materials, std::string *err,
226  std::istream *inStream, MaterialReader *readMatFn = NULL,
227  bool triangulate = true);
228 
230 void LoadMtl(std::map<std::string, int> *material_map,
231  std::vector<material_t> *materials, std::istream *inStream);
232 
233 } // namespace tinyobj
234 
235 #ifdef TINYOBJLOADER_IMPLEMENTATION
236 #include <cassert>
237 #include <cctype>
238 #include <cmath>
239 #include <cstddef>
240 #include <cstdlib>
241 #include <cstring>
242 #include <utility>
243 
244 #include <fstream>
245 #include <sstream>
246 
247 namespace tinyobj {
248 
249 MaterialReader::~MaterialReader() {}
250 
251 #define TINYOBJ_SSCANF_BUFFER_SIZE (4096)
252 
253 struct vertex_index {
254  int v_idx, vt_idx, vn_idx;
255  vertex_index() : v_idx(-1), vt_idx(-1), vn_idx(-1) {}
256  explicit vertex_index(int idx) : v_idx(idx), vt_idx(idx), vn_idx(idx) {}
257  vertex_index(int vidx, int vtidx, int vnidx)
258  : v_idx(vidx), vt_idx(vtidx), vn_idx(vnidx) {}
259 };
260 
261 struct tag_sizes {
262  tag_sizes() : num_ints(0), num_floats(0), num_strings(0) {}
263  int num_ints;
264  int num_floats;
265  int num_strings;
266 };
267 
268 struct obj_shape {
269  std::vector<float> v;
270  std::vector<float> vn;
271  std::vector<float> vt;
272 };
273 
274 // See
275 // http://stackoverflow.com/questions/6089231/getting-std-ifstream-to-handle-lf-cr-and-crlf
276 static std::istream &safeGetline(std::istream &is, std::string &t) {
277  t.clear();
278 
279  // The characters in the stream are read one-by-one using a std::streambuf.
280  // That is faster than reading them one-by-one using the std::istream.
281  // Code that uses streambuf this way must be guarded by a sentry object.
282  // The sentry object performs various tasks,
283  // such as thread synchronization and updating the stream state.
284 
285  std::istream::sentry se(is, true);
286  std::streambuf *sb = is.rdbuf();
287 
288  for (;;) {
289  int c = sb->sbumpc();
290  switch (c) {
291  case '\n':
292  return is;
293  case '\r':
294  if (sb->sgetc() == '\n') sb->sbumpc();
295  return is;
296  case EOF:
297  // Also handle the case when the last line has no line ending
298  if (t.empty()) is.setstate(std::ios::eofbit);
299  return is;
300  default:
301  t += static_cast<char>(c);
302  }
303  }
304 }
305 
306 #define IS_SPACE(x) (((x) == ' ') || ((x) == '\t'))
307 #define IS_DIGIT(x) \
308  (static_cast<unsigned int>((x) - '0') < static_cast<unsigned int>(10))
309 #define IS_NEW_LINE(x) (((x) == '\r') || ((x) == '\n') || ((x) == '\0'))
310 
311 // Make index zero-base, and also support relative index.
312 static inline int fixIndex(int idx, int n) {
313  if (idx > 0) return idx - 1;
314  if (idx == 0) return 0;
315  return n + idx; // negative value = relative
316 }
317 
318 static inline std::string parseString(const char **token) {
319  std::string s;
320  (*token) += strspn((*token), " \t");
321  size_t e = strcspn((*token), " \t\r");
322  s = std::string((*token), &(*token)[e]);
323  (*token) += e;
324  return s;
325 }
326 
327 static inline int parseInt(const char **token) {
328  (*token) += strspn((*token), " \t");
329  int i = atoi((*token));
330  (*token) += strcspn((*token), " \t\r");
331  return i;
332 }
333 
334 // Tries to parse a floating point number located at s.
335 //
336 // s_end should be a location in the string where reading should absolutely
337 // stop. For example at the end of the string, to prevent buffer overflows.
338 //
339 // Parses the following EBNF grammar:
340 // sign = "+" | "-" ;
341 // END = ? anything not in digit ?
342 // digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;
343 // integer = [sign] , digit , {digit} ;
344 // decimal = integer , ["." , integer] ;
345 // float = ( decimal , END ) | ( decimal , ("E" | "e") , integer , END ) ;
346 //
347 // Valid strings are for example:
348 // -0 +3.1417e+2 -0.0E-3 1.0324 -1.41 11e2
349 //
350 // If the parsing is a success, result is set to the parsed value and true
351 // is returned.
352 //
353 // The function is greedy and will parse until any of the following happens:
354 // - a non-conforming character is encountered.
355 // - s_end is reached.
356 //
357 // The following situations triggers a failure:
358 // - s >= s_end.
359 // - parse failure.
360 //
361 static bool tryParseDouble(const char *s, const char *s_end, double *result) {
362  if (s >= s_end) {
363  return false;
364  }
365 
366  double mantissa = 0.0;
367  // This exponent is base 2 rather than 10.
368  // However the exponent we parse is supposed to be one of ten,
369  // thus we must take care to convert the exponent/and or the
370  // mantissa to a * 2^E, where a is the mantissa and E is the
371  // exponent.
372  // To get the final double we will use ldexp, it requires the
373  // exponent to be in base 2.
374  int exponent = 0;
375 
376  // NOTE: THESE MUST BE DECLARED HERE SINCE WE ARE NOT ALLOWED
377  // TO JUMP OVER DEFINITIONS.
378  char sign = '+';
379  char exp_sign = '+';
380  char const *curr = s;
381 
382  // How many characters were read in a loop.
383  int read = 0;
384  // Tells whether a loop terminated due to reaching s_end.
385  bool end_not_reached = false;
386 
387  /*
388  BEGIN PARSING.
389  */
390 
391  // Find out what sign we've got.
392  if (*curr == '+' || *curr == '-') {
393  sign = *curr;
394  curr++;
395  } else if (IS_DIGIT(*curr)) { /* Pass through. */
396  } else {
397  goto fail;
398  }
399 
400  // Read the integer part.
401  end_not_reached = (curr != s_end);
402  while (end_not_reached && IS_DIGIT(*curr)) {
403  mantissa *= 10;
404  mantissa += static_cast<int>(*curr - 0x30);
405  curr++;
406  read++;
407  end_not_reached = (curr != s_end);
408  }
409 
410  // We must make sure we actually got something.
411  if (read == 0) goto fail;
412  // We allow numbers of form "#", "###" etc.
413  if (!end_not_reached) goto assemble;
414 
415  // Read the decimal part.
416  if (*curr == '.') {
417  curr++;
418  read = 1;
419  end_not_reached = (curr != s_end);
420  while (end_not_reached && IS_DIGIT(*curr)) {
421  // NOTE: Don't use powf here, it will absolutely murder precision.
422  mantissa += static_cast<int>(*curr - 0x30) * pow(10.0, -read);
423  read++;
424  curr++;
425  end_not_reached = (curr != s_end);
426  }
427  } else if (*curr == 'e' || *curr == 'E') {
428  } else {
429  goto assemble;
430  }
431 
432  if (!end_not_reached) goto assemble;
433 
434  // Read the exponent part.
435  if (*curr == 'e' || *curr == 'E') {
436  curr++;
437  // Figure out if a sign is present and if it is.
438  end_not_reached = (curr != s_end);
439  if (end_not_reached && (*curr == '+' || *curr == '-')) {
440  exp_sign = *curr;
441  curr++;
442  } else if (IS_DIGIT(*curr)) { /* Pass through. */
443  } else {
444  // Empty E is not allowed.
445  goto fail;
446  }
447 
448  read = 0;
449  end_not_reached = (curr != s_end);
450  while (end_not_reached && IS_DIGIT(*curr)) {
451  exponent *= 10;
452  exponent += static_cast<int>(*curr - 0x30);
453  curr++;
454  read++;
455  end_not_reached = (curr != s_end);
456  }
457  exponent *= (exp_sign == '+' ? 1 : -1);
458  if (read == 0) goto fail;
459  }
460 
461 assemble:
462  *result =
463  (sign == '+' ? 1 : -1) * ldexp(mantissa * pow(5.0, exponent), exponent);
464  return true;
465 fail:
466  return false;
467 }
468 
469 static inline float parseFloat(const char **token, double default_value = 0.0) {
470  (*token) += strspn((*token), " \t");
471  const char *end = (*token) + strcspn((*token), " \t\r");
472  double val = default_value;
473  tryParseDouble((*token), end, &val);
474  float f = static_cast<float>(val);
475  (*token) = end;
476  return f;
477 }
478 
479 static inline void parseFloat2(float *x, float *y, const char **token) {
480  (*x) = parseFloat(token);
481  (*y) = parseFloat(token);
482 }
483 
484 static inline void parseFloat3(float *x, float *y, float *z,
485  const char **token) {
486  (*x) = parseFloat(token);
487  (*y) = parseFloat(token);
488  (*z) = parseFloat(token);
489 }
490 
491 static inline void parseV(float *x, float *y, float *z, float *w,
492  const char **token) {
493  (*x) = parseFloat(token);
494  (*y) = parseFloat(token);
495  (*z) = parseFloat(token);
496  (*w) = parseFloat(token, 1.0);
497 }
498 
499 static tag_sizes parseTagTriple(const char **token) {
500  tag_sizes ts;
501 
502  ts.num_ints = atoi((*token));
503  (*token) += strcspn((*token), "/ \t\r");
504  if ((*token)[0] != '/') {
505  return ts;
506  }
507  (*token)++;
508 
509  ts.num_floats = atoi((*token));
510  (*token) += strcspn((*token), "/ \t\r");
511  if ((*token)[0] != '/') {
512  return ts;
513  }
514  (*token)++;
515 
516  ts.num_strings = atoi((*token));
517  (*token) += strcspn((*token), "/ \t\r") + 1;
518 
519  return ts;
520 }
521 
522 // Parse triples with index offsets: i, i/j/k, i//k, i/j
523 static vertex_index parseTriple(const char **token, int vsize, int vnsize,
524  int vtsize) {
525  vertex_index vi(-1);
526 
527  vi.v_idx = fixIndex(atoi((*token)), vsize);
528  (*token) += strcspn((*token), "/ \t\r");
529  if ((*token)[0] != '/') {
530  return vi;
531  }
532  (*token)++;
533 
534  // i//k
535  if ((*token)[0] == '/') {
536  (*token)++;
537  vi.vn_idx = fixIndex(atoi((*token)), vnsize);
538  (*token) += strcspn((*token), "/ \t\r");
539  return vi;
540  }
541 
542  // i/j/k or i/j
543  vi.vt_idx = fixIndex(atoi((*token)), vtsize);
544  (*token) += strcspn((*token), "/ \t\r");
545  if ((*token)[0] != '/') {
546  return vi;
547  }
548 
549  // i/j/k
550  (*token)++; // skip '/'
551  vi.vn_idx = fixIndex(atoi((*token)), vnsize);
552  (*token) += strcspn((*token), "/ \t\r");
553  return vi;
554 }
555 
556 // Parse raw triples: i, i/j/k, i//k, i/j
557 static vertex_index parseRawTriple(const char **token) {
558  vertex_index vi(static_cast<int>(0)); // 0 is an invalid index in OBJ
559 
560  vi.v_idx = atoi((*token));
561  (*token) += strcspn((*token), "/ \t\r");
562  if ((*token)[0] != '/') {
563  return vi;
564  }
565  (*token)++;
566 
567  // i//k
568  if ((*token)[0] == '/') {
569  (*token)++;
570  vi.vn_idx = atoi((*token));
571  (*token) += strcspn((*token), "/ \t\r");
572  return vi;
573  }
574 
575  // i/j/k or i/j
576  vi.vt_idx = atoi((*token));
577  (*token) += strcspn((*token), "/ \t\r");
578  if ((*token)[0] != '/') {
579  return vi;
580  }
581 
582  // i/j/k
583  (*token)++; // skip '/'
584  vi.vn_idx = atoi((*token));
585  (*token) += strcspn((*token), "/ \t\r");
586  return vi;
587 }
588 
589 static void InitMaterial(material_t *material) {
590  material->name = "";
591  material->ambient_texname = "";
592  material->diffuse_texname = "";
593  material->specular_texname = "";
594  material->specular_highlight_texname = "";
595  material->bump_texname = "";
596  material->displacement_texname = "";
597  material->alpha_texname = "";
598  for (int i = 0; i < 3; i++) {
599  material->ambient[i] = 0.f;
600  material->diffuse[i] = 0.f;
601  material->specular[i] = 0.f;
602  material->transmittance[i] = 0.f;
603  material->emission[i] = 0.f;
604  }
605  material->illum = 0;
606  material->dissolve = 1.f;
607  material->shininess = 1.f;
608  material->ior = 1.f;
609 
610  material->roughness = 0.f;
611  material->metallic = 0.f;
612  material->sheen = 0.f;
613  material->clearcoat_thickness = 0.f;
614  material->clearcoat_roughness = 0.f;
615  material->anisotropy_rotation = 0.f;
616  material->anisotropy = 0.f;
617  material->roughness_texname = "";
618  material->metallic_texname = "";
619  material->sheen_texname = "";
620  material->emissive_texname = "";
621  material->normal_texname = "";
622 
623  material->unknown_parameter.clear();
624 }
625 
626 static bool exportFaceGroupToShape(
627  shape_t *shape, const std::vector<std::vector<vertex_index> > &faceGroup,
628  const std::vector<tag_t> &tags, const int material_id,
629  const std::string &name, bool triangulate) {
630  if (faceGroup.empty()) {
631  return false;
632  }
633 
634  // Flatten vertices and indices
635  for (size_t i = 0; i < faceGroup.size(); i++) {
636  const std::vector<vertex_index> &face = faceGroup[i];
637 
638  vertex_index i0 = face[0];
639  vertex_index i1(-1);
640  vertex_index i2 = face[1];
641 
642  size_t npolys = face.size();
643 
644  if (triangulate) {
645  // Polygon -> triangle fan conversion
646  for (size_t k = 2; k < npolys; k++) {
647  i1 = i2;
648  i2 = face[k];
649 
650  index_t idx0, idx1, idx2;
651  idx0.vertex_index = i0.v_idx;
652  idx0.normal_index = i0.vn_idx;
653  idx0.texcoord_index = i0.vt_idx;
654  idx1.vertex_index = i1.v_idx;
655  idx1.normal_index = i1.vn_idx;
656  idx1.texcoord_index = i1.vt_idx;
657  idx2.vertex_index = i2.v_idx;
658  idx2.normal_index = i2.vn_idx;
659  idx2.texcoord_index = i2.vt_idx;
660 
661  shape->mesh.indices.push_back(idx0);
662  shape->mesh.indices.push_back(idx1);
663  shape->mesh.indices.push_back(idx2);
664 
665  shape->mesh.num_face_vertices.push_back(3);
666  shape->mesh.material_ids.push_back(material_id);
667  }
668  } else {
669  for (size_t k = 0; k < npolys; k++) {
670  index_t idx;
671  idx.vertex_index = face[k].v_idx;
672  idx.normal_index = face[k].vn_idx;
673  idx.texcoord_index = face[k].vt_idx;
674  shape->mesh.indices.push_back(idx);
675  }
676 
677  shape->mesh.num_face_vertices.push_back(
678  static_cast<unsigned char>(npolys));
679  shape->mesh.material_ids.push_back(material_id); // per face
680  }
681  }
682 
683  shape->name = name;
684  shape->mesh.tags = tags;
685 
686  return true;
687 }
688 
689 void LoadMtl(std::map<std::string, int> *material_map,
690  std::vector<material_t> *materials, std::istream *inStream) {
691  // Create a default material anyway.
692  material_t material;
693  InitMaterial(&material);
694 
695  while (inStream->peek() != -1) {
696  std::string linebuf;
697 
698  safeGetline(*inStream, linebuf);
699 
700  // Trim trailing whitespace.
701  if (linebuf.size() > 0) {
702  linebuf = linebuf.substr(0, linebuf.find_last_not_of(" \t") + 1);
703  }
704 
705  // Trim newline '\r\n' or '\n'
706  if (linebuf.size() > 0) {
707  if (linebuf[linebuf.size() - 1] == '\n')
708  linebuf.erase(linebuf.size() - 1);
709  }
710  if (linebuf.size() > 0) {
711  if (linebuf[linebuf.size() - 1] == '\r')
712  linebuf.erase(linebuf.size() - 1);
713  }
714 
715  // Skip if empty line.
716  if (linebuf.empty()) {
717  continue;
718  }
719 
720  // Skip leading space.
721  const char *token = linebuf.c_str();
722  token += strspn(token, " \t");
723 
724  assert(token);
725  if (token[0] == '\0') continue; // empty line
726 
727  if (token[0] == '#') continue; // comment line
728 
729  // new mtl
730  if ((0 == strncmp(token, "newmtl", 6)) && IS_SPACE((token[6]))) {
731  // flush previous material.
732  if (!material.name.empty()) {
733  material_map->insert(std::pair<std::string, int>(
734  material.name, static_cast<int>(materials->size())));
735  materials->push_back(material);
736  }
737 
738  // initial temporary material
739  InitMaterial(&material);
740 
741  // set new mtl name
742  char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE];
743  token += 7;
744 #ifdef _MSC_VER
745  sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf));
746 #else
747  sscanf(token, "%s", namebuf);
748 #endif
749  material.name = namebuf;
750  continue;
751  }
752 
753  // ambient
754  if (token[0] == 'K' && token[1] == 'a' && IS_SPACE((token[2]))) {
755  token += 2;
756  float r, g, b;
757  parseFloat3(&r, &g, &b, &token);
758  material.ambient[0] = r;
759  material.ambient[1] = g;
760  material.ambient[2] = b;
761  continue;
762  }
763 
764  // diffuse
765  if (token[0] == 'K' && token[1] == 'd' && IS_SPACE((token[2]))) {
766  token += 2;
767  float r, g, b;
768  parseFloat3(&r, &g, &b, &token);
769  material.diffuse[0] = r;
770  material.diffuse[1] = g;
771  material.diffuse[2] = b;
772  continue;
773  }
774 
775  // specular
776  if (token[0] == 'K' && token[1] == 's' && IS_SPACE((token[2]))) {
777  token += 2;
778  float r, g, b;
779  parseFloat3(&r, &g, &b, &token);
780  material.specular[0] = r;
781  material.specular[1] = g;
782  material.specular[2] = b;
783  continue;
784  }
785 
786  // transmittance
787  if ((token[0] == 'K' && token[1] == 't' && IS_SPACE((token[2]))) ||
788  (token[0] == 'T' && token[1] == 'f' && IS_SPACE((token[2])))) {
789  token += 2;
790  float r, g, b;
791  parseFloat3(&r, &g, &b, &token);
792  material.transmittance[0] = r;
793  material.transmittance[1] = g;
794  material.transmittance[2] = b;
795  continue;
796  }
797 
798  // ior(index of refraction)
799  if (token[0] == 'N' && token[1] == 'i' && IS_SPACE((token[2]))) {
800  token += 2;
801  material.ior = parseFloat(&token);
802  continue;
803  }
804 
805  // emission
806  if (token[0] == 'K' && token[1] == 'e' && IS_SPACE(token[2])) {
807  token += 2;
808  float r, g, b;
809  parseFloat3(&r, &g, &b, &token);
810  material.emission[0] = r;
811  material.emission[1] = g;
812  material.emission[2] = b;
813  continue;
814  }
815 
816  // shininess
817  if (token[0] == 'N' && token[1] == 's' && IS_SPACE(token[2])) {
818  token += 2;
819  material.shininess = parseFloat(&token);
820  continue;
821  }
822 
823  // illum model
824  if (0 == strncmp(token, "illum", 5) && IS_SPACE(token[5])) {
825  token += 6;
826  material.illum = parseInt(&token);
827  continue;
828  }
829 
830  // dissolve
831  if ((token[0] == 'd' && IS_SPACE(token[1]))) {
832  token += 1;
833  material.dissolve = parseFloat(&token);
834  continue;
835  }
836  if (token[0] == 'T' && token[1] == 'r' && IS_SPACE(token[2])) {
837  token += 2;
838  // Invert value of Tr(assume Tr is in range [0, 1])
839  material.dissolve = 1.0f - parseFloat(&token);
840  continue;
841  }
842 
843  // PBR: roughness
844  if (token[0] == 'P' && token[1] == 'r' && IS_SPACE(token[2])) {
845  token += 2;
846  material.roughness = parseFloat(&token);
847  continue;
848  }
849 
850  // PBR: metallic
851  if (token[0] == 'P' && token[1] == 'm' && IS_SPACE(token[2])) {
852  token += 2;
853  material.metallic = parseFloat(&token);
854  continue;
855  }
856 
857  // PBR: sheen
858  if (token[0] == 'P' && token[1] == 's' && IS_SPACE(token[2])) {
859  token += 2;
860  material.sheen = parseFloat(&token);
861  continue;
862  }
863 
864  // PBR: clearcoat thickness
865  if (token[0] == 'P' && token[1] == 'c' && IS_SPACE(token[2])) {
866  token += 2;
867  material.clearcoat_thickness = parseFloat(&token);
868  continue;
869  }
870 
871  // PBR: clearcoat roughness
872  if ((0 == strncmp(token, "Pcr", 3)) && IS_SPACE(token[3])) {
873  token += 4;
874  material.clearcoat_roughness = parseFloat(&token);
875  continue;
876  }
877 
878  // PBR: anisotropy
879  if ((0 == strncmp(token, "aniso", 5)) && IS_SPACE(token[5])) {
880  token += 6;
881  material.anisotropy = parseFloat(&token);
882  continue;
883  }
884 
885  // PBR: anisotropy rotation
886  if ((0 == strncmp(token, "anisor", 6)) && IS_SPACE(token[6])) {
887  token += 7;
888  material.anisotropy_rotation = parseFloat(&token);
889  continue;
890  }
891 
892  // ambient texture
893  if ((0 == strncmp(token, "map_Ka", 6)) && IS_SPACE(token[6])) {
894  token += 7;
895  material.ambient_texname = token;
896  continue;
897  }
898 
899  // diffuse texture
900  if ((0 == strncmp(token, "map_Kd", 6)) && IS_SPACE(token[6])) {
901  token += 7;
902  material.diffuse_texname = token;
903  continue;
904  }
905 
906  // specular texture
907  if ((0 == strncmp(token, "map_Ks", 6)) && IS_SPACE(token[6])) {
908  token += 7;
909  material.specular_texname = token;
910  continue;
911  }
912 
913  // specular highlight texture
914  if ((0 == strncmp(token, "map_Ns", 6)) && IS_SPACE(token[6])) {
915  token += 7;
916  material.specular_highlight_texname = token;
917  continue;
918  }
919 
920  // bump texture
921  if ((0 == strncmp(token, "map_bump", 8)) && IS_SPACE(token[8])) {
922  token += 9;
923  material.bump_texname = token;
924  continue;
925  }
926 
927  // alpha texture
928  if ((0 == strncmp(token, "map_d", 5)) && IS_SPACE(token[5])) {
929  token += 6;
930  material.alpha_texname = token;
931  continue;
932  }
933 
934  // bump texture
935  if ((0 == strncmp(token, "bump", 4)) && IS_SPACE(token[4])) {
936  token += 5;
937  material.bump_texname = token;
938  continue;
939  }
940 
941  // displacement texture
942  if ((0 == strncmp(token, "disp", 4)) && IS_SPACE(token[4])) {
943  token += 5;
944  material.displacement_texname = token;
945  continue;
946  }
947 
948  // PBR: roughness texture
949  if ((0 == strncmp(token, "map_Pr", 6)) && IS_SPACE(token[6])) {
950  token += 7;
951  material.roughness_texname = token;
952  continue;
953  }
954 
955  // PBR: metallic texture
956  if ((0 == strncmp(token, "map_Pm", 6)) && IS_SPACE(token[6])) {
957  token += 7;
958  material.metallic_texname = token;
959  continue;
960  }
961 
962  // PBR: sheen texture
963  if ((0 == strncmp(token, "map_Ps", 6)) && IS_SPACE(token[6])) {
964  token += 7;
965  material.sheen_texname = token;
966  continue;
967  }
968 
969  // PBR: emissive texture
970  if ((0 == strncmp(token, "map_Ke", 6)) && IS_SPACE(token[6])) {
971  token += 7;
972  material.emissive_texname = token;
973  continue;
974  }
975 
976  // PBR: normal map texture
977  if ((0 == strncmp(token, "norm", 4)) && IS_SPACE(token[4])) {
978  token += 5;
979  material.normal_texname = token;
980  continue;
981  }
982 
983  // unknown parameter
984  const char *_space = strchr(token, ' ');
985  if (!_space) {
986  _space = strchr(token, '\t');
987  }
988  if (_space) {
989  std::ptrdiff_t len = _space - token;
990  std::string key(token, static_cast<size_t>(len));
991  std::string value = _space + 1;
992  material.unknown_parameter.insert(
993  std::pair<std::string, std::string>(key, value));
994  }
995  }
996  // flush last material.
997  material_map->insert(std::pair<std::string, int>(
998  material.name, static_cast<int>(materials->size())));
999  materials->push_back(material);
1000 }
1001 
1002 bool MaterialFileReader::operator()(const std::string &matId,
1003  std::vector<material_t> *materials,
1004  std::map<std::string, int> *matMap,
1005  std::string *err) {
1006  std::string filepath;
1007 
1008  if (!m_mtlBasePath.empty()) {
1009  filepath = std::string(m_mtlBasePath) + matId;
1010  } else {
1011  filepath = matId;
1012  }
1013 
1014  std::ifstream matIStream(filepath.c_str());
1015  LoadMtl(matMap, materials, &matIStream);
1016  if (!matIStream) {
1017  std::stringstream ss;
1018  ss << "WARN: Material file [ " << filepath
1019  << " ] not found. Created a default material.";
1020  if (err) {
1021  (*err) += ss.str();
1022  }
1023  }
1024  return true;
1025 }
1026 
1027 bool MaterialStreamReader::operator()(const std::string &matId,
1028  std::vector<material_t> *materials,
1029  std::map<std::string, int> *matMap,
1030  std::string *err) {
1031  LoadMtl(matMap, materials, &m_inStream);
1032  if (!m_inStream) {
1033  std::stringstream ss;
1034  ss << "WARN: Material stream in error state."
1035  << " Created a default material.";
1036  if (err) {
1037  (*err) += ss.str();
1038  }
1039  }
1040  return true;
1041 }
1042 
1043 bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
1044  std::vector<material_t> *materials, std::string *err,
1045  const char *filename, const char *mtl_basepath,
1046  bool trianglulate) {
1047  attrib->vertices.clear();
1048  attrib->normals.clear();
1049  attrib->texcoords.clear();
1050  shapes->clear();
1051 
1052  std::stringstream errss;
1053 
1054  std::ifstream ifs(filename);
1055  if (!ifs) {
1056  errss << "Cannot open file [" << filename << "]" << std::endl;
1057  if (err) {
1058  (*err) = errss.str();
1059  }
1060  return false;
1061  }
1062 
1063  std::string basePath;
1064  if (mtl_basepath) {
1065  basePath = mtl_basepath;
1066  }
1067  MaterialFileReader matFileReader(basePath);
1068 
1069  return LoadObj(attrib, shapes, materials, err, &ifs, &matFileReader,
1070  trianglulate);
1071 }
1072 
1073 bool LoadObj(attrib_t *attrib, std::vector<shape_t> *shapes,
1074  std::vector<material_t> *materials, std::string *err,
1075  std::istream *inStream,
1076  MaterialReader *readMatFn /*= NULL*/,
1077  bool triangulate) {
1078  std::stringstream errss;
1079 
1080  std::vector<float> v;
1081  std::vector<float> vn;
1082  std::vector<float> vt;
1083  std::vector<tag_t> tags;
1084  std::vector<std::vector<vertex_index> > faceGroup;
1085  std::string name;
1086 
1087  // material
1088  std::map<std::string, int> material_map;
1089  int material = -1;
1090 
1091  shape_t shape;
1092 
1093  while (inStream->peek() != -1) {
1094  std::string linebuf;
1095  safeGetline(*inStream, linebuf);
1096 
1097  // Trim newline '\r\n' or '\n'
1098  if (linebuf.size() > 0) {
1099  if (linebuf[linebuf.size() - 1] == '\n')
1100  linebuf.erase(linebuf.size() - 1);
1101  }
1102  if (linebuf.size() > 0) {
1103  if (linebuf[linebuf.size() - 1] == '\r')
1104  linebuf.erase(linebuf.size() - 1);
1105  }
1106 
1107  // Skip if empty line.
1108  if (linebuf.empty()) {
1109  continue;
1110  }
1111 
1112  // Skip leading space.
1113  const char *token = linebuf.c_str();
1114  token += strspn(token, " \t");
1115 
1116  assert(token);
1117  if (token[0] == '\0') continue; // empty line
1118 
1119  if (token[0] == '#') continue; // comment line
1120 
1121  // vertex
1122  if (token[0] == 'v' && IS_SPACE((token[1]))) {
1123  token += 2;
1124  float x, y, z;
1125  parseFloat3(&x, &y, &z, &token);
1126  v.push_back(x);
1127  v.push_back(y);
1128  v.push_back(z);
1129  continue;
1130  }
1131 
1132  // normal
1133  if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) {
1134  token += 3;
1135  float x, y, z;
1136  parseFloat3(&x, &y, &z, &token);
1137  vn.push_back(x);
1138  vn.push_back(y);
1139  vn.push_back(z);
1140  continue;
1141  }
1142 
1143  // texcoord
1144  if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) {
1145  token += 3;
1146  float x, y;
1147  parseFloat2(&x, &y, &token);
1148  vt.push_back(x);
1149  vt.push_back(y);
1150  continue;
1151  }
1152 
1153  // face
1154  if (token[0] == 'f' && IS_SPACE((token[1]))) {
1155  token += 2;
1156  token += strspn(token, " \t");
1157 
1158  std::vector<vertex_index> face;
1159  face.reserve(3);
1160 
1161  while (!IS_NEW_LINE(token[0])) {
1162  vertex_index vi = parseTriple(&token, static_cast<int>(v.size() / 3),
1163  static_cast<int>(vn.size() / 3),
1164  static_cast<int>(vt.size() / 2));
1165  face.push_back(vi);
1166  size_t n = strspn(token, " \t\r");
1167  token += n;
1168  }
1169 
1170  // replace with emplace_back + std::move on C++11
1171  faceGroup.push_back(std::vector<vertex_index>());
1172  faceGroup[faceGroup.size() - 1].swap(face);
1173 
1174  continue;
1175  }
1176 
1177  // use mtl
1178  if ((0 == strncmp(token, "usemtl", 6)) && IS_SPACE((token[6]))) {
1179  char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE];
1180  token += 7;
1181 #ifdef _MSC_VER
1182  sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf));
1183 #else
1184  sscanf(token, "%s", namebuf);
1185 #endif
1186 
1187  int newMaterialId = -1;
1188  if (material_map.find(namebuf) != material_map.end()) {
1189  newMaterialId = material_map[namebuf];
1190  } else {
1191  // { error!! material not found }
1192  }
1193 
1194  if (newMaterialId != material) {
1195  // Create per-face material
1196  exportFaceGroupToShape(&shape, faceGroup, tags, material, name,
1197  triangulate);
1198  faceGroup.clear();
1199  material = newMaterialId;
1200  }
1201 
1202  continue;
1203  }
1204 
1205  // load mtl
1206  if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) {
1207  if (readMatFn) {
1208  char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE];
1209  token += 7;
1210 #ifdef _MSC_VER
1211  sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf));
1212 #else
1213  sscanf(token, "%s", namebuf);
1214 #endif
1215 
1216  std::string err_mtl;
1217  bool ok = (*readMatFn)(namebuf, materials, &material_map, &err_mtl);
1218  if (err) {
1219  (*err) += err_mtl;
1220  }
1221 
1222  if (!ok) {
1223  faceGroup.clear(); // for safety
1224  return false;
1225  }
1226  }
1227 
1228  continue;
1229  }
1230 
1231  // group name
1232  if (token[0] == 'g' && IS_SPACE((token[1]))) {
1233  // flush previous face group.
1234  bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name,
1235  triangulate);
1236  if (ret) {
1237  shapes->push_back(shape);
1238  }
1239 
1240  shape = shape_t();
1241 
1242  // material = -1;
1243  faceGroup.clear();
1244 
1245  std::vector<std::string> names;
1246  names.reserve(2);
1247 
1248  while (!IS_NEW_LINE(token[0])) {
1249  std::string str = parseString(&token);
1250  names.push_back(str);
1251  token += strspn(token, " \t\r"); // skip tag
1252  }
1253 
1254  assert(names.size() > 0);
1255 
1256  // names[0] must be 'g', so skip the 0th element.
1257  if (names.size() > 1) {
1258  name = names[1];
1259  } else {
1260  name = "";
1261  }
1262 
1263  continue;
1264  }
1265 
1266  // object name
1267  if (token[0] == 'o' && IS_SPACE((token[1]))) {
1268  // flush previous face group.
1269  bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name,
1270  triangulate);
1271  if (ret) {
1272  shapes->push_back(shape);
1273  }
1274 
1275  // material = -1;
1276  faceGroup.clear();
1277  shape = shape_t();
1278 
1279  // @todo { multiple object name? }
1280  char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE];
1281  token += 2;
1282 #ifdef _MSC_VER
1283  sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf));
1284 #else
1285  sscanf(token, "%s", namebuf);
1286 #endif
1287  name = std::string(namebuf);
1288 
1289  continue;
1290  }
1291 
1292  if (token[0] == 't' && IS_SPACE(token[1])) {
1293  tag_t tag;
1294 
1295  char namebuf[4096];
1296  token += 2;
1297 #ifdef _MSC_VER
1298  sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf));
1299 #else
1300  sscanf(token, "%s", namebuf);
1301 #endif
1302  tag.name = std::string(namebuf);
1303 
1304  token += tag.name.size() + 1;
1305 
1306  tag_sizes ts = parseTagTriple(&token);
1307 
1308  tag.intValues.resize(static_cast<size_t>(ts.num_ints));
1309 
1310  for (size_t i = 0; i < static_cast<size_t>(ts.num_ints); ++i) {
1311  tag.intValues[i] = atoi(token);
1312  token += strcspn(token, "/ \t\r") + 1;
1313  }
1314 
1315  tag.floatValues.resize(static_cast<size_t>(ts.num_floats));
1316  for (size_t i = 0; i < static_cast<size_t>(ts.num_floats); ++i) {
1317  tag.floatValues[i] = parseFloat(&token);
1318  token += strcspn(token, "/ \t\r") + 1;
1319  }
1320 
1321  tag.stringValues.resize(static_cast<size_t>(ts.num_strings));
1322  for (size_t i = 0; i < static_cast<size_t>(ts.num_strings); ++i) {
1323  char stringValueBuffer[4096];
1324 
1325 #ifdef _MSC_VER
1326  sscanf_s(token, "%s", stringValueBuffer,
1327  (unsigned)_countof(stringValueBuffer));
1328 #else
1329  sscanf(token, "%s", stringValueBuffer);
1330 #endif
1331  tag.stringValues[i] = stringValueBuffer;
1332  token += tag.stringValues[i].size() + 1;
1333  }
1334 
1335  tags.push_back(tag);
1336  }
1337 
1338  // Ignore unknown command.
1339  }
1340 
1341  bool ret = exportFaceGroupToShape(&shape, faceGroup, tags, material, name,
1342  triangulate);
1343  if (ret) {
1344  shapes->push_back(shape);
1345  }
1346  faceGroup.clear(); // for safety
1347 
1348  if (err) {
1349  (*err) += errss.str();
1350  }
1351 
1352  attrib->vertices.swap(v);
1353  attrib->normals.swap(vn);
1354  attrib->texcoords.swap(vt);
1355 
1356  return true;
1357 }
1358 
1359 bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback,
1360  void *user_data /*= NULL*/,
1361  MaterialReader *readMatFn /*= NULL*/,
1362  std::string *err /*= NULL*/) {
1363  std::stringstream errss;
1364 
1365  // material
1366  std::map<std::string, int> material_map;
1367  int material_id = -1; // -1 = invalid
1368 
1369  std::vector<index_t> indices;
1370  std::vector<material_t> materials;
1371  std::vector<std::string> names;
1372  names.reserve(2);
1373  std::string name;
1374  std::vector<const char *> names_out;
1375 
1376  std::string linebuf;
1377  while (inStream.peek() != -1) {
1378  safeGetline(inStream, linebuf);
1379 
1380  // Trim newline '\r\n' or '\n'
1381  if (linebuf.size() > 0) {
1382  if (linebuf[linebuf.size() - 1] == '\n')
1383  linebuf.erase(linebuf.size() - 1);
1384  }
1385  if (linebuf.size() > 0) {
1386  if (linebuf[linebuf.size() - 1] == '\r')
1387  linebuf.erase(linebuf.size() - 1);
1388  }
1389 
1390  // Skip if empty line.
1391  if (linebuf.empty()) {
1392  continue;
1393  }
1394 
1395  // Skip leading space.
1396  const char *token = linebuf.c_str();
1397  token += strspn(token, " \t");
1398 
1399  assert(token);
1400  if (token[0] == '\0') continue; // empty line
1401 
1402  if (token[0] == '#') continue; // comment line
1403 
1404  // vertex
1405  if (token[0] == 'v' && IS_SPACE((token[1]))) {
1406  token += 2;
1407  float x, y, z, w; // w is optional. default = 1.0
1408  parseV(&x, &y, &z, &w, &token);
1409  if (callback.vertex_cb) {
1410  callback.vertex_cb(user_data, x, y, z, w);
1411  }
1412  continue;
1413  }
1414 
1415  // normal
1416  if (token[0] == 'v' && token[1] == 'n' && IS_SPACE((token[2]))) {
1417  token += 3;
1418  float x, y, z;
1419  parseFloat3(&x, &y, &z, &token);
1420  if (callback.normal_cb) {
1421  callback.normal_cb(user_data, x, y, z);
1422  }
1423  continue;
1424  }
1425 
1426  // texcoord
1427  if (token[0] == 'v' && token[1] == 't' && IS_SPACE((token[2]))) {
1428  token += 3;
1429  float x, y, z; // y and z are optional. default = 0.0
1430  parseFloat3(&x, &y, &z, &token);
1431  if (callback.texcoord_cb) {
1432  callback.texcoord_cb(user_data, x, y, z);
1433  }
1434  continue;
1435  }
1436 
1437  // face
1438  if (token[0] == 'f' && IS_SPACE((token[1]))) {
1439  token += 2;
1440  token += strspn(token, " \t");
1441 
1442  indices.clear();
1443  while (!IS_NEW_LINE(token[0])) {
1444  vertex_index vi = parseRawTriple(&token);
1445 
1446  index_t idx;
1447  idx.vertex_index = vi.v_idx;
1448  idx.normal_index = vi.vn_idx;
1449  idx.texcoord_index = vi.vt_idx;
1450 
1451  indices.push_back(idx);
1452  size_t n = strspn(token, " \t\r");
1453  token += n;
1454  }
1455 
1456  if (callback.index_cb && indices.size() > 0) {
1457  callback.index_cb(user_data, &indices.at(0),
1458  static_cast<int>(indices.size()));
1459  }
1460 
1461  continue;
1462  }
1463 
1464  // use mtl
1465  if ((0 == strncmp(token, "usemtl", 6)) && IS_SPACE((token[6]))) {
1466  char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE];
1467  token += 7;
1468 #ifdef _MSC_VER
1469  sscanf_s(token, "%s", namebuf,
1470  static_cast<unsigned int>(_countof(namebuf)));
1471 #else
1472  sscanf(token, "%s", namebuf);
1473 #endif
1474 
1475  int newMaterialId = -1;
1476  if (material_map.find(namebuf) != material_map.end()) {
1477  newMaterialId = material_map[namebuf];
1478  } else {
1479  // { error!! material not found }
1480  }
1481 
1482  if (newMaterialId != material_id) {
1483  material_id = newMaterialId;
1484  }
1485 
1486  if (callback.usemtl_cb) {
1487  callback.usemtl_cb(user_data, namebuf, material_id);
1488  }
1489 
1490  continue;
1491  }
1492 
1493  // load mtl
1494  if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) {
1495  if (readMatFn) {
1496  char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE];
1497  token += 7;
1498 #ifdef _MSC_VER
1499  sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf));
1500 #else
1501  sscanf(token, "%s", namebuf);
1502 #endif
1503 
1504  std::string err_mtl;
1505  materials.clear();
1506  bool ok = (*readMatFn)(namebuf, &materials, &material_map, &err_mtl);
1507  if (err) {
1508  (*err) += err_mtl;
1509  }
1510 
1511  if (!ok) {
1512  return false;
1513  }
1514 
1515  if (callback.mtllib_cb) {
1516  callback.mtllib_cb(user_data, &materials.at(0),
1517  static_cast<int>(materials.size()));
1518  }
1519  }
1520 
1521  continue;
1522  }
1523 
1524  // group name
1525  if (token[0] == 'g' && IS_SPACE((token[1]))) {
1526  names.clear();
1527 
1528  while (!IS_NEW_LINE(token[0])) {
1529  std::string str = parseString(&token);
1530  names.push_back(str);
1531  token += strspn(token, " \t\r"); // skip tag
1532  }
1533 
1534  assert(names.size() > 0);
1535 
1536  // names[0] must be 'g', so skip the 0th element.
1537  if (names.size() > 1) {
1538  name = names[1];
1539  } else {
1540  name.clear();
1541  }
1542 
1543  if (callback.group_cb) {
1544  if (names.size() > 1) {
1545  // create const char* array.
1546  names_out.resize(names.size() - 1);
1547  for (size_t j = 0; j < names_out.size(); j++) {
1548  names_out[j] = names[j + 1].c_str();
1549  }
1550  callback.group_cb(user_data, &names_out.at(0),
1551  static_cast<int>(names_out.size()));
1552 
1553  } else {
1554  callback.group_cb(user_data, NULL, 0);
1555  }
1556  }
1557 
1558  continue;
1559  }
1560 
1561  // object name
1562  if (token[0] == 'o' && IS_SPACE((token[1]))) {
1563  // @todo { multiple object name? }
1564  char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE];
1565  token += 2;
1566 #ifdef _MSC_VER
1567  sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf));
1568 #else
1569  sscanf(token, "%s", namebuf);
1570 #endif
1571  std::string object_name = std::string(namebuf);
1572 
1573  if (callback.object_cb) {
1574  callback.object_cb(user_data, object_name.c_str());
1575  }
1576 
1577  continue;
1578  }
1579 
1580 #if 0 // @todo
1581  if (token[0] == 't' && IS_SPACE(token[1])) {
1582  tag_t tag;
1583 
1584  char namebuf[4096];
1585  token += 2;
1586 #ifdef _MSC_VER
1587  sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf));
1588 #else
1589  sscanf(token, "%s", namebuf);
1590 #endif
1591  tag.name = std::string(namebuf);
1592 
1593  token += tag.name.size() + 1;
1594 
1595  tag_sizes ts = parseTagTriple(&token);
1596 
1597  tag.intValues.resize(static_cast<size_t>(ts.num_ints));
1598 
1599  for (size_t i = 0; i < static_cast<size_t>(ts.num_ints); ++i) {
1600  tag.intValues[i] = atoi(token);
1601  token += strcspn(token, "/ \t\r") + 1;
1602  }
1603 
1604  tag.floatValues.resize(static_cast<size_t>(ts.num_floats));
1605  for (size_t i = 0; i < static_cast<size_t>(ts.num_floats); ++i) {
1606  tag.floatValues[i] = parseFloat(&token);
1607  token += strcspn(token, "/ \t\r") + 1;
1608  }
1609 
1610  tag.stringValues.resize(static_cast<size_t>(ts.num_strings));
1611  for (size_t i = 0; i < static_cast<size_t>(ts.num_strings); ++i) {
1612  char stringValueBuffer[4096];
1613 
1614 #ifdef _MSC_VER
1615  sscanf_s(token, "%s", stringValueBuffer,
1616  (unsigned)_countof(stringValueBuffer));
1617 #else
1618  sscanf(token, "%s", stringValueBuffer);
1619 #endif
1620  tag.stringValues[i] = stringValueBuffer;
1621  token += tag.stringValues[i].size() + 1;
1622  }
1623 
1624  tags.push_back(tag);
1625  }
1626 #endif
1627 
1628  // Ignore unknown command.
1629  }
1630 
1631  if (err) {
1632  (*err) += errss.str();
1633  }
1634 
1635  return true;
1636 }
1637 } // namespace tinyobj
1638 
1639 #endif
1640 
1641 #endif // TINY_OBJ_LOADER_H_
1642 #endif
tiny_obj_loader.h
sign
static double sign(double a, double b)
Definition: dopri5.c:77