diff --git a/extern/tinygltf/README.blender b/extern/tinygltf/README.blender index 2aba84dea80..3dcd6d15f03 100644 --- a/extern/tinygltf/README.blender +++ b/extern/tinygltf/README.blender @@ -1,6 +1,5 @@ Project: TinyGLTF URL: https://github.com/syoyo/tinygltf License: MIT -Upstream version: 2.5.0, 19a41d20ec0 -Local modifications: -* Silence "enum value not handled in switch" warnings due to JSON dependency. +Upstream version: 2.8.3, 84a83d39f55d +Local modifications: None diff --git a/extern/tinygltf/patches/TinyGLTF.diff b/extern/tinygltf/patches/TinyGLTF.diff deleted file mode 100644 index 411bd5621f3..00000000000 Binary files a/extern/tinygltf/patches/TinyGLTF.diff and /dev/null differ diff --git a/extern/tinygltf/tiny_gltf.h b/extern/tinygltf/tiny_gltf.h index 099e0c76d92..c935b89df9a 100644 --- a/extern/tinygltf/tiny_gltf.h +++ b/extern/tinygltf/tiny_gltf.h @@ -26,8 +26,16 @@ // THE SOFTWARE. // Version: +// - v2.8.1 Missed serialization texture sampler name fixed. PR#399. +// - v2.8.0 Add URICallbacks for custom URI handling in Buffer and Image. PR#397. +// - v2.7.0 Change WriteImageDataFunction user callback function signature. PR#393. +// - v2.6.3 Fix GLB file with empty BIN chunk was not handled. PR#382 and PR#383. +// - v2.6.2 Fix out-of-bounds access of accessors. PR#379. +// - v2.6.1 Better GLB validation check when loading. +// - v2.6.0 Support serializing sparse accessor(Thanks to @fynv). +// Disable expanding file path for security(no use of awkward `wordexp` anymore). // - v2.5.0 Add SetPreserveImageChannels() option to load image data as is. -// - v2.4.3 Fix null object output when when material has all default +// - v2.4.3 Fix null object output when material has all default // parameters. // - v2.4.2 Decode percent-encoded URI. // - v2.4.1 Fix some glTF object class does not have `extensions` and/or @@ -41,7 +49,7 @@ // - 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.1 Add comparison feature(Thanks to @Selmar). // - v2.0.0 glTF 2.0!. // // Tiny glTF loader is using following third party libraries: @@ -64,6 +72,11 @@ #include #include +//Auto-detect C++14 standard version +#if !defined(TINYGLTF_USE_CPP14) && defined(__cplusplus) && (__cplusplus >= 201402L) +#define TINYGLTF_USE_CPP14 +#endif + #ifndef TINYGLTF_USE_CPP14 #include #endif @@ -108,7 +121,11 @@ namespace tinygltf { #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_COMPONENT_TYPE_DOUBLE \ + (5130) // OpenGL double type. Note that some of glTF 2.0 validator does not + // support double type even the schema seems allow any value of + // integer: + // https://github.com/KhronosGroup/glTF/blob/b9884a2fd45130b4d673dd6c8a706ee21ee5c5f7/specification/2.0/schema/accessor.schema.json#L22 #define TINYGLTF_TEXTURE_FILTER_NEAREST (9728) #define TINYGLTF_TEXTURE_FILTER_LINEAR (9729) @@ -186,7 +203,11 @@ namespace tinygltf { #ifdef __ANDROID__ #ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS +#ifdef TINYGLTF_IMPLEMENTATION AAssetManager *asset_manager = nullptr; +#else +extern AAssetManager *asset_manager; +#endif #endif #endif @@ -219,7 +240,7 @@ static inline int32_t GetComponentSizeInBytes(uint32_t componentType) { } else if (componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) { return 8; } else { - // Unknown componenty type + // Unknown component type return -1; } } @@ -240,7 +261,7 @@ static inline int32_t GetNumComponentsInType(uint32_t ty) { } else if (ty == TINYGLTF_TYPE_MAT4) { return 16; } else { - // Unknown componenty type + // Unknown component type return -1; } } @@ -253,7 +274,6 @@ bool DecodeDataURI(std::vector *out, std::string &mime_type, #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 @@ -428,7 +448,7 @@ TINYGLTF_VALUE_GET(Value::Object, object_value_) #pragma clang diagnostic ignored "-Wpadded" #endif -/// Agregate object for representing a color +/// Aggregate object for representing a color using ColorValue = std::array; // === legacy interface ==== @@ -465,7 +485,7 @@ struct Parameter { if (it != std::end(json_double_value)) { return int(it->second); } - // As per the spec, if texCoord is ommited, this parameter is 0 + // As per the spec, if texCoord is omitted, this parameter is 0 return 0; } @@ -477,7 +497,7 @@ struct Parameter { if (it != std::end(json_double_value)) { return it->second; } - // As per the spec, if scale is ommited, this paramter is 1 + // As per the spec, if scale is omitted, this parameter is 1 return 1; } @@ -489,7 +509,7 @@ struct Parameter { if (it != std::end(json_double_value)) { return it->second; } - // As per the spec, if strenghth is ommited, this parameter is 1 + // As per the spec, if strength is omitted, this parameter is 1 return 1; } @@ -503,7 +523,7 @@ struct Parameter { /// material ColorValue ColorFactor() const { return { - {// this agregate intialize the std::array object, and uses C++11 RVO. + {// this aggregate initialize 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)}}; } @@ -527,9 +547,10 @@ 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"] + int target_node; // optional index of the node to target (alternative + // target should be provided by extension) + std::string target_path; // required with standard values of ["translation", + // "rotation", "scale", "weights"] Value extras; ExtensionMap extensions; ExtensionMap target_extensions; @@ -614,7 +635,8 @@ struct Sampler { int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT", // "REPEAT"], default "REPEAT" - //int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; // TinyGLTF extension. currently not used. + // int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; // TinyGLTF extension. currently + // not used. Value extras; ExtensionMap extensions; @@ -813,7 +835,7 @@ struct BufferView { size_t byteStride{0}; // minimum 4, maximum 252 (multiple of 4), default 0 = // understood to be tightly packed int target{0}; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"] for vertex indices - // or atttribs. Could be 0 for other data + // or attribs. Could be 0 for other data Value extras; ExtensionMap extensions; @@ -889,7 +911,7 @@ struct Accessor { return componentSizeInBytes * numComponents; } else { - // Check if byteStride is a mulple of the size of the accessor's component + // Check if byteStride is a multiple of the size of the accessor's component // type. int componentSizeInBytes = GetComponentSizeInBytes(static_cast(componentType)); @@ -928,7 +950,7 @@ struct PerspectiveCamera { PerspectiveCamera() : aspectRatio(0.0), yfov(0.0), - zfar(0.0) // 0 = use infinite projecton matrix + zfar(0.0) // 0 = use infinite projection matrix , znear(0.0) {} DEFAULT_METHODS(PerspectiveCamera) @@ -989,7 +1011,7 @@ struct Primitive { 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", + // where each target is a dict with attributes in ["POSITION, "NORMAL", // "TANGENT"] pointing // to their corresponding accessors ExtensionMap extensions; @@ -1124,7 +1146,7 @@ struct Light { std::vector color; double intensity{1.0}; std::string type; - double range{0.0}; // 0.0 = inifinite + double range{0.0}; // 0.0 = infinite SpotLight spot; Light() : intensity(1.0), range(0.0) {} @@ -1188,6 +1210,39 @@ enum SectionCheck { REQUIRE_ALL = 0x7f }; +/// +/// URIEncodeFunction type. Signature for custom URI encoding of external +/// resources such as .bin and image files. Used by tinygltf to re-encode the +/// final location of saved files. object_type may be used to encode buffer and +/// image URIs differently, for example. See +/// https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#uris +/// +typedef bool (*URIEncodeFunction)(const std::string &in_uri, + const std::string &object_type, + std::string *out_uri, void *user_data); + +/// +/// URIDecodeFunction type. Signature for custom URI decoding of external +/// resources such as .bin and image files. Used by tinygltf when computing +/// filenames to write resources. +/// +typedef bool (*URIDecodeFunction)(const std::string &in_uri, + std::string *out_uri, void *user_data); + +// Declaration of default uri decode function +bool URIDecode(const std::string &in_uri, std::string *out_uri, + void *user_data); + +/// +/// A structure containing URI callbacks and a pointer to their user data. +/// +struct URICallbacks { + URIEncodeFunction encode; // Optional encode method + URIDecodeFunction decode; // Required decode method + + void *user_data; // An argument that is passed to all uri callbacks +}; + /// /// LoadImageDataFunction type. Signature for custom image loading callbacks. /// @@ -1198,9 +1253,15 @@ typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *, /// /// WriteImageDataFunction type. Signature for custom image writing callbacks. +/// The out_uri parameter becomes the URI written to the gltf and may reference +/// a file or contain a data URI. /// -typedef bool (*WriteImageDataFunction)(const std::string *, const std::string *, - Image *, bool, void *); +typedef bool (*WriteImageDataFunction)(const std::string *basepath, + const std::string *filename, + const Image *image, bool embedImages, + const URICallbacks *uri_cb, + std::string *out_uri, + void *user_pointer); #ifndef TINYGLTF_NO_STB_IMAGE // Declaration of default image loader callback @@ -1212,7 +1273,8 @@ bool LoadImageData(Image *image, const int image_idx, std::string *err, #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 *); + const Image *image, bool embedImages, + const URICallbacks *uri_cb, std::string *out_uri, void *); #endif /// @@ -1274,7 +1336,7 @@ bool WriteWholeFile(std::string *err, const std::string &filepath, #endif /// -/// glTF Parser/Serialier context. +/// glTF Parser/Serializer context. /// class TinyGLTF { public: @@ -1303,8 +1365,10 @@ class TinyGLTF { /// /// 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. + /// `base_dir` is a search path of glTF asset(e.g. images). Path Must be an + /// expanded path (e.g. no tilde(`~`), no environment variables). 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, @@ -1323,6 +1387,8 @@ class TinyGLTF { /// /// Loads glTF binary asset from memory. /// `length` = strlen(str); + /// `base_dir` is a search path of glTF asset(e.g. images). Path Must be an + /// expanded path (e.g. no tilde(`~`), no environment variables). /// 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. /// @@ -1333,15 +1399,15 @@ class TinyGLTF { unsigned int check_sections = REQUIRE_VERSION); /// - /// Write glTF to stream, buffers and images will be embeded + /// Write glTF to stream, buffers and images will be embedded /// - bool WriteGltfSceneToStream(Model *model, std::ostream &stream, + bool WriteGltfSceneToStream(const Model *model, std::ostream &stream, bool prettyPrint, bool writeBinary); /// /// Write glTF to file. /// - bool WriteGltfSceneToFile(Model *model, const std::string &filename, + bool WriteGltfSceneToFile(const Model *model, const std::string &filename, bool embedImages, bool embedBuffers, bool prettyPrint, bool writeBinary); @@ -1360,6 +1426,11 @@ class TinyGLTF { /// void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data); + /// + /// Set callbacks to use for URI encoding and decoding and their user data + /// + void SetURICallbacks(URICallbacks callbacks); + /// /// Set callbacks to use for filesystem (fs) access and their user data /// @@ -1368,7 +1439,7 @@ class TinyGLTF { /// /// 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 + /// This may be helpful if you want to serialize a full description of glTF /// data. /// /// TODO(LTE): Supply parsing option as function arguments to @@ -1394,8 +1465,8 @@ class TinyGLTF { } /// - /// Specify whether preserve image channales when loading images or not. - /// (Not effective when the user suppy their own LoadImageData callbacks) + /// Specify whether preserve image channels when loading images or not. + /// (Not effective when the user supplies their own LoadImageData callbacks) /// void SetPreserveImageChannels(bool onoff) { preserve_image_channels_ = onoff; @@ -1425,6 +1496,10 @@ class TinyGLTF { bool preserve_image_channels_ = false; /// Default false(expand channels to /// RGBA) for backward compatibility. + // Warning & error messages + std::string warn_; + std::string err_; + FsCallbacks fs = { #ifndef TINYGLTF_NO_FS &tinygltf::FileExists, &tinygltf::ExpandFilePath, @@ -1438,6 +1513,15 @@ class TinyGLTF { #endif }; + URICallbacks uri_cb = { + // Use paths as-is by default. This will use JSON string escaping. + nullptr, + // Decode all URIs before using them as paths as the application may have + // percent encoded them. + &tinygltf::URIDecode, + // URI callback user data + nullptr}; + LoadImageDataFunction LoadImageData = #ifndef TINYGLTF_NO_STB_IMAGE &tinygltf::LoadImageData; @@ -1528,7 +1612,7 @@ class TinyGLTF { #endif #endif -// Disable GCC warnigs +// Disable GCC warnings #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wtype-limits" @@ -1577,7 +1661,7 @@ class TinyGLTF { // issue 143. // Define NOMINMAX to avoid min/max defines, -// but undef it after included windows.h +// but undef it after included Windows.h #ifndef NOMINMAX #define TINYGLTF_INTERNAL_NOMINMAX #define NOMINMAX @@ -1587,7 +1671,11 @@ class TinyGLTF { #define WIN32_LEAN_AND_MEAN #define TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN #endif -#include // include API for expanding a file path +#ifndef __MINGW32__ +#include // include API for expanding a file path +#else +#include +#endif #ifdef TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN #undef WIN32_LEAN_AND_MEAN @@ -1606,10 +1694,10 @@ class TinyGLTF { #endif #elif !defined(__ANDROID__) && !defined(__OpenBSD__) -#include +//#include #endif -#if defined(__sparcv9) +#if defined(__sparcv9) || defined(__powerpc__) // Big endian #else #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU @@ -1617,7 +1705,8 @@ class TinyGLTF { #endif #endif -namespace { +namespace tinygltf { +namespace detail { #ifdef TINYGLTF_USE_RAPIDJSON #ifdef TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR @@ -1693,10 +1782,11 @@ void JsonParse(JsonDocument &doc, const char *str, size_t length, (void)throwExc; doc.Parse(str, length); #else - doc = json::parse(str, str + length, nullptr, throwExc); + doc = detail::json::parse(str, str + length, nullptr, throwExc); #endif } } // namespace +} #ifdef __APPLE__ #include "TargetConditionals.h" @@ -1718,7 +1808,7 @@ namespace tinygltf { struct LoadImageDataOption { // true: preserve image channels(e.g. load as RGB image if the image has RGB // channels) default `false`(channels are expanded to RGBA for backward - // compatiblity). + // compatibility). bool preserve_channels{false}; }; @@ -1934,9 +2024,9 @@ bool Sampler::operator==(const Sampler &other) const { return this->extensions == other.extensions && this->extras == other.extras && this->magFilter == other.magFilter && this->minFilter == other.minFilter && this->name == other.name && - this->wrapT == other.wrapT; + this->wrapS == other.wrapS && this->wrapT == other.wrapT; - //this->wrapR == other.wrapR && this->wrapS == other.wrapS && + // this->wrapR == other.wrapR } bool Scene::operator==(const Scene &other) const { return this->extensions == other.extensions && this->extras == other.extras && @@ -2040,9 +2130,10 @@ static std::string GetBaseDir(const std::string &filepath) { 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); + auto idx = filepath.find_last_of("/\\"); + if (idx != std::string::npos) return filepath.substr(idx + 1); + return filepath; } std::string base64_encode(unsigned char const *, unsigned int len); @@ -2188,47 +2279,35 @@ std::string base64_decode(std::string const &encoded_string) { // TODO(syoyo): Use uriparser https://uriparser.github.io/ for stricter Uri // decoding? // -// https://stackoverflow.com/questions/18307429/encode-decode-url-in-c +// Uri Decoding from DLIB // http://dlib.net/dlib/server/server_http.cpp.html - -// --- dlib beign ------------------------------------------------------------ +// --- dlib begin ------------------------------------------------------------ // Copyright (C) 2003 Davis E. King (davis@dlib.net) -// License: Boost Software License See LICENSE.txt for the full license. +// License: Boost Software License +// Boost Software License - Version 1.0 - August 17th, 2003 +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software, unless such copies or derivative +// works are solely in the form of machine-executable object code generated by +// a source language processor. +// 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, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// namespace dlib { -#if 0 - inline unsigned char to_hex( unsigned char x ) - { - return x + (x > 9 ? ('A'-10) : '0'); - } - - const std::string urlencode( const std::string& s ) - { - std::ostringstream os; - - for ( std::string::const_iterator ci = s.begin(); ci != s.end(); ++ci ) - { - if ( (*ci >= 'a' && *ci <= 'z') || - (*ci >= 'A' && *ci <= 'Z') || - (*ci >= '0' && *ci <= '9') ) - { // allowed - os << *ci; - } - else if ( *ci == ' ') - { - os << '+'; - } - else - { - os << '%' << to_hex(static_cast(*ci >> 4)) << to_hex(static_cast(*ci % 16)); - } - } - - return os.str(); - } -#endif - inline unsigned char from_hex(unsigned char ch) { if (ch <= '9' && ch >= '0') ch -= '0'; @@ -2266,6 +2345,13 @@ static const std::string urldecode(const std::string &str) { } // namespace dlib // --- dlib end -------------------------------------------------------------- +bool URIDecode(const std::string &in_uri, std::string *out_uri, + void *user_data) { + (void)user_data; + *out_uri = dlib::urldecode(in_uri); + return true; +} + static bool LoadExternalFile(std::vector *out, std::string *err, std::string *warn, const std::string &filename, const std::string &basedir, bool required, @@ -2376,7 +2462,7 @@ bool LoadImageData(Image *image, const int image_idx, std::string *err, // 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 + // set the image data accordingly. 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: @@ -2391,7 +2477,7 @@ bool LoadImageData(Image *image, const int image_idx, std::string *err, // 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: + // image 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 @@ -2476,7 +2562,9 @@ static void WriteToMemory_stbi(void *context, void *data, int size) { } bool WriteImageData(const std::string *basepath, const std::string *filename, - Image *image, bool embedImages, void *fsPtr) { + const Image *image, bool embedImages, + const URICallbacks *uri_cb, std::string *out_uri, + void *fsPtr) { const std::string ext = GetFilePathExtension(*filename); // Write image to temporary buffer @@ -2518,9 +2606,8 @@ bool WriteImageData(const std::string *basepath, const std::string *filename, if (embedImages) { // Embed base64-encoded image into URI if (data.size()) { - image->uri = - header + - base64_encode(&data[0], static_cast(data.size())); + *out_uri = header + + base64_encode(&data[0], static_cast(data.size())); } else { // Throw error? } @@ -2538,20 +2625,33 @@ bool WriteImageData(const std::string *basepath, const std::string *filename, } else { // Throw error? } - image->uri = *filename; + if (uri_cb->encode) { + if (!uri_cb->encode(*filename, "image", out_uri, uri_cb->user_data)) { + return false; + } + } else { + *out_uri = *filename; + } } return true; } #endif +void TinyGLTF::SetURICallbacks(URICallbacks callbacks) { + assert(callbacks.decode); + if (callbacks.decode) { + uri_cb = callbacks; + } +} + void TinyGLTF::SetFsCallbacks(FsCallbacks callbacks) { fs = callbacks; } #ifdef _WIN32 static inline std::wstring UTF8ToWchar(const std::string &str) { int wstr_size = MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0); - std::wstring wstr(wstr_size, 0); + std::wstring wstr((size_t)wstr_size, 0); MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0], (int)wstr.size()); return wstr; @@ -2559,10 +2659,10 @@ static inline std::wstring UTF8ToWchar(const std::string &str) { static inline std::string WcharToUTF8(const std::wstring &wstr) { int str_size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), - nullptr, 0, NULL, NULL); - std::string str(str_size, 0); + nullptr, 0, nullptr, nullptr); + std::string str((size_t)str_size, 0); WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), &str[0], - (int)str.size(), NULL, NULL); + (int)str.size(), nullptr, nullptr); return str; } #endif @@ -2586,7 +2686,7 @@ bool FileExists(const std::string &abs_filename, void *) { } #else #ifdef _WIN32 -#if defined(_MSC_VER) || defined(__GLIBCXX__) +#if defined(_MSC_VER) || defined(__GLIBCXX__) || defined(_LIBCPP_VERSION) FILE *fp = nullptr; errno_t err = _wfopen_s(&fp, UTF8ToWchar(abs_filename).c_str(), L"rb"); if (err != 0) { @@ -2615,6 +2715,18 @@ bool FileExists(const std::string &abs_filename, void *) { } std::string ExpandFilePath(const std::string &filepath, void *) { + // https://github.com/syoyo/tinygltf/issues/368 + // + // No file path expansion in built-in FS function anymore, since glTF URI + // should not contain tilde('~') and environment variables, and for security + // reason(`wordexp`). + // + // Users need to supply `base_dir`(in `LoadASCIIFromString`, + // `LoadBinaryFromMemory`) in expanded absolute path. + + return filepath; + +#if 0 #ifdef _WIN32 // Assume input `filepath` is encoded in UTF-8 std::wstring wfilepath = UTF8ToWchar(filepath); @@ -2662,6 +2774,7 @@ std::string ExpandFilePath(const std::string &filepath, void *) { return s; #endif +#endif } bool ReadWholeFile(std::vector *out, std::string *err, @@ -2796,15 +2909,21 @@ static std::string MimeToExt(const std::string &mimeType) { return ""; } -static void UpdateImageObject(Image &image, std::string &baseDir, int index, - bool embedImages, - WriteImageDataFunction *WriteImageData = nullptr, - void *user_data = nullptr) { +static bool UpdateImageObject(const Image &image, std::string &baseDir, + int index, bool embedImages, + const URICallbacks *uri_cb, + WriteImageDataFunction *WriteImageData, + void *user_data, std::string *out_uri) { std::string filename; std::string ext; - // If image has uri, use it it as a filename + // If image has uri, use it as a filename if (image.uri.size()) { - filename = GetBaseFilename(image.uri); + std::string decoded_uri; + if (!uri_cb->decode(image.uri, &decoded_uri, uri_cb->user_data)) { + // A decode failure results in a failure to write the gltf. + return false; + } + filename = GetBaseFilename(decoded_uri); ext = GetFilePathExtension(filename); } else if (image.bufferView != -1) { // If there's no URI and the data exists in a buffer, @@ -2819,11 +2938,24 @@ static void UpdateImageObject(Image &image, std::string &baseDir, int index, filename = std::to_string(index) + "." + ext; } - // If callback is set, modify image data object - if (*WriteImageData != nullptr && !filename.empty()) { - std::string uri; - (*WriteImageData)(&baseDir, &filename, &image, embedImages, user_data); + // If callback is set and image data exists, modify image data object. If + // image data does not exist, this is not considered a failure and the + // original uri should be maintained. + bool imageWritten = false; + if (*WriteImageData != nullptr && !filename.empty() && !image.image.empty()) { + imageWritten = (*WriteImageData)(&baseDir, &filename, &image, embedImages, + uri_cb, out_uri, user_data); + if (!imageWritten) { + return false; + } } + + // Use the original uri if the image was not written. + if (!imageWritten) { + *out_uri = image.uri; + } + + return true; } bool IsDataURI(const std::string &in) { @@ -2937,8 +3069,8 @@ bool DecodeDataURI(std::vector *out, std::string &mime_type, return true; } -namespace { -bool GetInt(const json &o, int &val) { +namespace detail { +bool GetInt(const detail::json &o, int &val) { #ifdef TINYGLTF_USE_RAPIDJSON if (!o.IsDouble()) { if (o.IsInt()) { @@ -2960,8 +3092,8 @@ bool GetInt(const json &o, int &val) { #else auto type = o.type(); - if ((type == json::value_t::number_integer) || - (type == json::value_t::number_unsigned)) { + if ((type == detail::json::value_t::number_integer) || + (type == detail::json::value_t::number_unsigned)) { val = static_cast(o.get()); return true; } @@ -2971,7 +3103,7 @@ bool GetInt(const json &o, int &val) { } #ifdef TINYGLTF_USE_RAPIDJSON -bool GetDouble(const json &o, double &val) { +bool GetDouble(const detail::json &o, double &val) { if (o.IsDouble()) { val = o.GetDouble(); return true; @@ -2981,7 +3113,7 @@ bool GetDouble(const json &o, double &val) { } #endif -bool GetNumber(const json &o, double &val) { +bool GetNumber(const detail::json &o, double &val) { #ifdef TINYGLTF_USE_RAPIDJSON if (o.IsNumber()) { val = o.GetDouble(); @@ -2999,7 +3131,7 @@ bool GetNumber(const json &o, double &val) { #endif } -bool GetString(const json &o, std::string &val) { +bool GetString(const detail::json &o, std::string &val) { #ifdef TINYGLTF_USE_RAPIDJSON if (o.IsString()) { val = o.GetString(); @@ -3008,7 +3140,7 @@ bool GetString(const json &o, std::string &val) { return false; #else - if (o.type() == json::value_t::string) { + if (o.type() == detail::json::value_t::string) { val = o.get(); return true; } @@ -3017,7 +3149,7 @@ bool GetString(const json &o, std::string &val) { #endif } -bool IsArray(const json &o) { +bool IsArray(const detail::json &o) { #ifdef TINYGLTF_USE_RAPIDJSON return o.IsArray(); #else @@ -3025,7 +3157,7 @@ bool IsArray(const json &o) { #endif } -json_const_array_iterator ArrayBegin(const json &o) { +detail::json_const_array_iterator ArrayBegin(const detail::json &o) { #ifdef TINYGLTF_USE_RAPIDJSON return o.Begin(); #else @@ -3033,7 +3165,7 @@ json_const_array_iterator ArrayBegin(const json &o) { #endif } -json_const_array_iterator ArrayEnd(const json &o) { +detail::json_const_array_iterator ArrayEnd(const detail::json &o) { #ifdef TINYGLTF_USE_RAPIDJSON return o.End(); #else @@ -3041,7 +3173,7 @@ json_const_array_iterator ArrayEnd(const json &o) { #endif } -bool IsObject(const json &o) { +bool IsObject(const detail::json &o) { #ifdef TINYGLTF_USE_RAPIDJSON return o.IsObject(); #else @@ -3049,7 +3181,7 @@ bool IsObject(const json &o) { #endif } -json_const_iterator ObjectBegin(const json &o) { +detail::json_const_iterator ObjectBegin(const detail::json &o) { #ifdef TINYGLTF_USE_RAPIDJSON return o.MemberBegin(); #else @@ -3057,7 +3189,7 @@ json_const_iterator ObjectBegin(const json &o) { #endif } -json_const_iterator ObjectEnd(const json &o) { +detail::json_const_iterator ObjectEnd(const detail::json &o) { #ifdef TINYGLTF_USE_RAPIDJSON return o.MemberEnd(); #else @@ -3067,7 +3199,7 @@ json_const_iterator ObjectEnd(const json &o) { // Making this a const char* results in a pointer to a temporary when // TINYGLTF_USE_RAPIDJSON is off. -std::string GetKey(json_const_iterator &it) { +std::string GetKey(detail::json_const_iterator &it) { #ifdef TINYGLTF_USE_RAPIDJSON return it->name.GetString(); #else @@ -3075,7 +3207,7 @@ std::string GetKey(json_const_iterator &it) { #endif } -bool FindMember(const json &o, const char *member, json_const_iterator &it) { +bool FindMember(const detail::json &o, const char *member, detail::json_const_iterator &it) { #ifdef TINYGLTF_USE_RAPIDJSON if (!o.IsObject()) { return false; @@ -3088,7 +3220,7 @@ bool FindMember(const json &o, const char *member, json_const_iterator &it) { #endif } -const json &GetValue(json_const_iterator &it) { +const detail::json &GetValue(detail::json_const_iterator &it) { #ifdef TINYGLTF_USE_RAPIDJSON return it->value; #else @@ -3096,17 +3228,23 @@ const json &GetValue(json_const_iterator &it) { #endif } -std::string JsonToString(const json &o, int spacing = -1) { +std::string JsonToString(const detail::json &o, int spacing = -1) { #ifdef TINYGLTF_USE_RAPIDJSON using namespace rapidjson; StringBuffer buffer; if (spacing == -1) { Writer writer(buffer); - o.Accept(writer); + // TODO: Better error handling. + // https://github.com/syoyo/tinygltf/issues/332 + if (!o.Accept(writer)) { + return "tiny_gltf::JsonToString() failed rapidjson conversion"; + } } else { PrettyWriter writer(buffer); writer.SetIndent(' ', uint32_t(spacing)); - o.Accept(writer); + if (!o.Accept(writer)) { + return "tiny_gltf::JsonToString() failed rapidjson conversion"; + } } return buffer.GetString(); #else @@ -3116,7 +3254,7 @@ std::string JsonToString(const json &o, int spacing = -1) { } // namespace -static bool ParseJsonAsValue(Value *ret, const json &o) { +static bool ParseJsonAsValue(Value *ret, const detail::json &o) { Value val{}; #ifdef TINYGLTF_USE_RAPIDJSON using rapidjson::Type; @@ -3127,7 +3265,7 @@ static bool ParseJsonAsValue(Value *ret, const json &o) { Value entry; ParseJsonAsValue(&entry, it->value); if (entry.Type() != NULL_TYPE) - value_object.emplace(GetKey(it), std::move(entry)); + value_object.emplace(detail::GetKey(it), std::move(entry)); } if (value_object.size() > 0) val = Value(std::move(value_object)); } break; @@ -3152,11 +3290,11 @@ static bool ParseJsonAsValue(Value *ret, const json &o) { case Type::kNumberType: if (!o.IsDouble()) { int i = 0; - GetInt(o, i); + detail::GetInt(o, i); val = Value(i); } else { double d = 0.0; - GetDouble(o, d); + detail::GetDouble(o, d); val = Value(d); } break; @@ -3166,7 +3304,7 @@ static bool ParseJsonAsValue(Value *ret, const json &o) { } #else switch (o.type()) { - case json::value_t::object: { + case detail::json::value_t::object: { Value::Object value_object; for (auto it = o.begin(); it != o.end(); it++) { Value entry; @@ -3176,7 +3314,7 @@ static bool ParseJsonAsValue(Value *ret, const json &o) { } if (value_object.size() > 0) val = Value(std::move(value_object)); } break; - case json::value_t::array: { + case detail::json::value_t::array: { Value::Array value_array; value_array.reserve(o.size()); for (auto it = o.begin(); it != o.end(); it++) { @@ -3187,46 +3325,48 @@ static bool ParseJsonAsValue(Value *ret, const json &o) { } if (value_array.size() > 0) val = Value(std::move(value_array)); } break; - case json::value_t::string: + case detail::json::value_t::string: val = Value(o.get()); break; - case json::value_t::boolean: + case detail::json::value_t::boolean: val = Value(o.get()); break; - case json::value_t::number_integer: - case json::value_t::number_unsigned: + case detail::json::value_t::number_integer: + case detail::json::value_t::number_unsigned: val = Value(static_cast(o.get())); break; - case json::value_t::number_float: + case detail::json::value_t::number_float: val = Value(o.get()); break; - case json::value_t::null: - case json::value_t::binary: - case json::value_t::discarded: + case detail::json::value_t::null: + case detail::json::value_t::discarded: + case detail::json::value_t::binary: // default: break; } #endif + const bool isNotNull = val.Type() != NULL_TYPE; + if (ret) *ret = std::move(val); - return val.Type() != NULL_TYPE; + return isNotNull; } -static bool ParseExtrasProperty(Value *ret, const json &o) { - json_const_iterator it; - if (!FindMember(o, "extras", it)) { +static bool ParseExtrasProperty(Value *ret, const detail::json &o) { + detail::json_const_iterator it; + if (!detail::FindMember(o, "extras", it)) { return false; } - return ParseJsonAsValue(ret, GetValue(it)); + return ParseJsonAsValue(ret, detail::GetValue(it)); } -static bool ParseBooleanProperty(bool *ret, std::string *err, const json &o, +static bool ParseBooleanProperty(bool *ret, std::string *err, const detail::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)) { + detail::json_const_iterator it; + if (!detail::FindMember(o, property.c_str(), it)) { if (required) { if (err) { (*err) += "'" + property + "' property is missing"; @@ -3239,7 +3379,7 @@ static bool ParseBooleanProperty(bool *ret, std::string *err, const json &o, return false; } - auto &value = GetValue(it); + auto &value = detail::GetValue(it); bool isBoolean; bool boolValue = false; @@ -3270,12 +3410,12 @@ static bool ParseBooleanProperty(bool *ret, std::string *err, const json &o, return true; } -static bool ParseIntegerProperty(int *ret, std::string *err, const json &o, +static bool ParseIntegerProperty(int *ret, std::string *err, const detail::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)) { + detail::json_const_iterator it; + if (!detail::FindMember(o, property.c_str(), it)) { if (required) { if (err) { (*err) += "'" + property + "' property is missing"; @@ -3289,7 +3429,7 @@ static bool ParseIntegerProperty(int *ret, std::string *err, const json &o, } int intValue; - bool isInt = GetInt(GetValue(it), intValue); + bool isInt = detail::GetInt(detail::GetValue(it), intValue); if (!isInt) { if (required) { if (err) { @@ -3306,12 +3446,12 @@ static bool ParseIntegerProperty(int *ret, std::string *err, const json &o, return true; } -static bool ParseUnsignedProperty(size_t *ret, std::string *err, const json &o, +static bool ParseUnsignedProperty(size_t *ret, std::string *err, const detail::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)) { + detail::json_const_iterator it; + if (!detail::FindMember(o, property.c_str(), it)) { if (required) { if (err) { (*err) += "'" + property + "' property is missing"; @@ -3324,7 +3464,7 @@ static bool ParseUnsignedProperty(size_t *ret, std::string *err, const json &o, return false; } - auto &value = GetValue(it); + auto &value = detail::GetValue(it); size_t uValue = 0; bool isUValue; @@ -3359,13 +3499,13 @@ static bool ParseUnsignedProperty(size_t *ret, std::string *err, const json &o, return true; } -static bool ParseNumberProperty(double *ret, std::string *err, const json &o, +static bool ParseNumberProperty(double *ret, std::string *err, const detail::json &o, const std::string &property, const bool required, const std::string &parent_node = "") { - json_const_iterator it; + detail::json_const_iterator it; - if (!FindMember(o, property.c_str(), it)) { + if (!detail::FindMember(o, property.c_str(), it)) { if (required) { if (err) { (*err) += "'" + property + "' property is missing"; @@ -3379,7 +3519,7 @@ static bool ParseNumberProperty(double *ret, std::string *err, const json &o, } double numberValue; - bool isNumber = GetNumber(GetValue(it), numberValue); + bool isNumber = detail::GetNumber(detail::GetValue(it), numberValue); if (!isNumber) { if (required) { @@ -3398,11 +3538,11 @@ static bool ParseNumberProperty(double *ret, std::string *err, const json &o, } static bool ParseNumberArrayProperty(std::vector *ret, std::string *err, - const json &o, const std::string &property, + const detail::json &o, const std::string &property, bool required, const std::string &parent_node = "") { - json_const_iterator it; - if (!FindMember(o, property.c_str(), it)) { + detail::json_const_iterator it; + if (!detail::FindMember(o, property.c_str(), it)) { if (required) { if (err) { (*err) += "'" + property + "' property is missing"; @@ -3415,7 +3555,7 @@ static bool ParseNumberArrayProperty(std::vector *ret, std::string *err, return false; } - if (!IsArray(GetValue(it))) { + if (!detail::IsArray(detail::GetValue(it))) { if (required) { if (err) { (*err) += "'" + property + "' property is not an array"; @@ -3429,10 +3569,10 @@ static bool ParseNumberArrayProperty(std::vector *ret, std::string *err, } ret->clear(); - auto end = ArrayEnd(GetValue(it)); - for (auto i = ArrayBegin(GetValue(it)); i != end; ++i) { + auto end = detail::ArrayEnd(detail::GetValue(it)); + for (auto i = detail::ArrayBegin(detail::GetValue(it)); i != end; ++i) { double numberValue; - const bool isNumber = GetNumber(*i, numberValue); + const bool isNumber = detail::GetNumber(*i, numberValue); if (!isNumber) { if (required) { if (err) { @@ -3452,12 +3592,12 @@ static bool ParseNumberArrayProperty(std::vector *ret, std::string *err, } static bool ParseIntegerArrayProperty(std::vector *ret, std::string *err, - const json &o, + const detail::json &o, const std::string &property, bool required, const std::string &parent_node = "") { - json_const_iterator it; - if (!FindMember(o, property.c_str(), it)) { + detail::json_const_iterator it; + if (!detail::FindMember(o, property.c_str(), it)) { if (required) { if (err) { (*err) += "'" + property + "' property is missing"; @@ -3470,7 +3610,7 @@ static bool ParseIntegerArrayProperty(std::vector *ret, std::string *err, return false; } - if (!IsArray(GetValue(it))) { + if (!detail::IsArray(detail::GetValue(it))) { if (required) { if (err) { (*err) += "'" + property + "' property is not an array"; @@ -3484,10 +3624,10 @@ static bool ParseIntegerArrayProperty(std::vector *ret, std::string *err, } ret->clear(); - auto end = ArrayEnd(GetValue(it)); - for (auto i = ArrayBegin(GetValue(it)); i != end; ++i) { + auto end = detail::ArrayEnd(detail::GetValue(it)); + for (auto i = detail::ArrayBegin(detail::GetValue(it)); i != end; ++i) { int numberValue; - bool isNumber = GetInt(*i, numberValue); + bool isNumber = detail::GetInt(*i, numberValue); if (!isNumber) { if (required) { if (err) { @@ -3507,11 +3647,11 @@ static bool ParseIntegerArrayProperty(std::vector *ret, std::string *err, } static bool ParseStringProperty( - std::string *ret, std::string *err, const json &o, + std::string *ret, std::string *err, const detail::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)) { + detail::json_const_iterator it; + if (!detail::FindMember(o, property.c_str(), it)) { if (required) { if (err) { (*err) += "'" + property + "' property is missing"; @@ -3526,7 +3666,7 @@ static bool ParseStringProperty( } std::string strValue; - if (!GetString(GetValue(it), strValue)) { + if (!detail::GetString(detail::GetValue(it), strValue)) { if (required) { if (err) { (*err) += "'" + property + "' property is not a string type.\n"; @@ -3543,12 +3683,12 @@ static bool ParseStringProperty( } static bool ParseStringIntegerProperty(std::map *ret, - std::string *err, const json &o, + std::string *err, const detail::json &o, const std::string &property, bool required, const std::string &parent = "") { - json_const_iterator it; - if (!FindMember(o, property.c_str(), it)) { + detail::json_const_iterator it; + if (!detail::FindMember(o, property.c_str(), it)) { if (required) { if (err) { if (!parent.empty()) { @@ -3562,10 +3702,10 @@ static bool ParseStringIntegerProperty(std::map *ret, return false; } - const json &dict = GetValue(it); + const detail::json &dict = detail::GetValue(it); // Make sure we are dealing with an object / dictionary. - if (!IsObject(dict)) { + if (!detail::IsObject(dict)) { if (required) { if (err) { (*err) += "'" + property + "' property is not an object.\n"; @@ -3576,12 +3716,12 @@ static bool ParseStringIntegerProperty(std::map *ret, ret->clear(); - json_const_iterator dictIt(ObjectBegin(dict)); - json_const_iterator dictItEnd(ObjectEnd(dict)); + detail::json_const_iterator dictIt(detail::ObjectBegin(dict)); + detail::json_const_iterator dictItEnd(detail::ObjectEnd(dict)); for (; dictIt != dictItEnd; ++dictIt) { int intVal; - if (!GetInt(GetValue(dictIt), intVal)) { + if (!detail::GetInt(detail::GetValue(dictIt), intVal)) { if (required) { if (err) { (*err) += "'" + property + "' value is not an integer type.\n"; @@ -3591,16 +3731,16 @@ static bool ParseStringIntegerProperty(std::map *ret, } // Insert into the list. - (*ret)[GetKey(dictIt)] = intVal; + (*ret)[detail::GetKey(dictIt)] = intVal; } return true; } static bool ParseJSONProperty(std::map *ret, - std::string *err, const json &o, + std::string *err, const detail::json &o, const std::string &property, bool required) { - json_const_iterator it; - if (!FindMember(o, property.c_str(), it)) { + detail::json_const_iterator it; + if (!detail::FindMember(o, property.c_str(), it)) { if (required) { if (err) { (*err) += "'" + property + "' property is missing. \n'"; @@ -3609,9 +3749,9 @@ static bool ParseJSONProperty(std::map *ret, return false; } - const json &obj = GetValue(it); + const detail::json &obj = detail::GetValue(it); - if (!IsObject(obj)) { + if (!detail::IsObject(obj)) { if (required) { if (err) { (*err) += "'" + property + "' property is not a JSON object.\n"; @@ -3622,19 +3762,19 @@ static bool ParseJSONProperty(std::map *ret, ret->clear(); - json_const_iterator it2(ObjectBegin(obj)); - json_const_iterator itEnd(ObjectEnd(obj)); + detail::json_const_iterator it2(detail::ObjectBegin(obj)); + detail::json_const_iterator itEnd(detail::ObjectEnd(obj)); for (; it2 != itEnd; ++it2) { double numVal; - if (GetNumber(GetValue(it2), numVal)) - ret->emplace(std::string(GetKey(it2)), numVal); + if (detail::GetNumber(detail::GetValue(it2), numVal)) + ret->emplace(std::string(detail::GetKey(it2)), numVal); } return true; } static bool ParseParameterProperty(Parameter *param, std::string *err, - const json &o, const std::string &prop, + const detail::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 @@ -3649,7 +3789,8 @@ static bool ParseParameterProperty(Parameter *param, std::string *err, // Found a number array. return true; } else if (ParseNumberProperty(¶m->number_value, err, o, prop, false)) { - return param->has_number_value = true; + param->has_number_value = true; + return true; } else if (ParseJSONProperty(¶m->json_double_value, err, o, prop, false)) { return true; @@ -3666,25 +3807,25 @@ static bool ParseParameterProperty(Parameter *param, std::string *err, } static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err, - const json &o) { + const detail::json &o) { (void)err; - json_const_iterator it; - if (!FindMember(o, "extensions", it)) { + detail::json_const_iterator it; + if (!detail::FindMember(o, "extensions", it)) { return false; } - auto &obj = GetValue(it); - if (!IsObject(obj)) { + auto &obj = detail::GetValue(it); + if (!detail::IsObject(obj)) { return false; } ExtensionMap extensions; - json_const_iterator extIt = ObjectBegin(obj); // it.value().begin(); - json_const_iterator extEnd = ObjectEnd(obj); + detail::json_const_iterator extIt = detail::ObjectBegin(obj); // it.value().begin(); + detail::json_const_iterator extEnd = detail::ObjectEnd(obj); for (; extIt != extEnd; ++extIt) { - auto &itObj = GetValue(extIt); - if (!IsObject(itObj)) continue; - std::string key(GetKey(extIt)); + auto &itObj = detail::GetValue(extIt); + if (!detail::IsObject(itObj)) continue; + std::string key(detail::GetKey(extIt)); if (!ParseJsonAsValue(&extensions[key], itObj)) { if (!key.empty()) { // create empty object so that an extension object is still of type @@ -3699,7 +3840,7 @@ static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err, return true; } -static bool ParseAsset(Asset *asset, std::string *err, const json &o, +static bool ParseAsset(Asset *asset, std::string *err, const detail::json &o, bool store_original_json_for_extras_and_extensions) { ParseStringProperty(&asset->version, err, o, "version", true, "Asset"); ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset"); @@ -3713,15 +3854,15 @@ static bool ParseAsset(Asset *asset, std::string *err, const json &o, if (store_original_json_for_extras_and_extensions) { { - json_const_iterator it; - if (FindMember(o, "extensions", it)) { - asset->extensions_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extensions", it)) { + asset->extensions_json_string = detail::JsonToString(detail::GetValue(it)); } } { - json_const_iterator it; - if (FindMember(o, "extras", it)) { - asset->extras_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extras", it)) { + asset->extras_json_string = detail::JsonToString(detail::GetValue(it)); } } } @@ -3730,18 +3871,19 @@ static bool ParseAsset(Asset *asset, std::string *err, const json &o, } static bool ParseImage(Image *image, const int image_idx, std::string *err, - std::string *warn, const json &o, + std::string *warn, const detail::json &o, bool store_original_json_for_extras_and_extensions, const std::string &basedir, FsCallbacks *fs, + const URICallbacks *uri_cb, 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); + detail::json_const_iterator it; + bool hasBufferView = detail::FindMember(o, "bufferView", it); + bool hasURI = detail::FindMember(o, "uri", it); ParseStringProperty(&image->name, err, o, "name", false); @@ -3770,15 +3912,15 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err, if (store_original_json_for_extras_and_extensions) { { - json_const_iterator eit; - if (FindMember(o, "extensions", eit)) { - image->extensions_json_string = JsonToString(GetValue(eit)); + detail::json_const_iterator eit; + if (detail::FindMember(o, "extensions", eit)) { + image->extensions_json_string = detail::JsonToString(detail::GetValue(eit)); } } { - json_const_iterator eit; - if (FindMember(o, "extras", eit)) { - image->extras_json_string = JsonToString(GetValue(eit)); + detail::json_const_iterator eit; + if (detail::FindMember(o, "extras", eit)) { + image->extras_json_string = detail::JsonToString(detail::GetValue(eit)); } } } @@ -3842,8 +3984,19 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err, image->uri = uri; #ifdef TINYGLTF_NO_EXTERNAL_IMAGE return true; -#endif - std::string decoded_uri = dlib::urldecode(uri); +#else + std::string decoded_uri; + if (!uri_cb->decode(uri, &decoded_uri, uri_cb->user_data)) { + if (warn) { + (*warn) += "Failed to decode 'uri' for image[" + + std::to_string(image_idx) + "] name = [" + image->name + + "]\n"; + } + + // Image loading failure is not critical to overall gltf loading. + return true; + } + if (!LoadExternalFile(&img, err, warn, decoded_uri, basedir, /* required */ false, /* required bytes */ 0, /* checksize */ false, fs)) { @@ -3864,6 +4017,7 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err, } return false; } +#endif } if (*LoadImageData == nullptr) { @@ -3876,7 +4030,7 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err, static_cast(img.size()), load_image_user_data); } -static bool ParseTexture(Texture *texture, std::string *err, const json &o, +static bool ParseTexture(Texture *texture, std::string *err, const detail::json &o, bool store_original_json_for_extras_and_extensions, const std::string &basedir) { (void)basedir; @@ -3894,15 +4048,15 @@ static bool ParseTexture(Texture *texture, std::string *err, const json &o, if (store_original_json_for_extras_and_extensions) { { - json_const_iterator it; - if (FindMember(o, "extensions", it)) { - texture->extensions_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extensions", it)) { + texture->extensions_json_string = detail::JsonToString(detail::GetValue(it)); } } { - json_const_iterator it; - if (FindMember(o, "extras", it)) { - texture->extras_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extras", it)) { + texture->extras_json_string = detail::JsonToString(detail::GetValue(it)); } } } @@ -3913,7 +4067,7 @@ static bool ParseTexture(Texture *texture, std::string *err, const json &o, } static bool ParseTextureInfo( - TextureInfo *texinfo, std::string *err, const json &o, + TextureInfo *texinfo, std::string *err, const detail::json &o, bool store_original_json_for_extras_and_extensions) { if (texinfo == nullptr) { return false; @@ -3931,15 +4085,15 @@ static bool ParseTextureInfo( if (store_original_json_for_extras_and_extensions) { { - json_const_iterator it; - if (FindMember(o, "extensions", it)) { - texinfo->extensions_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extensions", it)) { + texinfo->extensions_json_string = detail::JsonToString(detail::GetValue(it)); } } { - json_const_iterator it; - if (FindMember(o, "extras", it)) { - texinfo->extras_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extras", it)) { + texinfo->extras_json_string = detail::JsonToString(detail::GetValue(it)); } } } @@ -3948,7 +4102,7 @@ static bool ParseTextureInfo( } static bool ParseNormalTextureInfo( - NormalTextureInfo *texinfo, std::string *err, const json &o, + NormalTextureInfo *texinfo, std::string *err, const detail::json &o, bool store_original_json_for_extras_and_extensions) { if (texinfo == nullptr) { return false; @@ -3967,15 +4121,15 @@ static bool ParseNormalTextureInfo( if (store_original_json_for_extras_and_extensions) { { - json_const_iterator it; - if (FindMember(o, "extensions", it)) { - texinfo->extensions_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extensions", it)) { + texinfo->extensions_json_string = detail::JsonToString(detail::GetValue(it)); } } { - json_const_iterator it; - if (FindMember(o, "extras", it)) { - texinfo->extras_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extras", it)) { + texinfo->extras_json_string = detail::JsonToString(detail::GetValue(it)); } } } @@ -3984,7 +4138,7 @@ static bool ParseNormalTextureInfo( } static bool ParseOcclusionTextureInfo( - OcclusionTextureInfo *texinfo, std::string *err, const json &o, + OcclusionTextureInfo *texinfo, std::string *err, const detail::json &o, bool store_original_json_for_extras_and_extensions) { if (texinfo == nullptr) { return false; @@ -4003,15 +4157,15 @@ static bool ParseOcclusionTextureInfo( if (store_original_json_for_extras_and_extensions) { { - json_const_iterator it; - if (FindMember(o, "extensions", it)) { - texinfo->extensions_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extensions", it)) { + texinfo->extensions_json_string = detail::JsonToString(detail::GetValue(it)); } } { - json_const_iterator it; - if (FindMember(o, "extras", it)) { - texinfo->extras_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extras", it)) { + texinfo->extras_json_string = detail::JsonToString(detail::GetValue(it)); } } } @@ -4019,10 +4173,10 @@ static bool ParseOcclusionTextureInfo( return true; } -static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o, +static bool ParseBuffer(Buffer *buffer, std::string *err, const detail::json &o, bool store_original_json_for_extras_and_extensions, - FsCallbacks *fs, const std::string &basedir, - bool is_binary = false, + FsCallbacks *fs, const URICallbacks *uri_cb, + const std::string &basedir, bool is_binary = false, const unsigned char *bin_data = nullptr, size_t bin_size = 0) { size_t byteLength; @@ -4042,10 +4196,10 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o, } } - json_const_iterator type; - if (FindMember(o, "type", type)) { + detail::json_const_iterator type; + if (detail::FindMember(o, "type", type)) { std::string typeStr; - if (GetString(GetValue(type), typeStr)) { + if (detail::GetString(detail::GetValue(type), typeStr)) { if (typeStr.compare("arraybuffer") == 0) { // buffer.type = "arraybuffer"; } @@ -4068,7 +4222,10 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o, } } else { // External .bin file. - std::string decoded_uri = dlib::urldecode(buffer->uri); + std::string decoded_uri; + if (!uri_cb->decode(buffer->uri, &decoded_uri, uri_cb->user_data)) { + return false; + } if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, decoded_uri, basedir, /* required */ true, byteLength, /* checkSize */ true, fs)) { @@ -4080,7 +4237,7 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o, if ((bin_size == 0) || (bin_data == nullptr)) { if (err) { - (*err) += "Invalid binary data in `Buffer'.\n"; + (*err) += "Invalid binary data in `Buffer', or GLB with empty BIN chunk.\n"; } return false; } @@ -4113,7 +4270,10 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o, } } else { // Assume external .bin file. - std::string decoded_uri = dlib::urldecode(buffer->uri); + std::string decoded_uri; + if (!uri_cb->decode(buffer->uri, &decoded_uri, uri_cb->user_data)) { + return false; + } if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, decoded_uri, basedir, /* required */ true, byteLength, /* checkSize */ true, fs)) { @@ -4129,15 +4289,15 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o, if (store_original_json_for_extras_and_extensions) { { - json_const_iterator it; - if (FindMember(o, "extensions", it)) { - buffer->extensions_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extensions", it)) { + buffer->extensions_json_string = detail::JsonToString(detail::GetValue(it)); } } { - json_const_iterator it; - if (FindMember(o, "extras", it)) { - buffer->extras_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extras", it)) { + buffer->extras_json_string = detail::JsonToString(detail::GetValue(it)); } } } @@ -4146,7 +4306,7 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o, } static bool ParseBufferView( - BufferView *bufferView, std::string *err, const json &o, + BufferView *bufferView, std::string *err, const detail::json &o, bool store_original_json_for_extras_and_extensions) { int buffer = -1; if (!ParseIntegerProperty(&buffer, err, o, "buffer", true, "BufferView")) { @@ -4201,15 +4361,15 @@ static bool ParseBufferView( if (store_original_json_for_extras_and_extensions) { { - json_const_iterator it; - if (FindMember(o, "extensions", it)) { - bufferView->extensions_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extensions", it)) { + bufferView->extensions_json_string = detail::JsonToString(detail::GetValue(it)); } } { - json_const_iterator it; - if (FindMember(o, "extras", it)) { - bufferView->extras_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extras", it)) { + bufferView->extras_json_string = detail::JsonToString(detail::GetValue(it)); } } } @@ -4222,40 +4382,48 @@ static bool ParseBufferView( } static bool ParseSparseAccessor(Accessor *accessor, std::string *err, - const json &o) { + const detail::json &o) { accessor->sparse.isSparse = true; int count = 0; - ParseIntegerProperty(&count, err, o, "count", true); + if (!ParseIntegerProperty(&count, err, o, "count", true, "SparseAccessor")) { + return false; + } - json_const_iterator indices_iterator; - json_const_iterator values_iterator; - if (!FindMember(o, "indices", indices_iterator)) { + detail::json_const_iterator indices_iterator; + detail::json_const_iterator values_iterator; + if (!detail::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"; + if (!detail::FindMember(o, "values", values_iterator)) { + (*err) = "the sparse object of this accessor doesn't have values"; return false; } - const json &indices_obj = GetValue(indices_iterator); - const json &values_obj = GetValue(values_iterator); + const detail::json &indices_obj = detail::GetValue(indices_iterator); + const detail::json &values_obj = detail::GetValue(values_iterator); int indices_buffer_view = 0, indices_byte_offset = 0, component_type = 0; - ParseIntegerProperty(&indices_buffer_view, err, indices_obj, "bufferView", - true); + if (!ParseIntegerProperty(&indices_buffer_view, err, indices_obj, + "bufferView", true, "SparseAccessor")) { + return false; + } ParseIntegerProperty(&indices_byte_offset, err, indices_obj, "byteOffset", - true); - ParseIntegerProperty(&component_type, err, indices_obj, "componentType", - true); + false); + if (!ParseIntegerProperty(&component_type, err, indices_obj, "componentType", + true, "SparseAccessor")) { + return false; + } int values_buffer_view = 0, values_byte_offset = 0; - ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView", - true); + if (!ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView", + true, "SparseAccessor")) { + return false; + } ParseIntegerProperty(&values_byte_offset, err, values_obj, "byteOffset", - true); + false); accessor->sparse.count = count; accessor->sparse.indices.bufferView = indices_buffer_view; @@ -4264,12 +4432,10 @@ static bool ParseSparseAccessor(Accessor *accessor, std::string *err, 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, +static bool ParseAccessor(Accessor *accessor, std::string *err, const detail::json &o, bool store_original_json_for_extras_and_extensions) { int bufferView = -1; ParseIntegerProperty(&bufferView, err, o, "bufferView", false, "Accessor"); @@ -4354,24 +4520,24 @@ static bool ParseAccessor(Accessor *accessor, std::string *err, const json &o, if (store_original_json_for_extras_and_extensions) { { - json_const_iterator it; - if (FindMember(o, "extensions", it)) { - accessor->extensions_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extensions", it)) { + accessor->extensions_json_string = detail::JsonToString(detail::GetValue(it)); } } { - json_const_iterator it; - if (FindMember(o, "extras", it)) { - accessor->extras_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extras", it)) { + accessor->extras_json_string = detail::JsonToString(detail::GetValue(it)); } } } // check if accessor has a "sparse" object: - json_const_iterator iterator; - if (FindMember(o, "sparse", iterator)) { + detail::json_const_iterator iterator; + if (detail::FindMember(o, "sparse", iterator)) { // here this accessor has a "sparse" subobject - return ParseSparseAccessor(accessor, err, GetValue(iterator)); + return ParseSparseAccessor(accessor, err, detail::GetValue(iterator)); } return true; @@ -4570,7 +4736,7 @@ static bool ParseDracoExtension(Primitive *primitive, Model *model, #endif static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err, - const json &o, + const detail::json &o, bool store_original_json_for_extras_and_extensions) { int material = -1; ParseIntegerProperty(&material, err, o, "material", false); @@ -4578,7 +4744,7 @@ static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err, int mode = TINYGLTF_MODE_TRIANGLES; ParseIntegerProperty(&mode, err, o, "mode", false); - primitive->mode = mode; // Why only triangled were supported ? + primitive->mode = mode; // Why only triangles were supported ? int indices = -1; ParseIntegerProperty(&indices, err, o, "indices", false); @@ -4589,23 +4755,23 @@ static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err, } // 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)); + detail::json_const_iterator targetsObject; + if (detail::FindMember(o, "targets", targetsObject) && + detail::IsArray(detail::GetValue(targetsObject))) { + auto targetsObjectEnd = detail::ArrayEnd(detail::GetValue(targetsObject)); + for (detail::json_const_array_iterator i = detail::ArrayBegin(detail::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)); + const detail::json &dict = *i; + if (detail::IsObject(dict)) { + detail::json_const_iterator dictIt(detail::ObjectBegin(dict)); + detail::json_const_iterator dictItEnd(detail::ObjectEnd(dict)); for (; dictIt != dictItEnd; ++dictIt) { int iVal; - if (GetInt(GetValue(dictIt), iVal)) - targetAttribues[GetKey(dictIt)] = iVal; + if (detail::GetInt(detail::GetValue(dictIt), iVal)) + targetAttribues[detail::GetKey(dictIt)] = iVal; } primitive->targets.emplace_back(std::move(targetAttribues)); } @@ -4617,15 +4783,15 @@ static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err, if (store_original_json_for_extras_and_extensions) { { - json_const_iterator it; - if (FindMember(o, "extensions", it)) { - primitive->extensions_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extensions", it)) { + primitive->extensions_json_string = detail::JsonToString(detail::GetValue(it)); } } { - json_const_iterator it; - if (FindMember(o, "extras", it)) { - primitive->extras_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extras", it)) { + primitive->extras_json_string = detail::JsonToString(detail::GetValue(it)); } } } @@ -4643,16 +4809,16 @@ static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err, return true; } -static bool ParseMesh(Mesh *mesh, Model *model, std::string *err, const json &o, +static bool ParseMesh(Mesh *mesh, Model *model, std::string *err, const detail::json &o, bool store_original_json_for_extras_and_extensions) { 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)); + detail::json_const_iterator primObject; + if (detail::FindMember(o, "primitives", primObject) && + detail::IsArray(detail::GetValue(primObject))) { + detail::json_const_array_iterator primEnd = detail::ArrayEnd(detail::GetValue(primObject)); + for (detail::json_const_array_iterator i = detail::ArrayBegin(detail::GetValue(primObject)); i != primEnd; ++i) { Primitive primitive; if (ParsePrimitive(&primitive, model, err, *i, @@ -4671,15 +4837,15 @@ static bool ParseMesh(Mesh *mesh, Model *model, std::string *err, const json &o, if (store_original_json_for_extras_and_extensions) { { - json_const_iterator it; - if (FindMember(o, "extensions", it)) { - mesh->extensions_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extensions", it)) { + mesh->extensions_json_string = detail::JsonToString(detail::GetValue(it)); } } { - json_const_iterator it; - if (FindMember(o, "extras", it)) { - mesh->extras_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extras", it)) { + mesh->extras_json_string = detail::JsonToString(detail::GetValue(it)); } } } @@ -4687,7 +4853,7 @@ static bool ParseMesh(Mesh *mesh, Model *model, std::string *err, const json &o, return true; } -static bool ParseNode(Node *node, std::string *err, const json &o, +static bool ParseNode(Node *node, std::string *err, const detail::json &o, bool store_original_json_for_extras_and_extensions) { ParseStringProperty(&node->name, err, o, "name", false); @@ -4720,15 +4886,15 @@ static bool ParseNode(Node *node, std::string *err, const json &o, if (store_original_json_for_extras_and_extensions) { { - json_const_iterator it; - if (FindMember(o, "extensions", it)) { - node->extensions_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extensions", it)) { + node->extensions_json_string = detail::JsonToString(detail::GetValue(it)); } } { - json_const_iterator it; - if (FindMember(o, "extras", it)) { - node->extras_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extras", it)) { + node->extras_json_string = detail::JsonToString(detail::GetValue(it)); } } } @@ -4737,7 +4903,7 @@ static bool ParseNode(Node *node, std::string *err, const json &o, } static bool ParsePbrMetallicRoughness( - PbrMetallicRoughness *pbr, std::string *err, const json &o, + PbrMetallicRoughness *pbr, std::string *err, const detail::json &o, bool store_original_json_for_extras_and_extensions) { if (pbr == nullptr) { return false; @@ -4759,17 +4925,17 @@ static bool ParsePbrMetallicRoughness( } { - json_const_iterator it; - if (FindMember(o, "baseColorTexture", it)) { - ParseTextureInfo(&pbr->baseColorTexture, err, GetValue(it), + detail::json_const_iterator it; + if (detail::FindMember(o, "baseColorTexture", it)) { + ParseTextureInfo(&pbr->baseColorTexture, err, detail::GetValue(it), store_original_json_for_extras_and_extensions); } } { - json_const_iterator it; - if (FindMember(o, "metallicRoughnessTexture", it)) { - ParseTextureInfo(&pbr->metallicRoughnessTexture, err, GetValue(it), + detail::json_const_iterator it; + if (detail::FindMember(o, "metallicRoughnessTexture", it)) { + ParseTextureInfo(&pbr->metallicRoughnessTexture, err, detail::GetValue(it), store_original_json_for_extras_and_extensions); } } @@ -4782,15 +4948,15 @@ static bool ParsePbrMetallicRoughness( if (store_original_json_for_extras_and_extensions) { { - json_const_iterator it; - if (FindMember(o, "extensions", it)) { - pbr->extensions_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extensions", it)) { + pbr->extensions_json_string = detail::JsonToString(detail::GetValue(it)); } } { - json_const_iterator it; - if (FindMember(o, "extras", it)) { - pbr->extras_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extras", it)) { + pbr->extras_json_string = detail::JsonToString(detail::GetValue(it)); } } } @@ -4798,7 +4964,7 @@ static bool ParsePbrMetallicRoughness( return true; } -static bool ParseMaterial(Material *material, std::string *err, const json &o, +static bool ParseMaterial(Material *material, std::string *err, const detail::json &o, bool store_original_json_for_extras_and_extensions) { ParseStringProperty(&material->name, err, o, "name", /* required */ false); @@ -4827,63 +4993,63 @@ static bool ParseMaterial(Material *material, std::string *err, const json &o, /* required */ false); { - json_const_iterator it; - if (FindMember(o, "pbrMetallicRoughness", it)) { + detail::json_const_iterator it; + if (detail::FindMember(o, "pbrMetallicRoughness", it)) { ParsePbrMetallicRoughness(&material->pbrMetallicRoughness, err, - GetValue(it), + detail::GetValue(it), store_original_json_for_extras_and_extensions); } } { - json_const_iterator it; - if (FindMember(o, "normalTexture", it)) { - ParseNormalTextureInfo(&material->normalTexture, err, GetValue(it), + detail::json_const_iterator it; + if (detail::FindMember(o, "normalTexture", it)) { + ParseNormalTextureInfo(&material->normalTexture, err, detail::GetValue(it), store_original_json_for_extras_and_extensions); } } { - json_const_iterator it; - if (FindMember(o, "occlusionTexture", it)) { - ParseOcclusionTextureInfo(&material->occlusionTexture, err, GetValue(it), + detail::json_const_iterator it; + if (detail::FindMember(o, "occlusionTexture", it)) { + ParseOcclusionTextureInfo(&material->occlusionTexture, err, detail::GetValue(it), store_original_json_for_extras_and_extensions); } } { - json_const_iterator it; - if (FindMember(o, "emissiveTexture", it)) { - ParseTextureInfo(&material->emissiveTexture, err, GetValue(it), + detail::json_const_iterator it; + if (detail::FindMember(o, "emissiveTexture", it)) { + ParseTextureInfo(&material->emissiveTexture, err, detail::GetValue(it), store_original_json_for_extras_and_extensions); } } // 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 + // example(pbrMetallicRoughness), but should be negligible 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)); + detail::json_const_iterator it(detail::ObjectBegin(o)); + detail::json_const_iterator itEnd(detail::ObjectEnd(o)); for (; it != itEnd; ++it) { - std::string key(GetKey(it)); + std::string key(detail::GetKey(it)); if (key == "pbrMetallicRoughness") { - if (IsObject(GetValue(it))) { - const json &values_object = GetValue(it); + if (detail::IsObject(detail::GetValue(it))) { + const detail::json &values_object = detail::GetValue(it); - json_const_iterator itVal(ObjectBegin(values_object)); - json_const_iterator itValEnd(ObjectEnd(values_object)); + detail::json_const_iterator itVal(detail::ObjectBegin(values_object)); + detail::json_const_iterator itValEnd(detail::ObjectEnd(values_object)); for (; itVal != itValEnd; ++itVal) { Parameter param; - if (ParseParameterProperty(¶m, err, values_object, GetKey(itVal), + if (ParseParameterProperty(¶m, err, values_object, detail::GetKey(itVal), false)) { - material->values.emplace(GetKey(itVal), std::move(param)); + material->values.emplace(detail::GetKey(itVal), std::move(param)); } } } @@ -4894,7 +5060,7 @@ static bool ParseMaterial(Material *material, std::string *err, const json &o, 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 + // doesn't correctly reflect the glTF specification if (key != "name") material->additionalValues.emplace(std::move(key), std::move(param)); } @@ -4907,15 +5073,15 @@ static bool ParseMaterial(Material *material, std::string *err, const json &o, if (store_original_json_for_extras_and_extensions) { { - json_const_iterator eit; - if (FindMember(o, "extensions", eit)) { - material->extensions_json_string = JsonToString(GetValue(eit)); + detail::json_const_iterator eit; + if (detail::FindMember(o, "extensions", eit)) { + material->extensions_json_string = detail::JsonToString(detail::GetValue(eit)); } } { - json_const_iterator eit; - if (FindMember(o, "extras", eit)) { - material->extras_json_string = JsonToString(GetValue(eit)); + detail::json_const_iterator eit; + if (detail::FindMember(o, "extras", eit)) { + material->extras_json_string = detail::JsonToString(detail::GetValue(eit)); } } } @@ -4924,7 +5090,7 @@ static bool ParseMaterial(Material *material, std::string *err, const json &o, } static bool ParseAnimationChannel( - AnimationChannel *channel, std::string *err, const json &o, + AnimationChannel *channel, std::string *err, const detail::json &o, bool store_original_json_for_extras_and_extensions) { int samplerIndex = -1; int targetIndex = -1; @@ -4936,16 +5102,11 @@ static bool ParseAnimationChannel( return false; } - json_const_iterator targetIt; - if (FindMember(o, "target", targetIt) && IsObject(GetValue(targetIt))) { - const json &target_object = GetValue(targetIt); + detail::json_const_iterator targetIt; + if (detail::FindMember(o, "target", targetIt) && detail::IsObject(detail::GetValue(targetIt))) { + const detail::json &target_object = detail::GetValue(targetIt); - if (!ParseIntegerProperty(&targetIndex, err, target_object, "node", true)) { - if (err) { - (*err) += "`node` field is missing in animation.channels.target\n"; - } - return false; - } + ParseIntegerProperty(&targetIndex, err, target_object, "node", false); if (!ParseStringProperty(&channel->target_path, err, target_object, "path", true)) { @@ -4956,9 +5117,9 @@ static bool ParseAnimationChannel( } ParseExtensionsProperty(&channel->target_extensions, err, target_object); if (store_original_json_for_extras_and_extensions) { - json_const_iterator it; - if (FindMember(target_object, "extensions", it)) { - channel->target_extensions_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(target_object, "extensions", it)) { + channel->target_extensions_json_string = detail::JsonToString(detail::GetValue(it)); } } } @@ -4971,15 +5132,15 @@ static bool ParseAnimationChannel( if (store_original_json_for_extras_and_extensions) { { - json_const_iterator it; - if (FindMember(o, "extensions", it)) { - channel->extensions_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extensions", it)) { + channel->extensions_json_string = detail::JsonToString(detail::GetValue(it)); } } { - json_const_iterator it; - if (FindMember(o, "extras", it)) { - channel->extras_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extras", it)) { + channel->extras_json_string = detail::JsonToString(detail::GetValue(it)); } } } @@ -4988,14 +5149,14 @@ static bool ParseAnimationChannel( } static bool ParseAnimation(Animation *animation, std::string *err, - const json &o, + const detail::json &o, bool store_original_json_for_extras_and_extensions) { { - 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)); + detail::json_const_iterator channelsIt; + if (detail::FindMember(o, "channels", channelsIt) && + detail::IsArray(detail::GetValue(channelsIt))) { + detail::json_const_array_iterator channelEnd = detail::ArrayEnd(detail::GetValue(channelsIt)); + for (detail::json_const_array_iterator i = detail::ArrayBegin(detail::GetValue(channelsIt)); i != channelEnd; ++i) { AnimationChannel channel; if (ParseAnimationChannel( @@ -5009,15 +5170,15 @@ static bool ParseAnimation(Animation *animation, std::string *err, } { - json_const_iterator samplerIt; - if (FindMember(o, "samplers", samplerIt) && IsArray(GetValue(samplerIt))) { - const json &sampler_array = GetValue(samplerIt); + detail::json_const_iterator samplerIt; + if (detail::FindMember(o, "samplers", samplerIt) && detail::IsArray(detail::GetValue(samplerIt))) { + const detail::json &sampler_array = detail::GetValue(samplerIt); - json_const_array_iterator it = ArrayBegin(sampler_array); - json_const_array_iterator itEnd = ArrayEnd(sampler_array); + detail::json_const_array_iterator it = detail::ArrayBegin(sampler_array); + detail::json_const_array_iterator itEnd = detail::ArrayEnd(sampler_array); for (; it != itEnd; ++it) { - const json &s = *it; + const detail::json &s = *it; AnimationSampler sampler; int inputIndex = -1; @@ -5043,15 +5204,15 @@ static bool ParseAnimation(Animation *animation, std::string *err, if (store_original_json_for_extras_and_extensions) { { - json_const_iterator eit; - if (FindMember(o, "extensions", eit)) { - sampler.extensions_json_string = JsonToString(GetValue(eit)); + detail::json_const_iterator eit; + if (detail::FindMember(o, "extensions", eit)) { + sampler.extensions_json_string = detail::JsonToString(detail::GetValue(eit)); } } { - json_const_iterator eit; - if (FindMember(o, "extras", eit)) { - sampler.extras_json_string = JsonToString(GetValue(eit)); + detail::json_const_iterator eit; + if (detail::FindMember(o, "extras", eit)) { + sampler.extras_json_string = detail::JsonToString(detail::GetValue(eit)); } } } @@ -5068,15 +5229,15 @@ static bool ParseAnimation(Animation *animation, std::string *err, if (store_original_json_for_extras_and_extensions) { { - json_const_iterator it; - if (FindMember(o, "extensions", it)) { - animation->extensions_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extensions", it)) { + animation->extensions_json_string = detail::JsonToString(detail::GetValue(it)); } } { - json_const_iterator it; - if (FindMember(o, "extras", it)) { - animation->extras_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extras", it)) { + animation->extras_json_string = detail::JsonToString(detail::GetValue(it)); } } } @@ -5084,7 +5245,7 @@ static bool ParseAnimation(Animation *animation, std::string *err, return true; } -static bool ParseSampler(Sampler *sampler, std::string *err, const json &o, +static bool ParseSampler(Sampler *sampler, std::string *err, const detail::json &o, bool store_original_json_for_extras_and_extensions) { ParseStringProperty(&sampler->name, err, o, "name", false); @@ -5092,36 +5253,37 @@ static bool ParseSampler(Sampler *sampler, std::string *err, const json &o, int magFilter = -1; int wrapS = TINYGLTF_TEXTURE_WRAP_REPEAT; int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT; - //int wrapR = 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 + // ParseIntegerProperty(&wrapR, err, o, "wrapR", false); // tinygltf + // extension - // TODO(syoyo): Check the value is alloed one. + // TODO(syoyo): Check the value is allowed 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; + // sampler->wrapR = wrapR; ParseExtensionsProperty(&(sampler->extensions), err, o); ParseExtrasProperty(&(sampler->extras), o); if (store_original_json_for_extras_and_extensions) { { - json_const_iterator it; - if (FindMember(o, "extensions", it)) { - sampler->extensions_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extensions", it)) { + sampler->extensions_json_string = detail::JsonToString(detail::GetValue(it)); } } { - json_const_iterator it; - if (FindMember(o, "extras", it)) { - sampler->extras_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extras", it)) { + sampler->extras_json_string = detail::JsonToString(detail::GetValue(it)); } } } @@ -5129,7 +5291,7 @@ static bool ParseSampler(Sampler *sampler, std::string *err, const json &o, return true; } -static bool ParseSkin(Skin *skin, std::string *err, const json &o, +static bool ParseSkin(Skin *skin, std::string *err, const detail::json &o, bool store_original_json_for_extras_and_extensions) { ParseStringProperty(&skin->name, err, o, "name", false, "Skin"); @@ -5152,15 +5314,15 @@ static bool ParseSkin(Skin *skin, std::string *err, const json &o, if (store_original_json_for_extras_and_extensions) { { - json_const_iterator it; - if (FindMember(o, "extensions", it)) { - skin->extensions_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extensions", it)) { + skin->extensions_json_string = detail::JsonToString(detail::GetValue(it)); } } { - json_const_iterator it; - if (FindMember(o, "extras", it)) { - skin->extras_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extras", it)) { + skin->extras_json_string = detail::JsonToString(detail::GetValue(it)); } } } @@ -5169,7 +5331,7 @@ static bool ParseSkin(Skin *skin, std::string *err, const json &o, } static bool ParsePerspectiveCamera( - PerspectiveCamera *camera, std::string *err, const json &o, + PerspectiveCamera *camera, std::string *err, const detail::json &o, bool store_original_json_for_extras_and_extensions) { double yfov = 0.0; if (!ParseNumberProperty(&yfov, err, o, "yfov", true, "OrthographicCamera")) { @@ -5199,15 +5361,15 @@ static bool ParsePerspectiveCamera( if (store_original_json_for_extras_and_extensions) { { - json_const_iterator it; - if (FindMember(o, "extensions", it)) { - camera->extensions_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extensions", it)) { + camera->extensions_json_string = detail::JsonToString(detail::GetValue(it)); } } { - json_const_iterator it; - if (FindMember(o, "extras", it)) { - camera->extras_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extras", it)) { + camera->extras_json_string = detail::JsonToString(detail::GetValue(it)); } } } @@ -5217,7 +5379,7 @@ static bool ParsePerspectiveCamera( return true; } -static bool ParseSpotLight(SpotLight *light, std::string *err, const json &o, +static bool ParseSpotLight(SpotLight *light, std::string *err, const detail::json &o, bool store_original_json_for_extras_and_extensions) { ParseNumberProperty(&light->innerConeAngle, err, o, "innerConeAngle", false); ParseNumberProperty(&light->outerConeAngle, err, o, "outerConeAngle", false); @@ -5227,15 +5389,15 @@ static bool ParseSpotLight(SpotLight *light, std::string *err, const json &o, if (store_original_json_for_extras_and_extensions) { { - json_const_iterator it; - if (FindMember(o, "extensions", it)) { - light->extensions_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extensions", it)) { + light->extensions_json_string = detail::JsonToString(detail::GetValue(it)); } } { - json_const_iterator it; - if (FindMember(o, "extras", it)) { - light->extras_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extras", it)) { + light->extras_json_string = detail::JsonToString(detail::GetValue(it)); } } } @@ -5246,7 +5408,7 @@ static bool ParseSpotLight(SpotLight *light, std::string *err, const json &o, } static bool ParseOrthographicCamera( - OrthographicCamera *camera, std::string *err, const json &o, + OrthographicCamera *camera, std::string *err, const detail::json &o, bool store_original_json_for_extras_and_extensions) { double xmag = 0.0; if (!ParseNumberProperty(&xmag, err, o, "xmag", true, "OrthographicCamera")) { @@ -5274,15 +5436,15 @@ static bool ParseOrthographicCamera( if (store_original_json_for_extras_and_extensions) { { - json_const_iterator it; - if (FindMember(o, "extensions", it)) { - camera->extensions_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extensions", it)) { + camera->extensions_json_string = detail::JsonToString(detail::GetValue(it)); } } { - json_const_iterator it; - if (FindMember(o, "extras", it)) { - camera->extras_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extras", it)) { + camera->extras_json_string = detail::JsonToString(detail::GetValue(it)); } } } @@ -5297,25 +5459,25 @@ static bool ParseOrthographicCamera( return true; } -static bool ParseCamera(Camera *camera, std::string *err, const json &o, +static bool ParseCamera(Camera *camera, std::string *err, const detail::json &o, bool store_original_json_for_extras_and_extensions) { 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)) { + detail::json_const_iterator orthoIt; + if (!detail::FindMember(o, "orthographic", orthoIt)) { if (err) { std::stringstream ss; - ss << "Orhographic camera description not found." << std::endl; + ss << "Orthographic camera description not found." << std::endl; (*err) += ss.str(); } return false; } - const json &v = GetValue(orthoIt); - if (!IsObject(v)) { + const detail::json &v = detail::GetValue(orthoIt); + if (!detail::IsObject(v)) { if (err) { std::stringstream ss; ss << "\"orthographic\" is not a JSON object." << std::endl; @@ -5330,8 +5492,8 @@ static bool ParseCamera(Camera *camera, std::string *err, const json &o, return false; } } else if (camera->type.compare("perspective") == 0) { - json_const_iterator perspIt; - if (!FindMember(o, "perspective", perspIt)) { + detail::json_const_iterator perspIt; + if (!detail::FindMember(o, "perspective", perspIt)) { if (err) { std::stringstream ss; ss << "Perspective camera description not found." << std::endl; @@ -5340,8 +5502,8 @@ static bool ParseCamera(Camera *camera, std::string *err, const json &o, return false; } - const json &v = GetValue(perspIt); - if (!IsObject(v)) { + const detail::json &v = detail::GetValue(perspIt); + if (!detail::IsObject(v)) { if (err) { std::stringstream ss; ss << "\"perspective\" is not a JSON object." << std::endl; @@ -5372,15 +5534,15 @@ static bool ParseCamera(Camera *camera, std::string *err, const json &o, if (store_original_json_for_extras_and_extensions) { { - json_const_iterator it; - if (FindMember(o, "extensions", it)) { - camera->extensions_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extensions", it)) { + camera->extensions_json_string = detail::JsonToString(detail::GetValue(it)); } } { - json_const_iterator it; - if (FindMember(o, "extras", it)) { - camera->extras_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extras", it)) { + camera->extras_json_string = detail::JsonToString(detail::GetValue(it)); } } } @@ -5388,15 +5550,15 @@ static bool ParseCamera(Camera *camera, std::string *err, const json &o, return true; } -static bool ParseLight(Light *light, std::string *err, const json &o, +static bool ParseLight(Light *light, std::string *err, const detail::json &o, bool store_original_json_for_extras_and_extensions) { if (!ParseStringProperty(&light->type, err, o, "type", true)) { return false; } if (light->type == "spot") { - json_const_iterator spotIt; - if (!FindMember(o, "spot", spotIt)) { + detail::json_const_iterator spotIt; + if (!detail::FindMember(o, "spot", spotIt)) { if (err) { std::stringstream ss; ss << "Spot light description not found." << std::endl; @@ -5405,8 +5567,8 @@ static bool ParseLight(Light *light, std::string *err, const json &o, return false; } - const json &v = GetValue(spotIt); - if (!IsObject(v)) { + const detail::json &v = detail::GetValue(spotIt); + if (!detail::IsObject(v)) { if (err) { std::stringstream ss; ss << "\"spot\" is not a JSON object." << std::endl; @@ -5430,15 +5592,15 @@ static bool ParseLight(Light *light, std::string *err, const json &o, if (store_original_json_for_extras_and_extensions) { { - json_const_iterator it; - if (FindMember(o, "extensions", it)) { - light->extensions_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extensions", it)) { + light->extensions_json_string = detail::JsonToString(detail::GetValue(it)); } } { - json_const_iterator it; - if (FindMember(o, "extras", it)) { - light->extras_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extras", it)) { + light->extras_json_string = detail::JsonToString(detail::GetValue(it)); } } } @@ -5458,13 +5620,13 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, return false; } - JsonDocument v; + detail::JsonDocument v; #if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \ defined(_CPPUNWIND)) && \ !defined(TINYGLTF_NOEXCEPTION) try { - JsonParse(v, json_str, json_str_length, true); + detail::JsonParse(v, json_str, json_str_length, true); } catch (const std::exception &e) { if (err) { @@ -5474,9 +5636,9 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, } #else { - JsonParse(v, json_str, json_str_length); + detail::JsonParse(v, json_str, json_str_length); - if (!IsObject(v)) { + if (!detail::IsObject(v)) { // Assume parsing was failed. if (err) { (*err) = "Failed to parse JSON object\n"; @@ -5486,7 +5648,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, } #endif - if (!IsObject(v)) { + if (!detail::IsObject(v)) { // root is not an object. if (err) { (*err) = "Root element is not a JSON object\n"; @@ -5496,13 +5658,13 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, { 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; + detail::json_const_iterator it; + if (detail::FindMember(v, "asset", it) && detail::IsObject(detail::GetValue(it))) { + auto &itObj = detail::GetValue(it); + detail::json_const_iterator version_it; std::string versionStr; - if (FindMember(itObj, "version", version_it) && - GetString(GetValue(version_it), versionStr)) { + if (detail::FindMember(itObj, "version", version_it) && + detail::GetString(detail::GetValue(version_it), versionStr)) { version_found = true; } } @@ -5519,9 +5681,9 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, // 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)); + auto IsArrayMemberPresent = [](const detail::json &_v, const char *name) -> bool { + detail::json_const_iterator it; + return detail::FindMember(_v, name, it) && detail::IsArray(detail::GetValue(it)); }; { @@ -5586,9 +5748,9 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, // 1. Parse Asset { - json_const_iterator it; - if (FindMember(v, "asset", it) && IsObject(GetValue(it))) { - const json &root = GetValue(it); + detail::json_const_iterator it; + if (detail::FindMember(v, "asset", it) && detail::IsObject(detail::GetValue(it))) { + const detail::json &root = detail::GetValue(it); ParseAsset(&model->asset, err, root, store_original_json_for_extras_and_extensions_); @@ -5596,21 +5758,21 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, } #ifdef TINYGLTF_USE_CPP14 - auto ForEachInArray = [](const json &_v, const char *member, + auto ForEachInArray = [](const detail::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 + auto ForEachInArray = [](const detail::json &_v, const char *member, + const std::function &cb) -> bool #endif { - json_const_iterator itm; - if (FindMember(_v, member, itm) && IsArray(GetValue(itm))) { - const json &root = GetValue(itm); - auto it = ArrayBegin(root); - auto end = ArrayEnd(root); + detail::json_const_iterator itm; + if (detail::FindMember(_v, member, itm) && detail::IsArray(detail::GetValue(itm))) { + const detail::json &root = detail::GetValue(itm); + auto it = detail::ArrayBegin(root); + auto end = detail::ArrayEnd(root); for (; it != end; ++it) { if (!cb(*it)) return false; } @@ -5620,18 +5782,18 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, // 2. Parse extensionUsed { - ForEachInArray(v, "extensionsUsed", [&](const json &o) { + ForEachInArray(v, "extensionsUsed", [&](const detail::json &o) { std::string str; - GetString(o, str); + detail::GetString(o, str); model->extensionsUsed.emplace_back(std::move(str)); return true; }); } { - ForEachInArray(v, "extensionsRequired", [&](const json &o) { + ForEachInArray(v, "extensionsRequired", [&](const detail::json &o) { std::string str; - GetString(o, str); + detail::GetString(o, str); model->extensionsRequired.emplace_back(std::move(str)); return true; }); @@ -5639,8 +5801,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, // 3. Parse Buffer { - bool success = ForEachInArray(v, "buffers", [&](const json &o) { - if (!IsObject(o)) { + bool success = ForEachInArray(v, "buffers", [&](const detail::json &o) { + if (!detail::IsObject(o)) { if (err) { (*err) += "`buffers' does not contain an JSON object."; } @@ -5649,7 +5811,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, Buffer buffer; if (!ParseBuffer(&buffer, err, o, store_original_json_for_extras_and_extensions_, &fs, - base_dir, is_binary_, bin_data_, bin_size_)) { + &uri_cb, base_dir, is_binary_, bin_data_, bin_size_)) { return false; } @@ -5663,8 +5825,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, } // 4. Parse BufferView { - bool success = ForEachInArray(v, "bufferViews", [&](const json &o) { - if (!IsObject(o)) { + bool success = ForEachInArray(v, "bufferViews", [&](const detail::json &o) { + if (!detail::IsObject(o)) { if (err) { (*err) += "`bufferViews' does not contain an JSON object."; } @@ -5687,8 +5849,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, // 5. Parse Accessor { - bool success = ForEachInArray(v, "accessors", [&](const json &o) { - if (!IsObject(o)) { + bool success = ForEachInArray(v, "accessors", [&](const detail::json &o) { + if (!detail::IsObject(o)) { if (err) { (*err) += "`accessors' does not contain an JSON object."; } @@ -5711,8 +5873,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, // 6. Parse Mesh { - bool success = ForEachInArray(v, "meshes", [&](const json &o) { - if (!IsObject(o)) { + bool success = ForEachInArray(v, "meshes", [&](const detail::json &o) { + if (!detail::IsObject(o)) { if (err) { (*err) += "`meshes' does not contain an JSON object."; } @@ -5760,25 +5922,32 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, model->bufferViews[size_t(bufferView)].target = TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER; - // we could optionally check if acessors' bufferView type is Scalar, as + // we could optionally check if accessors' bufferView type is Scalar, as // it should be } for (auto &attribute : primitive.attributes) { - model - ->bufferViews[size_t( - model->accessors[size_t(attribute.second)].bufferView)] - .target = TINYGLTF_TARGET_ARRAY_BUFFER; + const auto accessorsIndex = size_t(attribute.second); + if (accessorsIndex < model->accessors.size()) { + const auto bufferView = model->accessors[accessorsIndex].bufferView; + // bufferView could be null(-1) for sparse morph target + if (bufferView >= 0 && bufferView < (int)model->bufferViews.size()) { + model->bufferViews[size_t(bufferView)].target = + TINYGLTF_TARGET_ARRAY_BUFFER; + } + } } for (auto &target : primitive.targets) { for (auto &attribute : target) { - auto bufferView = - model->accessors[size_t(attribute.second)].bufferView; - // bufferView could be null(-1) for sparse morph target - if (bufferView >= 0) { - model->bufferViews[size_t(bufferView)].target = - TINYGLTF_TARGET_ARRAY_BUFFER; + const auto accessorsIndex = size_t(attribute.second); + if (accessorsIndex < model->accessors.size()) { + const auto bufferView = model->accessors[accessorsIndex].bufferView; + // bufferView could be null(-1) for sparse morph target + if (bufferView >= 0 && bufferView < (int)model->bufferViews.size()) { + model->bufferViews[size_t(bufferView)].target = + TINYGLTF_TARGET_ARRAY_BUFFER; + } } } } @@ -5787,8 +5956,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, // 7. Parse Node { - bool success = ForEachInArray(v, "nodes", [&](const json &o) { - if (!IsObject(o)) { + bool success = ForEachInArray(v, "nodes", [&](const detail::json &o) { + if (!detail::IsObject(o)) { if (err) { (*err) += "`nodes' does not contain an JSON object."; } @@ -5811,8 +5980,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, // 8. Parse scenes. { - bool success = ForEachInArray(v, "scenes", [&](const json &o) { - if (!IsObject(o)) { + bool success = ForEachInArray(v, "scenes", [&](const detail::json &o) { + if (!detail::IsObject(o)) { if (err) { (*err) += "`scenes' does not contain an JSON object."; } @@ -5831,15 +6000,15 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, if (store_original_json_for_extras_and_extensions_) { { - json_const_iterator it; - if (FindMember(o, "extensions", it)) { - scene.extensions_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extensions", it)) { + scene.extensions_json_string = detail::JsonToString(detail::GetValue(it)); } } { - json_const_iterator it; - if (FindMember(o, "extras", it)) { - scene.extras_json_string = JsonToString(GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extras", it)) { + scene.extras_json_string = detail::JsonToString(detail::GetValue(it)); } } } @@ -5855,17 +6024,17 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, // 9. Parse default scenes. { - json_const_iterator rootIt; + detail::json_const_iterator rootIt; int iVal; - if (FindMember(v, "scene", rootIt) && GetInt(GetValue(rootIt), iVal)) { + if (detail::FindMember(v, "scene", rootIt) && detail::GetInt(detail::GetValue(rootIt), iVal)) { model->defaultScene = iVal; } } // 10. Parse Material { - bool success = ForEachInArray(v, "materials", [&](const json &o) { - if (!IsObject(o)) { + bool success = ForEachInArray(v, "materials", [&](const detail::json &o) { + if (!detail::IsObject(o)) { if (err) { (*err) += "`materials' does not contain an JSON object."; } @@ -5903,8 +6072,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, { int idx = 0; - bool success = ForEachInArray(v, "images", [&](const json &o) { - if (!IsObject(o)) { + bool success = ForEachInArray(v, "images", [&](const detail::json &o) { + if (!detail::IsObject(o)) { if (err) { (*err) += "image[" + std::to_string(idx) + "] is not a JSON object."; } @@ -5913,7 +6082,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, Image image; if (!ParseImage(&image, idx, err, warn, o, store_original_json_for_extras_and_extensions_, base_dir, - &fs, &this->LoadImageData, load_image_user_data)) { + &fs, &uri_cb, &this->LoadImageData, + load_image_user_data)) { return false; } @@ -5969,8 +6139,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, // 12. Parse Texture { - bool success = ForEachInArray(v, "textures", [&](const json &o) { - if (!IsObject(o)) { + bool success = ForEachInArray(v, "textures", [&](const detail::json &o) { + if (!detail::IsObject(o)) { if (err) { (*err) += "`textures' does not contain an JSON object."; } @@ -5994,8 +6164,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, // 13. Parse Animation { - bool success = ForEachInArray(v, "animations", [&](const json &o) { - if (!IsObject(o)) { + bool success = ForEachInArray(v, "animations", [&](const detail::json &o) { + if (!detail::IsObject(o)) { if (err) { (*err) += "`animations' does not contain an JSON object."; } @@ -6018,8 +6188,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, // 14. Parse Skin { - bool success = ForEachInArray(v, "skins", [&](const json &o) { - if (!IsObject(o)) { + bool success = ForEachInArray(v, "skins", [&](const detail::json &o) { + if (!detail::IsObject(o)) { if (err) { (*err) += "`skins' does not contain an JSON object."; } @@ -6042,8 +6212,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, // 15. Parse Sampler { - bool success = ForEachInArray(v, "samplers", [&](const json &o) { - if (!IsObject(o)) { + bool success = ForEachInArray(v, "samplers", [&](const detail::json &o) { + if (!detail::IsObject(o)) { if (err) { (*err) += "`samplers' does not contain an JSON object."; } @@ -6066,8 +6236,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, // 16. Parse Camera { - bool success = ForEachInArray(v, "cameras", [&](const json &o) { - if (!IsObject(o)) { + bool success = ForEachInArray(v, "cameras", [&](const detail::json &o) { + if (!detail::IsObject(o)) { if (err) { (*err) += "`cameras' does not contain an JSON object."; } @@ -6093,26 +6263,26 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, // 18. Specific extension implementations { - json_const_iterator rootIt; - if (FindMember(v, "extensions", rootIt) && IsObject(GetValue(rootIt))) { - const json &root = GetValue(rootIt); + detail::json_const_iterator rootIt; + if (detail::FindMember(v, "extensions", rootIt) && detail::IsObject(detail::GetValue(rootIt))) { + const detail::json &root = detail::GetValue(rootIt); - json_const_iterator it(ObjectBegin(root)); - json_const_iterator itEnd(ObjectEnd(root)); + detail::json_const_iterator it(detail::ObjectBegin(root)); + detail::json_const_iterator itEnd(detail::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)) { + std::string key(detail::GetKey(it)); + if ((key == "KHR_lights_punctual") && detail::IsObject(detail::GetValue(it))) { + const detail::json &object = detail::GetValue(it); + detail::json_const_iterator itLight; + if (detail::FindMember(object, "lights", itLight)) { + const detail::json &lights = detail::GetValue(itLight); + if (!detail::IsArray(lights)) { continue; } - auto arrayIt(ArrayBegin(lights)); - auto arrayItEnd(ArrayEnd(lights)); + auto arrayIt(detail::ArrayBegin(lights)); + auto arrayItEnd(detail::ArrayEnd(lights)); for (; arrayIt != arrayItEnd; ++arrayIt) { Light light; if (!ParseLight(&light, err, *arrayIt, @@ -6131,8 +6301,8 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, ParseExtrasProperty(&model->extras, v); if (store_original_json_for_extras_and_extensions_) { - model->extras_json_string = JsonToString(v["extras"]); - model->extensions_json_string = JsonToString(v["extensions"]); + model->extras_json_string = detail::JsonToString(v["extras"]); + model->extensions_json_string = detail::JsonToString(v["extensions"]); } return true; @@ -6219,44 +6389,125 @@ bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err, unsigned int version; // 4 bytes unsigned int length; // 4 bytes - unsigned int model_length; // 4 bytes - unsigned int model_format; // 4 bytes; + unsigned int chunk0_length; // 4 bytes + unsigned int chunk0_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); + memcpy(&chunk0_length, bytes + 12, 4); // JSON data length + swap4(&chunk0_length); + memcpy(&chunk0_format, bytes + 16, 4); + swap4(&chunk0_format); + // https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#binary-gltf-layout + // // 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) || - (20 + model_length > length) || - (model_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format. + // + // https://github.com/syoyo/tinygltf/issues/372 + // Use 64bit uint to avoid integer overflow. + uint64_t header_and_json_size = 20ull + uint64_t(chunk0_length); + + if (header_and_json_size > std::numeric_limits::max()) { + // Do not allow 4GB or more GLB data. + (*err) = "Invalid glTF binary. GLB data exceeds 4GB."; + } + + if ((header_and_json_size > uint64_t(size)) || (chunk0_length < 1) || (length > size) || + (header_and_json_size > uint64_t(length)) || + (chunk0_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format. if (err) { (*err) = "Invalid glTF binary."; } return false; } + // Padding check + // The start and the end of each chunk must be aligned to a 4-byte boundary. + // No padding check for chunk0 start since its 4byte-boundary is ensured. + if ((header_and_json_size % 4) != 0) { + if (err) { + (*err) = "JSON Chunk end does not aligned to a 4-byte boundary."; + } + } + + //std::cout << "header_and_json_size = " << header_and_json_size << "\n"; + //std::cout << "length = " << length << "\n"; + + // Chunk1(BIN) data + // The spec says: When the binary buffer is empty or when it is stored by other means, this chunk SHOULD be omitted. + // So when header + JSON data == binary size, Chunk1 is omitted. + if (header_and_json_size == uint64_t(length)) { + + bin_data_ = nullptr; + bin_size_ = 0; + } else { + // Read Chunk1 info(BIN data) + // At least Chunk1 should have 12 bytes(8 bytes(header) + 4 bytes(bin payload could be 1~3 bytes, but need to be aligned to 4 bytes) + if ((header_and_json_size + 12ull) > uint64_t(length)) { + if (err) { + (*err) = "Insufficient storage space for Chunk1(BIN data). At least Chunk1 Must have 4 bytes or more bytes, but got " + std::to_string((header_and_json_size + 12ull) - uint64_t(length)) + ".\n"; + } + return false; + } + + unsigned int chunk1_length; // 4 bytes + unsigned int chunk1_format; // 4 bytes; + memcpy(&chunk1_length, bytes + header_and_json_size, 4); // JSON data length + swap4(&chunk1_length); + memcpy(&chunk1_format, bytes + header_and_json_size + 4, 4); + swap4(&chunk1_format); + + //std::cout << "chunk1_length = " << chunk1_length << "\n"; + + if (chunk1_length < 4) { + if (err) { + (*err) = "Insufficient Chunk1(BIN) data size."; + } + return false; + } + + if ((chunk1_length % 4) != 0) { + if (err) { + (*err) = "BIN Chunk end does not aligned to a 4-byte boundary."; + } + return false; + } + + if (uint64_t(chunk1_length) + header_and_json_size > uint64_t(length)) { + if (err) { + (*err) = "BIN Chunk data length exceeds the GLB size."; + } + return false; + } + + if (chunk1_format != 0x004e4942) { + if (err) { + (*err) = "Invalid type for chunk1 data."; + } + return false; + } + + //std::cout << "chunk1_length = " << chunk1_length << "\n"; + + bin_data_ = bytes + header_and_json_size + + 8; // 4 bytes (bin_buffer_length) + 4 bytes(bin_buffer_format) + + bin_size_ = size_t(chunk1_length); + } + // Extract JSON string. std::string jsonString(reinterpret_cast(&bytes[20]), - model_length); + chunk0_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); + chunk0_length, base_dir, check_sections); if (!ret) { return ret; } @@ -6303,43 +6554,43 @@ bool TinyGLTF::LoadBinaryFromFile(Model *model, std::string *err, /////////////////////// // GLTF Serialization /////////////////////// -namespace { -json JsonFromString(const char *s) { +namespace detail { +detail::json JsonFromString(const char *s) { #ifdef TINYGLTF_USE_RAPIDJSON - return json(s, GetAllocator()); + return detail::json(s, detail::GetAllocator()); #else - return json(s); + return detail::json(s); #endif } -void JsonAssign(json &dest, const json &src) { +void JsonAssign(detail::json &dest, const detail::json &src) { #ifdef TINYGLTF_USE_RAPIDJSON - dest.CopyFrom(src, GetAllocator()); + dest.CopyFrom(src, detail::GetAllocator()); #else dest = src; #endif } -void JsonAddMember(json &o, const char *key, json &&value) { +void JsonAddMember(detail::json &o, const char *key, detail::json &&value) { #ifdef TINYGLTF_USE_RAPIDJSON if (!o.IsObject()) { o.SetObject(); } - o.AddMember(json(key, GetAllocator()), std::move(value), GetAllocator()); + o.AddMember(detail::json(key, detail::GetAllocator()), std::move(value), detail::GetAllocator()); #else o[key] = std::move(value); #endif } -void JsonPushBack(json &o, json &&value) { +void JsonPushBack(detail::json &o, detail::json &&value) { #ifdef TINYGLTF_USE_RAPIDJSON - o.PushBack(std::move(value), GetAllocator()); + o.PushBack(std::move(value), detail::GetAllocator()); #else o.push_back(std::move(value)); #endif } -bool JsonIsNull(const json &o) { +bool JsonIsNull(const detail::json &o) { #ifdef TINYGLTF_USE_RAPIDJSON return o.IsNull(); #else @@ -6347,7 +6598,7 @@ bool JsonIsNull(const json &o) { #endif } -void JsonSetObject(json &o) { +void JsonSetObject(detail::json &o) { #ifdef TINYGLTF_USE_RAPIDJSON o.SetObject(); #else @@ -6355,66 +6606,66 @@ void JsonSetObject(json &o) { #endif } -void JsonReserveArray(json &o, size_t s) { +void JsonReserveArray(detail::json &o, size_t s) { #ifdef TINYGLTF_USE_RAPIDJSON o.SetArray(); - o.Reserve(static_cast(s), GetAllocator()); + o.Reserve(static_cast(s), detail::GetAllocator()); #endif (void)(o); (void)(s); } } // namespace -// typedef std::pair json_object_pair; +// typedef std::pair json_object_pair; template static void SerializeNumberProperty(const std::string &key, T number, - json &obj) { + detail::json &obj) { // obj.insert( - // json_object_pair(key, json(static_cast(number)))); + // json_object_pair(key, detail::json(static_cast(number)))); // obj[key] = static_cast(number); - JsonAddMember(obj, key.c_str(), json(number)); + detail::JsonAddMember(obj, key.c_str(), detail::json(number)); } #ifdef TINYGLTF_USE_RAPIDJSON template <> -void SerializeNumberProperty(const std::string &key, size_t number, json &obj) { - JsonAddMember(obj, key.c_str(), json(static_cast(number))); +void SerializeNumberProperty(const std::string &key, size_t number, detail::json &obj) { + detail::JsonAddMember(obj, key.c_str(), detail::json(static_cast(number))); } #endif template static void SerializeNumberArrayProperty(const std::string &key, const std::vector &value, - json &obj) { + detail::json &obj) { if (value.empty()) return; - json ary; - JsonReserveArray(ary, value.size()); + detail::json ary; + detail::JsonReserveArray(ary, value.size()); for (const auto &s : value) { - JsonPushBack(ary, json(s)); + detail::JsonPushBack(ary, detail::json(s)); } - JsonAddMember(obj, key.c_str(), std::move(ary)); + detail::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())); + const std::string &value, detail::json &obj) { + detail::JsonAddMember(obj, key.c_str(), detail::JsonFromString(value.c_str())); } static void SerializeStringArrayProperty(const std::string &key, const std::vector &value, - json &obj) { - json ary; - JsonReserveArray(ary, value.size()); + detail::json &obj) { + detail::json ary; + detail::JsonReserveArray(ary, value.size()); for (auto &s : value) { - JsonPushBack(ary, JsonFromString(s.c_str())); + detail::JsonPushBack(ary, detail::JsonFromString(s.c_str())); } - JsonAddMember(obj, key.c_str(), std::move(ary)); + detail::JsonAddMember(obj, key.c_str(), std::move(ary)); } -static bool ValueToJson(const Value &value, json *ret) { - json obj; +static bool ValueToJson(const Value &value, detail::json *ret) { + detail::json obj; #ifdef TINYGLTF_USE_RAPIDJSON switch (value.Type()) { case REAL_TYPE: @@ -6427,33 +6678,33 @@ 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(), detail::GetAllocator()); break; case ARRAY_TYPE: { obj.SetArray(); obj.Reserve(static_cast(value.ArrayLen()), - GetAllocator()); + detail::GetAllocator()); for (unsigned int i = 0; i < value.ArrayLen(); ++i) { Value elementValue = value.Get(int(i)); - json elementJson; + detail::json elementJson; if (ValueToJson(value.Get(int(i)), &elementJson)) - obj.PushBack(std::move(elementJson), GetAllocator()); + obj.PushBack(std::move(elementJson), detail::GetAllocator()); } break; } case BINARY_TYPE: // TODO - // obj = json(value.Get>()); + // obj = detail::json(value.Get>()); return false; break; case OBJECT_TYPE: { obj.SetObject(); Value::Object objMap = value.Get(); for (auto &it : objMap) { - json elementJson; + detail::json elementJson; if (ValueToJson(it.second, &elementJson)) { - obj.AddMember(json(it.first.c_str(), GetAllocator()), - std::move(elementJson), GetAllocator()); + obj.AddMember(detail::json(it.first.c_str(), detail::GetAllocator()), + std::move(elementJson), detail::GetAllocator()); } } break; @@ -6465,21 +6716,21 @@ static bool ValueToJson(const Value &value, json *ret) { #else switch (value.Type()) { case REAL_TYPE: - obj = json(value.Get()); + obj = detail::json(value.Get()); break; case INT_TYPE: - obj = json(value.Get()); + obj = detail::json(value.Get()); break; case BOOL_TYPE: - obj = json(value.Get()); + obj = detail::json(value.Get()); break; case STRING_TYPE: - obj = json(value.Get()); + obj = detail::json(value.Get()); break; case ARRAY_TYPE: { for (unsigned int i = 0; i < value.ArrayLen(); ++i) { Value elementValue = value.Get(int(i)); - json elementJson; + detail::json elementJson; if (ValueToJson(value.Get(int(i)), &elementJson)) obj.push_back(elementJson); } @@ -6493,7 +6744,7 @@ static bool ValueToJson(const Value &value, json *ret) { case OBJECT_TYPE: { Value::Object objMap = value.Get(); for (auto &it : objMap) { - json elementJson; + detail::json elementJson; if (ValueToJson(it.second, &elementJson)) obj[it.first] = elementJson; } break; @@ -6508,15 +6759,15 @@ static bool ValueToJson(const Value &value, json *ret) { } static void SerializeValue(const std::string &key, const Value &value, - json &obj) { - json ret; + detail::json &obj) { + detail::json ret; if (ValueToJson(value, &ret)) { - JsonAddMember(obj, key.c_str(), std::move(ret)); + detail::JsonAddMember(obj, key.c_str(), std::move(ret)); } } static void SerializeGltfBufferData(const std::vector &data, - json &o) { + detail::json &o) { std::string header = "data:application/octet-stream;base64,"; if (data.size() > 0) { std::string encodedData = @@ -6524,7 +6775,7 @@ static void SerializeGltfBufferData(const std::vector &data, SerializeStringProperty("uri", header + encodedData, o); } else { // Issue #229 - // size 0 is allowd. Just emit mime header. + // size 0 is allowed. Just emit mime header. SerializeStringProperty("uri", header, o); } } @@ -6562,14 +6813,14 @@ static bool SerializeGltfBufferData(const std::vector &data, } #if 0 // FIXME(syoyo): not used. will be removed in the future release. -static void SerializeParameterMap(ParameterMap ¶m, json &o) { +static void SerializeParameterMap(ParameterMap ¶m, detail::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; + detail::json json_double_value; for (std::map::iterator it = paramIt->second.json_double_value.begin(); it != paramIt->second.json_double_value.end(); ++it) { @@ -6592,33 +6843,33 @@ static void SerializeParameterMap(ParameterMap ¶m, json &o) { } #endif -static void SerializeExtensionMap(const ExtensionMap &extensions, json &o) { +static void SerializeExtensionMap(const ExtensionMap &extensions, detail::json &o) { if (!extensions.size()) return; - json extMap; + detail::json extMap; for (ExtensionMap::const_iterator extIt = extensions.begin(); extIt != extensions.end(); ++extIt) { // Allow an empty object for extension(#97) - json ret; + detail::json ret; bool isNull = true; if (ValueToJson(extIt->second, &ret)) { - isNull = JsonIsNull(ret); - JsonAddMember(extMap, extIt->first.c_str(), std::move(ret)); + isNull = detail::JsonIsNull(ret); + detail::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)); + detail::json empty; + detail::JsonSetObject(empty); + detail::JsonAddMember(extMap, extIt->first.c_str(), std::move(empty)); } } } - JsonAddMember(o, "extensions", std::move(extMap)); + detail::JsonAddMember(o, "extensions", std::move(extMap)); } -static void SerializeGltfAccessor(Accessor &accessor, json &o) { +static void SerializeGltfAccessor(const Accessor &accessor, detail::json &o) { if (accessor.bufferView >= 0) SerializeNumberProperty("bufferView", accessor.bufferView, o); @@ -6687,18 +6938,44 @@ static void SerializeGltfAccessor(Accessor &accessor, json &o) { if (accessor.extras.Type() != NULL_TYPE) { SerializeValue("extras", accessor.extras, o); } + + // sparse + if (accessor.sparse.isSparse) + { + detail::json sparse; + SerializeNumberProperty("count", accessor.sparse.count, sparse); + { + detail::json indices; + SerializeNumberProperty("bufferView", accessor.sparse.indices.bufferView, indices); + SerializeNumberProperty("byteOffset", accessor.sparse.indices.byteOffset, indices); + SerializeNumberProperty("componentType", accessor.sparse.indices.componentType, indices); + detail::JsonAddMember(sparse, "indices", std::move(indices)); + } + { + detail::json values; + SerializeNumberProperty("bufferView", accessor.sparse.values.bufferView, values); + SerializeNumberProperty("byteOffset", accessor.sparse.values.byteOffset, values); + detail::JsonAddMember(sparse, "values", std::move(values)); + } + detail::JsonAddMember(o, "sparse", std::move(sparse)); + } } -static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) { +static void SerializeGltfAnimationChannel(const AnimationChannel &channel, + detail::json &o) { SerializeNumberProperty("sampler", channel.sampler, o); { - json target; - SerializeNumberProperty("node", channel.target_node, target); + detail::json target; + + if (channel.target_node > 0) { + SerializeNumberProperty("node", channel.target_node, target); + } + SerializeStringProperty("path", channel.target_path, target); SerializeExtensionMap(channel.target_extensions, target); - JsonAddMember(o, "target", std::move(target)); + detail::JsonAddMember(o, "target", std::move(target)); } if (channel.extras.Type() != NULL_TYPE) { @@ -6708,7 +6985,8 @@ static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) { SerializeExtensionMap(channel.extensions, o); } -static void SerializeGltfAnimationSampler(AnimationSampler &sampler, json &o) { +static void SerializeGltfAnimationSampler(const AnimationSampler &sampler, + detail::json &o) { SerializeNumberProperty("input", sampler.input, o); SerializeNumberProperty("output", sampler.output, o); SerializeStringProperty("interpolation", sampler.interpolation, o); @@ -6718,33 +6996,33 @@ static void SerializeGltfAnimationSampler(AnimationSampler &sampler, json &o) { } } -static void SerializeGltfAnimation(Animation &animation, json &o) { +static void SerializeGltfAnimation(const Animation &animation, detail::json &o) { if (!animation.name.empty()) SerializeStringProperty("name", animation.name, o); { - json channels; - JsonReserveArray(channels, animation.channels.size()); + detail::json channels; + detail::JsonReserveArray(channels, animation.channels.size()); for (unsigned int i = 0; i < animation.channels.size(); ++i) { - json channel; + detail::json channel; AnimationChannel gltfChannel = animation.channels[i]; SerializeGltfAnimationChannel(gltfChannel, channel); - JsonPushBack(channels, std::move(channel)); + detail::JsonPushBack(channels, std::move(channel)); } - JsonAddMember(o, "channels", std::move(channels)); + detail::JsonAddMember(o, "channels", std::move(channels)); } { - json samplers; - JsonReserveArray(samplers, animation.samplers.size()); + detail::json samplers; + detail::JsonReserveArray(samplers, animation.samplers.size()); for (unsigned int i = 0; i < animation.samplers.size(); ++i) { - json sampler; + detail::json sampler; AnimationSampler gltfSampler = animation.samplers[i]; SerializeGltfAnimationSampler(gltfSampler, sampler); - JsonPushBack(samplers, std::move(sampler)); + detail::JsonPushBack(samplers, std::move(sampler)); } - JsonAddMember(o, "samplers", std::move(samplers)); + detail::JsonAddMember(o, "samplers", std::move(samplers)); } if (animation.extras.Type() != NULL_TYPE) { @@ -6754,7 +7032,7 @@ static void SerializeGltfAnimation(Animation &animation, json &o) { SerializeExtensionMap(animation.extensions, o); } -static void SerializeGltfAsset(Asset &asset, json &o) { +static void SerializeGltfAsset(const Asset &asset, detail::json &o) { if (!asset.generator.empty()) { SerializeStringProperty("generator", asset.generator, o); } @@ -6763,14 +7041,15 @@ static void SerializeGltfAsset(Asset &asset, json &o) { SerializeStringProperty("copyright", asset.copyright, o); } - if (asset.version.empty()) { + auto version = asset.version; + if (version.empty()) { // Just in case // `version` must be defined - asset.version = "2.0"; + version = "2.0"; } // TODO(syoyo): Do we need to check if `version` is greater or equal to 2.0? - SerializeStringProperty("version", asset.version, o); + SerializeStringProperty("version", version, o); if (asset.extras.Keys().size()) { SerializeValue("extras", asset.extras, o); @@ -6779,7 +7058,7 @@ static void SerializeGltfAsset(Asset &asset, json &o) { SerializeExtensionMap(asset.extensions, o); } -static void SerializeGltfBufferBin(Buffer &buffer, json &o, +static void SerializeGltfBufferBin(const Buffer &buffer, detail::json &o, std::vector &binBuffer) { SerializeNumberProperty("byteLength", buffer.data.size(), o); binBuffer = buffer.data; @@ -6791,7 +7070,7 @@ static void SerializeGltfBufferBin(Buffer &buffer, json &o, } } -static void SerializeGltfBuffer(Buffer &buffer, json &o) { +static void SerializeGltfBuffer(const Buffer &buffer, detail::json &o) { SerializeNumberProperty("byteLength", buffer.data.size(), o); SerializeGltfBufferData(buffer.data, o); @@ -6802,12 +7081,12 @@ static void SerializeGltfBuffer(Buffer &buffer, json &o) { } } -static bool SerializeGltfBuffer(Buffer &buffer, json &o, +static bool SerializeGltfBuffer(const Buffer &buffer, detail::json &o, const std::string &binFilename, - const std::string &binBaseFilename) { + const std::string &binUri) { if (!SerializeGltfBufferData(buffer.data, binFilename)) return false; SerializeNumberProperty("byteLength", buffer.data.size(), o); - SerializeStringProperty("uri", binBaseFilename, o); + SerializeStringProperty("uri", binUri, o); if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o); @@ -6817,7 +7096,7 @@ static bool SerializeGltfBuffer(Buffer &buffer, json &o, return true; } -static void SerializeGltfBufferView(BufferView &bufferView, json &o) { +static void SerializeGltfBufferView(const BufferView &bufferView, detail::json &o) { SerializeNumberProperty("buffer", bufferView.buffer, o); SerializeNumberProperty("byteLength", bufferView.byteLength, o); @@ -6843,14 +7122,15 @@ static void SerializeGltfBufferView(BufferView &bufferView, json &o) { } } -static void SerializeGltfImage(Image &image, json &o) { - // if uri empty, the mimeType and bufferview should be set - if (image.uri.empty()) { +static void SerializeGltfImage(const Image &image, const std::string &uri, + detail::json &o) { + // From 2.7.0, we look for `uri` parameter, not `Image.uri` + // if uri is empty, the mimeType and bufferview should be set + if (uri.empty()) { SerializeStringProperty("mimeType", image.mimeType, o); SerializeNumberProperty("bufferView", image.bufferView, o); } else { - // TODO(syoyo): dlib::urilencode? - SerializeStringProperty("uri", image.uri, o); + SerializeStringProperty("uri", uri, o); } if (image.name.size()) { @@ -6864,7 +7144,7 @@ static void SerializeGltfImage(Image &image, json &o) { SerializeExtensionMap(image.extensions, o); } -static void SerializeGltfTextureInfo(TextureInfo &texinfo, json &o) { +static void SerializeGltfTextureInfo(const TextureInfo &texinfo, detail::json &o) { SerializeNumberProperty("index", texinfo.index, o); if (texinfo.texCoord != 0) { @@ -6878,8 +7158,8 @@ static void SerializeGltfTextureInfo(TextureInfo &texinfo, json &o) { SerializeExtensionMap(texinfo.extensions, o); } -static void SerializeGltfNormalTextureInfo(NormalTextureInfo &texinfo, - json &o) { +static void SerializeGltfNormalTextureInfo(const NormalTextureInfo &texinfo, + detail::json &o) { SerializeNumberProperty("index", texinfo.index, o); if (texinfo.texCoord != 0) { @@ -6897,8 +7177,8 @@ static void SerializeGltfNormalTextureInfo(NormalTextureInfo &texinfo, SerializeExtensionMap(texinfo.extensions, o); } -static void SerializeGltfOcclusionTextureInfo(OcclusionTextureInfo &texinfo, - json &o) { +static void SerializeGltfOcclusionTextureInfo( + const OcclusionTextureInfo &texinfo, detail::json &o) { SerializeNumberProperty("index", texinfo.index, o); if (texinfo.texCoord != 0) { @@ -6916,8 +7196,8 @@ static void SerializeGltfOcclusionTextureInfo(OcclusionTextureInfo &texinfo, SerializeExtensionMap(texinfo.extensions, o); } -static void SerializeGltfPbrMetallicRoughness(PbrMetallicRoughness &pbr, - json &o) { +static void SerializeGltfPbrMetallicRoughness(const PbrMetallicRoughness &pbr, + detail::json &o) { std::vector default_baseColorFactor = {1.0, 1.0, 1.0, 1.0}; if (!Equals(pbr.baseColorFactor, default_baseColorFactor)) { SerializeNumberArrayProperty("baseColorFactor", pbr.baseColorFactor, @@ -6933,15 +7213,15 @@ static void SerializeGltfPbrMetallicRoughness(PbrMetallicRoughness &pbr, } if (pbr.baseColorTexture.index > -1) { - json texinfo; + detail::json texinfo; SerializeGltfTextureInfo(pbr.baseColorTexture, texinfo); - JsonAddMember(o, "baseColorTexture", std::move(texinfo)); + detail::JsonAddMember(o, "baseColorTexture", std::move(texinfo)); } if (pbr.metallicRoughnessTexture.index > -1) { - json texinfo; + detail::json texinfo; SerializeGltfTextureInfo(pbr.metallicRoughnessTexture, texinfo); - JsonAddMember(o, "metallicRoughnessTexture", std::move(texinfo)); + detail::JsonAddMember(o, "metallicRoughnessTexture", std::move(texinfo)); } SerializeExtensionMap(pbr.extensions, o); @@ -6951,7 +7231,7 @@ static void SerializeGltfPbrMetallicRoughness(PbrMetallicRoughness &pbr, } } -static void SerializeGltfMaterial(Material &material, json &o) { +static void SerializeGltfMaterial(const Material &material, detail::json &o) { if (material.name.size()) { SerializeStringProperty("name", material.name, o); } @@ -6967,24 +7247,24 @@ static void SerializeGltfMaterial(Material &material, json &o) { } if (material.doubleSided != false) - JsonAddMember(o, "doubleSided", json(material.doubleSided)); + detail::JsonAddMember(o, "doubleSided", detail::json(material.doubleSided)); if (material.normalTexture.index > -1) { - json texinfo; + detail::json texinfo; SerializeGltfNormalTextureInfo(material.normalTexture, texinfo); - JsonAddMember(o, "normalTexture", std::move(texinfo)); + detail::JsonAddMember(o, "normalTexture", std::move(texinfo)); } if (material.occlusionTexture.index > -1) { - json texinfo; + detail::json texinfo; SerializeGltfOcclusionTextureInfo(material.occlusionTexture, texinfo); - JsonAddMember(o, "occlusionTexture", std::move(texinfo)); + detail::JsonAddMember(o, "occlusionTexture", std::move(texinfo)); } if (material.emissiveTexture.index > -1) { - json texinfo; + detail::json texinfo; SerializeGltfTextureInfo(material.emissiveTexture, texinfo); - JsonAddMember(o, "emissiveTexture", std::move(texinfo)); + detail::JsonAddMember(o, "emissiveTexture", std::move(texinfo)); } std::vector default_emissiveFactor = {0.0, 0.0, 0.0}; @@ -6994,25 +7274,25 @@ static void SerializeGltfMaterial(Material &material, json &o) { } { - json pbrMetallicRoughness; + detail::json pbrMetallicRoughness; SerializeGltfPbrMetallicRoughness(material.pbrMetallicRoughness, pbrMetallicRoughness); // Issue 204 // Do not serialize `pbrMetallicRoughness` if pbrMetallicRoughness has all // default values(json is null). Otherwise it will serialize to // `pbrMetallicRoughness : null`, which cannot be read by other glTF - // importers(and validators). + // importers (and validators). // - if (!JsonIsNull(pbrMetallicRoughness)) { - JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness)); + if (!detail::JsonIsNull(pbrMetallicRoughness)) { + detail::JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness)); } } #if 0 // legacy way. just for the record. if (material.values.size()) { - json pbrMetallicRoughness; + detail::json pbrMetallicRoughness; SerializeParameterMap(material.values, pbrMetallicRoughness); - JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness)); + detail::JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness)); } SerializeParameterMap(material.additionalValues, o); @@ -7027,23 +7307,23 @@ static void SerializeGltfMaterial(Material &material, json &o) { } } -static void SerializeGltfMesh(Mesh &mesh, json &o) { - json primitives; - JsonReserveArray(primitives, mesh.primitives.size()); +static void SerializeGltfMesh(const Mesh &mesh, detail::json &o) { + detail::json primitives; + detail::JsonReserveArray(primitives, mesh.primitives.size()); for (unsigned int i = 0; i < mesh.primitives.size(); ++i) { - json primitive; + detail::json primitive; const Primitive &gltfPrimitive = mesh.primitives[i]; // don't make a copy { - json attributes; + detail::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)); + detail::JsonAddMember(primitive, "attributes", std::move(attributes)); } - // Indicies is optional + // Indices is optional if (gltfPrimitive.indices > -1) { SerializeNumberProperty("indices", gltfPrimitive.indices, primitive); } @@ -7056,19 +7336,19 @@ static void SerializeGltfMesh(Mesh &mesh, json &o) { // Morph targets if (gltfPrimitive.targets.size()) { - json targets; - JsonReserveArray(targets, gltfPrimitive.targets.size()); + detail::json targets; + detail::JsonReserveArray(targets, gltfPrimitive.targets.size()); for (unsigned int k = 0; k < gltfPrimitive.targets.size(); ++k) { - json targetAttributes; + detail::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)); + detail::JsonPushBack(targets, std::move(targetAttributes)); } - JsonAddMember(primitive, "targets", std::move(targets)); + detail::JsonAddMember(primitive, "targets", std::move(targets)); } SerializeExtensionMap(gltfPrimitive.extensions, primitive); @@ -7077,10 +7357,10 @@ static void SerializeGltfMesh(Mesh &mesh, json &o) { SerializeValue("extras", gltfPrimitive.extras, primitive); } - JsonPushBack(primitives, std::move(primitive)); + detail::JsonPushBack(primitives, std::move(primitive)); } - JsonAddMember(o, "primitives", std::move(primitives)); + detail::JsonAddMember(o, "primitives", std::move(primitives)); if (mesh.weights.size()) { SerializeNumberArrayProperty("weights", mesh.weights, o); @@ -7096,7 +7376,7 @@ static void SerializeGltfMesh(Mesh &mesh, json &o) { } } -static void SerializeSpotLight(SpotLight &spot, json &o) { +static void SerializeSpotLight(const SpotLight &spot, detail::json &o) { SerializeNumberProperty("innerConeAngle", spot.innerConeAngle, o); SerializeNumberProperty("outerConeAngle", spot.outerConeAngle, o); SerializeExtensionMap(spot.extensions, o); @@ -7105,7 +7385,7 @@ static void SerializeSpotLight(SpotLight &spot, json &o) { } } -static void SerializeGltfLight(Light &light, json &o) { +static void SerializeGltfLight(const Light &light, detail::json &o) { if (!light.name.empty()) SerializeStringProperty("name", light.name, o); SerializeNumberProperty("intensity", light.intensity, o); if (light.range > 0.0) { @@ -7114,9 +7394,9 @@ static void SerializeGltfLight(Light &light, json &o) { SerializeNumberArrayProperty("color", light.color, o); SerializeStringProperty("type", light.type, o); if (light.type == "spot") { - json spot; + detail::json spot; SerializeSpotLight(light.spot, spot); - JsonAddMember(o, "spot", std::move(spot)); + detail::JsonAddMember(o, "spot", std::move(spot)); } SerializeExtensionMap(light.extensions, o); if (light.extras.Type() != NULL_TYPE) { @@ -7124,7 +7404,7 @@ static void SerializeGltfLight(Light &light, json &o) { } } -static void SerializeGltfNode(Node &node, json &o) { +static void SerializeGltfNode(const Node &node, detail::json &o) { if (node.translation.size() > 0) { SerializeNumberArrayProperty("translation", node.translation, o); } @@ -7162,14 +7442,17 @@ static void SerializeGltfNode(Node &node, json &o) { SerializeNumberArrayProperty("children", node.children, o); } -static void SerializeGltfSampler(Sampler &sampler, json &o) { +static void SerializeGltfSampler(const Sampler &sampler, detail::json &o) { + if (!sampler.name.empty()) { + SerializeStringProperty("name", sampler.name, 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("wrapR", sampler.wrapR, o); SerializeNumberProperty("wrapS", sampler.wrapS, o); SerializeNumberProperty("wrapT", sampler.wrapT, o); @@ -7179,7 +7462,7 @@ static void SerializeGltfSampler(Sampler &sampler, json &o) { } static void SerializeGltfOrthographicCamera(const OrthographicCamera &camera, - json &o) { + detail::json &o) { SerializeNumberProperty("zfar", camera.zfar, o); SerializeNumberProperty("znear", camera.znear, o); SerializeNumberProperty("xmag", camera.xmag, o); @@ -7191,7 +7474,7 @@ static void SerializeGltfOrthographicCamera(const OrthographicCamera &camera, } static void SerializeGltfPerspectiveCamera(const PerspectiveCamera &camera, - json &o) { + detail::json &o) { SerializeNumberProperty("zfar", camera.zfar, o); SerializeNumberProperty("znear", camera.znear, o); if (camera.aspectRatio > 0) { @@ -7207,20 +7490,20 @@ static void SerializeGltfPerspectiveCamera(const PerspectiveCamera &camera, } } -static void SerializeGltfCamera(const Camera &camera, json &o) { +static void SerializeGltfCamera(const Camera &camera, detail::json &o) { SerializeStringProperty("type", camera.type, o); if (!camera.name.empty()) { SerializeStringProperty("name", camera.name, o); } if (camera.type.compare("orthographic") == 0) { - json orthographic; + detail::json orthographic; SerializeGltfOrthographicCamera(camera.orthographic, orthographic); - JsonAddMember(o, "orthographic", std::move(orthographic)); + detail::JsonAddMember(o, "orthographic", std::move(orthographic)); } else if (camera.type.compare("perspective") == 0) { - json perspective; + detail::json perspective; SerializeGltfPerspectiveCamera(camera.perspective, perspective); - JsonAddMember(o, "perspective", std::move(perspective)); + detail::JsonAddMember(o, "perspective", std::move(perspective)); } else { // ??? } @@ -7231,7 +7514,7 @@ static void SerializeGltfCamera(const Camera &camera, json &o) { SerializeExtensionMap(camera.extensions, o); } -static void SerializeGltfScene(Scene &scene, json &o) { +static void SerializeGltfScene(const Scene &scene, detail::json &o) { SerializeNumberArrayProperty("nodes", scene.nodes, o); if (scene.name.size()) { @@ -7243,7 +7526,7 @@ static void SerializeGltfScene(Scene &scene, json &o) { SerializeExtensionMap(scene.extensions, o); } -static void SerializeGltfSkin(Skin &skin, json &o) { +static void SerializeGltfSkin(const Skin &skin, detail::json &o) { // required SerializeNumberArrayProperty("joints", skin.joints, o); @@ -7260,7 +7543,7 @@ static void SerializeGltfSkin(Skin &skin, json &o) { } } -static void SerializeGltfTexture(Texture &texture, json &o) { +static void SerializeGltfTexture(const Texture &texture, detail::json &o) { if (texture.sampler > -1) { SerializeNumberProperty("sampler", texture.sampler, o); } @@ -7279,49 +7562,49 @@ static void SerializeGltfTexture(Texture &texture, json &o) { /// /// Serialize all properties except buffers and images. /// -static void SerializeGltfModel(Model *model, json &o) { +static void SerializeGltfModel(const Model *model, detail::json &o) { // ACCESSORS if (model->accessors.size()) { - json accessors; - JsonReserveArray(accessors, model->accessors.size()); + detail::json accessors; + detail::JsonReserveArray(accessors, model->accessors.size()); for (unsigned int i = 0; i < model->accessors.size(); ++i) { - json accessor; + detail::json accessor; SerializeGltfAccessor(model->accessors[i], accessor); - JsonPushBack(accessors, std::move(accessor)); + detail::JsonPushBack(accessors, std::move(accessor)); } - JsonAddMember(o, "accessors", std::move(accessors)); + detail::JsonAddMember(o, "accessors", std::move(accessors)); } // ANIMATIONS if (model->animations.size()) { - json animations; - JsonReserveArray(animations, model->animations.size()); + detail::json animations; + detail::JsonReserveArray(animations, model->animations.size()); for (unsigned int i = 0; i < model->animations.size(); ++i) { if (model->animations[i].channels.size()) { - json animation; + detail::json animation; SerializeGltfAnimation(model->animations[i], animation); - JsonPushBack(animations, std::move(animation)); + detail::JsonPushBack(animations, std::move(animation)); } } - JsonAddMember(o, "animations", std::move(animations)); + detail::JsonAddMember(o, "animations", std::move(animations)); } // ASSET - json asset; + detail::json asset; SerializeGltfAsset(model->asset, asset); - JsonAddMember(o, "asset", std::move(asset)); + detail::JsonAddMember(o, "asset", std::move(asset)); // BUFFERVIEWS if (model->bufferViews.size()) { - json bufferViews; - JsonReserveArray(bufferViews, model->bufferViews.size()); + detail::json bufferViews; + detail::JsonReserveArray(bufferViews, model->bufferViews.size()); for (unsigned int i = 0; i < model->bufferViews.size(); ++i) { - json bufferView; + detail::json bufferView; SerializeGltfBufferView(model->bufferViews[i], bufferView); - JsonPushBack(bufferViews, std::move(bufferView)); + detail::JsonPushBack(bufferViews, std::move(bufferView)); } - JsonAddMember(o, "bufferViews", std::move(bufferViews)); + detail::JsonAddMember(o, "bufferViews", std::move(bufferViews)); } // Extensions required @@ -7332,48 +7615,48 @@ static void SerializeGltfModel(Model *model, json &o) { // MATERIALS if (model->materials.size()) { - json materials; - JsonReserveArray(materials, model->materials.size()); + detail::json materials; + detail::JsonReserveArray(materials, model->materials.size()); for (unsigned int i = 0; i < model->materials.size(); ++i) { - json material; + detail::json material; SerializeGltfMaterial(model->materials[i], material); - if (JsonIsNull(material)) { + if (detail::JsonIsNull(material)) { // Issue 294. // `material` does not have any required parameters // so the result may be null(unmodified) when all material parameters // have default value. // // null is not allowed thus we create an empty JSON object. - JsonSetObject(material); + detail::JsonSetObject(material); } - JsonPushBack(materials, std::move(material)); + detail::JsonPushBack(materials, std::move(material)); } - JsonAddMember(o, "materials", std::move(materials)); + detail::JsonAddMember(o, "materials", std::move(materials)); } // MESHES if (model->meshes.size()) { - json meshes; - JsonReserveArray(meshes, model->meshes.size()); + detail::json meshes; + detail::JsonReserveArray(meshes, model->meshes.size()); for (unsigned int i = 0; i < model->meshes.size(); ++i) { - json mesh; + detail::json mesh; SerializeGltfMesh(model->meshes[i], mesh); - JsonPushBack(meshes, std::move(mesh)); + detail::JsonPushBack(meshes, std::move(mesh)); } - JsonAddMember(o, "meshes", std::move(meshes)); + detail::JsonAddMember(o, "meshes", std::move(meshes)); } // NODES if (model->nodes.size()) { - json nodes; - JsonReserveArray(nodes, model->nodes.size()); + detail::json nodes; + detail::JsonReserveArray(nodes, model->nodes.size()); for (unsigned int i = 0; i < model->nodes.size(); ++i) { - json node; + detail::json node; SerializeGltfNode(model->nodes[i], node); - JsonPushBack(nodes, std::move(node)); + detail::JsonPushBack(nodes, std::move(node)); } - JsonAddMember(o, "nodes", std::move(nodes)); + detail::JsonAddMember(o, "nodes", std::move(nodes)); } // SCENE @@ -7383,62 +7666,62 @@ static void SerializeGltfModel(Model *model, json &o) { // SCENES if (model->scenes.size()) { - json scenes; - JsonReserveArray(scenes, model->scenes.size()); + detail::json scenes; + detail::JsonReserveArray(scenes, model->scenes.size()); for (unsigned int i = 0; i < model->scenes.size(); ++i) { - json currentScene; + detail::json currentScene; SerializeGltfScene(model->scenes[i], currentScene); - JsonPushBack(scenes, std::move(currentScene)); + detail::JsonPushBack(scenes, std::move(currentScene)); } - JsonAddMember(o, "scenes", std::move(scenes)); + detail::JsonAddMember(o, "scenes", std::move(scenes)); } // SKINS if (model->skins.size()) { - json skins; - JsonReserveArray(skins, model->skins.size()); + detail::json skins; + detail::JsonReserveArray(skins, model->skins.size()); for (unsigned int i = 0; i < model->skins.size(); ++i) { - json skin; + detail::json skin; SerializeGltfSkin(model->skins[i], skin); - JsonPushBack(skins, std::move(skin)); + detail::JsonPushBack(skins, std::move(skin)); } - JsonAddMember(o, "skins", std::move(skins)); + detail::JsonAddMember(o, "skins", std::move(skins)); } // TEXTURES if (model->textures.size()) { - json textures; - JsonReserveArray(textures, model->textures.size()); + detail::json textures; + detail::JsonReserveArray(textures, model->textures.size()); for (unsigned int i = 0; i < model->textures.size(); ++i) { - json texture; + detail::json texture; SerializeGltfTexture(model->textures[i], texture); - JsonPushBack(textures, std::move(texture)); + detail::JsonPushBack(textures, std::move(texture)); } - JsonAddMember(o, "textures", std::move(textures)); + detail::JsonAddMember(o, "textures", std::move(textures)); } // SAMPLERS if (model->samplers.size()) { - json samplers; - JsonReserveArray(samplers, model->samplers.size()); + detail::json samplers; + detail::JsonReserveArray(samplers, model->samplers.size()); for (unsigned int i = 0; i < model->samplers.size(); ++i) { - json sampler; + detail::json sampler; SerializeGltfSampler(model->samplers[i], sampler); - JsonPushBack(samplers, std::move(sampler)); + detail::JsonPushBack(samplers, std::move(sampler)); } - JsonAddMember(o, "samplers", std::move(samplers)); + detail::JsonAddMember(o, "samplers", std::move(samplers)); } // CAMERAS if (model->cameras.size()) { - json cameras; - JsonReserveArray(cameras, model->cameras.size()); + detail::json cameras; + detail::JsonReserveArray(cameras, model->cameras.size()); for (unsigned int i = 0; i < model->cameras.size(); ++i) { - json camera; + detail::json camera; SerializeGltfCamera(model->cameras[i], camera); - JsonPushBack(cameras, std::move(camera)); + detail::JsonPushBack(cameras, std::move(camera)); } - JsonAddMember(o, "cameras", std::move(cameras)); + detail::JsonAddMember(o, "cameras", std::move(cameras)); } // EXTENSIONS @@ -7448,27 +7731,27 @@ static void SerializeGltfModel(Model *model, json &o) { // LIGHTS as KHR_lights_punctual if (model->lights.size()) { - json lights; - JsonReserveArray(lights, model->lights.size()); + detail::json lights; + detail::JsonReserveArray(lights, model->lights.size()); for (unsigned int i = 0; i < model->lights.size(); ++i) { - json light; + detail::json light; SerializeGltfLight(model->lights[i], light); - JsonPushBack(lights, std::move(light)); + detail::JsonPushBack(lights, std::move(light)); } - json khr_lights_cmn; - JsonAddMember(khr_lights_cmn, "lights", std::move(lights)); - json ext_j; + detail::json khr_lights_cmn; + detail::JsonAddMember(khr_lights_cmn, "lights", std::move(lights)); + detail::json ext_j; { - json_const_iterator it; - if (FindMember(o, "extensions", it)) { - JsonAssign(ext_j, GetValue(it)); + detail::json_const_iterator it; + if (detail::FindMember(o, "extensions", it)) { + detail::JsonAssign(ext_j, detail::GetValue(it)); } } - JsonAddMember(ext_j, "KHR_lights_punctual", std::move(khr_lights_cmn)); + detail::JsonAddMember(ext_j, "KHR_lights_punctual", std::move(khr_lights_cmn)); - JsonAddMember(o, "extensions", std::move(ext_j)); + detail::JsonAddMember(o, "extensions", std::move(ext_j)); // Also add "KHR_lights_punctual" to `extensionsUsed` { @@ -7523,37 +7806,32 @@ static bool WriteGltfFile(const std::string &output, return WriteGltfStream(gltfFile, content); } -static void WriteBinaryGltfStream(std::ostream &stream, +static bool WriteBinaryGltfStream(std::ostream &stream, const std::string &content, const std::vector &binBuffer) { const std::string header = "glTF"; const int version = 2; - // https://stackoverflow.com/questions/3407012/c-rounding-up-to-the-nearest-multiple-of-a-number - auto roundUp = [](uint32_t numToRound, uint32_t multiple) { - if (multiple == 0) return numToRound; - - uint32_t remainder = numToRound % multiple; - if (remainder == 0) return numToRound; - - return numToRound + multiple - remainder; - }; - - const uint32_t padding_size = - roundUp(uint32_t(content.size()), 4) - uint32_t(content.size()); + const uint32_t content_size = uint32_t(content.size()); + const uint32_t binBuffer_size = uint32_t(binBuffer.size()); + // determine number of padding bytes required to ensure 4 byte alignment + const uint32_t content_padding_size = + content_size % 4 == 0 ? 0 : 4 - content_size % 4; + const uint32_t bin_padding_size = + binBuffer_size % 4 == 0 ? 0 : 4 - binBuffer_size % 4; // 12 bytes for header, JSON content length, 8 bytes for JSON chunk info. - // Chunk data must be located at 4-byte boundary. + // Chunk data must be located at 4-byte boundary, which may require padding const uint32_t length = - 12 + 8 + roundUp(uint32_t(content.size()), 4) + - (binBuffer.size() ? (8 + roundUp(uint32_t(binBuffer.size()), 4)) : 0); + 12 + 8 + content_size + content_padding_size + + (binBuffer_size ? (8 + binBuffer_size + bin_padding_size) : 0); 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 uint32_t model_length = uint32_t(content.size()) + padding_size; + const uint32_t model_length = uint32_t(content.size()) + content_padding_size; const uint32_t model_format = 0x4E4F534A; stream.write(reinterpret_cast(&model_length), sizeof(model_length)); @@ -7562,13 +7840,11 @@ static void WriteBinaryGltfStream(std::ostream &stream, 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), ' '); + if (content_padding_size > 0) { + const std::string padding = std::string(size_t(content_padding_size), ' '); stream.write(padding.c_str(), std::streamsize(padding.size())); } if (binBuffer.size() > 0) { - const uint32_t bin_padding_size = - roundUp(uint32_t(binBuffer.size()), 4) - uint32_t(binBuffer.size()); // BIN chunk info, then BIN data const uint32_t bin_length = uint32_t(binBuffer.size()) + bin_padding_size; const uint32_t bin_format = 0x004e4942; @@ -7586,9 +7862,12 @@ static void WriteBinaryGltfStream(std::ostream &stream, std::streamsize(padding.size())); } } + + // TODO: Check error on stream.write + return true; } -static void WriteBinaryGltfFile(const std::string &output, +static bool WriteBinaryGltfFile(const std::string &output, const std::string &content, const std::vector &binBuffer) { #ifdef _WIN32 @@ -7606,13 +7885,13 @@ static void WriteBinaryGltfFile(const std::string &output, #else std::ofstream gltfFile(output.c_str(), std::ios::binary); #endif - WriteBinaryGltfStream(gltfFile, content, binBuffer); + return WriteBinaryGltfStream(gltfFile, content, binBuffer); } -bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream, +bool TinyGLTF::WriteGltfSceneToStream(const Model *model, std::ostream &stream, bool prettyPrint = true, bool writeBinary = false) { - JsonDocument output; + detail::JsonDocument output; /// Serialize all properties except buffers and images. SerializeGltfModel(model, output); @@ -7620,54 +7899,57 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream, // BUFFERS std::vector binBuffer; if (model->buffers.size()) { - json buffers; - JsonReserveArray(buffers, model->buffers.size()); + detail::json buffers; + detail::JsonReserveArray(buffers, model->buffers.size()); for (unsigned int i = 0; i < model->buffers.size(); ++i) { - json buffer; + detail::json buffer; if (writeBinary && i == 0 && model->buffers[i].uri.empty()) { SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer); } else { SerializeGltfBuffer(model->buffers[i], buffer); } - JsonPushBack(buffers, std::move(buffer)); + detail::JsonPushBack(buffers, std::move(buffer)); } - JsonAddMember(output, "buffers", std::move(buffers)); + detail::JsonAddMember(output, "buffers", std::move(buffers)); } // IMAGES if (model->images.size()) { - json images; - JsonReserveArray(images, model->images.size()); + detail::json images; + detail::JsonReserveArray(images, model->images.size()); for (unsigned int i = 0; i < model->images.size(); ++i) { - json image; + detail::json image; std::string dummystring = ""; // UpdateImageObject need baseDir but only uses it if embeddedImages is // enabled, since we won't write separate images when writing to a stream // we - 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)); + std::string uri; + if (!UpdateImageObject(model->images[i], dummystring, int(i), true, + &uri_cb, &this->WriteImageData, + this->write_image_user_data_, &uri)) { + return false; + } + SerializeGltfImage(model->images[i], uri, image); + detail::JsonPushBack(images, std::move(image)); } - JsonAddMember(output, "images", std::move(images)); + detail::JsonAddMember(output, "images", std::move(images)); } if (writeBinary) { - WriteBinaryGltfStream(stream, JsonToString(output), binBuffer); + return WriteBinaryGltfStream(stream, detail::JsonToString(output), binBuffer); } else { - WriteGltfStream(stream, JsonToString(output, prettyPrint ? 2 : -1)); + return WriteGltfStream(stream, detail::JsonToString(output, prettyPrint ? 2 : -1)); } - - return true; } -bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename, +bool TinyGLTF::WriteGltfSceneToFile(const Model *model, + const std::string &filename, bool embedImages = false, bool embedBuffers = false, bool prettyPrint = true, bool writeBinary = false) { - JsonDocument output; + detail::JsonDocument output; std::string defaultBinFilename = GetBaseFilename(filename); std::string defaultBinFileExt = ".bin"; std::string::size_type pos = @@ -7684,72 +7966,87 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename, SerializeGltfModel(model, output); // BUFFERS - std::vector usedUris; + std::vector usedFilenames; std::vector binBuffer; if (model->buffers.size()) { - json buffers; - JsonReserveArray(buffers, model->buffers.size()); + detail::json buffers; + detail::JsonReserveArray(buffers, model->buffers.size()); for (unsigned int i = 0; i < model->buffers.size(); ++i) { - json buffer; + detail::json buffer; if (writeBinary && i == 0 && model->buffers[i].uri.empty()) { SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer); } else if (embedBuffers) { SerializeGltfBuffer(model->buffers[i], buffer); } else { std::string binSavePath; + std::string binFilename; std::string binUri; if (!model->buffers[i].uri.empty() && !IsDataURI(model->buffers[i].uri)) { binUri = model->buffers[i].uri; + if (!uri_cb.decode(binUri, &binFilename, uri_cb.user_data)) { + return false; + } } else { - binUri = defaultBinFilename + defaultBinFileExt; + binFilename = defaultBinFilename + defaultBinFileExt; bool inUse = true; int numUsed = 0; while (inUse) { inUse = false; - for (const std::string &usedName : usedUris) { - if (binUri.compare(usedName) != 0) continue; + for (const std::string &usedName : usedFilenames) { + if (binFilename.compare(usedName) != 0) continue; inUse = true; - binUri = defaultBinFilename + std::to_string(numUsed++) + - defaultBinFileExt; + binFilename = defaultBinFilename + std::to_string(numUsed++) + + defaultBinFileExt; break; } } + + if (uri_cb.encode) { + if (!uri_cb.encode(binFilename, "buffer", &binUri, + uri_cb.user_data)) { + return false; + } + } else { + binUri = binFilename; + } } - usedUris.push_back(binUri); - binSavePath = JoinPath(baseDir, binUri); + usedFilenames.push_back(binFilename); + binSavePath = JoinPath(baseDir, binFilename); if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath, binUri)) { return false; } } - JsonPushBack(buffers, std::move(buffer)); + detail::JsonPushBack(buffers, std::move(buffer)); } - JsonAddMember(output, "buffers", std::move(buffers)); + detail::JsonAddMember(output, "buffers", std::move(buffers)); } // IMAGES if (model->images.size()) { - json images; - JsonReserveArray(images, model->images.size()); + detail::json images; + detail::JsonReserveArray(images, model->images.size()); for (unsigned int i = 0; i < model->images.size(); ++i) { - json image; + detail::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)); + std::string uri; + if (!UpdateImageObject(model->images[i], baseDir, int(i), embedImages, + &uri_cb, &this->WriteImageData, + this->write_image_user_data_, &uri)) { + return false; + } + SerializeGltfImage(model->images[i], uri, image); + detail::JsonPushBack(images, std::move(image)); } - JsonAddMember(output, "images", std::move(images)); + detail::JsonAddMember(output, "images", std::move(images)); } if (writeBinary) { - WriteBinaryGltfFile(filename, JsonToString(output), binBuffer); + return WriteBinaryGltfFile(filename, detail::JsonToString(output), binBuffer); } else { - WriteGltfFile(filename, JsonToString(output, (prettyPrint ? 2 : -1))); + return WriteGltfFile(filename, detail::JsonToString(output, (prettyPrint ? 2 : -1))); } - - return true; } } // namespace tinygltf