diff --git a/examples/callback_api/main.cc b/examples/callback_api/main.cc index a7a7422..1911501 100644 --- a/examples/callback_api/main.cc +++ b/examples/callback_api/main.cc @@ -129,6 +129,7 @@ int main(int argc, char **argv) { cb.object_cb = object_cb; MyMesh mesh; + std::string warn; std::string err; std::string filename = "../../models/cornell_box.obj"; if (argc > 1) { @@ -143,7 +144,11 @@ int main(int argc, char **argv) { tinyobj::MaterialFileReader mtlReader("../../models/"); - bool ret = tinyobj::LoadObjWithCallback(ifs, cb, &mesh, &mtlReader, &err); + bool ret = tinyobj::LoadObjWithCallback(ifs, cb, &mesh, &mtlReader, &warn, &err); + + if (!warn.empty()) { + std::cout << "WARN: " << warn << std::endl; + } if (!err.empty()) { std::cerr << err << std::endl; diff --git a/examples/obj_sticher/obj_sticher.cc b/examples/obj_sticher/obj_sticher.cc index 47c06eb..779f303 100644 --- a/examples/obj_sticher/obj_sticher.cc +++ b/examples/obj_sticher/obj_sticher.cc @@ -1,11 +1,10 @@ // // Stiches multiple .obj files into one .obj. // - -#define TINYOBJLOADER_IMPLEMENTATION -#include "../../tiny_obj_loader.h" #include "obj_writer.h" +#include "../../tiny_obj_loader.h" + #include #include #include @@ -134,8 +133,9 @@ int main(int argc, char **argv) for (int i = 0; i < num_objfiles; i++) { std::cout << "Loading " << argv[i+1] << " ... " << std::flush; + std::string warn; std::string err; - bool ret = tinyobj::LoadObj(&attributes[i], &shapes[i], &materials[i], &err, argv[i+1]); + bool ret = tinyobj::LoadObj(&attributes[i], &shapes[i], &materials[i], &warn, &err, argv[i+1]); if (!err.empty()) { std::cerr << err << std::endl; } diff --git a/examples/viewer/viewer.cc b/examples/viewer/viewer.cc index b5de2e6..9ab1142 100644 --- a/examples/viewer/viewer.cc +++ b/examples/viewer/viewer.cc @@ -308,9 +308,13 @@ static bool LoadObjAndConvert(float bmin[3], float bmax[3], base_dir += "/"; #endif + std::string warn; std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename, + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, filename, base_dir.c_str()); + if (!warn.empty()) { + std::cout << "WARN: " << warn << std::endl; + } if (!err.empty()) { std::cerr << err << std::endl; } diff --git a/examples/voxelize/main.cc b/examples/voxelize/main.cc index 035e3a1..6f11939 100644 --- a/examples/voxelize/main.cc +++ b/examples/voxelize/main.cc @@ -9,8 +9,9 @@ bool Voxelize(const char* filename, float voxelsizex, float voxelsizey, float vo tinyobj::attrib_t attrib; std::vector shapes; std::vector materials; + std::string warn; std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename); + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, filename); if (!err.empty()) { printf("err: %s\n", err.c_str()); diff --git a/loader_example.cc b/loader_example.cc index 595ae3a..82de6b3 100644 --- a/loader_example.cc +++ b/loader_example.cc @@ -285,14 +285,19 @@ static bool TestLoadObj(const char* filename, const char* basepath = NULL, timerutil t; t.start(); + std::string warn; std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename, + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, filename, basepath, triangulate); t.end(); printf("Parsing time: %lu [msecs]\n", t.msec()); + if (!warn.empty()) { + std::cout << "WARN: " << warn << std::endl; + } + if (!err.empty()) { - std::cerr << err << std::endl; + std::cerr << "ERR: " << err << std::endl; } if (!ret) { @@ -376,16 +381,12 @@ static bool TestStreamLoadObj() { virtual bool operator()(const std::string& matId, std::vector* materials, std::map* matMap, + std::string* warn, std::string* err) { + (void)err; (void)matId; - std::string warning; - LoadMtl(matMap, materials, &m_matSStream, &warning); + LoadMtl(matMap, materials, &m_matSStream, warn); - if (!warning.empty()) { - if (err) { - (*err) += warning; - } - } return true; } @@ -397,8 +398,9 @@ static bool TestStreamLoadObj() { tinyobj::attrib_t attrib; std::vector shapes; std::vector materials; + std::string warn; std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, &objStream, + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, &objStream, &matSSReader); if (!err.empty()) { diff --git a/tests/tester.cc b/tests/tester.cc index e24d1e2..094fd5f 100644 --- a/tests/tester.cc +++ b/tests/tester.cc @@ -1,87 +1,94 @@ #define TINYOBJLOADER_IMPLEMENTATION #include "../tiny_obj_loader.h" -#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file +#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do + // this in one cpp file #include "catch.hpp" +#include #include #include -#include +#include #include #include -#include -static void PrintInfo(const tinyobj::attrib_t &attrib, const std::vector& shapes, const std::vector& materials, bool triangulate = true) -{ +static void PrintInfo(const tinyobj::attrib_t& attrib, + const std::vector& shapes, + const std::vector& materials, + bool triangulate = true) { std::cout << "# of vertices : " << (attrib.vertices.size() / 3) << std::endl; std::cout << "# of normals : " << (attrib.normals.size() / 3) << std::endl; - std::cout << "# of texcoords : " << (attrib.texcoords.size() / 2) << std::endl; + std::cout << "# of texcoords : " << (attrib.texcoords.size() / 2) + << std::endl; std::cout << "# of shapes : " << shapes.size() << std::endl; std::cout << "# of materials : " << materials.size() << std::endl; for (size_t v = 0; v < attrib.vertices.size() / 3; v++) { printf(" v[%ld] = (%f, %f, %f)\n", v, - static_cast(attrib.vertices[3*v+0]), - static_cast(attrib.vertices[3*v+1]), - static_cast(attrib.vertices[3*v+2])); + static_cast(attrib.vertices[3 * v + 0]), + static_cast(attrib.vertices[3 * v + 1]), + static_cast(attrib.vertices[3 * v + 2])); } for (size_t v = 0; v < attrib.normals.size() / 3; v++) { printf(" n[%ld] = (%f, %f, %f)\n", v, - static_cast(attrib.normals[3*v+0]), - static_cast(attrib.normals[3*v+1]), - static_cast(attrib.normals[3*v+2])); + static_cast(attrib.normals[3 * v + 0]), + static_cast(attrib.normals[3 * v + 1]), + static_cast(attrib.normals[3 * v + 2])); } for (size_t v = 0; v < attrib.texcoords.size() / 2; v++) { printf(" uv[%ld] = (%f, %f)\n", v, - static_cast(attrib.texcoords[2*v+0]), - static_cast(attrib.texcoords[2*v+1])); + static_cast(attrib.texcoords[2 * v + 0]), + static_cast(attrib.texcoords[2 * v + 1])); } for (size_t i = 0; i < shapes.size(); i++) { printf("shape[%ld].name = %s\n", i, shapes[i].name.c_str()); - printf("Size of shape[%ld].indices: %ld\n", i, shapes[i].mesh.indices.size()); + printf("Size of shape[%ld].indices: %ld\n", i, + shapes[i].mesh.indices.size()); - if (triangulate) - { - printf("Size of shape[%ld].material_ids: %ld\n", i, shapes[i].mesh.material_ids.size()); - assert((shapes[i].mesh.indices.size() % 3) == 0); - for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) { - tinyobj::index_t i0 = shapes[i].mesh.indices[3*f+0]; - tinyobj::index_t i1 = shapes[i].mesh.indices[3*f+1]; - tinyobj::index_t i2 = shapes[i].mesh.indices[3*f+2]; - printf(" idx[%ld] = %d/%d/%d, %d/%d/%d, %d/%d/%d. mat_id = %d\n", f, - i0.vertex_index, i0.normal_index, i0.texcoord_index, - i1.vertex_index, i1.normal_index, i1.texcoord_index, - i2.vertex_index, i2.normal_index, i2.texcoord_index, - shapes[i].mesh.material_ids[f]); - } + if (triangulate) { + printf("Size of shape[%ld].material_ids: %ld\n", i, + shapes[i].mesh.material_ids.size()); + assert((shapes[i].mesh.indices.size() % 3) == 0); + for (size_t f = 0; f < shapes[i].mesh.indices.size() / 3; f++) { + tinyobj::index_t i0 = shapes[i].mesh.indices[3 * f + 0]; + tinyobj::index_t i1 = shapes[i].mesh.indices[3 * f + 1]; + tinyobj::index_t i2 = shapes[i].mesh.indices[3 * f + 2]; + printf(" idx[%ld] = %d/%d/%d, %d/%d/%d, %d/%d/%d. mat_id = %d\n", f, + i0.vertex_index, i0.normal_index, i0.texcoord_index, + i1.vertex_index, i1.normal_index, i1.texcoord_index, + i2.vertex_index, i2.normal_index, i2.texcoord_index, + shapes[i].mesh.material_ids[f]); + } } else { for (size_t f = 0; f < shapes[i].mesh.indices.size(); f++) { tinyobj::index_t idx = shapes[i].mesh.indices[f]; - printf(" idx[%ld] = %d/%d/%d\n", f, idx.vertex_index, idx.normal_index, idx.texcoord_index); + printf(" idx[%ld] = %d/%d/%d\n", f, idx.vertex_index, idx.normal_index, + idx.texcoord_index); } - printf("Size of shape[%ld].material_ids: %ld\n", i, shapes[i].mesh.material_ids.size()); - assert(shapes[i].mesh.material_ids.size() == shapes[i].mesh.num_face_vertices.size()); + printf("Size of shape[%ld].material_ids: %ld\n", i, + shapes[i].mesh.material_ids.size()); + assert(shapes[i].mesh.material_ids.size() == + shapes[i].mesh.num_face_vertices.size()); for (size_t m = 0; m < shapes[i].mesh.material_ids.size(); m++) { - printf(" material_id[%ld] = %d\n", m, - shapes[i].mesh.material_ids[m]); + printf(" material_id[%ld] = %d\n", m, shapes[i].mesh.material_ids[m]); } - } - printf("shape[%ld].num_faces: %ld\n", i, shapes[i].mesh.num_face_vertices.size()); + printf("shape[%ld].num_faces: %ld\n", i, + shapes[i].mesh.num_face_vertices.size()); for (size_t v = 0; v < shapes[i].mesh.num_face_vertices.size(); v++) { printf(" num_vertices[%ld] = %ld\n", v, - static_cast(shapes[i].mesh.num_face_vertices[v])); + static_cast(shapes[i].mesh.num_face_vertices[v])); } - //printf("shape[%ld].vertices: %ld\n", i, shapes[i].mesh.positions.size()); - //assert((shapes[i].mesh.positions.size() % 3) == 0); - //for (size_t v = 0; v < shapes[i].mesh.positions.size() / 3; v++) { + // printf("shape[%ld].vertices: %ld\n", i, shapes[i].mesh.positions.size()); + // assert((shapes[i].mesh.positions.size() % 3) == 0); + // for (size_t v = 0; v < shapes[i].mesh.positions.size() / 3; v++) { // printf(" v[%ld] = (%f, %f, %f)\n", v, // static_cast(shapes[i].mesh.positions[3*v+0]), // static_cast(shapes[i].mesh.positions[3*v+1]), @@ -92,35 +99,30 @@ static void PrintInfo(const tinyobj::attrib_t &attrib, const std::vector(shapes[i].mesh.tags[t].intValues[j])); - if (j < (shapes[i].mesh.tags[t].intValues.size()-1)) - { - printf(", "); - } + for (size_t j = 0; j < shapes[i].mesh.tags[t].intValues.size(); ++j) { + printf("%ld", static_cast(shapes[i].mesh.tags[t].intValues[j])); + if (j < (shapes[i].mesh.tags[t].intValues.size() - 1)) { + printf(", "); + } } printf("]"); printf(" floats: ["); - for (size_t j = 0; j < shapes[i].mesh.tags[t].floatValues.size(); ++j) - { - printf("%f", static_cast(shapes[i].mesh.tags[t].floatValues[j])); - if (j < (shapes[i].mesh.tags[t].floatValues.size()-1)) - { - printf(", "); - } + for (size_t j = 0; j < shapes[i].mesh.tags[t].floatValues.size(); ++j) { + printf("%f", static_cast( + shapes[i].mesh.tags[t].floatValues[j])); + if (j < (shapes[i].mesh.tags[t].floatValues.size() - 1)) { + printf(", "); + } } printf("]"); printf(" strings: ["); - for (size_t j = 0; j < shapes[i].mesh.tags[t].stringValues.size(); ++j) - { - printf("%s", shapes[i].mesh.tags[t].stringValues[j].c_str()); - if (j < (shapes[i].mesh.tags[t].stringValues.size()-1)) - { - printf(", "); - } + for (size_t j = 0; j < shapes[i].mesh.tags[t].stringValues.size(); ++j) { + printf("%s", shapes[i].mesh.tags[t].stringValues[j].c_str()); + if (j < (shapes[i].mesh.tags[t].stringValues.size() - 1)) { + printf(", "); + } } printf("]"); printf("\n"); @@ -129,25 +131,45 @@ static void PrintInfo(const tinyobj::attrib_t &attrib, const std::vector(materials[i].ambient[0]), static_cast(materials[i].ambient[1]), static_cast(materials[i].ambient[2])); - printf(" material.Kd = (%f, %f ,%f)\n", static_cast(materials[i].diffuse[0]), static_cast(materials[i].diffuse[1]), static_cast(materials[i].diffuse[2])); - printf(" material.Ks = (%f, %f ,%f)\n", static_cast(materials[i].specular[0]), static_cast(materials[i].specular[1]), static_cast(materials[i].specular[2])); - printf(" material.Tr = (%f, %f ,%f)\n", static_cast(materials[i].transmittance[0]), static_cast(materials[i].transmittance[1]), static_cast(materials[i].transmittance[2])); - printf(" material.Ke = (%f, %f ,%f)\n", static_cast(materials[i].emission[0]), static_cast(materials[i].emission[1]), static_cast(materials[i].emission[2])); - printf(" material.Ns = %f\n", static_cast(materials[i].shininess)); + printf(" material.Ka = (%f, %f ,%f)\n", + static_cast(materials[i].ambient[0]), + static_cast(materials[i].ambient[1]), + static_cast(materials[i].ambient[2])); + printf(" material.Kd = (%f, %f ,%f)\n", + static_cast(materials[i].diffuse[0]), + static_cast(materials[i].diffuse[1]), + static_cast(materials[i].diffuse[2])); + printf(" material.Ks = (%f, %f ,%f)\n", + static_cast(materials[i].specular[0]), + static_cast(materials[i].specular[1]), + static_cast(materials[i].specular[2])); + printf(" material.Tr = (%f, %f ,%f)\n", + static_cast(materials[i].transmittance[0]), + static_cast(materials[i].transmittance[1]), + static_cast(materials[i].transmittance[2])); + printf(" material.Ke = (%f, %f ,%f)\n", + static_cast(materials[i].emission[0]), + static_cast(materials[i].emission[1]), + static_cast(materials[i].emission[2])); + printf(" material.Ns = %f\n", + static_cast(materials[i].shininess)); printf(" material.Ni = %f\n", static_cast(materials[i].ior)); - printf(" material.dissolve = %f\n", static_cast(materials[i].dissolve)); - printf(" material.illum = %d\n", materials[i].illum); + printf(" material.dissolve = %f\n", + static_cast(materials[i].dissolve)); + printf(" material.illum = %d\n", materials[i].illum); printf(" material.map_Ka = %s\n", materials[i].ambient_texname.c_str()); printf(" material.map_Kd = %s\n", materials[i].diffuse_texname.c_str()); printf(" material.map_Ks = %s\n", materials[i].specular_texname.c_str()); - printf(" material.map_Ns = %s\n", materials[i].specular_highlight_texname.c_str()); + printf(" material.map_Ns = %s\n", + materials[i].specular_highlight_texname.c_str()); printf(" material.map_bump = %s\n", materials[i].bump_texname.c_str()); printf(" material.map_d = %s\n", materials[i].alpha_texname.c_str()); printf(" material.disp = %s\n", materials[i].displacement_texname.c_str()); printf(" material.refl = %s\n", materials[i].reflection_texname.c_str()); - std::map::const_iterator it(materials[i].unknown_parameter.begin()); - std::map::const_iterator itEnd(materials[i].unknown_parameter.end()); + std::map::const_iterator it( + materials[i].unknown_parameter.begin()); + std::map::const_iterator itEnd( + materials[i].unknown_parameter.end()); for (; it != itEnd; it++) { printf(" material.%s = %s\n", it->first.c_str(), it->second.c_str()); @@ -156,23 +178,25 @@ static void PrintInfo(const tinyobj::attrib_t &attrib, const std::vector shapes; std::vector materials; + std::string warn; std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, filename, basepath, triangulate); + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, + filename, basepath, triangulate); + + if (!warn.empty()) { + std::cout << "WARN: " << warn << std::endl; + } if (!err.empty()) { - std::cerr << err << std::endl; + std::cerr << "ERR: " << err << std::endl; } if (!ret) { @@ -185,13 +209,10 @@ TestLoadObj( return true; } -static bool -TestLoadObjFromPreopenedFile( - const char* filename, - const char* basepath = NULL, - bool readMaterials = true, - bool triangulate = true) -{ +static bool TestLoadObjFromPreopenedFile(const char* filename, + const char* basepath = NULL, + bool readMaterials = true, + bool triangulate = true) { std::string fullFilename = std::string(basepath) + filename; std::cout << "Loading " << fullFilename << std::endl; @@ -203,19 +224,24 @@ TestLoadObjFromPreopenedFile( } tinyobj::MaterialStreamReader materialStreamReader(fileStream); - tinyobj::MaterialStreamReader* materialReader = readMaterials - ? &materialStreamReader - : NULL; + tinyobj::MaterialStreamReader* materialReader = + readMaterials ? &materialStreamReader : NULL; tinyobj::attrib_t attrib; std::vector shapes; std::vector materials; + std::string warn; std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, &fileStream, materialReader); + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, + &fileStream, materialReader); + + if (!warn.empty()) { + std::cout << "WARN: " << warn << std::endl; + } if (!err.empty()) { - std::cerr << err << std::endl; + std::cerr << "ERR: " << err << std::endl; } if (!ret) { @@ -228,104 +254,106 @@ TestLoadObjFromPreopenedFile( return true; } -static bool -TestStreamLoadObj() -{ +static bool TestStreamLoadObj() { std::cout << "Stream Loading " << std::endl; std::stringstream objStream; - objStream - << "mtllib cube.mtl\n" - "\n" - "v 0.000000 2.000000 2.000000\n" - "v 0.000000 0.000000 2.000000\n" - "v 2.000000 0.000000 2.000000\n" - "v 2.000000 2.000000 2.000000\n" - "v 0.000000 2.000000 0.000000\n" - "v 0.000000 0.000000 0.000000\n" - "v 2.000000 0.000000 0.000000\n" - "v 2.000000 2.000000 0.000000\n" - "# 8 vertices\n" - "\n" - "g front cube\n" - "usemtl white\n" - "f 1 2 3 4\n" - "g back cube\n" - "# expects white material\n" - "f 8 7 6 5\n" - "g right cube\n" - "usemtl red\n" - "f 4 3 7 8\n" - "g top cube\n" - "usemtl white\n" - "f 5 1 4 8\n" - "g left cube\n" - "usemtl green\n" - "f 5 6 2 1\n" - "g bottom cube\n" - "usemtl white\n" - "f 2 6 7 3\n" - "# 6 elements"; + objStream << "mtllib cube.mtl\n" + "\n" + "v 0.000000 2.000000 2.000000\n" + "v 0.000000 0.000000 2.000000\n" + "v 2.000000 0.000000 2.000000\n" + "v 2.000000 2.000000 2.000000\n" + "v 0.000000 2.000000 0.000000\n" + "v 0.000000 0.000000 0.000000\n" + "v 2.000000 0.000000 0.000000\n" + "v 2.000000 2.000000 0.000000\n" + "# 8 vertices\n" + "\n" + "g front cube\n" + "usemtl white\n" + "f 1 2 3 4\n" + "g back cube\n" + "# expects white material\n" + "f 8 7 6 5\n" + "g right cube\n" + "usemtl red\n" + "f 4 3 7 8\n" + "g top cube\n" + "usemtl white\n" + "f 5 1 4 8\n" + "g left cube\n" + "usemtl green\n" + "f 5 6 2 1\n" + "g bottom cube\n" + "usemtl white\n" + "f 2 6 7 3\n" + "# 6 elements"; -std::string matStream( - "newmtl white\n" - "Ka 0 0 0\n" - "Kd 1 1 1\n" - "Ks 0 0 0\n" - "\n" - "newmtl red\n" - "Ka 0 0 0\n" - "Kd 1 0 0\n" - "Ks 0 0 0\n" - "\n" - "newmtl green\n" - "Ka 0 0 0\n" - "Kd 0 1 0\n" - "Ks 0 0 0\n" - "\n" - "newmtl blue\n" - "Ka 0 0 0\n" - "Kd 0 0 1\n" - "Ks 0 0 0\n" - "\n" - "newmtl light\n" - "Ka 20 20 20\n" - "Kd 1 1 1\n" - "Ks 0 0 0"); + std::string matStream( + "newmtl white\n" + "Ka 0 0 0\n" + "Kd 1 1 1\n" + "Ks 0 0 0\n" + "\n" + "newmtl red\n" + "Ka 0 0 0\n" + "Kd 1 0 0\n" + "Ks 0 0 0\n" + "\n" + "newmtl green\n" + "Ka 0 0 0\n" + "Kd 0 1 0\n" + "Ks 0 0 0\n" + "\n" + "newmtl blue\n" + "Ka 0 0 0\n" + "Kd 0 0 1\n" + "Ks 0 0 0\n" + "\n" + "newmtl light\n" + "Ka 20 20 20\n" + "Kd 1 1 1\n" + "Ks 0 0 0"); - using namespace tinyobj; - class MaterialStringStreamReader: - public MaterialReader - { - public: - MaterialStringStreamReader(const std::string& matSStream): m_matSStream(matSStream) {} - virtual ~MaterialStringStreamReader() {} - virtual bool operator() ( - const std::string& matId, - std::vector* materials, - std::map* matMap, - std::string* err) - { - (void)matId; - (void)err; - std::string warning; - LoadMtl(matMap, materials, &m_matSStream, &warning); - return true; - } + using namespace tinyobj; + class MaterialStringStreamReader : public MaterialReader { + public: + MaterialStringStreamReader(const std::string& matSStream) + : m_matSStream(matSStream) {} + virtual ~MaterialStringStreamReader() {} + virtual bool operator()(const std::string& matId, + std::vector* materials, + std::map* matMap, + std::string* warn, std::string* err) { + (void)matId; + (void)warn; + (void)err; + std::string warning; + std::string error_msg; + LoadMtl(matMap, materials, &m_matSStream, &warning, &error_msg); + return true; + } - private: - std::stringstream m_matSStream; - }; + private: + std::stringstream m_matSStream; + }; MaterialStringStreamReader matSSReader(matStream); tinyobj::attrib_t attrib; std::vector shapes; std::vector materials; + std::string warn; std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, &objStream, &matSSReader); - + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, + &objStream, &matSSReader); + + if (!warn.empty()) { + std::cout << "WARN: " << warn << std::endl; + } + if (!err.empty()) { - std::cerr << err << std::endl; + std::cerr << "ERR: " << err << std::endl; } if (!ret) { @@ -333,28 +361,33 @@ std::string matStream( } PrintInfo(attrib, shapes, materials); - + return true; } const char* gMtlBasePath = "../models/"; TEST_CASE("cornell_box", "[Loader]") { - - REQUIRE(true == TestLoadObj("../models/cornell_box.obj", gMtlBasePath)); + REQUIRE(true == TestLoadObj("../models/cornell_box.obj", gMtlBasePath)); } TEST_CASE("catmark_torus_creases0", "[Loader]") { - tinyobj::attrib_t attrib; std::vector shapes; std::vector materials; + std::string warn; std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/catmark_torus_creases0.obj", gMtlBasePath, /*triangulate*/false); + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, + "../models/catmark_torus_creases0.obj", + gMtlBasePath, /*triangulate*/ false); + + if (!warn.empty()) { + std::cout << "WARN: " << warn << std::endl; + } if (!err.empty()) { - std::cerr << err << std::endl; + std::cerr << "ERR: " << err << std::endl; } REQUIRE(true == ret); @@ -364,16 +397,22 @@ TEST_CASE("catmark_torus_creases0", "[Loader]") { } TEST_CASE("pbr", "[Loader]") { - tinyobj::attrib_t attrib; std::vector shapes; std::vector materials; + std::string warn; std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/pbr-mat-ext.obj", gMtlBasePath, /*triangulate*/false); + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, + "../models/pbr-mat-ext.obj", gMtlBasePath, + /*triangulate*/ false); + + if (!warn.empty()) { + std::cout << "WARN: " << warn << std::endl; + } if (!err.empty()) { - std::cerr << err << std::endl; + std::cerr << "ERR: " << err << std::endl; } REQUIRE(true == ret); REQUIRE(1 == materials.size()); @@ -391,18 +430,18 @@ TEST_CASE("pbr", "[Loader]") { REQUIRE(0 == materials[0].normal_texname.compare("normalmap.tex")); } -TEST_CASE("stream_load", "[Stream]") { - REQUIRE(true == TestStreamLoadObj()); -} +TEST_CASE("stream_load", "[Stream]") { REQUIRE(true == TestStreamLoadObj()); } TEST_CASE("stream_load_from_file_skipping_materials", "[Stream]") { REQUIRE(true == TestLoadObjFromPreopenedFile( - "../models/pbr-mat-ext.obj", gMtlBasePath, /*readMaterials*/false, /*triangulate*/false)); + "../models/pbr-mat-ext.obj", gMtlBasePath, + /*readMaterials*/ false, /*triangulate*/ false)); } TEST_CASE("stream_load_from_file_with_materials", "[Stream]") { REQUIRE(true == TestLoadObjFromPreopenedFile( - "../models/pbr-mat-ext.obj", gMtlBasePath, /*readMaterials*/true, /*triangulate*/false)); + "../models/pbr-mat-ext.obj", gMtlBasePath, + /*readMaterials*/ true, /*triangulate*/ false)); } TEST_CASE("trailing_whitespace_in_mtl", "[Issue92]") { @@ -410,11 +449,17 @@ TEST_CASE("trailing_whitespace_in_mtl", "[Issue92]") { std::vector shapes; std::vector materials; + std::string warn; std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/issue-92.obj", gMtlBasePath); + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, + "../models/issue-92.obj", gMtlBasePath); + + if (!warn.empty()) { + std::cout << "WARN: " << warn << std::endl; + } if (!err.empty()) { - std::cerr << err << std::endl; + std::cerr << "ERR: " << err << std::endl; } REQUIRE(true == ret); REQUIRE(1 == materials.size()); @@ -426,11 +471,17 @@ TEST_CASE("transmittance_filter", "[Issue95]") { std::vector shapes; std::vector materials; + std::string warn; std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/issue-95.obj", gMtlBasePath); + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, + "../models/issue-95.obj", gMtlBasePath); + + if (!warn.empty()) { + std::cout << "WARN: " << warn << std::endl; + } if (!err.empty()) { - std::cerr << err << std::endl; + std::cerr << "ERR: " << err << std::endl; } REQUIRE(true == ret); REQUIRE(1 == materials.size()); @@ -444,11 +495,17 @@ TEST_CASE("transmittance_filter_Tf", "[Issue95-Tf]") { std::vector shapes; std::vector materials; + std::string warn; std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/issue-95-2.obj", gMtlBasePath); + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, + "../models/issue-95-2.obj", gMtlBasePath); + + if (!warn.empty()) { + std::cout << "WARN: " << warn << std::endl; + } if (!err.empty()) { - std::cerr << err << std::endl; + std::cerr << "ERR: " << err << std::endl; } REQUIRE(true == ret); REQUIRE(1 == materials.size()); @@ -462,11 +519,17 @@ TEST_CASE("transmittance_filter_Kt", "[Issue95-Kt]") { std::vector shapes; std::vector materials; + std::string warn; std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/issue-95.obj", gMtlBasePath); + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, + "../models/issue-95.obj", gMtlBasePath); + + if (!warn.empty()) { + std::cout << "WARN: " << warn << std::endl; + } if (!err.empty()) { - std::cerr << err << std::endl; + std::cerr << "ERR: " << err << std::endl; } REQUIRE(true == ret); REQUIRE(1 == materials.size()); @@ -480,11 +543,17 @@ TEST_CASE("usemtl_at_last_line", "[Issue104]") { std::vector shapes; std::vector materials; + std::string warn; std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/usemtl-issue-104.obj", gMtlBasePath); + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, + "../models/usemtl-issue-104.obj", gMtlBasePath); + + if (!warn.empty()) { + std::cout << "WARN: " << warn << std::endl; + } if (!err.empty()) { - std::cerr << err << std::endl; + std::cerr << "ERR: " << err << std::endl; } REQUIRE(true == ret); REQUIRE(1 == shapes.size()); @@ -495,11 +564,18 @@ TEST_CASE("texture_opts", "[Issue85]") { std::vector shapes; std::vector materials; + std::string warn; std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/texture-options-issue-85.obj", gMtlBasePath); + bool ret = + tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, + "../models/texture-options-issue-85.obj", gMtlBasePath); + + if (!warn.empty()) { + std::cout << "WARN: " << warn << std::endl; + } if (!err.empty()) { - std::cerr << err << std::endl; + std::cerr << "ERR: " << err << std::endl; } REQUIRE(true == ret); REQUIRE(1 == shapes.size()); @@ -525,11 +601,13 @@ TEST_CASE("texture_opts", "[Issue85]") { REQUIRE(tinyobj::TEXTURE_TYPE_SPHERE == materials[2].diffuse_texopt.type); REQUIRE(tinyobj::TEXTURE_TYPE_CUBE_TOP == materials[2].specular_texopt.type); - REQUIRE(tinyobj::TEXTURE_TYPE_CUBE_BOTTOM == materials[2].specular_highlight_texopt.type); + REQUIRE(tinyobj::TEXTURE_TYPE_CUBE_BOTTOM == + materials[2].specular_highlight_texopt.type); REQUIRE(tinyobj::TEXTURE_TYPE_CUBE_LEFT == materials[2].ambient_texopt.type); REQUIRE(tinyobj::TEXTURE_TYPE_CUBE_RIGHT == materials[2].alpha_texopt.type); REQUIRE(tinyobj::TEXTURE_TYPE_CUBE_FRONT == materials[2].bump_texopt.type); - REQUIRE(tinyobj::TEXTURE_TYPE_CUBE_BACK == materials[2].displacement_texopt.type); + REQUIRE(tinyobj::TEXTURE_TYPE_CUBE_BACK == + materials[2].displacement_texopt.type); } TEST_CASE("mtllib_multiple_filenames", "[Issue112]") { @@ -537,11 +615,18 @@ TEST_CASE("mtllib_multiple_filenames", "[Issue112]") { std::vector shapes; std::vector materials; + std::string warn; std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/mtllib-multiple-files-issue-112.obj", gMtlBasePath); + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, + "../models/mtllib-multiple-files-issue-112.obj", + gMtlBasePath); + + if (!warn.empty()) { + std::cout << "WARN: " << warn << std::endl; + } if (!err.empty()) { - std::cerr << err << std::endl; + std::cerr << "ERR: " << err << std::endl; } REQUIRE(true == ret); REQUIRE(1 == materials.size()); @@ -552,11 +637,17 @@ TEST_CASE("tr_and_d", "[Issue43]") { std::vector shapes; std::vector materials; + std::string warn; std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/tr-and-d-issue-43.obj", gMtlBasePath); + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, + "../models/tr-and-d-issue-43.obj", gMtlBasePath); + + if (!warn.empty()) { + std::cout << "WARN: " << warn << std::endl; + } if (!err.empty()) { - std::cerr << err << std::endl; + std::cerr << "ERR: " << err << std::endl; } REQUIRE(true == ret); REQUIRE(2 == materials.size()); @@ -570,11 +661,17 @@ TEST_CASE("refl", "[refl]") { std::vector shapes; std::vector materials; + std::string warn; std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/refl.obj", gMtlBasePath); + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, + "../models/refl.obj", gMtlBasePath); + + if (!warn.empty()) { + std::cout << "WARN: " << warn << std::endl; + } if (!err.empty()) { - std::cerr << err << std::endl; + std::cerr << "ERR: " << err << std::endl; } PrintInfo(attrib, shapes, materials); @@ -590,11 +687,17 @@ TEST_CASE("map_Bump", "[bump]") { std::vector shapes; std::vector materials; + std::string warn; std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/map-bump.obj", gMtlBasePath); + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, + "../models/map-bump.obj", gMtlBasePath); + + if (!warn.empty()) { + std::cout << "WARN: " << warn << std::endl; + } if (!err.empty()) { - std::cerr << err << std::endl; + std::cerr << "ERR: " << err << std::endl; } PrintInfo(attrib, shapes, materials); @@ -610,11 +713,17 @@ TEST_CASE("g_ignored", "[Issue138]") { std::vector shapes; std::vector materials; + std::string warn; std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/issue-138.obj", gMtlBasePath); + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, + "../models/issue-138.obj", gMtlBasePath); + + if (!warn.empty()) { + std::cout << "WARN: " << warn << std::endl; + } if (!err.empty()) { - std::cerr << err << std::endl; + std::cerr << "ERR: " << err << std::endl; } PrintInfo(attrib, shapes, materials); @@ -622,7 +731,6 @@ TEST_CASE("g_ignored", "[Issue138]") { REQUIRE(true == ret); REQUIRE(2 == shapes.size()); REQUIRE(2 == materials.size()); - } TEST_CASE("vertex-col-ext", "[Issue144]") { @@ -630,15 +738,21 @@ TEST_CASE("vertex-col-ext", "[Issue144]") { std::vector shapes; std::vector materials; + std::string warn; std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/cube-vertexcol.obj", gMtlBasePath); + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, + "../models/cube-vertexcol.obj", gMtlBasePath); - if (!err.empty()) { - std::cerr << err << std::endl; + if (!warn.empty()) { + std::cout << "WARN: " << warn << std::endl; } - //PrintInfo(attrib, shapes, materials); + if (!err.empty()) { + std::cerr << "ERR: " << err << std::endl; + } + + // PrintInfo(attrib, shapes, materials); REQUIRE(true == ret); REQUIRE((8 * 3) == attrib.colors.size()); @@ -663,18 +777,23 @@ TEST_CASE("norm_texopts", "[norm]") { std::vector shapes; std::vector materials; + std::string warn; std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/norm-texopt.obj", gMtlBasePath); + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, + "../models/norm-texopt.obj", gMtlBasePath); + + if (!warn.empty()) { + std::cout << "WARN: " << warn << std::endl; + } if (!err.empty()) { - std::cerr << err << std::endl; + std::cerr << "ERR: " << err << std::endl; } REQUIRE(true == ret); REQUIRE(1 == shapes.size()); REQUIRE(1 == materials.size()); REQUIRE(3.0 == Approx(materials[0].normal_texopt.bump_multiplier)); - } TEST_CASE("zero-face-idx-value", "[Issue140]") { @@ -682,16 +801,21 @@ TEST_CASE("zero-face-idx-value", "[Issue140]") { std::vector shapes; std::vector materials; + std::string warn; std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/issue-140-zero-face-idx.obj", gMtlBasePath); + bool ret = + tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, + "../models/issue-140-zero-face-idx.obj", gMtlBasePath); + + if (!warn.empty()) { + std::cout << "WARN: " << warn << std::endl; + } - if (!err.empty()) { - std::cerr << err << std::endl; + std::cerr << "ERR: " << err << std::endl; } REQUIRE(false == ret); REQUIRE(!err.empty()); - } TEST_CASE("texture-name-whitespace", "[Issue145]") { @@ -699,12 +823,18 @@ TEST_CASE("texture-name-whitespace", "[Issue145]") { std::vector shapes; std::vector materials; + std::string warn; std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/texture-filename-with-whitespace.obj", gMtlBasePath); + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, + "../models/texture-filename-with-whitespace.obj", + gMtlBasePath); + + if (!warn.empty()) { + std::cout << "WARN: " << warn << std::endl; + } - if (!err.empty()) { - std::cerr << "[Issue145] " << err << std::endl; + std::cerr << "ERR: " << err << std::endl; } REQUIRE(true == ret); @@ -714,7 +844,6 @@ TEST_CASE("texture-name-whitespace", "[Issue145]") { REQUIRE(0 == materials[0].diffuse_texname.compare("texture 01.png")); REQUIRE(0 == materials[1].bump_texname.compare("bump 01.png")); REQUIRE(2 == Approx(materials[1].bump_texopt.bump_multiplier)); - } TEST_CASE("smoothing-group", "[Issue162]") { @@ -722,12 +851,18 @@ TEST_CASE("smoothing-group", "[Issue162]") { std::vector shapes; std::vector materials; + std::string warn; std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/issue-162-smoothing-group.obj", gMtlBasePath); + bool ret = + tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, + "../models/issue-162-smoothing-group.obj", gMtlBasePath); + + if (!warn.empty()) { + std::cout << "WARN: " << warn << std::endl; + } - if (!err.empty()) { - std::cerr << "[Issue162] " << err << std::endl; + std::cerr << "ERR: " << err << std::endl; } REQUIRE(true == ret); @@ -748,7 +883,6 @@ TEST_CASE("smoothing-group", "[Issue162]") { REQUIRE(0 == shapes[1].mesh.smoothing_group_ids[7]); REQUIRE(6 == shapes[1].mesh.smoothing_group_ids[8]); REQUIRE(6 == shapes[1].mesh.smoothing_group_ids[9]); - } TEST_CASE("invalid-face-definition", "[face]") { @@ -756,11 +890,18 @@ TEST_CASE("invalid-face-definition", "[face]") { std::vector shapes; std::vector materials; + std::string warn; std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/invalid-face-definition.obj", gMtlBasePath); + bool ret = + tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, + "../models/invalid-face-definition.obj", gMtlBasePath); + + if (!warn.empty()) { + std::cout << "WARN: " << warn << std::endl; + } if (!err.empty()) { - std::cerr << "[face] " << err << std::endl; + std::cerr << "ERR: " << err << std::endl; } REQUIRE(true == ret); @@ -768,23 +909,29 @@ TEST_CASE("invalid-face-definition", "[face]") { REQUIRE(0 == shapes[0].mesh.indices.size()); } -TEST_CASE("Empty mtl basedir", "[Issue177]") { +TEST_CASE("Empty mtl basedir", "[Issue177]") { tinyobj::attrib_t attrib; std::vector shapes; std::vector materials; - + + std::string warn; std::string err; - + // A case where the user explicitly provides an empty string // Win32 specific? - const char * userBaseDir = ""; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "issue-177.obj", userBaseDir); - - // if mtl loading fails, we get an error message (WARN) here - ret &= err.empty(); + const char* userBaseDir = ""; + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, + "issue-177.obj", userBaseDir); + + // if mtl loading fails, we get an warning message here + ret &= warn.empty(); + + if (!warn.empty()) { + std::cout << "WARN: " << warn << std::endl; + } if (!err.empty()) { - std::cerr << "[Issue177] " << err << std::endl; + std::cerr << "ERR: " << err << std::endl; } REQUIRE(true == ret); @@ -795,11 +942,17 @@ TEST_CASE("line-primitive", "[line]") { std::vector shapes; std::vector materials; + std::string warn; std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/line-prim.obj", gMtlBasePath); + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, + "../models/line-prim.obj", gMtlBasePath); + + if (!warn.empty()) { + std::cout << "WARN: " << warn << std::endl; + } if (!err.empty()) { - std::cerr << "[line] " << err << std::endl; + std::cerr << "ERR: " << err << std::endl; } REQUIRE(true == ret); @@ -812,17 +965,25 @@ TEST_CASE("multiple-group-names", "[group]") { std::vector shapes; std::vector materials; + std::string warn; std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/cube.obj", gMtlBasePath); + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, + "../models/cube.obj", gMtlBasePath); + + if (!warn.empty()) { + std::cout << "WARN: " << warn << std::endl; + } if (!err.empty()) { - std::cerr << "[group] " << err << std::endl; + std::cerr << "ERR: " << err << std::endl; } REQUIRE(true == ret); REQUIRE(6 == shapes.size()); REQUIRE(0 == shapes[0].name.compare("front cube")); - REQUIRE(0 == shapes[1].name.compare("back cube")); // multiple whitespaces are aggregated as single white space. + REQUIRE(0 == shapes[1].name.compare("back cube")); // multiple whitespaces + // are aggregated as + // single white space. } TEST_CASE("colorspace", "[Issue184]") { @@ -830,12 +991,20 @@ TEST_CASE("colorspace", "[Issue184]") { std::vector shapes; std::vector materials; + std::string warn; std::string err; - bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/colorspace-issue-184.obj", gMtlBasePath); + bool ret = + tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, + "../models/colorspace-issue-184.obj", gMtlBasePath); + + if (!warn.empty()) { + std::cout << "WARN: " << warn << std::endl; + } if (!err.empty()) { - std::cerr << err << std::endl; + std::cerr << "ERR: " << err << std::endl; } + REQUIRE(true == ret); REQUIRE(1 == shapes.size()); REQUIRE(1 == materials.size()); @@ -846,7 +1015,8 @@ TEST_CASE("colorspace", "[Issue184]") { // Fuzzer test. // Just check if it does not crash. -// Disable by default since Windows filesystem can't create filename of afl testdata +// Disable by default since Windows filesystem can't create filename of afl +// testdata #if 0 TEST_CASE("afl000000", "[AFL]") { diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index f627e94..8ef44a7 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -23,6 +23,7 @@ THE SOFTWARE. */ // +// version 1.3.0 : Separate warning and error message(breaking API of LoadObj) // version 1.2.3 : Added color space extension('-colorspace') to tex opts. // version 1.2.2 : Parse multiple group names. // version 1.2.1 : Added initial support for line('l') primitive(PR #178) @@ -117,8 +118,9 @@ namespace tinyobj { // // TinyObjLoader extension. // -// -colorspace SPACE # Color space of the texture. e.g. 'sRGB` or 'linear' -// +// -colorspace SPACE # Color space of the texture. e.g. +// 'sRGB` or 'linear' +// #ifdef TINYOBJLOADER_USE_DOUBLE //#pragma message "using double" @@ -153,9 +155,10 @@ typedef struct { bool blendu; // -blendu (default on) bool blendv; // -blendv (default on) real_t bump_multiplier; // -bm (for bump maps only, default 1.0) - + // extension - std::string colorspace; // Explicitly specify color space of stored value. Usually `sRGB` or `linear` (default empty). + std::string colorspace; // Explicitly specify color space of stored value. + // Usually `sRGB` or `linear` (default empty). } texture_option_t; typedef struct { @@ -307,7 +310,7 @@ class MaterialReader { virtual bool operator()(const std::string &matId, std::vector *materials, - std::map *matMap, + std::map *matMap, std::string *warn, std::string *err) = 0; }; @@ -318,7 +321,8 @@ class MaterialFileReader : public MaterialReader { virtual ~MaterialFileReader() {} virtual bool operator()(const std::string &matId, std::vector *materials, - std::map *matMap, std::string *err); + std::map *matMap, std::string *warn, + std::string *err); private: std::string m_mtlBaseDir; @@ -331,7 +335,8 @@ class MaterialStreamReader : public MaterialReader { virtual ~MaterialStreamReader() {} virtual bool operator()(const std::string &matId, std::vector *materials, - std::map *matMap, std::string *err); + std::map *matMap, std::string *warn, + std::string *err); private: std::istream &m_inStream; @@ -341,7 +346,7 @@ class MaterialStreamReader : public MaterialReader { /// 'attrib', 'shapes' and 'materials' will be filled with parsed shape data /// 'shapes' will be filled with parsed shape data /// Returns true when loading .obj become success. -/// Returns warning and error message into `err` +/// Returns warning message into `warn`, and error message into `err` /// 'mtl_basedir' is optional, and used for base directory for .mtl file. /// In default(`NULL'), .mtl file is searched from an application's working /// directory. @@ -350,34 +355,36 @@ class MaterialStreamReader : public MaterialReader { /// Option 'default_vcols_fallback' specifies whether vertex colors should /// always be defined, even if no colors are given (fallback to white). bool LoadObj(attrib_t *attrib, std::vector *shapes, - std::vector *materials, std::string *err, - const char *filename, const char *mtl_basedir = NULL, - bool triangulate = true, bool default_vcols_fallback = true); + std::vector *materials, std::string *warn, + std::string *err, const char *filename, + const char *mtl_basedir = NULL, bool triangulate = true, + bool default_vcols_fallback = true); /// Loads .obj from a file with custom user callback. /// .mtl is loaded as usual and parsed material_t data will be passed to /// `callback.mtllib_cb`. /// Returns true when loading .obj/.mtl become success. -/// Returns warning and error message into `err` +/// Returns warning message into `warn`, and error message into `err` /// See `examples/callback_api/` for how to use this function. bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, void *user_data = NULL, MaterialReader *readMatFn = NULL, - std::string *err = NULL); + std::string *warn = NULL, std::string *err = NULL); /// Loads object from a std::istream, uses GetMtlIStreamFn to retrieve /// std::istream for materials. /// Returns true when loading .obj become success. /// Returns warning and error message into `err` bool LoadObj(attrib_t *attrib, std::vector *shapes, - std::vector *materials, std::string *err, - std::istream *inStream, MaterialReader *readMatFn = NULL, - bool triangulate = true, bool default_vcols_fallback = true); + std::vector *materials, std::string *warn, + std::string *err, std::istream *inStream, + MaterialReader *readMatFn = NULL, bool triangulate = true, + bool default_vcols_fallback = true); /// Loads materials into std::map void LoadMtl(std::map *material_map, std::vector *materials, std::istream *inStream, - std::string *warning); + std::string *warning, std::string *err); } // namespace tinyobj @@ -721,12 +728,13 @@ static inline bool parseVertexWithColor(real_t *x, real_t *y, real_t *z, (*y) = parseReal(token, default_y); (*z) = parseReal(token, default_z); - const bool found_color = parseReal(token, r) && parseReal(token, g) && parseReal(token, b); - + const bool found_color = + parseReal(token, r) && parseReal(token, g) && parseReal(token, b); + if (!found_color) { (*r) = (*g) = (*b) = 1.0; } - + return found_color; } @@ -959,11 +967,12 @@ static bool ParseTextureNameAndOption(std::string *texname, } else if ((0 == strncmp(token, "-mm", 3)) && IS_SPACE((token[3]))) { token += 4; parseReal2(&(texopt->brightness), &(texopt->contrast), &token, 0.0, 1.0); - } else if ((0 == strncmp(token, "-colorspace", 11)) && IS_SPACE((token[11]))) { + } else if ((0 == strncmp(token, "-colorspace", 11)) && + IS_SPACE((token[11]))) { token += 12; texopt->colorspace = parseString(&token); } else { - // Assume texture filename +// Assume texture filename #if 0 size_t len = strcspn(token, " \t\r"); // untile next space texture_name = std::string(token, token + len); @@ -1303,7 +1312,9 @@ static void SplitString(const std::string &s, char delim, void LoadMtl(std::map *material_map, std::vector *materials, std::istream *inStream, - std::string *warning) { + std::string *warning, std::string *err) { + (void)err; + // Create a default material anyway. material_t material; InitMaterial(&material); @@ -1312,7 +1323,7 @@ void LoadMtl(std::map *material_map, bool has_d = false; bool has_tr = false; - std::stringstream ss; + std::stringstream warn_ss; std::string linebuf; while (inStream->peek() != -1) { @@ -1455,9 +1466,9 @@ void LoadMtl(std::map *material_map, material.dissolve = parseReal(&token); if (has_tr) { - ss << "WARN: Both `d` and `Tr` parameters defined for \"" - << material.name << "\". Use the value of `d` for dissolve." - << std::endl; + warn_ss << "Both `d` and `Tr` parameters defined for \"" + << material.name << "\". Use the value of `d` for dissolve." + << std::endl; } has_d = true; continue; @@ -1466,9 +1477,9 @@ void LoadMtl(std::map *material_map, token += 2; if (has_d) { // `d` wins. Ignore `Tr` value. - ss << "WARN: Both `d` and `Tr` parameters defined for \"" - << material.name << "\". Use the value of `d` for dissolve." - << std::endl; + warn_ss << "Both `d` and `Tr` parameters defined for \"" + << material.name << "\". Use the value of `d` for dissolve." + << std::endl; } else { // We invert value of Tr(assume Tr is in range [0, 1]) // NOTE: Interpretation of Tr is application(exporter) dependent. For @@ -1683,14 +1694,14 @@ void LoadMtl(std::map *material_map, materials->push_back(material); if (warning) { - (*warning) = ss.str(); + (*warning) = warn_ss.str(); } } bool MaterialFileReader::operator()(const std::string &matId, std::vector *materials, std::map *matMap, - std::string *err) { + std::string *warn, std::string *err) { std::string filepath; if (!m_mtlBaseDir.empty()) { @@ -1702,21 +1713,14 @@ bool MaterialFileReader::operator()(const std::string &matId, std::ifstream matIStream(filepath.c_str()); if (!matIStream) { std::stringstream ss; - ss << "WARN: Material file [ " << filepath << " ] not found." << std::endl; - if (err) { - (*err) += ss.str(); + ss << "Material file [ " << filepath << " ] not found." << std::endl; + if (warn) { + (*warn) += ss.str(); } return false; } - std::string warning; - LoadMtl(matMap, materials, &matIStream, &warning); - - if (!warning.empty()) { - if (err) { - (*err) += warning; - } - } + LoadMtl(matMap, materials, &matIStream, warn, err); return true; } @@ -1724,32 +1728,26 @@ bool MaterialFileReader::operator()(const std::string &matId, bool MaterialStreamReader::operator()(const std::string &matId, std::vector *materials, std::map *matMap, - std::string *err) { + std::string *warn, std::string *err) { + (void)err; (void)matId; if (!m_inStream) { std::stringstream ss; - ss << "WARN: Material stream in error state. " << std::endl; - if (err) { - (*err) += ss.str(); + ss << "Material stream in error state. " << std::endl; + if (warn) { + (*warn) += ss.str(); } return false; } - std::string warning; - LoadMtl(matMap, materials, &m_inStream, &warning); - - if (!warning.empty()) { - if (err) { - (*err) += warning; - } - } + LoadMtl(matMap, materials, &m_inStream, warn, err); return true; } bool LoadObj(attrib_t *attrib, std::vector *shapes, - std::vector *materials, std::string *err, - const char *filename, const char *mtl_basedir, + std::vector *materials, std::string *warn, + std::string *err, const char *filename, const char *mtl_basedir, bool trianglulate, bool default_vcols_fallback) { attrib->vertices.clear(); attrib->normals.clear(); @@ -1779,14 +1777,15 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, } MaterialFileReader matFileReader(baseDir); - return LoadObj(attrib, shapes, materials, err, &ifs, &matFileReader, + return LoadObj(attrib, shapes, materials, warn, err, &ifs, &matFileReader, trianglulate, default_vcols_fallback); } bool LoadObj(attrib_t *attrib, std::vector *shapes, - std::vector *materials, std::string *err, - std::istream *inStream, MaterialReader *readMatFn /*= NULL*/, - bool triangulate, bool default_vcols_fallback) { + std::vector *materials, std::string *warn, + std::string *err, std::istream *inStream, + MaterialReader *readMatFn /*= NULL*/, bool triangulate, + bool default_vcols_fallback) { std::stringstream errss; std::vector v; @@ -1813,7 +1812,7 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, shape_t shape; bool found_all_colors = true; - + size_t line_num = 0; std::string linebuf; while (inStream->peek() != -1) { @@ -1850,9 +1849,9 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, token += 2; real_t x, y, z; real_t r, g, b; - + found_all_colors &= parseVertexWithColor(&x, &y, &z, &r, &g, &b, &token); - + v.push_back(x); v.push_back(y); v.push_back(z); @@ -1862,7 +1861,7 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, vc.push_back(g); vc.push_back(b); } - + continue; } @@ -1936,8 +1935,10 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, } greatest_v_idx = greatest_v_idx > vi.v_idx ? greatest_v_idx : vi.v_idx; - greatest_vn_idx = greatest_vn_idx > vi.vn_idx ? greatest_vn_idx : vi.vn_idx; - greatest_vt_idx = greatest_vt_idx > vi.vt_idx ? greatest_vt_idx : vi.vt_idx; + greatest_vn_idx = + greatest_vn_idx > vi.vn_idx ? greatest_vn_idx : vi.vn_idx; + greatest_vt_idx = + greatest_vt_idx > vi.vt_idx ? greatest_vt_idx : vi.vt_idx; face.vertex_indices.push_back(vi); size_t n = strspn(token, " \t\r"); @@ -1949,7 +1950,7 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, continue; } - + // use mtl if ((0 == strncmp(token, "usemtl", 6)) && IS_SPACE((token[6]))) { token += 7; @@ -1986,19 +1987,24 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, SplitString(std::string(token), ' ', filenames); if (filenames.empty()) { - if (err) { - (*err) += - "WARN: Looks like empty filename for mtllib. Use default " + if (warn) { + (*warn) += + "Looks like empty filename for mtllib. Use default " "material. \n"; } } else { bool found = false; for (size_t s = 0; s < filenames.size(); s++) { + std::string warn_mtl; std::string err_mtl; bool ok = (*readMatFn)(filenames[s].c_str(), materials, - &material_map, &err_mtl); + &material_map, &warn_mtl, &err_mtl); + if (warn && (!warn_mtl.empty())) { + (*warn) += warn_mtl; + } + if (err && (!err_mtl.empty())) { - (*err) += err_mtl; // This should be warn message. + (*err) += err_mtl; } if (ok) { @@ -2008,9 +2014,9 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, } if (!found) { - if (err) { - (*err) += - "WARN: Failed to load material file(s). Use default " + if (warn) { + (*warn) += + "Failed to load material file(s). Use default " "material.\n"; } } @@ -2048,14 +2054,13 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, if (names.size() < 2) { // 'g' with empty names - if (err) { + if (warn) { std::stringstream ss; - ss << "WARN: Empty group name. line: " << line_num << "\n"; - (*err) += ss.str(); + ss << "Empty group name. line: " << line_num << "\n"; + (*warn) += ss.str(); name = ""; } } else { - std::stringstream ss; ss << names[1]; @@ -2068,7 +2073,6 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, } name = ss.str(); - } continue; @@ -2184,34 +2188,31 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, // Ignore unknown command. } - + // not all vertices have colors, no default colors desired? -> clear colors - if (!found_all_colors && !default_vcols_fallback) { + if (!found_all_colors && !default_vcols_fallback) { vc.clear(); } - - if (greatest_v_idx >= static_cast(v.size() / 3)) - { - if (err) { + + if (greatest_v_idx >= static_cast(v.size() / 3)) { + if (warn) { std::stringstream ss; - ss << "WARN: Vertex indices out of bounds.\n" << std::endl; - (*err) += ss.str(); + ss << "Vertex indices out of bounds.\n" << std::endl; + (*warn) += ss.str(); } } - if (greatest_vn_idx >= static_cast(vn.size() / 3)) - { - if (err) { + if (greatest_vn_idx >= static_cast(vn.size() / 3)) { + if (warn) { std::stringstream ss; - ss << "WARN: Vertex normal indices out of bounds.\n" << std::endl; - (*err) += ss.str(); + ss << "Vertex normal indices out of bounds.\n" << std::endl; + (*warn) += ss.str(); } } - if (greatest_vt_idx >= static_cast(vt.size() / 2)) - { - if (err) { + if (greatest_vt_idx >= static_cast(vt.size() / 2)) { + if (warn) { std::stringstream ss; - ss << "WARN: Vertex texcoord indices out of bounds.\n" << std::endl; - (*err) += ss.str(); + ss << "Vertex texcoord indices out of bounds.\n" << std::endl; + (*warn) += ss.str(); } } @@ -2241,6 +2242,7 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, void *user_data /*= NULL*/, MaterialReader *readMatFn /*= NULL*/, + std::string *warn, /* = NULL*/ std::string *err /*= NULL*/) { std::stringstream errss; @@ -2377,19 +2379,25 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, SplitString(std::string(token), ' ', filenames); if (filenames.empty()) { - if (err) { - (*err) += - "WARN: Looks like empty filename for mtllib. Use default " + if (warn) { + (*warn) += + "Looks like empty filename for mtllib. Use default " "material. \n"; } } else { bool found = false; for (size_t s = 0; s < filenames.size(); s++) { + std::string warn_mtl; std::string err_mtl; bool ok = (*readMatFn)(filenames[s].c_str(), &materials, - &material_map, &err_mtl); + &material_map, &warn_mtl, &err_mtl); + + if (warn && (!warn_mtl.empty())) { + (*warn) += warn_mtl; // This should be warn message. + } + if (err && (!err_mtl.empty())) { - (*err) += err_mtl; // This should be warn message. + (*err) += err_mtl; } if (ok) { @@ -2399,9 +2407,9 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, } if (!found) { - if (err) { - (*err) += - "WARN: Failed to load material file(s). Use default " + if (warn) { + (*warn) += + "Failed to load material file(s). Use default " "material.\n"; } } else {