1 #define TINYOBJLOADER_IMPLEMENTATION
2 #include "../tiny_obj_loader.h"
3 
4 #if defined(__clang__)
5 #pragma clang diagnostic push
6 #pragma clang diagnostic ignored "-Weverything"
7 #elif defined(__GNUC__)
8 #pragma GCC diagnostic ignored "-Wmissing-declarations"
9 #pragma GCC diagnostic ignored "-Wignored-qualifiers"
10 #pragma GCC diagnostic push
11 #pragma GCC diagnostic ignored "-Wcast-qual"
12 #pragma GCC diagnostic ignored "-Wsign-conversion"
13 #pragma GCC diagnostic ignored "-Wformat"
14 #pragma GCC diagnostic ignored "-Wswitch-default"
15 #endif
16 
17 
18 #include "acutest.h"
19 
20 #if defined(__clang__)
21 #pragma clang diagnostic pop
22 #elif defined(__GNUC__)
23 #pragma GCC diagnostic pop
24 #endif
25 
26 #include <cassert>
27 #include <cstdio>
28 #include <cstdlib>
29 #include <fstream>
30 #include <iostream>
31 #include <sstream>
32 
33 template <typename T>
FloatEquals(const T & a,const T & b)34 static bool FloatEquals(const T& a, const T& b) {
35   // Edit eps value as you wish.
36   const T eps = std::numeric_limits<T>::epsilon() * static_cast<T>(100);
37 
38   const T abs_diff = std::abs(a - b);
39 
40   if (abs_diff < eps) {
41     return true;
42   } else {
43     return false;
44   }
45 }
46 
PrintInfo(const tinyobj::attrib_t & attrib,const std::vector<tinyobj::shape_t> & shapes,const std::vector<tinyobj::material_t> & materials,bool triangulate=true)47 static void PrintInfo(const tinyobj::attrib_t& attrib,
48                       const std::vector<tinyobj::shape_t>& shapes,
49                       const std::vector<tinyobj::material_t>& materials,
50                       bool triangulate = true) {
51   std::cout << "# of vertices  : " << (attrib.vertices.size() / 3) << std::endl;
52   std::cout << "# of normals   : " << (attrib.normals.size() / 3) << std::endl;
53   std::cout << "# of texcoords : " << (attrib.texcoords.size() / 2)
54             << std::endl;
55 
56   std::cout << "# of shapes    : " << shapes.size() << std::endl;
57   std::cout << "# of materials : " << materials.size() << std::endl;
58 
59   for (size_t v = 0; v < attrib.vertices.size() / 3; v++) {
60     printf("  v[%ld] = (%f, %f, %f)\n", v,
61            static_cast<const double>(attrib.vertices[3 * v + 0]),
62            static_cast<const double>(attrib.vertices[3 * v + 1]),
63            static_cast<const double>(attrib.vertices[3 * v + 2]));
64   }
65 
66   for (size_t v = 0; v < attrib.normals.size() / 3; v++) {
67     printf("  n[%ld] = (%f, %f, %f)\n", v,
68            static_cast<const double>(attrib.normals[3 * v + 0]),
69            static_cast<const double>(attrib.normals[3 * v + 1]),
70            static_cast<const double>(attrib.normals[3 * v + 2]));
71   }
72 
73   for (size_t v = 0; v < attrib.texcoords.size() / 2; v++) {
74     printf("  uv[%ld] = (%f, %f)\n", v,
75            static_cast<const double>(attrib.texcoords[2 * v + 0]),
76            static_cast<const double>(attrib.texcoords[2 * v + 1]));
77   }
78 
79   for (size_t i = 0; i < shapes.size(); i++) {
80     printf("shape[%ld].name = %s\n", i, shapes[i].name.c_str());
81     printf("Size of shape[%ld].indices: %ld\n", i,
82            shapes[i].mesh.indices.size());
83 
84     if (triangulate) {
85       printf("Size of shape[%ld].material_ids: %ld\n", i,
86              shapes[i].mesh.material_ids.size());
87       assert((shapes[i].mesh.indices.size() % 3) == 0);
88       for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) {
89         tinyobj::index_t i0 = shapes[i].mesh.indices[3 * f + 0];
90         tinyobj::index_t i1 = shapes[i].mesh.indices[3 * f + 1];
91         tinyobj::index_t i2 = shapes[i].mesh.indices[3 * f + 2];
92         printf("  idx[%ld] = %d/%d/%d, %d/%d/%d, %d/%d/%d. mat_id = %d\n", f,
93                i0.vertex_index, i0.normal_index, i0.texcoord_index,
94                i1.vertex_index, i1.normal_index, i1.texcoord_index,
95                i2.vertex_index, i2.normal_index, i2.texcoord_index,
96                shapes[i].mesh.material_ids[f]);
97       }
98     } else {
99       for (size_t f = 0; f < shapes[i].mesh.indices.size(); f++) {
100         tinyobj::index_t idx = shapes[i].mesh.indices[f];
101         printf("  idx[%ld] = %d/%d/%d\n", f, idx.vertex_index, idx.normal_index,
102                idx.texcoord_index);
103       }
104 
105       printf("Size of shape[%ld].material_ids: %ld\n", i,
106              shapes[i].mesh.material_ids.size());
107       assert(shapes[i].mesh.material_ids.size() ==
108              shapes[i].mesh.num_face_vertices.size());
109       for (size_t m = 0; m < shapes[i].mesh.material_ids.size(); m++) {
110         printf("  material_id[%ld] = %d\n", m, shapes[i].mesh.material_ids[m]);
111       }
112     }
113 
114     printf("shape[%ld].num_faces: %ld\n", i,
115            shapes[i].mesh.num_face_vertices.size());
116     for (size_t v = 0; v < shapes[i].mesh.num_face_vertices.size(); v++) {
117       printf("  num_vertices[%ld] = %ld\n", v,
118              static_cast<long>(shapes[i].mesh.num_face_vertices[v]));
119     }
120 
121     // printf("shape[%ld].vertices: %ld\n", i, shapes[i].mesh.positions.size());
122     // assert((shapes[i].mesh.positions.size() % 3) == 0);
123     // for (size_t v = 0; v < shapes[i].mesh.positions.size() / 3; v++) {
124     //  printf("  v[%ld] = (%f, %f, %f)\n", v,
125     //    static_cast<const double>(shapes[i].mesh.positions[3*v+0]),
126     //    static_cast<const double>(shapes[i].mesh.positions[3*v+1]),
127     //    static_cast<const double>(shapes[i].mesh.positions[3*v+2]));
128     //}
129 
130     printf("shape[%ld].num_tags: %ld\n", i, shapes[i].mesh.tags.size());
131     for (size_t t = 0; t < shapes[i].mesh.tags.size(); t++) {
132       printf("  tag[%ld] = %s ", t, shapes[i].mesh.tags[t].name.c_str());
133       printf(" ints: [");
134       for (size_t j = 0; j < shapes[i].mesh.tags[t].intValues.size(); ++j) {
135         printf("%ld", static_cast<long>(shapes[i].mesh.tags[t].intValues[j]));
136         if (j < (shapes[i].mesh.tags[t].intValues.size() - 1)) {
137           printf(", ");
138         }
139       }
140       printf("]");
141 
142       printf(" floats: [");
143       for (size_t j = 0; j < shapes[i].mesh.tags[t].floatValues.size(); ++j) {
144         printf("%f", static_cast<const double>(
145                          shapes[i].mesh.tags[t].floatValues[j]));
146         if (j < (shapes[i].mesh.tags[t].floatValues.size() - 1)) {
147           printf(", ");
148         }
149       }
150       printf("]");
151 
152       printf(" strings: [");
153       for (size_t j = 0; j < shapes[i].mesh.tags[t].stringValues.size(); ++j) {
154         printf("%s", shapes[i].mesh.tags[t].stringValues[j].c_str());
155         if (j < (shapes[i].mesh.tags[t].stringValues.size() - 1)) {
156           printf(", ");
157         }
158       }
159       printf("]");
160       printf("\n");
161     }
162   }
163 
164   for (size_t i = 0; i < materials.size(); i++) {
165     printf("material[%ld].name = %s\n", i, materials[i].name.c_str());
166     printf("  material.Ka = (%f, %f ,%f)\n",
167            static_cast<const double>(materials[i].ambient[0]),
168            static_cast<const double>(materials[i].ambient[1]),
169            static_cast<const double>(materials[i].ambient[2]));
170     printf("  material.Kd = (%f, %f ,%f)\n",
171            static_cast<const double>(materials[i].diffuse[0]),
172            static_cast<const double>(materials[i].diffuse[1]),
173            static_cast<const double>(materials[i].diffuse[2]));
174     printf("  material.Ks = (%f, %f ,%f)\n",
175            static_cast<const double>(materials[i].specular[0]),
176            static_cast<const double>(materials[i].specular[1]),
177            static_cast<const double>(materials[i].specular[2]));
178     printf("  material.Tr = (%f, %f ,%f)\n",
179            static_cast<const double>(materials[i].transmittance[0]),
180            static_cast<const double>(materials[i].transmittance[1]),
181            static_cast<const double>(materials[i].transmittance[2]));
182     printf("  material.Ke = (%f, %f ,%f)\n",
183            static_cast<const double>(materials[i].emission[0]),
184            static_cast<const double>(materials[i].emission[1]),
185            static_cast<const double>(materials[i].emission[2]));
186     printf("  material.Ns = %f\n",
187            static_cast<const double>(materials[i].shininess));
188     printf("  material.Ni = %f\n", static_cast<const double>(materials[i].ior));
189     printf("  material.dissolve = %f\n",
190            static_cast<const double>(materials[i].dissolve));
191     printf("  material.illum = %d\n", materials[i].illum);
192     printf("  material.map_Ka = %s\n", materials[i].ambient_texname.c_str());
193     printf("  material.map_Kd = %s\n", materials[i].diffuse_texname.c_str());
194     printf("  material.map_Ks = %s\n", materials[i].specular_texname.c_str());
195     printf("  material.map_Ns = %s\n",
196            materials[i].specular_highlight_texname.c_str());
197     printf("  material.map_bump = %s\n", materials[i].bump_texname.c_str());
198     printf("  material.map_d = %s\n", materials[i].alpha_texname.c_str());
199     printf("  material.disp = %s\n", materials[i].displacement_texname.c_str());
200     printf("  material.refl = %s\n", materials[i].reflection_texname.c_str());
201     std::map<std::string, std::string>::const_iterator it(
202         materials[i].unknown_parameter.begin());
203     std::map<std::string, std::string>::const_iterator itEnd(
204         materials[i].unknown_parameter.end());
205 
206     for (; it != itEnd; it++) {
207       printf("  material.%s = %s\n", it->first.c_str(), it->second.c_str());
208     }
209     printf("\n");
210   }
211 }
212 
TestLoadObj(const char * filename,const char * basepath=NULL,bool triangulate=true)213 static bool TestLoadObj(const char* filename, const char* basepath = NULL,
214                         bool triangulate = true) {
215   std::cout << "Loading " << filename << std::endl;
216 
217   tinyobj::attrib_t attrib;
218   std::vector<tinyobj::shape_t> shapes;
219   std::vector<tinyobj::material_t> materials;
220 
221   std::string warn;
222   std::string err;
223   bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err,
224                               filename, basepath, triangulate);
225 
226   if (!warn.empty()) {
227     std::cout << "WARN: " << warn << std::endl;
228   }
229 
230   if (!err.empty()) {
231     std::cerr << "ERR: " << err << std::endl;
232   }
233 
234   if (!ret) {
235     printf("Failed to load/parse .obj.\n");
236     return false;
237   }
238 
239   PrintInfo(attrib, shapes, materials, triangulate);
240 
241   return true;
242 }
243 
TestLoadObjFromPreopenedFile(const char * filename,const char * basepath=NULL,bool readMaterials=true,bool triangulate=true)244 static bool TestLoadObjFromPreopenedFile(const char* filename,
245                                          const char* basepath = NULL,
246                                          bool readMaterials = true,
247                                          bool triangulate = true) {
248   std::string fullFilename = std::string(basepath) + filename;
249   std::cout << "Loading " << fullFilename << std::endl;
250 
251   std::ifstream fileStream(fullFilename.c_str());
252 
253   if (!fileStream) {
254     std::cerr << "Could not find specified file: " << fullFilename << std::endl;
255     return false;
256   }
257 
258   tinyobj::MaterialStreamReader materialStreamReader(fileStream);
259   tinyobj::MaterialStreamReader* materialReader =
260       readMaterials ? &materialStreamReader : NULL;
261 
262   tinyobj::attrib_t attrib;
263   std::vector<tinyobj::shape_t> shapes;
264   std::vector<tinyobj::material_t> materials;
265 
266   std::string warn;
267   std::string err;
268   bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err,
269                               &fileStream, materialReader);
270 
271   if (!warn.empty()) {
272     std::cout << "WARN: " << warn << std::endl;
273   }
274 
275   if (!err.empty()) {
276     std::cerr << "ERR: " << err << std::endl;
277   }
278 
279   if (!ret) {
280     printf("Failed to load/parse .obj.\n");
281     return false;
282   }
283 
284   std::cout << "Loaded material count: " << materials.size() << "\n";
285 
286   return true;
287 }
288 
TestStreamLoadObj()289 static bool TestStreamLoadObj() {
290   std::cout << "Stream Loading " << std::endl;
291 
292   std::stringstream objStream;
293   objStream << "mtllib cube.mtl\n"
294                "\n"
295                "v 0.000000 2.000000 2.000000\n"
296                "v 0.000000 0.000000 2.000000\n"
297                "v 2.000000 0.000000 2.000000\n"
298                "v 2.000000 2.000000 2.000000\n"
299                "v 0.000000 2.000000 0.000000\n"
300                "v 0.000000 0.000000 0.000000\n"
301                "v 2.000000 0.000000 0.000000\n"
302                "v 2.000000 2.000000 0.000000\n"
303                "# 8 vertices\n"
304                "\n"
305                "g front cube\n"
306                "usemtl white\n"
307                "f 1 2 3 4\n"
308                "g back cube\n"
309                "# expects white material\n"
310                "f 8 7 6 5\n"
311                "g right cube\n"
312                "usemtl red\n"
313                "f 4 3 7 8\n"
314                "g top cube\n"
315                "usemtl white\n"
316                "f 5 1 4 8\n"
317                "g left cube\n"
318                "usemtl green\n"
319                "f 5 6 2 1\n"
320                "g bottom cube\n"
321                "usemtl white\n"
322                "f 2 6 7 3\n"
323                "# 6 elements";
324 
325   std::string matStream(
326       "newmtl white\n"
327       "Ka 0 0 0\n"
328       "Kd 1 1 1\n"
329       "Ks 0 0 0\n"
330       "\n"
331       "newmtl red\n"
332       "Ka 0 0 0\n"
333       "Kd 1 0 0\n"
334       "Ks 0 0 0\n"
335       "\n"
336       "newmtl green\n"
337       "Ka 0 0 0\n"
338       "Kd 0 1 0\n"
339       "Ks 0 0 0\n"
340       "\n"
341       "newmtl blue\n"
342       "Ka 0 0 0\n"
343       "Kd 0 0 1\n"
344       "Ks 0 0 0\n"
345       "\n"
346       "newmtl light\n"
347       "Ka 20 20 20\n"
348       "Kd 1 1 1\n"
349       "Ks 0 0 0");
350 
351   using namespace tinyobj;
352   class MaterialStringStreamReader : public MaterialReader {
353    public:
354     MaterialStringStreamReader(const std::string& matSStream)
355         : m_matSStream(matSStream) {}
356     virtual ~MaterialStringStreamReader() {}
357     virtual bool operator()(const std::string& matId,
358                             std::vector<material_t>* materials,
359                             std::map<std::string, int>* matMap,
360                             std::string* warn, std::string* err) {
361       (void)matId;
362       (void)warn;
363       (void)err;
364       std::string warning;
365       std::string error_msg;
366       LoadMtl(matMap, materials, &m_matSStream, &warning, &error_msg);
367       return true;
368     }
369 
370    private:
371     std::stringstream m_matSStream;
372   };
373 
374   MaterialStringStreamReader matSSReader(matStream);
375   tinyobj::attrib_t attrib;
376   std::vector<tinyobj::shape_t> shapes;
377   std::vector<tinyobj::material_t> materials;
378   std::string warn;
379   std::string err;
380   bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err,
381                               &objStream, &matSSReader);
382 
383   if (!warn.empty()) {
384     std::cout << "WARN: " << warn << std::endl;
385   }
386 
387   if (!err.empty()) {
388     std::cerr << "ERR: " << err << std::endl;
389   }
390 
391   if (!ret) {
392     return false;
393   }
394 
395   PrintInfo(attrib, shapes, materials);
396 
397   return true;
398 }
399 
400 const char* gMtlBasePath = "../models/";
401 
test_cornell_box()402 void test_cornell_box() {
403   TEST_CHECK(true == TestLoadObj("../models/cornell_box.obj", gMtlBasePath));
404 }
405 
test_catmark_torus_creases0()406 void test_catmark_torus_creases0() {
407   tinyobj::attrib_t attrib;
408   std::vector<tinyobj::shape_t> shapes;
409   std::vector<tinyobj::material_t> materials;
410 
411   std::string warn;
412   std::string err;
413   bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err,
414                               "../models/catmark_torus_creases0.obj",
415                               gMtlBasePath, /*triangulate*/ false);
416 
417   if (!warn.empty()) {
418     std::cout << "WARN: " << warn << std::endl;
419   }
420 
421   if (!err.empty()) {
422     std::cerr << "ERR: " << err << std::endl;
423   }
424 
425   TEST_CHECK(true == ret);
426 
427   TEST_CHECK(1 == shapes.size());
428   TEST_CHECK(8 == shapes[0].mesh.tags.size());
429 }
430 
test_pbr()431 void test_pbr() {
432   tinyobj::attrib_t attrib;
433   std::vector<tinyobj::shape_t> shapes;
434   std::vector<tinyobj::material_t> materials;
435 
436   std::string warn;
437   std::string err;
438   bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err,
439                               "../models/pbr-mat-ext.obj", gMtlBasePath,
440                               /*triangulate*/ false);
441 
442   if (!warn.empty()) {
443     std::cout << "WARN: " << warn << std::endl;
444   }
445 
446   if (!err.empty()) {
447     std::cerr << "ERR: " << err << std::endl;
448   }
449   TEST_CHECK(true == ret);
450   TEST_CHECK(1 == materials.size());
451   TEST_CHECK(FloatEquals(0.2f, materials[0].roughness));
452   TEST_CHECK(FloatEquals(0.3f, materials[0].metallic));
453   TEST_CHECK(FloatEquals(0.4f, materials[0].sheen));
454   TEST_CHECK(FloatEquals(0.5f, materials[0].clearcoat_thickness));
455   TEST_CHECK(FloatEquals(0.6f, materials[0].clearcoat_roughness));
456   TEST_CHECK(FloatEquals(0.7f, materials[0].anisotropy));
457   TEST_CHECK(FloatEquals(0.8f, materials[0].anisotropy_rotation));
458   TEST_CHECK(0 == materials[0].roughness_texname.compare("roughness.tex"));
459   TEST_CHECK(0 == materials[0].metallic_texname.compare("metallic.tex"));
460   TEST_CHECK(0 == materials[0].sheen_texname.compare("sheen.tex"));
461   TEST_CHECK(0 == materials[0].emissive_texname.compare("emissive.tex"));
462   TEST_CHECK(0 == materials[0].normal_texname.compare("normalmap.tex"));
463 }
464 
test_stream_load()465 void test_stream_load() { TEST_CHECK(true == TestStreamLoadObj()); }
466 
test_stream_load_from_file_skipping_materials()467 void test_stream_load_from_file_skipping_materials() {
468   TEST_CHECK(true == TestLoadObjFromPreopenedFile(
469                          "../models/pbr-mat-ext.obj", gMtlBasePath,
470                          /*readMaterials*/ false, /*triangulate*/ false));
471 }
472 
test_stream_load_from_file_with_materials()473 void test_stream_load_from_file_with_materials() {
474   TEST_CHECK(true == TestLoadObjFromPreopenedFile(
475                          "../models/pbr-mat-ext.obj", gMtlBasePath,
476                          /*readMaterials*/ true, /*triangulate*/ false));
477 }
478 
test_trailing_whitespace_in_mtl_issue92()479 void test_trailing_whitespace_in_mtl_issue92() {
480   tinyobj::attrib_t attrib;
481   std::vector<tinyobj::shape_t> shapes;
482   std::vector<tinyobj::material_t> materials;
483 
484   std::string warn;
485   std::string err;
486   bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err,
487                               "../models/issue-92.obj", gMtlBasePath);
488 
489   if (!warn.empty()) {
490     std::cout << "WARN: " << warn << std::endl;
491   }
492 
493   if (!err.empty()) {
494     std::cerr << "ERR: " << err << std::endl;
495   }
496   TEST_CHECK(true == ret);
497   TEST_CHECK(1 == materials.size());
498   TEST_CHECK(0 == materials[0].diffuse_texname.compare("tmp.png"));
499 }
500 
test_transmittance_filter_issue95()501 void test_transmittance_filter_issue95() {
502   tinyobj::attrib_t attrib;
503   std::vector<tinyobj::shape_t> shapes;
504   std::vector<tinyobj::material_t> materials;
505 
506   std::string warn;
507   std::string err;
508   bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err,
509                               "../models/issue-95.obj", gMtlBasePath);
510 
511   if (!warn.empty()) {
512     std::cout << "WARN: " << warn << std::endl;
513   }
514 
515   if (!err.empty()) {
516     std::cerr << "ERR: " << err << std::endl;
517   }
518   TEST_CHECK(true == ret);
519   TEST_CHECK(1 == materials.size());
520   TEST_CHECK(FloatEquals(0.1f, materials[0].transmittance[0]));
521   TEST_CHECK(FloatEquals(0.2f, materials[0].transmittance[1]));
522   TEST_CHECK(FloatEquals(0.3f, materials[0].transmittance[2]));
523 }
524 
test_transmittance_filter_Tf_issue95()525 void test_transmittance_filter_Tf_issue95() {
526   tinyobj::attrib_t attrib;
527   std::vector<tinyobj::shape_t> shapes;
528   std::vector<tinyobj::material_t> materials;
529 
530   std::string warn;
531   std::string err;
532   bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err,
533                               "../models/issue-95-2.obj", gMtlBasePath);
534 
535   if (!warn.empty()) {
536     std::cout << "WARN: " << warn << std::endl;
537   }
538 
539   if (!err.empty()) {
540     std::cerr << "ERR: " << err << std::endl;
541   }
542   TEST_CHECK(true == ret);
543   TEST_CHECK(1 == materials.size());
544   TEST_CHECK(FloatEquals(0.1f, materials[0].transmittance[0]));
545   TEST_CHECK(FloatEquals(0.2f, materials[0].transmittance[1]));
546   TEST_CHECK(FloatEquals(0.3f, materials[0].transmittance[2]));
547 }
548 
test_transmittance_filter_Kt_issue95()549 void test_transmittance_filter_Kt_issue95() {
550   tinyobj::attrib_t attrib;
551   std::vector<tinyobj::shape_t> shapes;
552   std::vector<tinyobj::material_t> materials;
553 
554   std::string warn;
555   std::string err;
556   bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err,
557                               "../models/issue-95.obj", gMtlBasePath);
558 
559   if (!warn.empty()) {
560     std::cout << "WARN: " << warn << std::endl;
561   }
562 
563   if (!err.empty()) {
564     std::cerr << "ERR: " << err << std::endl;
565   }
566   TEST_CHECK(true == ret);
567   TEST_CHECK(1 == materials.size());
568   TEST_CHECK(FloatEquals(0.1f, materials[0].transmittance[0]));
569   TEST_CHECK(FloatEquals(0.2f, materials[0].transmittance[1]));
570   TEST_CHECK(FloatEquals(0.3f, materials[0].transmittance[2]));
571 }
572 
test_usemtl_at_last_line_issue104()573 void test_usemtl_at_last_line_issue104() {
574   tinyobj::attrib_t attrib;
575   std::vector<tinyobj::shape_t> shapes;
576   std::vector<tinyobj::material_t> materials;
577 
578   std::string warn;
579   std::string err;
580   bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err,
581                               "../models/usemtl-issue-104.obj", gMtlBasePath);
582 
583   if (!warn.empty()) {
584     std::cout << "WARN: " << warn << std::endl;
585   }
586 
587   if (!err.empty()) {
588     std::cerr << "ERR: " << err << std::endl;
589   }
590   TEST_CHECK(true == ret);
591   TEST_CHECK(1 == shapes.size());
592 }
593 
test_texture_opts_issue85()594 void test_texture_opts_issue85() {
595   tinyobj::attrib_t attrib;
596   std::vector<tinyobj::shape_t> shapes;
597   std::vector<tinyobj::material_t> materials;
598 
599   std::string warn;
600   std::string err;
601   bool ret =
602       tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err,
603                        "../models/texture-options-issue-85.obj", gMtlBasePath);
604 
605   if (!warn.empty()) {
606     std::cout << "WARN: " << warn << std::endl;
607   }
608 
609   if (!err.empty()) {
610     std::cerr << "ERR: " << err << std::endl;
611   }
612   TEST_CHECK(true == ret);
613   TEST_CHECK(1 == shapes.size());
614   TEST_CHECK(3 == materials.size());
615   TEST_CHECK(0 == materials[0].name.compare("default"));
616   TEST_CHECK(0 == materials[1].name.compare("bm2"));
617   TEST_CHECK(0 == materials[2].name.compare("bm3"));
618   TEST_CHECK(true == materials[0].ambient_texopt.clamp);
619   TEST_CHECK(FloatEquals(0.1f, materials[0].diffuse_texopt.origin_offset[0]));
620   TEST_CHECK(FloatEquals(0.0f, materials[0].diffuse_texopt.origin_offset[1]));
621   TEST_CHECK(FloatEquals(0.0f, materials[0].diffuse_texopt.origin_offset[2]));
622   TEST_CHECK(FloatEquals(0.1f, materials[0].specular_texopt.scale[0]));
623   TEST_CHECK(FloatEquals(0.2f, materials[0].specular_texopt.scale[1]));
624   TEST_CHECK(FloatEquals(1.0f, materials[0].specular_texopt.scale[2]));
625   TEST_CHECK(
626       FloatEquals(0.1f, materials[0].specular_highlight_texopt.turbulence[0]));
627   TEST_CHECK(
628       FloatEquals(0.2f, materials[0].specular_highlight_texopt.turbulence[1]));
629   TEST_CHECK(
630       FloatEquals(0.3f, materials[0].specular_highlight_texopt.turbulence[2]));
631   TEST_CHECK(FloatEquals(3.0f, materials[0].bump_texopt.bump_multiplier));
632 
633   TEST_CHECK(
634       FloatEquals(0.1f, materials[1].specular_highlight_texopt.brightness));
635   TEST_CHECK(
636       FloatEquals(0.3f, materials[1].specular_highlight_texopt.contrast));
637   TEST_CHECK('r' == materials[1].bump_texopt.imfchan);
638 
639   TEST_CHECK(tinyobj::TEXTURE_TYPE_SPHERE == materials[2].diffuse_texopt.type);
640   TEST_CHECK(tinyobj::TEXTURE_TYPE_CUBE_TOP ==
641              materials[2].specular_texopt.type);
642   TEST_CHECK(tinyobj::TEXTURE_TYPE_CUBE_BOTTOM ==
643              materials[2].specular_highlight_texopt.type);
644   TEST_CHECK(tinyobj::TEXTURE_TYPE_CUBE_LEFT ==
645              materials[2].ambient_texopt.type);
646   TEST_CHECK(tinyobj::TEXTURE_TYPE_CUBE_RIGHT ==
647              materials[2].alpha_texopt.type);
648   TEST_CHECK(tinyobj::TEXTURE_TYPE_CUBE_FRONT == materials[2].bump_texopt.type);
649   TEST_CHECK(tinyobj::TEXTURE_TYPE_CUBE_BACK ==
650              materials[2].displacement_texopt.type);
651 }
652 
test_mtllib_multiple_filenames_issue112()653 void test_mtllib_multiple_filenames_issue112() {
654   tinyobj::attrib_t attrib;
655   std::vector<tinyobj::shape_t> shapes;
656   std::vector<tinyobj::material_t> materials;
657 
658   std::string warn;
659   std::string err;
660   bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err,
661                               "../models/mtllib-multiple-files-issue-112.obj",
662                               gMtlBasePath);
663 
664   if (!warn.empty()) {
665     std::cout << "WARN: " << warn << std::endl;
666   }
667 
668   if (!err.empty()) {
669     std::cerr << "ERR: " << err << std::endl;
670   }
671   TEST_CHECK(true == ret);
672   TEST_CHECK(1 == materials.size());
673 }
674 
test_tr_and_d_issue43()675 void test_tr_and_d_issue43() {
676   tinyobj::attrib_t attrib;
677   std::vector<tinyobj::shape_t> shapes;
678   std::vector<tinyobj::material_t> materials;
679 
680   std::string warn;
681   std::string err;
682   bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err,
683                               "../models/tr-and-d-issue-43.obj", gMtlBasePath);
684 
685   if (!warn.empty()) {
686     std::cout << "WARN: " << warn << std::endl;
687   }
688 
689   if (!err.empty()) {
690     std::cerr << "ERR: " << err << std::endl;
691   }
692   TEST_CHECK(true == ret);
693   TEST_CHECK(2 == materials.size());
694 
695   TEST_CHECK(FloatEquals(0.75f, materials[0].dissolve));
696   TEST_CHECK(FloatEquals(0.75f, materials[1].dissolve));
697 }
698 
test_refl()699 void test_refl() {
700   tinyobj::attrib_t attrib;
701   std::vector<tinyobj::shape_t> shapes;
702   std::vector<tinyobj::material_t> materials;
703 
704   std::string warn;
705   std::string err;
706   bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err,
707                               "../models/refl.obj", gMtlBasePath);
708 
709   if (!warn.empty()) {
710     std::cout << "WARN: " << warn << std::endl;
711   }
712 
713   if (!err.empty()) {
714     std::cerr << "ERR: " << err << std::endl;
715   }
716 
717   PrintInfo(attrib, shapes, materials);
718 
719   TEST_CHECK(true == ret);
720   TEST_CHECK(5 == materials.size());
721 
722   TEST_CHECK(materials[0].reflection_texname.compare("reflection.tga") == 0);
723 }
724 
test_map_Bump()725 void test_map_Bump() {
726   tinyobj::attrib_t attrib;
727   std::vector<tinyobj::shape_t> shapes;
728   std::vector<tinyobj::material_t> materials;
729 
730   std::string warn;
731   std::string err;
732   bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err,
733                               "../models/map-bump.obj", gMtlBasePath);
734 
735   if (!warn.empty()) {
736     std::cout << "WARN: " << warn << std::endl;
737   }
738 
739   if (!err.empty()) {
740     std::cerr << "ERR: " << err << std::endl;
741   }
742 
743   PrintInfo(attrib, shapes, materials);
744 
745   TEST_CHECK(true == ret);
746   TEST_CHECK(2 == materials.size());
747 
748   TEST_CHECK(materials[0].bump_texname.compare("bump.jpg") == 0);
749 }
750 
test_g_ignored_issue138()751 void test_g_ignored_issue138() {
752   tinyobj::attrib_t attrib;
753   std::vector<tinyobj::shape_t> shapes;
754   std::vector<tinyobj::material_t> materials;
755 
756   std::string warn;
757   std::string err;
758   bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err,
759                               "../models/issue-138.obj", gMtlBasePath);
760 
761   if (!warn.empty()) {
762     std::cout << "WARN: " << warn << std::endl;
763   }
764 
765   if (!err.empty()) {
766     std::cerr << "ERR: " << err << std::endl;
767   }
768 
769   PrintInfo(attrib, shapes, materials);
770 
771   TEST_CHECK(true == ret);
772   TEST_CHECK(2 == shapes.size());
773   TEST_CHECK(2 == materials.size());
774 }
775 
test_vertex_col_ext_issue144()776 void test_vertex_col_ext_issue144() {
777   tinyobj::attrib_t attrib;
778   std::vector<tinyobj::shape_t> shapes;
779   std::vector<tinyobj::material_t> materials;
780 
781   std::string warn;
782   std::string err;
783 
784   bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err,
785                               "../models/cube-vertexcol.obj", gMtlBasePath);
786 
787   if (!warn.empty()) {
788     std::cout << "WARN: " << warn << std::endl;
789   }
790 
791   if (!err.empty()) {
792     std::cerr << "ERR: " << err << std::endl;
793   }
794 
795   // PrintInfo(attrib, shapes, materials);
796 
797   TEST_CHECK(true == ret);
798   TEST_CHECK((8 * 3) == attrib.colors.size());
799 
800   TEST_CHECK(FloatEquals(0.0f, attrib.colors[3 * 0 + 0]));
801   TEST_CHECK(FloatEquals(0.0f, attrib.colors[3 * 0 + 1]));
802   TEST_CHECK(FloatEquals(0.0f, attrib.colors[3 * 0 + 2]));
803 
804   TEST_CHECK(FloatEquals(0.0f, attrib.colors[3 * 1 + 0]));
805   TEST_CHECK(FloatEquals(0.0f, attrib.colors[3 * 1 + 1]));
806   TEST_CHECK(FloatEquals(1.0f, attrib.colors[3 * 1 + 2]));
807 
808   TEST_CHECK(FloatEquals(1.0f, attrib.colors[3 * 4 + 0]));
809 
810   TEST_CHECK(FloatEquals(1.0f, attrib.colors[3 * 7 + 0]));
811   TEST_CHECK(FloatEquals(1.0f, attrib.colors[3 * 7 + 1]));
812   TEST_CHECK(FloatEquals(1.0f, attrib.colors[3 * 7 + 2]));
813 }
814 
test_norm_texopts()815 void test_norm_texopts() {
816   tinyobj::attrib_t attrib;
817   std::vector<tinyobj::shape_t> shapes;
818   std::vector<tinyobj::material_t> materials;
819 
820   std::string warn;
821   std::string err;
822   bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err,
823                               "../models/norm-texopt.obj", gMtlBasePath);
824 
825   if (!warn.empty()) {
826     std::cout << "WARN: " << warn << std::endl;
827   }
828 
829   if (!err.empty()) {
830     std::cerr << "ERR: " << err << std::endl;
831   }
832 
833   TEST_CHECK(true == ret);
834   TEST_CHECK(1 == shapes.size());
835   TEST_CHECK(1 == materials.size());
836   TEST_CHECK(FloatEquals(3.0f, materials[0].normal_texopt.bump_multiplier));
837 }
838 
test_zero_face_idx_value_issue140()839 void test_zero_face_idx_value_issue140() {
840   tinyobj::attrib_t attrib;
841   std::vector<tinyobj::shape_t> shapes;
842   std::vector<tinyobj::material_t> materials;
843 
844   std::string warn;
845   std::string err;
846   bool ret =
847       tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err,
848                        "../models/issue-140-zero-face-idx.obj", gMtlBasePath);
849 
850   if (!warn.empty()) {
851     std::cout << "WARN: " << warn << std::endl;
852   }
853 
854   if (!err.empty()) {
855     std::cerr << "ERR: " << err << std::endl;
856   }
857   TEST_CHECK(false == ret);
858   TEST_CHECK(!err.empty());
859 }
860 
test_texture_name_whitespace_issue145()861 void test_texture_name_whitespace_issue145() {
862   tinyobj::attrib_t attrib;
863   std::vector<tinyobj::shape_t> shapes;
864   std::vector<tinyobj::material_t> materials;
865 
866   std::string warn;
867   std::string err;
868   bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err,
869                               "../models/texture-filename-with-whitespace.obj",
870                               gMtlBasePath);
871 
872   if (!warn.empty()) {
873     std::cout << "WARN: " << warn << std::endl;
874   }
875 
876   if (!err.empty()) {
877     std::cerr << "ERR: " << err << std::endl;
878   }
879 
880   TEST_CHECK(true == ret);
881   TEST_CHECK(err.empty());
882   TEST_CHECK(2 < materials.size());
883 
884   TEST_CHECK(0 == materials[0].diffuse_texname.compare("texture 01.png"));
885   TEST_CHECK(0 == materials[1].bump_texname.compare("bump 01.png"));
886   TEST_CHECK(FloatEquals(2.0f, materials[1].bump_texopt.bump_multiplier));
887 }
888 
test_smoothing_group_issue162()889 void test_smoothing_group_issue162() {
890   tinyobj::attrib_t attrib;
891   std::vector<tinyobj::shape_t> shapes;
892   std::vector<tinyobj::material_t> materials;
893 
894   std::string warn;
895   std::string err;
896   bool ret =
897       tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err,
898                        "../models/issue-162-smoothing-group.obj", gMtlBasePath);
899 
900   if (!warn.empty()) {
901     std::cout << "WARN: " << warn << std::endl;
902   }
903 
904   if (!err.empty()) {
905     std::cerr << "ERR: " << err << std::endl;
906   }
907 
908   TEST_CHECK(true == ret);
909   TEST_CHECK(2 == shapes.size());
910 
911   TEST_CHECK(2 == shapes[0].mesh.smoothing_group_ids.size());
912   TEST_CHECK(1 == shapes[0].mesh.smoothing_group_ids[0]);
913   TEST_CHECK(1 == shapes[0].mesh.smoothing_group_ids[1]);
914 
915   TEST_CHECK(10 == shapes[1].mesh.smoothing_group_ids.size());
916   TEST_CHECK(0 == shapes[1].mesh.smoothing_group_ids[0]);
917   TEST_CHECK(0 == shapes[1].mesh.smoothing_group_ids[1]);
918   TEST_CHECK(3 == shapes[1].mesh.smoothing_group_ids[2]);
919   TEST_CHECK(3 == shapes[1].mesh.smoothing_group_ids[3]);
920   TEST_CHECK(4 == shapes[1].mesh.smoothing_group_ids[4]);
921   TEST_CHECK(4 == shapes[1].mesh.smoothing_group_ids[5]);
922   TEST_CHECK(0 == shapes[1].mesh.smoothing_group_ids[6]);
923   TEST_CHECK(0 == shapes[1].mesh.smoothing_group_ids[7]);
924   TEST_CHECK(6 == shapes[1].mesh.smoothing_group_ids[8]);
925   TEST_CHECK(6 == shapes[1].mesh.smoothing_group_ids[9]);
926 }
927 
test_invalid_face_definition()928 void test_invalid_face_definition() {
929   tinyobj::attrib_t attrib;
930   std::vector<tinyobj::shape_t> shapes;
931   std::vector<tinyobj::material_t> materials;
932 
933   std::string warn;
934   std::string err;
935   bool ret =
936       tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err,
937                        "../models/invalid-face-definition.obj", gMtlBasePath);
938 
939   if (!warn.empty()) {
940     std::cout << "WARN: " << warn << std::endl;
941   }
942 
943   if (!err.empty()) {
944     std::cerr << "ERR: " << err << std::endl;
945   }
946 
947   TEST_CHECK(true == ret);
948   TEST_CHECK(1 == shapes.size());
949   TEST_CHECK(0 == shapes[0].mesh.indices.size());
950 }
951 
test_Empty_mtl_basedir_issue177()952 void test_Empty_mtl_basedir_issue177() {
953   tinyobj::attrib_t attrib;
954   std::vector<tinyobj::shape_t> shapes;
955   std::vector<tinyobj::material_t> materials;
956 
957   std::string warn;
958   std::string err;
959 
960   // A case where the user explicitly provides an empty string
961   // Win32 specific?
962   const char* userBaseDir = "";
963   bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err,
964                               "issue-177.obj", userBaseDir);
965 
966   // if mtl loading fails, we get an warning message here
967   ret &= warn.empty();
968 
969   if (!warn.empty()) {
970     std::cout << "WARN: " << warn << std::endl;
971   }
972 
973   if (!err.empty()) {
974     std::cerr << "ERR: " << err << std::endl;
975   }
976 
977   TEST_CHECK(true == ret);
978 }
979 
test_line_primitive()980 void test_line_primitive() {
981   tinyobj::attrib_t attrib;
982   std::vector<tinyobj::shape_t> shapes;
983   std::vector<tinyobj::material_t> materials;
984 
985   std::string warn;
986   std::string err;
987   bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err,
988                               "../models/line-prim.obj", gMtlBasePath);
989 
990   if (!warn.empty()) {
991     std::cout << "WARN: " << warn << std::endl;
992   }
993 
994   if (!err.empty()) {
995     std::cerr << "ERR: " << err << std::endl;
996   }
997 
998   TEST_CHECK(true == ret);
999   TEST_CHECK(1 == shapes.size());
1000   TEST_CHECK(8 == shapes[0].lines.indices.size());
1001   TEST_CHECK(2 == shapes[0].lines.num_line_vertices.size());
1002 }
1003 
test_points_primitive()1004 void test_points_primitive() {
1005   tinyobj::attrib_t attrib;
1006   std::vector<tinyobj::shape_t> shapes;
1007   std::vector<tinyobj::material_t> materials;
1008 
1009   std::string warn;
1010   std::string err;
1011   bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err,
1012                               "../models/points-prim.obj", gMtlBasePath);
1013 
1014   if (!warn.empty()) {
1015     std::cout << "WARN: " << warn << std::endl;
1016   }
1017 
1018   if (!err.empty()) {
1019     std::cerr << "ERR: " << err << std::endl;
1020   }
1021 
1022   TEST_CHECK(true == ret);
1023   TEST_CHECK(1 == shapes.size());
1024   TEST_CHECK(8 == shapes[0].points.indices.size());
1025 }
1026 
test_multiple_group_names()1027 void test_multiple_group_names() {
1028   tinyobj::attrib_t attrib;
1029   std::vector<tinyobj::shape_t> shapes;
1030   std::vector<tinyobj::material_t> materials;
1031 
1032   std::string warn;
1033   std::string err;
1034   bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err,
1035                               "../models/cube.obj", gMtlBasePath);
1036 
1037   if (!warn.empty()) {
1038     std::cout << "WARN: " << warn << std::endl;
1039   }
1040 
1041   if (!err.empty()) {
1042     std::cerr << "ERR: " << err << std::endl;
1043   }
1044 
1045   TEST_CHECK(true == ret);
1046   TEST_CHECK(6 == shapes.size());
1047   TEST_CHECK(0 == shapes[0].name.compare("front cube"));
1048   TEST_CHECK(0 == shapes[1].name.compare("back cube"));  // multiple whitespaces
1049                                                          // are aggregated as
1050                                                          // single white space.
1051 }
1052 
test_initialize_all_texopts()1053 void test_initialize_all_texopts() {
1054   tinyobj::attrib_t attrib;
1055   std::vector<tinyobj::shape_t> shapes;
1056   std::vector<tinyobj::material_t> materials;
1057 
1058   std::string warn;
1059   std::string err;
1060   bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err,
1061                               "../models/cornell_box.obj", gMtlBasePath, false);
1062 
1063   TEST_CHECK(ret == true);
1064   TEST_CHECK(0 < materials.size());
1065 
1066 #define TEST_CHECK_DEFAULT_TEXOPT(texopt)                \
1067   TEST_CHECK(tinyobj::TEXTURE_TYPE_NONE == texopt.type); \
1068   TEST_CHECK(0.0 == texopt.brightness);                  \
1069   TEST_CHECK(1.0 == texopt.contrast);                    \
1070   TEST_CHECK(false == texopt.clamp);                     \
1071   TEST_CHECK(true == texopt.blendu);                     \
1072   TEST_CHECK(true == texopt.blendv);                     \
1073   TEST_CHECK(1.0 == texopt.bump_multiplier);             \
1074   for (int j = 0; j < 3; j++) {                          \
1075     TEST_CHECK(0.0 == texopt.origin_offset[j]);          \
1076     TEST_CHECK(1.0 == texopt.scale[j]);                  \
1077     TEST_CHECK(0.0 == texopt.turbulence[j]);             \
1078   }
1079   for (size_t i = 0; i < materials.size(); i++) {
1080     TEST_CHECK_DEFAULT_TEXOPT(materials[i].ambient_texopt);
1081     TEST_CHECK_DEFAULT_TEXOPT(materials[i].diffuse_texopt);
1082     TEST_CHECK_DEFAULT_TEXOPT(materials[i].specular_texopt);
1083     TEST_CHECK_DEFAULT_TEXOPT(materials[i].specular_highlight_texopt);
1084     TEST_CHECK_DEFAULT_TEXOPT(materials[i].bump_texopt);
1085     TEST_CHECK_DEFAULT_TEXOPT(materials[i].displacement_texopt);
1086     TEST_CHECK_DEFAULT_TEXOPT(materials[i].alpha_texopt);
1087     TEST_CHECK_DEFAULT_TEXOPT(materials[i].reflection_texopt);
1088     TEST_CHECK_DEFAULT_TEXOPT(materials[i].roughness_texopt);
1089     TEST_CHECK_DEFAULT_TEXOPT(materials[i].metallic_texopt);
1090     TEST_CHECK_DEFAULT_TEXOPT(materials[i].sheen_texopt);
1091     TEST_CHECK_DEFAULT_TEXOPT(materials[i].emissive_texopt);
1092     TEST_CHECK_DEFAULT_TEXOPT(materials[i].normal_texopt);
1093   }
1094 #undef TEST_CHECK_DEFAULT_TEXOPT
1095 }
1096 
test_colorspace_issue184()1097 void test_colorspace_issue184() {
1098   tinyobj::attrib_t attrib;
1099   std::vector<tinyobj::shape_t> shapes;
1100   std::vector<tinyobj::material_t> materials;
1101 
1102   std::string warn;
1103   std::string err;
1104   bool ret =
1105       tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err,
1106                        "../models/colorspace-issue-184.obj", gMtlBasePath);
1107 
1108   if (!warn.empty()) {
1109     std::cout << "WARN: " << warn << std::endl;
1110   }
1111 
1112   if (!err.empty()) {
1113     std::cerr << "ERR: " << err << std::endl;
1114   }
1115 
1116   TEST_CHECK(true == ret);
1117   TEST_CHECK(1 == shapes.size());
1118   TEST_CHECK(1 == materials.size());
1119   TEST_CHECK(0 == materials[0].diffuse_texopt.colorspace.compare("sRGB"));
1120   TEST_CHECK(0 == materials[0].specular_texopt.colorspace.size());
1121   TEST_CHECK(0 == materials[0].bump_texopt.colorspace.compare("linear"));
1122 }
1123 
test_leading_decimal_dots_issue201()1124 void test_leading_decimal_dots_issue201() {
1125   tinyobj::attrib_t attrib;
1126   std::vector<tinyobj::shape_t> shapes;
1127   std::vector<tinyobj::material_t> materials;
1128 
1129   std::string warn;
1130   std::string err;
1131   bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err,
1132                               "../models/leading-decimal-dot-issue-201.obj",
1133                               gMtlBasePath);
1134 
1135   if (!warn.empty()) {
1136     std::cout << "WARN: " << warn << std::endl;
1137   }
1138 
1139   if (!err.empty()) {
1140     std::cerr << "ERR: " << err << std::endl;
1141   }
1142 
1143   TEST_CHECK(true == ret);
1144   TEST_CHECK(1 == shapes.size());
1145   TEST_CHECK(1 == materials.size());
1146   TEST_CHECK(FloatEquals(0.8e-1f, attrib.vertices[0]));
1147   TEST_CHECK(FloatEquals(-.7e+2f, attrib.vertices[1]));
1148   TEST_CHECK(FloatEquals(.575869f, attrib.vertices[3]));
1149   TEST_CHECK(FloatEquals(-.666304f, attrib.vertices[4]));
1150   TEST_CHECK(FloatEquals(.940448f, attrib.vertices[6]));
1151 }
1152 
test_mtl_default_search_path_v2_API_issue208()1153 void test_mtl_default_search_path_v2_API_issue208() {
1154   tinyobj::ObjReader reader;
1155 
1156   bool ret = reader.ParseFromFile("../models/cornell_box.obj");
1157 
1158   std::cout << "WARN: " << reader.Warning() << "\n";
1159 
1160   TEST_CHECK(ret == true);
1161   TEST_CHECK(reader.Warning().empty());
1162 }
1163 
test_leading_zero_in_exponent_notation_issue210()1164 void test_leading_zero_in_exponent_notation_issue210() {
1165   tinyobj::attrib_t attrib;
1166   std::vector<tinyobj::shape_t> shapes;
1167   std::vector<tinyobj::material_t> materials;
1168 
1169   std::string warn;
1170   std::string err;
1171   bool ret = tinyobj::LoadObj(
1172       &attrib, &shapes, &materials, &warn, &err,
1173       "../models/leading-zero-in-exponent-notation-issue-210.obj",
1174       gMtlBasePath);
1175 
1176   if (!warn.empty()) {
1177     std::cout << "WARN: " << warn << std::endl;
1178   }
1179 
1180   if (!err.empty()) {
1181     std::cerr << "ERR: " << err << std::endl;
1182   }
1183 
1184   TEST_CHECK(true == ret);
1185   TEST_CHECK(1 == shapes.size());
1186   TEST_CHECK(1 == materials.size());
1187   TEST_CHECK(FloatEquals(0.8e-001f, attrib.vertices[0]));
1188   TEST_CHECK(FloatEquals(-.7e+02f, attrib.vertices[1]));
1189 
1190   std::cout << "exp " << 0.8e-01 << std::endl;
1191   std::cout << "bora " << attrib.vertices[0] << std::endl;
1192 }
1193 
test_usemtl_then_o_issue235()1194 void test_usemtl_then_o_issue235() {
1195   tinyobj::attrib_t attrib;
1196   std::vector<tinyobj::shape_t> shapes;
1197   std::vector<tinyobj::material_t> materials;
1198 
1199   std::string warn;
1200   std::string err;
1201   bool ret = tinyobj::LoadObj(
1202       &attrib, &shapes, &materials, &warn, &err,
1203       "../models/issue-235-usemtl-then-o.obj",
1204       gMtlBasePath);
1205 
1206   if (!warn.empty()) {
1207     std::cout << "WARN: " << warn << std::endl;
1208   }
1209 
1210   if (!err.empty()) {
1211     std::cerr << "ERR: " << err << std::endl;
1212   }
1213 
1214   TEST_CHECK(true == ret);
1215   TEST_CHECK(2 == shapes.size());
1216   TEST_CHECK(2 == materials.size());
1217   TEST_CHECK(4 == shapes[1].mesh.indices[0].vertex_index);
1218 }
1219 
test_mtl_searchpaths_issue244()1220 void test_mtl_searchpaths_issue244() {
1221   tinyobj::attrib_t attrib;
1222   std::vector<tinyobj::shape_t> shapes;
1223   std::vector<tinyobj::material_t> materials;
1224 
1225   // .mtl is located at ./assets/issue-244.mtl
1226 #if _WIN32
1227   std::string search_paths("../;../models;./assets");
1228 #else
1229   std::string search_paths("../:../models:./assets");
1230 #endif
1231 
1232   std::string warn;
1233   std::string err;
1234   bool ret = tinyobj::LoadObj(
1235       &attrib, &shapes, &materials, &warn, &err,
1236       "../models/issue-244-mtl-searchpaths.obj",
1237       search_paths.c_str());
1238 
1239   TEST_CHECK(warn.empty());
1240 
1241   if (!warn.empty()) {
1242     std::cout << "WARN: " << warn << std::endl;
1243   }
1244 
1245   if (!err.empty()) {
1246     std::cerr << "ERR: " << err << std::endl;
1247   }
1248 
1249   TEST_CHECK(true == ret);
1250   TEST_CHECK(2 == shapes.size());
1251   TEST_CHECK(2 == materials.size());
1252   TEST_CHECK(4 == shapes[1].mesh.indices[0].vertex_index);
1253 }
1254 
test_usemtl_whitespace_issue246()1255 void test_usemtl_whitespace_issue246() {
1256   tinyobj::attrib_t attrib;
1257   std::vector<tinyobj::shape_t> shapes;
1258   std::vector<tinyobj::material_t> materials;
1259 
1260   std::string warn;
1261   std::string err;
1262   bool ret = tinyobj::LoadObj(
1263       &attrib, &shapes, &materials, &warn, &err,
1264       "../models/issue-246-usemtl-whitespace.obj",
1265       gMtlBasePath);
1266 
1267   TEST_CHECK(warn.empty());
1268 
1269   if (!warn.empty()) {
1270     std::cout << "WARN: " << warn << std::endl;
1271   }
1272 
1273   if (!err.empty()) {
1274     std::cerr << "ERR: " << err << std::endl;
1275   }
1276 
1277   TEST_CHECK(true == ret);
1278   TEST_CHECK(1 == shapes.size());
1279   TEST_CHECK(1 == materials.size());
1280   TEST_CHECK(0 == shapes[0].mesh.material_ids[0]);
1281 }
1282 
test_texres_texopt_issue248()1283 void test_texres_texopt_issue248() {
1284   tinyobj::attrib_t attrib;
1285   std::vector<tinyobj::shape_t> shapes;
1286   std::vector<tinyobj::material_t> materials;
1287 
1288   std::string warn;
1289   std::string err;
1290   bool ret = tinyobj::LoadObj(
1291       &attrib, &shapes, &materials, &warn, &err,
1292       "../models/issue-248-texres-texopt.obj",
1293       gMtlBasePath);
1294 
1295   TEST_CHECK(warn.empty());
1296 
1297   if (!warn.empty()) {
1298     std::cout << "WARN: " << warn << std::endl;
1299   }
1300 
1301   if (!err.empty()) {
1302     std::cerr << "ERR: " << err << std::endl;
1303   }
1304 
1305   TEST_CHECK(true == ret);
1306   TEST_CHECK(1 < materials.size());
1307   TEST_CHECK(512 == materials[0].diffuse_texopt.texture_resolution);
1308   TEST_CHECK("input.jpg" == materials[0].diffuse_texname);
1309 }
1310 
test_mtl_filename_with_whitespace_issue46()1311 void test_mtl_filename_with_whitespace_issue46() {
1312   tinyobj::attrib_t attrib;
1313   std::vector<tinyobj::shape_t> shapes;
1314   std::vector<tinyobj::material_t> materials;
1315 
1316   std::string warn;
1317   std::string err;
1318   bool ret =
1319       tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err,
1320                        "../models/mtl filename with whitespace issue46.obj",
1321                        gMtlBasePath);
1322 
1323   if (!warn.empty()) {
1324     std::cout << "WARN: " << warn << std::endl;
1325   }
1326 
1327   if (!err.empty()) {
1328     std::cerr << "ERR: " << err << std::endl;
1329   }
1330   TEST_CHECK(true == ret);
1331   TEST_CHECK(1 == materials.size());
1332   TEST_CHECK("green" == materials[0].name);
1333 }
1334 
test_face_missing_issue295()1335 void test_face_missing_issue295() {
1336   tinyobj::attrib_t attrib;
1337   std::vector<tinyobj::shape_t> shapes;
1338   std::vector<tinyobj::material_t> materials;
1339 
1340   std::string warn;
1341   std::string err;
1342   bool ret = tinyobj::LoadObj(
1343       &attrib, &shapes, &materials, &warn, &err,
1344       "../models/issue-295-trianguation-failure.obj",
1345       gMtlBasePath, /* triangualte */true);
1346 
1347   TEST_CHECK(warn.empty());
1348 
1349   if (!warn.empty()) {
1350     std::cout << "WARN: " << warn << std::endl;
1351   }
1352 
1353   if (!err.empty()) {
1354     std::cerr << "ERR: " << err << std::endl;
1355   }
1356 
1357   TEST_CHECK(true == ret);
1358   TEST_CHECK(1 == shapes.size());
1359 
1360   // 14 quad faces are triangulated into 28 triangles.
1361   TEST_CHECK(28 == shapes[0].mesh.num_face_vertices.size());
1362   TEST_CHECK(28 == shapes[0].mesh.smoothing_group_ids.size());
1363   TEST_CHECK(28 == shapes[0].mesh.material_ids.size());
1364   TEST_CHECK((3 * 28) == shapes[0].mesh.indices.size()); // 28 triangle faces x 3
1365 }
1366 
1367 // Fuzzer test.
1368 // Just check if it does not crash.
1369 // Disable by default since Windows filesystem can't create filename of afl
1370 // testdata
1371 #if 0
1372 
1373 void test_afl000000", "[AFL]() {
1374   tinyobj::attrib_t attrib;
1375   std::vector<tinyobj::shape_t> shapes;
1376   std::vector<tinyobj::material_t> materials;
1377 
1378   std::string err;
1379   bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "./afl/id:000000,sig:11,src:000000,op:havoc,rep:128", gMtlBasePath);
1380 
1381   TEST_CHECK(true == ret);
1382 }
1383 
1384 void test_afl000001", "[AFL]() {
1385   tinyobj::attrib_t attrib;
1386   std::vector<tinyobj::shape_t> shapes;
1387   std::vector<tinyobj::material_t> materials;
1388 
1389   std::string err;
1390   bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "./afl/id:000001,sig:11,src:000000,op:havoc,rep:64", gMtlBasePath);
1391 
1392   TEST_CHECK(true == ret);
1393 }
1394 #endif
1395 
1396 #if 0
1397 int
1398 main(
1399   int argc,
1400   char **argv)
1401 {
1402   if (argc > 1) {
1403     const char* basepath = NULL;
1404     if (argc > 2) {
1405       basepath = argv[2];
1406     }
1407     assert(true == TestLoadObj(argv[1], basepath));
1408   } else {
1409     //assert(true == TestLoadObj("cornell_box.obj"));
1410     //assert(true == TestLoadObj("cube.obj"));
1411     assert(true == TestStreamLoadObj());
1412     assert(true == TestLoadObj("catmark_torus_creases0.obj", NULL, false));
1413   }
1414 
1415   return 0;
1416 }
1417 #endif
1418 
1419 TEST_LIST = {
1420     {"cornell_box", test_cornell_box},
1421     {"catmark_torus_creases0", test_catmark_torus_creases0},
1422     {"pbr", test_pbr},
1423     {"stream_load", test_stream_load},
1424     {"stream_load_from_file_skipping_materials",
1425      test_stream_load_from_file_skipping_materials},
1426     {"stream_load_from_file_with_materials",
1427      test_stream_load_from_file_with_materials},
1428     {"trailing_whitespace_in_mtl_issue92",
1429      test_trailing_whitespace_in_mtl_issue92},
1430     {"transmittance_filter_issue95", test_transmittance_filter_issue95},
1431     {"transmittance_filter_Tf_issue95", test_transmittance_filter_Tf_issue95},
1432     {"transmittance_filter_Kt_issue95", test_transmittance_filter_Kt_issue95},
1433     {"usemtl_at_last_line_issue104", test_usemtl_at_last_line_issue104},
1434     {"texture_opts_issue85", test_texture_opts_issue85},
1435     {"mtllib_multiple_filenames_issue112",
1436      test_mtllib_multiple_filenames_issue112},
1437     {"tr_and_d_issue43", test_tr_and_d_issue43},
1438     {"refl", test_refl},
1439     {"map_bump", test_map_Bump},
1440     {"g_ignored_issue138", test_g_ignored_issue138},
1441     {"vertex_col_ext_issue144", test_vertex_col_ext_issue144},
1442     {"norm_texopts", test_norm_texopts},
1443     {"zero_face_idx_value_issue140", test_zero_face_idx_value_issue140},
1444     {"texture_name_whitespace_issue145", test_texture_name_whitespace_issue145},
1445     {"smoothing_group_issue162", test_smoothing_group_issue162},
1446     {"invalid_face_definition", test_invalid_face_definition},
1447     {"Empty_mtl_basedir_issue177", test_Empty_mtl_basedir_issue177},
1448     {"line_primitive", test_line_primitive},
1449     {"points_primitive", test_points_primitive},
1450     {"multiple_group_names", test_multiple_group_names},
1451     {"initialize_all_texopts", test_initialize_all_texopts},
1452     {"colorspace_issue184", test_colorspace_issue184},
1453     {"leading_decimal_dots_issue201", test_leading_decimal_dots_issue201},
1454     {"mtl_default_search_path_v2_API_issue208",
1455      test_mtl_default_search_path_v2_API_issue208},
1456     {"leading_zero_in_exponent_notation_issue210",
1457      test_leading_zero_in_exponent_notation_issue210},
1458     {"usemtl_then_o_issue235",
1459      test_usemtl_then_o_issue235},
1460     {"mtl_searchpaths_issue244",
1461      test_mtl_searchpaths_issue244},
1462     {"usemtl_whitespece_issue246",
1463      test_usemtl_whitespace_issue246},
1464     {"texres_texopt_issue248",
1465      test_texres_texopt_issue248},
1466     {"test_mtl_filename_with_whitespace_issue46",
1467      test_mtl_filename_with_whitespace_issue46},
1468     {"test_face_missing_issue295",
1469      test_face_missing_issue295},
1470     {NULL, NULL}};
1471