diff --git a/models/BoundsChecking/integer-out-of-bounds.gltf b/models/BoundsChecking/integer-out-of-bounds.gltf new file mode 100644 index 0000000..8f73d63 --- /dev/null +++ b/models/BoundsChecking/integer-out-of-bounds.gltf @@ -0,0 +1,67 @@ +{ + "scenes": [ + { + "nodes": [0] + } + ], + "nodes": [ + { + "mesh": 0 + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "POSITION": 1 + }, + "indices": 0 + } + ] + } + ], + "buffers": [ + { + "uri": "simpleTriangle.bin", + "byteLength": 44 + } + ], + "bufferViews": [ + { + "buffer": 0, + "byteOffset": 0, + "byteLength": 1e300, + "target": 34963 + }, + { + "buffer": 0, + "byteOffset": 8, + "byteLength": 36, + "target": 34962 + } + ], + "accessors": [ + { + "bufferView": 0, + "byteOffset": 0, + "componentType": 5123, + "count": 3, + "type": "SCALAR", + "max": [2], + "min": [0] + }, + { + "bufferView": 1, + "byteOffset": 0, + "componentType": 5126, + "count": 3, + "type": "VEC3", + "max": [1, 1, 0], + "min": [0, 0, 0] + } + ], + "asset": { + "version": "2.0" + } +} diff --git a/tests/tester.cc b/tests/tester.cc index a07c549..f3e96dd 100644 --- a/tests/tester.cc +++ b/tests/tester.cc @@ -37,7 +37,7 @@ TEST_CASE("datauri-in-glb", "[issue-79]") { if (!err.empty()) { std::cerr << err << std::endl; } - + REQUIRE(true == ret); } @@ -148,3 +148,134 @@ TEST_CASE("glb-invalid-length", "[bounds-checking]") { REQUIRE_THAT(err, Catch::Contains("Invalid glTF binary.")); REQUIRE_FALSE(ret); } + +TEST_CASE("integer-out-of-bounds", "[bounds-checking]") { + tinygltf::Model model; + tinygltf::TinyGLTF ctx; + std::string err; + std::string warn; + + // Loading is expected to fail, but not crash. + bool ret = ctx.LoadASCIIFromFile( + &model, &err, &warn, + "../models/BoundsChecking/integer-out-of-bounds.gltf"); + REQUIRE_THAT(err, Catch::Contains("not a positive integer")); + REQUIRE_FALSE(ret); +} + +TEST_CASE("parse-integer", "[bounds-checking]") { + SECTION("parses valid numbers") { + std::string err; + int result = 123; + CHECK(tinygltf::ParseIntegerProperty(&result, &err, {{"zero", 0}}, "zero", + true)); + REQUIRE(err == ""); + REQUIRE(result == 0); + + CHECK(tinygltf::ParseIntegerProperty(&result, &err, {{"int", -1234}}, "int", + true)); + REQUIRE(err == ""); + REQUIRE(result == -1234); + } + + SECTION("detects missing properties") { + std::string err; + int result = -1; + CHECK_FALSE(tinygltf::ParseIntegerProperty(&result, &err, {}, "int", true)); + REQUIRE_THAT(err, Catch::Contains("'int' property is missing")); + REQUIRE(result == -1); + } + + SECTION("handled missing but not required properties") { + std::string err; + int result = -1; + CHECK_FALSE( + tinygltf::ParseIntegerProperty(&result, &err, {}, "int", false)); + REQUIRE(err == ""); + REQUIRE(result == -1); + } + + SECTION("invalid integers") { + std::string err; + int result = -1; + + CHECK_FALSE(tinygltf::ParseIntegerProperty(&result, &err, {{"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)); + REQUIRE_THAT(err, Catch::Contains("not an integer type")); + + err.clear(); + CHECK_FALSE(tinygltf::ParseIntegerProperty( + &result, &err, {{"int", std::numeric_limits::quiet_NaN()}}, + "int", true)); + REQUIRE_THAT(err, Catch::Contains("not an integer type")); + } +} + +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}"); + + std::string err; + size_t result = 123; + CHECK( + tinygltf::ParseUnsignedProperty(&result, &err, zero_obj, "zero", true)); + REQUIRE(err == ""); + REQUIRE(result == 0); + } + + SECTION("invalid integers") { + std::string err; + size_t result = -1; + + CHECK_FALSE(tinygltf::ParseUnsignedProperty(&result, &err, {{"int", -1234}}, + "int", true)); + REQUIRE_THAT(err, Catch::Contains("not a positive integer")); + + err.clear(); + CHECK_FALSE(tinygltf::ParseUnsignedProperty(&result, &err, {{"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}}, + "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()}}, + "int", true)); + REQUIRE_THAT(err, Catch::Contains("not a positive integer")); + } +} + +TEST_CASE("parse-integer-array", "[bounds-checking]") { + SECTION("parses valid integers") { + std::string err; + std::vector result; + CHECK(tinygltf::ParseIntegerArrayProperty(&result, &err, + {{"x", {-1, 2, 3}}}, "x", true)); + REQUIRE(err == ""); + REQUIRE(result.size() == 3); + REQUIRE(result[0] == -1); + REQUIRE(result[1] == 2); + REQUIRE(result[2] == 3); + } + + SECTION("invalid integers") { + std::string err; + std::vector result; + CHECK_FALSE(tinygltf::ParseIntegerArrayProperty( + &result, &err, {{"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 7f2e239..b499184 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -2271,6 +2271,74 @@ static bool ParseBooleanProperty(bool *ret, std::string *err, const json &o, return true; } +static bool ParseIntegerProperty(int *ret, std::string *err, const json &o, + const std::string &property, + const bool required, + const std::string &parent_node = "") { + json::const_iterator it = o.find(property); + if (it == o.end()) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is missing"; + if (!parent_node.empty()) { + (*err) += " in " + parent_node; + } + (*err) += ".\n"; + } + } + return false; + } + + if (!it.value().is_number_integer()) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is not an integer type.\n"; + } + } + return false; + } + + if (ret) { + (*ret) = it.value().get(); + } + + return true; +} + +static bool ParseUnsignedProperty(size_t *ret, std::string *err, const json &o, + const std::string &property, + const bool required, + const std::string &parent_node = "") { + json::const_iterator it = o.find(property); + if (it == o.end()) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is missing"; + if (!parent_node.empty()) { + (*err) += " in " + parent_node; + } + (*err) += ".\n"; + } + } + return false; + } + + if (!it.value().is_number_unsigned()) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is not a positive integer.\n"; + } + } + return false; + } + + if (ret) { + (*ret) = it.value().get(); + } + + return true; +} + static bool ParseNumberProperty(double *ret, std::string *err, const json &o, const std::string &property, const bool required, @@ -2357,6 +2425,59 @@ static bool ParseNumberArrayProperty(std::vector *ret, std::string *err, return true; } +static bool ParseIntegerArrayProperty(std::vector *ret, std::string *err, + const json &o, + const std::string &property, + bool required, + const std::string &parent_node = "") { + json::const_iterator it = o.find(property); + if (it == o.end()) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is missing"; + if (!parent_node.empty()) { + (*err) += " in " + parent_node; + } + (*err) += ".\n"; + } + } + return false; + } + + if (!it.value().is_array()) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is not an array"; + if (!parent_node.empty()) { + (*err) += " in " + parent_node; + } + (*err) += ".\n"; + } + } + return false; + } + + ret->clear(); + for (json::const_iterator i = it.value().begin(); i != it.value().end(); + i++) { + if (!i.value().is_number_integer()) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is not an integer type.\n"; + if (!parent_node.empty()) { + (*err) += " in " + parent_node; + } + (*err) += ".\n"; + } + } + return false; + } + ret->push_back(i.value()); + } + + return true; +} + static bool ParseStringProperty( std::string *ret, std::string *err, const json &o, const std::string &property, bool required, @@ -2392,10 +2513,11 @@ static bool ParseStringProperty( return true; } -static bool ParseStringIntProperty(std::map *ret, - std::string *err, const json &o, - const std::string &property, bool required, - const std::string &parent = "") { +static bool ParseStringIntegerProperty(std::map *ret, + std::string *err, const json &o, + const std::string &property, + bool required, + const std::string &parent = "") { json::const_iterator it = o.find(property); if (it == o.end()) { if (required) { @@ -2428,17 +2550,17 @@ static bool ParseStringIntProperty(std::map *ret, json::const_iterator dictItEnd(dict.end()); for (; dictIt != dictItEnd; ++dictIt) { - if (!dictIt.value().is_number()) { + if (!dictIt.value().is_number_integer()) { if (required) { if (err) { - (*err) += "'" + property + "' value is not an int.\n"; + (*err) += "'" + property + "' value is not an integer type.\n"; } } return false; } // Insert into the list. - (*ret)[dictIt.key()] = static_cast(dictIt.value()); + (*ret)[dictIt.key()] = dictIt.value(); } return true; } @@ -2589,8 +2711,8 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err, ParseExtrasProperty(&image->extras, o); if (hasBufferView) { - double bufferView = -1; - if (!ParseNumberProperty(&bufferView, err, o, "bufferView", true)) { + int bufferView = -1; + if (!ParseIntegerProperty(&bufferView, err, o, "bufferView", true)) { if (err) { (*err) += "Failed to parse `bufferView` for image[" + std::to_string(image_idx) + "] name = \"" + image->name + @@ -2602,18 +2724,18 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err, std::string mime_type; ParseStringProperty(&mime_type, err, o, "mimeType", false); - double width = 0.0; - ParseNumberProperty(&width, err, o, "width", false); + int width = 0; + ParseIntegerProperty(&width, err, o, "width", false); - double height = 0.0; - ParseNumberProperty(&height, err, o, "height", false); + int height = 0; + ParseIntegerProperty(&height, err, o, "height", false); // Just only save some information here. Loading actual image data from // bufferView is done after this `ParseImage` function. - image->bufferView = static_cast(bufferView); + image->bufferView = bufferView; image->mimeType = mime_type; - image->width = static_cast(width); - image->height = static_cast(height); + image->width = width; + image->height = height; return true; } @@ -2681,14 +2803,14 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err, static bool ParseTexture(Texture *texture, std::string *err, const json &o, const std::string &basedir) { (void)basedir; - double sampler = -1.0; - double source = -1.0; - ParseNumberProperty(&sampler, err, o, "sampler", false); + int sampler = -1; + int source = -1; + ParseIntegerProperty(&sampler, err, o, "sampler", false); - ParseNumberProperty(&source, err, o, "source", false); + ParseIntegerProperty(&source, err, o, "source", false); - texture->sampler = static_cast(sampler); - texture->source = static_cast(source); + texture->sampler = sampler; + texture->source = source; ParseExtensionsProperty(&texture->extensions, err, o); ParseExtrasProperty(&texture->extras, o); @@ -2703,8 +2825,9 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o, bool is_binary = false, const unsigned char *bin_data = nullptr, size_t bin_size = 0) { - double byteLength; - if (!ParseNumberProperty(&byteLength, err, o, "byteLength", true, "Buffer")) { + size_t byteLength; + if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true, + "Buffer")) { return false; } @@ -2729,14 +2852,13 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o, } } - size_t bytes = static_cast(byteLength); if (is_binary) { // Still binary glTF accepts external dataURI. if (!buffer->uri.empty()) { // First try embedded data URI. if (IsDataURI(buffer->uri)) { std::string mime_type; - if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, bytes, + if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength, true)) { if (err) { (*err) += @@ -2747,7 +2869,8 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o, } else { // External .bin file. if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, - buffer->uri, basedir, true, bytes, true, fs)) { + buffer->uri, basedir, true, byteLength, true, + fs)) { return false; } } @@ -2780,7 +2903,8 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o, } else { if (IsDataURI(buffer->uri)) { std::string mime_type; - if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, bytes, true)) { + if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength, + true)) { if (err) { (*err) += "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n"; } @@ -2789,7 +2913,7 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o, } else { // Assume external .bin file. if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, buffer->uri, - basedir, true, bytes, true, fs)) { + basedir, true, byteLength, true, fs)) { return false; } } @@ -2802,31 +2926,28 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o, static bool ParseBufferView(BufferView *bufferView, std::string *err, const json &o) { - double buffer = -1.0; - if (!ParseNumberProperty(&buffer, err, o, "buffer", true, "BufferView")) { + int buffer = -1; + if (!ParseIntegerProperty(&buffer, err, o, "buffer", true, "BufferView")) { return false; } - double byteOffset = 0.0; - ParseNumberProperty(&byteOffset, err, o, "byteOffset", false); + size_t byteOffset = 0; + ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false); - double byteLength = 1.0; - if (!ParseNumberProperty(&byteLength, err, o, "byteLength", true, - "BufferView")) { + size_t byteLength = 1; + if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true, + "BufferView")) { return false; } size_t byteStride = 0; - double byteStrideValue = 0.0; - if (!ParseNumberProperty(&byteStrideValue, err, o, "byteStride", false)) { + if (!ParseUnsignedProperty(&byteStride, err, o, "byteStride", false)) { // Spec says: When byteStride of referenced bufferView is not defined, it // means that accessor elements are tightly packed, i.e., effective stride // equals the size of the element. // We cannot determine the actual byteStride until Accessor are parsed, thus // set 0(= tightly packed) here(as done in OpenGL's VertexAttribPoiner) byteStride = 0; - } else { - byteStride = static_cast(byteStrideValue); } if ((byteStride > 252) || ((byteStride % 4) != 0)) { @@ -2841,23 +2962,22 @@ static bool ParseBufferView(BufferView *bufferView, std::string *err, return false; } - double target = 0.0; - ParseNumberProperty(&target, err, o, "target", false); - int targetValue = static_cast(target); - if ((targetValue == TINYGLTF_TARGET_ARRAY_BUFFER) || - (targetValue == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)) { + int target = 0; + ParseIntegerProperty(&target, err, o, "target", false); + if ((target == TINYGLTF_TARGET_ARRAY_BUFFER) || + (target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)) { // OK } else { - targetValue = 0; + target = 0; } - bufferView->target = targetValue; + bufferView->target = target; ParseStringProperty(&bufferView->name, err, o, "name", false); - bufferView->buffer = static_cast(buffer); - bufferView->byteOffset = static_cast(byteOffset); - bufferView->byteLength = static_cast(byteLength); - bufferView->byteStride = static_cast(byteStride); + bufferView->buffer = buffer; + bufferView->byteOffset = byteOffset; + bufferView->byteLength = byteLength; + bufferView->byteStride = byteStride; return true; } @@ -2865,8 +2985,8 @@ static bool ParseSparseAccessor(Accessor *accessor, std::string *err, const json &o) { accessor->sparse.isSparse = true; - double count = 0.0; - ParseNumberProperty(&count, err, o, "count", true); + int count = 0; + ParseIntegerProperty(&count, err, o, "count", true); const auto indices_iterator = o.find("indices"); const auto values_iterator = o.find("values"); @@ -2883,24 +3003,26 @@ static bool ParseSparseAccessor(Accessor *accessor, std::string *err, const json &indices_obj = *indices_iterator; const json &values_obj = *values_iterator; - double indices_buffer_view = 0.0, indices_byte_offset = 0.0, - component_type = 0.0; - ParseNumberProperty(&indices_buffer_view, err, indices_obj, "bufferView", - true); - ParseNumberProperty(&indices_byte_offset, err, indices_obj, "byteOffset", - true); - ParseNumberProperty(&component_type, err, indices_obj, "componentType", true); + int indices_buffer_view = 0, indices_byte_offset = 0, component_type = 0; + ParseIntegerProperty(&indices_buffer_view, err, indices_obj, "bufferView", + true); + ParseIntegerProperty(&indices_byte_offset, err, indices_obj, "byteOffset", + true); + ParseIntegerProperty(&component_type, err, indices_obj, "componentType", + true); - double values_buffer_view = 0.0, values_byte_offset = 0.0; - ParseNumberProperty(&values_buffer_view, err, values_obj, "bufferView", true); - ParseNumberProperty(&values_byte_offset, err, values_obj, "byteOffset", true); + int values_buffer_view = 0, values_byte_offset = 0; + ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView", + true); + ParseIntegerProperty(&values_byte_offset, err, values_obj, "byteOffset", + true); - accessor->sparse.count = static_cast(count); - accessor->sparse.indices.bufferView = static_cast(indices_buffer_view); - accessor->sparse.indices.byteOffset = static_cast(indices_byte_offset); - accessor->sparse.indices.componentType = static_cast(component_type); - accessor->sparse.values.bufferView = static_cast(values_buffer_view); - accessor->sparse.values.byteOffset = static_cast(values_byte_offset); + accessor->sparse.count = count; + accessor->sparse.indices.bufferView = indices_buffer_view; + accessor->sparse.indices.byteOffset = indices_byte_offset; + accessor->sparse.indices.componentType = component_type; + accessor->sparse.values.bufferView = values_buffer_view; + accessor->sparse.values.byteOffset = values_byte_offset; // todo check theses values @@ -2908,23 +3030,23 @@ static bool ParseSparseAccessor(Accessor *accessor, std::string *err, } static bool ParseAccessor(Accessor *accessor, std::string *err, const json &o) { - double bufferView = -1.0; - ParseNumberProperty(&bufferView, err, o, "bufferView", false, "Accessor"); + int bufferView = -1; + ParseIntegerProperty(&bufferView, err, o, "bufferView", false, "Accessor"); - double byteOffset = 0.0; - ParseNumberProperty(&byteOffset, err, o, "byteOffset", false, "Accessor"); + size_t byteOffset = 0; + ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false, "Accessor"); bool normalized = false; ParseBooleanProperty(&normalized, err, o, "normalized", false, "Accessor"); - double componentType = 0.0; - if (!ParseNumberProperty(&componentType, err, o, "componentType", true, - "Accessor")) { + size_t componentType = 0; + if (!ParseUnsignedProperty(&componentType, err, o, "componentType", true, + "Accessor")) { return false; } - double count = 0.0; - if (!ParseNumberProperty(&count, err, o, "count", true, "Accessor")) { + size_t count = 0; + if (!ParseUnsignedProperty(&count, err, o, "count", true, "Accessor")) { return false; } @@ -2966,19 +3088,19 @@ static bool ParseAccessor(Accessor *accessor, std::string *err, const json &o) { ParseNumberArrayProperty(&accessor->maxValues, err, o, "max", false, "Accessor"); - accessor->count = static_cast(count); - accessor->bufferView = static_cast(bufferView); - accessor->byteOffset = static_cast(byteOffset); + accessor->count = count; + accessor->bufferView = bufferView; + accessor->byteOffset = byteOffset; accessor->normalized = normalized; { - int comp = static_cast(componentType); - if (comp >= TINYGLTF_COMPONENT_TYPE_BYTE && - comp <= TINYGLTF_COMPONENT_TYPE_DOUBLE) { + if (componentType >= TINYGLTF_COMPONENT_TYPE_BYTE && + componentType <= TINYGLTF_COMPONENT_TYPE_DOUBLE) { // OK - accessor->componentType = comp; + accessor->componentType = componentType; } else { std::stringstream ss; - ss << "Invalid `componentType` in accessor. Got " << comp << "\n"; + ss << "Invalid `componentType` in accessor. Got " << componentType + << "\n"; if (err) { (*err) += ss.str(); } @@ -3192,21 +3314,19 @@ static bool ParseDracoExtension(Primitive *primitive, Model *model, static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err, const json &o) { - double material = -1.0; - ParseNumberProperty(&material, err, o, "material", false); - primitive->material = static_cast(material); + int material = -1; + ParseIntegerProperty(&material, err, o, "material", false); + primitive->material = material; - double mode = static_cast(TINYGLTF_MODE_TRIANGLES); - ParseNumberProperty(&mode, err, o, "mode", false); + int mode = TINYGLTF_MODE_TRIANGLES; + ParseIntegerProperty(&mode, err, o, "mode", false); + primitive->mode = mode; // Why only triangled were supported ? - int primMode = static_cast(mode); - primitive->mode = primMode; // Why only triangled were supported ? - - double indices = -1.0; - ParseNumberProperty(&indices, err, o, "indices", false); - primitive->indices = static_cast(indices); - if (!ParseStringIntProperty(&primitive->attributes, err, o, "attributes", - true, "Primitive")) { + int indices = -1; + ParseIntegerProperty(&indices, err, o, "indices", false); + primitive->indices = indices; + if (!ParseStringIntegerProperty(&primitive->attributes, err, o, "attributes", + true, "Primitive")) { return false; } @@ -3299,9 +3419,9 @@ static bool ParseLight(Light *light, std::string *err, const json &o) { static bool ParseNode(Node *node, std::string *err, const json &o) { ParseStringProperty(&node->name, err, o, "name", false); - double skin = -1.0; - ParseNumberProperty(&skin, err, o, "skin", false); - node->skin = static_cast(skin); + int skin = -1; + ParseIntegerProperty(&skin, err, o, "skin", false); + node->skin = skin; // Matrix and T/R/S are exclusive if (!ParseNumberArrayProperty(&node->matrix, err, o, "matrix", false)) { @@ -3310,29 +3430,16 @@ static bool ParseNode(Node *node, std::string *err, const json &o) { ParseNumberArrayProperty(&node->translation, err, o, "translation", false); } - double camera = -1.0; - ParseNumberProperty(&camera, err, o, "camera", false); - node->camera = static_cast(camera); + int camera = -1; + ParseIntegerProperty(&camera, err, o, "camera", false); + node->camera = camera; - double mesh = -1.0; - ParseNumberProperty(&mesh, err, o, "mesh", false); - node->mesh = int(mesh); + int mesh = -1; + ParseIntegerProperty(&mesh, err, o, "mesh", false); + node->mesh = mesh; node->children.clear(); - json::const_iterator childrenObject = o.find("children"); - if ((childrenObject != o.end()) && childrenObject.value().is_array()) { - for (json::const_iterator i = childrenObject.value().begin(); - i != childrenObject.value().end(); i++) { - if (!i.value().is_number()) { - if (err) { - (*err) += "Invalid `children` array.\n"; - } - return false; - } - const int &childrenNode = static_cast(i.value()); - node->children.push_back(childrenNode); - } - } + ParseIntegerArrayProperty(&node->children, err, o, "children", false); ParseExtensionsProperty(&node->extensions, err, o); ParseExtrasProperty(&(node->extras), o); @@ -3383,10 +3490,10 @@ static bool ParseMaterial(Material *material, std::string *err, const json &o) { static bool ParseAnimationChannel(AnimationChannel *channel, std::string *err, const json &o) { - double samplerIndex = -1.0; - double targetIndex = -1.0; - if (!ParseNumberProperty(&samplerIndex, err, o, "sampler", true, - "AnimationChannel")) { + int samplerIndex = -1; + int targetIndex = -1; + if (!ParseIntegerProperty(&samplerIndex, err, o, "sampler", true, + "AnimationChannel")) { if (err) { (*err) += "`sampler` field is missing in animation channels\n"; } @@ -3397,7 +3504,7 @@ static bool ParseAnimationChannel(AnimationChannel *channel, std::string *err, if ((targetIt != o.end()) && targetIt.value().is_object()) { const json &target_object = targetIt.value(); - if (!ParseNumberProperty(&targetIndex, err, target_object, "node", true)) { + if (!ParseIntegerProperty(&targetIndex, err, target_object, "node", true)) { if (err) { (*err) += "`node` field is missing in animation.channels.target\n"; } @@ -3413,8 +3520,8 @@ static bool ParseAnimationChannel(AnimationChannel *channel, std::string *err, } } - channel->sampler = static_cast(samplerIndex); - channel->target_node = static_cast(targetIndex); + channel->sampler = samplerIndex; + channel->target_node = targetIndex; ParseExtrasProperty(&(channel->extras), o); @@ -3449,9 +3556,9 @@ static bool ParseAnimation(Animation *animation, std::string *err, const json &s = it->get(); AnimationSampler sampler; - double inputIndex = -1.0; - double outputIndex = -1.0; - if (!ParseNumberProperty(&inputIndex, err, s, "input", true)) { + int inputIndex = -1; + int outputIndex = -1; + if (!ParseIntegerProperty(&inputIndex, err, s, "input", true)) { if (err) { (*err) += "`input` field is missing in animation.sampler\n"; } @@ -3459,14 +3566,14 @@ static bool ParseAnimation(Animation *animation, std::string *err, } ParseStringProperty(&sampler.interpolation, err, s, "interpolation", false); - if (!ParseNumberProperty(&outputIndex, err, s, "output", true)) { + if (!ParseIntegerProperty(&outputIndex, err, s, "output", true)) { if (err) { (*err) += "`output` field is missing in animation.sampler\n"; } return false; } - sampler.input = static_cast(inputIndex); - sampler.output = static_cast(outputIndex); + sampler.input = inputIndex; + sampler.output = outputIndex; ParseExtrasProperty(&(sampler.extras), s); animation->samplers.push_back(sampler); } @@ -3483,20 +3590,19 @@ static bool ParseAnimation(Animation *animation, std::string *err, static bool ParseSampler(Sampler *sampler, std::string *err, const json &o) { ParseStringProperty(&sampler->name, err, o, "name", false); - double minFilter = - static_cast(TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR); - double magFilter = static_cast(TINYGLTF_TEXTURE_FILTER_LINEAR); - double wrapS = static_cast(TINYGLTF_TEXTURE_WRAP_REPEAT); - double wrapT = static_cast(TINYGLTF_TEXTURE_WRAP_REPEAT); - ParseNumberProperty(&minFilter, err, o, "minFilter", false); - ParseNumberProperty(&magFilter, err, o, "magFilter", false); - ParseNumberProperty(&wrapS, err, o, "wrapS", false); - ParseNumberProperty(&wrapT, err, o, "wrapT", false); + int minFilter = TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR; + int magFilter = TINYGLTF_TEXTURE_FILTER_LINEAR; + int wrapS = TINYGLTF_TEXTURE_WRAP_REPEAT; + int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT; + ParseIntegerProperty(&minFilter, err, o, "minFilter", false); + ParseIntegerProperty(&magFilter, err, o, "magFilter", false); + ParseIntegerProperty(&wrapS, err, o, "wrapS", false); + ParseIntegerProperty(&wrapT, err, o, "wrapT", false); - sampler->minFilter = static_cast(minFilter); - sampler->magFilter = static_cast(magFilter); - sampler->wrapS = static_cast(wrapS); - sampler->wrapT = static_cast(wrapT); + sampler->minFilter = minFilter; + sampler->magFilter = magFilter; + sampler->wrapS = wrapS; + sampler->wrapT = wrapT; ParseExtrasProperty(&(sampler->extras), o); @@ -3506,23 +3612,19 @@ static bool ParseSampler(Sampler *sampler, std::string *err, const json &o) { static bool ParseSkin(Skin *skin, std::string *err, const json &o) { ParseStringProperty(&skin->name, err, o, "name", false, "Skin"); - std::vector joints; - if (!ParseNumberArrayProperty(&joints, err, o, "joints", false, "Skin")) { + std::vector joints; + if (!ParseIntegerArrayProperty(&joints, err, o, "joints", false, "Skin")) { return false; } + skin->joints = std::move(joints); - double skeleton = -1.0; - ParseNumberProperty(&skeleton, err, o, "skeleton", false, "Skin"); - skin->skeleton = static_cast(skeleton); + int skeleton = -1; + ParseIntegerProperty(&skeleton, err, o, "skeleton", false, "Skin"); + skin->skeleton = skeleton; - skin->joints.resize(joints.size()); - for (size_t i = 0; i < joints.size(); i++) { - skin->joints[i] = static_cast(joints[i]); - } - - double invBind = -1.0; - ParseNumberProperty(&invBind, err, o, "inverseBindMatrices", true, "Skin"); - skin->inverseBindMatrices = static_cast(invBind); + int invBind = -1; + ParseIntegerProperty(&invBind, err, o, "inverseBindMatrices", true, "Skin"); + skin->inverseBindMatrices = invBind; return true; } @@ -3999,18 +4101,15 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, return false; } const json &o = it->get(); - std::vector nodes; - if (!ParseNumberArrayProperty(&nodes, err, o, "nodes", false)) { + std::vector nodes; + if (!ParseIntegerArrayProperty(&nodes, err, o, "nodes", false)) { return false; } Scene scene; + scene.nodes = std::move(nodes); + ParseStringProperty(&scene.name, err, o, "name", false); - std::vector nodesIds; - for (size_t i = 0; i < nodes.size(); i++) { - nodesIds.push_back(static_cast(nodes[i])); - } - scene.nodes = nodesIds; ParseExtensionsProperty(&scene.extensions, err, o); ParseExtrasProperty(&scene.extras, o); @@ -4023,10 +4122,10 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, // 9. Parse default scenes. { json::const_iterator rootIt = v.find("scene"); - if ((rootIt != v.end()) && rootIt.value().is_number()) { + if ((rootIt != v.end()) && rootIt.value().is_number_integer()) { const int defaultScene = rootIt.value(); - model->defaultScene = static_cast(defaultScene); + model->defaultScene = defaultScene; } }