diff --git a/models/cube.obj b/models/cube.obj index 9213e17..f12451f 100644 --- a/models/cube.obj +++ b/models/cube.obj @@ -13,7 +13,8 @@ v 2.000000 2.000000 0.000000 g front cube usemtl white f 1 2 3 4 -g back cube +# two white spaces between 'back' and 'cube' +g back cube # expects white material f 8 7 6 5 g right cube diff --git a/tests/tester.cc b/tests/tester.cc index 0a26d2e..25b8836 100644 --- a/tests/tester.cc +++ b/tests/tester.cc @@ -807,6 +807,24 @@ TEST_CASE("line-primitive", "[line]") { REQUIRE(6 == shapes[0].path.indices.size()); } +TEST_CASE("multiple-group-names", "[group]") { + tinyobj::attrib_t attrib; + std::vector shapes; + std::vector materials; + + std::string err; + bool ret = tinyobj::LoadObj(&attrib, &shapes, &materials, &err, "../models/cube.obj", gMtlBasePath); + + if (!err.empty()) { + std::cerr << "[group] " << 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. +} + // Fuzzer test. // Just check if it does not crash. // Disable by default since Windows filesystem can't create filename of afl testdata diff --git a/tiny_obj_loader.h b/tiny_obj_loader.h index 70fb4b9..025f112 100644 --- a/tiny_obj_loader.h +++ b/tiny_obj_loader.h @@ -23,6 +23,7 @@ THE SOFTWARE. */ // +// version 1.2.2 : Parse multiple group names. // version 1.2.1 : Added initial support for line('l') primitive(PR #178) // version 1.2.0 : Hardened implementation(#175) // version 1.1.1 : Support smoothing groups(#162) @@ -1790,10 +1791,13 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, shape_t shape; + size_t line_num = 0; std::string linebuf; while (inStream->peek() != -1) { safeGetline(*inStream, linebuf); + line_num++; + // Trim newline '\r\n' or '\n' if (linebuf.size() > 0) { if (linebuf[linebuf.size() - 1] == '\n') @@ -2001,7 +2005,6 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, faceGroup.clear(); std::vector names; - names.reserve(2); while (!IS_NEW_LINE(token[0])) { std::string str = parseString(&token); @@ -2009,13 +2012,31 @@ bool LoadObj(attrib_t *attrib, std::vector *shapes, token += strspn(token, " \t\r"); // skip tag } - assert(names.size() > 0); + // names[0] must be 'g' - // names[0] must be 'g', so skip the 0th element. - if (names.size() > 1) { - name = names[1]; + if (names.size() < 2) { + // 'g' with empty names + if (err) { + std::stringstream ss; + ss << "WARN: Empty group name. line: " << line_num << "\n"; + (*err) += ss.str(); + name = ""; + } } else { - name = ""; + + std::stringstream ss; + ss << names[1]; + + // tinyobjloader does not support multiple groups for a primitive. + // Currently we concatinate multiple group names with a space to get + // single group name. + + for (size_t i = 2; i < names.size(); i++) { + ss << " " << names[i]; + } + + name = ss.str(); + } continue; @@ -2169,7 +2190,6 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, std::vector materials; std::vector names; names.reserve(2); - std::string name; std::vector names_out; std::string linebuf; @@ -2346,13 +2366,6 @@ bool LoadObjWithCallback(std::istream &inStream, const callback_t &callback, assert(names.size() > 0); - // names[0] must be 'g', so skip the 0th element. - if (names.size() > 1) { - name = names[1]; - } else { - name.clear(); - } - if (callback.group_cb) { if (names.size() > 1) { // create const char* array.