From 95f05757d6e745b6f715299cebaae3d6eebd70c4 Mon Sep 17 00:00:00 2001 From: jrkoonce <30676875+jrkoonce@users.noreply.github.com> Date: Wed, 4 Sep 2019 12:15:51 -0500 Subject: [PATCH] Revert "Support simultaneous gltf load/saves (TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR)" This reverts commit ab63db63188375dfa4f4b70383365c9b3f0a85b4. --- loader_example.cc | 2 +- tests/tester.cc | 101 +- tiny_gltf - Copy.h | 6880 -------------------------------------------- tiny_gltf.h | 61 +- 4 files changed, 69 insertions(+), 6975 deletions(-) delete mode 100644 tiny_gltf - Copy.h diff --git a/loader_example.cc b/loader_example.cc index d1652bd..2adc498 100644 --- a/loader_example.cc +++ b/loader_example.cc @@ -4,7 +4,7 @@ #define TINYGLTF_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_WRITE_IMPLEMENTATION -#define TINYGLTF_USE_RAPIDJSON +//#define TINYGLTF_USE_RAPIDJSON #include "tiny_gltf.h" #include diff --git a/tests/tester.cc b/tests/tester.cc index 19e5018..83187d1 100644 --- a/tests/tester.cc +++ b/tests/tester.cc @@ -1,35 +1,33 @@ #define TINYGLTF_IMPLEMENTATION #define STB_IMAGE_IMPLEMENTATION #define STB_IMAGE_WRITE_IMPLEMENTATION -#define TINYGLTF_USE_RAPIDJSON -#define TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR #include "tiny_gltf.h" -#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do - // this in one cpp file +#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file #include "catch.hpp" -#include #include #include -#include +#include #include #include +#include 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; @@ -44,13 +42,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; } @@ -84,6 +82,7 @@ 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("invalid-primitive-indices", "[bounds-checking]") { @@ -138,13 +137,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, @@ -171,13 +167,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); } @@ -185,8 +181,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); } @@ -194,8 +189,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); } @@ -204,14 +199,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(); @@ -219,8 +214,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")); } } @@ -244,19 +240,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(); @@ -264,8 +260,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")); } } @@ -275,8 +272,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); @@ -301,27 +298,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()); @@ -331,4 +323,5 @@ TEST_CASE("pbr-khr-texture-transform", "[material]") { REQUIRE(scale[0] == Approx(1.0)); REQUIRE(scale[1] == Approx(-1.0)); + } diff --git a/tiny_gltf - Copy.h b/tiny_gltf - Copy.h deleted file mode 100644 index 68600d7..0000000 --- a/tiny_gltf - Copy.h +++ /dev/null @@ -1,6880 +0,0 @@ -// -// Header-only tiny glTF 2.0 loader and serializer. -// -// -// The MIT License (MIT) -// -// Copyright (c) 2015 - 2019 Syoyo Fujita, AurĂ©lien Chatelain and many -// contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - -// Version: -// - v2.3.1 Set default value of minFilter and magFilter in Sampler to -1. -// - v2.3.0 Modified Material representation according to glTF 2.0 schema -// (and introduced TextureInfo class) -// Change the behavior of `Value::IsNumber`. It return true either the -// value is int or real. -// - v2.2.0 Add loading 16bit PNG support. Add Sparse accessor support(Thanks -// to @Ybalrid) -// - v2.1.0 Add draco compression. -// - v2.0.1 Add comparsion feature(Thanks to @Selmar). -// - v2.0.0 glTF 2.0!. -// -// Tiny glTF loader is using following third party libraries: -// -// - jsonhpp: C++ JSON library. -// - base64: base64 decode/encode library. -// - stb_image: Image loading library. -// -#ifndef TINY_GLTF_H_ -#define TINY_GLTF_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef TINYGLTF_USE_CPP14 -#include -#endif - -#ifdef __ANDROID__ -#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS -#include -#endif -#endif - -namespace tinygltf { - -#define TINYGLTF_MODE_POINTS (0) -#define TINYGLTF_MODE_LINE (1) -#define TINYGLTF_MODE_LINE_LOOP (2) -#define TINYGLTF_MODE_LINE_STRIP (3) -#define TINYGLTF_MODE_TRIANGLES (4) -#define TINYGLTF_MODE_TRIANGLE_STRIP (5) -#define TINYGLTF_MODE_TRIANGLE_FAN (6) - -#define TINYGLTF_COMPONENT_TYPE_BYTE (5120) -#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE (5121) -#define TINYGLTF_COMPONENT_TYPE_SHORT (5122) -#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT (5123) -#define TINYGLTF_COMPONENT_TYPE_INT (5124) -#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125) -#define TINYGLTF_COMPONENT_TYPE_FLOAT (5126) -#define TINYGLTF_COMPONENT_TYPE_DOUBLE (5130) - -#define TINYGLTF_TEXTURE_FILTER_NEAREST (9728) -#define TINYGLTF_TEXTURE_FILTER_LINEAR (9729) -#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST (9984) -#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST (9985) -#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR (9986) -#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR (9987) - -#define TINYGLTF_TEXTURE_WRAP_REPEAT (10497) -#define TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE (33071) -#define TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT (33648) - -// Redeclarations of the above for technique.parameters. -#define TINYGLTF_PARAMETER_TYPE_BYTE (5120) -#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE (5121) -#define TINYGLTF_PARAMETER_TYPE_SHORT (5122) -#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT (5123) -#define TINYGLTF_PARAMETER_TYPE_INT (5124) -#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT (5125) -#define TINYGLTF_PARAMETER_TYPE_FLOAT (5126) - -#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC2 (35664) -#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC3 (35665) -#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC4 (35666) - -#define TINYGLTF_PARAMETER_TYPE_INT_VEC2 (35667) -#define TINYGLTF_PARAMETER_TYPE_INT_VEC3 (35668) -#define TINYGLTF_PARAMETER_TYPE_INT_VEC4 (35669) - -#define TINYGLTF_PARAMETER_TYPE_BOOL (35670) -#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC2 (35671) -#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC3 (35672) -#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC4 (35673) - -#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT2 (35674) -#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT3 (35675) -#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT4 (35676) - -#define TINYGLTF_PARAMETER_TYPE_SAMPLER_2D (35678) - -// End parameter types - -#define TINYGLTF_TYPE_VEC2 (2) -#define TINYGLTF_TYPE_VEC3 (3) -#define TINYGLTF_TYPE_VEC4 (4) -#define TINYGLTF_TYPE_MAT2 (32 + 2) -#define TINYGLTF_TYPE_MAT3 (32 + 3) -#define TINYGLTF_TYPE_MAT4 (32 + 4) -#define TINYGLTF_TYPE_SCALAR (64 + 1) -#define TINYGLTF_TYPE_VECTOR (64 + 4) -#define TINYGLTF_TYPE_MATRIX (64 + 16) - -#define TINYGLTF_IMAGE_FORMAT_JPEG (0) -#define TINYGLTF_IMAGE_FORMAT_PNG (1) -#define TINYGLTF_IMAGE_FORMAT_BMP (2) -#define TINYGLTF_IMAGE_FORMAT_GIF (3) - -#define TINYGLTF_TEXTURE_FORMAT_ALPHA (6406) -#define TINYGLTF_TEXTURE_FORMAT_RGB (6407) -#define TINYGLTF_TEXTURE_FORMAT_RGBA (6408) -#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE (6409) -#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE_ALPHA (6410) - -#define TINYGLTF_TEXTURE_TARGET_TEXTURE2D (3553) -#define TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE (5121) - -#define TINYGLTF_TARGET_ARRAY_BUFFER (34962) -#define TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER (34963) - -#define TINYGLTF_SHADER_TYPE_VERTEX_SHADER (35633) -#define TINYGLTF_SHADER_TYPE_FRAGMENT_SHADER (35632) - -#define TINYGLTF_DOUBLE_EPS (1.e-12) -#define TINYGLTF_DOUBLE_EQUAL(a, b) (std::fabs((b) - (a)) < TINYGLTF_DOUBLE_EPS) - -#ifdef __ANDROID__ -#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS -AAssetManager *asset_manager = nullptr; -#endif -#endif - -typedef enum { - NULL_TYPE = 0, - REAL_TYPE = 1, - INT_TYPE = 2, - BOOL_TYPE = 3, - STRING_TYPE = 4, - ARRAY_TYPE = 5, - BINARY_TYPE = 6, - OBJECT_TYPE = 7 -} Type; - -static inline int32_t GetComponentSizeInBytes(uint32_t componentType) { - if (componentType == TINYGLTF_COMPONENT_TYPE_BYTE) { - return 1; - } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) { - return 1; - } else if (componentType == TINYGLTF_COMPONENT_TYPE_SHORT) { - return 2; - } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) { - return 2; - } else if (componentType == TINYGLTF_COMPONENT_TYPE_INT) { - return 4; - } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) { - return 4; - } else if (componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) { - return 4; - } else if (componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) { - return 8; - } else { - // Unknown componenty type - return -1; - } -} - -static inline int32_t GetNumComponentsInType(uint32_t ty) { - if (ty == TINYGLTF_TYPE_SCALAR) { - return 1; - } else if (ty == TINYGLTF_TYPE_VEC2) { - return 2; - } else if (ty == TINYGLTF_TYPE_VEC3) { - return 3; - } else if (ty == TINYGLTF_TYPE_VEC4) { - return 4; - } else if (ty == TINYGLTF_TYPE_MAT2) { - return 4; - } else if (ty == TINYGLTF_TYPE_MAT3) { - return 9; - } else if (ty == TINYGLTF_TYPE_MAT4) { - return 16; - } else { - // Unknown componenty type - return -1; - } -} - -// TODO(syoyo): Move these functions to TinyGLTF class -bool IsDataURI(const std::string &in); -bool DecodeDataURI(std::vector *out, std::string &mime_type, - const std::string &in, size_t reqBytes, bool checkSize); - -#ifdef __clang__ -#pragma clang diagnostic push -// Suppress warning for : static Value null_value -// https://stackoverflow.com/questions/15708411/how-to-deal-with-global-constructor-warning-in-clang -#pragma clang diagnostic ignored "-Wexit-time-destructors" -#pragma clang diagnostic ignored "-Wpadded" -#endif - -// Simple class to represent JSON object -class Value { - public: - typedef std::vector Array; - typedef std::map Object; - - Value() - : type_(NULL_TYPE), - int_value_(0), - real_value_(0.0), - boolean_value_(false) {} - - explicit Value(bool b) : type_(BOOL_TYPE) { boolean_value_ = b; } - explicit Value(int i) : type_(INT_TYPE) { - int_value_ = i; - real_value_ = i; - } - explicit Value(double n) : type_(REAL_TYPE) { real_value_ = n; } - explicit Value(const std::string &s) : type_(STRING_TYPE) { - string_value_ = s; - } - explicit Value(std::string &&s) - : type_(STRING_TYPE), string_value_(std::move(s)) {} - explicit Value(const unsigned char *p, size_t n) : type_(BINARY_TYPE) { - binary_value_.resize(n); - memcpy(binary_value_.data(), p, n); - } - explicit Value(std::vector &&v) noexcept - : type_(BINARY_TYPE), - binary_value_(std::move(v)) {} - explicit Value(const Array &a) : type_(ARRAY_TYPE) { array_value_ = a; } - explicit Value(Array &&a) noexcept : type_(ARRAY_TYPE), - array_value_(std::move(a)) {} - - explicit Value(const Object &o) : type_(OBJECT_TYPE) { object_value_ = o; } - explicit Value(Object &&o) noexcept : type_(OBJECT_TYPE), - object_value_(std::move(o)) {} - Value(Value &&rhs) noexcept : type_(rhs.type_), - int_value_(rhs.int_value_), - real_value_(rhs.real_value_), - string_value_(std::move(rhs.string_value_)), - binary_value_(std::move(rhs.binary_value_)), - array_value_(std::move(rhs.array_value_)), - object_value_(std::move(rhs.object_value_)), - boolean_value_(rhs.boolean_value_) {} - Value(const Value &rhs) = default; - Value &operator=(const Value &rhs) = default; - Value &operator=(Value &&rhs) { - if (this != &rhs) { - this->~Value(); - new (reinterpret_cast(this)) Value(std::move(rhs)); - } - return *this; - } - - char Type() const { return static_cast(type_); } - - bool IsBool() const { return (type_ == BOOL_TYPE); } - - bool IsInt() const { return (type_ == INT_TYPE); } - - bool IsNumber() const { return (type_ == REAL_TYPE) || (type_ == INT_TYPE); } - - bool IsReal() const { return (type_ == REAL_TYPE); } - - bool IsString() const { return (type_ == STRING_TYPE); } - - bool IsBinary() const { return (type_ == BINARY_TYPE); } - - bool IsArray() const { return (type_ == ARRAY_TYPE); } - - bool IsObject() const { return (type_ == OBJECT_TYPE); } - - // Use this function if you want to have number value as double. - double GetNumberAsDouble() const { - if (type_ == INT_TYPE) { - return double(int_value_); - } else { - return real_value_; - } - } - - // Use this function if you want to have number value as int. - double GetNumberAsInt() const { - if (type_ == REAL_TYPE) { - return int(real_value_); - } else { - return int_value_; - } - } - - // Accessor - template - const T &Get() const; - template - T &Get(); - - // Lookup value from an array - const Value &Get(int idx) const { - static Value null_value; - assert(IsArray()); - assert(idx >= 0); - return (static_cast(idx) < array_value_.size()) - ? array_value_[static_cast(idx)] - : null_value; - } - - // Lookup value from a key-value pair - const Value &Get(const std::string &key) const { - static Value null_value; - assert(IsObject()); - Object::const_iterator it = object_value_.find(key); - return (it != object_value_.end()) ? it->second : null_value; - } - - size_t ArrayLen() const { - if (!IsArray()) return 0; - return array_value_.size(); - } - - // Valid only for object type. - bool Has(const std::string &key) const { - if (!IsObject()) return false; - Object::const_iterator it = object_value_.find(key); - return (it != object_value_.end()) ? true : false; - } - - // List keys - std::vector Keys() const { - std::vector keys; - if (!IsObject()) return keys; // empty - - for (Object::const_iterator it = object_value_.begin(); - it != object_value_.end(); ++it) { - keys.push_back(it->first); - } - - return keys; - } - - size_t Size() const { return (IsArray() ? ArrayLen() : Keys().size()); } - - bool operator==(const tinygltf::Value &other) const; - - protected: - int type_ = NULL_TYPE; - - int int_value_ = 0; - double real_value_ = 0.0; - std::string string_value_; - std::vector binary_value_; - Array array_value_; - Object object_value_; - bool boolean_value_ = false; -}; - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -#define TINYGLTF_VALUE_GET(ctype, var) \ - template <> \ - inline const ctype &Value::Get() const { \ - return var; \ - } \ - template <> \ - inline ctype &Value::Get() { \ - return var; \ - } -TINYGLTF_VALUE_GET(bool, boolean_value_) -TINYGLTF_VALUE_GET(double, real_value_) -TINYGLTF_VALUE_GET(int, int_value_) -TINYGLTF_VALUE_GET(std::string, string_value_) -TINYGLTF_VALUE_GET(std::vector, binary_value_) -TINYGLTF_VALUE_GET(Value::Array, array_value_) -TINYGLTF_VALUE_GET(Value::Object, object_value_) -#undef TINYGLTF_VALUE_GET - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wc++98-compat" -#pragma clang diagnostic ignored "-Wpadded" -#endif - -/// Agregate object for representing a color -using ColorValue = std::array; - -// === legacy interface ==== -// TODO(syoyo): Deprecate `Parameter` class. -struct Parameter { - bool bool_value = false; - bool has_number_value = false; - std::string string_value; - std::vector number_array; - std::map json_double_value; - double number_value = 0.0; - - // context sensitive methods. depending the type of the Parameter you are - // accessing, these are either valid or not - // If this parameter represent a texture map in a material, will return the - // texture index - - /// Return the index of a texture if this Parameter is a texture map. - /// Returned value is only valid if the parameter represent a texture from a - /// material - int TextureIndex() const { - const auto it = json_double_value.find("index"); - if (it != std::end(json_double_value)) { - return int(it->second); - } - return -1; - } - - /// Return the index of a texture coordinate set if this Parameter is a - /// texture map. Returned value is only valid if the parameter represent a - /// texture from a material - int TextureTexCoord() const { - const auto it = json_double_value.find("texCoord"); - if (it != std::end(json_double_value)) { - return int(it->second); - } - // As per the spec, if texCoord is ommited, this parameter is 0 - return 0; - } - - /// Return the scale of a texture if this Parameter is a normal texture map. - /// Returned value is only valid if the parameter represent a normal texture - /// from a material - double TextureScale() const { - const auto it = json_double_value.find("scale"); - if (it != std::end(json_double_value)) { - return it->second; - } - // As per the spec, if scale is ommited, this paramter is 1 - return 1; - } - - /// Return the strength of a texture if this Parameter is a an occlusion map. - /// Returned value is only valid if the parameter represent an occlusion map - /// from a material - double TextureStrength() const { - const auto it = json_double_value.find("strength"); - if (it != std::end(json_double_value)) { - return it->second; - } - // As per the spec, if strenghth is ommited, this parameter is 1 - return 1; - } - - /// Material factor, like the roughness or metalness of a material - /// Returned value is only valid if the parameter represent a texture from a - /// material - double Factor() const { return number_value; } - - /// Return the color of a material - /// Returned value is only valid if the parameter represent a texture from a - /// material - ColorValue ColorFactor() const { - return { - {// this agregate intialize the std::array object, and uses C++11 RVO. - number_array[0], number_array[1], number_array[2], - (number_array.size() > 3 ? number_array[3] : 1.0)}}; - } - - bool operator==(const Parameter &) const; -}; - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wpadded" -#endif - -typedef std::map ParameterMap; -typedef std::map ExtensionMap; - -struct AnimationChannel { - int sampler; // required - int target_node; // required (index of the node to target) - std::string target_path; // required in ["translation", "rotation", "scale", - // "weights"] - Value extras; - ExtensionMap extensions; - - AnimationChannel() : sampler(-1), target_node(-1) {} - bool operator==(const AnimationChannel &) const; -}; - -struct AnimationSampler { - int input; // required - int output; // required - std::string interpolation; // "LINEAR", "STEP","CUBICSPLINE" or user defined - // string. default "LINEAR" - Value extras; - - AnimationSampler() : input(-1), output(-1), interpolation("LINEAR") {} - bool operator==(const AnimationSampler &) const; -}; - -struct Animation { - std::string name; - std::vector channels; - std::vector samplers; - Value extras; - ExtensionMap extensions; - - bool operator==(const Animation &) const; -}; - -struct Skin { - std::string name; - int inverseBindMatrices; // required here but not in the spec - int skeleton; // The index of the node used as a skeleton root - std::vector joints; // Indices of skeleton nodes - - Skin() { - inverseBindMatrices = -1; - skeleton = -1; - } - bool operator==(const Skin &) const; -}; - -struct Sampler { - std::string name; - // glTF 2.0 spec does not define default value for `minFilter` and - // `magFilter`. Set -1 in TinyGLTF(issue #186) - int minFilter = - -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR", - // "NEAREST_MIPMAP_LINEAR", "LINEAR_MIPMAP_NEAREST", - // "NEAREST_MIPMAP_LINEAR", "LINEAR_MIPMAP_LINEAR"] - int magFilter = - -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR"] - int wrapS = - TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT", - // "REPEAT"], default "REPEAT" - int wrapT = - TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT", - // "REPEAT"], default "REPEAT" - int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; // TinyGLTF extension - Value extras; - - Sampler() - : minFilter(-1), - magFilter(-1), - wrapS(TINYGLTF_TEXTURE_WRAP_REPEAT), - wrapT(TINYGLTF_TEXTURE_WRAP_REPEAT), - wrapR(TINYGLTF_TEXTURE_WRAP_REPEAT) {} - Sampler(const Sampler &) = default; - Sampler(Sampler &&rhs) noexcept : name(std::move(rhs.name)), - minFilter(rhs.minFilter), - magFilter(rhs.magFilter), - wrapS(rhs.wrapS), - wrapT(rhs.wrapT), - wrapR(rhs.wrapR), - extras(std::move(rhs.extras)) {} - bool operator==(const Sampler &) const; -}; - -struct Image { - std::string name; - int width; - int height; - int component; - int bits; // bit depth per channel. 8(byte), 16 or 32. - int pixel_type; // pixel type(TINYGLTF_COMPONENT_TYPE_***). usually - // UBYTE(bits = 8) or USHORT(bits = 16) - std::vector image; - int bufferView; // (required if no uri) - std::string mimeType; // (required if no uri) ["image/jpeg", "image/png", - // "image/bmp", "image/gif"] - std::string uri; // (required if no mimeType) - Value extras; - ExtensionMap extensions; - - // When this flag is true, data is stored to `image` in as-is format(e.g. jpeg - // compressed for "image/jpeg" mime) This feature is good if you use custom - // image loader function. (e.g. delayed decoding of images for faster glTF - // parsing) Default parser for Image does not provide as-is loading feature at - // the moment. (You can manipulate this by providing your own LoadImageData - // function) - bool as_is; - - Image() : as_is(false) { - bufferView = -1; - width = -1; - height = -1; - component = -1; - } - Image(const Image &) = default; - Image(Image &&rhs) noexcept : name(std::move(rhs.name)), - width(rhs.width), - height(rhs.height), - component(rhs.component), - bits(rhs.bits), - pixel_type(rhs.pixel_type), - image(std::move(rhs.image)), - bufferView(rhs.bufferView), - mimeType(std::move(rhs.mimeType)), - uri(std::move(rhs.uri)), - extras(std::move(rhs.extras)), - extensions(std::move(rhs.extensions)), - as_is(rhs.as_is) {} - - bool operator==(const Image &) const; -}; - -struct Texture { - std::string name; - - int sampler; - int source; - Value extras; - ExtensionMap extensions; - - Texture() : sampler(-1), source(-1) {} - Texture(const Texture &) = default; - Texture(Texture &&rhs) noexcept : name(std::move(rhs.name)), - sampler(rhs.sampler), - source(rhs.source), - extras(std::move(rhs.extras)), - extensions(std::move(rhs.extensions)) {} - - bool operator==(const Texture &) const; -}; - -struct TextureInfo { - int index = -1; // required. - int texCoord; // The set index of texture's TEXCOORD attribute used for - // texture coordinate mapping. - - Value extras; - ExtensionMap extensions; - - TextureInfo() : index(-1), texCoord(0) {} - TextureInfo(const TextureInfo &) = default; - TextureInfo(TextureInfo &&rhs) noexcept - : index(rhs.index), - texCoord(rhs.texCoord), - extras(std::move(rhs.extras)), - extensions(std::move(rhs.extensions)) {} - bool operator==(const TextureInfo &) const; -}; - -struct NormalTextureInfo { - int index = -1; // required - int texCoord; // The set index of texture's TEXCOORD attribute used for - // texture coordinate mapping. - double scale; // scaledNormal = normalize(( - // * 2.0 - 1.0) * vec3(, , 1.0)) - - Value extras; - ExtensionMap extensions; - - NormalTextureInfo() : index(-1), texCoord(0), scale(1.0) {} - NormalTextureInfo(const NormalTextureInfo &) = default; - NormalTextureInfo(NormalTextureInfo &&rhs) noexcept - : index(rhs.index), - texCoord(rhs.texCoord), - scale(rhs.scale), - extras(std::move(rhs.extras)), - extensions(std::move(rhs.extensions)) {} - bool operator==(const NormalTextureInfo &) const; -}; - -struct OcclusionTextureInfo { - int index = -1; // required - int texCoord; // The set index of texture's TEXCOORD attribute used for - // texture coordinate mapping. - double strength; // occludedColor = lerp(color, color * , ) - - Value extras; - ExtensionMap extensions; - - OcclusionTextureInfo() : index(-1), texCoord(0), strength(1.0) {} - OcclusionTextureInfo(const OcclusionTextureInfo &) = default; - OcclusionTextureInfo(OcclusionTextureInfo &&rhs) noexcept - : index(rhs.index), - texCoord(rhs.texCoord), - strength(rhs.strength), - extras(std::move(rhs.extras)), - extensions(std::move(rhs.extensions)) {} - bool operator==(const OcclusionTextureInfo &) const; -}; - -// pbrMetallicRoughness class defined in glTF 2.0 spec. -struct PbrMetallicRoughness { - std::vector baseColorFactor; // len = 4. default [1,1,1,1] - TextureInfo baseColorTexture; - double metallicFactor; // default 1 - double roughnessFactor; // default 1 - TextureInfo metallicRoughnessTexture; - - Value extras; - ExtensionMap extensions; - - PbrMetallicRoughness() : metallicFactor(1.0), roughnessFactor(1.0) {} - PbrMetallicRoughness(const PbrMetallicRoughness &) = default; - PbrMetallicRoughness(PbrMetallicRoughness &&rhs) noexcept - : baseColorFactor(std::move(rhs.baseColorFactor)), - baseColorTexture(std::move(rhs.baseColorTexture)), - metallicFactor(rhs.metallicFactor), - roughnessFactor(rhs.roughnessFactor), - metallicRoughnessTexture(std::move(rhs.metallicRoughnessTexture)), - extras(std::move(rhs.extras)), - extensions(std::move(rhs.extensions)) {} - bool operator==(const PbrMetallicRoughness &) const; -}; - -// Each extension should be stored in a ParameterMap. -// members not in the values could be included in the ParameterMap -// to keep a single material model -struct Material { - std::string name; - - std::vector emissiveFactor; // length 3. default [0, 0, 0] - std::string alphaMode; // default "OPAQUE" - double alphaCutoff; // default 0.5 - bool doubleSided; // default false; - - PbrMetallicRoughness pbrMetallicRoughness; - - NormalTextureInfo normalTexture; - OcclusionTextureInfo occlusionTexture; - TextureInfo emissiveTexture; - - // For backward compatibility - // TODO(syoyo): Remove `values` and `additionalValues` in the next release. - ParameterMap values; - ParameterMap additionalValues; - - ExtensionMap extensions; - Value extras; - - Material() : alphaMode("OPAQUE"), alphaCutoff(0.5), doubleSided(false) {} - Material(const Material &) = default; - Material(Material &&rhs) noexcept - : name(std::move(rhs.name)), - emissiveFactor(std::move(rhs.emissiveFactor)), - alphaMode(std::move(rhs.alphaMode)), - alphaCutoff(rhs.alphaCutoff), - doubleSided(rhs.doubleSided), - pbrMetallicRoughness(std::move(rhs.pbrMetallicRoughness)), - normalTexture(std::move(rhs.normalTexture)), - occlusionTexture(std::move(rhs.occlusionTexture)), - emissiveTexture(std::move(rhs.emissiveTexture)), - values(std::move(rhs.values)), - additionalValues(std::move(rhs.additionalValues)), - extensions(std::move(rhs.extensions)), - extras(std::move(rhs.extras)) {} - - bool operator==(const Material &) const; -}; - -struct BufferView { - std::string name; - int buffer; // Required - size_t byteOffset; // minimum 0, default 0 - size_t byteLength; // required, minimum 1 - size_t byteStride; // minimum 4, maximum 252 (multiple of 4), default 0 = - // understood to be tightly packed - int target; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"] - Value extras; - bool dracoDecoded; // Flag indicating this has been draco decoded - - BufferView() : byteOffset(0), byteStride(0), dracoDecoded(false) {} - BufferView(const BufferView &) = default; - BufferView(BufferView &&rhs) noexcept : name(std::move(rhs.name)), - buffer(rhs.buffer), - byteOffset(rhs.byteOffset), - byteLength(rhs.byteLength), - byteStride(rhs.byteStride), - target(rhs.target), - extras(std::move(rhs.extras)), - dracoDecoded(rhs.dracoDecoded) {} - bool operator==(const BufferView &) const; -}; - -struct Accessor { - int bufferView; // optional in spec but required here since sparse accessor - // are not supported - std::string name; - size_t byteOffset; - bool normalized; // optional. - int componentType; // (required) One of TINYGLTF_COMPONENT_TYPE_*** - size_t count; // required - int type; // (required) One of TINYGLTF_TYPE_*** .. - Value extras; - - std::vector minValues; // optional - std::vector maxValues; // optional - - struct { - int count; - bool isSparse; - struct { - int byteOffset; - int bufferView; - int componentType; // a TINYGLTF_COMPONENT_TYPE_ value - } indices; - struct { - int bufferView; - int byteOffset; - } values; - } sparse; - - /// - /// Utility function to compute byteStride for a given bufferView object. - /// Returns -1 upon invalid glTF value or parameter configuration. - /// - int ByteStride(const BufferView &bufferViewObject) const { - if (bufferViewObject.byteStride == 0) { - // Assume data is tightly packed. - int componentSizeInBytes = - GetComponentSizeInBytes(static_cast(componentType)); - if (componentSizeInBytes <= 0) { - return -1; - } - - int numComponents = GetNumComponentsInType(static_cast(type)); - if (numComponents <= 0) { - return -1; - } - - return componentSizeInBytes * numComponents; - } else { - // Check if byteStride is a mulple of the size of the accessor's component - // type. - int componentSizeInBytes = - GetComponentSizeInBytes(static_cast(componentType)); - if (componentSizeInBytes <= 0) { - return -1; - } - - if ((bufferViewObject.byteStride % uint32_t(componentSizeInBytes)) != 0) { - return -1; - } - return static_cast(bufferViewObject.byteStride); - } - - return 0; - } - - Accessor() { - bufferView = -1; - sparse.isSparse = false; - } - Accessor(const Accessor &) = default; - Accessor(Accessor &&rhs) noexcept : bufferView(rhs.bufferView), - name(std::move(rhs.name)), - byteOffset(rhs.byteOffset), - normalized(rhs.normalized), - componentType(rhs.componentType), - count(rhs.count), - type(rhs.type), - extras(std::move(rhs.extras)), - minValues(std::move(rhs.minValues)), - maxValues(std::move(rhs.maxValues)), - sparse(rhs.sparse) {} - bool operator==(const tinygltf::Accessor &) const; -}; - -struct PerspectiveCamera { - double aspectRatio; // min > 0 - double yfov; // required. min > 0 - double zfar; // min > 0 - double znear; // required. min > 0 - - PerspectiveCamera() - : aspectRatio(0.0), - yfov(0.0), - zfar(0.0) // 0 = use infinite projecton matrix - , - znear(0.0) {} - PerspectiveCamera(const PerspectiveCamera &) = default; - PerspectiveCamera(PerspectiveCamera &&rhs) noexcept - : aspectRatio(rhs.aspectRatio), - yfov(rhs.yfov), - zfar(rhs.zfar), - znear(rhs.znear), - extensions(std::move(rhs.extensions)), - extras(std::move(rhs.extras)) {} - bool operator==(const PerspectiveCamera &) const; - - ExtensionMap extensions; - Value extras; -}; - -struct OrthographicCamera { - double xmag; // required. must not be zero. - double ymag; // required. must not be zero. - double zfar; // required. `zfar` must be greater than `znear`. - double znear; // required - - OrthographicCamera() : xmag(0.0), ymag(0.0), zfar(0.0), znear(0.0) {} - OrthographicCamera(const OrthographicCamera &) = default; - OrthographicCamera(OrthographicCamera &&rhs) noexcept - : xmag(rhs.xmag), - ymag(rhs.ymag), - zfar(rhs.zfar), - znear(rhs.znear), - extensions(std::move(rhs.extensions)), - extras(std::move(rhs.extras)) {} - bool operator==(const OrthographicCamera &) const; - - ExtensionMap extensions; - Value extras; -}; - -struct Camera { - std::string type; // required. "perspective" or "orthographic" - std::string name; - - PerspectiveCamera perspective; - OrthographicCamera orthographic; - - Camera() {} - Camera(const Camera &) = default; - Camera(Camera &&rhs) noexcept : type(std::move(rhs.type)), - name(std::move(rhs.name)), - perspective(std::move(rhs.perspective)), - orthographic(std::move(rhs.orthographic)), - extensions(std::move(rhs.extensions)), - extras(std::move(rhs.extras)) {} - bool operator==(const Camera &) const; - - ExtensionMap extensions; - Value extras; -}; - -struct Primitive { - std::map attributes; // (required) A dictionary object of - // integer, where each integer - // is the index of the accessor - // containing an attribute. - int material; // The index of the material to apply to this primitive - // when rendering. - int indices; // The index of the accessor that contains the indices. - int mode; // one of TINYGLTF_MODE_*** - std::vector > targets; // array of morph targets, - // where each target is a dict with attribues in ["POSITION, "NORMAL", - // "TANGENT"] pointing - // to their corresponding accessors - ExtensionMap extensions; - Value extras; - - Primitive() { - material = -1; - indices = -1; - } - Primitive(const Primitive &) = default; - Primitive(Primitive &&rhs) noexcept : attributes(std::move(rhs.attributes)), - material(rhs.material), - indices(rhs.indices), - mode(rhs.mode), - targets(std::move(rhs.targets)), - extensions(std::move(rhs.extensions)), - extras(std::move(rhs.extras)) {} - bool operator==(const Primitive &) const; -}; - -struct Mesh { - std::string name; - std::vector primitives; - std::vector weights; // weights to be applied to the Morph Targets - ExtensionMap extensions; - Value extras; - - Mesh() = default; - ~Mesh() = default; - Mesh(const Mesh &) = default; - Mesh(Mesh &&rhs) noexcept : name(std::move(rhs.name)), - primitives(std::move(rhs.primitives)), - weights(std::move(rhs.weights)), - extensions(std::move(rhs.extensions)), - extras(std::move(rhs.extras)) {} - Mesh &operator=(const Mesh &) = default; - Mesh &operator=(Mesh &&rhs) { - if (&rhs != this) { - this->~Mesh(); - new (reinterpret_cast(this)) Mesh(std::move(rhs)); - } - return *this; - } - bool operator==(const Mesh &) const; -}; - -class Node { - public: - Node() : camera(-1), skin(-1), mesh(-1) {} - - // TODO(syoyo): Could use `default` - Node(const Node &rhs) { - camera = rhs.camera; - - name = rhs.name; - skin = rhs.skin; - mesh = rhs.mesh; - children = rhs.children; - rotation = rhs.rotation; - scale = rhs.scale; - translation = rhs.translation; - matrix = rhs.matrix; - weights = rhs.weights; - - extensions = rhs.extensions; - extras = rhs.extras; - } - Node(Node &&rhs) noexcept : camera(rhs.camera), - name(std::move(rhs.name)), - skin(rhs.skin), - mesh(rhs.mesh), - children(std::move(rhs.children)), - rotation(std::move(rhs.rotation)), - scale(std::move(rhs.scale)), - translation(std::move(rhs.translation)), - matrix(std::move(rhs.matrix)), - weights(std::move(rhs.weights)), - extensions(std::move(rhs.extensions)), - extras(std::move(rhs.extras)) {} - ~Node() {} - - Node &operator=(const Node &rhs) = default; - Node &operator=(Node &&rhs) { - if (&rhs != this) { - this->~Node(); - new (reinterpret_cast(this)) Node(std::move(rhs)); - } - return *this; - } - - bool operator==(const Node &) const; - - int camera; // the index of the camera referenced by this node - - std::string name; - int skin; - int mesh; - std::vector children; - std::vector rotation; // length must be 0 or 4 - std::vector scale; // length must be 0 or 3 - std::vector translation; // length must be 0 or 3 - std::vector matrix; // length must be 0 or 16 - std::vector weights; // The weights of the instantiated Morph Target - - ExtensionMap extensions; - Value extras; -}; - -struct Buffer { - Buffer() = default; - Buffer(Buffer &&rhs) noexcept : name(std::move(rhs.name)), - data(std::move(rhs.data)), - uri(std::move(rhs.uri)), - extras(std::move(rhs.extras)) {} - std::string name; - std::vector data; - std::string - uri; // considered as required here but not in the spec (need to clarify) - Value extras; - - bool operator==(const Buffer &) const; -}; - -struct Asset { - std::string version; // required - std::string generator; - std::string minVersion; - std::string copyright; - ExtensionMap extensions; - Value extras; - - Asset() = default; - ~Asset() = default; - Asset(const Asset &) = default; - Asset(Asset &&rhs) noexcept : version(std::move(rhs.version)), - generator(std::move(rhs.generator)), - minVersion(std::move(rhs.minVersion)), - copyright(std::move(rhs.copyright)), - extensions(std::move(rhs.extensions)), - extras(std::move(rhs.extras)) {} - Asset &operator=(const Asset &) = default; - Asset &operator=(Asset &&rhs) { - if (&rhs != this) { - this->~Asset(); - new (reinterpret_cast(this)) Asset(std::move(rhs)); - } - return *this; - } - bool operator==(const Asset &) const; -}; - -struct Scene { - std::string name; - std::vector nodes; - - ExtensionMap extensions; - Value extras; - - Scene() = default; - Scene(const Scene &) = default; - Scene(Scene &&rhs) noexcept : name(std::move(rhs.name)), - nodes(std::move(rhs.nodes)), - extensions(std::move(rhs.extensions)), - extras(std::move(rhs.extras)) {} - bool operator==(const Scene &) const; -}; - -struct SpotLight { - double innerConeAngle; - double outerConeAngle; - - SpotLight() : innerConeAngle(0.0), outerConeAngle(0.7853981634) {} - SpotLight(const SpotLight &) = default; - SpotLight(SpotLight &&rhs) noexcept : innerConeAngle(rhs.innerConeAngle), - outerConeAngle(rhs.outerConeAngle), - extensions(std::move(rhs.extensions)), - extras(std::move(rhs.extras)) {} - - bool operator==(const SpotLight &) const; - - ExtensionMap extensions; - Value extras; -}; - -struct Light { - std::string name; - std::vector color; - double intensity; - std::string type; - double range; - SpotLight spot; - - Light() : intensity(1.0), range(0.0) {} - Light(const Light &) = default; - Light(Light &&rhs) noexcept : name(std::move(rhs.name)), - color(std::move(rhs.color)), - intensity(rhs.intensity), - type(std::move(rhs.type)), - range(rhs.range), - spot(std::move(rhs.spot)), - extensions(std::move(rhs.extensions)), - extras(std::move(rhs.extras)) {} - - bool operator==(const Light &) const; - - ExtensionMap extensions; - Value extras; -}; - -class Model { - public: - Model() {} - - Model(const Model &) = default; - Model &operator=(const Model &) = default; - Model(Model &&rhs) noexcept - : accessors(std::move(rhs.accessors)), - animations(std::move(rhs.animations)), - buffers(std::move(rhs.buffers)), - bufferViews(std::move(rhs.bufferViews)), - materials(std::move(rhs.materials)), - meshes(std::move(rhs.meshes)), - nodes(std::move(rhs.nodes)), - textures(std::move(rhs.textures)), - images(std::move(rhs.images)), - skins(std::move(rhs.skins)), - samplers(std::move(rhs.samplers)), - cameras(std::move(rhs.cameras)), - scenes(std::move(rhs.scenes)), - lights(std::move(rhs.lights)), - extensions(std::move(rhs.extensions)), - defaultScene(rhs.defaultScene), - extensionsUsed(std::move(rhs.extensionsUsed)), - extensionsRequired(std::move(rhs.extensionsRequired)), - asset(std::move(rhs.asset)), - extras(std::move(rhs.extras)) {} - - ~Model() {} - - bool operator==(const Model &) const; - - std::vector accessors; - std::vector animations; - std::vector buffers; - std::vector bufferViews; - std::vector materials; - std::vector meshes; - std::vector nodes; - std::vector textures; - std::vector images; - std::vector skins; - std::vector samplers; - std::vector cameras; - std::vector scenes; - std::vector lights; - ExtensionMap extensions; - - int defaultScene; - std::vector extensionsUsed; - std::vector extensionsRequired; - - Asset asset; - - Value extras; -}; - -enum SectionCheck { - NO_REQUIRE = 0x00, - REQUIRE_VERSION = 0x01, - REQUIRE_SCENE = 0x02, - REQUIRE_SCENES = 0x04, - REQUIRE_NODES = 0x08, - REQUIRE_ACCESSORS = 0x10, - REQUIRE_BUFFERS = 0x20, - REQUIRE_BUFFER_VIEWS = 0x40, - REQUIRE_ALL = 0x7f -}; - -/// -/// LoadImageDataFunction type. Signature for custom image loading callbacks. -/// -typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *, - std::string *, int, int, - const unsigned char *, int, void *); - -/// -/// WriteImageDataFunction type. Signature for custom image writing callbacks. -/// -typedef bool (*WriteImageDataFunction)(const std::string *, const std::string *, - Image *, bool, void *); - -#ifndef TINYGLTF_NO_STB_IMAGE -// Declaration of default image loader callback -bool LoadImageData(Image *image, const int image_idx, std::string *err, - std::string *warn, int req_width, int req_height, - const unsigned char *bytes, int size, void *); -#endif - -#ifndef TINYGLTF_NO_STB_IMAGE_WRITE -// Declaration of default image writer callback -bool WriteImageData(const std::string *basepath, const std::string *filename, - Image *image, bool embedImages, void *); -#endif - -/// -/// FilExistsFunction type. Signature for custom filesystem callbacks. -/// -typedef bool (*FileExistsFunction)(const std::string &abs_filename, void *); - -/// -/// ExpandFilePathFunction type. Signature for custom filesystem callbacks. -/// -typedef std::string (*ExpandFilePathFunction)(const std::string &, void *); - -/// -/// ReadWholeFileFunction type. Signature for custom filesystem callbacks. -/// -typedef bool (*ReadWholeFileFunction)(std::vector *, - std::string *, const std::string &, - void *); - -/// -/// WriteWholeFileFunction type. Signature for custom filesystem callbacks. -/// -typedef bool (*WriteWholeFileFunction)(std::string *, const std::string &, - const std::vector &, - void *); - -/// -/// A structure containing all required filesystem callbacks and a pointer to -/// their user data. -/// -struct FsCallbacks { - FileExistsFunction FileExists; - ExpandFilePathFunction ExpandFilePath; - ReadWholeFileFunction ReadWholeFile; - WriteWholeFileFunction WriteWholeFile; - - void *user_data; // An argument that is passed to all fs callbacks -}; - -#ifndef TINYGLTF_NO_FS -// Declaration of default filesystem callbacks - -bool FileExists(const std::string &abs_filename, void *); - -std::string ExpandFilePath(const std::string &filepath, void *); - -bool ReadWholeFile(std::vector *out, std::string *err, - const std::string &filepath, void *); - -bool WriteWholeFile(std::string *err, const std::string &filepath, - const std::vector &contents, void *); -#endif - -/// -/// glTF Parser/Serialier context. -/// -class TinyGLTF { - public: -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wc++98-compat" -#endif - - TinyGLTF() : bin_data_(nullptr), bin_size_(0), is_binary_(false) {} - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - - ~TinyGLTF() {} - - /// - /// Loads glTF ASCII asset from a file. - /// Set warning message to `warn` for example it fails to load asserts. - /// Returns false and set error string to `err` if there's an error. - /// - bool LoadASCIIFromFile(Model *model, std::string *err, std::string *warn, - const std::string &filename, - unsigned int check_sections = REQUIRE_VERSION); - - /// - /// Loads glTF ASCII asset from string(memory). - /// `length` = strlen(str); - /// Set warning message to `warn` for example it fails to load asserts. - /// Returns false and set error string to `err` if there's an error. - /// - bool LoadASCIIFromString(Model *model, std::string *err, std::string *warn, - const char *str, const unsigned int length, - const std::string &base_dir, - unsigned int check_sections = REQUIRE_VERSION); - - /// - /// Loads glTF binary asset from a file. - /// Set warning message to `warn` for example it fails to load asserts. - /// Returns false and set error string to `err` if there's an error. - /// - bool LoadBinaryFromFile(Model *model, std::string *err, std::string *warn, - const std::string &filename, - unsigned int check_sections = REQUIRE_VERSION); - - /// - /// Loads glTF binary asset from memory. - /// `length` = strlen(str); - /// Set warning message to `warn` for example it fails to load asserts. - /// Returns false and set error string to `err` if there's an error. - /// - bool LoadBinaryFromMemory(Model *model, std::string *err, std::string *warn, - const unsigned char *bytes, - const unsigned int length, - const std::string &base_dir = "", - unsigned int check_sections = REQUIRE_VERSION); - - /// - /// Write glTF to stream, buffers and images will be embeded - /// - bool WriteGltfSceneToStream(Model *model, std::ostream &stream, - bool prettyPrint, bool writeBinary); - - /// - /// Write glTF to file. - /// - bool WriteGltfSceneToFile(Model *model, const std::string &filename, - bool embedImages, bool embedBuffers, - bool prettyPrint, bool writeBinary); - - /// - /// Set callback to use for loading image data - /// - void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data); - - /// - /// Set callback to use for writing image data - /// - void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data); - - /// - /// Set callbacks to use for filesystem (fs) access and their user data - /// - void SetFsCallbacks(FsCallbacks callbacks); - - /// - /// Set serializing default values(default = false). - /// When true, default values are force serialized to .glTF. - /// This may be helpfull if you want to serialize a full description of glTF - /// data. - /// - /// TODO(LTE): Supply parsing option as function arguments to - /// `LoadASCIIFromFile()` and others, not by a class method - /// - void SetSerializeDefaultValues(const bool enabled) { - serialize_default_values_ = enabled; - } - - bool GetSerializeDefaultValues() const { return serialize_default_values_; } - - private: - /// - /// Loads glTF asset from string(memory). - /// `length` = strlen(str); - /// Set warning message to `warn` for example it fails to load asserts - /// Returns false and set error string to `err` if there's an error. - /// - bool LoadFromString(Model *model, std::string *err, std::string *warn, - const char *str, const unsigned int length, - const std::string &base_dir, unsigned int check_sections); - - const unsigned char *bin_data_ = nullptr; - size_t bin_size_ = 0; - bool is_binary_ = false; - - bool serialize_default_values_ = false; ///< Serialize default values? - - FsCallbacks fs = { -#ifndef TINYGLTF_NO_FS - &tinygltf::FileExists, &tinygltf::ExpandFilePath, - &tinygltf::ReadWholeFile, &tinygltf::WriteWholeFile, - - nullptr // Fs callback user data -#else - nullptr, nullptr, nullptr, nullptr, - - nullptr // Fs callback user data -#endif - }; - - LoadImageDataFunction LoadImageData = -#ifndef TINYGLTF_NO_STB_IMAGE - &tinygltf::LoadImageData; -#else - nullptr; -#endif - void *load_image_user_data_ = reinterpret_cast(&fs); - - WriteImageDataFunction WriteImageData = -#ifndef TINYGLTF_NO_STB_IMAGE_WRITE - &tinygltf::WriteImageData; -#else - nullptr; -#endif - void *write_image_user_data_ = reinterpret_cast(&fs); -}; - -#ifdef __clang__ -#pragma clang diagnostic pop // -Wpadded -#endif - -} // namespace tinygltf - -#endif // TINY_GLTF_H_ - -#if defined(TINYGLTF_IMPLEMENTATION) || defined(__INTELLISENSE__) -#include -//#include -#ifndef TINYGLTF_NO_FS -#include -#endif -#include - -#ifdef __clang__ -// Disable some warnings for external files. -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wfloat-equal" -#pragma clang diagnostic ignored "-Wexit-time-destructors" -#pragma clang diagnostic ignored "-Wconversion" -#pragma clang diagnostic ignored "-Wold-style-cast" -#pragma clang diagnostic ignored "-Wglobal-constructors" -#pragma clang diagnostic ignored "-Wreserved-id-macro" -#pragma clang diagnostic ignored "-Wdisabled-macro-expansion" -#pragma clang diagnostic ignored "-Wpadded" -#pragma clang diagnostic ignored "-Wc++98-compat" -#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" -#pragma clang diagnostic ignored "-Wdocumentation-unknown-command" -#pragma clang diagnostic ignored "-Wswitch-enum" -#pragma clang diagnostic ignored "-Wimplicit-fallthrough" -#pragma clang diagnostic ignored "-Wweak-vtables" -#pragma clang diagnostic ignored "-Wcovered-switch-default" -#if __has_warning("-Wdouble-promotion") -#pragma clang diagnostic ignored "-Wdouble-promotion" -#endif -#if __has_warning("-Wcomma") -#pragma clang diagnostic ignored "-Wcomma" -#endif -#if __has_warning("-Wzero-as-null-pointer-constant") -#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" -#endif -#if __has_warning("-Wcast-qual") -#pragma clang diagnostic ignored "-Wcast-qual" -#endif -#if __has_warning("-Wmissing-variable-declarations") -#pragma clang diagnostic ignored "-Wmissing-variable-declarations" -#endif -#if __has_warning("-Wmissing-prototypes") -#pragma clang diagnostic ignored "-Wmissing-prototypes" -#endif -#if __has_warning("-Wcast-align") -#pragma clang diagnostic ignored "-Wcast-align" -#endif -#if __has_warning("-Wnewline-eof") -#pragma clang diagnostic ignored "-Wnewline-eof" -#endif -#if __has_warning("-Wunused-parameter") -#pragma clang diagnostic ignored "-Wunused-parameter" -#endif -#if __has_warning("-Wmismatched-tags") -#pragma clang diagnostic ignored "-Wmismatched-tags" -#endif -#if __has_warning("-Wextra-semi-stmt") -#pragma clang diagnostic ignored "-Wextra-semi-stmt" -#endif -#endif - -// Disable GCC warnigs -#ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wtype-limits" -#endif // __GNUC__ - -#ifndef TINYGLTF_NO_INCLUDE_JSON -#ifndef TINYGLTF_USE_RAPIDJSON -#include "json.hpp" -#else -#include "document.h" -#include "prettywriter.h" -#include "rapidjson.h" -#include "stringbuffer.h" -#include "writer.h" -#endif -#endif - -#ifdef TINYGLTF_ENABLE_DRACO -#include "draco/compression/decode.h" -#include "draco/core/decoder_buffer.h" -#endif - -#ifndef TINYGLTF_NO_STB_IMAGE -#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE -#include "stb_image.h" -#endif -#endif - -#ifndef TINYGLTF_NO_STB_IMAGE_WRITE -#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE -#include "stb_image_write.h" -#endif -#endif - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif - -#ifdef _WIN32 - -// issue 143. -// Define NOMINMAX to avoid min/max defines, -// but undef it after included windows.h -#ifndef NOMINMAX -#define TINYGLTF_INTERNAL_NOMINMAX -#define NOMINMAX -#endif - -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#define TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN -#endif -#include // include API for expanding a file path - -#ifdef TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN -#undef WIN32_LEAN_AND_MEAN -#endif - -#if defined(TINYGLTF_INTERNAL_NOMINMAX) -#undef NOMINMAX -#endif - -#elif !defined(__ANDROID__) -#include -#endif - -#if defined(__sparcv9) -// Big endian -#else -#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU -#define TINYGLTF_LITTLE_ENDIAN 1 -#endif -#endif - -namespace { -#ifdef TINYGLTF_USE_RAPIDJSON - -#ifdef TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR -// This uses the RapidJSON CRTAllocator. It is thread safe and multiple -// documents may be active at once. -using json = rapidjson::GenericValue, rapidjson::CrtAllocator>; -using json_const_iterator = json::ConstMemberIterator; -using json_const_array_iterator = json const *; -using JsonDocument = - rapidjson::GenericDocument, rapidjson::CrtAllocator>; -rapidjson::CrtAllocator s_CrtAllocator; //stateless and thread safe -rapidjson::CrtAllocator &GetAllocator() { return s_CrtAllocator; } -#else -// This uses the default RapidJSON MemoryPoolAllocator. It is very fast, but -// not thread safe. Only a single JsonDocument may be active at any one time, -// meaning only a single gltf load/save can be active any one time. -using json = rapidjson::Value; -using json_const_iterator = json::ConstMemberIterator; -using json_const_array_iterator = json const *; -rapidjson::Document *s_pActiveDocument = nullptr; -rapidjson::Document::AllocatorType &GetAllocator() { - assert(s_pActiveDocument); //Root json node must be JsonDocument type - return s_pActiveDocument->GetAllocator(); -} -struct JsonDocument : public rapidjson::Document { - JsonDocument() { - assert(s_pActiveDocument == - nullptr); // When using default allocator, only one document can be active at a time, if you need - // multiple active at once, define TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR - s_pActiveDocument = this; - } - 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; -}; -#endif // TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR -#else -using nlohmann::json; -using json_const_iterator = json::const_iterator; -using json_const_array_iterator = json_const_iterator; -using JsonDocument = json; -#endif - -void JsonParse(JsonDocument &doc, const char *str, size_t length, - bool throwExc = false) { -#ifdef TINYGLTF_USE_RAPIDJSON - doc.Parse(str, length); -#else - doc = json::parse(str, str + length, nullptr, throwExc); -#endif -} - -JsonDocument JsonConstruct(const char *str) { - JsonDocument doc; - JsonParse(doc, str, strlen(str)); - return doc; -} -} // namespace - -#ifdef __APPLE__ -#include "TargetConditionals.h" -#endif - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wc++98-compat" -#endif - -namespace tinygltf { - -// Equals function for Value, for recursivity -static bool Equals(const tinygltf::Value &one, const tinygltf::Value &other) { - if (one.Type() != other.Type()) return false; - - switch (one.Type()) { - case NULL_TYPE: - return true; - case BOOL_TYPE: - return one.Get() == other.Get(); - case REAL_TYPE: - return TINYGLTF_DOUBLE_EQUAL(one.Get(), other.Get()); - case INT_TYPE: - return one.Get() == other.Get(); - case OBJECT_TYPE: { - auto oneObj = one.Get(); - auto otherObj = other.Get(); - if (oneObj.size() != otherObj.size()) return false; - for (auto &it : oneObj) { - auto otherIt = otherObj.find(it.first); - if (otherIt == otherObj.end()) return false; - - if (!Equals(it.second, otherIt->second)) return false; - } - return true; - } - case ARRAY_TYPE: { - if (one.Size() != other.Size()) return false; - for (int i = 0; i < int(one.Size()); ++i) - if (!Equals(one.Get(i), other.Get(i))) return false; - return true; - } - case STRING_TYPE: - return one.Get() == other.Get(); - case BINARY_TYPE: - return one.Get >() == - other.Get >(); - default: { - // unhandled type - return false; - } - } -} - -// Equals function for std::vector using TINYGLTF_DOUBLE_EPSILON -static bool Equals(const std::vector &one, - const std::vector &other) { - if (one.size() != other.size()) return false; - for (int i = 0; i < int(one.size()); ++i) { - if (!TINYGLTF_DOUBLE_EQUAL(one[size_t(i)], other[size_t(i)])) return false; - } - return true; -} - -bool Accessor::operator==(const Accessor &other) const { - return this->bufferView == other.bufferView && - this->byteOffset == other.byteOffset && - this->componentType == other.componentType && - this->count == other.count && this->extras == other.extras && - Equals(this->maxValues, other.maxValues) && - Equals(this->minValues, other.minValues) && this->name == other.name && - this->normalized == other.normalized && this->type == other.type; -} -bool Animation::operator==(const Animation &other) const { - return this->channels == other.channels && - this->extensions == other.extensions && this->extras == other.extras && - this->name == other.name && this->samplers == other.samplers; -} -bool AnimationChannel::operator==(const AnimationChannel &other) const { - return this->extensions == other.extensions && this->extras == other.extras && - this->target_node == other.target_node && - this->target_path == other.target_path && - this->sampler == other.sampler; -} -bool AnimationSampler::operator==(const AnimationSampler &other) const { - return this->extras == other.extras && this->input == other.input && - this->interpolation == other.interpolation && - this->output == other.output; -} -bool Asset::operator==(const Asset &other) const { - return this->copyright == other.copyright && - this->extensions == other.extensions && this->extras == other.extras && - this->generator == other.generator && - this->minVersion == other.minVersion && this->version == other.version; -} -bool Buffer::operator==(const Buffer &other) const { - return this->data == other.data && this->extras == other.extras && - this->name == other.name && this->uri == other.uri; -} -bool BufferView::operator==(const BufferView &other) const { - return this->buffer == other.buffer && this->byteLength == other.byteLength && - this->byteOffset == other.byteOffset && - this->byteStride == other.byteStride && this->name == other.name && - this->target == other.target && this->extras == other.extras && - this->dracoDecoded == other.dracoDecoded; -} -bool Camera::operator==(const Camera &other) const { - return this->name == other.name && this->extensions == other.extensions && - this->extras == other.extras && - this->orthographic == other.orthographic && - this->perspective == other.perspective && this->type == other.type; -} -bool Image::operator==(const Image &other) const { - return this->bufferView == other.bufferView && - this->component == other.component && this->extras == other.extras && - this->height == other.height && this->image == other.image && - this->mimeType == other.mimeType && this->name == other.name && - this->uri == other.uri && this->width == other.width; -} -bool Light::operator==(const Light &other) const { - return Equals(this->color, other.color) && this->name == other.name && - this->type == other.type; -} -bool Material::operator==(const Material &other) const { - return (this->pbrMetallicRoughness == other.pbrMetallicRoughness) && - (this->normalTexture == other.normalTexture) && - (this->occlusionTexture == other.occlusionTexture) && - (this->emissiveTexture == other.emissiveTexture) && - Equals(this->emissiveFactor, other.emissiveFactor) && - (this->alphaMode == other.alphaMode) && - TINYGLTF_DOUBLE_EQUAL(this->alphaCutoff, other.alphaCutoff) && - (this->doubleSided == other.doubleSided) && - (this->extensions == other.extensions) && - (this->extras == other.extras) && (this->values == other.values) && - (this->additionalValues == other.additionalValues) && - (this->name == other.name); -} -bool Mesh::operator==(const Mesh &other) const { - return this->extensions == other.extensions && this->extras == other.extras && - this->name == other.name && this->primitives == other.primitives; -} -bool Model::operator==(const Model &other) const { - return this->accessors == other.accessors && - this->animations == other.animations && this->asset == other.asset && - this->buffers == other.buffers && - this->bufferViews == other.bufferViews && - this->cameras == other.cameras && - this->defaultScene == other.defaultScene && - this->extensions == other.extensions && - this->extensionsRequired == other.extensionsRequired && - this->extensionsUsed == other.extensionsUsed && - this->extras == other.extras && this->images == other.images && - this->lights == other.lights && this->materials == other.materials && - this->meshes == other.meshes && this->nodes == other.nodes && - this->samplers == other.samplers && this->scenes == other.scenes && - this->skins == other.skins && this->textures == other.textures; -} -bool Node::operator==(const Node &other) const { - return this->camera == other.camera && this->children == other.children && - this->extensions == other.extensions && this->extras == other.extras && - Equals(this->matrix, other.matrix) && this->mesh == other.mesh && - this->name == other.name && Equals(this->rotation, other.rotation) && - Equals(this->scale, other.scale) && this->skin == other.skin && - Equals(this->translation, other.translation) && - Equals(this->weights, other.weights); -} -bool SpotLight::operator==(const SpotLight &other) const { - return this->extensions == other.extensions && this->extras == other.extras && - TINYGLTF_DOUBLE_EQUAL(this->innerConeAngle, other.innerConeAngle) && - TINYGLTF_DOUBLE_EQUAL(this->outerConeAngle, other.outerConeAngle); -} -bool OrthographicCamera::operator==(const OrthographicCamera &other) const { - return this->extensions == other.extensions && this->extras == other.extras && - TINYGLTF_DOUBLE_EQUAL(this->xmag, other.xmag) && - TINYGLTF_DOUBLE_EQUAL(this->ymag, other.ymag) && - TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) && - TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear); -} -bool Parameter::operator==(const Parameter &other) const { - if (this->bool_value != other.bool_value || - this->has_number_value != other.has_number_value) - return false; - - if (!TINYGLTF_DOUBLE_EQUAL(this->number_value, other.number_value)) - return false; - - if (this->json_double_value.size() != other.json_double_value.size()) - return false; - for (auto &it : this->json_double_value) { - auto otherIt = other.json_double_value.find(it.first); - if (otherIt == other.json_double_value.end()) return false; - - if (!TINYGLTF_DOUBLE_EQUAL(it.second, otherIt->second)) return false; - } - - if (!Equals(this->number_array, other.number_array)) return false; - - if (this->string_value != other.string_value) return false; - - return true; -} -bool PerspectiveCamera::operator==(const PerspectiveCamera &other) const { - return TINYGLTF_DOUBLE_EQUAL(this->aspectRatio, other.aspectRatio) && - this->extensions == other.extensions && this->extras == other.extras && - TINYGLTF_DOUBLE_EQUAL(this->yfov, other.yfov) && - TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) && - TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear); -} -bool Primitive::operator==(const Primitive &other) const { - return this->attributes == other.attributes && this->extras == other.extras && - this->indices == other.indices && this->material == other.material && - this->mode == other.mode && this->targets == other.targets; -} -bool Sampler::operator==(const Sampler &other) const { - return this->extras == other.extras && this->magFilter == other.magFilter && - this->minFilter == other.minFilter && this->name == other.name && - this->wrapR == other.wrapR && this->wrapS == other.wrapS && - this->wrapT == other.wrapT; -} -bool Scene::operator==(const Scene &other) const { - return this->extensions == other.extensions && this->extras == other.extras && - this->name == other.name && this->nodes == other.nodes; -} -bool Skin::operator==(const Skin &other) const { - return this->inverseBindMatrices == other.inverseBindMatrices && - this->joints == other.joints && this->name == other.name && - this->skeleton == other.skeleton; -} -bool Texture::operator==(const Texture &other) const { - return this->extensions == other.extensions && this->extras == other.extras && - this->name == other.name && this->sampler == other.sampler && - this->source == other.source; -} -bool TextureInfo::operator==(const TextureInfo &other) const { - return this->extensions == other.extensions && this->extras == other.extras && - this->index == other.index && this->texCoord == other.texCoord; -} -bool NormalTextureInfo::operator==(const NormalTextureInfo &other) const { - return this->extensions == other.extensions && this->extras == other.extras && - this->index == other.index && this->texCoord == other.texCoord && - TINYGLTF_DOUBLE_EQUAL(this->scale, other.scale); -} -bool OcclusionTextureInfo::operator==(const OcclusionTextureInfo &other) const { - return this->extensions == other.extensions && this->extras == other.extras && - this->index == other.index && this->texCoord == other.texCoord && - TINYGLTF_DOUBLE_EQUAL(this->strength, other.strength); -} -bool PbrMetallicRoughness::operator==(const PbrMetallicRoughness &other) const { - return this->extensions == other.extensions && this->extras == other.extras && - (this->baseColorTexture == other.baseColorTexture) && - (this->metallicRoughnessTexture == other.metallicRoughnessTexture) && - Equals(this->baseColorFactor, other.baseColorFactor) && - TINYGLTF_DOUBLE_EQUAL(this->metallicFactor, other.metallicFactor) && - TINYGLTF_DOUBLE_EQUAL(this->roughnessFactor, other.roughnessFactor); -} -bool Value::operator==(const Value &other) const { - return Equals(*this, other); -} - -static void swap4(unsigned int *val) { -#ifdef TINYGLTF_LITTLE_ENDIAN - (void)val; -#else - unsigned int tmp = *val; - unsigned char *dst = reinterpret_cast(val); - unsigned char *src = reinterpret_cast(&tmp); - - dst[0] = src[3]; - dst[1] = src[2]; - dst[2] = src[1]; - dst[3] = src[0]; -#endif -} - -static std::string JoinPath(const std::string &path0, - const std::string &path1) { - if (path0.empty()) { - return path1; - } else { - // check '/' - char lastChar = *path0.rbegin(); - if (lastChar != '/') { - return path0 + std::string("/") + path1; - } else { - return path0 + path1; - } - } -} - -static std::string FindFile(const std::vector &paths, - const std::string &filepath, FsCallbacks *fs) { - if (fs == nullptr || fs->ExpandFilePath == nullptr || - fs->FileExists == nullptr) { - // Error, fs callback[s] missing - return std::string(); - } - - for (size_t i = 0; i < paths.size(); i++) { - std::string absPath = - fs->ExpandFilePath(JoinPath(paths[i], filepath), fs->user_data); - if (fs->FileExists(absPath, fs->user_data)) { - return absPath; - } - } - - return std::string(); -} - -static std::string GetFilePathExtension(const std::string &FileName) { - if (FileName.find_last_of(".") != std::string::npos) - return FileName.substr(FileName.find_last_of(".") + 1); - return ""; -} - -static std::string GetBaseDir(const std::string &filepath) { - if (filepath.find_last_of("/\\") != std::string::npos) - return filepath.substr(0, filepath.find_last_of("/\\")); - return ""; -} - -// https://stackoverflow.com/questions/8520560/get-a-file-name-from-a-path -static std::string GetBaseFilename(const std::string &filepath) { - return filepath.substr(filepath.find_last_of("/\\") + 1); -} - -std::string base64_encode(unsigned char const *, unsigned int len); -std::string base64_decode(std::string const &s); - -/* - base64.cpp and base64.h - - Copyright (C) 2004-2008 RenĂ© Nyffenegger - - This source code is provided 'as-is', without any express or implied - warranty. In no event will the author be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this source code must not be misrepresented; you must not - claim that you wrote the original source code. If you use this source code - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original source code. - - 3. This notice may not be removed or altered from any source distribution. - - RenĂ© Nyffenegger rene.nyffenegger@adp-gmbh.ch - -*/ - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wsign-conversion" -#pragma clang diagnostic ignored "-Wconversion" -#endif - -static inline bool is_base64(unsigned char c) { - return (isalnum(c) || (c == '+') || (c == '/')); -} - -std::string base64_encode(unsigned char const *bytes_to_encode, - unsigned int in_len) { - std::string ret; - int i = 0; - int j = 0; - unsigned char char_array_3[3]; - unsigned char char_array_4[4]; - - const char *base64_chars = - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; - - while (in_len--) { - char_array_3[i++] = *(bytes_to_encode++); - if (i == 3) { - char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; - char_array_4[1] = - ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); - char_array_4[2] = - ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); - char_array_4[3] = char_array_3[2] & 0x3f; - - for (i = 0; (i < 4); i++) ret += base64_chars[char_array_4[i]]; - i = 0; - } - } - - if (i) { - for (j = i; j < 3; j++) char_array_3[j] = '\0'; - - char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; - char_array_4[1] = - ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); - char_array_4[2] = - ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); - - for (j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]]; - - while ((i++ < 3)) ret += '='; - } - - return ret; -} - -std::string base64_decode(std::string const &encoded_string) { - int in_len = static_cast(encoded_string.size()); - int i = 0; - int j = 0; - int in_ = 0; - unsigned char char_array_4[4], char_array_3[3]; - std::string ret; - - const std::string base64_chars = - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" - "abcdefghijklmnopqrstuvwxyz" - "0123456789+/"; - - while (in_len-- && (encoded_string[in_] != '=') && - is_base64(encoded_string[in_])) { - char_array_4[i++] = encoded_string[in_]; - in_++; - if (i == 4) { - for (i = 0; i < 4; i++) - char_array_4[i] = - static_cast(base64_chars.find(char_array_4[i])); - - char_array_3[0] = - (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); - char_array_3[1] = - ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); - char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; - - for (i = 0; (i < 3); i++) ret += char_array_3[i]; - i = 0; - } - } - - if (i) { - for (j = i; j < 4; j++) char_array_4[j] = 0; - - for (j = 0; j < 4; j++) - char_array_4[j] = - static_cast(base64_chars.find(char_array_4[j])); - - char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); - char_array_3[1] = - ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); - char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; - - for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; - } - - return ret; -} -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -static bool LoadExternalFile(std::vector *out, std::string *err, - std::string *warn, const std::string &filename, - const std::string &basedir, bool required, - size_t reqBytes, bool checkSize, FsCallbacks *fs) { - if (fs == nullptr || fs->FileExists == nullptr || - fs->ExpandFilePath == nullptr || fs->ReadWholeFile == nullptr) { - // This is a developer error, assert() ? - if (err) { - (*err) += "FS callback[s] not set\n"; - } - return false; - } - - std::string *failMsgOut = required ? err : warn; - - out->clear(); - - std::vector paths; - paths.push_back(basedir); - paths.push_back("."); - - std::string filepath = FindFile(paths, filename, fs); - if (filepath.empty() || filename.empty()) { - if (failMsgOut) { - (*failMsgOut) += "File not found : " + filename + "\n"; - } - return false; - } - - std::vector buf; - std::string fileReadErr; - bool fileRead = - fs->ReadWholeFile(&buf, &fileReadErr, filepath, fs->user_data); - if (!fileRead) { - if (failMsgOut) { - (*failMsgOut) += - "File read error : " + filepath + " : " + fileReadErr + "\n"; - } - return false; - } - - size_t sz = buf.size(); - if (sz == 0) { - if (failMsgOut) { - (*failMsgOut) += "File is empty : " + filepath + "\n"; - } - return false; - } - - if (checkSize) { - if (reqBytes == sz) { - out->swap(buf); - return true; - } else { - std::stringstream ss; - ss << "File size mismatch : " << filepath << ", requestedBytes " - << reqBytes << ", but got " << sz << std::endl; - if (failMsgOut) { - (*failMsgOut) += ss.str(); - } - return false; - } - } - - out->swap(buf); - return true; -} - -void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) { - LoadImageData = func; - load_image_user_data_ = user_data; -} - -#ifndef TINYGLTF_NO_STB_IMAGE -bool LoadImageData(Image *image, const int image_idx, std::string *err, - std::string *warn, int req_width, int req_height, - const unsigned char *bytes, int size, void *user_data) { - (void)user_data; - (void)warn; - - int w = 0, h = 0, comp = 0, req_comp = 0; - - unsigned char *data = nullptr; - - // force 32-bit textures for common Vulkan compatibility. It appears that - // some GPU drivers do not support 24-bit images for Vulkan - req_comp = 4; - int bits = 8; - int pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE; - - // It is possible that the image we want to load is a 16bit per channel image - // We are going to attempt to load it as 16bit per channel, and if it worked, - // set the image data accodingly. We are casting the returned pointer into - // unsigned char, because we are representing "bytes". But we are updating - // the Image metadata to signal that this image uses 2 bytes (16bits) per - // channel: - if (stbi_is_16_bit_from_memory(bytes, size)) { - data = reinterpret_cast( - stbi_load_16_from_memory(bytes, size, &w, &h, &comp, req_comp)); - if (data) { - bits = 16; - pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT; - } - } - - // at this point, if data is still NULL, it means that the image wasn't - // 16bit per channel, we are going to load it as a normal 8bit per channel - // mage as we used to do: - // if image cannot be decoded, ignore parsing and keep it by its path - // don't break in this case - // FIXME we should only enter this function if the image is embedded. If - // image->uri references - // an image file, it should be left as it is. Image loading should not be - // mandatory (to support other formats) - if (!data) data = stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp); - if (!data) { - // NOTE: you can use `warn` instead of `err` - if (err) { - (*err) += - "Unknown image format. STB cannot decode image data for image[" + - std::to_string(image_idx) + "] name = \"" + image->name + "\".\n"; - } - return false; - } - - if ((w < 1) || (h < 1)) { - stbi_image_free(data); - if (err) { - (*err) += "Invalid image data for image[" + std::to_string(image_idx) + - "] name = \"" + image->name + "\"\n"; - } - return false; - } - - if (req_width > 0) { - if (req_width != w) { - stbi_image_free(data); - if (err) { - (*err) += "Image width mismatch for image[" + - std::to_string(image_idx) + "] name = \"" + image->name + - "\"\n"; - } - return false; - } - } - - if (req_height > 0) { - if (req_height != h) { - stbi_image_free(data); - if (err) { - (*err) += "Image height mismatch. for image[" + - std::to_string(image_idx) + "] name = \"" + image->name + - "\"\n"; - } - return false; - } - } - - image->width = w; - image->height = h; - image->component = req_comp; - image->bits = bits; - image->pixel_type = pixel_type; - image->image.resize(static_cast(w * h * req_comp) * size_t(bits / 8)); - std::copy(data, data + w * h * req_comp * (bits / 8), image->image.begin()); - stbi_image_free(data); - - return true; -} -#endif - -void TinyGLTF::SetImageWriter(WriteImageDataFunction func, void *user_data) { - WriteImageData = func; - write_image_user_data_ = user_data; -} - -#ifndef TINYGLTF_NO_STB_IMAGE_WRITE -static void WriteToMemory_stbi(void *context, void *data, int size) { - std::vector *buffer = - reinterpret_cast *>(context); - - unsigned char *pData = reinterpret_cast(data); - - buffer->insert(buffer->end(), pData, pData + size); -} - -bool WriteImageData(const std::string *basepath, const std::string *filename, - Image *image, bool embedImages, void *fsPtr) { - const std::string ext = GetFilePathExtension(*filename); - - // Write image to temporary buffer - std::string header; - std::vector data; - - if (ext == "png") { - if ((image->bits != 8) || - (image->pixel_type != TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)) { - // Unsupported pixel format - return false; - } - - if (!stbi_write_png_to_func(WriteToMemory_stbi, &data, image->width, - image->height, image->component, - &image->image[0], 0)) { - return false; - } - header = "data:image/png;base64,"; - } else if (ext == "jpg") { - if (!stbi_write_jpg_to_func(WriteToMemory_stbi, &data, image->width, - image->height, image->component, - &image->image[0], 100)) { - return false; - } - header = "data:image/jpeg;base64,"; - } else if (ext == "bmp") { - if (!stbi_write_bmp_to_func(WriteToMemory_stbi, &data, image->width, - image->height, image->component, - &image->image[0])) { - return false; - } - header = "data:image/bmp;base64,"; - } else if (!embedImages) { - // Error: can't output requested format to file - return false; - } - - if (embedImages) { - // Embed base64-encoded image into URI - if (data.size()) { - image->uri = - header + - base64_encode(&data[0], static_cast(data.size())); - } else { - // Throw error? - } - } else { - // Write image to disc - FsCallbacks *fs = reinterpret_cast(fsPtr); - if ((fs != nullptr) && (fs->WriteWholeFile != nullptr)) { - const std::string imagefilepath = JoinPath(*basepath, *filename); - std::string writeError; - if (!fs->WriteWholeFile(&writeError, imagefilepath, data, - fs->user_data)) { - // Could not write image file to disc; Throw error ? - return false; - } - } else { - // Throw error? - } - image->uri = *filename; - } - - return true; -} -#endif - -void TinyGLTF::SetFsCallbacks(FsCallbacks callbacks) { fs = callbacks; } - -#ifndef TINYGLTF_NO_FS -// Default implementations of filesystem functions - -bool FileExists(const std::string &abs_filename, void *) { - bool ret; -#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS - if (asset_manager) { - AAsset *asset = AAssetManager_open(asset_manager, abs_filename.c_str(), - AASSET_MODE_STREAMING); - if (!asset) { - return false; - } - AAsset_close(asset); - ret = true; - } else { - return false; - } -#else -#ifdef _WIN32 - FILE *fp; - errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb"); - if (err != 0) { - return false; - } -#else - FILE *fp = fopen(abs_filename.c_str(), "rb"); -#endif - if (fp) { - ret = true; - fclose(fp); - } else { - ret = false; - } -#endif - - return ret; -} - -std::string ExpandFilePath(const std::string &filepath, void *) { -#ifdef _WIN32 - DWORD len = ExpandEnvironmentStringsA(filepath.c_str(), NULL, 0); - char *str = new char[len]; - ExpandEnvironmentStringsA(filepath.c_str(), str, len); - - std::string s(str); - - delete[] str; - - return s; -#else - -#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \ - defined(__ANDROID__) || defined(__EMSCRIPTEN__) - // no expansion - std::string s = filepath; -#else - std::string s; - wordexp_t p; - - if (filepath.empty()) { - return ""; - } - - // char** w; - int ret = wordexp(filepath.c_str(), &p, 0); - if (ret) { - // err - s = filepath; - return s; - } - - // Use first element only. - if (p.we_wordv) { - s = std::string(p.we_wordv[0]); - wordfree(&p); - } else { - s = filepath; - } - -#endif - - return s; -#endif -} - -bool ReadWholeFile(std::vector *out, std::string *err, - const std::string &filepath, void *) { -#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS - if (asset_manager) { - AAsset *asset = AAssetManager_open(asset_manager, filepath.c_str(), - AASSET_MODE_STREAMING); - if (!asset) { - if (err) { - (*err) += "File open error : " + filepath + "\n"; - } - return false; - } - size_t size = AAsset_getLength(asset); - if (size <= 0) { - if (err) { - (*err) += "Invalid file size : " + filepath + - " (does the path point to a directory?)"; - } - } - out->resize(size); - AAsset_read(asset, reinterpret_cast(&out->at(0)), size); - AAsset_close(asset); - return true; - } else { - if (err) { - (*err) += "No asset manager specified : " + filepath + "\n"; - } - return false; - } -#else - std::ifstream f(filepath.c_str(), std::ifstream::binary); - if (!f) { - if (err) { - (*err) += "File open error : " + filepath + "\n"; - } - return false; - } - - f.seekg(0, f.end); - size_t sz = static_cast(f.tellg()); - f.seekg(0, f.beg); - - if (int(sz) < 0) { - if (err) { - (*err) += "Invalid file size : " + filepath + - " (does the path point to a directory?)"; - } - return false; - } else if (sz == 0) { - if (err) { - (*err) += "File is empty : " + filepath + "\n"; - } - return false; - } - - out->resize(sz); - f.read(reinterpret_cast(&out->at(0)), - static_cast(sz)); - f.close(); - - return true; -#endif -} - -bool WriteWholeFile(std::string *err, const std::string &filepath, - const std::vector &contents, void *) { - std::ofstream f(filepath.c_str(), std::ofstream::binary); - if (!f) { - if (err) { - (*err) += "File open error for writing : " + filepath + "\n"; - } - return false; - } - - f.write(reinterpret_cast(&contents.at(0)), - static_cast(contents.size())); - if (!f) { - if (err) { - (*err) += "File write error: " + filepath + "\n"; - } - return false; - } - - f.close(); - return true; -} - -#endif // TINYGLTF_NO_FS - -static std::string MimeToExt(const std::string &mimeType) { - if (mimeType == "image/jpeg") { - return "jpg"; - } else if (mimeType == "image/png") { - return "png"; - } else if (mimeType == "image/bmp") { - return "bmp"; - } else if (mimeType == "image/gif") { - return "gif"; - } - - return ""; -} - -static void UpdateImageObject(Image &image, std::string &baseDir, int index, - bool embedImages, - WriteImageDataFunction *WriteImageData = nullptr, - void *user_data = nullptr) { - std::string filename; - std::string ext; - - // If image have uri. Use it it as a filename - if (image.uri.size()) { - filename = GetBaseFilename(image.uri); - ext = GetFilePathExtension(filename); - - } else if (image.name.size()) { - ext = MimeToExt(image.mimeType); - // Otherwise use name as filename - filename = image.name + "." + ext; - } else { - ext = MimeToExt(image.mimeType); - // Fallback to index of image as filename - filename = std::to_string(index) + "." + ext; - } - - // If callback is set, modify image data object - if (*WriteImageData != nullptr) { - std::string uri; - (*WriteImageData)(&baseDir, &filename, &image, embedImages, user_data); - } -} - -bool IsDataURI(const std::string &in) { - std::string header = "data:application/octet-stream;base64,"; - if (in.find(header) == 0) { - return true; - } - - header = "data:image/jpeg;base64,"; - if (in.find(header) == 0) { - return true; - } - - header = "data:image/png;base64,"; - if (in.find(header) == 0) { - return true; - } - - header = "data:image/bmp;base64,"; - if (in.find(header) == 0) { - return true; - } - - header = "data:image/gif;base64,"; - if (in.find(header) == 0) { - return true; - } - - header = "data:text/plain;base64,"; - if (in.find(header) == 0) { - return true; - } - - header = "data:application/gltf-buffer;base64,"; - if (in.find(header) == 0) { - return true; - } - - return false; -} - -bool DecodeDataURI(std::vector *out, std::string &mime_type, - const std::string &in, size_t reqBytes, bool checkSize) { - std::string header = "data:application/octet-stream;base64,"; - std::string data; - if (in.find(header) == 0) { - data = base64_decode(in.substr(header.size())); // cut mime string. - } - - if (data.empty()) { - header = "data:image/jpeg;base64,"; - if (in.find(header) == 0) { - mime_type = "image/jpeg"; - data = base64_decode(in.substr(header.size())); // cut mime string. - } - } - - if (data.empty()) { - header = "data:image/png;base64,"; - if (in.find(header) == 0) { - mime_type = "image/png"; - data = base64_decode(in.substr(header.size())); // cut mime string. - } - } - - if (data.empty()) { - header = "data:image/bmp;base64,"; - if (in.find(header) == 0) { - mime_type = "image/bmp"; - data = base64_decode(in.substr(header.size())); // cut mime string. - } - } - - if (data.empty()) { - header = "data:image/gif;base64,"; - if (in.find(header) == 0) { - mime_type = "image/gif"; - data = base64_decode(in.substr(header.size())); // cut mime string. - } - } - - if (data.empty()) { - header = "data:text/plain;base64,"; - if (in.find(header) == 0) { - mime_type = "text/plain"; - data = base64_decode(in.substr(header.size())); - } - } - - if (data.empty()) { - header = "data:application/gltf-buffer;base64,"; - if (in.find(header) == 0) { - data = base64_decode(in.substr(header.size())); - } - } - - if (data.empty()) { - return false; - } - - if (checkSize) { - if (data.size() != reqBytes) { - return false; - } - out->resize(reqBytes); - } else { - out->resize(data.size()); - } - std::copy(data.begin(), data.end(), out->begin()); - return true; -} - -namespace { -bool GetInt(const json &o, int &val) { -#ifdef TINYGLTF_USE_RAPIDJSON - if (!o.IsDouble()) { - if (o.IsInt()) { - val = o.GetInt(); - return true; - } else if (o.IsUint()) { - val = static_cast(o.GetUint()); - return true; - } else if (o.IsInt64()) { - val = static_cast(o.GetInt64()); - return true; - } else if (o.IsUint64()) { - val = static_cast(o.GetUint64()); - return true; - } - } - - return false; -#else - auto type = o.type(); - - if ((type == json::value_t::number_integer) || - (type == json::value_t::number_unsigned)) { - val = static_cast(o.get()); - return true; - } - - return false; -#endif -} - -bool GetDouble(const json &o, double &val) { -#ifdef TINYGLTF_USE_RAPIDJSON - if (o.IsDouble()) { - val = o.GetDouble(); - return true; - } - - return false; -#else - if (o.type() == json::value_t::number_float) { - val = static_cast(o.get()); - return true; - } - - return false; -#endif -} - -bool GetNumber(const json &o, double &val) { -#ifdef TINYGLTF_USE_RAPIDJSON - if (o.IsNumber()) { - val = o.GetDouble(); - return true; - } - - return false; -#else - if (o.is_number()) { - val = o.get(); - return true; - } - - return false; -#endif -} - -bool GetString(const json &o, std::string &val) { -#ifdef TINYGLTF_USE_RAPIDJSON - if (o.IsString()) { - val = o.GetString(); - return true; - } - - return false; -#else - if (o.type() == json::value_t::string) { - val = o.get(); - return true; - } - - return false; -#endif -} - -bool IsArray(const json &o) { -#ifdef TINYGLTF_USE_RAPIDJSON - return o.IsArray(); -#else - return o.is_array(); -#endif -} - -json_const_array_iterator ArrayBegin(const json &o) { -#ifdef TINYGLTF_USE_RAPIDJSON - return o.Begin(); -#else - return o.begin(); -#endif -} - -json_const_array_iterator ArrayEnd(const json &o) { -#ifdef TINYGLTF_USE_RAPIDJSON - return o.End(); -#else - return o.end(); -#endif -} - -bool IsObject(const json &o) { -#ifdef TINYGLTF_USE_RAPIDJSON - return o.IsObject(); -#else - return o.is_object(); -#endif -} - -json_const_iterator ObjectBegin(const json &o) { -#ifdef TINYGLTF_USE_RAPIDJSON - return o.MemberBegin(); -#else - return o.begin(); -#endif -} - -json_const_iterator ObjectEnd(const json &o) { -#ifdef TINYGLTF_USE_RAPIDJSON - return o.MemberEnd(); -#else - return o.end(); -#endif -} - -const char *GetKey(json_const_iterator &it) { -#ifdef TINYGLTF_USE_RAPIDJSON - return it->name.GetString(); -#else - return it.key().c_str(); -#endif -} - -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 - it = o.find(member); - return it != o.end(); -#endif -} - -const json &GetValue(json_const_iterator &it) { -#ifdef TINYGLTF_USE_RAPIDJSON - return it->value; -#else - return it.value(); -#endif -} -} // namespace - -static bool ParseJsonAsValue(Value *ret, const json &o) { - Value val{}; -#ifdef TINYGLTF_USE_RAPIDJSON - using rapidjson::Type; - switch (o.GetType()) { - case Type::kObjectType: { - Value::Object value_object; - for (auto it = o.MemberBegin(); it != o.MemberEnd(); ++it) { - Value entry; - ParseJsonAsValue(&entry, it->value); - if (entry.Type() != NULL_TYPE) - value_object.emplace(GetKey(it), std::move(entry)); - } - if (value_object.size() > 0) val = Value(std::move(value_object)); - } break; - case Type::kArrayType: { - Value::Array value_array; - value_array.reserve(o.Size()); - for (auto it = o.Begin(); it != o.End(); ++it) { - Value entry; - ParseJsonAsValue(&entry, *it); - if (entry.Type() != NULL_TYPE) - value_array.emplace_back(std::move(entry)); - } - if (value_array.size() > 0) val = Value(std::move(value_array)); - } break; - case Type::kStringType: - val = Value(std::string(o.GetString())); - break; - case Type::kFalseType: - case Type::kTrueType: - val = Value(o.GetBool()); - break; - case Type::kNumberType: - if (!o.IsDouble()) { - int i = 0; - GetInt(o, i); - val = Value(i); - } else { - double d = 0.0; - GetDouble(o, d); - val = Value(d); - } - break; - case Type::kNullType: - default: - // default: - break; - } -#else - switch (o.type()) { - case json::value_t::object: { - Value::Object value_object; - for (auto it = o.begin(); it != o.end(); it++) { - Value entry; - ParseJsonAsValue(&entry, it.value()); - if (entry.Type() != NULL_TYPE) - value_object.emplace(it.key(), std::move(entry)); - } - if (value_object.size() > 0) val = Value(std::move(value_object)); - } break; - case json::value_t::array: { - Value::Array value_array; - value_array.reserve(o.size()); - for (auto it = o.begin(); it != o.end(); it++) { - Value entry; - ParseJsonAsValue(&entry, it.value()); - if (entry.Type() != NULL_TYPE) - value_array.emplace_back(std::move(entry)); - } - if (value_array.size() > 0) val = Value(std::move(value_array)); - } break; - case json::value_t::string: - val = Value(o.get()); - break; - case json::value_t::boolean: - val = Value(o.get()); - break; - case json::value_t::number_integer: - case json::value_t::number_unsigned: - val = Value(static_cast(o.get())); - break; - case json::value_t::number_float: - val = Value(o.get()); - break; - case json::value_t::null: - case json::value_t::discarded: - // default: - break; - } -#endif - if (ret) *ret = std::move(val); - - return val.Type() != NULL_TYPE; -} - -static bool ParseExtrasProperty(Value *ret, const json &o) { - json_const_iterator it; - if (!FindMember(o, "extras", it)) { - return false; - } - - return ParseJsonAsValue(ret, GetValue(it)); -} - -static bool ParseBooleanProperty(bool *ret, std::string *err, const json &o, - const std::string &property, - const bool required, - const std::string &parent_node = "") { - json_const_iterator it; - if (!FindMember(o, property.c_str(), it)) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is missing"; - if (!parent_node.empty()) { - (*err) += " in " + parent_node; - } - (*err) += ".\n"; - } - } - return false; - } - - auto &value = GetValue(it); - - bool isBoolean; - bool boolValue; -#ifdef TINYGLTF_USE_RAPIDJSON - isBoolean = value.IsBool(); - if (isBoolean) { - boolValue = value.GetBool(); - } -#else - isBoolean = value.is_boolean(); - if (isBoolean) { - boolValue = value.get(); - } -#endif - if (!isBoolean) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is not a bool type.\n"; - } - } - return false; - } - - if (ret) { - (*ret) = boolValue; - } - - 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; - if (!FindMember(o, property.c_str(), it)) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is missing"; - if (!parent_node.empty()) { - (*err) += " in " + parent_node; - } - (*err) += ".\n"; - } - } - return false; - } - - int intValue; - bool isInt = GetInt(GetValue(it), intValue); - if (!isInt) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is not an integer type.\n"; - } - } - return false; - } - - if (ret) { - (*ret) = intValue; - } - - 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; - if (!FindMember(o, property.c_str(), it)) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is missing"; - if (!parent_node.empty()) { - (*err) += " in " + parent_node; - } - (*err) += ".\n"; - } - } - return false; - } - - auto &value = GetValue(it); - - size_t uValue; - bool isUValue; -#ifdef TINYGLTF_USE_RAPIDJSON - isUValue = false; - if (value.IsUint()) { - uValue = value.GetUint(); - isUValue = true; - } else if (value.IsUint64()) { - uValue = value.GetUint64(); - isUValue = true; - } -#else - isUValue = value.is_number_unsigned(); - if (isUValue) { - uValue = value.get(); - } -#endif - if (!isUValue) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is not a positive integer.\n"; - } - } - return false; - } - - if (ret) { - (*ret) = uValue; - } - - return true; -} - -static bool ParseNumberProperty(double *ret, std::string *err, const json &o, - const std::string &property, - const bool required, - const std::string &parent_node = "") { - json_const_iterator it; - - if (!FindMember(o, property.c_str(), it)) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is missing"; - if (!parent_node.empty()) { - (*err) += " in " + parent_node; - } - (*err) += ".\n"; - } - } - return false; - } - - double doubleValue; - bool isDouble = GetDouble(GetValue(it), doubleValue); - - if (!isDouble) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is not a number type.\n"; - } - } - return false; - } - - if (ret) { - (*ret) = doubleValue; - } - - return true; -} - -static bool ParseNumberArrayProperty(std::vector *ret, std::string *err, - const json &o, const std::string &property, - bool required, - const std::string &parent_node = "") { - json_const_iterator it; - if (!FindMember(o, property.c_str(), it)) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is missing"; - if (!parent_node.empty()) { - (*err) += " in " + parent_node; - } - (*err) += ".\n"; - } - } - return false; - } - - if (!IsArray(GetValue(it))) { - 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(); - auto end = ArrayEnd(GetValue(it)); - for (auto i = ArrayBegin(GetValue(it)); i != end; ++i) { - double numberValue; - const bool isNumber = GetNumber(*i, numberValue); - if (!isNumber) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is not a number.\n"; - if (!parent_node.empty()) { - (*err) += " in " + parent_node; - } - (*err) += ".\n"; - } - } - return false; - } - ret->push_back(numberValue); - } - - 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; - if (!FindMember(o, property.c_str(), it)) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is missing"; - if (!parent_node.empty()) { - (*err) += " in " + parent_node; - } - (*err) += ".\n"; - } - } - return false; - } - - if (!IsArray(GetValue(it))) { - 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(); - auto end = ArrayEnd(GetValue(it)); - for (auto i = ArrayBegin(GetValue(it)); i != end; ++i) { - int numberValue; - bool isNumber = GetInt(*i, numberValue); - if (!isNumber) { - 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(numberValue); - } - - return true; -} - -static bool ParseStringProperty( - std::string *ret, std::string *err, const json &o, - const std::string &property, bool required, - const std::string &parent_node = std::string()) { - json_const_iterator it; - if (!FindMember(o, property.c_str(), it)) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is missing"; - if (parent_node.empty()) { - (*err) += ".\n"; - } else { - (*err) += " in `" + parent_node + "'.\n"; - } - } - } - return false; - } - - std::string strValue; - if (!GetString(GetValue(it), strValue)) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is not a string type.\n"; - } - } - return false; - } - - if (ret) { - (*ret) = std::move(strValue); - } - - return true; -} - -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; - if (!FindMember(o, property.c_str(), it)) { - if (required) { - if (err) { - if (!parent.empty()) { - (*err) += - "'" + property + "' property is missing in " + parent + ".\n"; - } else { - (*err) += "'" + property + "' property is missing.\n"; - } - } - } - return false; - } - - const json &dict = GetValue(it); - - // Make sure we are dealing with an object / dictionary. - if (!IsObject(dict)) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is not an object.\n"; - } - } - return false; - } - - ret->clear(); - - json_const_iterator dictIt(ObjectBegin(dict)); - json_const_iterator dictItEnd(ObjectEnd(dict)); - - for (; dictIt != dictItEnd; ++dictIt) { - int intVal; - if (!GetInt(GetValue(dictIt), intVal)) { - if (required) { - if (err) { - (*err) += "'" + property + "' value is not an integer type.\n"; - } - } - return false; - } - - // Insert into the list. - (*ret)[GetKey(dictIt)] = intVal; - } - return true; -} - -static bool ParseJSONProperty(std::map *ret, - std::string *err, const json &o, - const std::string &property, bool required) { - json_const_iterator it; - if (!FindMember(o, property.c_str(), it)) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is missing. \n'"; - } - } - return false; - } - - const json &obj = GetValue(it); - - if (!IsObject(obj)) { - if (required) { - if (err) { - (*err) += "'" + property + "' property is not a JSON object.\n"; - } - } - return false; - } - - ret->clear(); - - json_const_iterator it2(ObjectBegin(obj)); - json_const_iterator itEnd(ObjectEnd(obj)); - for (; it2 != itEnd; ++it2) { - double numVal; - if (GetNumber(GetValue(it2), numVal)) - ret->emplace(std::string(GetKey(it2)), numVal); - } - - return true; -} - -static bool ParseParameterProperty(Parameter *param, std::string *err, - const json &o, const std::string &prop, - bool required) { - // A parameter value can either be a string or an array of either a boolean or - // a number. Booleans of any kind aren't supported here. Granted, it - // complicates the Parameter structure and breaks it semantically in the sense - // that the client probably works off the assumption that if the string is - // empty the vector is used, etc. Would a tagged union work? - if (ParseStringProperty(¶m->string_value, err, o, prop, false)) { - // Found string property. - return true; - } else if (ParseNumberArrayProperty(¶m->number_array, err, o, prop, - false)) { - // Found a number array. - return true; - } else if (ParseNumberProperty(¶m->number_value, err, o, prop, false)) { - return param->has_number_value = true; - } else if (ParseJSONProperty(¶m->json_double_value, err, o, prop, - false)) { - return true; - } else if (ParseBooleanProperty(¶m->bool_value, err, o, prop, false)) { - return true; - } else { - if (required) { - if (err) { - (*err) += "parameter must be a string or number / number array.\n"; - } - } - return false; - } -} - -static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err, - const json &o) { - (void)err; - - json_const_iterator it; - if (!FindMember(o, "extensions", it)) { - return false; - } - - auto &obj = GetValue(it); - if (!IsObject(obj)) { - return false; - } - ExtensionMap extensions; - json_const_iterator extIt = ObjectBegin(obj); // it.value().begin(); - json_const_iterator extEnd = ObjectEnd(obj); - for (; extIt != extEnd; ++extIt) { - auto &itObj = GetValue(extIt); - if (!IsObject(itObj)) continue; - std::string key(GetKey(extIt)); - if (!ParseJsonAsValue(&extensions[key], itObj)) { - if (!key.empty()) { - // create empty object so that an extension object is still of type - // object - extensions[key] = Value{Value::Object{}}; - } - } - } - if (ret) { - (*ret) = std::move(extensions); - } - return true; -} - -static bool ParseAsset(Asset *asset, std::string *err, const json &o) { - ParseStringProperty(&asset->version, err, o, "version", true, "Asset"); - ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset"); - ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset"); - - ParseExtensionsProperty(&asset->extensions, err, o); - - // Unity exporter version is added as extra here - ParseExtrasProperty(&(asset->extras), o); - - return true; -} - -static bool ParseImage(Image *image, const int image_idx, std::string *err, - std::string *warn, const json &o, - const std::string &basedir, FsCallbacks *fs, - LoadImageDataFunction *LoadImageData = nullptr, - void *load_image_user_data = nullptr) { - // A glTF image must either reference a bufferView or an image uri - - // schema says oneOf [`bufferView`, `uri`] - // TODO(syoyo): Check the type of each parameters. - json_const_iterator it; - bool hasBufferView = FindMember(o, "bufferView", it); - bool hasURI = FindMember(o, "uri", it); - - ParseStringProperty(&image->name, err, o, "name", false); - - if (hasBufferView && hasURI) { - // Should not both defined. - if (err) { - (*err) += - "Only one of `bufferView` or `uri` should be defined, but both are " - "defined for image[" + - std::to_string(image_idx) + "] name = \"" + image->name + "\"\n"; - } - return false; - } - - if (!hasBufferView && !hasURI) { - if (err) { - (*err) += "Neither required `bufferView` nor `uri` defined for image[" + - std::to_string(image_idx) + "] name = \"" + image->name + - "\"\n"; - } - return false; - } - - ParseExtensionsProperty(&image->extensions, err, o); - ParseExtrasProperty(&image->extras, o); - - if (hasBufferView) { - 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 + - "\"\n"; - } - return false; - } - - std::string mime_type; - ParseStringProperty(&mime_type, err, o, "mimeType", false); - - int width = 0; - ParseIntegerProperty(&width, err, o, "width", 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 = bufferView; - image->mimeType = mime_type; - image->width = width; - image->height = height; - - return true; - } - - // Parse URI & Load image data. - - std::string uri; - std::string tmp_err; - if (!ParseStringProperty(&uri, &tmp_err, o, "uri", true)) { - if (err) { - (*err) += "Failed to parse `uri` for image[" + std::to_string(image_idx) + - "] name = \"" + image->name + "\".\n"; - } - return false; - } - - std::vector img; - - if (IsDataURI(uri)) { - if (!DecodeDataURI(&img, image->mimeType, uri, 0, false)) { - if (err) { - (*err) += "Failed to decode 'uri' for image[" + - std::to_string(image_idx) + "] name = [" + image->name + - "]\n"; - } - return false; - } - } else { - // Assume external file - // Keep texture path (for textures that cannot be decoded) - image->uri = uri; -#ifdef TINYGLTF_NO_EXTERNAL_IMAGE - return true; -#endif - if (!LoadExternalFile(&img, err, warn, uri, basedir, false, 0, false, fs)) { - if (warn) { - (*warn) += "Failed to load external 'uri' for image[" + - std::to_string(image_idx) + "] name = [" + image->name + - "]\n"; - } - // If the image cannot be loaded, keep uri as image->uri. - return true; - } - - if (img.empty()) { - if (warn) { - (*warn) += "Image data is empty for image[" + - std::to_string(image_idx) + "] name = [" + image->name + - "] \n"; - } - return false; - } - } - - if (*LoadImageData == nullptr) { - if (err) { - (*err) += "No LoadImageData callback specified.\n"; - } - return false; - } - return (*LoadImageData)(image, image_idx, err, warn, 0, 0, &img.at(0), - static_cast(img.size()), load_image_user_data); -} - -static bool ParseTexture(Texture *texture, std::string *err, const json &o, - const std::string &basedir) { - (void)basedir; - int sampler = -1; - int source = -1; - ParseIntegerProperty(&sampler, err, o, "sampler", false); - - ParseIntegerProperty(&source, err, o, "source", false); - - texture->sampler = sampler; - texture->source = source; - - ParseExtensionsProperty(&texture->extensions, err, o); - ParseExtrasProperty(&texture->extras, o); - - ParseStringProperty(&texture->name, err, o, "name", false); - - return true; -} - -static bool ParseTextureInfo(TextureInfo *texinfo, std::string *err, - const json &o) { - if (texinfo == nullptr) { - return false; - } - - if (!ParseIntegerProperty(&texinfo->index, err, o, "index", - /* required */ true, "TextureInfo")) { - return false; - } - - ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false); - - ParseExtensionsProperty(&texinfo->extensions, err, o); - ParseExtrasProperty(&texinfo->extras, o); - - return true; -} - -static bool ParseNormalTextureInfo(NormalTextureInfo *texinfo, std::string *err, - const json &o) { - if (texinfo == nullptr) { - return false; - } - - if (!ParseIntegerProperty(&texinfo->index, err, o, "index", - /* required */ true, "NormalTextureInfo")) { - return false; - } - - ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false); - ParseNumberProperty(&texinfo->scale, err, o, "scale", false); - - ParseExtensionsProperty(&texinfo->extensions, err, o); - ParseExtrasProperty(&texinfo->extras, o); - - return true; -} - -static bool ParseOcclusionTextureInfo(OcclusionTextureInfo *texinfo, - std::string *err, const json &o) { - if (texinfo == nullptr) { - return false; - } - - if (!ParseIntegerProperty(&texinfo->index, err, o, "index", - /* required */ true, "NormalTextureInfo")) { - return false; - } - - ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false); - ParseNumberProperty(&texinfo->strength, err, o, "strength", false); - - ParseExtensionsProperty(&texinfo->extensions, err, o); - ParseExtrasProperty(&texinfo->extras, o); - - return true; -} - -static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o, - FsCallbacks *fs, const std::string &basedir, - bool is_binary = false, - const unsigned char *bin_data = nullptr, - size_t bin_size = 0) { - size_t byteLength; - if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true, - "Buffer")) { - return false; - } - - // In glTF 2.0, uri is not mandatory anymore - buffer->uri.clear(); - ParseStringProperty(&buffer->uri, err, o, "uri", false, "Buffer"); - - // having an empty uri for a non embedded image should not be valid - if (!is_binary && buffer->uri.empty()) { - if (err) { - (*err) += "'uri' is missing from non binary glTF file buffer.\n"; - } - } - - json_const_iterator type; - if (FindMember(o, "type", type)) { - std::string typeStr; - if (GetString(GetValue(type), typeStr)) { - if (typeStr.compare("arraybuffer") == 0) { - // buffer.type = "arraybuffer"; - } - } - } - - 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, byteLength, - true)) { - if (err) { - (*err) += - "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n"; - } - return false; - } - } else { - // External .bin file. - if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, - buffer->uri, basedir, true, byteLength, true, - fs)) { - return false; - } - } - } else { - // load data from (embedded) binary data - - if ((bin_size == 0) || (bin_data == nullptr)) { - if (err) { - (*err) += "Invalid binary data in `Buffer'.\n"; - } - return false; - } - - if (byteLength > bin_size) { - if (err) { - std::stringstream ss; - ss << "Invalid `byteLength'. Must be equal or less than binary size: " - "`byteLength' = " - << byteLength << ", binary size = " << bin_size << std::endl; - (*err) += ss.str(); - } - return false; - } - - // Read buffer data - buffer->data.resize(static_cast(byteLength)); - memcpy(&(buffer->data.at(0)), bin_data, static_cast(byteLength)); - } - - } else { - if (IsDataURI(buffer->uri)) { - std::string mime_type; - if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength, - true)) { - if (err) { - (*err) += "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n"; - } - return false; - } - } else { - // Assume external .bin file. - if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, buffer->uri, - basedir, true, byteLength, true, fs)) { - return false; - } - } - } - - ParseStringProperty(&buffer->name, err, o, "name", false); - - return true; -} - -static bool ParseBufferView(BufferView *bufferView, std::string *err, - const json &o) { - int buffer = -1; - if (!ParseIntegerProperty(&buffer, err, o, "buffer", true, "BufferView")) { - return false; - } - - size_t byteOffset = 0; - ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false); - - size_t byteLength = 1; - if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true, - "BufferView")) { - return false; - } - - size_t byteStride = 0; - 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; - } - - if ((byteStride > 252) || ((byteStride % 4) != 0)) { - if (err) { - std::stringstream ss; - ss << "Invalid `byteStride' value. `byteStride' must be the multiple of " - "4 : " - << byteStride << std::endl; - - (*err) += ss.str(); - } - return false; - } - - int target = 0; - ParseIntegerProperty(&target, err, o, "target", false); - if ((target == TINYGLTF_TARGET_ARRAY_BUFFER) || - (target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)) { - // OK - } else { - target = 0; - } - bufferView->target = target; - - ParseStringProperty(&bufferView->name, err, o, "name", false); - - bufferView->buffer = buffer; - bufferView->byteOffset = byteOffset; - bufferView->byteLength = byteLength; - bufferView->byteStride = byteStride; - return true; -} - -static bool ParseSparseAccessor(Accessor *accessor, std::string *err, - const json &o) { - accessor->sparse.isSparse = true; - - int count = 0; - ParseIntegerProperty(&count, err, o, "count", true); - - json_const_iterator indices_iterator; - json_const_iterator values_iterator; - if (!FindMember(o, "indices", indices_iterator)) { - (*err) = "the sparse object of this accessor doesn't have indices"; - return false; - } - - if (!FindMember(o, "values", values_iterator)) { - (*err) = "the sparse object ob ths accessor doesn't have values"; - return false; - } - - const json &indices_obj = GetValue(indices_iterator); - const json &values_obj = GetValue(values_iterator); - - 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); - - 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 = 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 - - return true; -} - -static bool ParseAccessor(Accessor *accessor, std::string *err, const json &o) { - int bufferView = -1; - ParseIntegerProperty(&bufferView, err, o, "bufferView", false, "Accessor"); - - size_t byteOffset = 0; - ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false, "Accessor"); - - bool normalized = false; - ParseBooleanProperty(&normalized, err, o, "normalized", false, "Accessor"); - - size_t componentType = 0; - if (!ParseUnsignedProperty(&componentType, err, o, "componentType", true, - "Accessor")) { - return false; - } - - size_t count = 0; - if (!ParseUnsignedProperty(&count, err, o, "count", true, "Accessor")) { - return false; - } - - std::string type; - if (!ParseStringProperty(&type, err, o, "type", true, "Accessor")) { - return false; - } - - if (type.compare("SCALAR") == 0) { - accessor->type = TINYGLTF_TYPE_SCALAR; - } else if (type.compare("VEC2") == 0) { - accessor->type = TINYGLTF_TYPE_VEC2; - } else if (type.compare("VEC3") == 0) { - accessor->type = TINYGLTF_TYPE_VEC3; - } else if (type.compare("VEC4") == 0) { - accessor->type = TINYGLTF_TYPE_VEC4; - } else if (type.compare("MAT2") == 0) { - accessor->type = TINYGLTF_TYPE_MAT2; - } else if (type.compare("MAT3") == 0) { - accessor->type = TINYGLTF_TYPE_MAT3; - } else if (type.compare("MAT4") == 0) { - accessor->type = TINYGLTF_TYPE_MAT4; - } else { - std::stringstream ss; - ss << "Unsupported `type` for accessor object. Got \"" << type << "\"\n"; - if (err) { - (*err) += ss.str(); - } - return false; - } - - ParseStringProperty(&accessor->name, err, o, "name", false); - - accessor->minValues.clear(); - accessor->maxValues.clear(); - ParseNumberArrayProperty(&accessor->minValues, err, o, "min", false, - "Accessor"); - - ParseNumberArrayProperty(&accessor->maxValues, err, o, "max", false, - "Accessor"); - - accessor->count = count; - accessor->bufferView = bufferView; - accessor->byteOffset = byteOffset; - accessor->normalized = normalized; - { - if (componentType >= TINYGLTF_COMPONENT_TYPE_BYTE && - componentType <= TINYGLTF_COMPONENT_TYPE_DOUBLE) { - // OK - accessor->componentType = int(componentType); - } else { - std::stringstream ss; - ss << "Invalid `componentType` in accessor. Got " << componentType - << "\n"; - if (err) { - (*err) += ss.str(); - } - return false; - } - } - - ParseExtrasProperty(&(accessor->extras), o); - - // check if accessor has a "sparse" object: - json_const_iterator iterator; - if (FindMember(o, "sparse", iterator)) { - // here this accessor has a "sparse" subobject - return ParseSparseAccessor(accessor, err, GetValue(iterator)); - } - - return true; -} - -#ifdef TINYGLTF_ENABLE_DRACO - -static void DecodeIndexBuffer(draco::Mesh *mesh, size_t componentSize, - std::vector &outBuffer) { - if (componentSize == 4) { - assert(sizeof(mesh->face(draco::FaceIndex(0))[0]) == componentSize); - memcpy(outBuffer.data(), &mesh->face(draco::FaceIndex(0))[0], - outBuffer.size()); - } else { - size_t faceStride = componentSize * 3; - for (draco::FaceIndex f(0); f < mesh->num_faces(); ++f) { - const draco::Mesh::Face &face = mesh->face(f); - if (componentSize == 2) { - uint16_t indices[3] = {(uint16_t)face[0].value(), - (uint16_t)face[1].value(), - (uint16_t)face[2].value()}; - memcpy(outBuffer.data() + f.value() * faceStride, &indices[0], - faceStride); - } else { - uint8_t indices[3] = {(uint8_t)face[0].value(), - (uint8_t)face[1].value(), - (uint8_t)face[2].value()}; - memcpy(outBuffer.data() + f.value() * faceStride, &indices[0], - faceStride); - } - } - } -} - -template -static bool GetAttributeForAllPoints(draco::Mesh *mesh, - const draco::PointAttribute *pAttribute, - std::vector &outBuffer) { - size_t byteOffset = 0; - T values[4] = {0, 0, 0, 0}; - for (draco::PointIndex i(0); i < mesh->num_points(); ++i) { - const draco::AttributeValueIndex val_index = pAttribute->mapped_index(i); - if (!pAttribute->ConvertValue(val_index, pAttribute->num_components(), - values)) - return false; - - memcpy(outBuffer.data() + byteOffset, &values[0], - sizeof(T) * pAttribute->num_components()); - byteOffset += sizeof(T) * pAttribute->num_components(); - } - - return true; -} - -static bool GetAttributeForAllPoints(uint32_t componentType, draco::Mesh *mesh, - const draco::PointAttribute *pAttribute, - std::vector &outBuffer) { - bool decodeResult = false; - switch (componentType) { - case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: - decodeResult = - GetAttributeForAllPoints(mesh, pAttribute, outBuffer); - break; - case TINYGLTF_COMPONENT_TYPE_BYTE: - decodeResult = - GetAttributeForAllPoints(mesh, pAttribute, outBuffer); - break; - case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: - decodeResult = - GetAttributeForAllPoints(mesh, pAttribute, outBuffer); - break; - case TINYGLTF_COMPONENT_TYPE_SHORT: - decodeResult = - GetAttributeForAllPoints(mesh, pAttribute, outBuffer); - break; - case TINYGLTF_COMPONENT_TYPE_INT: - decodeResult = - GetAttributeForAllPoints(mesh, pAttribute, outBuffer); - break; - case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT: - decodeResult = - GetAttributeForAllPoints(mesh, pAttribute, outBuffer); - break; - case TINYGLTF_COMPONENT_TYPE_FLOAT: - decodeResult = - GetAttributeForAllPoints(mesh, pAttribute, outBuffer); - break; - case TINYGLTF_COMPONENT_TYPE_DOUBLE: - decodeResult = - GetAttributeForAllPoints(mesh, pAttribute, outBuffer); - break; - default: - return false; - } - - return decodeResult; -} - -static bool ParseDracoExtension(Primitive *primitive, Model *model, - std::string *err, - const Value &dracoExtensionValue) { - auto bufferViewValue = dracoExtensionValue.Get("bufferView"); - if (!bufferViewValue.IsInt()) return false; - auto attributesValue = dracoExtensionValue.Get("attributes"); - if (!attributesValue.IsObject()) return false; - - auto attributesObject = attributesValue.Get(); - int bufferView = bufferViewValue.Get(); - - BufferView &view = model->bufferViews[bufferView]; - Buffer &buffer = model->buffers[view.buffer]; - // BufferView has already been decoded - if (view.dracoDecoded) return true; - view.dracoDecoded = true; - - const char *bufferViewData = - reinterpret_cast(buffer.data.data() + view.byteOffset); - size_t bufferViewSize = view.byteLength; - - // decode draco - draco::DecoderBuffer decoderBuffer; - decoderBuffer.Init(bufferViewData, bufferViewSize); - draco::Decoder decoder; - auto decodeResult = decoder.DecodeMeshFromBuffer(&decoderBuffer); - if (!decodeResult.ok()) { - return false; - } - const std::unique_ptr &mesh = decodeResult.value(); - - // create new bufferView for indices - if (primitive->indices >= 0) { - int32_t componentSize = GetComponentSizeInBytes( - model->accessors[primitive->indices].componentType); - Buffer decodedIndexBuffer; - decodedIndexBuffer.data.resize(mesh->num_faces() * 3 * componentSize); - - DecodeIndexBuffer(mesh.get(), componentSize, decodedIndexBuffer.data); - - model->buffers.emplace_back(std::move(decodedIndexBuffer)); - - BufferView decodedIndexBufferView; - decodedIndexBufferView.buffer = int(model->buffers.size() - 1); - decodedIndexBufferView.byteLength = - int(mesh->num_faces() * 3 * componentSize); - decodedIndexBufferView.byteOffset = 0; - decodedIndexBufferView.byteStride = 0; - decodedIndexBufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER; - model->bufferViews.emplace_back(std::move(decodedIndexBufferView)); - - model->accessors[primitive->indices].bufferView = - int(model->bufferViews.size() - 1); - model->accessors[primitive->indices].count = int(mesh->num_faces() * 3); - } - - for (const auto &attribute : attributesObject) { - if (!attribute.second.IsInt()) return false; - auto primitiveAttribute = primitive->attributes.find(attribute.first); - if (primitiveAttribute == primitive->attributes.end()) return false; - - int dracoAttributeIndex = attribute.second.Get(); - const auto pAttribute = mesh->GetAttributeByUniqueId(dracoAttributeIndex); - const auto pBuffer = pAttribute->buffer(); - const auto componentType = - model->accessors[primitiveAttribute->second].componentType; - - // Create a new buffer for this decoded buffer - Buffer decodedBuffer; - size_t bufferSize = mesh->num_points() * pAttribute->num_components() * - GetComponentSizeInBytes(componentType); - decodedBuffer.data.resize(bufferSize); - - if (!GetAttributeForAllPoints(componentType, mesh.get(), pAttribute, - decodedBuffer.data)) - return false; - - model->buffers.emplace_back(std::move(decodedBuffer)); - - BufferView decodedBufferView; - decodedBufferView.buffer = int(model->buffers.size() - 1); - decodedBufferView.byteLength = bufferSize; - decodedBufferView.byteOffset = pAttribute->byte_offset(); - decodedBufferView.byteStride = pAttribute->byte_stride(); - decodedBufferView.target = primitive->indices >= 0 - ? TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER - : TINYGLTF_TARGET_ARRAY_BUFFER; - model->bufferViews.emplace_back(std::move(decodedBufferView)); - - model->accessors[primitiveAttribute->second].bufferView = - int(model->bufferViews.size() - 1); - model->accessors[primitiveAttribute->second].count = - int(mesh->num_points()); - } - - return true; -} -#endif - -static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err, - const json &o) { - int material = -1; - ParseIntegerProperty(&material, err, o, "material", false); - primitive->material = material; - - int mode = TINYGLTF_MODE_TRIANGLES; - ParseIntegerProperty(&mode, err, o, "mode", false); - primitive->mode = mode; // Why only triangled were supported ? - - int indices = -1; - ParseIntegerProperty(&indices, err, o, "indices", false); - primitive->indices = indices; - if (!ParseStringIntegerProperty(&primitive->attributes, err, o, "attributes", - true, "Primitive")) { - return false; - } - - // Look for morph targets - json_const_iterator targetsObject; - if (FindMember(o, "targets", targetsObject) && - IsArray(GetValue(targetsObject))) { - auto targetsObjectEnd = ArrayEnd(GetValue(targetsObject)); - for (json_const_array_iterator i = ArrayBegin(GetValue(targetsObject)); - i != targetsObjectEnd; ++i) { - std::map targetAttribues; - - const json &dict = *i; - if (IsObject(dict)) { - json_const_iterator dictIt(ObjectBegin(dict)); - json_const_iterator dictItEnd(ObjectEnd(dict)); - - for (; dictIt != dictItEnd; ++dictIt) { - int iVal; - if (GetInt(GetValue(dictIt), iVal)) - targetAttribues[GetKey(dictIt)] = iVal; - } - primitive->targets.emplace_back(std::move(targetAttribues)); - } - } - } - - ParseExtrasProperty(&(primitive->extras), o); - - ParseExtensionsProperty(&primitive->extensions, err, o); - -#ifdef TINYGLTF_ENABLE_DRACO - auto dracoExtension = - primitive->extensions.find("KHR_draco_mesh_compression"); - if (dracoExtension != primitive->extensions.end()) { - ParseDracoExtension(primitive, model, err, dracoExtension->second); - } -#else - (void)model; -#endif - - return true; -} - -static bool ParseMesh(Mesh *mesh, Model *model, std::string *err, - const json &o) { - ParseStringProperty(&mesh->name, err, o, "name", false); - - mesh->primitives.clear(); - json_const_iterator primObject; - if (FindMember(o, "primitives", primObject) && - IsArray(GetValue(primObject))) { - json_const_array_iterator primEnd = ArrayEnd(GetValue(primObject)); - for (json_const_array_iterator i = ArrayBegin(GetValue(primObject)); - i != primEnd; ++i) { - Primitive primitive; - if (ParsePrimitive(&primitive, model, err, *i)) { - // Only add the primitive if the parsing succeeds. - mesh->primitives.emplace_back(std::move(primitive)); - } - } - } - - // Should probably check if has targets and if dimensions fit - ParseNumberArrayProperty(&mesh->weights, err, o, "weights", false); - - ParseExtensionsProperty(&mesh->extensions, err, o); - ParseExtrasProperty(&(mesh->extras), o); - - return true; -} - -static bool ParseNode(Node *node, std::string *err, const json &o) { - ParseStringProperty(&node->name, err, o, "name", false); - - 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)) { - ParseNumberArrayProperty(&node->rotation, err, o, "rotation", false); - ParseNumberArrayProperty(&node->scale, err, o, "scale", false); - ParseNumberArrayProperty(&node->translation, err, o, "translation", false); - } - - int camera = -1; - ParseIntegerProperty(&camera, err, o, "camera", false); - node->camera = camera; - - int mesh = -1; - ParseIntegerProperty(&mesh, err, o, "mesh", false); - node->mesh = mesh; - - node->children.clear(); - ParseIntegerArrayProperty(&node->children, err, o, "children", false); - - ParseNumberArrayProperty(&node->weights, err, o, "weights", false); - - ParseExtensionsProperty(&node->extensions, err, o); - ParseExtrasProperty(&(node->extras), o); - - return true; -} - -static bool ParsePbrMetallicRoughness(PbrMetallicRoughness *pbr, - std::string *err, const json &o) { - if (pbr == nullptr) { - return false; - } - - std::vector baseColorFactor; - if (ParseNumberArrayProperty(&baseColorFactor, err, o, "baseColorFactor", - /* required */ false)) { - if (baseColorFactor.size() != 4) { - if (err) { - (*err) += - "Array length of `baseColorFactor` parameter in " - "pbrMetallicRoughness must be 4, but got " + - std::to_string(baseColorFactor.size()) + "\n"; - } - return false; - } - } else { - // fill with default values - baseColorFactor = {1.0, 1.0, 1.0, 1.0}; - } - pbr->baseColorFactor = baseColorFactor; - - { - json_const_iterator it; - if (FindMember(o, "baseColorTexture", it)) { - ParseTextureInfo(&pbr->baseColorTexture, err, GetValue(it)); - } - } - - { - json_const_iterator it; - if (FindMember(o, "metallicRoughnessTexture", it)) { - ParseTextureInfo(&pbr->metallicRoughnessTexture, err, GetValue(it)); - } - } - - ParseNumberProperty(&pbr->metallicFactor, err, o, "metallicFactor", false); - ParseNumberProperty(&pbr->roughnessFactor, err, o, "roughnessFactor", false); - - ParseExtensionsProperty(&pbr->extensions, err, o); - ParseExtrasProperty(&pbr->extras, o); - - return true; -} - -static bool ParseMaterial(Material *material, std::string *err, const json &o) { - ParseStringProperty(&material->name, err, o, "name", /* required */ false); - - if (ParseNumberArrayProperty(&material->emissiveFactor, err, o, - "emissiveFactor", - /* required */ false)) { - if (material->emissiveFactor.size() != 3) { - if (err) { - (*err) += - "Array length of `emissiveFactor` parameter in " - "material must be 3, but got " + - std::to_string(material->emissiveFactor.size()) + "\n"; - } - return false; - } - } else { - // fill with default values - material->emissiveFactor = {0.0, 0.0, 0.0}; - } - - ParseStringProperty(&material->alphaMode, err, o, "alphaMode", - /* required */ false); - ParseNumberProperty(&material->alphaCutoff, err, o, "alphaCutoff", - /* required */ false); - ParseBooleanProperty(&material->doubleSided, err, o, "doubleSided", - /* required */ false); - - { - json_const_iterator it; - if (FindMember(o, "pbrMetallicRoughness", it)) { - ParsePbrMetallicRoughness(&material->pbrMetallicRoughness, err, - GetValue(it)); - } - } - - { - json_const_iterator it; - if (FindMember(o, "normalTexture", it)) { - ParseNormalTextureInfo(&material->normalTexture, err, GetValue(it)); - } - } - - { - json_const_iterator it; - if (FindMember(o, "occlusionTexture", it)) { - ParseOcclusionTextureInfo(&material->occlusionTexture, err, GetValue(it)); - } - } - - { - json_const_iterator it; - if (FindMember(o, "emissiveTexture", it)) { - ParseTextureInfo(&material->emissiveTexture, err, GetValue(it)); - } - } - - // Old code path. For backward compatibility, we still store material values - // as Parameter. This will create duplicated information for - // example(pbrMetallicRoughness), but should be neglible in terms of memory - // consumption. - // TODO(syoyo): Remove in the next major release. - material->values.clear(); - material->additionalValues.clear(); - - json_const_iterator it(ObjectBegin(o)); - json_const_iterator itEnd(ObjectEnd(o)); - - for (; it != itEnd; ++it) { - std::string key(GetKey(it)); - if (key == "pbrMetallicRoughness") { - if (IsObject(GetValue(it))) { - const json &values_object = GetValue(it); - - 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)); - } - } - } - } 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)); - } - } - } - - material->extensions.clear(); - ParseExtensionsProperty(&material->extensions, err, o); - ParseExtrasProperty(&(material->extras), o); - - return true; -} - -static bool ParseAnimationChannel(AnimationChannel *channel, std::string *err, - const json &o) { - 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"; - } - return false; - } - - json_const_iterator targetIt; - if (FindMember(o, "target", targetIt) && IsObject(GetValue(targetIt))) { - const json &target_object = GetValue(targetIt); - - if (!ParseIntegerProperty(&targetIndex, err, target_object, "node", true)) { - if (err) { - (*err) += "`node` field is missing in animation.channels.target\n"; - } - return false; - } - - if (!ParseStringProperty(&channel->target_path, err, target_object, "path", - true)) { - if (err) { - (*err) += "`path` field is missing in animation.channels.target\n"; - } - return false; - } - } - - channel->sampler = samplerIndex; - channel->target_node = targetIndex; - - ParseExtensionsProperty(&channel->extensions, err, o); - ParseExtrasProperty(&(channel->extras), o); - - return true; -} - -static bool ParseAnimation(Animation *animation, std::string *err, - const json &o) { - { - json_const_iterator channelsIt; - if (FindMember(o, "channels", channelsIt) && - IsArray(GetValue(channelsIt))) { - json_const_array_iterator channelEnd = ArrayEnd(GetValue(channelsIt)); - for (json_const_array_iterator i = ArrayBegin(GetValue(channelsIt)); - i != channelEnd; ++i) { - AnimationChannel channel; - if (ParseAnimationChannel(&channel, err, *i)) { - // Only add the channel if the parsing succeeds. - animation->channels.emplace_back(std::move(channel)); - } - } - } - } - - { - json_const_iterator samplerIt; - if (FindMember(o, "samplers", samplerIt) && IsArray(GetValue(samplerIt))) { - const json &sampler_array = GetValue(samplerIt); - - json_const_array_iterator it = ArrayBegin(sampler_array); - json_const_array_iterator itEnd = ArrayEnd(sampler_array); - - for (; it != itEnd; ++it) { - const json &s = *it; - - AnimationSampler sampler; - int inputIndex = -1; - int outputIndex = -1; - if (!ParseIntegerProperty(&inputIndex, err, s, "input", true)) { - if (err) { - (*err) += "`input` field is missing in animation.sampler\n"; - } - return false; - } - ParseStringProperty(&sampler.interpolation, err, s, "interpolation", - false); - if (!ParseIntegerProperty(&outputIndex, err, s, "output", true)) { - if (err) { - (*err) += "`output` field is missing in animation.sampler\n"; - } - return false; - } - sampler.input = inputIndex; - sampler.output = outputIndex; - ParseExtrasProperty(&(sampler.extras), s); - animation->samplers.emplace_back(std::move(sampler)); - } - } - } - - ParseStringProperty(&animation->name, err, o, "name", false); - - ParseExtensionsProperty(&animation->extensions, err, o); - ParseExtrasProperty(&(animation->extras), o); - - return true; -} - -static bool ParseSampler(Sampler *sampler, std::string *err, const json &o) { - ParseStringProperty(&sampler->name, err, o, "name", false); - - int minFilter = -1; - int magFilter = -1; - int wrapS = TINYGLTF_TEXTURE_WRAP_REPEAT; - int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT; - int wrapR = 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); - ParseIntegerProperty(&wrapR, err, o, "wrapR", false); // tinygltf extension - - // TODO(syoyo): Check the value is alloed one. - // (e.g. we allow 9728(NEAREST), but don't allow 9727) - - sampler->minFilter = minFilter; - sampler->magFilter = magFilter; - sampler->wrapS = wrapS; - sampler->wrapT = wrapT; - sampler->wrapR = wrapR; - - ParseExtrasProperty(&(sampler->extras), o); - - return true; -} - -static bool ParseSkin(Skin *skin, std::string *err, const json &o) { - ParseStringProperty(&skin->name, err, o, "name", false, "Skin"); - - std::vector joints; - if (!ParseIntegerArrayProperty(&joints, err, o, "joints", false, "Skin")) { - return false; - } - skin->joints = std::move(joints); - - int skeleton = -1; - ParseIntegerProperty(&skeleton, err, o, "skeleton", false, "Skin"); - skin->skeleton = skeleton; - - int invBind = -1; - ParseIntegerProperty(&invBind, err, o, "inverseBindMatrices", true, "Skin"); - skin->inverseBindMatrices = invBind; - - return true; -} - -static bool ParsePerspectiveCamera(PerspectiveCamera *camera, std::string *err, - const json &o) { - double yfov = 0.0; - if (!ParseNumberProperty(&yfov, err, o, "yfov", true, "OrthographicCamera")) { - return false; - } - - double znear = 0.0; - if (!ParseNumberProperty(&znear, err, o, "znear", true, - "PerspectiveCamera")) { - return false; - } - - double aspectRatio = 0.0; // = invalid - ParseNumberProperty(&aspectRatio, err, o, "aspectRatio", false, - "PerspectiveCamera"); - - double zfar = 0.0; // = invalid - ParseNumberProperty(&zfar, err, o, "zfar", false, "PerspectiveCamera"); - - camera->aspectRatio = aspectRatio; - camera->zfar = zfar; - camera->yfov = yfov; - camera->znear = znear; - - ParseExtensionsProperty(&camera->extensions, err, o); - ParseExtrasProperty(&(camera->extras), o); - - // TODO(syoyo): Validate parameter values. - - return true; -} - -static bool ParseSpotLight(SpotLight *light, std::string *err, const json &o) { - ParseNumberProperty(&light->innerConeAngle, err, o, "innerConeAngle", false); - ParseNumberProperty(&light->outerConeAngle, err, o, "outerConeAngle", false); - - ParseExtensionsProperty(&light->extensions, err, o); - ParseExtrasProperty(&light->extras, o); - - // TODO(syoyo): Validate parameter values. - - return true; -} - -static bool ParseOrthographicCamera(OrthographicCamera *camera, - std::string *err, const json &o) { - double xmag = 0.0; - if (!ParseNumberProperty(&xmag, err, o, "xmag", true, "OrthographicCamera")) { - return false; - } - - double ymag = 0.0; - if (!ParseNumberProperty(&ymag, err, o, "ymag", true, "OrthographicCamera")) { - return false; - } - - double zfar = 0.0; - if (!ParseNumberProperty(&zfar, err, o, "zfar", true, "OrthographicCamera")) { - return false; - } - - double znear = 0.0; - if (!ParseNumberProperty(&znear, err, o, "znear", true, - "OrthographicCamera")) { - return false; - } - - ParseExtensionsProperty(&camera->extensions, err, o); - ParseExtrasProperty(&(camera->extras), o); - - camera->xmag = xmag; - camera->ymag = ymag; - camera->zfar = zfar; - camera->znear = znear; - - // TODO(syoyo): Validate parameter values. - - return true; -} - -static bool ParseCamera(Camera *camera, std::string *err, const json &o) { - if (!ParseStringProperty(&camera->type, err, o, "type", true, "Camera")) { - return false; - } - - if (camera->type.compare("orthographic") == 0) { - json_const_iterator orthoIt; - if (!FindMember(o, "orthographic", orthoIt)) { - if (err) { - std::stringstream ss; - ss << "Orhographic camera description not found." << std::endl; - (*err) += ss.str(); - } - return false; - } - - const json &v = GetValue(orthoIt); - if (!IsObject(v)) { - if (err) { - std::stringstream ss; - ss << "\"orthographic\" is not a JSON object." << std::endl; - (*err) += ss.str(); - } - return false; - } - - if (!ParseOrthographicCamera(&camera->orthographic, err, v)) { - return false; - } - } else if (camera->type.compare("perspective") == 0) { - json_const_iterator perspIt; - if (!FindMember(o, "perspective", perspIt)) { - if (err) { - std::stringstream ss; - ss << "Perspective camera description not found." << std::endl; - (*err) += ss.str(); - } - return false; - } - - const json &v = GetValue(perspIt); - if (!IsObject(v)) { - if (err) { - std::stringstream ss; - ss << "\"perspective\" is not a JSON object." << std::endl; - (*err) += ss.str(); - } - return false; - } - - if (!ParsePerspectiveCamera(&camera->perspective, err, v)) { - return false; - } - } else { - if (err) { - std::stringstream ss; - ss << "Invalid camera type: \"" << camera->type - << "\". Must be \"perspective\" or \"orthographic\"" << std::endl; - (*err) += ss.str(); - } - return false; - } - - ParseStringProperty(&camera->name, err, o, "name", false); - - ParseExtensionsProperty(&camera->extensions, err, o); - ParseExtrasProperty(&(camera->extras), o); - - return true; -} - -static bool ParseLight(Light *light, std::string *err, const json &o) { - if (!ParseStringProperty(&light->type, err, o, "type", true)) { - return false; - } - - if (light->type == "spot") { - json_const_iterator spotIt; - if (!FindMember(o, "spot", spotIt)) { - if (err) { - std::stringstream ss; - ss << "Spot light description not found." << std::endl; - (*err) += ss.str(); - } - return false; - } - - const json &v = GetValue(spotIt); - if (!IsObject(v)) { - if (err) { - std::stringstream ss; - ss << "\"spot\" is not a JSON object." << std::endl; - (*err) += ss.str(); - } - return false; - } - - if (!ParseSpotLight(&light->spot, err, v)) { - return false; - } - } - - ParseStringProperty(&light->name, err, o, "name", false); - ParseNumberArrayProperty(&light->color, err, o, "color", false); - ParseNumberProperty(&light->range, err, o, "range", false); - ParseNumberProperty(&light->intensity, err, o, "intensity", false); - ParseExtensionsProperty(&light->extensions, err, o); - ParseExtrasProperty(&(light->extras), o); - return true; -} - -bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, - const char *str, unsigned int length, - const std::string &base_dir, - unsigned int check_sections) { - if (length < 4) { - if (err) { - (*err) = "JSON string too short.\n"; - } - return false; - } - - JsonDocument v; - -#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \ - defined(_CPPUNWIND)) && \ - not defined(TINYGLTF_NOEXCEPTION) - try { - JsonParse(v, str, length, true); - - } catch (const std::exception &e) { - if (err) { - (*err) = e.what(); - } - return false; - } -#else - { - JsonParse(v, str, length); - - if (!IsObject(v)) { - // Assume parsing was failed. - if (err) { - (*err) = "Failed to parse JSON object\n"; - } - return false; - } - } -#endif - - if (!IsObject(v)) { - // root is not an object. - if (err) { - (*err) = "Root element is not a JSON object\n"; - } - return false; - } - - { - bool version_found = false; - json_const_iterator it; - if (FindMember(v, "asset", it) && IsObject(GetValue(it))) { - auto &itObj = GetValue(it); - json_const_iterator version_it; - std::string versionStr; - if (FindMember(itObj, "version", version_it) && - GetString(GetValue(version_it), versionStr)) { - version_found = true; - } - } - if (version_found) { - // OK - } else if (check_sections & REQUIRE_VERSION) { - if (err) { - (*err) += "\"asset\" object not found in .gltf or not an object type\n"; - } - return false; - } - } - - // scene is not mandatory. - // FIXME Maybe a better way to handle it than removing the code - - auto IsArrayMemberPresent = [](const json &v, const char *name) -> bool { - json_const_iterator it; - return FindMember(v, name, it) && IsArray(GetValue(it)); - }; - - { - if ((check_sections & REQUIRE_SCENES) && - !IsArrayMemberPresent(v, "scenes")) { - if (err) { - (*err) += "\"scenes\" object not found in .gltf or not an array type\n"; - } - return false; - } - } - - { - if ((check_sections & REQUIRE_NODES) && !IsArrayMemberPresent(v, "nodes")) { - if (err) { - (*err) += "\"nodes\" object not found in .gltf\n"; - } - return false; - } - } - - { - if ((check_sections & REQUIRE_ACCESSORS) && - !IsArrayMemberPresent(v, "accessors")) { - if (err) { - (*err) += "\"accessors\" object not found in .gltf\n"; - } - return false; - } - } - - { - if ((check_sections & REQUIRE_BUFFERS) && - !IsArrayMemberPresent(v, "buffers")) { - if (err) { - (*err) += "\"buffers\" object not found in .gltf\n"; - } - return false; - } - } - - { - if ((check_sections & REQUIRE_BUFFER_VIEWS) && - !IsArrayMemberPresent(v, "bufferViews")) { - if (err) { - (*err) += "\"bufferViews\" object not found in .gltf\n"; - } - return false; - } - } - - model->buffers.clear(); - model->bufferViews.clear(); - model->accessors.clear(); - model->meshes.clear(); - model->cameras.clear(); - model->nodes.clear(); - model->extensionsUsed.clear(); - model->extensionsRequired.clear(); - model->extensions.clear(); - model->defaultScene = -1; - - // 1. Parse Asset - { - json_const_iterator it; - if (FindMember(v, "asset", it) && IsObject(GetValue(it))) { - const json &root = GetValue(it); - - ParseAsset(&model->asset, err, root); - } - } - -#ifdef TINYGLTF_USE_CPP14 - auto ForEachInArray = [](const json &v, const char *member, - const auto &cb) -> bool -#else - // The std::function<> implementation can be less efficient because it will - // allocate heap when the size of the captured lambda is above 16 bytes with - // clang and gcc, but it does not require C++14. - auto ForEachInArray = [](const json &v, const char *member, - const std::function &cb) -> bool -#endif - { - json_const_iterator it; - if (FindMember(v, member, it) && IsArray(GetValue(it))) { - const json &root = GetValue(it); - auto it = ArrayBegin(root); - auto end = ArrayEnd(root); - for (; it != end; ++it) { - if (!cb(*it)) return false; - } - } - return true; - }; - - // 2. Parse extensionUsed - { - ForEachInArray(v, "extensionsUsed", [&](const json &o) { - std::string str; - GetString(o, str); - model->extensionsUsed.emplace_back(std::move(str)); - return true; - }); - } - - { - ForEachInArray(v, "extensionsRequired", [&](const json &o) { - std::string str; - GetString(o, str); - model->extensionsRequired.emplace_back(std::move(str)); - return true; - }); - } - - // 3. Parse Buffer - { - bool success = ForEachInArray(v, "buffers", [&](const json &o) { - if (!IsObject(o)) { - if (err) { - (*err) += "`buffers' does not contain an JSON object."; - } - return false; - } - Buffer buffer; - if (!ParseBuffer(&buffer, err, o, &fs, base_dir, is_binary_, bin_data_, - bin_size_)) { - return false; - } - - model->buffers.emplace_back(std::move(buffer)); - return true; - }); - - if (!success) { - return false; - } - } - // 4. Parse BufferView - { - bool success = ForEachInArray(v, "bufferViews", [&](const json &o) { - if (!IsObject(o)) { - if (err) { - (*err) += "`bufferViews' does not contain an JSON object."; - } - return false; - } - BufferView bufferView; - if (!ParseBufferView(&bufferView, err, o)) { - return false; - } - - model->bufferViews.emplace_back(std::move(bufferView)); - return true; - }); - - if (!success) { - return false; - } - } - - // 5. Parse Accessor - { - bool success = ForEachInArray(v, "accessors", [&](const json &o) { - if (!IsObject(o)) { - if (err) { - (*err) += "`accessors' does not contain an JSON object."; - } - return false; - } - Accessor accessor; - if (!ParseAccessor(&accessor, err, o)) { - return false; - } - - model->accessors.emplace_back(std::move(accessor)); - return true; - }); - - if (!success) { - return false; - } - } - - // 6. Parse Mesh - { - bool success = ForEachInArray(v, "meshes", [&](const json &o) { - if (!IsObject(o)) { - if (err) { - (*err) += "`meshes' does not contain an JSON object."; - } - return false; - } - Mesh mesh; - if (!ParseMesh(&mesh, model, err, o)) { - return false; - } - - model->meshes.emplace_back(std::move(mesh)); - return true; - }); - - if (!success) { - return false; - } - } - - // Assign missing bufferView target types - // - Look for missing Mesh indices - // - Look for missing bufferView targets - for (auto &mesh : model->meshes) { - for (auto &primitive : mesh.primitives) { - if (primitive.indices > - -1) // has indices from parsing step, must be Element Array Buffer - { - if (size_t(primitive.indices) >= model->accessors.size()) { - if (err) { - (*err) += "primitive indices accessor out of bounds"; - } - return false; - } - - auto bufferView = - model->accessors[size_t(primitive.indices)].bufferView; - if (bufferView < 0 || size_t(bufferView) >= model->bufferViews.size()) { - if (err) { - (*err) += "accessor[" + std::to_string(primitive.indices) + - "] invalid bufferView"; - } - return false; - } - - model->bufferViews[size_t(bufferView)].target = - TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER; - // we could optionally check if acessors' bufferView type is Scalar, as - // it should be - } - } - } - // find any missing targets, must be an array buffer type if not fulfilled - // from previous check - for (auto &bufferView : model->bufferViews) { - if (bufferView.target == 0) // missing target type - { - bufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER; - } - } - - // 7. Parse Node - { - bool success = ForEachInArray(v, "nodes", [&](const json &o) { - if (!IsObject(o)) { - if (err) { - (*err) += "`nodes' does not contain an JSON object."; - } - return false; - } - Node node; - if (!ParseNode(&node, err, o)) { - return false; - } - - model->nodes.emplace_back(std::move(node)); - return true; - }); - - if (!success) { - return false; - } - } - - // 8. Parse scenes. - { - bool success = ForEachInArray(v, "scenes", [&](const json &o) { - if (!IsObject(o)) { - if (err) { - (*err) += "`scenes' does not contain an JSON object."; - } - return false; - } - std::vector nodes; - ParseIntegerArrayProperty(&nodes, err, o, "nodes", false); - - Scene scene; - scene.nodes = std::move(nodes); - - ParseStringProperty(&scene.name, err, o, "name", false); - - ParseExtensionsProperty(&scene.extensions, err, o); - ParseExtrasProperty(&scene.extras, o); - - model->scenes.emplace_back(std::move(scene)); - return true; - }); - - if (!success) { - return false; - } - } - - // 9. Parse default scenes. - { - json_const_iterator rootIt; - int iVal; - if (FindMember(v, "scene", rootIt) && GetInt(GetValue(rootIt), iVal)) { - model->defaultScene = iVal; - } - } - - // 10. Parse Material - { - bool success = ForEachInArray(v, "materials", [&](const json &o) { - if (!IsObject(o)) { - if (err) { - (*err) += "`materials' does not contain an JSON object."; - } - return false; - } - Material material; - ParseStringProperty(&material.name, err, o, "name", false); - - if (!ParseMaterial(&material, err, o)) { - return false; - } - - model->materials.emplace_back(std::move(material)); - return true; - }); - - if (!success) { - return false; - } - } - - // 11. Parse Image - { - int idx = 0; - bool success = ForEachInArray(v, "images", [&](const json &o) { - if (!IsObject(o)) { - if (err) { - (*err) += "image[" + std::to_string(idx) + "] is not a JSON object."; - } - return false; - } - Image image; - if (!ParseImage(&image, idx, err, warn, o, base_dir, &fs, - &this->LoadImageData, load_image_user_data_)) { - return false; - } - - if (image.bufferView != -1) { - // Load image from the buffer view. - if (size_t(image.bufferView) >= model->bufferViews.size()) { - if (err) { - std::stringstream ss; - ss << "image[" << idx << "] bufferView \"" << image.bufferView - << "\" not found in the scene." << std::endl; - (*err) += ss.str(); - } - return false; - } - - const BufferView &bufferView = - model->bufferViews[size_t(image.bufferView)]; - if (size_t(bufferView.buffer) >= model->buffers.size()) { - if (err) { - std::stringstream ss; - ss << "image[" << idx << "] buffer \"" << bufferView.buffer - << "\" not found in the scene." << std::endl; - (*err) += ss.str(); - } - return false; - } - const Buffer &buffer = model->buffers[size_t(bufferView.buffer)]; - - if (*LoadImageData == nullptr) { - if (err) { - (*err) += "No LoadImageData callback specified.\n"; - } - return false; - } - bool ret = LoadImageData( - &image, idx, err, warn, image.width, image.height, - &buffer.data[bufferView.byteOffset], - static_cast(bufferView.byteLength), load_image_user_data_); - if (!ret) { - return false; - } - } - - model->images.emplace_back(std::move(image)); - ++idx; - return true; - }); - - if (!success) { - return false; - } - } - - // 12. Parse Texture - { - bool success = ForEachInArray(v, "textures", [&](const json &o) { - if (!IsObject(o)) { - if (err) { - (*err) += "`textures' does not contain an JSON object."; - } - return false; - } - Texture texture; - if (!ParseTexture(&texture, err, o, base_dir)) { - return false; - } - - model->textures.emplace_back(std::move(texture)); - return true; - }); - - if (!success) { - return false; - } - } - - // 13. Parse Animation - { - bool success = ForEachInArray(v, "animations", [&](const json &o) { - if (!IsObject(o)) { - if (err) { - (*err) += "`animations' does not contain an JSON object."; - } - return false; - } - Animation animation; - if (!ParseAnimation(&animation, err, o)) { - return false; - } - - model->animations.emplace_back(std::move(animation)); - return true; - }); - - if (!success) { - return false; - } - } - - // 14. Parse Skin - { - bool success = ForEachInArray(v, "skins", [&](const json &o) { - if (!IsObject(o)) { - if (err) { - (*err) += "`skins' does not contain an JSON object."; - } - return false; - } - Skin skin; - if (!ParseSkin(&skin, err, o)) { - return false; - } - - model->skins.emplace_back(std::move(skin)); - return true; - }); - - if (!success) { - return false; - } - } - - // 15. Parse Sampler - { - bool success = ForEachInArray(v, "samplers", [&](const json &o) { - if (!IsObject(o)) { - if (err) { - (*err) += "`samplers' does not contain an JSON object."; - } - return false; - } - Sampler sampler; - if (!ParseSampler(&sampler, err, o)) { - return false; - } - - model->samplers.emplace_back(std::move(sampler)); - return true; - }); - - if (!success) { - return false; - } - } - - // 16. Parse Camera - { - bool success = ForEachInArray(v, "cameras", [&](const json &o) { - if (!IsObject(o)) { - if (err) { - (*err) += "`cameras' does not contain an JSON object."; - } - return false; - } - Camera camera; - if (!ParseCamera(&camera, err, o)) { - return false; - } - - model->cameras.emplace_back(std::move(camera)); - return true; - }); - - if (!success) { - return false; - } - } - - // 17. Parse Extensions - ParseExtensionsProperty(&model->extensions, err, v); - - // 18. Specific extension implementations - { - json_const_iterator rootIt; - if (FindMember(v, "extensions", rootIt) && IsObject(GetValue(rootIt))) { - const json &root = GetValue(rootIt); - - json_const_iterator it(ObjectBegin(root)); - json_const_iterator itEnd(ObjectEnd(root)); - for (; it != itEnd; ++it) { - // parse KHR_lights_punctual extension - std::string key(GetKey(it)); - if ((key == "KHR_lights_punctual") && IsObject(GetValue(it))) { - const json &object = GetValue(it); - json_const_iterator itLight; - if (FindMember(object, "lights", itLight)) { - const json &lights = GetValue(itLight); - if (!IsArray(lights)) { - continue; - } - - auto arrayIt(ArrayBegin(lights)); - auto arrayItEnd(ArrayEnd(lights)); - for (; arrayIt != arrayItEnd; ++arrayIt) { - Light light; - if (!ParseLight(&light, err, *arrayIt)) { - return false; - } - model->lights.emplace_back(std::move(light)); - } - } - } - } - } - } - - // 19. Parse Extras - ParseExtrasProperty(&model->extras, v); - - return true; -} - -bool TinyGLTF::LoadASCIIFromString(Model *model, std::string *err, - std::string *warn, const char *str, - unsigned int length, - const std::string &base_dir, - unsigned int check_sections) { - is_binary_ = false; - bin_data_ = nullptr; - bin_size_ = 0; - - return LoadFromString(model, err, warn, str, length, base_dir, - check_sections); -} - -bool TinyGLTF::LoadASCIIFromFile(Model *model, std::string *err, - std::string *warn, const std::string &filename, - unsigned int check_sections) { - std::stringstream ss; - - if (fs.ReadWholeFile == nullptr) { - // Programmer error, assert() ? - ss << "Failed to read file: " << filename - << ": one or more FS callback not set" << std::endl; - if (err) { - (*err) = ss.str(); - } - return false; - } - - std::vector data; - std::string fileerr; - bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data); - if (!fileread) { - ss << "Failed to read file: " << filename << ": " << fileerr << std::endl; - if (err) { - (*err) = ss.str(); - } - return false; - } - - size_t sz = data.size(); - if (sz == 0) { - if (err) { - (*err) = "Empty file."; - } - return false; - } - - std::string basedir = GetBaseDir(filename); - - bool ret = LoadASCIIFromString( - model, err, warn, reinterpret_cast(&data.at(0)), - static_cast(data.size()), basedir, check_sections); - - return ret; -} - -bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err, - std::string *warn, - const unsigned char *bytes, - unsigned int size, - const std::string &base_dir, - unsigned int check_sections) { - if (size < 20) { - if (err) { - (*err) = "Too short data size for glTF Binary."; - } - return false; - } - - if (bytes[0] == 'g' && bytes[1] == 'l' && bytes[2] == 'T' && - bytes[3] == 'F') { - // ok - } else { - if (err) { - (*err) = "Invalid magic."; - } - return false; - } - - unsigned int version; // 4 bytes - unsigned int length; // 4 bytes - unsigned int model_length; // 4 bytes - unsigned int model_format; // 4 bytes; - - // @todo { Endian swap for big endian machine. } - memcpy(&version, bytes + 4, 4); - swap4(&version); - memcpy(&length, bytes + 8, 4); - swap4(&length); - memcpy(&model_length, bytes + 12, 4); - swap4(&model_length); - memcpy(&model_format, bytes + 16, 4); - swap4(&model_format); - - // In case the Bin buffer is not present, the size is exactly 20 + size of - // JSON contents, - // so use "greater than" operator. - if ((20 + model_length > size) || (model_length < 1) || (length > size) || - (model_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format. - if (err) { - (*err) = "Invalid glTF binary."; - } - return false; - } - - // Extract JSON string. - std::string jsonString(reinterpret_cast(&bytes[20]), - model_length); - - is_binary_ = true; - bin_data_ = bytes + 20 + model_length + - 8; // 4 bytes (buffer_length) + 4 bytes(buffer_format) - bin_size_ = - length - (20 + model_length); // extract header + JSON scene data. - - bool ret = LoadFromString(model, err, warn, - reinterpret_cast(&bytes[20]), - model_length, base_dir, check_sections); - if (!ret) { - return ret; - } - - return true; -} - -bool TinyGLTF::LoadBinaryFromFile(Model *model, std::string *err, - std::string *warn, - const std::string &filename, - unsigned int check_sections) { - std::stringstream ss; - - if (fs.ReadWholeFile == nullptr) { - // Programmer error, assert() ? - ss << "Failed to read file: " << filename - << ": one or more FS callback not set" << std::endl; - if (err) { - (*err) = ss.str(); - } - return false; - } - - std::vector data; - std::string fileerr; - bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data); - if (!fileread) { - ss << "Failed to read file: " << filename << ": " << fileerr << std::endl; - if (err) { - (*err) = ss.str(); - } - return false; - } - - std::string basedir = GetBaseDir(filename); - - bool ret = LoadBinaryFromMemory(model, err, warn, &data.at(0), - static_cast(data.size()), - basedir, check_sections); - - return ret; -} - -/////////////////////// -// GLTF Serialization -/////////////////////// -namespace { -json JsonFromString(const char *s) { -#ifdef TINYGLTF_USE_RAPIDJSON - return json(s, GetAllocator()); -#else - return json(s); -#endif -} - -std::string JsonToString(const json &o, int spacing = -1) { -#ifdef TINYGLTF_USE_RAPIDJSON - using namespace rapidjson; - StringBuffer buffer; - if (spacing == -1) { - Writer writer(buffer); - o.Accept(writer); - } else { - PrettyWriter writer(buffer); - writer.SetIndent(' ', spacing); - o.Accept(writer); - } - return buffer.GetString(); -#else - return o.dump(spacing); -#endif -} - -void JsonAssign(json &dest, const json &src) { -#ifdef TINYGLTF_USE_RAPIDJSON - dest.CopyFrom(src, GetAllocator()); -#else - dest = src; -#endif -} - -void JsonAddMember(json &o, const char *key, json &&value) { -#ifdef TINYGLTF_USE_RAPIDJSON - if (!o.IsObject()) { - o.SetObject(); - } - o.AddMember(json(key, GetAllocator()), std::move(value), - GetAllocator()); -#else - o[key] = std::move(value); -#endif -} - -void JsonPushBack(json &o, json &&value) { -#ifdef TINYGLTF_USE_RAPIDJSON - o.PushBack(std::move(value), GetAllocator()); -#else - o.push_back(std::move(value)); -#endif -} - -bool JsonIsNull(const json &o) { -#ifdef TINYGLTF_USE_RAPIDJSON - return o.IsNull(); -#else - return o.is_null(); -#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 - o.SetArray(); - o.Reserve(static_cast(s), - GetAllocator()); -#endif - (void)(o); - (void)(s); -} -} // namespace - -// typedef std::pair json_object_pair; - -template -static void SerializeNumberProperty(const std::string &key, T number, - json &obj) { - // obj.insert( - // json_object_pair(key, json(static_cast(number)))); - // obj[key] = static_cast(number); - JsonAddMember(obj, key.c_str(), json(number)); -} - -template -static void SerializeNumberArrayProperty(const std::string &key, - const std::vector &value, - json &obj) { - if (value.empty()) return; - - json ary; - JsonReserveArray(ary, value.size()); - for (const auto &s : value) { - JsonPushBack(ary, json(s)); - } - JsonAddMember(obj, key.c_str(), std::move(ary)); -} - -static void SerializeStringProperty(const std::string &key, - const std::string &value, json &obj) { - JsonAddMember(obj, key.c_str(), JsonFromString(value.c_str())); -} - -static void SerializeStringArrayProperty(const std::string &key, - const std::vector &value, - json &obj) { - json ary; - JsonReserveArray(ary, value.size()); - for (auto &s : value) { - JsonPushBack(ary, JsonFromString(s.c_str())); - } - JsonAddMember(obj, key.c_str(), std::move(ary)); -} - -static bool ValueToJson(const Value &value, json *ret) { - json obj; -#ifdef TINYGLTF_USE_RAPIDJSON - switch (value.Type()) { - case REAL_TYPE: - obj.SetDouble(value.Get()); - break; - case INT_TYPE: - obj.SetInt(value.Get()); - break; - case BOOL_TYPE: - obj.SetBool(value.Get()); - break; - case STRING_TYPE: - obj.SetString(value.Get().c_str(), - GetAllocator()); - break; - case ARRAY_TYPE: { - obj.SetArray(); - obj.Reserve(static_cast(value.ArrayLen()), - GetAllocator()); - for (unsigned int i = 0; i < value.ArrayLen(); ++i) { - Value elementValue = value.Get(int(i)); - json elementJson; - if (ValueToJson(value.Get(int(i)), &elementJson)) - obj.PushBack(std::move(elementJson), - GetAllocator()); - } - break; - } - case BINARY_TYPE: - // TODO - // obj = json(value.Get>()); - return false; - break; - case OBJECT_TYPE: { - obj.SetObject(); - Value::Object objMap = value.Get(); - for (auto &it : objMap) { - json elementJson; - if (ValueToJson(it.second, &elementJson)) { - obj.AddMember( - json(it.first.c_str(), GetAllocator()), - std::move(elementJson), GetAllocator()); - } - } - break; - } - case NULL_TYPE: - default: - return false; - } -#else - switch (value.Type()) { - case REAL_TYPE: - obj = json(value.Get()); - break; - case INT_TYPE: - obj = json(value.Get()); - break; - case BOOL_TYPE: - obj = json(value.Get()); - break; - case STRING_TYPE: - obj = json(value.Get()); - break; - case ARRAY_TYPE: { - for (unsigned int i = 0; i < value.ArrayLen(); ++i) { - Value elementValue = value.Get(int(i)); - json elementJson; - if (ValueToJson(value.Get(int(i)), &elementJson)) - obj.push_back(elementJson); - } - break; - } - case BINARY_TYPE: - // TODO - // obj = json(value.Get>()); - return false; - break; - case OBJECT_TYPE: { - Value::Object objMap = value.Get(); - for (auto &it : objMap) { - json elementJson; - if (ValueToJson(it.second, &elementJson)) obj[it.first] = elementJson; - } - break; - } - case NULL_TYPE: - default: - return false; - } -#endif - if (ret) *ret = std::move(obj); - return true; -} - -static void SerializeValue(const std::string &key, const Value &value, - json &obj) { - json ret; - if (ValueToJson(value, &ret)) { - JsonAddMember(obj, key.c_str(), std::move(ret)); - } -} - -static void SerializeGltfBufferData(const std::vector &data, - json &o) { - std::string header = "data:application/octet-stream;base64,"; - std::string encodedData = - base64_encode(&data[0], static_cast(data.size())); - SerializeStringProperty("uri", header + encodedData, o); -} - -static bool SerializeGltfBufferData(const std::vector &data, - const std::string &binFilename) { - std::ofstream output(binFilename.c_str(), std::ofstream::binary); - if (!output.is_open()) return false; - output.write(reinterpret_cast(&data[0]), - std::streamsize(data.size())); - output.close(); - return true; -} - -#if 0 // FIXME(syoyo): not used. will be removed in the future release. -static void SerializeParameterMap(ParameterMap ¶m, json &o) { - for (ParameterMap::iterator paramIt = param.begin(); paramIt != param.end(); - ++paramIt) { - if (paramIt->second.number_array.size()) { - SerializeNumberArrayProperty(paramIt->first, - paramIt->second.number_array, o); - } else if (paramIt->second.json_double_value.size()) { - json json_double_value; - for (std::map::iterator it = - paramIt->second.json_double_value.begin(); - it != paramIt->second.json_double_value.end(); ++it) { - if (it->first == "index") { - json_double_value[it->first] = paramIt->second.TextureIndex(); - } else { - json_double_value[it->first] = it->second; - } - } - - o[paramIt->first] = json_double_value; - } else if (!paramIt->second.string_value.empty()) { - SerializeStringProperty(paramIt->first, paramIt->second.string_value, o); - } else if (paramIt->second.has_number_value) { - o[paramIt->first] = paramIt->second.number_value; - } else { - o[paramIt->first] = paramIt->second.bool_value; - } - } -} -#endif - -static void SerializeExtensionMap(ExtensionMap &extensions, json &o) { - if (!extensions.size()) return; - - json extMap; - for (ExtensionMap::iterator extIt = extensions.begin(); - extIt != extensions.end(); ++extIt) { - // Allow an empty object for extension(#97) - json ret; - bool isNull = true; - if (ValueToJson(extIt->second, &ret)) { - isNull = JsonIsNull(ret); - JsonAddMember(extMap, extIt->first.c_str(), std::move(ret)); - } - if (isNull) { - if (!(extIt->first.empty())) { // name should not be empty, but for sure - // 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)); - } - } - } - JsonAddMember(o, "extensions", std::move(extMap)); -} - -static void SerializeGltfAccessor(Accessor &accessor, json &o) { - SerializeNumberProperty("bufferView", accessor.bufferView, o); - - if (accessor.byteOffset != 0.0) - SerializeNumberProperty("byteOffset", int(accessor.byteOffset), o); - - SerializeNumberProperty("componentType", accessor.componentType, o); - SerializeNumberProperty("count", accessor.count, o); - SerializeNumberArrayProperty("min", accessor.minValues, o); - SerializeNumberArrayProperty("max", accessor.maxValues, o); - SerializeValue("normalized", Value(accessor.normalized), o); - std::string type; - switch (accessor.type) { - case TINYGLTF_TYPE_SCALAR: - type = "SCALAR"; - break; - case TINYGLTF_TYPE_VEC2: - type = "VEC2"; - break; - case TINYGLTF_TYPE_VEC3: - type = "VEC3"; - break; - case TINYGLTF_TYPE_VEC4: - type = "VEC4"; - break; - case TINYGLTF_TYPE_MAT2: - type = "MAT2"; - break; - case TINYGLTF_TYPE_MAT3: - type = "MAT3"; - break; - case TINYGLTF_TYPE_MAT4: - type = "MAT4"; - break; - } - - SerializeStringProperty("type", type, o); - if (!accessor.name.empty()) SerializeStringProperty("name", accessor.name, o); - - if (accessor.extras.Type() != NULL_TYPE) { - SerializeValue("extras", accessor.extras, o); - } -} - -static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) { - SerializeNumberProperty("sampler", channel.sampler, o); - { - json target; - SerializeNumberProperty("node", channel.target_node, target); - SerializeStringProperty("path", channel.target_path, target); - - JsonAddMember(o, "target", std::move(target)); - } - - if (channel.extras.Type() != NULL_TYPE) { - SerializeValue("extras", channel.extras, o); - } - - SerializeExtensionMap(channel.extensions, o); -} - -static void SerializeGltfAnimationSampler(AnimationSampler &sampler, json &o) { - SerializeNumberProperty("input", sampler.input, o); - SerializeNumberProperty("output", sampler.output, o); - SerializeStringProperty("interpolation", sampler.interpolation, o); - - if (sampler.extras.Type() != NULL_TYPE) { - SerializeValue("extras", sampler.extras, o); - } -} - -static void SerializeGltfAnimation(Animation &animation, json &o) { - if (!animation.name.empty()) - SerializeStringProperty("name", animation.name, o); - - { - json channels; - JsonReserveArray(channels, animation.channels.size()); - for (unsigned int i = 0; i < animation.channels.size(); ++i) { - json channel; - AnimationChannel gltfChannel = animation.channels[i]; - SerializeGltfAnimationChannel(gltfChannel, channel); - JsonPushBack(channels, std::move(channel)); - } - - JsonAddMember(o, "channels", std::move(channels)); - } - - { - json samplers; - for (unsigned int i = 0; i < animation.samplers.size(); ++i) { - json sampler; - AnimationSampler gltfSampler = animation.samplers[i]; - SerializeGltfAnimationSampler(gltfSampler, sampler); - JsonPushBack(samplers, std::move(sampler)); - } - JsonAddMember(o, "samplers", std::move(samplers)); - } - - if (animation.extras.Type() != NULL_TYPE) { - SerializeValue("extras", animation.extras, o); - } - - SerializeExtensionMap(animation.extensions, o); -} - -static void SerializeGltfAsset(Asset &asset, json &o) { - if (!asset.generator.empty()) { - SerializeStringProperty("generator", asset.generator, o); - } - - if (!asset.copyright.empty()) { - SerializeStringProperty("copyright", asset.copyright, o); - } - - if (!asset.version.empty()) { - SerializeStringProperty("version", asset.version, o); - } - - if (asset.extras.Keys().size()) { - SerializeValue("extras", asset.extras, o); - } - - SerializeExtensionMap(asset.extensions, o); -} - -static void SerializeGltfBuffer(Buffer &buffer, json &o) { - SerializeNumberProperty("byteLength", buffer.data.size(), o); - SerializeGltfBufferData(buffer.data, o); - - if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o); - - if (buffer.extras.Type() != NULL_TYPE) { - SerializeValue("extras", buffer.extras, o); - } -} - -static bool SerializeGltfBuffer(Buffer &buffer, json &o, - const std::string &binFilename, - const std::string &binBaseFilename) { - if (!SerializeGltfBufferData(buffer.data, binFilename)) return false; - SerializeNumberProperty("byteLength", buffer.data.size(), o); - SerializeStringProperty("uri", binBaseFilename, o); - - if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o); - - if (buffer.extras.Type() != NULL_TYPE) { - SerializeValue("extras", buffer.extras, o); - } - return true; -} - -static void SerializeGltfBufferView(BufferView &bufferView, json &o) { - SerializeNumberProperty("buffer", bufferView.buffer, o); - SerializeNumberProperty("byteLength", bufferView.byteLength, o); - - // byteStride is optional, minimum allowed is 4 - if (bufferView.byteStride >= 4) { - SerializeNumberProperty("byteStride", bufferView.byteStride, o); - } - // byteOffset is optional, default is 0 - if (bufferView.byteOffset > 0) { - SerializeNumberProperty("byteOffset", bufferView.byteOffset, o); - } - // Target is optional, check if it contains a valid value - if (bufferView.target == TINYGLTF_TARGET_ARRAY_BUFFER || - bufferView.target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER) { - SerializeNumberProperty("target", bufferView.target, o); - } - if (bufferView.name.size()) { - SerializeStringProperty("name", bufferView.name, o); - } - - if (bufferView.extras.Type() != NULL_TYPE) { - SerializeValue("extras", bufferView.extras, o); - } -} - -static void SerializeGltfImage(Image &image, json &o) { - // if uri empty, the mimeType and bufferview should be set - if (image.uri.empty()) { - SerializeStringProperty("mimeType", image.mimeType, o); - SerializeNumberProperty("bufferView", image.bufferView, o); - } else { - SerializeStringProperty("uri", image.uri, o); - } - - if (image.name.size()) { - SerializeStringProperty("name", image.name, o); - } - - if (image.extras.Type() != NULL_TYPE) { - SerializeValue("extras", image.extras, o); - } - - SerializeExtensionMap(image.extensions, o); -} - -static void SerializeGltfTextureInfo(TextureInfo &texinfo, json &o) { - SerializeNumberProperty("index", texinfo.index, o); - - if (texinfo.texCoord != 0) { - SerializeNumberProperty("texCoord", texinfo.texCoord, o); - } - - if (texinfo.extras.Type() != NULL_TYPE) { - SerializeValue("extras", texinfo.extras, o); - } - - SerializeExtensionMap(texinfo.extensions, o); -} - -static void SerializeGltfNormalTextureInfo(NormalTextureInfo &texinfo, - json &o) { - SerializeNumberProperty("index", texinfo.index, o); - - if (texinfo.texCoord != 0) { - SerializeNumberProperty("texCoord", texinfo.texCoord, o); - } - - if (!TINYGLTF_DOUBLE_EQUAL(texinfo.scale, 1.0)) { - SerializeNumberProperty("scale", texinfo.scale, o); - } - - if (texinfo.extras.Type() != NULL_TYPE) { - SerializeValue("extras", texinfo.extras, o); - } - - SerializeExtensionMap(texinfo.extensions, o); -} - -static void SerializeGltfOcclusionTextureInfo(OcclusionTextureInfo &texinfo, - json &o) { - SerializeNumberProperty("index", texinfo.index, o); - - if (texinfo.texCoord != 0) { - SerializeNumberProperty("texCoord", texinfo.texCoord, o); - } - - if (!TINYGLTF_DOUBLE_EQUAL(texinfo.strength, 1.0)) { - SerializeNumberProperty("strength", texinfo.strength, o); - } - - if (texinfo.extras.Type() != NULL_TYPE) { - SerializeValue("extras", texinfo.extras, o); - } - - SerializeExtensionMap(texinfo.extensions, o); -} - -static void SerializeGltfPbrMetallicRoughness(PbrMetallicRoughness &pbr, - json &o) { - std::vector default_baseColorFactor = {1.0, 1.0, 1.0, 1.0}; - if (!Equals(pbr.baseColorFactor, default_baseColorFactor)) { - SerializeNumberArrayProperty("baseColorFactor", pbr.baseColorFactor, - o); - } - - if (!TINYGLTF_DOUBLE_EQUAL(pbr.metallicFactor, 1.0)) { - SerializeNumberProperty("metallicFactor", pbr.metallicFactor, o); - } - - if (!TINYGLTF_DOUBLE_EQUAL(pbr.roughnessFactor, 1.0)) { - SerializeNumberProperty("roughnessFactor", pbr.roughnessFactor, o); - } - - if (pbr.baseColorTexture.index > -1) { - json texinfo; - SerializeGltfTextureInfo(pbr.baseColorTexture, texinfo); - JsonAddMember(o, "baseColorTexture", std::move(texinfo)); - } - - if (pbr.metallicRoughnessTexture.index > -1) { - json texinfo; - SerializeGltfTextureInfo(pbr.metallicRoughnessTexture, texinfo); - JsonAddMember(o, "metallicRoughnessTexture", std::move(texinfo)); - } - - SerializeExtensionMap(pbr.extensions, o); - - if (pbr.extras.Type() != NULL_TYPE) { - SerializeValue("extras", pbr.extras, o); - } -} - -static void SerializeGltfMaterial(Material &material, json &o) { - if (material.name.size()) { - SerializeStringProperty("name", material.name, o); - } - - // QUESTION(syoyo): Write material parameters regardless of its default value? - - if (!TINYGLTF_DOUBLE_EQUAL(material.alphaCutoff, 0.5)) { - SerializeNumberProperty("alphaCutoff", material.alphaCutoff, o); - } - - if (material.alphaMode.compare("OPAQUE") != 0) { - SerializeStringProperty("alphaMode", material.alphaMode, o); - } - - JsonAddMember(o, "doubleSided", json(material.doubleSided)); - - if (material.normalTexture.index > -1) { - json texinfo; - SerializeGltfNormalTextureInfo(material.normalTexture, texinfo); - JsonAddMember(o, "normalTexture", std::move(texinfo)); - } - - if (material.occlusionTexture.index > -1) { - json texinfo; - SerializeGltfOcclusionTextureInfo(material.occlusionTexture, texinfo); - JsonAddMember(o, "occlusionTexture", std::move(texinfo)); - } - - if (material.emissiveTexture.index > -1) { - json texinfo; - SerializeGltfTextureInfo(material.emissiveTexture, texinfo); - JsonAddMember(o, "emissiveTexture", std::move(texinfo)); - } - - std::vector default_emissiveFactor = {0.0, 0.0, 0.0}; - if (!Equals(material.emissiveFactor, default_emissiveFactor)) { - SerializeNumberArrayProperty("emissiveFactor", - material.emissiveFactor, o); - } - - { - json pbrMetallicRoughness; - SerializeGltfPbrMetallicRoughness(material.pbrMetallicRoughness, - pbrMetallicRoughness); - JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness)); - } - -#if 0 // legacy way. just for the record. - if (material.values.size()) { - json pbrMetallicRoughness; - SerializeParameterMap(material.values, pbrMetallicRoughness); - JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness)); - } - - SerializeParameterMap(material.additionalValues, o); -#else - -#endif - - SerializeExtensionMap(material.extensions, o); - - if (material.extras.Type() != NULL_TYPE) { - SerializeValue("extras", material.extras, o); - } -} - -static void SerializeGltfMesh(Mesh &mesh, json &o) { - json primitives; - JsonReserveArray(primitives, mesh.primitives.size()); - for (unsigned int i = 0; i < mesh.primitives.size(); ++i) { - json primitive; - const Primitive &gltfPrimitive = mesh.primitives[i]; // don't make a copy - { - json attributes; - for (auto attrIt = gltfPrimitive.attributes.begin(); - attrIt != gltfPrimitive.attributes.end(); ++attrIt) { - SerializeNumberProperty(attrIt->first, attrIt->second, attributes); - } - - JsonAddMember(primitive, "attributes", std::move(attributes)); - } - - // Indicies is optional - if (gltfPrimitive.indices > -1) { - SerializeNumberProperty("indices", gltfPrimitive.indices, primitive); - } - // Material is optional - if (gltfPrimitive.material > -1) { - SerializeNumberProperty("material", gltfPrimitive.material, - primitive); - } - SerializeNumberProperty("mode", gltfPrimitive.mode, primitive); - - // Morph targets - if (gltfPrimitive.targets.size()) { - json targets; - JsonReserveArray(targets, gltfPrimitive.targets.size()); - for (unsigned int k = 0; k < gltfPrimitive.targets.size(); ++k) { - json targetAttributes; - std::map targetData = gltfPrimitive.targets[k]; - for (std::map::iterator attrIt = targetData.begin(); - attrIt != targetData.end(); ++attrIt) { - SerializeNumberProperty(attrIt->first, attrIt->second, - targetAttributes); - } - JsonPushBack(targets, std::move(targetAttributes)); - } - JsonAddMember(primitive, "targets", std::move(targets)); - } - - if (gltfPrimitive.extras.Type() != NULL_TYPE) { - SerializeValue("extras", gltfPrimitive.extras, primitive); - } - - JsonPushBack(primitives, std::move(primitive)); - } - - JsonAddMember(o, "primitives", std::move(primitives)); - - if (mesh.weights.size()) { - SerializeNumberArrayProperty("weights", mesh.weights, o); - } - - if (mesh.name.size()) { - SerializeStringProperty("name", mesh.name, o); - } - - if (mesh.extras.Type() != NULL_TYPE) { - SerializeValue("extras", mesh.extras, o); - } -} - -static void SerializeSpotLight(SpotLight &spot, json &o) { - SerializeNumberProperty("innerConeAngle", spot.innerConeAngle, o); - SerializeNumberProperty("outerConeAngle", spot.outerConeAngle, o); - SerializeExtensionMap(spot.extensions, o); -} - -static void SerializeGltfLight(Light &light, json &o) { - if (!light.name.empty()) SerializeStringProperty("name", light.name, o); - SerializeNumberProperty("intensity", light.intensity, o); - SerializeNumberProperty("range", light.range, o); - SerializeNumberArrayProperty("color", light.color, o); - SerializeStringProperty("type", light.type, o); - if (light.type == "spot") { - json spot; - SerializeSpotLight(light.spot, spot); - JsonAddMember(o, "spot", std::move(spot)); - } -} - -static void SerializeGltfNode(Node &node, json &o) { - if (node.translation.size() > 0) { - SerializeNumberArrayProperty("translation", node.translation, o); - } - if (node.rotation.size() > 0) { - SerializeNumberArrayProperty("rotation", node.rotation, o); - } - if (node.scale.size() > 0) { - SerializeNumberArrayProperty("scale", node.scale, o); - } - if (node.matrix.size() > 0) { - SerializeNumberArrayProperty("matrix", node.matrix, o); - } - if (node.mesh != -1) { - SerializeNumberProperty("mesh", node.mesh, o); - } - - if (node.skin != -1) { - SerializeNumberProperty("skin", node.skin, o); - } - - if (node.camera != -1) { - SerializeNumberProperty("camera", node.camera, o); - } - - if (node.weights.size() > 0) { - SerializeNumberArrayProperty("weights", node.weights, o); - } - - if (node.extras.Type() != NULL_TYPE) { - SerializeValue("extras", node.extras, o); - } - - SerializeExtensionMap(node.extensions, o); - if (!node.name.empty()) SerializeStringProperty("name", node.name, o); - SerializeNumberArrayProperty("children", node.children, o); -} - -static void SerializeGltfSampler(Sampler &sampler, json &o) { - if (sampler.magFilter != -1) { - SerializeNumberProperty("magFilter", sampler.magFilter, o); - } - if (sampler.minFilter != -1) { - SerializeNumberProperty("minFilter", sampler.minFilter, o); - } - SerializeNumberProperty("wrapR", sampler.wrapR, o); - SerializeNumberProperty("wrapS", sampler.wrapS, o); - SerializeNumberProperty("wrapT", sampler.wrapT, o); - - if (sampler.extras.Type() != NULL_TYPE) { - SerializeValue("extras", sampler.extras, o); - } -} - -static void SerializeGltfOrthographicCamera(const OrthographicCamera &camera, - json &o) { - SerializeNumberProperty("zfar", camera.zfar, o); - SerializeNumberProperty("znear", camera.znear, o); - SerializeNumberProperty("xmag", camera.xmag, o); - SerializeNumberProperty("ymag", camera.ymag, o); - - if (camera.extras.Type() != NULL_TYPE) { - SerializeValue("extras", camera.extras, o); - } -} - -static void SerializeGltfPerspectiveCamera(const PerspectiveCamera &camera, - json &o) { - SerializeNumberProperty("zfar", camera.zfar, o); - SerializeNumberProperty("znear", camera.znear, o); - if (camera.aspectRatio > 0) { - SerializeNumberProperty("aspectRatio", camera.aspectRatio, o); - } - - if (camera.yfov > 0) { - SerializeNumberProperty("yfov", camera.yfov, o); - } - - if (camera.extras.Type() != NULL_TYPE) { - SerializeValue("extras", camera.extras, o); - } -} - -static void SerializeGltfCamera(const Camera &camera, json &o) { - SerializeStringProperty("type", camera.type, o); - if (!camera.name.empty()) { - SerializeStringProperty("name", camera.name, o); - } - - if (camera.type.compare("orthographic") == 0) { - json orthographic; - SerializeGltfOrthographicCamera(camera.orthographic, orthographic); - JsonAddMember(o, "orthographic", std::move(orthographic)); - } else if (camera.type.compare("perspective") == 0) { - json perspective; - SerializeGltfPerspectiveCamera(camera.perspective, perspective); - JsonAddMember(o, "perspective", std::move(perspective)); - } else { - // ??? - } -} - -static void SerializeGltfScene(Scene &scene, json &o) { - SerializeNumberArrayProperty("nodes", scene.nodes, o); - - if (scene.name.size()) { - SerializeStringProperty("name", scene.name, o); - } - if (scene.extras.Type() != NULL_TYPE) { - SerializeValue("extras", scene.extras, o); - } - SerializeExtensionMap(scene.extensions, o); -} - -static void SerializeGltfSkin(Skin &skin, json &o) { - if (skin.inverseBindMatrices != -1) - SerializeNumberProperty("inverseBindMatrices", skin.inverseBindMatrices, o); - - SerializeNumberArrayProperty("joints", skin.joints, o); - SerializeNumberProperty("skeleton", skin.skeleton, o); - if (skin.name.size()) { - SerializeStringProperty("name", skin.name, o); - } -} - -static void SerializeGltfTexture(Texture &texture, json &o) { - if (texture.sampler > -1) { - SerializeNumberProperty("sampler", texture.sampler, o); - } - if (texture.source > -1) { - SerializeNumberProperty("source", texture.source, o); - } - if (texture.name.size()) { - SerializeStringProperty("name", texture.name, o); - } - if (texture.extras.Type() != NULL_TYPE) { - SerializeValue("extras", texture.extras, o); - } - SerializeExtensionMap(texture.extensions, o); -} - -/// -/// Serialize all properties except buffers and images. -/// -static void SerializeGltfModel(Model *model, json &o) { - // ACCESSORS - json accessors; - JsonReserveArray(accessors, model->accessors.size()); - for (unsigned int i = 0; i < model->accessors.size(); ++i) { - json accessor; - SerializeGltfAccessor(model->accessors[i], accessor); - JsonPushBack(accessors, std::move(accessor)); - } - JsonAddMember(o, "accessors", std::move(accessors)); - - // ANIMATIONS - if (model->animations.size()) { - json animations; - JsonReserveArray(animations, model->animations.size()); - for (unsigned int i = 0; i < model->animations.size(); ++i) { - if (model->animations[i].channels.size()) { - json animation; - SerializeGltfAnimation(model->animations[i], animation); - JsonPushBack(animations, std::move(animation)); - } - } - - JsonAddMember(o, "animations", std::move(animations)); - } - - // ASSET - json asset; - SerializeGltfAsset(model->asset, asset); - JsonAddMember(o, "asset", std::move(asset)); - - // BUFFERVIEWS - json bufferViews; - JsonReserveArray(bufferViews, model->bufferViews.size()); - for (unsigned int i = 0; i < model->bufferViews.size(); ++i) { - json bufferView; - SerializeGltfBufferView(model->bufferViews[i], bufferView); - JsonPushBack(bufferViews, std::move(bufferView)); - } - JsonAddMember(o, "bufferViews", std::move(bufferViews)); - - // Extensions used - if (model->extensionsUsed.size()) { - SerializeStringArrayProperty("extensionsUsed", model->extensionsUsed, o); - } - - // Extensions required - if (model->extensionsRequired.size()) { - SerializeStringArrayProperty("extensionsRequired", - model->extensionsRequired, o); - } - - // MATERIALS - if (model->materials.size()) { - json materials; - JsonReserveArray(materials, model->materials.size()); - for (unsigned int i = 0; i < model->materials.size(); ++i) { - json material; - SerializeGltfMaterial(model->materials[i], material); - JsonPushBack(materials, std::move(material)); - } - JsonAddMember(o, "materials", std::move(materials)); - } - - // MESHES - if (model->meshes.size()) { - json meshes; - JsonReserveArray(meshes, model->meshes.size()); - for (unsigned int i = 0; i < model->meshes.size(); ++i) { - json mesh; - SerializeGltfMesh(model->meshes[i], mesh); - JsonPushBack(meshes, std::move(mesh)); - } - JsonAddMember(o, "meshes", std::move(meshes)); - } - - // NODES - if (model->nodes.size()) { - json nodes; - JsonReserveArray(nodes, model->nodes.size()); - for (unsigned int i = 0; i < model->nodes.size(); ++i) { - json node; - SerializeGltfNode(model->nodes[i], node); - JsonPushBack(nodes, std::move(node)); - } - JsonAddMember(o, "nodes", std::move(nodes)); - } - - // SCENE - if (model->defaultScene > -1) { - SerializeNumberProperty("scene", model->defaultScene, o); - } - - // SCENES - if (model->scenes.size()) { - json scenes; - JsonReserveArray(scenes, model->scenes.size()); - for (unsigned int i = 0; i < model->scenes.size(); ++i) { - json currentScene; - SerializeGltfScene(model->scenes[i], currentScene); - JsonPushBack(scenes, std::move(currentScene)); - } - JsonAddMember(o, "scenes", std::move(scenes)); - } - - // SKINS - if (model->skins.size()) { - json skins; - JsonReserveArray(skins, model->skins.size()); - for (unsigned int i = 0; i < model->skins.size(); ++i) { - json skin; - SerializeGltfSkin(model->skins[i], skin); - JsonPushBack(skins, std::move(skin)); - } - JsonAddMember(o, "skins", std::move(skins)); - } - - // TEXTURES - if (model->textures.size()) { - json textures; - JsonReserveArray(textures, model->textures.size()); - for (unsigned int i = 0; i < model->textures.size(); ++i) { - json texture; - SerializeGltfTexture(model->textures[i], texture); - JsonPushBack(textures, std::move(texture)); - } - JsonAddMember(o, "textures", std::move(textures)); - } - - // SAMPLERS - if (model->samplers.size()) { - json samplers; - JsonReserveArray(samplers, model->samplers.size()); - for (unsigned int i = 0; i < model->samplers.size(); ++i) { - json sampler; - SerializeGltfSampler(model->samplers[i], sampler); - JsonPushBack(samplers, std::move(sampler)); - } - JsonAddMember(o, "samplers", std::move(samplers)); - } - - // CAMERAS - if (model->cameras.size()) { - json cameras; - JsonReserveArray(cameras, model->cameras.size()); - for (unsigned int i = 0; i < model->cameras.size(); ++i) { - json camera; - SerializeGltfCamera(model->cameras[i], camera); - JsonPushBack(cameras, std::move(camera)); - } - JsonAddMember(o, "cameras", std::move(cameras)); - } - - // EXTENSIONS - SerializeExtensionMap(model->extensions, o); - - // LIGHTS as KHR_lights_cmn - if (model->lights.size()) { - json lights; - JsonReserveArray(lights, model->lights.size()); - for (unsigned int i = 0; i < model->lights.size(); ++i) { - json light; - SerializeGltfLight(model->lights[i], light); - JsonPushBack(lights, std::move(light)); - } - json khr_lights_cmn; - JsonAddMember(khr_lights_cmn, "lights", std::move(lights)); - json ext_j; - - { - json_const_iterator it; - if (!FindMember(o, "extensions", it)) { - JsonAssign(ext_j, GetValue(it)); - } - } - - JsonAddMember(ext_j, "KHR_lights_punctual", std::move(khr_lights_cmn)); - - JsonAddMember(o, "extensions", std::move(ext_j)); - } - - // EXTRAS - if (model->extras.Type() != NULL_TYPE) { - SerializeValue("extras", model->extras, o); - } -} - -static bool WriteGltfStream(std::ostream &stream, const std::string &content) { - stream << content << std::endl; - return true; -} - -static bool WriteGltfFile(const std::string &output, - const std::string &content) { - std::ofstream gltfFile(output.c_str()); - if (!gltfFile.is_open()) return false; - return WriteGltfStream(gltfFile, content); -} - -static void WriteBinaryGltfStream(std::ostream &stream, - const std::string &content) { - const std::string header = "glTF"; - const int version = 2; - const int padding_size = content.size() % 4; - - // 12 bytes for header, JSON content length, 8 bytes for JSON chunk info, - // padding - const int length = 12 + 8 + int(content.size()) + padding_size; - - stream.write(header.c_str(), std::streamsize(header.size())); - stream.write(reinterpret_cast(&version), sizeof(version)); - stream.write(reinterpret_cast(&length), sizeof(length)); - - // JSON chunk info, then JSON data - const int model_length = int(content.size()) + padding_size; - const int model_format = 0x4E4F534A; - stream.write(reinterpret_cast(&model_length), - sizeof(model_length)); - stream.write(reinterpret_cast(&model_format), - sizeof(model_format)); - stream.write(content.c_str(), std::streamsize(content.size())); - - // Chunk must be multiplies of 4, so pad with spaces - if (padding_size > 0) { - const std::string padding = std::string(size_t(padding_size), ' '); - stream.write(padding.c_str(), std::streamsize(padding.size())); - } -} - -static void WriteBinaryGltfFile(const std::string &output, - const std::string &content) { - std::ofstream gltfFile(output.c_str(), std::ios::binary); - WriteBinaryGltfStream(gltfFile, content); -} - -bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream, - bool prettyPrint = true, - bool writeBinary = false) { - JsonDocument output; - - /// Serialize all properties except buffers and images. - SerializeGltfModel(model, output); - - // BUFFERS - std::vector usedUris; - json buffers; - JsonReserveArray(buffers, model->buffers.size()); - for (unsigned int i = 0; i < model->buffers.size(); ++i) { - json buffer; - SerializeGltfBuffer(model->buffers[i], buffer); - JsonPushBack(buffers, std::move(buffer)); - } - JsonAddMember(output, "buffers", std::move(buffers)); - - // IMAGES - if (model->images.size()) { - json images; - JsonReserveArray(images, model->images.size()); - for (unsigned int i = 0; i < model->images.size(); ++i) { - json image; - - std::string dummystring = ""; - // UpdateImageObject need baseDir but only uses it if embededImages is - // enable, since we won't write separte images when writing to a stream we - // use a dummystring - UpdateImageObject(model->images[i], dummystring, int(i), false, - &this->WriteImageData, this->write_image_user_data_); - SerializeGltfImage(model->images[i], image); - JsonPushBack(images, std::move(image)); - } - JsonAddMember(output, "images", std::move(images)); - } - - if (writeBinary) { - WriteBinaryGltfStream(stream, JsonToString(output)); - } else { - WriteGltfStream(stream, JsonToString(output, prettyPrint ? 2 : -1)); - } - - return true; -} - -bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename, - bool embedImages = false, - bool embedBuffers = false, - bool prettyPrint = true, - bool writeBinary = false) { - JsonDocument output; - std::string defaultBinFilename = GetBaseFilename(filename); - std::string defaultBinFileExt = ".bin"; - std::string::size_type pos = - defaultBinFilename.rfind('.', defaultBinFilename.length()); - - if (pos != std::string::npos) { - defaultBinFilename = defaultBinFilename.substr(0, pos); - } - std::string baseDir = GetBaseDir(filename); - if (baseDir.empty()) { - baseDir = "./"; - } - /// Serialize all properties except buffers and images. - SerializeGltfModel(model, output); - - // BUFFERS - std::vector usedUris; - json buffers; - JsonReserveArray(buffers, model->buffers.size()); - for (unsigned int i = 0; i < model->buffers.size(); ++i) { - json buffer; - if (embedBuffers) { - SerializeGltfBuffer(model->buffers[i], buffer); - } else { - std::string binSavePath; - std::string binUri; - if (!model->buffers[i].uri.empty() && !IsDataURI(model->buffers[i].uri)) { - binUri = model->buffers[i].uri; - } else { - binUri = defaultBinFilename + defaultBinFileExt; - bool inUse = true; - int numUsed = 0; - while (inUse) { - inUse = false; - for (const std::string &usedName : usedUris) { - if (binUri.compare(usedName) != 0) continue; - inUse = true; - binUri = defaultBinFilename + std::to_string(numUsed++) + - defaultBinFileExt; - break; - } - } - } - usedUris.push_back(binUri); - binSavePath = JoinPath(baseDir, binUri); - if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath, - binUri)) { - return false; - } - } - JsonPushBack(buffers, std::move(buffer)); - } - JsonAddMember(output, "buffers", std::move(buffers)); - - // IMAGES - if (model->images.size()) { - json images; - JsonReserveArray(images, model->images.size()); - for (unsigned int i = 0; i < model->images.size(); ++i) { - json image; - - UpdateImageObject(model->images[i], baseDir, int(i), embedImages, - &this->WriteImageData, this->write_image_user_data_); - SerializeGltfImage(model->images[i], image); - JsonPushBack(images, std::move(image)); - } - JsonAddMember(output, "images", std::move(images)); - } - - if (writeBinary) { - WriteBinaryGltfFile(filename, JsonToString(output)); - } else { - WriteGltfFile(filename, JsonToString(output, (prettyPrint ? 2 : -1))); - } - - return true; -} - -} // namespace tinygltf - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -#endif // TINYGLTF_IMPLEMENTATION diff --git a/tiny_gltf.h b/tiny_gltf.h index 2b2bba2..5b7e69b 100644 --- a/tiny_gltf.h +++ b/tiny_gltf.h @@ -1650,52 +1650,33 @@ class TinyGLTF { namespace { #ifdef TINYGLTF_USE_RAPIDJSON - -#ifdef TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR - // This uses the RapidJSON CRTAllocator. It is thread safe and multiple - // documents may be active at once. - using json = rapidjson::GenericValue, rapidjson::CrtAllocator>; - using json_const_iterator = json::ConstMemberIterator; - using json_const_array_iterator = json const *; - using JsonDocument = - rapidjson::GenericDocument, rapidjson::CrtAllocator>; - rapidjson::CrtAllocator s_CrtAllocator; //stateless and thread safe - rapidjson::CrtAllocator &GetAllocator() { return s_CrtAllocator; } -#else - // This uses the default RapidJSON MemoryPoolAllocator. It is very fast, but - // not thread safe. Only a single JsonDocument may be active at any one time, - // meaning only a single gltf load/save can be active any one time. using json = rapidjson::Value; using json_const_iterator = json::ConstMemberIterator; - using json_const_array_iterator = json const *; - rapidjson::Document *s_pActiveDocument = nullptr; - rapidjson::Document::AllocatorType &GetAllocator() { - assert(s_pActiveDocument); //Root json node must be JsonDocument type - return s_pActiveDocument->GetAllocator(); - } - struct JsonDocument : public rapidjson::Document { + using json_const_array_iterator = json const*; + + rapidjson::Document* s_pActiveDocument = nullptr; + + struct JsonDocument : public rapidjson::Document + { JsonDocument() { - assert(s_pActiveDocument == - nullptr); // When using default allocator, only one document can be active at a time, if you need - // multiple active at once, define TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR + assert(s_pActiveDocument == nullptr); //Code assumes only one document is active at a time s_pActiveDocument = this; } - JsonDocument(const JsonDocument &) = delete; - JsonDocument(JsonDocument &&rhs) noexcept - : rapidjson::Document(std::move(rhs)) { + JsonDocument(const JsonDocument&) = delete; + JsonDocument(JsonDocument&& rhs) noexcept : rapidjson::Document(std::move(rhs)) + { s_pActiveDocument = this; rhs.isNil = true; } ~JsonDocument() { - if (!isNil) { + if (!isNil) + { s_pActiveDocument = nullptr; } } - private: bool isNil = false; }; -#endif // TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR #else using nlohmann::json; using json_const_iterator = json::const_iterator; @@ -5693,7 +5674,7 @@ namespace json JsonFromString(const char* s) { #ifdef TINYGLTF_USE_RAPIDJSON - return json(s, GetAllocator()); + return json(s, s_pActiveDocument->GetAllocator()); #else return json(s); #endif @@ -5724,7 +5705,7 @@ namespace void JsonAssign(json& dest, const json& src) { #ifdef TINYGLTF_USE_RAPIDJSON - dest.CopyFrom(src, GetAllocator()); + dest.CopyFrom(src, s_pActiveDocument->GetAllocator()); #else dest = src; #endif @@ -5737,7 +5718,7 @@ namespace { o.SetObject(); } - o.AddMember(json(key, GetAllocator()), std::move(value), GetAllocator()); + o.AddMember(json(key, s_pActiveDocument->GetAllocator()), std::move(value), s_pActiveDocument->GetAllocator()); #else o[key] = std::move(value); #endif @@ -5746,7 +5727,7 @@ namespace void JsonPushBack(json& o, json&& value) { #ifdef TINYGLTF_USE_RAPIDJSON - o.PushBack(std::move(value), GetAllocator()); + o.PushBack(std::move(value), s_pActiveDocument->GetAllocator()); #else o.push_back(std::move(value)); #endif @@ -5774,7 +5755,7 @@ namespace { #ifdef TINYGLTF_USE_RAPIDJSON o.SetArray(); - o.Reserve(static_cast(s), GetAllocator()); + o.Reserve(static_cast(s), s_pActiveDocument->GetAllocator()); #endif (void)(o); (void)(s); @@ -5839,16 +5820,16 @@ static bool ValueToJson(const Value &value, json *ret) { obj.SetBool(value.Get()); break; case STRING_TYPE: - obj.SetString(value.Get().c_str(), GetAllocator()); + obj.SetString(value.Get().c_str(), s_pActiveDocument->GetAllocator()); break; case ARRAY_TYPE: { obj.SetArray(); - obj.Reserve(static_cast(value.ArrayLen()), GetAllocator()); + obj.Reserve(static_cast(value.ArrayLen()), s_pActiveDocument->GetAllocator()); for (unsigned int i = 0; i < value.ArrayLen(); ++i) { Value elementValue = value.Get(int(i)); json elementJson; if (ValueToJson(value.Get(int(i)), &elementJson)) - obj.PushBack(std::move(elementJson), GetAllocator()); + obj.PushBack(std::move(elementJson), s_pActiveDocument->GetAllocator()); } break; } @@ -5863,7 +5844,7 @@ static bool ValueToJson(const Value &value, json *ret) { for (auto &it : objMap) { json elementJson; if (ValueToJson(it.second, &elementJson)) { - obj.AddMember(json(it.first.c_str(), GetAllocator()), std::move(elementJson), GetAllocator()); + obj.AddMember(json(it.first.c_str(), s_pActiveDocument->GetAllocator()), std::move(elementJson), s_pActiveDocument->GetAllocator()); } } break;