blob: e0f99ccb0fe8ff9b5d9ffccb9a8f81dda9b57787 [file] [log] [blame]
//
// 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