| // |
| // Header-only tiny glTF 2.0 loader and serializer. |
| // |
| // |
| // The MIT License (MIT) |
| // |
| // Copyright (c) 2015 - Present Syoyo Fujita, Aurélien Chatelain and many |
| // contributors. |
| // |
| // Permission is hereby granted, free of charge, to any person obtaining a copy |
| // of this software and associated documentation files (the "Software"), to deal |
| // in the Software without restriction, including without limitation the rights |
| // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| // copies of the Software, and to permit persons to whom the Software is |
| // furnished to do so, subject to the following conditions: |
| // |
| // The above copyright notice and this permission notice shall be included in |
| // all copies or substantial portions of the Software. |
| // |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| // THE SOFTWARE. |
| |
| // Version: - v2.9.* |
| // See https://github.com/syoyo/tinygltf/releases for release history. |
| // |
| // Tiny glTF loader is using following third party libraries: |
| // |
| // - jsonhpp: C++ JSON library. |
| // - base64: base64 decode/encode library. |
| // - stb_image: Image loading library. |
| // |
| #ifndef TINY_GLTF_H_ |
| #define TINY_GLTF_H_ |
| |
| #include <array> |
| #include <cassert> |
| #include <cmath> // std::fabs |
| #include <cstdint> |
| #include <cstdlib> |
| #include <cstring> |
| #include <functional> |
| #include <limits> |
| #include <map> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| // Auto-detect C++14 standard version |
| #if !defined(TINYGLTF_USE_CPP14) && defined(__cplusplus) && \ |
| (__cplusplus >= 201402L) |
| #define TINYGLTF_USE_CPP14 |
| #endif |
| |
| #ifdef __ANDROID__ |
| #ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS |
| #include <android/asset_manager.h> |
| #endif |
| #endif |
| |
| #ifdef __GNUC__ |
| #if (__GNUC__ < 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ <= 8)) |
| #define TINYGLTF_NOEXCEPT |
| #else |
| #define TINYGLTF_NOEXCEPT noexcept |
| #endif |
| #else |
| #define TINYGLTF_NOEXCEPT noexcept |
| #endif |
| |
| #define DEFAULT_METHODS(x) \ |
| ~x() = default; \ |
| x(const x &) = default; \ |
| x(x &&) TINYGLTF_NOEXCEPT = default; \ |
| x &operator=(const x &) = default; \ |
| x &operator=(x &&) TINYGLTF_NOEXCEPT = default; |
| |
| namespace tinygltf { |
| |
| #define TINYGLTF_MODE_POINTS (0) |
| #define TINYGLTF_MODE_LINE (1) |
| #define TINYGLTF_MODE_LINE_LOOP (2) |
| #define TINYGLTF_MODE_LINE_STRIP (3) |
| #define TINYGLTF_MODE_TRIANGLES (4) |
| #define TINYGLTF_MODE_TRIANGLE_STRIP (5) |
| #define TINYGLTF_MODE_TRIANGLE_FAN (6) |
| |
| #define TINYGLTF_COMPONENT_TYPE_BYTE (5120) |
| #define TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE (5121) |
| #define TINYGLTF_COMPONENT_TYPE_SHORT (5122) |
| #define TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT (5123) |
| #define TINYGLTF_COMPONENT_TYPE_INT (5124) |
| #define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125) |
| #define TINYGLTF_COMPONENT_TYPE_FLOAT (5126) |
| #define TINYGLTF_COMPONENT_TYPE_DOUBLE \ |
| (5130) // OpenGL double type. Note that some of glTF 2.0 validator does not |
| // support double type even the schema seems allow any value of |
| // integer: |
| // https://github.com/KhronosGroup/glTF/blob/b9884a2fd45130b4d673dd6c8a706ee21ee5c5f7/specification/2.0/schema/accessor.schema.json#L22 |
| |
| #define TINYGLTF_TEXTURE_FILTER_NEAREST (9728) |
| #define TINYGLTF_TEXTURE_FILTER_LINEAR (9729) |
| #define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST (9984) |
| #define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST (9985) |
| #define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR (9986) |
| #define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR (9987) |
| |
| #define TINYGLTF_TEXTURE_WRAP_REPEAT (10497) |
| #define TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE (33071) |
| #define TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT (33648) |
| |
| // Redeclarations of the above for technique.parameters. |
| #define TINYGLTF_PARAMETER_TYPE_BYTE (5120) |
| #define TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE (5121) |
| #define TINYGLTF_PARAMETER_TYPE_SHORT (5122) |
| #define TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT (5123) |
| #define TINYGLTF_PARAMETER_TYPE_INT (5124) |
| #define TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT (5125) |
| #define TINYGLTF_PARAMETER_TYPE_FLOAT (5126) |
| |
| #define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC2 (35664) |
| #define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC3 (35665) |
| #define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC4 (35666) |
| |
| #define TINYGLTF_PARAMETER_TYPE_INT_VEC2 (35667) |
| #define TINYGLTF_PARAMETER_TYPE_INT_VEC3 (35668) |
| #define TINYGLTF_PARAMETER_TYPE_INT_VEC4 (35669) |
| |
| #define TINYGLTF_PARAMETER_TYPE_BOOL (35670) |
| #define TINYGLTF_PARAMETER_TYPE_BOOL_VEC2 (35671) |
| #define TINYGLTF_PARAMETER_TYPE_BOOL_VEC3 (35672) |
| #define TINYGLTF_PARAMETER_TYPE_BOOL_VEC4 (35673) |
| |
| #define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT2 (35674) |
| #define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT3 (35675) |
| #define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT4 (35676) |
| |
| #define TINYGLTF_PARAMETER_TYPE_SAMPLER_2D (35678) |
| |
| // End parameter types |
| |
| #define TINYGLTF_TYPE_VEC2 (2) |
| #define TINYGLTF_TYPE_VEC3 (3) |
| #define TINYGLTF_TYPE_VEC4 (4) |
| #define TINYGLTF_TYPE_MAT2 (32 + 2) |
| #define TINYGLTF_TYPE_MAT3 (32 + 3) |
| #define TINYGLTF_TYPE_MAT4 (32 + 4) |
| #define TINYGLTF_TYPE_SCALAR (64 + 1) |
| #define TINYGLTF_TYPE_VECTOR (64 + 4) |
| #define TINYGLTF_TYPE_MATRIX (64 + 16) |
| |
| #define TINYGLTF_IMAGE_FORMAT_JPEG (0) |
| #define TINYGLTF_IMAGE_FORMAT_PNG (1) |
| #define TINYGLTF_IMAGE_FORMAT_BMP (2) |
| #define TINYGLTF_IMAGE_FORMAT_GIF (3) |
| |
| #define TINYGLTF_TEXTURE_FORMAT_ALPHA (6406) |
| #define TINYGLTF_TEXTURE_FORMAT_RGB (6407) |
| #define TINYGLTF_TEXTURE_FORMAT_RGBA (6408) |
| #define TINYGLTF_TEXTURE_FORMAT_LUMINANCE (6409) |
| #define TINYGLTF_TEXTURE_FORMAT_LUMINANCE_ALPHA (6410) |
| |
| #define TINYGLTF_TEXTURE_TARGET_TEXTURE2D (3553) |
| #define TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE (5121) |
| |
| #define TINYGLTF_TARGET_ARRAY_BUFFER (34962) |
| #define TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER (34963) |
| |
| #define TINYGLTF_SHADER_TYPE_VERTEX_SHADER (35633) |
| #define TINYGLTF_SHADER_TYPE_FRAGMENT_SHADER (35632) |
| |
| #define TINYGLTF_DOUBLE_EPS (1.e-12) |
| #define TINYGLTF_DOUBLE_EQUAL(a, b) (std::fabs((b) - (a)) < TINYGLTF_DOUBLE_EPS) |
| |
| #ifdef __ANDROID__ |
| #ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS |
| #ifdef TINYGLTF_IMPLEMENTATION |
| AAssetManager *asset_manager = nullptr; |
| #else |
| extern AAssetManager *asset_manager; |
| #endif |
| #endif |
| #endif |
| |
| typedef enum { |
| NULL_TYPE, |
| REAL_TYPE, |
| INT_TYPE, |
| BOOL_TYPE, |
| STRING_TYPE, |
| ARRAY_TYPE, |
| BINARY_TYPE, |
| OBJECT_TYPE |
| } Type; |
| |
| typedef enum { |
| Permissive, |
| Strict |
| } ParseStrictness; |
| |
| static inline int32_t GetComponentSizeInBytes(uint32_t componentType) { |
| if (componentType == TINYGLTF_COMPONENT_TYPE_BYTE) { |
| return 1; |
| } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) { |
| return 1; |
| } else if (componentType == TINYGLTF_COMPONENT_TYPE_SHORT) { |
| return 2; |
| } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) { |
| return 2; |
| } else if (componentType == TINYGLTF_COMPONENT_TYPE_INT) { |
| return 4; |
| } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) { |
| return 4; |
| } else if (componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) { |
| return 4; |
| } else if (componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) { |
| return 8; |
| } else { |
| // Unknown component type |
| return -1; |
| } |
| } |
| |
| static inline int32_t GetNumComponentsInType(uint32_t ty) { |
| if (ty == TINYGLTF_TYPE_SCALAR) { |
| return 1; |
| } else if (ty == TINYGLTF_TYPE_VEC2) { |
| return 2; |
| } else if (ty == TINYGLTF_TYPE_VEC3) { |
| return 3; |
| } else if (ty == TINYGLTF_TYPE_VEC4) { |
| return 4; |
| } else if (ty == TINYGLTF_TYPE_MAT2) { |
| return 4; |
| } else if (ty == TINYGLTF_TYPE_MAT3) { |
| return 9; |
| } else if (ty == TINYGLTF_TYPE_MAT4) { |
| return 16; |
| } else { |
| // Unknown component type |
| return -1; |
| } |
| } |
| |
| // TODO(syoyo): Move these functions to TinyGLTF class |
| bool IsDataURI(const std::string &in); |
| bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type, |
| const std::string &in, size_t reqBytes, bool checkSize); |
| |
| #ifdef __clang__ |
| #pragma clang diagnostic push |
| // Suppress warning for : static Value null_value |
| #pragma clang diagnostic ignored "-Wexit-time-destructors" |
| #pragma clang diagnostic ignored "-Wpadded" |
| #endif |
| |
| // Simple class to represent JSON object |
| class Value { |
| public: |
| typedef std::vector<Value> Array; |
| typedef std::map<std::string, Value> Object; |
| |
| Value() = default; |
| |
| explicit Value(bool b) : type_(BOOL_TYPE) { boolean_value_ = b; } |
| explicit Value(int i) : type_(INT_TYPE) { |
| int_value_ = i; |
| real_value_ = i; |
| } |
| explicit Value(double n) : type_(REAL_TYPE) { real_value_ = n; } |
| explicit Value(const std::string &s) : type_(STRING_TYPE) { |
| string_value_ = s; |
| } |
| explicit Value(std::string &&s) |
| : type_(STRING_TYPE), string_value_(std::move(s)) {} |
| explicit Value(const char *s) : type_(STRING_TYPE) { string_value_ = s; } |
| explicit Value(const unsigned char *p, size_t n) : type_(BINARY_TYPE) { |
| binary_value_.resize(n); |
| memcpy(binary_value_.data(), p, n); |
| } |
| explicit Value(std::vector<unsigned char> &&v) noexcept |
| : type_(BINARY_TYPE), |
| binary_value_(std::move(v)) {} |
| explicit Value(const Array &a) : type_(ARRAY_TYPE) { array_value_ = a; } |
| explicit Value(Array &&a) noexcept : type_(ARRAY_TYPE), |
| array_value_(std::move(a)) {} |
| |
| explicit Value(const Object &o) : type_(OBJECT_TYPE) { object_value_ = o; } |
| explicit Value(Object &&o) noexcept : type_(OBJECT_TYPE), |
| object_value_(std::move(o)) {} |
| |
| DEFAULT_METHODS(Value) |
| |
| char Type() const { return static_cast<char>(type_); } |
| |
| bool IsBool() const { return (type_ == BOOL_TYPE); } |
| |
| bool IsInt() const { return (type_ == INT_TYPE); } |
| |
| bool IsNumber() const { return (type_ == REAL_TYPE) || (type_ == INT_TYPE); } |
| |
| bool IsReal() const { return (type_ == REAL_TYPE); } |
| |
| bool IsString() const { return (type_ == STRING_TYPE); } |
| |
| bool IsBinary() const { return (type_ == BINARY_TYPE); } |
| |
| bool IsArray() const { return (type_ == ARRAY_TYPE); } |
| |
| bool IsObject() const { return (type_ == OBJECT_TYPE); } |
| |
| // Use this function if you want to have number value as double. |
| double GetNumberAsDouble() const { |
| if (type_ == INT_TYPE) { |
| return double(int_value_); |
| } else { |
| return real_value_; |
| } |
| } |
| |
| // Use this function if you want to have number value as int. |
| // TODO(syoyo): Support int value larger than 32 bits |
| int GetNumberAsInt() const { |
| if (type_ == REAL_TYPE) { |
| return int(real_value_); |
| } else { |
| return int_value_; |
| } |
| } |
| |
| // Accessor |
| template <typename T> |
| const T &Get() const; |
| template <typename T> |
| T &Get(); |
| |
| // Lookup value from an array |
| const Value &Get(size_t idx) const { |
| static Value null_value; |
| assert(IsArray()); |
| return (idx < array_value_.size()) |
| ? array_value_[idx] |
| : null_value; |
| } |
| |
| // Lookup value from a key-value pair |
| const Value &Get(const std::string &key) const { |
| static Value null_value; |
| assert(IsObject()); |
| Object::const_iterator it = object_value_.find(key); |
| return (it != object_value_.end()) ? it->second : null_value; |
| } |
| |
| size_t ArrayLen() const { |
| if (!IsArray()) return 0; |
| return array_value_.size(); |
| } |
| |
| // Valid only for object type. |
| bool Has(const std::string &key) const { |
| if (!IsObject()) return false; |
| Object::const_iterator it = object_value_.find(key); |
| return (it != object_value_.end()) ? true : false; |
| } |
| |
| // List keys |
| std::vector<std::string> Keys() const { |
| std::vector<std::string> keys; |
| if (!IsObject()) return keys; // empty |
| |
| for (Object::const_iterator it = object_value_.begin(); |
| it != object_value_.end(); ++it) { |
| keys.push_back(it->first); |
| } |
| |
| return keys; |
| } |
| |
| size_t Size() const { return (IsArray() ? ArrayLen() : Keys().size()); } |
| |
| bool operator==(const tinygltf::Value &other) const; |
| |
| protected: |
| int type_ = NULL_TYPE; |
| |
| int int_value_ = 0; |
| double real_value_ = 0.0; |
| std::string string_value_; |
| std::vector<unsigned char> binary_value_; |
| Array array_value_; |
| Object object_value_; |
| bool boolean_value_ = false; |
| }; |
| |
| #ifdef __clang__ |
| #pragma clang diagnostic pop |
| #endif |
| |
| #define TINYGLTF_VALUE_GET(ctype, var) \ |
| template <> \ |
| inline const ctype &Value::Get<ctype>() const { \ |
| return var; \ |
| } \ |
| template <> \ |
| inline ctype &Value::Get<ctype>() { \ |
| return var; \ |
| } |
| TINYGLTF_VALUE_GET(bool, boolean_value_) |
| TINYGLTF_VALUE_GET(double, real_value_) |
| TINYGLTF_VALUE_GET(int, int_value_) |
| TINYGLTF_VALUE_GET(std::string, string_value_) |
| TINYGLTF_VALUE_GET(std::vector<unsigned char>, binary_value_) |
| TINYGLTF_VALUE_GET(Value::Array, array_value_) |
| TINYGLTF_VALUE_GET(Value::Object, object_value_) |
| #undef TINYGLTF_VALUE_GET |
| |
| #ifdef __clang__ |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Wc++98-compat" |
| #pragma clang diagnostic ignored "-Wpadded" |
| #endif |
| |
| /// Aggregate object for representing a color |
| using ColorValue = std::array<double, 4>; |
| |
| // === legacy interface ==== |
| // TODO(syoyo): Deprecate `Parameter` class. |
| struct Parameter { |
| bool bool_value = false; |
| bool has_number_value = false; |
| std::string string_value; |
| std::vector<double> number_array; |
| std::map<std::string, double> json_double_value; |
| double number_value = 0.0; |
| |
| // context sensitive methods. depending the type of the Parameter you are |
| // accessing, these are either valid or not |
| // If this parameter represent a texture map in a material, will return the |
| // texture index |
| |
| /// Return the index of a texture if this Parameter is a texture map. |
| /// Returned value is only valid if the parameter represent a texture from a |
| /// material |
| int TextureIndex() const { |
| const auto it = json_double_value.find("index"); |
| if (it != std::end(json_double_value)) { |
| return int(it->second); |
| } |
| return -1; |
| } |
| |
| /// Return the index of a texture coordinate set if this Parameter is a |
| /// texture map. Returned value is only valid if the parameter represent a |
| /// texture from a material |
| int TextureTexCoord() const { |
| const auto it = json_double_value.find("texCoord"); |
| if (it != std::end(json_double_value)) { |
| return int(it->second); |
| } |
| // As per the spec, if texCoord is omitted, this parameter is 0 |
| return 0; |
| } |
| |
| /// Return the scale of a texture if this Parameter is a normal texture map. |
| /// Returned value is only valid if the parameter represent a normal texture |
| /// from a material |
| double TextureScale() const { |
| const auto it = json_double_value.find("scale"); |
| if (it != std::end(json_double_value)) { |
| return it->second; |
| } |
| // As per the spec, if scale is omitted, this parameter is 1 |
| return 1; |
| } |
| |
| /// Return the strength of a texture if this Parameter is a an occlusion map. |
| /// Returned value is only valid if the parameter represent an occlusion map |
| /// from a material |
| double TextureStrength() const { |
| const auto it = json_double_value.find("strength"); |
| if (it != std::end(json_double_value)) { |
| return it->second; |
| } |
| // As per the spec, if strength is omitted, this parameter is 1 |
| return 1; |
| } |
| |
| /// Material factor, like the roughness or metalness of a material |
| /// Returned value is only valid if the parameter represent a texture from a |
| /// material |
| double Factor() const { return number_value; } |
| |
| /// Return the color of a material |
| /// Returned value is only valid if the parameter represent a texture from a |
| /// material |
| ColorValue ColorFactor() const { |
| return { |
| {// this aggregate initialize the std::array object, and uses C++11 RVO. |
| number_array[0], number_array[1], number_array[2], |
| (number_array.size() > 3 ? number_array[3] : 1.0)}}; |
| } |
| |
| Parameter() = default; |
| DEFAULT_METHODS(Parameter) |
| bool operator==(const Parameter &) const; |
| }; |
| |
| #ifdef __clang__ |
| #pragma clang diagnostic pop |
| #endif |
| |
| #ifdef __clang__ |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Wpadded" |
| #endif |
| |
| typedef std::map<std::string, Parameter> ParameterMap; |
| typedef std::map<std::string, Value> ExtensionMap; |
| |
| struct AnimationChannel { |
| int sampler{-1}; // required |
| int target_node{-1}; // optional index of the node to target (alternative |
| // target should be provided by extension) |
| std::string target_path; // required with standard values of ["translation", |
| // "rotation", "scale", "weights"] |
| Value extras; |
| ExtensionMap extensions; |
| Value target_extras; |
| ExtensionMap target_extensions; |
| |
| // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. |
| std::string extras_json_string; |
| std::string extensions_json_string; |
| std::string target_extras_json_string; |
| std::string target_extensions_json_string; |
| |
| AnimationChannel() = default; |
| DEFAULT_METHODS(AnimationChannel) |
| bool operator==(const AnimationChannel &) const; |
| }; |
| |
| struct AnimationSampler { |
| int input{-1}; // required |
| int output{-1}; // required |
| std::string interpolation; // "LINEAR", "STEP","CUBICSPLINE" or user defined |
| // string. default "LINEAR" |
| Value extras; |
| ExtensionMap extensions; |
| |
| // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. |
| std::string extras_json_string; |
| std::string extensions_json_string; |
| |
| AnimationSampler() : interpolation("LINEAR") {} |
| DEFAULT_METHODS(AnimationSampler) |
| bool operator==(const AnimationSampler &) const; |
| }; |
| |
| struct Animation { |
| std::string name; |
| std::vector<AnimationChannel> channels; |
| std::vector<AnimationSampler> samplers; |
| Value extras; |
| ExtensionMap extensions; |
| |
| // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. |
| std::string extras_json_string; |
| std::string extensions_json_string; |
| |
| Animation() = default; |
| DEFAULT_METHODS(Animation) |
| bool operator==(const Animation &) const; |
| }; |
| |
| struct Skin { |
| std::string name; |
| int inverseBindMatrices{-1}; // required here but not in the spec |
| int skeleton{-1}; // The index of the node used as a skeleton root |
| std::vector<int> joints; // Indices of skeleton nodes |
| |
| Value extras; |
| ExtensionMap extensions; |
| |
| // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. |
| std::string extras_json_string; |
| std::string extensions_json_string; |
| |
| Skin() = default; |
| DEFAULT_METHODS(Skin) |
| bool operator==(const Skin &) const; |
| }; |
| |
| struct Sampler { |
| std::string name; |
| // glTF 2.0 spec does not define default value for `minFilter` and |
| // `magFilter`. Set -1 in TinyGLTF(issue #186) |
| int minFilter = |
| -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR", |
| // "NEAREST_MIPMAP_NEAREST", "LINEAR_MIPMAP_NEAREST", |
| // "NEAREST_MIPMAP_LINEAR", "LINEAR_MIPMAP_LINEAR"] |
| int magFilter = |
| -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR"] |
| int wrapS = |
| TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT", |
| // "REPEAT"], default "REPEAT" |
| int wrapT = |
| TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT", |
| // "REPEAT"], default "REPEAT" |
| // int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; // TinyGLTF extension. currently |
| // not used. |
| |
| Value extras; |
| ExtensionMap extensions; |
| |
| // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. |
| std::string extras_json_string; |
| std::string extensions_json_string; |
| |
| Sampler() = default; |
| DEFAULT_METHODS(Sampler) |
| bool operator==(const Sampler &) const; |
| }; |
| |
| struct Image { |
| std::string name; |
| int width{-1}; |
| int height{-1}; |
| int component{-1}; |
| int bits{-1}; // bit depth per channel. 8(byte), 16 or 32. |
| int pixel_type{-1}; // pixel type(TINYGLTF_COMPONENT_TYPE_***). usually |
| // UBYTE(bits = 8) or USHORT(bits = 16) |
| std::vector<unsigned char> image; |
| int bufferView{-1}; // (required if no uri) |
| std::string mimeType; // (required if no uri) ["image/jpeg", "image/png", |
| // "image/bmp", "image/gif"] |
| std::string uri; // (required if no mimeType) uri is not decoded(e.g. |
| // whitespace may be represented as %20) |
| Value extras; |
| ExtensionMap extensions; |
| |
| // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. |
| std::string extras_json_string; |
| std::string extensions_json_string; |
| |
| // When this flag is true, data is stored to `image` in as-is format(e.g. jpeg |
| // compressed for "image/jpeg" mime) This feature is good if you use custom |
| // image loader function. (e.g. delayed decoding of images for faster glTF |
| // parsing). |
| bool as_is{false}; |
| |
| Image() = default; |
| DEFAULT_METHODS(Image) |
| |
| bool operator==(const Image &) const; |
| }; |
| |
| struct Texture { |
| std::string name; |
| |
| int sampler{-1}; |
| int source{-1}; |
| Value extras; |
| ExtensionMap extensions; |
| |
| // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. |
| std::string extras_json_string; |
| std::string extensions_json_string; |
| |
| Texture() = default; |
| DEFAULT_METHODS(Texture) |
| |
| bool operator==(const Texture &) const; |
| }; |
| |
| struct TextureInfo { |
| int index{-1}; // required. |
| int texCoord{0}; // The set index of texture's TEXCOORD attribute used for |
| // texture coordinate mapping. |
| |
| Value extras; |
| ExtensionMap extensions; |
| |
| // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. |
| std::string extras_json_string; |
| std::string extensions_json_string; |
| |
| TextureInfo() = default; |
| DEFAULT_METHODS(TextureInfo) |
| bool operator==(const TextureInfo &) const; |
| }; |
| |
| struct NormalTextureInfo { |
| int index{-1}; // required |
| int texCoord{0}; // The set index of texture's TEXCOORD attribute used for |
| // texture coordinate mapping. |
| double scale{ |
| 1.0}; // scaledNormal = normalize((<sampled normal texture value> |
| // * 2.0 - 1.0) * vec3(<normal scale>, <normal scale>, 1.0)) |
| |
| Value extras; |
| ExtensionMap extensions; |
| |
| // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. |
| std::string extras_json_string; |
| std::string extensions_json_string; |
| |
| NormalTextureInfo() = default; |
| DEFAULT_METHODS(NormalTextureInfo) |
| bool operator==(const NormalTextureInfo &) const; |
| }; |
| |
| struct OcclusionTextureInfo { |
| int index{-1}; // required |
| int texCoord{0}; // The set index of texture's TEXCOORD attribute used for |
| // texture coordinate mapping. |
| double strength{1.0}; // occludedColor = lerp(color, color * <sampled |
| // occlusion texture value>, <occlusion strength>) |
| |
| Value extras; |
| ExtensionMap extensions; |
| |
| // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. |
| std::string extras_json_string; |
| std::string extensions_json_string; |
| |
| OcclusionTextureInfo() = default; |
| DEFAULT_METHODS(OcclusionTextureInfo) |
| bool operator==(const OcclusionTextureInfo &) const; |
| }; |
| |
| // pbrMetallicRoughness class defined in glTF 2.0 spec. |
| struct PbrMetallicRoughness { |
| std::vector<double> baseColorFactor{1.0, 1.0, 1.0, 1.0}; // len = 4. default [1,1,1,1] |
| TextureInfo baseColorTexture; |
| double metallicFactor{1.0}; // default 1 |
| double roughnessFactor{1.0}; // default 1 |
| TextureInfo metallicRoughnessTexture; |
| |
| Value extras; |
| ExtensionMap extensions; |
| |
| // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. |
| std::string extras_json_string; |
| std::string extensions_json_string; |
| |
| PbrMetallicRoughness() = default; |
| DEFAULT_METHODS(PbrMetallicRoughness) |
| |
| bool operator==(const PbrMetallicRoughness &) const; |
| }; |
| |
| // Each extension should be stored in a ParameterMap. |
| // members not in the values could be included in the ParameterMap |
| // to keep a single material model |
| struct Material { |
| std::string name; |
| |
| std::vector<double> emissiveFactor{0.0, 0.0, 0.0}; // length 3. default [0, 0, 0] |
| std::string alphaMode{"OPAQUE"}; // default "OPAQUE" |
| double alphaCutoff{0.5}; // default 0.5 |
| bool doubleSided{false}; // default false |
| std::vector<int> lods; // level of detail materials (MSFT_lod) |
| |
| PbrMetallicRoughness pbrMetallicRoughness; |
| |
| NormalTextureInfo normalTexture; |
| OcclusionTextureInfo occlusionTexture; |
| TextureInfo emissiveTexture; |
| |
| // For backward compatibility |
| // TODO(syoyo): Remove `values` and `additionalValues` in the next release. |
| ParameterMap values; |
| ParameterMap additionalValues; |
| |
| ExtensionMap extensions; |
| Value extras; |
| |
| // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. |
| std::string extras_json_string; |
| std::string extensions_json_string; |
| |
| Material() = default; |
| DEFAULT_METHODS(Material) |
| |
| bool operator==(const Material &) const; |
| }; |
| |
| struct BufferView { |
| std::string name; |
| int buffer{-1}; // Required |
| size_t byteOffset{0}; // minimum 0, default 0 |
| size_t byteLength{0}; // required, minimum 1. 0 = invalid |
| size_t byteStride{0}; // minimum 4, maximum 252 (multiple of 4), default 0 = |
| // understood to be tightly packed |
| int target{0}; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"] for vertex indices |
| // or attribs. Could be 0 for other data |
| Value extras; |
| ExtensionMap extensions; |
| |
| // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. |
| std::string extras_json_string; |
| std::string extensions_json_string; |
| |
| bool dracoDecoded{false}; // Flag indicating this has been draco decoded |
| |
| BufferView() = default; |
| DEFAULT_METHODS(BufferView) |
| bool operator==(const BufferView &) const; |
| }; |
| |
| struct Accessor { |
| int bufferView{-1}; // optional in spec but required here since sparse |
| // accessor are not supported |
| std::string name; |
| size_t byteOffset{0}; |
| bool normalized{false}; // optional. |
| int componentType{-1}; // (required) One of TINYGLTF_COMPONENT_TYPE_*** |
| size_t count{0}; // required |
| int type{-1}; // (required) One of TINYGLTF_TYPE_*** .. |
| Value extras; |
| ExtensionMap extensions; |
| |
| // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. |
| std::string extras_json_string; |
| std::string extensions_json_string; |
| |
| std::vector<double> |
| minValues; // optional. integer value is promoted to double |
| std::vector<double> |
| maxValues; // optional. integer value is promoted to double |
| |
| struct Sparse { |
| int count; |
| bool isSparse; |
| struct { |
| size_t byteOffset; |
| int bufferView; |
| int componentType; // a TINYGLTF_COMPONENT_TYPE_ value |
| Value extras; |
| ExtensionMap extensions; |
| std::string extras_json_string; |
| std::string extensions_json_string; |
| } indices; |
| struct { |
| int bufferView; |
| size_t byteOffset; |
| Value extras; |
| ExtensionMap extensions; |
| std::string extras_json_string; |
| std::string extensions_json_string; |
| } values; |
| Value extras; |
| ExtensionMap extensions; |
| std::string extras_json_string; |
| std::string extensions_json_string; |
| }; |
| |
| Sparse sparse; |
| |
| /// |
| /// Utility function to compute byteStride for a given bufferView object. |
| /// Returns -1 upon invalid glTF value or parameter configuration. |
| /// |
| int ByteStride(const BufferView &bufferViewObject) const { |
| if (bufferViewObject.byteStride == 0) { |
| // Assume data is tightly packed. |
| int componentSizeInBytes = |
| GetComponentSizeInBytes(static_cast<uint32_t>(componentType)); |
| if (componentSizeInBytes <= 0) { |
| return -1; |
| } |
| |
| int numComponents = GetNumComponentsInType(static_cast<uint32_t>(type)); |
| if (numComponents <= 0) { |
| return -1; |
| } |
| |
| return componentSizeInBytes * numComponents; |
| } else { |
| // Check if byteStride is a multiple of the size of the accessor's |
| // component type. |
| int componentSizeInBytes = |
| GetComponentSizeInBytes(static_cast<uint32_t>(componentType)); |
| if (componentSizeInBytes <= 0) { |
| return -1; |
| } |
| |
| if ((bufferViewObject.byteStride % uint32_t(componentSizeInBytes)) != 0) { |
| return -1; |
| } |
| return static_cast<int>(bufferViewObject.byteStride); |
| } |
| |
| // unreachable return 0; |
| } |
| |
| Accessor() |
| |
| { |
| sparse.isSparse = false; |
| } |
| DEFAULT_METHODS(Accessor) |
| bool operator==(const tinygltf::Accessor &) const; |
| }; |
| |
| struct PerspectiveCamera { |
| double aspectRatio{0.0}; // min > 0 |
| double yfov{0.0}; // required. min > 0 |
| double zfar{0.0}; // min > 0 |
| double znear{0.0}; // required. min > 0 |
| |
| PerspectiveCamera() = default; |
| DEFAULT_METHODS(PerspectiveCamera) |
| bool operator==(const PerspectiveCamera &) const; |
| |
| ExtensionMap extensions; |
| Value extras; |
| |
| // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. |
| std::string extras_json_string; |
| std::string extensions_json_string; |
| }; |
| |
| struct OrthographicCamera { |
| double xmag{0.0}; // required. must not be zero. |
| double ymag{0.0}; // required. must not be zero. |
| double zfar{0.0}; // required. `zfar` must be greater than `znear`. |
| double znear{0.0}; // required |
| |
| OrthographicCamera() = default; |
| DEFAULT_METHODS(OrthographicCamera) |
| bool operator==(const OrthographicCamera &) const; |
| |
| ExtensionMap extensions; |
| Value extras; |
| |
| // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. |
| std::string extras_json_string; |
| std::string extensions_json_string; |
| }; |
| |
| struct Camera { |
| std::string type; // required. "perspective" or "orthographic" |
| std::string name; |
| |
| PerspectiveCamera perspective; |
| OrthographicCamera orthographic; |
| |
| Camera() = default; |
| DEFAULT_METHODS(Camera) |
| bool operator==(const Camera &) const; |
| |
| ExtensionMap extensions; |
| Value extras; |
| |
| // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. |
| std::string extras_json_string; |
| std::string extensions_json_string; |
| }; |
| |
| struct Primitive { |
| std::map<std::string, int> attributes; // (required) A dictionary object of |
| // integer, where each integer |
| // is the index of the accessor |
| // containing an attribute. |
| int material{-1}; // The index of the material to apply to this primitive |
| // when rendering. |
| int indices{-1}; // The index of the accessor that contains the indices. |
| int mode{-1}; // one of TINYGLTF_MODE_*** |
| std::vector<std::map<std::string, int> > targets; // array of morph targets, |
| // where each target is a dict with attributes in ["POSITION, "NORMAL", |
| // "TANGENT"] pointing |
| // to their corresponding accessors |
| ExtensionMap extensions; |
| Value extras; |
| |
| // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. |
| std::string extras_json_string; |
| std::string extensions_json_string; |
| |
| Primitive() = default; |
| DEFAULT_METHODS(Primitive) |
| bool operator==(const Primitive &) const; |
| }; |
| |
| struct Mesh { |
| std::string name; |
| std::vector<Primitive> primitives; |
| std::vector<double> weights; // weights to be applied to the Morph Targets |
| ExtensionMap extensions; |
| Value extras; |
| |
| // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. |
| std::string extras_json_string; |
| std::string extensions_json_string; |
| |
| Mesh() = default; |
| DEFAULT_METHODS(Mesh) |
| bool operator==(const Mesh &) const; |
| }; |
| |
| class Node { |
| public: |
| Node() = default; |
| |
| DEFAULT_METHODS(Node) |
| |
| bool operator==(const Node &) const; |
| |
| int camera{-1}; // the index of the camera referenced by this node |
| |
| std::string name; |
| int skin{-1}; |
| int mesh{-1}; |
| int light{-1}; // light source index (KHR_lights_punctual) |
| int emitter{-1}; // audio emitter index (KHR_audio) |
| std::vector<int> lods; // level of detail nodes (MSFT_lod) |
| std::vector<int> children; |
| std::vector<double> rotation; // length must be 0 or 4 |
| std::vector<double> scale; // length must be 0 or 3 |
| std::vector<double> translation; // length must be 0 or 3 |
| std::vector<double> matrix; // length must be 0 or 16 |
| std::vector<double> weights; // The weights of the instantiated Morph Target |
| |
| ExtensionMap extensions; |
| Value extras; |
| |
| // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. |
| std::string extras_json_string; |
| std::string extensions_json_string; |
| }; |
| |
| struct Buffer { |
| std::string name; |
| std::vector<unsigned char> data; |
| std::string |
| uri; // considered as required here but not in the spec (need to clarify) |
| // uri is not decoded(e.g. whitespace may be represented as %20) |
| Value extras; |
| ExtensionMap extensions; |
| |
| // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. |
| std::string extras_json_string; |
| std::string extensions_json_string; |
| |
| Buffer() = default; |
| DEFAULT_METHODS(Buffer) |
| bool operator==(const Buffer &) const; |
| }; |
| |
| struct Asset { |
| std::string version = "2.0"; // required |
| std::string generator; |
| std::string minVersion; |
| std::string copyright; |
| ExtensionMap extensions; |
| Value extras; |
| |
| // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. |
| std::string extras_json_string; |
| std::string extensions_json_string; |
| |
| Asset() = default; |
| DEFAULT_METHODS(Asset) |
| bool operator==(const Asset &) const; |
| }; |
| |
| struct Scene { |
| std::string name; |
| std::vector<int> nodes; |
| std::vector<int> audioEmitters; // KHR_audio global emitters |
| |
| ExtensionMap extensions; |
| Value extras; |
| |
| // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. |
| std::string extras_json_string; |
| std::string extensions_json_string; |
| |
| Scene() = default; |
| DEFAULT_METHODS(Scene) |
| bool operator==(const Scene &) const; |
| }; |
| |
| struct SpotLight { |
| double innerConeAngle{0.0}; |
| double outerConeAngle{0.7853981634}; |
| |
| SpotLight() = default; |
| DEFAULT_METHODS(SpotLight) |
| bool operator==(const SpotLight &) const; |
| |
| ExtensionMap extensions; |
| Value extras; |
| |
| // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. |
| std::string extras_json_string; |
| std::string extensions_json_string; |
| }; |
| |
| struct Light { |
| std::string name; |
| std::vector<double> color; |
| double intensity{1.0}; |
| std::string type; |
| double range{0.0}; // 0.0 = infinite |
| SpotLight spot; |
| |
| Light() = default; |
| DEFAULT_METHODS(Light) |
| |
| bool operator==(const Light &) const; |
| |
| ExtensionMap extensions; |
| Value extras; |
| |
| // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. |
| std::string extras_json_string; |
| std::string extensions_json_string; |
| }; |
| |
| struct PositionalEmitter { |
| double coneInnerAngle{6.283185307179586}; |
| double coneOuterAngle{6.283185307179586}; |
| double coneOuterGain{0.0}; |
| double maxDistance{100.0}; |
| double refDistance{1.0}; |
| double rolloffFactor{1.0}; |
| |
| PositionalEmitter() = default; |
| DEFAULT_METHODS(PositionalEmitter) |
| bool operator==(const PositionalEmitter &) const; |
| |
| ExtensionMap extensions; |
| Value extras; |
| |
| // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. |
| std::string extras_json_string; |
| std::string extensions_json_string; |
| }; |
| |
| struct AudioEmitter { |
| std::string name; |
| double gain{1.0}; |
| bool loop{false}; |
| bool playing{false}; |
| std::string |
| type; // positional - Positional audio emitters. Using sound cones, the |
| // orientation is +Z having the same front side for a glTF asset. |
| // global - Global audio emitters are not affected by the position |
| // of audio listeners. coneInnerAngle, coneOuterAngle, |
| // coneOuterGain, distanceModel, maxDistance, refDistance, and |
| // rolloffFactor should all be ignored when set. |
| std::string |
| distanceModel; // linear - A linear distance model calculating the |
| // gain induced by the distance according to: 1.0 |
| // - rolloffFactor * (distance - refDistance) / |
| // (maxDistance - refDistance) |
| // inverse - (default) An inverse distance model |
| // calculating the gain induced by the distance according |
| // to: refDistance / (refDistance + rolloffFactor * |
| // (Math.max(distance, refDistance) - refDistance)) |
| // exponential - An exponential distance model calculating |
| // the gain induced by the distance according to: |
| // pow((Math.max(distance, refDistance) / refDistance, |
| // -rolloffFactor)) |
| PositionalEmitter positional; |
| int source{-1}; |
| |
| AudioEmitter() : type("global"), distanceModel("inverse") {} |
| DEFAULT_METHODS(AudioEmitter) |
| |
| bool operator==(const AudioEmitter &) const; |
| |
| ExtensionMap extensions; |
| Value extras; |
| |
| // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. |
| std::string extras_json_string; |
| std::string extensions_json_string; |
| }; |
| |
| struct AudioSource { |
| std::string name; |
| std::string uri; |
| int bufferView{-1}; // (required if no uri) |
| std::string |
| mimeType; // (required if no uri) The audio's MIME type. Required if |
| // bufferView is defined. Unless specified by another |
| // extension, the only supported mimeType is audio/mpeg. |
| |
| AudioSource() = default; |
| DEFAULT_METHODS(AudioSource) |
| |
| bool operator==(const AudioSource &) const; |
| |
| Value extras; |
| ExtensionMap extensions; |
| |
| // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. |
| std::string extras_json_string; |
| std::string extensions_json_string; |
| }; |
| |
| class Model { |
| public: |
| Model() = default; |
| DEFAULT_METHODS(Model) |
| |
| bool operator==(const Model &) const; |
| |
| std::vector<Accessor> accessors; |
| std::vector<Animation> animations; |
| std::vector<Buffer> buffers; |
| std::vector<BufferView> bufferViews; |
| std::vector<Material> materials; |
| std::vector<Mesh> meshes; |
| std::vector<Node> nodes; |
| std::vector<Texture> textures; |
| std::vector<Image> images; |
| std::vector<Skin> skins; |
| std::vector<Sampler> samplers; |
| std::vector<Camera> cameras; |
| std::vector<Scene> scenes; |
| std::vector<Light> lights; |
| std::vector<AudioEmitter> audioEmitters; |
| std::vector<AudioSource> audioSources; |
| |
| int defaultScene{-1}; |
| std::vector<std::string> extensionsUsed; |
| std::vector<std::string> extensionsRequired; |
| |
| Asset asset; |
| |
| Value extras; |
| ExtensionMap extensions; |
| |
| // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. |
| std::string extras_json_string; |
| std::string extensions_json_string; |
| }; |
| |
| enum SectionCheck { |
| NO_REQUIRE = 0x00, |
| REQUIRE_VERSION = 0x01, |
| REQUIRE_SCENE = 0x02, |
| REQUIRE_SCENES = 0x04, |
| REQUIRE_NODES = 0x08, |
| REQUIRE_ACCESSORS = 0x10, |
| REQUIRE_BUFFERS = 0x20, |
| REQUIRE_BUFFER_VIEWS = 0x40, |
| REQUIRE_ALL = 0x7f |
| }; |
| |
| /// |
| /// URIEncodeFunction type. Signature for custom URI encoding of external |
| /// resources such as .bin and image files. Used by tinygltf to re-encode the |
| /// final location of saved files. object_type may be used to encode buffer and |
| /// image URIs differently, for example. See |
| /// https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#uris |
| /// |
| using URIEncodeFunction = std::function<bool( |
| const std::string & /* in_uri */, const std::string & /* object_type */, |
| std::string * /* out_uri */, void * /* user_data */)>; |
| |
| /// |
| /// URIDecodeFunction type. Signature for custom URI decoding of external |
| /// resources such as .bin and image files. Used by tinygltf when computing |
| /// filenames to write resources. |
| /// |
| using URIDecodeFunction = |
| std::function<bool(const std::string & /* in_uri */, |
| std::string * /* out_uri */, void * /* user_data */)>; |
| |
| // Declaration of default uri decode function |
| bool URIDecode(const std::string &in_uri, std::string *out_uri, |
| void *user_data); |
| |
| /// |
| /// A structure containing URI callbacks and a pointer to their user data. |
| /// |
| struct URICallbacks { |
| URIEncodeFunction encode; // Optional encode method |
| URIDecodeFunction decode; // Required decode method |
| |
| void *user_data; // An argument that is passed to all uri callbacks |
| }; |
| |
| /// |
| /// FileExistsFunction type. Signature for custom filesystem callbacks. |
| /// |
| using FileExistsFunction = std::function<bool( |
| const std::string & /* abs_filename */, void * /* user_data */)>; |
| |
| /// |
| /// ExpandFilePathFunction type. Signature for custom filesystem callbacks. |
| /// |
| using ExpandFilePathFunction = |
| std::function<std::string(const std::string &, void *)>; |
| |
| /// |
| /// ReadWholeFileFunction type. Signature for custom filesystem callbacks. |
| /// |
| using ReadWholeFileFunction = std::function<bool( |
| std::vector<unsigned char> *, std::string *, const std::string &, void *)>; |
| |
| /// |
| /// WriteWholeFileFunction type. Signature for custom filesystem callbacks. |
| /// |
| using WriteWholeFileFunction = |
| std::function<bool(std::string *, const std::string &, |
| const std::vector<unsigned char> &, void *)>; |
| |
| /// |
| /// GetFileSizeFunction type. Signature for custom filesystem callbacks. |
| /// |
| using GetFileSizeFunction = |
| std::function<bool(size_t *filesize_out, std::string *err, |
| const std::string &abs_filename, void *userdata)>; |
| |
| /// |
| /// A structure containing all required filesystem callbacks and a pointer to |
| /// their user data. |
| /// |
| struct FsCallbacks { |
| FileExistsFunction FileExists; |
| ExpandFilePathFunction ExpandFilePath; |
| ReadWholeFileFunction ReadWholeFile; |
| WriteWholeFileFunction WriteWholeFile; |
| GetFileSizeFunction GetFileSizeInBytes; // To avoid GetFileSize Win32 API, |
| // add `InBytes` suffix. |
| |
| void *user_data; // An argument that is passed to all fs callbacks |
| }; |
| |
| #ifndef TINYGLTF_NO_FS |
| // Declaration of default filesystem callbacks |
| |
| bool FileExists(const std::string &abs_filename, void *); |
| |
| /// |
| /// Expand file path(e.g. `~` to home directory on posix, `%APPDATA%` to |
| /// `C:\\Users\\tinygltf\\AppData`) |
| /// |
| /// @param[in] filepath File path string. Assume UTF-8 |
| /// @param[in] userdata User data. Set to `nullptr` if you don't need it. |
| /// |
| std::string ExpandFilePath(const std::string &filepath, void *userdata); |
| |
| bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err, |
| const std::string &filepath, void *); |
| |
| bool WriteWholeFile(std::string *err, const std::string &filepath, |
| const std::vector<unsigned char> &contents, void *); |
| |
| bool GetFileSizeInBytes(size_t *filesize_out, std::string *err, |
| const std::string &filepath, void *); |
| #endif |
| |
| /// |
| /// LoadImageDataFunction type. Signature for custom image loading callbacks. |
| /// |
| using LoadImageDataFunction = std::function<bool( |
| Image * /* image */, const int /* image_idx */, std::string * /* err */, |
| std::string * /* warn */, int /* req_width */, int /* req_height */, |
| const unsigned char * /* bytes */, int /* size */, void * /*user_data */)>; |
| |
| /// |
| /// WriteImageDataFunction type. Signature for custom image writing callbacks. |
| /// The out_uri parameter becomes the URI written to the gltf and may reference |
| /// a file or contain a data URI. |
| /// |
| using WriteImageDataFunction = std::function<bool( |
| const std::string * /* basepath */, const std::string * /* filename */, |
| const Image *image, bool /* embedImages */, |
| const FsCallbacks * /* fs_cb */, const URICallbacks * /* uri_cb */, |
| std::string * /* out_uri */, void * /* user_pointer */)>; |
| |
| #ifndef TINYGLTF_NO_STB_IMAGE |
| // Declaration of default image loader callback |
| bool LoadImageData(Image *image, const int image_idx, std::string *err, |
| std::string *warn, int req_width, int req_height, |
| const unsigned char *bytes, int size, void *); |
| #endif |
| |
| #ifndef TINYGLTF_NO_STB_IMAGE_WRITE |
| // Declaration of default image writer callback |
| bool WriteImageData(const std::string *basepath, const std::string *filename, |
| const Image *image, bool embedImages, |
| const FsCallbacks* fs_cb, const URICallbacks *uri_cb, |
| std::string *out_uri, void *); |
| #endif |
| |
| /// |
| /// glTF Parser/Serializer context. |
| /// |
| class TinyGLTF { |
| public: |
| #ifdef __clang__ |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Wc++98-compat" |
| #endif |
| |
| TinyGLTF() = default; |
| |
| #ifdef __clang__ |
| #pragma clang diagnostic pop |
| #endif |
| |
| ~TinyGLTF() = default; |
| |
| /// |
| /// Loads glTF ASCII asset from a file. |
| /// Set warning message to `warn` for example it fails to load asserts. |
| /// Returns false and set error string to `err` if there's an error. |
| /// |
| bool LoadASCIIFromFile(Model *model, std::string *err, std::string *warn, |
| const std::string &filename, |
| unsigned int check_sections = REQUIRE_VERSION); |
| |
| /// |
| /// Loads glTF ASCII asset from string(memory). |
| /// `length` = strlen(str); |
| /// `base_dir` is a search path of glTF asset(e.g. images). Path Must be an |
| /// expanded path (e.g. no tilde(`~`), no environment variables). Set warning |
| /// message to `warn` for example it fails to load asserts. Returns false and |
| /// set error string to `err` if there's an error. |
| /// |
| bool LoadASCIIFromString(Model *model, std::string *err, std::string *warn, |
| const char *str, const unsigned int length, |
| const std::string &base_dir, |
| unsigned int check_sections = REQUIRE_VERSION); |
| |
| /// |
| /// Loads glTF binary asset from a file. |
| /// Set warning message to `warn` for example it fails to load asserts. |
| /// Returns false and set error string to `err` if there's an error. |
| /// |
| bool LoadBinaryFromFile(Model *model, std::string *err, std::string *warn, |
| const std::string &filename, |
| unsigned int check_sections = REQUIRE_VERSION); |
| |
| /// |
| /// Loads glTF binary asset from memory. |
| /// `length` = strlen(str); |
| /// `base_dir` is a search path of glTF asset(e.g. images). Path Must be an |
| /// expanded path (e.g. no tilde(`~`), no environment variables). |
| /// Set warning message to `warn` for example it fails to load asserts. |
| /// Returns false and set error string to `err` if there's an error. |
| /// |
| bool LoadBinaryFromMemory(Model *model, std::string *err, std::string *warn, |
| const unsigned char *bytes, |
| const unsigned int length, |
| const std::string &base_dir = "", |
| unsigned int check_sections = REQUIRE_VERSION); |
| |
| /// |
| /// Write glTF to stream, buffers and images will be embedded |
| /// |
| bool WriteGltfSceneToStream(const Model *model, std::ostream &stream, |
| bool prettyPrint, bool writeBinary); |
| |
| /// |
| /// Write glTF to file. |
| /// |
| bool WriteGltfSceneToFile(const Model *model, const std::string &filename, |
| bool embedImages, bool embedBuffers, |
| bool prettyPrint, bool writeBinary); |
| |
| /// |
| /// Sets the parsing strictness. |
| /// |
| void SetParseStrictness(ParseStrictness strictness); |
| |
| /// |
| /// Set callback to use for loading image data. Passing the nullptr is akin to |
| /// calling RemoveImageLoader(). |
| /// |
| void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data); |
| |
| /// |
| /// Unset(remove) callback of loading image data |
| /// |
| void RemoveImageLoader(); |
| |
| /// |
| /// Set callback to use for writing image data |
| /// |
| void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data); |
| |
| /// |
| /// Set callbacks to use for URI encoding and decoding and their user data. |
| /// Returns false if there is an error with the callbacks. If err is not |
| /// nullptr, explanation will be written there. |
| /// |
| bool SetURICallbacks(URICallbacks callbacks, std::string* err = nullptr); |
| |
| /// |
| /// Set callbacks to use for filesystem (fs) access and their user data. |
| /// Returns false if there is an error with the callbacks. If err is not |
| /// nullptr, explanation will be written there. |
| /// |
| bool SetFsCallbacks(FsCallbacks callbacks, std::string* err = nullptr); |
| |
| /// |
| /// Set serializing default values(default = false). |
| /// When true, default values are force serialized to .glTF. |
| /// This may be helpful if you want to serialize a full description of glTF |
| /// data. |
| /// |
| /// TODO(LTE): Supply parsing option as function arguments to |
| /// `LoadASCIIFromFile()` and others, not by a class method |
| /// |
| void SetSerializeDefaultValues(const bool enabled) { |
| serialize_default_values_ = enabled; |
| } |
| |
| bool GetSerializeDefaultValues() const { return serialize_default_values_; } |
| |
| /// |
| /// Store original JSON string for `extras` and `extensions`. |
| /// This feature will be useful when the user want to reconstruct custom data |
| /// structure from JSON string. |
| /// |
| void SetStoreOriginalJSONForExtrasAndExtensions(const bool enabled) { |
| store_original_json_for_extras_and_extensions_ = enabled; |
| } |
| |
| bool GetStoreOriginalJSONForExtrasAndExtensions() const { |
| return store_original_json_for_extras_and_extensions_; |
| } |
| |
| /// |
| /// Specify whether preserve image channels when loading images or not. |
| /// (Not effective when the user supplies their own LoadImageData callbacks) |
| /// |
| void SetPreserveImageChannels(bool onoff) { |
| preserve_image_channels_ = onoff; |
| } |
| |
| bool GetPreserveImageChannels() const { return preserve_image_channels_; } |
| |
| /// |
| /// Specifiy whether image data is decoded/decompressed during load, or left as is |
| /// |
| void SetImagesAsIs(bool onoff) { |
| images_as_is_ = onoff; |
| } |
| |
| bool GetImagesAsIs() const { return images_as_is_; } |
| |
| /// |
| /// Set maximum allowed external file size in bytes. |
| /// Default: 2GB |
| /// Only effective for built-in ReadWholeFileFunction FS function. |
| /// |
| void SetMaxExternalFileSize(size_t max_bytes) { |
| max_external_file_size_ = max_bytes; |
| } |
| |
| size_t GetMaxExternalFileSize() const { return max_external_file_size_; } |
| |
| private: |
| /// |
| /// Loads glTF asset from string(memory). |
| /// `length` = strlen(str); |
| /// Set warning message to `warn` for example it fails to load asserts |
| /// Returns false and set error string to `err` if there's an error. |
| /// |
| bool LoadFromString(Model *model, std::string *err, std::string *warn, |
| const char *str, const unsigned int length, |
| const std::string &base_dir, unsigned int check_sections); |
| |
| const unsigned char *bin_data_ = nullptr; |
| size_t bin_size_ = 0; |
| bool is_binary_ = false; |
| |
| ParseStrictness strictness_ = ParseStrictness::Strict; |
| |
| bool serialize_default_values_ = false; ///< Serialize default values? |
| |
| bool store_original_json_for_extras_and_extensions_ = false; |
| |
| bool preserve_image_channels_ = false; /// Default false(expand channels to |
| /// RGBA) for backward compatibility. |
| |
| bool images_as_is_ = false; /// Default false (decode/decompress images) |
| |
| size_t max_external_file_size_{ |
| size_t((std::numeric_limits<int32_t>::max)())}; // Default 2GB |
| |
| // Warning & error messages |
| std::string warn_; |
| std::string err_; |
| |
| FsCallbacks fs = { |
| #ifndef TINYGLTF_NO_FS |
| &tinygltf::FileExists, |
| &tinygltf::ExpandFilePath, |
| &tinygltf::ReadWholeFile, |
| &tinygltf::WriteWholeFile, |
| &tinygltf::GetFileSizeInBytes, |
| |
| nullptr // Fs callback user data |
| #else |
| nullptr, nullptr, nullptr, nullptr, nullptr, |
| |
| nullptr // Fs callback user data |
| #endif |
| }; |
| |
| URICallbacks uri_cb = { |
| // Use paths as-is by default. This will use JSON string escaping. |
| nullptr, |
| // Decode all URIs before using them as paths as the application may have |
| // percent encoded them. |
| &tinygltf::URIDecode, |
| // URI callback user data |
| nullptr}; |
| |
| LoadImageDataFunction LoadImageData = |
| #ifndef TINYGLTF_NO_STB_IMAGE |
| &tinygltf::LoadImageData; |
| #else |
| nullptr; |
| #endif |
| void *load_image_user_data_{nullptr}; |
| bool user_image_loader_{false}; |
| |
| WriteImageDataFunction WriteImageData = |
| #ifndef TINYGLTF_NO_STB_IMAGE_WRITE |
| &tinygltf::WriteImageData; |
| #else |
| nullptr; |
| #endif |
| void *write_image_user_data_{nullptr}; |
| }; |
| |
| #ifdef __clang__ |
| #pragma clang diagnostic pop // -Wpadded |
| #endif |
| |
| } // namespace tinygltf |
| |
| #endif // TINY_GLTF_H_ |
| |
| #if defined(TINYGLTF_IMPLEMENTATION) || defined(__INTELLISENSE__) |
| #include <algorithm> |
| // #include <cassert> |
| #ifndef TINYGLTF_NO_FS |
| #include <sys/stat.h> // for is_directory check |
| |
| #include <cstdio> |
| #include <fstream> |
| #endif |
| #include <sstream> |
| |
| #ifdef __clang__ |
| // Disable some warnings for external files. |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Wfloat-equal" |
| #pragma clang diagnostic ignored "-Wexit-time-destructors" |
| #pragma clang diagnostic ignored "-Wconversion" |
| #pragma clang diagnostic ignored "-Wold-style-cast" |
| #pragma clang diagnostic ignored "-Wglobal-constructors" |
| #if __has_warning("-Wreserved-id-macro") |
| #pragma clang diagnostic ignored "-Wreserved-id-macro" |
| #endif |
| #pragma clang diagnostic ignored "-Wdisabled-macro-expansion" |
| #pragma clang diagnostic ignored "-Wpadded" |
| #pragma clang diagnostic ignored "-Wc++98-compat" |
| #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" |
| #pragma clang diagnostic ignored "-Wdocumentation-unknown-command" |
| #pragma clang diagnostic ignored "-Wswitch-enum" |
| #pragma clang diagnostic ignored "-Wimplicit-fallthrough" |
| #pragma clang diagnostic ignored "-Wweak-vtables" |
| #pragma clang diagnostic ignored "-Wcovered-switch-default" |
| #if __has_warning("-Wdouble-promotion") |
| #pragma clang diagnostic ignored "-Wdouble-promotion" |
| #endif |
| #if __has_warning("-Wcomma") |
| #pragma clang diagnostic ignored "-Wcomma" |
| #endif |
| #if __has_warning("-Wzero-as-null-pointer-constant") |
| #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" |
| #endif |
| #if __has_warning("-Wcast-qual") |
| #pragma clang diagnostic ignored "-Wcast-qual" |
| #endif |
| #if __has_warning("-Wmissing-variable-declarations") |
| #pragma clang diagnostic ignored "-Wmissing-variable-declarations" |
| #endif |
| #if __has_warning("-Wmissing-prototypes") |
| #pragma clang diagnostic ignored "-Wmissing-prototypes" |
| #endif |
| #if __has_warning("-Wcast-align") |
| #pragma clang diagnostic ignored "-Wcast-align" |
| #endif |
| #if __has_warning("-Wnewline-eof") |
| #pragma clang diagnostic ignored "-Wnewline-eof" |
| #endif |
| #if __has_warning("-Wunused-parameter") |
| #pragma clang diagnostic ignored "-Wunused-parameter" |
| #endif |
| #if __has_warning("-Wmismatched-tags") |
| #pragma clang diagnostic ignored "-Wmismatched-tags" |
| #endif |
| #if __has_warning("-Wextra-semi-stmt") |
| #pragma clang diagnostic ignored "-Wextra-semi-stmt" |
| #endif |
| #endif |
| |
| // Disable GCC warnings |
| #ifdef __GNUC__ |
| #pragma GCC diagnostic push |
| #pragma GCC diagnostic ignored "-Wtype-limits" |
| #endif // __GNUC__ |
| |
| #ifndef TINYGLTF_NO_INCLUDE_JSON |
| #ifndef TINYGLTF_USE_RAPIDJSON |
| #include "json.hpp" |
| #else |
| #ifndef TINYGLTF_NO_INCLUDE_RAPIDJSON |
| #include "document.h" |
| #include "prettywriter.h" |
| #include "rapidjson.h" |
| #include "stringbuffer.h" |
| #include "writer.h" |
| #endif |
| #endif |
| #endif |
| |
| #ifdef TINYGLTF_ENABLE_DRACO |
| #include "draco/compression/decode.h" |
| #include "draco/core/decoder_buffer.h" |
| #endif |
| |
| #ifndef TINYGLTF_NO_STB_IMAGE |
| #ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE |
| #include "stb_image.h" |
| #endif |
| #endif |
| |
| #ifndef TINYGLTF_NO_STB_IMAGE_WRITE |
| #ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE |
| #include "stb_image_write.h" |
| #endif |
| #endif |
| |
| #ifdef __clang__ |
| #pragma clang diagnostic pop |
| #endif |
| |
| #ifdef __GNUC__ |
| #pragma GCC diagnostic pop |
| #endif |
| |
| #ifdef _WIN32 |
| |
| // issue 143. |
| // Define NOMINMAX to avoid min/max defines, |
| // but undef it after included Windows.h |
| #ifndef NOMINMAX |
| #define TINYGLTF_INTERNAL_NOMINMAX |
| #define NOMINMAX |
| #endif |
| |
| #ifndef WIN32_LEAN_AND_MEAN |
| #define WIN32_LEAN_AND_MEAN |
| #define TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN |
| #endif |
| #ifndef __MINGW32__ |
| #include <Windows.h> // include API for expanding a file path |
| #else |
| #include <windows.h> |
| #endif |
| |
| #ifdef TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN |
| #undef WIN32_LEAN_AND_MEAN |
| #endif |
| |
| #if defined(TINYGLTF_INTERNAL_NOMINMAX) |
| #undef NOMINMAX |
| #endif |
| |
| #if defined(__GLIBCXX__) // mingw |
| |
| #include <fcntl.h> // _O_RDONLY |
| |
| #include <ext/stdio_filebuf.h> // fstream (all sorts of IO stuff) + stdio_filebuf (=streambuf) |
| |
| #endif |
| |
| #elif !defined(__ANDROID__) && !defined(__OpenBSD__) |
| // #include <wordexp.h> |
| #endif |
| |
| #if defined(__sparcv9) || defined(__powerpc__) |
| // Big endian |
| #else |
| #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU |
| #define TINYGLTF_LITTLE_ENDIAN 1 |
| #endif |
| #endif |
| |
| namespace tinygltf { |
| namespace detail { |
| #ifdef TINYGLTF_USE_RAPIDJSON |
| |
| #ifdef TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR |
| // This uses the RapidJSON CRTAllocator. It is thread safe and multiple |
| // documents may be active at once. |
| using json = |
| rapidjson::GenericValue<rapidjson::UTF8<>, rapidjson::CrtAllocator>; |
| using json_iterator = json::MemberIterator; |
| using json_const_iterator = json::ConstMemberIterator; |
| using json_const_array_iterator = json const *; |
| using JsonDocument = |
| rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator>; |
| rapidjson::CrtAllocator s_CrtAllocator; // stateless and thread safe |
| rapidjson::CrtAllocator &GetAllocator() { return s_CrtAllocator; } |
| #else |
| // This uses the default RapidJSON MemoryPoolAllocator. It is very fast, but |
| // not thread safe. Only a single JsonDocument may be active at any one time, |
| // meaning only a single gltf load/save can be active any one time. |
| using json = rapidjson::Value; |
| using json_iterator = json::MemberIterator; |
| using json_const_iterator = json::ConstMemberIterator; |
| using json_const_array_iterator = json const *; |
| rapidjson::Document *s_pActiveDocument = nullptr; |
| rapidjson::Document::AllocatorType &GetAllocator() { |
| assert(s_pActiveDocument); // Root json node must be JsonDocument type |
| return s_pActiveDocument->GetAllocator(); |
| } |
| |
| #ifdef __clang__ |
| #pragma clang diagnostic push |
| // Suppress JsonDocument(JsonDocument &&rhs) noexcept |
| #pragma clang diagnostic ignored "-Wunused-member-function" |
| #endif |
| |
| struct JsonDocument : public rapidjson::Document { |
| JsonDocument() { |
| assert(s_pActiveDocument == |
| nullptr); // When using default allocator, only one document can be |
| // active at a time, if you need multiple active at once, |
| // define TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR |
| s_pActiveDocument = this; |
| } |
| JsonDocument(const JsonDocument &) = delete; |
| JsonDocument(JsonDocument &&rhs) noexcept |
| : rapidjson::Document(std::move(rhs)) { |
| s_pActiveDocument = this; |
| rhs.isNil = true; |
| } |
| ~JsonDocument() { |
| if (!isNil) { |
| s_pActiveDocument = nullptr; |
| } |
| } |
| |
| private: |
| bool isNil = false; |
| }; |
| |
| #ifdef __clang__ |
| #pragma clang diagnostic pop |
| #endif |
| |
| #endif // TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR |
| |
| #else |
| using nlohmann::json; |
| using json_iterator = json::iterator; |
| using json_const_iterator = json::const_iterator; |
| using json_const_array_iterator = json_const_iterator; |
| using JsonDocument = json; |
| #endif |
| |
| void JsonParse(JsonDocument &doc, const char *str, size_t length, |
| bool throwExc = false) { |
| #ifdef TINYGLTF_USE_RAPIDJSON |
| (void)throwExc; |
| doc.Parse(str, length); |
| #else |
| doc = detail::json::parse(str, str + length, nullptr, throwExc); |
| #endif |
| } |
| } // namespace detail |
| } // namespace tinygltf |
| |
| #ifdef __APPLE__ |
| #include "TargetConditionals.h" |
| #endif |
| |
| #ifdef __clang__ |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Wc++98-compat" |
| #endif |
| |
| namespace tinygltf { |
| |
| /// |
| /// Internal LoadImageDataOption struct. |
| /// This struct is passed through `user_pointer` in LoadImageData. |
| /// The struct is not passed when the user supply their own LoadImageData |
| /// callbacks. |
| /// |
| struct LoadImageDataOption { |
| // true: preserve image channels(e.g. load as RGB image if the image has RGB |
| // channels) default `false`(channels are expanded to RGBA for backward |
| // compatibility). |
| bool preserve_channels{false}; |
| // true: do not decode/decompress image data. |
| // default `false`: decode/decompress image data. |
| bool as_is{false}; |
| }; |
| |
| // Equals function for Value, for recursivity |
| static bool Equals(const tinygltf::Value &one, const tinygltf::Value &other) { |
| if (one.Type() != other.Type()) return false; |
| |
| switch (one.Type()) { |
| case NULL_TYPE: |
| return true; |
| case BOOL_TYPE: |
| return one.Get<bool>() == other.Get<bool>(); |
| case REAL_TYPE: |
| return TINYGLTF_DOUBLE_EQUAL(one.Get<double>(), other.Get<double>()); |
| case INT_TYPE: |
| return one.Get<int>() == other.Get<int>(); |
| case OBJECT_TYPE: { |
| auto oneObj = one.Get<tinygltf::Value::Object>(); |
| auto otherObj = other.Get<tinygltf::Value::Object>(); |
| if (oneObj.size() != otherObj.size()) return false; |
| for (auto &it : oneObj) { |
| auto otherIt = otherObj.find(it.first); |
| if (otherIt == otherObj.end()) return false; |
| |
| if (!Equals(it.second, otherIt->second)) return false; |
| } |
| return true; |
| } |
| case ARRAY_TYPE: { |
| if (one.Size() != other.Size()) return false; |
| for (size_t i = 0; i < one.Size(); ++i) |
| if (!Equals(one.Get(i), other.Get(i))) return false; |
| return true; |
| } |
| case STRING_TYPE: |
| return one.Get<std::string>() == other.Get<std::string>(); |
| case BINARY_TYPE: |
| return one.Get<std::vector<unsigned char> >() == |
| other.Get<std::vector<unsigned char> >(); |
| default: { |
| // unhandled type |
| return false; |
| } |
| } |
| } |
| |
| // Equals function for std::vector<double> using TINYGLTF_DOUBLE_EPSILON |
| static bool Equals(const std::vector<double> &one, |
| const std::vector<double> &other) { |
| if (one.size() != other.size()) return false; |
| for (int i = 0; i < int(one.size()); ++i) { |
| if (!TINYGLTF_DOUBLE_EQUAL(one[size_t(i)], other[size_t(i)])) return false; |
| } |
| return true; |
| } |
| |
| bool Accessor::operator==(const Accessor &other) const { |
| return this->bufferView == other.bufferView && |
| this->byteOffset == other.byteOffset && |
| this->componentType == other.componentType && |
| this->count == other.count && this->extensions == other.extensions && |
| this->extras == other.extras && |
| Equals(this->maxValues, other.maxValues) && |
| Equals(this->minValues, other.minValues) && this->name == other.name && |
| this->normalized == other.normalized && this->type == other.type; |
| } |
| bool Animation::operator==(const Animation &other) const { |
| return this->channels == other.channels && |
| this->extensions == other.extensions && this->extras == other.extras && |
| this->name == other.name && this->samplers == other.samplers; |
| } |
| bool AnimationChannel::operator==(const AnimationChannel &other) const { |
| return this->extensions == other.extensions && this->extras == other.extras && |
| this->target_node == other.target_node && |
| this->target_path == other.target_path && |
| this->sampler == other.sampler; |
| } |
| bool AnimationSampler::operator==(const AnimationSampler &other) const { |
| return this->extras == other.extras && this->extensions == other.extensions && |
| this->input == other.input && |
| this->interpolation == other.interpolation && |
| this->output == other.output; |
| } |
| bool Asset::operator==(const Asset &other) const { |
| return this->copyright == other.copyright && |
| this->extensions == other.extensions && this->extras == other.extras && |
| this->generator == other.generator && |
| this->minVersion == other.minVersion && this->version == other.version; |
| } |
| bool Buffer::operator==(const Buffer &other) const { |
| return this->data == other.data && this->extensions == other.extensions && |
| this->extras == other.extras && this->name == other.name && |
| this->uri == other.uri; |
| } |
| bool BufferView::operator==(const BufferView &other) const { |
| return this->buffer == other.buffer && this->byteLength == other.byteLength && |
| this->byteOffset == other.byteOffset && |
| this->byteStride == other.byteStride && this->name == other.name && |
| this->target == other.target && this->extensions == other.extensions && |
| this->extras == other.extras && |
| this->dracoDecoded == other.dracoDecoded; |
| } |
| bool Camera::operator==(const Camera &other) const { |
| return this->name == other.name && this->extensions == other.extensions && |
| this->extras == other.extras && |
| this->orthographic == other.orthographic && |
| this->perspective == other.perspective && this->type == other.type; |
| } |
| bool Image::operator==(const Image &other) const { |
| return this->bufferView == other.bufferView && |
| this->component == other.component && |
| this->extensions == other.extensions && this->extras == other.extras && |
| this->height == other.height && this->image == other.image && |
| this->mimeType == other.mimeType && this->name == other.name && |
| this->uri == other.uri && this->width == other.width; |
| } |
| bool Light::operator==(const Light &other) const { |
| return Equals(this->color, other.color) && this->name == other.name && |
| this->type == other.type; |
| } |
| bool AudioEmitter::operator==(const AudioEmitter &other) const { |
| return this->name == other.name && |
| TINYGLTF_DOUBLE_EQUAL(this->gain, other.gain) && |
| this->loop == other.loop && this->playing == other.playing && |
| this->type == other.type && |
| this->distanceModel == other.distanceModel && |
| this->source == other.source; |
| } |
| bool AudioSource::operator==(const AudioSource &other) const { |
| return this->name == other.name && this->uri == other.uri; |
| } |
| bool Material::operator==(const Material &other) const { |
| return (this->pbrMetallicRoughness == other.pbrMetallicRoughness) && |
| (this->normalTexture == other.normalTexture) && |
| (this->occlusionTexture == other.occlusionTexture) && |
| (this->emissiveTexture == other.emissiveTexture) && |
| Equals(this->emissiveFactor, other.emissiveFactor) && |
| (this->alphaMode == other.alphaMode) && |
| TINYGLTF_DOUBLE_EQUAL(this->alphaCutoff, other.alphaCutoff) && |
| (this->doubleSided == other.doubleSided) && |
| (this->extensions == other.extensions) && |
| (this->extras == other.extras) && (this->values == other.values) && |
| (this->additionalValues == other.additionalValues) && |
| (this->name == other.name); |
| } |
| bool Mesh::operator==(const Mesh &other) const { |
| return this->extensions == other.extensions && this->extras == other.extras && |
| this->name == other.name && Equals(this->weights, other.weights) && |
| this->primitives == other.primitives; |
| } |
| bool Model::operator==(const Model &other) const { |
| return this->accessors == other.accessors && |
| this->animations == other.animations && this->asset == other.asset && |
| this->buffers == other.buffers && |
| this->bufferViews == other.bufferViews && |
| this->cameras == other.cameras && |
| this->defaultScene == other.defaultScene && |
| this->extensions == other.extensions && |
| this->extensionsRequired == other.extensionsRequired && |
| this->extensionsUsed == other.extensionsUsed && |
| this->extras == other.extras && this->images == other.images && |
| this->lights == other.lights && this->materials == other.materials && |
| this->meshes == other.meshes && this->nodes == other.nodes && |
| this->samplers == other.samplers && this->scenes == other.scenes && |
| this->skins == other.skins && this->textures == other.textures; |
| } |
| bool Node::operator==(const Node &other) const { |
| return this->camera == other.camera && this->children == other.children && |
| this->extensions == other.extensions && this->extras == other.extras && |
| Equals(this->matrix, other.matrix) && this->mesh == other.mesh && |
| (this->light == other.light) && (this->emitter == other.emitter) && |
| this->name == other.name && Equals(this->rotation, other.rotation) && |
| Equals(this->scale, other.scale) && this->skin == other.skin && |
| Equals(this->translation, other.translation) && |
| Equals(this->weights, other.weights); |
| } |
| bool SpotLight::operator==(const SpotLight &other) const { |
| return this->extensions == other.extensions && this->extras == other.extras && |
| TINYGLTF_DOUBLE_EQUAL(this->innerConeAngle, other.innerConeAngle) && |
| TINYGLTF_DOUBLE_EQUAL(this->outerConeAngle, other.outerConeAngle); |
| } |
| bool PositionalEmitter::operator==(const PositionalEmitter &other) const { |
| return this->extensions == other.extensions && this->extras == other.extras && |
| TINYGLTF_DOUBLE_EQUAL(this->coneInnerAngle, other.coneInnerAngle) && |
| TINYGLTF_DOUBLE_EQUAL(this->coneOuterAngle, other.coneOuterAngle) && |
| TINYGLTF_DOUBLE_EQUAL(this->coneOuterGain, other.coneOuterGain) && |
| TINYGLTF_DOUBLE_EQUAL(this->maxDistance, other.maxDistance) && |
| TINYGLTF_DOUBLE_EQUAL(this->refDistance, other.refDistance) && |
| TINYGLTF_DOUBLE_EQUAL(this->rolloffFactor, other.rolloffFactor); |
| } |
| bool OrthographicCamera::operator==(const OrthographicCamera &other) const { |
| return this->extensions == other.extensions && this->extras == other.extras && |
| TINYGLTF_DOUBLE_EQUAL(this->xmag, other.xmag) && |
| TINYGLTF_DOUBLE_EQUAL(this->ymag, other.ymag) && |
| TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) && |
| TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear); |
| } |
| bool Parameter::operator==(const Parameter &other) const { |
| if (this->bool_value != other.bool_value || |
| this->has_number_value != other.has_number_value) |
| return false; |
| |
| if (!TINYGLTF_DOUBLE_EQUAL(this->number_value, other.number_value)) |
| return false; |
| |
| if (this->json_double_value.size() != other.json_double_value.size()) |
| return false; |
| for (auto &it : this->json_double_value) { |
| auto otherIt = other.json_double_value.find(it.first); |
| if (otherIt == other.json_double_value.end()) return false; |
| |
| if (!TINYGLTF_DOUBLE_EQUAL(it.second, otherIt->second)) return false; |
| } |
| |
| if (!Equals(this->number_array, other.number_array)) return false; |
| |
| if (this->string_value != other.string_value) return false; |
| |
| return true; |
| } |
| bool PerspectiveCamera::operator==(const PerspectiveCamera &other) const { |
| return TINYGLTF_DOUBLE_EQUAL(this->aspectRatio, other.aspectRatio) && |
| this->extensions == other.extensions && this->extras == other.extras && |
| TINYGLTF_DOUBLE_EQUAL(this->yfov, other.yfov) && |
| TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) && |
| TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear); |
| } |
| bool Primitive::operator==(const Primitive &other) const { |
| return this->attributes == other.attributes && this->extras == other.extras && |
| this->indices == other.indices && this->material == other.material && |
| this->mode == other.mode && this->targets == other.targets; |
| } |
| bool Sampler::operator==(const Sampler &other) const { |
| return this->extensions == other.extensions && this->extras == other.extras && |
| this->magFilter == other.magFilter && |
| this->minFilter == other.minFilter && this->name == other.name && |
| this->wrapS == other.wrapS && this->wrapT == other.wrapT; |
| |
| // this->wrapR == other.wrapR |
| } |
| bool Scene::operator==(const Scene &other) const { |
| return this->extensions == other.extensions && this->extras == other.extras && |
| this->name == other.name && this->nodes == other.nodes; |
| } |
| bool Skin::operator==(const Skin &other) const { |
| return this->extensions == other.extensions && this->extras == other.extras && |
| this->inverseBindMatrices == other.inverseBindMatrices && |
| this->joints == other.joints && this->name == other.name && |
| this->skeleton == other.skeleton; |
| } |
| bool Texture::operator==(const Texture &other) const { |
| return this->extensions == other.extensions && this->extras == other.extras && |
| this->name == other.name && this->sampler == other.sampler && |
| this->source == other.source; |
| } |
| bool TextureInfo::operator==(const TextureInfo &other) const { |
| return this->extensions == other.extensions && this->extras == other.extras && |
| this->index == other.index && this->texCoord == other.texCoord; |
| } |
| bool NormalTextureInfo::operator==(const NormalTextureInfo &other) const { |
| return this->extensions == other.extensions && this->extras == other.extras && |
| this->index == other.index && this->texCoord == other.texCoord && |
| TINYGLTF_DOUBLE_EQUAL(this->scale, other.scale); |
| } |
| bool OcclusionTextureInfo::operator==(const OcclusionTextureInfo &other) const { |
| return this->extensions == other.extensions && this->extras == other.extras && |
| this->index == other.index && this->texCoord == other.texCoord && |
| TINYGLTF_DOUBLE_EQUAL(this->strength, other.strength); |
| } |
| bool PbrMetallicRoughness::operator==(const PbrMetallicRoughness &other) const { |
| return this->extensions == other.extensions && this->extras == other.extras && |
| (this->baseColorTexture == other.baseColorTexture) && |
| (this->metallicRoughnessTexture == other.metallicRoughnessTexture) && |
| Equals(this->baseColorFactor, other.baseColorFactor) && |
| TINYGLTF_DOUBLE_EQUAL(this->metallicFactor, other.metallicFactor) && |
| TINYGLTF_DOUBLE_EQUAL(this->roughnessFactor, other.roughnessFactor); |
| } |
| bool Value::operator==(const Value &other) const { |
| return Equals(*this, other); |
| } |
| |
| static void swap4(unsigned int *val) { |
| #ifdef TINYGLTF_LITTLE_ENDIAN |
| (void)val; |
| #else |
| unsigned int tmp = *val; |
| unsigned char *dst = reinterpret_cast<unsigned char *>(val); |
| unsigned char *src = reinterpret_cast<unsigned char *>(&tmp); |
| |
| dst[0] = src[3]; |
| dst[1] = src[2]; |
| dst[2] = src[1]; |
| dst[3] = src[0]; |
| #endif |
| } |
| |
| static std::string JoinPath(const std::string &path0, |
| const std::string &path1) { |
| if (path0.empty()) { |
| return path1; |
| } else { |
| // check '/' |
| char lastChar = *path0.rbegin(); |
| if (lastChar != '/') { |
| return path0 + std::string("/") + path1; |
| } else { |
| return path0 + path1; |
| } |
| } |
| } |
| |
| static std::string FindFile(const std::vector<std::string> &paths, |
| const std::string &filepath, FsCallbacks *fs) { |
| if (fs == nullptr || fs->ExpandFilePath == nullptr || |
| fs->FileExists == nullptr) { |
| // Error, fs callback[s] missing |
| return std::string(); |
| } |
| |
| // https://github.com/syoyo/tinygltf/issues/416 |
| // Use strlen() since std::string's size/length reports the number of elements |
| // in the buffer, not the length of string(null-terminated) strip |
| // null-character in the middle of string. |
| size_t slength = strlen(filepath.c_str()); |
| if (slength == 0) { |
| return std::string(); |
| } |
| |
| std::string cleaned_filepath = std::string(filepath.c_str()); |
| |
| for (size_t i = 0; i < paths.size(); i++) { |
| std::string absPath = |
| fs->ExpandFilePath(JoinPath(paths[i], cleaned_filepath), fs->user_data); |
| if (fs->FileExists(absPath, fs->user_data)) { |
| return absPath; |
| } |
| } |
| |
| return std::string(); |
| } |
| |
| static std::string GetFilePathExtension(const std::string &FileName) { |
| if (FileName.find_last_of(".") != std::string::npos) |
| return FileName.substr(FileName.find_last_of(".") + 1); |
| return ""; |
| } |
| |
| static std::string GetBaseDir(const std::string &filepath) { |
| if (filepath.find_last_of("/\\") != std::string::npos) |
| return filepath.substr(0, filepath.find_last_of("/\\") + 1); |
| return ""; |
| } |
| |
| static std::string GetBaseFilename(const std::string &filepath) { |
| auto idx = filepath.find_last_of("/\\"); |
| if (idx != std::string::npos) return filepath.substr(idx + 1); |
| return filepath; |
| } |
| |
| std::string base64_encode(unsigned char const *, unsigned int len); |
| std::string base64_decode(std::string const &s); |
| |
| /* |
| base64.cpp and base64.h |
| |
| Copyright (C) 2004-2008 René Nyffenegger |
| |
| This source code is provided 'as-is', without any express or implied |
| warranty. In no event will the author be held liable for any damages |
| arising from the use of this software. |
| |
| Permission is granted to anyone to use this software for any purpose, |
| including commercial applications, and to alter it and redistribute it |
| freely, subject to the following restrictions: |
| |
| 1. The origin of this source code must not be misrepresented; you must not |
| claim that you wrote the original source code. If you use this source code |
| in a product, an acknowledgment in the product documentation would be |
| appreciated but is not required. |
| |
| 2. Altered source versions must be plainly marked as such, and must not be |
| misrepresented as being the original source code. |
| |
| 3. This notice may not be removed or altered from any source distribution. |
| |
| René Nyffenegger rene.nyffenegger@adp-gmbh.ch |
| |
| */ |
| |
| #ifdef __clang__ |
| #pragma clang diagnostic push |
| #pragma clang diagnostic ignored "-Wsign-conversion" |
| #pragma clang diagnostic ignored "-Wconversion" |
| #endif |
| |
| static inline bool is_base64(unsigned char c) { |
| return (isalnum(c) || (c == '+') || (c == '/')); |
| } |
| |
| std::string base64_encode(unsigned char const *bytes_to_encode, |
| unsigned int in_len) { |
| std::string ret; |
| int i = 0; |
| int j = 0; |
| unsigned char char_array_3[3]; |
| unsigned char char_array_4[4]; |
| |
| const char *base64_chars = |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZ" |
| "abcdefghijklmnopqrstuvwxyz" |
| "0123456789+/"; |
| |
| while (in_len--) { |
| char_array_3[i++] = *(bytes_to_encode++); |
| if (i == 3) { |
| char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; |
| char_array_4[1] = |
| ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); |
| char_array_4[2] = |
| ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); |
| char_array_4[3] = char_array_3[2] & 0x3f; |
| |
| for (i = 0; (i < 4); i++) ret += base64_chars[char_array_4[i]]; |
| i = 0; |
| } |
| } |
| |
| if (i) { |
| for (j = i; j < 3; j++) char_array_3[j] = '\0'; |
| |
| char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; |
| char_array_4[1] = |
| ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); |
| char_array_4[2] = |
| ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); |
| |
| for (j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]]; |
| |
| while ((i++ < 3)) ret += '='; |
| } |
| |
| return ret; |
| } |
| |
| std::string base64_decode(std::string const &encoded_string) { |
| int in_len = static_cast<int>(encoded_string.size()); |
| int i = 0; |
| int j = 0; |
| int in_ = 0; |
| unsigned char char_array_4[4], char_array_3[3]; |
| std::string ret; |
| |
| const std::string base64_chars = |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZ" |
| "abcdefghijklmnopqrstuvwxyz" |
| "0123456789+/"; |
| |
| while (in_len-- && (encoded_string[in_] != '=') && |
| is_base64(encoded_string[in_])) { |
| char_array_4[i++] = encoded_string[in_]; |
| in_++; |
| if (i == 4) { |
| for (i = 0; i < 4; i++) |
| char_array_4[i] = |
| static_cast<unsigned char>(base64_chars.find(char_array_4[i])); |
| |
| char_array_3[0] = |
| (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); |
| char_array_3[1] = |
| ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); |
| char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; |
| |
| for (i = 0; (i < 3); i++) ret += char_array_3[i]; |
| i = 0; |
| } |
| } |
| |
| if (i) { |
| for (j = i; j < 4; j++) char_array_4[j] = 0; |
| |
| for (j = 0; j < 4; j++) |
| char_array_4[j] = |
| static_cast<unsigned char>(base64_chars.find(char_array_4[j])); |
| |
| char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); |
| char_array_3[1] = |
| ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); |
| char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; |
| |
| for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; |
| } |
| |
| return ret; |
| } |
| #ifdef __clang__ |
| #pragma clang diagnostic pop |
| #endif |
| |
| // https://github.com/syoyo/tinygltf/issues/228 |
| // TODO(syoyo): Use uriparser https://uriparser.github.io/ for stricter Uri |
| // decoding? |
| // |
| // Uri Decoding from DLIB |
| // http://dlib.net/dlib/server/server_http.cpp.html |
| // --- dlib begin ------------------------------------------------------------ |
| // Copyright (C) 2003 Davis E. King (davis@dlib.net) |
| // License: Boost Software License |
| // Boost Software License - Version 1.0 - August 17th, 2003 |
| |
| // Permission is hereby granted, free of charge, to any person or organization |
| // obtaining a copy of the software and accompanying documentation covered by |
| // this license (the "Software") to use, reproduce, display, distribute, |
| // execute, and transmit the Software, and to prepare derivative works of the |
| // Software, and to permit third-parties to whom the Software is furnished to |
| // do so, all subject to the following: |
| // The copyright notices in the Software and this entire statement, including |
| // the above license grant, this restriction and the following disclaimer, |
| // must be included in all copies of the Software, in whole or in part, and |
| // all derivative works of the Software, unless such copies or derivative |
| // works are solely in the form of machine-executable object code generated by |
| // a source language processor. |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT |
| // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE |
| // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, |
| // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
| // DEALINGS IN THE SOFTWARE. |
| // |
| namespace dlib { |
| |
| inline unsigned char from_hex(unsigned char ch) { |
| if (ch <= '9' && ch >= '0') |
| ch -= '0'; |
| else if (ch <= 'f' && ch >= 'a') |
| ch -= 'a' - 10; |
| else if (ch <= 'F' && ch >= 'A') |
| ch -= 'A' - 10; |
| else |
| ch = 0; |
| return ch; |
| } |
| |
| static const std::string urldecode(const std::string &str) { |
| using namespace std; |
| string result; |
| string::size_type i; |
| for (i = 0; i < str.size(); ++i) { |
| if (str[i] == '+') { |
| result += ' '; |
| } else if (str[i] == '%' && str.size() > i + 2) { |
| const unsigned char ch1 = |
| from_hex(static_cast<unsigned char>(str[i + 1])); |
| const unsigned char ch2 = |
| from_hex(static_cast<unsigned char>(str[i + 2])); |
| const unsigned char ch = static_cast<unsigned char>((ch1 << 4) | ch2); |
| result += static_cast<char>(ch); |
| i += 2; |
| } else { |
| result += str[i]; |
| } |
| } |
| return result; |
| } |
| |
| } // namespace dlib |
| // --- dlib end -------------------------------------------------------------- |
| |
| bool URIDecode(const std::string &in_uri, std::string *out_uri, |
| void *user_data) { |
| (void)user_data; |
| *out_uri = dlib::urldecode(in_uri); |
| return true; |
| } |
| |
| static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err, |
| std::string *warn, const std::string &filename, |
| const std::string &basedir, bool required, |
| size_t reqBytes, bool checkSize, |
| size_t maxFileSize, FsCallbacks *fs) { |
| if (fs == nullptr || fs->FileExists == nullptr || |
| fs->ExpandFilePath == nullptr || fs->ReadWholeFile == nullptr) { |
| // This is a developer error, assert() ? |
| if (err) { |
| (*err) += "FS callback[s] not set\n"; |
| } |
| return false; |
| } |
| |
| std::string *failMsgOut = required ? err : warn; |
| |
| out->clear(); |
| |
| std::vector<std::string> paths; |
| paths.push_back(basedir); |
| paths.push_back("."); |
| |
| std::string filepath = FindFile(paths, filename, fs); |
| if (filepath.empty() || filename.empty()) { |
| if (failMsgOut) { |
| (*failMsgOut) += "File not found : " + filename + "\n"; |
| } |
| return false; |
| } |
| |
| // Check file size |
| if (fs->GetFileSizeInBytes) { |
| size_t file_size{0}; |
| std::string _err; |
| bool ok = |
| fs->GetFileSizeInBytes(&file_size, &_err, filepath, fs->user_data); |
| if (!ok) { |
| if (_err.size()) { |
| if (failMsgOut) { |
| (*failMsgOut) += "Getting file size failed : " + filename + |
| ", err = " + _err + "\n"; |
| } |
| } |
| return false; |
| } |
| |
| if (file_size > maxFileSize) { |
| if (failMsgOut) { |
| (*failMsgOut) += "File size " + std::to_string(file_size) + |
| " exceeds maximum allowed file size " + |
| std::to_string(maxFileSize) + " : " + filepath + "\n"; |
| } |
| return false; |
| } |
| } |
| |
| std::vector<unsigned char> buf; |
| std::string fileReadErr; |
| bool fileRead = |
| fs->ReadWholeFile(&buf, &fileReadErr, filepath, fs->user_data); |
| if (!fileRead) { |
| if (failMsgOut) { |
| (*failMsgOut) += |
| "File read error : " + filepath + " : " + fileReadErr + "\n"; |
| } |
| return false; |
| } |
| |
| size_t sz = buf.size(); |
| if (sz == 0) { |
| if (failMsgOut) { |
| (*failMsgOut) += "File is empty : " + filepath + "\n"; |
| } |
| return false; |
| } |
| |
| if (checkSize) { |
| if (reqBytes == sz) { |
| out->swap(buf); |
| return true; |
| } else { |
| std::stringstream ss; |
| ss << "File size mismatch : " << filepath << ", requestedBytes " |
| << reqBytes << ", but got " << sz << std::endl; |
| if (failMsgOut) { |
| (*failMsgOut) += ss.str(); |
| } |
| return false; |
| } |
| } |
| |
| out->swap(buf); |
| return true; |
| } |
| |
| void TinyGLTF::SetParseStrictness(ParseStrictness strictness) { |
| strictness_ = strictness; |
| } |
| |
| void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) { |
| if (func == nullptr) { |
| RemoveImageLoader(); |
| return; |
| } |
| LoadImageData = std::move(func); |
| load_image_user_data_ = user_data; |
| user_image_loader_ = true; |
| } |
| |
| void TinyGLTF::RemoveImageLoader() { |
| LoadImageData = |
| #ifndef TINYGLTF_NO_STB_IMAGE |
| &tinygltf::LoadImageData; |
| #else |
| nullptr; |
| #endif |
| |
| load_image_user_data_ = nullptr; |
| user_image_loader_ = false; |
| } |
| |
| #ifndef TINYGLTF_NO_STB_IMAGE |
| bool LoadImageData(Image *image, const int image_idx, std::string *err, |
| std::string *warn, int req_width, int req_height, |
| const unsigned char *bytes, int size, void *user_data) { |
| (void)warn; |
| |
| LoadImageDataOption option; |
| if (user_data) { |
| option = *reinterpret_cast<LoadImageDataOption *>(user_data); |
| } |
| |
| int w = 0, h = 0, comp = 0, req_comp = 0; |
| |
| // Try to decode image header |
| if (!stbi_info_from_memory(bytes, size, &w, &h, &comp)) { |
| // On failure, if we load images as is, we just warn. |
| std::string* msgOut = option.as_is ? warn : err; |
| if (msgOut) { |
| (*msgOut) += |
| "Unknown image format. STB cannot decode image header for image[" + |
| std::to_string(image_idx) + "] name = \"" + image->name + "\".\n"; |
| } |
| if (!option.as_is) { |
| // If we decode images, error out. |
| return false; |
| } else { |
| // If we load images as is, we copy the image data, |
| // set all image properties to invalid, and report success. |
| image->width = image->height = image->component = -1; |
| image->bits = image->pixel_type = -1; |
| image->image.resize(static_cast<size_t>(size)); |
| std::copy(bytes, bytes + size, image->image.begin()); |
| return true; |
| } |
| } |
| |
| int bits = 8; |
| int pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE; |
| |
| if (stbi_is_16_bit_from_memory(bytes, size)) { |
| bits = 16; |
| pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT; |
| } |
| |
| // preserve_channels true: Use channels stored in the image file. |
| // false: force 32-bit textures for common Vulkan compatibility. It appears |
| // that some GPU drivers do not support 24-bit images for Vulkan |
| req_comp = (option.preserve_channels || option.as_is) ? 0 : 4; |
| |
| unsigned char* data = nullptr; |
| // Perform image decoding if requested |
| if (!option.as_is) { |
| // If the image is marked as 16 bit per channel, attempt to decode it as such first. |
| // If that fails, we are going to attempt to load it as 8 bit per channel image. |
| if (bits == 16) { |
| data = reinterpret_cast<unsigned char *>(stbi_load_16_from_memory(bytes, size, &w, &h, &comp, req_comp)); |
| } |
| // Load as 8 bit per channel data |
| if (!data) { |
| data = stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp); |
| if (!data) { |
| if (err) { |
| (*err) += |
| "Unknown image format. STB cannot decode image data for image[" + |
| std::to_string(image_idx) + "] name = \"" + image->name + "\".\n"; |
| } |
| return false; |
| } |
| // If we were succesful, mark as 8 bit |
| bits = 8; |
| pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE; |
| } |
| } |
| |
| if ((w < 1) || (h < 1)) { |
| stbi_image_free(data); |
| if (err) { |
| (*err) += "Invalid image data for image[" + std::to_string(image_idx) + |
| "] name = \"" + image->name + "\"\n"; |
| } |
| return false; |
| } |
| |
| if (req_width > 0) { |
| if (req_width != w) { |
| stbi_image_free(data); |
| if (err) { |
| (*err) += "Image width mismatch for image[" + |
| std::to_string(image_idx) + "] name = \"" + image->name + |
| "\"\n"; |
| } |
| return false; |
| } |
| } |
| |
| if (req_height > 0) { |
| if (req_height != h) { |
| stbi_image_free(data); |
| if (err) { |
| (*err) += "Image height mismatch. for image[" + |
| std::to_string(image_idx) + "] name = \"" + image->name + |
| "\"\n"; |
| } |
| return false; |
| } |
| } |
| |
| if (req_comp != 0) { |
| // loaded data has `req_comp` channels(components) |
| comp = req_comp; |
| } |
| |
| image->width = w; |
| image->height = h; |
| image->component = comp; |
| image->bits = bits; |
| image->pixel_type = pixel_type; |
| image->as_is = option.as_is; |
| |
| if (option.as_is) { |
| // Store the original image data |
| image->image.resize(static_cast<size_t>(size)); |
| std::copy(bytes, bytes + size, image->image.begin()); |
| } |
| else { |
| // Store the decoded image data |
| image->image.resize(static_cast<size_t>(w * h * comp) * size_t(bits / 8)); |
| std::copy(data, data + w * h * comp * (bits / 8), image->image.begin()); |
| } |
| |
| stbi_image_free(data); |
| return true; |
| } |
| #endif |
| |
| void TinyGLTF::SetImageWriter(WriteImageDataFunction func, void *user_data) { |
| WriteImageData = std::move(func); |
| write_image_user_data_ = user_data; |
| } |
| |
| #ifndef TINYGLTF_NO_STB_IMAGE_WRITE |
| static void WriteToMemory_stbi(void *context, void *data, int size) { |
| std::vector<unsigned char> *buffer = |
| reinterpret_cast<std::vector<unsigned char> *>(context); |
| |
| unsigned char *pData = reinterpret_cast<unsigned char *>(data); |
| |
| buffer->insert(buffer->end(), pData, pData + size); |
| } |
| |
| bool WriteImageData(const std::string *basepath, const std::string *filename, |
| const Image *image, bool embedImages, |
| const FsCallbacks* fs_cb, const URICallbacks *uri_cb, |
| std::string *out_uri, void *) { |
| const std::string ext = GetFilePathExtension(*filename); |
| |
| // Write image to temporary buffer |
| std::string header; |
| std::vector<unsigned char> data; |
| |
| // If the image data is already encoded, take it as is |
| if (image->as_is) { |
| data = image->image; |
| } |
| |
| if (ext == "png") { |
| if (!image->as_is) { |
| if ((image->bits != 8) || |
| (image->pixel_type != TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)) { |
| // Unsupported pixel format |
| return false; |
| } |
| |
| if (!stbi_write_png_to_func(WriteToMemory_stbi, &data, image->width, |
| image->height, image->component, |
| &image->image[0], 0)) { |
| return false; |
| } |
| } |
| header = "data:image/png;base64,"; |
| } else if (ext == "jpg") { |
| if (!image->as_is && |
| !stbi_write_jpg_to_func(WriteToMemory_stbi, &data, image->width, |
| image->height, image->component, |
| &image->image[0], 100)) { |
| return false; |
| } |
| header = "data:image/jpeg;base64,"; |
| } else if (ext == "bmp") { |
| if (!image->as_is && |
| !stbi_write_bmp_to_func(WriteToMemory_stbi, &data, image->width, |
| image->height, image->component, |
| &image->image[0])) { |
| return false; |
| } |
| header = "data:image/bmp;base64,"; |
| } else if (!embedImages) { |
| // Error: can't output requested format to file |
| return false; |
| } |
| |
| if (embedImages) { |
| // Embed base64-encoded image into URI |
| if (data.size()) { |
| *out_uri = header + base64_encode(&data[0], |
| static_cast<unsigned int>(data.size())); |
| } else { |
| // Throw error? |
| } |
| } else { |
| // Write image to disc |
| if ((fs_cb != nullptr) && (fs_cb->WriteWholeFile != nullptr)) { |
| const std::string imagefilepath = JoinPath(*basepath, *filename); |
| std::string writeError; |
| if (!fs_cb->WriteWholeFile(&writeError, imagefilepath, data, |
| fs_cb->user_data)) { |
| // Could not write image file to disc; Throw error ? |
| return false; |
| } |
| } else { |
| // Throw error? |
| } |
| if (uri_cb->encode) { |
| if (!uri_cb->encode(*filename, "image", out_uri, uri_cb->user_data)) { |
| return false; |
| } |
| } else { |
| *out_uri = *filename; |
| } |
| } |
| |
| return true; |
| } |
| #endif |
| |
| bool TinyGLTF::SetURICallbacks(URICallbacks callbacks, std::string* err) { |
| if (callbacks.decode == nullptr) { |
| if (err != nullptr) { |
| *err = "URI Callback require a non-null decode function."; |
| } |
| return false; |
| } |
| |
| if (callbacks.decode) { |
| uri_cb = std::move(callbacks); |
| } |
| return true; |
| } |
| |
| bool TinyGLTF::SetFsCallbacks(FsCallbacks callbacks, std::string *err) { |
| // If callbacks are defined at all, they must all be defined. |
| if (callbacks.FileExists == nullptr || callbacks.ExpandFilePath == nullptr || |
| callbacks.ReadWholeFile == nullptr || |
| callbacks.WriteWholeFile == nullptr || |
| callbacks.GetFileSizeInBytes == nullptr) { |
| if (err != nullptr) { |
| *err = |
| "FS Callbacks must be completely defined. At least one callback is " |
| "null."; |
| } |
| return false; |
| } |
| fs = std::move(callbacks); |
| return true; |
| } |
| |
| #ifdef _WIN32 |
|