From 08ff49d2d032dd591aa38669f8cddcf881cc13fd Mon Sep 17 00:00:00 2001 From: E J Burns Date: Mon, 28 Apr 2014 14:37:32 +0100 Subject: [PATCH] Added a LoadObj function that reads from a std::istream. + Added a LoadObj function that reads from a std::istream. This should allow more generic usage and possibly make testing a little easier. o This LoadObj accepts a function that returns a std::unique_ptr for a material. + Modified LoadMtl to read from a std::istream to allow more generic usage. + Modified test.cc to check that the changes work as expected and nothing was broken. Tests: + Compiled test.cc, checked diff of output against pre change output. Same output where expected. --- test.cc | 137 +++++++++++++++++++++++++++++++++++++++------ tiny_obj_loader.cc | 68 +++++++++++++--------- tiny_obj_loader.h | 16 ++++++ 3 files changed, 179 insertions(+), 42 deletions(-) diff --git a/test.cc b/test.cc index 4ee4adc..b5e7d0c 100644 --- a/test.cc +++ b/test.cc @@ -3,23 +3,15 @@ #include #include #include +#include #include +#include +#include +#include +#include -static bool -TestLoadObj( - const char* filename, - const char* basepath = NULL) +static void PrintInfo(const std::vector& shapes) { - std::cout << "Loading " << filename << std::endl; - - std::vector shapes; - std::string err = tinyobj::LoadObj(shapes, filename, basepath); - - if (!err.empty()) { - std::cerr << err << std::endl; - return false; - } - std::cout << "# of shapes : " << shapes.size() << std::endl; for (size_t i = 0; i < shapes.size(); i++) { @@ -53,14 +45,124 @@ TestLoadObj( printf(" material.map_Kd = %s\n", shapes[i].material.diffuse_texname.c_str()); printf(" material.map_Ks = %s\n", shapes[i].material.specular_texname.c_str()); printf(" material.map_Ns = %s\n", shapes[i].material.normal_texname.c_str()); - std::map::iterator it(shapes[i].material.unknown_parameter.begin()); - std::map::iterator itEnd(shapes[i].material.unknown_parameter.end()); + auto it = shapes[i].material.unknown_parameter.begin(); + auto itEnd = shapes[i].material.unknown_parameter.end(); for (; it != itEnd; it++) { printf(" material.%s = %s\n", it->first.c_str(), it->second.c_str()); } printf("\n"); } +} +static bool +TestLoadObj( + const char* filename, + const char* basepath = NULL) +{ + std::cout << "Loading " << filename << std::endl; + + std::vector shapes; + std::string err = tinyobj::LoadObj(shapes, filename, basepath); + + if (!err.empty()) { + std::cerr << err << std::endl; + return false; + } + + PrintInfo(shapes); + + return true; +} + + +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"; + + auto getMatFileIStreamFunc = + [](const std::string& matId) + { + if (matId == "cube.mtl") { + + std::unique_ptr matStream( + new std::stringstream( + "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")); + + return matStream; + } + + std::unique_ptr emptyUP( nullptr ); + return emptyUP; + }; + + std::vector shapes; + std::string err = tinyobj::LoadObj(shapes, objStream, getMatFileIStreamFunc); + + if (!err.empty()) { + std::cerr << err << std::endl; + return false; + } + + PrintInfo(shapes); + return true; } @@ -79,7 +181,8 @@ main( } else { assert(true == TestLoadObj("cornell_box.obj")); assert(true == TestLoadObj("cube.obj")); + assert(true == TestStreamLoadObj()); } - + return 0; } diff --git a/tiny_obj_loader.cc b/tiny_obj_loader.cc index e0fb3db..e7c2346 100644 --- a/tiny_obj_loader.cc +++ b/tiny_obj_loader.cc @@ -290,35 +290,19 @@ exportFaceGroupToShape( } - std::string LoadMtl ( std::map& material_map, - const char* filename, - const char* mtl_basepath) + std::istream& inStream) { material_map.clear(); std::stringstream err; - std::string filepath; - - if (mtl_basepath) { - filepath = std::string(mtl_basepath) + std::string(filename); - } else { - filepath = std::string(filename); - } - - std::ifstream ifs(filepath.c_str()); - if (!ifs) { - err << "Cannot open file [" << filepath << "]" << std::endl; - return err.str(); - } - material_t material; int maxchars = 8192; // Alloc enough size. std::vector buf(maxchars); // Alloc enough size. - while (ifs.peek() != -1) { - ifs.getline(&buf[0], maxchars); + while (inStream.peek() != -1) { + inStream.getline(&buf[0], maxchars); std::string linebuf(&buf[0]); @@ -511,6 +495,31 @@ LoadObj( return err.str(); } + auto getMatFileIStreamFunc = + [&](const std::string& matId) + { + std::string filepath; + + if (mtl_basepath) { + filepath = std::string(mtl_basepath) + matId; + } else { + filepath = matId; + } + + std::unique_ptr ifs( new std::ifstream(filepath.c_str()) ); + return ifs; + }; + + return LoadObj(shapes, ifs, getMatFileIStreamFunc); +} + +std::string LoadObj( + std::vector& shapes, + std::istream& inStream, + GetMtlIStreamFn getMatFn) +{ + std::stringstream err; + std::vector v; std::vector vn; std::vector vt; @@ -524,8 +533,8 @@ LoadObj( int maxchars = 8192; // Alloc enough size. std::vector buf(maxchars); // Alloc enough size. - while (ifs.peek() != -1) { - ifs.getline(&buf[0], maxchars); + while (inStream.peek() != -1) { + inStream.getline(&buf[0], maxchars); std::string linebuf(&buf[0]); @@ -625,11 +634,20 @@ LoadObj( token += 7; sscanf(token, "%s", namebuf); - std::string err_mtl = LoadMtl(material_map, namebuf, mtl_basepath); - if (!err_mtl.empty()) { - faceGroup.clear(); // for safety - return err_mtl; + if (!getMatFn) { + err << "Could not read material, no callable function target."; + return err.str(); } + + std::unique_ptr matIStream = getMatFn(namebuf); + if (matIStream) { + std::string err_mtl = LoadMtl(material_map, *matIStream); + if (!err_mtl.empty()) { + faceGroup.clear(); // for safety + return err_mtl; + } + } + continue; } diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index 7bc68cf..c4a0a30 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -9,6 +9,8 @@ #include #include #include +#include +#include namespace tinyobj { @@ -49,6 +51,13 @@ typedef struct mesh_t mesh; } shape_t; +/// typedef for a function that returns a pointer to an istream for +/// the material identified by the const std::string& +typedef std::function< + std::unique_ptr( + const std::string&) + > GetMtlIStreamFn; + /// Loads .obj from a file. /// 'shapes' will be filled with parsed shape data /// The function returns error string. @@ -59,6 +68,13 @@ std::string LoadObj( const char* filename, const char* mtl_basepath = NULL); +/// Loads object from a std::istream, uses GetMtlIStreamFn to retrieve +/// std::istream for materials. +/// Returns empty string when loading .obj success. +std::string LoadObj( + std::vector& shapes, // [output] + std::istream& inStream, + GetMtlIStreamFn getMatFn); }; #endif // _TINY_OBJ_LOADER_H