diff --git a/tests/tester.cc b/tests/tester.cc index 5573bb6..00b4eb0 100644 --- a/tests/tester.cc +++ b/tests/tester.cc @@ -184,6 +184,48 @@ TestLoadObj( return 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; + + std::ifstream fileStream(fullFilename); + + if (!fileStream) { + std::cerr << "Could not find specified file: " << fullFilename << std::endl; + return false; + } + + tinyobj::MaterialStreamReader materialStreamReader(fileStream); + tinyobj::MaterialStreamReader* materialReader = readMaterials + ? &materialStreamReader + : NULL; + + tinyobj::attrib_t attrib; + std::vector shapes; + std::vector materials; + + std::string err; + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, &fileStream, materialReader); + + if (!err.empty()) { + std::cerr << err << std::endl; + } + + if (!ret) { + printf("Failed to load/parse .obj.\n"); + return false; + } + + std::cout << "Loaded material count: " << materials.size() << "\n"; + + return true; +} static bool TestStreamLoadObj() @@ -351,6 +393,16 @@ 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)); +} + +TEST_CASE("stream_load_from_file_with_materials", "[Stream]") { + REQUIRE(true == TestLoadObjFromPreopenedFile( + "../models/pbr-mat-ext.obj", gMtlBasePath, /*readMaterials*/true, /*triangulate*/false)); +} + TEST_CASE("trailing_whitespace_in_mtl", "[Issue92]") { tinyobj::attrib_t attrib; std::vector shapes; diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index 0200ff5..228e819 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -179,6 +179,19 @@ class MaterialFileReader : public MaterialReader { std::string m_mtlBasePath; }; +class MaterialStreamReader : public MaterialReader { + public: + explicit MaterialStreamReader(std::istream &inStream) + : m_inStream(inStream) {} + virtual ~MaterialStreamReader() {} + virtual bool operator()(const std::string &matId, + std::vector *materials, + std::map *matMap, std::string *err); + + private: + std::istream &m_inStream; +}; + /// Loads .obj from a file. /// 'attrib', 'shapes' and 'materials' will be filled with parsed shape data /// 'shapes' will be filled with parsed shape data @@ -209,7 +222,7 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, /// 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, + std::istream *inStream, MaterialReader *readMatFn = NULL, bool triangulate = true); /// Loads materials into std::map @@ -1010,6 +1023,22 @@ bool MaterialFileReader::operator()(const std::string &matId, return true; } +bool MaterialStreamReader::operator()(const std::string &matId, + std::vector *materials, + std::map *matMap, + std::string *err) { + LoadMtl(matMap, materials, &m_inStream); + if (!m_inStream) { + std::stringstream ss; + ss << "WARN: Material stream in error state." + << " Created a default material."; + if (err) { + (*err) += ss.str(); + } + } + return true; +} + bool LoadObj(attrib_t *attrib, std::vector *shapes, std::vector *materials, std::string *err, const char *filename, const char *mtl_basepath, @@ -1042,7 +1071,8 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, bool LoadObj(attrib_t *attrib, std::vector *shapes, std::vector *materials, std::string *err, - std::istream *inStream, MaterialReader *readMatFn, + std::istream *inStream, + MaterialReader *readMatFn /*= NULL*/, bool triangulate) { std::stringstream errss; @@ -1173,23 +1203,25 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, // load mtl if ((0 == strncmp(token, "mtllib", 6)) && IS_SPACE((token[6]))) { - char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; - token += 7; + if (readMatFn) { + char namebuf[TINYOBJ_SSCANF_BUFFER_SIZE]; + token += 7; #ifdef _MSC_VER - sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); + sscanf_s(token, "%s", namebuf, (unsigned)_countof(namebuf)); #else - sscanf(token, "%s", namebuf); + sscanf(token, "%s", namebuf); #endif - std::string err_mtl; - bool ok = (*readMatFn)(namebuf, materials, &material_map, &err_mtl); - if (err) { - (*err) += err_mtl; - } + std::string err_mtl; + bool ok = (*readMatFn)(namebuf, materials, &material_map, &err_mtl); + if (err) { + (*err) += err_mtl; + } - if (!ok) { - faceGroup.clear(); // for safety - return false; + if (!ok) { + faceGroup.clear(); // for safety + return false; + } } continue;