52 #if !defined(TINYGLTF_USE_CPP14) && defined(__cplusplus) && \
53 (__cplusplus >= 201402L)
54 #define TINYGLTF_USE_CPP14
58 #ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
59 #include <android/asset_manager.h>
64 #if (__GNUC__ < 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ <= 8))
65 #define TINYGLTF_NOEXCEPT
67 #define TINYGLTF_NOEXCEPT noexcept
70 #define TINYGLTF_NOEXCEPT noexcept
73 #define DEFAULT_METHODS(x) \
75 x(const x &) = default; \
76 x(x &&) TINYGLTF_NOEXCEPT = default; \
77 x &operator=(const x &) = default; \
78 x &operator=(x &&) TINYGLTF_NOEXCEPT = default;
82 #define TINYGLTF_MODE_POINTS (0)
83 #define TINYGLTF_MODE_LINE (1)
84 #define TINYGLTF_MODE_LINE_LOOP (2)
85 #define TINYGLTF_MODE_LINE_STRIP (3)
86 #define TINYGLTF_MODE_TRIANGLES (4)
87 #define TINYGLTF_MODE_TRIANGLE_STRIP (5)
88 #define TINYGLTF_MODE_TRIANGLE_FAN (6)
90 #define TINYGLTF_COMPONENT_TYPE_BYTE (5120)
91 #define TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE (5121)
92 #define TINYGLTF_COMPONENT_TYPE_SHORT (5122)
93 #define TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT (5123)
94 #define TINYGLTF_COMPONENT_TYPE_INT (5124)
95 #define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125)
96 #define TINYGLTF_COMPONENT_TYPE_FLOAT (5126)
97 #define TINYGLTF_COMPONENT_TYPE_DOUBLE \
98 (5130) // OpenGL double type. Note that some of glTF 2.0 validator does not
103 #define TINYGLTF_TEXTURE_FILTER_NEAREST (9728)
104 #define TINYGLTF_TEXTURE_FILTER_LINEAR (9729)
105 #define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST (9984)
106 #define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST (9985)
107 #define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR (9986)
108 #define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR (9987)
110 #define TINYGLTF_TEXTURE_WRAP_REPEAT (10497)
111 #define TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE (33071)
112 #define TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT (33648)
115 #define TINYGLTF_PARAMETER_TYPE_BYTE (5120)
116 #define TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE (5121)
117 #define TINYGLTF_PARAMETER_TYPE_SHORT (5122)
118 #define TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT (5123)
119 #define TINYGLTF_PARAMETER_TYPE_INT (5124)
120 #define TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT (5125)
121 #define TINYGLTF_PARAMETER_TYPE_FLOAT (5126)
123 #define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC2 (35664)
124 #define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC3 (35665)
125 #define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC4 (35666)
127 #define TINYGLTF_PARAMETER_TYPE_INT_VEC2 (35667)
128 #define TINYGLTF_PARAMETER_TYPE_INT_VEC3 (35668)
129 #define TINYGLTF_PARAMETER_TYPE_INT_VEC4 (35669)
131 #define TINYGLTF_PARAMETER_TYPE_BOOL (35670)
132 #define TINYGLTF_PARAMETER_TYPE_BOOL_VEC2 (35671)
133 #define TINYGLTF_PARAMETER_TYPE_BOOL_VEC3 (35672)
134 #define TINYGLTF_PARAMETER_TYPE_BOOL_VEC4 (35673)
136 #define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT2 (35674)
137 #define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT3 (35675)
138 #define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT4 (35676)
140 #define TINYGLTF_PARAMETER_TYPE_SAMPLER_2D (35678)
144 #define TINYGLTF_TYPE_VEC2 (2)
145 #define TINYGLTF_TYPE_VEC3 (3)
146 #define TINYGLTF_TYPE_VEC4 (4)
147 #define TINYGLTF_TYPE_MAT2 (32 + 2)
148 #define TINYGLTF_TYPE_MAT3 (32 + 3)
149 #define TINYGLTF_TYPE_MAT4 (32 + 4)
150 #define TINYGLTF_TYPE_SCALAR (64 + 1)
151 #define TINYGLTF_TYPE_VECTOR (64 + 4)
152 #define TINYGLTF_TYPE_MATRIX (64 + 16)
154 #define TINYGLTF_IMAGE_FORMAT_JPEG (0)
155 #define TINYGLTF_IMAGE_FORMAT_PNG (1)
156 #define TINYGLTF_IMAGE_FORMAT_BMP (2)
157 #define TINYGLTF_IMAGE_FORMAT_GIF (3)
159 #define TINYGLTF_TEXTURE_FORMAT_ALPHA (6406)
160 #define TINYGLTF_TEXTURE_FORMAT_RGB (6407)
161 #define TINYGLTF_TEXTURE_FORMAT_RGBA (6408)
162 #define TINYGLTF_TEXTURE_FORMAT_LUMINANCE (6409)
163 #define TINYGLTF_TEXTURE_FORMAT_LUMINANCE_ALPHA (6410)
165 #define TINYGLTF_TEXTURE_TARGET_TEXTURE2D (3553)
166 #define TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE (5121)
168 #define TINYGLTF_TARGET_ARRAY_BUFFER (34962)
169 #define TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER (34963)
171 #define TINYGLTF_SHADER_TYPE_VERTEX_SHADER (35633)
172 #define TINYGLTF_SHADER_TYPE_FRAGMENT_SHADER (35632)
174 #define TINYGLTF_DOUBLE_EPS (1.e-12)
175 #define TINYGLTF_DOUBLE_EQUAL(a, b) (std::fabs((b) - (a)) < TINYGLTF_DOUBLE_EPS)
178 #ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
179 #ifdef TINYGLTF_IMPLEMENTATION
180 AAssetManager *asset_manager =
nullptr;
182 extern AAssetManager *asset_manager;
249 bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
250 const std::string &in,
size_t reqBytes,
bool checkSize);
253 #pragma clang diagnostic push
255 #pragma clang diagnostic ignored "-Wexit-time-destructors"
256 #pragma clang diagnostic ignored "-Wpadded"
263 typedef std::map<std::string, Value>
Object;
283 explicit Value(std::vector<unsigned char> &&v) noexcept
334 template <
typename T>
335 const T &
Get()
const;
336 template <
typename T>
341 static Value null_value;
350 static Value null_value;
353 return (it !=
object_value_.end()) ? it->second : null_value;
362 bool Has(
const std::string &key)
const {
369 std::vector<std::string>
Keys()
const {
370 std::vector<std::string> keys;
375 keys.push_back(it->first);
398 #pragma clang diagnostic pop
401 #define TINYGLTF_VALUE_GET(ctype, var) \
403 inline const ctype &Value::Get<ctype>() const { \
407 inline ctype &Value::Get<ctype>() { \
417 #undef TINYGLTF_VALUE_GET
420 #pragma clang diagnostic push
421 #pragma clang diagnostic ignored "-Wc++98-compat"
422 #pragma clang diagnostic ignored "-Wpadded"
449 return int(it->second);
460 return int(it->second);
511 #pragma clang diagnostic pop
515 #pragma clang diagnostic push
516 #pragma clang diagnostic ignored "-Wpadded"
871 int componentSizeInBytes =
873 if (componentSizeInBytes <= 0) {
878 if (numComponents <= 0) {
882 return componentSizeInBytes * numComponents;
886 int componentSizeInBytes =
888 if (componentSizeInBytes <= 0) {
892 if ((bufferViewObject.
byteStride % uint32_t(componentSizeInBytes)) != 0) {
895 return static_cast<int>(bufferViewObject.
byteStride);
974 std::vector<std::map<std::string, int> >
targets;
1268 std::string *out_uri,
void *user_data);
1276 std::string *out_uri,
void *user_data);
1279 bool URIDecode(
const std::string &in_uri, std::string *out_uri,
1296 std::string *, int, int,
1297 const unsigned char *, int,
1298 void *user_pointer);
1306 const std::string *filename,
1307 const Image *image,
bool embedImages,
1309 std::string *out_uri,
1310 void *user_pointer);
1312 #ifndef TINYGLTF_NO_STB_IMAGE
1315 std::string *warn,
int req_width,
int req_height,
1316 const unsigned char *bytes,
int size,
void *);
1319 #ifndef TINYGLTF_NO_STB_IMAGE_WRITE
1321 bool WriteImageData(
const std::string *basepath,
const std::string *filename,
1322 const Image *image,
bool embedImages,
1323 const URICallbacks *uri_cb, std::string *out_uri,
void *);
1340 std::string *,
const std::string &,
1347 const std::vector<unsigned char> &,
1354 const std::string &abs_filename,
1372 #ifndef TINYGLTF_NO_FS
1375 bool FileExists(
const std::string &abs_filename,
void *);
1384 std::string
ExpandFilePath(
const std::string &filepath,
void *userdata);
1386 bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
1387 const std::string &filepath,
void *);
1389 bool WriteWholeFile(std::string *err,
const std::string &filepath,
1390 const std::vector<unsigned char> &contents,
void *);
1393 const std::string &filepath,
void *);
1402 #pragma clang diagnostic push
1403 #pragma clang diagnostic ignored "-Wc++98-compat"
1408 #pragma clang diagnostic pop
1420 const std::string &filename,
1432 const char *str,
const unsigned int length,
1433 const std::string &base_dir,
1442 const std::string &filename,
1454 const unsigned char *bytes,
1455 const unsigned int length,
1456 const std::string &base_dir =
"",
1463 bool prettyPrint,
bool writeBinary);
1469 bool embedImages,
bool embedBuffers,
1470 bool prettyPrint,
bool writeBinary);
1512 serialize_default_values_ = enabled;
1523 store_original_json_for_extras_and_extensions_ = enabled;
1527 return store_original_json_for_extras_and_extensions_;
1535 preserve_image_channels_ = onoff;
1544 max_external_file_size_ = max_bytes;
1558 bool LoadFromString(
Model *model, std::string *err, std::string *warn,
1559 const char *str,
const unsigned int length,
1560 const std::string &base_dir,
unsigned int check_sections);
1562 const unsigned char *bin_data_ =
nullptr;
1563 size_t bin_size_ = 0;
1564 bool is_binary_ =
false;
1568 bool serialize_default_values_ =
false;
1570 bool store_original_json_for_extras_and_extensions_ =
false;
1572 bool preserve_image_channels_ =
false;
1575 size_t max_external_file_size_{
1576 size_t((std::numeric_limits<int32_t>::max)())};
1584 URICallbacks uri_cb = {
1594 #ifndef TINYGLTF_NO_STB_IMAGE
1599 void *load_image_user_data_{
nullptr};
1600 bool user_image_loader_{
false};
1603 #ifndef TINYGLTF_NO_STB_IMAGE_WRITE
1608 void *write_image_user_data_{
nullptr};
1612 #pragma clang diagnostic pop // -Wpadded
1617 #endif // TINY_GLTF_H_
1619 #if defined(TINYGLTF_IMPLEMENTATION) || defined(__INTELLISENSE__)
1620 #include <algorithm>
1622 #ifndef TINYGLTF_NO_FS
1623 #include <sys/stat.h>
1632 #pragma clang diagnostic push
1633 #pragma clang diagnostic ignored "-Wfloat-equal"
1634 #pragma clang diagnostic ignored "-Wexit-time-destructors"
1635 #pragma clang diagnostic ignored "-Wconversion"
1636 #pragma clang diagnostic ignored "-Wold-style-cast"
1637 #pragma clang diagnostic ignored "-Wglobal-constructors"
1638 #if __has_warning("-Wreserved-id-macro")
1639 #pragma clang diagnostic ignored "-Wreserved-id-macro"
1641 #pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
1642 #pragma clang diagnostic ignored "-Wpadded"
1643 #pragma clang diagnostic ignored "-Wc++98-compat"
1644 #pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
1645 #pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
1646 #pragma clang diagnostic ignored "-Wswitch-enum"
1647 #pragma clang diagnostic ignored "-Wimplicit-fallthrough"
1648 #pragma clang diagnostic ignored "-Wweak-vtables"
1649 #pragma clang diagnostic ignored "-Wcovered-switch-default"
1650 #if __has_warning("-Wdouble-promotion")
1651 #pragma clang diagnostic ignored "-Wdouble-promotion"
1653 #if __has_warning("-Wcomma")
1654 #pragma clang diagnostic ignored "-Wcomma"
1656 #if __has_warning("-Wzero-as-null-pointer-constant")
1657 #pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
1659 #if __has_warning("-Wcast-qual")
1660 #pragma clang diagnostic ignored "-Wcast-qual"
1662 #if __has_warning("-Wmissing-variable-declarations")
1663 #pragma clang diagnostic ignored "-Wmissing-variable-declarations"
1665 #if __has_warning("-Wmissing-prototypes")
1666 #pragma clang diagnostic ignored "-Wmissing-prototypes"
1668 #if __has_warning("-Wcast-align")
1669 #pragma clang diagnostic ignored "-Wcast-align"
1671 #if __has_warning("-Wnewline-eof")
1672 #pragma clang diagnostic ignored "-Wnewline-eof"
1674 #if __has_warning("-Wunused-parameter")
1675 #pragma clang diagnostic ignored "-Wunused-parameter"
1677 #if __has_warning("-Wmismatched-tags")
1678 #pragma clang diagnostic ignored "-Wmismatched-tags"
1680 #if __has_warning("-Wextra-semi-stmt")
1681 #pragma clang diagnostic ignored "-Wextra-semi-stmt"
1687 #pragma GCC diagnostic push
1688 #pragma GCC diagnostic ignored "-Wtype-limits"
1691 #ifndef TINYGLTF_NO_INCLUDE_JSON
1692 #ifndef TINYGLTF_USE_RAPIDJSON
1695 #ifndef TINYGLTF_NO_INCLUDE_RAPIDJSON
1696 #include "document.h"
1697 #include "prettywriter.h"
1698 #include "rapidjson.h"
1699 #include "stringbuffer.h"
1705 #ifdef TINYGLTF_ENABLE_DRACO
1706 #include "draco/compression/decode.h"
1707 #include "draco/core/decoder_buffer.h"
1710 #ifndef TINYGLTF_NO_STB_IMAGE
1711 #ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE
1716 #ifndef TINYGLTF_NO_STB_IMAGE_WRITE
1717 #ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE
1723 #pragma clang diagnostic pop
1727 #pragma GCC diagnostic pop
1736 #define TINYGLTF_INTERNAL_NOMINMAX
1740 #ifndef WIN32_LEAN_AND_MEAN
1741 #define WIN32_LEAN_AND_MEAN
1742 #define TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1745 #include <Windows.h>
1747 #include <windows.h>
1750 #ifdef TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1751 #undef WIN32_LEAN_AND_MEAN
1754 #if defined(TINYGLTF_INTERNAL_NOMINMAX)
1758 #if defined(__GLIBCXX__) // mingw
1762 #include <ext/stdio_filebuf.h>
1766 #elif !defined(__ANDROID__) && !defined(__OpenBSD__)
1770 #if defined(__sparcv9) || defined(__powerpc__)
1773 #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
1774 #define TINYGLTF_LITTLE_ENDIAN 1
1782 #ifndef TINYGLTF_NO_FS
1791 nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
1799 #ifdef TINYGLTF_USE_RAPIDJSON
1801 #ifdef TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
1805 rapidjson::GenericValue<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
1806 using json_iterator = json::MemberIterator;
1807 using json_const_iterator = json::ConstMemberIterator;
1808 using json_const_array_iterator =
json const *;
1809 using JsonDocument =
1810 rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
1811 rapidjson::CrtAllocator s_CrtAllocator;
1812 rapidjson::CrtAllocator &GetAllocator() {
return s_CrtAllocator; }
1817 using json = rapidjson::Value;
1818 using json_iterator = json::MemberIterator;
1819 using json_const_iterator = json::ConstMemberIterator;
1820 using json_const_array_iterator =
json const *;
1821 rapidjson::Document *s_pActiveDocument =
nullptr;
1822 rapidjson::Document::AllocatorType &GetAllocator() {
1823 assert(s_pActiveDocument);
1824 return s_pActiveDocument->GetAllocator();
1828 #pragma clang diagnostic push
1830 #pragma clang diagnostic ignored "-Wunused-member-function"
1833 struct JsonDocument :
public rapidjson::Document {
1835 assert(s_pActiveDocument ==
1839 s_pActiveDocument =
this;
1841 JsonDocument(
const JsonDocument &) =
delete;
1842 JsonDocument(JsonDocument &&rhs) noexcept
1843 : rapidjson::Document(std::move(rhs)) {
1844 s_pActiveDocument =
this;
1849 s_pActiveDocument =
nullptr;
1858 #pragma clang diagnostic pop
1861 #endif // TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
1865 using json_iterator = json::iterator;
1866 using json_const_iterator = json::const_iterator;
1867 using json_const_array_iterator = json_const_iterator;
1868 using JsonDocument =
json;
1871 void JsonParse(JsonDocument &doc,
const char *str,
size_t length,
1872 bool throwExc =
false) {
1873 #ifdef TINYGLTF_USE_RAPIDJSON
1875 doc.Parse(str, length);
1884 #include "TargetConditionals.h"
1888 #pragma clang diagnostic push
1889 #pragma clang diagnostic ignored "-Wc++98-compat"
1900 struct LoadImageDataOption {
1904 bool preserve_channels{
false};
1909 if (one.
Type() != other.
Type())
return false;
1911 switch (one.
Type()) {
1915 return one.
Get<
bool>() == other.
Get<
bool>();
1919 return one.
Get<
int>() == other.
Get<
int>();
1923 if (oneObj.size() != otherObj.size())
return false;
1924 for (
auto &it : oneObj) {
1925 auto otherIt = otherObj.find(it.first);
1926 if (otherIt == otherObj.end())
return false;
1928 if (!Equals(it.second, otherIt->second))
return false;
1933 if (one.
Size() != other.
Size())
return false;
1934 for (
size_t i = 0;
i < one.
Size(); ++
i)
1935 if (!Equals(one.
Get(
i), other.
Get(
i)))
return false;
1939 return one.
Get<std::string>() == other.
Get<std::string>();
1941 return one.
Get<std::vector<unsigned char> >() ==
1942 other.
Get<std::vector<unsigned char> >();
1951 static bool Equals(
const std::vector<double> &one,
1952 const std::vector<double> &other) {
1953 if (one.size() != other.size())
return false;
1954 for (
int i = 0;
i < int(one.size()); ++
i) {
1961 return this->
bufferView == other.bufferView &&
1965 this->
extras == other.extras &&
1966 Equals(this->
maxValues, other.maxValues) &&
1967 Equals(this->
minValues, other.minValues) && this->
name == other.name &&
1968 this->
normalized == other.normalized && this->
type == other.type;
1971 return this->
channels == other.channels &&
1973 this->
name == other.name && this->
samplers == other.samplers;
1976 return this->
extensions == other.extensions && this->
extras == other.extras &&
1979 this->
sampler == other.sampler;
1982 return this->
extras == other.extras && this->
extensions == other.extensions &&
1983 this->
input == other.input &&
1985 this->
output == other.output;
1988 return this->
copyright == other.copyright &&
1994 return this->
data == other.data && this->
extensions == other.extensions &&
1995 this->
extras == other.extras && this->
name == other.name &&
1996 this->
uri == other.uri;
1999 return this->
buffer == other.buffer && this->
byteLength == other.byteLength &&
2001 this->
byteStride == other.byteStride && this->
name == other.name &&
2003 this->
extras == other.extras &&
2007 return this->
name == other.name && this->
extensions == other.extensions &&
2008 this->
extras == other.extras &&
2013 return this->
bufferView == other.bufferView &&
2016 this->
height == other.height && this->image == other.image &&
2017 this->
mimeType == other.mimeType && this->
name == other.name &&
2018 this->
uri == other.uri && this->
width == other.width;
2021 return Equals(this->
color, other.color) && this->
name == other.name &&
2022 this->
type == other.type;
2025 return this->
name == other.name &&
2027 this->
loop == other.loop && this->
playing == other.playing &&
2028 this->
type == other.type &&
2030 this->
source == other.source;
2033 return this->
name == other.name && this->
uri == other.uri;
2045 (this->
extras == other.extras) && (this->
values == other.values) &&
2047 (this->
name == other.name);
2050 return this->
extensions == other.extensions && this->
extras == other.extras &&
2051 this->
name == other.name && Equals(this->
weights, other.weights) &&
2055 return this->
accessors == other.accessors &&
2057 this->
buffers == other.buffers &&
2059 this->
cameras == other.cameras &&
2064 this->
extras == other.extras && this->
images == other.images &&
2066 this->
meshes == other.meshes && this->
nodes == other.nodes &&
2067 this->
samplers == other.samplers && this->
scenes == other.scenes &&
2068 this->
skins == other.skins && this->
textures == other.textures;
2071 return this->
camera == other.camera && this->
children == other.children &&
2073 Equals(this->
matrix, other.matrix) && this->
mesh == other.mesh &&
2074 (this->
light == other.light) && (this->
emitter == other.emitter) &&
2075 this->
name == other.name && Equals(this->
rotation, other.rotation) &&
2076 Equals(this->
scale, other.scale) && this->
skin == other.skin &&
2078 Equals(this->
weights, other.weights);
2081 return this->
extensions == other.extensions && this->
extras == other.extras &&
2086 return this->
extensions == other.extensions && this->
extras == other.extras &&
2095 return this->
extensions == other.extensions && this->
extras == other.extras &&
2103 this->has_number_value != other.has_number_value)
2112 auto otherIt = other.json_double_value.find(it.first);
2113 if (otherIt == other.json_double_value.end())
return false;
2118 if (!Equals(this->
number_array, other.number_array))
return false;
2120 if (this->
string_value != other.string_value)
return false;
2132 return this->
attributes == other.attributes && this->
extras == other.extras &&
2134 this->
mode == other.mode && this->
targets == other.targets;
2137 return this->
extensions == other.extensions && this->
extras == other.extras &&
2139 this->
minFilter == other.minFilter && this->
name == other.name &&
2140 this->
wrapS == other.wrapS && this->
wrapT == other.wrapT;
2145 return this->
extensions == other.extensions && this->
extras == other.extras &&
2146 this->
name == other.name && this->
nodes == other.nodes;
2149 return this->
extensions == other.extensions && this->
extras == other.extras &&
2151 this->
joints == other.joints && this->
name == other.name &&
2155 return this->
extensions == other.extensions && this->
extras == other.extras &&
2156 this->
name == other.name && this->
sampler == other.sampler &&
2157 this->
source == other.source;
2160 return this->
extensions == other.extensions && this->
extras == other.extras &&
2161 this->
index == other.index && this->
texCoord == other.texCoord;
2164 return this->
extensions == other.extensions && this->
extras == other.extras &&
2165 this->
index == other.index && this->
texCoord == other.texCoord &&
2169 return this->
extensions == other.extensions && this->
extras == other.extras &&
2170 this->
index == other.index && this->
texCoord == other.texCoord &&
2174 return this->
extensions == other.extensions && this->
extras == other.extras &&
2182 return Equals(*
this, other);
2185 static void swap4(
unsigned int *val) {
2186 #ifdef TINYGLTF_LITTLE_ENDIAN
2189 unsigned int tmp = *val;
2190 unsigned char *
dst =
reinterpret_cast<unsigned char *
>(val);
2191 unsigned char *
src =
reinterpret_cast<unsigned char *
>(&tmp);
2200 static std::string JoinPath(
const std::string &path0,
2201 const std::string &path1) {
2202 if (path0.empty()) {
2206 char lastChar = *path0.rbegin();
2207 if (lastChar !=
'/') {
2208 return path0 + std::string(
"/") + path1;
2210 return path0 + path1;
2215 static std::string FindFile(
const std::vector<std::string> &paths,
2216 const std::string &filepath, FsCallbacks *fs) {
2217 if (fs ==
nullptr || fs->ExpandFilePath ==
nullptr ||
2218 fs->FileExists ==
nullptr) {
2220 return std::string();
2227 size_t slength = strlen(filepath.c_str());
2229 return std::string();
2232 std::string cleaned_filepath = std::string(filepath.c_str());
2234 for (
size_t i = 0;
i < paths.size();
i++) {
2235 std::string absPath =
2236 fs->ExpandFilePath(JoinPath(paths[
i], cleaned_filepath), fs->user_data);
2237 if (fs->FileExists(absPath, fs->user_data)) {
2242 return std::string();
2245 static std::string GetFilePathExtension(
const std::string &FileName) {
2246 if (FileName.find_last_of(
".") != std::string::npos)
2247 return FileName.substr(FileName.find_last_of(
".") + 1);
2251 static std::string GetBaseDir(
const std::string &filepath) {
2252 if (filepath.find_last_of(
"/\\") != std::string::npos)
2253 return filepath.substr(0, filepath.find_last_of(
"/\\"));
2257 static std::string GetBaseFilename(
const std::string &filepath) {
2258 auto idx = filepath.find_last_of(
"/\\");
2259 if (idx != std::string::npos)
return filepath.substr(idx + 1);
2263 std::string base64_encode(
unsigned char const *,
unsigned int len);
2264 std::string base64_decode(std::string
const &
s);
2294 #pragma clang diagnostic push
2295 #pragma clang diagnostic ignored "-Wsign-conversion"
2296 #pragma clang diagnostic ignored "-Wconversion"
2299 static inline bool is_base64(
unsigned char c) {
2300 return (isalnum(
c) || (
c ==
'+') || (
c ==
'/'));
2303 std::string base64_encode(
unsigned char const *bytes_to_encode,
2304 unsigned int in_len) {
2308 unsigned char char_array_3[3];
2309 unsigned char char_array_4[4];
2311 const char *base64_chars =
2312 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2313 "abcdefghijklmnopqrstuvwxyz"
2317 char_array_3[
i++] = *(bytes_to_encode++);
2319 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
2321 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
2323 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
2324 char_array_4[3] = char_array_3[2] & 0x3f;
2326 for (
i = 0; (
i < 4);
i++) ret += base64_chars[char_array_4[
i]];
2332 for (
j =
i;
j < 3;
j++) char_array_3[
j] =
'\0';
2334 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
2336 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
2338 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
2340 for (
j = 0; (
j <
i + 1);
j++) ret += base64_chars[char_array_4[
j]];
2342 while ((
i++ < 3)) ret +=
'=';
2348 std::string base64_decode(std::string
const &encodedString) {
2349 int in_len =
static_cast<int>(encodedString.size());
2353 unsigned char char_array_4[4], char_array_3[3];
2356 const std::string base64_chars =
2357 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2358 "abcdefghijklmnopqrstuvwxyz"
2361 while (in_len-- && (encodedString[in_] !=
'=') &&
2362 is_base64(encodedString[in_])) {
2363 char_array_4[
i++] = encodedString[in_];
2366 for (
i = 0;
i < 4;
i++)
2368 static_cast<unsigned char>(base64_chars.find(char_array_4[
i]));
2371 (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
2373 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
2374 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
2376 for (
i = 0; (
i < 3);
i++) ret += char_array_3[
i];
2382 for (
j =
i;
j < 4;
j++) char_array_4[
j] = 0;
2384 for (
j = 0;
j < 4;
j++)
2386 static_cast<unsigned char>(base64_chars.find(char_array_4[
j]));
2388 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
2390 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
2391 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
2393 for (
j = 0; (
j <
i - 1);
j++) {
2394 ret += char_array_3[
j];
2401 #pragma clang diagnostic pop
2437 inline unsigned char from_hex(
unsigned char ch) {
2438 if (ch <= '9' && ch >=
'0')
2440 else if (ch <= 'f' && ch >=
'a')
2442 else if (ch <= 'F' && ch >=
'A')
2449 static const std::string urldecode(
const std::string &str) {
2452 string::size_type
i;
2453 for (
i = 0;
i < str.size(); ++
i) {
2454 if (str[
i] ==
'+') {
2456 }
else if (str[
i] ==
'%' && str.size() >
i + 2) {
2457 const unsigned char ch1 =
2458 from_hex(
static_cast<unsigned char>(str[
i + 1]));
2459 const unsigned char ch2 =
2460 from_hex(
static_cast<unsigned char>(str[
i + 2]));
2461 const unsigned char ch =
static_cast<unsigned char>((ch1 << 4) | ch2);
2462 result +=
static_cast<char>(ch);
2474 bool URIDecode(
const std::string &in_uri, std::string *out_uri,
2477 *out_uri = dlib::urldecode(in_uri);
2481 static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
2482 std::string *warn,
const std::string &filename,
2483 const std::string &basedir,
bool required,
2484 size_t reqBytes,
bool checkSize,
2485 size_t maxFileSize, FsCallbacks *fs) {
2486 if (fs ==
nullptr || fs->FileExists ==
nullptr ||
2487 fs->ExpandFilePath ==
nullptr || fs->ReadWholeFile ==
nullptr) {
2490 (*err) +=
"FS callback[s] not set\n";
2495 std::string *failMsgOut = required ? err : warn;
2499 std::vector<std::string> paths;
2500 paths.push_back(basedir);
2501 paths.push_back(
".");
2503 std::string filepath = FindFile(paths, filename, fs);
2504 if (filepath.empty() || filename.empty()) {
2506 (*failMsgOut) +=
"File not found : " + filename +
"\n";
2512 if (fs->GetFileSizeInBytes) {
2513 size_t file_size{0};
2516 fs->GetFileSizeInBytes(&file_size, &_err, filepath, fs->user_data);
2520 (*failMsgOut) +=
"Getting file size failed : " + filename +
2521 ", err = " + _err +
"\n";
2527 if (file_size > maxFileSize) {
2530 " exceeds maximum allowed file size " +
2537 std::vector<unsigned char> buf;
2538 std::string fileReadErr;
2540 fs->ReadWholeFile(&buf, &fileReadErr, filepath, fs->user_data);
2544 "File read error : " + filepath +
" : " + fileReadErr +
"\n";
2549 size_t sz = buf.size();
2552 (*failMsgOut) +=
"File is empty : " + filepath +
"\n";
2558 if (reqBytes == sz) {
2562 std::stringstream ss;
2563 ss <<
"File size mismatch : " << filepath <<
", requestedBytes "
2564 << reqBytes <<
", but got " << sz << std::endl;
2566 (*failMsgOut) += ss.str();
2577 strictness_ = strictness;
2582 load_image_user_data_ = user_data;
2583 user_image_loader_ =
true;
2588 #ifndef TINYGLTF_NO_STB_IMAGE
2594 load_image_user_data_ =
nullptr;
2595 user_image_loader_ =
false;
2598 #ifndef TINYGLTF_NO_STB_IMAGE
2599 bool LoadImageData(Image *image,
const int image_idx, std::string *err,
2600 std::string *warn,
int req_width,
int req_height,
2601 const unsigned char *bytes,
int size,
void *user_data) {
2604 LoadImageDataOption option;
2606 option = *
reinterpret_cast<LoadImageDataOption *
>(user_data);
2609 int w = 0, h = 0, comp = 0, req_comp = 0;
2611 unsigned char *data =
nullptr;
2616 req_comp = option.preserve_channels ? 0 : 4;
2627 data =
reinterpret_cast<unsigned char *
>(
2649 "Unknown image format. STB cannot decode image data for image[" +
2650 std::to_string(image_idx) +
"] name = \"" + image->name +
"\".\n";
2655 if ((w < 1) || (h < 1)) {
2658 (*err) +=
"Invalid image data for image[" +
std::to_string(image_idx) +
2659 "] name = \"" + image->name +
"\"\n";
2664 if (req_width > 0) {
2665 if (req_width != w) {
2668 (*err) +=
"Image width mismatch for image[" +
2676 if (req_height > 0) {
2677 if (req_height != h) {
2680 (*err) +=
"Image height mismatch. for image[" +
2688 if (req_comp != 0) {
2695 image->component = comp;
2697 image->pixel_type = pixel_type;
2698 image->image.resize(
static_cast<size_t>(w * h * comp) *
size_t(bits / 8));
2699 std::copy(data, data + w * h * comp * (bits / 8), image->image.begin());
2708 write_image_user_data_ = user_data;
2711 #ifndef TINYGLTF_NO_STB_IMAGE_WRITE
2712 static void WriteToMemory_stbi(
void *context,
void *data,
int size) {
2713 std::vector<unsigned char> *buffer =
2714 reinterpret_cast<std::vector<unsigned char> *
>(context);
2716 unsigned char *pData =
reinterpret_cast<unsigned char *
>(data);
2718 buffer->insert(buffer->end(), pData, pData + size);
2721 bool WriteImageData(
const std::string *basepath,
const std::string *filename,
2722 const Image *image,
bool embedImages,
2723 const URICallbacks *uri_cb, std::string *out_uri,
2725 const std::string ext = GetFilePathExtension(*filename);
2729 std::vector<unsigned char> data;
2732 if ((image->bits != 8) ||
2739 image->height, image->component,
2740 &image->image[0], 0)) {
2743 header =
"data:image/png;base64,";
2744 }
else if (ext ==
"jpg") {
2746 image->height, image->component,
2747 &image->image[0], 100)) {
2750 header =
"data:image/jpeg;base64,";
2751 }
else if (ext ==
"bmp") {
2753 image->height, image->component,
2754 &image->image[0])) {
2757 header =
"data:image/bmp;base64,";
2758 }
else if (!embedImages) {
2766 *out_uri = header + base64_encode(&data[0],
2767 static_cast<unsigned int>(data.size()));
2773 FsCallbacks *fs =
reinterpret_cast<FsCallbacks *
>(fsPtr);
2774 if ((fs !=
nullptr) && (fs->WriteWholeFile !=
nullptr)) {
2775 const std::string imagefilepath = JoinPath(*basepath, *filename);
2776 std::string writeError;
2777 if (!fs->WriteWholeFile(&writeError, imagefilepath, data,
2785 if (uri_cb->encode) {
2786 if (!uri_cb->encode(*filename,
"image", out_uri, uri_cb->user_data)) {
2790 *out_uri = *filename;
2799 assert(callbacks.decode);
2800 if (callbacks.decode) {
2808 static inline std::wstring UTF8ToWchar(
const std::string &str) {
2810 MultiByteToWideChar(CP_UTF8, 0, str.data(), (
int)str.size(),
nullptr, 0);
2811 std::wstring wstr((
size_t)wstr_size, 0);
2812 MultiByteToWideChar(CP_UTF8, 0, str.data(), (
int)str.size(), &wstr[0],
2817 static inline std::string WcharToUTF8(
const std::wstring &wstr) {
2818 int str_size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (
int)wstr.size(),
2819 nullptr, 0,
nullptr,
nullptr);
2820 std::string str((
size_t)str_size, 0);
2821 WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (
int)wstr.size(), &str[0],
2822 (
int)str.size(),
nullptr,
nullptr);
2827 #ifndef TINYGLTF_NO_FS
2830 bool FileExists(
const std::string &abs_filename,
void *) {
2832 #ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
2833 if (asset_manager) {
2834 AAsset *asset = AAssetManager_open(asset_manager, abs_filename.c_str(),
2835 AASSET_MODE_STREAMING);
2839 AAsset_close(asset);
2846 #if defined(_MSC_VER) || defined(_LIBCPP_VERSION)
2849 DWORD result = GetFileAttributesW(UTF8ToWchar(abs_filename).c_str());
2850 if (result == INVALID_FILE_ATTRIBUTES) {
2853 if (result & FILE_ATTRIBUTE_DIRECTORY) {
2858 errno_t err = _wfopen_s(&fp, UTF8ToWchar(abs_filename).c_str(), L
"rb");
2862 #elif defined(__GLIBCXX__)
2863 FILE *fp = fopen(abs_filename.c_str(),
"rb");
2870 errno_t err = fopen_s(&fp, abs_filename.c_str(),
"rb");
2878 if (stat(abs_filename.c_str(), &sb)) {
2881 if (S_ISDIR(sb.st_mode)) {
2885 FILE *fp = fopen(abs_filename.c_str(),
"rb");
2898 std::string
ExpandFilePath(
const std::string &filepath,
void *) {
2913 std::wstring wfilepath = UTF8ToWchar(filepath);
2914 DWORD wlen = ExpandEnvironmentStringsW(wfilepath.c_str(),
nullptr, 0);
2915 wchar_t *wstr =
new wchar_t[wlen];
2916 ExpandEnvironmentStringsW(wfilepath.c_str(), wstr, wlen);
2918 std::wstring ws(wstr);
2920 return WcharToUTF8(ws);
2924 #if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \
2925 defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__OpenBSD__)
2927 std::string
s = filepath;
2932 if (filepath.empty()) {
2937 std::string quoted_path =
"\"" + filepath +
"\"";
2939 int ret = wordexp(quoted_path.c_str(), &p, 0);
2948 s = std::string(p.we_wordv[0]);
2962 const std::string &filepath,
void *userdata) {
2965 #ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
2966 if (asset_manager) {
2967 AAsset *asset = AAssetManager_open(asset_manager, filepath.c_str(),
2968 AASSET_MODE_STREAMING);
2971 (*err) +=
"File open error : " + filepath +
"\n";
2975 size_t size = AAsset_getLength(asset);
2979 (*err) +=
"Invalid file size : " + filepath +
2980 " (does the path point to a directory?)";
2988 (*err) +=
"No asset manager specified : " + filepath +
"\n";
2994 #if defined(__GLIBCXX__) // mingw
2995 int file_descriptor =
2996 _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY);
2997 __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
2998 std::istream
f(&wfile_buf);
2999 #elif defined(_MSC_VER) || defined(_LIBCPP_VERSION)
3002 std::ifstream
f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary);
3005 std::ifstream
f(filepath.c_str(), std::ifstream::binary);
3008 std::ifstream
f(filepath.c_str(), std::ifstream::binary);
3012 (*err) +=
"File open error : " + filepath +
"\n";
3022 "File read error. Maybe empty file or invalid file : " + filepath +
3029 const auto sz =
f.tellg();
3036 (*err) +=
"Invalid file size : " + filepath +
3037 " (does the path point to a directory?)";
3040 }
else if (sz == std::streamoff(0)) {
3042 (*err) +=
"File is empty : " + filepath +
"\n";
3045 }
else if (sz >= (std::numeric_limits<std::streamoff>::max)()) {
3047 (*err) +=
"Invalid file size : " + filepath +
"\n";
3052 (*filesize_out) = sz;
3057 bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
3058 const std::string &filepath,
void *) {
3060 #ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
3061 if (asset_manager) {
3062 AAsset *asset = AAssetManager_open(asset_manager, filepath.c_str(),
3063 AASSET_MODE_STREAMING);
3066 (*err) +=
"File open error : " + filepath +
"\n";
3070 size_t size = AAsset_getLength(asset);
3073 (*err) +=
"Invalid file size : " + filepath +
3074 " (does the path point to a directory?)";
3079 AAsset_read(asset,
reinterpret_cast<char *
>(&out->at(0)), size);
3080 AAsset_close(asset);
3084 (*err) +=
"No asset manager specified : " + filepath +
"\n";
3090 #if defined(__GLIBCXX__) // mingw
3091 int file_descriptor =
3092 _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY);
3093 __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
3094 std::istream
f(&wfile_buf);
3095 #elif defined(_MSC_VER) || defined(_LIBCPP_VERSION)
3098 std::ifstream
f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary);
3101 std::ifstream
f(filepath.c_str(), std::ifstream::binary);
3104 std::ifstream
f(filepath.c_str(), std::ifstream::binary);
3108 (*err) +=
"File open error : " + filepath +
"\n";
3118 "File read error. Maybe empty file or invalid file : " + filepath +
3125 const auto sz =
f.tellg();
3132 (*err) +=
"Invalid file size : " + filepath +
3133 " (does the path point to a directory?)";
3136 }
else if (sz == std::streamoff(0)) {
3138 (*err) +=
"File is empty : " + filepath +
"\n";
3141 }
else if (sz >= (std::numeric_limits<std::streamoff>::max)()) {
3143 (*err) +=
"Invalid file size : " + filepath +
"\n";
3149 f.read(
reinterpret_cast<char *
>(&out->at(0)),
3150 static_cast<std::streamsize
>(sz));
3156 bool WriteWholeFile(std::string *err,
const std::string &filepath,
3157 const std::vector<unsigned char> &contents,
void *) {
3159 #if defined(__GLIBCXX__) // mingw
3160 int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(),
3161 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY, _S_IWRITE);
3162 __gnu_cxx::stdio_filebuf<char> wfile_buf(
3163 file_descriptor, std::ios_base::out | std::ios_base::binary);
3164 std::ostream
f(&wfile_buf);
3165 #elif defined(_MSC_VER)
3166 std::ofstream
f(UTF8ToWchar(filepath).c_str(), std::ofstream::binary);
3168 std::ofstream
f(filepath.c_str(), std::ofstream::binary);
3171 std::ofstream
f(filepath.c_str(), std::ofstream::binary);
3175 (*err) +=
"File open error for writing : " + filepath +
"\n";
3180 f.write(
reinterpret_cast<const char *
>(&contents.at(0)),
3181 static_cast<std::streamsize
>(contents.size()));
3184 (*err) +=
"File write error: " + filepath +
"\n";
3192 #endif // TINYGLTF_NO_FS
3194 static std::string MimeToExt(
const std::string &mimeType) {
3195 if (mimeType ==
"image/jpeg") {
3197 }
else if (mimeType ==
"image/png") {
3199 }
else if (mimeType ==
"image/bmp") {
3201 }
else if (mimeType ==
"image/gif") {
3208 static bool UpdateImageObject(
const Image &image, std::string &baseDir,
3209 int index,
bool embedImages,
3210 const URICallbacks *uri_cb,
3212 void *user_data, std::string *out_uri) {
3213 std::string filename;
3216 if (image.uri.size()) {
3217 std::string decoded_uri;
3218 if (!uri_cb->decode(image.uri, &decoded_uri, uri_cb->user_data)) {
3222 filename = GetBaseFilename(decoded_uri);
3223 ext = GetFilePathExtension(filename);
3224 }
else if (image.bufferView != -1) {
3227 }
else if (image.name.size()) {
3228 ext = MimeToExt(image.mimeType);
3230 filename = image.name +
"." + ext;
3232 ext = MimeToExt(image.mimeType);
3240 bool imageWritten =
false;
3241 if (*
WriteImageData !=
nullptr && !filename.empty() && !image.image.empty()) {
3242 imageWritten = (*WriteImageData)(&baseDir, &filename, &image, embedImages,
3243 uri_cb, out_uri, user_data);
3244 if (!imageWritten) {
3250 if (!imageWritten) {
3251 *out_uri = image.uri;
3258 std::string header =
"data:application/octet-stream;base64,";
3259 if (in.find(header) == 0) {
3263 header =
"data:image/jpeg;base64,";
3264 if (in.find(header) == 0) {
3268 header =
"data:image/png;base64,";
3269 if (in.find(header) == 0) {
3273 header =
"data:image/bmp;base64,";
3274 if (in.find(header) == 0) {
3278 header =
"data:image/gif;base64,";
3279 if (in.find(header) == 0) {
3283 header =
"data:text/plain;base64,";
3284 if (in.find(header) == 0) {
3288 header =
"data:application/gltf-buffer;base64,";
3289 if (in.find(header) == 0) {
3296 bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
3297 const std::string &in,
size_t reqBytes,
bool checkSize) {
3298 std::string header =
"data:application/octet-stream;base64,";
3300 if (in.find(header) == 0) {
3301 data = base64_decode(in.substr(header.size()));
3305 header =
"data:image/jpeg;base64,";
3306 if (in.find(header) == 0) {
3307 mime_type =
"image/jpeg";
3308 data = base64_decode(in.substr(header.size()));
3313 header =
"data:image/png;base64,";
3314 if (in.find(header) == 0) {
3315 mime_type =
"image/png";
3316 data = base64_decode(in.substr(header.size()));
3321 header =
"data:image/bmp;base64,";
3322 if (in.find(header) == 0) {
3323 mime_type =
"image/bmp";
3324 data = base64_decode(in.substr(header.size()));
3329 header =
"data:image/gif;base64,";
3330 if (in.find(header) == 0) {
3331 mime_type =
"image/gif";
3332 data = base64_decode(in.substr(header.size()));
3337 header =
"data:text/plain;base64,";
3338 if (in.find(header) == 0) {
3339 mime_type =
"text/plain";
3340 data = base64_decode(in.substr(header.size()));
3345 header =
"data:application/gltf-buffer;base64,";
3346 if (in.find(header) == 0) {
3347 data = base64_decode(in.substr(header.size()));
3357 if (data.size() != reqBytes) {
3360 out->resize(reqBytes);
3362 out->resize(data.size());
3364 std::copy(data.begin(), data.end(), out->begin());
3370 #ifdef TINYGLTF_USE_RAPIDJSON
3371 if (!o.IsDouble()) {
3375 }
else if (o.IsUint()) {
3376 val =
static_cast<int>(o.GetUint());
3378 }
else if (o.IsInt64()) {
3379 val =
static_cast<int>(o.GetInt64());
3381 }
else if (o.IsUint64()) {
3382 val =
static_cast<int>(o.GetUint64());
3389 auto type = o.type();
3391 if ((
type == detail::json::value_t::number_integer) ||
3392 (
type == detail::json::value_t::number_unsigned)) {
3393 val =
static_cast<int>(o.get<int64_t>());
3401 #ifdef TINYGLTF_USE_RAPIDJSON
3404 val = o.GetDouble();
3413 #ifdef TINYGLTF_USE_RAPIDJSON
3415 val = o.GetDouble();
3421 if (o.is_number()) {
3422 val = o.get<
double>();
3430 bool GetString(
const detail::json &o, std::string &val) {
3431 #ifdef TINYGLTF_USE_RAPIDJSON
3433 val = o.GetString();
3439 if (o.type() == detail::json::value_t::string) {
3440 val = o.get<std::string>();
3449 #ifdef TINYGLTF_USE_RAPIDJSON
3452 return o.is_array();
3456 detail::json_const_array_iterator ArrayBegin(
const detail::json &o) {
3457 #ifdef TINYGLTF_USE_RAPIDJSON
3464 detail::json_const_array_iterator ArrayEnd(
const detail::json &o) {
3465 #ifdef TINYGLTF_USE_RAPIDJSON
3473 #ifdef TINYGLTF_USE_RAPIDJSON
3474 return o.IsObject();
3476 return o.is_object();
3480 detail::json_const_iterator ObjectBegin(
const detail::json &o) {
3481 #ifdef TINYGLTF_USE_RAPIDJSON
3482 return o.MemberBegin();
3488 detail::json_const_iterator ObjectEnd(
const detail::json &o) {
3489 #ifdef TINYGLTF_USE_RAPIDJSON
3490 return o.MemberEnd();
3498 std::string GetKey(detail::json_const_iterator &it) {
3499 #ifdef TINYGLTF_USE_RAPIDJSON
3500 return it->name.GetString();
3502 return it.key().c_str();
3506 bool FindMember(
const detail::json &o,
const char *member,
3507 detail::json_const_iterator &it) {
3508 #ifdef TINYGLTF_USE_RAPIDJSON
3509 if (!o.IsObject()) {
3512 it = o.FindMember(member);
3513 return it != o.MemberEnd();
3515 it = o.find(member);
3516 return it != o.end();
3521 detail::json_iterator &it) {
3522 #ifdef TINYGLTF_USE_RAPIDJSON
3523 if (!o.IsObject()) {
3526 it = o.FindMember(member);
3527 return it != o.MemberEnd();
3529 it = o.find(member);
3530 return it != o.end();
3534 void Erase(
detail::json &o, detail::json_iterator &it) {
3535 #ifdef TINYGLTF_USE_RAPIDJSON
3543 #ifdef TINYGLTF_USE_RAPIDJSON
3544 return o.ObjectEmpty();
3550 const detail::json &GetValue(detail::json_const_iterator &it) {
3551 #ifdef TINYGLTF_USE_RAPIDJSON
3559 #ifdef TINYGLTF_USE_RAPIDJSON
3566 std::string JsonToString(
const detail::json &o,
int spacing = -1) {
3567 #ifdef TINYGLTF_USE_RAPIDJSON
3568 using namespace rapidjson;
3569 StringBuffer buffer;
3570 if (spacing == -1) {
3571 Writer<StringBuffer> writer(buffer);
3574 if (!o.Accept(writer)) {
3575 return "tiny_gltf::JsonToString() failed rapidjson conversion";
3578 PrettyWriter<StringBuffer> writer(buffer);
3579 writer.SetIndent(
' ', uint32_t(spacing));
3580 if (!o.Accept(writer)) {
3581 return "tiny_gltf::JsonToString() failed rapidjson conversion";
3584 return buffer.GetString();
3586 return o.dump(spacing);
3592 static bool ParseJsonAsValue(Value *ret,
const detail::json &o) {
3594 #ifdef TINYGLTF_USE_RAPIDJSON
3596 switch (o.GetType()) {
3597 case Type::kObjectType: {
3599 for (
auto it = o.MemberBegin(); it != o.MemberEnd(); ++it) {
3601 ParseJsonAsValue(&entry, it->value);
3603 value_object.emplace(detail::GetKey(it), std::move(entry));
3605 if (value_object.size() > 0) val = Value(std::move(value_object));
3607 case Type::kArrayType: {
3609 value_array.reserve(o.Size());
3610 for (
auto it = o.Begin(); it != o.End(); ++it) {
3612 ParseJsonAsValue(&entry, *it);
3614 value_array.emplace_back(std::move(entry));
3616 if (value_array.size() > 0) val = Value(std::move(value_array));
3618 case Type::kStringType:
3619 val = Value(std::string(o.GetString()));
3621 case Type::kFalseType:
3622 case Type::kTrueType:
3623 val = Value(o.GetBool());
3625 case Type::kNumberType:
3626 if (!o.IsDouble()) {
3628 detail::GetInt(o,
i);
3632 detail::GetDouble(o, d);
3636 case Type::kNullType:
3644 for (
auto it = o.begin(); it != o.end(); it++) {
3646 ParseJsonAsValue(&entry, it.value());
3648 value_object.emplace(it.key(), std::move(entry));
3650 if (value_object.size() > 0) val = Value(std::move(value_object));
3654 value_array.reserve(o.size());
3655 for (
auto it = o.begin(); it != o.end(); it++) {
3657 ParseJsonAsValue(&entry, it.value());
3659 value_array.emplace_back(std::move(entry));
3661 if (value_array.size() > 0) val = Value(std::move(value_array));
3663 case detail::json::value_t::string:
3664 val = Value(o.get<std::string>());
3666 case detail::json::value_t::boolean:
3667 val = Value(o.get<
bool>());
3669 case detail::json::value_t::number_integer:
3670 case detail::json::value_t::number_unsigned:
3671 val = Value(
static_cast<int>(o.get<int64_t>()));
3673 case detail::json::value_t::number_float:
3674 val = Value(o.get<
double>());
3676 case detail::json::value_t::null:
3677 case detail::json::value_t::discarded:
3678 case detail::json::value_t::binary:
3683 const bool isNotNull = val.Type() !=
NULL_TYPE;
3685 if (ret) *ret = std::move(val);
3690 static bool ParseExtrasProperty(Value *ret,
const detail::json &o) {
3691 detail::json_const_iterator it;
3692 if (!detail::FindMember(o,
"extras", it)) {
3696 return ParseJsonAsValue(ret, detail::GetValue(it));
3699 static bool ParseBooleanProperty(
bool *ret, std::string *err,
3701 const std::string &property,
3702 const bool required,
3703 const std::string &parent_node =
"") {
3704 detail::json_const_iterator it;
3705 if (!detail::FindMember(o, property.c_str(), it)) {
3708 (*err) +=
"'" +
property +
"' property is missing";
3709 if (!parent_node.empty()) {
3710 (*err) +=
" in " + parent_node;
3718 auto &
value = detail::GetValue(it);
3721 bool boolValue =
false;
3722 #ifdef TINYGLTF_USE_RAPIDJSON
3723 isBoolean =
value.IsBool();
3725 boolValue =
value.GetBool();
3728 isBoolean =
value.is_boolean();
3730 boolValue =
value.get<
bool>();
3736 (*err) +=
"'" +
property +
"' property is not a bool type.\n";
3749 static bool ParseIntegerProperty(
int *ret, std::string *err,
3751 const std::string &property,
3752 const bool required,
3753 const std::string &parent_node =
"") {
3754 detail::json_const_iterator it;
3755 if (!detail::FindMember(o, property.c_str(), it)) {
3758 (*err) +=
"'" +
property +
"' property is missing";
3759 if (!parent_node.empty()) {
3760 (*err) +=
" in " + parent_node;
3769 bool isInt = detail::GetInt(detail::GetValue(it), intValue);
3773 (*err) +=
"'" +
property +
"' property is not an integer type.\n";
3786 static bool ParseUnsignedProperty(
size_t *ret, std::string *err,
3788 const std::string &property,
3789 const bool required,
3790 const std::string &parent_node =
"") {
3791 detail::json_const_iterator it;
3792 if (!detail::FindMember(o, property.c_str(), it)) {
3795 (*err) +=
"'" +
property +
"' property is missing";
3796 if (!parent_node.empty()) {
3797 (*err) +=
" in " + parent_node;
3805 auto &
value = detail::GetValue(it);
3809 #ifdef TINYGLTF_USE_RAPIDJSON
3811 if (
value.IsUint()) {
3812 uValue =
value.GetUint();
3814 }
else if (
value.IsUint64()) {
3815 uValue =
value.GetUint64();
3819 isUValue =
value.is_number_unsigned();
3821 uValue =
value.get<
size_t>();
3827 (*err) +=
"'" +
property +
"' property is not a positive integer.\n";
3840 static bool ParseNumberProperty(
double *ret, std::string *err,
3842 const std::string &property,
3843 const bool required,
3844 const std::string &parent_node =
"") {
3845 detail::json_const_iterator it;
3847 if (!detail::FindMember(o, property.c_str(), it)) {
3850 (*err) +=
"'" +
property +
"' property is missing";
3851 if (!parent_node.empty()) {
3852 (*err) +=
" in " + parent_node;
3861 bool isNumber = detail::GetNumber(detail::GetValue(it), numberValue);
3866 (*err) +=
"'" +
property +
"' property is not a number type.\n";
3873 (*ret) = numberValue;
3879 static bool ParseNumberArrayProperty(std::vector<double> *ret, std::string *err,
3881 const std::string &property,
bool required,
3882 const std::string &parent_node =
"") {
3883 detail::json_const_iterator it;
3884 if (!detail::FindMember(o, property.c_str(), it)) {
3887 (*err) +=
"'" +
property +
"' property is missing";
3888 if (!parent_node.empty()) {
3889 (*err) +=
" in " + parent_node;
3897 if (!detail::IsArray(detail::GetValue(it))) {
3900 (*err) +=
"'" +
property +
"' property is not an array";
3901 if (!parent_node.empty()) {
3902 (*err) +=
" in " + parent_node;
3911 auto end = detail::ArrayEnd(detail::GetValue(it));
3912 for (
auto i = detail::ArrayBegin(detail::GetValue(it));
i != end; ++
i) {
3914 const bool isNumber = detail::GetNumber(*
i, numberValue);
3918 (*err) +=
"'" +
property +
"' property is not a number.\n";
3919 if (!parent_node.empty()) {
3920 (*err) +=
" in " + parent_node;
3927 ret->push_back(numberValue);
3933 static bool ParseIntegerArrayProperty(std::vector<int> *ret, std::string *err,
3935 const std::string &property,
3937 const std::string &parent_node =
"") {
3938 detail::json_const_iterator it;
3939 if (!detail::FindMember(o, property.c_str(), it)) {
3942 (*err) +=
"'" +
property +
"' property is missing";
3943 if (!parent_node.empty()) {
3944 (*err) +=
" in " + parent_node;
3952 if (!detail::IsArray(detail::GetValue(it))) {
3955 (*err) +=
"'" +
property +
"' property is not an array";
3956 if (!parent_node.empty()) {
3957 (*err) +=
" in " + parent_node;
3966 auto end = detail::ArrayEnd(detail::GetValue(it));
3967 for (
auto i = detail::ArrayBegin(detail::GetValue(it));
i != end; ++
i) {
3969 bool isNumber = detail::GetInt(*
i, numberValue);
3973 (*err) +=
"'" +
property +
"' property is not an integer type.\n";
3974 if (!parent_node.empty()) {
3975 (*err) +=
" in " + parent_node;
3982 ret->push_back(numberValue);
3988 static bool ParseStringProperty(
3989 std::string *ret, std::string *err,
const detail::json &o,
3990 const std::string &property,
bool required,
3991 const std::string &parent_node = std::string()) {
3992 detail::json_const_iterator it;
3993 if (!detail::FindMember(o, property.c_str(), it)) {
3996 (*err) +=
"'" +
property +
"' property is missing";
3997 if (parent_node.empty()) {
4000 (*err) +=
" in `" + parent_node +
"'.\n";
4007 std::string strValue;
4008 if (!detail::GetString(detail::GetValue(it), strValue)) {
4011 (*err) +=
"'" +
property +
"' property is not a string type.\n";
4018 (*ret) = std::move(strValue);
4024 static bool ParseStringIntegerProperty(std::map<std::string, int> *ret,
4026 const std::string &property,
4028 const std::string &parent =
"") {
4029 detail::json_const_iterator it;
4030 if (!detail::FindMember(o, property.c_str(), it)) {
4033 if (!parent.empty()) {
4035 "'" +
property +
"' property is missing in " + parent +
".\n";
4037 (*err) +=
"'" +
property +
"' property is missing.\n";
4047 if (!detail::IsObject(dict)) {
4050 (*err) +=
"'" +
property +
"' property is not an object.\n";
4058 detail::json_const_iterator dictIt(detail::ObjectBegin(dict));
4059 detail::json_const_iterator dictItEnd(detail::ObjectEnd(dict));
4061 for (; dictIt != dictItEnd; ++dictIt) {
4063 if (!detail::GetInt(detail::GetValue(dictIt), intVal)) {
4066 (*err) +=
"'" +
property +
"' value is not an integer type.\n";
4073 (*ret)[detail::GetKey(dictIt)] = intVal;
4078 static bool ParseJSONProperty(std::map<std::string, double> *ret,
4080 const std::string &property,
bool required) {
4081 detail::json_const_iterator it;
4082 if (!detail::FindMember(o, property.c_str(), it)) {
4085 (*err) +=
"'" +
property +
"' property is missing. \n'";
4093 if (!detail::IsObject(obj)) {
4096 (*err) +=
"'" +
property +
"' property is not a JSON object.\n";
4104 detail::json_const_iterator it2(detail::ObjectBegin(obj));
4105 detail::json_const_iterator itEnd(detail::ObjectEnd(obj));
4106 for (; it2 != itEnd; ++it2) {
4108 if (detail::GetNumber(detail::GetValue(it2), numVal))
4109 ret->emplace(std::string(detail::GetKey(it2)), numVal);
4115 static bool ParseParameterProperty(Parameter *param, std::string *err,
4117 const std::string &prop,
bool required) {
4123 if (ParseStringProperty(¶m->string_value, err, o, prop,
false)) {
4126 }
else if (ParseNumberArrayProperty(¶m->number_array, err, o, prop,
4130 }
else if (ParseNumberProperty(¶m->number_value, err, o, prop,
false)) {
4131 param->has_number_value =
true;
4133 }
else if (ParseJSONProperty(¶m->json_double_value, err, o, prop,
4136 }
else if (ParseBooleanProperty(¶m->bool_value, err, o, prop,
false)) {
4141 (*err) +=
"parameter must be a string or number / number array.\n";
4148 static bool ParseExtensionsProperty(
ExtensionMap *ret, std::string *err,
4152 detail::json_const_iterator it;
4153 if (!detail::FindMember(o,
"extensions", it)) {
4157 auto &obj = detail::GetValue(it);
4158 if (!detail::IsObject(obj)) {
4162 detail::json_const_iterator extIt =
4163 detail::ObjectBegin(obj);
4164 detail::json_const_iterator extEnd = detail::ObjectEnd(obj);
4165 for (; extIt != extEnd; ++extIt) {
4166 auto &itObj = detail::GetValue(extIt);
4167 if (!detail::IsObject(itObj))
continue;
4168 std::string
key(detail::GetKey(extIt));
4169 if (!ParseJsonAsValue(&extensions[key], itObj)) {
4178 (*ret) = std::move(extensions);
4183 template <
typename GltfType>
4184 static bool ParseExtrasAndExtensions(GltfType *target, std::string *err,
4186 bool store_json_strings) {
4187 ParseExtensionsProperty(&target->extensions, err, o);
4188 ParseExtrasProperty(&target->extras, o);
4190 if (store_json_strings) {
4192 detail::json_const_iterator it;
4193 if (detail::FindMember(o,
"extensions", it)) {
4194 target->extensions_json_string =
4195 detail::JsonToString(detail::GetValue(it));
4199 detail::json_const_iterator it;
4200 if (detail::FindMember(o,
"extras", it)) {
4201 target->extras_json_string = detail::JsonToString(detail::GetValue(it));
4208 static bool ParseAsset(Asset *asset, std::string *err,
const detail::json &o,
4209 bool store_original_json_for_extras_and_extensions) {
4210 ParseStringProperty(&asset->version, err, o,
"version",
true,
"Asset");
4211 ParseStringProperty(&asset->generator, err, o,
"generator",
false,
"Asset");
4212 ParseStringProperty(&asset->minVersion, err, o,
"minVersion",
false,
"Asset");
4213 ParseStringProperty(&asset->copyright, err, o,
"copyright",
false,
"Asset");
4215 ParseExtrasAndExtensions(asset, err, o,
4216 store_original_json_for_extras_and_extensions);
4220 static bool ParseImage(Image *image,
const int image_idx, std::string *err,
4222 bool store_original_json_for_extras_and_extensions,
4223 const std::string &basedir,
const size_t max_file_size,
4224 FsCallbacks *fs,
const URICallbacks *uri_cb,
4226 void *load_image_user_data =
nullptr) {
4231 detail::json_const_iterator it;
4232 bool hasBufferView = detail::FindMember(o,
"bufferView", it);
4233 bool hasURI = detail::FindMember(o,
"uri", it);
4235 ParseStringProperty(&image->name, err, o,
"name",
false);
4237 if (hasBufferView && hasURI) {
4241 "Only one of `bufferView` or `uri` should be defined, but both are "
4242 "defined for image[" +
4243 std::to_string(image_idx) +
"] name = \"" + image->name +
"\"\n";
4248 if (!hasBufferView && !hasURI) {
4250 (*err) +=
"Neither required `bufferView` nor `uri` defined for image[" +
4257 ParseExtrasAndExtensions(image, err, o,
4258 store_original_json_for_extras_and_extensions);
4260 if (hasBufferView) {
4261 int bufferView = -1;
4262 if (!ParseIntegerProperty(&bufferView, err, o,
"bufferView",
true)) {
4264 (*err) +=
"Failed to parse `bufferView` for image[" +
4271 std::string mime_type;
4272 ParseStringProperty(&mime_type, err, o,
"mimeType",
false);
4275 ParseIntegerProperty(&width, err, o,
"width",
false);
4278 ParseIntegerProperty(&height, err, o,
"height",
false);
4282 image->bufferView = bufferView;
4283 image->mimeType = mime_type;
4284 image->width = width;
4285 image->height = height;
4293 std::string tmp_err;
4294 if (!ParseStringProperty(&uri, &tmp_err, o,
"uri",
true)) {
4296 (*err) +=
"Failed to parse `uri` for image[" +
std::to_string(image_idx) +
4297 "] name = \"" + image->name +
"\".\n";
4302 std::vector<unsigned char> img;
4307 (*err) +=
"Failed to decode 'uri' for image[" +
4317 #ifdef TINYGLTF_NO_EXTERNAL_IMAGE
4320 std::string decoded_uri;
4321 if (!uri_cb->decode(uri, &decoded_uri, uri_cb->user_data)) {
4323 (*warn) +=
"Failed to decode 'uri' for image[" +
4332 if (!LoadExternalFile(&img, err, warn, decoded_uri, basedir,
4335 max_file_size, fs)) {
4337 (*warn) +=
"Failed to load external 'uri' for image[" +
4347 (*warn) +=
"Image data is empty for image[" +
4358 (*err) +=
"No LoadImageData callback specified.\n";
4362 return (*
LoadImageData)(image, image_idx, err, warn, 0, 0, &img.at(0),
4363 static_cast<int>(img.size()), load_image_user_data);
4366 static bool ParseTexture(Texture *texture, std::string *err,
4368 bool store_original_json_for_extras_and_extensions,
4369 const std::string &basedir) {
4373 ParseIntegerProperty(&sampler, err, o,
"sampler",
false);
4375 ParseIntegerProperty(&
source, err, o,
"source",
false);
4377 texture->sampler = sampler;
4378 texture->source =
source;
4380 ParseExtrasAndExtensions(texture, err, o,
4381 store_original_json_for_extras_and_extensions);
4383 ParseStringProperty(&texture->name, err, o,
"name",
false);
4388 static bool ParseTextureInfo(
4389 TextureInfo *texinfo, std::string *err,
const detail::json &o,
4390 bool store_original_json_for_extras_and_extensions) {
4391 if (texinfo ==
nullptr) {
4395 if (!ParseIntegerProperty(&texinfo->index, err, o,
"index",
4396 true,
"TextureInfo")) {
4400 ParseIntegerProperty(&texinfo->texCoord, err, o,
"texCoord",
false);
4402 ParseExtrasAndExtensions(texinfo, err, o,
4403 store_original_json_for_extras_and_extensions);
4408 static bool ParseNormalTextureInfo(
4409 NormalTextureInfo *texinfo, std::string *err,
const detail::json &o,
4410 bool store_original_json_for_extras_and_extensions) {
4411 if (texinfo ==
nullptr) {
4415 if (!ParseIntegerProperty(&texinfo->index, err, o,
"index",
4416 true,
"NormalTextureInfo")) {
4420 ParseIntegerProperty(&texinfo->texCoord, err, o,
"texCoord",
false);
4421 ParseNumberProperty(&texinfo->scale, err, o,
"scale",
false);
4423 ParseExtrasAndExtensions(texinfo, err, o,
4424 store_original_json_for_extras_and_extensions);
4429 static bool ParseOcclusionTextureInfo(
4430 OcclusionTextureInfo *texinfo, std::string *err,
const detail::json &o,
4431 bool store_original_json_for_extras_and_extensions) {
4432 if (texinfo ==
nullptr) {
4436 if (!ParseIntegerProperty(&texinfo->index, err, o,
"index",
4437 true,
"NormalTextureInfo")) {
4441 ParseIntegerProperty(&texinfo->texCoord, err, o,
"texCoord",
false);
4442 ParseNumberProperty(&texinfo->strength, err, o,
"strength",
false);
4444 ParseExtrasAndExtensions(texinfo, err, o,
4445 store_original_json_for_extras_and_extensions);
4450 static bool ParseBuffer(Buffer *buffer, std::string *err,
const detail::json &o,
4451 bool store_original_json_for_extras_and_extensions,
4452 FsCallbacks *fs,
const URICallbacks *uri_cb,
4453 const std::string &basedir,
4454 const size_t max_buffer_size,
bool is_binary =
false,
4455 const unsigned char *bin_data =
nullptr,
4456 size_t bin_size = 0) {
4458 if (!ParseUnsignedProperty(&byteLength, err, o,
"byteLength",
true,
4464 buffer->uri.clear();
4465 ParseStringProperty(&buffer->uri, err, o,
"uri",
false,
"Buffer");
4468 if (!is_binary && buffer->uri.empty()) {
4470 (*err) +=
"'uri' is missing from non binary glTF file buffer.\n";
4474 detail::json_const_iterator
type;
4475 if (detail::FindMember(o,
"type",
type)) {
4476 std::string typeStr;
4477 if (detail::GetString(detail::GetValue(
type), typeStr)) {
4478 if (typeStr.compare(
"arraybuffer") == 0) {
4486 if (!buffer->uri.empty()) {
4489 std::string mime_type;
4490 if (!
DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
4494 "Failed to decode 'uri' : " + buffer->uri +
" in Buffer\n";
4500 std::string decoded_uri;
4501 if (!uri_cb->decode(buffer->uri, &decoded_uri, uri_cb->user_data)) {
4504 if (!LoadExternalFile(&buffer->data, err,
nullptr,
4505 decoded_uri, basedir,
true,
4507 max_buffer_size, fs)) {
4514 if ((bin_size == 0) || (bin_data ==
nullptr)) {
4517 "Invalid binary data in `Buffer', or GLB with empty BIN chunk.\n";
4522 if (byteLength > bin_size) {
4524 std::stringstream ss;
4525 ss <<
"Invalid `byteLength'. Must be equal or less than binary size: "
4527 << byteLength <<
", binary size = " << bin_size << std::endl;
4534 buffer->data.resize(
static_cast<size_t>(byteLength));
4535 memcpy(&(buffer->data.at(0)), bin_data,
static_cast<size_t>(byteLength));
4540 std::string mime_type;
4541 if (!
DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
4544 (*err) +=
"Failed to decode 'uri' : " + buffer->uri +
" in Buffer\n";
4550 std::string decoded_uri;
4551 if (!uri_cb->decode(buffer->uri, &decoded_uri, uri_cb->user_data)) {
4554 if (!LoadExternalFile(&buffer->data, err,
nullptr, decoded_uri,
4555 basedir,
true, byteLength,
4557 max_buffer_size, fs)) {
4563 ParseStringProperty(&buffer->name, err, o,
"name",
false);
4565 ParseExtrasAndExtensions(buffer, err, o,
4566 store_original_json_for_extras_and_extensions);
4571 static bool ParseBufferView(
4572 BufferView *bufferView, std::string *err,
const detail::json &o,
4573 bool store_original_json_for_extras_and_extensions) {
4575 if (!ParseIntegerProperty(&buffer, err, o,
"buffer",
true,
"BufferView")) {
4579 size_t byteOffset = 0;
4580 ParseUnsignedProperty(&byteOffset, err, o,
"byteOffset",
false);
4582 size_t byteLength = 1;
4583 if (!ParseUnsignedProperty(&byteLength, err, o,
"byteLength",
true,
4588 size_t byteStride = 0;
4589 if (!ParseUnsignedProperty(&byteStride, err, o,
"byteStride",
false)) {
4598 if ((byteStride > 252) || ((byteStride % 4) != 0)) {
4600 std::stringstream ss;
4601 ss <<
"Invalid `byteStride' value. `byteStride' must be the multiple of "
4603 << byteStride << std::endl;
4611 ParseIntegerProperty(&target, err, o,
"target",
false);
4618 bufferView->target = target;
4620 ParseStringProperty(&bufferView->name, err, o,
"name",
false);
4622 ParseExtrasAndExtensions(bufferView, err, o,
4623 store_original_json_for_extras_and_extensions);
4625 bufferView->buffer = buffer;
4626 bufferView->byteOffset = byteOffset;
4627 bufferView->byteLength = byteLength;
4628 bufferView->byteStride = byteStride;
4632 static bool ParseSparseAccessor(
4633 Accessor::Sparse *sparse, std::string *err,
const detail::json &o,
4634 bool store_original_json_for_extras_and_extensions) {
4635 sparse->isSparse =
true;
4638 if (!ParseIntegerProperty(&count, err, o,
"count",
true,
"SparseAccessor")) {
4642 ParseExtrasAndExtensions(sparse, err, o,
4643 store_original_json_for_extras_and_extensions);
4645 detail::json_const_iterator indices_iterator;
4646 detail::json_const_iterator values_iterator;
4647 if (!detail::FindMember(o,
"indices", indices_iterator)) {
4648 (*err) =
"the sparse object of this accessor doesn't have indices";
4652 if (!detail::FindMember(o,
"values", values_iterator)) {
4653 (*err) =
"the sparse object of this accessor doesn't have values";
4657 const detail::json &indices_obj = detail::GetValue(indices_iterator);
4658 const detail::json &values_obj = detail::GetValue(values_iterator);
4660 int indices_buffer_view = 0, component_type = 0;
4661 size_t indices_byte_offset = 0;
4662 if (!ParseIntegerProperty(&indices_buffer_view, err, indices_obj,
4663 "bufferView",
true,
"SparseAccessor")) {
4666 ParseUnsignedProperty(&indices_byte_offset, err, indices_obj,
"byteOffset",
4668 if (!ParseIntegerProperty(&component_type, err, indices_obj,
"componentType",
4669 true,
"SparseAccessor")) {
4673 int values_buffer_view = 0;
4674 size_t values_byte_offset = 0;
4675 if (!ParseIntegerProperty(&values_buffer_view, err, values_obj,
"bufferView",
4676 true,
"SparseAccessor")) {
4679 ParseUnsignedProperty(&values_byte_offset, err, values_obj,
"byteOffset",
4682 sparse->count = count;
4683 sparse->indices.bufferView = indices_buffer_view;
4684 sparse->indices.byteOffset = indices_byte_offset;
4685 sparse->indices.componentType = component_type;
4686 ParseExtrasAndExtensions(&sparse->indices, err, indices_obj,
4687 store_original_json_for_extras_and_extensions);
4689 sparse->values.bufferView = values_buffer_view;
4690 sparse->values.byteOffset = values_byte_offset;
4691 ParseExtrasAndExtensions(&sparse->values, err, values_obj,
4692 store_original_json_for_extras_and_extensions);
4697 static bool ParseAccessor(Accessor *accessor, std::string *err,
4699 bool store_original_json_for_extras_and_extensions) {
4700 int bufferView = -1;
4701 ParseIntegerProperty(&bufferView, err, o,
"bufferView",
false,
"Accessor");
4703 size_t byteOffset = 0;
4704 ParseUnsignedProperty(&byteOffset, err, o,
"byteOffset",
false,
"Accessor");
4706 bool normalized =
false;
4707 ParseBooleanProperty(&normalized, err, o,
"normalized",
false,
"Accessor");
4709 size_t componentType = 0;
4710 if (!ParseUnsignedProperty(&componentType, err, o,
"componentType",
true,
4716 if (!ParseUnsignedProperty(&count, err, o,
"count",
true,
"Accessor")) {
4721 if (!ParseStringProperty(&
type, err, o,
"type",
true,
"Accessor")) {
4725 if (
type.compare(
"SCALAR") == 0) {
4727 }
else if (
type.compare(
"VEC2") == 0) {
4729 }
else if (
type.compare(
"VEC3") == 0) {
4731 }
else if (
type.compare(
"VEC4") == 0) {
4733 }
else if (
type.compare(
"MAT2") == 0) {
4735 }
else if (
type.compare(
"MAT3") == 0) {
4737 }
else if (
type.compare(
"MAT4") == 0) {
4740 std::stringstream ss;
4741 ss <<
"Unsupported `type` for accessor object. Got \"" <<
type <<
"\"\n";
4748 ParseStringProperty(&accessor->name, err, o,
"name",
false);
4750 accessor->minValues.clear();
4751 accessor->maxValues.clear();
4752 ParseNumberArrayProperty(&accessor->minValues, err, o,
"min",
false,
4755 ParseNumberArrayProperty(&accessor->maxValues, err, o,
"max",
false,
4758 accessor->count = count;
4759 accessor->bufferView = bufferView;
4760 accessor->byteOffset = byteOffset;
4761 accessor->normalized = normalized;
4766 accessor->componentType = int(componentType);
4768 std::stringstream ss;
4769 ss <<
"Invalid `componentType` in accessor. Got " << componentType
4778 ParseExtrasAndExtensions(accessor, err, o,
4779 store_original_json_for_extras_and_extensions);
4782 detail::json_const_iterator iterator;
4783 if (detail::FindMember(o,
"sparse", iterator)) {
4785 return ParseSparseAccessor(&accessor->sparse, err,
4786 detail::GetValue(iterator),
4787 store_original_json_for_extras_and_extensions);
4793 #ifdef TINYGLTF_ENABLE_DRACO
4795 static void DecodeIndexBuffer(draco::Mesh *mesh,
size_t componentSize,
4796 std::vector<uint8_t> &outBuffer) {
4797 if (componentSize == 4) {
4798 assert(
sizeof(mesh->face(draco::FaceIndex(0))[0]) == componentSize);
4799 memcpy(outBuffer.data(), &mesh->face(draco::FaceIndex(0))[0],
4802 size_t faceStride = componentSize * 3;
4803 for (draco::FaceIndex
f(0);
f < mesh->num_faces(); ++
f) {
4805 if (componentSize == 2) {
4806 uint16_t indices[3] = {(uint16_t)face[0].
value(),
4807 (uint16_t)face[1].
value(),
4808 (uint16_t)face[2].
value()};
4809 memcpy(outBuffer.data() +
f.value() * faceStride, &indices[0],
4812 uint8_t indices[3] = {(uint8_t)face[0].
value(),
4813 (uint8_t)face[1].
value(),
4814 (uint8_t)face[2].
value()};
4815 memcpy(outBuffer.data() +
f.value() * faceStride, &indices[0],
4822 template <
typename T>
4823 static bool GetAttributeForAllPoints(draco::Mesh *mesh,
4824 const draco::PointAttribute *pAttribute,
4825 std::vector<uint8_t> &outBuffer) {
4826 size_t byteOffset = 0;
4827 T values[4] = {0, 0, 0, 0};
4828 for (draco::PointIndex
i(0);
i < mesh->num_points(); ++
i) {
4829 const draco::AttributeValueIndex val_index = pAttribute->mapped_index(
i);
4830 if (!pAttribute->ConvertValue<T>(val_index, pAttribute->num_components(),
4834 memcpy(outBuffer.data() + byteOffset, &values[0],
4835 sizeof(T) * pAttribute->num_components());
4836 byteOffset +=
sizeof(T) * pAttribute->num_components();
4842 static bool GetAttributeForAllPoints(uint32_t componentType, draco::Mesh *mesh,
4843 const draco::PointAttribute *pAttribute,
4844 std::vector<uint8_t> &outBuffer) {
4845 bool decodeResult =
false;
4846 switch (componentType) {
4849 GetAttributeForAllPoints<uint8_t>(mesh, pAttribute, outBuffer);
4853 GetAttributeForAllPoints<int8_t>(mesh, pAttribute, outBuffer);
4857 GetAttributeForAllPoints<uint16_t>(mesh, pAttribute, outBuffer);
4861 GetAttributeForAllPoints<int16_t>(mesh, pAttribute, outBuffer);
4865 GetAttributeForAllPoints<int32_t>(mesh, pAttribute, outBuffer);
4869 GetAttributeForAllPoints<uint32_t>(mesh, pAttribute, outBuffer);
4873 GetAttributeForAllPoints<float>(mesh, pAttribute, outBuffer);
4877 GetAttributeForAllPoints<double>(mesh, pAttribute, outBuffer);
4883 return decodeResult;
4886 static bool ParseDracoExtension(Primitive *primitive, Model *model,
4887 std::string *err, std::string *warn,
4888 const Value &dracoExtensionValue,
4891 auto bufferViewValue = dracoExtensionValue.Get(
"bufferView");
4892 if (!bufferViewValue.IsInt())
return false;
4893 auto attributesValue = dracoExtensionValue.Get(
"attributes");
4894 if (!attributesValue.IsObject())
return false;
4896 auto attributesObject = attributesValue.Get<
Value::Object>();
4897 int bufferView = bufferViewValue.Get<
int>();
4899 BufferView &view = model->bufferViews[bufferView];
4900 Buffer &buffer = model->buffers[view.buffer];
4902 if (view.dracoDecoded)
return true;
4903 view.dracoDecoded =
true;
4905 const char *bufferViewData =
4906 reinterpret_cast<const char *
>(buffer.data.data() + view.byteOffset);
4907 size_t bufferViewSize = view.byteLength;
4910 draco::DecoderBuffer decoderBuffer;
4911 decoderBuffer.Init(bufferViewData, bufferViewSize);
4912 draco::Decoder decoder;
4913 auto decodeResult = decoder.DecodeMeshFromBuffer(&decoderBuffer);
4914 if (!decodeResult.ok()) {
4917 const std::unique_ptr<draco::Mesh> &mesh = decodeResult.value();
4920 if (primitive->indices >= 0) {
4922 const draco::PointIndex::ValueType numPoint = mesh->num_points();
4926 if (numPoint <
static_cast<draco::PointIndex::ValueType
>(
4927 std::numeric_limits<uint8_t>::max())) {
4930 numPoint <
static_cast<draco::PointIndex::ValueType
>(
4931 std::numeric_limits<uint16_t>::max())) {
4937 if (supposedComponentType > model->accessors[primitive->indices].componentType) {
4940 "GLTF component type " +
std::to_string(model->accessors[primitive->indices].componentType) +
4941 " is not sufficient for number of stored points,"
4944 model->accessors[primitive->indices].componentType = supposedComponentType;
4949 model->accessors[primitive->indices].componentType);
4950 Buffer decodedIndexBuffer;
4951 decodedIndexBuffer.data.resize(mesh->num_faces() * 3 * componentSize);
4953 DecodeIndexBuffer(mesh.get(), componentSize, decodedIndexBuffer.data);
4955 model->buffers.emplace_back(std::move(decodedIndexBuffer));
4957 BufferView decodedIndexBufferView;
4958 decodedIndexBufferView.buffer = int(model->buffers.size() - 1);
4959 decodedIndexBufferView.byteLength =
4960 int(mesh->num_faces() * 3 * componentSize);
4961 decodedIndexBufferView.byteOffset = 0;
4962 decodedIndexBufferView.byteStride = 0;
4964 model->bufferViews.emplace_back(std::move(decodedIndexBufferView));
4966 model->accessors[primitive->indices].bufferView =
4967 int(model->bufferViews.size() - 1);
4968 model->accessors[primitive->indices].count = int(mesh->num_faces() * 3);
4971 for (
const auto &attribute : attributesObject) {
4972 if (!attribute.second.IsInt())
return false;
4973 auto primitiveAttribute = primitive->attributes.find(attribute.first);
4974 if (primitiveAttribute == primitive->attributes.end())
return false;
4976 int dracoAttributeIndex = attribute.second.Get<
int>();
4977 const auto pAttribute = mesh->GetAttributeByUniqueId(dracoAttributeIndex);
4978 const auto componentType =
4979 model->accessors[primitiveAttribute->second].componentType;
4982 Buffer decodedBuffer;
4983 size_t bufferSize = mesh->num_points() * pAttribute->num_components() *
4985 decodedBuffer.data.resize(bufferSize);
4987 if (!GetAttributeForAllPoints(componentType, mesh.get(), pAttribute,
4988 decodedBuffer.data))
4991 model->buffers.emplace_back(std::move(decodedBuffer));
4993 BufferView decodedBufferView;
4994 decodedBufferView.buffer = int(model->buffers.size() - 1);
4995 decodedBufferView.byteLength = bufferSize;
4996 decodedBufferView.byteOffset = pAttribute->byte_offset();
4997 decodedBufferView.byteStride = pAttribute->byte_stride();
4998 decodedBufferView.target = primitive->indices >= 0
5001 model->bufferViews.emplace_back(std::move(decodedBufferView));
5003 model->accessors[primitiveAttribute->second].bufferView =
5004 int(model->bufferViews.size() - 1);
5005 model->accessors[primitiveAttribute->second].count =
5006 int(mesh->num_points());
5013 static bool ParsePrimitive(Primitive *primitive, Model *model,
5014 std::string *err, std::string *warn,
5016 bool store_original_json_for_extras_and_extensions,
5019 ParseIntegerProperty(&material, err, o,
"material",
false);
5020 primitive->material = material;
5023 ParseIntegerProperty(&
mode, err, o,
"mode",
false);
5024 primitive->mode =
mode;
5027 ParseIntegerProperty(&indices, err, o,
"indices",
false);
5028 primitive->indices = indices;
5029 if (!ParseStringIntegerProperty(&primitive->attributes, err, o,
"attributes",
5030 true,
"Primitive")) {
5035 detail::json_const_iterator targetsObject;
5036 if (detail::FindMember(o,
"targets", targetsObject) &&
5037 detail::IsArray(detail::GetValue(targetsObject))) {
5038 auto targetsObjectEnd = detail::ArrayEnd(detail::GetValue(targetsObject));
5039 for (detail::json_const_array_iterator
i =
5040 detail::ArrayBegin(detail::GetValue(targetsObject));
5041 i != targetsObjectEnd; ++
i) {
5042 std::map<std::string, int> targetAttribues;
5045 if (detail::IsObject(dict)) {
5046 detail::json_const_iterator dictIt(detail::ObjectBegin(dict));
5047 detail::json_const_iterator dictItEnd(detail::ObjectEnd(dict));
5049 for (; dictIt != dictItEnd; ++dictIt) {
5051 if (detail::GetInt(detail::GetValue(dictIt), iVal))
5052 targetAttribues[detail::GetKey(dictIt)] = iVal;
5054 primitive->targets.emplace_back(std::move(targetAttribues));
5059 ParseExtrasAndExtensions(primitive, err, o,
5060 store_original_json_for_extras_and_extensions);
5062 #ifdef TINYGLTF_ENABLE_DRACO
5063 auto dracoExtension =
5064 primitive->extensions.find(
"KHR_draco_mesh_compression");
5065 if (dracoExtension != primitive->extensions.end()) {
5066 ParseDracoExtension(primitive, model, err, warn, dracoExtension->second, strictness);
5077 static bool ParseMesh(Mesh *mesh, Model *model,
5078 std::string *err, std::string *warn,
5080 bool store_original_json_for_extras_and_extensions,
5082 ParseStringProperty(&mesh->name, err, o,
"name",
false);
5084 mesh->primitives.clear();
5085 detail::json_const_iterator primObject;
5086 if (detail::FindMember(o,
"primitives", primObject) &&
5087 detail::IsArray(detail::GetValue(primObject))) {
5088 detail::json_const_array_iterator primEnd =
5089 detail::ArrayEnd(detail::GetValue(primObject));
5090 for (detail::json_const_array_iterator
i =
5091 detail::ArrayBegin(detail::GetValue(primObject));
5092 i != primEnd; ++
i) {
5093 Primitive primitive;
5094 if (ParsePrimitive(&primitive, model, err, warn, *
i,
5095 store_original_json_for_extras_and_extensions,
5098 mesh->primitives.emplace_back(std::move(primitive));
5104 ParseNumberArrayProperty(&mesh->weights, err, o,
"weights",
false);
5106 ParseExtrasAndExtensions(mesh, err, o,
5107 store_original_json_for_extras_and_extensions);
5112 static bool ParseNode(Node *node, std::string *err,
const detail::json &o,
5113 bool store_original_json_for_extras_and_extensions) {
5114 ParseStringProperty(&node->name, err, o,
"name",
false);
5117 ParseIntegerProperty(&skin, err, o,
"skin",
false);
5121 if (!ParseNumberArrayProperty(&node->matrix, err, o,
"matrix",
false)) {
5122 ParseNumberArrayProperty(&node->rotation, err, o,
"rotation",
false);
5123 ParseNumberArrayProperty(&node->scale, err, o,
"scale",
false);
5124 ParseNumberArrayProperty(&node->translation, err, o,
"translation",
false);
5128 ParseIntegerProperty(&camera, err, o,
"camera",
false);
5129 node->camera = camera;
5132 ParseIntegerProperty(&mesh, err, o,
"mesh",
false);
5135 node->children.clear();
5136 ParseIntegerArrayProperty(&node->children, err, o,
"children",
false);
5138 ParseNumberArrayProperty(&node->weights, err, o,
"weights",
false);
5140 ParseExtrasAndExtensions(node, err, o,
5141 store_original_json_for_extras_and_extensions);
5145 if (node->extensions.count(
"KHR_lights_punctual") != 0) {
5146 auto const &light_ext = node->extensions[
"KHR_lights_punctual"];
5147 if (light_ext.Has(
"light")) {
5148 light = light_ext.Get(
"light").GetNumberAsInt();
5152 "Node has extension KHR_lights_punctual, but does not reference "
5153 "a light source.\n";
5158 node->light = light;
5162 if (node->extensions.count(
"KHR_audio") != 0) {
5163 auto const &audio_ext = node->extensions[
"KHR_audio"];
5164 if (audio_ext.Has(
"emitter")) {
5165 emitter = audio_ext.Get(
"emitter").GetNumberAsInt();
5169 "Node has extension KHR_audio, but does not reference "
5170 "a audio emitter.\n";
5175 node->emitter = emitter;
5178 if (node->extensions.count(
"MSFT_lod") != 0) {
5179 auto const &msft_lod_ext = node->extensions[
"MSFT_lod"];
5180 if (msft_lod_ext.Has(
"ids")) {
5181 auto idsArr = msft_lod_ext.Get(
"ids");
5182 for (
size_t i = 0;
i < idsArr.ArrayLen(); ++
i) {
5183 node->lods.emplace_back(idsArr.Get(
i).GetNumberAsInt());
5188 "Node has extension MSFT_lod, but does not reference "
5189 "other nodes via their ids.\n";
5198 static bool ParseScene(Scene *scene, std::string *err,
const detail::json &o,
5199 bool store_original_json_for_extras_and_extensions) {
5200 ParseStringProperty(&scene->name, err, o,
"name",
false);
5201 ParseIntegerArrayProperty(&scene->nodes, err, o,
"nodes",
false);
5203 ParseExtrasAndExtensions(scene, err, o,
5204 store_original_json_for_extras_and_extensions);
5207 if (scene->extensions.count(
"KHR_audio") != 0) {
5208 auto const &audio_ext = scene->extensions[
"KHR_audio"];
5209 if (audio_ext.Has(
"emitters")) {
5210 auto emittersArr = audio_ext.Get(
"emitters");
5211 for (
size_t i = 0;
i < emittersArr.ArrayLen(); ++
i) {
5212 scene->audioEmitters.emplace_back(emittersArr.Get(
i).GetNumberAsInt());
5217 "Node has extension KHR_audio, but does not reference "
5218 "a audio emitter.\n";
5227 static bool ParsePbrMetallicRoughness(
5228 PbrMetallicRoughness *pbr, std::string *err,
const detail::json &o,
5229 bool store_original_json_for_extras_and_extensions) {
5230 if (pbr ==
nullptr) {
5234 std::vector<double> baseColorFactor;
5235 if (ParseNumberArrayProperty(&baseColorFactor, err, o,
"baseColorFactor",
5237 if (baseColorFactor.size() != 4) {
5240 "Array length of `baseColorFactor` parameter in "
5241 "pbrMetallicRoughness must be 4, but got " +
5246 pbr->baseColorFactor = baseColorFactor;
5250 detail::json_const_iterator it;
5251 if (detail::FindMember(o,
"baseColorTexture", it)) {
5252 ParseTextureInfo(&pbr->baseColorTexture, err, detail::GetValue(it),
5253 store_original_json_for_extras_and_extensions);
5258 detail::json_const_iterator it;
5259 if (detail::FindMember(o,
"metallicRoughnessTexture", it)) {
5260 ParseTextureInfo(&pbr->metallicRoughnessTexture, err,
5261 detail::GetValue(it),
5262 store_original_json_for_extras_and_extensions);
5266 ParseNumberProperty(&pbr->metallicFactor, err, o,
"metallicFactor",
false);
5267 ParseNumberProperty(&pbr->roughnessFactor, err, o,
"roughnessFactor",
false);
5269 ParseExtrasAndExtensions(pbr, err, o,
5270 store_original_json_for_extras_and_extensions);
5275 static bool ParseMaterial(Material *material, std::string *err, std::string *warn,
5277 bool store_original_json_for_extras_and_extensions,
5279 ParseStringProperty(&material->name, err, o,
"name",
false);
5281 if (ParseNumberArrayProperty(&material->emissiveFactor, err, o,
5287 "Array length of `emissiveFactor` parameter in "
5288 "material must be 3, but got 4\n";
5290 material->emissiveFactor.resize(3);
5292 else if (material->emissiveFactor.size() != 3) {
5295 "Array length of `emissiveFactor` parameter in "
5296 "material must be 3, but got " +
5303 material->emissiveFactor = {0.0, 0.0, 0.0};
5306 ParseStringProperty(&material->alphaMode, err, o,
"alphaMode",
5308 ParseNumberProperty(&material->alphaCutoff, err, o,
"alphaCutoff",
5310 ParseBooleanProperty(&material->doubleSided, err, o,
"doubleSided",
5314 detail::json_const_iterator it;
5315 if (detail::FindMember(o,
"pbrMetallicRoughness", it)) {
5316 ParsePbrMetallicRoughness(&material->pbrMetallicRoughness, err,
5317 detail::GetValue(it),
5318 store_original_json_for_extras_and_extensions);
5323 detail::json_const_iterator it;
5324 if (detail::FindMember(o,
"normalTexture", it)) {
5325 ParseNormalTextureInfo(&material->normalTexture, err,
5326 detail::GetValue(it),
5327 store_original_json_for_extras_and_extensions);
5332 detail::json_const_iterator it;
5333 if (detail::FindMember(o,
"occlusionTexture", it)) {
5334 ParseOcclusionTextureInfo(&material->occlusionTexture, err,
5335 detail::GetValue(it),
5336 store_original_json_for_extras_and_extensions);
5341 detail::json_const_iterator it;
5342 if (detail::FindMember(o,
"emissiveTexture", it)) {
5343 ParseTextureInfo(&material->emissiveTexture, err, detail::GetValue(it),
5344 store_original_json_for_extras_and_extensions);
5353 material->values.clear();
5354 material->additionalValues.clear();
5356 detail::json_const_iterator it(detail::ObjectBegin(o));
5357 detail::json_const_iterator itEnd(detail::ObjectEnd(o));
5359 for (; it != itEnd; ++it) {
5360 std::string
key(detail::GetKey(it));
5361 if (key ==
"pbrMetallicRoughness") {
5362 if (detail::IsObject(detail::GetValue(it))) {
5363 const detail::json &values_object = detail::GetValue(it);
5365 detail::json_const_iterator itVal(detail::ObjectBegin(values_object));
5366 detail::json_const_iterator itValEnd(detail::ObjectEnd(values_object));
5368 for (; itVal != itValEnd; ++itVal) {
5370 if (ParseParameterProperty(¶m, err, values_object,
5371 detail::GetKey(itVal),
false)) {
5372 material->values.emplace(detail::GetKey(itVal), std::move(param));
5376 }
else if (key ==
"extensions" || key ==
"extras") {
5381 if (ParseParameterProperty(¶m, err, o, key,
false)) {
5385 material->additionalValues.emplace(std::move(key), std::move(param));
5390 material->extensions.clear();
5391 ParseExtrasAndExtensions(material, err, o,
5392 store_original_json_for_extras_and_extensions);
5394 material->lods.clear();
5395 if (material->extensions.count(
"MSFT_lod") != 0) {
5396 auto const &msft_lod_ext = material->extensions[
"MSFT_lod"];
5397 if (msft_lod_ext.Has(
"ids")) {
5398 auto idsArr = msft_lod_ext.Get(
"ids");
5399 for (
size_t i = 0;
i < idsArr.ArrayLen(); ++
i) {
5400 material->lods.emplace_back(idsArr.Get(
i).GetNumberAsInt());
5405 "Material has extension MSFT_lod, but does not reference "
5406 "other materials via their ids.\n";
5415 static bool ParseAnimationChannel(
5416 AnimationChannel *channel, std::string *err,
const detail::json &o,
5417 bool store_original_json_for_extras_and_extensions) {
5418 int samplerIndex = -1;
5419 int targetIndex = -1;
5420 if (!ParseIntegerProperty(&samplerIndex, err, o,
"sampler",
true,
5421 "AnimationChannel")) {
5423 (*err) +=
"`sampler` field is missing in animation channels\n";
5428 detail::json_const_iterator targetIt;
5429 if (detail::FindMember(o,
"target", targetIt) &&
5430 detail::IsObject(detail::GetValue(targetIt))) {
5431 const detail::json &target_object = detail::GetValue(targetIt);
5433 ParseIntegerProperty(&targetIndex, err, target_object,
"node",
false);
5435 if (!ParseStringProperty(&channel->target_path, err, target_object,
"path",
5438 (*err) +=
"`path` field is missing in animation.channels.target\n";
5442 ParseExtensionsProperty(&channel->target_extensions, err, target_object);
5443 ParseExtrasProperty(&channel->target_extras, target_object);
5444 if (store_original_json_for_extras_and_extensions) {
5446 detail::json_const_iterator it;
5447 if (detail::FindMember(target_object,
"extensions", it)) {
5448 channel->target_extensions_json_string =
5449 detail::JsonToString(detail::GetValue(it));
5453 detail::json_const_iterator it;
5454 if (detail::FindMember(target_object,
"extras", it)) {
5455 channel->target_extras_json_string =
5456 detail::JsonToString(detail::GetValue(it));
5462 channel->sampler = samplerIndex;
5463 channel->target_node = targetIndex;
5465 ParseExtrasAndExtensions(channel, err, o,
5466 store_original_json_for_extras_and_extensions);
5471 static bool ParseAnimation(Animation *animation, std::string *err,
5473 bool store_original_json_for_extras_and_extensions) {
5475 detail::json_const_iterator channelsIt;
5476 if (detail::FindMember(o,
"channels", channelsIt) &&
5477 detail::IsArray(detail::GetValue(channelsIt))) {
5478 detail::json_const_array_iterator channelEnd =
5479 detail::ArrayEnd(detail::GetValue(channelsIt));
5480 for (detail::json_const_array_iterator
i =
5481 detail::ArrayBegin(detail::GetValue(channelsIt));
5482 i != channelEnd; ++
i) {
5483 AnimationChannel channel;
5484 if (ParseAnimationChannel(
5486 store_original_json_for_extras_and_extensions)) {
5488 animation->channels.emplace_back(std::move(channel));
5495 detail::json_const_iterator samplerIt;
5496 if (detail::FindMember(o,
"samplers", samplerIt) &&
5497 detail::IsArray(detail::GetValue(samplerIt))) {
5498 const detail::json &sampler_array = detail::GetValue(samplerIt);
5500 detail::json_const_array_iterator it = detail::ArrayBegin(sampler_array);
5501 detail::json_const_array_iterator itEnd = detail::ArrayEnd(sampler_array);
5503 for (; it != itEnd; ++it) {
5506 AnimationSampler sampler;
5507 int inputIndex = -1;
5508 int outputIndex = -1;
5509 if (!ParseIntegerProperty(&inputIndex, err,
s,
"input",
true)) {
5511 (*err) +=
"`input` field is missing in animation.sampler\n";
5515 ParseStringProperty(&sampler.interpolation, err,
s,
"interpolation",
5517 if (!ParseIntegerProperty(&outputIndex, err,
s,
"output",
true)) {
5519 (*err) +=
"`output` field is missing in animation.sampler\n";
5523 sampler.input = inputIndex;
5524 sampler.output = outputIndex;
5525 ParseExtrasAndExtensions(&sampler, err, o,
5526 store_original_json_for_extras_and_extensions);
5528 animation->samplers.emplace_back(std::move(sampler));
5533 ParseStringProperty(&animation->name, err, o,
"name",
false);
5535 ParseExtrasAndExtensions(animation, err, o,
5536 store_original_json_for_extras_and_extensions);
5541 static bool ParseSampler(Sampler *sampler, std::string *err,
5543 bool store_original_json_for_extras_and_extensions) {
5544 ParseStringProperty(&sampler->name, err, o,
"name",
false);
5551 ParseIntegerProperty(&minFilter, err, o,
"minFilter",
false);
5552 ParseIntegerProperty(&magFilter, err, o,
"magFilter",
false);
5553 ParseIntegerProperty(&wrapS, err, o,
"wrapS",
false);
5554 ParseIntegerProperty(&wrapT, err, o,
"wrapT",
false);
5561 sampler->minFilter = minFilter;
5562 sampler->magFilter = magFilter;
5563 sampler->wrapS = wrapS;
5564 sampler->wrapT = wrapT;
5567 ParseExtrasAndExtensions(sampler, err, o,
5568 store_original_json_for_extras_and_extensions);
5573 static bool ParseSkin(Skin *skin, std::string *err,
const detail::json &o,
5574 bool store_original_json_for_extras_and_extensions) {
5575 ParseStringProperty(&skin->name, err, o,
"name",
false,
"Skin");
5577 std::vector<int> joints;
5578 if (!ParseIntegerArrayProperty(&joints, err, o,
"joints",
false,
"Skin")) {
5581 skin->joints = std::move(joints);
5584 ParseIntegerProperty(&skeleton, err, o,
"skeleton",
false,
"Skin");
5585 skin->skeleton = skeleton;
5588 ParseIntegerProperty(&invBind, err, o,
"inverseBindMatrices",
true,
"Skin");
5589 skin->inverseBindMatrices = invBind;
5591 ParseExtrasAndExtensions(skin, err, o,
5592 store_original_json_for_extras_and_extensions);
5597 static bool ParsePerspectiveCamera(
5598 PerspectiveCamera *camera, std::string *err,
const detail::json &o,
5599 bool store_original_json_for_extras_and_extensions) {
5601 if (!ParseNumberProperty(&yfov, err, o,
"yfov",
true,
"OrthographicCamera")) {
5606 if (!ParseNumberProperty(&znear, err, o,
"znear",
true,
5607 "PerspectiveCamera")) {
5611 double aspectRatio = 0.0;
5612 ParseNumberProperty(&aspectRatio, err, o,
"aspectRatio",
false,
5613 "PerspectiveCamera");
5616 ParseNumberProperty(&zfar, err, o,
"zfar",
false,
"PerspectiveCamera");
5618 camera->aspectRatio = aspectRatio;
5619 camera->zfar = zfar;
5620 camera->yfov = yfov;
5621 camera->znear = znear;
5623 ParseExtrasAndExtensions(camera, err, o,
5624 store_original_json_for_extras_and_extensions);
5631 static bool ParseSpotLight(SpotLight *light, std::string *err,
5633 bool store_original_json_for_extras_and_extensions) {
5634 ParseNumberProperty(&light->innerConeAngle, err, o,
"innerConeAngle",
false);
5635 ParseNumberProperty(&light->outerConeAngle, err, o,
"outerConeAngle",
false);
5637 ParseExtrasAndExtensions(light, err, o,
5638 store_original_json_for_extras_and_extensions);
5645 static bool ParseOrthographicCamera(
5646 OrthographicCamera *camera, std::string *err,
const detail::json &o,
5647 bool store_original_json_for_extras_and_extensions) {
5649 if (!ParseNumberProperty(&xmag, err, o,
"xmag",
true,
"OrthographicCamera")) {
5654 if (!ParseNumberProperty(&ymag, err, o,
"ymag",
true,
"OrthographicCamera")) {
5659 if (!ParseNumberProperty(&zfar, err, o,
"zfar",
true,
"OrthographicCamera")) {
5664 if (!ParseNumberProperty(&znear, err, o,
"znear",
true,
5665 "OrthographicCamera")) {
5669 ParseExtrasAndExtensions(camera, err, o,
5670 store_original_json_for_extras_and_extensions);
5672 camera->xmag = xmag;
5673 camera->ymag = ymag;
5674 camera->zfar = zfar;
5675 camera->znear = znear;
5682 static bool ParseCamera(Camera *camera, std::string *err,
const detail::json &o,
5683 bool store_original_json_for_extras_and_extensions) {
5684 if (!ParseStringProperty(&camera->type, err, o,
"type",
true,
"Camera")) {
5688 if (camera->type.compare(
"orthographic") == 0) {
5689 detail::json_const_iterator orthoIt;
5690 if (!detail::FindMember(o,
"orthographic", orthoIt)) {
5692 std::stringstream ss;
5693 ss <<
"Orthographic camera description not found." << std::endl;
5700 if (!detail::IsObject(v)) {
5702 std::stringstream ss;
5703 ss <<
"\"orthographic\" is not a JSON object." << std::endl;
5709 if (!ParseOrthographicCamera(
5710 &camera->orthographic, err, v,
5711 store_original_json_for_extras_and_extensions)) {
5714 }
else if (camera->type.compare(
"perspective") == 0) {
5715 detail::json_const_iterator perspIt;
5716 if (!detail::FindMember(o,
"perspective", perspIt)) {
5718 std::stringstream ss;
5719 ss <<
"Perspective camera description not found." << std::endl;
5726 if (!detail::IsObject(v)) {
5728 std::stringstream ss;
5729 ss <<
"\"perspective\" is not a JSON object." << std::endl;
5735 if (!ParsePerspectiveCamera(
5736 &camera->perspective, err, v,
5737 store_original_json_for_extras_and_extensions)) {
5742 std::stringstream ss;
5743 ss <<
"Invalid camera type: \"" << camera->type
5744 <<
"\". Must be \"perspective\" or \"orthographic\"" << std::endl;
5750 ParseStringProperty(&camera->name, err, o,
"name",
false);
5752 ParseExtrasAndExtensions(camera, err, o,
5753 store_original_json_for_extras_and_extensions);
5758 static bool ParseLight(Light *light, std::string *err,
const detail::json &o,
5759 bool store_original_json_for_extras_and_extensions) {
5760 if (!ParseStringProperty(&light->type, err, o,
"type",
true)) {
5764 if (light->type ==
"spot") {
5765 detail::json_const_iterator spotIt;
5766 if (!detail::FindMember(o,
"spot", spotIt)) {
5768 std::stringstream ss;
5769 ss <<
"Spot light description not found." << std::endl;
5776 if (!detail::IsObject(v)) {
5778 std::stringstream ss;
5779 ss <<
"\"spot\" is not a JSON object." << std::endl;
5785 if (!ParseSpotLight(&light->spot, err, v,
5786 store_original_json_for_extras_and_extensions)) {
5791 ParseStringProperty(&light->name, err, o,
"name",
false);
5792 ParseNumberArrayProperty(&light->color, err, o,
"color",
false);
5793 ParseNumberProperty(&light->range, err, o,
"range",
false);
5794 ParseNumberProperty(&light->intensity, err, o,
"intensity",
false);
5796 ParseExtrasAndExtensions(light, err, o,
5797 store_original_json_for_extras_and_extensions);
5802 static bool ParsePositionalEmitter(
5803 PositionalEmitter *positional, std::string *err,
const detail::json &o,
5804 bool store_original_json_for_extras_and_extensions) {
5805 ParseNumberProperty(&positional->coneInnerAngle, err, o,
"coneInnerAngle",
5807 ParseNumberProperty(&positional->coneOuterAngle, err, o,
"coneOuterAngle",
5809 ParseNumberProperty(&positional->coneOuterGain, err, o,
"coneOuterGain",
5811 ParseNumberProperty(&positional->maxDistance, err, o,
"maxDistance",
false);
5812 ParseNumberProperty(&positional->refDistance, err, o,
"refDistance",
false);
5813 ParseNumberProperty(&positional->rolloffFactor, err, o,
"rolloffFactor",
5816 ParseExtrasAndExtensions(positional, err, o,
5817 store_original_json_for_extras_and_extensions);
5822 static bool ParseAudioEmitter(
5823 AudioEmitter *emitter, std::string *err,
const detail::json &o,
5824 bool store_original_json_for_extras_and_extensions) {
5825 if (!ParseStringProperty(&emitter->type, err, o,
"type",
true)) {
5829 if (emitter->type ==
"positional") {
5830 detail::json_const_iterator positionalIt;
5831 if (!detail::FindMember(o,
"positional", positionalIt)) {
5833 std::stringstream ss;
5834 ss <<
"Positional emitter description not found." << std::endl;
5840 const detail::json &v = detail::GetValue(positionalIt);
5841 if (!detail::IsObject(v)) {
5843 std::stringstream ss;
5844 ss <<
"\"positional\" is not a JSON object." << std::endl;
5850 if (!ParsePositionalEmitter(
5851 &emitter->positional, err, v,
5852 store_original_json_for_extras_and_extensions)) {
5857 ParseStringProperty(&emitter->name, err, o,
"name",
false);
5858 ParseNumberProperty(&emitter->gain, err, o,
"gain",
false);
5859 ParseBooleanProperty(&emitter->loop, err, o,
"loop",
false);
5860 ParseBooleanProperty(&emitter->playing, err, o,
"playing",
false);
5861 ParseStringProperty(&emitter->distanceModel, err, o,
"distanceModel",
false);
5862 ParseIntegerProperty(&emitter->source, err, o,
"source",
true);
5864 ParseExtrasAndExtensions(emitter, err, o,
5865 store_original_json_for_extras_and_extensions);
5870 static bool ParseAudioSource(
5872 bool store_original_json_for_extras_and_extensions) {
5873 ParseStringProperty(&
source->name, err, o,
"name",
false);
5874 ParseStringProperty(&
source->uri, err, o,
"uri",
false);
5876 if (
source->uri.empty()) {
5877 ParseIntegerProperty(&
source->bufferView, err, o,
"bufferView",
true);
5878 ParseStringProperty(&
source->mimeType, err, o,
"mimeType",
true);
5881 ParseExtrasAndExtensions(
source, err, o,
5882 store_original_json_for_extras_and_extensions);
5889 template <
typename Callback>
5890 bool ForEachInArray(
const detail::json &_v,
const char *member, Callback &&cb) {
5891 detail::json_const_iterator itm;
5892 if (detail::FindMember(_v, member, itm) &&
5893 detail::IsArray(detail::GetValue(itm))) {
5895 auto it = detail::ArrayBegin(root);
5896 auto end = detail::ArrayEnd(root);
5897 for (; it != end; ++it) {
5898 if (!cb(*it))
return false;
5906 bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
5907 const char *json_str,
5908 unsigned int json_str_length,
5909 const std::string &base_dir,
5910 unsigned int check_sections) {
5911 if (json_str_length < 4) {
5913 (*err) =
"JSON string too short.\n";
5918 detail::JsonDocument v;
5920 #if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
5921 defined(_CPPUNWIND)) && \
5922 !defined(TINYGLTF_NOEXCEPTION)
5924 detail::JsonParse(v, json_str, json_str_length,
true);
5926 }
catch (
const std::exception &e) {
5934 detail::JsonParse(v, json_str, json_str_length);
5936 if (!detail::IsObject(v)) {
5939 (*err) =
"Failed to parse JSON object\n";
5946 if (!detail::IsObject(v)) {
5949 (*err) =
"Root element is not a JSON object\n";
5955 bool version_found =
false;
5956 detail::json_const_iterator it;
5957 if (detail::FindMember(v,
"asset", it) &&
5958 detail::IsObject(detail::GetValue(it))) {
5959 auto &itObj = detail::GetValue(it);
5960 detail::json_const_iterator version_it;
5961 std::string versionStr;
5962 if (detail::FindMember(itObj,
"version", version_it) &&
5963 detail::GetString(detail::GetValue(version_it), versionStr)) {
5964 version_found =
true;
5967 if (version_found) {
5971 (*err) +=
"\"asset\" object not found in .gltf or not an object type\n";
5981 const char *name) ->
bool {
5982 detail::json_const_iterator it;
5983 return detail::FindMember(_v, name, it) &&
5984 detail::IsArray(detail::GetValue(it));
5989 !IsArrayMemberPresent(v,
"scenes")) {
5991 (*err) +=
"\"scenes\" object not found in .gltf or not an array type\n";
5998 if ((check_sections &
REQUIRE_NODES) && !IsArrayMemberPresent(v,
"nodes")) {
6000 (*err) +=
"\"nodes\" object not found in .gltf\n";
6008 !IsArrayMemberPresent(v,
"accessors")) {
6010 (*err) +=
"\"accessors\" object not found in .gltf\n";
6018 !IsArrayMemberPresent(v,
"buffers")) {
6020 (*err) +=
"\"buffers\" object not found in .gltf\n";
6028 !IsArrayMemberPresent(v,
"bufferViews")) {
6030 (*err) +=
"\"bufferViews\" object not found in .gltf\n";
6036 model->buffers.clear();
6037 model->bufferViews.clear();
6038 model->accessors.clear();
6039 model->meshes.clear();
6040 model->cameras.clear();
6041 model->nodes.clear();
6042 model->extensionsUsed.clear();
6043 model->extensionsRequired.clear();
6044 model->extensions.clear();
6045 model->defaultScene = -1;
6049 detail::json_const_iterator it;
6050 if (detail::FindMember(v,
"asset", it) &&
6051 detail::IsObject(detail::GetValue(it))) {
6054 ParseAsset(&model->asset, err, root,
6055 store_original_json_for_extras_and_extensions_);
6059 using detail::ForEachInArray;
6063 ForEachInArray(v,
"extensionsUsed", [&](
const detail::json &o) {
6065 detail::GetString(o, str);
6066 model->extensionsUsed.emplace_back(std::move(str));
6072 ForEachInArray(v,
"extensionsRequired", [&](
const detail::json &o) {
6074 detail::GetString(o, str);
6075 model->extensionsRequired.emplace_back(std::move(str));
6082 bool success = ForEachInArray(v,
"buffers", [&](
const detail::json &o) {
6083 if (!detail::IsObject(o)) {
6085 (*err) +=
"`buffers' does not contain an JSON object.";
6090 if (!ParseBuffer(&buffer, err, o,
6091 store_original_json_for_extras_and_extensions_, &fs,
6092 &uri_cb, base_dir, max_external_file_size_, is_binary_,
6093 bin_data_, bin_size_)) {
6097 model->buffers.emplace_back(std::move(buffer));
6107 bool success = ForEachInArray(v,
"bufferViews", [&](
const detail::json &o) {
6108 if (!detail::IsObject(o)) {
6110 (*err) +=
"`bufferViews' does not contain an JSON object.";
6114 BufferView bufferView;
6115 if (!ParseBufferView(&bufferView, err, o,
6116 store_original_json_for_extras_and_extensions_)) {
6120 model->bufferViews.emplace_back(std::move(bufferView));
6131 bool success = ForEachInArray(v,
"accessors", [&](
const detail::json &o) {
6132 if (!detail::IsObject(o)) {
6134 (*err) +=
"`accessors' does not contain an JSON object.";
6139 if (!ParseAccessor(&accessor, err, o,
6140 store_original_json_for_extras_and_extensions_)) {
6144 model->accessors.emplace_back(std::move(accessor));
6155 bool success = ForEachInArray(v,
"meshes", [&](
const detail::json &o) {
6156 if (!detail::IsObject(o)) {
6158 (*err) +=
"`meshes' does not contain an JSON object.";
6163 if (!ParseMesh(&mesh, model, err, warn, o,
6164 store_original_json_for_extras_and_extensions_,
6169 model->meshes.emplace_back(std::move(mesh));
6181 for (
auto &mesh : model->meshes) {
6182 for (
auto &primitive : mesh.primitives) {
6183 if (primitive.indices >
6186 if (
size_t(primitive.indices) >= model->accessors.size()) {
6188 (*err) +=
"primitive indices accessor out of bounds";
6193 const auto bufferView =
6194 model->accessors[size_t(primitive.indices)].bufferView;
6195 if (bufferView < 0) {
6197 }
else if (
size_t(bufferView) >= model->bufferViews.size()) {
6200 "] invalid bufferView";
6204 model->bufferViews[size_t(bufferView)].target =
6211 for (
auto &attribute : primitive.attributes) {
6212 const auto accessorsIndex = size_t(attribute.second);
6213 if (accessorsIndex < model->accessors.size()) {
6214 const auto bufferView = model->accessors[accessorsIndex].bufferView;
6216 if (bufferView >= 0 && bufferView < (
int)model->bufferViews.size()) {
6217 model->bufferViews[size_t(bufferView)].target =
6223 for (
auto &target : primitive.targets) {
6224 for (
auto &attribute : target) {
6225 const auto accessorsIndex = size_t(attribute.second);
6226 if (accessorsIndex < model->accessors.size()) {
6227 const auto bufferView = model->accessors[accessorsIndex].bufferView;
6229 if (bufferView >= 0 &&
6230 bufferView < (
int)model->bufferViews.size()) {
6231 model->bufferViews[size_t(bufferView)].target =
6242 bool success = ForEachInArray(v,
"nodes", [&](
const detail::json &o) {
6243 if (!detail::IsObject(o)) {
6245 (*err) +=
"`nodes' does not contain an JSON object.";
6250 if (!ParseNode(&node, err, o,
6251 store_original_json_for_extras_and_extensions_)) {
6255 model->nodes.emplace_back(std::move(node));
6266 bool success = ForEachInArray(v,
"scenes", [&](
const detail::json &o) {
6267 if (!detail::IsObject(o)) {
6269 (*err) +=
"`scenes' does not contain an JSON object.";
6275 if (!ParseScene(&scene, err, o,
6276 store_original_json_for_extras_and_extensions_)) {
6280 model->scenes.emplace_back(std::move(scene));
6291 detail::json_const_iterator rootIt;
6293 if (detail::FindMember(v,
"scene", rootIt) &&
6294 detail::GetInt(detail::GetValue(rootIt), iVal)) {
6295 model->defaultScene = iVal;
6301 bool success = ForEachInArray(v,
"materials", [&](
const detail::json &o) {
6302 if (!detail::IsObject(o)) {
6304 (*err) +=
"`materials' does not contain an JSON object.";
6309 ParseStringProperty(&material.name, err, o,
"name",
false);
6311 if (!ParseMaterial(&material, err, warn, o,
6312 store_original_json_for_extras_and_extensions_,
6317 model->materials.emplace_back(std::move(material));
6327 void *load_image_user_data{
nullptr};
6329 LoadImageDataOption load_image_option;
6331 if (user_image_loader_) {
6333 load_image_user_data = load_image_user_data_;
6335 load_image_option.preserve_channels = preserve_image_channels_;
6336 load_image_user_data =
reinterpret_cast<void *
>(&load_image_option);
6341 bool success = ForEachInArray(v,
"images", [&](
const detail::json &o) {
6342 if (!detail::IsObject(o)) {
6344 (*err) +=
"image[" +
std::to_string(idx) +
"] is not a JSON object.";
6349 if (!ParseImage(&image, idx, err, warn, o,
6350 store_original_json_for_extras_and_extensions_, base_dir,
6351 max_external_file_size_, &fs, &uri_cb,
6352 &this->LoadImageData, load_image_user_data)) {
6356 if (image.bufferView != -1) {
6358 if (
size_t(image.bufferView) >= model->bufferViews.size()) {
6360 std::stringstream ss;
6361 ss <<
"image[" << idx <<
"] bufferView \"" << image.bufferView
6362 <<
"\" not found in the scene." << std::endl;
6368 const BufferView &bufferView =
6369 model->bufferViews[size_t(image.bufferView)];
6370 if (
size_t(bufferView.buffer) >= model->buffers.size()) {
6372 std::stringstream ss;
6373 ss <<
"image[" << idx <<
"] buffer \"" << bufferView.buffer
6374 <<
"\" not found in the scene." << std::endl;
6379 const Buffer &buffer = model->buffers[size_t(bufferView.buffer)];
6383 (*err) +=
"No LoadImageData callback specified.\n";
6388 &image, idx, err, warn, image.width, image.height,
6389 &buffer.data[bufferView.byteOffset],
6390 static_cast<int>(bufferView.byteLength), load_image_user_data);
6396 model->images.emplace_back(std::move(image));
6408 bool success = ForEachInArray(v,
"textures", [&](
const detail::json &o) {
6409 if (!detail::IsObject(o)) {
6411 (*err) +=
"`textures' does not contain an JSON object.";
6416 if (!ParseTexture(&texture, err, o,
6417 store_original_json_for_extras_and_extensions_,
6422 model->textures.emplace_back(std::move(texture));
6433 bool success = ForEachInArray(v,
"animations", [&](
const detail::json &o) {
6434 if (!detail::IsObject(o)) {
6436 (*err) +=
"`animations' does not contain an JSON object.";
6440 Animation animation;
6441 if (!ParseAnimation(&animation, err, o,
6442 store_original_json_for_extras_and_extensions_)) {
6446 model->animations.emplace_back(std::move(animation));
6457 bool success = ForEachInArray(v,
"skins", [&](
const detail::json &o) {
6458 if (!detail::IsObject(o)) {
6460 (*err) +=
"`skins' does not contain an JSON object.";
6465 if (!ParseSkin(&skin, err, o,
6466 store_original_json_for_extras_and_extensions_)) {
6470 model->skins.emplace_back(std::move(skin));
6481 bool success = ForEachInArray(v,
"samplers", [&](
const detail::json &o) {
6482 if (!detail::IsObject(o)) {
6484 (*err) +=
"`samplers' does not contain an JSON object.";
6489 if (!ParseSampler(&sampler, err, o,
6490 store_original_json_for_extras_and_extensions_)) {
6494 model->samplers.emplace_back(std::move(sampler));
6505 bool success = ForEachInArray(v,
"cameras", [&](
const detail::json &o) {
6506 if (!detail::IsObject(o)) {
6508 (*err) +=
"`cameras' does not contain an JSON object.";
6513 if (!ParseCamera(&camera, err, o,
6514 store_original_json_for_extras_and_extensions_)) {
6518 model->cameras.emplace_back(std::move(camera));
6528 ParseExtrasAndExtensions(model, err, v,
6529 store_original_json_for_extras_and_extensions_);
6533 detail::json_const_iterator rootIt;
6534 if (detail::FindMember(v,
"extensions", rootIt) &&
6535 detail::IsObject(detail::GetValue(rootIt))) {
6538 detail::json_const_iterator it(detail::ObjectBegin(root));
6539 detail::json_const_iterator itEnd(detail::ObjectEnd(root));
6540 for (; it != itEnd; ++it) {
6542 std::string
key(detail::GetKey(it));
6543 if ((key ==
"KHR_lights_punctual") &&
6544 detail::IsObject(detail::GetValue(it))) {
6546 detail::json_const_iterator itLight;
6547 if (detail::FindMember(
object,
"lights", itLight)) {
6548 const detail::json &lights = detail::GetValue(itLight);
6549 if (!detail::IsArray(lights)) {
6553 auto arrayIt(detail::ArrayBegin(lights));
6554 auto arrayItEnd(detail::ArrayEnd(lights));
6555 for (; arrayIt != arrayItEnd; ++arrayIt) {
6557 if (!ParseLight(&light, err, *arrayIt,
6558 store_original_json_for_extras_and_extensions_)) {
6561 model->lights.emplace_back(std::move(light));
6566 if ((key ==
"KHR_audio") && detail::IsObject(detail::GetValue(it))) {
6568 detail::json_const_iterator itKhrAudio;
6569 if (detail::FindMember(
object,
"emitters", itKhrAudio)) {
6570 const detail::json &emitters = detail::GetValue(itKhrAudio);
6571 if (!detail::IsArray(emitters)) {
6575 auto arrayIt(detail::ArrayBegin(emitters));
6576 auto arrayItEnd(detail::ArrayEnd(emitters));
6577 for (; arrayIt != arrayItEnd; ++arrayIt) {
6578 AudioEmitter emitter;
6579 if (!ParseAudioEmitter(
6580 &emitter, err, *arrayIt,
6581 store_original_json_for_extras_and_extensions_)) {
6584 model->audioEmitters.emplace_back(std::move(emitter));
6588 if (detail::FindMember(
object,
"sources", itKhrAudio)) {
6589 const detail::json &sources = detail::GetValue(itKhrAudio);
6590 if (!detail::IsArray(sources)) {
6594 auto arrayIt(detail::ArrayBegin(sources));
6595 auto arrayItEnd(detail::ArrayEnd(sources));
6596 for (; arrayIt != arrayItEnd; ++arrayIt) {
6598 if (!ParseAudioSource(
6600 store_original_json_for_extras_and_extensions_)) {
6603 model->audioSources.emplace_back(std::move(
source));
6615 std::string *warn,
const char *str,
6616 unsigned int length,
6617 const std::string &base_dir,
6618 unsigned int check_sections) {
6620 bin_data_ =
nullptr;
6623 return LoadFromString(model, err, warn, str, length, base_dir,
6628 std::string *warn,
const std::string &filename,
6629 unsigned int check_sections) {
6630 std::stringstream ss;
6632 if (fs.ReadWholeFile ==
nullptr) {
6634 ss <<
"Failed to read file: " << filename
6635 <<
": one or more FS callback not set" << std::endl;
6642 std::vector<unsigned char> data;
6643 std::string fileerr;
6644 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
6646 ss <<
"Failed to read file: " << filename <<
": " << fileerr << std::endl;
6653 size_t sz = data.size();
6656 (*err) =
"Empty file.";
6661 std::string basedir = GetBaseDir(filename);
6663 bool ret = LoadASCIIFromString(
6664 model, err, warn,
reinterpret_cast<const char *
>(&data.at(0)),
6665 static_cast<unsigned int>(data.size()), basedir, check_sections);
6672 const unsigned char *bytes,
6674 const std::string &base_dir,
6675 unsigned int check_sections) {
6678 (*err) =
"Too short data size for glTF Binary.";
6683 if (bytes[0] ==
'g' && bytes[1] ==
'l' && bytes[2] ==
'T' &&
6688 (*err) =
"Invalid magic.";
6693 unsigned int version;
6694 unsigned int length;
6695 unsigned int chunk0_length;
6696 unsigned int chunk0_format;
6698 memcpy(&version, bytes + 4, 4);
6700 memcpy(&length, bytes + 8, 4);
6702 memcpy(&chunk0_length, bytes + 12, 4);
6703 swap4(&chunk0_length);
6704 memcpy(&chunk0_format, bytes + 16, 4);
6705 swap4(&chunk0_format);
6715 uint64_t header_and_json_size = 20ull + uint64_t(chunk0_length);
6717 if (header_and_json_size > (std::numeric_limits<uint32_t>::max)()) {
6720 (*err) =
"Invalid glTF binary. GLB data exceeds 4GB.";
6725 if ((header_and_json_size > uint64_t(size)) || (chunk0_length < 1) ||
6726 (length > size) || (header_and_json_size > uint64_t(length)) ||
6727 (chunk0_format != 0x4E4F534A)) {
6729 (*err) =
"Invalid glTF binary.";
6737 if ((header_and_json_size % 4) != 0) {
6739 (*err) =
"JSON Chunk end does not aligned to a 4-byte boundary.";
6751 if (header_and_json_size == uint64_t(length)) {
6752 bin_data_ =
nullptr;
6762 if ((header_and_json_size + 8ull) > uint64_t(length)) {
6765 "Insufficient storage space for Chunk1(BIN data). At least Chunk1 "
6766 "Must have 8 or more bytes, but got " +
6767 std::to_string((header_and_json_size + 8ull) - uint64_t(length)) +
6773 unsigned int chunk1_length{0};
6774 unsigned int chunk1_format{0};
6775 memcpy(&chunk1_length, bytes + header_and_json_size,
6777 swap4(&chunk1_length);
6778 memcpy(&chunk1_format, bytes + header_and_json_size + 4, 4);
6779 swap4(&chunk1_format);
6781 if (chunk1_format != 0x004e4942) {
6783 (*err) =
"Invalid chunkType for Chunk1.";
6788 if (chunk1_length == 0) {
6790 if (header_and_json_size + 8 > uint64_t(length)) {
6792 (*err) =
"BIN Chunk header location exceeds the GLB size.";
6797 bin_data_ =
nullptr;
6804 if (chunk1_length < 4) {
6806 (*err) =
"Insufficient Chunk1(BIN) data size.";
6811 if ((chunk1_length % 4) != 0) {
6814 (*warn) +=
"BIN Chunk end is not aligned to a 4-byte boundary.\n";
6819 (*err) =
"BIN Chunk end is not aligned to a 4-byte boundary.";
6826 if (uint64_t(chunk1_length) + header_and_json_size + 8 > uint64_t(length)) {
6828 (*err) =
"BIN Chunk data length exceeds the GLB size.";
6833 bin_data_ = bytes + header_and_json_size +
6837 bin_size_ = size_t(chunk1_length);
6842 bool ret = LoadFromString(model, err, warn,
6843 reinterpret_cast<const char *
>(&bytes[20]),
6844 chunk0_length, base_dir, check_sections);
6854 const std::string &filename,
6855 unsigned int check_sections) {
6856 std::stringstream ss;
6858 if (fs.ReadWholeFile ==
nullptr) {
6860 ss <<
"Failed to read file: " << filename
6861 <<
": one or more FS callback not set" << std::endl;
6868 std::vector<unsigned char> data;
6869 std::string fileerr;
6870 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
6872 ss <<
"Failed to read file: " << filename <<
": " << fileerr << std::endl;
6879 std::string basedir = GetBaseDir(filename);
6881 bool ret = LoadBinaryFromMemory(model, err, warn, &data.at(0),
6882 static_cast<unsigned int>(data.size()),
6883 basedir, check_sections);
6893 #ifdef TINYGLTF_USE_RAPIDJSON
6901 #ifdef TINYGLTF_USE_RAPIDJSON
6902 dest.CopyFrom(
src, detail::GetAllocator());
6909 #ifdef TINYGLTF_USE_RAPIDJSON
6910 if (!o.IsObject()) {
6918 detail::json_const_iterator it;
6919 if (detail::FindMember(o, key, it)) {
6920 o[
key] = std::move(value);
6922 o.AddMember(
detail::json(key, detail::GetAllocator()), std::move(value),
6923 detail::GetAllocator());
6926 o[
key] = std::move(value);
6931 #ifdef TINYGLTF_USE_RAPIDJSON
6932 o.PushBack(std::move(value), detail::GetAllocator());
6934 o.push_back(std::move(value));
6939 #ifdef TINYGLTF_USE_RAPIDJSON
6947 #ifdef TINYGLTF_USE_RAPIDJSON
6955 #ifdef TINYGLTF_USE_RAPIDJSON
6957 o.Reserve(
static_cast<rapidjson::SizeType
>(
s), detail::GetAllocator());
6966 template <
typename T>
6967 static void SerializeNumberProperty(
const std::string &key, T number,
6975 #ifdef TINYGLTF_USE_RAPIDJSON
6977 void SerializeNumberProperty(
const std::string &key,
size_t number,
6979 detail::JsonAddMember(obj,
key.c_str(),
6984 template <
typename T>
6985 static void SerializeNumberArrayProperty(
const std::string &key,
6986 const std::vector<T> &value,
6988 if (
value.empty())
return;
6991 detail::JsonReserveArray(ary,
value.size());
6992 for (
const auto &
s : value) {
6995 detail::JsonAddMember(obj,
key.c_str(), std::move(ary));
6998 static void SerializeStringProperty(
const std::string &key,
6999 const std::string &value,
7001 detail::JsonAddMember(obj,
key.c_str(),
7002 detail::JsonFromString(
value.c_str()));
7005 static void SerializeStringArrayProperty(
const std::string &key,
7006 const std::vector<std::string> &value,
7009 detail::JsonReserveArray(ary,
value.size());
7010 for (
auto &
s : value) {
7011 detail::JsonPushBack(ary, detail::JsonFromString(
s.c_str()));
7013 detail::JsonAddMember(obj,
key.c_str(), std::move(ary));
7016 static bool ValueToJson(
const Value &value,
detail::json *ret) {
7018 #ifdef TINYGLTF_USE_RAPIDJSON
7019 switch (
value.Type()) {
7021 obj.SetDouble(
value.Get<
double>());
7024 obj.SetInt(
value.Get<
int>());
7027 obj.SetBool(
value.Get<
bool>());
7030 obj.SetString(
value.Get<std::string>().c_str(), detail::GetAllocator());
7034 obj.Reserve(
static_cast<rapidjson::SizeType
>(
value.ArrayLen()),
7035 detail::GetAllocator());
7036 for (
unsigned int i = 0;
i <
value.ArrayLen(); ++
i) {
7037 Value elementValue =
value.Get(
int(
i));
7039 if (ValueToJson(
value.Get(
int(
i)), &elementJson))
7040 obj.PushBack(std::move(elementJson), detail::GetAllocator());
7052 for (
auto &it : objMap) {
7054 if (ValueToJson(it.second, &elementJson)) {
7055 obj.AddMember(
detail::json(it.first.c_str(), detail::GetAllocator()),
7056 std::move(elementJson), detail::GetAllocator());
7066 switch (
value.Type()) {
7080 for (
size_t i = 0;
i <
value.ArrayLen(); ++
i) {
7081 Value elementValue =
value.Get(
i);
7083 if (ValueToJson(
value.Get(
i), &elementJson))
7084 obj.push_back(elementJson);
7095 for (
auto &it : objMap) {
7097 if (ValueToJson(it.second, &elementJson)) obj[it.first] = elementJson;
7106 if (ret) *ret = std::move(obj);
7110 static void SerializeValue(
const std::string &key,
const Value &value,
7113 if (ValueToJson(value, &ret)) {
7114 detail::JsonAddMember(obj,
key.c_str(), std::move(ret));
7118 static void SerializeGltfBufferData(
const std::vector<unsigned char> &data,
7120 std::string header =
"data:application/octet-stream;base64,";
7121 if (data.size() > 0) {
7122 std::string encodedData =
7123 base64_encode(&data[0],
static_cast<unsigned int>(data.size()));
7124 SerializeStringProperty(
"uri", header + encodedData, o);
7128 SerializeStringProperty(
"uri", header, o);
7132 static bool SerializeGltfBufferData(
const std::vector<unsigned char> &data,
7133 const std::string &binFilename) {
7135 #if defined(__GLIBCXX__) // mingw
7136 int file_descriptor = _wopen(UTF8ToWchar(binFilename).c_str(),
7137 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY, _S_IWRITE);
7138 __gnu_cxx::stdio_filebuf<char> wfile_buf(
7139 file_descriptor, std::ios_base::out | std::ios_base::binary);
7140 std::ostream output(&wfile_buf);
7141 if (!wfile_buf.is_open())
return false;
7142 #elif defined(_MSC_VER)
7143 std::ofstream output(UTF8ToWchar(binFilename).c_str(), std::ofstream::binary);
7144 if (!output.is_open())
return false;
7146 std::ofstream output(binFilename.c_str(), std::ofstream::binary);
7147 if (!output.is_open())
return false;
7150 std::ofstream output(binFilename.c_str(), std::ofstream::binary);
7151 if (!output.is_open())
return false;
7153 if (data.size() > 0) {
7154 output.write(
reinterpret_cast<const char *
>(&data[0]),
7155 std::streamsize(data.size()));
7164 #if 0 // FIXME(syoyo): not used. will be removed in the future release.
7166 for (ParameterMap::iterator paramIt = param.begin(); paramIt != param.end();
7168 if (paramIt->second.number_array.size()) {
7169 SerializeNumberArrayProperty<double>(paramIt->first,
7170 paramIt->second.number_array, o);
7171 }
else if (paramIt->second.json_double_value.size()) {
7173 for (std::map<std::string, double>::iterator it =
7174 paramIt->second.json_double_value.begin();
7175 it != paramIt->second.json_double_value.end(); ++it) {
7176 if (it->first ==
"index") {
7177 json_double_value[it->first] = paramIt->second.TextureIndex();
7179 json_double_value[it->first] = it->second;
7183 o[paramIt->first] = json_double_value;
7184 }
else if (!paramIt->second.string_value.empty()) {
7185 SerializeStringProperty(paramIt->first, paramIt->second.string_value, o);
7186 }
else if (paramIt->second.has_number_value) {
7187 o[paramIt->first] = paramIt->second.number_value;
7189 o[paramIt->first] = paramIt->second.bool_value;
7195 static void SerializeExtensionMap(
const ExtensionMap &extensions,
7197 if (!extensions.size())
return;
7200 for (ExtensionMap::const_iterator extIt = extensions.begin();
7201 extIt != extensions.end(); ++extIt) {
7205 if (ValueToJson(extIt->second, &ret)) {
7206 isNull = detail::JsonIsNull(ret);
7207 detail::JsonAddMember(extMap, extIt->first.c_str(), std::move(ret));
7210 if (!(extIt->first.empty())) {
7214 detail::JsonSetObject(empty);
7215 detail::JsonAddMember(extMap, extIt->first.c_str(), std::move(empty));
7219 detail::JsonAddMember(o,
"extensions", std::move(extMap));
7222 static void SerializeExtras(
const Value &extras,
detail::json &o) {
7223 if (extras.Type() !=
NULL_TYPE) SerializeValue(
"extras", extras, o);
7226 template <
typename GltfType>
7227 void SerializeExtrasAndExtensions(
const GltfType &obj,
detail::json &o) {
7228 SerializeExtensionMap(obj.extensions, o);
7229 SerializeExtras(obj.extras, o);
7232 static void SerializeGltfAccessor(
const Accessor &accessor,
detail::json &o) {
7233 if (accessor.bufferView >= 0)
7234 SerializeNumberProperty<int>(
"bufferView", accessor.bufferView, o);
7236 if (accessor.byteOffset != 0)
7237 SerializeNumberProperty<size_t>(
"byteOffset", accessor.byteOffset, o);
7239 SerializeNumberProperty<int>(
"componentType", accessor.componentType, o);
7240 SerializeNumberProperty<size_t>(
"count", accessor.count, o);
7244 SerializeNumberArrayProperty<double>(
"min", accessor.minValues, o);
7245 SerializeNumberArrayProperty<double>(
"max", accessor.maxValues, o);
7250 std::vector<int> values;
7251 std::transform(accessor.minValues.begin(), accessor.minValues.end(),
7252 std::back_inserter(values),
7253 [](
double v) { return static_cast<int>(v); });
7255 SerializeNumberArrayProperty<int>(
"min", values, o);
7259 std::vector<int> values;
7260 std::transform(accessor.maxValues.begin(), accessor.maxValues.end(),
7261 std::back_inserter(values),
7262 [](
double v) { return static_cast<int>(v); });
7264 SerializeNumberArrayProperty<int>(
"max", values, o);
7268 if (accessor.normalized)
7269 SerializeValue(
"normalized", Value(accessor.normalized), o);
7271 switch (accessor.type) {
7295 SerializeStringProperty(
"type",
type, o);
7296 if (!accessor.name.empty()) SerializeStringProperty(
"name", accessor.name, o);
7298 SerializeExtrasAndExtensions(accessor, o);
7301 if (accessor.sparse.isSparse) {
7303 SerializeNumberProperty<int>(
"count", accessor.sparse.count, sparse);
7306 SerializeNumberProperty<int>(
"bufferView",
7307 accessor.sparse.indices.bufferView, indices);
7308 SerializeNumberProperty<size_t>(
"byteOffset",
7309 accessor.sparse.indices.byteOffset, indices);
7310 SerializeNumberProperty<int>(
7311 "componentType", accessor.sparse.indices.componentType, indices);
7312 SerializeExtrasAndExtensions(accessor.sparse.indices, indices);
7313 detail::JsonAddMember(sparse,
"indices", std::move(indices));
7317 SerializeNumberProperty<int>(
"bufferView",
7318 accessor.sparse.values.bufferView, values);
7319 SerializeNumberProperty<size_t>(
"byteOffset",
7320 accessor.sparse.values.byteOffset, values);
7321 SerializeExtrasAndExtensions(accessor.sparse.values, values);
7322 detail::JsonAddMember(sparse,
"values", std::move(values));
7324 SerializeExtrasAndExtensions(accessor.sparse, sparse);
7325 detail::JsonAddMember(o,
"sparse", std::move(sparse));
7329 static void SerializeGltfAnimationChannel(
const AnimationChannel &channel,
7331 SerializeNumberProperty(
"sampler", channel.sampler, o);
7335 if (channel.target_node >= 0) {
7336 SerializeNumberProperty(
"node", channel.target_node, target);
7339 SerializeStringProperty(
"path", channel.target_path, target);
7341 SerializeExtensionMap(channel.target_extensions, target);
7342 SerializeExtras(channel.target_extras, target);
7344 detail::JsonAddMember(o,
"target", std::move(target));
7347 SerializeExtrasAndExtensions(channel, o);
7350 static void SerializeGltfAnimationSampler(
const AnimationSampler &sampler,
7352 SerializeNumberProperty(
"input", sampler.input, o);
7353 SerializeNumberProperty(
"output", sampler.output, o);
7354 SerializeStringProperty(
"interpolation", sampler.interpolation, o);
7356 SerializeExtrasAndExtensions(sampler, o);
7359 static void SerializeGltfAnimation(
const Animation &animation,
7361 if (!animation.name.empty())
7362 SerializeStringProperty(
"name", animation.name, o);
7366 detail::JsonReserveArray(channels, animation.channels.size());
7367 for (
unsigned int i = 0;
i < animation.channels.size(); ++
i) {
7369 AnimationChannel gltfChannel = animation.channels[
i];
7370 SerializeGltfAnimationChannel(gltfChannel, channel);
7371 detail::JsonPushBack(channels, std::move(channel));
7374 detail::JsonAddMember(o,
"channels", std::move(channels));
7379 detail::JsonReserveArray(samplers, animation.samplers.size());
7380 for (
unsigned int i = 0;
i < animation.samplers.size(); ++
i) {
7382 AnimationSampler gltfSampler = animation.samplers[
i];
7383 SerializeGltfAnimationSampler(gltfSampler, sampler);
7384 detail::JsonPushBack(samplers, std::move(sampler));
7386 detail::JsonAddMember(o,
"samplers", std::move(samplers));
7389 SerializeExtrasAndExtensions(animation, o);
7392 static void SerializeGltfAsset(
const Asset &asset,
detail::json &o) {
7393 if (!asset.generator.empty()) {
7394 SerializeStringProperty(
"generator", asset.generator, o);
7397 if (!asset.copyright.empty()) {
7398 SerializeStringProperty(
"copyright", asset.copyright, o);
7401 auto version = asset.version;
7402 if (version.empty()) {
7409 SerializeStringProperty(
"version", version, o);
7411 SerializeExtrasAndExtensions(asset, o);
7414 static void SerializeGltfBufferBin(
const Buffer &buffer,
detail::json &o,
7415 std::vector<unsigned char> &binBuffer) {
7416 SerializeNumberProperty(
"byteLength", buffer.data.size(), o);
7417 binBuffer = buffer.data;
7419 if (buffer.name.size()) SerializeStringProperty(
"name", buffer.name, o);
7421 SerializeExtrasAndExtensions(buffer, o);
7424 static void SerializeGltfBuffer(
const Buffer &buffer,
detail::json &o) {
7425 SerializeNumberProperty(
"byteLength", buffer.data.size(), o);
7426 SerializeGltfBufferData(buffer.data, o);
7428 if (buffer.name.size()) SerializeStringProperty(
"name", buffer.name, o);
7430 SerializeExtrasAndExtensions(buffer, o);
7433 static bool SerializeGltfBuffer(
const Buffer &buffer,
detail::json &o,
7434 const std::string &binFilename,
7435 const std::string &binUri) {
7436 if (!SerializeGltfBufferData(buffer.data, binFilename))
return false;
7437 SerializeNumberProperty(
"byteLength", buffer.data.size(), o);
7438 SerializeStringProperty(
"uri", binUri, o);
7440 if (buffer.name.size()) SerializeStringProperty(
"name", buffer.name, o);
7442 SerializeExtrasAndExtensions(buffer, o);
7446 static void SerializeGltfBufferView(
const BufferView &bufferView,
7448 SerializeNumberProperty(
"buffer", bufferView.buffer, o);
7449 SerializeNumberProperty<size_t>(
"byteLength", bufferView.byteLength, o);
7452 if (bufferView.byteStride >= 4) {
7453 SerializeNumberProperty<size_t>(
"byteStride", bufferView.byteStride, o);
7456 if (bufferView.byteOffset > 0) {
7457 SerializeNumberProperty<size_t>(
"byteOffset", bufferView.byteOffset, o);
7462 SerializeNumberProperty(
"target", bufferView.target, o);
7464 if (bufferView.name.size()) {
7465 SerializeStringProperty(
"name", bufferView.name, o);
7468 SerializeExtrasAndExtensions(bufferView, o);
7471 static void SerializeGltfImage(
const Image &image,
const std::string &uri,
7476 SerializeStringProperty(
"mimeType", image.mimeType, o);
7477 SerializeNumberProperty<int>(
"bufferView", image.bufferView, o);
7479 SerializeStringProperty(
"uri", uri, o);
7482 if (image.name.size()) {
7483 SerializeStringProperty(
"name", image.name, o);
7486 SerializeExtrasAndExtensions(image, o);
7489 static void SerializeGltfTextureInfo(
const TextureInfo &texinfo,
7491 SerializeNumberProperty(
"index", texinfo.index, o);
7493 if (texinfo.texCoord != 0) {
7494 SerializeNumberProperty(
"texCoord", texinfo.texCoord, o);
7497 SerializeExtrasAndExtensions(texinfo, o);
7500 static void SerializeGltfNormalTextureInfo(
const NormalTextureInfo &texinfo,
7502 SerializeNumberProperty(
"index", texinfo.index, o);
7504 if (texinfo.texCoord != 0) {
7505 SerializeNumberProperty(
"texCoord", texinfo.texCoord, o);
7509 SerializeNumberProperty(
"scale", texinfo.scale, o);
7512 SerializeExtrasAndExtensions(texinfo, o);
7515 static void SerializeGltfOcclusionTextureInfo(
7516 const OcclusionTextureInfo &texinfo,
detail::json &o) {
7517 SerializeNumberProperty(
"index", texinfo.index, o);
7519 if (texinfo.texCoord != 0) {
7520 SerializeNumberProperty(
"texCoord", texinfo.texCoord, o);
7524 SerializeNumberProperty(
"strength", texinfo.strength, o);
7527 SerializeExtrasAndExtensions(texinfo, o);
7530 static void SerializeGltfPbrMetallicRoughness(
const PbrMetallicRoughness &pbr,
7532 std::vector<double> default_baseColorFactor = {1.0, 1.0, 1.0, 1.0};
7533 if (!Equals(pbr.baseColorFactor, default_baseColorFactor)) {
7534 SerializeNumberArrayProperty<double>(
"baseColorFactor", pbr.baseColorFactor,
7539 SerializeNumberProperty(
"metallicFactor", pbr.metallicFactor, o);
7543 SerializeNumberProperty(
"roughnessFactor", pbr.roughnessFactor, o);
7546 if (pbr.baseColorTexture.index > -1) {
7548 SerializeGltfTextureInfo(pbr.baseColorTexture, texinfo);
7549 detail::JsonAddMember(o,
"baseColorTexture", std::move(texinfo));
7552 if (pbr.metallicRoughnessTexture.index > -1) {
7554 SerializeGltfTextureInfo(pbr.metallicRoughnessTexture, texinfo);
7555 detail::JsonAddMember(o,
"metallicRoughnessTexture", std::move(texinfo));
7558 SerializeExtrasAndExtensions(pbr, o);
7561 static void SerializeGltfMaterial(
const Material &material,
detail::json &o) {
7562 if (material.name.size()) {
7563 SerializeStringProperty(
"name", material.name, o);
7569 SerializeNumberProperty(
"alphaCutoff", material.alphaCutoff, o);
7572 if (material.alphaMode.compare(
"OPAQUE") != 0) {
7573 SerializeStringProperty(
"alphaMode", material.alphaMode, o);
7576 if (material.doubleSided !=
false)
7577 detail::JsonAddMember(o,
"doubleSided",
detail::json(material.doubleSided));
7579 if (material.normalTexture.index > -1) {
7581 SerializeGltfNormalTextureInfo(material.normalTexture, texinfo);
7582 detail::JsonAddMember(o,
"normalTexture", std::move(texinfo));
7585 if (material.occlusionTexture.index > -1) {
7587 SerializeGltfOcclusionTextureInfo(material.occlusionTexture, texinfo);
7588 detail::JsonAddMember(o,
"occlusionTexture", std::move(texinfo));
7591 if (material.emissiveTexture.index > -1) {
7593 SerializeGltfTextureInfo(material.emissiveTexture, texinfo);
7594 detail::JsonAddMember(o,
"emissiveTexture", std::move(texinfo));
7597 std::vector<double> default_emissiveFactor = {0.0, 0.0, 0.0};
7598 if (!Equals(material.emissiveFactor, default_emissiveFactor)) {
7599 SerializeNumberArrayProperty<double>(
"emissiveFactor",
7600 material.emissiveFactor, o);
7605 SerializeGltfPbrMetallicRoughness(material.pbrMetallicRoughness,
7606 pbrMetallicRoughness);
7613 if (!detail::JsonIsNull(pbrMetallicRoughness)) {
7614 detail::JsonAddMember(o,
"pbrMetallicRoughness",
7615 std::move(pbrMetallicRoughness));
7619 #if 0 // legacy way. just for the record.
7620 if (material.values.size()) {
7622 SerializeParameterMap(material.values, pbrMetallicRoughness);
7623 detail::JsonAddMember(o,
"pbrMetallicRoughness", std::move(pbrMetallicRoughness));
7626 SerializeParameterMap(material.additionalValues, o);
7629 SerializeExtrasAndExtensions(material, o);
7632 if (!material.lods.empty()) {
7633 detail::json_iterator it;
7634 if (!detail::FindMember(o,
"extensions", it)) {
7636 detail::JsonSetObject(extensions);
7637 detail::JsonAddMember(o,
"extensions", std::move(extensions));
7638 detail::FindMember(o,
"extensions", it);
7640 auto &extensions = detail::GetValue(it);
7641 if (!detail::FindMember(extensions,
"MSFT_lod", it)) {
7643 detail::JsonSetObject(lod);
7644 detail::JsonAddMember(extensions,
"MSFT_lod", std::move(lod));
7645 detail::FindMember(extensions,
"MSFT_lod", it);
7647 SerializeNumberArrayProperty<int>(
"ids", material.lods, detail::GetValue(it));
7649 detail::json_iterator ext_it;
7650 if (detail::FindMember(o,
"extensions", ext_it)) {
7651 auto &extensions = detail::GetValue(ext_it);
7652 detail::json_iterator lp_it;
7653 if (detail::FindMember(extensions,
"MSFT_lod", lp_it)) {
7654 detail::Erase(extensions, lp_it);
7656 if (detail::IsEmpty(extensions)) {
7657 detail::Erase(o, ext_it);
7663 static void SerializeGltfMesh(
const Mesh &mesh,
detail::json &o) {
7665 detail::JsonReserveArray(primitives, mesh.primitives.size());
7666 for (
unsigned int i = 0;
i < mesh.primitives.size(); ++
i) {
7668 const Primitive &gltfPrimitive = mesh.primitives[
i];
7671 for (
auto attrIt = gltfPrimitive.attributes.begin();
7672 attrIt != gltfPrimitive.attributes.end(); ++attrIt) {
7673 SerializeNumberProperty<int>(attrIt->first, attrIt->second, attributes);
7676 detail::JsonAddMember(primitive,
"attributes", std::move(attributes));
7680 if (gltfPrimitive.indices > -1) {
7681 SerializeNumberProperty<int>(
"indices", gltfPrimitive.indices, primitive);
7684 if (gltfPrimitive.material > -1) {
7685 SerializeNumberProperty<int>(
"material", gltfPrimitive.material,
7688 SerializeNumberProperty<int>(
"mode", gltfPrimitive.mode, primitive);
7691 if (gltfPrimitive.targets.size()) {
7693 detail::JsonReserveArray(targets, gltfPrimitive.targets.size());
7694 for (
unsigned int k = 0; k < gltfPrimitive.targets.size(); ++k) {
7696 std::map<std::string, int> targetData = gltfPrimitive.targets[k];
7697 for (std::map<std::string, int>::iterator attrIt = targetData.begin();
7698 attrIt != targetData.end(); ++attrIt) {
7699 SerializeNumberProperty<int>(attrIt->first, attrIt->second,
7702 detail::JsonPushBack(targets, std::move(targetAttributes));
7704 detail::JsonAddMember(primitive,
"targets", std::move(targets));
7707 SerializeExtrasAndExtensions(gltfPrimitive, primitive);
7709 detail::JsonPushBack(primitives, std::move(primitive));
7712 detail::JsonAddMember(o,
"primitives", std::move(primitives));
7714 if (mesh.weights.size()) {
7715 SerializeNumberArrayProperty<double>(
"weights", mesh.weights, o);
7718 if (mesh.name.size()) {
7719 SerializeStringProperty(
"name", mesh.name, o);
7722 SerializeExtrasAndExtensions(mesh, o);
7725 static void SerializeSpotLight(
const SpotLight &spot,
detail::json &o) {
7726 SerializeNumberProperty(
"innerConeAngle", spot.innerConeAngle, o);
7727 SerializeNumberProperty(
"outerConeAngle", spot.outerConeAngle, o);
7728 SerializeExtrasAndExtensions(spot, o);
7731 static void SerializeGltfLight(
const Light &light,
detail::json &o) {
7732 if (!light.name.empty()) SerializeStringProperty(
"name", light.name, o);
7733 SerializeNumberProperty(
"intensity", light.intensity, o);
7734 if (light.range > 0.0) {
7735 SerializeNumberProperty(
"range", light.range, o);
7737 SerializeNumberArrayProperty(
"color", light.color, o);
7738 SerializeStringProperty(
"type", light.type, o);
7739 if (light.type ==
"spot") {
7741 SerializeSpotLight(light.spot, spot);
7742 detail::JsonAddMember(o,
"spot", std::move(spot));
7744 SerializeExtrasAndExtensions(light, o);
7747 static void SerializeGltfPositionalEmitter(
const PositionalEmitter &positional,
7750 SerializeNumberProperty(
"coneInnerAngle", positional.coneInnerAngle, o);
7752 SerializeNumberProperty(
"coneOuterAngle", positional.coneOuterAngle, o);
7753 if (positional.coneOuterGain > 0.0)
7754 SerializeNumberProperty(
"coneOuterGain", positional.coneOuterGain, o);
7756 SerializeNumberProperty(
"maxDistance", positional.maxDistance, o);
7758 SerializeNumberProperty(
"refDistance", positional.refDistance, o);
7760 SerializeNumberProperty(
"rolloffFactor", positional.rolloffFactor, o);
7762 SerializeExtrasAndExtensions(positional, o);
7765 static void SerializeGltfAudioEmitter(
const AudioEmitter &emitter,
7767 if (!emitter.name.empty()) SerializeStringProperty(
"name", emitter.name, o);
7769 SerializeNumberProperty(
"gain", emitter.gain, o);
7770 if (emitter.loop) SerializeNumberProperty(
"loop", emitter.loop, o);
7771 if (emitter.playing) SerializeNumberProperty(
"playing", emitter.playing, o);
7772 if (!emitter.type.empty()) SerializeStringProperty(
"type", emitter.type, o);
7773 if (!emitter.distanceModel.empty())
7774 SerializeStringProperty(
"distanceModel", emitter.distanceModel, o);
7775 if (emitter.type ==
"positional") {
7777 SerializeGltfPositionalEmitter(emitter.positional, positional);
7778 detail::JsonAddMember(o,
"positional", std::move(positional));
7780 SerializeNumberProperty(
"source", emitter.source, o);
7781 SerializeExtrasAndExtensions(emitter, o);
7784 static void SerializeGltfAudioSource(
const AudioSource &
source,
7788 std::string mimeType;
7791 if (!
source.name.empty()) SerializeStringProperty(
"name",
source.name, o);
7792 if (
source.uri.empty()) {
7793 SerializeStringProperty(
"mimeType",
source.mimeType, o);
7794 SerializeNumberProperty<int>(
"bufferView",
source.bufferView, o);
7796 SerializeStringProperty(
"uri",
source.uri, o);
7798 SerializeExtrasAndExtensions(
source, o);
7801 static void SerializeGltfNode(
const Node &node,
detail::json &o) {
7802 if (node.translation.size() > 0) {
7803 SerializeNumberArrayProperty<double>(
"translation", node.translation, o);
7805 if (node.rotation.size() > 0) {
7806 SerializeNumberArrayProperty<double>(
"rotation", node.rotation, o);
7808 if (node.scale.size() > 0) {
7809 SerializeNumberArrayProperty<double>(
"scale", node.scale, o);
7811 if (node.matrix.size() > 0) {
7812 SerializeNumberArrayProperty<double>(
"matrix", node.matrix, o);
7814 if (node.mesh != -1) {
7815 SerializeNumberProperty<int>(
"mesh", node.mesh, o);
7818 if (node.skin != -1) {
7819 SerializeNumberProperty<int>(
"skin", node.skin, o);
7822 if (node.camera != -1) {
7823 SerializeNumberProperty<int>(
"camera", node.camera, o);
7826 if (node.weights.size() > 0) {
7827 SerializeNumberArrayProperty<double>(
"weights", node.weights, o);
7830 SerializeExtrasAndExtensions(node, o);
7837 if (node.light != -1) {
7838 detail::json_iterator it;
7839 if (!detail::FindMember(o,
"extensions", it)) {
7841 detail::JsonSetObject(extensions);
7842 detail::JsonAddMember(o,
"extensions", std::move(extensions));
7843 detail::FindMember(o,
"extensions", it);
7845 auto &extensions = detail::GetValue(it);
7846 if (!detail::FindMember(extensions,
"KHR_lights_punctual", it)) {
7848 detail::JsonSetObject(lights_punctual);
7849 detail::JsonAddMember(extensions,
"KHR_lights_punctual",
7850 std::move(lights_punctual));
7851 detail::FindMember(extensions,
"KHR_lights_punctual", it);
7853 SerializeNumberProperty(
"light", node.light, detail::GetValue(it));
7856 detail::json_iterator ext_it;
7857 if (detail::FindMember(o,
"extensions", ext_it)) {
7858 auto &extensions = detail::GetValue(ext_it);
7859 detail::json_iterator lp_it;
7860 if (detail::FindMember(extensions,
"KHR_lights_punctual", lp_it)) {
7861 detail::Erase(extensions, lp_it);
7863 if (detail::IsEmpty(extensions)) {
7864 detail::Erase(o, ext_it);
7870 if (node.emitter != -1) {
7871 detail::json_iterator it;
7872 if (!detail::FindMember(o,
"extensions", it)) {
7874 detail::JsonSetObject(extensions);
7875 detail::JsonAddMember(o,
"extensions", std::move(extensions));
7876 detail::FindMember(o,
"extensions", it);
7878 auto &extensions = detail::GetValue(it);
7879 if (!detail::FindMember(extensions,
"KHR_audio", it)) {
7881 detail::JsonSetObject(audio);
7882 detail::JsonAddMember(extensions,
"KHR_audio", std::move(audio));
7883 detail::FindMember(extensions,
"KHR_audio", it);
7885 SerializeNumberProperty(
"emitter", node.emitter, detail::GetValue(it));
7887 detail::json_iterator ext_it;
7888 if (detail::FindMember(o,
"extensions", ext_it)) {
7889 auto &extensions = detail::GetValue(ext_it);
7890 detail::json_iterator lp_it;
7891 if (detail::FindMember(extensions,
"KHR_audio", lp_it)) {
7892 detail::Erase(extensions, lp_it);
7894 if (detail::IsEmpty(extensions)) {
7895 detail::Erase(o, ext_it);
7901 if (!node.lods.empty()) {
7902 detail::json_iterator it;
7903 if (!detail::FindMember(o,
"extensions", it)) {
7905 detail::JsonSetObject(extensions);
7906 detail::JsonAddMember(o,
"extensions", std::move(extensions));
7907 detail::FindMember(o,
"extensions", it);
7909 auto &extensions = detail::GetValue(it);
7910 if (!detail::FindMember(extensions,
"MSFT_lod", it)) {
7912 detail::JsonSetObject(lod);
7913 detail::JsonAddMember(extensions,
"MSFT_lod", std::move(lod));
7914 detail::FindMember(extensions,
"MSFT_lod", it);
7916 SerializeNumberArrayProperty<int>(
"ids", node.lods, detail::GetValue(it));
7918 detail::json_iterator ext_it;
7919 if (detail::FindMember(o,
"extensions", ext_it)) {
7920 auto &extensions = detail::GetValue(ext_it);
7921 detail::json_iterator lp_it;
7922 if (detail::FindMember(extensions,
"MSFT_lod", lp_it)) {
7923 detail::Erase(extensions, lp_it);
7925 if (detail::IsEmpty(extensions)) {
7926 detail::Erase(o, ext_it);
7931 if (!node.name.empty()) SerializeStringProperty(
"name", node.name, o);
7932 SerializeNumberArrayProperty<int>(
"children", node.children, o);
7935 static void SerializeGltfSampler(
const Sampler &sampler,
detail::json &o) {
7936 if (!sampler.name.empty()) {
7937 SerializeStringProperty(
"name", sampler.name, o);
7939 if (sampler.magFilter != -1) {
7940 SerializeNumberProperty(
"magFilter", sampler.magFilter, o);
7942 if (sampler.minFilter != -1) {
7943 SerializeNumberProperty(
"minFilter", sampler.minFilter, o);
7946 SerializeNumberProperty(
"wrapS", sampler.wrapS, o);
7947 SerializeNumberProperty(
"wrapT", sampler.wrapT, o);
7949 SerializeExtrasAndExtensions(sampler, o);
7952 static void SerializeGltfOrthographicCamera(
const OrthographicCamera &camera,
7954 SerializeNumberProperty(
"zfar", camera.zfar, o);
7955 SerializeNumberProperty(
"znear", camera.znear, o);
7956 SerializeNumberProperty(
"xmag", camera.xmag, o);
7957 SerializeNumberProperty(
"ymag", camera.ymag, o);
7959 SerializeExtrasAndExtensions(camera, o);
7962 static void SerializeGltfPerspectiveCamera(
const PerspectiveCamera &camera,
7964 SerializeNumberProperty(
"zfar", camera.zfar, o);
7965 SerializeNumberProperty(
"znear", camera.znear, o);
7966 if (camera.aspectRatio > 0) {
7967 SerializeNumberProperty(
"aspectRatio", camera.aspectRatio, o);
7970 if (camera.yfov > 0) {
7971 SerializeNumberProperty(
"yfov", camera.yfov, o);
7974 SerializeExtrasAndExtensions(camera, o);
7977 static void SerializeGltfCamera(
const Camera &camera,
detail::json &o) {
7978 SerializeStringProperty(
"type", camera.type, o);
7979 if (!camera.name.empty()) {
7980 SerializeStringProperty(
"name", camera.name, o);
7983 if (camera.type.compare(
"orthographic") == 0) {
7985 SerializeGltfOrthographicCamera(camera.orthographic, orthographic);
7986 detail::JsonAddMember(o,
"orthographic", std::move(orthographic));
7987 }
else if (camera.type.compare(
"perspective") == 0) {
7989 SerializeGltfPerspectiveCamera(camera.perspective, perspective);
7990 detail::JsonAddMember(o,
"perspective", std::move(perspective));
7995 SerializeExtrasAndExtensions(camera, o);
7998 static void SerializeGltfScene(
const Scene &scene,
detail::json &o) {
7999 SerializeNumberArrayProperty<int>(
"nodes", scene.nodes, o);
8001 if (scene.name.size()) {
8002 SerializeStringProperty(
"name", scene.name, o);
8004 SerializeExtrasAndExtensions(scene, o);
8007 if (!scene.audioEmitters.empty()) {
8008 detail::json_iterator it;
8009 if (!detail::FindMember(o,
"extensions", it)) {
8011 detail::JsonSetObject(extensions);
8012 detail::JsonAddMember(o,
"extensions", std::move(extensions));
8013 detail::FindMember(o,
"extensions", it);
8015 auto &extensions = detail::GetValue(it);
8016 if (!detail::FindMember(extensions,
"KHR_audio", it)) {
8018 detail::JsonSetObject(audio);
8019 detail::JsonAddMember(extensions,
"KHR_audio", std::move(audio));
8020 detail::FindMember(o,
"KHR_audio", it);
8022 SerializeNumberArrayProperty(
"emitters", scene.audioEmitters,
8023 detail::GetValue(it));
8025 detail::json_iterator ext_it;
8026 if (detail::FindMember(o,
"extensions", ext_it)) {
8027 auto &extensions = detail::GetValue(ext_it);
8028 detail::json_iterator lp_it;
8029 if (detail::FindMember(extensions,
"KHR_audio", lp_it)) {
8030 detail::Erase(extensions, lp_it);
8032 if (detail::IsEmpty(extensions)) {
8033 detail::Erase(o, ext_it);
8039 static void SerializeGltfSkin(
const Skin &skin,
detail::json &o) {
8041 SerializeNumberArrayProperty<int>(
"joints", skin.joints, o);
8043 if (skin.inverseBindMatrices >= 0) {
8044 SerializeNumberProperty(
"inverseBindMatrices", skin.inverseBindMatrices, o);
8047 if (skin.skeleton >= 0) {
8048 SerializeNumberProperty(
"skeleton", skin.skeleton, o);
8051 if (skin.name.size()) {
8052 SerializeStringProperty(
"name", skin.name, o);
8055 SerializeExtrasAndExtensions(skin, o);
8058 static void SerializeGltfTexture(
const Texture &texture,
detail::json &o) {
8059 if (texture.sampler > -1) {
8060 SerializeNumberProperty(
"sampler", texture.sampler, o);
8062 if (texture.source > -1) {
8063 SerializeNumberProperty(
"source", texture.source, o);
8065 if (texture.name.size()) {
8066 SerializeStringProperty(
"name", texture.name, o);
8068 SerializeExtrasAndExtensions(texture, o);
8074 static void SerializeGltfModel(
const Model *model,
detail::json &o) {
8076 if (model->accessors.size()) {
8078 detail::JsonReserveArray(accessors, model->accessors.size());
8079 for (
unsigned int i = 0;
i < model->accessors.size(); ++
i) {
8081 SerializeGltfAccessor(model->accessors[
i], accessor);
8082 detail::JsonPushBack(accessors, std::move(accessor));
8084 detail::JsonAddMember(o,
"accessors", std::move(accessors));
8088 if (model->animations.size()) {
8090 detail::JsonReserveArray(animations, model->animations.size());
8091 for (
unsigned int i = 0;
i < model->animations.size(); ++
i) {
8092 if (model->animations[
i].channels.size()) {
8094 SerializeGltfAnimation(model->animations[
i], animation);
8095 detail::JsonPushBack(animations, std::move(animation));
8099 detail::JsonAddMember(o,
"animations", std::move(animations));
8104 SerializeGltfAsset(model->asset, asset);
8105 detail::JsonAddMember(o,
"asset", std::move(asset));
8108 if (model->bufferViews.size()) {
8110 detail::JsonReserveArray(bufferViews, model->bufferViews.size());
8111 for (
unsigned int i = 0;
i < model->bufferViews.size(); ++
i) {
8113 SerializeGltfBufferView(model->bufferViews[
i], bufferView);
8114 detail::JsonPushBack(bufferViews, std::move(bufferView));
8116 detail::JsonAddMember(o,
"bufferViews", std::move(bufferViews));
8120 if (model->extensionsRequired.size()) {
8121 SerializeStringArrayProperty(
"extensionsRequired",
8122 model->extensionsRequired, o);
8126 if (model->materials.size()) {
8128 detail::JsonReserveArray(materials, model->materials.size());
8129 for (
unsigned int i = 0;
i < model->materials.size(); ++
i) {
8131 SerializeGltfMaterial(model->materials[
i], material);
8133 if (detail::JsonIsNull(material)) {
8140 detail::JsonSetObject(material);
8142 detail::JsonPushBack(materials, std::move(material));
8144 detail::JsonAddMember(o,
"materials", std::move(materials));
8148 if (model->meshes.size()) {
8150 detail::JsonReserveArray(meshes, model->meshes.size());
8151 for (
unsigned int i = 0;
i < model->meshes.size(); ++
i) {
8153 SerializeGltfMesh(model->meshes[
i], mesh);
8154 detail::JsonPushBack(meshes, std::move(mesh));
8156 detail::JsonAddMember(o,
"meshes", std::move(meshes));
8160 if (model->nodes.size()) {
8162 detail::JsonReserveArray(nodes, model->nodes.size());
8163 for (
unsigned int i = 0;
i < model->nodes.size(); ++
i) {
8165 SerializeGltfNode(model->nodes[
i], node);
8167 if (detail::JsonIsNull(node)) {
8174 detail::JsonSetObject(node);
8176 detail::JsonPushBack(nodes, std::move(node));
8178 detail::JsonAddMember(o,
"nodes", std::move(nodes));
8182 if (model->defaultScene > -1) {
8183 SerializeNumberProperty<int>(
"scene", model->defaultScene, o);
8187 if (model->scenes.size()) {
8189 detail::JsonReserveArray(scenes, model->scenes.size());
8190 for (
unsigned int i = 0;
i < model->scenes.size(); ++
i) {
8192 SerializeGltfScene(model->scenes[
i], currentScene);
8193 if (detail::JsonIsNull(currentScene)) {
8200 detail::JsonSetObject(currentScene);
8202 detail::JsonPushBack(scenes, std::move(currentScene));
8204 detail::JsonAddMember(o,
"scenes", std::move(scenes));
8208 if (model->skins.size()) {
8210 detail::JsonReserveArray(skins, model->skins.size());
8211 for (
unsigned int i = 0;
i < model->skins.size(); ++
i) {
8213 SerializeGltfSkin(model->skins[
i], skin);
8214 detail::JsonPushBack(skins, std::move(skin));
8216 detail::JsonAddMember(o,
"skins", std::move(skins));
8220 if (model->textures.size()) {
8222 detail::JsonReserveArray(textures, model->textures.size());
8223 for (
unsigned int i = 0;
i < model->textures.size(); ++
i) {
8225 SerializeGltfTexture(model->textures[
i], texture);
8226 detail::JsonPushBack(textures, std::move(texture));
8228 detail::JsonAddMember(o,
"textures", std::move(textures));
8232 if (model->samplers.size()) {
8234 detail::JsonReserveArray(samplers, model->samplers.size());
8235 for (
unsigned int i = 0;
i < model->samplers.size(); ++
i) {
8237 SerializeGltfSampler(model->samplers[
i], sampler);
8238 detail::JsonPushBack(samplers, std::move(sampler));
8240 detail::JsonAddMember(o,
"samplers", std::move(samplers));
8244 if (model->cameras.size()) {
8246 detail::JsonReserveArray(cameras, model->cameras.size());
8247 for (
unsigned int i = 0;
i < model->cameras.size(); ++
i) {
8249 SerializeGltfCamera(model->cameras[
i], camera);
8250 detail::JsonPushBack(cameras, std::move(camera));
8252 detail::JsonAddMember(o,
"cameras", std::move(cameras));
8256 SerializeExtrasAndExtensions(*model, o);
8258 auto extensionsUsed = model->extensionsUsed;
8261 if (model->lights.size()) {
8263 detail::JsonReserveArray(lights, model->lights.size());
8264 for (
unsigned int i = 0;
i < model->lights.size(); ++
i) {
8266 SerializeGltfLight(model->lights[
i], light);
8267 detail::JsonPushBack(lights, std::move(light));
8270 detail::JsonAddMember(khr_lights_cmn,
"lights", std::move(lights));
8274 detail::json_const_iterator it;
8275 if (detail::FindMember(o,
"extensions", it)) {
8276 detail::JsonAssign(ext_j, detail::GetValue(it));
8280 detail::JsonAddMember(ext_j,
"KHR_lights_punctual",
8281 std::move(khr_lights_cmn));
8283 detail::JsonAddMember(o,
"extensions", std::move(ext_j));
8287 auto has_khr_lights_punctual =
8288 std::find_if(extensionsUsed.begin(), extensionsUsed.end(),
8289 [](
const std::string &
s) {
8290 return (s.compare(
"KHR_lights_punctual") == 0);
8293 if (has_khr_lights_punctual == extensionsUsed.end()) {
8294 extensionsUsed.push_back(
"KHR_lights_punctual");
8300 if (!model->audioEmitters.empty() || !model->audioSources.empty()) {
8302 detail::JsonReserveArray(emitters, model->audioEmitters.size());
8303 for (
unsigned int i = 0;
i < model->audioEmitters.size(); ++
i) {
8305 SerializeGltfAudioEmitter(model->audioEmitters[
i], emitter);
8306 detail::JsonPushBack(emitters, std::move(emitter));
8309 detail::JsonAddMember(khr_audio_cmn,
"emitters", std::move(emitters));
8312 detail::JsonReserveArray(sources, model->audioSources.size());
8313 for (
unsigned int i = 0;
i < model->audioSources.size(); ++
i) {
8315 SerializeGltfAudioSource(model->audioSources[
i],
source);
8316 detail::JsonPushBack(sources, std::move(
source));
8318 detail::JsonAddMember(khr_audio_cmn,
"sources", std::move(sources));
8322 detail::json_const_iterator it;
8323 if (detail::FindMember(o,
"extensions", it)) {
8324 detail::JsonAssign(ext_j, detail::GetValue(it));
8328 detail::JsonAddMember(ext_j,
"KHR_audio", std::move(khr_audio_cmn));
8330 detail::JsonAddMember(o,
"extensions", std::move(ext_j));
8334 auto has_khr_audio = std::find_if(
8335 extensionsUsed.begin(), extensionsUsed.end(),
8336 [](
const std::string &
s) { return (s.compare(
"KHR_audio") == 0); });
8338 if (has_khr_audio == extensionsUsed.end()) {
8339 extensionsUsed.push_back(
"KHR_audio");
8347 auto msft_lod_nodes_it = std::find_if(
8348 model->nodes.begin(), model->nodes.end(),
8349 [](
const Node& node) { return !node.lods.empty(); });
8352 auto msft_lod_materials_it = std::find_if(
8353 model->materials.begin(), model->materials.end(),
8354 [](
const Material& material) {return !material.lods.empty(); });
8358 if (msft_lod_nodes_it != model->nodes.end() || msft_lod_materials_it != model->materials.end()) {
8360 auto has_msft_lod = std::find_if(
8361 extensionsUsed.begin(), extensionsUsed.end(),
8362 [](
const std::string &
s) { return (s.compare(
"MSFT_lod") == 0); });
8365 if (has_msft_lod == extensionsUsed.end()) {
8366 extensionsUsed.push_back(
"MSFT_lod");
8371 if (extensionsUsed.size()) {
8372 SerializeStringArrayProperty(
"extensionsUsed", extensionsUsed, o);
8376 static bool WriteGltfStream(std::ostream &stream,
const std::string &content) {
8377 stream << content << std::endl;
8378 return stream.good();
8381 static bool WriteGltfFile(
const std::string &output,
8382 const std::string &content) {
8384 #if defined(_MSC_VER)
8385 std::ofstream gltfFile(UTF8ToWchar(output).c_str());
8386 #elif defined(__GLIBCXX__)
8387 int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
8388 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY, _S_IWRITE);
8389 __gnu_cxx::stdio_filebuf<char> wfile_buf(
8390 file_descriptor, std::ios_base::out | std::ios_base::binary);
8391 std::ostream gltfFile(&wfile_buf);
8392 if (!wfile_buf.is_open())
return false;
8394 std::ofstream gltfFile(output.c_str());
8395 if (!gltfFile.is_open())
return false;
8398 std::ofstream gltfFile(output.c_str());
8399 if (!gltfFile.is_open())
return false;
8401 return WriteGltfStream(gltfFile, content);
8404 static bool WriteBinaryGltfStream(std::ostream &stream,
8405 const std::string &content,
8406 const std::vector<unsigned char> &binBuffer) {
8407 const std::string header =
"glTF";
8408 const int version = 2;
8410 const uint32_t content_size = uint32_t(content.size());
8411 const uint32_t binBuffer_size = uint32_t(binBuffer.size());
8413 const uint32_t content_padding_size =
8414 content_size % 4 == 0 ? 0 : 4 - content_size % 4;
8415 const uint32_t bin_padding_size =
8416 binBuffer_size % 4 == 0 ? 0 : 4 - binBuffer_size % 4;
8420 const uint32_t length =
8421 12 + 8 + content_size + content_padding_size +
8422 (binBuffer_size ? (8 + binBuffer_size + bin_padding_size) : 0);
8424 stream.write(header.c_str(), std::streamsize(header.size()));
8425 stream.write(
reinterpret_cast<const char *
>(&version),
sizeof(version));
8426 stream.write(
reinterpret_cast<const char *
>(&length),
sizeof(length));
8429 const uint32_t model_length = uint32_t(content.size()) + content_padding_size;
8430 const uint32_t model_format = 0x4E4F534A;
8431 stream.write(
reinterpret_cast<const char *
>(&model_length),
8432 sizeof(model_length));
8433 stream.write(
reinterpret_cast<const char *
>(&model_format),
8434 sizeof(model_format));
8435 stream.write(content.c_str(), std::streamsize(content.size()));
8438 if (content_padding_size > 0) {
8439 const std::string padding = std::string(
size_t(content_padding_size),
' ');
8440 stream.write(padding.c_str(), std::streamsize(padding.size()));
8442 if (binBuffer.size() > 0) {
8444 const uint32_t bin_length = uint32_t(binBuffer.size()) + bin_padding_size;
8445 const uint32_t bin_format = 0x004e4942;
8446 stream.write(
reinterpret_cast<const char *
>(&bin_length),
8447 sizeof(bin_length));
8448 stream.write(
reinterpret_cast<const char *
>(&bin_format),
8449 sizeof(bin_format));
8450 stream.write(
reinterpret_cast<const char *
>(binBuffer.data()),
8451 std::streamsize(binBuffer.size()));
8453 if (bin_padding_size > 0) {
8454 const std::vector<unsigned char> padding =
8455 std::vector<unsigned char>(
size_t(bin_padding_size), 0);
8456 stream.write(
reinterpret_cast<const char *
>(padding.data()),
8457 std::streamsize(padding.size()));
8462 return stream.good();
8465 static bool WriteBinaryGltfFile(
const std::string &output,
8466 const std::string &content,
8467 const std::vector<unsigned char> &binBuffer) {
8469 #if defined(_MSC_VER)
8470 std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary);
8471 #elif defined(__GLIBCXX__)
8472 int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
8473 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY, _S_IWRITE);
8474 __gnu_cxx::stdio_filebuf<char> wfile_buf(
8475 file_descriptor, std::ios_base::out | std::ios_base::binary);
8476 std::ostream gltfFile(&wfile_buf);
8478 std::ofstream gltfFile(output.c_str(), std::ios::binary);
8481 std::ofstream gltfFile(output.c_str(), std::ios::binary);
8483 return WriteBinaryGltfStream(gltfFile, content, binBuffer);
8487 bool prettyPrint =
true,
8488 bool writeBinary =
false) {
8489 detail::JsonDocument output;
8492 SerializeGltfModel(model, output);
8495 std::vector<unsigned char> binBuffer;
8496 if (model->buffers.size()) {
8498 detail::JsonReserveArray(buffers, model->buffers.size());
8499 for (
unsigned int i = 0;
i < model->buffers.size(); ++
i) {
8501 if (writeBinary &&
i == 0 && model->buffers[
i].uri.empty()) {
8502 SerializeGltfBufferBin(model->buffers[
i], buffer, binBuffer);
8504 SerializeGltfBuffer(model->buffers[
i], buffer);
8506 detail::JsonPushBack(buffers, std::move(buffer));
8508 detail::JsonAddMember(output,
"buffers", std::move(buffers));
8512 if (model->images.size()) {
8514 detail::JsonReserveArray(images, model->images.size());
8515 for (
unsigned int i = 0;
i < model->images.size(); ++
i) {
8518 std::string dummystring;
8523 if (!UpdateImageObject(model->images[
i], dummystring,
int(
i),
true,
8524 &uri_cb, &this->WriteImageData,
8525 this->write_image_user_data_, &uri)) {
8528 SerializeGltfImage(model->images[
i], uri, image);
8529 detail::JsonPushBack(images, std::move(image));
8531 detail::JsonAddMember(output,
"images", std::move(images));
8535 return WriteBinaryGltfStream(stream, detail::JsonToString(output),
8538 return WriteGltfStream(stream,
8539 detail::JsonToString(output, prettyPrint ? 2 : -1));
8545 const std::string &filename,
8546 bool embedImages =
false,
8547 bool embedBuffers =
false,
8548 bool prettyPrint =
true,
8549 bool writeBinary =
false) {
8550 detail::JsonDocument output;
8551 std::string defaultBinFilename = GetBaseFilename(filename);
8552 std::string defaultBinFileExt =
".bin";
8553 std::string::size_type pos =
8554 defaultBinFilename.rfind(
'.', defaultBinFilename.length());
8556 if (pos != std::string::npos) {
8557 defaultBinFilename = defaultBinFilename.substr(0, pos);
8559 std::string baseDir = GetBaseDir(filename);
8560 if (baseDir.empty()) {
8564 SerializeGltfModel(model, output);
8567 std::vector<std::string> usedFilenames;
8568 std::vector<unsigned char> binBuffer;
8569 if (model->buffers.size()) {
8571 detail::JsonReserveArray(buffers, model->buffers.size());
8572 for (
unsigned int i = 0;
i < model->buffers.size(); ++
i) {
8574 if (writeBinary &&
i == 0 && model->buffers[
i].uri.empty()) {
8575 SerializeGltfBufferBin(model->buffers[
i], buffer, binBuffer);
8576 }
else if (embedBuffers) {
8577 SerializeGltfBuffer(model->buffers[
i], buffer);
8579 std::string binSavePath;
8580 std::string binFilename;
8582 if (!model->buffers[
i].uri.empty() &&
8584 binUri = model->buffers[
i].uri;
8585 if (!uri_cb.decode(binUri, &binFilename, uri_cb.user_data)) {
8589 binFilename = defaultBinFilename + defaultBinFileExt;
8594 for (
const std::string &usedName : usedFilenames) {
8595 if (binFilename.compare(usedName) != 0)
continue;
8603 if (uri_cb.encode) {
8604 if (!uri_cb.encode(binFilename,
"buffer", &binUri,
8605 uri_cb.user_data)) {
8609 binUri = binFilename;
8612 usedFilenames.push_back(binFilename);
8613 binSavePath = JoinPath(baseDir, binFilename);
8614 if (!SerializeGltfBuffer(model->buffers[
i], buffer, binSavePath,
8619 detail::JsonPushBack(buffers, std::move(buffer));
8621 detail::JsonAddMember(output,
"buffers", std::move(buffers));
8625 if (model->images.size()) {
8627 detail::JsonReserveArray(images, model->images.size());
8628 for (
unsigned int i = 0;
i < model->images.size(); ++
i) {
8632 if (!UpdateImageObject(model->images[
i], baseDir,
int(
i), embedImages,
8633 &uri_cb, &this->WriteImageData,
8634 this->write_image_user_data_, &uri)) {
8637 SerializeGltfImage(model->images[
i], uri, image);
8638 detail::JsonPushBack(images, std::move(image));
8640 detail::JsonAddMember(output,
"images", std::move(images));
8644 return WriteBinaryGltfFile(filename, detail::JsonToString(output),
8647 return WriteGltfFile(filename,
8648 detail::JsonToString(output, (prettyPrint ? 2 : -1)));
8655 #pragma clang diagnostic pop
8658 #endif // TINYGLTF_IMPLEMENTATION