diff --git a/tests/tester.cc b/tests/tester.cc index 144e26f..d9d2dca 100644 --- a/tests/tester.cc +++ b/tests/tester.cc @@ -167,12 +167,12 @@ TEST_CASE("parse-integer", "[bounds-checking]") { SECTION("parses valid numbers") { std::string err; int result = 123; - CHECK(tinygltf::ParseIntegerProperty(&result, &err, {{"zero", 0}}, "zero", + CHECK(tinygltf::ParseIntegerProperty(&result, &err, JsonConstruct("{\"zero\" : 0}"), "zero", true)); REQUIRE(err == ""); REQUIRE(result == 0); - CHECK(tinygltf::ParseIntegerProperty(&result, &err, {{"int", -1234}}, "int", + CHECK(tinygltf::ParseIntegerProperty(&result, &err, JsonConstruct("{\"int\": -1234}"), "int", true)); REQUIRE(err == ""); REQUIRE(result == -1234); @@ -181,7 +181,7 @@ TEST_CASE("parse-integer", "[bounds-checking]") { SECTION("detects missing properties") { std::string err; int result = -1; - CHECK_FALSE(tinygltf::ParseIntegerProperty(&result, &err, {}, "int", true)); + CHECK_FALSE(tinygltf::ParseIntegerProperty(&result, &err, JsonConstruct(""), "int", true)); REQUIRE_THAT(err, Catch::Contains("'int' property is missing")); REQUIRE(result == -1); } @@ -190,7 +190,7 @@ TEST_CASE("parse-integer", "[bounds-checking]") { std::string err; int result = -1; CHECK_FALSE( - tinygltf::ParseIntegerProperty(&result, &err, {}, "int", false)); + tinygltf::ParseIntegerProperty(&result, &err, JsonConstruct(""), "int", false)); REQUIRE(err == ""); REQUIRE(result == -1); } @@ -199,21 +199,26 @@ TEST_CASE("parse-integer", "[bounds-checking]") { std::string err; int result = -1; - CHECK_FALSE(tinygltf::ParseIntegerProperty(&result, &err, {{"int", 0.5}}, - "int", true)); + CHECK_FALSE(tinygltf::ParseIntegerProperty(&result, &err, JsonConstruct("{\"int\": 0.5}"), + "int", true)); REQUIRE_THAT(err, Catch::Contains("not an integer type")); // Excessively large values and NaN aren't allowed either. err.clear(); - CHECK_FALSE(tinygltf::ParseIntegerProperty(&result, &err, {{"int", 1e300}}, - "int", true)); + CHECK_FALSE(tinygltf::ParseIntegerProperty(&result, &err, JsonConstruct("{\"int\": 1e300}"), + "int", true)); REQUIRE_THAT(err, Catch::Contains("not an integer type")); err.clear(); - CHECK_FALSE(tinygltf::ParseIntegerProperty( - &result, &err, {{"int", std::numeric_limits::quiet_NaN()}}, + { + JsonDocument o; + double nan = std::numeric_limits::quiet_NaN(); + tinygltf::JsonAddMember(o, "int", json(nan)); + CHECK_FALSE(tinygltf::ParseIntegerProperty( + &result, &err, o, "int", true)); - REQUIRE_THAT(err, Catch::Contains("not an integer type")); + REQUIRE_THAT(err, Catch::Contains("not an integer type")); + } } } @@ -221,7 +226,7 @@ TEST_CASE("parse-unsigned", "[bounds-checking]") { SECTION("parses valid unsigned integers") { // Use string-based parsing here, using the initializer list syntax doesn't // parse 0 as unsigned. - json zero_obj = json::parse("{\"zero\": 0}"); + auto zero_obj = JsonConstruct("{\"zero\": 0}"); std::string err; size_t result = 123; @@ -235,26 +240,31 @@ TEST_CASE("parse-unsigned", "[bounds-checking]") { std::string err; size_t result = -1; - CHECK_FALSE(tinygltf::ParseUnsignedProperty(&result, &err, {{"int", -1234}}, + CHECK_FALSE(tinygltf::ParseUnsignedProperty(&result, &err, JsonConstruct("{\"int\": -1234}"), "int", true)); REQUIRE_THAT(err, Catch::Contains("not a positive integer")); err.clear(); - CHECK_FALSE(tinygltf::ParseUnsignedProperty(&result, &err, {{"int", 0.5}}, + CHECK_FALSE(tinygltf::ParseUnsignedProperty(&result, &err, JsonConstruct("{\"int\": 0.5}"), "int", true)); REQUIRE_THAT(err, Catch::Contains("not a positive integer")); // Excessively large values and NaN aren't allowed either. err.clear(); - CHECK_FALSE(tinygltf::ParseUnsignedProperty(&result, &err, {{"int", 1e300}}, + CHECK_FALSE(tinygltf::ParseUnsignedProperty(&result, &err, JsonConstruct("{\"int\": 1e300}"), "int", true)); REQUIRE_THAT(err, Catch::Contains("not a positive integer")); err.clear(); - CHECK_FALSE(tinygltf::ParseUnsignedProperty( - &result, &err, {{"int", std::numeric_limits::quiet_NaN()}}, + { + JsonDocument o; + double nan = std::numeric_limits::quiet_NaN(); + tinygltf::JsonAddMember(o, "int", json(nan)); + CHECK_FALSE(tinygltf::ParseUnsignedProperty( + &result, &err, o, "int", true)); - REQUIRE_THAT(err, Catch::Contains("not a positive integer")); + REQUIRE_THAT(err, Catch::Contains("not a positive integer")); + } } } @@ -263,7 +273,7 @@ TEST_CASE("parse-integer-array", "[bounds-checking]") { std::string err; std::vector result; CHECK(tinygltf::ParseIntegerArrayProperty(&result, &err, - {{"x", {-1, 2, 3}}}, "x", true)); + JsonConstruct("{\"x\": [-1, 2, 3]}"), "x", true)); REQUIRE(err == ""); REQUIRE(result.size() == 3); REQUIRE(result[0] == -1); @@ -275,7 +285,7 @@ TEST_CASE("parse-integer-array", "[bounds-checking]") { std::string err; std::vector result; CHECK_FALSE(tinygltf::ParseIntegerArrayProperty( - &result, &err, {{"x", {-1, 1e300, 3}}}, "x", true)); + &result, &err, JsonConstruct("{\"x\": [-1, 1e300, 3]}"), "x", true)); REQUIRE_THAT(err, Catch::Contains("not an integer type")); } } diff --git a/tiny_gltf.h b/tiny_gltf.h index 581f75b..b6f0453 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -1637,9 +1637,20 @@ namespace assert(s_pActiveDocument == nullptr); //Code assumes only one document is active at a time s_pActiveDocument = this; } - ~JsonDocument() { - s_pActiveDocument = nullptr; + JsonDocument(const JsonDocument&) = delete; + JsonDocument(JsonDocument&& rhs) noexcept : rapidjson::Document(std::move(rhs)) + { + s_pActiveDocument = this; + rhs.isNil = true; } + ~JsonDocument() { + if (!isNil) + { + s_pActiveDocument = nullptr; + } + } + private: + bool isNil = false; }; #else using nlohmann::json; @@ -1656,6 +1667,13 @@ namespace doc = json::parse(str, str + length, nullptr, throwExc); #endif } + + JsonDocument JsonConstruct(const char* str) + { + JsonDocument doc; + JsonParse(doc, str, strlen(str)); + return doc; + } } #ifdef __APPLE__ @@ -2878,9 +2896,13 @@ namespace } - bool FindMember(const json& o, const char* member, json_const_iterator& it) + bool FindMember(const json& o, const char* member, json_const_iterator& it) { #ifdef TINYGLTF_USE_RAPIDJSON + if (!o.IsObject()) + { + return false; + } it = o.FindMember(member); return it != o.MemberEnd(); #else @@ -3476,7 +3498,7 @@ static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err, if (!IsObject(itObj)) continue; std::string key(GetKey(extIt)); if (!ParseJsonAsValue(&extensions[key], itObj)) { - if (key.empty()) { + if (!key.empty()) { // create empty object so that an extension object is still of type // object extensions[key] = Value{Value::Object{}}; @@ -4436,18 +4458,33 @@ static bool ParseMaterial(Material *material, std::string *err, const json &o) { for (; it != itEnd; ++it) { std::string key(GetKey(it)); + if (key == "pbrMetallicRoughness") { + if (IsObject(GetValue(it))) { + const json &values_object = GetValue(it); - if ((key == "pbrMetallicRoughness") || - (key == "extensions") || - (key == "extras")) { - continue; //Remove duplicating these in parameters + json_const_iterator itVal(ObjectBegin(values_object)); + json_const_iterator itValEnd(ObjectEnd(values_object)); + + for (; itVal != itValEnd; ++itVal) { + Parameter param; + if (ParseParameterProperty(¶m, err, values_object, GetKey(itVal), + false)) { + material->values.emplace(GetKey(itVal), std::move(param)); + } + } + } } - - Parameter param; - if (ParseParameterProperty(¶m, err, o, key, false)) { - // names of materials have already been parsed. Putting it in this map - // doesn't correctly reflext the glTF specification - if (key != "name") material->additionalValues.emplace(std::move(key), std::move(param)); + else if (key == "extensions" || key == "extras") { + // done later, skip, otherwise poorly parsed contents will be saved in the + // parametermap and serialized again later + } + else { + Parameter param; + if (ParseParameterProperty(¶m, err, o, key, false)) { + // names of materials have already been parsed. Putting it in this map + // doesn't correctly reflext the glTF specification + if (key != "name") material->additionalValues.emplace(std::move(key), std::move(param)); + } } } @@ -5607,8 +5644,8 @@ bool TinyGLTF::LoadBinaryFromFile(Model *model, std::string *err, /////////////////////// // GLTF Serialization /////////////////////// -namespace -{ +//namespace +//{ json JsonFromString(const char* s) { #ifdef TINYGLTF_USE_RAPIDJSON @@ -5680,6 +5717,15 @@ namespace #endif } + void JsonSetObject(json& o) + { +#ifdef TINYGLTF_USE_RAPIDJSON + o.SetObject(); +#else + o = o.object({}); +#endif + } + void JsonReserveArray(json& o, size_t s) { #ifdef TINYGLTF_USE_RAPIDJSON @@ -5689,7 +5735,7 @@ namespace (void)(o); (void)(s); } -} +//} // typedef std::pair json_object_pair; @@ -5903,6 +5949,7 @@ static void SerializeExtensionMap(ExtensionMap &extensions, json &o) { // create empty object so that an extension name is still included in // json. json empty; + JsonSetObject(empty); JsonAddMember(extMap, extIt->first.c_str(), std::move(empty)); } }