tiny_gltf 1.0.1 → 2.5.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 +11 -9
- data/ext/tiny_gltf/json.hpp +19421 -7390
- data/ext/tiny_gltf/rb_tiny_gltf.h +1 -2
- data/ext/tiny_gltf/rb_tiny_gltf_mesh.cpp +1 -1
- data/ext/tiny_gltf/rb_tiny_gltf_node.cpp +14 -14
- data/ext/tiny_gltf/rb_tiny_gltf_sampler.cpp +3 -1
- data/ext/tiny_gltf/tiny_gltf.h +573 -244
- data/lib/tiny_gltf/version.rb +1 -1
- data/tiny_gltf.gemspec +1 -1
- metadata +9 -9
    
        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 - Present 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,10 @@ | |
| 26 26 | 
             
            // THE SOFTWARE.
         | 
| 27 27 |  | 
| 28 28 | 
             
            // Version:
         | 
| 29 | 
            +
            //  - v2.5.0 Add SetPreserveImageChannels() option to load image data as is.
         | 
| 30 | 
            +
            //  - v2.4.3 Fix null object output when when material has all default
         | 
| 31 | 
            +
            //  parameters.
         | 
| 32 | 
            +
            //  - v2.4.2 Decode percent-encoded URI.
         | 
| 29 33 | 
             
            //  - v2.4.1 Fix some glTF object class does not have `extensions` and/or
         | 
| 30 34 | 
             
            //  `extras` property.
         | 
| 31 35 | 
             
            //  - v2.4.0 Experimental RapidJSON and C++14 support(Thanks to @jrkoone).
         | 
| @@ -51,6 +55,7 @@ | |
| 51 55 |  | 
| 52 56 | 
             
            #include <array>
         | 
| 53 57 | 
             
            #include <cassert>
         | 
| 58 | 
            +
            #include <cmath>  // std::fabs
         | 
| 54 59 | 
             
            #include <cstdint>
         | 
| 55 60 | 
             
            #include <cstdlib>
         | 
| 56 61 | 
             
            #include <cstring>
         | 
| @@ -103,7 +108,7 @@ namespace tinygltf { | |
| 103 108 | 
             
            #define TINYGLTF_COMPONENT_TYPE_INT (5124)
         | 
| 104 109 | 
             
            #define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125)
         | 
| 105 110 | 
             
            #define TINYGLTF_COMPONENT_TYPE_FLOAT (5126)
         | 
| 106 | 
            -
            #define TINYGLTF_COMPONENT_TYPE_DOUBLE (5130)
         | 
| 111 | 
            +
            #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
         | 
| 107 112 |  | 
| 108 113 | 
             
            #define TINYGLTF_TEXTURE_FILTER_NEAREST (9728)
         | 
| 109 114 | 
             
            #define TINYGLTF_TEXTURE_FILTER_LINEAR (9729)
         | 
| @@ -186,14 +191,14 @@ AAssetManager *asset_manager = nullptr; | |
| 186 191 | 
             
            #endif
         | 
| 187 192 |  | 
| 188 193 | 
             
            typedef enum {
         | 
| 189 | 
            -
              NULL_TYPE | 
| 190 | 
            -
              REAL_TYPE | 
| 191 | 
            -
              INT_TYPE | 
| 192 | 
            -
              BOOL_TYPE | 
| 193 | 
            -
              STRING_TYPE | 
| 194 | 
            -
              ARRAY_TYPE | 
| 195 | 
            -
              BINARY_TYPE | 
| 196 | 
            -
              OBJECT_TYPE | 
| 194 | 
            +
              NULL_TYPE,
         | 
| 195 | 
            +
              REAL_TYPE,
         | 
| 196 | 
            +
              INT_TYPE,
         | 
| 197 | 
            +
              BOOL_TYPE,
         | 
| 198 | 
            +
              STRING_TYPE,
         | 
| 199 | 
            +
              ARRAY_TYPE,
         | 
| 200 | 
            +
              BINARY_TYPE,
         | 
| 201 | 
            +
              OBJECT_TYPE
         | 
| 197 202 | 
             
            } Type;
         | 
| 198 203 |  | 
| 199 204 | 
             
            static inline int32_t GetComponentSizeInBytes(uint32_t componentType) {
         | 
| @@ -248,7 +253,6 @@ bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type, | |
| 248 253 | 
             
            #ifdef __clang__
         | 
| 249 254 | 
             
            #pragma clang diagnostic push
         | 
| 250 255 | 
             
            // Suppress warning for : static Value null_value
         | 
| 251 | 
            -
            // https://stackoverflow.com/questions/15708411/how-to-deal-with-global-constructor-warning-in-clang
         | 
| 252 256 | 
             
            #pragma clang diagnostic ignored "-Wexit-time-destructors"
         | 
| 253 257 | 
             
            #pragma clang diagnostic ignored "-Wpadded"
         | 
| 254 258 | 
             
            #endif
         | 
| @@ -293,7 +297,7 @@ class Value { | |
| 293 297 |  | 
| 294 298 | 
             
              DEFAULT_METHODS(Value)
         | 
| 295 299 |  | 
| 296 | 
            -
              char Type() const { return static_cast< | 
| 300 | 
            +
              char Type() const { return static_cast<char>(type_); }
         | 
| 297 301 |  | 
| 298 302 | 
             
              bool IsBool() const { return (type_ == BOOL_TYPE); }
         | 
| 299 303 |  | 
| @@ -321,7 +325,8 @@ class Value { | |
| 321 325 | 
             
              }
         | 
| 322 326 |  | 
| 323 327 | 
             
              // Use this function if you want to have number value as int.
         | 
| 324 | 
            -
               | 
| 328 | 
            +
              // TODO(syoyo): Support int value larger than 32 bits
         | 
| 329 | 
            +
              int GetNumberAsInt() const {
         | 
| 325 330 | 
             
                if (type_ == REAL_TYPE) {
         | 
| 326 331 | 
             
                  return int(real_value_);
         | 
| 327 332 | 
             
                } else {
         | 
| @@ -526,10 +531,12 @@ struct AnimationChannel { | |
| 526 531 | 
             
                                        // "weights"]
         | 
| 527 532 | 
             
              Value extras;
         | 
| 528 533 | 
             
              ExtensionMap extensions;
         | 
| 534 | 
            +
              ExtensionMap target_extensions;
         | 
| 529 535 |  | 
| 530 536 | 
             
              // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
         | 
| 531 537 | 
             
              std::string extras_json_string;
         | 
| 532 538 | 
             
              std::string extensions_json_string;
         | 
| 539 | 
            +
              std::string target_extensions_json_string;
         | 
| 533 540 |  | 
| 534 541 | 
             
              AnimationChannel() : sampler(-1), target_node(-1) {}
         | 
| 535 542 | 
             
              DEFAULT_METHODS(AnimationChannel)
         | 
| @@ -596,7 +603,7 @@ struct Sampler { | |
| 596 603 | 
             
              // `magFilter`. Set -1 in TinyGLTF(issue #186)
         | 
| 597 604 | 
             
              int minFilter =
         | 
| 598 605 | 
             
                  -1;  // optional. -1 = no filter defined. ["NEAREST", "LINEAR",
         | 
| 599 | 
            -
                       // " | 
| 606 | 
            +
                       // "NEAREST_MIPMAP_NEAREST", "LINEAR_MIPMAP_NEAREST",
         | 
| 600 607 | 
             
                       // "NEAREST_MIPMAP_LINEAR", "LINEAR_MIPMAP_LINEAR"]
         | 
| 601 608 | 
             
              int magFilter =
         | 
| 602 609 | 
             
                  -1;  // optional. -1 = no filter defined. ["NEAREST", "LINEAR"]
         | 
| @@ -606,7 +613,7 @@ struct Sampler { | |
| 606 613 | 
             
              int wrapT =
         | 
| 607 614 | 
             
                  TINYGLTF_TEXTURE_WRAP_REPEAT;  // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
         | 
| 608 615 | 
             
                                                 // "REPEAT"], default "REPEAT"
         | 
| 609 | 
            -
              int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT;  // TinyGLTF extension
         | 
| 616 | 
            +
              //int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT;  // TinyGLTF extension. currently not used.
         | 
| 610 617 |  | 
| 611 618 | 
             
              Value extras;
         | 
| 612 619 | 
             
              ExtensionMap extensions;
         | 
| @@ -619,8 +626,7 @@ struct Sampler { | |
| 619 626 | 
             
                  : minFilter(-1),
         | 
| 620 627 | 
             
                    magFilter(-1),
         | 
| 621 628 | 
             
                    wrapS(TINYGLTF_TEXTURE_WRAP_REPEAT),
         | 
| 622 | 
            -
                    wrapT(TINYGLTF_TEXTURE_WRAP_REPEAT) | 
| 623 | 
            -
                    wrapR(TINYGLTF_TEXTURE_WRAP_REPEAT) {}
         | 
| 629 | 
            +
                    wrapT(TINYGLTF_TEXTURE_WRAP_REPEAT) {}
         | 
| 624 630 | 
             
              DEFAULT_METHODS(Sampler)
         | 
| 625 631 | 
             
              bool operator==(const Sampler &) const;
         | 
| 626 632 | 
             
            };
         | 
| @@ -637,7 +643,8 @@ struct Image { | |
| 637 643 | 
             
              int bufferView;        // (required if no uri)
         | 
| 638 644 | 
             
              std::string mimeType;  // (required if no uri) ["image/jpeg", "image/png",
         | 
| 639 645 | 
             
                                     // "image/bmp", "image/gif"]
         | 
| 640 | 
            -
              std::string uri;       // (required if no mimeType)
         | 
| 646 | 
            +
              std::string uri;       // (required if no mimeType) uri is not decoded(e.g.
         | 
| 647 | 
            +
                                     // whitespace may be represented as %20)
         | 
| 641 648 | 
             
              Value extras;
         | 
| 642 649 | 
             
              ExtensionMap extensions;
         | 
| 643 650 |  | 
| @@ -658,6 +665,8 @@ struct Image { | |
| 658 665 | 
             
                width = -1;
         | 
| 659 666 | 
             
                height = -1;
         | 
| 660 667 | 
             
                component = -1;
         | 
| 668 | 
            +
                bits = -1;
         | 
| 669 | 
            +
                pixel_type = -1;
         | 
| 661 670 | 
             
              }
         | 
| 662 671 | 
             
              DEFAULT_METHODS(Image)
         | 
| 663 672 |  | 
| @@ -797,12 +806,13 @@ struct Material { | |
| 797 806 |  | 
| 798 807 | 
             
            struct BufferView {
         | 
| 799 808 | 
             
              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; | 
| 809 | 
            +
              int buffer{-1};        // Required
         | 
| 810 | 
            +
              size_t byteOffset{0};  // minimum 0, default 0
         | 
| 811 | 
            +
              size_t byteLength{0};  // required, minimum 1. 0 = invalid
         | 
| 812 | 
            +
              size_t byteStride{0};  // minimum 4, maximum 252 (multiple of 4), default 0 =
         | 
| 813 | 
            +
                                     // understood to be tightly packed
         | 
| 814 | 
            +
              int target{0};  // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"] for vertex indices
         | 
| 815 | 
            +
                              // or atttribs. Could be 0 for other data
         | 
| 806 816 | 
             
              Value extras;
         | 
| 807 817 | 
             
              ExtensionMap extensions;
         | 
| 808 818 |  | 
| @@ -810,9 +820,15 @@ struct BufferView { | |
| 810 820 | 
             
              std::string extras_json_string;
         | 
| 811 821 | 
             
              std::string extensions_json_string;
         | 
| 812 822 |  | 
| 813 | 
            -
              bool dracoDecoded;  // Flag indicating this has been draco decoded
         | 
| 823 | 
            +
              bool dracoDecoded{false};  // Flag indicating this has been draco decoded
         | 
| 814 824 |  | 
| 815 | 
            -
              BufferView() | 
| 825 | 
            +
              BufferView()
         | 
| 826 | 
            +
                  : buffer(-1),
         | 
| 827 | 
            +
                    byteOffset(0),
         | 
| 828 | 
            +
                    byteLength(0),
         | 
| 829 | 
            +
                    byteStride(0),
         | 
| 830 | 
            +
                    target(0),
         | 
| 831 | 
            +
                    dracoDecoded(false) {}
         | 
| 816 832 | 
             
              DEFAULT_METHODS(BufferView)
         | 
| 817 833 | 
             
              bool operator==(const BufferView &) const;
         | 
| 818 834 | 
             
            };
         | 
| @@ -833,8 +849,10 @@ struct Accessor { | |
| 833 849 | 
             
              std::string extras_json_string;
         | 
| 834 850 | 
             
              std::string extensions_json_string;
         | 
| 835 851 |  | 
| 836 | 
            -
              std::vector<double> | 
| 837 | 
            -
             | 
| 852 | 
            +
              std::vector<double>
         | 
| 853 | 
            +
                  minValues;  // optional. integer value is promoted to double
         | 
| 854 | 
            +
              std::vector<double>
         | 
| 855 | 
            +
                  maxValues;  // optional. integer value is promoted to double
         | 
| 838 856 |  | 
| 839 857 | 
             
              struct {
         | 
| 840 858 | 
             
                int count;
         | 
| @@ -887,13 +905,13 @@ struct Accessor { | |
| 887 905 | 
             
                // unreachable return 0;
         | 
| 888 906 | 
             
              }
         | 
| 889 907 |  | 
| 890 | 
            -
              Accessor() | 
| 891 | 
            -
             | 
| 892 | 
            -
             | 
| 893 | 
            -
             | 
| 894 | 
            -
             | 
| 895 | 
            -
             | 
| 896 | 
            -
             | 
| 908 | 
            +
              Accessor()
         | 
| 909 | 
            +
                  : bufferView(-1),
         | 
| 910 | 
            +
                    byteOffset(0),
         | 
| 911 | 
            +
                    normalized(false),
         | 
| 912 | 
            +
                    componentType(-1),
         | 
| 913 | 
            +
                    count(0),
         | 
| 914 | 
            +
                    type(-1) {
         | 
| 897 915 | 
             
                sparse.isSparse = false;
         | 
| 898 916 | 
             
              }
         | 
| 899 917 | 
             
              DEFAULT_METHODS(Accessor)
         | 
| @@ -983,6 +1001,7 @@ struct Primitive { | |
| 983 1001 | 
             
              Primitive() {
         | 
| 984 1002 | 
             
                material = -1;
         | 
| 985 1003 | 
             
                indices = -1;
         | 
| 1004 | 
            +
                mode = -1;
         | 
| 986 1005 | 
             
              }
         | 
| 987 1006 | 
             
              DEFAULT_METHODS(Primitive)
         | 
| 988 1007 | 
             
              bool operator==(const Primitive &) const;
         | 
| @@ -1037,6 +1056,7 @@ struct Buffer { | |
| 1037 1056 | 
             
              std::vector<unsigned char> data;
         | 
| 1038 1057 | 
             
              std::string
         | 
| 1039 1058 | 
             
                  uri;  // considered as required here but not in the spec (need to clarify)
         | 
| 1059 | 
            +
                        // uri is not decoded(e.g. whitespace may be represented as %20)
         | 
| 1040 1060 | 
             
              Value extras;
         | 
| 1041 1061 | 
             
              ExtensionMap extensions;
         | 
| 1042 1062 |  | 
| @@ -1050,7 +1070,7 @@ struct Buffer { | |
| 1050 1070 | 
             
            };
         | 
| 1051 1071 |  | 
| 1052 1072 | 
             
            struct Asset {
         | 
| 1053 | 
            -
              std::string version;  // required
         | 
| 1073 | 
            +
              std::string version = "2.0";  // required
         | 
| 1054 1074 | 
             
              std::string generator;
         | 
| 1055 1075 | 
             
              std::string minVersion;
         | 
| 1056 1076 | 
             
              std::string copyright;
         | 
| @@ -1101,9 +1121,9 @@ struct SpotLight { | |
| 1101 1121 | 
             
            struct Light {
         | 
| 1102 1122 | 
             
              std::string name;
         | 
| 1103 1123 | 
             
              std::vector<double> color;
         | 
| 1104 | 
            -
              double intensity;
         | 
| 1124 | 
            +
              double intensity{1.0};
         | 
| 1105 1125 | 
             
              std::string type;
         | 
| 1106 | 
            -
              double range;
         | 
| 1126 | 
            +
              double range{0.0};  // 0.0 = inifinite
         | 
| 1107 1127 | 
             
              SpotLight spot;
         | 
| 1108 1128 |  | 
| 1109 1129 | 
             
              Light() : intensity(1.0), range(0.0) {}
         | 
| @@ -1141,7 +1161,7 @@ class Model { | |
| 1141 1161 | 
             
              std::vector<Scene> scenes;
         | 
| 1142 1162 | 
             
              std::vector<Light> lights;
         | 
| 1143 1163 |  | 
| 1144 | 
            -
              int defaultScene;
         | 
| 1164 | 
            +
              int defaultScene = -1;
         | 
| 1145 1165 | 
             
              std::vector<std::string> extensionsUsed;
         | 
| 1146 1166 | 
             
              std::vector<std::string> extensionsRequired;
         | 
| 1147 1167 |  | 
| @@ -1172,7 +1192,8 @@ enum SectionCheck { | |
| 1172 1192 | 
             
            ///
         | 
| 1173 1193 | 
             
            typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *,
         | 
| 1174 1194 | 
             
                                                  std::string *, int, int,
         | 
| 1175 | 
            -
                                                  const unsigned char *, int, | 
| 1195 | 
            +
                                                  const unsigned char *, int,
         | 
| 1196 | 
            +
                                                  void *user_pointer);
         | 
| 1176 1197 |  | 
| 1177 1198 | 
             
            ///
         | 
| 1178 1199 | 
             
            /// WriteImageDataFunction type. Signature for custom image writing callbacks.
         | 
| @@ -1235,7 +1256,14 @@ struct FsCallbacks { | |
| 1235 1256 |  | 
| 1236 1257 | 
             
            bool FileExists(const std::string &abs_filename, void *);
         | 
| 1237 1258 |  | 
| 1238 | 
            -
             | 
| 1259 | 
            +
            ///
         | 
| 1260 | 
            +
            /// Expand file path(e.g. `~` to home directory on posix, `%APPDATA%` to
         | 
| 1261 | 
            +
            /// `C:\\Users\\tinygltf\\AppData`)
         | 
| 1262 | 
            +
            ///
         | 
| 1263 | 
            +
            /// @param[in] filepath File path string. Assume UTF-8
         | 
| 1264 | 
            +
            /// @param[in] userdata User data. Set to `nullptr` if you don't need it.
         | 
| 1265 | 
            +
            ///
         | 
| 1266 | 
            +
            std::string ExpandFilePath(const std::string &filepath, void *userdata);
         | 
| 1239 1267 |  | 
| 1240 1268 | 
             
            bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
         | 
| 1241 1269 | 
             
                               const std::string &filepath, void *);
         | 
| @@ -1321,6 +1349,11 @@ class TinyGLTF { | |
| 1321 1349 | 
             
              ///
         | 
| 1322 1350 | 
             
              void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data);
         | 
| 1323 1351 |  | 
| 1352 | 
            +
              ///
         | 
| 1353 | 
            +
              /// Unset(remove) callback of loading image data
         | 
| 1354 | 
            +
              ///
         | 
| 1355 | 
            +
              void RemoveImageLoader();
         | 
| 1356 | 
            +
             | 
| 1324 1357 | 
             
              ///
         | 
| 1325 1358 | 
             
              /// Set callback to use for writing image data
         | 
| 1326 1359 | 
             
              ///
         | 
| @@ -1359,6 +1392,16 @@ class TinyGLTF { | |
| 1359 1392 | 
             
                return store_original_json_for_extras_and_extensions_;
         | 
| 1360 1393 | 
             
              }
         | 
| 1361 1394 |  | 
| 1395 | 
            +
              ///
         | 
| 1396 | 
            +
              /// Specify whether preserve image channales when loading images or not.
         | 
| 1397 | 
            +
              /// (Not effective when the user suppy their own LoadImageData callbacks)
         | 
| 1398 | 
            +
              ///
         | 
| 1399 | 
            +
              void SetPreserveImageChannels(bool onoff) {
         | 
| 1400 | 
            +
                preserve_image_channels_ = onoff;
         | 
| 1401 | 
            +
              }
         | 
| 1402 | 
            +
             | 
| 1403 | 
            +
              bool GetPreserveImageChannels() const { return preserve_image_channels_; }
         | 
| 1404 | 
            +
             | 
| 1362 1405 | 
             
             private:
         | 
| 1363 1406 | 
             
              ///
         | 
| 1364 1407 | 
             
              /// Loads glTF asset from string(memory).
         | 
| @@ -1378,6 +1421,9 @@ class TinyGLTF { | |
| 1378 1421 |  | 
| 1379 1422 | 
             
              bool store_original_json_for_extras_and_extensions_ = false;
         | 
| 1380 1423 |  | 
| 1424 | 
            +
              bool preserve_image_channels_ = false;  /// Default false(expand channels to
         | 
| 1425 | 
            +
                                                      /// RGBA) for backward compatibility.
         | 
| 1426 | 
            +
             | 
| 1381 1427 | 
             
              FsCallbacks fs = {
         | 
| 1382 1428 | 
             
            #ifndef TINYGLTF_NO_FS
         | 
| 1383 1429 | 
             
                  &tinygltf::FileExists, &tinygltf::ExpandFilePath,
         | 
| @@ -1397,7 +1443,8 @@ class TinyGLTF { | |
| 1397 1443 | 
             
            #else
         | 
| 1398 1444 | 
             
                  nullptr;
         | 
| 1399 1445 | 
             
            #endif
         | 
| 1400 | 
            -
              void *load_image_user_data_ | 
| 1446 | 
            +
              void *load_image_user_data_{nullptr};
         | 
| 1447 | 
            +
              bool user_image_loader_{false};
         | 
| 1401 1448 |  | 
| 1402 1449 | 
             
              WriteImageDataFunction WriteImageData =
         | 
| 1403 1450 | 
             
            #ifndef TINYGLTF_NO_STB_IMAGE_WRITE
         | 
| @@ -1405,7 +1452,7 @@ class TinyGLTF { | |
| 1405 1452 | 
             
            #else
         | 
| 1406 1453 | 
             
                  nullptr;
         | 
| 1407 1454 | 
             
            #endif
         | 
| 1408 | 
            -
              void *write_image_user_data_ | 
| 1455 | 
            +
              void *write_image_user_data_{nullptr};
         | 
| 1409 1456 | 
             
            };
         | 
| 1410 1457 |  | 
| 1411 1458 | 
             
            #ifdef __clang__
         | 
| @@ -1490,6 +1537,7 @@ class TinyGLTF { | |
| 1490 1537 | 
             
            #ifndef TINYGLTF_USE_RAPIDJSON
         | 
| 1491 1538 | 
             
            #include "json.hpp"
         | 
| 1492 1539 | 
             
            #else
         | 
| 1540 | 
            +
            #ifndef TINYGLTF_NO_INCLUDE_RAPIDJSON
         | 
| 1493 1541 | 
             
            #include "document.h"
         | 
| 1494 1542 | 
             
            #include "prettywriter.h"
         | 
| 1495 1543 | 
             
            #include "rapidjson.h"
         | 
| @@ -1497,6 +1545,7 @@ class TinyGLTF { | |
| 1497 1545 | 
             
            #include "writer.h"
         | 
| 1498 1546 | 
             
            #endif
         | 
| 1499 1547 | 
             
            #endif
         | 
| 1548 | 
            +
            #endif
         | 
| 1500 1549 |  | 
| 1501 1550 | 
             
            #ifdef TINYGLTF_ENABLE_DRACO
         | 
| 1502 1551 | 
             
            #include "draco/compression/decode.h"
         | 
| @@ -1547,14 +1596,15 @@ class TinyGLTF { | |
| 1547 1596 | 
             
            #undef NOMINMAX
         | 
| 1548 1597 | 
             
            #endif
         | 
| 1549 1598 |  | 
| 1550 | 
            -
            #if defined(__GLIBCXX__) | 
| 1599 | 
            +
            #if defined(__GLIBCXX__)  // mingw
         | 
| 1551 1600 |  | 
| 1552 | 
            -
            #include <ext/stdio_filebuf.h>  // fstream (all sorts of IO stuff) + stdio_filebuf (=streambuf)
         | 
| 1553 1601 | 
             
            #include <fcntl.h>  // _O_RDONLY
         | 
| 1554 1602 |  | 
| 1603 | 
            +
            #include <ext/stdio_filebuf.h>  // fstream (all sorts of IO stuff) + stdio_filebuf (=streambuf)
         | 
| 1604 | 
            +
             | 
| 1555 1605 | 
             
            #endif
         | 
| 1556 1606 |  | 
| 1557 | 
            -
            #elif !defined(__ANDROID__)
         | 
| 1607 | 
            +
            #elif !defined(__ANDROID__) && !defined(__OpenBSD__)
         | 
| 1558 1608 | 
             
            #include <wordexp.h>
         | 
| 1559 1609 | 
             
            #endif
         | 
| 1560 1610 |  | 
| @@ -1658,6 +1708,19 @@ void JsonParse(JsonDocument &doc, const char *str, size_t length, | |
| 1658 1708 |  | 
| 1659 1709 | 
             
            namespace tinygltf {
         | 
| 1660 1710 |  | 
| 1711 | 
            +
            ///
         | 
| 1712 | 
            +
            /// Internal LoadImageDataOption struct.
         | 
| 1713 | 
            +
            /// This struct is passed through `user_pointer` in LoadImageData.
         | 
| 1714 | 
            +
            /// The struct is not passed when the user supply their own LoadImageData
         | 
| 1715 | 
            +
            /// callbacks.
         | 
| 1716 | 
            +
            ///
         | 
| 1717 | 
            +
            struct LoadImageDataOption {
         | 
| 1718 | 
            +
              // true: preserve image channels(e.g. load as RGB image if the image has RGB
         | 
| 1719 | 
            +
              // channels) default `false`(channels are expanded to RGBA for backward
         | 
| 1720 | 
            +
              // compatiblity).
         | 
| 1721 | 
            +
              bool preserve_channels{false};
         | 
| 1722 | 
            +
            };
         | 
| 1723 | 
            +
             | 
| 1661 1724 | 
             
            // Equals function for Value, for recursivity
         | 
| 1662 1725 | 
             
            static bool Equals(const tinygltf::Value &one, const tinygltf::Value &other) {
         | 
| 1663 1726 | 
             
              if (one.Type() != other.Type()) return false;
         | 
| @@ -1870,8 +1933,10 @@ bool Sampler::operator==(const Sampler &other) const { | |
| 1870 1933 | 
             
              return this->extensions == other.extensions && this->extras == other.extras &&
         | 
| 1871 1934 | 
             
                     this->magFilter == other.magFilter &&
         | 
| 1872 1935 | 
             
                     this->minFilter == other.minFilter && this->name == other.name &&
         | 
| 1873 | 
            -
                     this-> | 
| 1936 | 
            +
                     this->wrapS == other.wrapS &&
         | 
| 1874 1937 | 
             
                     this->wrapT == other.wrapT;
         | 
| 1938 | 
            +
             | 
| 1939 | 
            +
                     //this->wrapR == other.wrapR
         | 
| 1875 1940 | 
             
            }
         | 
| 1876 1941 | 
             
            bool Scene::operator==(const Scene &other) const {
         | 
| 1877 1942 | 
             
              return this->extensions == other.extensions && this->extras == other.extras &&
         | 
| @@ -1975,9 +2040,11 @@ static std::string GetBaseDir(const std::string &filepath) { | |
| 1975 2040 | 
             
              return "";
         | 
| 1976 2041 | 
             
            }
         | 
| 1977 2042 |  | 
| 1978 | 
            -
            // https://stackoverflow.com/questions/8520560/get-a-file-name-from-a-path
         | 
| 1979 2043 | 
             
            static std::string GetBaseFilename(const std::string &filepath) {
         | 
| 1980 | 
            -
               | 
| 2044 | 
            +
              auto idx = filepath.find_last_of("/\\");
         | 
| 2045 | 
            +
              if (idx != std::string::npos)
         | 
| 2046 | 
            +
                return filepath.substr(idx + 1);
         | 
| 2047 | 
            +
              return filepath;
         | 
| 1981 2048 | 
             
            }
         | 
| 1982 2049 |  | 
| 1983 2050 | 
             
            std::string base64_encode(unsigned char const *, unsigned int len);
         | 
| @@ -2119,6 +2186,76 @@ std::string base64_decode(std::string const &encoded_string) { | |
| 2119 2186 | 
             
            #pragma clang diagnostic pop
         | 
| 2120 2187 | 
             
            #endif
         | 
| 2121 2188 |  | 
| 2189 | 
            +
            // https://github.com/syoyo/tinygltf/issues/228
         | 
| 2190 | 
            +
            // TODO(syoyo): Use uriparser https://uriparser.github.io/ for stricter Uri
         | 
| 2191 | 
            +
            // decoding?
         | 
| 2192 | 
            +
            //
         | 
| 2193 | 
            +
            // Uri Decoding from DLIB
         | 
| 2194 | 
            +
            // http://dlib.net/dlib/server/server_http.cpp.html
         | 
| 2195 | 
            +
            // --- dlib begin ------------------------------------------------------------
         | 
| 2196 | 
            +
            // Copyright (C) 2003  Davis E. King (davis@dlib.net)
         | 
| 2197 | 
            +
            // License: Boost Software License
         | 
| 2198 | 
            +
            // Boost Software License - Version 1.0 - August 17th, 2003
         | 
| 2199 | 
            +
             | 
| 2200 | 
            +
            // Permission is hereby granted, free of charge, to any person or organization
         | 
| 2201 | 
            +
            // obtaining a copy of the software and accompanying documentation covered by
         | 
| 2202 | 
            +
            // this license (the "Software") to use, reproduce, display, distribute,
         | 
| 2203 | 
            +
            // execute, and transmit the Software, and to prepare derivative works of the
         | 
| 2204 | 
            +
            // Software, and to permit third-parties to whom the Software is furnished to
         | 
| 2205 | 
            +
            // do so, all subject to the following:
         | 
| 2206 | 
            +
            // The copyright notices in the Software and this entire statement, including
         | 
| 2207 | 
            +
            // the above license grant, this restriction and the following disclaimer,
         | 
| 2208 | 
            +
            // must be included in all copies of the Software, in whole or in part, and
         | 
| 2209 | 
            +
            // all derivative works of the Software, unless such copies or derivative
         | 
| 2210 | 
            +
            // works are solely in the form of machine-executable object code generated by
         | 
| 2211 | 
            +
            // a source language processor.
         | 
| 2212 | 
            +
            // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         | 
| 2213 | 
            +
            // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         | 
| 2214 | 
            +
            // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
         | 
| 2215 | 
            +
            // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
         | 
| 2216 | 
            +
            // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
         | 
| 2217 | 
            +
            // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
         | 
| 2218 | 
            +
            // DEALINGS IN THE SOFTWARE.
         | 
| 2219 | 
            +
            //
         | 
| 2220 | 
            +
            namespace dlib {
         | 
| 2221 | 
            +
             | 
| 2222 | 
            +
            inline unsigned char from_hex(unsigned char ch) {
         | 
| 2223 | 
            +
              if (ch <= '9' && ch >= '0')
         | 
| 2224 | 
            +
                ch -= '0';
         | 
| 2225 | 
            +
              else if (ch <= 'f' && ch >= 'a')
         | 
| 2226 | 
            +
                ch -= 'a' - 10;
         | 
| 2227 | 
            +
              else if (ch <= 'F' && ch >= 'A')
         | 
| 2228 | 
            +
                ch -= 'A' - 10;
         | 
| 2229 | 
            +
              else
         | 
| 2230 | 
            +
                ch = 0;
         | 
| 2231 | 
            +
              return ch;
         | 
| 2232 | 
            +
            }
         | 
| 2233 | 
            +
             | 
| 2234 | 
            +
            static const std::string urldecode(const std::string &str) {
         | 
| 2235 | 
            +
              using namespace std;
         | 
| 2236 | 
            +
              string result;
         | 
| 2237 | 
            +
              string::size_type i;
         | 
| 2238 | 
            +
              for (i = 0; i < str.size(); ++i) {
         | 
| 2239 | 
            +
                if (str[i] == '+') {
         | 
| 2240 | 
            +
                  result += ' ';
         | 
| 2241 | 
            +
                } else if (str[i] == '%' && str.size() > i + 2) {
         | 
| 2242 | 
            +
                  const unsigned char ch1 =
         | 
| 2243 | 
            +
                      from_hex(static_cast<unsigned char>(str[i + 1]));
         | 
| 2244 | 
            +
                  const unsigned char ch2 =
         | 
| 2245 | 
            +
                      from_hex(static_cast<unsigned char>(str[i + 2]));
         | 
| 2246 | 
            +
                  const unsigned char ch = static_cast<unsigned char>((ch1 << 4) | ch2);
         | 
| 2247 | 
            +
                  result += static_cast<char>(ch);
         | 
| 2248 | 
            +
                  i += 2;
         | 
| 2249 | 
            +
                } else {
         | 
| 2250 | 
            +
                  result += str[i];
         | 
| 2251 | 
            +
                }
         | 
| 2252 | 
            +
              }
         | 
| 2253 | 
            +
              return result;
         | 
| 2254 | 
            +
            }
         | 
| 2255 | 
            +
             | 
| 2256 | 
            +
            }  // namespace dlib
         | 
| 2257 | 
            +
            // --- dlib end --------------------------------------------------------------
         | 
| 2258 | 
            +
             | 
| 2122 2259 | 
             
            static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
         | 
| 2123 2260 | 
             
                                         std::string *warn, const std::string &filename,
         | 
| 2124 2261 | 
             
                                         const std::string &basedir, bool required,
         | 
| @@ -2190,22 +2327,40 @@ static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err, | |
| 2190 2327 | 
             
            void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) {
         | 
| 2191 2328 | 
             
              LoadImageData = func;
         | 
| 2192 2329 | 
             
              load_image_user_data_ = user_data;
         | 
| 2330 | 
            +
              user_image_loader_ = true;
         | 
| 2331 | 
            +
            }
         | 
| 2332 | 
            +
             | 
| 2333 | 
            +
            void TinyGLTF::RemoveImageLoader() {
         | 
| 2334 | 
            +
              LoadImageData =
         | 
| 2335 | 
            +
            #ifndef TINYGLTF_NO_STB_IMAGE
         | 
| 2336 | 
            +
                  &tinygltf::LoadImageData;
         | 
| 2337 | 
            +
            #else
         | 
| 2338 | 
            +
                  nullptr;
         | 
| 2339 | 
            +
            #endif
         | 
| 2340 | 
            +
             | 
| 2341 | 
            +
              load_image_user_data_ = nullptr;
         | 
| 2342 | 
            +
              user_image_loader_ = false;
         | 
| 2193 2343 | 
             
            }
         | 
| 2194 2344 |  | 
| 2195 2345 | 
             
            #ifndef TINYGLTF_NO_STB_IMAGE
         | 
| 2196 2346 | 
             
            bool LoadImageData(Image *image, const int image_idx, std::string *err,
         | 
| 2197 2347 | 
             
                               std::string *warn, int req_width, int req_height,
         | 
| 2198 2348 | 
             
                               const unsigned char *bytes, int size, void *user_data) {
         | 
| 2199 | 
            -
              (void)user_data;
         | 
| 2200 2349 | 
             
              (void)warn;
         | 
| 2201 2350 |  | 
| 2351 | 
            +
              LoadImageDataOption option;
         | 
| 2352 | 
            +
              if (user_data) {
         | 
| 2353 | 
            +
                option = *reinterpret_cast<LoadImageDataOption *>(user_data);
         | 
| 2354 | 
            +
              }
         | 
| 2355 | 
            +
             | 
| 2202 2356 | 
             
              int w = 0, h = 0, comp = 0, req_comp = 0;
         | 
| 2203 2357 |  | 
| 2204 2358 | 
             
              unsigned char *data = nullptr;
         | 
| 2205 2359 |  | 
| 2206 | 
            -
              //  | 
| 2207 | 
            -
              //  | 
| 2208 | 
            -
               | 
| 2360 | 
            +
              // preserve_channels true: Use channels stored in the image file.
         | 
| 2361 | 
            +
              // false: force 32-bit textures for common Vulkan compatibility. It appears
         | 
| 2362 | 
            +
              // that some GPU drivers do not support 24-bit images for Vulkan
         | 
| 2363 | 
            +
              req_comp = option.preserve_channels ? 0 : 4;
         | 
| 2209 2364 | 
             
              int bits = 8;
         | 
| 2210 2365 | 
             
              int pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
         | 
| 2211 2366 |  | 
| @@ -2277,13 +2432,18 @@ bool LoadImageData(Image *image, const int image_idx, std::string *err, | |
| 2277 2432 | 
             
                }
         | 
| 2278 2433 | 
             
              }
         | 
| 2279 2434 |  | 
| 2435 | 
            +
              if (req_comp != 0) {
         | 
| 2436 | 
            +
                // loaded data has `req_comp` channels(components)
         | 
| 2437 | 
            +
                comp = req_comp;
         | 
| 2438 | 
            +
              }
         | 
| 2439 | 
            +
             | 
| 2280 2440 | 
             
              image->width = w;
         | 
| 2281 2441 | 
             
              image->height = h;
         | 
| 2282 | 
            -
              image->component =  | 
| 2442 | 
            +
              image->component = comp;
         | 
| 2283 2443 | 
             
              image->bits = bits;
         | 
| 2284 2444 | 
             
              image->pixel_type = pixel_type;
         | 
| 2285 | 
            -
              image->image.resize(static_cast<size_t>(w * h *  | 
| 2286 | 
            -
              std::copy(data, data + w * h *  | 
| 2445 | 
            +
              image->image.resize(static_cast<size_t>(w * h * comp) * size_t(bits / 8));
         | 
| 2446 | 
            +
              std::copy(data, data + w * h * comp * (bits / 8), image->image.begin());
         | 
| 2287 2447 | 
             
              stbi_image_free(data);
         | 
| 2288 2448 |  | 
| 2289 2449 | 
             
              return true;
         | 
| @@ -2379,11 +2539,22 @@ void TinyGLTF::SetFsCallbacks(FsCallbacks callbacks) { fs = callbacks; } | |
| 2379 2539 |  | 
| 2380 2540 | 
             
            #ifdef _WIN32
         | 
| 2381 2541 | 
             
            static inline std::wstring UTF8ToWchar(const std::string &str) {
         | 
| 2382 | 
            -
              int wstr_size = | 
| 2542 | 
            +
              int wstr_size =
         | 
| 2543 | 
            +
                  MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
         | 
| 2383 2544 | 
             
              std::wstring wstr(wstr_size, 0);
         | 
| 2384 | 
            -
              MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0], | 
| 2545 | 
            +
              MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0],
         | 
| 2546 | 
            +
                                  (int)wstr.size());
         | 
| 2385 2547 | 
             
              return wstr;
         | 
| 2386 2548 | 
             
            }
         | 
| 2549 | 
            +
             | 
| 2550 | 
            +
            static inline std::string WcharToUTF8(const std::wstring &wstr) {
         | 
| 2551 | 
            +
              int str_size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(),
         | 
| 2552 | 
            +
                                                 nullptr, 0, NULL, NULL);
         | 
| 2553 | 
            +
              std::string str(str_size, 0);
         | 
| 2554 | 
            +
              WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), &str[0],
         | 
| 2555 | 
            +
                                  (int)str.size(), NULL, NULL);
         | 
| 2556 | 
            +
              return str;
         | 
| 2557 | 
            +
            }
         | 
| 2387 2558 | 
             
            #endif
         | 
| 2388 2559 |  | 
| 2389 2560 | 
             
            #ifndef TINYGLTF_NO_FS
         | 
| @@ -2435,19 +2606,20 @@ bool FileExists(const std::string &abs_filename, void *) { | |
| 2435 2606 |  | 
| 2436 2607 | 
             
            std::string ExpandFilePath(const std::string &filepath, void *) {
         | 
| 2437 2608 | 
             
            #ifdef _WIN32
         | 
| 2438 | 
            -
               | 
| 2439 | 
            -
               | 
| 2440 | 
            -
               | 
| 2609 | 
            +
              // Assume input `filepath` is encoded in UTF-8
         | 
| 2610 | 
            +
              std::wstring wfilepath = UTF8ToWchar(filepath);
         | 
| 2611 | 
            +
              DWORD wlen = ExpandEnvironmentStringsW(wfilepath.c_str(), nullptr, 0);
         | 
| 2612 | 
            +
              wchar_t *wstr = new wchar_t[wlen];
         | 
| 2613 | 
            +
              ExpandEnvironmentStringsW(wfilepath.c_str(), wstr, wlen);
         | 
| 2441 2614 |  | 
| 2442 | 
            -
              std:: | 
| 2615 | 
            +
              std::wstring ws(wstr);
         | 
| 2616 | 
            +
              delete[] wstr;
         | 
| 2617 | 
            +
              return WcharToUTF8(ws);
         | 
| 2443 2618 |  | 
| 2444 | 
            -
              delete[] str;
         | 
| 2445 | 
            -
             | 
| 2446 | 
            -
              return s;
         | 
| 2447 2619 | 
             
            #else
         | 
| 2448 2620 |  | 
| 2449 2621 | 
             
            #if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \
         | 
| 2450 | 
            -
                defined(__ANDROID__) || defined(__EMSCRIPTEN__)
         | 
| 2622 | 
            +
                defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__OpenBSD__)
         | 
| 2451 2623 | 
             
              // no expansion
         | 
| 2452 2624 | 
             
              std::string s = filepath;
         | 
| 2453 2625 | 
             
            #else
         | 
| @@ -2458,8 +2630,10 @@ std::string ExpandFilePath(const std::string &filepath, void *) { | |
| 2458 2630 | 
             
                return "";
         | 
| 2459 2631 | 
             
              }
         | 
| 2460 2632 |  | 
| 2633 | 
            +
              // Quote the string to keep any spaces in filepath intact.
         | 
| 2634 | 
            +
              std::string quoted_path = "\"" + filepath + "\"";
         | 
| 2461 2635 | 
             
              // char** w;
         | 
| 2462 | 
            -
              int ret = wordexp( | 
| 2636 | 
            +
              int ret = wordexp(quoted_path.c_str(), &p, 0);
         | 
| 2463 2637 | 
             
              if (ret) {
         | 
| 2464 2638 | 
             
                // err
         | 
| 2465 2639 | 
             
                s = filepath;
         | 
| @@ -2493,11 +2667,12 @@ bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err, | |
| 2493 2667 | 
             
                  return false;
         | 
| 2494 2668 | 
             
                }
         | 
| 2495 2669 | 
             
                size_t size = AAsset_getLength(asset);
         | 
| 2496 | 
            -
                if (size  | 
| 2670 | 
            +
                if (size == 0) {
         | 
| 2497 2671 | 
             
                  if (err) {
         | 
| 2498 2672 | 
             
                    (*err) += "Invalid file size : " + filepath +
         | 
| 2499 2673 | 
             
                              " (does the path point to a directory?)";
         | 
| 2500 2674 | 
             
                  }
         | 
| 2675 | 
            +
                  return false;
         | 
| 2501 2676 | 
             
                }
         | 
| 2502 2677 | 
             
                out->resize(size);
         | 
| 2503 2678 | 
             
                AAsset_read(asset, reinterpret_cast<char *>(&out->at(0)), size);
         | 
| @@ -2511,13 +2686,17 @@ bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err, | |
| 2511 2686 | 
             
              }
         | 
| 2512 2687 | 
             
            #else
         | 
| 2513 2688 | 
             
            #ifdef _WIN32
         | 
| 2514 | 
            -
            #if defined(__GLIBCXX__) | 
| 2515 | 
            -
              int file_descriptor = | 
| 2689 | 
            +
            #if defined(__GLIBCXX__)  // mingw
         | 
| 2690 | 
            +
              int file_descriptor =
         | 
| 2691 | 
            +
                  _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY);
         | 
| 2516 2692 | 
             
              __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
         | 
| 2517 2693 | 
             
              std::istream f(&wfile_buf);
         | 
| 2518 | 
            -
            #elif defined(_MSC_VER)
         | 
| 2694 | 
            +
            #elif defined(_MSC_VER) || defined(_LIBCPP_VERSION)
         | 
| 2695 | 
            +
              // For libcxx, assume _LIBCPP_HAS_OPEN_WITH_WCHAR is defined to accept
         | 
| 2696 | 
            +
              // `wchar_t *`
         | 
| 2519 2697 | 
             
              std::ifstream f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary);
         | 
| 2520 | 
            -
            #else | 
| 2698 | 
            +
            #else
         | 
| 2699 | 
            +
              // Unknown compiler/runtime
         | 
| 2521 2700 | 
             
              std::ifstream f(filepath.c_str(), std::ifstream::binary);
         | 
| 2522 2701 | 
             
            #endif
         | 
| 2523 2702 | 
             
            #else
         | 
| @@ -2558,13 +2737,15 @@ bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err, | |
| 2558 2737 | 
             
            bool WriteWholeFile(std::string *err, const std::string &filepath,
         | 
| 2559 2738 | 
             
                                const std::vector<unsigned char> &contents, void *) {
         | 
| 2560 2739 | 
             
            #ifdef _WIN32
         | 
| 2561 | 
            -
            #if defined(__GLIBCXX__) | 
| 2562 | 
            -
              int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(), | 
| 2563 | 
            -
             | 
| 2740 | 
            +
            #if defined(__GLIBCXX__)  // mingw
         | 
| 2741 | 
            +
              int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(),
         | 
| 2742 | 
            +
                                           _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
         | 
| 2743 | 
            +
              __gnu_cxx::stdio_filebuf<char> wfile_buf(
         | 
| 2744 | 
            +
                  file_descriptor, std::ios_base::out | std::ios_base::binary);
         | 
| 2564 2745 | 
             
              std::ostream f(&wfile_buf);
         | 
| 2565 2746 | 
             
            #elif defined(_MSC_VER)
         | 
| 2566 2747 | 
             
              std::ofstream f(UTF8ToWchar(filepath).c_str(), std::ofstream::binary);
         | 
| 2567 | 
            -
            #else | 
| 2748 | 
            +
            #else  // clang?
         | 
| 2568 2749 | 
             
              std::ofstream f(filepath.c_str(), std::ofstream::binary);
         | 
| 2569 2750 | 
             
            #endif
         | 
| 2570 2751 | 
             
            #else
         | 
| @@ -2611,12 +2792,13 @@ static void UpdateImageObject(Image &image, std::string &baseDir, int index, | |
| 2611 2792 | 
             
                                          void *user_data = nullptr) {
         | 
| 2612 2793 | 
             
              std::string filename;
         | 
| 2613 2794 | 
             
              std::string ext;
         | 
| 2614 | 
            -
             | 
| 2615 | 
            -
              // If image have uri. Use it it as a filename
         | 
| 2795 | 
            +
              // If image has uri, use it it as a filename
         | 
| 2616 2796 | 
             
              if (image.uri.size()) {
         | 
| 2617 2797 | 
             
                filename = GetBaseFilename(image.uri);
         | 
| 2618 2798 | 
             
                ext = GetFilePathExtension(filename);
         | 
| 2619 | 
            -
             | 
| 2799 | 
            +
              } else if (image.bufferView != -1) {
         | 
| 2800 | 
            +
                // If there's no URI and the data exists in a buffer,
         | 
| 2801 | 
            +
                // don't change properties or write images
         | 
| 2620 2802 | 
             
              } else if (image.name.size()) {
         | 
| 2621 2803 | 
             
                ext = MimeToExt(image.mimeType);
         | 
| 2622 2804 | 
             
                // Otherwise use name as filename
         | 
| @@ -2628,7 +2810,7 @@ static void UpdateImageObject(Image &image, std::string &baseDir, int index, | |
| 2628 2810 | 
             
              }
         | 
| 2629 2811 |  | 
| 2630 2812 | 
             
              // If callback is set, modify image data object
         | 
| 2631 | 
            -
              if (*WriteImageData != nullptr) {
         | 
| 2813 | 
            +
              if (*WriteImageData != nullptr && !filename.empty()) {
         | 
| 2632 2814 | 
             
                std::string uri;
         | 
| 2633 2815 | 
             
                (*WriteImageData)(&baseDir, &filename, &image, embedImages, user_data);
         | 
| 2634 2816 | 
             
              }
         | 
| @@ -2728,6 +2910,7 @@ bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type, | |
| 2728 2910 | 
             
                }
         | 
| 2729 2911 | 
             
              }
         | 
| 2730 2912 |  | 
| 2913 | 
            +
              // TODO(syoyo): Allow empty buffer? #229
         | 
| 2731 2914 | 
             
              if (data.empty()) {
         | 
| 2732 2915 | 
             
                return false;
         | 
| 2733 2916 | 
             
              }
         | 
| @@ -2872,7 +3055,9 @@ json_const_iterator ObjectEnd(const json &o) { | |
| 2872 3055 | 
             
            #endif
         | 
| 2873 3056 | 
             
            }
         | 
| 2874 3057 |  | 
| 2875 | 
            -
            const char | 
| 3058 | 
            +
            // Making this a const char* results in a pointer to a temporary when
         | 
| 3059 | 
            +
            // TINYGLTF_USE_RAPIDJSON is off.
         | 
| 3060 | 
            +
            std::string GetKey(json_const_iterator &it) {
         | 
| 2876 3061 | 
             
            #ifdef TINYGLTF_USE_RAPIDJSON
         | 
| 2877 3062 | 
             
              return it->name.GetString();
         | 
| 2878 3063 | 
             
            #else
         | 
| @@ -3007,6 +3192,7 @@ static bool ParseJsonAsValue(Value *ret, const json &o) { | |
| 3007 3192 | 
             
                  break;
         | 
| 3008 3193 | 
             
                case json::value_t::null:
         | 
| 3009 3194 | 
             
                case json::value_t::discarded:
         | 
| 3195 | 
            +
                case json::value_t::binary:
         | 
| 3010 3196 | 
             
                  // default:
         | 
| 3011 3197 | 
             
                  break;
         | 
| 3012 3198 | 
             
              }
         | 
| @@ -3508,6 +3694,7 @@ static bool ParseAsset(Asset *asset, std::string *err, const json &o, | |
| 3508 3694 | 
             
              ParseStringProperty(&asset->version, err, o, "version", true, "Asset");
         | 
| 3509 3695 | 
             
              ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset");
         | 
| 3510 3696 | 
             
              ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset");
         | 
| 3697 | 
            +
              ParseStringProperty(&asset->copyright, err, o, "copyright", false, "Asset");
         | 
| 3511 3698 |  | 
| 3512 3699 | 
             
              ParseExtensionsProperty(&asset->extensions, err, o);
         | 
| 3513 3700 |  | 
| @@ -3646,7 +3833,10 @@ static bool ParseImage(Image *image, const int image_idx, std::string *err, | |
| 3646 3833 | 
             
            #ifdef TINYGLTF_NO_EXTERNAL_IMAGE
         | 
| 3647 3834 | 
             
                return true;
         | 
| 3648 3835 | 
             
            #endif
         | 
| 3649 | 
            -
                 | 
| 3836 | 
            +
                std::string decoded_uri = dlib::urldecode(uri);
         | 
| 3837 | 
            +
                if (!LoadExternalFile(&img, err, warn, decoded_uri, basedir,
         | 
| 3838 | 
            +
                                      /* required */ false, /* required bytes */ 0,
         | 
| 3839 | 
            +
                                      /* checksize */ false, fs)) {
         | 
| 3650 3840 | 
             
                  if (warn) {
         | 
| 3651 3841 | 
             
                    (*warn) += "Failed to load external 'uri' for image[" +
         | 
| 3652 3842 | 
             
                               std::to_string(image_idx) + "] name = [" + image->name +
         | 
| @@ -3868,9 +4058,10 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o, | |
| 3868 4058 | 
             
                    }
         | 
| 3869 4059 | 
             
                  } else {
         | 
| 3870 4060 | 
             
                    // External .bin file.
         | 
| 4061 | 
            +
                    std::string decoded_uri = dlib::urldecode(buffer->uri);
         | 
| 3871 4062 | 
             
                    if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr,
         | 
| 3872 | 
            -
                                           | 
| 3873 | 
            -
                                          fs)) {
         | 
| 4063 | 
            +
                                          decoded_uri, basedir, /* required */ true,
         | 
| 4064 | 
            +
                                          byteLength, /* checkSize */ true, fs)) {
         | 
| 3874 4065 | 
             
                      return false;
         | 
| 3875 4066 | 
             
                    }
         | 
| 3876 4067 | 
             
                  }
         | 
| @@ -3912,8 +4103,10 @@ static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o, | |
| 3912 4103 | 
             
                  }
         | 
| 3913 4104 | 
             
                } else {
         | 
| 3914 4105 | 
             
                  // Assume external .bin file.
         | 
| 3915 | 
            -
                   | 
| 3916 | 
            -
             | 
| 4106 | 
            +
                  std::string decoded_uri = dlib::urldecode(buffer->uri);
         | 
| 4107 | 
            +
                  if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, decoded_uri,
         | 
| 4108 | 
            +
                                        basedir, /* required */ true, byteLength,
         | 
| 4109 | 
            +
                                        /* checkSize */ true, fs)) {
         | 
| 3917 4110 | 
             
                    return false;
         | 
| 3918 4111 | 
             
                  }
         | 
| 3919 4112 | 
             
                }
         | 
| @@ -4023,7 +4216,9 @@ static bool ParseSparseAccessor(Accessor *accessor, std::string *err, | |
| 4023 4216 | 
             
              accessor->sparse.isSparse = true;
         | 
| 4024 4217 |  | 
| 4025 4218 | 
             
              int count = 0;
         | 
| 4026 | 
            -
              ParseIntegerProperty(&count, err, o, "count", true) | 
| 4219 | 
            +
              if (!ParseIntegerProperty(&count, err, o, "count", true, "SparseAccessor")) {
         | 
| 4220 | 
            +
                return false;
         | 
| 4221 | 
            +
              }
         | 
| 4027 4222 |  | 
| 4028 4223 | 
             
              json_const_iterator indices_iterator;
         | 
| 4029 4224 | 
             
              json_const_iterator values_iterator;
         | 
| @@ -4041,18 +4236,24 @@ static bool ParseSparseAccessor(Accessor *accessor, std::string *err, | |
| 4041 4236 | 
             
              const json &values_obj = GetValue(values_iterator);
         | 
| 4042 4237 |  | 
| 4043 4238 | 
             
              int indices_buffer_view = 0, indices_byte_offset = 0, component_type = 0;
         | 
| 4044 | 
            -
              ParseIntegerProperty(&indices_buffer_view, err, indices_obj, "bufferView",
         | 
| 4045 | 
            -
                                   true) | 
| 4239 | 
            +
              if (!ParseIntegerProperty(&indices_buffer_view, err, indices_obj, "bufferView",
         | 
| 4240 | 
            +
                                   true, "SparseAccessor")) {
         | 
| 4241 | 
            +
                return false;
         | 
| 4242 | 
            +
              }
         | 
| 4046 4243 | 
             
              ParseIntegerProperty(&indices_byte_offset, err, indices_obj, "byteOffset",
         | 
| 4047 | 
            -
                                    | 
| 4048 | 
            -
              ParseIntegerProperty(&component_type, err, indices_obj, "componentType",
         | 
| 4049 | 
            -
                                   true) | 
| 4244 | 
            +
                                   false);
         | 
| 4245 | 
            +
              if (!ParseIntegerProperty(&component_type, err, indices_obj, "componentType",
         | 
| 4246 | 
            +
                                   true, "SparseAccessor")) {
         | 
| 4247 | 
            +
                return false;
         | 
| 4248 | 
            +
              }
         | 
| 4050 4249 |  | 
| 4051 4250 | 
             
              int values_buffer_view = 0, values_byte_offset = 0;
         | 
| 4052 | 
            -
              ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView",
         | 
| 4053 | 
            -
                                   true) | 
| 4251 | 
            +
              if (!ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView",
         | 
| 4252 | 
            +
                                   true, "SparseAccessor")) {
         | 
| 4253 | 
            +
                return false;
         | 
| 4254 | 
            +
              }
         | 
| 4054 4255 | 
             
              ParseIntegerProperty(&values_byte_offset, err, values_obj, "byteOffset",
         | 
| 4055 | 
            -
                                    | 
| 4256 | 
            +
                                   false);
         | 
| 4056 4257 |  | 
| 4057 4258 | 
             
              accessor->sparse.count = count;
         | 
| 4058 4259 | 
             
              accessor->sparse.indices.bufferView = indices_buffer_view;
         | 
| @@ -4061,8 +4262,6 @@ static bool ParseSparseAccessor(Accessor *accessor, std::string *err, | |
| 4061 4262 | 
             
              accessor->sparse.values.bufferView = values_buffer_view;
         | 
| 4062 4263 | 
             
              accessor->sparse.values.byteOffset = values_byte_offset;
         | 
| 4063 4264 |  | 
| 4064 | 
            -
              // todo check theses values
         | 
| 4065 | 
            -
             | 
| 4066 4265 | 
             
              return true;
         | 
| 4067 4266 | 
             
            }
         | 
| 4068 4267 |  | 
| @@ -4270,6 +4469,7 @@ static bool GetAttributeForAllPoints(uint32_t componentType, draco::Mesh *mesh, | |
| 4270 4469 | 
             
            static bool ParseDracoExtension(Primitive *primitive, Model *model,
         | 
| 4271 4470 | 
             
                                            std::string *err,
         | 
| 4272 4471 | 
             
                                            const Value &dracoExtensionValue) {
         | 
| 4472 | 
            +
              (void)err;
         | 
| 4273 4473 | 
             
              auto bufferViewValue = dracoExtensionValue.Get("bufferView");
         | 
| 4274 4474 | 
             
              if (!bufferViewValue.IsInt()) return false;
         | 
| 4275 4475 | 
             
              auto attributesValue = dracoExtensionValue.Get("attributes");
         | 
| @@ -4330,7 +4530,6 @@ static bool ParseDracoExtension(Primitive *primitive, Model *model, | |
| 4330 4530 |  | 
| 4331 4531 | 
             
                int dracoAttributeIndex = attribute.second.Get<int>();
         | 
| 4332 4532 | 
             
                const auto pAttribute = mesh->GetAttributeByUniqueId(dracoAttributeIndex);
         | 
| 4333 | 
            -
                const auto pBuffer = pAttribute->buffer();
         | 
| 4334 4533 | 
             
                const auto componentType =
         | 
| 4335 4534 | 
             
                    model->accessors[primitiveAttribute->second].componentType;
         | 
| 4336 4535 |  | 
| @@ -4751,6 +4950,13 @@ static bool ParseAnimationChannel( | |
| 4751 4950 | 
             
                  }
         | 
| 4752 4951 | 
             
                  return false;
         | 
| 4753 4952 | 
             
                }
         | 
| 4953 | 
            +
                ParseExtensionsProperty(&channel->target_extensions, err, target_object);
         | 
| 4954 | 
            +
                if (store_original_json_for_extras_and_extensions) {
         | 
| 4955 | 
            +
                  json_const_iterator it;
         | 
| 4956 | 
            +
                  if (FindMember(target_object, "extensions", it)) {
         | 
| 4957 | 
            +
                    channel->target_extensions_json_string = JsonToString(GetValue(it));
         | 
| 4958 | 
            +
                  }
         | 
| 4959 | 
            +
                }
         | 
| 4754 4960 | 
             
              }
         | 
| 4755 4961 |  | 
| 4756 4962 | 
             
              channel->sampler = samplerIndex;
         | 
| @@ -4882,12 +5088,12 @@ static bool ParseSampler(Sampler *sampler, std::string *err, const json &o, | |
| 4882 5088 | 
             
              int magFilter = -1;
         | 
| 4883 5089 | 
             
              int wrapS = TINYGLTF_TEXTURE_WRAP_REPEAT;
         | 
| 4884 5090 | 
             
              int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT;
         | 
| 4885 | 
            -
              int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT;
         | 
| 5091 | 
            +
              //int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT;
         | 
| 4886 5092 | 
             
              ParseIntegerProperty(&minFilter, err, o, "minFilter", false);
         | 
| 4887 5093 | 
             
              ParseIntegerProperty(&magFilter, err, o, "magFilter", false);
         | 
| 4888 5094 | 
             
              ParseIntegerProperty(&wrapS, err, o, "wrapS", false);
         | 
| 4889 5095 | 
             
              ParseIntegerProperty(&wrapT, err, o, "wrapT", false);
         | 
| 4890 | 
            -
              ParseIntegerProperty(&wrapR, err, o, "wrapR", false);  // tinygltf extension
         | 
| 5096 | 
            +
              //ParseIntegerProperty(&wrapR, err, o, "wrapR", false);  // tinygltf extension
         | 
| 4891 5097 |  | 
| 4892 5098 | 
             
              // TODO(syoyo): Check the value is alloed one.
         | 
| 4893 5099 | 
             
              // (e.g. we allow 9728(NEAREST), but don't allow 9727)
         | 
| @@ -4896,7 +5102,7 @@ static bool ParseSampler(Sampler *sampler, std::string *err, const json &o, | |
| 4896 5102 | 
             
              sampler->magFilter = magFilter;
         | 
| 4897 5103 | 
             
              sampler->wrapS = wrapS;
         | 
| 4898 5104 | 
             
              sampler->wrapT = wrapT;
         | 
| 4899 | 
            -
              sampler->wrapR = wrapR;
         | 
| 5105 | 
            +
              //sampler->wrapR = wrapR;
         | 
| 4900 5106 |  | 
| 4901 5107 | 
             
              ParseExtensionsProperty(&(sampler->extensions), err, o);
         | 
| 4902 5108 | 
             
              ParseExtrasProperty(&(sampler->extras), o);
         | 
| @@ -5252,7 +5458,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, | |
| 5252 5458 |  | 
| 5253 5459 | 
             
            #if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
         | 
| 5254 5460 | 
             
                 defined(_CPPUNWIND)) &&                               \
         | 
| 5255 | 
            -
             | 
| 5461 | 
            +
                !defined(TINYGLTF_NOEXCEPTION)
         | 
| 5256 5462 | 
             
              try {
         | 
| 5257 5463 | 
             
                JsonParse(v, json_str, json_str_length, true);
         | 
| 5258 5464 |  | 
| @@ -5525,7 +5731,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, | |
| 5525 5731 |  | 
| 5526 5732 | 
             
              // Assign missing bufferView target types
         | 
| 5527 5733 | 
             
              // - Look for missing Mesh indices
         | 
| 5528 | 
            -
              // - Look for missing  | 
| 5734 | 
            +
              // - Look for missing Mesh attributes
         | 
| 5529 5735 | 
             
              for (auto &mesh : model->meshes) {
         | 
| 5530 5736 | 
             
                for (auto &primitive : mesh.primitives) {
         | 
| 5531 5737 | 
             
                  if (primitive.indices >
         | 
| @@ -5553,14 +5759,25 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, | |
| 5553 5759 | 
             
                    // we could optionally check if acessors' bufferView type is Scalar, as
         | 
| 5554 5760 | 
             
                    // it should be
         | 
| 5555 5761 | 
             
                  }
         | 
| 5556 | 
            -
             | 
| 5557 | 
            -
             | 
| 5558 | 
            -
             | 
| 5559 | 
            -
             | 
| 5560 | 
            -
             | 
| 5561 | 
            -
             | 
| 5562 | 
            -
             | 
| 5563 | 
            -
             | 
| 5762 | 
            +
             | 
| 5763 | 
            +
                  for (auto &attribute : primitive.attributes) {
         | 
| 5764 | 
            +
                    model
         | 
| 5765 | 
            +
                        ->bufferViews[size_t(
         | 
| 5766 | 
            +
                            model->accessors[size_t(attribute.second)].bufferView)]
         | 
| 5767 | 
            +
                        .target = TINYGLTF_TARGET_ARRAY_BUFFER;
         | 
| 5768 | 
            +
                  }
         | 
| 5769 | 
            +
             | 
| 5770 | 
            +
                  for (auto &target : primitive.targets) {
         | 
| 5771 | 
            +
                    for (auto &attribute : target) {
         | 
| 5772 | 
            +
                      auto bufferView =
         | 
| 5773 | 
            +
                          model->accessors[size_t(attribute.second)].bufferView;
         | 
| 5774 | 
            +
                      // bufferView could be null(-1) for sparse morph target
         | 
| 5775 | 
            +
                      if (bufferView >= 0) {
         | 
| 5776 | 
            +
                        model->bufferViews[size_t(bufferView)].target =
         | 
| 5777 | 
            +
                            TINYGLTF_TARGET_ARRAY_BUFFER;
         | 
| 5778 | 
            +
                      }
         | 
| 5779 | 
            +
                    }
         | 
| 5780 | 
            +
                  }
         | 
| 5564 5781 | 
             
                }
         | 
| 5565 5782 | 
             
              }
         | 
| 5566 5783 |  | 
| @@ -5612,13 +5829,13 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, | |
| 5612 5829 | 
             
                    {
         | 
| 5613 5830 | 
             
                      json_const_iterator it;
         | 
| 5614 5831 | 
             
                      if (FindMember(o, "extensions", it)) {
         | 
| 5615 | 
            -
                         | 
| 5832 | 
            +
                        scene.extensions_json_string = JsonToString(GetValue(it));
         | 
| 5616 5833 | 
             
                      }
         | 
| 5617 5834 | 
             
                    }
         | 
| 5618 5835 | 
             
                    {
         | 
| 5619 5836 | 
             
                      json_const_iterator it;
         | 
| 5620 5837 | 
             
                      if (FindMember(o, "extras", it)) {
         | 
| 5621 | 
            -
                         | 
| 5838 | 
            +
                        scene.extras_json_string = JsonToString(GetValue(it));
         | 
| 5622 5839 | 
             
                      }
         | 
| 5623 5840 | 
             
                    }
         | 
| 5624 5841 | 
             
                  }
         | 
| @@ -5668,6 +5885,18 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, | |
| 5668 5885 | 
             
              }
         | 
| 5669 5886 |  | 
| 5670 5887 | 
             
              // 11. Parse Image
         | 
| 5888 | 
            +
              void *load_image_user_data{nullptr};
         | 
| 5889 | 
            +
             | 
| 5890 | 
            +
              LoadImageDataOption load_image_option;
         | 
| 5891 | 
            +
             | 
| 5892 | 
            +
              if (user_image_loader_) {
         | 
| 5893 | 
            +
                // Use user supplied pointer
         | 
| 5894 | 
            +
                load_image_user_data = load_image_user_data_;
         | 
| 5895 | 
            +
              } else {
         | 
| 5896 | 
            +
                load_image_option.preserve_channels = preserve_image_channels_;
         | 
| 5897 | 
            +
                load_image_user_data = reinterpret_cast<void *>(&load_image_option);
         | 
| 5898 | 
            +
              }
         | 
| 5899 | 
            +
             | 
| 5671 5900 | 
             
              {
         | 
| 5672 5901 | 
             
                int idx = 0;
         | 
| 5673 5902 | 
             
                bool success = ForEachInArray(v, "images", [&](const json &o) {
         | 
| @@ -5680,7 +5909,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, | |
| 5680 5909 | 
             
                  Image image;
         | 
| 5681 5910 | 
             
                  if (!ParseImage(&image, idx, err, warn, o,
         | 
| 5682 5911 | 
             
                                  store_original_json_for_extras_and_extensions_, base_dir,
         | 
| 5683 | 
            -
                                  &fs, &this->LoadImageData,  | 
| 5912 | 
            +
                                  &fs, &this->LoadImageData, load_image_user_data)) {
         | 
| 5684 5913 | 
             
                    return false;
         | 
| 5685 5914 | 
             
                  }
         | 
| 5686 5915 |  | 
| @@ -5718,7 +5947,7 @@ bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, | |
| 5718 5947 | 
             
                    bool ret = LoadImageData(
         | 
| 5719 5948 | 
             
                        &image, idx, err, warn, image.width, image.height,
         | 
| 5720 5949 | 
             
                        &buffer.data[bufferView.byteOffset],
         | 
| 5721 | 
            -
                        static_cast<int>(bufferView.byteLength),  | 
| 5950 | 
            +
                        static_cast<int>(bufferView.byteLength), load_image_user_data);
         | 
| 5722 5951 | 
             
                    if (!ret) {
         | 
| 5723 5952 | 
             
                      return false;
         | 
| 5724 5953 | 
             
                    }
         | 
| @@ -6143,6 +6372,13 @@ static void SerializeNumberProperty(const std::string &key, T number, | |
| 6143 6372 | 
             
              JsonAddMember(obj, key.c_str(), json(number));
         | 
| 6144 6373 | 
             
            }
         | 
| 6145 6374 |  | 
| 6375 | 
            +
            #ifdef TINYGLTF_USE_RAPIDJSON
         | 
| 6376 | 
            +
            template <>
         | 
| 6377 | 
            +
            void SerializeNumberProperty(const std::string &key, size_t number, json &obj) {
         | 
| 6378 | 
            +
              JsonAddMember(obj, key.c_str(), json(static_cast<uint64_t>(number)));
         | 
| 6379 | 
            +
            }
         | 
| 6380 | 
            +
            #endif
         | 
| 6381 | 
            +
             | 
| 6146 6382 | 
             
            template <typename T>
         | 
| 6147 6383 | 
             
            static void SerializeNumberArrayProperty(const std::string &key,
         | 
| 6148 6384 | 
             
                                                     const std::vector<T> &value,
         | 
| @@ -6278,17 +6514,25 @@ static void SerializeValue(const std::string &key, const Value &value, | |
| 6278 6514 | 
             
            static void SerializeGltfBufferData(const std::vector<unsigned char> &data,
         | 
| 6279 6515 | 
             
                                                json &o) {
         | 
| 6280 6516 | 
             
              std::string header = "data:application/octet-stream;base64,";
         | 
| 6281 | 
            -
               | 
| 6282 | 
            -
             | 
| 6283 | 
            -
             | 
| 6517 | 
            +
              if (data.size() > 0) {
         | 
| 6518 | 
            +
                std::string encodedData =
         | 
| 6519 | 
            +
                    base64_encode(&data[0], static_cast<unsigned int>(data.size()));
         | 
| 6520 | 
            +
                SerializeStringProperty("uri", header + encodedData, o);
         | 
| 6521 | 
            +
              } else {
         | 
| 6522 | 
            +
                // Issue #229
         | 
| 6523 | 
            +
                // size 0 is allowd. Just emit mime header.
         | 
| 6524 | 
            +
                SerializeStringProperty("uri", header, o);
         | 
| 6525 | 
            +
              }
         | 
| 6284 6526 | 
             
            }
         | 
| 6285 6527 |  | 
| 6286 6528 | 
             
            static bool SerializeGltfBufferData(const std::vector<unsigned char> &data,
         | 
| 6287 6529 | 
             
                                                const std::string &binFilename) {
         | 
| 6288 6530 | 
             
            #ifdef _WIN32
         | 
| 6289 | 
            -
            #if defined(__GLIBCXX__) | 
| 6290 | 
            -
             | 
| 6291 | 
            -
             | 
| 6531 | 
            +
            #if defined(__GLIBCXX__)  // mingw
         | 
| 6532 | 
            +
              int file_descriptor = _wopen(UTF8ToWchar(binFilename).c_str(),
         | 
| 6533 | 
            +
                                           _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
         | 
| 6534 | 
            +
              __gnu_cxx::stdio_filebuf<char> wfile_buf(
         | 
| 6535 | 
            +
                  file_descriptor, std::ios_base::out | std::ios_base::binary);
         | 
| 6292 6536 | 
             
              std::ostream output(&wfile_buf);
         | 
| 6293 6537 | 
             
              if (!wfile_buf.is_open()) return false;
         | 
| 6294 6538 | 
             
            #elif defined(_MSC_VER)
         | 
| @@ -6302,8 +6546,14 @@ static bool SerializeGltfBufferData(const std::vector<unsigned char> &data, | |
| 6302 6546 | 
             
              std::ofstream output(binFilename.c_str(), std::ofstream::binary);
         | 
| 6303 6547 | 
             
              if (!output.is_open()) return false;
         | 
| 6304 6548 | 
             
            #endif
         | 
| 6305 | 
            -
               | 
| 6306 | 
            -
             | 
| 6549 | 
            +
              if (data.size() > 0) {
         | 
| 6550 | 
            +
                output.write(reinterpret_cast<const char *>(&data[0]),
         | 
| 6551 | 
            +
                             std::streamsize(data.size()));
         | 
| 6552 | 
            +
              } else {
         | 
| 6553 | 
            +
                // Issue #229
         | 
| 6554 | 
            +
                // size 0 will be still valid buffer data.
         | 
| 6555 | 
            +
                // write empty file.
         | 
| 6556 | 
            +
              }
         | 
| 6307 6557 | 
             
              return true;
         | 
| 6308 6558 | 
             
            }
         | 
| 6309 6559 |  | 
| @@ -6365,15 +6615,41 @@ static void SerializeExtensionMap(const ExtensionMap &extensions, json &o) { | |
| 6365 6615 | 
             
            }
         | 
| 6366 6616 |  | 
| 6367 6617 | 
             
            static void SerializeGltfAccessor(Accessor &accessor, json &o) {
         | 
| 6368 | 
            -
               | 
| 6618 | 
            +
              if (accessor.bufferView >= 0)
         | 
| 6619 | 
            +
                SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
         | 
| 6369 6620 |  | 
| 6370 | 
            -
              if (accessor.byteOffset != 0 | 
| 6621 | 
            +
              if (accessor.byteOffset != 0)
         | 
| 6371 6622 | 
             
                SerializeNumberProperty<int>("byteOffset", int(accessor.byteOffset), o);
         | 
| 6372 6623 |  | 
| 6373 6624 | 
             
              SerializeNumberProperty<int>("componentType", accessor.componentType, o);
         | 
| 6374 6625 | 
             
              SerializeNumberProperty<size_t>("count", accessor.count, o);
         | 
| 6375 | 
            -
             | 
| 6376 | 
            -
               | 
| 6626 | 
            +
             | 
| 6627 | 
            +
              if ((accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) ||
         | 
| 6628 | 
            +
                  (accessor.componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE)) {
         | 
| 6629 | 
            +
                SerializeNumberArrayProperty<double>("min", accessor.minValues, o);
         | 
| 6630 | 
            +
                SerializeNumberArrayProperty<double>("max", accessor.maxValues, o);
         | 
| 6631 | 
            +
              } else {
         | 
| 6632 | 
            +
                // Issue #301. Serialize as integer.
         | 
| 6633 | 
            +
                // Assume int value is within [-2**31-1, 2**31-1]
         | 
| 6634 | 
            +
                {
         | 
| 6635 | 
            +
                  std::vector<int> values;
         | 
| 6636 | 
            +
                  std::transform(accessor.minValues.begin(), accessor.minValues.end(),
         | 
| 6637 | 
            +
                                 std::back_inserter(values),
         | 
| 6638 | 
            +
                                 [](double v) { return static_cast<int>(v); });
         | 
| 6639 | 
            +
             | 
| 6640 | 
            +
                  SerializeNumberArrayProperty<int>("min", values, o);
         | 
| 6641 | 
            +
                }
         | 
| 6642 | 
            +
             | 
| 6643 | 
            +
                {
         | 
| 6644 | 
            +
                  std::vector<int> values;
         | 
| 6645 | 
            +
                  std::transform(accessor.maxValues.begin(), accessor.maxValues.end(),
         | 
| 6646 | 
            +
                                 std::back_inserter(values),
         | 
| 6647 | 
            +
                                 [](double v) { return static_cast<int>(v); });
         | 
| 6648 | 
            +
             | 
| 6649 | 
            +
                  SerializeNumberArrayProperty<int>("max", values, o);
         | 
| 6650 | 
            +
                }
         | 
| 6651 | 
            +
              }
         | 
| 6652 | 
            +
             | 
| 6377 6653 | 
             
              if (accessor.normalized)
         | 
| 6378 6654 | 
             
                SerializeValue("normalized", Value(accessor.normalized), o);
         | 
| 6379 6655 | 
             
              std::string type;
         | 
| @@ -6416,6 +6692,8 @@ static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) { | |
| 6416 6692 | 
             
                SerializeNumberProperty("node", channel.target_node, target);
         | 
| 6417 6693 | 
             
                SerializeStringProperty("path", channel.target_path, target);
         | 
| 6418 6694 |  | 
| 6695 | 
            +
                SerializeExtensionMap(channel.target_extensions, target);
         | 
| 6696 | 
            +
             | 
| 6419 6697 | 
             
                JsonAddMember(o, "target", std::move(target));
         | 
| 6420 6698 | 
             
              }
         | 
| 6421 6699 |  | 
| @@ -6455,6 +6733,7 @@ static void SerializeGltfAnimation(Animation &animation, json &o) { | |
| 6455 6733 |  | 
| 6456 6734 | 
             
              {
         | 
| 6457 6735 | 
             
                json samplers;
         | 
| 6736 | 
            +
                JsonReserveArray(samplers, animation.samplers.size());
         | 
| 6458 6737 | 
             
                for (unsigned int i = 0; i < animation.samplers.size(); ++i) {
         | 
| 6459 6738 | 
             
                  json sampler;
         | 
| 6460 6739 | 
             
                  AnimationSampler gltfSampler = animation.samplers[i];
         | 
| @@ -6480,10 +6759,15 @@ static void SerializeGltfAsset(Asset &asset, json &o) { | |
| 6480 6759 | 
             
                SerializeStringProperty("copyright", asset.copyright, o);
         | 
| 6481 6760 | 
             
              }
         | 
| 6482 6761 |  | 
| 6483 | 
            -
              if ( | 
| 6484 | 
            -
                 | 
| 6762 | 
            +
              if (asset.version.empty()) {
         | 
| 6763 | 
            +
                // Just in case
         | 
| 6764 | 
            +
                // `version` must be defined
         | 
| 6765 | 
            +
                asset.version = "2.0";
         | 
| 6485 6766 | 
             
              }
         | 
| 6486 6767 |  | 
| 6768 | 
            +
              // TODO(syoyo): Do we need to check if `version` is greater or equal to 2.0?
         | 
| 6769 | 
            +
              SerializeStringProperty("version", asset.version, o);
         | 
| 6770 | 
            +
             | 
| 6487 6771 | 
             
              if (asset.extras.Keys().size()) {
         | 
| 6488 6772 | 
             
                SerializeValue("extras", asset.extras, o);
         | 
| 6489 6773 | 
             
              }
         | 
| @@ -6491,10 +6775,10 @@ static void SerializeGltfAsset(Asset &asset, json &o) { | |
| 6491 6775 | 
             
              SerializeExtensionMap(asset.extensions, o);
         | 
| 6492 6776 | 
             
            }
         | 
| 6493 6777 |  | 
| 6494 | 
            -
             | 
| 6495 | 
            -
             | 
| 6778 | 
            +
            static void SerializeGltfBufferBin(Buffer &buffer, json &o,
         | 
| 6779 | 
            +
                                               std::vector<unsigned char> &binBuffer) {
         | 
| 6496 6780 | 
             
              SerializeNumberProperty("byteLength", buffer.data.size(), o);
         | 
| 6497 | 
            -
              binBuffer=buffer.data;
         | 
| 6781 | 
            +
              binBuffer = buffer.data;
         | 
| 6498 6782 |  | 
| 6499 6783 | 
             
              if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
         | 
| 6500 6784 |  | 
| @@ -6561,6 +6845,7 @@ static void SerializeGltfImage(Image &image, json &o) { | |
| 6561 6845 | 
             
                SerializeStringProperty("mimeType", image.mimeType, o);
         | 
| 6562 6846 | 
             
                SerializeNumberProperty<int>("bufferView", image.bufferView, o);
         | 
| 6563 6847 | 
             
              } else {
         | 
| 6848 | 
            +
                // TODO(syoyo): dlib::urilencode?
         | 
| 6564 6849 | 
             
                SerializeStringProperty("uri", image.uri, o);
         | 
| 6565 6850 | 
             
              }
         | 
| 6566 6851 |  | 
| @@ -6677,8 +6962,8 @@ static void SerializeGltfMaterial(Material &material, json &o) { | |
| 6677 6962 | 
             
                SerializeStringProperty("alphaMode", material.alphaMode, o);
         | 
| 6678 6963 | 
             
              }
         | 
| 6679 6964 |  | 
| 6680 | 
            -
              if(material.doubleSided != false)
         | 
| 6681 | 
            -
             | 
| 6965 | 
            +
              if (material.doubleSided != false)
         | 
| 6966 | 
            +
                JsonAddMember(o, "doubleSided", json(material.doubleSided));
         | 
| 6682 6967 |  | 
| 6683 6968 | 
             
              if (material.normalTexture.index > -1) {
         | 
| 6684 6969 | 
             
                json texinfo;
         | 
| @@ -6782,7 +7067,7 @@ static void SerializeGltfMesh(Mesh &mesh, json &o) { | |
| 6782 7067 | 
             
                  JsonAddMember(primitive, "targets", std::move(targets));
         | 
| 6783 7068 | 
             
                }
         | 
| 6784 7069 |  | 
| 6785 | 
            -
                SerializeExtensionMap(gltfPrimitive.extensions,  | 
| 7070 | 
            +
                SerializeExtensionMap(gltfPrimitive.extensions, primitive);
         | 
| 6786 7071 |  | 
| 6787 7072 | 
             
                if (gltfPrimitive.extras.Type() != NULL_TYPE) {
         | 
| 6788 7073 | 
             
                  SerializeValue("extras", gltfPrimitive.extras, primitive);
         | 
| @@ -6819,7 +7104,9 @@ static void SerializeSpotLight(SpotLight &spot, json &o) { | |
| 6819 7104 | 
             
            static void SerializeGltfLight(Light &light, json &o) {
         | 
| 6820 7105 | 
             
              if (!light.name.empty()) SerializeStringProperty("name", light.name, o);
         | 
| 6821 7106 | 
             
              SerializeNumberProperty("intensity", light.intensity, o);
         | 
| 6822 | 
            -
               | 
| 7107 | 
            +
              if (light.range > 0.0) {
         | 
| 7108 | 
            +
                SerializeNumberProperty("range", light.range, o);
         | 
| 7109 | 
            +
              }
         | 
| 6823 7110 | 
             
              SerializeNumberArrayProperty("color", light.color, o);
         | 
| 6824 7111 | 
             
              SerializeStringProperty("type", light.type, o);
         | 
| 6825 7112 | 
             
              if (light.type == "spot") {
         | 
| @@ -6878,7 +7165,7 @@ static void SerializeGltfSampler(Sampler &sampler, json &o) { | |
| 6878 7165 | 
             
              if (sampler.minFilter != -1) {
         | 
| 6879 7166 | 
             
                SerializeNumberProperty("minFilter", sampler.minFilter, o);
         | 
| 6880 7167 | 
             
              }
         | 
| 6881 | 
            -
              SerializeNumberProperty("wrapR", sampler.wrapR, o);
         | 
| 7168 | 
            +
              //SerializeNumberProperty("wrapR", sampler.wrapR, o);
         | 
| 6882 7169 | 
             
              SerializeNumberProperty("wrapS", sampler.wrapS, o);
         | 
| 6883 7170 | 
             
              SerializeNumberProperty("wrapT", sampler.wrapT, o);
         | 
| 6884 7171 |  | 
| @@ -6933,6 +7220,11 @@ static void SerializeGltfCamera(const Camera &camera, json &o) { | |
| 6933 7220 | 
             
              } else {
         | 
| 6934 7221 | 
             
                // ???
         | 
| 6935 7222 | 
             
              }
         | 
| 7223 | 
            +
             | 
| 7224 | 
            +
              if (camera.extras.Type() != NULL_TYPE) {
         | 
| 7225 | 
            +
                SerializeValue("extras", camera.extras, o);
         | 
| 7226 | 
            +
              }
         | 
| 7227 | 
            +
              SerializeExtensionMap(camera.extensions, o);
         | 
| 6936 7228 | 
             
            }
         | 
| 6937 7229 |  | 
| 6938 7230 | 
             
            static void SerializeGltfScene(Scene &scene, json &o) {
         | 
| @@ -6948,11 +7240,17 @@ static void SerializeGltfScene(Scene &scene, json &o) { | |
| 6948 7240 | 
             
            }
         | 
| 6949 7241 |  | 
| 6950 7242 | 
             
            static void SerializeGltfSkin(Skin &skin, json &o) {
         | 
| 6951 | 
            -
               | 
| 7243 | 
            +
              // required
         | 
| 7244 | 
            +
              SerializeNumberArrayProperty<int>("joints", skin.joints, o);
         | 
| 7245 | 
            +
             | 
| 7246 | 
            +
              if (skin.inverseBindMatrices >= 0) {
         | 
| 6952 7247 | 
             
                SerializeNumberProperty("inverseBindMatrices", skin.inverseBindMatrices, o);
         | 
| 7248 | 
            +
              }
         | 
| 7249 | 
            +
             | 
| 7250 | 
            +
              if (skin.skeleton >= 0) {
         | 
| 7251 | 
            +
                SerializeNumberProperty("skeleton", skin.skeleton, o);
         | 
| 7252 | 
            +
              }
         | 
| 6953 7253 |  | 
| 6954 | 
            -
              SerializeNumberArrayProperty<int>("joints", skin.joints, o);
         | 
| 6955 | 
            -
              SerializeNumberProperty("skeleton", skin.skeleton, o);
         | 
| 6956 7254 | 
             
              if (skin.name.size()) {
         | 
| 6957 7255 | 
             
                SerializeStringProperty("name", skin.name, o);
         | 
| 6958 7256 | 
             
              }
         | 
| @@ -6979,14 +7277,16 @@ static void SerializeGltfTexture(Texture &texture, json &o) { | |
| 6979 7277 | 
             
            ///
         | 
| 6980 7278 | 
             
            static void SerializeGltfModel(Model *model, json &o) {
         | 
| 6981 7279 | 
             
              // ACCESSORS
         | 
| 6982 | 
            -
               | 
| 6983 | 
            -
             | 
| 6984 | 
            -
             | 
| 6985 | 
            -
                 | 
| 6986 | 
            -
             | 
| 6987 | 
            -
             | 
| 7280 | 
            +
              if (model->accessors.size()) {
         | 
| 7281 | 
            +
                json accessors;
         | 
| 7282 | 
            +
                JsonReserveArray(accessors, model->accessors.size());
         | 
| 7283 | 
            +
                for (unsigned int i = 0; i < model->accessors.size(); ++i) {
         | 
| 7284 | 
            +
                  json accessor;
         | 
| 7285 | 
            +
                  SerializeGltfAccessor(model->accessors[i], accessor);
         | 
| 7286 | 
            +
                  JsonPushBack(accessors, std::move(accessor));
         | 
| 7287 | 
            +
                }
         | 
| 7288 | 
            +
                JsonAddMember(o, "accessors", std::move(accessors));
         | 
| 6988 7289 | 
             
              }
         | 
| 6989 | 
            -
              JsonAddMember(o, "accessors", std::move(accessors));
         | 
| 6990 7290 |  | 
| 6991 7291 | 
             
              // ANIMATIONS
         | 
| 6992 7292 | 
             
              if (model->animations.size()) {
         | 
| @@ -7009,18 +7309,15 @@ static void SerializeGltfModel(Model *model, json &o) { | |
| 7009 7309 | 
             
              JsonAddMember(o, "asset", std::move(asset));
         | 
| 7010 7310 |  | 
| 7011 7311 | 
             
              // BUFFERVIEWS
         | 
| 7012 | 
            -
               | 
| 7013 | 
            -
             | 
| 7014 | 
            -
             | 
| 7015 | 
            -
                 | 
| 7016 | 
            -
             | 
| 7017 | 
            -
             | 
| 7018 | 
            -
             | 
| 7019 | 
            -
             | 
| 7020 | 
            -
             | 
| 7021 | 
            -
              // Extensions used
         | 
| 7022 | 
            -
              if (model->extensionsUsed.size()) {
         | 
| 7023 | 
            -
                SerializeStringArrayProperty("extensionsUsed", model->extensionsUsed, o);
         | 
| 7312 | 
            +
              if (model->bufferViews.size()) {
         | 
| 7313 | 
            +
                json bufferViews;
         | 
| 7314 | 
            +
                JsonReserveArray(bufferViews, model->bufferViews.size());
         | 
| 7315 | 
            +
                for (unsigned int i = 0; i < model->bufferViews.size(); ++i) {
         | 
| 7316 | 
            +
                  json bufferView;
         | 
| 7317 | 
            +
                  SerializeGltfBufferView(model->bufferViews[i], bufferView);
         | 
| 7318 | 
            +
                  JsonPushBack(bufferViews, std::move(bufferView));
         | 
| 7319 | 
            +
                }
         | 
| 7320 | 
            +
                JsonAddMember(o, "bufferViews", std::move(bufferViews));
         | 
| 7024 7321 | 
             
              }
         | 
| 7025 7322 |  | 
| 7026 7323 | 
             
              // Extensions required
         | 
| @@ -7036,6 +7333,16 @@ static void SerializeGltfModel(Model *model, json &o) { | |
| 7036 7333 | 
             
                for (unsigned int i = 0; i < model->materials.size(); ++i) {
         | 
| 7037 7334 | 
             
                  json material;
         | 
| 7038 7335 | 
             
                  SerializeGltfMaterial(model->materials[i], material);
         | 
| 7336 | 
            +
             | 
| 7337 | 
            +
                  if (JsonIsNull(material)) {
         | 
| 7338 | 
            +
                    // Issue 294.
         | 
| 7339 | 
            +
                    // `material` does not have any required parameters
         | 
| 7340 | 
            +
                    // so the result may be null(unmodified) when all material parameters
         | 
| 7341 | 
            +
                    // have default value.
         | 
| 7342 | 
            +
                    //
         | 
| 7343 | 
            +
                    // null is not allowed thus we create an empty JSON object.
         | 
| 7344 | 
            +
                    JsonSetObject(material);
         | 
| 7345 | 
            +
                  }
         | 
| 7039 7346 | 
             
                  JsonPushBack(materials, std::move(material));
         | 
| 7040 7347 | 
             
                }
         | 
| 7041 7348 | 
             
                JsonAddMember(o, "materials", std::move(materials));
         | 
| @@ -7133,7 +7440,9 @@ static void SerializeGltfModel(Model *model, json &o) { | |
| 7133 7440 | 
             
              // EXTENSIONS
         | 
| 7134 7441 | 
             
              SerializeExtensionMap(model->extensions, o);
         | 
| 7135 7442 |  | 
| 7136 | 
            -
               | 
| 7443 | 
            +
              auto extensionsUsed = model->extensionsUsed;
         | 
| 7444 | 
            +
             | 
| 7445 | 
            +
              // LIGHTS as KHR_lights_punctual
         | 
| 7137 7446 | 
             
              if (model->lights.size()) {
         | 
| 7138 7447 | 
             
                json lights;
         | 
| 7139 7448 | 
             
                JsonReserveArray(lights, model->lights.size());
         | 
| @@ -7148,7 +7457,7 @@ static void SerializeGltfModel(Model *model, json &o) { | |
| 7148 7457 |  | 
| 7149 7458 | 
             
                {
         | 
| 7150 7459 | 
             
                  json_const_iterator it;
         | 
| 7151 | 
            -
                  if ( | 
| 7460 | 
            +
                  if (FindMember(o, "extensions", it)) {
         | 
| 7152 7461 | 
             
                    JsonAssign(ext_j, GetValue(it));
         | 
| 7153 7462 | 
             
                  }
         | 
| 7154 7463 | 
             
                }
         | 
| @@ -7156,6 +7465,24 @@ static void SerializeGltfModel(Model *model, json &o) { | |
| 7156 7465 | 
             
                JsonAddMember(ext_j, "KHR_lights_punctual", std::move(khr_lights_cmn));
         | 
| 7157 7466 |  | 
| 7158 7467 | 
             
                JsonAddMember(o, "extensions", std::move(ext_j));
         | 
| 7468 | 
            +
             | 
| 7469 | 
            +
                // Also add "KHR_lights_punctual" to `extensionsUsed`
         | 
| 7470 | 
            +
                {
         | 
| 7471 | 
            +
                  auto has_khr_lights_punctual =
         | 
| 7472 | 
            +
                      std::find_if(extensionsUsed.begin(), extensionsUsed.end(),
         | 
| 7473 | 
            +
                                   [](const std::string &s) {
         | 
| 7474 | 
            +
                                     return (s.compare("KHR_lights_punctual") == 0);
         | 
| 7475 | 
            +
                                   });
         | 
| 7476 | 
            +
             | 
| 7477 | 
            +
                  if (has_khr_lights_punctual == extensionsUsed.end()) {
         | 
| 7478 | 
            +
                    extensionsUsed.push_back("KHR_lights_punctual");
         | 
| 7479 | 
            +
                  }
         | 
| 7480 | 
            +
                }
         | 
| 7481 | 
            +
              }
         | 
| 7482 | 
            +
             | 
| 7483 | 
            +
              // Extensions used
         | 
| 7484 | 
            +
              if (extensionsUsed.size()) {
         | 
| 7485 | 
            +
                SerializeStringArrayProperty("extensionsUsed", extensionsUsed, o);
         | 
| 7159 7486 | 
             
              }
         | 
| 7160 7487 |  | 
| 7161 7488 | 
             
              // EXTRAS
         | 
| @@ -7175,8 +7502,10 @@ static bool WriteGltfFile(const std::string &output, | |
| 7175 7502 | 
             
            #if defined(_MSC_VER)
         | 
| 7176 7503 | 
             
              std::ofstream gltfFile(UTF8ToWchar(output).c_str());
         | 
| 7177 7504 | 
             
            #elif defined(__GLIBCXX__)
         | 
| 7178 | 
            -
              int file_descriptor = _wopen(UTF8ToWchar(output).c_str(), | 
| 7179 | 
            -
             | 
| 7505 | 
            +
              int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
         | 
| 7506 | 
            +
                                           _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
         | 
| 7507 | 
            +
              __gnu_cxx::stdio_filebuf<char> wfile_buf(
         | 
| 7508 | 
            +
                  file_descriptor, std::ios_base::out | std::ios_base::binary);
         | 
| 7180 7509 | 
             
              std::ostream gltfFile(&wfile_buf);
         | 
| 7181 7510 | 
             
              if (!wfile_buf.is_open()) return false;
         | 
| 7182 7511 | 
             
            #else
         | 
| @@ -7196,33 +7525,25 @@ static void WriteBinaryGltfStream(std::ostream &stream, | |
| 7196 7525 | 
             
              const std::string header = "glTF";
         | 
| 7197 7526 | 
             
              const int version = 2;
         | 
| 7198 7527 |  | 
| 7199 | 
            -
               | 
| 7200 | 
            -
               | 
| 7201 | 
            -
               | 
| 7202 | 
            -
             | 
| 7203 | 
            -
             | 
| 7204 | 
            -
             | 
| 7205 | 
            -
                  uint32_t remainder = numToRound % multiple;
         | 
| 7206 | 
            -
                  if (remainder == 0)
         | 
| 7207 | 
            -
                      return numToRound;
         | 
| 7208 | 
            -
             | 
| 7209 | 
            -
                  return numToRound + multiple - remainder;
         | 
| 7210 | 
            -
              };
         | 
| 7211 | 
            -
             | 
| 7212 | 
            -
              const uint32_t padding_size = roundUp(content.size(), 4) - content.size();
         | 
| 7528 | 
            +
              const uint32_t content_size = uint32_t(content.size());
         | 
| 7529 | 
            +
              const uint32_t binBuffer_size = uint32_t(binBuffer.size());
         | 
| 7530 | 
            +
              // determine number of padding bytes required to ensure 4 byte alignment
         | 
| 7531 | 
            +
              const uint32_t content_padding_size = content_size % 4 == 0 ? 0 : 4 - content_size % 4;
         | 
| 7532 | 
            +
              const uint32_t bin_padding_size = binBuffer_size % 4 == 0 ? 0 : 4 - binBuffer_size % 4;
         | 
| 7213 7533 |  | 
| 7214 7534 | 
             
              // 12 bytes for header, JSON content length, 8 bytes for JSON chunk info.
         | 
| 7215 | 
            -
              // Chunk data must be located at 4-byte boundary | 
| 7216 | 
            -
              const  | 
| 7217 | 
            -
                   | 
| 7535 | 
            +
              // Chunk data must be located at 4-byte boundary, which may require padding
         | 
| 7536 | 
            +
              const uint32_t length =
         | 
| 7537 | 
            +
                  12 + 8 + content_size + content_padding_size +
         | 
| 7538 | 
            +
                  (binBuffer_size ? (8 + binBuffer_size + bin_padding_size) : 0);
         | 
| 7218 7539 |  | 
| 7219 7540 | 
             
              stream.write(header.c_str(), std::streamsize(header.size()));
         | 
| 7220 7541 | 
             
              stream.write(reinterpret_cast<const char *>(&version), sizeof(version));
         | 
| 7221 7542 | 
             
              stream.write(reinterpret_cast<const char *>(&length), sizeof(length));
         | 
| 7222 7543 |  | 
| 7223 7544 | 
             
              // JSON chunk info, then JSON data
         | 
| 7224 | 
            -
              const  | 
| 7225 | 
            -
              const  | 
| 7545 | 
            +
              const uint32_t model_length = uint32_t(content.size()) + content_padding_size;
         | 
| 7546 | 
            +
              const uint32_t model_format = 0x4E4F534A;
         | 
| 7226 7547 | 
             
              stream.write(reinterpret_cast<const char *>(&model_length),
         | 
| 7227 7548 | 
             
                           sizeof(model_length));
         | 
| 7228 7549 | 
             
              stream.write(reinterpret_cast<const char *>(&model_format),
         | 
| @@ -7230,24 +7551,26 @@ static void WriteBinaryGltfStream(std::ostream &stream, | |
| 7230 7551 | 
             
              stream.write(content.c_str(), std::streamsize(content.size()));
         | 
| 7231 7552 |  | 
| 7232 7553 | 
             
              // Chunk must be multiplies of 4, so pad with spaces
         | 
| 7233 | 
            -
              if ( | 
| 7234 | 
            -
                const std::string padding = std::string(size_t( | 
| 7554 | 
            +
              if (content_padding_size > 0) {
         | 
| 7555 | 
            +
                const std::string padding = std::string(size_t(content_padding_size), ' ');
         | 
| 7235 7556 | 
             
                stream.write(padding.c_str(), std::streamsize(padding.size()));
         | 
| 7236 7557 | 
             
              }
         | 
| 7237 | 
            -
              if (binBuffer.size() > 0){
         | 
| 7238 | 
            -
                const uint32_t bin_padding_size = roundUp(binBuffer.size(), 4) - binBuffer.size();
         | 
| 7558 | 
            +
              if (binBuffer.size() > 0) {
         | 
| 7239 7559 | 
             
                // BIN chunk info, then BIN data
         | 
| 7240 | 
            -
                const  | 
| 7241 | 
            -
                const  | 
| 7560 | 
            +
                const uint32_t bin_length = uint32_t(binBuffer.size()) + bin_padding_size;
         | 
| 7561 | 
            +
                const uint32_t bin_format = 0x004e4942;
         | 
| 7242 7562 | 
             
                stream.write(reinterpret_cast<const char *>(&bin_length),
         | 
| 7243 | 
            -
             | 
| 7563 | 
            +
                             sizeof(bin_length));
         | 
| 7244 7564 | 
             
                stream.write(reinterpret_cast<const char *>(&bin_format),
         | 
| 7245 | 
            -
             | 
| 7246 | 
            -
                stream.write( | 
| 7565 | 
            +
                             sizeof(bin_format));
         | 
| 7566 | 
            +
                stream.write(reinterpret_cast<const char *>(binBuffer.data()),
         | 
| 7567 | 
            +
                             std::streamsize(binBuffer.size()));
         | 
| 7247 7568 | 
             
                // Chunksize must be multiplies of 4, so pad with zeroes
         | 
| 7248 7569 | 
             
                if (bin_padding_size > 0) {
         | 
| 7249 | 
            -
                  const std::vector<unsigned char> padding = | 
| 7250 | 
            -
             | 
| 7570 | 
            +
                  const std::vector<unsigned char> padding =
         | 
| 7571 | 
            +
                      std::vector<unsigned char>(size_t(bin_padding_size), 0);
         | 
| 7572 | 
            +
                  stream.write(reinterpret_cast<const char *>(padding.data()),
         | 
| 7573 | 
            +
                               std::streamsize(padding.size()));
         | 
| 7251 7574 | 
             
                }
         | 
| 7252 7575 | 
             
              }
         | 
| 7253 7576 | 
             
            }
         | 
| @@ -7259,8 +7582,10 @@ static void WriteBinaryGltfFile(const std::string &output, | |
| 7259 7582 | 
             
            #if defined(_MSC_VER)
         | 
| 7260 7583 | 
             
              std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary);
         | 
| 7261 7584 | 
             
            #elif defined(__GLIBCXX__)
         | 
| 7262 | 
            -
              int file_descriptor = _wopen(UTF8ToWchar(output).c_str(), | 
| 7263 | 
            -
             | 
| 7585 | 
            +
              int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
         | 
| 7586 | 
            +
                                           _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
         | 
| 7587 | 
            +
              __gnu_cxx::stdio_filebuf<char> wfile_buf(
         | 
| 7588 | 
            +
                  file_descriptor, std::ios_base::out | std::ios_base::binary);
         | 
| 7264 7589 | 
             
              std::ostream gltfFile(&wfile_buf);
         | 
| 7265 7590 | 
             
            #else
         | 
| 7266 7591 | 
             
              std::ofstream gltfFile(output.c_str(), std::ios::binary);
         | 
| @@ -7268,7 +7593,7 @@ static void WriteBinaryGltfFile(const std::string &output, | |
| 7268 7593 | 
             
            #else
         | 
| 7269 7594 | 
             
              std::ofstream gltfFile(output.c_str(), std::ios::binary);
         | 
| 7270 7595 | 
             
            #endif
         | 
| 7271 | 
            -
              WriteBinaryGltfStream(gltfFile, content,binBuffer);
         | 
| 7596 | 
            +
              WriteBinaryGltfStream(gltfFile, content, binBuffer);
         | 
| 7272 7597 | 
             
            }
         | 
| 7273 7598 |  | 
| 7274 7599 | 
             
            bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
         | 
| @@ -7280,20 +7605,21 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream, | |
| 7280 7605 | 
             
              SerializeGltfModel(model, output);
         | 
| 7281 7606 |  | 
| 7282 7607 | 
             
              // BUFFERS
         | 
| 7283 | 
            -
              std::vector<std::string> usedUris;
         | 
| 7284 7608 | 
             
              std::vector<unsigned char> binBuffer;
         | 
| 7285 | 
            -
               | 
| 7286 | 
            -
             | 
| 7287 | 
            -
             | 
| 7288 | 
            -
                 | 
| 7289 | 
            -
             | 
| 7290 | 
            -
                   | 
| 7291 | 
            -
             | 
| 7292 | 
            -
                   | 
| 7609 | 
            +
              if (model->buffers.size()) {
         | 
| 7610 | 
            +
                json buffers;
         | 
| 7611 | 
            +
                JsonReserveArray(buffers, model->buffers.size());
         | 
| 7612 | 
            +
                for (unsigned int i = 0; i < model->buffers.size(); ++i) {
         | 
| 7613 | 
            +
                  json buffer;
         | 
| 7614 | 
            +
                  if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
         | 
| 7615 | 
            +
                    SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
         | 
| 7616 | 
            +
                  } else {
         | 
| 7617 | 
            +
                    SerializeGltfBuffer(model->buffers[i], buffer);
         | 
| 7618 | 
            +
                  }
         | 
| 7619 | 
            +
                  JsonPushBack(buffers, std::move(buffer));
         | 
| 7293 7620 | 
             
                }
         | 
| 7294 | 
            -
                 | 
| 7621 | 
            +
                JsonAddMember(output, "buffers", std::move(buffers));
         | 
| 7295 7622 | 
             
              }
         | 
| 7296 | 
            -
              JsonAddMember(output, "buffers", std::move(buffers));
         | 
| 7297 7623 |  | 
| 7298 7624 | 
             
              // IMAGES
         | 
| 7299 7625 | 
             
              if (model->images.size()) {
         | 
| @@ -7303,9 +7629,9 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream, | |
| 7303 7629 | 
             
                  json image;
         | 
| 7304 7630 |  | 
| 7305 7631 | 
             
                  std::string dummystring = "";
         | 
| 7306 | 
            -
                  // UpdateImageObject need baseDir but only uses it if  | 
| 7307 | 
            -
                  //  | 
| 7308 | 
            -
                  //  | 
| 7632 | 
            +
                  // UpdateImageObject need baseDir but only uses it if embeddedImages is
         | 
| 7633 | 
            +
                  // enabled, since we won't write separate images when writing to a stream
         | 
| 7634 | 
            +
                  // we
         | 
| 7309 7635 | 
             
                  UpdateImageObject(model->images[i], dummystring, int(i), false,
         | 
| 7310 7636 | 
             
                                    &this->WriteImageData, this->write_image_user_data_);
         | 
| 7311 7637 | 
             
                  SerializeGltfImage(model->images[i], image);
         | 
| @@ -7315,7 +7641,7 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream, | |
| 7315 7641 | 
             
              }
         | 
| 7316 7642 |  | 
| 7317 7643 | 
             
              if (writeBinary) {
         | 
| 7318 | 
            -
                WriteBinaryGltfStream(stream, JsonToString(output),binBuffer);
         | 
| 7644 | 
            +
                WriteBinaryGltfStream(stream, JsonToString(output), binBuffer);
         | 
| 7319 7645 | 
             
              } else {
         | 
| 7320 7646 | 
             
                WriteGltfStream(stream, JsonToString(output, prettyPrint ? 2 : -1));
         | 
| 7321 7647 | 
             
              }
         | 
| @@ -7347,44 +7673,47 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename, | |
| 7347 7673 | 
             
              // BUFFERS
         | 
| 7348 7674 | 
             
              std::vector<std::string> usedUris;
         | 
| 7349 7675 | 
             
              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;
         | 
| 7676 | 
            +
              if (model->buffers.size()) {
         | 
| 7677 | 
            +
                json buffers;
         | 
| 7678 | 
            +
                JsonReserveArray(buffers, model->buffers.size());
         | 
| 7679 | 
            +
                for (unsigned int i = 0; i < model->buffers.size(); ++i) {
         | 
| 7680 | 
            +
                  json buffer;
         | 
| 7681 | 
            +
                  if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
         | 
| 7682 | 
            +
                    SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
         | 
| 7683 | 
            +
                  } else if (embedBuffers) {
         | 
| 7684 | 
            +
                    SerializeGltfBuffer(model->buffers[i], buffer);
         | 
| 7363 7685 | 
             
                  } else {
         | 
| 7364 | 
            -
                     | 
| 7365 | 
            -
                     | 
| 7366 | 
            -
                     | 
| 7367 | 
            -
             | 
| 7368 | 
            -
                       | 
| 7369 | 
            -
             | 
| 7370 | 
            -
             | 
| 7371 | 
            -
             | 
| 7372 | 
            -
             | 
| 7373 | 
            -
             | 
| 7374 | 
            -
                         | 
| 7686 | 
            +
                    std::string binSavePath;
         | 
| 7687 | 
            +
                    std::string binUri;
         | 
| 7688 | 
            +
                    if (!model->buffers[i].uri.empty() &&
         | 
| 7689 | 
            +
                        !IsDataURI(model->buffers[i].uri)) {
         | 
| 7690 | 
            +
                      binUri = model->buffers[i].uri;
         | 
| 7691 | 
            +
                    } else {
         | 
| 7692 | 
            +
                      binUri = defaultBinFilename + defaultBinFileExt;
         | 
| 7693 | 
            +
                      bool inUse = true;
         | 
| 7694 | 
            +
                      int numUsed = 0;
         | 
| 7695 | 
            +
                      while (inUse) {
         | 
| 7696 | 
            +
                        inUse = false;
         | 
| 7697 | 
            +
                        for (const std::string &usedName : usedUris) {
         | 
| 7698 | 
            +
                          if (binUri.compare(usedName) != 0) continue;
         | 
| 7699 | 
            +
                          inUse = true;
         | 
| 7700 | 
            +
                          binUri = defaultBinFilename + std::to_string(numUsed++) +
         | 
| 7701 | 
            +
                                   defaultBinFileExt;
         | 
| 7702 | 
            +
                          break;
         | 
| 7703 | 
            +
                        }
         | 
| 7375 7704 | 
             
                      }
         | 
| 7376 7705 | 
             
                    }
         | 
| 7706 | 
            +
                    usedUris.push_back(binUri);
         | 
| 7707 | 
            +
                    binSavePath = JoinPath(baseDir, binUri);
         | 
| 7708 | 
            +
                    if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
         | 
| 7709 | 
            +
                                             binUri)) {
         | 
| 7710 | 
            +
                      return false;
         | 
| 7711 | 
            +
                    }
         | 
| 7377 7712 | 
             
                  }
         | 
| 7378 | 
            -
                   | 
| 7379 | 
            -
                  binSavePath = JoinPath(baseDir, binUri);
         | 
| 7380 | 
            -
                  if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
         | 
| 7381 | 
            -
                                           binUri)) {
         | 
| 7382 | 
            -
                    return false;
         | 
| 7383 | 
            -
                  }
         | 
| 7713 | 
            +
                  JsonPushBack(buffers, std::move(buffer));
         | 
| 7384 7714 | 
             
                }
         | 
| 7385 | 
            -
                 | 
| 7715 | 
            +
                JsonAddMember(output, "buffers", std::move(buffers));
         | 
| 7386 7716 | 
             
              }
         | 
| 7387 | 
            -
              JsonAddMember(output, "buffers", std::move(buffers));
         | 
| 7388 7717 |  | 
| 7389 7718 | 
             
              // IMAGES
         | 
| 7390 7719 | 
             
              if (model->images.size()) {
         | 
| @@ -7402,7 +7731,7 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename, | |
| 7402 7731 | 
             
              }
         | 
| 7403 7732 |  | 
| 7404 7733 | 
             
              if (writeBinary) {
         | 
| 7405 | 
            -
                WriteBinaryGltfFile(filename, JsonToString(output),binBuffer);
         | 
| 7734 | 
            +
                WriteBinaryGltfFile(filename, JsonToString(output), binBuffer);
         | 
| 7406 7735 | 
             
              } else {
         | 
| 7407 7736 | 
             
                WriteGltfFile(filename, JsonToString(output, (prettyPrint ? 2 : -1)));
         | 
| 7408 7737 | 
             
              }
         |