tiny_gltf 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +6 -6
- data/ext/tiny_gltf/json.hpp +11842 -6158
- data/ext/tiny_gltf/rb_tiny_gltf.h +1 -2
- data/ext/tiny_gltf/tiny_gltf.h +396 -176
- data/lib/tiny_gltf/version.rb +1 -1
- data/tiny_gltf.gemspec +1 -1
- metadata +9 -9
| @@ -7,6 +7,7 @@ | |
| 7 7 | 
             
            #endif
         | 
| 8 8 |  | 
| 9 9 | 
             
            #include <time.h> // work around C++/C linkage error on some platforms
         | 
| 10 | 
            +
            #include "ruby.h"
         | 
| 10 11 |  | 
| 11 12 | 
             
            #if __cplusplus
         | 
| 12 13 | 
             
              #include <algorithm>
         | 
| @@ -15,8 +16,6 @@ | |
| 15 16 | 
             
              extern "C" {
         | 
| 16 17 | 
             
            #endif
         | 
| 17 18 |  | 
| 18 | 
            -
            #include "ruby.h"
         | 
| 19 | 
            -
             | 
| 20 19 | 
             
            void Init_tiny_gltf(void);
         | 
| 21 20 | 
             
            VALUE rb_tgltf_load(int argc, VALUE *argv, VALUE self);
         | 
| 22 21 |  | 
    
        data/ext/tiny_gltf/tiny_gltf.h
    CHANGED
    
    | @@ -4,7 +4,7 @@ | |
| 4 4 | 
             
            //
         | 
| 5 5 | 
             
            // The MIT License (MIT)
         | 
| 6 6 | 
             
            //
         | 
| 7 | 
            -
            // Copyright (c) 2015 -  | 
| 7 | 
            +
            // Copyright (c) 2015 - 2020 Syoyo Fujita, Aurélien Chatelain and many
         | 
| 8 8 | 
             
            // contributors.
         | 
| 9 9 | 
             
            //
         | 
| 10 10 | 
             
            // Permission is hereby granted, free of charge, to any person obtaining a copy
         | 
| @@ -26,6 +26,7 @@ | |
| 26 26 | 
             
            // THE SOFTWARE.
         | 
| 27 27 |  | 
| 28 28 | 
             
            // Version:
         | 
| 29 | 
            +
            //  - v2.4.2 Decode percent-encoded URI.
         | 
| 29 30 | 
             
            //  - v2.4.1 Fix some glTF object class does not have `extensions` and/or
         | 
| 30 31 | 
             
            //  `extras` property.
         | 
| 31 32 | 
             
            //  - v2.4.0 Experimental RapidJSON and C++14 support(Thanks to @jrkoone).
         | 
| @@ -51,6 +52,7 @@ | |
| 51 52 |  | 
| 52 53 | 
             
            #include <array>
         | 
| 53 54 | 
             
            #include <cassert>
         | 
| 55 | 
            +
            #include <cmath>  // std::fabs
         | 
| 54 56 | 
             
            #include <cstdint>
         | 
| 55 57 | 
             
            #include <cstdlib>
         | 
| 56 58 | 
             
            #include <cstring>
         | 
| @@ -321,7 +323,8 @@ class Value { | |
| 321 323 | 
             
              }
         | 
| 322 324 |  | 
| 323 325 | 
             
              // Use this function if you want to have number value as int.
         | 
| 324 | 
            -
               | 
| 326 | 
            +
              // TODO(syoyo): Support int value larger than 32 bits
         | 
| 327 | 
            +
              int GetNumberAsInt() const {
         | 
| 325 328 | 
             
                if (type_ == REAL_TYPE) {
         | 
| 326 329 | 
             
                  return int(real_value_);
         | 
| 327 330 | 
             
                } else {
         | 
| @@ -526,10 +529,12 @@ struct AnimationChannel { | |
| 526 529 | 
             
                                        // "weights"]
         | 
| 527 530 | 
             
              Value extras;
         | 
| 528 531 | 
             
              ExtensionMap extensions;
         | 
| 532 | 
            +
              ExtensionMap target_extensions;
         | 
| 529 533 |  | 
| 530 534 | 
             
              // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
         | 
| 531 535 | 
             
              std::string extras_json_string;
         | 
| 532 536 | 
             
              std::string extensions_json_string;
         | 
| 537 | 
            +
              std::string target_extensions_json_string;
         | 
| 533 538 |  | 
| 534 539 | 
             
              AnimationChannel() : sampler(-1), target_node(-1) {}
         | 
| 535 540 | 
             
              DEFAULT_METHODS(AnimationChannel)
         | 
| @@ -637,7 +642,8 @@ struct Image { | |
| 637 642 | 
             
              int bufferView;        // (required if no uri)
         | 
| 638 643 | 
             
              std::string mimeType;  // (required if no uri) ["image/jpeg", "image/png",
         | 
| 639 644 | 
             
                                     // "image/bmp", "image/gif"]
         | 
| 640 | 
            -
              std::string uri;       // (required if no mimeType)
         | 
| 645 | 
            +
              std::string uri;       // (required if no mimeType) uri is not decoded(e.g.
         | 
| 646 | 
            +
                                     // whitespace may be represented as %20)
         | 
| 641 647 | 
             
              Value extras;
         | 
| 642 648 | 
             
              ExtensionMap extensions;
         | 
| 643 649 |  | 
| @@ -658,6 +664,8 @@ struct Image { | |
| 658 664 | 
             
                width = -1;
         | 
| 659 665 | 
             
                height = -1;
         | 
| 660 666 | 
             
                component = -1;
         | 
| 667 | 
            +
                bits = -1;
         | 
| 668 | 
            +
                pixel_type = -1;
         | 
| 661 669 | 
             
              }
         | 
| 662 670 | 
             
              DEFAULT_METHODS(Image)
         | 
| 663 671 |  | 
| @@ -797,12 +805,13 @@ struct Material { | |
| 797 805 |  | 
| 798 806 | 
             
            struct BufferView {
         | 
| 799 807 | 
             
              std::string name;
         | 
| 800 | 
            -
              int buffer; | 
| 801 | 
            -
              size_t byteOffset;  // minimum 0, default 0
         | 
| 802 | 
            -
              size_t byteLength;  // required, minimum 1
         | 
| 803 | 
            -
              size_t byteStride;  // minimum 4, maximum 252 (multiple of 4), default 0 =
         | 
| 804 | 
            -
             | 
| 805 | 
            -
              int target; | 
| 808 | 
            +
              int buffer{-1};        // Required
         | 
| 809 | 
            +
              size_t byteOffset{0};  // minimum 0, default 0
         | 
| 810 | 
            +
              size_t byteLength{0};  // required, minimum 1. 0 = invalid
         | 
| 811 | 
            +
              size_t byteStride{0};  // minimum 4, maximum 252 (multiple of 4), default 0 =
         | 
| 812 | 
            +
                                     // understood to be tightly packed
         | 
| 813 | 
            +
              int target{0};  // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"] for vertex indices
         | 
| 814 | 
            +
                              // or atttribs. Could be 0 for other data
         | 
| 806 815 | 
             
              Value extras;
         | 
| 807 816 | 
             
              ExtensionMap extensions;
         | 
| 808 817 |  | 
| @@ -810,9 +819,15 @@ struct BufferView { | |
| 810 819 | 
             
              std::string extras_json_string;
         | 
| 811 820 | 
             
              std::string extensions_json_string;
         | 
| 812 821 |  | 
| 813 | 
            -
              bool dracoDecoded;  // Flag indicating this has been draco decoded
         | 
| 822 | 
            +
              bool dracoDecoded{false};  // Flag indicating this has been draco decoded
         | 
| 814 823 |  | 
| 815 | 
            -
              BufferView() | 
| 824 | 
            +
              BufferView()
         | 
| 825 | 
            +
                  : buffer(-1),
         | 
| 826 | 
            +
                    byteOffset(0),
         | 
| 827 | 
            +
                    byteLength(0),
         | 
| 828 | 
            +
                    byteStride(0),
         | 
| 829 | 
            +
                    target(0),
         | 
| 830 | 
            +
                    dracoDecoded(false) {}
         | 
| 816 831 | 
             
              DEFAULT_METHODS(BufferView)
         | 
| 817 832 | 
             
              bool operator==(const BufferView &) const;
         | 
| 818 833 | 
             
            };
         | 
| @@ -887,13 +902,13 @@ struct Accessor { | |
| 887 902 | 
             
                // unreachable return 0;
         | 
| 888 903 | 
             
              }
         | 
| 889 904 |  | 
| 890 | 
            -
              Accessor() | 
| 891 | 
            -
             | 
| 892 | 
            -
             | 
| 893 | 
            -
             | 
| 894 | 
            -
             | 
| 895 | 
            -
             | 
| 896 | 
            -
             | 
| 905 | 
            +
              Accessor()
         | 
| 906 | 
            +
                  : bufferView(-1),
         | 
| 907 | 
            +
                    byteOffset(0),
         | 
| 908 | 
            +
                    normalized(false),
         | 
| 909 | 
            +
                    componentType(-1),
         | 
| 910 | 
            +
                    count(0),
         | 
| 911 | 
            +
                    type(-1) {
         | 
| 897 912 | 
             
                sparse.isSparse = false;
         | 
| 898 913 | 
             
              }
         | 
| 899 914 | 
             
              DEFAULT_METHODS(Accessor)
         | 
| @@ -983,6 +998,7 @@ struct Primitive { | |
| 983 998 | 
             
              Primitive() {
         | 
| 984 999 | 
             
                material = -1;
         | 
| 985 1000 | 
             
                indices = -1;
         | 
| 1001 | 
            +
                mode = -1;
         | 
| 986 1002 | 
             
              }
         | 
| 987 1003 | 
             
              DEFAULT_METHODS(Primitive)
         | 
| 988 1004 | 
             
              bool operator==(const Primitive &) const;
         | 
| @@ -1037,6 +1053,7 @@ struct Buffer { | |
| 1037 1053 | 
             
              std::vector<unsigned char> data;
         | 
| 1038 1054 | 
             
              std::string
         | 
| 1039 1055 | 
             
                  uri;  // considered as required here but not in the spec (need to clarify)
         | 
| 1056 | 
            +
                        // uri is not decoded(e.g. whitespace may be represented as %20)
         | 
| 1040 1057 | 
             
              Value extras;
         | 
| 1041 1058 | 
             
              ExtensionMap extensions;
         | 
| 1042 1059 |  | 
| @@ -1101,9 +1118,9 @@ struct SpotLight { | |
| 1101 1118 | 
             
            struct Light {
         | 
| 1102 1119 | 
             
              std::string name;
         | 
| 1103 1120 | 
             
              std::vector<double> color;
         | 
| 1104 | 
            -
              double intensity;
         | 
| 1121 | 
            +
              double intensity{1.0};
         | 
| 1105 1122 | 
             
              std::string type;
         | 
| 1106 | 
            -
              double range;
         | 
| 1123 | 
            +
              double range{0.0};  // 0.0 = inifinite
         | 
| 1107 1124 | 
             
              SpotLight spot;
         | 
| 1108 1125 |  | 
| 1109 1126 | 
             
              Light() : intensity(1.0), range(0.0) {}
         | 
| @@ -1141,7 +1158,7 @@ class Model { | |
| 1141 1158 | 
             
              std::vector<Scene> scenes;
         | 
| 1142 1159 | 
             
              std::vector<Light> lights;
         | 
| 1143 1160 |  | 
| 1144 | 
            -
              int defaultScene;
         | 
| 1161 | 
            +
              int defaultScene = -1;
         | 
| 1145 1162 | 
             
              std::vector<std::string> extensionsUsed;
         | 
| 1146 1163 | 
             
              std::vector<std::string> extensionsRequired;
         | 
| 1147 1164 |  | 
| @@ -1235,7 +1252,14 @@ struct FsCallbacks { | |
| 1235 1252 |  | 
| 1236 1253 | 
             
            bool FileExists(const std::string &abs_filename, void *);
         | 
| 1237 1254 |  | 
| 1238 | 
            -
             | 
| 1255 | 
            +
            ///
         | 
| 1256 | 
            +
            /// Expand file path(e.g. `~` to home directory on posix, `%APPDATA%` to
         | 
| 1257 | 
            +
            /// `C:\Users\tinygltf\AppData`)
         | 
| 1258 | 
            +
            ///
         | 
| 1259 | 
            +
            /// @param[in] filepath File path string. Assume UTF-8
         | 
| 1260 | 
            +
            /// @param[in] userdata User data. Set to `nullptr` if you don't need it.
         | 
| 1261 | 
            +
            ///
         | 
| 1262 | 
            +
            std::string ExpandFilePath(const std::string &filepath, void *userdata);
         | 
| 1239 1263 |  | 
| 1240 1264 | 
             
            bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
         | 
| 1241 1265 | 
             
                               const std::string &filepath, void *);
         | 
| @@ -1547,11 +1571,12 @@ class TinyGLTF { | |
| 1547 1571 | 
             
            #undef NOMINMAX
         | 
| 1548 1572 | 
             
            #endif
         | 
| 1549 1573 |  | 
| 1550 | 
            -
            #if defined(__GLIBCXX__) | 
| 1574 | 
            +
            #if defined(__GLIBCXX__)  // mingw
         | 
| 1551 1575 |  | 
| 1552 | 
            -
            #include <ext/stdio_filebuf.h>  // fstream (all sorts of IO stuff) + stdio_filebuf (=streambuf)
         | 
| 1553 1576 | 
             
            #include <fcntl.h>  // _O_RDONLY
         | 
| 1554 1577 |  | 
| 1578 | 
            +
            #include <ext/stdio_filebuf.h>  // fstream (all sorts of IO stuff) + stdio_filebuf (=streambuf)
         | 
| 1579 | 
            +
             | 
| 1555 1580 | 
             
            #endif
         | 
| 1556 1581 |  | 
| 1557 1582 | 
             
            #elif !defined(__ANDROID__)
         | 
| @@ -2119,6 +2144,88 @@ std::string base64_decode(std::string const &encoded_string) { | |
| 2119 2144 | 
             
            #pragma clang diagnostic pop
         | 
| 2120 2145 | 
             
            #endif
         | 
| 2121 2146 |  | 
| 2147 | 
            +
            // https://github.com/syoyo/tinygltf/issues/228
         | 
| 2148 | 
            +
            // TODO(syoyo): Use uriparser https://uriparser.github.io/ for stricter Uri
         | 
| 2149 | 
            +
            // decoding?
         | 
| 2150 | 
            +
            //
         | 
| 2151 | 
            +
            // https://stackoverflow.com/questions/18307429/encode-decode-url-in-c
         | 
| 2152 | 
            +
            // http://dlib.net/dlib/server/server_http.cpp.html
         | 
| 2153 | 
            +
             | 
| 2154 | 
            +
            // --- dlib beign ------------------------------------------------------------
         | 
| 2155 | 
            +
            // Copyright (C) 2003  Davis E. King (davis@dlib.net)
         | 
| 2156 | 
            +
            // License: Boost Software License   See LICENSE.txt for the full license.
         | 
| 2157 | 
            +
             | 
| 2158 | 
            +
            namespace dlib {
         | 
| 2159 | 
            +
             | 
| 2160 | 
            +
            #if 0
         | 
| 2161 | 
            +
                    inline unsigned char to_hex( unsigned char x )
         | 
| 2162 | 
            +
                    {
         | 
| 2163 | 
            +
                        return x + (x > 9 ? ('A'-10) : '0');
         | 
| 2164 | 
            +
                    }
         | 
| 2165 | 
            +
             | 
| 2166 | 
            +
                    const std::string urlencode( const std::string& s )
         | 
| 2167 | 
            +
                    {
         | 
| 2168 | 
            +
                        std::ostringstream os;
         | 
| 2169 | 
            +
             | 
| 2170 | 
            +
                        for ( std::string::const_iterator ci = s.begin(); ci != s.end(); ++ci )
         | 
| 2171 | 
            +
                        {
         | 
| 2172 | 
            +
                            if ( (*ci >= 'a' && *ci <= 'z') ||
         | 
| 2173 | 
            +
                                 (*ci >= 'A' && *ci <= 'Z') ||
         | 
| 2174 | 
            +
                                 (*ci >= '0' && *ci <= '9') )
         | 
| 2175 | 
            +
                            { // allowed
         | 
| 2176 | 
            +
                                os << *ci;
         | 
| 2177 | 
            +
                            }
         | 
| 2178 | 
            +
                            else if ( *ci == ' ')
         | 
| 2179 | 
            +
                            {
         | 
| 2180 | 
            +
                                os << '+';
         | 
| 2181 | 
            +
                            }
         | 
| 2182 | 
            +
                            else
         | 
| 2183 | 
            +
                            {
         | 
| 2184 | 
            +
                                os << '%' << to_hex(static_cast<unsigned char>(*ci >> 4)) << to_hex(static_cast<unsigned char>(*ci % 16));
         | 
| 2185 | 
            +
                            }
         | 
| 2186 | 
            +
                        }
         | 
| 2187 | 
            +
             | 
| 2188 | 
            +
                        return os.str();
         | 
| 2189 | 
            +
                    }
         | 
| 2190 | 
            +
            #endif
         | 
| 2191 | 
            +
             | 
| 2192 | 
            +
            inline unsigned char from_hex(unsigned char ch) {
         | 
| 2193 | 
            +
              if (ch <= '9' && ch >= '0')
         | 
| 2194 | 
            +
                ch -= '0';
         | 
| 2195 | 
            +
              else if (ch <= 'f' && ch >= 'a')
         | 
| 2196 | 
            +
                ch -= 'a' - 10;
         | 
| 2197 | 
            +
              else if (ch <= 'F' && ch >= 'A')
         | 
| 2198 | 
            +
                ch -= 'A' - 10;
         | 
| 2199 | 
            +
              else
         | 
| 2200 | 
            +
                ch = 0;
         | 
| 2201 | 
            +
              return ch;
         | 
| 2202 | 
            +
            }
         | 
| 2203 | 
            +
             | 
| 2204 | 
            +
            static const std::string urldecode(const std::string &str) {
         | 
| 2205 | 
            +
              using namespace std;
         | 
| 2206 | 
            +
              string result;
         | 
| 2207 | 
            +
              string::size_type i;
         | 
| 2208 | 
            +
              for (i = 0; i < str.size(); ++i) {
         | 
| 2209 | 
            +
                if (str[i] == '+') {
         | 
| 2210 | 
            +
                  result += ' ';
         | 
| 2211 | 
            +
                } else if (str[i] == '%' && str.size() > i + 2) {
         | 
| 2212 | 
            +
                  const unsigned char ch1 =
         | 
| 2213 | 
            +
                      from_hex(static_cast<unsigned char>(str[i + 1]));
         | 
| 2214 | 
            +
                  const unsigned char ch2 =
         | 
| 2215 | 
            +
                      from_hex(static_cast<unsigned char>(str[i + 2]));
         | 
| 2216 | 
            +
                  const unsigned char ch = static_cast<unsigned char>((ch1 << 4) | ch2);
         | 
| 2217 | 
            +
                  result += static_cast<char>(ch);
         | 
| 2218 | 
            +
                  i += 2;
         | 
| 2219 | 
            +
                } else {
         | 
| 2220 | 
            +
                  result += str[i];
         | 
| 2221 | 
            +
                }
         | 
| 2222 | 
            +
              }
         | 
| 2223 | 
            +
              return result;
         | 
| 2224 | 
            +
            }
         | 
| 2225 | 
            +
             | 
| 2226 | 
            +
            }  // namespace dlib
         | 
| 2227 | 
            +
            // --- dlib end --------------------------------------------------------------
         | 
| 2228 | 
            +
             | 
| 2122 2229 | 
             
            static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
         | 
| 2123 2230 | 
             
                                         std::string *warn, const std::string &filename,
         | 
| 2124 2231 | 
             
                                         const std::string &basedir, bool required,
         | 
| @@ -2379,11 +2486,22 @@ void TinyGLTF::SetFsCallbacks(FsCallbacks callbacks) { fs = callbacks; } | |
| 2379 2486 |  | 
| 2380 2487 | 
             
            #ifdef _WIN32
         | 
| 2381 2488 | 
             
            static inline std::wstring UTF8ToWchar(const std::string &str) {
         | 
| 2382 | 
            -
              int wstr_size = | 
| 2489 | 
            +
              int wstr_size =
         | 
| 2490 | 
            +
                  MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
         | 
| 2383 2491 | 
             
              std::wstring wstr(wstr_size, 0);
         | 
| 2384 | 
            -
              MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0], | 
| 2492 | 
            +
              MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0],
         | 
| 2493 | 
            +
                                  (int)wstr.size());
         | 
| 2385 2494 | 
             
              return wstr;
         | 
| 2386 2495 | 
             
            }
         | 
| 2496 | 
            +
             | 
| 2497 | 
            +
            static inline std::string WcharToUTF8(const std::wstring &wstr) {
         | 
| 2498 | 
            +
              int str_size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(),
         | 
| 2499 | 
            +
                                                 nullptr, 0, NULL, NULL);
         | 
| 2500 | 
            +
              std::string str(str_size, 0);
         | 
| 2501 | 
            +
              WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), &str[0],
         | 
| 2502 | 
            +
                                  (int)str.size(), NULL, NULL);
         | 
| 2503 | 
            +
              return str;
         | 
| 2504 | 
            +
            }
         | 
| 2387 2505 | 
             
            #endif
         | 
| 2388 2506 |  | 
| 2389 2507 | 
             
            #ifndef TINYGLTF_NO_FS
         | 
| @@ -2435,15 +2553,16 @@ bool FileExists(const std::string &abs_filename, void *) { | |
| 2435 2553 |  | 
| 2436 2554 | 
             
            std::string ExpandFilePath(const std::string &filepath, void *) {
         | 
| 2437 2555 | 
             
            #ifdef _WIN32
         | 
| 2438 | 
            -
               | 
| 2439 | 
            -
               | 
| 2440 | 
            -
               | 
| 2556 | 
            +
              // Assume input `filepath` is encoded in UTF-8
         | 
| 2557 | 
            +
              std::wstring wfilepath = UTF8ToWchar(filepath);
         | 
| 2558 | 
            +
              DWORD wlen = ExpandEnvironmentStringsW(wfilepath.c_str(), nullptr, 0);
         | 
| 2559 | 
            +
              wchar_t *wstr = new wchar_t[wlen];
         | 
| 2560 | 
            +
              ExpandEnvironmentStringsW(wfilepath.c_str(), wstr, wlen);
         | 
| 2441 2561 |  | 
| 2442 | 
            -
              std:: | 
| 2562 | 
            +
              std::wstring ws(wstr);
         | 
| 2563 | 
            +
              delete[] wstr;
         | 
| 2564 | 
            +
              return WcharToUTF8(ws);
         | 
| 2443 2565 |  | 
| 2444 | 
            -
              delete[] str;
         | 
| 2445 | 
            -
             | 
| 2446 | 
            -
              return s;
         | 
| 2447 2566 | 
             
            #else
         | 
| 2448 2567 |  | 
| 2449 2568 | 
             
            #if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \
         | 
| @@ -2458,8 +2577,10 @@ std::string ExpandFilePath(const std::string &filepath, void *) { | |
| 2458 2577 | 
             
                return "";
         | 
| 2459 2578 | 
             
              }
         | 
| 2460 2579 |  | 
| 2580 | 
            +
              // Quote the string to keep any spaces in filepath intact.
         | 
| 2581 | 
            +
              std::string quoted_path = "\"" + filepath + "\"";
         | 
| 2461 2582 | 
             
              // char** w;
         | 
| 2462 | 
            -
              int ret = wordexp( | 
| 2583 | 
            +
              int ret = wordexp(quoted_path.c_str(), &p, 0);
         | 
| 2463 2584 | 
             
              if (ret) {
         | 
| 2464 2585 | 
             
                // err
         | 
| 2465 2586 | 
             
                s = filepath;
         | 
| @@ -2493,11 +2614,12 @@ bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err, | |
| 2493 2614 | 
             
                  return false;
         | 
| 2494 2615 | 
             
                }
         | 
| 2495 2616 | 
             
                size_t size = AAsset_getLength(asset);
         | 
| 2496 | 
            -
                if (size  | 
| 2617 | 
            +
                if (size == 0) {
         | 
| 2497 2618 | 
             
                  if (err) {
         | 
| 2498 2619 | 
             
                    (*err) += "Invalid file size : " + filepath +
         | 
| 2499 2620 | 
             
                              " (does the path point to a directory?)";
         | 
| 2500 2621 | 
             
                  }
         | 
| 2622 | 
            +
                  return false;
         | 
| 2501 2623 | 
             
                }
         | 
| 2502 2624 | 
             
                out->resize(size);
         | 
| 2503 2625 | 
             
                AAsset_read(asset, reinterpret_cast<char *>(&out->at(0)), size);
         | 
| @@ -2511,13 +2633,17 @@ bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err, | |
| 2511 2633 | 
             
              }
         | 
| 2512 2634 | 
             
            #else
         | 
| 2513 2635 | 
             
            #ifdef _WIN32
         | 
| 2514 | 
            -
            #if defined(__GLIBCXX__) | 
| 2515 | 
            -
              int file_descriptor = | 
| 2636 | 
            +
            #if defined(__GLIBCXX__)  // mingw
         | 
| 2637 | 
            +
              int file_descriptor =
         | 
| 2638 | 
            +
                  _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY);
         | 
| 2516 2639 | 
             
              __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
         | 
| 2517 2640 | 
             
              std::istream f(&wfile_buf);
         | 
| 2518 | 
            -
            #elif defined(_MSC_VER)
         | 
| 2641 | 
            +
            #elif defined(_MSC_VER) || defined(_LIBCPP_VERSION)
         | 
| 2642 | 
            +
              // For libcxx, assume _LIBCPP_HAS_OPEN_WITH_WCHAR is defined to accept
         | 
| 2643 | 
            +
              // `wchar_t *`
         | 
| 2519 2644 | 
             
              std::ifstream f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary);
         | 
| 2520 | 
            -
            #else | 
| 2645 | 
            +
            #else
         | 
| 2646 | 
            +
              // Unknown compiler/runtime
         | 
| 2521 2647 | 
             
              std::ifstream f(filepath.c_str(), std::ifstream::binary);
         | 
| 2522 2648 | 
             
            #endif
         | 
| 2523 2649 | 
             
            #else
         | 
| @@ -2558,13 +2684,15 @@ bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err, | |
| 2558 2684 | 
             
            bool WriteWholeFile(std::string *err, const std::string &filepath,
         | 
| 2559 2685 | 
             
                                const std::vector<unsigned char> &contents, void *) {
         | 
| 2560 2686 | 
             
            #ifdef _WIN32
         | 
| 2561 | 
            -
            #if defined(__GLIBCXX__) | 
| 2562 | 
            -
              int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(), | 
| 2563 | 
            -
             | 
| 2687 | 
            +
            #if defined(__GLIBCXX__)  // mingw
         | 
| 2688 | 
            +
              int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(),
         | 
| 2689 | 
            +
                                           _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
         | 
| 2690 | 
            +
              __gnu_cxx::stdio_filebuf<char> wfile_buf(
         | 
| 2691 | 
            +
                  file_descriptor, std::ios_base::out | std::ios_base::binary);
         | 
| 2564 2692 | 
             
              std::ostream f(&wfile_buf);
         | 
| 2565 2693 | 
             
            #elif defined(_MSC_VER)
         | 
| 2566 2694 | 
             
              std::ofstream f(UTF8ToWchar(filepath).c_str(), std::ofstream::binary);
         | 
| 2567 | 
            -
            #else | 
| 2695 | 
            +
            #else  // clang?
         | 
| 2568 2696 | 
             
              std::ofstream f(filepath.c_str(), std::ofstream::binary);
         | 
| 2569 2697 | 
             
            #endif
         | 
| 2570 2698 | 
             
            #else
         | 
| @@ -2611,12 +2739,13 @@ static void UpdateImageObject(Image &image, std::string &baseDir, int index, | |
| 2611 2739 | 
             
                                          void *user_data = nullptr) {
         | 
| 2612 2740 | 
             
              std::string filename;
         | 
| 2613 2741 | 
             
              std::string ext;
         | 
| 2614 | 
            -
             | 
| 2615 | 
            -
              // If image have uri. Use it it as a filename
         | 
| 2742 | 
            +
              // If image has uri, use it it as a filename
         | 
| 2616 2743 | 
             
              if (image.uri.size()) {
         | 
| 2617 2744 | 
             
                filename = GetBaseFilename(image.uri);
         | 
| 2618 2745 | 
             
                ext = GetFilePathExtension(filename);
         | 
| 2619 | 
            -
             | 
| 2746 | 
            +
              } else if (image.bufferView != -1) {
         | 
| 2747 | 
            +
                // If there's no URI and the data exists in a buffer,
         | 
| 2748 | 
            +
                // don't change properties or write images
         | 
| 2620 2749 | 
             
              } else if (image.name.size()) {
         | 
| 2621 2750 | 
             
                ext = MimeToExt(image.mimeType);
         | 
| 2622 2751 | 
             
                // Otherwise use name as filename
         | 
| @@ -2628,7 +2757,7 @@ static void UpdateImageObject(Image &image, std::string &baseDir, int index, | |
| 2628 2757 | 
             
              }
         | 
| 2629 2758 |  | 
| 2630 2759 | 
             
              // If callback is set, modify image data object
         | 
| 2631 | 
            -
              if (*WriteImageData != nullptr) {
         | 
| 2760 | 
            +
              if (*WriteImageData != nullptr && !filename.empty()) {
         | 
| 2632 2761 | 
             
                std::string uri;
         | 
| 2633 2762 | 
             
                (*WriteImageData)(&baseDir, &filename, &image, embedImages, user_data);
         | 
| 2634 2763 | 
             
              }
         | 
| @@ -2728,6 +2857,7 @@ bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type, | |
| 2728 2857 | 
             
                }
         | 
| 2729 2858 | 
             
              }
         | 
| 2730 2859 |  | 
| 2860 | 
            +
              // TODO(syoyo): Allow empty buffer? #229
         | 
| 2731 2861 | 
             
              if (data.empty()) {
         | 
| 2732 2862 | 
             
                return false;
         | 
| 2733 2863 | 
             
              }
         | 
| @@ -2872,7 +3002,9 @@ json_const_iterator ObjectEnd(const json &o) { | |
| 2872 3002 | 
             
            #endif
         | 
| 2873 3003 | 
             
            }
         | 
| 2874 3004 |  | 
| 2875 | 
            -
            const char | 
| 3005 | 
            +
            // Making this a const char* results in a pointer to a temporary when
         | 
| 3006 | 
            +
            // TINYGLTF_USE_RAPIDJSON is off.
         | 
| 3007 | 
            +
            std::string GetKey(json_const_iterator &it) {
         | 
| 2876 3008 | 
             
            #ifdef TINYGLTF_USE_RAPIDJSON
         | 
| 2877 3009 | 
             
              return it->name.GetString();
         | 
| 2878 3010 | 
             
            #else
         | 
| @@ -3508,6 +3640,7 @@ static bool ParseAsset(Asset *asset, std::string *err, const json &o, | |
| 3508 3640 | 
             
              ParseStringProperty(&asset->version, err, o, "version", true, "Asset");
         | 
| 3509 3641 | 
             
              ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset");
         | 
| 3510 3642 | 
             
              ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset");
         | 
| 3643 | 
            +
              ParseStringProperty(&asset->copyright, err, o, "copyright", false, "Asset");
         | 
| 3511 3644 |  | 
| 3512 3645 | 
             
              ParseExtensionsProperty(&asset->extensions, err, o);
         | 
| 3513 3646 |  | 
| @@ -3646,7 +3779,10 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err, | |
| 3646 3779 | 
             
            #ifdef TINYGLTF_NO_EXTERNAL_IMAGE
         | 
| 3647 3780 | 
             
                return true;
         | 
| 3648 3781 | 
             
            #endif
         | 
| 3649 | 
            -
                 | 
| 3782 | 
            +
                std::string decoded_uri = dlib::urldecode(uri);
         | 
| 3783 | 
            +
                if (!LoadExternalFile(&img, err, warn, decoded_uri, basedir,
         | 
| 3784 | 
            +
                                      /* required */ false, /* required bytes */ 0,
         | 
| 3785 | 
            +
                                      /* checksize */ false, fs)) {
         | 
| 3650 3786 | 
             
                  if (warn) {
         | 
| 3651 3787 | 
             
                    (*warn) += "Failed to load external 'uri' for image[" +
         | 
| 3652 3788 | 
             
                               std::to_string(image_idx) + "] name = [" + image->name +
         | 
| @@ -3868,9 +4004,10 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o, | |
| 3868 4004 | 
             
                    }
         | 
| 3869 4005 | 
             
                  } else {
         | 
| 3870 4006 | 
             
                    // External .bin file.
         | 
| 4007 | 
            +
                    std::string decoded_uri = dlib::urldecode(buffer->uri);
         | 
| 3871 4008 | 
             
                    if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr,
         | 
| 3872 | 
            -
                                           | 
| 3873 | 
            -
                                          fs)) {
         | 
| 4009 | 
            +
                                          decoded_uri, basedir, /* required */ true,
         | 
| 4010 | 
            +
                                          byteLength, /* checkSize */ true, fs)) {
         | 
| 3874 4011 | 
             
                      return false;
         | 
| 3875 4012 | 
             
                    }
         | 
| 3876 4013 | 
             
                  }
         | 
| @@ -3912,8 +4049,10 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o, | |
| 3912 4049 | 
             
                  }
         | 
| 3913 4050 | 
             
                } else {
         | 
| 3914 4051 | 
             
                  // Assume external .bin file.
         | 
| 3915 | 
            -
                   | 
| 3916 | 
            -
             | 
| 4052 | 
            +
                  std::string decoded_uri = dlib::urldecode(buffer->uri);
         | 
| 4053 | 
            +
                  if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, decoded_uri,
         | 
| 4054 | 
            +
                                        basedir, /* required */ true, byteLength,
         | 
| 4055 | 
            +
                                        /* checkSize */ true, fs)) {
         | 
| 3917 4056 | 
             
                    return false;
         | 
| 3918 4057 | 
             
                  }
         | 
| 3919 4058 | 
             
                }
         | 
| @@ -4751,6 +4890,13 @@ static bool ParseAnimationChannel( | |
| 4751 4890 | 
             
                  }
         | 
| 4752 4891 | 
             
                  return false;
         | 
| 4753 4892 | 
             
                }
         | 
| 4893 | 
            +
                ParseExtensionsProperty(&channel->target_extensions, err, target_object);
         | 
| 4894 | 
            +
                if (store_original_json_for_extras_and_extensions) {
         | 
| 4895 | 
            +
                  json_const_iterator it;
         | 
| 4896 | 
            +
                  if (FindMember(target_object, "extensions", it)) {
         | 
| 4897 | 
            +
                    channel->target_extensions_json_string = JsonToString(GetValue(it));
         | 
| 4898 | 
            +
                  }
         | 
| 4899 | 
            +
                }
         | 
| 4754 4900 | 
             
              }
         | 
| 4755 4901 |  | 
| 4756 4902 | 
             
              channel->sampler = samplerIndex;
         | 
| @@ -5252,7 +5398,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, | |
| 5252 5398 |  | 
| 5253 5399 | 
             
            #if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
         | 
| 5254 5400 | 
             
                 defined(_CPPUNWIND)) &&                               \
         | 
| 5255 | 
            -
             | 
| 5401 | 
            +
                !defined(TINYGLTF_NOEXCEPTION)
         | 
| 5256 5402 | 
             
              try {
         | 
| 5257 5403 | 
             
                JsonParse(v, json_str, json_str_length, true);
         | 
| 5258 5404 |  | 
| @@ -5525,7 +5671,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, | |
| 5525 5671 |  | 
| 5526 5672 | 
             
              // Assign missing bufferView target types
         | 
| 5527 5673 | 
             
              // - Look for missing Mesh indices
         | 
| 5528 | 
            -
              // - Look for missing  | 
| 5674 | 
            +
              // - Look for missing Mesh attributes
         | 
| 5529 5675 | 
             
              for (auto &mesh : model->meshes) {
         | 
| 5530 5676 | 
             
                for (auto &primitive : mesh.primitives) {
         | 
| 5531 5677 | 
             
                  if (primitive.indices >
         | 
| @@ -5553,14 +5699,25 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, | |
| 5553 5699 | 
             
                    // we could optionally check if acessors' bufferView type is Scalar, as
         | 
| 5554 5700 | 
             
                    // it should be
         | 
| 5555 5701 | 
             
                  }
         | 
| 5556 | 
            -
             | 
| 5557 | 
            -
             | 
| 5558 | 
            -
             | 
| 5559 | 
            -
             | 
| 5560 | 
            -
             | 
| 5561 | 
            -
             | 
| 5562 | 
            -
             | 
| 5563 | 
            -
             | 
| 5702 | 
            +
             | 
| 5703 | 
            +
                  for (auto &attribute : primitive.attributes) {
         | 
| 5704 | 
            +
                    model
         | 
| 5705 | 
            +
                        ->bufferViews[size_t(
         | 
| 5706 | 
            +
                            model->accessors[size_t(attribute.second)].bufferView)]
         | 
| 5707 | 
            +
                        .target = TINYGLTF_TARGET_ARRAY_BUFFER;
         | 
| 5708 | 
            +
                  }
         | 
| 5709 | 
            +
             | 
| 5710 | 
            +
                  for (auto &target : primitive.targets) {
         | 
| 5711 | 
            +
                    for (auto &attribute : target) {
         | 
| 5712 | 
            +
                      auto bufferView =
         | 
| 5713 | 
            +
                          model->accessors[size_t(attribute.second)].bufferView;
         | 
| 5714 | 
            +
                      // bufferView could be null(-1) for sparse morph target
         | 
| 5715 | 
            +
                      if (bufferView >= 0) {
         | 
| 5716 | 
            +
                        model->bufferViews[size_t(bufferView)].target =
         | 
| 5717 | 
            +
                            TINYGLTF_TARGET_ARRAY_BUFFER;
         | 
| 5718 | 
            +
                      }
         | 
| 5719 | 
            +
                    }
         | 
| 5720 | 
            +
                  }
         | 
| 5564 5721 | 
             
                }
         | 
| 5565 5722 | 
             
              }
         | 
| 5566 5723 |  | 
| @@ -6143,6 +6300,13 @@ static void SerializeNumberProperty(const std::string &key, T number, | |
| 6143 6300 | 
             
              JsonAddMember(obj, key.c_str(), json(number));
         | 
| 6144 6301 | 
             
            }
         | 
| 6145 6302 |  | 
| 6303 | 
            +
            #ifdef TINYGLTF_USE_RAPIDJSON
         | 
| 6304 | 
            +
            template <>
         | 
| 6305 | 
            +
            void SerializeNumberProperty(const std::string &key, size_t number, json &obj) {
         | 
| 6306 | 
            +
              JsonAddMember(obj, key.c_str(), json(static_cast<uint64_t>(number)));
         | 
| 6307 | 
            +
            }
         | 
| 6308 | 
            +
            #endif
         | 
| 6309 | 
            +
             | 
| 6146 6310 | 
             
            template <typename T>
         | 
| 6147 6311 | 
             
            static void SerializeNumberArrayProperty(const std::string &key,
         | 
| 6148 6312 | 
             
                                                     const std::vector<T> &value,
         | 
| @@ -6278,17 +6442,25 @@ static void SerializeValue(const std::string &key, const Value &value, | |
| 6278 6442 | 
             
            static void SerializeGltfBufferData(const std::vector<unsigned char> &data,
         | 
| 6279 6443 | 
             
                                                json &o) {
         | 
| 6280 6444 | 
             
              std::string header = "data:application/octet-stream;base64,";
         | 
| 6281 | 
            -
               | 
| 6282 | 
            -
             | 
| 6283 | 
            -
             | 
| 6445 | 
            +
              if (data.size() > 0) {
         | 
| 6446 | 
            +
                std::string encodedData =
         | 
| 6447 | 
            +
                    base64_encode(&data[0], static_cast<unsigned int>(data.size()));
         | 
| 6448 | 
            +
                SerializeStringProperty("uri", header + encodedData, o);
         | 
| 6449 | 
            +
              } else {
         | 
| 6450 | 
            +
                // Issue #229
         | 
| 6451 | 
            +
                // size 0 is allowd. Just emit mime header.
         | 
| 6452 | 
            +
                SerializeStringProperty("uri", header, o);
         | 
| 6453 | 
            +
              }
         | 
| 6284 6454 | 
             
            }
         | 
| 6285 6455 |  | 
| 6286 6456 | 
             
            static bool SerializeGltfBufferData(const std::vector<unsigned char> &data,
         | 
| 6287 6457 | 
             
                                                const std::string &binFilename) {
         | 
| 6288 6458 | 
             
            #ifdef _WIN32
         | 
| 6289 | 
            -
            #if defined(__GLIBCXX__) | 
| 6290 | 
            -
             | 
| 6291 | 
            -
             | 
| 6459 | 
            +
            #if defined(__GLIBCXX__)  // mingw
         | 
| 6460 | 
            +
              int file_descriptor = _wopen(UTF8ToWchar(binFilename).c_str(),
         | 
| 6461 | 
            +
                                           _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
         | 
| 6462 | 
            +
              __gnu_cxx::stdio_filebuf<char> wfile_buf(
         | 
| 6463 | 
            +
                  file_descriptor, std::ios_base::out | std::ios_base::binary);
         | 
| 6292 6464 | 
             
              std::ostream output(&wfile_buf);
         | 
| 6293 6465 | 
             
              if (!wfile_buf.is_open()) return false;
         | 
| 6294 6466 | 
             
            #elif defined(_MSC_VER)
         | 
| @@ -6302,8 +6474,14 @@ static bool SerializeGltfBufferData(const std::vector<unsigned char> &data, | |
| 6302 6474 | 
             
              std::ofstream output(binFilename.c_str(), std::ofstream::binary);
         | 
| 6303 6475 | 
             
              if (!output.is_open()) return false;
         | 
| 6304 6476 | 
             
            #endif
         | 
| 6305 | 
            -
               | 
| 6306 | 
            -
             | 
| 6477 | 
            +
              if (data.size() > 0) {
         | 
| 6478 | 
            +
                output.write(reinterpret_cast<const char *>(&data[0]),
         | 
| 6479 | 
            +
                             std::streamsize(data.size()));
         | 
| 6480 | 
            +
              } else {
         | 
| 6481 | 
            +
                // Issue #229
         | 
| 6482 | 
            +
                // size 0 will be still valid buffer data.
         | 
| 6483 | 
            +
                // write empty file.
         | 
| 6484 | 
            +
              }
         | 
| 6307 6485 | 
             
              return true;
         | 
| 6308 6486 | 
             
            }
         | 
| 6309 6487 |  | 
| @@ -6365,9 +6543,10 @@ static void SerializeExtensionMap(const ExtensionMap &extensions, json &o) { | |
| 6365 6543 | 
             
            }
         | 
| 6366 6544 |  | 
| 6367 6545 | 
             
            static void SerializeGltfAccessor(Accessor &accessor, json &o) {
         | 
| 6368 | 
            -
               | 
| 6546 | 
            +
              if (accessor.bufferView >= 0)
         | 
| 6547 | 
            +
                SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
         | 
| 6369 6548 |  | 
| 6370 | 
            -
              if (accessor.byteOffset != 0 | 
| 6549 | 
            +
              if (accessor.byteOffset != 0)
         | 
| 6371 6550 | 
             
                SerializeNumberProperty<int>("byteOffset", int(accessor.byteOffset), o);
         | 
| 6372 6551 |  | 
| 6373 6552 | 
             
              SerializeNumberProperty<int>("componentType", accessor.componentType, o);
         | 
| @@ -6416,6 +6595,8 @@ static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) { | |
| 6416 6595 | 
             
                SerializeNumberProperty("node", channel.target_node, target);
         | 
| 6417 6596 | 
             
                SerializeStringProperty("path", channel.target_path, target);
         | 
| 6418 6597 |  | 
| 6598 | 
            +
                SerializeExtensionMap(channel.target_extensions, target);
         | 
| 6599 | 
            +
             | 
| 6419 6600 | 
             
                JsonAddMember(o, "target", std::move(target));
         | 
| 6420 6601 | 
             
              }
         | 
| 6421 6602 |  | 
| @@ -6455,6 +6636,7 @@ static void SerializeGltfAnimation(Animation &animation, json &o) { | |
| 6455 6636 |  | 
| 6456 6637 | 
             
              {
         | 
| 6457 6638 | 
             
                json samplers;
         | 
| 6639 | 
            +
                JsonReserveArray(samplers, animation.samplers.size());
         | 
| 6458 6640 | 
             
                for (unsigned int i = 0; i < animation.samplers.size(); ++i) {
         | 
| 6459 6641 | 
             
                  json sampler;
         | 
| 6460 6642 | 
             
                  AnimationSampler gltfSampler = animation.samplers[i];
         | 
| @@ -6491,10 +6673,10 @@ static void SerializeGltfAsset(Asset &asset, json &o) { | |
| 6491 6673 | 
             
              SerializeExtensionMap(asset.extensions, o);
         | 
| 6492 6674 | 
             
            }
         | 
| 6493 6675 |  | 
| 6494 | 
            -
             | 
| 6495 | 
            -
             | 
| 6676 | 
            +
            static void SerializeGltfBufferBin(Buffer &buffer, json &o,
         | 
| 6677 | 
            +
                                               std::vector<unsigned char> &binBuffer) {
         | 
| 6496 6678 | 
             
              SerializeNumberProperty("byteLength", buffer.data.size(), o);
         | 
| 6497 | 
            -
              binBuffer=buffer.data;
         | 
| 6679 | 
            +
              binBuffer = buffer.data;
         | 
| 6498 6680 |  | 
| 6499 6681 | 
             
              if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
         | 
| 6500 6682 |  | 
| @@ -6561,6 +6743,7 @@ static void SerializeGltfImage(Image &image, json &o) { | |
| 6561 6743 | 
             
                SerializeStringProperty("mimeType", image.mimeType, o);
         | 
| 6562 6744 | 
             
                SerializeNumberProperty<int>("bufferView", image.bufferView, o);
         | 
| 6563 6745 | 
             
              } else {
         | 
| 6746 | 
            +
                // TODO(syoyo): dlib::urilencode?
         | 
| 6564 6747 | 
             
                SerializeStringProperty("uri", image.uri, o);
         | 
| 6565 6748 | 
             
              }
         | 
| 6566 6749 |  | 
| @@ -6677,8 +6860,8 @@ static void SerializeGltfMaterial(Material &material, json &o) { | |
| 6677 6860 | 
             
                SerializeStringProperty("alphaMode", material.alphaMode, o);
         | 
| 6678 6861 | 
             
              }
         | 
| 6679 6862 |  | 
| 6680 | 
            -
              if(material.doubleSided != false)
         | 
| 6681 | 
            -
             | 
| 6863 | 
            +
              if (material.doubleSided != false)
         | 
| 6864 | 
            +
                JsonAddMember(o, "doubleSided", json(material.doubleSided));
         | 
| 6682 6865 |  | 
| 6683 6866 | 
             
              if (material.normalTexture.index > -1) {
         | 
| 6684 6867 | 
             
                json texinfo;
         | 
| @@ -6782,7 +6965,7 @@ static void SerializeGltfMesh(Mesh &mesh, json &o) { | |
| 6782 6965 | 
             
                  JsonAddMember(primitive, "targets", std::move(targets));
         | 
| 6783 6966 | 
             
                }
         | 
| 6784 6967 |  | 
| 6785 | 
            -
                SerializeExtensionMap(gltfPrimitive.extensions,  | 
| 6968 | 
            +
                SerializeExtensionMap(gltfPrimitive.extensions, primitive);
         | 
| 6786 6969 |  | 
| 6787 6970 | 
             
                if (gltfPrimitive.extras.Type() != NULL_TYPE) {
         | 
| 6788 6971 | 
             
                  SerializeValue("extras", gltfPrimitive.extras, primitive);
         | 
| @@ -6819,7 +7002,9 @@ static void SerializeSpotLight(SpotLight &spot, json &o) { | |
| 6819 7002 | 
             
            static void SerializeGltfLight(Light &light, json &o) {
         | 
| 6820 7003 | 
             
              if (!light.name.empty()) SerializeStringProperty("name", light.name, o);
         | 
| 6821 7004 | 
             
              SerializeNumberProperty("intensity", light.intensity, o);
         | 
| 6822 | 
            -
               | 
| 7005 | 
            +
              if (light.range > 0.0) {
         | 
| 7006 | 
            +
                SerializeNumberProperty("range", light.range, o);
         | 
| 7007 | 
            +
              }
         | 
| 6823 7008 | 
             
              SerializeNumberArrayProperty("color", light.color, o);
         | 
| 6824 7009 | 
             
              SerializeStringProperty("type", light.type, o);
         | 
| 6825 7010 | 
             
              if (light.type == "spot") {
         | 
| @@ -6933,6 +7118,11 @@ static void SerializeGltfCamera(const Camera &camera, json &o) { | |
| 6933 7118 | 
             
              } else {
         | 
| 6934 7119 | 
             
                // ???
         | 
| 6935 7120 | 
             
              }
         | 
| 7121 | 
            +
             | 
| 7122 | 
            +
              if (camera.extras.Type() != NULL_TYPE) {
         | 
| 7123 | 
            +
                SerializeValue("extras", camera.extras, o);
         | 
| 7124 | 
            +
              }
         | 
| 7125 | 
            +
              SerializeExtensionMap(camera.extensions, o);
         | 
| 6936 7126 | 
             
            }
         | 
| 6937 7127 |  | 
| 6938 7128 | 
             
            static void SerializeGltfScene(Scene &scene, json &o) {
         | 
| @@ -6979,14 +7169,16 @@ static void SerializeGltfTexture(Texture &texture, json &o) { | |
| 6979 7169 | 
             
            ///
         | 
| 6980 7170 | 
             
            static void SerializeGltfModel(Model *model, json &o) {
         | 
| 6981 7171 | 
             
              // ACCESSORS
         | 
| 6982 | 
            -
               | 
| 6983 | 
            -
             | 
| 6984 | 
            -
             | 
| 6985 | 
            -
                 | 
| 6986 | 
            -
             | 
| 6987 | 
            -
             | 
| 7172 | 
            +
              if (model->accessors.size()) {
         | 
| 7173 | 
            +
                json accessors;
         | 
| 7174 | 
            +
                JsonReserveArray(accessors, model->accessors.size());
         | 
| 7175 | 
            +
                for (unsigned int i = 0; i < model->accessors.size(); ++i) {
         | 
| 7176 | 
            +
                  json accessor;
         | 
| 7177 | 
            +
                  SerializeGltfAccessor(model->accessors[i], accessor);
         | 
| 7178 | 
            +
                  JsonPushBack(accessors, std::move(accessor));
         | 
| 7179 | 
            +
                }
         | 
| 7180 | 
            +
                JsonAddMember(o, "accessors", std::move(accessors));
         | 
| 6988 7181 | 
             
              }
         | 
| 6989 | 
            -
              JsonAddMember(o, "accessors", std::move(accessors));
         | 
| 6990 7182 |  | 
| 6991 7183 | 
             
              // ANIMATIONS
         | 
| 6992 7184 | 
             
              if (model->animations.size()) {
         | 
| @@ -7009,18 +7201,15 @@ static void SerializeGltfModel(Model *model, json &o) { | |
| 7009 7201 | 
             
              JsonAddMember(o, "asset", std::move(asset));
         | 
| 7010 7202 |  | 
| 7011 7203 | 
             
              // BUFFERVIEWS
         | 
| 7012 | 
            -
               | 
| 7013 | 
            -
             | 
| 7014 | 
            -
             | 
| 7015 | 
            -
                 | 
| 7016 | 
            -
             | 
| 7017 | 
            -
             | 
| 7018 | 
            -
             | 
| 7019 | 
            -
             | 
| 7020 | 
            -
             | 
| 7021 | 
            -
              // Extensions used
         | 
| 7022 | 
            -
              if (model->extensionsUsed.size()) {
         | 
| 7023 | 
            -
                SerializeStringArrayProperty("extensionsUsed", model->extensionsUsed, o);
         | 
| 7204 | 
            +
              if (model->bufferViews.size()) {
         | 
| 7205 | 
            +
                json bufferViews;
         | 
| 7206 | 
            +
                JsonReserveArray(bufferViews, model->bufferViews.size());
         | 
| 7207 | 
            +
                for (unsigned int i = 0; i < model->bufferViews.size(); ++i) {
         | 
| 7208 | 
            +
                  json bufferView;
         | 
| 7209 | 
            +
                  SerializeGltfBufferView(model->bufferViews[i], bufferView);
         | 
| 7210 | 
            +
                  JsonPushBack(bufferViews, std::move(bufferView));
         | 
| 7211 | 
            +
                }
         | 
| 7212 | 
            +
                JsonAddMember(o, "bufferViews", std::move(bufferViews));
         | 
| 7024 7213 | 
             
              }
         | 
| 7025 7214 |  | 
| 7026 7215 | 
             
              // Extensions required
         | 
| @@ -7133,7 +7322,9 @@ static void SerializeGltfModel(Model *model, json &o) { | |
| 7133 7322 | 
             
              // EXTENSIONS
         | 
| 7134 7323 | 
             
              SerializeExtensionMap(model->extensions, o);
         | 
| 7135 7324 |  | 
| 7136 | 
            -
               | 
| 7325 | 
            +
              auto extensionsUsed = model->extensionsUsed;
         | 
| 7326 | 
            +
             | 
| 7327 | 
            +
              // LIGHTS as KHR_lights_punctual
         | 
| 7137 7328 | 
             
              if (model->lights.size()) {
         | 
| 7138 7329 | 
             
                json lights;
         | 
| 7139 7330 | 
             
                JsonReserveArray(lights, model->lights.size());
         | 
| @@ -7148,7 +7339,7 @@ static void SerializeGltfModel(Model *model, json &o) { | |
| 7148 7339 |  | 
| 7149 7340 | 
             
                {
         | 
| 7150 7341 | 
             
                  json_const_iterator it;
         | 
| 7151 | 
            -
                  if ( | 
| 7342 | 
            +
                  if (FindMember(o, "extensions", it)) {
         | 
| 7152 7343 | 
             
                    JsonAssign(ext_j, GetValue(it));
         | 
| 7153 7344 | 
             
                  }
         | 
| 7154 7345 | 
             
                }
         | 
| @@ -7156,6 +7347,24 @@ static void SerializeGltfModel(Model *model, json &o) { | |
| 7156 7347 | 
             
                JsonAddMember(ext_j, "KHR_lights_punctual", std::move(khr_lights_cmn));
         | 
| 7157 7348 |  | 
| 7158 7349 | 
             
                JsonAddMember(o, "extensions", std::move(ext_j));
         | 
| 7350 | 
            +
             | 
| 7351 | 
            +
                // Also add "KHR_lights_punctual" to `extensionsUsed`
         | 
| 7352 | 
            +
                {
         | 
| 7353 | 
            +
                  auto has_khr_lights_punctual =
         | 
| 7354 | 
            +
                      std::find_if(extensionsUsed.begin(), extensionsUsed.end(),
         | 
| 7355 | 
            +
                                   [](const std::string &s) {
         | 
| 7356 | 
            +
                                     return (s.compare("KHR_lights_punctual") == 0);
         | 
| 7357 | 
            +
                                   });
         | 
| 7358 | 
            +
             | 
| 7359 | 
            +
                  if (has_khr_lights_punctual == extensionsUsed.end()) {
         | 
| 7360 | 
            +
                    extensionsUsed.push_back("KHR_lights_punctual");
         | 
| 7361 | 
            +
                  }
         | 
| 7362 | 
            +
                }
         | 
| 7363 | 
            +
              }
         | 
| 7364 | 
            +
             | 
| 7365 | 
            +
              // Extensions used
         | 
| 7366 | 
            +
              if (model->extensionsUsed.size()) {
         | 
| 7367 | 
            +
                SerializeStringArrayProperty("extensionsUsed", extensionsUsed, o);
         | 
| 7159 7368 | 
             
              }
         | 
| 7160 7369 |  | 
| 7161 7370 | 
             
              // EXTRAS
         | 
| @@ -7175,8 +7384,10 @@ static bool WriteGltfFile(const std::string &output, | |
| 7175 7384 | 
             
            #if defined(_MSC_VER)
         | 
| 7176 7385 | 
             
              std::ofstream gltfFile(UTF8ToWchar(output).c_str());
         | 
| 7177 7386 | 
             
            #elif defined(__GLIBCXX__)
         | 
| 7178 | 
            -
              int file_descriptor = _wopen(UTF8ToWchar(output).c_str(), | 
| 7179 | 
            -
             | 
| 7387 | 
            +
              int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
         | 
| 7388 | 
            +
                                           _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
         | 
| 7389 | 
            +
              __gnu_cxx::stdio_filebuf<char> wfile_buf(
         | 
| 7390 | 
            +
                  file_descriptor, std::ios_base::out | std::ios_base::binary);
         | 
| 7180 7391 | 
             
              std::ostream gltfFile(&wfile_buf);
         | 
| 7181 7392 | 
             
              if (!wfile_buf.is_open()) return false;
         | 
| 7182 7393 | 
             
            #else
         | 
| @@ -7197,32 +7408,31 @@ static void WriteBinaryGltfStream(std::ostream &stream, | |
| 7197 7408 | 
             
              const int version = 2;
         | 
| 7198 7409 |  | 
| 7199 7410 | 
             
              // https://stackoverflow.com/questions/3407012/c-rounding-up-to-the-nearest-multiple-of-a-number
         | 
| 7200 | 
            -
              auto roundUp = [](uint32_t numToRound, uint32_t multiple)
         | 
| 7201 | 
            -
             | 
| 7202 | 
            -
                  if (multiple == 0)
         | 
| 7203 | 
            -
                      return numToRound;
         | 
| 7411 | 
            +
              auto roundUp = [](uint32_t numToRound, uint32_t multiple) {
         | 
| 7412 | 
            +
                if (multiple == 0) return numToRound;
         | 
| 7204 7413 |  | 
| 7205 | 
            -
             | 
| 7206 | 
            -
             | 
| 7207 | 
            -
                      return numToRound;
         | 
| 7414 | 
            +
                uint32_t remainder = numToRound % multiple;
         | 
| 7415 | 
            +
                if (remainder == 0) return numToRound;
         | 
| 7208 7416 |  | 
| 7209 | 
            -
             | 
| 7417 | 
            +
                return numToRound + multiple - remainder;
         | 
| 7210 7418 | 
             
              };
         | 
| 7211 7419 |  | 
| 7212 | 
            -
              const uint32_t padding_size = | 
| 7420 | 
            +
              const uint32_t padding_size =
         | 
| 7421 | 
            +
                  roundUp(uint32_t(content.size()), 4) - uint32_t(content.size());
         | 
| 7213 7422 |  | 
| 7214 7423 | 
             
              // 12 bytes for header, JSON content length, 8 bytes for JSON chunk info.
         | 
| 7215 7424 | 
             
              // Chunk data must be located at 4-byte boundary.
         | 
| 7216 | 
            -
              const  | 
| 7217 | 
            -
                   | 
| 7425 | 
            +
              const uint32_t length =
         | 
| 7426 | 
            +
                  12 + 8 + roundUp(uint32_t(content.size()), 4) +
         | 
| 7427 | 
            +
                  (binBuffer.size() ? (8 + roundUp(uint32_t(binBuffer.size()), 4)) : 0);
         | 
| 7218 7428 |  | 
| 7219 7429 | 
             
              stream.write(header.c_str(), std::streamsize(header.size()));
         | 
| 7220 7430 | 
             
              stream.write(reinterpret_cast<const char *>(&version), sizeof(version));
         | 
| 7221 7431 | 
             
              stream.write(reinterpret_cast<const char *>(&length), sizeof(length));
         | 
| 7222 7432 |  | 
| 7223 7433 | 
             
              // JSON chunk info, then JSON data
         | 
| 7224 | 
            -
              const  | 
| 7225 | 
            -
              const  | 
| 7434 | 
            +
              const uint32_t model_length = uint32_t(content.size()) + padding_size;
         | 
| 7435 | 
            +
              const uint32_t model_format = 0x4E4F534A;
         | 
| 7226 7436 | 
             
              stream.write(reinterpret_cast<const char *>(&model_length),
         | 
| 7227 7437 | 
             
                           sizeof(model_length));
         | 
| 7228 7438 | 
             
              stream.write(reinterpret_cast<const char *>(&model_format),
         | 
| @@ -7234,20 +7444,24 @@ static void WriteBinaryGltfStream(std::ostream &stream, | |
| 7234 7444 | 
             
                const std::string padding = std::string(size_t(padding_size), ' ');
         | 
| 7235 7445 | 
             
                stream.write(padding.c_str(), std::streamsize(padding.size()));
         | 
| 7236 7446 | 
             
              }
         | 
| 7237 | 
            -
              if (binBuffer.size() > 0){
         | 
| 7238 | 
            -
                const uint32_t bin_padding_size = | 
| 7447 | 
            +
              if (binBuffer.size() > 0) {
         | 
| 7448 | 
            +
                const uint32_t bin_padding_size =
         | 
| 7449 | 
            +
                    roundUp(uint32_t(binBuffer.size()), 4) - uint32_t(binBuffer.size());
         | 
| 7239 7450 | 
             
                // BIN chunk info, then BIN data
         | 
| 7240 | 
            -
                const  | 
| 7241 | 
            -
                const  | 
| 7451 | 
            +
                const uint32_t bin_length = uint32_t(binBuffer.size()) + bin_padding_size;
         | 
| 7452 | 
            +
                const uint32_t bin_format = 0x004e4942;
         | 
| 7242 7453 | 
             
                stream.write(reinterpret_cast<const char *>(&bin_length),
         | 
| 7243 | 
            -
             | 
| 7454 | 
            +
                             sizeof(bin_length));
         | 
| 7244 7455 | 
             
                stream.write(reinterpret_cast<const char *>(&bin_format),
         | 
| 7245 | 
            -
             | 
| 7246 | 
            -
                stream.write( | 
| 7456 | 
            +
                             sizeof(bin_format));
         | 
| 7457 | 
            +
                stream.write(reinterpret_cast<const char *>(binBuffer.data()),
         | 
| 7458 | 
            +
                             std::streamsize(binBuffer.size()));
         | 
| 7247 7459 | 
             
                // Chunksize must be multiplies of 4, so pad with zeroes
         | 
| 7248 7460 | 
             
                if (bin_padding_size > 0) {
         | 
| 7249 | 
            -
                  const std::vector<unsigned char> padding = | 
| 7250 | 
            -
             | 
| 7461 | 
            +
                  const std::vector<unsigned char> padding =
         | 
| 7462 | 
            +
                      std::vector<unsigned char>(size_t(bin_padding_size), 0);
         | 
| 7463 | 
            +
                  stream.write(reinterpret_cast<const char *>(padding.data()),
         | 
| 7464 | 
            +
                               std::streamsize(padding.size()));
         | 
| 7251 7465 | 
             
                }
         | 
| 7252 7466 | 
             
              }
         | 
| 7253 7467 | 
             
            }
         | 
| @@ -7259,8 +7473,10 @@ static void WriteBinaryGltfFile(const std::string &output, | |
| 7259 7473 | 
             
            #if defined(_MSC_VER)
         | 
| 7260 7474 | 
             
              std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary);
         | 
| 7261 7475 | 
             
            #elif defined(__GLIBCXX__)
         | 
| 7262 | 
            -
              int file_descriptor = _wopen(UTF8ToWchar(output).c_str(), | 
| 7263 | 
            -
             | 
| 7476 | 
            +
              int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
         | 
| 7477 | 
            +
                                           _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
         | 
| 7478 | 
            +
              __gnu_cxx::stdio_filebuf<char> wfile_buf(
         | 
| 7479 | 
            +
                  file_descriptor, std::ios_base::out | std::ios_base::binary);
         | 
| 7264 7480 | 
             
              std::ostream gltfFile(&wfile_buf);
         | 
| 7265 7481 | 
             
            #else
         | 
| 7266 7482 | 
             
              std::ofstream gltfFile(output.c_str(), std::ios::binary);
         | 
| @@ -7268,7 +7484,7 @@ static void WriteBinaryGltfFile(const std::string &output, | |
| 7268 7484 | 
             
            #else
         | 
| 7269 7485 | 
             
              std::ofstream gltfFile(output.c_str(), std::ios::binary);
         | 
| 7270 7486 | 
             
            #endif
         | 
| 7271 | 
            -
              WriteBinaryGltfStream(gltfFile, content,binBuffer);
         | 
| 7487 | 
            +
              WriteBinaryGltfStream(gltfFile, content, binBuffer);
         | 
| 7272 7488 | 
             
            }
         | 
| 7273 7489 |  | 
| 7274 7490 | 
             
            bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
         | 
| @@ -7280,20 +7496,21 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream, | |
| 7280 7496 | 
             
              SerializeGltfModel(model, output);
         | 
| 7281 7497 |  | 
| 7282 7498 | 
             
              // BUFFERS
         | 
| 7283 | 
            -
              std::vector<std::string> usedUris;
         | 
| 7284 7499 | 
             
              std::vector<unsigned char> binBuffer;
         | 
| 7285 | 
            -
               | 
| 7286 | 
            -
             | 
| 7287 | 
            -
             | 
| 7288 | 
            -
                 | 
| 7289 | 
            -
             | 
| 7290 | 
            -
                   | 
| 7291 | 
            -
             | 
| 7292 | 
            -
                   | 
| 7500 | 
            +
              if (model->buffers.size()) {
         | 
| 7501 | 
            +
                json buffers;
         | 
| 7502 | 
            +
                JsonReserveArray(buffers, model->buffers.size());
         | 
| 7503 | 
            +
                for (unsigned int i = 0; i < model->buffers.size(); ++i) {
         | 
| 7504 | 
            +
                  json buffer;
         | 
| 7505 | 
            +
                  if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
         | 
| 7506 | 
            +
                    SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
         | 
| 7507 | 
            +
                  } else {
         | 
| 7508 | 
            +
                    SerializeGltfBuffer(model->buffers[i], buffer);
         | 
| 7509 | 
            +
                  }
         | 
| 7510 | 
            +
                  JsonPushBack(buffers, std::move(buffer));
         | 
| 7293 7511 | 
             
                }
         | 
| 7294 | 
            -
                 | 
| 7512 | 
            +
                JsonAddMember(output, "buffers", std::move(buffers));
         | 
| 7295 7513 | 
             
              }
         | 
| 7296 | 
            -
              JsonAddMember(output, "buffers", std::move(buffers));
         | 
| 7297 7514 |  | 
| 7298 7515 | 
             
              // IMAGES
         | 
| 7299 7516 | 
             
              if (model->images.size()) {
         | 
| @@ -7303,9 +7520,9 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream, | |
| 7303 7520 | 
             
                  json image;
         | 
| 7304 7521 |  | 
| 7305 7522 | 
             
                  std::string dummystring = "";
         | 
| 7306 | 
            -
                  // UpdateImageObject need baseDir but only uses it if  | 
| 7307 | 
            -
                  //  | 
| 7308 | 
            -
                  //  | 
| 7523 | 
            +
                  // UpdateImageObject need baseDir but only uses it if embeddedImages is
         | 
| 7524 | 
            +
                  // enabled, since we won't write separate images when writing to a stream
         | 
| 7525 | 
            +
                  // we
         | 
| 7309 7526 | 
             
                  UpdateImageObject(model->images[i], dummystring, int(i), false,
         | 
| 7310 7527 | 
             
                                    &this->WriteImageData, this->write_image_user_data_);
         | 
| 7311 7528 | 
             
                  SerializeGltfImage(model->images[i], image);
         | 
| @@ -7315,7 +7532,7 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream, | |
| 7315 7532 | 
             
              }
         | 
| 7316 7533 |  | 
| 7317 7534 | 
             
              if (writeBinary) {
         | 
| 7318 | 
            -
                WriteBinaryGltfStream(stream, JsonToString(output),binBuffer);
         | 
| 7535 | 
            +
                WriteBinaryGltfStream(stream, JsonToString(output), binBuffer);
         | 
| 7319 7536 | 
             
              } else {
         | 
| 7320 7537 | 
             
                WriteGltfStream(stream, JsonToString(output, prettyPrint ? 2 : -1));
         | 
| 7321 7538 | 
             
              }
         | 
| @@ -7347,44 +7564,47 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename, | |
| 7347 7564 | 
             
              // BUFFERS
         | 
| 7348 7565 | 
             
              std::vector<std::string> usedUris;
         | 
| 7349 7566 | 
             
              std::vector<unsigned char> binBuffer;
         | 
| 7350 | 
            -
               | 
| 7351 | 
            -
             | 
| 7352 | 
            -
             | 
| 7353 | 
            -
                 | 
| 7354 | 
            -
             | 
| 7355 | 
            -
                   | 
| 7356 | 
            -
             | 
| 7357 | 
            -
                   | 
| 7358 | 
            -
             | 
| 7359 | 
            -
                  std::string binSavePath;
         | 
| 7360 | 
            -
                  std::string binUri;
         | 
| 7361 | 
            -
                  if (!model->buffers[i].uri.empty() && !IsDataURI(model->buffers[i].uri)) {
         | 
| 7362 | 
            -
                    binUri = model->buffers[i].uri;
         | 
| 7567 | 
            +
              if (model->buffers.size()) {
         | 
| 7568 | 
            +
                json buffers;
         | 
| 7569 | 
            +
                JsonReserveArray(buffers, model->buffers.size());
         | 
| 7570 | 
            +
                for (unsigned int i = 0; i < model->buffers.size(); ++i) {
         | 
| 7571 | 
            +
                  json buffer;
         | 
| 7572 | 
            +
                  if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
         | 
| 7573 | 
            +
                    SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
         | 
| 7574 | 
            +
                  } else if (embedBuffers) {
         | 
| 7575 | 
            +
                    SerializeGltfBuffer(model->buffers[i], buffer);
         | 
| 7363 7576 | 
             
                  } else {
         | 
| 7364 | 
            -
                     | 
| 7365 | 
            -
                     | 
| 7366 | 
            -
                     | 
| 7367 | 
            -
             | 
| 7368 | 
            -
                       | 
| 7369 | 
            -
             | 
| 7370 | 
            -
             | 
| 7371 | 
            -
             | 
| 7372 | 
            -
             | 
| 7373 | 
            -
             | 
| 7374 | 
            -
                         | 
| 7577 | 
            +
                    std::string binSavePath;
         | 
| 7578 | 
            +
                    std::string binUri;
         | 
| 7579 | 
            +
                    if (!model->buffers[i].uri.empty() &&
         | 
| 7580 | 
            +
                        !IsDataURI(model->buffers[i].uri)) {
         | 
| 7581 | 
            +
                      binUri = model->buffers[i].uri;
         | 
| 7582 | 
            +
                    } else {
         | 
| 7583 | 
            +
                      binUri = defaultBinFilename + defaultBinFileExt;
         | 
| 7584 | 
            +
                      bool inUse = true;
         | 
| 7585 | 
            +
                      int numUsed = 0;
         | 
| 7586 | 
            +
                      while (inUse) {
         | 
| 7587 | 
            +
                        inUse = false;
         | 
| 7588 | 
            +
                        for (const std::string &usedName : usedUris) {
         | 
| 7589 | 
            +
                          if (binUri.compare(usedName) != 0) continue;
         | 
| 7590 | 
            +
                          inUse = true;
         | 
| 7591 | 
            +
                          binUri = defaultBinFilename + std::to_string(numUsed++) +
         | 
| 7592 | 
            +
                                   defaultBinFileExt;
         | 
| 7593 | 
            +
                          break;
         | 
| 7594 | 
            +
                        }
         | 
| 7375 7595 | 
             
                      }
         | 
| 7376 7596 | 
             
                    }
         | 
| 7597 | 
            +
                    usedUris.push_back(binUri);
         | 
| 7598 | 
            +
                    binSavePath = JoinPath(baseDir, binUri);
         | 
| 7599 | 
            +
                    if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
         | 
| 7600 | 
            +
                                             binUri)) {
         | 
| 7601 | 
            +
                      return false;
         | 
| 7602 | 
            +
                    }
         | 
| 7377 7603 | 
             
                  }
         | 
| 7378 | 
            -
                   | 
| 7379 | 
            -
                  binSavePath = JoinPath(baseDir, binUri);
         | 
| 7380 | 
            -
                  if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
         | 
| 7381 | 
            -
                                           binUri)) {
         | 
| 7382 | 
            -
                    return false;
         | 
| 7383 | 
            -
                  }
         | 
| 7604 | 
            +
                  JsonPushBack(buffers, std::move(buffer));
         | 
| 7384 7605 | 
             
                }
         | 
| 7385 | 
            -
                 | 
| 7606 | 
            +
                JsonAddMember(output, "buffers", std::move(buffers));
         | 
| 7386 7607 | 
             
              }
         | 
| 7387 | 
            -
              JsonAddMember(output, "buffers", std::move(buffers));
         | 
| 7388 7608 |  | 
| 7389 7609 | 
             
              // IMAGES
         | 
| 7390 7610 | 
             
              if (model->images.size()) {
         | 
| @@ -7402,7 +7622,7 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename, | |
| 7402 7622 | 
             
              }
         | 
| 7403 7623 |  | 
| 7404 7624 | 
             
              if (writeBinary) {
         | 
| 7405 | 
            -
                WriteBinaryGltfFile(filename, JsonToString(output),binBuffer);
         | 
| 7625 | 
            +
                WriteBinaryGltfFile(filename, JsonToString(output), binBuffer);
         | 
| 7406 7626 | 
             
              } else {
         | 
| 7407 7627 | 
             
                WriteGltfFile(filename, JsonToString(output, (prettyPrint ? 2 : -1)));
         | 
| 7408 7628 | 
             
              }
         |