From c670f08a3bddf6daa0c230219a0185c9c58fe879 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sat, 17 Sep 2022 19:52:25 +0900 Subject: [PATCH 1/7] Fix parsing GLB file with empty Chunk1(BIN data). --- tiny_gltf.h | 95 +++++++++++++++++++++++++++++------------------------ 1 file changed, 52 insertions(+), 43 deletions(-) diff --git a/tiny_gltf.h b/tiny_gltf.h index 734c726..0971c18 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -4113,7 +4113,7 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o, if ((bin_size == 0) || (bin_data == nullptr)) { if (err) { - (*err) += "Invalid binary data in `Buffer'.\n"; + (*err) += "Invalid binary data in `Buffer', or GLB with empty BIN chunk.\n"; } return false; } @@ -6286,15 +6286,15 @@ bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err, // // https://github.com/syoyo/tinygltf/issues/372 // Use 64bit uint to avoid integer overflow. - uint64_t json_size = 20ull + uint64_t(chunk0_length); + uint64_t header_and_json_size = 20ull + uint64_t(chunk0_length); - if (json_size > std::numeric_limits::max()) { + if (header_and_json_size > std::numeric_limits::max()) { // Do not allow 4GB or more GLB data. (*err) = "Invalid glTF binary. GLB data exceeds 4GB."; } - if ((json_size > uint64_t(size)) || (chunk0_length < 1) || (length > size) || - (json_size > uint64_t(length)) || + if ((header_and_json_size > uint64_t(size)) || (chunk0_length < 1) || (length > size) || + (header_and_json_size > uint64_t(length)) || (chunk0_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format. if (err) { (*err) = "Invalid glTF binary."; @@ -6305,61 +6305,70 @@ bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err, // Padding check // The start and the end of each chunk must be aligned to a 4-byte boundary. // No padding check for chunk0 start since its 4byte-boundary is ensured. - if ((json_size % 4) != 0) { + if ((header_and_json_size % 4) != 0) { if (err) { (*err) = "JSON Chunk end does not aligned to a 4-byte boundary."; } } - // Read Chunk1 info(BIN data) - if ((json_size + 8ull) > uint64_t(length)) { - if (err) { - (*err) = "Insufficient storage space for Chunk1(BIN data)."; + // Chunk1(BIN) data + // The spec says: When the binary buffer is empty or when it is stored by other means, this chunk SHOULD be omitted. + if (header_and_json_size <= uint64_t(length)) { + + // Just in case... + bin_data_ = nullptr; + bin_size_ = 0; + } else { + // Read Chunk1 info(BIN data) + // At least Chunk1 should have 12 bytes(8 bytes(header) + 4 bytes(bin content)) + if ((header_and_json_size + 12ull) >= uint64_t(length)) { + if (err) { + (*err) = "Insufficient storage space for Chunk1(BIN data). At least Chunk1 Must have 12bytes or more bytes, but got " + std::to_string((header_and_json_size + 12ull) - uint64_t(length)) + ".\n"; + } + return false; } - return false; - } - unsigned int chunk1_length; // 4 bytes - unsigned int chunk1_format; // 4 bytes; - memcpy(&chunk1_length, bytes + json_size, 4); // JSON data length - swap4(&chunk1_length); - memcpy(&chunk1_format, bytes + json_size + 4, 4); - swap4(&chunk1_format); + unsigned int chunk1_length; // 4 bytes + unsigned int chunk1_format; // 4 bytes; + memcpy(&chunk1_length, bytes + header_and_json_size, 4); // JSON data length + swap4(&chunk1_length); + memcpy(&chunk1_format, bytes + header_and_json_size + 4, 4); + swap4(&chunk1_format); - if (chunk1_length < 4) { - // TODO: Do we allow 0byte BIN data? - if (err) { - (*err) = "Insufficient Chunk1(BIN) data size."; + if (chunk1_length < 4) { + if (err) { + (*err) = "Insufficient Chunk1(BIN) data size."; + } + return false; } - return false; - } - if ((chunk1_length % 4) != 0) { - if (err) { - (*err) = "BIN Chunk end does not aligned to a 4-byte boundary."; + if ((chunk1_length % 4) != 0) { + if (err) { + (*err) = "BIN Chunk end does not aligned to a 4-byte boundary."; + } + return false; } - return false; - } - if (uint64_t(chunk1_length) + json_size > uint64_t(length)) { - if (err) { - (*err) = "BIN Chunk data length exceeds the GLB size."; + if (uint64_t(chunk1_length) + header_and_json_size > uint64_t(length)) { + if (err) { + (*err) = "BIN Chunk data length exceeds the GLB size."; + } + return false; } - return false; - } - if (chunk1_format != 0x004e4942) { - if (err) { - (*err) = "Invlid type for chunk1 data."; + if (chunk1_format != 0x004e4942) { + if (err) { + (*err) = "Invlid type for chunk1 data."; + } + return false; } - return false; + + bin_data_ = bytes + header_and_json_size + + 8; // 4 bytes (bin_buffer_length) + 4 bytes(bin_buffer_format) + + bin_size_ = size_t(chunk1_length); } - bin_data_ = bytes + json_size + - 8; // 4 bytes (bin_buffer_length) + 4 bytes(bin_buffer_format) - - bin_size_ = size_t(chunk1_length); - // Extract JSON string. std::string jsonString(reinterpret_cast(&bytes[20]), chunk0_length); From 6b7ec9f4945b0dcc941e9a1d7727b1718435e737 Mon Sep 17 00:00:00 2001 From: Kh4n Date: Sat, 17 Sep 2022 12:28:39 -0500 Subject: [PATCH 2/7] added tests to cover empty, empty buffer, and single byte buffer cases --- tests/issue-261.gltf | 378 +++++++++++++++++++++++++++++++++++++++++++ tests/tester.cc | 220 ++++++++++++++++--------- 2 files changed, 526 insertions(+), 72 deletions(-) create mode 100644 tests/issue-261.gltf diff --git a/tests/issue-261.gltf b/tests/issue-261.gltf new file mode 100644 index 0000000..d1c5ca7 --- /dev/null +++ b/tests/issue-261.gltf @@ -0,0 +1,378 @@ +{ + "accessors": [ + { + "bufferView": 0, + "componentType": 5126, + "count": 8, + "max": [ + 0.5, + 0.5, + 0.5 + ], + "min": [ + -0.5, + -0.5, + -0.5 + ], + "type": "VEC3" + }, + { + "bufferView": 1, + "componentType": 5125, + "count": 36, + "type": "SCALAR" + } + ], + "asset": { + "copyright": "NVIDIA Corporation", + "generator": "Iray glTF plugin", + "version": "2.0" + }, + "bufferViews": [ + { + "buffer": 0, + "byteLength": 96, + "byteStride": 12, + "target": 34962 + }, + { + "buffer": 0, + "byteLength": 144, + "byteOffset": 96, + "target": 34963 + } + ], + "buffers": [ + { + "byteLength": 240, + "uri": "data:application/octet-stream;base64,AAAAvwAAAL8AAAC/AAAAvwAAAL8AAAA/AAAAvwAAAD8AAAC/AAAAvwAAAD8AAAA/AAAAPwAAAL8AAAC/AAAAPwAAAL8AAAA/AAAAPwAAAD8AAAC/AAAAPwAAAD8AAAA/AAAAAAEAAAADAAAAAAAAAAMAAAACAAAAAQAAAAUAAAAHAAAAAQAAAAcAAAADAAAABQAAAAQAAAAGAAAABQAAAAYAAAAHAAAABAAAAAAAAAACAAAABAAAAAIAAAAGAAAABAAAAAUAAAABAAAABAAAAAEAAAAAAAAAAgAAAAMAAAAHAAAAAgAAAAcAAAAGAAAA" + } + ], + "cameras": [ + { + "extensions": { + "NV_Iray": { + "mip_burn_highlights": 0.699999988079071, + "mip_burn_highlights_per_component": false, + "mip_camera_shutter": 4.0, + "mip_cm2_factor": 1.0, + "mip_crush_blacks": 0.5, + "mip_f_number": 1.0, + "mip_film_iso": 100.0, + "mip_gamma": 2.200000047683716, + "mip_saturation": 1.0, + "mip_vignetting": 0.00019999999494757503, + "mip_whitepoint": [ + 1.0, + 1.0, + 1.0, + 1.0 + ], + "tm_enable_tonemapper": true, + "tm_tonemapper": "mia_exposure_photographic" + } + }, + "extras": { + "resolution": [ + 640, + 480 + ] + }, + "name": "default", + "perspective": { + "aspectRatio": 1.3333333730697632, + "yfov": 0.9272952079772949, + "zfar": 1000.0, + "znear": 0.1 + }, + "type": "perspective" + } + ], + "extensions": { + "KHR_lights_punctual": { + "lights": [ + { + "color": [ + 1.0, + 1.0, + 1.0 + ], + "intensity": 1000.0, + "name": "light", + "type": "point" + } + ] + }, + "NV_MDL": { + "modules": [ + "mdl::base", + "mdl::nvidia::core_definitions", + "mdl::state" + ], + "shaders": [ + { + "definition": "mdl::base::environment_spherical(texture_2d)", + "name": "env_shd" + }, + { + "arguments": { + "base_color": [ + 0.0055217444896698, + 0.36859095096588135, + 0.0041161770932376385 + ], + "normal=": 2 + }, + "definition": "mdl::nvidia::core_definitions::flex_material", + "name": "cube_instance_material" + }, + { + "definition": "mdl::state::normal()", + "name": "mdl::state::normal_341" + }, + { + "arguments": { + "base_color": [ + 0.0055217444896698, + 0.36859095096588135, + 0.0041161770932376385 + ], + "normal=": 4 + }, + "definition": "mdl::nvidia::core_definitions::flex_material", + "name": "cube_instance_material" + }, + { + "definition": "mdl::state::normal()", + "name": "mdl::state::normal_341" + } + ] + } + }, + "extensionsUsed": [ + "NV_MDL", + "NV_Iray", + "KHR_lights_punctual" + ], + "materials": [ + { + "doubleSided": true, + "extras": { + "mdl_shader": 1 + }, + "name": "cube_instance_material", + "pbrMetallicRoughness": { + "baseColorFactor": [ + 0.0055217444896698, + 0.36859095096588135, + 0.0041161770932376385, + 1.0 + ] + } + } + ], + "meshes": [ + { + "name": "cube", + "primitives": [ + { + "attributes": { + "POSITION": 0 + }, + "indices": 1, + "material": 0, + "mode": 4 + } + ] + } + ], + "nodes": [ + { + "camera": 0, + "extensions": { + "NV_Iray": { + "iview:fkey": -1, + "iview:fov": 53.130104064941406, + "iview:interest": [ + 0.1342654824256897, + -0.14356137812137604, + 0.037264324724674225 + ], + "iview:position": [ + 0.9729073643684387, + 1.2592438459396362, + 2.4199187755584717 + ], + "iview:roll": 0.0, + "iview:up": [ + 0.0, + 1.0, + 0.0 + ] + } + }, + "matrix": [ + 0.9432751389105357, + -1.1769874756875739e-16, + -0.3320120665176343, + 0.0, + -0.16119596696756341, + 0.8742297945345237, + -0.45797175303889276, + 0.0, + 0.290254840694694, + 0.48551237507207207, + 0.8246392308792816, + 0.0, + 0.9729073377709113, + 1.2592438262175363, + 2.419918748461627, + 1.0 + ], + "name": "CamInst" + }, + { + "extensions": { + "NV_Iray": { + "caustic": true, + "caustic_cast": true, + "caustic_recv": true, + "face_back": true, + "face_front": true, + "finalgather": true, + "finalgather_cast": true, + "finalgather_recv": true, + "globillum": true, + "globillum_cast": true, + "globillum_recv": true, + "material=": 3, + "pickable": true, + "reflection_cast": true, + "reflection_recv": true, + "refraction_cast": true, + "refraction_recv": true, + "shadow_cast": true, + "shadow_recv": true, + "transparency_cast": true, + "transparency_recv": true, + "visible": true + } + }, + "mesh": 0, + "name": "cube_instance" + }, + { + "extensions": { + "KHR_lights_punctual": { + "light": 0 + }, + "NV_Iray": { + "shadow_cast": true, + "visible": false + } + }, + "matrix": [ + 0.6988062355563571, + -7.76042172309776e-17, + -0.7153110128800992, + -0.0, + -0.4276881690736487, + 0.8015668284138362, + -0.41781987700564616, + -0.0, + 0.57336957992379, + 0.5979051928078428, + 0.5601398979107212, + 0.0, + 2.3640632834071384, + 2.465226204472449, + 2.309515908392224, + 1.0 + ], + "name": "light_inst" + } + ], + "scene": 0, + "scenes": [ + { + "extensions": { + "NV_Iray": { + "CP_canny_threshold1": 40, + "CP_canny_threshold2": 120, + "CP_color_quantization": 8, + "IVP_color": [ + 1.0, + 0.0, + 0.0, + 1.0 + ], + "TM_drago_bias": 0.8500000238418579, + "TM_drago_gamma2": 2.200000047683716, + "TM_drago_saturation": 1.0, + "TM_durand_contrast": 4.0, + "TM_durand_gamma": 2.200000047683716, + "TM_durand_saturation": 1.0, + "TM_durand_sigma_color": 2.0, + "TM_durand_sigma_space": 2.0, + "TM_linear_gamma": 2.200000047683716, + "TM_reinhard_color_adapt": 0.8999999761581421, + "TM_reinhard_gamma": 1.0, + "TM_reinhard_intensity": 0.0, + "TM_reinhard_light_adapt": 1.0, + "TM_reye_Ywhite": 1000000.0, + "TM_reye_bsize": 2, + "TM_reye_bthres": 3.0, + "TM_reye_gamma": 2.200000047683716, + "TM_reye_key": 0.5, + "TM_reye_saturation": 1.0, + "TM_reye_whitebalance": [ + 1.0, + 0.9965101480484009, + 0.9805564880371094, + 0.0 + ], + "environment_dome_depth": 200.0, + "environment_dome_height": 200.0, + "environment_dome_mode": "infinite", + "environment_dome_position": [ + 0.0, + 0.0, + 0.0 + ], + "environment_dome_radius": 100.0, + "environment_dome_rotation_angle": 0.0, + "environment_dome_width": 200.0, + "environment_function=": 0, + "environment_function_intensity": 1.9900000095367432, + "iray_instancing": "off", + "iview::inline_color": [ + 1.0, + 0.0, + 0.0, + 1.0 + ], + "iview::inline_width": 1.0, + "iview::magnifier_size": 300, + "iview::offset": 10.0, + "iview::outline_color": [ + 0.0, + 0.0, + 0.0, + 1.0 + ], + "iview::outline_width": 2.0, + "iview::overview": true, + "iview::zoom_factor": 1.0, + "samples": 4.0, + "shadow_cast": true, + "shadow_recv": true + } + }, + "nodes": [ + 0, + 1, + 2 + ] + } + ] +} diff --git a/tests/tester.cc b/tests/tester.cc index c8acf43..c06aab4 100644 --- a/tests/tester.cc +++ b/tests/tester.cc @@ -6,39 +6,37 @@ // Nlohmann json(include ../json.hpp) #include "json.hpp" -#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file -#include "catch.hpp" - +#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do + // this in one cpp file +#include #include #include -#include +#include #include #include -#include -static JsonDocument JsonConstruct(const char* str) -{ +#include "catch.hpp" + +static JsonDocument JsonConstruct(const char* str) { JsonDocument doc; JsonParse(doc, str, strlen(str)); return doc; } - TEST_CASE("parse-error", "[parse]") { - tinygltf::Model model; tinygltf::TinyGLTF ctx; std::string err; std::string warn; - bool ret = ctx.LoadASCIIFromString(&model, &err, &warn, "bora", static_cast(strlen("bora")), /* basedir*/ ""); + bool ret = ctx.LoadASCIIFromString(&model, &err, &warn, "bora", + static_cast(strlen("bora")), + /* basedir*/ ""); REQUIRE(false == ret); - } TEST_CASE("datauri-in-glb", "[issue-79]") { - tinygltf::Model model; tinygltf::TinyGLTF ctx; std::string err; @@ -53,13 +51,13 @@ TEST_CASE("datauri-in-glb", "[issue-79]") { } TEST_CASE("extension-with-empty-object", "[issue-97]") { - tinygltf::Model model; tinygltf::TinyGLTF ctx; std::string err; std::string warn; - bool ret = ctx.LoadASCIIFromFile(&model, &err, &warn, "../models/Extensions-issue97/test.gltf"); + bool ret = ctx.LoadASCIIFromFile(&model, &err, &warn, + "../models/Extensions-issue97/test.gltf"); if (!err.empty()) { std::cerr << err << std::endl; } @@ -93,17 +91,17 @@ TEST_CASE("extension-with-empty-object", "[issue-97]") { REQUIRE(m.materials[0].extensions.size() == 1); REQUIRE(m.materials[0].extensions.count("VENDOR_material_some_ext") == 1); } - } TEST_CASE("extension-overwrite", "[issue-261]") { - tinygltf::Model model; tinygltf::TinyGLTF ctx; std::string err; std::string warn; - bool ret = ctx.LoadASCIIFromFile(&model, &err, &warn, "../models/Extensions-overwrite-issue261/issue-261.gltf"); + bool ret = ctx.LoadASCIIFromFile( + &model, &err, &warn, + "../models/Extensions-overwrite-issue261/issue-261.gltf"); if (!err.empty()) { std::cerr << err << std::endl; } @@ -112,9 +110,12 @@ TEST_CASE("extension-overwrite", "[issue-261]") { REQUIRE(model.extensionsUsed.size() == 3); { bool has_ext_lights = false; - has_ext_lights |= (model.extensionsUsed[0].compare("KHR_lights_punctual") == 0); - has_ext_lights |= (model.extensionsUsed[1].compare("KHR_lights_punctual") == 0); - has_ext_lights |= (model.extensionsUsed[2].compare("KHR_lights_punctual") == 0); + has_ext_lights |= + (model.extensionsUsed[0].compare("KHR_lights_punctual") == 0); + has_ext_lights |= + (model.extensionsUsed[1].compare("KHR_lights_punctual") == 0); + has_ext_lights |= + (model.extensionsUsed[2].compare("KHR_lights_punctual") == 0); REQUIRE(true == has_ext_lights); } @@ -144,9 +145,7 @@ TEST_CASE("extension-overwrite", "[issue-261]") { REQUIRE(m.extensions.size() == 2); REQUIRE(m.extensions.count("NV_MDL")); REQUIRE(m.extensions.count("KHR_lights_punctual")); - } - } TEST_CASE("invalid-primitive-indices", "[bounds-checking]") { @@ -201,10 +200,13 @@ TEST_CASE("glb-invalid-length", "[bounds-checking]") { // This glb has a much longer length than the provided data and should fail // initial range checks. - const unsigned char glb_invalid_length[] = "glTF" - "\x20\x00\x00\x00" "\x6c\x66\x00\x00" // - // | version | length | - "\x02\x00\x00\x00" "\x4a\x53\x4f\x4e{}"; // + const unsigned char glb_invalid_length[] = + "glTF" + "\x20\x00\x00\x00" + "\x6c\x66\x00\x00" // + // | version | length | + "\x02\x00\x00\x00" + "\x4a\x53\x4f\x4e{}"; // // | model length | model format | bool ret = ctx.LoadBinaryFromMemory(&model, &err, &warn, glb_invalid_length, @@ -231,13 +233,13 @@ TEST_CASE("parse-integer", "[bounds-checking]") { SECTION("parses valid numbers") { std::string err; int result = 123; - CHECK(tinygltf::ParseIntegerProperty(&result, &err, JsonConstruct("{\"zero\" : 0}"), "zero", - true)); + CHECK(tinygltf::ParseIntegerProperty( + &result, &err, JsonConstruct("{\"zero\" : 0}"), "zero", true)); REQUIRE(err == ""); REQUIRE(result == 0); - CHECK(tinygltf::ParseIntegerProperty(&result, &err, JsonConstruct("{\"int\": -1234}"), "int", - true)); + CHECK(tinygltf::ParseIntegerProperty( + &result, &err, JsonConstruct("{\"int\": -1234}"), "int", true)); REQUIRE(err == ""); REQUIRE(result == -1234); } @@ -245,7 +247,8 @@ TEST_CASE("parse-integer", "[bounds-checking]") { SECTION("detects missing properties") { std::string err; int result = -1; - CHECK_FALSE(tinygltf::ParseIntegerProperty(&result, &err, JsonConstruct(""), "int", true)); + CHECK_FALSE(tinygltf::ParseIntegerProperty(&result, &err, JsonConstruct(""), + "int", true)); REQUIRE_THAT(err, Catch::Contains("'int' property is missing")); REQUIRE(result == -1); } @@ -253,8 +256,8 @@ TEST_CASE("parse-integer", "[bounds-checking]") { SECTION("handled missing but not required properties") { std::string err; int result = -1; - CHECK_FALSE( - tinygltf::ParseIntegerProperty(&result, &err, JsonConstruct(""), "int", false)); + CHECK_FALSE(tinygltf::ParseIntegerProperty(&result, &err, JsonConstruct(""), + "int", false)); REQUIRE(err == ""); REQUIRE(result == -1); } @@ -263,14 +266,14 @@ TEST_CASE("parse-integer", "[bounds-checking]") { std::string err; int result = -1; - CHECK_FALSE(tinygltf::ParseIntegerProperty(&result, &err, JsonConstruct("{\"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, JsonConstruct("{\"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(); @@ -278,9 +281,8 @@ TEST_CASE("parse-integer", "[bounds-checking]") { JsonDocument o; double nan = std::numeric_limits::quiet_NaN(); tinygltf::JsonAddMember(o, "int", json(nan)); - CHECK_FALSE(tinygltf::ParseIntegerProperty( - &result, &err, o, - "int", true)); + CHECK_FALSE( + tinygltf::ParseIntegerProperty(&result, &err, o, "int", true)); REQUIRE_THAT(err, Catch::Contains("not an integer type")); } } @@ -304,19 +306,19 @@ TEST_CASE("parse-unsigned", "[bounds-checking]") { std::string err; size_t result = -1; - CHECK_FALSE(tinygltf::ParseUnsignedProperty(&result, &err, JsonConstruct("{\"int\": -1234}"), - "int", true)); + 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, JsonConstruct("{\"int\": 0.5}"), - "int", true)); + 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, JsonConstruct("{\"int\": 1e300}"), - "int", true)); + CHECK_FALSE(tinygltf::ParseUnsignedProperty( + &result, &err, JsonConstruct("{\"int\": 1e300}"), "int", true)); REQUIRE_THAT(err, Catch::Contains("not a positive integer")); err.clear(); @@ -324,9 +326,8 @@ TEST_CASE("parse-unsigned", "[bounds-checking]") { JsonDocument o; double nan = std::numeric_limits::quiet_NaN(); tinygltf::JsonAddMember(o, "int", json(nan)); - CHECK_FALSE(tinygltf::ParseUnsignedProperty( - &result, &err, o, - "int", true)); + CHECK_FALSE( + tinygltf::ParseUnsignedProperty(&result, &err, o, "int", true)); REQUIRE_THAT(err, Catch::Contains("not a positive integer")); } } @@ -336,8 +337,8 @@ TEST_CASE("parse-integer-array", "[bounds-checking]") { SECTION("parses valid integers") { std::string err; std::vector result; - CHECK(tinygltf::ParseIntegerArrayProperty(&result, &err, - JsonConstruct("{\"x\": [-1, 2, 3]}"), "x", true)); + CHECK(tinygltf::ParseIntegerArrayProperty( + &result, &err, JsonConstruct("{\"x\": [-1, 2, 3]}"), "x", true)); REQUIRE(err == ""); REQUIRE(result.size() == 3); REQUIRE(result[0] == -1); @@ -362,22 +363,27 @@ TEST_CASE("pbr-khr-texture-transform", "[material]") { // Loading is expected to fail, but not crash. bool ret = ctx.LoadASCIIFromFile( - &model, &err, &warn, - "../models/Cube-texture-ext/Cube-textransform.gltf"); + &model, &err, &warn, "../models/Cube-texture-ext/Cube-textransform.gltf"); REQUIRE(ret == true); REQUIRE(model.materials.size() == 2); - REQUIRE(model.materials[0].emissiveTexture.extensions.count("KHR_texture_transform") == 1); - REQUIRE(model.materials[0].emissiveTexture.extensions["KHR_texture_transform"].IsObject()); + REQUIRE(model.materials[0].emissiveTexture.extensions.count( + "KHR_texture_transform") == 1); + REQUIRE(model.materials[0] + .emissiveTexture.extensions["KHR_texture_transform"] + .IsObject()); - tinygltf::Value::Object &texform = model.materials[0].emissiveTexture.extensions["KHR_texture_transform"].Get(); + tinygltf::Value::Object& texform = + model.materials[0] + .emissiveTexture.extensions["KHR_texture_transform"] + .Get(); REQUIRE(texform.count("scale")); REQUIRE(texform["scale"].IsArray()); - // Note: It looks json.hpp parse integer JSON number as integer, not floating point. - // IsNumber return true either value is int or floating point. + // Note: It looks json.hpp parse integer JSON number as integer, not floating + // point. IsNumber return true either value is int or floating point. REQUIRE(texform["scale"].Get(0).IsNumber()); REQUIRE(texform["scale"].Get(1).IsNumber()); @@ -387,18 +393,18 @@ TEST_CASE("pbr-khr-texture-transform", "[material]") { REQUIRE(scale[0] == Approx(1.0)); REQUIRE(scale[1] == Approx(-1.0)); - } TEST_CASE("image-uri-spaces", "[issue-236]") { - tinygltf::Model model; tinygltf::TinyGLTF ctx; std::string err; std::string warn; // Test image file with single spaces. - bool ret = ctx.LoadASCIIFromFile(&model, &err, &warn, "../models/CubeImageUriSpaces/CubeImageUriSpaces.gltf"); + bool ret = ctx.LoadASCIIFromFile( + &model, &err, &warn, + "../models/CubeImageUriSpaces/CubeImageUriSpaces.gltf"); if (!err.empty()) { std::cerr << err << std::endl; } @@ -407,7 +413,9 @@ TEST_CASE("image-uri-spaces", "[issue-236]") { // Test image file with a beginning space, trailing space, and greater than // one consecutive spaces. - ret = ctx.LoadASCIIFromFile(&model, &err, &warn, "../models/CubeImageUriSpaces/CubeImageUriMultipleSpaces.gltf"); + ret = ctx.LoadASCIIFromFile( + &model, &err, &warn, + "../models/CubeImageUriSpaces/CubeImageUriMultipleSpaces.gltf"); if (!err.empty()) { std::cerr << err << std::endl; } @@ -416,11 +424,11 @@ TEST_CASE("image-uri-spaces", "[issue-236]") { } TEST_CASE("serialize-empty-material", "[issue-294]") { - tinygltf::Model m; tinygltf::Material mat; - mat.pbrMetallicRoughness.baseColorFactor = {1.0f, 1.0f, 1.0f, 1.0f}; // default baseColorFactor + mat.pbrMetallicRoughness.baseColorFactor = {1.0f, 1.0f, 1.0f, + 1.0f}; // default baseColorFactor m.materials.push_back(mat); std::stringstream os; @@ -433,24 +441,23 @@ TEST_CASE("serialize-empty-material", "[issue-294]") { REQUIRE(1 == j["materials"].size()); REQUIRE(j["materials"][0].is_object()); - } TEST_CASE("empty-skeleton-id", "[issue-321]") { - tinygltf::Model model; tinygltf::TinyGLTF ctx; std::string err; std::string warn; - bool ret = ctx.LoadASCIIFromFile(&model, &err, &warn, "../models/regression/unassigned-skeleton.gltf"); + bool ret = ctx.LoadASCIIFromFile( + &model, &err, &warn, "../models/regression/unassigned-skeleton.gltf"); if (!err.empty()) { std::cerr << err << std::endl; } REQUIRE(true == ret); REQUIRE(model.skins.size() == 1); - REQUIRE(model.skins[0].skeleton == -1); // unassigned + REQUIRE(model.skins[0].skeleton == -1); // unassigned std::stringstream os; @@ -463,15 +470,85 @@ TEST_CASE("empty-skeleton-id", "[issue-321]") { REQUIRE(1 == j["skins"].size()); REQUIRE(j["skins"][0].is_object()); REQUIRE(j["skins"][0].count("skeleton") == 0); +} +TEST_CASE("empty-bin-buffer", "[issue-382]") { + tinygltf::Model model; + tinygltf::TinyGLTF ctx; + std::string err; + std::string warn; + + tinygltf::Model model_empty; + std::stringstream stream; + bool ret = ctx.WriteGltfSceneToStream(&model_empty, stream, false, true); + REQUIRE(ret == true); + std::string str = stream.str(); + const unsigned char* bytes = (unsigned char*)str.data(); + ret = ctx.LoadBinaryFromMemory(&model, &err, &warn, bytes, str.size()); + if (!err.empty()) { + std::cerr << err << std::endl; + } + REQUIRE(true == ret); + + tinygltf::Model model_empty_buffer; + model_empty_buffer.buffers.push_back(tinygltf::Buffer()); + stream = std::stringstream(); + ret = ctx.WriteGltfSceneToStream(&model_empty_buffer, stream, false, true); + REQUIRE(ret == true); + str = stream.str(); + bytes = (unsigned char*)str.data(); + ret = ctx.LoadBinaryFromMemory(&model, &err, &warn, bytes, str.size()); + if (err.empty()) { + std::cerr << "there should have been an error reported" << std::endl; + } + REQUIRE(false == ret); + + tinygltf::Model model_single_byte_buffer; + tinygltf::Buffer buffer; + buffer.data.push_back(0); + model_single_byte_buffer.buffers.push_back(buffer); + stream = std::stringstream(); + ret = ctx.WriteGltfSceneToStream(&model_single_byte_buffer, stream, false, + true); + REQUIRE(ret == true); + str = stream.str(); + bytes = (unsigned char*)str.data(); + ret = ctx.LoadBinaryFromMemory(&model_single_byte_buffer, &err, &warn, bytes, + str.size()); + if (err.empty()) { + std::cerr << "there should have been an error reported" << std::endl; + } + REQUIRE(false == ret); + + // bool ret = ctx.LoadBinaryFromFile( + // &model, &err, &warn, "../models/EmptyBinChunk-issue382/empty.glb"); + // if (!err.empty()) { + // std::cerr << err << std::endl; + // } + // REQUIRE(true == ret); + + // ret = ctx.LoadBinaryFromFile( + // &model, &err, &warn, + // "../models/EmptyBinChunk-issue382/empty_buffer.glb"); + // if (err.empty()) { + // std::cerr << "there should have been an error reported" << std::endl; + // } + // REQUIRE(false == ret); + + // ret = ctx.LoadBinaryFromFile( + // &model, &err, &warn, + // "../models/EmptyBinChunk-issue382/single_byte_buffer.glb"); + // if (err.empty()) { + // std::cerr << "there should have been an error reported" << std::endl; + // } + // REQUIRE(false == ret); } #ifndef TINYGLTF_NO_FS TEST_CASE("expandpath-utf-8", "[pr-226]") { + std::string s1 = "\xe5\xaf\xb9"; // utf-8 string - std::string s1 = "\xe5\xaf\xb9"; // utf-8 string - - std::string ret = tinygltf::ExpandFilePath(s1, /* userdata */nullptr); + std::string ret = tinygltf::ExpandFilePath(s1, /* userdata */ nullptr); // expected: E5 AF B9 REQUIRE(3 == ret.size()); @@ -479,6 +556,5 @@ TEST_CASE("expandpath-utf-8", "[pr-226]") { REQUIRE(0xe5 == static_cast(ret[0])); REQUIRE(0xaf == static_cast(ret[1])); REQUIRE(0xb9 == static_cast(ret[2])); - } #endif From 651449009007580e5e15e50093360cfe9b3e0be3 Mon Sep 17 00:00:00 2001 From: Kh4n Date: Sat, 17 Sep 2022 12:29:58 -0500 Subject: [PATCH 3/7] update gitignore to remove test file readd accidental removals in gitignore undo autoformat more undo autoformatting --- .gitignore | 7 + Makefile | 13 -- tests/issue-261.gltf | 378 ------------------------------------------- tests/tester.cc | 207 +++++++++++------------- tiny_gltf.h | 2 +- 5 files changed, 98 insertions(+), 509 deletions(-) delete mode 100644 Makefile delete mode 100644 tests/issue-261.gltf diff --git a/.gitignore b/.gitignore index 879c4cd..2fdbb60 100644 --- a/.gitignore +++ b/.gitignore @@ -68,4 +68,11 @@ loader_example tests/tester tests/tester_noexcept tests/issue-97.gltf +tests/issue-261.gltf +# unignore +!./Makefile +!examples/build-gltf/Makefile +!examples/raytrace/cornellbox_suzanne.obj +!tests/Makefile +!tools/windows/premake5.exe \ No newline at end of file diff --git a/Makefile b/Makefile deleted file mode 100644 index ba27550..0000000 --- a/Makefile +++ /dev/null @@ -1,13 +0,0 @@ - -# Use this for strict compilation check(will work on clang 3.8+) -#EXTRA_CXXFLAGS := -fsanitize=address -Wall -Werror -Weverything -Wno-c++11-long-long -Wno-c++98-compat - -# With draco -# EXTRA_CXXFLAGS := -I../draco/src/ -I../draco/build -DTINYGLTF_ENABLE_DRACO -L../draco/build -# EXTRA_LINKFLAGS := -L../draco/build/ -ldracodec -ldraco - -all: - clang++ $(EXTRA_CXXFLAGS) -std=c++11 -g -O0 -o loader_example loader_example.cc $(EXTRA_LINKFLAGS) - -lint: - deps/cpplint.py tiny_gltf.h diff --git a/tests/issue-261.gltf b/tests/issue-261.gltf deleted file mode 100644 index d1c5ca7..0000000 --- a/tests/issue-261.gltf +++ /dev/null @@ -1,378 +0,0 @@ -{ - "accessors": [ - { - "bufferView": 0, - "componentType": 5126, - "count": 8, - "max": [ - 0.5, - 0.5, - 0.5 - ], - "min": [ - -0.5, - -0.5, - -0.5 - ], - "type": "VEC3" - }, - { - "bufferView": 1, - "componentType": 5125, - "count": 36, - "type": "SCALAR" - } - ], - "asset": { - "copyright": "NVIDIA Corporation", - "generator": "Iray glTF plugin", - "version": "2.0" - }, - "bufferViews": [ - { - "buffer": 0, - "byteLength": 96, - "byteStride": 12, - "target": 34962 - }, - { - "buffer": 0, - "byteLength": 144, - "byteOffset": 96, - "target": 34963 - } - ], - "buffers": [ - { - "byteLength": 240, - "uri": "data:application/octet-stream;base64,AAAAvwAAAL8AAAC/AAAAvwAAAL8AAAA/AAAAvwAAAD8AAAC/AAAAvwAAAD8AAAA/AAAAPwAAAL8AAAC/AAAAPwAAAL8AAAA/AAAAPwAAAD8AAAC/AAAAPwAAAD8AAAA/AAAAAAEAAAADAAAAAAAAAAMAAAACAAAAAQAAAAUAAAAHAAAAAQAAAAcAAAADAAAABQAAAAQAAAAGAAAABQAAAAYAAAAHAAAABAAAAAAAAAACAAAABAAAAAIAAAAGAAAABAAAAAUAAAABAAAABAAAAAEAAAAAAAAAAgAAAAMAAAAHAAAAAgAAAAcAAAAGAAAA" - } - ], - "cameras": [ - { - "extensions": { - "NV_Iray": { - "mip_burn_highlights": 0.699999988079071, - "mip_burn_highlights_per_component": false, - "mip_camera_shutter": 4.0, - "mip_cm2_factor": 1.0, - "mip_crush_blacks": 0.5, - "mip_f_number": 1.0, - "mip_film_iso": 100.0, - "mip_gamma": 2.200000047683716, - "mip_saturation": 1.0, - "mip_vignetting": 0.00019999999494757503, - "mip_whitepoint": [ - 1.0, - 1.0, - 1.0, - 1.0 - ], - "tm_enable_tonemapper": true, - "tm_tonemapper": "mia_exposure_photographic" - } - }, - "extras": { - "resolution": [ - 640, - 480 - ] - }, - "name": "default", - "perspective": { - "aspectRatio": 1.3333333730697632, - "yfov": 0.9272952079772949, - "zfar": 1000.0, - "znear": 0.1 - }, - "type": "perspective" - } - ], - "extensions": { - "KHR_lights_punctual": { - "lights": [ - { - "color": [ - 1.0, - 1.0, - 1.0 - ], - "intensity": 1000.0, - "name": "light", - "type": "point" - } - ] - }, - "NV_MDL": { - "modules": [ - "mdl::base", - "mdl::nvidia::core_definitions", - "mdl::state" - ], - "shaders": [ - { - "definition": "mdl::base::environment_spherical(texture_2d)", - "name": "env_shd" - }, - { - "arguments": { - "base_color": [ - 0.0055217444896698, - 0.36859095096588135, - 0.0041161770932376385 - ], - "normal=": 2 - }, - "definition": "mdl::nvidia::core_definitions::flex_material", - "name": "cube_instance_material" - }, - { - "definition": "mdl::state::normal()", - "name": "mdl::state::normal_341" - }, - { - "arguments": { - "base_color": [ - 0.0055217444896698, - 0.36859095096588135, - 0.0041161770932376385 - ], - "normal=": 4 - }, - "definition": "mdl::nvidia::core_definitions::flex_material", - "name": "cube_instance_material" - }, - { - "definition": "mdl::state::normal()", - "name": "mdl::state::normal_341" - } - ] - } - }, - "extensionsUsed": [ - "NV_MDL", - "NV_Iray", - "KHR_lights_punctual" - ], - "materials": [ - { - "doubleSided": true, - "extras": { - "mdl_shader": 1 - }, - "name": "cube_instance_material", - "pbrMetallicRoughness": { - "baseColorFactor": [ - 0.0055217444896698, - 0.36859095096588135, - 0.0041161770932376385, - 1.0 - ] - } - } - ], - "meshes": [ - { - "name": "cube", - "primitives": [ - { - "attributes": { - "POSITION": 0 - }, - "indices": 1, - "material": 0, - "mode": 4 - } - ] - } - ], - "nodes": [ - { - "camera": 0, - "extensions": { - "NV_Iray": { - "iview:fkey": -1, - "iview:fov": 53.130104064941406, - "iview:interest": [ - 0.1342654824256897, - -0.14356137812137604, - 0.037264324724674225 - ], - "iview:position": [ - 0.9729073643684387, - 1.2592438459396362, - 2.4199187755584717 - ], - "iview:roll": 0.0, - "iview:up": [ - 0.0, - 1.0, - 0.0 - ] - } - }, - "matrix": [ - 0.9432751389105357, - -1.1769874756875739e-16, - -0.3320120665176343, - 0.0, - -0.16119596696756341, - 0.8742297945345237, - -0.45797175303889276, - 0.0, - 0.290254840694694, - 0.48551237507207207, - 0.8246392308792816, - 0.0, - 0.9729073377709113, - 1.2592438262175363, - 2.419918748461627, - 1.0 - ], - "name": "CamInst" - }, - { - "extensions": { - "NV_Iray": { - "caustic": true, - "caustic_cast": true, - "caustic_recv": true, - "face_back": true, - "face_front": true, - "finalgather": true, - "finalgather_cast": true, - "finalgather_recv": true, - "globillum": true, - "globillum_cast": true, - "globillum_recv": true, - "material=": 3, - "pickable": true, - "reflection_cast": true, - "reflection_recv": true, - "refraction_cast": true, - "refraction_recv": true, - "shadow_cast": true, - "shadow_recv": true, - "transparency_cast": true, - "transparency_recv": true, - "visible": true - } - }, - "mesh": 0, - "name": "cube_instance" - }, - { - "extensions": { - "KHR_lights_punctual": { - "light": 0 - }, - "NV_Iray": { - "shadow_cast": true, - "visible": false - } - }, - "matrix": [ - 0.6988062355563571, - -7.76042172309776e-17, - -0.7153110128800992, - -0.0, - -0.4276881690736487, - 0.8015668284138362, - -0.41781987700564616, - -0.0, - 0.57336957992379, - 0.5979051928078428, - 0.5601398979107212, - 0.0, - 2.3640632834071384, - 2.465226204472449, - 2.309515908392224, - 1.0 - ], - "name": "light_inst" - } - ], - "scene": 0, - "scenes": [ - { - "extensions": { - "NV_Iray": { - "CP_canny_threshold1": 40, - "CP_canny_threshold2": 120, - "CP_color_quantization": 8, - "IVP_color": [ - 1.0, - 0.0, - 0.0, - 1.0 - ], - "TM_drago_bias": 0.8500000238418579, - "TM_drago_gamma2": 2.200000047683716, - "TM_drago_saturation": 1.0, - "TM_durand_contrast": 4.0, - "TM_durand_gamma": 2.200000047683716, - "TM_durand_saturation": 1.0, - "TM_durand_sigma_color": 2.0, - "TM_durand_sigma_space": 2.0, - "TM_linear_gamma": 2.200000047683716, - "TM_reinhard_color_adapt": 0.8999999761581421, - "TM_reinhard_gamma": 1.0, - "TM_reinhard_intensity": 0.0, - "TM_reinhard_light_adapt": 1.0, - "TM_reye_Ywhite": 1000000.0, - "TM_reye_bsize": 2, - "TM_reye_bthres": 3.0, - "TM_reye_gamma": 2.200000047683716, - "TM_reye_key": 0.5, - "TM_reye_saturation": 1.0, - "TM_reye_whitebalance": [ - 1.0, - 0.9965101480484009, - 0.9805564880371094, - 0.0 - ], - "environment_dome_depth": 200.0, - "environment_dome_height": 200.0, - "environment_dome_mode": "infinite", - "environment_dome_position": [ - 0.0, - 0.0, - 0.0 - ], - "environment_dome_radius": 100.0, - "environment_dome_rotation_angle": 0.0, - "environment_dome_width": 200.0, - "environment_function=": 0, - "environment_function_intensity": 1.9900000095367432, - "iray_instancing": "off", - "iview::inline_color": [ - 1.0, - 0.0, - 0.0, - 1.0 - ], - "iview::inline_width": 1.0, - "iview::magnifier_size": 300, - "iview::offset": 10.0, - "iview::outline_color": [ - 0.0, - 0.0, - 0.0, - 1.0 - ], - "iview::outline_width": 2.0, - "iview::overview": true, - "iview::zoom_factor": 1.0, - "samples": 4.0, - "shadow_cast": true, - "shadow_recv": true - } - }, - "nodes": [ - 0, - 1, - 2 - ] - } - ] -} diff --git a/tests/tester.cc b/tests/tester.cc index c06aab4..0be911e 100644 --- a/tests/tester.cc +++ b/tests/tester.cc @@ -6,37 +6,39 @@ // Nlohmann json(include ../json.hpp) #include "json.hpp" -#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do - // this in one cpp file -#include -#include -#include -#include -#include -#include - +#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file #include "catch.hpp" -static JsonDocument JsonConstruct(const char* str) { +#include +#include +#include +#include +#include +#include + +static JsonDocument JsonConstruct(const char* str) +{ JsonDocument doc; JsonParse(doc, str, strlen(str)); return doc; } + TEST_CASE("parse-error", "[parse]") { + tinygltf::Model model; tinygltf::TinyGLTF ctx; std::string err; std::string warn; - bool ret = ctx.LoadASCIIFromString(&model, &err, &warn, "bora", - static_cast(strlen("bora")), - /* basedir*/ ""); + bool ret = ctx.LoadASCIIFromString(&model, &err, &warn, "bora", static_cast(strlen("bora")), /* basedir*/ ""); REQUIRE(false == ret); + } TEST_CASE("datauri-in-glb", "[issue-79]") { + tinygltf::Model model; tinygltf::TinyGLTF ctx; std::string err; @@ -51,13 +53,13 @@ TEST_CASE("datauri-in-glb", "[issue-79]") { } TEST_CASE("extension-with-empty-object", "[issue-97]") { + tinygltf::Model model; tinygltf::TinyGLTF ctx; std::string err; std::string warn; - bool ret = ctx.LoadASCIIFromFile(&model, &err, &warn, - "../models/Extensions-issue97/test.gltf"); + bool ret = ctx.LoadASCIIFromFile(&model, &err, &warn, "../models/Extensions-issue97/test.gltf"); if (!err.empty()) { std::cerr << err << std::endl; } @@ -91,17 +93,17 @@ TEST_CASE("extension-with-empty-object", "[issue-97]") { REQUIRE(m.materials[0].extensions.size() == 1); REQUIRE(m.materials[0].extensions.count("VENDOR_material_some_ext") == 1); } + } TEST_CASE("extension-overwrite", "[issue-261]") { + tinygltf::Model model; tinygltf::TinyGLTF ctx; std::string err; std::string warn; - bool ret = ctx.LoadASCIIFromFile( - &model, &err, &warn, - "../models/Extensions-overwrite-issue261/issue-261.gltf"); + bool ret = ctx.LoadASCIIFromFile(&model, &err, &warn, "../models/Extensions-overwrite-issue261/issue-261.gltf"); if (!err.empty()) { std::cerr << err << std::endl; } @@ -110,12 +112,9 @@ TEST_CASE("extension-overwrite", "[issue-261]") { REQUIRE(model.extensionsUsed.size() == 3); { bool has_ext_lights = false; - has_ext_lights |= - (model.extensionsUsed[0].compare("KHR_lights_punctual") == 0); - has_ext_lights |= - (model.extensionsUsed[1].compare("KHR_lights_punctual") == 0); - has_ext_lights |= - (model.extensionsUsed[2].compare("KHR_lights_punctual") == 0); + has_ext_lights |= (model.extensionsUsed[0].compare("KHR_lights_punctual") == 0); + has_ext_lights |= (model.extensionsUsed[1].compare("KHR_lights_punctual") == 0); + has_ext_lights |= (model.extensionsUsed[2].compare("KHR_lights_punctual") == 0); REQUIRE(true == has_ext_lights); } @@ -145,7 +144,9 @@ TEST_CASE("extension-overwrite", "[issue-261]") { REQUIRE(m.extensions.size() == 2); REQUIRE(m.extensions.count("NV_MDL")); REQUIRE(m.extensions.count("KHR_lights_punctual")); + } + } TEST_CASE("invalid-primitive-indices", "[bounds-checking]") { @@ -200,13 +201,10 @@ TEST_CASE("glb-invalid-length", "[bounds-checking]") { // This glb has a much longer length than the provided data and should fail // initial range checks. - const unsigned char glb_invalid_length[] = - "glTF" - "\x20\x00\x00\x00" - "\x6c\x66\x00\x00" // - // | version | length | - "\x02\x00\x00\x00" - "\x4a\x53\x4f\x4e{}"; // + const unsigned char glb_invalid_length[] = "glTF" + "\x20\x00\x00\x00" "\x6c\x66\x00\x00" // + // | version | length | + "\x02\x00\x00\x00" "\x4a\x53\x4f\x4e{}"; // // | model length | model format | bool ret = ctx.LoadBinaryFromMemory(&model, &err, &warn, glb_invalid_length, @@ -233,13 +231,13 @@ TEST_CASE("parse-integer", "[bounds-checking]") { SECTION("parses valid numbers") { std::string err; int result = 123; - CHECK(tinygltf::ParseIntegerProperty( - &result, &err, JsonConstruct("{\"zero\" : 0}"), "zero", true)); + CHECK(tinygltf::ParseIntegerProperty(&result, &err, JsonConstruct("{\"zero\" : 0}"), "zero", + true)); REQUIRE(err == ""); REQUIRE(result == 0); - CHECK(tinygltf::ParseIntegerProperty( - &result, &err, JsonConstruct("{\"int\": -1234}"), "int", true)); + CHECK(tinygltf::ParseIntegerProperty(&result, &err, JsonConstruct("{\"int\": -1234}"), "int", + true)); REQUIRE(err == ""); REQUIRE(result == -1234); } @@ -247,8 +245,7 @@ TEST_CASE("parse-integer", "[bounds-checking]") { SECTION("detects missing properties") { std::string err; int result = -1; - CHECK_FALSE(tinygltf::ParseIntegerProperty(&result, &err, JsonConstruct(""), - "int", true)); + CHECK_FALSE(tinygltf::ParseIntegerProperty(&result, &err, JsonConstruct(""), "int", true)); REQUIRE_THAT(err, Catch::Contains("'int' property is missing")); REQUIRE(result == -1); } @@ -256,8 +253,8 @@ TEST_CASE("parse-integer", "[bounds-checking]") { SECTION("handled missing but not required properties") { std::string err; int result = -1; - CHECK_FALSE(tinygltf::ParseIntegerProperty(&result, &err, JsonConstruct(""), - "int", false)); + CHECK_FALSE( + tinygltf::ParseIntegerProperty(&result, &err, JsonConstruct(""), "int", false)); REQUIRE(err == ""); REQUIRE(result == -1); } @@ -266,14 +263,14 @@ TEST_CASE("parse-integer", "[bounds-checking]") { std::string err; int result = -1; - CHECK_FALSE(tinygltf::ParseIntegerProperty( - &result, &err, JsonConstruct("{\"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, JsonConstruct("{\"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(); @@ -281,8 +278,9 @@ TEST_CASE("parse-integer", "[bounds-checking]") { JsonDocument o; double nan = std::numeric_limits::quiet_NaN(); tinygltf::JsonAddMember(o, "int", json(nan)); - CHECK_FALSE( - tinygltf::ParseIntegerProperty(&result, &err, o, "int", true)); + CHECK_FALSE(tinygltf::ParseIntegerProperty( + &result, &err, o, + "int", true)); REQUIRE_THAT(err, Catch::Contains("not an integer type")); } } @@ -306,19 +304,19 @@ TEST_CASE("parse-unsigned", "[bounds-checking]") { std::string err; size_t result = -1; - CHECK_FALSE(tinygltf::ParseUnsignedProperty( - &result, &err, JsonConstruct("{\"int\": -1234}"), "int", true)); + 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, JsonConstruct("{\"int\": 0.5}"), "int", true)); + 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, JsonConstruct("{\"int\": 1e300}"), "int", true)); + CHECK_FALSE(tinygltf::ParseUnsignedProperty(&result, &err, JsonConstruct("{\"int\": 1e300}"), + "int", true)); REQUIRE_THAT(err, Catch::Contains("not a positive integer")); err.clear(); @@ -326,8 +324,9 @@ TEST_CASE("parse-unsigned", "[bounds-checking]") { JsonDocument o; double nan = std::numeric_limits::quiet_NaN(); tinygltf::JsonAddMember(o, "int", json(nan)); - CHECK_FALSE( - tinygltf::ParseUnsignedProperty(&result, &err, o, "int", true)); + CHECK_FALSE(tinygltf::ParseUnsignedProperty( + &result, &err, o, + "int", true)); REQUIRE_THAT(err, Catch::Contains("not a positive integer")); } } @@ -337,8 +336,8 @@ TEST_CASE("parse-integer-array", "[bounds-checking]") { SECTION("parses valid integers") { std::string err; std::vector result; - CHECK(tinygltf::ParseIntegerArrayProperty( - &result, &err, JsonConstruct("{\"x\": [-1, 2, 3]}"), "x", true)); + CHECK(tinygltf::ParseIntegerArrayProperty(&result, &err, + JsonConstruct("{\"x\": [-1, 2, 3]}"), "x", true)); REQUIRE(err == ""); REQUIRE(result.size() == 3); REQUIRE(result[0] == -1); @@ -363,27 +362,22 @@ TEST_CASE("pbr-khr-texture-transform", "[material]") { // Loading is expected to fail, but not crash. bool ret = ctx.LoadASCIIFromFile( - &model, &err, &warn, "../models/Cube-texture-ext/Cube-textransform.gltf"); + &model, &err, &warn, + "../models/Cube-texture-ext/Cube-textransform.gltf"); REQUIRE(ret == true); REQUIRE(model.materials.size() == 2); - REQUIRE(model.materials[0].emissiveTexture.extensions.count( - "KHR_texture_transform") == 1); - REQUIRE(model.materials[0] - .emissiveTexture.extensions["KHR_texture_transform"] - .IsObject()); + REQUIRE(model.materials[0].emissiveTexture.extensions.count("KHR_texture_transform") == 1); + REQUIRE(model.materials[0].emissiveTexture.extensions["KHR_texture_transform"].IsObject()); - tinygltf::Value::Object& texform = - model.materials[0] - .emissiveTexture.extensions["KHR_texture_transform"] - .Get(); + tinygltf::Value::Object &texform = model.materials[0].emissiveTexture.extensions["KHR_texture_transform"].Get(); REQUIRE(texform.count("scale")); REQUIRE(texform["scale"].IsArray()); - // Note: It looks json.hpp parse integer JSON number as integer, not floating - // point. IsNumber return true either value is int or floating point. + // Note: It looks json.hpp parse integer JSON number as integer, not floating point. + // IsNumber return true either value is int or floating point. REQUIRE(texform["scale"].Get(0).IsNumber()); REQUIRE(texform["scale"].Get(1).IsNumber()); @@ -393,18 +387,18 @@ TEST_CASE("pbr-khr-texture-transform", "[material]") { REQUIRE(scale[0] == Approx(1.0)); REQUIRE(scale[1] == Approx(-1.0)); + } TEST_CASE("image-uri-spaces", "[issue-236]") { + tinygltf::Model model; tinygltf::TinyGLTF ctx; std::string err; std::string warn; // Test image file with single spaces. - bool ret = ctx.LoadASCIIFromFile( - &model, &err, &warn, - "../models/CubeImageUriSpaces/CubeImageUriSpaces.gltf"); + bool ret = ctx.LoadASCIIFromFile(&model, &err, &warn, "../models/CubeImageUriSpaces/CubeImageUriSpaces.gltf"); if (!err.empty()) { std::cerr << err << std::endl; } @@ -413,9 +407,7 @@ TEST_CASE("image-uri-spaces", "[issue-236]") { // Test image file with a beginning space, trailing space, and greater than // one consecutive spaces. - ret = ctx.LoadASCIIFromFile( - &model, &err, &warn, - "../models/CubeImageUriSpaces/CubeImageUriMultipleSpaces.gltf"); + ret = ctx.LoadASCIIFromFile(&model, &err, &warn, "../models/CubeImageUriSpaces/CubeImageUriMultipleSpaces.gltf"); if (!err.empty()) { std::cerr << err << std::endl; } @@ -424,11 +416,11 @@ TEST_CASE("image-uri-spaces", "[issue-236]") { } TEST_CASE("serialize-empty-material", "[issue-294]") { + tinygltf::Model m; tinygltf::Material mat; - mat.pbrMetallicRoughness.baseColorFactor = {1.0f, 1.0f, 1.0f, - 1.0f}; // default baseColorFactor + mat.pbrMetallicRoughness.baseColorFactor = {1.0f, 1.0f, 1.0f, 1.0f}; // default baseColorFactor m.materials.push_back(mat); std::stringstream os; @@ -441,23 +433,24 @@ TEST_CASE("serialize-empty-material", "[issue-294]") { REQUIRE(1 == j["materials"].size()); REQUIRE(j["materials"][0].is_object()); + } TEST_CASE("empty-skeleton-id", "[issue-321]") { + tinygltf::Model model; tinygltf::TinyGLTF ctx; std::string err; std::string warn; - bool ret = ctx.LoadASCIIFromFile( - &model, &err, &warn, "../models/regression/unassigned-skeleton.gltf"); + bool ret = ctx.LoadASCIIFromFile(&model, &err, &warn, "../models/regression/unassigned-skeleton.gltf"); if (!err.empty()) { std::cerr << err << std::endl; } REQUIRE(true == ret); REQUIRE(model.skins.size() == 1); - REQUIRE(model.skins[0].skeleton == -1); // unassigned + REQUIRE(model.skins[0].skeleton == -1); // unassigned std::stringstream os; @@ -470,8 +463,26 @@ TEST_CASE("empty-skeleton-id", "[issue-321]") { REQUIRE(1 == j["skins"].size()); REQUIRE(j["skins"][0].is_object()); REQUIRE(j["skins"][0].count("skeleton") == 0); + } +#ifndef TINYGLTF_NO_FS +TEST_CASE("expandpath-utf-8", "[pr-226]") { + + std::string s1 = "\xe5\xaf\xb9"; // utf-8 string + + std::string ret = tinygltf::ExpandFilePath(s1, /* userdata */nullptr); + + // expected: E5 AF B9 + REQUIRE(3 == ret.size()); + + REQUIRE(0xe5 == static_cast(ret[0])); + REQUIRE(0xaf == static_cast(ret[1])); + REQUIRE(0xb9 == static_cast(ret[2])); + +} +#endif + TEST_CASE("empty-bin-buffer", "[issue-382]") { tinygltf::Model model; tinygltf::TinyGLTF ctx; @@ -519,42 +530,4 @@ TEST_CASE("empty-bin-buffer", "[issue-382]") { std::cerr << "there should have been an error reported" << std::endl; } REQUIRE(false == ret); - - // bool ret = ctx.LoadBinaryFromFile( - // &model, &err, &warn, "../models/EmptyBinChunk-issue382/empty.glb"); - // if (!err.empty()) { - // std::cerr << err << std::endl; - // } - // REQUIRE(true == ret); - - // ret = ctx.LoadBinaryFromFile( - // &model, &err, &warn, - // "../models/EmptyBinChunk-issue382/empty_buffer.glb"); - // if (err.empty()) { - // std::cerr << "there should have been an error reported" << std::endl; - // } - // REQUIRE(false == ret); - - // ret = ctx.LoadBinaryFromFile( - // &model, &err, &warn, - // "../models/EmptyBinChunk-issue382/single_byte_buffer.glb"); - // if (err.empty()) { - // std::cerr << "there should have been an error reported" << std::endl; - // } - // REQUIRE(false == ret); -} - -#ifndef TINYGLTF_NO_FS -TEST_CASE("expandpath-utf-8", "[pr-226]") { - std::string s1 = "\xe5\xaf\xb9"; // utf-8 string - - std::string ret = tinygltf::ExpandFilePath(s1, /* userdata */ nullptr); - - // expected: E5 AF B9 - REQUIRE(3 == ret.size()); - - REQUIRE(0xe5 == static_cast(ret[0])); - REQUIRE(0xaf == static_cast(ret[1])); - REQUIRE(0xb9 == static_cast(ret[2])); -} -#endif +} \ No newline at end of file diff --git a/tiny_gltf.h b/tiny_gltf.h index 0971c18..c43d7ac 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -7894,4 +7894,4 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename, #pragma clang diagnostic pop #endif -#endif // TINYGLTF_IMPLEMENTATION +#endif // TINYGLTF_IMPLEMENTATION \ No newline at end of file From 387fd61b83e9ffabc31afb1f1cdc507fec5ff657 Mon Sep 17 00:00:00 2001 From: Kh4n Date: Sat, 17 Sep 2022 13:02:39 -0500 Subject: [PATCH 4/7] update test to match gltf-validator --- tests/tester.cc | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/tests/tester.cc b/tests/tester.cc index 0be911e..947d02b 100644 --- a/tests/tester.cc +++ b/tests/tester.cc @@ -519,15 +519,13 @@ TEST_CASE("empty-bin-buffer", "[issue-382]") { buffer.data.push_back(0); model_single_byte_buffer.buffers.push_back(buffer); stream = std::stringstream(); - ret = ctx.WriteGltfSceneToStream(&model_single_byte_buffer, stream, false, - true); + ret = ctx.WriteGltfSceneToStream(&model_single_byte_buffer, stream, false, true); REQUIRE(ret == true); str = stream.str(); bytes = (unsigned char*)str.data(); - ret = ctx.LoadBinaryFromMemory(&model_single_byte_buffer, &err, &warn, bytes, - str.size()); - if (err.empty()) { - std::cerr << "there should have been an error reported" << std::endl; + ret = ctx.LoadBinaryFromMemory(&model_single_byte_buffer, &err, &warn, bytes, str.size()); + if (!err.empty()) { + std::cerr << err << std::endl; } - REQUIRE(false == ret); + REQUIRE(true == ret); } \ No newline at end of file From a778c089d1374b8c6fbd2c11b178693273631caf Mon Sep 17 00:00:00 2001 From: Kh4n Date: Sat, 17 Sep 2022 15:39:28 -0500 Subject: [PATCH 5/7] readd toplevel makefile --- .gitignore | 2 +- Makefile | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 Makefile diff --git a/.gitignore b/.gitignore index 2fdbb60..299b317 100644 --- a/.gitignore +++ b/.gitignore @@ -71,7 +71,7 @@ tests/issue-97.gltf tests/issue-261.gltf # unignore -!./Makefile +!/Makefile !examples/build-gltf/Makefile !examples/raytrace/cornellbox_suzanne.obj !tests/Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ba27550 --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ + +# Use this for strict compilation check(will work on clang 3.8+) +#EXTRA_CXXFLAGS := -fsanitize=address -Wall -Werror -Weverything -Wno-c++11-long-long -Wno-c++98-compat + +# With draco +# EXTRA_CXXFLAGS := -I../draco/src/ -I../draco/build -DTINYGLTF_ENABLE_DRACO -L../draco/build +# EXTRA_LINKFLAGS := -L../draco/build/ -ldracodec -ldraco + +all: + clang++ $(EXTRA_CXXFLAGS) -std=c++11 -g -O0 -o loader_example loader_example.cc $(EXTRA_LINKFLAGS) + +lint: + deps/cpplint.py tiny_gltf.h From 612e57816feae1326ba32288c5ab12eb4cacb6cf Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Sun, 18 Sep 2022 21:01:39 +0900 Subject: [PATCH 6/7] Fix handling <4 byte BIN data. Fix handling GLB file with empty CHUNK1(BIN). --- .gitignore | 4 ++-- tiny_gltf.h | 19 +++++++++++++------ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 299b317..92ed8ed 100644 --- a/.gitignore +++ b/.gitignore @@ -71,8 +71,8 @@ tests/issue-97.gltf tests/issue-261.gltf # unignore -!/Makefile +!Makefile !examples/build-gltf/Makefile !examples/raytrace/cornellbox_suzanne.obj !tests/Makefile -!tools/windows/premake5.exe \ No newline at end of file +!tools/windows/premake5.exe diff --git a/tiny_gltf.h b/tiny_gltf.h index c43d7ac..fb99d92 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -6311,19 +6311,22 @@ bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err, } } + //std::cout << "header_and_json_size = " << header_and_json_size << "\n"; + //std::cout << "length = " << length << "\n"; + // Chunk1(BIN) data // The spec says: When the binary buffer is empty or when it is stored by other means, this chunk SHOULD be omitted. - if (header_and_json_size <= uint64_t(length)) { + // So when header + JSON data == binary size, Chunk1 is omitted. + if (header_and_json_size == uint64_t(length)) { - // Just in case... bin_data_ = nullptr; bin_size_ = 0; } else { // Read Chunk1 info(BIN data) - // At least Chunk1 should have 12 bytes(8 bytes(header) + 4 bytes(bin content)) - if ((header_and_json_size + 12ull) >= uint64_t(length)) { + // At least Chunk1 should have 12 bytes(8 bytes(header) + 4 bytes(bin payload could be 1~3 bytes, but need to be aliged to 4 bytes) + if ((header_and_json_size + 12ull) > uint64_t(length)) { if (err) { - (*err) = "Insufficient storage space for Chunk1(BIN data). At least Chunk1 Must have 12bytes or more bytes, but got " + std::to_string((header_and_json_size + 12ull) - uint64_t(length)) + ".\n"; + (*err) = "Insufficient storage space for Chunk1(BIN data). At least Chunk1 Must have 4 bytes or more bytes, but got " + std::to_string((header_and_json_size + 12ull) - uint64_t(length)) + ".\n"; } return false; } @@ -6335,6 +6338,8 @@ bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err, memcpy(&chunk1_format, bytes + header_and_json_size + 4, 4); swap4(&chunk1_format); + //std::cout << "chunk1_length = " << chunk1_length << "\n"; + if (chunk1_length < 4) { if (err) { (*err) = "Insufficient Chunk1(BIN) data size."; @@ -6363,6 +6368,8 @@ bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err, return false; } + //std::cout << "chunk1_length = " << chunk1_length << "\n"; + bin_data_ = bytes + header_and_json_size + 8; // 4 bytes (bin_buffer_length) + 4 bytes(bin_buffer_format) @@ -7894,4 +7901,4 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename, #pragma clang diagnostic pop #endif -#endif // TINYGLTF_IMPLEMENTATION \ No newline at end of file +#endif // TINYGLTF_IMPLEMENTATION From e9fbc03e2dd50ae0ab95a33881ecd504753fcae2 Mon Sep 17 00:00:00 2001 From: Syoyo Fujita Date: Mon, 19 Sep 2022 03:29:57 +0900 Subject: [PATCH 7/7] Clear error/warn message. --- tests/tester.cc | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/tester.cc b/tests/tester.cc index 947d02b..0d69a83 100644 --- a/tests/tester.cc +++ b/tests/tester.cc @@ -501,6 +501,9 @@ TEST_CASE("empty-bin-buffer", "[issue-382]") { } REQUIRE(true == ret); + err.clear(); + warn.clear(); + tinygltf::Model model_empty_buffer; model_empty_buffer.buffers.push_back(tinygltf::Buffer()); stream = std::stringstream(); @@ -514,6 +517,9 @@ TEST_CASE("empty-bin-buffer", "[issue-382]") { } REQUIRE(false == ret); + err.clear(); + warn.clear(); + tinygltf::Model model_single_byte_buffer; tinygltf::Buffer buffer; buffer.data.push_back(0); @@ -522,10 +528,15 @@ TEST_CASE("empty-bin-buffer", "[issue-382]") { ret = ctx.WriteGltfSceneToStream(&model_single_byte_buffer, stream, false, true); REQUIRE(ret == true); str = stream.str(); + { + std::ofstream ofs("tmp.glb"); + ofs.write(str.data(), str.size()); + } + bytes = (unsigned char*)str.data(); ret = ctx.LoadBinaryFromMemory(&model_single_byte_buffer, &err, &warn, bytes, str.size()); if (!err.empty()) { std::cerr << err << std::endl; } REQUIRE(true == ret); -} \ No newline at end of file +}