blob: 1e757edb297bac1a9717fabe85a4ccdd18ba3c1f [file] [log] [blame]
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001//
Syoyo Fujita9a1ea7e2017-06-20 02:17:28 +09002// Header-only tiny glTF 2.0 loader and serializer.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003//
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004//
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09005// The MIT License (MIT)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09006//
Syoyo Fujitab702de72021-03-02 19:08:29 +09007// Copyright (c) 2015 - Present Syoyo Fujita, Aurélien Chatelain and many
Syoyo Fujita5b407452017-06-04 17:42:41 +09008// contributors.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09009//
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090010// Permission is hereby granted, free of charge, to any person obtaining a copy
11// of this software and associated documentation files (the "Software"), to deal
12// in the Software without restriction, including without limitation the rights
13// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14// copies of the Software, and to permit persons to whom the Software is
15// furnished to do so, subject to the following conditions:
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090016//
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090017// The above copyright notice and this permission notice shall be included in
18// all copies or substantial portions of the Software.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090019//
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090020// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26// THE SOFTWARE.
27
Syoyo Fujita5a6df342023-06-04 19:07:00 +090028// Version: - v2.8.10
29// See https://github.com/syoyo/tinygltf/releases for release history.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090030//
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090031// Tiny glTF loader is using following third party libraries:
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090032//
Syoyo Fujita2e21be72017-11-05 17:13:01 +090033// - jsonhpp: C++ JSON library.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090034// - base64: base64 decode/encode library.
35// - stb_image: Image loading library.
36//
Syoyo Fujita2840a0e2017-06-21 02:52:11 +090037#ifndef TINY_GLTF_H_
38#define TINY_GLTF_H_
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090039
Syoyo Fujitad42767e2018-03-15 21:52:00 -050040#include <array>
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090041#include <cassert>
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +090042#include <cmath> // std::fabs
Syoyo Fujitad42767e2018-03-15 21:52:00 -050043#include <cstdint>
Selmar Kok31cb7f92018-10-03 15:39:05 +020044#include <cstdlib>
Syoyo Fujita641b3cc2018-10-04 15:43:33 +090045#include <cstring>
Syoyo Fujita046400b2019-07-24 19:26:48 +090046#include <limits>
Syoyo Fujita523c9bf2016-04-20 14:06:56 +090047#include <map>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090048#include <string>
49#include <vector>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090050
zbendefy69eeea12022-09-05 23:54:57 +020051//Auto-detect C++14 standard version
52#if !defined(TINYGLTF_USE_CPP14) && defined(__cplusplus) && (__cplusplus >= 201402L)
53#define TINYGLTF_USE_CPP14
54#endif
55
Sascha Willems5f9cb242018-12-28 20:53:41 +010056#ifdef __ANDROID__
57#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
58#include <android/asset_manager.h>
59#endif
60#endif
61
Selmar Kok79e3df22019-10-29 16:22:07 +010062#ifdef __GNUC__
63#if (__GNUC__ < 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ <= 8))
Selmar Kokb74fade2019-10-29 16:09:32 +010064#define TINYGLTF_NOEXCEPT
Selmar Kok79e3df22019-10-29 16:22:07 +010065#else
66#define TINYGLTF_NOEXCEPT noexcept
Selmar Kokb74fade2019-10-29 16:09:32 +010067#endif
Selmar Kok79e3df22019-10-29 16:22:07 +010068#else
69#define TINYGLTF_NOEXCEPT noexcept
70#endif
71
Syoyo Fujita6e08b172019-10-30 17:25:38 +090072#define DEFAULT_METHODS(x) \
73 ~x() = default; \
74 x(const x &) = default; \
75 x(x &&) TINYGLTF_NOEXCEPT = default; \
76 x &operator=(const x &) = default; \
77 x &operator=(x &&) TINYGLTF_NOEXCEPT = default;
Selmar Kokb74fade2019-10-29 16:09:32 +010078
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090079namespace tinygltf {
80
81#define TINYGLTF_MODE_POINTS (0)
82#define TINYGLTF_MODE_LINE (1)
83#define TINYGLTF_MODE_LINE_LOOP (2)
Thomas Tissot6c4a0062019-01-30 13:10:51 +010084#define TINYGLTF_MODE_LINE_STRIP (3)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090085#define TINYGLTF_MODE_TRIANGLES (4)
86#define TINYGLTF_MODE_TRIANGLE_STRIP (5)
87#define TINYGLTF_MODE_TRIANGLE_FAN (6)
88
89#define TINYGLTF_COMPONENT_TYPE_BYTE (5120)
90#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE (5121)
91#define TINYGLTF_COMPONENT_TYPE_SHORT (5122)
92#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT (5123)
93#define TINYGLTF_COMPONENT_TYPE_INT (5124)
94#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125)
95#define TINYGLTF_COMPONENT_TYPE_FLOAT (5126)
Syoyo Fujita52ff00a2022-08-16 20:08:45 +090096#define TINYGLTF_COMPONENT_TYPE_DOUBLE \
97 (5130) // OpenGL double type. Note that some of glTF 2.0 validator does not
98 // support double type even the schema seems allow any value of
99 // integer:
100 // https://github.com/KhronosGroup/glTF/blob/b9884a2fd45130b4d673dd6c8a706ee21ee5c5f7/specification/2.0/schema/accessor.schema.json#L22
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900101
Syoyo Fujitac2615632016-06-19 21:56:06 +0900102#define TINYGLTF_TEXTURE_FILTER_NEAREST (9728)
103#define TINYGLTF_TEXTURE_FILTER_LINEAR (9729)
104#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST (9984)
105#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST (9985)
106#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR (9986)
107#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR (9987)
108
Cemalettin Dervis246d8662017-12-07 20:29:51 +0100109#define TINYGLTF_TEXTURE_WRAP_REPEAT (10497)
Syoyo Fujitac2615632016-06-19 21:56:06 +0900110#define TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE (33071)
Luke San Antonio Bialecki904612c2016-08-19 17:28:03 -0400111#define TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT (33648)
Syoyo Fujitac2615632016-06-19 21:56:06 +0900112
Luke San Antoniocdf4cb72016-06-14 21:32:11 -0400113// Redeclarations of the above for technique.parameters.
114#define TINYGLTF_PARAMETER_TYPE_BYTE (5120)
115#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE (5121)
116#define TINYGLTF_PARAMETER_TYPE_SHORT (5122)
117#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT (5123)
118#define TINYGLTF_PARAMETER_TYPE_INT (5124)
119#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT (5125)
120#define TINYGLTF_PARAMETER_TYPE_FLOAT (5126)
121
122#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC2 (35664)
123#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC3 (35665)
124#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC4 (35666)
125
126#define TINYGLTF_PARAMETER_TYPE_INT_VEC2 (35667)
127#define TINYGLTF_PARAMETER_TYPE_INT_VEC3 (35668)
128#define TINYGLTF_PARAMETER_TYPE_INT_VEC4 (35669)
129
130#define TINYGLTF_PARAMETER_TYPE_BOOL (35670)
131#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC2 (35671)
132#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC3 (35672)
133#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC4 (35673)
134
135#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT2 (35674)
136#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT3 (35675)
137#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT4 (35676)
138
139#define TINYGLTF_PARAMETER_TYPE_SAMPLER_2D (35678)
140
141// End parameter types
142
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900143#define TINYGLTF_TYPE_VEC2 (2)
144#define TINYGLTF_TYPE_VEC3 (3)
145#define TINYGLTF_TYPE_VEC4 (4)
146#define TINYGLTF_TYPE_MAT2 (32 + 2)
147#define TINYGLTF_TYPE_MAT3 (32 + 3)
148#define TINYGLTF_TYPE_MAT4 (32 + 4)
149#define TINYGLTF_TYPE_SCALAR (64 + 1)
150#define TINYGLTF_TYPE_VECTOR (64 + 4)
151#define TINYGLTF_TYPE_MATRIX (64 + 16)
152
153#define TINYGLTF_IMAGE_FORMAT_JPEG (0)
154#define TINYGLTF_IMAGE_FORMAT_PNG (1)
155#define TINYGLTF_IMAGE_FORMAT_BMP (2)
156#define TINYGLTF_IMAGE_FORMAT_GIF (3)
157
Luke San Antonio6d616f52016-06-23 14:09:23 -0400158#define TINYGLTF_TEXTURE_FORMAT_ALPHA (6406)
159#define TINYGLTF_TEXTURE_FORMAT_RGB (6407)
Syoyo Fujitabde70212016-02-07 17:38:17 +0900160#define TINYGLTF_TEXTURE_FORMAT_RGBA (6408)
Luke San Antonio6d616f52016-06-23 14:09:23 -0400161#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE (6409)
162#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE_ALPHA (6410)
163
Syoyo Fujitabde70212016-02-07 17:38:17 +0900164#define TINYGLTF_TEXTURE_TARGET_TEXTURE2D (3553)
165#define TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE (5121)
166
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900167#define TINYGLTF_TARGET_ARRAY_BUFFER (34962)
168#define TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER (34963)
169
Luke San Antoniocdf4cb72016-06-14 21:32:11 -0400170#define TINYGLTF_SHADER_TYPE_VERTEX_SHADER (35633)
171#define TINYGLTF_SHADER_TYPE_FRAGMENT_SHADER (35632)
172
Selmar Kok31cb7f92018-10-03 15:39:05 +0200173#define TINYGLTF_DOUBLE_EPS (1.e-12)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900174#define TINYGLTF_DOUBLE_EQUAL(a, b) (std::fabs((b) - (a)) < TINYGLTF_DOUBLE_EPS)
Selmar Kok31cb7f92018-10-03 15:39:05 +0200175
Sascha Willems5f9cb242018-12-28 20:53:41 +0100176#ifdef __ANDROID__
177#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
Serdar Kocdemir264ae4c2022-10-30 22:32:59 +0000178#ifdef TINYGLTF_IMPLEMENTATION
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000179AAssetManager *asset_manager = nullptr;
Serdar Kocdemir264ae4c2022-10-30 22:32:59 +0000180#else
181extern AAssetManager *asset_manager;
182#endif
Sascha Willems5f9cb242018-12-28 20:53:41 +0100183#endif
184#endif
185
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900186typedef enum {
Idriss Chaouch425195b2021-05-20 12:11:00 +0100187 NULL_TYPE,
188 REAL_TYPE,
189 INT_TYPE,
190 BOOL_TYPE,
191 STRING_TYPE,
192 ARRAY_TYPE,
193 BINARY_TYPE,
194 OBJECT_TYPE
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900195} Type;
196
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500197static inline int32_t GetComponentSizeInBytes(uint32_t componentType) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900198 if (componentType == TINYGLTF_COMPONENT_TYPE_BYTE) {
199 return 1;
200 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
201 return 1;
202 } else if (componentType == TINYGLTF_COMPONENT_TYPE_SHORT) {
203 return 2;
204 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
205 return 2;
206 } else if (componentType == TINYGLTF_COMPONENT_TYPE_INT) {
207 return 4;
208 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) {
209 return 4;
210 } else if (componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) {
211 return 4;
212 } else if (componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) {
213 return 8;
214 } else {
imallettd9ce9eb2022-10-07 10:37:09 -0700215 // Unknown component type
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900216 return -1;
217 }
218}
219
DingboDingboDingbo83ccb9f2019-08-29 18:49:15 -0400220static inline int32_t GetNumComponentsInType(uint32_t ty) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900221 if (ty == TINYGLTF_TYPE_SCALAR) {
222 return 1;
223 } else if (ty == TINYGLTF_TYPE_VEC2) {
224 return 2;
225 } else if (ty == TINYGLTF_TYPE_VEC3) {
226 return 3;
227 } else if (ty == TINYGLTF_TYPE_VEC4) {
228 return 4;
229 } else if (ty == TINYGLTF_TYPE_MAT2) {
230 return 4;
231 } else if (ty == TINYGLTF_TYPE_MAT3) {
232 return 9;
233 } else if (ty == TINYGLTF_TYPE_MAT4) {
234 return 16;
235 } else {
imallettd9ce9eb2022-10-07 10:37:09 -0700236 // Unknown component type
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900237 return -1;
238 }
239}
240
Syoyo Fujita150f2432019-07-25 19:22:44 +0900241// TODO(syoyo): Move these functions to TinyGLTF class
Selmar Kok0d0e97e2018-08-22 14:01:57 +0200242bool IsDataURI(const std::string &in);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900243bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
244 const std::string &in, size_t reqBytes, bool checkSize);
Selmar Kok0d0e97e2018-08-22 14:01:57 +0200245
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900246#ifdef __clang__
247#pragma clang diagnostic push
248// Suppress warning for : static Value null_value
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900249#pragma clang diagnostic ignored "-Wexit-time-destructors"
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900250#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900251#endif
252
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900253// Simple class to represent JSON object
254class Value {
255 public:
256 typedef std::vector<Value> Array;
257 typedef std::map<std::string, Value> Object;
258
Syoyo Fujita046400b2019-07-24 19:26:48 +0900259 Value()
260 : type_(NULL_TYPE),
261 int_value_(0),
Syoyo Fujita150f2432019-07-25 19:22:44 +0900262 real_value_(0.0),
Syoyo Fujita046400b2019-07-24 19:26:48 +0900263 boolean_value_(false) {}
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900264
265 explicit Value(bool b) : type_(BOOL_TYPE) { boolean_value_ = b; }
Syoyo Fujitaff515702019-08-24 16:29:14 +0900266 explicit Value(int i) : type_(INT_TYPE) {
267 int_value_ = i;
268 real_value_ = i;
269 }
Syoyo Fujita150f2432019-07-25 19:22:44 +0900270 explicit Value(double n) : type_(REAL_TYPE) { real_value_ = n; }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900271 explicit Value(const std::string &s) : type_(STRING_TYPE) {
272 string_value_ = s;
273 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900274 explicit Value(std::string &&s)
275 : type_(STRING_TYPE), string_value_(std::move(s)) {}
David Siegel49caa652023-04-08 23:44:53 +0200276 explicit Value(const char *s) : type_(STRING_TYPE) {
277 string_value_ = s;
278 }
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900279 explicit Value(const unsigned char *p, size_t n) : type_(BINARY_TYPE) {
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900280 binary_value_.resize(n);
281 memcpy(binary_value_.data(), p, n);
282 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900283 explicit Value(std::vector<unsigned char> &&v) noexcept
284 : type_(BINARY_TYPE),
285 binary_value_(std::move(v)) {}
286 explicit Value(const Array &a) : type_(ARRAY_TYPE) { array_value_ = a; }
287 explicit Value(Array &&a) noexcept : type_(ARRAY_TYPE),
288 array_value_(std::move(a)) {}
jrkoonced1e14722019-08-27 11:51:02 -0500289
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900290 explicit Value(const Object &o) : type_(OBJECT_TYPE) { object_value_ = o; }
291 explicit Value(Object &&o) noexcept : type_(OBJECT_TYPE),
292 object_value_(std::move(o)) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100293
294 DEFAULT_METHODS(Value)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900295
Hill Mad1e32862021-02-20 22:30:44 -0800296 char Type() const { return static_cast<char>(type_); }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900297
298 bool IsBool() const { return (type_ == BOOL_TYPE); }
299
300 bool IsInt() const { return (type_ == INT_TYPE); }
301
Syoyo Fujita150f2432019-07-25 19:22:44 +0900302 bool IsNumber() const { return (type_ == REAL_TYPE) || (type_ == INT_TYPE); }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900303
Syoyo Fujita150f2432019-07-25 19:22:44 +0900304 bool IsReal() const { return (type_ == REAL_TYPE); }
Syoyo Fujita046400b2019-07-24 19:26:48 +0900305
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900306 bool IsString() const { return (type_ == STRING_TYPE); }
307
308 bool IsBinary() const { return (type_ == BINARY_TYPE); }
309
310 bool IsArray() const { return (type_ == ARRAY_TYPE); }
311
312 bool IsObject() const { return (type_ == OBJECT_TYPE); }
313
Syoyo Fujita150f2432019-07-25 19:22:44 +0900314 // Use this function if you want to have number value as double.
315 double GetNumberAsDouble() const {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900316 if (type_ == INT_TYPE) {
317 return double(int_value_);
Syoyo Fujita150f2432019-07-25 19:22:44 +0900318 } else {
319 return real_value_;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900320 }
Syoyo Fujita150f2432019-07-25 19:22:44 +0900321 }
Syoyo Fujita046400b2019-07-24 19:26:48 +0900322
Syoyo Fujita150f2432019-07-25 19:22:44 +0900323 // Use this function if you want to have number value as int.
Syoyo Fujitaff0a2e92020-05-11 19:07:00 +0900324 // TODO(syoyo): Support int value larger than 32 bits
325 int GetNumberAsInt() const {
Syoyo Fujita150f2432019-07-25 19:22:44 +0900326 if (type_ == REAL_TYPE) {
327 return int(real_value_);
328 } else {
329 return int_value_;
330 }
Syoyo Fujita046400b2019-07-24 19:26:48 +0900331 }
332
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900333 // Accessor
334 template <typename T>
335 const T &Get() const;
336 template <typename T>
337 T &Get();
338
339 // Lookup value from an array
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900340 const Value &Get(int idx) const {
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900341 static Value null_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900342 assert(IsArray());
343 assert(idx >= 0);
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900344 return (static_cast<size_t>(idx) < array_value_.size())
345 ? array_value_[static_cast<size_t>(idx)]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900346 : null_value;
347 }
348
349 // Lookup value from a key-value pair
350 const Value &Get(const std::string &key) const {
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900351 static Value null_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900352 assert(IsObject());
353 Object::const_iterator it = object_value_.find(key);
354 return (it != object_value_.end()) ? it->second : null_value;
355 }
356
357 size_t ArrayLen() const {
358 if (!IsArray()) return 0;
359 return array_value_.size();
360 }
361
362 // Valid only for object type.
363 bool Has(const std::string &key) const {
364 if (!IsObject()) return false;
365 Object::const_iterator it = object_value_.find(key);
366 return (it != object_value_.end()) ? true : false;
367 }
368
369 // List keys
370 std::vector<std::string> Keys() const {
371 std::vector<std::string> keys;
372 if (!IsObject()) return keys; // empty
373
374 for (Object::const_iterator it = object_value_.begin();
375 it != object_value_.end(); ++it) {
376 keys.push_back(it->first);
377 }
378
379 return keys;
380 }
381
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900382 size_t Size() const { return (IsArray() ? ArrayLen() : Keys().size()); }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900383
384 bool operator==(const tinygltf::Value &other) const;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +0000385
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900386 protected:
Syoyo Fujita046400b2019-07-24 19:26:48 +0900387 int type_ = NULL_TYPE;
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900388
Syoyo Fujita046400b2019-07-24 19:26:48 +0900389 int int_value_ = 0;
Syoyo Fujita150f2432019-07-25 19:22:44 +0900390 double real_value_ = 0.0;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900391 std::string string_value_;
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900392 std::vector<unsigned char> binary_value_;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900393 Array array_value_;
394 Object object_value_;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900395 bool boolean_value_ = false;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900396};
397
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900398#ifdef __clang__
399#pragma clang diagnostic pop
400#endif
401
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900402#define TINYGLTF_VALUE_GET(ctype, var) \
403 template <> \
404 inline const ctype &Value::Get<ctype>() const { \
405 return var; \
406 } \
407 template <> \
408 inline ctype &Value::Get<ctype>() { \
409 return var; \
410 }
411TINYGLTF_VALUE_GET(bool, boolean_value_)
Syoyo Fujita150f2432019-07-25 19:22:44 +0900412TINYGLTF_VALUE_GET(double, real_value_)
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900413TINYGLTF_VALUE_GET(int, int_value_)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900414TINYGLTF_VALUE_GET(std::string, string_value_)
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900415TINYGLTF_VALUE_GET(std::vector<unsigned char>, binary_value_)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900416TINYGLTF_VALUE_GET(Value::Array, array_value_)
417TINYGLTF_VALUE_GET(Value::Object, object_value_)
418#undef TINYGLTF_VALUE_GET
419
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900420#ifdef __clang__
421#pragma clang diagnostic push
422#pragma clang diagnostic ignored "-Wc++98-compat"
423#pragma clang diagnostic ignored "-Wpadded"
424#endif
425
imallettd9ce9eb2022-10-07 10:37:09 -0700426/// Aggregate object for representing a color
Arthur Brainville58453192018-01-08 18:25:52 +0100427using ColorValue = std::array<double, 4>;
428
Syoyo Fujita046400b2019-07-24 19:26:48 +0900429// === legacy interface ====
430// TODO(syoyo): Deprecate `Parameter` class.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500431struct Parameter {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200432 bool bool_value = false;
Ben Buzbeef6af2242018-04-25 15:13:05 -0700433 bool has_number_value = false;
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900434 std::string string_value;
435 std::vector<double> number_array;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000436 std::map<std::string, double> json_double_value;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200437 double number_value = 0.0;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900438
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500439 // context sensitive methods. depending the type of the Parameter you are
440 // accessing, these are either valid or not
441 // If this parameter represent a texture map in a material, will return the
442 // texture index
Arthur Brainville58453192018-01-08 18:25:52 +0100443
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500444 /// Return the index of a texture if this Parameter is a texture map.
445 /// Returned value is only valid if the parameter represent a texture from a
446 /// material
Arthur Brainville41fe7722018-01-08 18:32:48 +0100447 int TextureIndex() const {
Arthur Brainville58453192018-01-08 18:25:52 +0100448 const auto it = json_double_value.find("index");
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500449 if (it != std::end(json_double_value)) {
Arthur Brainville58453192018-01-08 18:25:52 +0100450 return int(it->second);
451 }
452 return -1;
453 }
454
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000455 /// Return the index of a texture coordinate set if this Parameter is a
456 /// texture map. Returned value is only valid if the parameter represent a
457 /// texture from a material
Sascha Willemseb011062019-02-23 21:15:45 +0100458 int TextureTexCoord() const {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000459 const auto it = json_double_value.find("texCoord");
460 if (it != std::end(json_double_value)) {
461 return int(it->second);
462 }
imallettd9ce9eb2022-10-07 10:37:09 -0700463 // As per the spec, if texCoord is omitted, this parameter is 0
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000464 return 0;
Sascha Willemseb011062019-02-23 21:15:45 +0100465 }
466
Christophe820ede82019-07-04 15:21:21 +0900467 /// Return the scale of a texture if this Parameter is a normal texture map.
468 /// Returned value is only valid if the parameter represent a normal texture
469 /// from a material
470 double TextureScale() const {
471 const auto it = json_double_value.find("scale");
472 if (it != std::end(json_double_value)) {
473 return it->second;
474 }
imallettd9ce9eb2022-10-07 10:37:09 -0700475 // As per the spec, if scale is omitted, this parameter is 1
Arthur Brainville8a98d982019-07-05 00:26:02 +0200476 return 1;
477 }
Arthur Brianville (Ybalrid)2a9d9de2019-07-05 00:30:47 +0200478
Arthur Brainville8a98d982019-07-05 00:26:02 +0200479 /// Return the strength of a texture if this Parameter is a an occlusion map.
480 /// Returned value is only valid if the parameter represent an occlusion map
481 /// from a material
482 double TextureStrength() const {
483 const auto it = json_double_value.find("strength");
484 if (it != std::end(json_double_value)) {
485 return it->second;
486 }
imallettd9ce9eb2022-10-07 10:37:09 -0700487 // As per the spec, if strength is omitted, this parameter is 1
Arthur Brainville8a98d982019-07-05 00:26:02 +0200488 return 1;
Christophe820ede82019-07-04 15:21:21 +0900489 }
490
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500491 /// Material factor, like the roughness or metalness of a material
492 /// Returned value is only valid if the parameter represent a texture from a
493 /// material
Ben Buzbeef6af2242018-04-25 15:13:05 -0700494 double Factor() const { return number_value; }
Arthur Brainville58453192018-01-08 18:25:52 +0100495
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500496 /// Return the color of a material
497 /// Returned value is only valid if the parameter represent a texture from a
498 /// material
Arthur Brainville95853912018-01-08 18:37:44 +0100499 ColorValue ColorFactor() const {
Arthur Brainville58453192018-01-08 18:25:52 +0100500 return {
imallettd9ce9eb2022-10-07 10:37:09 -0700501 {// this aggregate initialize the std::array object, and uses C++11 RVO.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500502 number_array[0], number_array[1], number_array[2],
503 (number_array.size() > 3 ? number_array[3] : 1.0)}};
Arthur Brainville58453192018-01-08 18:25:52 +0100504 }
Selmar Kok31cb7f92018-10-03 15:39:05 +0200505
Selmar Kokff2b1f92019-10-21 17:58:09 +0200506 Parameter() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100507 DEFAULT_METHODS(Parameter)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900508 bool operator==(const Parameter &) const;
Arthur Brainville58453192018-01-08 18:25:52 +0100509};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900510
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900511#ifdef __clang__
512#pragma clang diagnostic pop
513#endif
514
515#ifdef __clang__
516#pragma clang diagnostic push
517#pragma clang diagnostic ignored "-Wpadded"
518#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900519
Syoyo Fujitabde70212016-02-07 17:38:17 +0900520typedef std::map<std::string, Parameter> ParameterMap;
Selmar Kokfa7022f2018-04-04 18:10:20 +0200521typedef std::map<std::string, Value> ExtensionMap;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900522
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000523struct AnimationChannel {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900524 int sampler; // required
Jack Mousseau283b5522023-01-15 11:45:45 -0800525 int target_node; // optional index of the node to target (alternative
526 // target should be provided by extension)
527 std::string target_path; // required with standard values of ["translation",
528 // "rotation", "scale", "weights"]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900529 Value extras;
Selmar Kok4e2988e2019-08-16 14:08:08 +0200530 ExtensionMap extensions;
David Siegeld852f502023-06-05 23:28:05 +0200531 Value target_extras;
Selmar Kok973d9b32020-01-21 18:45:24 +0100532 ExtensionMap target_extensions;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900533
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900534 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
535 std::string extras_json_string;
536 std::string extensions_json_string;
David Siegeld852f502023-06-05 23:28:05 +0200537 std::string target_extras_json_string;
Selmar Kok973d9b32020-01-21 18:45:24 +0100538 std::string target_extensions_json_string;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900539
Syoyo Fujita5b407452017-06-04 17:42:41 +0900540 AnimationChannel() : sampler(-1), target_node(-1) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100541 DEFAULT_METHODS(AnimationChannel)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900542 bool operator==(const AnimationChannel &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000543};
544
545struct AnimationSampler {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900546 int input; // required
547 int output; // required
Arthur Brianville (Ybalrid)2a9d9de2019-07-05 00:30:47 +0200548 std::string interpolation; // "LINEAR", "STEP","CUBICSPLINE" or user defined
549 // string. default "LINEAR"
Jens Olssonb3af2f12018-06-04 10:17:49 +0200550 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900551 ExtensionMap extensions;
552
553 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
554 std::string extras_json_string;
555 std::string extensions_json_string;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000556
Syoyo Fujita5b407452017-06-04 17:42:41 +0900557 AnimationSampler() : input(-1), output(-1), interpolation("LINEAR") {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100558 DEFAULT_METHODS(AnimationSampler)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900559 bool operator==(const AnimationSampler &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000560};
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900561
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900562struct Animation {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900563 std::string name;
564 std::vector<AnimationChannel> channels;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000565 std::vector<AnimationSampler> samplers;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900566 Value extras;
Selmar Kok4e2988e2019-08-16 14:08:08 +0200567 ExtensionMap extensions;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200568
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900569 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
570 std::string extras_json_string;
571 std::string extensions_json_string;
572
Selmar Kokff2b1f92019-10-21 17:58:09 +0200573 Animation() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100574 DEFAULT_METHODS(Animation)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900575 bool operator==(const Animation &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900576};
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900577
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000578struct Skin {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900579 std::string name;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900580 int inverseBindMatrices; // required here but not in the spec
581 int skeleton; // The index of the node used as a skeleton root
582 std::vector<int> joints; // Indices of skeleton nodes
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000583
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900584 Value extras;
585 ExtensionMap extensions;
586
587 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
588 std::string extras_json_string;
589 std::string extensions_json_string;
590
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900591 Skin() {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000592 inverseBindMatrices = -1;
Aurélien Chatelain8d5e0a72017-06-19 13:52:08 +0000593 skeleton = -1;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000594 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100595 DEFAULT_METHODS(Skin)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900596 bool operator==(const Skin &) const;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000597};
598
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000599struct Sampler {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900600 std::string name;
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900601 // glTF 2.0 spec does not define default value for `minFilter` and
602 // `magFilter`. Set -1 in TinyGLTF(issue #186)
603 int minFilter =
604 -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR",
zz8a35b8f2021-05-14 13:49:33 +0800605 // "NEAREST_MIPMAP_NEAREST", "LINEAR_MIPMAP_NEAREST",
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900606 // "NEAREST_MIPMAP_LINEAR", "LINEAR_MIPMAP_LINEAR"]
607 int magFilter =
608 -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR"]
609 int wrapS =
610 TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
611 // "REPEAT"], default "REPEAT"
612 int wrapT =
613 TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
614 // "REPEAT"], default "REPEAT"
Syoyo Fujita52ff00a2022-08-16 20:08:45 +0900615 // int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; // TinyGLTF extension. currently
616 // not used.
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900617
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900618 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900619 ExtensionMap extensions;
620
621 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
622 std::string extras_json_string;
623 std::string extensions_json_string;
Syoyo Fujitac2615632016-06-19 21:56:06 +0900624
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000625 Sampler()
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900626 : minFilter(-1),
627 magFilter(-1),
timmmeh73584ba2019-01-30 18:38:46 -0800628 wrapS(TINYGLTF_TEXTURE_WRAP_REPEAT),
Syoyo Fujita2c521b32020-12-04 00:50:46 +0900629 wrapT(TINYGLTF_TEXTURE_WRAP_REPEAT) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100630 DEFAULT_METHODS(Sampler)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900631 bool operator==(const Sampler &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000632};
633
Syoyo Fujita5b407452017-06-04 17:42:41 +0900634struct Image {
Syoyo Fujitac2615632016-06-19 21:56:06 +0900635 std::string name;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900636 int width;
637 int height;
638 int component;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000639 int bits; // bit depth per channel. 8(byte), 16 or 32.
640 int pixel_type; // pixel type(TINYGLTF_COMPONENT_TYPE_***). usually
641 // UBYTE(bits = 8) or USHORT(bits = 16)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900642 std::vector<unsigned char> image;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900643 int bufferView; // (required if no uri)
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500644 std::string mimeType; // (required if no uri) ["image/jpeg", "image/png",
645 // "image/bmp", "image/gif"]
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900646 std::string uri; // (required if no mimeType) uri is not decoded(e.g.
647 // whitespace may be represented as %20)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900648 Value extras;
Syoyo Fujita3e53feb2018-09-02 15:36:17 +0900649 ExtensionMap extensions;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900650
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900651 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
652 std::string extras_json_string;
653 std::string extensions_json_string;
654
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900655 // When this flag is true, data is stored to `image` in as-is format(e.g. jpeg
656 // compressed for "image/jpeg" mime) This feature is good if you use custom
657 // image loader function. (e.g. delayed decoding of images for faster glTF
658 // parsing) Default parser for Image does not provide as-is loading feature at
659 // the moment. (You can manipulate this by providing your own LoadImageData
660 // function)
Selmar Kokfa0a9982018-10-03 15:46:23 +0200661 bool as_is;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900662
663 Image() : as_is(false) {
664 bufferView = -1;
665 width = -1;
666 height = -1;
667 component = -1;
daemyung jang1739d022020-07-03 13:27:39 +0900668 bits = -1;
669 pixel_type = -1;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900670 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100671 DEFAULT_METHODS(Image)
jrkoonced1e14722019-08-27 11:51:02 -0500672
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900673 bool operator==(const Image &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000674};
675
676struct Texture {
Vladimír Vondruš239be2c2018-07-24 23:23:56 +0200677 std::string name;
678
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000679 int sampler;
Selmar Kok8eb39042018-10-05 14:29:35 +0200680 int source;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900681 Value extras;
Selmar Kokfa7022f2018-04-04 18:10:20 +0200682 ExtensionMap extensions;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900683
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900684 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
685 std::string extras_json_string;
686 std::string extensions_json_string;
687
Syoyo Fujita5b407452017-06-04 17:42:41 +0900688 Texture() : sampler(-1), source(-1) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100689 DEFAULT_METHODS(Texture)
jrkoonced1e14722019-08-27 11:51:02 -0500690
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900691 bool operator==(const Texture &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000692};
Syoyo Fujitabde70212016-02-07 17:38:17 +0900693
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900694struct TextureInfo {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900695 int index = -1; // required.
696 int texCoord; // The set index of texture's TEXCOORD attribute used for
697 // texture coordinate mapping.
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900698
699 Value extras;
700 ExtensionMap extensions;
701
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900702 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
703 std::string extras_json_string;
704 std::string extensions_json_string;
705
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900706 TextureInfo() : index(-1), texCoord(0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100707 DEFAULT_METHODS(TextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900708 bool operator==(const TextureInfo &) const;
709};
710
711struct NormalTextureInfo {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900712 int index = -1; // required
713 int texCoord; // The set index of texture's TEXCOORD attribute used for
714 // texture coordinate mapping.
715 double scale; // scaledNormal = normalize((<sampled normal texture value>
716 // * 2.0 - 1.0) * vec3(<normal scale>, <normal scale>, 1.0))
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900717
718 Value extras;
719 ExtensionMap extensions;
720
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900721 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
722 std::string extras_json_string;
723 std::string extensions_json_string;
724
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900725 NormalTextureInfo() : index(-1), texCoord(0), scale(1.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100726 DEFAULT_METHODS(NormalTextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900727 bool operator==(const NormalTextureInfo &) const;
728};
729
730struct OcclusionTextureInfo {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900731 int index = -1; // required
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900732 int texCoord; // The set index of texture's TEXCOORD attribute used for
733 // texture coordinate mapping.
734 double strength; // occludedColor = lerp(color, color * <sampled occlusion
735 // texture value>, <occlusion strength>)
736
737 Value extras;
738 ExtensionMap extensions;
739
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900740 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
741 std::string extras_json_string;
742 std::string extensions_json_string;
743
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900744 OcclusionTextureInfo() : index(-1), texCoord(0), strength(1.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100745 DEFAULT_METHODS(OcclusionTextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900746 bool operator==(const OcclusionTextureInfo &) const;
747};
748
749// pbrMetallicRoughness class defined in glTF 2.0 spec.
750struct PbrMetallicRoughness {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900751 std::vector<double> baseColorFactor; // len = 4. default [1,1,1,1]
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900752 TextureInfo baseColorTexture;
753 double metallicFactor; // default 1
754 double roughnessFactor; // default 1
755 TextureInfo metallicRoughnessTexture;
756
757 Value extras;
758 ExtensionMap extensions;
759
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900760 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
761 std::string extras_json_string;
762 std::string extensions_json_string;
763
764 PbrMetallicRoughness()
765 : baseColorFactor(std::vector<double>{1.0, 1.0, 1.0, 1.0}),
766 metallicFactor(1.0),
767 roughnessFactor(1.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100768 DEFAULT_METHODS(PbrMetallicRoughness)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900769 bool operator==(const PbrMetallicRoughness &) const;
770};
771
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000772// Each extension should be stored in a ParameterMap.
773// members not in the values could be included in the ParameterMap
774// to keep a single material model
775struct Material {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900776 std::string name;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900777
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900778 std::vector<double> emissiveFactor; // length 3. default [0, 0, 0]
Syoyo Fujita046400b2019-07-24 19:26:48 +0900779 std::string alphaMode; // default "OPAQUE"
780 double alphaCutoff; // default 0.5
781 bool doubleSided; // default false;
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900782
783 PbrMetallicRoughness pbrMetallicRoughness;
784
785 NormalTextureInfo normalTexture;
786 OcclusionTextureInfo occlusionTexture;
787 TextureInfo emissiveTexture;
788
Syoyo Fujita046400b2019-07-24 19:26:48 +0900789 // For backward compatibility
790 // TODO(syoyo): Remove `values` and `additionalValues` in the next release.
791 ParameterMap values;
792 ParameterMap additionalValues;
Selmar09d2ff12018-03-15 17:30:42 +0100793
794 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900795 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200796
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900797 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
798 std::string extras_json_string;
799 std::string extensions_json_string;
800
Syoyo Fujita046400b2019-07-24 19:26:48 +0900801 Material() : alphaMode("OPAQUE"), alphaCutoff(0.5), doubleSided(false) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100802 DEFAULT_METHODS(Material)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900803
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900804 bool operator==(const Material &) const;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000805};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900806
Syoyo Fujita5b407452017-06-04 17:42:41 +0900807struct BufferView {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900808 std::string name;
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900809 int buffer{-1}; // Required
Syoyo Fujita4e47bc72020-01-02 22:07:25 +0900810 size_t byteOffset{0}; // minimum 0, default 0
811 size_t byteLength{0}; // required, minimum 1. 0 = invalid
812 size_t byteStride{0}; // minimum 4, maximum 252 (multiple of 4), default 0 =
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900813 // understood to be tightly packed
814 int target{0}; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"] for vertex indices
imallettd9ce9eb2022-10-07 10:37:09 -0700815 // or attribs. Could be 0 for other data
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900816 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900817 ExtensionMap extensions;
818
819 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
820 std::string extras_json_string;
821 std::string extensions_json_string;
822
Syoyo Fujita4e47bc72020-01-02 22:07:25 +0900823 bool dracoDecoded{false}; // Flag indicating this has been draco decoded
Syoyo Fujitad17ff662017-05-29 02:53:12 +0900824
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900825 BufferView()
826 : buffer(-1),
827 byteOffset(0),
828 byteLength(0),
829 byteStride(0),
830 target(0),
831 dracoDecoded(false) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100832 DEFAULT_METHODS(BufferView)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900833 bool operator==(const BufferView &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000834};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900835
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000836struct Accessor {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900837 int bufferView; // optional in spec but required here since sparse accessor
838 // are not supported
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900839 std::string name;
840 size_t byteOffset;
Fabien Freling9056aee2019-03-21 17:03:18 +0100841 bool normalized; // optional.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000842 int componentType; // (required) One of TINYGLTF_COMPONENT_TYPE_***
Syoyo Fujita5b407452017-06-04 17:42:41 +0900843 size_t count; // required
844 int type; // (required) One of TINYGLTF_TYPE_*** ..
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900845 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900846 ExtensionMap extensions;
847
848 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
849 std::string extras_json_string;
850 std::string extensions_json_string;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000851
Syoyo Fujita7905a5b2021-01-21 20:33:11 +0900852 std::vector<double>
853 minValues; // optional. integer value is promoted to double
854 std::vector<double>
855 maxValues; // optional. integer value is promoted to double
Syoyo Fujitad9d012a2017-07-12 18:29:29 +0900856
David Siegeld852f502023-06-05 23:28:05 +0200857 struct Sparse {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000858 int count;
859 bool isSparse;
860 struct {
861 int byteOffset;
862 int bufferView;
863 int componentType; // a TINYGLTF_COMPONENT_TYPE_ value
David Siegeld852f502023-06-05 23:28:05 +0200864 Value extras;
865 ExtensionMap extensions;
866 std::string extras_json_string;
867 std::string extensions_json_string;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000868 } indices;
869 struct {
870 int bufferView;
871 int byteOffset;
David Siegeld852f502023-06-05 23:28:05 +0200872 Value extras;
873 ExtensionMap extensions;
874 std::string extras_json_string;
875 std::string extensions_json_string;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000876 } values;
David Siegeld852f502023-06-05 23:28:05 +0200877 Value extras;
878 ExtensionMap extensions;
879 std::string extras_json_string;
880 std::string extensions_json_string;
881 };
882
883 Sparse sparse;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000884
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900885 ///
886 /// Utility function to compute byteStride for a given bufferView object.
887 /// Returns -1 upon invalid glTF value or parameter configuration.
888 ///
889 int ByteStride(const BufferView &bufferViewObject) const {
890 if (bufferViewObject.byteStride == 0) {
891 // Assume data is tightly packed.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500892 int componentSizeInBytes =
893 GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900894 if (componentSizeInBytes <= 0) {
895 return -1;
896 }
897
Syoyo Fujita81d75df2019-08-30 19:19:52 +0900898 int numComponents = GetNumComponentsInType(static_cast<uint32_t>(type));
899 if (numComponents <= 0) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900900 return -1;
901 }
902
Syoyo Fujita81d75df2019-08-30 19:19:52 +0900903 return componentSizeInBytes * numComponents;
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900904 } else {
imallettd9ce9eb2022-10-07 10:37:09 -0700905 // Check if byteStride is a multiple of the size of the accessor's component
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500906 // type.
907 int componentSizeInBytes =
908 GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900909 if (componentSizeInBytes <= 0) {
910 return -1;
911 }
912
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900913 if ((bufferViewObject.byteStride % uint32_t(componentSizeInBytes)) != 0) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900914 return -1;
915 }
Arthur Brainville8ce4e542018-01-10 01:27:12 +0100916 return static_cast<int>(bufferViewObject.byteStride);
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900917 }
918
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900919 // unreachable return 0;
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900920 }
921
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900922 Accessor()
923 : bufferView(-1),
924 byteOffset(0),
925 normalized(false),
926 componentType(-1),
927 count(0),
928 type(-1) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000929 sparse.isSparse = false;
930 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100931 DEFAULT_METHODS(Accessor)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900932 bool operator==(const tinygltf::Accessor &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000933};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900934
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900935struct PerspectiveCamera {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200936 double aspectRatio; // min > 0
937 double yfov; // required. min > 0
938 double zfar; // min > 0
939 double znear; // required. min > 0
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900940
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900941 PerspectiveCamera()
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900942 : aspectRatio(0.0),
943 yfov(0.0),
imallettd9ce9eb2022-10-07 10:37:09 -0700944 zfar(0.0) // 0 = use infinite projection matrix
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900945 ,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900946 znear(0.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100947 DEFAULT_METHODS(PerspectiveCamera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900948 bool operator==(const PerspectiveCamera &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900949
Selmar09d2ff12018-03-15 17:30:42 +0100950 ExtensionMap extensions;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900951 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900952
953 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
954 std::string extras_json_string;
955 std::string extensions_json_string;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900956};
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000957
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900958struct OrthographicCamera {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200959 double xmag; // required. must not be zero.
960 double ymag; // required. must not be zero.
961 double zfar; // required. `zfar` must be greater than `znear`.
962 double znear; // required
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000963
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900964 OrthographicCamera() : xmag(0.0), ymag(0.0), zfar(0.0), znear(0.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100965 DEFAULT_METHODS(OrthographicCamera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900966 bool operator==(const OrthographicCamera &) const;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900967
Selmar09d2ff12018-03-15 17:30:42 +0100968 ExtensionMap extensions;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900969 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900970
971 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
972 std::string extras_json_string;
973 std::string extensions_json_string;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900974};
975
976struct Camera {
977 std::string type; // required. "perspective" or "orthographic"
978 std::string name;
979
980 PerspectiveCamera perspective;
981 OrthographicCamera orthographic;
982
983 Camera() {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100984 DEFAULT_METHODS(Camera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900985 bool operator==(const Camera &) const;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900986
Selmar09d2ff12018-03-15 17:30:42 +0100987 ExtensionMap extensions;
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000988 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900989
990 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
991 std::string extras_json_string;
992 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900993};
994
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000995struct Primitive {
996 std::map<std::string, int> attributes; // (required) A dictionary object of
997 // integer, where each integer
998 // is the index of the accessor
999 // containing an attribute.
1000 int material; // The index of the material to apply to this primitive
Syoyo Fujita5b407452017-06-04 17:42:41 +09001001 // when rendering.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001002 int indices; // The index of the accessor that contains the indices.
1003 int mode; // one of TINYGLTF_MODE_***
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001004 std::vector<std::map<std::string, int> > targets; // array of morph targets,
imallettd9ce9eb2022-10-07 10:37:09 -07001005 // where each target is a dict with attributes in ["POSITION, "NORMAL",
Syoyo Fujita5b407452017-06-04 17:42:41 +09001006 // "TANGENT"] pointing
1007 // to their corresponding accessors
Alex Wood7319db72019-01-24 15:38:16 -05001008 ExtensionMap extensions;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001009 Value extras;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001010
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001011 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1012 std::string extras_json_string;
1013 std::string extensions_json_string;
1014
Syoyo Fujita5b407452017-06-04 17:42:41 +09001015 Primitive() {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001016 material = -1;
1017 indices = -1;
daemyung jang1739d022020-07-03 13:27:39 +09001018 mode = -1;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001019 }
Selmar Kokb74fade2019-10-29 16:09:32 +01001020 DEFAULT_METHODS(Primitive)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001021 bool operator==(const Primitive &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001022};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001023
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001024struct Mesh {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001025 std::string name;
1026 std::vector<Primitive> primitives;
Syoyo Fujita5b407452017-06-04 17:42:41 +09001027 std::vector<double> weights; // weights to be applied to the Morph Targets
Selmar09d2ff12018-03-15 17:30:42 +01001028 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001029 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001030
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001031 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1032 std::string extras_json_string;
1033 std::string extensions_json_string;
1034
jrkoonced1e14722019-08-27 11:51:02 -05001035 Mesh() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001036 DEFAULT_METHODS(Mesh)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001037 bool operator==(const Mesh &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001038};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001039
1040class Node {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001041 public:
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09001042 Node() : camera(-1), skin(-1), mesh(-1) {}
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001043
Selmar Kokb74fade2019-10-29 16:09:32 +01001044 DEFAULT_METHODS(Node)
Syoyo Fujitad6b0d0a2019-06-29 17:31:13 +09001045
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001046 bool operator==(const Node &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001047
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001048 int camera; // the index of the camera referenced by this node
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001049
1050 std::string name;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00001051 int skin;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001052 int mesh;
David Siegelc1648782023-05-09 01:33:57 +02001053 int light; // light source index (KHR_lights_punctual)
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001054 std::vector<int> children;
Syoyo Fujita7c877972016-03-08 01:31:49 +09001055 std::vector<double> rotation; // length must be 0 or 4
1056 std::vector<double> scale; // length must be 0 or 3
1057 std::vector<double> translation; // length must be 0 or 3
1058 std::vector<double> matrix; // length must be 0 or 16
Syoyo Fujita5b407452017-06-04 17:42:41 +09001059 std::vector<double> weights; // The weights of the instantiated Morph Target
Ben Buzbee3b735bb2018-04-24 11:39:30 -07001060
Selmar09d2ff12018-03-15 17:30:42 +01001061 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001062 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001063
1064 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1065 std::string extras_json_string;
1066 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001067};
1068
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001069struct Buffer {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001070 std::string name;
1071 std::vector<unsigned char> data;
Syoyo Fujita5b407452017-06-04 17:42:41 +09001072 std::string
1073 uri; // considered as required here but not in the spec (need to clarify)
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001074 // uri is not decoded(e.g. whitespace may be represented as %20)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001075 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001076 ExtensionMap extensions;
1077
1078 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1079 std::string extras_json_string;
1080 std::string extensions_json_string;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001081
Selmar Kokb74fade2019-10-29 16:09:32 +01001082 Buffer() = default;
1083 DEFAULT_METHODS(Buffer)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001084 bool operator==(const Buffer &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001085};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001086
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001087struct Asset {
Syoyo Fujitab702de72021-03-02 19:08:29 +09001088 std::string version = "2.0"; // required
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001089 std::string generator;
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +00001090 std::string minVersion;
1091 std::string copyright;
Selmar09d2ff12018-03-15 17:30:42 +01001092 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001093 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001094
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001095 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1096 std::string extras_json_string;
1097 std::string extensions_json_string;
1098
jrkoonced1e14722019-08-27 11:51:02 -05001099 Asset() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001100 DEFAULT_METHODS(Asset)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001101 bool operator==(const Asset &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001102};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001103
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001104struct Scene {
1105 std::string name;
1106 std::vector<int> nodes;
1107
Selmar09d2ff12018-03-15 17:30:42 +01001108 ExtensionMap extensions;
1109 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001110
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001111 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1112 std::string extras_json_string;
1113 std::string extensions_json_string;
1114
jrkoonced1e14722019-08-27 11:51:02 -05001115 Scene() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001116 DEFAULT_METHODS(Scene)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001117 bool operator==(const Scene &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001118};
1119
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001120struct SpotLight {
1121 double innerConeAngle;
1122 double outerConeAngle;
1123
Johan Bowald52936a02019-07-17 09:06:45 +02001124 SpotLight() : innerConeAngle(0.0), outerConeAngle(0.7853981634) {}
Selmar Kokb74fade2019-10-29 16:09:32 +01001125 DEFAULT_METHODS(SpotLight)
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001126 bool operator==(const SpotLight &) const;
1127
1128 ExtensionMap extensions;
1129 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001130
1131 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1132 std::string extras_json_string;
1133 std::string extensions_json_string;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001134};
1135
Emanuel Schrade186322b2017-11-06 11:14:41 +01001136struct Light {
1137 std::string name;
1138 std::vector<double> color;
Syoyo Fujita3dad3032020-05-13 21:22:23 +09001139 double intensity{1.0};
Emanuel Schrade186322b2017-11-06 11:14:41 +01001140 std::string type;
imallettd9ce9eb2022-10-07 10:37:09 -07001141 double range{0.0}; // 0.0 = infinite
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001142 SpotLight spot;
1143
Benjamin Schmithüsenb2d7d882019-07-09 16:32:42 +02001144 Light() : intensity(1.0), range(0.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +01001145 DEFAULT_METHODS(Light)
Arthur Brainville (Ybalrid)9eeaf202019-09-05 13:02:05 +02001146
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001147 bool operator==(const Light &) const;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001148
1149 ExtensionMap extensions;
1150 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001151
1152 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1153 std::string extras_json_string;
1154 std::string extensions_json_string;
Emanuel Schrade186322b2017-11-06 11:14:41 +01001155};
1156
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001157class Model {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001158 public:
Selmar Kokff2b1f92019-10-21 17:58:09 +02001159 Model() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001160 DEFAULT_METHODS(Model)
Syoyo Fujitad6b0d0a2019-06-29 17:31:13 +09001161
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001162 bool operator==(const Model &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001163
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001164 std::vector<Accessor> accessors;
1165 std::vector<Animation> animations;
1166 std::vector<Buffer> buffers;
1167 std::vector<BufferView> bufferViews;
1168 std::vector<Material> materials;
1169 std::vector<Mesh> meshes;
1170 std::vector<Node> nodes;
1171 std::vector<Texture> textures;
1172 std::vector<Image> images;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00001173 std::vector<Skin> skins;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001174 std::vector<Sampler> samplers;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09001175 std::vector<Camera> cameras;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001176 std::vector<Scene> scenes;
Emanuel Schrade186322b2017-11-06 11:14:41 +01001177 std::vector<Light> lights;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001178
sammyKhana0a62bd2020-01-17 13:41:16 +01001179 int defaultScene = -1;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001180 std::vector<std::string> extensionsUsed;
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00001181 std::vector<std::string> extensionsRequired;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001182
1183 Asset asset;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001184
1185 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001186 ExtensionMap extensions;
1187
1188 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1189 std::string extras_json_string;
1190 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001191};
1192
Syoyo Fujita0614eb82016-10-14 18:50:14 +09001193enum SectionCheck {
Luke San Antonio9e36b612016-06-23 14:10:51 -04001194 NO_REQUIRE = 0x00,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001195 REQUIRE_VERSION = 0x01,
1196 REQUIRE_SCENE = 0x02,
1197 REQUIRE_SCENES = 0x04,
1198 REQUIRE_NODES = 0x08,
1199 REQUIRE_ACCESSORS = 0x10,
1200 REQUIRE_BUFFERS = 0x20,
1201 REQUIRE_BUFFER_VIEWS = 0x40,
1202 REQUIRE_ALL = 0x7f
Luke San Antonio9e36b612016-06-23 14:10:51 -04001203};
1204
Squareysff644d82018-03-13 22:36:18 +01001205///
Pyarelal Knowles385946d2023-01-03 18:18:04 -08001206/// URIEncodeFunction type. Signature for custom URI encoding of external
1207/// resources such as .bin and image files. Used by tinygltf to re-encode the
1208/// final location of saved files. object_type may be used to encode buffer and
1209/// image URIs differently, for example. See
1210/// https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#uris
1211///
1212typedef bool (*URIEncodeFunction)(const std::string &in_uri,
1213 const std::string &object_type,
1214 std::string *out_uri, void *user_data);
1215
1216///
1217/// URIDecodeFunction type. Signature for custom URI decoding of external
1218/// resources such as .bin and image files. Used by tinygltf when computing
1219/// filenames to write resources.
1220///
1221typedef bool (*URIDecodeFunction)(const std::string &in_uri,
1222 std::string *out_uri, void *user_data);
1223
1224// Declaration of default uri decode function
1225bool URIDecode(const std::string &in_uri, std::string *out_uri,
1226 void *user_data);
1227
1228///
1229/// A structure containing URI callbacks and a pointer to their user data.
1230///
1231struct URICallbacks {
1232 URIEncodeFunction encode; // Optional encode method
1233 URIDecodeFunction decode; // Required decode method
1234
1235 void *user_data; // An argument that is passed to all uri callbacks
1236};
1237
1238///
Squareysff644d82018-03-13 22:36:18 +01001239/// LoadImageDataFunction type. Signature for custom image loading callbacks.
1240///
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001241typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *,
1242 std::string *, int, int,
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001243 const unsigned char *, int,
1244 void *user_pointer);
Squareysff644d82018-03-13 22:36:18 +01001245
johan bowald642a3432018-04-01 12:37:18 +02001246///
1247/// WriteImageDataFunction type. Signature for custom image writing callbacks.
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08001248/// The out_uri parameter becomes the URI written to the gltf and may reference
1249/// a file or contain a data URI.
johan bowald642a3432018-04-01 12:37:18 +02001250///
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08001251typedef bool (*WriteImageDataFunction)(const std::string *basepath,
1252 const std::string *filename,
1253 const Image *image, bool embedImages,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08001254 const URICallbacks *uri_cb,
1255 std::string *out_uri,
1256 void *user_pointer);
johan bowald642a3432018-04-01 12:37:18 +02001257
Squareys2d3594d2018-03-13 22:40:53 +01001258#ifndef TINYGLTF_NO_STB_IMAGE
Squareysff644d82018-03-13 22:36:18 +01001259// Declaration of default image loader callback
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001260bool LoadImageData(Image *image, const int image_idx, std::string *err,
1261 std::string *warn, int req_width, int req_height,
1262 const unsigned char *bytes, int size, void *);
Squareys2d3594d2018-03-13 22:40:53 +01001263#endif
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001264
johan bowald642a3432018-04-01 12:37:18 +02001265#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
1266// Declaration of default image writer callback
1267bool WriteImageData(const std::string *basepath, const std::string *filename,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08001268 const Image *image, bool embedImages,
1269 const URICallbacks *uri_cb, std::string *out_uri, void *);
johan bowald642a3432018-04-01 12:37:18 +02001270#endif
1271
Paolo Jovone6601bf2018-07-07 20:43:33 +02001272///
1273/// FilExistsFunction type. Signature for custom filesystem callbacks.
1274///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001275typedef bool (*FileExistsFunction)(const std::string &abs_filename, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001276
1277///
1278/// ExpandFilePathFunction type. Signature for custom filesystem callbacks.
1279///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001280typedef std::string (*ExpandFilePathFunction)(const std::string &, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001281
1282///
1283/// ReadWholeFileFunction type. Signature for custom filesystem callbacks.
1284///
1285typedef bool (*ReadWholeFileFunction)(std::vector<unsigned char> *,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001286 std::string *, const std::string &,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001287 void *);
1288
1289///
1290/// WriteWholeFileFunction type. Signature for custom filesystem callbacks.
1291///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001292typedef bool (*WriteWholeFileFunction)(std::string *, const std::string &,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001293 const std::vector<unsigned char> &,
1294 void *);
1295
1296///
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09001297/// GetFileSizeFunction type. Signature for custom filesystem callbacks.
1298///
1299typedef bool (*GetFileSizeFunction)(size_t *filesize_out, std::string *err, const std::string &abs_filename,
1300 void *userdata);
1301
1302///
Paolo Jovone6601bf2018-07-07 20:43:33 +02001303/// A structure containing all required filesystem callbacks and a pointer to
1304/// their user data.
1305///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001306struct FsCallbacks {
1307 FileExistsFunction FileExists;
1308 ExpandFilePathFunction ExpandFilePath;
1309 ReadWholeFileFunction ReadWholeFile;
1310 WriteWholeFileFunction WriteWholeFile;
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09001311 GetFileSizeFunction GetFileSizeInBytes; // To avoid GetFileSize Win32 API, add `InBytes` suffix.
Paolo Jovone6601bf2018-07-07 20:43:33 +02001312
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001313 void *user_data; // An argument that is passed to all fs callbacks
Paolo Jovone6601bf2018-07-07 20:43:33 +02001314};
1315
1316#ifndef TINYGLTF_NO_FS
1317// Declaration of default filesystem callbacks
1318
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001319bool FileExists(const std::string &abs_filename, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001320
Syoyo Fujita3d939fd2020-06-06 18:13:15 +09001321///
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04001322/// Expand file path(e.g. `~` to home directory on posix, `%APPDATA%` to
Syoyo Fujita2c521b32020-12-04 00:50:46 +09001323/// `C:\\Users\\tinygltf\\AppData`)
Syoyo Fujita3d939fd2020-06-06 18:13:15 +09001324///
1325/// @param[in] filepath File path string. Assume UTF-8
1326/// @param[in] userdata User data. Set to `nullptr` if you don't need it.
1327///
1328std::string ExpandFilePath(const std::string &filepath, void *userdata);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001329
1330bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001331 const std::string &filepath, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001332
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001333bool WriteWholeFile(std::string *err, const std::string &filepath,
1334 const std::vector<unsigned char> &contents, void *);
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09001335
1336bool GetFileSizeInBytes(size_t *filesize_out, std::string *err, const std::string &filepath,
1337 void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001338#endif
1339
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001340///
imallettd9ce9eb2022-10-07 10:37:09 -07001341/// glTF Parser/Serializer context.
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001342///
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001343class TinyGLTF {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001344 public:
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001345#ifdef __clang__
1346#pragma clang diagnostic push
1347#pragma clang diagnostic ignored "-Wc++98-compat"
1348#endif
1349
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001350 TinyGLTF() : bin_data_(nullptr), bin_size_(0), is_binary_(false) {}
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001351
1352#ifdef __clang__
1353#pragma clang diagnostic pop
1354#endif
1355
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001356 ~TinyGLTF() {}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001357
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001358 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001359 /// Loads glTF ASCII asset from a file.
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001360 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001361 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001362 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001363 bool LoadASCIIFromFile(Model *model, std::string *err, std::string *warn,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001364 const std::string &filename,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001365 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001366
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001367 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001368 /// Loads glTF ASCII asset from string(memory).
1369 /// `length` = strlen(str);
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001370 /// `base_dir` is a search path of glTF asset(e.g. images). Path Must be an
1371 /// expanded path (e.g. no tilde(`~`), no environment variables). Set warning
1372 /// message to `warn` for example it fails to load asserts. Returns false and
1373 /// set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001374 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001375 bool LoadASCIIFromString(Model *model, std::string *err, std::string *warn,
1376 const char *str, const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001377 const std::string &base_dir,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001378 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001379
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001380 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001381 /// Loads glTF binary asset from a file.
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001382 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001383 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001384 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001385 bool LoadBinaryFromFile(Model *model, std::string *err, std::string *warn,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001386 const std::string &filename,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001387 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001388
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001389 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001390 /// Loads glTF binary asset from memory.
1391 /// `length` = strlen(str);
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001392 /// `base_dir` is a search path of glTF asset(e.g. images). Path Must be an
1393 /// expanded path (e.g. no tilde(`~`), no environment variables).
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001394 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001395 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001396 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001397 bool LoadBinaryFromMemory(Model *model, std::string *err, std::string *warn,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001398 const unsigned char *bytes,
1399 const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001400 const std::string &base_dir = "",
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001401 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001402
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001403 ///
imallettd9ce9eb2022-10-07 10:37:09 -07001404 /// Write glTF to stream, buffers and images will be embedded
Johan Bowald1af7c1d2019-07-16 14:50:46 +02001405 ///
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08001406 bool WriteGltfSceneToStream(const Model *model, std::ostream &stream,
Johan Bowald1af7c1d2019-07-16 14:50:46 +02001407 bool prettyPrint, bool writeBinary);
1408
1409 ///
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001410 /// Write glTF to file.
1411 ///
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08001412 bool WriteGltfSceneToFile(const Model *model, const std::string &filename,
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001413 bool embedImages, bool embedBuffers,
1414 bool prettyPrint, bool writeBinary);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00001415
Squareysff644d82018-03-13 22:36:18 +01001416 ///
1417 /// Set callback to use for loading image data
1418 ///
1419 void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data);
1420
johan bowald642a3432018-04-01 12:37:18 +02001421 ///
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001422 /// Unset(remove) callback of loading image data
1423 ///
1424 void RemoveImageLoader();
1425
1426 ///
johan bowald642a3432018-04-01 12:37:18 +02001427 /// Set callback to use for writing image data
1428 ///
1429 void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data);
1430
Paolo Jovone6601bf2018-07-07 20:43:33 +02001431 ///
Pyarelal Knowles385946d2023-01-03 18:18:04 -08001432 /// Set callbacks to use for URI encoding and decoding and their user data
1433 ///
1434 void SetURICallbacks(URICallbacks callbacks);
1435
1436 ///
Paolo Jovone6601bf2018-07-07 20:43:33 +02001437 /// Set callbacks to use for filesystem (fs) access and their user data
1438 ///
1439 void SetFsCallbacks(FsCallbacks callbacks);
1440
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001441 ///
1442 /// Set serializing default values(default = false).
1443 /// When true, default values are force serialized to .glTF.
imallettd9ce9eb2022-10-07 10:37:09 -07001444 /// This may be helpful if you want to serialize a full description of glTF
Syoyo Fujitaff515702019-08-24 16:29:14 +09001445 /// data.
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001446 ///
Syoyo Fujitaff515702019-08-24 16:29:14 +09001447 /// TODO(LTE): Supply parsing option as function arguments to
1448 /// `LoadASCIIFromFile()` and others, not by a class method
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001449 ///
1450 void SetSerializeDefaultValues(const bool enabled) {
1451 serialize_default_values_ = enabled;
1452 }
1453
Syoyo Fujitaff515702019-08-24 16:29:14 +09001454 bool GetSerializeDefaultValues() const { return serialize_default_values_; }
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001455
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001456 ///
1457 /// Store original JSON string for `extras` and `extensions`.
1458 /// This feature will be useful when the user want to reconstruct custom data
1459 /// structure from JSON string.
1460 ///
1461 void SetStoreOriginalJSONForExtrasAndExtensions(const bool enabled) {
1462 store_original_json_for_extras_and_extensions_ = enabled;
1463 }
1464
1465 bool GetStoreOriginalJSONForExtrasAndExtensions() const {
1466 return store_original_json_for_extras_and_extensions_;
1467 }
1468
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001469 ///
imallettd9ce9eb2022-10-07 10:37:09 -07001470 /// Specify whether preserve image channels when loading images or not.
1471 /// (Not effective when the user supplies their own LoadImageData callbacks)
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001472 ///
1473 void SetPreserveImageChannels(bool onoff) {
1474 preserve_image_channels_ = onoff;
1475 }
1476
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09001477 ///
1478 /// Set maximum allowed external file size in bytes.
1479 /// Default: 2GB
1480 /// Only effective for built-in ReadWholeFileFunction FS function.
1481 ///
1482 void SetMaxExternalFileSize(size_t max_bytes) {
1483 max_external_file_size_ = max_bytes;
1484 }
1485
1486 size_t GetMaxExternalFileSize() const {
1487 return max_external_file_size_;
1488 }
1489
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001490 bool GetPreserveImageChannels() const { return preserve_image_channels_; }
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001491
Syoyo Fujitabeded612016-05-01 20:03:43 +09001492 private:
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001493 ///
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001494 /// Loads glTF asset from string(memory).
1495 /// `length` = strlen(str);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001496 /// Set warning message to `warn` for example it fails to load asserts
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001497 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001498 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001499 bool LoadFromString(Model *model, std::string *err, std::string *warn,
1500 const char *str, const unsigned int length,
1501 const std::string &base_dir, unsigned int check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001502
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001503 const unsigned char *bin_data_ = nullptr;
1504 size_t bin_size_ = 0;
1505 bool is_binary_ = false;
1506
Syoyo Fujitaff515702019-08-24 16:29:14 +09001507 bool serialize_default_values_ = false; ///< Serialize default values?
Squareysff644d82018-03-13 22:36:18 +01001508
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001509 bool store_original_json_for_extras_and_extensions_ = false;
1510
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001511 bool preserve_image_channels_ = false; /// Default false(expand channels to
1512 /// RGBA) for backward compatibility.
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001513
Syoyo Fujita1a5046e2023-04-23 23:08:41 +09001514 size_t max_external_file_size_{size_t((std::numeric_limits<int32_t>::max)())}; // Default 2GB
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09001515
Syoyo Fujita9117abb2022-08-16 20:10:26 +09001516 // Warning & error messages
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09001517 std::string warn_;
1518 std::string err_;
1519
Paolo Jovone6601bf2018-07-07 20:43:33 +02001520 FsCallbacks fs = {
1521#ifndef TINYGLTF_NO_FS
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001522 &tinygltf::FileExists, &tinygltf::ExpandFilePath,
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09001523 &tinygltf::ReadWholeFile, &tinygltf::WriteWholeFile, &tinygltf::GetFileSizeInBytes,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001524
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001525 nullptr // Fs callback user data
Paolo Jovone6601bf2018-07-07 20:43:33 +02001526#else
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09001527 nullptr, nullptr, nullptr, nullptr, nullptr,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001528
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001529 nullptr // Fs callback user data
Paolo Jovone6601bf2018-07-07 20:43:33 +02001530#endif
1531 };
1532
Pyarelal Knowles385946d2023-01-03 18:18:04 -08001533 URICallbacks uri_cb = {
1534 // Use paths as-is by default. This will use JSON string escaping.
1535 nullptr,
1536 // Decode all URIs before using them as paths as the application may have
1537 // percent encoded them.
1538 &tinygltf::URIDecode,
1539 // URI callback user data
1540 nullptr};
1541
Squareysff644d82018-03-13 22:36:18 +01001542 LoadImageDataFunction LoadImageData =
1543#ifndef TINYGLTF_NO_STB_IMAGE
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001544 &tinygltf::LoadImageData;
Squareysff644d82018-03-13 22:36:18 +01001545#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001546 nullptr;
Squareysff644d82018-03-13 22:36:18 +01001547#endif
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001548 void *load_image_user_data_{nullptr};
1549 bool user_image_loader_{false};
johan bowald642a3432018-04-01 12:37:18 +02001550
1551 WriteImageDataFunction WriteImageData =
1552#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001553 &tinygltf::WriteImageData;
johan bowald642a3432018-04-01 12:37:18 +02001554#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001555 nullptr;
johan bowald642a3432018-04-01 12:37:18 +02001556#endif
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001557 void *write_image_user_data_{nullptr};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001558};
1559
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001560#ifdef __clang__
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001561#pragma clang diagnostic pop // -Wpadded
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001562#endif
1563
Syoyo Fujita7c877972016-03-08 01:31:49 +09001564} // namespace tinygltf
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001565
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001566#endif // TINY_GLTF_H_
Syoyo Fujitac8d3c762017-05-25 01:42:38 +09001567
Selmar Kok31cb7f92018-10-03 15:39:05 +02001568#if defined(TINYGLTF_IMPLEMENTATION) || defined(__INTELLISENSE__)
Syoyo Fujita7c877972016-03-08 01:31:49 +09001569#include <algorithm>
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001570//#include <cassert>
Paolo Jovone6601bf2018-07-07 20:43:33 +02001571#ifndef TINYGLTF_NO_FS
Syoyo Fujita125d8e52019-11-09 20:52:56 +09001572#include <cstdio>
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001573#include <fstream>
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09001574#include <sys/stat.h> // for is_directory check
Paolo Jovone6601bf2018-07-07 20:43:33 +02001575#endif
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001576#include <sstream>
Syoyo Fujitabeded612016-05-01 20:03:43 +09001577
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001578#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001579// Disable some warnings for external files.
1580#pragma clang diagnostic push
1581#pragma clang diagnostic ignored "-Wfloat-equal"
1582#pragma clang diagnostic ignored "-Wexit-time-destructors"
1583#pragma clang diagnostic ignored "-Wconversion"
1584#pragma clang diagnostic ignored "-Wold-style-cast"
Syoyo Fujita643ce102016-05-01 17:19:37 +09001585#pragma clang diagnostic ignored "-Wglobal-constructors"
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001586#if __has_warning("-Wreserved-id-macro")
Syoyo Fujita643ce102016-05-01 17:19:37 +09001587#pragma clang diagnostic ignored "-Wreserved-id-macro"
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001588#endif
Syoyo Fujita643ce102016-05-01 17:19:37 +09001589#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
1590#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001591#pragma clang diagnostic ignored "-Wc++98-compat"
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001592#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001593#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
1594#pragma clang diagnostic ignored "-Wswitch-enum"
1595#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001596#pragma clang diagnostic ignored "-Wweak-vtables"
1597#pragma clang diagnostic ignored "-Wcovered-switch-default"
Syoyo Fujitaa8f0b1c2018-08-28 21:33:40 +09001598#if __has_warning("-Wdouble-promotion")
1599#pragma clang diagnostic ignored "-Wdouble-promotion"
1600#endif
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001601#if __has_warning("-Wcomma")
Syoyo Fujitac8d3c762017-05-25 01:42:38 +09001602#pragma clang diagnostic ignored "-Wcomma"
1603#endif
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001604#if __has_warning("-Wzero-as-null-pointer-constant")
1605#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
1606#endif
1607#if __has_warning("-Wcast-qual")
1608#pragma clang diagnostic ignored "-Wcast-qual"
1609#endif
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001610#if __has_warning("-Wmissing-variable-declarations")
1611#pragma clang diagnostic ignored "-Wmissing-variable-declarations"
1612#endif
1613#if __has_warning("-Wmissing-prototypes")
1614#pragma clang diagnostic ignored "-Wmissing-prototypes"
1615#endif
1616#if __has_warning("-Wcast-align")
1617#pragma clang diagnostic ignored "-Wcast-align"
1618#endif
1619#if __has_warning("-Wnewline-eof")
1620#pragma clang diagnostic ignored "-Wnewline-eof"
1621#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001622#if __has_warning("-Wunused-parameter")
1623#pragma clang diagnostic ignored "-Wunused-parameter"
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001624#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001625#if __has_warning("-Wmismatched-tags")
1626#pragma clang diagnostic ignored "-Wmismatched-tags"
1627#endif
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001628#if __has_warning("-Wextra-semi-stmt")
1629#pragma clang diagnostic ignored "-Wextra-semi-stmt"
1630#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001631#endif
1632
imallettd9ce9eb2022-10-07 10:37:09 -07001633// Disable GCC warnings
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001634#ifdef __GNUC__
1635#pragma GCC diagnostic push
1636#pragma GCC diagnostic ignored "-Wtype-limits"
Syoyo Fujitaca56f722019-03-07 21:04:25 +09001637#endif // __GNUC__
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001638
krokofc0116b2019-03-03 08:28:49 +02001639#ifndef TINYGLTF_NO_INCLUDE_JSON
jrkooncecba5d6c2019-08-29 11:26:22 -05001640#ifndef TINYGLTF_USE_RAPIDJSON
Alex Wood7319db72019-01-24 15:38:16 -05001641#include "json.hpp"
jrkooncecba5d6c2019-08-29 11:26:22 -05001642#else
Alvaro Barua3ddf8302021-03-18 00:21:23 +00001643#ifndef TINYGLTF_NO_INCLUDE_RAPIDJSON
jrkooncecba5d6c2019-08-29 11:26:22 -05001644#include "document.h"
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001645#include "prettywriter.h"
1646#include "rapidjson.h"
jrkooncecba5d6c2019-08-29 11:26:22 -05001647#include "stringbuffer.h"
1648#include "writer.h"
jrkooncecba5d6c2019-08-29 11:26:22 -05001649#endif
krokof4b6d112019-03-03 01:11:31 +02001650#endif
Alvaro Barua3ddf8302021-03-18 00:21:23 +00001651#endif
Alex Wood7319db72019-01-24 15:38:16 -05001652
1653#ifdef TINYGLTF_ENABLE_DRACO
Alex Wood7319db72019-01-24 15:38:16 -05001654#include "draco/compression/decode.h"
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001655#include "draco/core/decoder_buffer.h"
Alex Wood7319db72019-01-24 15:38:16 -05001656#endif
Squareys2d3594d2018-03-13 22:40:53 +01001657
1658#ifndef TINYGLTF_NO_STB_IMAGE
krokofc0116b2019-03-03 08:28:49 +02001659#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE
Alex Wood7319db72019-01-24 15:38:16 -05001660#include "stb_image.h"
Squareys2d3594d2018-03-13 22:40:53 +01001661#endif
krokof4b6d112019-03-03 01:11:31 +02001662#endif
Squareys2d3594d2018-03-13 22:40:53 +01001663
johan bowald642a3432018-04-01 12:37:18 +02001664#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
krokofc0116b2019-03-03 08:28:49 +02001665#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE
Alex Wood7319db72019-01-24 15:38:16 -05001666#include "stb_image_write.h"
johan bowald642a3432018-04-01 12:37:18 +02001667#endif
krokof4b6d112019-03-03 01:11:31 +02001668#endif
johan bowald642a3432018-04-01 12:37:18 +02001669
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001670#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001671#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001672#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001673
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001674#ifdef __GNUC__
1675#pragma GCC diagnostic pop
1676#endif
1677
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001678#ifdef _WIN32
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001679
1680// issue 143.
1681// Define NOMINMAX to avoid min/max defines,
imallett3a295882022-10-07 11:20:39 -07001682// but undef it after included Windows.h
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001683#ifndef NOMINMAX
1684#define TINYGLTF_INTERNAL_NOMINMAX
1685#define NOMINMAX
1686#endif
1687
Syoyo Fujita87be0ce2019-02-19 21:36:32 +09001688#ifndef WIN32_LEAN_AND_MEAN
1689#define WIN32_LEAN_AND_MEAN
1690#define TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1691#endif
imallett3a295882022-10-07 11:20:39 -07001692#ifndef __MINGW32__
imallett56e10982022-10-07 10:35:16 -07001693#include <Windows.h> // include API for expanding a file path
imallett3a295882022-10-07 11:20:39 -07001694#else
1695#include <windows.h>
1696#endif
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001697
Syoyo Fujita87be0ce2019-02-19 21:36:32 +09001698#ifdef TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1699#undef WIN32_LEAN_AND_MEAN
1700#endif
1701
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001702#if defined(TINYGLTF_INTERNAL_NOMINMAX)
1703#undef NOMINMAX
1704#endif
1705
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001706#if defined(__GLIBCXX__) // mingw
Syoyo Fujita45cac782019-11-09 20:42:55 +09001707
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001708#include <fcntl.h> // _O_RDONLY
1709
1710#include <ext/stdio_filebuf.h> // fstream (all sorts of IO stuff) + stdio_filebuf (=streambuf)
Syoyo Fujita45cac782019-11-09 20:42:55 +09001711
1712#endif
1713
Julian Smith0598a202021-08-25 12:06:08 +01001714#elif !defined(__ANDROID__) && !defined(__OpenBSD__)
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001715//#include <wordexp.h>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001716#endif
1717
Christopher Sean Morrison2c9b2562022-05-14 19:00:19 -04001718#if defined(__sparcv9) || defined(__powerpc__)
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001719// Big endian
1720#else
1721#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
1722#define TINYGLTF_LITTLE_ENDIAN 1
1723#endif
1724#endif
1725
David03ad33c2023-02-15 23:35:51 -06001726namespace tinygltf {
David1f9a4b92023-02-15 22:56:18 -06001727namespace detail {
jrkooncecba5d6c2019-08-29 11:26:22 -05001728#ifdef TINYGLTF_USE_RAPIDJSON
jrkooncece7fa742019-09-04 13:31:44 -05001729
1730#ifdef TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001731// This uses the RapidJSON CRTAllocator. It is thread safe and multiple
1732// documents may be active at once.
1733using json =
1734 rapidjson::GenericValue<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
David Siegelcfe64fb2023-06-07 15:18:38 +02001735using json_iterator = json::MemberIterator;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001736using json_const_iterator = json::ConstMemberIterator;
1737using json_const_array_iterator = json const *;
1738using JsonDocument =
jrkooncece7fa742019-09-04 13:31:44 -05001739 rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001740rapidjson::CrtAllocator s_CrtAllocator; // stateless and thread safe
1741rapidjson::CrtAllocator &GetAllocator() { return s_CrtAllocator; }
jrkooncece7fa742019-09-04 13:31:44 -05001742#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001743// This uses the default RapidJSON MemoryPoolAllocator. It is very fast, but
1744// not thread safe. Only a single JsonDocument may be active at any one time,
1745// meaning only a single gltf load/save can be active any one time.
1746using json = rapidjson::Value;
David Siegelcfe64fb2023-06-07 15:18:38 +02001747using json_iterator = json::MemberIterator;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001748using json_const_iterator = json::ConstMemberIterator;
1749using json_const_array_iterator = json const *;
1750rapidjson::Document *s_pActiveDocument = nullptr;
1751rapidjson::Document::AllocatorType &GetAllocator() {
1752 assert(s_pActiveDocument); // Root json node must be JsonDocument type
1753 return s_pActiveDocument->GetAllocator();
1754}
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001755
1756#ifdef __clang__
1757#pragma clang diagnostic push
1758// Suppress JsonDocument(JsonDocument &&rhs) noexcept
1759#pragma clang diagnostic ignored "-Wunused-member-function"
1760#endif
1761
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001762struct JsonDocument : public rapidjson::Document {
1763 JsonDocument() {
1764 assert(s_pActiveDocument ==
1765 nullptr); // When using default allocator, only one document can be
1766 // active at a time, if you need multiple active at once,
1767 // define TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
1768 s_pActiveDocument = this;
jrkooncece7fa742019-09-04 13:31:44 -05001769 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001770 JsonDocument(const JsonDocument &) = delete;
1771 JsonDocument(JsonDocument &&rhs) noexcept
jrkooncece7fa742019-09-04 13:31:44 -05001772 : rapidjson::Document(std::move(rhs)) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001773 s_pActiveDocument = this;
1774 rhs.isNil = true;
1775 }
1776 ~JsonDocument() {
1777 if (!isNil) {
1778 s_pActiveDocument = nullptr;
jrkooncecba5d6c2019-08-29 11:26:22 -05001779 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001780 }
jrkooncece7fa742019-09-04 13:31:44 -05001781
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001782 private:
1783 bool isNil = false;
1784};
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001785
1786#ifdef __clang__
1787#pragma clang diagnostic pop
1788#endif
1789
jrkooncece7fa742019-09-04 13:31:44 -05001790#endif // TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001791
jrkooncecba5d6c2019-08-29 11:26:22 -05001792#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001793using nlohmann::json;
David Siegelcfe64fb2023-06-07 15:18:38 +02001794using json_iterator = json::iterator;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001795using json_const_iterator = json::const_iterator;
1796using json_const_array_iterator = json_const_iterator;
1797using JsonDocument = json;
jrkooncecba5d6c2019-08-29 11:26:22 -05001798#endif
1799
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001800void JsonParse(JsonDocument &doc, const char *str, size_t length,
1801 bool throwExc = false) {
jrkooncecba5d6c2019-08-29 11:26:22 -05001802#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001803 (void)throwExc;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001804 doc.Parse(str, length);
jrkooncecba5d6c2019-08-29 11:26:22 -05001805#else
David03ad33c2023-02-15 23:35:51 -06001806 doc = detail::json::parse(str, str + length, nullptr, throwExc);
jrkooncecba5d6c2019-08-29 11:26:22 -05001807#endif
jrkooncecba5d6c2019-08-29 11:26:22 -05001808}
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001809} // namespace
David03ad33c2023-02-15 23:35:51 -06001810}
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001811
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001812#ifdef __APPLE__
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001813#include "TargetConditionals.h"
Syoyo Fujitab23f6fe2017-11-15 01:03:09 +09001814#endif
1815
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001816#ifdef __clang__
1817#pragma clang diagnostic push
1818#pragma clang diagnostic ignored "-Wc++98-compat"
1819#endif
1820
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09001821namespace tinygltf {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001822
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001823///
1824/// Internal LoadImageDataOption struct.
1825/// This struct is passed through `user_pointer` in LoadImageData.
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001826/// The struct is not passed when the user supply their own LoadImageData
1827/// callbacks.
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001828///
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001829struct LoadImageDataOption {
1830 // true: preserve image channels(e.g. load as RGB image if the image has RGB
1831 // channels) default `false`(channels are expanded to RGBA for backward
imallettd9ce9eb2022-10-07 10:37:09 -07001832 // compatibility).
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001833 bool preserve_channels{false};
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001834};
1835
Selmar Kok31cb7f92018-10-03 15:39:05 +02001836// Equals function for Value, for recursivity
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001837static bool Equals(const tinygltf::Value &one, const tinygltf::Value &other) {
1838 if (one.Type() != other.Type()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001839
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001840 switch (one.Type()) {
1841 case NULL_TYPE:
1842 return true;
1843 case BOOL_TYPE:
1844 return one.Get<bool>() == other.Get<bool>();
Syoyo Fujita150f2432019-07-25 19:22:44 +09001845 case REAL_TYPE:
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001846 return TINYGLTF_DOUBLE_EQUAL(one.Get<double>(), other.Get<double>());
1847 case INT_TYPE:
1848 return one.Get<int>() == other.Get<int>();
1849 case OBJECT_TYPE: {
1850 auto oneObj = one.Get<tinygltf::Value::Object>();
1851 auto otherObj = other.Get<tinygltf::Value::Object>();
1852 if (oneObj.size() != otherObj.size()) return false;
1853 for (auto &it : oneObj) {
1854 auto otherIt = otherObj.find(it.first);
1855 if (otherIt == otherObj.end()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001856
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001857 if (!Equals(it.second, otherIt->second)) return false;
1858 }
1859 return true;
1860 }
1861 case ARRAY_TYPE: {
1862 if (one.Size() != other.Size()) return false;
1863 for (int i = 0; i < int(one.Size()); ++i)
Selmara63cc632019-04-16 16:57:43 +02001864 if (!Equals(one.Get(i), other.Get(i))) return false;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001865 return true;
1866 }
1867 case STRING_TYPE:
1868 return one.Get<std::string>() == other.Get<std::string>();
1869 case BINARY_TYPE:
1870 return one.Get<std::vector<unsigned char> >() ==
1871 other.Get<std::vector<unsigned char> >();
1872 default: {
1873 // unhandled type
1874 return false;
1875 }
1876 }
Selmar Kok31cb7f92018-10-03 15:39:05 +02001877}
1878
1879// Equals function for std::vector<double> using TINYGLTF_DOUBLE_EPSILON
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001880static bool Equals(const std::vector<double> &one,
1881 const std::vector<double> &other) {
1882 if (one.size() != other.size()) return false;
1883 for (int i = 0; i < int(one.size()); ++i) {
1884 if (!TINYGLTF_DOUBLE_EQUAL(one[size_t(i)], other[size_t(i)])) return false;
1885 }
1886 return true;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001887}
1888
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001889bool Accessor::operator==(const Accessor &other) const {
1890 return this->bufferView == other.bufferView &&
1891 this->byteOffset == other.byteOffset &&
1892 this->componentType == other.componentType &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001893 this->count == other.count && this->extensions == other.extensions &&
1894 this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001895 Equals(this->maxValues, other.maxValues) &&
1896 Equals(this->minValues, other.minValues) && this->name == other.name &&
1897 this->normalized == other.normalized && this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001898}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001899bool Animation::operator==(const Animation &other) const {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001900 return this->channels == other.channels &&
1901 this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001902 this->name == other.name && this->samplers == other.samplers;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001903}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001904bool AnimationChannel::operator==(const AnimationChannel &other) const {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001905 return this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001906 this->target_node == other.target_node &&
1907 this->target_path == other.target_path &&
1908 this->sampler == other.sampler;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001909}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001910bool AnimationSampler::operator==(const AnimationSampler &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001911 return this->extras == other.extras && this->extensions == other.extensions &&
1912 this->input == other.input &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001913 this->interpolation == other.interpolation &&
1914 this->output == other.output;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001915}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001916bool Asset::operator==(const Asset &other) const {
1917 return this->copyright == other.copyright &&
1918 this->extensions == other.extensions && this->extras == other.extras &&
1919 this->generator == other.generator &&
1920 this->minVersion == other.minVersion && this->version == other.version;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001921}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001922bool Buffer::operator==(const Buffer &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001923 return this->data == other.data && this->extensions == other.extensions &&
1924 this->extras == other.extras && this->name == other.name &&
1925 this->uri == other.uri;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001926}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001927bool BufferView::operator==(const BufferView &other) const {
1928 return this->buffer == other.buffer && this->byteLength == other.byteLength &&
1929 this->byteOffset == other.byteOffset &&
1930 this->byteStride == other.byteStride && this->name == other.name &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001931 this->target == other.target && this->extensions == other.extensions &&
1932 this->extras == other.extras &&
Alexander Wood0d77a292019-01-26 08:58:45 -05001933 this->dracoDecoded == other.dracoDecoded;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001934}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001935bool Camera::operator==(const Camera &other) const {
1936 return this->name == other.name && this->extensions == other.extensions &&
1937 this->extras == other.extras &&
1938 this->orthographic == other.orthographic &&
1939 this->perspective == other.perspective && this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001940}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001941bool Image::operator==(const Image &other) const {
1942 return this->bufferView == other.bufferView &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001943 this->component == other.component &&
1944 this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001945 this->height == other.height && this->image == other.image &&
1946 this->mimeType == other.mimeType && this->name == other.name &&
1947 this->uri == other.uri && this->width == other.width;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001948}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001949bool Light::operator==(const Light &other) const {
1950 return Equals(this->color, other.color) && this->name == other.name &&
1951 this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001952}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001953bool Material::operator==(const Material &other) const {
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001954 return (this->pbrMetallicRoughness == other.pbrMetallicRoughness) &&
1955 (this->normalTexture == other.normalTexture) &&
1956 (this->occlusionTexture == other.occlusionTexture) &&
1957 (this->emissiveTexture == other.emissiveTexture) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09001958 Equals(this->emissiveFactor, other.emissiveFactor) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001959 (this->alphaMode == other.alphaMode) &&
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001960 TINYGLTF_DOUBLE_EQUAL(this->alphaCutoff, other.alphaCutoff) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001961 (this->doubleSided == other.doubleSided) &&
1962 (this->extensions == other.extensions) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09001963 (this->extras == other.extras) && (this->values == other.values) &&
1964 (this->additionalValues == other.additionalValues) &&
1965 (this->name == other.name);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001966}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001967bool Mesh::operator==(const Mesh &other) const {
1968 return this->extensions == other.extensions && this->extras == other.extras &&
Selmar Kok81b672b2019-10-18 16:08:44 +02001969 this->name == other.name && Equals(this->weights, other.weights) &&
1970 this->primitives == other.primitives;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001971}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001972bool Model::operator==(const Model &other) const {
1973 return this->accessors == other.accessors &&
1974 this->animations == other.animations && this->asset == other.asset &&
1975 this->buffers == other.buffers &&
1976 this->bufferViews == other.bufferViews &&
1977 this->cameras == other.cameras &&
1978 this->defaultScene == other.defaultScene &&
1979 this->extensions == other.extensions &&
1980 this->extensionsRequired == other.extensionsRequired &&
1981 this->extensionsUsed == other.extensionsUsed &&
1982 this->extras == other.extras && this->images == other.images &&
1983 this->lights == other.lights && this->materials == other.materials &&
1984 this->meshes == other.meshes && this->nodes == other.nodes &&
1985 this->samplers == other.samplers && this->scenes == other.scenes &&
1986 this->skins == other.skins && this->textures == other.textures;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001987}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001988bool Node::operator==(const Node &other) const {
1989 return this->camera == other.camera && this->children == other.children &&
1990 this->extensions == other.extensions && this->extras == other.extras &&
1991 Equals(this->matrix, other.matrix) && this->mesh == other.mesh &&
1992 this->name == other.name && Equals(this->rotation, other.rotation) &&
1993 Equals(this->scale, other.scale) && this->skin == other.skin &&
1994 Equals(this->translation, other.translation) &&
1995 Equals(this->weights, other.weights);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001996}
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001997bool SpotLight::operator==(const SpotLight &other) const {
1998 return this->extensions == other.extensions && this->extras == other.extras &&
1999 TINYGLTF_DOUBLE_EQUAL(this->innerConeAngle, other.innerConeAngle) &&
2000 TINYGLTF_DOUBLE_EQUAL(this->outerConeAngle, other.outerConeAngle);
2001}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002002bool OrthographicCamera::operator==(const OrthographicCamera &other) const {
2003 return this->extensions == other.extensions && this->extras == other.extras &&
2004 TINYGLTF_DOUBLE_EQUAL(this->xmag, other.xmag) &&
2005 TINYGLTF_DOUBLE_EQUAL(this->ymag, other.ymag) &&
2006 TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
2007 TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
Selmar Kok31cb7f92018-10-03 15:39:05 +02002008}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002009bool Parameter::operator==(const Parameter &other) const {
2010 if (this->bool_value != other.bool_value ||
2011 this->has_number_value != other.has_number_value)
2012 return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002013
Selmar Kok2bda71c2018-10-05 14:36:05 +02002014 if (!TINYGLTF_DOUBLE_EQUAL(this->number_value, other.number_value))
2015 return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002016
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002017 if (this->json_double_value.size() != other.json_double_value.size())
2018 return false;
2019 for (auto &it : this->json_double_value) {
2020 auto otherIt = other.json_double_value.find(it.first);
2021 if (otherIt == other.json_double_value.end()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002022
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002023 if (!TINYGLTF_DOUBLE_EQUAL(it.second, otherIt->second)) return false;
2024 }
2025
2026 if (!Equals(this->number_array, other.number_array)) return false;
2027
2028 if (this->string_value != other.string_value) return false;
2029
2030 return true;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002031}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002032bool PerspectiveCamera::operator==(const PerspectiveCamera &other) const {
2033 return TINYGLTF_DOUBLE_EQUAL(this->aspectRatio, other.aspectRatio) &&
2034 this->extensions == other.extensions && this->extras == other.extras &&
2035 TINYGLTF_DOUBLE_EQUAL(this->yfov, other.yfov) &&
2036 TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
2037 TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
Selmar Kok31cb7f92018-10-03 15:39:05 +02002038}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002039bool Primitive::operator==(const Primitive &other) const {
2040 return this->attributes == other.attributes && this->extras == other.extras &&
2041 this->indices == other.indices && this->material == other.material &&
2042 this->mode == other.mode && this->targets == other.targets;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002043}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002044bool Sampler::operator==(const Sampler &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09002045 return this->extensions == other.extensions && this->extras == other.extras &&
2046 this->magFilter == other.magFilter &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002047 this->minFilter == other.minFilter && this->name == other.name &&
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002048 this->wrapS == other.wrapS && this->wrapT == other.wrapT;
Syoyo Fujita2c521b32020-12-04 00:50:46 +09002049
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002050 // this->wrapR == other.wrapR
Selmar Kok31cb7f92018-10-03 15:39:05 +02002051}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002052bool Scene::operator==(const Scene &other) const {
2053 return this->extensions == other.extensions && this->extras == other.extras &&
2054 this->name == other.name && this->nodes == other.nodes;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002055}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002056bool Skin::operator==(const Skin &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09002057 return this->extensions == other.extensions && this->extras == other.extras &&
2058 this->inverseBindMatrices == other.inverseBindMatrices &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002059 this->joints == other.joints && this->name == other.name &&
2060 this->skeleton == other.skeleton;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002061}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002062bool Texture::operator==(const Texture &other) const {
2063 return this->extensions == other.extensions && this->extras == other.extras &&
2064 this->name == other.name && this->sampler == other.sampler &&
2065 this->source == other.source;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002066}
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09002067bool TextureInfo::operator==(const TextureInfo &other) const {
2068 return this->extensions == other.extensions && this->extras == other.extras &&
2069 this->index == other.index && this->texCoord == other.texCoord;
2070}
2071bool NormalTextureInfo::operator==(const NormalTextureInfo &other) const {
2072 return this->extensions == other.extensions && this->extras == other.extras &&
2073 this->index == other.index && this->texCoord == other.texCoord &&
2074 TINYGLTF_DOUBLE_EQUAL(this->scale, other.scale);
2075}
2076bool OcclusionTextureInfo::operator==(const OcclusionTextureInfo &other) const {
2077 return this->extensions == other.extensions && this->extras == other.extras &&
2078 this->index == other.index && this->texCoord == other.texCoord &&
2079 TINYGLTF_DOUBLE_EQUAL(this->strength, other.strength);
2080}
2081bool PbrMetallicRoughness::operator==(const PbrMetallicRoughness &other) const {
2082 return this->extensions == other.extensions && this->extras == other.extras &&
2083 (this->baseColorTexture == other.baseColorTexture) &&
2084 (this->metallicRoughnessTexture == other.metallicRoughnessTexture) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09002085 Equals(this->baseColorFactor, other.baseColorFactor) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09002086 TINYGLTF_DOUBLE_EQUAL(this->metallicFactor, other.metallicFactor) &&
2087 TINYGLTF_DOUBLE_EQUAL(this->roughnessFactor, other.roughnessFactor);
2088}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002089bool Value::operator==(const Value &other) const {
2090 return Equals(*this, other);
Selmar Kok31cb7f92018-10-03 15:39:05 +02002091}
2092
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002093static void swap4(unsigned int *val) {
2094#ifdef TINYGLTF_LITTLE_ENDIAN
2095 (void)val;
2096#else
2097 unsigned int tmp = *val;
2098 unsigned char *dst = reinterpret_cast<unsigned char *>(val);
2099 unsigned char *src = reinterpret_cast<unsigned char *>(&tmp);
2100
2101 dst[0] = src[3];
2102 dst[1] = src[2];
2103 dst[2] = src[1];
2104 dst[3] = src[0];
2105#endif
2106}
2107
Syoyo Fujitabeded612016-05-01 20:03:43 +09002108static std::string JoinPath(const std::string &path0,
2109 const std::string &path1) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002110 if (path0.empty()) {
2111 return path1;
2112 } else {
2113 // check '/'
2114 char lastChar = *path0.rbegin();
2115 if (lastChar != '/') {
2116 return path0 + std::string("/") + path1;
2117 } else {
2118 return path0 + path1;
2119 }
2120 }
2121}
2122
Syoyo Fujita643ce102016-05-01 17:19:37 +09002123static std::string FindFile(const std::vector<std::string> &paths,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002124 const std::string &filepath, FsCallbacks *fs) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002125 if (fs == nullptr || fs->ExpandFilePath == nullptr ||
2126 fs->FileExists == nullptr) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002127 // Error, fs callback[s] missing
2128 return std::string();
2129 }
2130
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002131 // https://github.com/syoyo/tinygltf/issues/416
2132 // Use strlen() since std::string's size/length reports the number of elements in the buffer, not the length of string(null-terminated)
2133 // strip null-character in the middle of string.
2134 size_t slength = strlen(filepath.c_str());
2135 if (slength == 0) {
2136 return std::string();
2137 }
2138
2139 std::string cleaned_filepath = std::string(filepath.c_str());
2140
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002141 for (size_t i = 0; i < paths.size(); i++) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002142 std::string absPath =
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002143 fs->ExpandFilePath(JoinPath(paths[i], cleaned_filepath), fs->user_data);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002144 if (fs->FileExists(absPath, fs->user_data)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002145 return absPath;
2146 }
2147 }
2148
2149 return std::string();
2150}
2151
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09002152static std::string GetFilePathExtension(const std::string &FileName) {
johan bowald642a3432018-04-01 12:37:18 +02002153 if (FileName.find_last_of(".") != std::string::npos)
2154 return FileName.substr(FileName.find_last_of(".") + 1);
2155 return "";
2156}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002157
Syoyo Fujita643ce102016-05-01 17:19:37 +09002158static std::string GetBaseDir(const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002159 if (filepath.find_last_of("/\\") != std::string::npos)
2160 return filepath.substr(0, filepath.find_last_of("/\\"));
2161 return "";
2162}
2163
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002164static std::string GetBaseFilename(const std::string &filepath) {
Martin Gerhardyfae65432022-03-13 18:42:39 +01002165 auto idx = filepath.find_last_of("/\\");
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002166 if (idx != std::string::npos) return filepath.substr(idx + 1);
Alexander Wood190382a2021-10-08 12:19:13 -04002167 return filepath;
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002168}
2169
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002170std::string base64_encode(unsigned char const *, unsigned int len);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002171std::string base64_decode(std::string const &s);
2172
2173/*
2174 base64.cpp and base64.h
2175
2176 Copyright (C) 2004-2008 René Nyffenegger
2177
2178 This source code is provided 'as-is', without any express or implied
2179 warranty. In no event will the author be held liable for any damages
2180 arising from the use of this software.
2181
2182 Permission is granted to anyone to use this software for any purpose,
2183 including commercial applications, and to alter it and redistribute it
2184 freely, subject to the following restrictions:
2185
2186 1. The origin of this source code must not be misrepresented; you must not
2187 claim that you wrote the original source code. If you use this source code
2188 in a product, an acknowledgment in the product documentation would be
2189 appreciated but is not required.
2190
2191 2. Altered source versions must be plainly marked as such, and must not be
2192 misrepresented as being the original source code.
2193
2194 3. This notice may not be removed or altered from any source distribution.
2195
2196 René Nyffenegger rene.nyffenegger@adp-gmbh.ch
2197
2198*/
2199
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002200#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09002201#pragma clang diagnostic push
Syoyo Fujita643ce102016-05-01 17:19:37 +09002202#pragma clang diagnostic ignored "-Wsign-conversion"
2203#pragma clang diagnostic ignored "-Wconversion"
Syoyo Fujita6fd91622016-05-01 20:27:30 +09002204#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002205
2206static inline bool is_base64(unsigned char c) {
2207 return (isalnum(c) || (c == '+') || (c == '/'));
2208}
2209
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002210std::string base64_encode(unsigned char const *bytes_to_encode,
2211 unsigned int in_len) {
johan bowald30c53472018-03-30 11:49:36 +02002212 std::string ret;
2213 int i = 0;
2214 int j = 0;
2215 unsigned char char_array_3[3];
2216 unsigned char char_array_4[4];
2217
Syoyo Fujitaff515702019-08-24 16:29:14 +09002218 const char *base64_chars =
2219 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2220 "abcdefghijklmnopqrstuvwxyz"
2221 "0123456789+/";
2222
johan bowald30c53472018-03-30 11:49:36 +02002223 while (in_len--) {
2224 char_array_3[i++] = *(bytes_to_encode++);
2225 if (i == 3) {
2226 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002227 char_array_4[1] =
2228 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
2229 char_array_4[2] =
2230 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
johan bowald30c53472018-03-30 11:49:36 +02002231 char_array_4[3] = char_array_3[2] & 0x3f;
2232
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002233 for (i = 0; (i < 4); i++) ret += base64_chars[char_array_4[i]];
johan bowald30c53472018-03-30 11:49:36 +02002234 i = 0;
2235 }
2236 }
2237
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002238 if (i) {
2239 for (j = i; j < 3; j++) char_array_3[j] = '\0';
johan bowald30c53472018-03-30 11:49:36 +02002240
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002241 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
2242 char_array_4[1] =
2243 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
2244 char_array_4[2] =
2245 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
johan bowald30c53472018-03-30 11:49:36 +02002246
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002247 for (j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]];
johan bowald30c53472018-03-30 11:49:36 +02002248
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002249 while ((i++ < 3)) ret += '=';
johan bowald30c53472018-03-30 11:49:36 +02002250 }
2251
2252 return ret;
johan bowald30c53472018-03-30 11:49:36 +02002253}
2254
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002255std::string base64_decode(std::string const &encoded_string) {
Syoyo Fujita8c6774c2016-05-01 20:36:29 +09002256 int in_len = static_cast<int>(encoded_string.size());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002257 int i = 0;
2258 int j = 0;
2259 int in_ = 0;
2260 unsigned char char_array_4[4], char_array_3[3];
2261 std::string ret;
2262
Syoyo Fujitaff515702019-08-24 16:29:14 +09002263 const std::string base64_chars =
2264 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2265 "abcdefghijklmnopqrstuvwxyz"
2266 "0123456789+/";
2267
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002268 while (in_len-- && (encoded_string[in_] != '=') &&
2269 is_base64(encoded_string[in_])) {
2270 char_array_4[i++] = encoded_string[in_];
2271 in_++;
2272 if (i == 4) {
2273 for (i = 0; i < 4; i++)
Syoyo Fujitabeded612016-05-01 20:03:43 +09002274 char_array_4[i] =
2275 static_cast<unsigned char>(base64_chars.find(char_array_4[i]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002276
2277 char_array_3[0] =
2278 (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
2279 char_array_3[1] =
2280 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
2281 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
2282
Syoyo Fujita7c877972016-03-08 01:31:49 +09002283 for (i = 0; (i < 3); i++) ret += char_array_3[i];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002284 i = 0;
2285 }
2286 }
2287
2288 if (i) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09002289 for (j = i; j < 4; j++) char_array_4[j] = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002290
2291 for (j = 0; j < 4; j++)
Syoyo Fujitabeded612016-05-01 20:03:43 +09002292 char_array_4[j] =
2293 static_cast<unsigned char>(base64_chars.find(char_array_4[j]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002294
2295 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
2296 char_array_3[1] =
2297 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
2298 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
2299
Syoyo Fujita7c877972016-03-08 01:31:49 +09002300 for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002301 }
2302
2303 return ret;
2304}
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002305#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09002306#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +09002307#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002308
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002309// https://github.com/syoyo/tinygltf/issues/228
2310// TODO(syoyo): Use uriparser https://uriparser.github.io/ for stricter Uri
2311// decoding?
2312//
Alexander Woode4bc6c72021-10-14 08:54:59 -04002313// Uri Decoding from DLIB
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002314// http://dlib.net/dlib/server/server_http.cpp.html
Alexander Woode4bc6c72021-10-14 08:54:59 -04002315// --- dlib begin ------------------------------------------------------------
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002316// Copyright (C) 2003 Davis E. King (davis@dlib.net)
Alexander Woode4bc6c72021-10-14 08:54:59 -04002317// License: Boost Software License
2318// Boost Software License - Version 1.0 - August 17th, 2003
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002319
Alexander Woode4bc6c72021-10-14 08:54:59 -04002320// Permission is hereby granted, free of charge, to any person or organization
2321// obtaining a copy of the software and accompanying documentation covered by
2322// this license (the "Software") to use, reproduce, display, distribute,
2323// execute, and transmit the Software, and to prepare derivative works of the
2324// Software, and to permit third-parties to whom the Software is furnished to
2325// do so, all subject to the following:
2326// The copyright notices in the Software and this entire statement, including
2327// the above license grant, this restriction and the following disclaimer,
2328// must be included in all copies of the Software, in whole or in part, and
2329// all derivative works of the Software, unless such copies or derivative
2330// works are solely in the form of machine-executable object code generated by
2331// a source language processor.
2332// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2333// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2334// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
2335// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
2336// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
2337// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2338// DEALINGS IN THE SOFTWARE.
Syoyo Fujitacbd38852022-02-26 21:19:15 +09002339//
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002340namespace dlib {
2341
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002342inline unsigned char from_hex(unsigned char ch) {
2343 if (ch <= '9' && ch >= '0')
2344 ch -= '0';
2345 else if (ch <= 'f' && ch >= 'a')
2346 ch -= 'a' - 10;
2347 else if (ch <= 'F' && ch >= 'A')
2348 ch -= 'A' - 10;
2349 else
2350 ch = 0;
2351 return ch;
2352}
2353
2354static const std::string urldecode(const std::string &str) {
2355 using namespace std;
2356 string result;
2357 string::size_type i;
2358 for (i = 0; i < str.size(); ++i) {
2359 if (str[i] == '+') {
2360 result += ' ';
2361 } else if (str[i] == '%' && str.size() > i + 2) {
2362 const unsigned char ch1 =
2363 from_hex(static_cast<unsigned char>(str[i + 1]));
2364 const unsigned char ch2 =
2365 from_hex(static_cast<unsigned char>(str[i + 2]));
2366 const unsigned char ch = static_cast<unsigned char>((ch1 << 4) | ch2);
2367 result += static_cast<char>(ch);
2368 i += 2;
2369 } else {
2370 result += str[i];
2371 }
2372 }
2373 return result;
2374}
2375
2376} // namespace dlib
2377// --- dlib end --------------------------------------------------------------
2378
Pyarelal Knowles385946d2023-01-03 18:18:04 -08002379bool URIDecode(const std::string &in_uri, std::string *out_uri,
2380 void *user_data) {
2381 (void)user_data;
2382 *out_uri = dlib::urldecode(in_uri);
2383 return true;
2384}
2385
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002386static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002387 std::string *warn, const std::string &filename,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002388 const std::string &basedir, bool required,
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002389 size_t reqBytes, bool checkSize, size_t maxFileSize, FsCallbacks *fs) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002390 if (fs == nullptr || fs->FileExists == nullptr ||
2391 fs->ExpandFilePath == nullptr || fs->ReadWholeFile == nullptr) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002392 // This is a developer error, assert() ?
2393 if (err) {
2394 (*err) += "FS callback[s] not set\n";
2395 }
2396 return false;
2397 }
2398
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002399 std::string *failMsgOut = required ? err : warn;
Selmar Koke3b3fa92018-08-22 19:04:21 +02002400
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002401 out->clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002402
2403 std::vector<std::string> paths;
2404 paths.push_back(basedir);
2405 paths.push_back(".");
2406
Paolo Jovone6601bf2018-07-07 20:43:33 +02002407 std::string filepath = FindFile(paths, filename, fs);
jianghaosenb721fb72017-08-25 21:01:17 +08002408 if (filepath.empty() || filename.empty()) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002409 if (failMsgOut) {
2410 (*failMsgOut) += "File not found : " + filename + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002411 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002412 return false;
2413 }
2414
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002415 // Check file size
2416 if (fs->GetFileSizeInBytes) {
2417
2418 size_t file_size{0};
2419 std::string _err;
David Siegelcc93e1f2023-04-26 12:13:41 +02002420 bool ok = fs->GetFileSizeInBytes(&file_size, &_err, filepath, fs->user_data);
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002421 if (!ok) {
2422 if (_err.size()) {
2423 if (failMsgOut) {
2424 (*failMsgOut) += "Getting file size failed : " + filename + ", err = " + _err + "\n";
2425 }
2426 }
2427 return false;
2428 }
2429
2430 if (file_size > maxFileSize) {
2431 if (failMsgOut) {
2432 (*failMsgOut) += "File size " + std::to_string(file_size) + " exceeds maximum allowed file size " + std::to_string(maxFileSize) + " : " + filepath + "\n";
2433 }
2434 return false;
2435 }
2436 }
2437
Paolo Jovone6601bf2018-07-07 20:43:33 +02002438 std::vector<unsigned char> buf;
2439 std::string fileReadErr;
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002440 bool fileRead =
2441 fs->ReadWholeFile(&buf, &fileReadErr, filepath, fs->user_data);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002442 if (!fileRead) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002443 if (failMsgOut) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002444 (*failMsgOut) +=
2445 "File read error : " + filepath + " : " + fileReadErr + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002446 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002447 return false;
2448 }
2449
Paolo Jovone6601bf2018-07-07 20:43:33 +02002450 size_t sz = buf.size();
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09002451 if (sz == 0) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002452 if (failMsgOut) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002453 (*failMsgOut) += "File is empty : " + filepath + "\n";
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002454 }
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09002455 return false;
2456 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002457
2458 if (checkSize) {
2459 if (reqBytes == sz) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002460 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002461 return true;
2462 } else {
2463 std::stringstream ss;
2464 ss << "File size mismatch : " << filepath << ", requestedBytes "
2465 << reqBytes << ", but got " << sz << std::endl;
Selmar Koke3b3fa92018-08-22 19:04:21 +02002466 if (failMsgOut) {
2467 (*failMsgOut) += ss.str();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002468 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002469 return false;
2470 }
2471 }
2472
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002473 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002474 return true;
2475}
2476
Squareysff644d82018-03-13 22:36:18 +01002477void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002478 LoadImageData = func;
2479 load_image_user_data_ = user_data;
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002480 user_image_loader_ = true;
2481}
2482
2483void TinyGLTF::RemoveImageLoader() {
2484 LoadImageData =
2485#ifndef TINYGLTF_NO_STB_IMAGE
2486 &tinygltf::LoadImageData;
2487#else
2488 nullptr;
2489#endif
2490
2491 load_image_user_data_ = nullptr;
2492 user_image_loader_ = false;
Squareysff644d82018-03-13 22:36:18 +01002493}
2494
Squareys2d3594d2018-03-13 22:40:53 +01002495#ifndef TINYGLTF_NO_STB_IMAGE
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002496bool LoadImageData(Image *image, const int image_idx, std::string *err,
2497 std::string *warn, int req_width, int req_height,
2498 const unsigned char *bytes, int size, void *user_data) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002499 (void)warn;
2500
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002501 LoadImageDataOption option;
2502 if (user_data) {
2503 option = *reinterpret_cast<LoadImageDataOption *>(user_data);
2504 }
2505
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002506 int w = 0, h = 0, comp = 0, req_comp = 0;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002507
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002508 unsigned char *data = nullptr;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002509
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002510 // preserve_channels true: Use channels stored in the image file.
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09002511 // false: force 32-bit textures for common Vulkan compatibility. It appears
2512 // that some GPU drivers do not support 24-bit images for Vulkan
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002513 req_comp = option.preserve_channels ? 0 : 4;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002514 int bits = 8;
2515 int pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002516
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002517 // It is possible that the image we want to load is a 16bit per channel image
2518 // We are going to attempt to load it as 16bit per channel, and if it worked,
imallettd9ce9eb2022-10-07 10:37:09 -07002519 // set the image data accordingly. We are casting the returned pointer into
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002520 // unsigned char, because we are representing "bytes". But we are updating
2521 // the Image metadata to signal that this image uses 2 bytes (16bits) per
2522 // channel:
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002523 if (stbi_is_16_bit_from_memory(bytes, size)) {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002524 data = reinterpret_cast<unsigned char *>(
2525 stbi_load_16_from_memory(bytes, size, &w, &h, &comp, req_comp));
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002526 if (data) {
2527 bits = 16;
2528 pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT;
2529 }
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002530 }
2531
2532 // at this point, if data is still NULL, it means that the image wasn't
2533 // 16bit per channel, we are going to load it as a normal 8bit per channel
imallettd9ce9eb2022-10-07 10:37:09 -07002534 // image as we used to do:
Aurélien Chatelain5cb43462017-05-24 09:55:14 +00002535 // if image cannot be decoded, ignore parsing and keep it by its path
2536 // don't break in this case
Syoyo Fujita5b407452017-06-04 17:42:41 +09002537 // FIXME we should only enter this function if the image is embedded. If
2538 // image->uri references
2539 // an image file, it should be left as it is. Image loading should not be
2540 // mandatory (to support other formats)
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002541 if (!data) data = stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002542 if (!data) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002543 // NOTE: you can use `warn` instead of `err`
Syoyo Fujitabeded612016-05-01 20:03:43 +09002544 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002545 (*err) +=
2546 "Unknown image format. STB cannot decode image data for image[" +
2547 std::to_string(image_idx) + "] name = \"" + image->name + "\".\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002548 }
Omar C. Fd492efc2018-02-10 09:50:35 +02002549 return false;
Syoyo Fujitabeded612016-05-01 20:03:43 +09002550 }
2551
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002552 if ((w < 1) || (h < 1)) {
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002553 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002554 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002555 (*err) += "Invalid image data for image[" + std::to_string(image_idx) +
2556 "] name = \"" + image->name + "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002557 }
Omar C. Fd492efc2018-02-10 09:50:35 +02002558 return false;
Syoyo Fujitabeded612016-05-01 20:03:43 +09002559 }
2560
2561 if (req_width > 0) {
2562 if (req_width != w) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002563 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002564 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002565 (*err) += "Image width mismatch for image[" +
2566 std::to_string(image_idx) + "] name = \"" + image->name +
2567 "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002568 }
2569 return false;
2570 }
2571 }
2572
2573 if (req_height > 0) {
2574 if (req_height != h) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002575 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002576 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002577 (*err) += "Image height mismatch. for image[" +
2578 std::to_string(image_idx) + "] name = \"" + image->name +
2579 "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002580 }
2581 return false;
2582 }
2583 }
2584
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002585 if (req_comp != 0) {
2586 // loaded data has `req_comp` channels(components)
2587 comp = req_comp;
2588 }
2589
Syoyo Fujitabeded612016-05-01 20:03:43 +09002590 image->width = w;
2591 image->height = h;
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002592 image->component = comp;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002593 image->bits = bits;
2594 image->pixel_type = pixel_type;
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002595 image->image.resize(static_cast<size_t>(w * h * comp) * size_t(bits / 8));
2596 std::copy(data, data + w * h * comp * (bits / 8), image->image.begin());
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002597 stbi_image_free(data);
Syoyo Fujita4be2f882016-11-24 16:20:34 +09002598
Syoyo Fujitabeded612016-05-01 20:03:43 +09002599 return true;
2600}
Squareys2d3594d2018-03-13 22:40:53 +01002601#endif
Syoyo Fujitabeded612016-05-01 20:03:43 +09002602
johan bowald642a3432018-04-01 12:37:18 +02002603void TinyGLTF::SetImageWriter(WriteImageDataFunction func, void *user_data) {
2604 WriteImageData = func;
2605 write_image_user_data_ = user_data;
2606}
2607
2608#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
2609static void WriteToMemory_stbi(void *context, void *data, int size) {
2610 std::vector<unsigned char> *buffer =
2611 reinterpret_cast<std::vector<unsigned char> *>(context);
2612
2613 unsigned char *pData = reinterpret_cast<unsigned char *>(data);
2614
2615 buffer->insert(buffer->end(), pData, pData + size);
2616}
2617
2618bool WriteImageData(const std::string *basepath, const std::string *filename,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08002619 const Image *image, bool embedImages,
2620 const URICallbacks *uri_cb, std::string *out_uri,
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08002621 void *fsPtr) {
johan bowald642a3432018-04-01 12:37:18 +02002622 const std::string ext = GetFilePathExtension(*filename);
2623
Paolo Jovone6601bf2018-07-07 20:43:33 +02002624 // Write image to temporary buffer
2625 std::string header;
2626 std::vector<unsigned char> data;
johan bowald642a3432018-04-01 12:37:18 +02002627
Paolo Jovone6601bf2018-07-07 20:43:33 +02002628 if (ext == "png") {
Syoyo Fujitaca56f722019-03-07 21:04:25 +09002629 if ((image->bits != 8) ||
2630 (image->pixel_type != TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)) {
Syoyo Fujitae8a46c42019-03-07 20:50:58 +09002631 // Unsupported pixel format
2632 return false;
2633 }
2634
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002635 if (!stbi_write_png_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002636 image->height, image->component,
2637 &image->image[0], 0)) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002638 return false;
2639 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002640 header = "data:image/png;base64,";
2641 } else if (ext == "jpg") {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002642 if (!stbi_write_jpg_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002643 image->height, image->component,
2644 &image->image[0], 100)) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002645 return false;
2646 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002647 header = "data:image/jpeg;base64,";
2648 } else if (ext == "bmp") {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002649 if (!stbi_write_bmp_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002650 image->height, image->component,
2651 &image->image[0])) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002652 return false;
2653 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002654 header = "data:image/bmp;base64,";
2655 } else if (!embedImages) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002656 // Error: can't output requested format to file
2657 return false;
Paolo Jovone6601bf2018-07-07 20:43:33 +02002658 }
johan bowald642a3432018-04-01 12:37:18 +02002659
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002660 if (embedImages) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002661 // Embed base64-encoded image into URI
johan bowald642a3432018-04-01 12:37:18 +02002662 if (data.size()) {
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08002663 *out_uri = header +
2664 base64_encode(&data[0], static_cast<unsigned int>(data.size()));
johan bowald642a3432018-04-01 12:37:18 +02002665 } else {
2666 // Throw error?
2667 }
2668 } else {
2669 // Write image to disc
Paolo Jovone6601bf2018-07-07 20:43:33 +02002670 FsCallbacks *fs = reinterpret_cast<FsCallbacks *>(fsPtr);
Syoyo Fujita33514af2018-11-05 13:32:43 +09002671 if ((fs != nullptr) && (fs->WriteWholeFile != nullptr)) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002672 const std::string imagefilepath = JoinPath(*basepath, *filename);
2673 std::string writeError;
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002674 if (!fs->WriteWholeFile(&writeError, imagefilepath, data,
2675 fs->user_data)) {
2676 // Could not write image file to disc; Throw error ?
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002677 return false;
Paolo Jovone6601bf2018-07-07 20:43:33 +02002678 }
johan bowald642a3432018-04-01 12:37:18 +02002679 } else {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002680 // Throw error?
johan bowald642a3432018-04-01 12:37:18 +02002681 }
Pyarelal Knowles385946d2023-01-03 18:18:04 -08002682 if (uri_cb->encode) {
2683 if (!uri_cb->encode(*filename, "image", out_uri, uri_cb->user_data)) {
2684 return false;
2685 }
2686 } else {
2687 *out_uri = *filename;
2688 }
johan bowald642a3432018-04-01 12:37:18 +02002689 }
2690
2691 return true;
2692}
2693#endif
2694
Pyarelal Knowles385946d2023-01-03 18:18:04 -08002695void TinyGLTF::SetURICallbacks(URICallbacks callbacks) {
2696 assert(callbacks.decode);
2697 if (callbacks.decode) {
2698 uri_cb = callbacks;
2699 }
2700}
2701
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002702void TinyGLTF::SetFsCallbacks(FsCallbacks callbacks) { fs = callbacks; }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002703
Harokyangfb256602019-10-30 16:13:52 +08002704#ifdef _WIN32
2705static inline std::wstring UTF8ToWchar(const std::string &str) {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002706 int wstr_size =
2707 MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
imallett56e10982022-10-07 10:35:16 -07002708 std::wstring wstr((size_t)wstr_size, 0);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002709 MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0],
2710 (int)wstr.size());
Harokyangfb256602019-10-30 16:13:52 +08002711 return wstr;
2712}
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002713
2714static inline std::string WcharToUTF8(const std::wstring &wstr) {
2715 int str_size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(),
imallett56e10982022-10-07 10:35:16 -07002716 nullptr, 0, nullptr, nullptr);
2717 std::string str((size_t)str_size, 0);
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002718 WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), &str[0],
imallett56e10982022-10-07 10:35:16 -07002719 (int)str.size(), nullptr, nullptr);
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002720 return str;
2721}
Harokyangfb256602019-10-30 16:13:52 +08002722#endif
2723
Paolo Jovone6601bf2018-07-07 20:43:33 +02002724#ifndef TINYGLTF_NO_FS
2725// Default implementations of filesystem functions
2726
2727bool FileExists(const std::string &abs_filename, void *) {
2728 bool ret;
Sascha Willems5f9cb242018-12-28 20:53:41 +01002729#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002730 if (asset_manager) {
2731 AAsset *asset = AAssetManager_open(asset_manager, abs_filename.c_str(),
2732 AASSET_MODE_STREAMING);
2733 if (!asset) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01002734 return false;
2735 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002736 AAsset_close(asset);
2737 ret = true;
2738 } else {
2739 return false;
2740 }
Sascha Willems5f9cb242018-12-28 20:53:41 +01002741#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002742#ifdef _WIN32
operatios1668d1e2022-09-24 22:37:14 +03002743#if defined(_MSC_VER) || defined(__GLIBCXX__) || defined(_LIBCPP_VERSION)
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002744
2745 // First check if a file is a directory.
2746 DWORD result = GetFileAttributesW(UTF8ToWchar(abs_filename).c_str());
2747 if (result == INVALID_FILE_ATTRIBUTES) {
2748 return false;
2749 }
2750 if (result & FILE_ATTRIBUTE_DIRECTORY) {
2751 return false;
2752 }
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002753
Syoyo Fujita45cac782019-11-09 20:42:55 +09002754 FILE *fp = nullptr;
Harokyangfb256602019-10-30 16:13:52 +08002755 errno_t err = _wfopen_s(&fp, UTF8ToWchar(abs_filename).c_str(), L"rb");
Paolo Jovone6601bf2018-07-07 20:43:33 +02002756 if (err != 0) {
2757 return false;
2758 }
2759#else
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002760 // TODO: is_directory check
Syoyo Fujita45cac782019-11-09 20:42:55 +09002761 FILE *fp = nullptr;
2762 errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb");
2763 if (err != 0) {
2764 return false;
2765 }
2766#endif
2767
2768#else
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002769 struct stat sb;
2770 if (stat(abs_filename.c_str(), &sb)) {
2771 return false;
2772 }
2773 if (S_ISDIR(sb.st_mode)) {
2774 return false;
2775 }
2776
Paolo Jovone6601bf2018-07-07 20:43:33 +02002777 FILE *fp = fopen(abs_filename.c_str(), "rb");
2778#endif
2779 if (fp) {
2780 ret = true;
2781 fclose(fp);
2782 } else {
2783 ret = false;
2784 }
Sascha Willems5f9cb242018-12-28 20:53:41 +01002785#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002786
2787 return ret;
2788}
2789
2790std::string ExpandFilePath(const std::string &filepath, void *) {
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002791 // https://github.com/syoyo/tinygltf/issues/368
2792 //
2793 // No file path expansion in built-in FS function anymore, since glTF URI
2794 // should not contain tilde('~') and environment variables, and for security
2795 // reason(`wordexp`).
2796 //
2797 // Users need to supply `base_dir`(in `LoadASCIIFromString`,
2798 // `LoadBinaryFromMemory`) in expanded absolute path.
2799
2800 return filepath;
2801
2802#if 0
Paolo Jovone6601bf2018-07-07 20:43:33 +02002803#ifdef _WIN32
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002804 // Assume input `filepath` is encoded in UTF-8
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002805 std::wstring wfilepath = UTF8ToWchar(filepath);
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002806 DWORD wlen = ExpandEnvironmentStringsW(wfilepath.c_str(), nullptr, 0);
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002807 wchar_t *wstr = new wchar_t[wlen];
2808 ExpandEnvironmentStringsW(wfilepath.c_str(), wstr, wlen);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002809
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002810 std::wstring ws(wstr);
2811 delete[] wstr;
2812 return WcharToUTF8(ws);
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002813
Paolo Jovone6601bf2018-07-07 20:43:33 +02002814#else
2815
2816#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \
Julian Smith0598a202021-08-25 12:06:08 +01002817 defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__OpenBSD__)
Paolo Jovone6601bf2018-07-07 20:43:33 +02002818 // no expansion
2819 std::string s = filepath;
2820#else
2821 std::string s;
2822 wordexp_t p;
2823
2824 if (filepath.empty()) {
2825 return "";
2826 }
2827
Frank Galliganaa3c5a12020-01-13 15:06:56 -08002828 // Quote the string to keep any spaces in filepath intact.
2829 std::string quoted_path = "\"" + filepath + "\"";
Paolo Jovone6601bf2018-07-07 20:43:33 +02002830 // char** w;
Frank Galliganaa3c5a12020-01-13 15:06:56 -08002831 int ret = wordexp(quoted_path.c_str(), &p, 0);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002832 if (ret) {
2833 // err
2834 s = filepath;
2835 return s;
2836 }
2837
2838 // Use first element only.
2839 if (p.we_wordv) {
2840 s = std::string(p.we_wordv[0]);
2841 wordfree(&p);
2842 } else {
2843 s = filepath;
2844 }
2845
2846#endif
2847
2848 return s;
2849#endif
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002850#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002851}
2852
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002853bool GetFileSizeInBytes(size_t *filesize_out, std::string *err,
2854 const std::string &filepath, void *userdata) {
2855 (void)userdata;
2856
2857#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
2858 if (asset_manager) {
2859 AAsset *asset = AAssetManager_open(asset_manager, filepath.c_str(),
2860 AASSET_MODE_STREAMING);
2861 if (!asset) {
2862 if (err) {
2863 (*err) += "File open error : " + filepath + "\n";
2864 }
2865 return false;
2866 }
2867 size_t size = AAsset_getLength(asset);
2868
2869 if (size == 0) {
2870 if (err) {
2871 (*err) += "Invalid file size : " + filepath +
2872 " (does the path point to a directory?)";
2873 }
2874 return false;
2875 }
2876
2877 return true;
2878 } else {
2879 if (err) {
2880 (*err) += "No asset manager specified : " + filepath + "\n";
2881 }
2882 return false;
2883 }
2884#else
2885#ifdef _WIN32
2886#if defined(__GLIBCXX__) // mingw
2887 int file_descriptor =
2888 _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY);
2889 __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
2890 std::istream f(&wfile_buf);
2891#elif defined(_MSC_VER) || defined(_LIBCPP_VERSION)
2892 // For libcxx, assume _LIBCPP_HAS_OPEN_WITH_WCHAR is defined to accept
2893 // `wchar_t *`
2894 std::ifstream f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary);
2895#else
2896 // Unknown compiler/runtime
2897 std::ifstream f(filepath.c_str(), std::ifstream::binary);
2898#endif
2899#else
2900 std::ifstream f(filepath.c_str(), std::ifstream::binary);
2901#endif
2902 if (!f) {
2903 if (err) {
2904 (*err) += "File open error : " + filepath + "\n";
2905 }
2906 return false;
2907 }
2908
2909 // For directory(and pipe?), peek() will fail(Posix gnustl/libc++ only)
David Siegel47208b22023-06-05 22:14:30 +02002910 f.peek();
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002911 if (!f) {
2912 if (err) {
2913 (*err) += "File read error. Maybe empty file or invalid file : " + filepath + "\n";
2914 }
2915 return false;
2916 }
2917
2918 f.seekg(0, f.end);
2919 size_t sz = static_cast<size_t>(f.tellg());
2920
2921 //std::cout << "sz = " << sz << "\n";
2922 f.seekg(0, f.beg);
2923
2924 if (int64_t(sz) < 0) {
2925 if (err) {
2926 (*err) += "Invalid file size : " + filepath +
2927 " (does the path point to a directory?)";
2928 }
2929 return false;
2930 } else if (sz == 0) {
2931 if (err) {
2932 (*err) += "File is empty : " + filepath + "\n";
2933 }
2934 return false;
2935 } else if (sz >= (std::numeric_limits<std::streamoff>::max)()) {
2936 if (err) {
2937 (*err) += "Invalid file size : " + filepath + "\n";
2938 }
2939 return false;
2940 }
2941
2942 (*filesize_out) = sz;
2943 return true;
2944#endif
2945}
2946
Paolo Jovone6601bf2018-07-07 20:43:33 +02002947bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
2948 const std::string &filepath, void *) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01002949#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
2950 if (asset_manager) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002951 AAsset *asset = AAssetManager_open(asset_manager, filepath.c_str(),
2952 AASSET_MODE_STREAMING);
Sascha Willems5f9cb242018-12-28 20:53:41 +01002953 if (!asset) {
2954 if (err) {
2955 (*err) += "File open error : " + filepath + "\n";
2956 }
2957 return false;
2958 }
2959 size_t size = AAsset_getLength(asset);
Syoyo Fujita73c4cce2020-04-28 01:06:34 +09002960 if (size == 0) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01002961 if (err) {
2962 (*err) += "Invalid file size : " + filepath +
2963 " (does the path point to a directory?)";
2964 }
Syoyo Fujita73c4cce2020-04-28 01:06:34 +09002965 return false;
Sascha Willems5f9cb242018-12-28 20:53:41 +01002966 }
2967 out->resize(size);
2968 AAsset_read(asset, reinterpret_cast<char *>(&out->at(0)), size);
2969 AAsset_close(asset);
2970 return true;
2971 } else {
2972 if (err) {
2973 (*err) += "No asset manager specified : " + filepath + "\n";
2974 }
2975 return false;
2976 }
2977#else
Harokyang5cecef22019-10-30 15:16:46 +08002978#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002979#if defined(__GLIBCXX__) // mingw
2980 int file_descriptor =
2981 _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY);
Syoyo Fujita45cac782019-11-09 20:42:55 +09002982 __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
2983 std::istream f(&wfile_buf);
Syoyo Fujitaaba57bb2020-05-18 20:50:45 +09002984#elif defined(_MSC_VER) || defined(_LIBCPP_VERSION)
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04002985 // For libcxx, assume _LIBCPP_HAS_OPEN_WITH_WCHAR is defined to accept
2986 // `wchar_t *`
Harokyangfb256602019-10-30 16:13:52 +08002987 std::ifstream f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary);
Syoyo Fujitaaba57bb2020-05-18 20:50:45 +09002988#else
2989 // Unknown compiler/runtime
Syoyo Fujita45cac782019-11-09 20:42:55 +09002990 std::ifstream f(filepath.c_str(), std::ifstream::binary);
2991#endif
Harokyang5cecef22019-10-30 15:16:46 +08002992#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002993 std::ifstream f(filepath.c_str(), std::ifstream::binary);
Harokyang5cecef22019-10-30 15:16:46 +08002994#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002995 if (!f) {
2996 if (err) {
2997 (*err) += "File open error : " + filepath + "\n";
2998 }
2999 return false;
3000 }
3001
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09003002 // For directory(and pipe?), peek() will fail(Posix gnustl/libc++ only)
David Siegel47208b22023-06-05 22:14:30 +02003003 f.peek();
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09003004 if (!f) {
3005 if (err) {
3006 (*err) += "File read error. Maybe empty file or invalid file : " + filepath + "\n";
3007 }
3008 return false;
3009 }
3010
Paolo Jovone6601bf2018-07-07 20:43:33 +02003011 f.seekg(0, f.end);
3012 size_t sz = static_cast<size_t>(f.tellg());
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09003013
3014 //std::cout << "sz = " << sz << "\n";
Paolo Jovone6601bf2018-07-07 20:43:33 +02003015 f.seekg(0, f.beg);
3016
Syoyo Fujitae8862472019-10-20 17:47:50 +09003017 if (int64_t(sz) < 0) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02003018 if (err) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003019 (*err) += "Invalid file size : " + filepath +
3020 " (does the path point to a directory?)";
Paolo Jovone6601bf2018-07-07 20:43:33 +02003021 }
3022 return false;
3023 } else if (sz == 0) {
3024 if (err) {
3025 (*err) += "File is empty : " + filepath + "\n";
3026 }
3027 return false;
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09003028 } else if (sz >= (std::numeric_limits<std::streamoff>::max)()) {
3029 if (err) {
3030 (*err) += "Invalid file size : " + filepath + "\n";
3031 }
3032 return false;
Paolo Jovone6601bf2018-07-07 20:43:33 +02003033 }
3034
3035 out->resize(sz);
3036 f.read(reinterpret_cast<char *>(&out->at(0)),
3037 static_cast<std::streamsize>(sz));
Paolo Jovone6601bf2018-07-07 20:43:33 +02003038
3039 return true;
Sascha Willems5f9cb242018-12-28 20:53:41 +01003040#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02003041}
3042
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003043bool WriteWholeFile(std::string *err, const std::string &filepath,
3044 const std::vector<unsigned char> &contents, void *) {
Harokyangfb256602019-10-30 16:13:52 +08003045#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09003046#if defined(__GLIBCXX__) // mingw
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09003047 int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(),
3048 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
3049 __gnu_cxx::stdio_filebuf<char> wfile_buf(
3050 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09003051 std::ostream f(&wfile_buf);
3052#elif defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08003053 std::ofstream f(UTF8ToWchar(filepath).c_str(), std::ofstream::binary);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09003054#else // clang?
Syoyo Fujita45cac782019-11-09 20:42:55 +09003055 std::ofstream f(filepath.c_str(), std::ofstream::binary);
3056#endif
Harokyangfb256602019-10-30 16:13:52 +08003057#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02003058 std::ofstream f(filepath.c_str(), std::ofstream::binary);
Harokyangfb256602019-10-30 16:13:52 +08003059#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02003060 if (!f) {
3061 if (err) {
3062 (*err) += "File open error for writing : " + filepath + "\n";
3063 }
3064 return false;
3065 }
3066
3067 f.write(reinterpret_cast<const char *>(&contents.at(0)),
3068 static_cast<std::streamsize>(contents.size()));
3069 if (!f) {
3070 if (err) {
3071 (*err) += "File write error: " + filepath + "\n";
3072 }
3073 return false;
3074 }
3075
Paolo Jovone6601bf2018-07-07 20:43:33 +02003076 return true;
3077}
3078
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003079#endif // TINYGLTF_NO_FS
Paolo Jovone6601bf2018-07-07 20:43:33 +02003080
johan bowald642a3432018-04-01 12:37:18 +02003081static std::string MimeToExt(const std::string &mimeType) {
3082 if (mimeType == "image/jpeg") {
3083 return "jpg";
3084 } else if (mimeType == "image/png") {
3085 return "png";
3086 } else if (mimeType == "image/bmp") {
3087 return "bmp";
3088 } else if (mimeType == "image/gif") {
3089 return "gif";
3090 }
3091
3092 return "";
3093}
3094
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08003095static bool UpdateImageObject(const Image &image, std::string &baseDir,
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08003096 int index, bool embedImages,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08003097 const URICallbacks *uri_cb,
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08003098 WriteImageDataFunction *WriteImageData,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08003099 void *user_data, std::string *out_uri) {
johan bowald642a3432018-04-01 12:37:18 +02003100 std::string filename;
3101 std::string ext;
imallettd9ce9eb2022-10-07 10:37:09 -07003102 // If image has uri, use it as a filename
johan bowald642a3432018-04-01 12:37:18 +02003103 if (image.uri.size()) {
Pyarelal Knowles385946d2023-01-03 18:18:04 -08003104 std::string decoded_uri;
3105 if (!uri_cb->decode(image.uri, &decoded_uri, uri_cb->user_data)) {
3106 // A decode failure results in a failure to write the gltf.
3107 return false;
3108 }
3109 filename = GetBaseFilename(decoded_uri);
johan bowald642a3432018-04-01 12:37:18 +02003110 ext = GetFilePathExtension(filename);
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09003111 } else if (image.bufferView != -1) {
3112 // If there's no URI and the data exists in a buffer,
3113 // don't change properties or write images
johan bowald642a3432018-04-01 12:37:18 +02003114 } else if (image.name.size()) {
3115 ext = MimeToExt(image.mimeType);
3116 // Otherwise use name as filename
3117 filename = image.name + "." + ext;
3118 } else {
3119 ext = MimeToExt(image.mimeType);
3120 // Fallback to index of image as filename
3121 filename = std::to_string(index) + "." + ext;
3122 }
3123
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08003124 // If callback is set and image data exists, modify image data object. If
3125 // image data does not exist, this is not considered a failure and the
3126 // original uri should be maintained.
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08003127 bool imageWritten = false;
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08003128 if (*WriteImageData != nullptr && !filename.empty() && !image.image.empty()) {
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08003129 imageWritten = (*WriteImageData)(&baseDir, &filename, &image, embedImages,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08003130 uri_cb, out_uri, user_data);
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08003131 if (!imageWritten) {
3132 return false;
3133 }
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08003134 }
3135
3136 // Use the original uri if the image was not written.
3137 if (!imageWritten) {
3138 *out_uri = image.uri;
johan bowald642a3432018-04-01 12:37:18 +02003139 }
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08003140
3141 return true;
johan bowald642a3432018-04-01 12:37:18 +02003142}
3143
Selmar Kok0d0e97e2018-08-22 14:01:57 +02003144bool IsDataURI(const std::string &in) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003145 std::string header = "data:application/octet-stream;base64,";
3146 if (in.find(header) == 0) {
3147 return true;
3148 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09003149
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02003150 header = "data:image/jpeg;base64,";
3151 if (in.find(header) == 0) {
3152 return true;
3153 }
Squareys43374632018-03-13 22:20:48 +01003154
Syoyo Fujita620eed12016-01-02 23:37:12 +09003155 header = "data:image/png;base64,";
3156 if (in.find(header) == 0) {
3157 return true;
3158 }
Squareys43374632018-03-13 22:20:48 +01003159
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02003160 header = "data:image/bmp;base64,";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003161 if (in.find(header) == 0) {
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02003162 return true;
3163 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09003164
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02003165 header = "data:image/gif;base64,";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003166 if (in.find(header) == 0) {
Syoyo Fujita620eed12016-01-02 23:37:12 +09003167 return true;
3168 }
3169
Luke San Antoniocaa24b02016-06-15 02:23:09 -04003170 header = "data:text/plain;base64,";
3171 if (in.find(header) == 0) {
3172 return true;
3173 }
3174
Syoyo Fujita20244e12018-03-15 11:01:05 -05003175 header = "data:application/gltf-buffer;base64,";
3176 if (in.find(header) == 0) {
3177 return true;
3178 }
3179
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003180 return false;
3181}
3182
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09003183bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
3184 const std::string &in, size_t reqBytes, bool checkSize) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003185 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujita620eed12016-01-02 23:37:12 +09003186 std::string data;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003187 if (in.find(header) == 0) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09003188 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003189 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09003190
3191 if (data.empty()) {
3192 header = "data:image/jpeg;base64,";
3193 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02003194 mime_type = "image/jpeg";
Syoyo Fujita7c877972016-03-08 01:31:49 +09003195 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09003196 }
3197 }
3198
3199 if (data.empty()) {
3200 header = "data:image/png;base64,";
3201 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02003202 mime_type = "image/png";
Syoyo Fujita7c877972016-03-08 01:31:49 +09003203 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09003204 }
3205 }
Squareys43374632018-03-13 22:20:48 +01003206
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02003207 if (data.empty()) {
3208 header = "data:image/bmp;base64,";
3209 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02003210 mime_type = "image/bmp";
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02003211 data = base64_decode(in.substr(header.size())); // cut mime string.
3212 }
3213 }
3214
3215 if (data.empty()) {
3216 header = "data:image/gif;base64,";
3217 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02003218 mime_type = "image/gif";
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02003219 data = base64_decode(in.substr(header.size())); // cut mime string.
3220 }
3221 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09003222
3223 if (data.empty()) {
Luke San Antoniocaa24b02016-06-15 02:23:09 -04003224 header = "data:text/plain;base64,";
3225 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02003226 mime_type = "text/plain";
Luke San Antoniocaa24b02016-06-15 02:23:09 -04003227 data = base64_decode(in.substr(header.size()));
3228 }
3229 }
3230
3231 if (data.empty()) {
Syoyo Fujita20244e12018-03-15 11:01:05 -05003232 header = "data:application/gltf-buffer;base64,";
3233 if (in.find(header) == 0) {
3234 data = base64_decode(in.substr(header.size()));
3235 }
3236 }
3237
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09003238 // TODO(syoyo): Allow empty buffer? #229
Syoyo Fujita20244e12018-03-15 11:01:05 -05003239 if (data.empty()) {
Syoyo Fujita620eed12016-01-02 23:37:12 +09003240 return false;
Syoyo Fujitabde70212016-02-07 17:38:17 +09003241 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09003242
3243 if (checkSize) {
3244 if (data.size() != reqBytes) {
3245 return false;
3246 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003247 out->resize(reqBytes);
Syoyo Fujita620eed12016-01-02 23:37:12 +09003248 } else {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003249 out->resize(data.size());
Syoyo Fujita620eed12016-01-02 23:37:12 +09003250 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003251 std::copy(data.begin(), data.end(), out->begin());
Syoyo Fujita620eed12016-01-02 23:37:12 +09003252 return true;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003253}
3254
David1f9a4b92023-02-15 22:56:18 -06003255namespace detail {
David03ad33c2023-02-15 23:35:51 -06003256bool GetInt(const detail::json &o, int &val) {
jrkoonce5cecc412019-08-29 11:45:04 -05003257#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003258 if (!o.IsDouble()) {
3259 if (o.IsInt()) {
3260 val = o.GetInt();
3261 return true;
3262 } else if (o.IsUint()) {
3263 val = static_cast<int>(o.GetUint());
3264 return true;
3265 } else if (o.IsInt64()) {
3266 val = static_cast<int>(o.GetInt64());
3267 return true;
3268 } else if (o.IsUint64()) {
3269 val = static_cast<int>(o.GetUint64());
jrkooncecba5d6c2019-08-29 11:26:22 -05003270 return true;
3271 }
jrkoonce5cecc412019-08-29 11:45:04 -05003272 }
3273
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003274 return false;
jrkoonce5cecc412019-08-29 11:45:04 -05003275#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003276 auto type = o.type();
jrkooncecba5d6c2019-08-29 11:26:22 -05003277
David03ad33c2023-02-15 23:35:51 -06003278 if ((type == detail::json::value_t::number_integer) ||
3279 (type == detail::json::value_t::number_unsigned)) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003280 val = static_cast<int>(o.get<int64_t>());
3281 return true;
jrkoonce5cecc412019-08-29 11:45:04 -05003282 }
3283
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003284 return false;
jrkoonce5cecc412019-08-29 11:45:04 -05003285#endif
jrkooncecba5d6c2019-08-29 11:26:22 -05003286}
3287
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003288#ifdef TINYGLTF_USE_RAPIDJSON
David03ad33c2023-02-15 23:35:51 -06003289bool GetDouble(const detail::json &o, double &val) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003290 if (o.IsDouble()) {
3291 val = o.GetDouble();
3292 return true;
3293 }
3294
3295 return false;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003296}
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003297#endif
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003298
David03ad33c2023-02-15 23:35:51 -06003299bool GetNumber(const detail::json &o, double &val) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003300#ifdef TINYGLTF_USE_RAPIDJSON
3301 if (o.IsNumber()) {
3302 val = o.GetDouble();
3303 return true;
3304 }
3305
3306 return false;
3307#else
3308 if (o.is_number()) {
3309 val = o.get<double>();
3310 return true;
3311 }
3312
3313 return false;
3314#endif
3315}
3316
David03ad33c2023-02-15 23:35:51 -06003317bool GetString(const detail::json &o, std::string &val) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003318#ifdef TINYGLTF_USE_RAPIDJSON
3319 if (o.IsString()) {
3320 val = o.GetString();
3321 return true;
3322 }
3323
3324 return false;
3325#else
David03ad33c2023-02-15 23:35:51 -06003326 if (o.type() == detail::json::value_t::string) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003327 val = o.get<std::string>();
3328 return true;
3329 }
3330
3331 return false;
3332#endif
3333}
3334
David03ad33c2023-02-15 23:35:51 -06003335bool IsArray(const detail::json &o) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003336#ifdef TINYGLTF_USE_RAPIDJSON
3337 return o.IsArray();
3338#else
3339 return o.is_array();
3340#endif
3341}
3342
David03ad33c2023-02-15 23:35:51 -06003343detail::json_const_array_iterator ArrayBegin(const detail::json &o) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003344#ifdef TINYGLTF_USE_RAPIDJSON
3345 return o.Begin();
3346#else
3347 return o.begin();
3348#endif
3349}
3350
David03ad33c2023-02-15 23:35:51 -06003351detail::json_const_array_iterator ArrayEnd(const detail::json &o) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003352#ifdef TINYGLTF_USE_RAPIDJSON
3353 return o.End();
3354#else
3355 return o.end();
3356#endif
3357}
3358
David03ad33c2023-02-15 23:35:51 -06003359bool IsObject(const detail::json &o) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003360#ifdef TINYGLTF_USE_RAPIDJSON
3361 return o.IsObject();
3362#else
3363 return o.is_object();
3364#endif
3365}
3366
David03ad33c2023-02-15 23:35:51 -06003367detail::json_const_iterator ObjectBegin(const detail::json &o) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003368#ifdef TINYGLTF_USE_RAPIDJSON
3369 return o.MemberBegin();
3370#else
3371 return o.begin();
3372#endif
3373}
3374
David03ad33c2023-02-15 23:35:51 -06003375detail::json_const_iterator ObjectEnd(const detail::json &o) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003376#ifdef TINYGLTF_USE_RAPIDJSON
3377 return o.MemberEnd();
3378#else
3379 return o.end();
3380#endif
3381}
3382
Rahul Sheth01d54382020-07-10 14:27:37 -04003383// Making this a const char* results in a pointer to a temporary when
3384// TINYGLTF_USE_RAPIDJSON is off.
David03ad33c2023-02-15 23:35:51 -06003385std::string GetKey(detail::json_const_iterator &it) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003386#ifdef TINYGLTF_USE_RAPIDJSON
3387 return it->name.GetString();
3388#else
3389 return it.key().c_str();
3390#endif
3391}
3392
David03ad33c2023-02-15 23:35:51 -06003393bool FindMember(const detail::json &o, const char *member, detail::json_const_iterator &it) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003394#ifdef TINYGLTF_USE_RAPIDJSON
3395 if (!o.IsObject()) {
3396 return false;
3397 }
3398 it = o.FindMember(member);
3399 return it != o.MemberEnd();
3400#else
3401 it = o.find(member);
3402 return it != o.end();
3403#endif
3404}
3405
David Siegelcfe64fb2023-06-07 15:18:38 +02003406bool FindMember(detail::json &o, const char *member, detail::json_iterator &it) {
3407#ifdef TINYGLTF_USE_RAPIDJSON
3408 if (!o.IsObject()) {
3409 return false;
3410 }
3411 it = o.FindMember(member);
3412 return it != o.MemberEnd();
3413#else
3414 it = o.find(member);
3415 return it != o.end();
3416#endif
3417}
3418
3419void Erase(detail::json & o, detail::json_iterator &it) {
3420#ifdef TINYGLTF_USE_RAPIDJSON
3421 o.EraseMember(it);
3422#else
3423 o.erase(it);
3424#endif
3425}
3426
3427bool IsEmpty(const detail::json & o) {
3428#ifdef TINYGLTF_USE_RAPIDJSON
David Siegela1a34cb2023-06-07 15:30:02 +02003429 return o.ObjectEmpty();
David Siegelcfe64fb2023-06-07 15:18:38 +02003430#else
3431 return o.empty();
3432#endif
3433}
3434
David03ad33c2023-02-15 23:35:51 -06003435const detail::json &GetValue(detail::json_const_iterator &it) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003436#ifdef TINYGLTF_USE_RAPIDJSON
3437 return it->value;
3438#else
3439 return it.value();
3440#endif
3441}
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003442
David Siegelcfe64fb2023-06-07 15:18:38 +02003443detail::json &GetValue(detail::json_iterator &it) {
3444#ifdef TINYGLTF_USE_RAPIDJSON
3445 return it->value;
3446#else
3447 return it.value();
3448#endif
3449}
3450
David03ad33c2023-02-15 23:35:51 -06003451std::string JsonToString(const detail::json &o, int spacing = -1) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003452#ifdef TINYGLTF_USE_RAPIDJSON
3453 using namespace rapidjson;
3454 StringBuffer buffer;
3455 if (spacing == -1) {
3456 Writer<StringBuffer> writer(buffer);
Syoyo Fujita544969b2022-07-13 19:03:33 +09003457 // TODO: Better error handling.
3458 // https://github.com/syoyo/tinygltf/issues/332
3459 if (!o.Accept(writer)) {
3460 return "tiny_gltf::JsonToString() failed rapidjson conversion";
3461 }
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003462 } else {
3463 PrettyWriter<StringBuffer> writer(buffer);
3464 writer.SetIndent(' ', uint32_t(spacing));
Syoyo Fujita544969b2022-07-13 19:03:33 +09003465 if (!o.Accept(writer)) {
3466 return "tiny_gltf::JsonToString() failed rapidjson conversion";
3467 }
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003468 }
3469 return buffer.GetString();
3470#else
3471 return o.dump(spacing);
3472#endif
3473}
3474
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003475} // namespace
3476
David03ad33c2023-02-15 23:35:51 -06003477static bool ParseJsonAsValue(Value *ret, const detail::json &o) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003478 Value val{};
jrkooncecba5d6c2019-08-29 11:26:22 -05003479#ifdef TINYGLTF_USE_RAPIDJSON
3480 using rapidjson::Type;
3481 switch (o.GetType()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003482 case Type::kObjectType: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003483 Value::Object value_object;
jrkooncecba5d6c2019-08-29 11:26:22 -05003484 for (auto it = o.MemberBegin(); it != o.MemberEnd(); ++it) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003485 Value entry;
jrkooncecba5d6c2019-08-29 11:26:22 -05003486 ParseJsonAsValue(&entry, it->value);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003487 if (entry.Type() != NULL_TYPE)
David1f9a4b92023-02-15 22:56:18 -06003488 value_object.emplace(detail::GetKey(it), std::move(entry));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003489 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003490 if (value_object.size() > 0) val = Value(std::move(value_object));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003491 } break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003492 case Type::kArrayType: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003493 Value::Array value_array;
jrkoonce5cecc412019-08-29 11:45:04 -05003494 value_array.reserve(o.Size());
jrkooncecba5d6c2019-08-29 11:26:22 -05003495 for (auto it = o.Begin(); it != o.End(); ++it) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003496 Value entry;
jrkooncecba5d6c2019-08-29 11:26:22 -05003497 ParseJsonAsValue(&entry, *it);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003498 if (entry.Type() != NULL_TYPE)
3499 value_array.emplace_back(std::move(entry));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003500 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003501 if (value_array.size() > 0) val = Value(std::move(value_array));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003502 } break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003503 case Type::kStringType:
3504 val = Value(std::string(o.GetString()));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003505 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003506 case Type::kFalseType:
3507 case Type::kTrueType:
3508 val = Value(o.GetBool());
Selmar Kokfa7022f2018-04-04 18:10:20 +02003509 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003510 case Type::kNumberType:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003511 if (!o.IsDouble()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003512 int i = 0;
David1f9a4b92023-02-15 22:56:18 -06003513 detail::GetInt(o, i);
jrkooncecba5d6c2019-08-29 11:26:22 -05003514 val = Value(i);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003515 } else {
jrkooncecba5d6c2019-08-29 11:26:22 -05003516 double d = 0.0;
David38318022023-02-15 23:21:09 -06003517 detail::GetDouble(o, d);
jrkooncecba5d6c2019-08-29 11:26:22 -05003518 val = Value(d);
3519 }
Selmar Kokfa7022f2018-04-04 18:10:20 +02003520 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003521 case Type::kNullType:
Selmar Kokfa7022f2018-04-04 18:10:20 +02003522 break;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003523 // all types are covered, so no `case default`
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003524 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003525#else
3526 switch (o.type()) {
David03ad33c2023-02-15 23:35:51 -06003527 case detail::json::value_t::object: {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003528 Value::Object value_object;
3529 for (auto it = o.begin(); it != o.end(); it++) {
3530 Value entry;
3531 ParseJsonAsValue(&entry, it.value());
3532 if (entry.Type() != NULL_TYPE)
3533 value_object.emplace(it.key(), std::move(entry));
3534 }
3535 if (value_object.size() > 0) val = Value(std::move(value_object));
3536 } break;
David03ad33c2023-02-15 23:35:51 -06003537 case detail::json::value_t::array: {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003538 Value::Array value_array;
3539 value_array.reserve(o.size());
3540 for (auto it = o.begin(); it != o.end(); it++) {
3541 Value entry;
3542 ParseJsonAsValue(&entry, it.value());
3543 if (entry.Type() != NULL_TYPE)
3544 value_array.emplace_back(std::move(entry));
3545 }
3546 if (value_array.size() > 0) val = Value(std::move(value_array));
3547 } break;
David03ad33c2023-02-15 23:35:51 -06003548 case detail::json::value_t::string:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003549 val = Value(o.get<std::string>());
3550 break;
David03ad33c2023-02-15 23:35:51 -06003551 case detail::json::value_t::boolean:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003552 val = Value(o.get<bool>());
3553 break;
David03ad33c2023-02-15 23:35:51 -06003554 case detail::json::value_t::number_integer:
3555 case detail::json::value_t::number_unsigned:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003556 val = Value(static_cast<int>(o.get<int64_t>()));
3557 break;
David03ad33c2023-02-15 23:35:51 -06003558 case detail::json::value_t::number_float:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003559 val = Value(o.get<double>());
3560 break;
David03ad33c2023-02-15 23:35:51 -06003561 case detail::json::value_t::null:
3562 case detail::json::value_t::discarded:
3563 case detail::json::value_t::binary:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003564 // default:
3565 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003566 }
3567#endif
Marcin Kacprzakf4f5c3c2022-09-02 16:15:54 +02003568 const bool isNotNull = val.Type() != NULL_TYPE;
3569
jrkooncecba5d6c2019-08-29 11:26:22 -05003570 if (ret) *ret = std::move(val);
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003571
Marcin Kacprzakf4f5c3c2022-09-02 16:15:54 +02003572 return isNotNull;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003573}
3574
David03ad33c2023-02-15 23:35:51 -06003575static bool ParseExtrasProperty(Value *ret, const detail::json &o) {
3576 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06003577 if (!detail::FindMember(o, "extras", it)) {
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003578 return false;
3579 }
3580
David1f9a4b92023-02-15 22:56:18 -06003581 return ParseJsonAsValue(ret, detail::GetValue(it));
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003582}
3583
David03ad33c2023-02-15 23:35:51 -06003584static bool ParseBooleanProperty(bool *ret, std::string *err, const detail::json &o,
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09003585 const std::string &property,
3586 const bool required,
3587 const std::string &parent_node = "") {
David03ad33c2023-02-15 23:35:51 -06003588 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06003589 if (!detail::FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003590 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003591 if (err) {
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09003592 (*err) += "'" + property + "' property is missing";
3593 if (!parent_node.empty()) {
3594 (*err) += " in " + parent_node;
3595 }
3596 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003597 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003598 }
3599 return false;
3600 }
3601
David1f9a4b92023-02-15 22:56:18 -06003602 auto &value = detail::GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003603
3604 bool isBoolean;
jrkoonce0d2b6ef2019-09-04 13:46:45 -05003605 bool boolValue = false;
jrkooncecba5d6c2019-08-29 11:26:22 -05003606#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003607 isBoolean = value.IsBool();
3608 if (isBoolean) {
3609 boolValue = value.GetBool();
3610 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003611#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003612 isBoolean = value.is_boolean();
3613 if (isBoolean) {
3614 boolValue = value.get<bool>();
3615 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003616#endif
3617 if (!isBoolean) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003618 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003619 if (err) {
3620 (*err) += "'" + property + "' property is not a bool type.\n";
3621 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003622 }
3623 return false;
3624 }
3625
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003626 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003627 (*ret) = boolValue;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003628 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003629
3630 return true;
3631}
3632
David03ad33c2023-02-15 23:35:51 -06003633static bool ParseIntegerProperty(int *ret, std::string *err, const detail::json &o,
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003634 const std::string &property,
3635 const bool required,
3636 const std::string &parent_node = "") {
David03ad33c2023-02-15 23:35:51 -06003637 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06003638 if (!detail::FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003639 if (required) {
3640 if (err) {
3641 (*err) += "'" + property + "' property is missing";
3642 if (!parent_node.empty()) {
3643 (*err) += " in " + parent_node;
3644 }
3645 (*err) += ".\n";
3646 }
3647 }
3648 return false;
3649 }
3650
jrkooncecba5d6c2019-08-29 11:26:22 -05003651 int intValue;
David1f9a4b92023-02-15 22:56:18 -06003652 bool isInt = detail::GetInt(detail::GetValue(it), intValue);
jrkooncecba5d6c2019-08-29 11:26:22 -05003653 if (!isInt) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003654 if (required) {
3655 if (err) {
3656 (*err) += "'" + property + "' property is not an integer type.\n";
3657 }
3658 }
3659 return false;
3660 }
3661
3662 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003663 (*ret) = intValue;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003664 }
3665
3666 return true;
3667}
3668
David03ad33c2023-02-15 23:35:51 -06003669static bool ParseUnsignedProperty(size_t *ret, std::string *err, const detail::json &o,
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003670 const std::string &property,
3671 const bool required,
3672 const std::string &parent_node = "") {
David03ad33c2023-02-15 23:35:51 -06003673 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06003674 if (!detail::FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003675 if (required) {
3676 if (err) {
3677 (*err) += "'" + property + "' property is missing";
3678 if (!parent_node.empty()) {
3679 (*err) += " in " + parent_node;
3680 }
3681 (*err) += ".\n";
3682 }
3683 }
3684 return false;
3685 }
3686
David1f9a4b92023-02-15 22:56:18 -06003687 auto &value = detail::GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003688
jrkoonce0d2b6ef2019-09-04 13:46:45 -05003689 size_t uValue = 0;
jrkooncecba5d6c2019-08-29 11:26:22 -05003690 bool isUValue;
3691#ifdef TINYGLTF_USE_RAPIDJSON
3692 isUValue = false;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003693 if (value.IsUint()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003694 uValue = value.GetUint();
3695 isUValue = true;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003696 } else if (value.IsUint64()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003697 uValue = value.GetUint64();
3698 isUValue = true;
3699 }
3700#else
3701 isUValue = value.is_number_unsigned();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003702 if (isUValue) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003703 uValue = value.get<size_t>();
3704 }
3705#endif
3706 if (!isUValue) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003707 if (required) {
3708 if (err) {
3709 (*err) += "'" + property + "' property is not a positive integer.\n";
3710 }
3711 }
3712 return false;
3713 }
3714
3715 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003716 (*ret) = uValue;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003717 }
3718
3719 return true;
3720}
3721
David03ad33c2023-02-15 23:35:51 -06003722static bool ParseNumberProperty(double *ret, std::string *err, const detail::json &o,
Syoyo Fujita5b407452017-06-04 17:42:41 +09003723 const std::string &property,
3724 const bool required,
3725 const std::string &parent_node = "") {
David03ad33c2023-02-15 23:35:51 -06003726 detail::json_const_iterator it;
jrkooncecba5d6c2019-08-29 11:26:22 -05003727
David1f9a4b92023-02-15 22:56:18 -06003728 if (!detail::FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003729 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003730 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003731 (*err) += "'" + property + "' property is missing";
3732 if (!parent_node.empty()) {
3733 (*err) += " in " + parent_node;
3734 }
3735 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003736 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003737 }
3738 return false;
3739 }
3740
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003741 double numberValue;
David1f9a4b92023-02-15 22:56:18 -06003742 bool isNumber = detail::GetNumber(detail::GetValue(it), numberValue);
jrkooncecba5d6c2019-08-29 11:26:22 -05003743
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003744 if (!isNumber) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003745 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003746 if (err) {
3747 (*err) += "'" + property + "' property is not a number type.\n";
3748 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003749 }
3750 return false;
3751 }
3752
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003753 if (ret) {
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003754 (*ret) = numberValue;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003755 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003756
3757 return true;
3758}
3759
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003760static bool ParseNumberArrayProperty(std::vector<double> *ret, std::string *err,
David03ad33c2023-02-15 23:35:51 -06003761 const detail::json &o, const std::string &property,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003762 bool required,
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003763 const std::string &parent_node = "") {
David03ad33c2023-02-15 23:35:51 -06003764 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06003765 if (!detail::FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003766 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003767 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003768 (*err) += "'" + property + "' property is missing";
3769 if (!parent_node.empty()) {
3770 (*err) += " in " + parent_node;
3771 }
3772 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003773 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003774 }
3775 return false;
3776 }
3777
David1f9a4b92023-02-15 22:56:18 -06003778 if (!detail::IsArray(detail::GetValue(it))) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003779 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003780 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003781 (*err) += "'" + property + "' property is not an array";
3782 if (!parent_node.empty()) {
3783 (*err) += " in " + parent_node;
3784 }
3785 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003786 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003787 }
3788 return false;
3789 }
3790
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003791 ret->clear();
David1f9a4b92023-02-15 22:56:18 -06003792 auto end = detail::ArrayEnd(detail::GetValue(it));
3793 for (auto i = detail::ArrayBegin(detail::GetValue(it)); i != end; ++i) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003794 double numberValue;
David1f9a4b92023-02-15 22:56:18 -06003795 const bool isNumber = detail::GetNumber(*i, numberValue);
jrkooncecba5d6c2019-08-29 11:26:22 -05003796 if (!isNumber) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003797 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003798 if (err) {
3799 (*err) += "'" + property + "' property is not a number.\n";
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003800 if (!parent_node.empty()) {
3801 (*err) += " in " + parent_node;
3802 }
3803 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003804 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003805 }
3806 return false;
3807 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003808 ret->push_back(numberValue);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003809 }
3810
3811 return true;
3812}
3813
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003814static bool ParseIntegerArrayProperty(std::vector<int> *ret, std::string *err,
David03ad33c2023-02-15 23:35:51 -06003815 const detail::json &o,
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003816 const std::string &property,
3817 bool required,
3818 const std::string &parent_node = "") {
David03ad33c2023-02-15 23:35:51 -06003819 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06003820 if (!detail::FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003821 if (required) {
3822 if (err) {
3823 (*err) += "'" + property + "' property is missing";
3824 if (!parent_node.empty()) {
3825 (*err) += " in " + parent_node;
3826 }
3827 (*err) += ".\n";
3828 }
3829 }
3830 return false;
3831 }
3832
David1f9a4b92023-02-15 22:56:18 -06003833 if (!detail::IsArray(detail::GetValue(it))) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003834 if (required) {
3835 if (err) {
3836 (*err) += "'" + property + "' property is not an array";
3837 if (!parent_node.empty()) {
3838 (*err) += " in " + parent_node;
3839 }
3840 (*err) += ".\n";
3841 }
3842 }
3843 return false;
3844 }
3845
3846 ret->clear();
David1f9a4b92023-02-15 22:56:18 -06003847 auto end = detail::ArrayEnd(detail::GetValue(it));
3848 for (auto i = detail::ArrayBegin(detail::GetValue(it)); i != end; ++i) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003849 int numberValue;
David1f9a4b92023-02-15 22:56:18 -06003850 bool isNumber = detail::GetInt(*i, numberValue);
jrkooncecba5d6c2019-08-29 11:26:22 -05003851 if (!isNumber) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003852 if (required) {
3853 if (err) {
3854 (*err) += "'" + property + "' property is not an integer type.\n";
3855 if (!parent_node.empty()) {
3856 (*err) += " in " + parent_node;
3857 }
3858 (*err) += ".\n";
3859 }
3860 }
3861 return false;
3862 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003863 ret->push_back(numberValue);
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003864 }
3865
3866 return true;
3867}
3868
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003869static bool ParseStringProperty(
David03ad33c2023-02-15 23:35:51 -06003870 std::string *ret, std::string *err, const detail::json &o,
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003871 const std::string &property, bool required,
3872 const std::string &parent_node = std::string()) {
David03ad33c2023-02-15 23:35:51 -06003873 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06003874 if (!detail::FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003875 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003876 if (err) {
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003877 (*err) += "'" + property + "' property is missing";
3878 if (parent_node.empty()) {
3879 (*err) += ".\n";
3880 } else {
3881 (*err) += " in `" + parent_node + "'.\n";
3882 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003883 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003884 }
3885 return false;
3886 }
3887
jrkooncecba5d6c2019-08-29 11:26:22 -05003888 std::string strValue;
David1f9a4b92023-02-15 22:56:18 -06003889 if (!detail::GetString(detail::GetValue(it), strValue)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003890 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003891 if (err) {
3892 (*err) += "'" + property + "' property is not a string type.\n";
3893 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003894 }
3895 return false;
3896 }
3897
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003898 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003899 (*ret) = std::move(strValue);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003900 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003901
3902 return true;
3903}
3904
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003905static bool ParseStringIntegerProperty(std::map<std::string, int> *ret,
David03ad33c2023-02-15 23:35:51 -06003906 std::string *err, const detail::json &o,
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003907 const std::string &property,
3908 bool required,
3909 const std::string &parent = "") {
David03ad33c2023-02-15 23:35:51 -06003910 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06003911 if (!detail::FindMember(o, property.c_str(), it)) {
Luke San Antonio19894c72016-06-14 21:19:51 -04003912 if (required) {
3913 if (err) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09003914 if (!parent.empty()) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003915 (*err) +=
3916 "'" + property + "' property is missing in " + parent + ".\n";
Syoyo Fujita57c10182017-10-19 18:48:26 +09003917 } else {
3918 (*err) += "'" + property + "' property is missing.\n";
3919 }
Luke San Antonio19894c72016-06-14 21:19:51 -04003920 }
3921 }
3922 return false;
3923 }
3924
David03ad33c2023-02-15 23:35:51 -06003925 const detail::json &dict = detail::GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003926
Luke San Antonio19894c72016-06-14 21:19:51 -04003927 // Make sure we are dealing with an object / dictionary.
David1f9a4b92023-02-15 22:56:18 -06003928 if (!detail::IsObject(dict)) {
Luke San Antonio19894c72016-06-14 21:19:51 -04003929 if (required) {
3930 if (err) {
3931 (*err) += "'" + property + "' property is not an object.\n";
3932 }
3933 }
3934 return false;
3935 }
3936
3937 ret->clear();
Luke San Antonio19894c72016-06-14 21:19:51 -04003938
David03ad33c2023-02-15 23:35:51 -06003939 detail::json_const_iterator dictIt(detail::ObjectBegin(dict));
3940 detail::json_const_iterator dictItEnd(detail::ObjectEnd(dict));
Luke San Antonio19894c72016-06-14 21:19:51 -04003941
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09003942 for (; dictIt != dictItEnd; ++dictIt) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003943 int intVal;
David1f9a4b92023-02-15 22:56:18 -06003944 if (!detail::GetInt(detail::GetValue(dictIt), intVal)) {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09003945 if (required) {
3946 if (err) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003947 (*err) += "'" + property + "' value is not an integer type.\n";
Luke San Antonio19894c72016-06-14 21:19:51 -04003948 }
3949 }
3950 return false;
3951 }
3952
3953 // Insert into the list.
David1f9a4b92023-02-15 22:56:18 -06003954 (*ret)[detail::GetKey(dictIt)] = intVal;
Luke San Antonio19894c72016-06-14 21:19:51 -04003955 }
3956 return true;
3957}
3958
Syoyo Fujita5b407452017-06-04 17:42:41 +09003959static bool ParseJSONProperty(std::map<std::string, double> *ret,
David03ad33c2023-02-15 23:35:51 -06003960 std::string *err, const detail::json &o,
Syoyo Fujita5b407452017-06-04 17:42:41 +09003961 const std::string &property, bool required) {
David03ad33c2023-02-15 23:35:51 -06003962 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06003963 if (!detail::FindMember(o, property.c_str(), it)) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003964 if (required) {
Syoyo Fujita5b407452017-06-04 17:42:41 +09003965 if (err) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003966 (*err) += "'" + property + "' property is missing. \n'";
3967 }
3968 }
3969 return false;
3970 }
3971
David03ad33c2023-02-15 23:35:51 -06003972 const detail::json &obj = detail::GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003973
David1f9a4b92023-02-15 22:56:18 -06003974 if (!detail::IsObject(obj)) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003975 if (required) {
3976 if (err) {
3977 (*err) += "'" + property + "' property is not a JSON object.\n";
3978 }
3979 }
3980 return false;
3981 }
3982
3983 ret->clear();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003984
David03ad33c2023-02-15 23:35:51 -06003985 detail::json_const_iterator it2(detail::ObjectBegin(obj));
3986 detail::json_const_iterator itEnd(detail::ObjectEnd(obj));
jrkooncecba5d6c2019-08-29 11:26:22 -05003987 for (; it2 != itEnd; ++it2) {
3988 double numVal;
David1f9a4b92023-02-15 22:56:18 -06003989 if (detail::GetNumber(detail::GetValue(it2), numVal))
3990 ret->emplace(std::string(detail::GetKey(it2)), numVal);
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003991 }
3992
3993 return true;
3994}
3995
Selmar09d2ff12018-03-15 17:30:42 +01003996static bool ParseParameterProperty(Parameter *param, std::string *err,
David03ad33c2023-02-15 23:35:51 -06003997 const detail::json &o, const std::string &prop,
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003998 bool required) {
Selmar09d2ff12018-03-15 17:30:42 +01003999 // A parameter value can either be a string or an array of either a boolean or
4000 // a number. Booleans of any kind aren't supported here. Granted, it
4001 // complicates the Parameter structure and breaks it semantically in the sense
4002 // that the client probably works off the assumption that if the string is
4003 // empty the vector is used, etc. Would a tagged union work?
4004 if (ParseStringProperty(&param->string_value, err, o, prop, false)) {
4005 // Found string property.
4006 return true;
4007 } else if (ParseNumberArrayProperty(&param->number_array, err, o, prop,
4008 false)) {
4009 // Found a number array.
4010 return true;
Ben Buzbeef6af2242018-04-25 15:13:05 -07004011 } else if (ParseNumberProperty(&param->number_value, err, o, prop, false)) {
Marcin Kacprzakb12a54e2022-09-02 16:13:11 +02004012 param->has_number_value = true;
4013 return true;
Selmar09d2ff12018-03-15 17:30:42 +01004014 } else if (ParseJSONProperty(&param->json_double_value, err, o, prop,
4015 false)) {
4016 return true;
4017 } else if (ParseBooleanProperty(&param->bool_value, err, o, prop, false)) {
4018 return true;
4019 } else {
4020 if (required) {
4021 if (err) {
4022 (*err) += "parameter must be a string or number / number array.\n";
4023 }
4024 }
4025 return false;
4026 }
4027}
4028
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004029static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err,
David03ad33c2023-02-15 23:35:51 -06004030 const detail::json &o) {
Syoyo Fujita48f6db02018-04-15 18:40:55 +09004031 (void)err;
4032
David03ad33c2023-02-15 23:35:51 -06004033 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06004034 if (!detail::FindMember(o, "extensions", it)) {
Selmar09d2ff12018-03-15 17:30:42 +01004035 return false;
4036 }
jrkooncecba5d6c2019-08-29 11:26:22 -05004037
David1f9a4b92023-02-15 22:56:18 -06004038 auto &obj = detail::GetValue(it);
4039 if (!detail::IsObject(obj)) {
Selmar09d2ff12018-03-15 17:30:42 +01004040 return false;
4041 }
4042 ExtensionMap extensions;
David03ad33c2023-02-15 23:35:51 -06004043 detail::json_const_iterator extIt = detail::ObjectBegin(obj); // it.value().begin();
4044 detail::json_const_iterator extEnd = detail::ObjectEnd(obj);
jrkooncecba5d6c2019-08-29 11:26:22 -05004045 for (; extIt != extEnd; ++extIt) {
David1f9a4b92023-02-15 22:56:18 -06004046 auto &itObj = detail::GetValue(extIt);
4047 if (!detail::IsObject(itObj)) continue;
4048 std::string key(detail::GetKey(extIt));
jrkooncecba5d6c2019-08-29 11:26:22 -05004049 if (!ParseJsonAsValue(&extensions[key], itObj)) {
jrkoonce06c30c42019-09-03 15:56:48 -05004050 if (!key.empty()) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004051 // create empty object so that an extension object is still of type
4052 // object
jrkooncecba5d6c2019-08-29 11:26:22 -05004053 extensions[key] = Value{Value::Object{}};
Selmar Kokee3d0662018-10-08 16:20:43 +02004054 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004055 }
Selmar09d2ff12018-03-15 17:30:42 +01004056 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004057 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004058 (*ret) = std::move(extensions);
Selmar09d2ff12018-03-15 17:30:42 +01004059 }
4060 return true;
4061}
4062
David Siegel22cafa12023-06-05 22:18:59 +02004063template <typename GltfType>
4064static bool ParseExtrasAndExtensions(GltfType * target, std::string *err,
4065 const detail::json & o, bool store_json_strings) {
4066
4067 ParseExtensionsProperty(&target->extensions, err, o);
4068 ParseExtrasProperty(&target->extras, o);
4069
4070 if (store_json_strings) {
4071 {
4072 detail::json_const_iterator it;
4073 if (detail::FindMember(o, "extensions", it)) {
4074 target->extensions_json_string = detail::JsonToString(detail::GetValue(it));
4075 }
4076 }
4077 {
4078 detail::json_const_iterator it;
4079 if (detail::FindMember(o, "extras", it)) {
4080 target->extras_json_string = detail::JsonToString(detail::GetValue(it));
4081 }
4082 }
4083 }
4084 return true;
4085}
4086
David03ad33c2023-02-15 23:35:51 -06004087static bool ParseAsset(Asset *asset, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004088 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09004089 ParseStringProperty(&asset->version, err, o, "version", true, "Asset");
4090 ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset");
4091 ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset");
Miguel Sousa22bfc842020-02-20 14:16:58 +01004092 ParseStringProperty(&asset->copyright, err, o, "copyright", false, "Asset");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004093
David Siegel22cafa12023-06-05 22:18:59 +02004094 ParseExtrasAndExtensions(asset, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004095 return true;
4096}
4097
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004098static bool ParseImage(Image *image, const int image_idx, std::string *err,
David03ad33c2023-02-15 23:35:51 -06004099 std::string *warn, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004100 bool store_original_json_for_extras_and_extensions,
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09004101 const std::string &basedir, const size_t max_file_size, FsCallbacks *fs,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08004102 const URICallbacks *uri_cb,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004103 LoadImageDataFunction *LoadImageData = nullptr,
Paolo Jovone6601bf2018-07-07 20:43:33 +02004104 void *load_image_user_data = nullptr) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004105 // A glTF image must either reference a bufferView or an image uri
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004106
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004107 // schema says oneOf [`bufferView`, `uri`]
4108 // TODO(syoyo): Check the type of each parameters.
David03ad33c2023-02-15 23:35:51 -06004109 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06004110 bool hasBufferView = detail::FindMember(o, "bufferView", it);
4111 bool hasURI = detail::FindMember(o, "uri", it);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004112
Syoyo Fujitaaf3ebb22019-01-06 18:55:57 +09004113 ParseStringProperty(&image->name, err, o, "name", false);
4114
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004115 if (hasBufferView && hasURI) {
4116 // Should not both defined.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004117 if (err) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09004118 (*err) +=
4119 "Only one of `bufferView` or `uri` should be defined, but both are "
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004120 "defined for image[" +
4121 std::to_string(image_idx) + "] name = \"" + image->name + "\"\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004122 }
4123 return false;
4124 }
4125
4126 if (!hasBufferView && !hasURI) {
4127 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004128 (*err) += "Neither required `bufferView` nor `uri` defined for image[" +
4129 std::to_string(image_idx) + "] name = \"" + image->name +
4130 "\"\n";
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004131 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004132 return false;
4133 }
4134
David Siegel22cafa12023-06-05 22:18:59 +02004135 ParseExtrasAndExtensions(image, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004136
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004137 if (hasBufferView) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004138 int bufferView = -1;
4139 if (!ParseIntegerProperty(&bufferView, err, o, "bufferView", true)) {
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004140 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004141 (*err) += "Failed to parse `bufferView` for image[" +
4142 std::to_string(image_idx) + "] name = \"" + image->name +
4143 "\"\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004144 }
4145 return false;
4146 }
4147
4148 std::string mime_type;
4149 ParseStringProperty(&mime_type, err, o, "mimeType", false);
4150
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004151 int width = 0;
4152 ParseIntegerProperty(&width, err, o, "width", false);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004153
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004154 int height = 0;
4155 ParseIntegerProperty(&height, err, o, "height", false);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004156
4157 // Just only save some information here. Loading actual image data from
4158 // bufferView is done after this `ParseImage` function.
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004159 image->bufferView = bufferView;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004160 image->mimeType = mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004161 image->width = width;
4162 image->height = height;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004163
4164 return true;
4165 }
4166
Syoyo Fujita246654a2018-03-21 20:32:22 +09004167 // Parse URI & Load image data.
4168
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004169 std::string uri;
4170 std::string tmp_err;
Syoyo Fujita246654a2018-03-21 20:32:22 +09004171 if (!ParseStringProperty(&uri, &tmp_err, o, "uri", true)) {
4172 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004173 (*err) += "Failed to parse `uri` for image[" + std::to_string(image_idx) +
4174 "] name = \"" + image->name + "\".\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004175 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09004176 return false;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004177 }
4178
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004179 std::vector<unsigned char> img;
Syoyo Fujitabeded612016-05-01 20:03:43 +09004180
Syoyo Fujita246654a2018-03-21 20:32:22 +09004181 if (IsDataURI(uri)) {
johan bowald642a3432018-04-01 12:37:18 +02004182 if (!DecodeDataURI(&img, image->mimeType, uri, 0, false)) {
Syoyo Fujita246654a2018-03-21 20:32:22 +09004183 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004184 (*err) += "Failed to decode 'uri' for image[" +
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09004185 std::to_string(image_idx) + "] name = \"" + image->name +
4186 "\"\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004187 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09004188 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004189 }
4190 } else {
Syoyo Fujita246654a2018-03-21 20:32:22 +09004191 // Assume external file
4192 // Keep texture path (for textures that cannot be decoded)
4193 image->uri = uri;
Selmar67af3c92018-03-16 11:48:19 +01004194#ifdef TINYGLTF_NO_EXTERNAL_IMAGE
Syoyo Fujita246654a2018-03-21 20:32:22 +09004195 return true;
AlvaroBarua43172232022-09-11 00:41:43 +01004196#else
Pyarelal Knowles385946d2023-01-03 18:18:04 -08004197 std::string decoded_uri;
4198 if (!uri_cb->decode(uri, &decoded_uri, uri_cb->user_data)) {
4199 if (warn) {
4200 (*warn) += "Failed to decode 'uri' for image[" +
Syoyo Fujita877d8562023-04-23 21:47:31 +09004201 std::to_string(image_idx) + "] name = \"" + image->name +
4202 "\"\n";
Pyarelal Knowles385946d2023-01-03 18:18:04 -08004203 }
4204
4205 // Image loading failure is not critical to overall gltf loading.
4206 return true;
4207 }
4208
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004209 if (!LoadExternalFile(&img, err, warn, decoded_uri, basedir,
4210 /* required */ false, /* required bytes */ 0,
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09004211 /* checksize */ false, /* max file size */ max_file_size, fs)) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004212 if (warn) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004213 (*warn) += "Failed to load external 'uri' for image[" +
Syoyo Fujita877d8562023-04-23 21:47:31 +09004214 std::to_string(image_idx) + "] name = \"" + decoded_uri +
4215 "\"\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004216 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09004217 // If the image cannot be loaded, keep uri as image->uri.
4218 return true;
4219 }
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004220
Syoyo Fujita246654a2018-03-21 20:32:22 +09004221 if (img.empty()) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004222 if (warn) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004223 (*warn) += "Image data is empty for image[" +
Syoyo Fujita877d8562023-04-23 21:47:31 +09004224 std::to_string(image_idx) + "] name = \"" + image->name +
4225 "\" \n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09004226 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09004227 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004228 }
AlvaroBarua43172232022-09-11 00:41:43 +01004229#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004230 }
4231
Squareysff644d82018-03-13 22:36:18 +01004232 if (*LoadImageData == nullptr) {
4233 if (err) {
4234 (*err) += "No LoadImageData callback specified.\n";
4235 }
4236 return false;
4237 }
Syoyo Fujitaaf3ebb22019-01-06 18:55:57 +09004238 return (*LoadImageData)(image, image_idx, err, warn, 0, 0, &img.at(0),
Paolo Jovone6601bf2018-07-07 20:43:33 +02004239 static_cast<int>(img.size()), load_image_user_data);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004240}
4241
David03ad33c2023-02-15 23:35:51 -06004242static bool ParseTexture(Texture *texture, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004243 bool store_original_json_for_extras_and_extensions,
Syoyo Fujitabeded612016-05-01 20:03:43 +09004244 const std::string &basedir) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004245 (void)basedir;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004246 int sampler = -1;
4247 int source = -1;
4248 ParseIntegerProperty(&sampler, err, o, "sampler", false);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004249
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004250 ParseIntegerProperty(&source, err, o, "source", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09004251
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004252 texture->sampler = sampler;
4253 texture->source = source;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004254
David Siegel22cafa12023-06-05 22:18:59 +02004255 ParseExtrasAndExtensions(texture, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004256
Vladimír Vondruš239be2c2018-07-24 23:23:56 +02004257 ParseStringProperty(&texture->name, err, o, "name", false);
4258
Syoyo Fujitabde70212016-02-07 17:38:17 +09004259 return true;
4260}
4261
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004262static bool ParseTextureInfo(
David03ad33c2023-02-15 23:35:51 -06004263 TextureInfo *texinfo, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004264 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004265 if (texinfo == nullptr) {
4266 return false;
4267 }
4268
4269 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
4270 /* required */ true, "TextureInfo")) {
4271 return false;
4272 }
4273
4274 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
4275
David Siegel22cafa12023-06-05 22:18:59 +02004276 ParseExtrasAndExtensions(texinfo, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004277
Syoyo Fujita046400b2019-07-24 19:26:48 +09004278 return true;
4279}
4280
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004281static bool ParseNormalTextureInfo(
David03ad33c2023-02-15 23:35:51 -06004282 NormalTextureInfo *texinfo, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004283 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004284 if (texinfo == nullptr) {
4285 return false;
4286 }
4287
4288 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
4289 /* required */ true, "NormalTextureInfo")) {
4290 return false;
4291 }
4292
4293 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
4294 ParseNumberProperty(&texinfo->scale, err, o, "scale", false);
4295
David Siegel22cafa12023-06-05 22:18:59 +02004296 ParseExtrasAndExtensions(texinfo, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004297
Syoyo Fujita046400b2019-07-24 19:26:48 +09004298 return true;
4299}
4300
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004301static bool ParseOcclusionTextureInfo(
David03ad33c2023-02-15 23:35:51 -06004302 OcclusionTextureInfo *texinfo, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004303 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004304 if (texinfo == nullptr) {
4305 return false;
4306 }
4307
4308 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
4309 /* required */ true, "NormalTextureInfo")) {
4310 return false;
4311 }
4312
4313 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
4314 ParseNumberProperty(&texinfo->strength, err, o, "strength", false);
4315
David Siegel22cafa12023-06-05 22:18:59 +02004316 ParseExtrasAndExtensions(texinfo, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004317
Syoyo Fujita046400b2019-07-24 19:26:48 +09004318 return true;
4319}
4320
David03ad33c2023-02-15 23:35:51 -06004321static bool ParseBuffer(Buffer *buffer, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004322 bool store_original_json_for_extras_and_extensions,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08004323 FsCallbacks *fs, const URICallbacks *uri_cb,
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09004324 const std::string &basedir, const size_t max_buffer_size, bool is_binary = false,
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09004325 const unsigned char *bin_data = nullptr,
Syoyo Fujitabeded612016-05-01 20:03:43 +09004326 size_t bin_size = 0) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004327 size_t byteLength;
4328 if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true,
4329 "Buffer")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004330 return false;
4331 }
4332
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004333 // In glTF 2.0, uri is not mandatory anymore
Syoyo Fujita20244e12018-03-15 11:01:05 -05004334 buffer->uri.clear();
4335 ParseStringProperty(&buffer->uri, err, o, "uri", false, "Buffer");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004336
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004337 // having an empty uri for a non embedded image should not be valid
Syoyo Fujita20244e12018-03-15 11:01:05 -05004338 if (!is_binary && buffer->uri.empty()) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004339 if (err) {
4340 (*err) += "'uri' is missing from non binary glTF file buffer.\n";
4341 }
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004342 }
4343
David03ad33c2023-02-15 23:35:51 -06004344 detail::json_const_iterator type;
David1f9a4b92023-02-15 22:56:18 -06004345 if (detail::FindMember(o, "type", type)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004346 std::string typeStr;
David1f9a4b92023-02-15 22:56:18 -06004347 if (detail::GetString(detail::GetValue(type), typeStr)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004348 if (typeStr.compare("arraybuffer") == 0) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004349 // buffer.type = "arraybuffer";
4350 }
4351 }
4352 }
4353
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004354 if (is_binary) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004355 // Still binary glTF accepts external dataURI.
Syoyo Fujita20244e12018-03-15 11:01:05 -05004356 if (!buffer->uri.empty()) {
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004357 // First try embedded data URI.
4358 if (IsDataURI(buffer->uri)) {
4359 std::string mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004360 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004361 true)) {
4362 if (err) {
4363 (*err) +=
4364 "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
4365 }
4366 return false;
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004367 }
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004368 } else {
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004369 // External .bin file.
Pyarelal Knowles385946d2023-01-03 18:18:04 -08004370 std::string decoded_uri;
4371 if (!uri_cb->decode(buffer->uri, &decoded_uri, uri_cb->user_data)) {
4372 return false;
4373 }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09004374 if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr,
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004375 decoded_uri, basedir, /* required */ true,
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09004376 byteLength, /* checkSize */ true, /* max_file_size */max_buffer_size, fs)) {
Vladimír Vondrušfd84ceb2018-08-16 21:07:56 +02004377 return false;
4378 }
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004379 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004380 } else {
Syoyo Fujitabeded612016-05-01 20:03:43 +09004381 // load data from (embedded) binary data
4382
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09004383 if ((bin_size == 0) || (bin_data == nullptr)) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09004384 if (err) {
Syoyo Fujitac670f082022-09-17 19:52:25 +09004385 (*err) += "Invalid binary data in `Buffer', or GLB with empty BIN chunk.\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09004386 }
4387 return false;
4388 }
4389
4390 if (byteLength > bin_size) {
4391 if (err) {
4392 std::stringstream ss;
4393 ss << "Invalid `byteLength'. Must be equal or less than binary size: "
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004394 "`byteLength' = "
4395 << byteLength << ", binary size = " << bin_size << std::endl;
Syoyo Fujitabeded612016-05-01 20:03:43 +09004396 (*err) += ss.str();
4397 }
4398 return false;
4399 }
4400
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004401 // Read buffer data
4402 buffer->data.resize(static_cast<size_t>(byteLength));
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004403 memcpy(&(buffer->data.at(0)), bin_data, static_cast<size_t>(byteLength));
Syoyo Fujitabeded612016-05-01 20:03:43 +09004404 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004405
4406 } else {
Syoyo Fujita20244e12018-03-15 11:01:05 -05004407 if (IsDataURI(buffer->uri)) {
johan bowaldef151a42018-04-01 13:44:48 +02004408 std::string mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004409 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
4410 true)) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004411 if (err) {
Syoyo Fujita20244e12018-03-15 11:01:05 -05004412 (*err) += "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004413 }
4414 return false;
4415 }
4416 } else {
4417 // Assume external .bin file.
Pyarelal Knowles385946d2023-01-03 18:18:04 -08004418 std::string decoded_uri;
4419 if (!uri_cb->decode(buffer->uri, &decoded_uri, uri_cb->user_data)) {
4420 return false;
4421 }
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004422 if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, decoded_uri,
4423 basedir, /* required */ true, byteLength,
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09004424 /* checkSize */ true, /* max file size */max_buffer_size, fs)) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004425 return false;
4426 }
4427 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004428 }
4429
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004430 ParseStringProperty(&buffer->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004431
David Siegel22cafa12023-06-05 22:18:59 +02004432 ParseExtrasAndExtensions(buffer, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004433
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004434 return true;
4435}
4436
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004437static bool ParseBufferView(
David03ad33c2023-02-15 23:35:51 -06004438 BufferView *bufferView, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004439 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004440 int buffer = -1;
4441 if (!ParseIntegerProperty(&buffer, err, o, "buffer", true, "BufferView")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004442 return false;
4443 }
4444
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004445 size_t byteOffset = 0;
4446 ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004447
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004448 size_t byteLength = 1;
4449 if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true,
4450 "BufferView")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004451 return false;
4452 }
4453
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004454 size_t byteStride = 0;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004455 if (!ParseUnsignedProperty(&byteStride, err, o, "byteStride", false)) {
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004456 // Spec says: When byteStride of referenced bufferView is not defined, it
4457 // means that accessor elements are tightly packed, i.e., effective stride
4458 // equals the size of the element.
4459 // We cannot determine the actual byteStride until Accessor are parsed, thus
4460 // set 0(= tightly packed) here(as done in OpenGL's VertexAttribPoiner)
4461 byteStride = 0;
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004462 }
4463
4464 if ((byteStride > 252) || ((byteStride % 4) != 0)) {
4465 if (err) {
4466 std::stringstream ss;
4467 ss << "Invalid `byteStride' value. `byteStride' must be the multiple of "
4468 "4 : "
4469 << byteStride << std::endl;
4470
4471 (*err) += ss.str();
4472 }
4473 return false;
4474 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004475
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004476 int target = 0;
4477 ParseIntegerProperty(&target, err, o, "target", false);
4478 if ((target == TINYGLTF_TARGET_ARRAY_BUFFER) ||
4479 (target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004480 // OK
4481 } else {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004482 target = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004483 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004484 bufferView->target = target;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004485
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004486 ParseStringProperty(&bufferView->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004487
David Siegel22cafa12023-06-05 22:18:59 +02004488 ParseExtrasAndExtensions(bufferView, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004489
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004490 bufferView->buffer = buffer;
4491 bufferView->byteOffset = byteOffset;
4492 bufferView->byteLength = byteLength;
4493 bufferView->byteStride = byteStride;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004494 return true;
4495}
4496
David Siegeld852f502023-06-05 23:28:05 +02004497static bool ParseSparseAccessor(Accessor::Sparse *sparse, std::string *err,
4498 const detail::json &o,
4499 bool store_original_json_for_extras_and_extensions) {
4500 sparse->isSparse = true;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004501
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004502 int count = 0;
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004503 if (!ParseIntegerProperty(&count, err, o, "count", true, "SparseAccessor")) {
4504 return false;
4505 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004506
David Siegeld852f502023-06-05 23:28:05 +02004507 ParseExtrasAndExtensions(sparse, err, o, store_original_json_for_extras_and_extensions);
4508
David03ad33c2023-02-15 23:35:51 -06004509 detail::json_const_iterator indices_iterator;
4510 detail::json_const_iterator values_iterator;
David1f9a4b92023-02-15 22:56:18 -06004511 if (!detail::FindMember(o, "indices", indices_iterator)) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004512 (*err) = "the sparse object of this accessor doesn't have indices";
4513 return false;
4514 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004515
David1f9a4b92023-02-15 22:56:18 -06004516 if (!detail::FindMember(o, "values", values_iterator)) {
imallettd9ce9eb2022-10-07 10:37:09 -07004517 (*err) = "the sparse object of this accessor doesn't have values";
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004518 return false;
4519 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004520
David03ad33c2023-02-15 23:35:51 -06004521 const detail::json &indices_obj = detail::GetValue(indices_iterator);
4522 const detail::json &values_obj = detail::GetValue(values_iterator);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004523
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004524 int indices_buffer_view = 0, indices_byte_offset = 0, component_type = 0;
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09004525 if (!ParseIntegerProperty(&indices_buffer_view, err, indices_obj,
4526 "bufferView", true, "SparseAccessor")) {
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004527 return false;
4528 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004529 ParseIntegerProperty(&indices_byte_offset, err, indices_obj, "byteOffset",
Jack Mousseau94715172022-02-24 14:25:37 -08004530 false);
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004531 if (!ParseIntegerProperty(&component_type, err, indices_obj, "componentType",
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09004532 true, "SparseAccessor")) {
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004533 return false;
4534 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004535
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004536 int values_buffer_view = 0, values_byte_offset = 0;
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004537 if (!ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView",
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09004538 true, "SparseAccessor")) {
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004539 return false;
4540 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004541 ParseIntegerProperty(&values_byte_offset, err, values_obj, "byteOffset",
Jack Mousseau94715172022-02-24 14:25:37 -08004542 false);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004543
David Siegeld852f502023-06-05 23:28:05 +02004544 sparse->count = count;
4545 sparse->indices.bufferView = indices_buffer_view;
4546 sparse->indices.byteOffset = indices_byte_offset;
4547 sparse->indices.componentType = component_type;
4548 ParseExtrasAndExtensions(&sparse->indices, err, indices_obj,
4549 store_original_json_for_extras_and_extensions);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004550
David Siegeld852f502023-06-05 23:28:05 +02004551 sparse->values.bufferView = values_buffer_view;
4552 sparse->values.byteOffset = values_byte_offset;
4553 ParseExtrasAndExtensions(&sparse->values, err, values_obj,
4554 store_original_json_for_extras_and_extensions);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004555
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004556 return true;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004557}
4558
David03ad33c2023-02-15 23:35:51 -06004559static bool ParseAccessor(Accessor *accessor, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004560 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004561 int bufferView = -1;
4562 ParseIntegerProperty(&bufferView, err, o, "bufferView", false, "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004563
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004564 size_t byteOffset = 0;
4565 ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false, "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004566
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004567 bool normalized = false;
4568 ParseBooleanProperty(&normalized, err, o, "normalized", false, "Accessor");
4569
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004570 size_t componentType = 0;
4571 if (!ParseUnsignedProperty(&componentType, err, o, "componentType", true,
4572 "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004573 return false;
4574 }
4575
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004576 size_t count = 0;
4577 if (!ParseUnsignedProperty(&count, err, o, "count", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004578 return false;
4579 }
4580
4581 std::string type;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09004582 if (!ParseStringProperty(&type, err, o, "type", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004583 return false;
4584 }
4585
4586 if (type.compare("SCALAR") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004587 accessor->type = TINYGLTF_TYPE_SCALAR;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004588 } else if (type.compare("VEC2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004589 accessor->type = TINYGLTF_TYPE_VEC2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004590 } else if (type.compare("VEC3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004591 accessor->type = TINYGLTF_TYPE_VEC3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004592 } else if (type.compare("VEC4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004593 accessor->type = TINYGLTF_TYPE_VEC4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004594 } else if (type.compare("MAT2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004595 accessor->type = TINYGLTF_TYPE_MAT2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004596 } else if (type.compare("MAT3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004597 accessor->type = TINYGLTF_TYPE_MAT3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004598 } else if (type.compare("MAT4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004599 accessor->type = TINYGLTF_TYPE_MAT4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004600 } else {
4601 std::stringstream ss;
4602 ss << "Unsupported `type` for accessor object. Got \"" << type << "\"\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004603 if (err) {
4604 (*err) += ss.str();
4605 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004606 return false;
4607 }
4608
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004609 ParseStringProperty(&accessor->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004610
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004611 accessor->minValues.clear();
4612 accessor->maxValues.clear();
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004613 ParseNumberArrayProperty(&accessor->minValues, err, o, "min", false,
4614 "Accessor");
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004615
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004616 ParseNumberArrayProperty(&accessor->maxValues, err, o, "max", false,
4617 "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004618
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004619 accessor->count = count;
4620 accessor->bufferView = bufferView;
4621 accessor->byteOffset = byteOffset;
jianghaosen4748ad62017-08-29 20:08:37 +08004622 accessor->normalized = normalized;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004623 {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004624 if (componentType >= TINYGLTF_COMPONENT_TYPE_BYTE &&
4625 componentType <= TINYGLTF_COMPONENT_TYPE_DOUBLE) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004626 // OK
Arthur Brainville (Ybalrid)811e1d32019-06-15 07:32:38 +02004627 accessor->componentType = int(componentType);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004628 } else {
4629 std::stringstream ss;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004630 ss << "Invalid `componentType` in accessor. Got " << componentType
4631 << "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004632 if (err) {
4633 (*err) += ss.str();
4634 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004635 return false;
4636 }
4637 }
4638
David Siegel22cafa12023-06-05 22:18:59 +02004639 ParseExtrasAndExtensions(accessor, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004640
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004641 // check if accessor has a "sparse" object:
David03ad33c2023-02-15 23:35:51 -06004642 detail::json_const_iterator iterator;
David1f9a4b92023-02-15 22:56:18 -06004643 if (detail::FindMember(o, "sparse", iterator)) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004644 // here this accessor has a "sparse" subobject
David Siegeld852f502023-06-05 23:28:05 +02004645 return ParseSparseAccessor(&accessor->sparse, err, detail::GetValue(iterator),
4646 store_original_json_for_extras_and_extensions);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004647 }
4648
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004649 return true;
4650}
4651
Alex Wood7319db72019-01-24 15:38:16 -05004652#ifdef TINYGLTF_ENABLE_DRACO
4653
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004654static void DecodeIndexBuffer(draco::Mesh *mesh, size_t componentSize,
4655 std::vector<uint8_t> &outBuffer) {
4656 if (componentSize == 4) {
Alex Wood7319db72019-01-24 15:38:16 -05004657 assert(sizeof(mesh->face(draco::FaceIndex(0))[0]) == componentSize);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004658 memcpy(outBuffer.data(), &mesh->face(draco::FaceIndex(0))[0],
4659 outBuffer.size());
4660 } else {
Alex Wood7319db72019-01-24 15:38:16 -05004661 size_t faceStride = componentSize * 3;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004662 for (draco::FaceIndex f(0); f < mesh->num_faces(); ++f) {
4663 const draco::Mesh::Face &face = mesh->face(f);
4664 if (componentSize == 2) {
4665 uint16_t indices[3] = {(uint16_t)face[0].value(),
4666 (uint16_t)face[1].value(),
4667 (uint16_t)face[2].value()};
4668 memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
4669 faceStride);
4670 } else {
4671 uint8_t indices[3] = {(uint8_t)face[0].value(),
4672 (uint8_t)face[1].value(),
4673 (uint8_t)face[2].value()};
4674 memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
4675 faceStride);
Alex Wood7319db72019-01-24 15:38:16 -05004676 }
4677 }
4678 }
4679}
4680
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004681template <typename T>
4682static bool GetAttributeForAllPoints(draco::Mesh *mesh,
4683 const draco::PointAttribute *pAttribute,
4684 std::vector<uint8_t> &outBuffer) {
Alex Wood7319db72019-01-24 15:38:16 -05004685 size_t byteOffset = 0;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004686 T values[4] = {0, 0, 0, 0};
4687 for (draco::PointIndex i(0); i < mesh->num_points(); ++i) {
Alex Wood7319db72019-01-24 15:38:16 -05004688 const draco::AttributeValueIndex val_index = pAttribute->mapped_index(i);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004689 if (!pAttribute->ConvertValue<T>(val_index, pAttribute->num_components(),
4690 values))
Alex Wood7319db72019-01-24 15:38:16 -05004691 return false;
4692
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004693 memcpy(outBuffer.data() + byteOffset, &values[0],
4694 sizeof(T) * pAttribute->num_components());
Alex Wood7319db72019-01-24 15:38:16 -05004695 byteOffset += sizeof(T) * pAttribute->num_components();
4696 }
4697
4698 return true;
4699}
4700
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004701static bool GetAttributeForAllPoints(uint32_t componentType, draco::Mesh *mesh,
4702 const draco::PointAttribute *pAttribute,
4703 std::vector<uint8_t> &outBuffer) {
Alex Wood7319db72019-01-24 15:38:16 -05004704 bool decodeResult = false;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004705 switch (componentType) {
4706 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
4707 decodeResult =
4708 GetAttributeForAllPoints<uint8_t>(mesh, pAttribute, outBuffer);
4709 break;
4710 case TINYGLTF_COMPONENT_TYPE_BYTE:
4711 decodeResult =
4712 GetAttributeForAllPoints<int8_t>(mesh, pAttribute, outBuffer);
4713 break;
4714 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
4715 decodeResult =
4716 GetAttributeForAllPoints<uint16_t>(mesh, pAttribute, outBuffer);
4717 break;
4718 case TINYGLTF_COMPONENT_TYPE_SHORT:
4719 decodeResult =
4720 GetAttributeForAllPoints<int16_t>(mesh, pAttribute, outBuffer);
4721 break;
4722 case TINYGLTF_COMPONENT_TYPE_INT:
4723 decodeResult =
4724 GetAttributeForAllPoints<int32_t>(mesh, pAttribute, outBuffer);
4725 break;
4726 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
4727 decodeResult =
4728 GetAttributeForAllPoints<uint32_t>(mesh, pAttribute, outBuffer);
4729 break;
4730 case TINYGLTF_COMPONENT_TYPE_FLOAT:
4731 decodeResult =
4732 GetAttributeForAllPoints<float>(mesh, pAttribute, outBuffer);
4733 break;
4734 case TINYGLTF_COMPONENT_TYPE_DOUBLE:
4735 decodeResult =
4736 GetAttributeForAllPoints<double>(mesh, pAttribute, outBuffer);
4737 break;
4738 default:
4739 return false;
Alex Wood7319db72019-01-24 15:38:16 -05004740 }
4741
4742 return decodeResult;
4743}
4744
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004745static bool ParseDracoExtension(Primitive *primitive, Model *model,
4746 std::string *err,
4747 const Value &dracoExtensionValue) {
snowapril84e15262021-03-20 19:25:58 +09004748 (void)err;
Alex Wood7319db72019-01-24 15:38:16 -05004749 auto bufferViewValue = dracoExtensionValue.Get("bufferView");
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004750 if (!bufferViewValue.IsInt()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004751 auto attributesValue = dracoExtensionValue.Get("attributes");
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004752 if (!attributesValue.IsObject()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004753
4754 auto attributesObject = attributesValue.Get<Value::Object>();
4755 int bufferView = bufferViewValue.Get<int>();
4756
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004757 BufferView &view = model->bufferViews[bufferView];
4758 Buffer &buffer = model->buffers[view.buffer];
Alex Wood7319db72019-01-24 15:38:16 -05004759 // BufferView has already been decoded
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004760 if (view.dracoDecoded) return true;
Alex Wood7319db72019-01-24 15:38:16 -05004761 view.dracoDecoded = true;
4762
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004763 const char *bufferViewData =
4764 reinterpret_cast<const char *>(buffer.data.data() + view.byteOffset);
Alex Wood7319db72019-01-24 15:38:16 -05004765 size_t bufferViewSize = view.byteLength;
4766
4767 // decode draco
4768 draco::DecoderBuffer decoderBuffer;
4769 decoderBuffer.Init(bufferViewData, bufferViewSize);
4770 draco::Decoder decoder;
4771 auto decodeResult = decoder.DecodeMeshFromBuffer(&decoderBuffer);
4772 if (!decodeResult.ok()) {
4773 return false;
4774 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004775 const std::unique_ptr<draco::Mesh> &mesh = decodeResult.value();
Alex Wood7319db72019-01-24 15:38:16 -05004776
4777 // create new bufferView for indices
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004778 if (primitive->indices >= 0) {
4779 int32_t componentSize = GetComponentSizeInBytes(
4780 model->accessors[primitive->indices].componentType);
Alex Wood7319db72019-01-24 15:38:16 -05004781 Buffer decodedIndexBuffer;
4782 decodedIndexBuffer.data.resize(mesh->num_faces() * 3 * componentSize);
4783
4784 DecodeIndexBuffer(mesh.get(), componentSize, decodedIndexBuffer.data);
4785
4786 model->buffers.emplace_back(std::move(decodedIndexBuffer));
4787
4788 BufferView decodedIndexBufferView;
4789 decodedIndexBufferView.buffer = int(model->buffers.size() - 1);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004790 decodedIndexBufferView.byteLength =
4791 int(mesh->num_faces() * 3 * componentSize);
Alex Wood7319db72019-01-24 15:38:16 -05004792 decodedIndexBufferView.byteOffset = 0;
4793 decodedIndexBufferView.byteStride = 0;
4794 decodedIndexBufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER;
4795 model->bufferViews.emplace_back(std::move(decodedIndexBufferView));
4796
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004797 model->accessors[primitive->indices].bufferView =
4798 int(model->bufferViews.size() - 1);
Alexander Wood0d77a292019-01-26 08:58:45 -05004799 model->accessors[primitive->indices].count = int(mesh->num_faces() * 3);
Alex Wood7319db72019-01-24 15:38:16 -05004800 }
4801
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004802 for (const auto &attribute : attributesObject) {
4803 if (!attribute.second.IsInt()) return false;
Alexander Wood0d77a292019-01-26 08:58:45 -05004804 auto primitiveAttribute = primitive->attributes.find(attribute.first);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004805 if (primitiveAttribute == primitive->attributes.end()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004806
4807 int dracoAttributeIndex = attribute.second.Get<int>();
4808 const auto pAttribute = mesh->GetAttributeByUniqueId(dracoAttributeIndex);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004809 const auto componentType =
4810 model->accessors[primitiveAttribute->second].componentType;
Alex Wood7319db72019-01-24 15:38:16 -05004811
4812 // Create a new buffer for this decoded buffer
4813 Buffer decodedBuffer;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004814 size_t bufferSize = mesh->num_points() * pAttribute->num_components() *
4815 GetComponentSizeInBytes(componentType);
Alex Wood7319db72019-01-24 15:38:16 -05004816 decodedBuffer.data.resize(bufferSize);
4817
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004818 if (!GetAttributeForAllPoints(componentType, mesh.get(), pAttribute,
4819 decodedBuffer.data))
Alex Wood7319db72019-01-24 15:38:16 -05004820 return false;
4821
4822 model->buffers.emplace_back(std::move(decodedBuffer));
4823
4824 BufferView decodedBufferView;
4825 decodedBufferView.buffer = int(model->buffers.size() - 1);
4826 decodedBufferView.byteLength = bufferSize;
4827 decodedBufferView.byteOffset = pAttribute->byte_offset();
4828 decodedBufferView.byteStride = pAttribute->byte_stride();
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004829 decodedBufferView.target = primitive->indices >= 0
4830 ? TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER
4831 : TINYGLTF_TARGET_ARRAY_BUFFER;
Alex Wood7319db72019-01-24 15:38:16 -05004832 model->bufferViews.emplace_back(std::move(decodedBufferView));
4833
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004834 model->accessors[primitiveAttribute->second].bufferView =
4835 int(model->bufferViews.size() - 1);
4836 model->accessors[primitiveAttribute->second].count =
4837 int(mesh->num_points());
Alex Wood7319db72019-01-24 15:38:16 -05004838 }
4839
4840 return true;
4841}
4842#endif
4843
4844static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err,
David03ad33c2023-02-15 23:35:51 -06004845 const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004846 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004847 int material = -1;
4848 ParseIntegerProperty(&material, err, o, "material", false);
4849 primitive->material = material;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004850
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004851 int mode = TINYGLTF_MODE_TRIANGLES;
4852 ParseIntegerProperty(&mode, err, o, "mode", false);
imallettd9ce9eb2022-10-07 10:37:09 -07004853 primitive->mode = mode; // Why only triangles were supported ?
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004854
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004855 int indices = -1;
4856 ParseIntegerProperty(&indices, err, o, "indices", false);
4857 primitive->indices = indices;
4858 if (!ParseStringIntegerProperty(&primitive->attributes, err, o, "attributes",
4859 true, "Primitive")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004860 return false;
4861 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004862
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004863 // Look for morph targets
David03ad33c2023-02-15 23:35:51 -06004864 detail::json_const_iterator targetsObject;
David1f9a4b92023-02-15 22:56:18 -06004865 if (detail::FindMember(o, "targets", targetsObject) &&
4866 detail::IsArray(detail::GetValue(targetsObject))) {
4867 auto targetsObjectEnd = detail::ArrayEnd(detail::GetValue(targetsObject));
David03ad33c2023-02-15 23:35:51 -06004868 for (detail::json_const_array_iterator i = detail::ArrayBegin(detail::GetValue(targetsObject));
jrkooncecba5d6c2019-08-29 11:26:22 -05004869 i != targetsObjectEnd; ++i) {
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004870 std::map<std::string, int> targetAttribues;
4871
David03ad33c2023-02-15 23:35:51 -06004872 const detail::json &dict = *i;
David1f9a4b92023-02-15 22:56:18 -06004873 if (detail::IsObject(dict)) {
David03ad33c2023-02-15 23:35:51 -06004874 detail::json_const_iterator dictIt(detail::ObjectBegin(dict));
4875 detail::json_const_iterator dictItEnd(detail::ObjectEnd(dict));
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004876
jrkooncecba5d6c2019-08-29 11:26:22 -05004877 for (; dictIt != dictItEnd; ++dictIt) {
4878 int iVal;
David1f9a4b92023-02-15 22:56:18 -06004879 if (detail::GetInt(detail::GetValue(dictIt), iVal))
4880 targetAttribues[detail::GetKey(dictIt)] = iVal;
jrkooncecba5d6c2019-08-29 11:26:22 -05004881 }
4882 primitive->targets.emplace_back(std::move(targetAttribues));
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004883 }
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004884 }
4885 }
4886
David Siegel22cafa12023-06-05 22:18:59 +02004887 ParseExtrasAndExtensions(primitive, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004888
Alex Wood7319db72019-01-24 15:38:16 -05004889#ifdef TINYGLTF_ENABLE_DRACO
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004890 auto dracoExtension =
4891 primitive->extensions.find("KHR_draco_mesh_compression");
4892 if (dracoExtension != primitive->extensions.end()) {
4893 ParseDracoExtension(primitive, model, err, dracoExtension->second);
Alex Wood7319db72019-01-24 15:38:16 -05004894 }
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09004895#else
4896 (void)model;
Alex Wood7319db72019-01-24 15:38:16 -05004897#endif
4898
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004899 return true;
4900}
4901
David03ad33c2023-02-15 23:35:51 -06004902static bool ParseMesh(Mesh *mesh, Model *model, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004903 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004904 ParseStringProperty(&mesh->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004905
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004906 mesh->primitives.clear();
David03ad33c2023-02-15 23:35:51 -06004907 detail::json_const_iterator primObject;
David1f9a4b92023-02-15 22:56:18 -06004908 if (detail::FindMember(o, "primitives", primObject) &&
4909 detail::IsArray(detail::GetValue(primObject))) {
David03ad33c2023-02-15 23:35:51 -06004910 detail::json_const_array_iterator primEnd = detail::ArrayEnd(detail::GetValue(primObject));
4911 for (detail::json_const_array_iterator i = detail::ArrayBegin(detail::GetValue(primObject));
jrkooncecba5d6c2019-08-29 11:26:22 -05004912 i != primEnd; ++i) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004913 Primitive primitive;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004914 if (ParsePrimitive(&primitive, model, err, *i,
4915 store_original_json_for_extras_and_extensions)) {
Luke San Antonio10a3e1d2016-06-15 00:05:01 -04004916 // Only add the primitive if the parsing succeeds.
jrkoonced1e14722019-08-27 11:51:02 -05004917 mesh->primitives.emplace_back(std::move(primitive));
Luke San Antonio10a3e1d2016-06-15 00:05:01 -04004918 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004919 }
4920 }
4921
Aurélien Chatelain38a6a752017-05-24 09:54:55 +00004922 // Should probably check if has targets and if dimensions fit
4923 ParseNumberArrayProperty(&mesh->weights, err, o, "weights", false);
4924
David Siegel22cafa12023-06-05 22:18:59 +02004925 ParseExtrasAndExtensions(mesh, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004926
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004927 return true;
4928}
4929
David03ad33c2023-02-15 23:35:51 -06004930static bool ParseNode(Node *node, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004931 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004932 ParseStringProperty(&node->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004933
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004934 int skin = -1;
4935 ParseIntegerProperty(&skin, err, o, "skin", false);
4936 node->skin = skin;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00004937
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004938 // Matrix and T/R/S are exclusive
Syoyo Fujita5b407452017-06-04 17:42:41 +09004939 if (!ParseNumberArrayProperty(&node->matrix, err, o, "matrix", false)) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004940 ParseNumberArrayProperty(&node->rotation, err, o, "rotation", false);
4941 ParseNumberArrayProperty(&node->scale, err, o, "scale", false);
4942 ParseNumberArrayProperty(&node->translation, err, o, "translation", false);
4943 }
4944
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004945 int camera = -1;
4946 ParseIntegerProperty(&camera, err, o, "camera", false);
4947 node->camera = camera;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004948
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004949 int mesh = -1;
4950 ParseIntegerProperty(&mesh, err, o, "mesh", false);
4951 node->mesh = mesh;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004952
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004953 node->children.clear();
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004954 ParseIntegerArrayProperty(&node->children, err, o, "children", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004955
Benjamin Schmithüsenad63bf72019-08-16 14:19:27 +02004956 ParseNumberArrayProperty(&node->weights, err, o, "weights", false);
4957
David Siegel22cafa12023-06-05 22:18:59 +02004958 ParseExtrasAndExtensions(node, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004959
David Siegelc1648782023-05-09 01:33:57 +02004960 // KHR_lights_punctual: parse light source reference
4961 int light = -1;
4962 if (node->extensions.count("KHR_lights_punctual") != 0) {
4963 auto const& light_ext = node->extensions["KHR_lights_punctual"];
4964 if (light_ext.Has("light")) {
4965 light = light_ext.Get("light").GetNumberAsInt();
4966 } else {
David Siegel157063f2023-06-06 15:31:58 +02004967 if (err) {
4968 *err += "Node has extension KHR_lights_punctual, but does not reference "
4969 "a light source.\n";
4970 }
David Siegelc1648782023-05-09 01:33:57 +02004971 return false;
4972 }
4973 }
4974 node->light = light;
4975
Emanuel Schrade186322b2017-11-06 11:14:41 +01004976 return true;
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004977}
4978
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004979static bool ParsePbrMetallicRoughness(
David03ad33c2023-02-15 23:35:51 -06004980 PbrMetallicRoughness *pbr, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004981 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004982 if (pbr == nullptr) {
4983 return false;
4984 }
4985
4986 std::vector<double> baseColorFactor;
4987 if (ParseNumberArrayProperty(&baseColorFactor, err, o, "baseColorFactor",
4988 /* required */ false)) {
4989 if (baseColorFactor.size() != 4) {
4990 if (err) {
4991 (*err) +=
4992 "Array length of `baseColorFactor` parameter in "
4993 "pbrMetallicRoughness must be 4, but got " +
4994 std::to_string(baseColorFactor.size()) + "\n";
4995 }
4996 return false;
4997 }
Selmar Kokc3353e12019-10-18 18:22:35 +02004998 pbr->baseColorFactor = baseColorFactor;
Syoyo Fujita046400b2019-07-24 19:26:48 +09004999 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09005000
5001 {
David03ad33c2023-02-15 23:35:51 -06005002 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06005003 if (detail::FindMember(o, "baseColorTexture", it)) {
5004 ParseTextureInfo(&pbr->baseColorTexture, err, detail::GetValue(it),
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005005 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09005006 }
5007 }
5008
5009 {
David03ad33c2023-02-15 23:35:51 -06005010 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06005011 if (detail::FindMember(o, "metallicRoughnessTexture", it)) {
5012 ParseTextureInfo(&pbr->metallicRoughnessTexture, err, detail::GetValue(it),
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005013 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09005014 }
5015 }
5016
5017 ParseNumberProperty(&pbr->metallicFactor, err, o, "metallicFactor", false);
5018 ParseNumberProperty(&pbr->roughnessFactor, err, o, "roughnessFactor", false);
5019
David Siegel22cafa12023-06-05 22:18:59 +02005020 ParseExtrasAndExtensions(pbr, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005021
Syoyo Fujita046400b2019-07-24 19:26:48 +09005022 return true;
5023}
5024
David03ad33c2023-02-15 23:35:51 -06005025static bool ParseMaterial(Material *material, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005026 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09005027 ParseStringProperty(&material->name, err, o, "name", /* required */ false);
5028
Syoyo Fujitaff515702019-08-24 16:29:14 +09005029 if (ParseNumberArrayProperty(&material->emissiveFactor, err, o,
5030 "emissiveFactor",
5031 /* required */ false)) {
Selmar Kok6df800d2019-08-19 11:05:28 +02005032 if (material->emissiveFactor.size() != 3) {
5033 if (err) {
5034 (*err) +=
5035 "Array length of `emissiveFactor` parameter in "
5036 "material must be 3, but got " +
5037 std::to_string(material->emissiveFactor.size()) + "\n";
5038 }
5039 return false;
5040 }
Syoyo Fujitaff515702019-08-24 16:29:14 +09005041 } else {
Selmar Kok6df800d2019-08-19 11:05:28 +02005042 // fill with default values
Selmar Kok6dba6c62019-08-19 11:23:31 +02005043 material->emissiveFactor = {0.0, 0.0, 0.0};
Selmar Kok6df800d2019-08-19 11:05:28 +02005044 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09005045
5046 ParseStringProperty(&material->alphaMode, err, o, "alphaMode",
5047 /* required */ false);
5048 ParseNumberProperty(&material->alphaCutoff, err, o, "alphaCutoff",
5049 /* required */ false);
5050 ParseBooleanProperty(&material->doubleSided, err, o, "doubleSided",
5051 /* required */ false);
5052
5053 {
David03ad33c2023-02-15 23:35:51 -06005054 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06005055 if (detail::FindMember(o, "pbrMetallicRoughness", it)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09005056 ParsePbrMetallicRoughness(&material->pbrMetallicRoughness, err,
David1f9a4b92023-02-15 22:56:18 -06005057 detail::GetValue(it),
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005058 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09005059 }
5060 }
5061
5062 {
David03ad33c2023-02-15 23:35:51 -06005063 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06005064 if (detail::FindMember(o, "normalTexture", it)) {
5065 ParseNormalTextureInfo(&material->normalTexture, err, detail::GetValue(it),
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005066 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09005067 }
5068 }
5069
5070 {
David03ad33c2023-02-15 23:35:51 -06005071 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06005072 if (detail::FindMember(o, "occlusionTexture", it)) {
5073 ParseOcclusionTextureInfo(&material->occlusionTexture, err, detail::GetValue(it),
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005074 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09005075 }
5076 }
5077
5078 {
David03ad33c2023-02-15 23:35:51 -06005079 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06005080 if (detail::FindMember(o, "emissiveTexture", it)) {
5081 ParseTextureInfo(&material->emissiveTexture, err, detail::GetValue(it),
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005082 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09005083 }
5084 }
5085
5086 // Old code path. For backward compatibility, we still store material values
5087 // as Parameter. This will create duplicated information for
imallettd9ce9eb2022-10-07 10:37:09 -07005088 // example(pbrMetallicRoughness), but should be negligible in terms of memory
Syoyo Fujita046400b2019-07-24 19:26:48 +09005089 // consumption.
5090 // TODO(syoyo): Remove in the next major release.
5091 material->values.clear();
5092 material->additionalValues.clear();
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04005093
David03ad33c2023-02-15 23:35:51 -06005094 detail::json_const_iterator it(detail::ObjectBegin(o));
5095 detail::json_const_iterator itEnd(detail::ObjectEnd(o));
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04005096
jrkooncecba5d6c2019-08-29 11:26:22 -05005097 for (; it != itEnd; ++it) {
David1f9a4b92023-02-15 22:56:18 -06005098 std::string key(detail::GetKey(it));
jrkoonce06c30c42019-09-03 15:56:48 -05005099 if (key == "pbrMetallicRoughness") {
David1f9a4b92023-02-15 22:56:18 -06005100 if (detail::IsObject(detail::GetValue(it))) {
David03ad33c2023-02-15 23:35:51 -06005101 const detail::json &values_object = detail::GetValue(it);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005102
David03ad33c2023-02-15 23:35:51 -06005103 detail::json_const_iterator itVal(detail::ObjectBegin(values_object));
5104 detail::json_const_iterator itValEnd(detail::ObjectEnd(values_object));
jrkoonce06c30c42019-09-03 15:56:48 -05005105
5106 for (; itVal != itValEnd; ++itVal) {
5107 Parameter param;
David1f9a4b92023-02-15 22:56:18 -06005108 if (ParseParameterProperty(&param, err, values_object, detail::GetKey(itVal),
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005109 false)) {
David1f9a4b92023-02-15 22:56:18 -06005110 material->values.emplace(detail::GetKey(itVal), std::move(param));
jrkoonce06c30c42019-09-03 15:56:48 -05005111 }
5112 }
5113 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005114 } else if (key == "extensions" || key == "extras") {
5115 // done later, skip, otherwise poorly parsed contents will be saved in the
5116 // parametermap and serialized again later
5117 } else {
jrkoonce06c30c42019-09-03 15:56:48 -05005118 Parameter param;
5119 if (ParseParameterProperty(&param, err, o, key, false)) {
5120 // names of materials have already been parsed. Putting it in this map
imallettd9ce9eb2022-10-07 10:37:09 -07005121 // doesn't correctly reflect the glTF specification
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005122 if (key != "name")
5123 material->additionalValues.emplace(std::move(key), std::move(param));
jrkoonce06c30c42019-09-03 15:56:48 -05005124 }
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00005125 }
Syoyo Fujita5b407452017-06-04 17:42:41 +09005126 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005127
David Siegel22cafa12023-06-05 22:18:59 +02005128 material->extensions.clear(); // Note(agnat): Why?
5129 ParseExtrasAndExtensions(material, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005130
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04005131 return true;
5132}
5133
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005134static bool ParseAnimationChannel(
David03ad33c2023-02-15 23:35:51 -06005135 AnimationChannel *channel, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005136 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005137 int samplerIndex = -1;
5138 int targetIndex = -1;
5139 if (!ParseIntegerProperty(&samplerIndex, err, o, "sampler", true,
5140 "AnimationChannel")) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005141 if (err) {
5142 (*err) += "`sampler` field is missing in animation channels\n";
5143 }
5144 return false;
5145 }
5146
David03ad33c2023-02-15 23:35:51 -06005147 detail::json_const_iterator targetIt;
David1f9a4b92023-02-15 22:56:18 -06005148 if (detail::FindMember(o, "target", targetIt) && detail::IsObject(detail::GetValue(targetIt))) {
David03ad33c2023-02-15 23:35:51 -06005149 const detail::json &target_object = detail::GetValue(targetIt);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005150
Jack Mousseau283b5522023-01-15 11:45:45 -08005151 ParseIntegerProperty(&targetIndex, err, target_object, "node", false);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005152
5153 if (!ParseStringProperty(&channel->target_path, err, target_object, "path",
5154 true)) {
5155 if (err) {
5156 (*err) += "`path` field is missing in animation.channels.target\n";
5157 }
5158 return false;
5159 }
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09005160 ParseExtensionsProperty(&channel->target_extensions, err, target_object);
David Siegeld852f502023-06-05 23:28:05 +02005161 ParseExtrasProperty(&channel->target_extras, target_object);
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09005162 if (store_original_json_for_extras_and_extensions) {
David Siegeld852f502023-06-05 23:28:05 +02005163 {
5164 detail::json_const_iterator it;
5165 if (detail::FindMember(target_object, "extensions", it)) {
5166 channel->target_extensions_json_string = detail::JsonToString(detail::GetValue(it));
5167 }
5168 }
5169 {
5170 detail::json_const_iterator it;
5171 if (detail::FindMember(target_object, "extras", it)) {
5172 channel->target_extras_json_string = detail::JsonToString(detail::GetValue(it));
5173 }
Selmar Kok973d9b32020-01-21 18:45:24 +01005174 }
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09005175 }
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005176 }
5177
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005178 channel->sampler = samplerIndex;
5179 channel->target_node = targetIndex;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005180
David Siegel22cafa12023-06-05 22:18:59 +02005181 ParseExtrasAndExtensions(channel, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005182
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005183 return true;
5184}
5185
5186static bool ParseAnimation(Animation *animation, std::string *err,
David03ad33c2023-02-15 23:35:51 -06005187 const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005188 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005189 {
David03ad33c2023-02-15 23:35:51 -06005190 detail::json_const_iterator channelsIt;
David1f9a4b92023-02-15 22:56:18 -06005191 if (detail::FindMember(o, "channels", channelsIt) &&
5192 detail::IsArray(detail::GetValue(channelsIt))) {
David03ad33c2023-02-15 23:35:51 -06005193 detail::json_const_array_iterator channelEnd = detail::ArrayEnd(detail::GetValue(channelsIt));
5194 for (detail::json_const_array_iterator i = detail::ArrayBegin(detail::GetValue(channelsIt));
jrkooncecba5d6c2019-08-29 11:26:22 -05005195 i != channelEnd; ++i) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005196 AnimationChannel channel;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005197 if (ParseAnimationChannel(
5198 &channel, err, *i,
5199 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005200 // Only add the channel if the parsing succeeds.
jrkooncecba5d6c2019-08-29 11:26:22 -05005201 animation->channels.emplace_back(std::move(channel));
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005202 }
5203 }
5204 }
5205 }
5206
5207 {
David03ad33c2023-02-15 23:35:51 -06005208 detail::json_const_iterator samplerIt;
David1f9a4b92023-02-15 22:56:18 -06005209 if (detail::FindMember(o, "samplers", samplerIt) && detail::IsArray(detail::GetValue(samplerIt))) {
David03ad33c2023-02-15 23:35:51 -06005210 const detail::json &sampler_array = detail::GetValue(samplerIt);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005211
David03ad33c2023-02-15 23:35:51 -06005212 detail::json_const_array_iterator it = detail::ArrayBegin(sampler_array);
5213 detail::json_const_array_iterator itEnd = detail::ArrayEnd(sampler_array);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005214
jrkooncecba5d6c2019-08-29 11:26:22 -05005215 for (; it != itEnd; ++it) {
David03ad33c2023-02-15 23:35:51 -06005216 const detail::json &s = *it;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005217
5218 AnimationSampler sampler;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005219 int inputIndex = -1;
5220 int outputIndex = -1;
5221 if (!ParseIntegerProperty(&inputIndex, err, s, "input", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005222 if (err) {
5223 (*err) += "`input` field is missing in animation.sampler\n";
5224 }
5225 return false;
5226 }
Evan Birenbaum6bdffed2019-02-14 13:30:57 -08005227 ParseStringProperty(&sampler.interpolation, err, s, "interpolation",
5228 false);
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005229 if (!ParseIntegerProperty(&outputIndex, err, s, "output", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005230 if (err) {
5231 (*err) += "`output` field is missing in animation.sampler\n";
5232 }
5233 return false;
5234 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005235 sampler.input = inputIndex;
5236 sampler.output = outputIndex;
David Siegel22cafa12023-06-05 22:18:59 +02005237 ParseExtrasAndExtensions(&sampler, err, o,
5238 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005239
jrkooncecba5d6c2019-08-29 11:26:22 -05005240 animation->samplers.emplace_back(std::move(sampler));
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005241 }
5242 }
5243 }
5244
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005245 ParseStringProperty(&animation->name, err, o, "name", false);
5246
David Siegel22cafa12023-06-05 22:18:59 +02005247 ParseExtrasAndExtensions(animation, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005248
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005249 return true;
5250}
5251
David03ad33c2023-02-15 23:35:51 -06005252static bool ParseSampler(Sampler *sampler, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005253 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujitac2615632016-06-19 21:56:06 +09005254 ParseStringProperty(&sampler->name, err, o, "name", false);
5255
Syoyo Fujitaee179b22019-08-16 13:11:30 +09005256 int minFilter = -1;
5257 int magFilter = -1;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005258 int wrapS = TINYGLTF_TEXTURE_WRAP_REPEAT;
5259 int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT;
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09005260 // int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005261 ParseIntegerProperty(&minFilter, err, o, "minFilter", false);
5262 ParseIntegerProperty(&magFilter, err, o, "magFilter", false);
5263 ParseIntegerProperty(&wrapS, err, o, "wrapS", false);
5264 ParseIntegerProperty(&wrapT, err, o, "wrapT", false);
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09005265 // ParseIntegerProperty(&wrapR, err, o, "wrapR", false); // tinygltf
5266 // extension
Syoyo Fujitaee179b22019-08-16 13:11:30 +09005267
imallettd9ce9eb2022-10-07 10:37:09 -07005268 // TODO(syoyo): Check the value is allowed one.
Syoyo Fujitaee179b22019-08-16 13:11:30 +09005269 // (e.g. we allow 9728(NEAREST), but don't allow 9727)
Syoyo Fujitac2615632016-06-19 21:56:06 +09005270
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005271 sampler->minFilter = minFilter;
5272 sampler->magFilter = magFilter;
5273 sampler->wrapS = wrapS;
5274 sampler->wrapT = wrapT;
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09005275 // sampler->wrapR = wrapR;
Syoyo Fujitac2615632016-06-19 21:56:06 +09005276
David Siegel22cafa12023-06-05 22:18:59 +02005277 ParseExtrasAndExtensions(sampler, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005278
Syoyo Fujitac2615632016-06-19 21:56:06 +09005279 return true;
5280}
5281
David03ad33c2023-02-15 23:35:51 -06005282static bool ParseSkin(Skin *skin, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005283 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09005284 ParseStringProperty(&skin->name, err, o, "name", false, "Skin");
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005285
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005286 std::vector<int> joints;
5287 if (!ParseIntegerArrayProperty(&joints, err, o, "joints", false, "Skin")) {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005288 return false;
5289 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005290 skin->joints = std::move(joints);
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005291
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005292 int skeleton = -1;
5293 ParseIntegerProperty(&skeleton, err, o, "skeleton", false, "Skin");
5294 skin->skeleton = skeleton;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005295
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005296 int invBind = -1;
5297 ParseIntegerProperty(&invBind, err, o, "inverseBindMatrices", true, "Skin");
5298 skin->inverseBindMatrices = invBind;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005299
David Siegel22cafa12023-06-05 22:18:59 +02005300 ParseExtrasAndExtensions(skin, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005301
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005302 return true;
5303}
5304
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005305static bool ParsePerspectiveCamera(
David03ad33c2023-02-15 23:35:51 -06005306 PerspectiveCamera *camera, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005307 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005308 double yfov = 0.0;
5309 if (!ParseNumberProperty(&yfov, err, o, "yfov", true, "OrthographicCamera")) {
5310 return false;
5311 }
5312
5313 double znear = 0.0;
5314 if (!ParseNumberProperty(&znear, err, o, "znear", true,
5315 "PerspectiveCamera")) {
5316 return false;
5317 }
5318
5319 double aspectRatio = 0.0; // = invalid
5320 ParseNumberProperty(&aspectRatio, err, o, "aspectRatio", false,
5321 "PerspectiveCamera");
5322
5323 double zfar = 0.0; // = invalid
5324 ParseNumberProperty(&zfar, err, o, "zfar", false, "PerspectiveCamera");
5325
Selmar Kok31cb7f92018-10-03 15:39:05 +02005326 camera->aspectRatio = aspectRatio;
5327 camera->zfar = zfar;
5328 camera->yfov = yfov;
5329 camera->znear = znear;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005330
David Siegel22cafa12023-06-05 22:18:59 +02005331 ParseExtrasAndExtensions(camera, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005332
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005333 // TODO(syoyo): Validate parameter values.
5334
5335 return true;
5336}
5337
David03ad33c2023-02-15 23:35:51 -06005338static bool ParseSpotLight(SpotLight *light, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005339 bool store_original_json_for_extras_and_extensions) {
Johan Bowald52936a02019-07-17 09:06:45 +02005340 ParseNumberProperty(&light->innerConeAngle, err, o, "innerConeAngle", false);
5341 ParseNumberProperty(&light->outerConeAngle, err, o, "outerConeAngle", false);
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005342
David Siegel22cafa12023-06-05 22:18:59 +02005343 ParseExtrasAndExtensions(light, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005344
Johan Bowald52936a02019-07-17 09:06:45 +02005345 // TODO(syoyo): Validate parameter values.
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005346
Johan Bowald52936a02019-07-17 09:06:45 +02005347 return true;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005348}
5349
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005350static bool ParseOrthographicCamera(
David03ad33c2023-02-15 23:35:51 -06005351 OrthographicCamera *camera, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005352 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005353 double xmag = 0.0;
5354 if (!ParseNumberProperty(&xmag, err, o, "xmag", true, "OrthographicCamera")) {
5355 return false;
5356 }
5357
5358 double ymag = 0.0;
5359 if (!ParseNumberProperty(&ymag, err, o, "ymag", true, "OrthographicCamera")) {
5360 return false;
5361 }
5362
5363 double zfar = 0.0;
5364 if (!ParseNumberProperty(&zfar, err, o, "zfar", true, "OrthographicCamera")) {
5365 return false;
5366 }
5367
5368 double znear = 0.0;
5369 if (!ParseNumberProperty(&znear, err, o, "znear", true,
5370 "OrthographicCamera")) {
5371 return false;
5372 }
5373
David Siegel22cafa12023-06-05 22:18:59 +02005374 ParseExtrasAndExtensions(camera, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005375
Selmar Kok31cb7f92018-10-03 15:39:05 +02005376 camera->xmag = xmag;
5377 camera->ymag = ymag;
5378 camera->zfar = zfar;
5379 camera->znear = znear;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005380
5381 // TODO(syoyo): Validate parameter values.
5382
5383 return true;
5384}
5385
David03ad33c2023-02-15 23:35:51 -06005386static bool ParseCamera(Camera *camera, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005387 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005388 if (!ParseStringProperty(&camera->type, err, o, "type", true, "Camera")) {
5389 return false;
5390 }
5391
5392 if (camera->type.compare("orthographic") == 0) {
David03ad33c2023-02-15 23:35:51 -06005393 detail::json_const_iterator orthoIt;
David1f9a4b92023-02-15 22:56:18 -06005394 if (!detail::FindMember(o, "orthographic", orthoIt)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005395 if (err) {
5396 std::stringstream ss;
imallettd9ce9eb2022-10-07 10:37:09 -07005397 ss << "Orthographic camera description not found." << std::endl;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005398 (*err) += ss.str();
5399 }
5400 return false;
5401 }
5402
David03ad33c2023-02-15 23:35:51 -06005403 const detail::json &v = detail::GetValue(orthoIt);
David1f9a4b92023-02-15 22:56:18 -06005404 if (!detail::IsObject(v)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005405 if (err) {
5406 std::stringstream ss;
5407 ss << "\"orthographic\" is not a JSON object." << std::endl;
5408 (*err) += ss.str();
5409 }
5410 return false;
5411 }
5412
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005413 if (!ParseOrthographicCamera(
5414 &camera->orthographic, err, v,
5415 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005416 return false;
5417 }
5418 } else if (camera->type.compare("perspective") == 0) {
David03ad33c2023-02-15 23:35:51 -06005419 detail::json_const_iterator perspIt;
David1f9a4b92023-02-15 22:56:18 -06005420 if (!detail::FindMember(o, "perspective", perspIt)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005421 if (err) {
5422 std::stringstream ss;
5423 ss << "Perspective camera description not found." << std::endl;
5424 (*err) += ss.str();
5425 }
5426 return false;
5427 }
5428
David03ad33c2023-02-15 23:35:51 -06005429 const detail::json &v = detail::GetValue(perspIt);
David1f9a4b92023-02-15 22:56:18 -06005430 if (!detail::IsObject(v)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005431 if (err) {
5432 std::stringstream ss;
5433 ss << "\"perspective\" is not a JSON object." << std::endl;
5434 (*err) += ss.str();
5435 }
5436 return false;
5437 }
5438
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005439 if (!ParsePerspectiveCamera(
5440 &camera->perspective, err, v,
5441 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005442 return false;
5443 }
5444 } else {
5445 if (err) {
5446 std::stringstream ss;
5447 ss << "Invalid camera type: \"" << camera->type
5448 << "\". Must be \"perspective\" or \"orthographic\"" << std::endl;
5449 (*err) += ss.str();
5450 }
5451 return false;
5452 }
5453
5454 ParseStringProperty(&camera->name, err, o, "name", false);
5455
David Siegel22cafa12023-06-05 22:18:59 +02005456 ParseExtrasAndExtensions(camera, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005457
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005458 return true;
5459}
5460
David03ad33c2023-02-15 23:35:51 -06005461static bool ParseLight(Light *light, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005462 bool store_original_json_for_extras_and_extensions) {
Johan Bowald52936a02019-07-17 09:06:45 +02005463 if (!ParseStringProperty(&light->type, err, o, "type", true)) {
5464 return false;
5465 }
5466
5467 if (light->type == "spot") {
David03ad33c2023-02-15 23:35:51 -06005468 detail::json_const_iterator spotIt;
David1f9a4b92023-02-15 22:56:18 -06005469 if (!detail::FindMember(o, "spot", spotIt)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005470 if (err) {
5471 std::stringstream ss;
5472 ss << "Spot light description not found." << std::endl;
5473 (*err) += ss.str();
5474 }
5475 return false;
Benjamin Schmithüsen4557b6a2019-07-09 16:55:55 +02005476 }
5477
David03ad33c2023-02-15 23:35:51 -06005478 const detail::json &v = detail::GetValue(spotIt);
David1f9a4b92023-02-15 22:56:18 -06005479 if (!detail::IsObject(v)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005480 if (err) {
5481 std::stringstream ss;
5482 ss << "\"spot\" is not a JSON object." << std::endl;
5483 (*err) += ss.str();
5484 }
5485 return false;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005486 }
5487
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005488 if (!ParseSpotLight(&light->spot, err, v,
5489 store_original_json_for_extras_and_extensions)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005490 return false;
5491 }
5492 }
5493
5494 ParseStringProperty(&light->name, err, o, "name", false);
5495 ParseNumberArrayProperty(&light->color, err, o, "color", false);
5496 ParseNumberProperty(&light->range, err, o, "range", false);
5497 ParseNumberProperty(&light->intensity, err, o, "intensity", false);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005498
David Siegel22cafa12023-06-05 22:18:59 +02005499 ParseExtrasAndExtensions(light, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005500
Johan Bowald52936a02019-07-17 09:06:45 +02005501 return true;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005502}
5503
David Siegelbec8a6d2023-06-06 15:36:07 +02005504namespace detail {
5505
5506template <typename Callback>
5507bool ForEachInArray(const detail::json &_v, const char *member, Callback && cb) {
5508 detail::json_const_iterator itm;
5509 if (detail::FindMember(_v, member, itm) && detail::IsArray(detail::GetValue(itm))) {
5510 const detail::json &root = detail::GetValue(itm);
5511 auto it = detail::ArrayBegin(root);
5512 auto end = detail::ArrayEnd(root);
5513 for (; it != end; ++it) {
5514 if (!cb(*it)) return false;
5515 }
5516 }
5517 return true;
5518};
5519
5520} // end of namespace detail
5521
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09005522bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005523 const char *json_str,
5524 unsigned int json_str_length,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09005525 const std::string &base_dir,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005526 unsigned int check_sections) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005527 if (json_str_length < 4) {
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09005528 if (err) {
5529 (*err) = "JSON string too short.\n";
5530 }
5531 return false;
5532 }
5533
David03ad33c2023-02-15 23:35:51 -06005534 detail::JsonDocument v;
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005535
Syoyo Fujitad42767e2018-03-15 21:52:00 -05005536#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
5537 defined(_CPPUNWIND)) && \
Syoyo Fujitac4166e42020-01-08 02:38:01 +09005538 !defined(TINYGLTF_NOEXCEPTION)
Syoyo Fujitacdf84902017-08-01 20:12:08 +09005539 try {
David03ad33c2023-02-15 23:35:51 -06005540 detail::JsonParse(v, json_str, json_str_length, true);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005541
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005542 } catch (const std::exception &e) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005543 if (err) {
Syoyo Fujitacdf84902017-08-01 20:12:08 +09005544 (*err) = e.what();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005545 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005546 return false;
5547 }
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005548#else
5549 {
David03ad33c2023-02-15 23:35:51 -06005550 detail::JsonParse(v, json_str, json_str_length);
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005551
David1f9a4b92023-02-15 22:56:18 -06005552 if (!detail::IsObject(v)) {
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005553 // Assume parsing was failed.
5554 if (err) {
5555 (*err) = "Failed to parse JSON object\n";
5556 }
5557 return false;
5558 }
5559 }
5560#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005561
David1f9a4b92023-02-15 22:56:18 -06005562 if (!detail::IsObject(v)) {
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09005563 // root is not an object.
5564 if (err) {
5565 (*err) = "Root element is not a JSON object\n";
5566 }
5567 return false;
5568 }
5569
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02005570 {
5571 bool version_found = false;
David03ad33c2023-02-15 23:35:51 -06005572 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06005573 if (detail::FindMember(v, "asset", it) && detail::IsObject(detail::GetValue(it))) {
5574 auto &itObj = detail::GetValue(it);
David03ad33c2023-02-15 23:35:51 -06005575 detail::json_const_iterator version_it;
jrkooncecba5d6c2019-08-29 11:26:22 -05005576 std::string versionStr;
David1f9a4b92023-02-15 22:56:18 -06005577 if (detail::FindMember(itObj, "version", version_it) &&
5578 detail::GetString(detail::GetValue(version_it), versionStr)) {
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02005579 version_found = true;
5580 }
5581 }
5582 if (version_found) {
5583 // OK
5584 } else if (check_sections & REQUIRE_VERSION) {
5585 if (err) {
5586 (*err) += "\"asset\" object not found in .gltf or not an object type\n";
5587 }
5588 return false;
5589 }
5590 }
5591
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005592 // scene is not mandatory.
Syoyo Fujita5b407452017-06-04 17:42:41 +09005593 // FIXME Maybe a better way to handle it than removing the code
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005594
David03ad33c2023-02-15 23:35:51 -06005595 auto IsArrayMemberPresent = [](const detail::json &_v, const char *name) -> bool {
5596 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06005597 return detail::FindMember(_v, name, it) && detail::IsArray(detail::GetValue(it));
jrkooncecba5d6c2019-08-29 11:26:22 -05005598 };
5599
Syoyo Fujita83675312017-12-02 21:14:13 +09005600 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005601 if ((check_sections & REQUIRE_SCENES) &&
5602 !IsArrayMemberPresent(v, "scenes")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005603 if (err) {
5604 (*err) += "\"scenes\" object not found in .gltf or not an array type\n";
5605 }
5606 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005607 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005608 }
5609
Syoyo Fujita83675312017-12-02 21:14:13 +09005610 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005611 if ((check_sections & REQUIRE_NODES) && !IsArrayMemberPresent(v, "nodes")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005612 if (err) {
5613 (*err) += "\"nodes\" object not found in .gltf\n";
5614 }
5615 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005616 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005617 }
5618
Syoyo Fujita83675312017-12-02 21:14:13 +09005619 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005620 if ((check_sections & REQUIRE_ACCESSORS) &&
5621 !IsArrayMemberPresent(v, "accessors")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005622 if (err) {
5623 (*err) += "\"accessors\" object not found in .gltf\n";
5624 }
5625 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005626 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005627 }
5628
Syoyo Fujita83675312017-12-02 21:14:13 +09005629 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005630 if ((check_sections & REQUIRE_BUFFERS) &&
5631 !IsArrayMemberPresent(v, "buffers")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005632 if (err) {
5633 (*err) += "\"buffers\" object not found in .gltf\n";
5634 }
5635 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005636 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005637 }
5638
Syoyo Fujita83675312017-12-02 21:14:13 +09005639 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005640 if ((check_sections & REQUIRE_BUFFER_VIEWS) &&
5641 !IsArrayMemberPresent(v, "bufferViews")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005642 if (err) {
5643 (*err) += "\"bufferViews\" object not found in .gltf\n";
5644 }
5645 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005646 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005647 }
5648
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005649 model->buffers.clear();
5650 model->bufferViews.clear();
5651 model->accessors.clear();
5652 model->meshes.clear();
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005653 model->cameras.clear();
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005654 model->nodes.clear();
5655 model->extensionsUsed.clear();
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00005656 model->extensionsRequired.clear();
Selmar09d2ff12018-03-15 17:30:42 +01005657 model->extensions.clear();
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005658 model->defaultScene = -1;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005659
Syoyo Fujita83675312017-12-02 21:14:13 +09005660 // 1. Parse Asset
5661 {
David03ad33c2023-02-15 23:35:51 -06005662 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06005663 if (detail::FindMember(v, "asset", it) && detail::IsObject(detail::GetValue(it))) {
David03ad33c2023-02-15 23:35:51 -06005664 const detail::json &root = detail::GetValue(it);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005665
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005666 ParseAsset(&model->asset, err, root,
5667 store_original_json_for_extras_and_extensions_);
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00005668 }
5669 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005670
David Siegelbec8a6d2023-06-06 15:36:07 +02005671 using detail::ForEachInArray;
jrkooncecba5d6c2019-08-29 11:26:22 -05005672
jrkooncecba5d6c2019-08-29 11:26:22 -05005673 // 2. Parse extensionUsed
5674 {
David03ad33c2023-02-15 23:35:51 -06005675 ForEachInArray(v, "extensionsUsed", [&](const detail::json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005676 std::string str;
David1f9a4b92023-02-15 22:56:18 -06005677 detail::GetString(o, str);
jrkooncecba5d6c2019-08-29 11:26:22 -05005678 model->extensionsUsed.emplace_back(std::move(str));
5679 return true;
5680 });
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005681 }
5682
Syoyo Fujita83675312017-12-02 21:14:13 +09005683 {
David03ad33c2023-02-15 23:35:51 -06005684 ForEachInArray(v, "extensionsRequired", [&](const detail::json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005685 std::string str;
David1f9a4b92023-02-15 22:56:18 -06005686 detail::GetString(o, str);
jrkooncecba5d6c2019-08-29 11:26:22 -05005687 model->extensionsRequired.emplace_back(std::move(str));
5688 return true;
5689 });
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005690 }
5691
Syoyo Fujita83675312017-12-02 21:14:13 +09005692 // 3. Parse Buffer
5693 {
David03ad33c2023-02-15 23:35:51 -06005694 bool success = ForEachInArray(v, "buffers", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06005695 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005696 if (err) {
5697 (*err) += "`buffers' does not contain an JSON object.";
Syoyo Fujitabeded612016-05-01 20:03:43 +09005698 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005699 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005700 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005701 Buffer buffer;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005702 if (!ParseBuffer(&buffer, err, o,
5703 store_original_json_for_extras_and_extensions_, &fs,
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09005704 &uri_cb, base_dir, max_external_file_size_, is_binary_, bin_data_, bin_size_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005705 return false;
5706 }
5707
5708 model->buffers.emplace_back(std::move(buffer));
5709 return true;
5710 });
5711
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005712 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005713 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005714 }
5715 }
Syoyo Fujita83675312017-12-02 21:14:13 +09005716 // 4. Parse BufferView
5717 {
David03ad33c2023-02-15 23:35:51 -06005718 bool success = ForEachInArray(v, "bufferViews", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06005719 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005720 if (err) {
5721 (*err) += "`bufferViews' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09005722 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005723 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005724 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005725 BufferView bufferView;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005726 if (!ParseBufferView(&bufferView, err, o,
5727 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005728 return false;
5729 }
5730
5731 model->bufferViews.emplace_back(std::move(bufferView));
5732 return true;
5733 });
5734
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005735 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005736 return false;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005737 }
5738 }
5739
Syoyo Fujita83675312017-12-02 21:14:13 +09005740 // 5. Parse Accessor
5741 {
David03ad33c2023-02-15 23:35:51 -06005742 bool success = ForEachInArray(v, "accessors", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06005743 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005744 if (err) {
5745 (*err) += "`accessors' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09005746 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005747 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005748 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005749 Accessor accessor;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005750 if (!ParseAccessor(&accessor, err, o,
5751 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005752 return false;
5753 }
5754
5755 model->accessors.emplace_back(std::move(accessor));
5756 return true;
5757 });
5758
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005759 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005760 return false;
Syoyo Fujitac2615632016-06-19 21:56:06 +09005761 }
5762 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005763
Syoyo Fujita83675312017-12-02 21:14:13 +09005764 // 6. Parse Mesh
5765 {
David03ad33c2023-02-15 23:35:51 -06005766 bool success = ForEachInArray(v, "meshes", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06005767 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005768 if (err) {
5769 (*err) += "`meshes' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09005770 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005771 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005772 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005773 Mesh mesh;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005774 if (!ParseMesh(&mesh, model, err, o,
5775 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005776 return false;
5777 }
5778
5779 model->meshes.emplace_back(std::move(mesh));
5780 return true;
5781 });
5782
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005783 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005784 return false;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005785 }
5786 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01005787
viperscape9df05802018-12-05 14:11:01 -05005788 // Assign missing bufferView target types
5789 // - Look for missing Mesh indices
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01005790 // - Look for missing Mesh attributes
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00005791 for (auto &mesh : model->meshes) {
5792 for (auto &primitive : mesh.primitives) {
5793 if (primitive.indices >
5794 -1) // has indices from parsing step, must be Element Array Buffer
viperscape9df05802018-12-05 14:11:01 -05005795 {
Jeff McGlynn89152522019-04-25 16:33:56 -07005796 if (size_t(primitive.indices) >= model->accessors.size()) {
5797 if (err) {
5798 (*err) += "primitive indices accessor out of bounds";
5799 }
5800 return false;
5801 }
5802
Syoyo Fujitacea69e32019-08-20 17:10:30 +09005803 auto bufferView =
5804 model->accessors[size_t(primitive.indices)].bufferView;
Jeff McGlynn89152522019-04-25 16:33:56 -07005805 if (bufferView < 0 || size_t(bufferView) >= model->bufferViews.size()) {
5806 if (err) {
5807 (*err) += "accessor[" + std::to_string(primitive.indices) +
5808 "] invalid bufferView";
5809 }
5810 return false;
5811 }
5812
Syoyo Fujitacea69e32019-08-20 17:10:30 +09005813 model->bufferViews[size_t(bufferView)].target =
Jeff McGlynn89152522019-04-25 16:33:56 -07005814 TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER;
imallettd9ce9eb2022-10-07 10:37:09 -07005815 // we could optionally check if accessors' bufferView type is Scalar, as
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00005816 // it should be
viperscape9df05802018-12-05 14:11:01 -05005817 }
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01005818
5819 for (auto &attribute : primitive.attributes) {
Nirmal Patele4132162022-09-06 09:16:31 -07005820 const auto accessorsIndex = size_t(attribute.second);
5821 if (accessorsIndex < model->accessors.size()) {
5822 const auto bufferView = model->accessors[accessorsIndex].bufferView;
5823 // bufferView could be null(-1) for sparse morph target
imallett56e10982022-10-07 10:35:16 -07005824 if (bufferView >= 0 && bufferView < (int)model->bufferViews.size()) {
Nirmal Patele4132162022-09-06 09:16:31 -07005825 model->bufferViews[size_t(bufferView)].target =
5826 TINYGLTF_TARGET_ARRAY_BUFFER;
5827 }
5828 }
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01005829 }
Marcin Kacprzakc3d67162020-01-22 13:13:35 +01005830
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09005831 for (auto &target : primitive.targets) {
5832 for (auto &attribute : target) {
Nirmal Patele4132162022-09-06 09:16:31 -07005833 const auto accessorsIndex = size_t(attribute.second);
5834 if (accessorsIndex < model->accessors.size()) {
5835 const auto bufferView = model->accessors[accessorsIndex].bufferView;
5836 // bufferView could be null(-1) for sparse morph target
imallett56e10982022-10-07 10:35:16 -07005837 if (bufferView >= 0 && bufferView < (int)model->bufferViews.size()) {
Nirmal Patele4132162022-09-06 09:16:31 -07005838 model->bufferViews[size_t(bufferView)].target =
5839 TINYGLTF_TARGET_ARRAY_BUFFER;
5840 }
Rahul Sheth125e4a22020-07-13 13:56:50 -04005841 }
Marcin Kacprzakc3d67162020-01-22 13:13:35 +01005842 }
5843 }
viperscape9df05802018-12-05 14:11:01 -05005844 }
5845 }
5846
Syoyo Fujita83675312017-12-02 21:14:13 +09005847 // 7. Parse Node
5848 {
David03ad33c2023-02-15 23:35:51 -06005849 bool success = ForEachInArray(v, "nodes", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06005850 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005851 if (err) {
5852 (*err) += "`nodes' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005853 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005854 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005855 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005856 Node node;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005857 if (!ParseNode(&node, err, o,
5858 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005859 return false;
5860 }
5861
5862 model->nodes.emplace_back(std::move(node));
5863 return true;
5864 });
5865
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005866 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005867 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005868 }
5869 }
5870
5871 // 8. Parse scenes.
5872 {
David03ad33c2023-02-15 23:35:51 -06005873 bool success = ForEachInArray(v, "scenes", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06005874 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005875 if (err) {
5876 (*err) += "`scenes' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005877 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005878 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005879 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005880 std::vector<int> nodes;
jrkooncea3b8b352019-09-03 10:30:19 -05005881 ParseIntegerArrayProperty(&nodes, err, o, "nodes", false);
jrkooncecba5d6c2019-08-29 11:26:22 -05005882
5883 Scene scene;
5884 scene.nodes = std::move(nodes);
5885
5886 ParseStringProperty(&scene.name, err, o, "name", false);
5887
David Siegel22cafa12023-06-05 22:18:59 +02005888 ParseExtrasAndExtensions(&scene, err, o, store_original_json_for_extras_and_extensions_);
jrkooncecba5d6c2019-08-29 11:26:22 -05005889 model->scenes.emplace_back(std::move(scene));
5890 return true;
5891 });
5892
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005893 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005894 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005895 }
5896 }
5897
5898 // 9. Parse default scenes.
5899 {
David03ad33c2023-02-15 23:35:51 -06005900 detail::json_const_iterator rootIt;
jrkooncecba5d6c2019-08-29 11:26:22 -05005901 int iVal;
David1f9a4b92023-02-15 22:56:18 -06005902 if (detail::FindMember(v, "scene", rootIt) && detail::GetInt(detail::GetValue(rootIt), iVal)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005903 model->defaultScene = iVal;
Syoyo Fujita83675312017-12-02 21:14:13 +09005904 }
5905 }
5906
5907 // 10. Parse Material
5908 {
David03ad33c2023-02-15 23:35:51 -06005909 bool success = ForEachInArray(v, "materials", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06005910 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005911 if (err) {
5912 (*err) += "`materials' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005913 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005914 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005915 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005916 Material material;
5917 ParseStringProperty(&material.name, err, o, "name", false);
5918
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005919 if (!ParseMaterial(&material, err, o,
5920 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005921 return false;
5922 }
5923
5924 model->materials.emplace_back(std::move(material));
5925 return true;
5926 });
5927
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005928 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005929 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005930 }
5931 }
5932
5933 // 11. Parse Image
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09005934 void *load_image_user_data{nullptr};
5935
5936 LoadImageDataOption load_image_option;
5937
5938 if (user_image_loader_) {
5939 // Use user supplied pointer
5940 load_image_user_data = load_image_user_data_;
5941 } else {
5942 load_image_option.preserve_channels = preserve_image_channels_;
5943 load_image_user_data = reinterpret_cast<void *>(&load_image_option);
5944 }
5945
Syoyo Fujita83675312017-12-02 21:14:13 +09005946 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005947 int idx = 0;
David03ad33c2023-02-15 23:35:51 -06005948 bool success = ForEachInArray(v, "images", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06005949 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005950 if (err) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005951 (*err) += "image[" + std::to_string(idx) + "] is not a JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005952 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005953 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005954 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005955 Image image;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005956 if (!ParseImage(&image, idx, err, warn, o,
5957 store_original_json_for_extras_and_extensions_, base_dir,
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09005958 max_external_file_size_, &fs, &uri_cb, &this->LoadImageData,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08005959 load_image_user_data)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005960 return false;
5961 }
5962
5963 if (image.bufferView != -1) {
5964 // Load image from the buffer view.
5965 if (size_t(image.bufferView) >= model->bufferViews.size()) {
5966 if (err) {
5967 std::stringstream ss;
5968 ss << "image[" << idx << "] bufferView \"" << image.bufferView
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005969 << "\" not found in the scene." << std::endl;
jrkooncecba5d6c2019-08-29 11:26:22 -05005970 (*err) += ss.str();
5971 }
5972 return false;
5973 }
5974
5975 const BufferView &bufferView =
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005976 model->bufferViews[size_t(image.bufferView)];
jrkooncecba5d6c2019-08-29 11:26:22 -05005977 if (size_t(bufferView.buffer) >= model->buffers.size()) {
5978 if (err) {
5979 std::stringstream ss;
5980 ss << "image[" << idx << "] buffer \"" << bufferView.buffer
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005981 << "\" not found in the scene." << std::endl;
jrkooncecba5d6c2019-08-29 11:26:22 -05005982 (*err) += ss.str();
5983 }
5984 return false;
5985 }
5986 const Buffer &buffer = model->buffers[size_t(bufferView.buffer)];
5987
5988 if (*LoadImageData == nullptr) {
5989 if (err) {
5990 (*err) += "No LoadImageData callback specified.\n";
5991 }
5992 return false;
5993 }
5994 bool ret = LoadImageData(
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005995 &image, idx, err, warn, image.width, image.height,
5996 &buffer.data[bufferView.byteOffset],
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09005997 static_cast<int>(bufferView.byteLength), load_image_user_data);
jrkooncecba5d6c2019-08-29 11:26:22 -05005998 if (!ret) {
5999 return false;
6000 }
6001 }
6002
6003 model->images.emplace_back(std::move(image));
6004 ++idx;
6005 return true;
6006 });
6007
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006008 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006009 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006010 }
6011 }
6012
6013 // 12. Parse Texture
6014 {
David03ad33c2023-02-15 23:35:51 -06006015 bool success = ForEachInArray(v, "textures", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006016 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006017 if (err) {
6018 (*err) += "`textures' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006019 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006020 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006021 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006022 Texture texture;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006023 if (!ParseTexture(&texture, err, o,
6024 store_original_json_for_extras_and_extensions_,
6025 base_dir)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006026 return false;
6027 }
6028
6029 model->textures.emplace_back(std::move(texture));
6030 return true;
6031 });
6032
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006033 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006034 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006035 }
6036 }
6037
6038 // 13. Parse Animation
6039 {
David03ad33c2023-02-15 23:35:51 -06006040 bool success = ForEachInArray(v, "animations", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006041 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006042 if (err) {
6043 (*err) += "`animations' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006044 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006045 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006046 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006047 Animation animation;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006048 if (!ParseAnimation(&animation, err, o,
6049 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006050 return false;
6051 }
6052
6053 model->animations.emplace_back(std::move(animation));
6054 return true;
6055 });
6056
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006057 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006058 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006059 }
6060 }
6061
6062 // 14. Parse Skin
6063 {
David03ad33c2023-02-15 23:35:51 -06006064 bool success = ForEachInArray(v, "skins", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006065 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006066 if (err) {
6067 (*err) += "`skins' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006068 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006069 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006070 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006071 Skin skin;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006072 if (!ParseSkin(&skin, err, o,
6073 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006074 return false;
6075 }
6076
6077 model->skins.emplace_back(std::move(skin));
6078 return true;
6079 });
6080
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006081 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006082 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006083 }
6084 }
6085
6086 // 15. Parse Sampler
6087 {
David03ad33c2023-02-15 23:35:51 -06006088 bool success = ForEachInArray(v, "samplers", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006089 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006090 if (err) {
6091 (*err) += "`samplers' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006092 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006093 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006094 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006095 Sampler sampler;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006096 if (!ParseSampler(&sampler, err, o,
6097 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006098 return false;
6099 }
6100
6101 model->samplers.emplace_back(std::move(sampler));
6102 return true;
6103 });
6104
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006105 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006106 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006107 }
6108 }
6109
6110 // 16. Parse Camera
6111 {
David03ad33c2023-02-15 23:35:51 -06006112 bool success = ForEachInArray(v, "cameras", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006113 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006114 if (err) {
6115 (*err) += "`cameras' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006116 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006117 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006118 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006119 Camera camera;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006120 if (!ParseCamera(&camera, err, o,
6121 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006122 return false;
6123 }
6124
6125 model->cameras.emplace_back(std::move(camera));
6126 return true;
6127 });
6128
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006129 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006130 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006131 }
6132 }
6133
David Siegel22cafa12023-06-05 22:18:59 +02006134 // 17. Parse Extras & Extensions
6135 ParseExtrasAndExtensions(model, err, v, store_original_json_for_extras_and_extensions_);
Selmar09d2ff12018-03-15 17:30:42 +01006136
6137 // 18. Specific extension implementations
Syoyo Fujita83675312017-12-02 21:14:13 +09006138 {
David03ad33c2023-02-15 23:35:51 -06006139 detail::json_const_iterator rootIt;
David1f9a4b92023-02-15 22:56:18 -06006140 if (detail::FindMember(v, "extensions", rootIt) && detail::IsObject(detail::GetValue(rootIt))) {
David03ad33c2023-02-15 23:35:51 -06006141 const detail::json &root = detail::GetValue(rootIt);
Syoyo Fujita83675312017-12-02 21:14:13 +09006142
David03ad33c2023-02-15 23:35:51 -06006143 detail::json_const_iterator it(detail::ObjectBegin(root));
6144 detail::json_const_iterator itEnd(detail::ObjectEnd(root));
Syoyo Fujita83675312017-12-02 21:14:13 +09006145 for (; it != itEnd; ++it) {
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02006146 // parse KHR_lights_punctual extension
David1f9a4b92023-02-15 22:56:18 -06006147 std::string key(detail::GetKey(it));
6148 if ((key == "KHR_lights_punctual") && detail::IsObject(detail::GetValue(it))) {
David03ad33c2023-02-15 23:35:51 -06006149 const detail::json &object = detail::GetValue(it);
6150 detail::json_const_iterator itLight;
David1f9a4b92023-02-15 22:56:18 -06006151 if (detail::FindMember(object, "lights", itLight)) {
David03ad33c2023-02-15 23:35:51 -06006152 const detail::json &lights = detail::GetValue(itLight);
David1f9a4b92023-02-15 22:56:18 -06006153 if (!detail::IsArray(lights)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006154 continue;
Syoyo Fujita83675312017-12-02 21:14:13 +09006155 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006156
David1f9a4b92023-02-15 22:56:18 -06006157 auto arrayIt(detail::ArrayBegin(lights));
6158 auto arrayItEnd(detail::ArrayEnd(lights));
jrkooncecba5d6c2019-08-29 11:26:22 -05006159 for (; arrayIt != arrayItEnd; ++arrayIt) {
6160 Light light;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006161 if (!ParseLight(&light, err, *arrayIt,
6162 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006163 return false;
6164 }
6165 model->lights.emplace_back(std::move(light));
6166 }
Syoyo Fujita83675312017-12-02 21:14:13 +09006167 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01006168 }
6169 }
6170 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01006171 }
Syoyo Fujita83675312017-12-02 21:14:13 +09006172
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09006173 return true;
6174}
6175
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006176bool TinyGLTF::LoadASCIIFromString(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006177 std::string *warn, const char *str,
6178 unsigned int length,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006179 const std::string &base_dir,
6180 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006181 is_binary_ = false;
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09006182 bin_data_ = nullptr;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006183 bin_size_ = 0;
6184
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006185 return LoadFromString(model, err, warn, str, length, base_dir,
6186 check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006187}
6188
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006189bool TinyGLTF::LoadASCIIFromFile(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006190 std::string *warn, const std::string &filename,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006191 unsigned int check_sections) {
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006192 std::stringstream ss;
6193
Paolo Jovone6601bf2018-07-07 20:43:33 +02006194 if (fs.ReadWholeFile == nullptr) {
6195 // Programmer error, assert() ?
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006196 ss << "Failed to read file: " << filename
6197 << ": one or more FS callback not set" << std::endl;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09006198 if (err) {
6199 (*err) = ss.str();
6200 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006201 return false;
6202 }
6203
Paolo Jovone6601bf2018-07-07 20:43:33 +02006204 std::vector<unsigned char> data;
6205 std::string fileerr;
6206 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006207 if (!fileread) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02006208 ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
6209 if (err) {
6210 (*err) = ss.str();
6211 }
6212 return false;
6213 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006214
Paolo Jovone6601bf2018-07-07 20:43:33 +02006215 size_t sz = data.size();
Luke San Antoniob310b4a2016-06-23 14:17:37 -04006216 if (sz == 0) {
6217 if (err) {
6218 (*err) = "Empty file.";
6219 }
6220 return false;
6221 }
6222
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006223 std::string basedir = GetBaseDir(filename);
6224
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006225 bool ret = LoadASCIIFromString(
6226 model, err, warn, reinterpret_cast<const char *>(&data.at(0)),
6227 static_cast<unsigned int>(data.size()), basedir, check_sections);
Luke San Antonio9e36b612016-06-23 14:10:51 -04006228
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006229 return ret;
6230}
6231
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006232bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006233 std::string *warn,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006234 const unsigned char *bytes,
6235 unsigned int size,
6236 const std::string &base_dir,
6237 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006238 if (size < 20) {
6239 if (err) {
6240 (*err) = "Too short data size for glTF Binary.";
6241 }
6242 return false;
6243 }
6244
Syoyo Fujitabeded612016-05-01 20:03:43 +09006245 if (bytes[0] == 'g' && bytes[1] == 'l' && bytes[2] == 'T' &&
6246 bytes[3] == 'F') {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006247 // ok
6248 } else {
6249 if (err) {
6250 (*err) = "Invalid magic.";
6251 }
6252 return false;
6253 }
6254
Syoyo Fujitabeded612016-05-01 20:03:43 +09006255 unsigned int version; // 4 bytes
6256 unsigned int length; // 4 bytes
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006257 unsigned int chunk0_length; // 4 bytes
6258 unsigned int chunk0_format; // 4 bytes;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006259
Syoyo Fujitabeded612016-05-01 20:03:43 +09006260 memcpy(&version, bytes + 4, 4);
6261 swap4(&version);
6262 memcpy(&length, bytes + 8, 4);
6263 swap4(&length);
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006264 memcpy(&chunk0_length, bytes + 12, 4); // JSON data length
6265 swap4(&chunk0_length);
6266 memcpy(&chunk0_format, bytes + 16, 4);
6267 swap4(&chunk0_format);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006268
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006269 // https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#binary-gltf-layout
6270 //
Syoyo Fujitae69069d2018-03-15 22:01:18 -05006271 // In case the Bin buffer is not present, the size is exactly 20 + size of
6272 // JSON contents,
6273 // so use "greater than" operator.
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006274 //
6275 // https://github.com/syoyo/tinygltf/issues/372
6276 // Use 64bit uint to avoid integer overflow.
Syoyo Fujitac670f082022-09-17 19:52:25 +09006277 uint64_t header_and_json_size = 20ull + uint64_t(chunk0_length);
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006278
Syoyo Fujitac670f082022-09-17 19:52:25 +09006279 if (header_and_json_size > std::numeric_limits<uint32_t>::max()) {
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006280 // Do not allow 4GB or more GLB data.
6281 (*err) = "Invalid glTF binary. GLB data exceeds 4GB.";
6282 }
6283
Syoyo Fujitac670f082022-09-17 19:52:25 +09006284 if ((header_and_json_size > uint64_t(size)) || (chunk0_length < 1) || (length > size) ||
6285 (header_and_json_size > uint64_t(length)) ||
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006286 (chunk0_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006287 if (err) {
6288 (*err) = "Invalid glTF binary.";
6289 }
6290 return false;
6291 }
6292
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006293 // Padding check
6294 // The start and the end of each chunk must be aligned to a 4-byte boundary.
6295 // No padding check for chunk0 start since its 4byte-boundary is ensured.
Syoyo Fujitac670f082022-09-17 19:52:25 +09006296 if ((header_and_json_size % 4) != 0) {
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006297 if (err) {
6298 (*err) = "JSON Chunk end does not aligned to a 4-byte boundary.";
6299 }
6300 }
6301
Syoyo Fujita612e5782022-09-18 21:01:39 +09006302 //std::cout << "header_and_json_size = " << header_and_json_size << "\n";
6303 //std::cout << "length = " << length << "\n";
6304
Syoyo Fujitac670f082022-09-17 19:52:25 +09006305 // Chunk1(BIN) data
6306 // The spec says: When the binary buffer is empty or when it is stored by other means, this chunk SHOULD be omitted.
Syoyo Fujita612e5782022-09-18 21:01:39 +09006307 // So when header + JSON data == binary size, Chunk1 is omitted.
6308 if (header_and_json_size == uint64_t(length)) {
Syoyo Fujitac670f082022-09-17 19:52:25 +09006309
Syoyo Fujitac670f082022-09-17 19:52:25 +09006310 bin_data_ = nullptr;
6311 bin_size_ = 0;
6312 } else {
6313 // Read Chunk1 info(BIN data)
imallettd9ce9eb2022-10-07 10:37:09 -07006314 // At least Chunk1 should have 12 bytes(8 bytes(header) + 4 bytes(bin payload could be 1~3 bytes, but need to be aligned to 4 bytes)
Syoyo Fujita612e5782022-09-18 21:01:39 +09006315 if ((header_and_json_size + 12ull) > uint64_t(length)) {
Syoyo Fujitac670f082022-09-17 19:52:25 +09006316 if (err) {
Syoyo Fujita612e5782022-09-18 21:01:39 +09006317 (*err) = "Insufficient storage space for Chunk1(BIN data). At least Chunk1 Must have 4 bytes or more bytes, but got " + std::to_string((header_and_json_size + 12ull) - uint64_t(length)) + ".\n";
Syoyo Fujitac670f082022-09-17 19:52:25 +09006318 }
6319 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006320 }
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006321
Syoyo Fujitac670f082022-09-17 19:52:25 +09006322 unsigned int chunk1_length; // 4 bytes
6323 unsigned int chunk1_format; // 4 bytes;
6324 memcpy(&chunk1_length, bytes + header_and_json_size, 4); // JSON data length
6325 swap4(&chunk1_length);
6326 memcpy(&chunk1_format, bytes + header_and_json_size + 4, 4);
6327 swap4(&chunk1_format);
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006328
Syoyo Fujita612e5782022-09-18 21:01:39 +09006329 //std::cout << "chunk1_length = " << chunk1_length << "\n";
6330
Syoyo Fujitac670f082022-09-17 19:52:25 +09006331 if (chunk1_length < 4) {
6332 if (err) {
6333 (*err) = "Insufficient Chunk1(BIN) data size.";
6334 }
6335 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006336 }
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006337
Syoyo Fujitac670f082022-09-17 19:52:25 +09006338 if ((chunk1_length % 4) != 0) {
6339 if (err) {
6340 (*err) = "BIN Chunk end does not aligned to a 4-byte boundary.";
6341 }
6342 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006343 }
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006344
Syoyo Fujitac670f082022-09-17 19:52:25 +09006345 if (uint64_t(chunk1_length) + header_and_json_size > uint64_t(length)) {
6346 if (err) {
6347 (*err) = "BIN Chunk data length exceeds the GLB size.";
6348 }
6349 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006350 }
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006351
Syoyo Fujitac670f082022-09-17 19:52:25 +09006352 if (chunk1_format != 0x004e4942) {
6353 if (err) {
imallettd9ce9eb2022-10-07 10:37:09 -07006354 (*err) = "Invalid type for chunk1 data.";
Syoyo Fujitac670f082022-09-17 19:52:25 +09006355 }
6356 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006357 }
Syoyo Fujitac670f082022-09-17 19:52:25 +09006358
Syoyo Fujita612e5782022-09-18 21:01:39 +09006359 //std::cout << "chunk1_length = " << chunk1_length << "\n";
6360
Syoyo Fujitac670f082022-09-17 19:52:25 +09006361 bin_data_ = bytes + header_and_json_size +
6362 8; // 4 bytes (bin_buffer_length) + 4 bytes(bin_buffer_format)
6363
6364 bin_size_ = size_t(chunk1_length);
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006365 }
6366
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006367 // Extract JSON string.
Syoyo Fujitabeded612016-05-01 20:03:43 +09006368 std::string jsonString(reinterpret_cast<const char *>(&bytes[20]),
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006369 chunk0_length);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006370
6371 is_binary_ = true;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006372
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006373 bool ret = LoadFromString(model, err, warn,
6374 reinterpret_cast<const char *>(&bytes[20]),
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006375 chunk0_length, base_dir, check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006376 if (!ret) {
6377 return ret;
6378 }
6379
6380 return true;
6381}
6382
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006383bool TinyGLTF::LoadBinaryFromFile(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006384 std::string *warn,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006385 const std::string &filename,
6386 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006387 std::stringstream ss;
6388
Paolo Jovone6601bf2018-07-07 20:43:33 +02006389 if (fs.ReadWholeFile == nullptr) {
6390 // Programmer error, assert() ?
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006391 ss << "Failed to read file: " << filename
6392 << ": one or more FS callback not set" << std::endl;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006393 if (err) {
6394 (*err) = ss.str();
6395 }
6396 return false;
6397 }
6398
Paolo Jovone6601bf2018-07-07 20:43:33 +02006399 std::vector<unsigned char> data;
6400 std::string fileerr;
6401 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006402 if (!fileread) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02006403 ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
6404 if (err) {
6405 (*err) = ss.str();
6406 }
6407 return false;
6408 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006409
Syoyo Fujitabeded612016-05-01 20:03:43 +09006410 std::string basedir = GetBaseDir(filename);
6411
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006412 bool ret = LoadBinaryFromMemory(model, err, warn, &data.at(0),
6413 static_cast<unsigned int>(data.size()),
6414 basedir, check_sections);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006415
6416 return ret;
6417}
6418
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006419///////////////////////
6420// GLTF Serialization
6421///////////////////////
David1f9a4b92023-02-15 22:56:18 -06006422namespace detail {
David03ad33c2023-02-15 23:35:51 -06006423detail::json JsonFromString(const char *s) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006424#ifdef TINYGLTF_USE_RAPIDJSON
David03ad33c2023-02-15 23:35:51 -06006425 return detail::json(s, detail::GetAllocator());
jrkooncecba5d6c2019-08-29 11:26:22 -05006426#else
David03ad33c2023-02-15 23:35:51 -06006427 return detail::json(s);
jrkooncecba5d6c2019-08-29 11:26:22 -05006428#endif
jrkoonce63419a12019-09-03 17:06:41 -05006429}
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006430
David03ad33c2023-02-15 23:35:51 -06006431void JsonAssign(detail::json &dest, const detail::json &src) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006432#ifdef TINYGLTF_USE_RAPIDJSON
David03ad33c2023-02-15 23:35:51 -06006433 dest.CopyFrom(src, detail::GetAllocator());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006434#else
6435 dest = src;
6436#endif
6437}
6438
David03ad33c2023-02-15 23:35:51 -06006439void JsonAddMember(detail::json &o, const char *key, detail::json &&value) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006440#ifdef TINYGLTF_USE_RAPIDJSON
6441 if (!o.IsObject()) {
6442 o.SetObject();
6443 }
Syoyo Fujita147a00a2023-06-04 05:45:24 +09006444
6445 // Issue 420.
6446 // AddMember may create duplicated key, so use [] API when a key already exists.
6447 // https://github.com/Tencent/rapidjson/issues/771#issuecomment-254386863
6448 detail::json_const_iterator it;
6449 if (detail::FindMember(o, key, it)) {
6450 o[key] = std::move(value); // replace
6451 } else {
6452 o.AddMember(detail::json(key, detail::GetAllocator()), std::move(value), detail::GetAllocator());
6453 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006454#else
6455 o[key] = std::move(value);
6456#endif
6457}
6458
David03ad33c2023-02-15 23:35:51 -06006459void JsonPushBack(detail::json &o, detail::json &&value) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006460#ifdef TINYGLTF_USE_RAPIDJSON
David03ad33c2023-02-15 23:35:51 -06006461 o.PushBack(std::move(value), detail::GetAllocator());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006462#else
6463 o.push_back(std::move(value));
6464#endif
6465}
6466
David03ad33c2023-02-15 23:35:51 -06006467bool JsonIsNull(const detail::json &o) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006468#ifdef TINYGLTF_USE_RAPIDJSON
6469 return o.IsNull();
6470#else
6471 return o.is_null();
6472#endif
6473}
6474
David03ad33c2023-02-15 23:35:51 -06006475void JsonSetObject(detail::json &o) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006476#ifdef TINYGLTF_USE_RAPIDJSON
6477 o.SetObject();
6478#else
6479 o = o.object({});
6480#endif
6481}
6482
David03ad33c2023-02-15 23:35:51 -06006483void JsonReserveArray(detail::json &o, size_t s) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006484#ifdef TINYGLTF_USE_RAPIDJSON
6485 o.SetArray();
David03ad33c2023-02-15 23:35:51 -06006486 o.Reserve(static_cast<rapidjson::SizeType>(s), detail::GetAllocator());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006487#endif
6488 (void)(o);
6489 (void)(s);
6490}
6491} // namespace
6492
David03ad33c2023-02-15 23:35:51 -06006493// typedef std::pair<std::string, detail::json> json_object_pair;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006494
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006495template <typename T>
6496static void SerializeNumberProperty(const std::string &key, T number,
David03ad33c2023-02-15 23:35:51 -06006497 detail::json &obj) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006498 // obj.insert(
David03ad33c2023-02-15 23:35:51 -06006499 // json_object_pair(key, detail::json(static_cast<double>(number))));
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006500 // obj[key] = static_cast<double>(number);
David03ad33c2023-02-15 23:35:51 -06006501 detail::JsonAddMember(obj, key.c_str(), detail::json(number));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006502}
6503
Ivo van Dongen272a9df2020-05-29 11:24:11 +03006504#ifdef TINYGLTF_USE_RAPIDJSON
6505template <>
David03ad33c2023-02-15 23:35:51 -06006506void SerializeNumberProperty(const std::string &key, size_t number, detail::json &obj) {
6507 detail::JsonAddMember(obj, key.c_str(), detail::json(static_cast<uint64_t>(number)));
Ivo van Dongen272a9df2020-05-29 11:24:11 +03006508}
6509#endif
6510
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006511template <typename T>
6512static void SerializeNumberArrayProperty(const std::string &key,
6513 const std::vector<T> &value,
David03ad33c2023-02-15 23:35:51 -06006514 detail::json &obj) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006515 if (value.empty()) return;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006516
David03ad33c2023-02-15 23:35:51 -06006517 detail::json ary;
David1f9a4b92023-02-15 22:56:18 -06006518 detail::JsonReserveArray(ary, value.size());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006519 for (const auto &s : value) {
David03ad33c2023-02-15 23:35:51 -06006520 detail::JsonPushBack(ary, detail::json(s));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006521 }
David1f9a4b92023-02-15 22:56:18 -06006522 detail::JsonAddMember(obj, key.c_str(), std::move(ary));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006523}
6524
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006525static void SerializeStringProperty(const std::string &key,
David03ad33c2023-02-15 23:35:51 -06006526 const std::string &value, detail::json &obj) {
David1f9a4b92023-02-15 22:56:18 -06006527 detail::JsonAddMember(obj, key.c_str(), detail::JsonFromString(value.c_str()));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006528}
6529
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006530static void SerializeStringArrayProperty(const std::string &key,
6531 const std::vector<std::string> &value,
David03ad33c2023-02-15 23:35:51 -06006532 detail::json &obj) {
6533 detail::json ary;
David1f9a4b92023-02-15 22:56:18 -06006534 detail::JsonReserveArray(ary, value.size());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006535 for (auto &s : value) {
David1f9a4b92023-02-15 22:56:18 -06006536 detail::JsonPushBack(ary, detail::JsonFromString(s.c_str()));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006537 }
David1f9a4b92023-02-15 22:56:18 -06006538 detail::JsonAddMember(obj, key.c_str(), std::move(ary));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006539}
6540
David03ad33c2023-02-15 23:35:51 -06006541static bool ValueToJson(const Value &value, detail::json *ret) {
6542 detail::json obj;
jrkooncecba5d6c2019-08-29 11:26:22 -05006543#ifdef TINYGLTF_USE_RAPIDJSON
6544 switch (value.Type()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006545 case REAL_TYPE:
6546 obj.SetDouble(value.Get<double>());
6547 break;
6548 case INT_TYPE:
6549 obj.SetInt(value.Get<int>());
6550 break;
6551 case BOOL_TYPE:
6552 obj.SetBool(value.Get<bool>());
6553 break;
6554 case STRING_TYPE:
David03ad33c2023-02-15 23:35:51 -06006555 obj.SetString(value.Get<std::string>().c_str(), detail::GetAllocator());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006556 break;
6557 case ARRAY_TYPE: {
6558 obj.SetArray();
6559 obj.Reserve(static_cast<rapidjson::SizeType>(value.ArrayLen()),
David03ad33c2023-02-15 23:35:51 -06006560 detail::GetAllocator());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006561 for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
6562 Value elementValue = value.Get(int(i));
David03ad33c2023-02-15 23:35:51 -06006563 detail::json elementJson;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006564 if (ValueToJson(value.Get(int(i)), &elementJson))
David03ad33c2023-02-15 23:35:51 -06006565 obj.PushBack(std::move(elementJson), detail::GetAllocator());
jrkooncecba5d6c2019-08-29 11:26:22 -05006566 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006567 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05006568 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006569 case BINARY_TYPE:
6570 // TODO
David03ad33c2023-02-15 23:35:51 -06006571 // obj = detail::json(value.Get<std::vector<unsigned char>>());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006572 return false;
6573 break;
6574 case OBJECT_TYPE: {
6575 obj.SetObject();
6576 Value::Object objMap = value.Get<Value::Object>();
6577 for (auto &it : objMap) {
David03ad33c2023-02-15 23:35:51 -06006578 detail::json elementJson;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006579 if (ValueToJson(it.second, &elementJson)) {
David03ad33c2023-02-15 23:35:51 -06006580 obj.AddMember(detail::json(it.first.c_str(), detail::GetAllocator()),
6581 std::move(elementJson), detail::GetAllocator());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006582 }
6583 }
6584 break;
6585 }
6586 case NULL_TYPE:
6587 default:
6588 return false;
jrkooncecba5d6c2019-08-29 11:26:22 -05006589 }
6590#else
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006591 switch (value.Type()) {
Syoyo Fujita150f2432019-07-25 19:22:44 +09006592 case REAL_TYPE:
David03ad33c2023-02-15 23:35:51 -06006593 obj = detail::json(value.Get<double>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006594 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006595 case INT_TYPE:
David03ad33c2023-02-15 23:35:51 -06006596 obj = detail::json(value.Get<int>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006597 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006598 case BOOL_TYPE:
David03ad33c2023-02-15 23:35:51 -06006599 obj = detail::json(value.Get<bool>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006600 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006601 case STRING_TYPE:
David03ad33c2023-02-15 23:35:51 -06006602 obj = detail::json(value.Get<std::string>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006603 break;
6604 case ARRAY_TYPE: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006605 for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
6606 Value elementValue = value.Get(int(i));
David03ad33c2023-02-15 23:35:51 -06006607 detail::json elementJson;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006608 if (ValueToJson(value.Get(int(i)), &elementJson))
Selmar Kokfa7022f2018-04-04 18:10:20 +02006609 obj.push_back(elementJson);
6610 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006611 break;
6612 }
Selmar Kokfa7022f2018-04-04 18:10:20 +02006613 case BINARY_TYPE:
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006614 // TODO
6615 // obj = json(value.Get<std::vector<unsigned char>>());
Selmar Kokfa7022f2018-04-04 18:10:20 +02006616 return false;
6617 break;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006618 case OBJECT_TYPE: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006619 Value::Object objMap = value.Get<Value::Object>();
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006620 for (auto &it : objMap) {
David03ad33c2023-02-15 23:35:51 -06006621 detail::json elementJson;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006622 if (ValueToJson(it.second, &elementJson)) obj[it.first] = elementJson;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006623 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006624 break;
6625 }
6626 case NULL_TYPE:
6627 default:
6628 return false;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006629 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006630#endif
6631 if (ret) *ret = std::move(obj);
Selmar Kokfa7022f2018-04-04 18:10:20 +02006632 return true;
6633}
6634
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006635static void SerializeValue(const std::string &key, const Value &value,
David03ad33c2023-02-15 23:35:51 -06006636 detail::json &obj) {
6637 detail::json ret;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006638 if (ValueToJson(value, &ret)) {
David1f9a4b92023-02-15 22:56:18 -06006639 detail::JsonAddMember(obj, key.c_str(), std::move(ret));
jrkooncecba5d6c2019-08-29 11:26:22 -05006640 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006641}
6642
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006643static void SerializeGltfBufferData(const std::vector<unsigned char> &data,
David03ad33c2023-02-15 23:35:51 -06006644 detail::json &o) {
johan bowald30c53472018-03-30 11:49:36 +02006645 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09006646 if (data.size() > 0) {
6647 std::string encodedData =
6648 base64_encode(&data[0], static_cast<unsigned int>(data.size()));
6649 SerializeStringProperty("uri", header + encodedData, o);
6650 } else {
6651 // Issue #229
imallettd9ce9eb2022-10-07 10:37:09 -07006652 // size 0 is allowed. Just emit mime header.
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09006653 SerializeStringProperty("uri", header, o);
6654 }
johan bowald30c53472018-03-30 11:49:36 +02006655}
6656
Selmar Koke4677492018-10-25 16:45:49 +02006657static bool SerializeGltfBufferData(const std::vector<unsigned char> &data,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006658 const std::string &binFilename) {
Harokyangfb256602019-10-30 16:13:52 +08006659#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09006660#if defined(__GLIBCXX__) // mingw
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09006661 int file_descriptor = _wopen(UTF8ToWchar(binFilename).c_str(),
6662 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
6663 __gnu_cxx::stdio_filebuf<char> wfile_buf(
6664 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09006665 std::ostream output(&wfile_buf);
6666 if (!wfile_buf.is_open()) return false;
6667#elif defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08006668 std::ofstream output(UTF8ToWchar(binFilename).c_str(), std::ofstream::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09006669 if (!output.is_open()) return false;
Harokyangfb256602019-10-30 16:13:52 +08006670#else
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006671 std::ofstream output(binFilename.c_str(), std::ofstream::binary);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006672 if (!output.is_open()) return false;
Syoyo Fujita45cac782019-11-09 20:42:55 +09006673#endif
6674#else
6675 std::ofstream output(binFilename.c_str(), std::ofstream::binary);
6676 if (!output.is_open()) return false;
6677#endif
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09006678 if (data.size() > 0) {
6679 output.write(reinterpret_cast<const char *>(&data[0]),
6680 std::streamsize(data.size()));
6681 } else {
6682 // Issue #229
6683 // size 0 will be still valid buffer data.
6684 // write empty file.
6685 }
Selmar Koke4677492018-10-25 16:45:49 +02006686 return true;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006687}
6688
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006689#if 0 // FIXME(syoyo): not used. will be removed in the future release.
David03ad33c2023-02-15 23:35:51 -06006690static void SerializeParameterMap(ParameterMap &param, detail::json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006691 for (ParameterMap::iterator paramIt = param.begin(); paramIt != param.end();
6692 ++paramIt) {
6693 if (paramIt->second.number_array.size()) {
6694 SerializeNumberArrayProperty<double>(paramIt->first,
6695 paramIt->second.number_array, o);
6696 } else if (paramIt->second.json_double_value.size()) {
David03ad33c2023-02-15 23:35:51 -06006697 detail::json json_double_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006698 for (std::map<std::string, double>::iterator it =
6699 paramIt->second.json_double_value.begin();
6700 it != paramIt->second.json_double_value.end(); ++it) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006701 if (it->first == "index") {
6702 json_double_value[it->first] = paramIt->second.TextureIndex();
6703 } else {
6704 json_double_value[it->first] = it->second;
6705 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006706 }
6707
Syoyo Fujita83675312017-12-02 21:14:13 +09006708 o[paramIt->first] = json_double_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006709 } else if (!paramIt->second.string_value.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006710 SerializeStringProperty(paramIt->first, paramIt->second.string_value, o);
Ben Buzbeef6af2242018-04-25 15:13:05 -07006711 } else if (paramIt->second.has_number_value) {
6712 o[paramIt->first] = paramIt->second.number_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006713 } else {
Syoyo Fujita83675312017-12-02 21:14:13 +09006714 o[paramIt->first] = paramIt->second.bool_value;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006715 }
6716 }
6717}
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006718#endif
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006719
David03ad33c2023-02-15 23:35:51 -06006720static void SerializeExtensionMap(const ExtensionMap &extensions, detail::json &o) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006721 if (!extensions.size()) return;
Selmar09d2ff12018-03-15 17:30:42 +01006722
David03ad33c2023-02-15 23:35:51 -06006723 detail::json extMap;
Selmar Kok81b672b2019-10-18 16:08:44 +02006724 for (ExtensionMap::const_iterator extIt = extensions.begin();
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006725 extIt != extensions.end(); ++extIt) {
Syoyo Fujita924d86e2018-10-08 21:15:12 +09006726 // Allow an empty object for extension(#97)
David03ad33c2023-02-15 23:35:51 -06006727 detail::json ret;
jrkooncecba5d6c2019-08-29 11:26:22 -05006728 bool isNull = true;
Syoyo Fujita924d86e2018-10-08 21:15:12 +09006729 if (ValueToJson(extIt->second, &ret)) {
David1f9a4b92023-02-15 22:56:18 -06006730 isNull = detail::JsonIsNull(ret);
6731 detail::JsonAddMember(extMap, extIt->first.c_str(), std::move(ret));
Selmar Kokdb7f4e42018-10-10 18:10:58 +02006732 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006733 if (isNull) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006734 if (!(extIt->first.empty())) { // name should not be empty, but for sure
6735 // create empty object so that an extension name is still included in
6736 // json.
David03ad33c2023-02-15 23:35:51 -06006737 detail::json empty;
David1f9a4b92023-02-15 22:56:18 -06006738 detail::JsonSetObject(empty);
6739 detail::JsonAddMember(extMap, extIt->first.c_str(), std::move(empty));
Syoyo Fujita924d86e2018-10-08 21:15:12 +09006740 }
6741 }
Selmar09d2ff12018-03-15 17:30:42 +01006742 }
David1f9a4b92023-02-15 22:56:18 -06006743 detail::JsonAddMember(o, "extensions", std::move(extMap));
Selmar09d2ff12018-03-15 17:30:42 +01006744}
6745
David Siegel07616e82023-06-06 01:24:53 +02006746static void SerializeExtras(const Value & extras, detail::json & o) {
6747 if (extras.Type() != NULL_TYPE)
6748 SerializeValue("extras", extras, o);
6749}
6750
6751template <typename GltfType>
6752void SerializeExtrasAndExtensions(const GltfType & obj, detail::json & o) {
6753 SerializeExtensionMap(obj.extensions, o);
6754 SerializeExtras(obj.extras, o);
6755}
6756
David03ad33c2023-02-15 23:35:51 -06006757static void SerializeGltfAccessor(const Accessor &accessor, detail::json &o) {
Sanjeet Suhag5ecede72020-03-04 17:40:10 -05006758 if (accessor.bufferView >= 0)
6759 SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006760
Syoyo Fujita18f0e202020-04-29 19:16:35 +09006761 if (accessor.byteOffset != 0)
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006762 SerializeNumberProperty<int>("byteOffset", int(accessor.byteOffset), o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006763
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006764 SerializeNumberProperty<int>("componentType", accessor.componentType, o);
6765 SerializeNumberProperty<size_t>("count", accessor.count, o);
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09006766
6767 if ((accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) ||
6768 (accessor.componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE)) {
6769 SerializeNumberArrayProperty<double>("min", accessor.minValues, o);
6770 SerializeNumberArrayProperty<double>("max", accessor.maxValues, o);
6771 } else {
6772 // Issue #301. Serialize as integer.
6773 // Assume int value is within [-2**31-1, 2**31-1]
6774 {
6775 std::vector<int> values;
6776 std::transform(accessor.minValues.begin(), accessor.minValues.end(),
6777 std::back_inserter(values),
6778 [](double v) { return static_cast<int>(v); });
6779
6780 SerializeNumberArrayProperty<int>("min", values, o);
6781 }
6782
6783 {
6784 std::vector<int> values;
6785 std::transform(accessor.maxValues.begin(), accessor.maxValues.end(),
6786 std::back_inserter(values),
6787 [](double v) { return static_cast<int>(v); });
6788
6789 SerializeNumberArrayProperty<int>("max", values, o);
6790 }
6791 }
6792
Eero Pajarre2e8a1152019-11-18 13:09:25 +02006793 if (accessor.normalized)
6794 SerializeValue("normalized", Value(accessor.normalized), o);
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006795 std::string type;
6796 switch (accessor.type) {
6797 case TINYGLTF_TYPE_SCALAR:
6798 type = "SCALAR";
6799 break;
6800 case TINYGLTF_TYPE_VEC2:
6801 type = "VEC2";
6802 break;
6803 case TINYGLTF_TYPE_VEC3:
6804 type = "VEC3";
6805 break;
6806 case TINYGLTF_TYPE_VEC4:
6807 type = "VEC4";
6808 break;
6809 case TINYGLTF_TYPE_MAT2:
6810 type = "MAT2";
6811 break;
6812 case TINYGLTF_TYPE_MAT3:
6813 type = "MAT3";
6814 break;
6815 case TINYGLTF_TYPE_MAT4:
6816 type = "MAT4";
6817 break;
6818 }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09006819
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006820 SerializeStringProperty("type", type, o);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09006821 if (!accessor.name.empty()) SerializeStringProperty("name", accessor.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006822
David Siegel07616e82023-06-06 01:24:53 +02006823 SerializeExtrasAndExtensions(accessor, o);
feiy0b315432022-08-13 10:08:17 +08006824
6825 // sparse
6826 if (accessor.sparse.isSparse)
6827 {
David03ad33c2023-02-15 23:35:51 -06006828 detail::json sparse;
feiy0b315432022-08-13 10:08:17 +08006829 SerializeNumberProperty<int>("count", accessor.sparse.count, sparse);
6830 {
David03ad33c2023-02-15 23:35:51 -06006831 detail::json indices;
feiy0b315432022-08-13 10:08:17 +08006832 SerializeNumberProperty<int>("bufferView", accessor.sparse.indices.bufferView, indices);
6833 SerializeNumberProperty<int>("byteOffset", accessor.sparse.indices.byteOffset, indices);
6834 SerializeNumberProperty<int>("componentType", accessor.sparse.indices.componentType, indices);
David Siegel07616e82023-06-06 01:24:53 +02006835 SerializeExtrasAndExtensions(accessor.sparse.indices, indices);
David1f9a4b92023-02-15 22:56:18 -06006836 detail::JsonAddMember(sparse, "indices", std::move(indices));
feiy0b315432022-08-13 10:08:17 +08006837 }
6838 {
David03ad33c2023-02-15 23:35:51 -06006839 detail::json values;
feiy0b315432022-08-13 10:08:17 +08006840 SerializeNumberProperty<int>("bufferView", accessor.sparse.values.bufferView, values);
feiy1a8814d2022-08-14 19:49:07 +08006841 SerializeNumberProperty<int>("byteOffset", accessor.sparse.values.byteOffset, values);
David Siegel07616e82023-06-06 01:24:53 +02006842 SerializeExtrasAndExtensions(accessor.sparse.values, values);
David1f9a4b92023-02-15 22:56:18 -06006843 detail::JsonAddMember(sparse, "values", std::move(values));
feiy0b315432022-08-13 10:08:17 +08006844 }
David Siegel07616e82023-06-06 01:24:53 +02006845 SerializeExtrasAndExtensions(accessor.sparse, sparse);
David1f9a4b92023-02-15 22:56:18 -06006846 detail::JsonAddMember(o, "sparse", std::move(sparse));
feiy0b315432022-08-13 10:08:17 +08006847 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006848}
6849
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08006850static void SerializeGltfAnimationChannel(const AnimationChannel &channel,
David03ad33c2023-02-15 23:35:51 -06006851 detail::json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006852 SerializeNumberProperty("sampler", channel.sampler, o);
jrkooncecba5d6c2019-08-29 11:26:22 -05006853 {
David03ad33c2023-02-15 23:35:51 -06006854 detail::json target;
Jack Mousseau283b5522023-01-15 11:45:45 -08006855
Loïc Escalesa75355b2023-04-18 21:03:39 +02006856 if (channel.target_node >= 0) {
Jack Mousseau283b5522023-01-15 11:45:45 -08006857 SerializeNumberProperty("node", channel.target_node, target);
6858 }
6859
jrkooncecba5d6c2019-08-29 11:26:22 -05006860 SerializeStringProperty("path", channel.target_path, target);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006861
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09006862 SerializeExtensionMap(channel.target_extensions, target);
David Siegel07616e82023-06-06 01:24:53 +02006863 SerializeExtras(channel.target_extras, target);
Selmar Kok973d9b32020-01-21 18:45:24 +01006864
David1f9a4b92023-02-15 22:56:18 -06006865 detail::JsonAddMember(o, "target", std::move(target));
jrkooncecba5d6c2019-08-29 11:26:22 -05006866 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02006867
David Siegel07616e82023-06-06 01:24:53 +02006868 SerializeExtrasAndExtensions(channel, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006869}
6870
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08006871static void SerializeGltfAnimationSampler(const AnimationSampler &sampler,
David03ad33c2023-02-15 23:35:51 -06006872 detail::json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006873 SerializeNumberProperty("input", sampler.input, o);
6874 SerializeNumberProperty("output", sampler.output, o);
6875 SerializeStringProperty("interpolation", sampler.interpolation, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006876
David Siegel07616e82023-06-06 01:24:53 +02006877 SerializeExtrasAndExtensions(sampler, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006878}
6879
David03ad33c2023-02-15 23:35:51 -06006880static void SerializeGltfAnimation(const Animation &animation, detail::json &o) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09006881 if (!animation.name.empty())
6882 SerializeStringProperty("name", animation.name, o);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006883
jrkooncecba5d6c2019-08-29 11:26:22 -05006884 {
David03ad33c2023-02-15 23:35:51 -06006885 detail::json channels;
David1f9a4b92023-02-15 22:56:18 -06006886 detail::JsonReserveArray(channels, animation.channels.size());
jrkooncecba5d6c2019-08-29 11:26:22 -05006887 for (unsigned int i = 0; i < animation.channels.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06006888 detail::json channel;
jrkooncecba5d6c2019-08-29 11:26:22 -05006889 AnimationChannel gltfChannel = animation.channels[i];
6890 SerializeGltfAnimationChannel(gltfChannel, channel);
David1f9a4b92023-02-15 22:56:18 -06006891 detail::JsonPushBack(channels, std::move(channel));
jrkooncecba5d6c2019-08-29 11:26:22 -05006892 }
6893
David1f9a4b92023-02-15 22:56:18 -06006894 detail::JsonAddMember(o, "channels", std::move(channels));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006895 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006896
jrkooncecba5d6c2019-08-29 11:26:22 -05006897 {
David03ad33c2023-02-15 23:35:51 -06006898 detail::json samplers;
David1f9a4b92023-02-15 22:56:18 -06006899 detail::JsonReserveArray(samplers, animation.samplers.size());
jrkooncecba5d6c2019-08-29 11:26:22 -05006900 for (unsigned int i = 0; i < animation.samplers.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06006901 detail::json sampler;
jrkooncecba5d6c2019-08-29 11:26:22 -05006902 AnimationSampler gltfSampler = animation.samplers[i];
6903 SerializeGltfAnimationSampler(gltfSampler, sampler);
David1f9a4b92023-02-15 22:56:18 -06006904 detail::JsonPushBack(samplers, std::move(sampler));
jrkooncecba5d6c2019-08-29 11:26:22 -05006905 }
David1f9a4b92023-02-15 22:56:18 -06006906 detail::JsonAddMember(o, "samplers", std::move(samplers));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006907 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006908
David Siegel07616e82023-06-06 01:24:53 +02006909 SerializeExtrasAndExtensions(animation, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006910}
6911
David03ad33c2023-02-15 23:35:51 -06006912static void SerializeGltfAsset(const Asset &asset, detail::json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006913 if (!asset.generator.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006914 SerializeStringProperty("generator", asset.generator, o);
6915 }
6916
Christophe820ede82019-07-04 15:21:21 +09006917 if (!asset.copyright.empty()) {
6918 SerializeStringProperty("copyright", asset.copyright, o);
6919 }
6920
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08006921 auto version = asset.version;
6922 if (version.empty()) {
Syoyo Fujitab702de72021-03-02 19:08:29 +09006923 // Just in case
6924 // `version` must be defined
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08006925 version = "2.0";
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006926 }
6927
Syoyo Fujitab702de72021-03-02 19:08:29 +09006928 // TODO(syoyo): Do we need to check if `version` is greater or equal to 2.0?
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08006929 SerializeStringProperty("version", version, o);
Syoyo Fujitab702de72021-03-02 19:08:29 +09006930
David Siegel07616e82023-06-06 01:24:53 +02006931 SerializeExtrasAndExtensions(asset, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006932}
6933
David03ad33c2023-02-15 23:35:51 -06006934static void SerializeGltfBufferBin(const Buffer &buffer, detail::json &o,
Syoyo Fujitac4166e42020-01-08 02:38:01 +09006935 std::vector<unsigned char> &binBuffer) {
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02006936 SerializeNumberProperty("byteLength", buffer.data.size(), o);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09006937 binBuffer = buffer.data;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02006938
6939 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
6940
David Siegel07616e82023-06-06 01:24:53 +02006941 SerializeExtrasAndExtensions(buffer, o);
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02006942}
6943
David03ad33c2023-02-15 23:35:51 -06006944static void SerializeGltfBuffer(const Buffer &buffer, detail::json &o) {
johan bowald30c53472018-03-30 11:49:36 +02006945 SerializeNumberProperty("byteLength", buffer.data.size(), o);
6946 SerializeGltfBufferData(buffer.data, o);
6947
6948 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006949
David Siegel07616e82023-06-06 01:24:53 +02006950 SerializeExtrasAndExtensions(buffer, o);
johan bowald30c53472018-03-30 11:49:36 +02006951}
6952
David03ad33c2023-02-15 23:35:51 -06006953static bool SerializeGltfBuffer(const Buffer &buffer, detail::json &o,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006954 const std::string &binFilename,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08006955 const std::string &binUri) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006956 if (!SerializeGltfBufferData(buffer.data, binFilename)) return false;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006957 SerializeNumberProperty("byteLength", buffer.data.size(), o);
Pyarelal Knowles385946d2023-01-03 18:18:04 -08006958 SerializeStringProperty("uri", binUri, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006959
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006960 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006961
David Siegel07616e82023-06-06 01:24:53 +02006962 SerializeExtrasAndExtensions(buffer, o);
Selmar Koke4677492018-10-25 16:45:49 +02006963 return true;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006964}
6965
David03ad33c2023-02-15 23:35:51 -06006966static void SerializeGltfBufferView(const BufferView &bufferView, detail::json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006967 SerializeNumberProperty("buffer", bufferView.buffer, o);
6968 SerializeNumberProperty<size_t>("byteLength", bufferView.byteLength, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006969
Johan Bowaldfaa27222018-03-28 14:44:45 +02006970 // byteStride is optional, minimum allowed is 4
6971 if (bufferView.byteStride >= 4) {
6972 SerializeNumberProperty<size_t>("byteStride", bufferView.byteStride, o);
6973 }
6974 // byteOffset is optional, default is 0
6975 if (bufferView.byteOffset > 0) {
6976 SerializeNumberProperty<size_t>("byteOffset", bufferView.byteOffset, o);
6977 }
6978 // Target is optional, check if it contains a valid value
6979 if (bufferView.target == TINYGLTF_TARGET_ARRAY_BUFFER ||
6980 bufferView.target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER) {
6981 SerializeNumberProperty("target", bufferView.target, o);
6982 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006983 if (bufferView.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006984 SerializeStringProperty("name", bufferView.name, o);
6985 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02006986
David Siegel07616e82023-06-06 01:24:53 +02006987 SerializeExtrasAndExtensions(bufferView, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006988}
6989
Pyarelal Knowlesa9121552022-12-29 14:12:29 -08006990static void SerializeGltfImage(const Image &image, const std::string &uri,
David03ad33c2023-02-15 23:35:51 -06006991 detail::json &o) {
Syoyo Fujita584f1df2022-12-29 21:05:53 +09006992 // From 2.7.0, we look for `uri` parameter, not `Image.uri`
6993 // if uri is empty, the mimeType and bufferview should be set
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08006994 if (uri.empty()) {
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02006995 SerializeStringProperty("mimeType", image.mimeType, o);
6996 SerializeNumberProperty<int>("bufferView", image.bufferView, o);
6997 } else {
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08006998 SerializeStringProperty("uri", uri, o);
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02006999 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007000
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007001 if (image.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007002 SerializeStringProperty("name", image.name, o);
7003 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007004
David Siegel07616e82023-06-06 01:24:53 +02007005 SerializeExtrasAndExtensions(image, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007006}
7007
David03ad33c2023-02-15 23:35:51 -06007008static void SerializeGltfTextureInfo(const TextureInfo &texinfo, detail::json &o) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007009 SerializeNumberProperty("index", texinfo.index, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007010
Syoyo Fujita046400b2019-07-24 19:26:48 +09007011 if (texinfo.texCoord != 0) {
7012 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
7013 }
7014
David Siegel07616e82023-06-06 01:24:53 +02007015 SerializeExtrasAndExtensions(texinfo, o);
Syoyo Fujita046400b2019-07-24 19:26:48 +09007016}
7017
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007018static void SerializeGltfNormalTextureInfo(const NormalTextureInfo &texinfo,
David03ad33c2023-02-15 23:35:51 -06007019 detail::json &o) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007020 SerializeNumberProperty("index", texinfo.index, o);
7021
7022 if (texinfo.texCoord != 0) {
7023 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
7024 }
7025
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007026 if (!TINYGLTF_DOUBLE_EQUAL(texinfo.scale, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007027 SerializeNumberProperty("scale", texinfo.scale, o);
7028 }
7029
David Siegel07616e82023-06-06 01:24:53 +02007030 SerializeExtrasAndExtensions(texinfo, o);
Syoyo Fujita046400b2019-07-24 19:26:48 +09007031}
7032
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007033static void SerializeGltfOcclusionTextureInfo(
David03ad33c2023-02-15 23:35:51 -06007034 const OcclusionTextureInfo &texinfo, detail::json &o) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007035 SerializeNumberProperty("index", texinfo.index, o);
7036
7037 if (texinfo.texCoord != 0) {
7038 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
7039 }
7040
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007041 if (!TINYGLTF_DOUBLE_EQUAL(texinfo.strength, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007042 SerializeNumberProperty("strength", texinfo.strength, o);
7043 }
7044
David Siegel07616e82023-06-06 01:24:53 +02007045 SerializeExtrasAndExtensions(texinfo, o);
Syoyo Fujita046400b2019-07-24 19:26:48 +09007046}
7047
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007048static void SerializeGltfPbrMetallicRoughness(const PbrMetallicRoughness &pbr,
David03ad33c2023-02-15 23:35:51 -06007049 detail::json &o) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007050 std::vector<double> default_baseColorFactor = {1.0, 1.0, 1.0, 1.0};
7051 if (!Equals(pbr.baseColorFactor, default_baseColorFactor)) {
7052 SerializeNumberArrayProperty<double>("baseColorFactor", pbr.baseColorFactor,
7053 o);
7054 }
7055
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007056 if (!TINYGLTF_DOUBLE_EQUAL(pbr.metallicFactor, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007057 SerializeNumberProperty("metallicFactor", pbr.metallicFactor, o);
7058 }
7059
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007060 if (!TINYGLTF_DOUBLE_EQUAL(pbr.roughnessFactor, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007061 SerializeNumberProperty("roughnessFactor", pbr.roughnessFactor, o);
7062 }
7063
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007064 if (pbr.baseColorTexture.index > -1) {
David03ad33c2023-02-15 23:35:51 -06007065 detail::json texinfo;
Syoyo Fujita046400b2019-07-24 19:26:48 +09007066 SerializeGltfTextureInfo(pbr.baseColorTexture, texinfo);
David1f9a4b92023-02-15 22:56:18 -06007067 detail::JsonAddMember(o, "baseColorTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007068 }
7069
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007070 if (pbr.metallicRoughnessTexture.index > -1) {
David03ad33c2023-02-15 23:35:51 -06007071 detail::json texinfo;
Syoyo Fujita046400b2019-07-24 19:26:48 +09007072 SerializeGltfTextureInfo(pbr.metallicRoughnessTexture, texinfo);
David1f9a4b92023-02-15 22:56:18 -06007073 detail::JsonAddMember(o, "metallicRoughnessTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007074 }
7075
David Siegel07616e82023-06-06 01:24:53 +02007076 SerializeExtrasAndExtensions(pbr, o);
Syoyo Fujita046400b2019-07-24 19:26:48 +09007077}
7078
David03ad33c2023-02-15 23:35:51 -06007079static void SerializeGltfMaterial(const Material &material, detail::json &o) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007080 if (material.name.size()) {
7081 SerializeStringProperty("name", material.name, o);
7082 }
7083
7084 // QUESTION(syoyo): Write material parameters regardless of its default value?
7085
7086 if (!TINYGLTF_DOUBLE_EQUAL(material.alphaCutoff, 0.5)) {
7087 SerializeNumberProperty("alphaCutoff", material.alphaCutoff, o);
7088 }
7089
Patrick Härtld9a468b2019-08-14 14:14:07 +02007090 if (material.alphaMode.compare("OPAQUE") != 0) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007091 SerializeStringProperty("alphaMode", material.alphaMode, o);
7092 }
7093
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007094 if (material.doubleSided != false)
David03ad33c2023-02-15 23:35:51 -06007095 detail::JsonAddMember(o, "doubleSided", detail::json(material.doubleSided));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007096
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007097 if (material.normalTexture.index > -1) {
David03ad33c2023-02-15 23:35:51 -06007098 detail::json texinfo;
Syoyo Fujita046400b2019-07-24 19:26:48 +09007099 SerializeGltfNormalTextureInfo(material.normalTexture, texinfo);
David1f9a4b92023-02-15 22:56:18 -06007100 detail::JsonAddMember(o, "normalTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007101 }
7102
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007103 if (material.occlusionTexture.index > -1) {
David03ad33c2023-02-15 23:35:51 -06007104 detail::json texinfo;
Syoyo Fujita046400b2019-07-24 19:26:48 +09007105 SerializeGltfOcclusionTextureInfo(material.occlusionTexture, texinfo);
David1f9a4b92023-02-15 22:56:18 -06007106 detail::JsonAddMember(o, "occlusionTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007107 }
7108
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007109 if (material.emissiveTexture.index > -1) {
David03ad33c2023-02-15 23:35:51 -06007110 detail::json texinfo;
Syoyo Fujita046400b2019-07-24 19:26:48 +09007111 SerializeGltfTextureInfo(material.emissiveTexture, texinfo);
David1f9a4b92023-02-15 22:56:18 -06007112 detail::JsonAddMember(o, "emissiveTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007113 }
7114
7115 std::vector<double> default_emissiveFactor = {0.0, 0.0, 0.0};
7116 if (!Equals(material.emissiveFactor, default_emissiveFactor)) {
7117 SerializeNumberArrayProperty<double>("emissiveFactor",
7118 material.emissiveFactor, o);
7119 }
7120
7121 {
David03ad33c2023-02-15 23:35:51 -06007122 detail::json pbrMetallicRoughness;
Syoyo Fujita046400b2019-07-24 19:26:48 +09007123 SerializeGltfPbrMetallicRoughness(material.pbrMetallicRoughness,
7124 pbrMetallicRoughness);
Syoyo Fujita7e009042019-09-13 15:32:22 +09007125 // Issue 204
7126 // Do not serialize `pbrMetallicRoughness` if pbrMetallicRoughness has all
7127 // default values(json is null). Otherwise it will serialize to
7128 // `pbrMetallicRoughness : null`, which cannot be read by other glTF
imallettd9ce9eb2022-10-07 10:37:09 -07007129 // importers (and validators).
Syoyo Fujita7e009042019-09-13 15:32:22 +09007130 //
David1f9a4b92023-02-15 22:56:18 -06007131 if (!detail::JsonIsNull(pbrMetallicRoughness)) {
7132 detail::JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness));
Syoyo Fujita7e009042019-09-13 15:32:22 +09007133 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09007134 }
7135
7136#if 0 // legacy way. just for the record.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007137 if (material.values.size()) {
David03ad33c2023-02-15 23:35:51 -06007138 detail::json pbrMetallicRoughness;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007139 SerializeParameterMap(material.values, pbrMetallicRoughness);
David1f9a4b92023-02-15 22:56:18 -06007140 detail::JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007141 }
7142
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007143 SerializeParameterMap(material.additionalValues, o);
Syoyo Fujita046400b2019-07-24 19:26:48 +09007144#else
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007145
Syoyo Fujita046400b2019-07-24 19:26:48 +09007146#endif
7147
David Siegel07616e82023-06-06 01:24:53 +02007148 SerializeExtrasAndExtensions(material, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007149}
7150
David03ad33c2023-02-15 23:35:51 -06007151static void SerializeGltfMesh(const Mesh &mesh, detail::json &o) {
7152 detail::json primitives;
David1f9a4b92023-02-15 22:56:18 -06007153 detail::JsonReserveArray(primitives, mesh.primitives.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007154 for (unsigned int i = 0; i < mesh.primitives.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007155 detail::json primitive;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007156 const Primitive &gltfPrimitive = mesh.primitives[i]; // don't make a copy
jrkooncecba5d6c2019-08-29 11:26:22 -05007157 {
David03ad33c2023-02-15 23:35:51 -06007158 detail::json attributes;
jrkooncecba5d6c2019-08-29 11:26:22 -05007159 for (auto attrIt = gltfPrimitive.attributes.begin();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007160 attrIt != gltfPrimitive.attributes.end(); ++attrIt) {
jrkooncecba5d6c2019-08-29 11:26:22 -05007161 SerializeNumberProperty<int>(attrIt->first, attrIt->second, attributes);
7162 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007163
David1f9a4b92023-02-15 22:56:18 -06007164 detail::JsonAddMember(primitive, "attributes", std::move(attributes));
jrkooncecba5d6c2019-08-29 11:26:22 -05007165 }
Johan Bowaldfaa27222018-03-28 14:44:45 +02007166
imallettd9ce9eb2022-10-07 10:37:09 -07007167 // Indices is optional
Johan Bowaldfaa27222018-03-28 14:44:45 +02007168 if (gltfPrimitive.indices > -1) {
7169 SerializeNumberProperty<int>("indices", gltfPrimitive.indices, primitive);
7170 }
7171 // Material is optional
7172 if (gltfPrimitive.material > -1) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09007173 SerializeNumberProperty<int>("material", gltfPrimitive.material,
7174 primitive);
Johan Bowaldfaa27222018-03-28 14:44:45 +02007175 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007176 SerializeNumberProperty<int>("mode", gltfPrimitive.mode, primitive);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007177
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007178 // Morph targets
7179 if (gltfPrimitive.targets.size()) {
David03ad33c2023-02-15 23:35:51 -06007180 detail::json targets;
David1f9a4b92023-02-15 22:56:18 -06007181 detail::JsonReserveArray(targets, gltfPrimitive.targets.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007182 for (unsigned int k = 0; k < gltfPrimitive.targets.size(); ++k) {
David03ad33c2023-02-15 23:35:51 -06007183 detail::json targetAttributes;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007184 std::map<std::string, int> targetData = gltfPrimitive.targets[k];
7185 for (std::map<std::string, int>::iterator attrIt = targetData.begin();
7186 attrIt != targetData.end(); ++attrIt) {
7187 SerializeNumberProperty<int>(attrIt->first, attrIt->second,
7188 targetAttributes);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007189 }
David1f9a4b92023-02-15 22:56:18 -06007190 detail::JsonPushBack(targets, std::move(targetAttributes));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007191 }
David1f9a4b92023-02-15 22:56:18 -06007192 detail::JsonAddMember(primitive, "targets", std::move(targets));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007193 }
7194
David Siegel07616e82023-06-06 01:24:53 +02007195 SerializeExtrasAndExtensions(gltfPrimitive, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007196
David1f9a4b92023-02-15 22:56:18 -06007197 detail::JsonPushBack(primitives, std::move(primitive));
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007198 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007199
David1f9a4b92023-02-15 22:56:18 -06007200 detail::JsonAddMember(o, "primitives", std::move(primitives));
jrkooncecba5d6c2019-08-29 11:26:22 -05007201
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007202 if (mesh.weights.size()) {
7203 SerializeNumberArrayProperty<double>("weights", mesh.weights, o);
7204 }
7205
7206 if (mesh.name.size()) {
7207 SerializeStringProperty("name", mesh.name, o);
7208 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007209
David Siegel07616e82023-06-06 01:24:53 +02007210 SerializeExtrasAndExtensions(mesh, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007211}
7212
David03ad33c2023-02-15 23:35:51 -06007213static void SerializeSpotLight(const SpotLight &spot, detail::json &o) {
Johan Bowald52936a02019-07-17 09:06:45 +02007214 SerializeNumberProperty("innerConeAngle", spot.innerConeAngle, o);
7215 SerializeNumberProperty("outerConeAngle", spot.outerConeAngle, o);
David Siegel07616e82023-06-06 01:24:53 +02007216 SerializeExtrasAndExtensions(spot, o);
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007217}
7218
David03ad33c2023-02-15 23:35:51 -06007219static void SerializeGltfLight(const Light &light, detail::json &o) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09007220 if (!light.name.empty()) SerializeStringProperty("name", light.name, o);
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007221 SerializeNumberProperty("intensity", light.intensity, o);
Syoyo Fujita3dad3032020-05-13 21:22:23 +09007222 if (light.range > 0.0) {
7223 SerializeNumberProperty("range", light.range, o);
7224 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01007225 SerializeNumberArrayProperty("color", light.color, o);
7226 SerializeStringProperty("type", light.type, o);
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007227 if (light.type == "spot") {
David03ad33c2023-02-15 23:35:51 -06007228 detail::json spot;
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007229 SerializeSpotLight(light.spot, spot);
David1f9a4b92023-02-15 22:56:18 -06007230 detail::JsonAddMember(o, "spot", std::move(spot));
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007231 }
David Siegel07616e82023-06-06 01:24:53 +02007232 SerializeExtrasAndExtensions(light, o);
Emanuel Schrade186322b2017-11-06 11:14:41 +01007233}
7234
David03ad33c2023-02-15 23:35:51 -06007235static void SerializeGltfNode(const Node &node, detail::json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007236 if (node.translation.size() > 0) {
7237 SerializeNumberArrayProperty<double>("translation", node.translation, o);
7238 }
7239 if (node.rotation.size() > 0) {
7240 SerializeNumberArrayProperty<double>("rotation", node.rotation, o);
7241 }
7242 if (node.scale.size() > 0) {
7243 SerializeNumberArrayProperty<double>("scale", node.scale, o);
7244 }
7245 if (node.matrix.size() > 0) {
7246 SerializeNumberArrayProperty<double>("matrix", node.matrix, o);
7247 }
7248 if (node.mesh != -1) {
7249 SerializeNumberProperty<int>("mesh", node.mesh, o);
7250 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007251
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007252 if (node.skin != -1) {
7253 SerializeNumberProperty<int>("skin", node.skin, o);
7254 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007255
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007256 if (node.camera != -1) {
7257 SerializeNumberProperty<int>("camera", node.camera, o);
7258 }
7259
Benjamin Schmithüsen74c3c102019-08-16 14:24:26 +02007260 if (node.weights.size() > 0) {
7261 SerializeNumberArrayProperty<double>("weights", node.weights, o);
7262 }
7263
David Siegel07616e82023-06-06 01:24:53 +02007264 SerializeExtrasAndExtensions(node, o);
Jens Olssonb96f6962018-05-24 15:29:54 +02007265
David Siegelcfe64fb2023-06-07 15:18:38 +02007266 // Note(agnat): If the asset was loaded from disk, the node may already
7267 // contain the KHR_lights_punctual extension. If it was constructed in
7268 // memory it does not. In any case we update the JSON property using
7269 // the value from the struct. Last, if the node does not have a light
7270 // reference but the extension is still present, we remove it.
7271 if (node.light != -1) {
7272 detail::json_iterator it;
7273 if (!detail::FindMember(o, "extensions", it)) {
7274 detail::json extensions;
7275 detail::JsonSetObject(extensions);
7276 detail::JsonAddMember(o, "extensions", std::move(extensions));
7277 detail::FindMember(o, "extensions", it);
7278 }
7279 auto & extensions = detail::GetValue(it);
7280 if ( ! detail::FindMember(extensions, "KHR_lights_punctual", it)) {
7281 detail::json lights_punctual;
7282 detail::JsonSetObject(lights_punctual);
7283 detail::JsonAddMember(extensions, "KHR_lights_punctual", std::move(lights_punctual));
7284 detail::FindMember(o, "KHR_lights_punctual", it);
7285 }
David Siegel8d5d0b32023-06-07 15:35:35 +02007286 SerializeNumberProperty("light", node.light, detail::GetValue(it));
David Siegelcfe64fb2023-06-07 15:18:38 +02007287 } else {
7288 // node has no light ref (any longer)... so we clean up
7289 detail::json_iterator ext_it;
7290 if (detail::FindMember(o, "extensions", ext_it)) {
7291 auto & extensions = detail::GetValue(ext_it);
7292 detail::json_iterator lp_it;
7293 if (detail::FindMember(extensions, "KHR_lights_punctual", lp_it)) {
7294 detail::Erase(extensions, lp_it);
7295 }
7296 if (detail::IsEmpty(extensions)) {
7297 detail::Erase(o, ext_it);
7298 }
7299 }
7300 }
7301
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09007302 if (!node.name.empty()) SerializeStringProperty("name", node.name, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007303 SerializeNumberArrayProperty<int>("children", node.children, o);
7304}
7305
David03ad33c2023-02-15 23:35:51 -06007306static void SerializeGltfSampler(const Sampler &sampler, detail::json &o) {
s00665032137a7ca2023-01-13 12:52:08 +07007307 if (!sampler.name.empty()) {
7308 SerializeStringProperty("name", sampler.name, o);
7309 }
Syoyo Fujitaee179b22019-08-16 13:11:30 +09007310 if (sampler.magFilter != -1) {
7311 SerializeNumberProperty("magFilter", sampler.magFilter, o);
7312 }
7313 if (sampler.minFilter != -1) {
7314 SerializeNumberProperty("minFilter", sampler.minFilter, o);
7315 }
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09007316 // SerializeNumberProperty("wrapR", sampler.wrapR, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007317 SerializeNumberProperty("wrapS", sampler.wrapS, o);
7318 SerializeNumberProperty("wrapT", sampler.wrapT, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007319
David Siegel07616e82023-06-06 01:24:53 +02007320 SerializeExtrasAndExtensions(sampler, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007321}
7322
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007323static void SerializeGltfOrthographicCamera(const OrthographicCamera &camera,
David03ad33c2023-02-15 23:35:51 -06007324 detail::json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007325 SerializeNumberProperty("zfar", camera.zfar, o);
7326 SerializeNumberProperty("znear", camera.znear, o);
7327 SerializeNumberProperty("xmag", camera.xmag, o);
7328 SerializeNumberProperty("ymag", camera.ymag, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007329
David Siegel07616e82023-06-06 01:24:53 +02007330 SerializeExtrasAndExtensions(camera, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007331}
7332
7333static void SerializeGltfPerspectiveCamera(const PerspectiveCamera &camera,
David03ad33c2023-02-15 23:35:51 -06007334 detail::json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007335 SerializeNumberProperty("zfar", camera.zfar, o);
7336 SerializeNumberProperty("znear", camera.znear, o);
7337 if (camera.aspectRatio > 0) {
7338 SerializeNumberProperty("aspectRatio", camera.aspectRatio, o);
7339 }
7340
7341 if (camera.yfov > 0) {
7342 SerializeNumberProperty("yfov", camera.yfov, o);
7343 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007344
David Siegel07616e82023-06-06 01:24:53 +02007345 SerializeExtrasAndExtensions(camera, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007346}
7347
David03ad33c2023-02-15 23:35:51 -06007348static void SerializeGltfCamera(const Camera &camera, detail::json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007349 SerializeStringProperty("type", camera.type, o);
7350 if (!camera.name.empty()) {
Selmar Kokee3d0662018-10-08 16:20:43 +02007351 SerializeStringProperty("name", camera.name, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007352 }
7353
7354 if (camera.type.compare("orthographic") == 0) {
David03ad33c2023-02-15 23:35:51 -06007355 detail::json orthographic;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007356 SerializeGltfOrthographicCamera(camera.orthographic, orthographic);
David1f9a4b92023-02-15 22:56:18 -06007357 detail::JsonAddMember(o, "orthographic", std::move(orthographic));
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007358 } else if (camera.type.compare("perspective") == 0) {
David03ad33c2023-02-15 23:35:51 -06007359 detail::json perspective;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007360 SerializeGltfPerspectiveCamera(camera.perspective, perspective);
David1f9a4b92023-02-15 22:56:18 -06007361 detail::JsonAddMember(o, "perspective", std::move(perspective));
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007362 } else {
7363 // ???
7364 }
Syoyofe77cc52020-05-09 02:41:07 +09007365
David Siegel07616e82023-06-06 01:24:53 +02007366 SerializeExtrasAndExtensions(camera, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007367}
7368
David03ad33c2023-02-15 23:35:51 -06007369static void SerializeGltfScene(const Scene &scene, detail::json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007370 SerializeNumberArrayProperty<int>("nodes", scene.nodes, o);
7371
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007372 if (scene.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007373 SerializeStringProperty("name", scene.name, o);
7374 }
David Siegel07616e82023-06-06 01:24:53 +02007375 SerializeExtrasAndExtensions(scene, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007376}
7377
David03ad33c2023-02-15 23:35:51 -06007378static void SerializeGltfSkin(const Skin &skin, detail::json &o) {
Syoyo Fujitad07b9762021-04-28 19:15:16 +09007379 // required
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007380 SerializeNumberArrayProperty<int>("joints", skin.joints, o);
Syoyo Fujitad07b9762021-04-28 19:15:16 +09007381
7382 if (skin.inverseBindMatrices >= 0) {
7383 SerializeNumberProperty("inverseBindMatrices", skin.inverseBindMatrices, o);
7384 }
7385
7386 if (skin.skeleton >= 0) {
7387 SerializeNumberProperty("skeleton", skin.skeleton, o);
7388 }
7389
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007390 if (skin.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007391 SerializeStringProperty("name", skin.name, o);
7392 }
David Siegel07616e82023-06-06 01:24:53 +02007393
7394 SerializeExtrasAndExtensions(skin, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007395}
7396
David03ad33c2023-02-15 23:35:51 -06007397static void SerializeGltfTexture(const Texture &texture, detail::json &o) {
johan bowaldb97d34c2018-04-02 07:29:29 +02007398 if (texture.sampler > -1) {
johan bowald642a3432018-04-01 12:37:18 +02007399 SerializeNumberProperty("sampler", texture.sampler, o);
7400 }
Selmar Kok8eb39042018-10-05 14:29:35 +02007401 if (texture.source > -1) {
7402 SerializeNumberProperty("source", texture.source, o);
7403 }
Christophe820ede82019-07-04 15:21:21 +09007404 if (texture.name.size()) {
7405 SerializeStringProperty("name", texture.name, o);
7406 }
David Siegel07616e82023-06-06 01:24:53 +02007407 SerializeExtrasAndExtensions(texture, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007408}
7409
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007410///
7411/// Serialize all properties except buffers and images.
7412///
David03ad33c2023-02-15 23:35:51 -06007413static void SerializeGltfModel(const Model *model, detail::json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007414 // ACCESSORS
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007415 if (model->accessors.size()) {
David03ad33c2023-02-15 23:35:51 -06007416 detail::json accessors;
David1f9a4b92023-02-15 22:56:18 -06007417 detail::JsonReserveArray(accessors, model->accessors.size());
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007418 for (unsigned int i = 0; i < model->accessors.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007419 detail::json accessor;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007420 SerializeGltfAccessor(model->accessors[i], accessor);
David1f9a4b92023-02-15 22:56:18 -06007421 detail::JsonPushBack(accessors, std::move(accessor));
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007422 }
David1f9a4b92023-02-15 22:56:18 -06007423 detail::JsonAddMember(o, "accessors", std::move(accessors));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007424 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007425
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007426 // ANIMATIONS
7427 if (model->animations.size()) {
David03ad33c2023-02-15 23:35:51 -06007428 detail::json animations;
David1f9a4b92023-02-15 22:56:18 -06007429 detail::JsonReserveArray(animations, model->animations.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007430 for (unsigned int i = 0; i < model->animations.size(); ++i) {
7431 if (model->animations[i].channels.size()) {
David03ad33c2023-02-15 23:35:51 -06007432 detail::json animation;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007433 SerializeGltfAnimation(model->animations[i], animation);
David1f9a4b92023-02-15 22:56:18 -06007434 detail::JsonPushBack(animations, std::move(animation));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007435 }
7436 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007437
David1f9a4b92023-02-15 22:56:18 -06007438 detail::JsonAddMember(o, "animations", std::move(animations));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007439 }
7440
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007441 // ASSET
David03ad33c2023-02-15 23:35:51 -06007442 detail::json asset;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007443 SerializeGltfAsset(model->asset, asset);
David1f9a4b92023-02-15 22:56:18 -06007444 detail::JsonAddMember(o, "asset", std::move(asset));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007445
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007446 // BUFFERVIEWS
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007447 if (model->bufferViews.size()) {
David03ad33c2023-02-15 23:35:51 -06007448 detail::json bufferViews;
David1f9a4b92023-02-15 22:56:18 -06007449 detail::JsonReserveArray(bufferViews, model->bufferViews.size());
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007450 for (unsigned int i = 0; i < model->bufferViews.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007451 detail::json bufferView;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007452 SerializeGltfBufferView(model->bufferViews[i], bufferView);
David1f9a4b92023-02-15 22:56:18 -06007453 detail::JsonPushBack(bufferViews, std::move(bufferView));
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007454 }
David1f9a4b92023-02-15 22:56:18 -06007455 detail::JsonAddMember(o, "bufferViews", std::move(bufferViews));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007456 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007457
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007458 // Extensions required
7459 if (model->extensionsRequired.size()) {
7460 SerializeStringArrayProperty("extensionsRequired",
7461 model->extensionsRequired, o);
7462 }
7463
7464 // MATERIALS
7465 if (model->materials.size()) {
David03ad33c2023-02-15 23:35:51 -06007466 detail::json materials;
David1f9a4b92023-02-15 22:56:18 -06007467 detail::JsonReserveArray(materials, model->materials.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007468 for (unsigned int i = 0; i < model->materials.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007469 detail::json material;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007470 SerializeGltfMaterial(model->materials[i], material);
Syoyo Fujita68adc4b2020-10-22 22:27:12 +09007471
David1f9a4b92023-02-15 22:56:18 -06007472 if (detail::JsonIsNull(material)) {
Syoyo Fujita68adc4b2020-10-22 22:27:12 +09007473 // Issue 294.
7474 // `material` does not have any required parameters
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09007475 // so the result may be null(unmodified) when all material parameters
7476 // have default value.
Syoyo Fujita68adc4b2020-10-22 22:27:12 +09007477 //
7478 // null is not allowed thus we create an empty JSON object.
David1f9a4b92023-02-15 22:56:18 -06007479 detail::JsonSetObject(material);
Syoyo Fujita68adc4b2020-10-22 22:27:12 +09007480 }
David1f9a4b92023-02-15 22:56:18 -06007481 detail::JsonPushBack(materials, std::move(material));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007482 }
David1f9a4b92023-02-15 22:56:18 -06007483 detail::JsonAddMember(o, "materials", std::move(materials));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007484 }
7485
7486 // MESHES
7487 if (model->meshes.size()) {
David03ad33c2023-02-15 23:35:51 -06007488 detail::json meshes;
David1f9a4b92023-02-15 22:56:18 -06007489 detail::JsonReserveArray(meshes, model->meshes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007490 for (unsigned int i = 0; i < model->meshes.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007491 detail::json mesh;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007492 SerializeGltfMesh(model->meshes[i], mesh);
David1f9a4b92023-02-15 22:56:18 -06007493 detail::JsonPushBack(meshes, std::move(mesh));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007494 }
David1f9a4b92023-02-15 22:56:18 -06007495 detail::JsonAddMember(o, "meshes", std::move(meshes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007496 }
7497
7498 // NODES
7499 if (model->nodes.size()) {
David03ad33c2023-02-15 23:35:51 -06007500 detail::json nodes;
David1f9a4b92023-02-15 22:56:18 -06007501 detail::JsonReserveArray(nodes, model->nodes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007502 for (unsigned int i = 0; i < model->nodes.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007503 detail::json node;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007504 SerializeGltfNode(model->nodes[i], node);
David1f9a4b92023-02-15 22:56:18 -06007505 detail::JsonPushBack(nodes, std::move(node));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007506 }
David1f9a4b92023-02-15 22:56:18 -06007507 detail::JsonAddMember(o, "nodes", std::move(nodes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007508 }
7509
7510 // SCENE
7511 if (model->defaultScene > -1) {
7512 SerializeNumberProperty<int>("scene", model->defaultScene, o);
7513 }
7514
7515 // SCENES
7516 if (model->scenes.size()) {
David03ad33c2023-02-15 23:35:51 -06007517 detail::json scenes;
David1f9a4b92023-02-15 22:56:18 -06007518 detail::JsonReserveArray(scenes, model->scenes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007519 for (unsigned int i = 0; i < model->scenes.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007520 detail::json currentScene;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007521 SerializeGltfScene(model->scenes[i], currentScene);
David1f9a4b92023-02-15 22:56:18 -06007522 detail::JsonPushBack(scenes, std::move(currentScene));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007523 }
David1f9a4b92023-02-15 22:56:18 -06007524 detail::JsonAddMember(o, "scenes", std::move(scenes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007525 }
7526
7527 // SKINS
7528 if (model->skins.size()) {
David03ad33c2023-02-15 23:35:51 -06007529 detail::json skins;
David1f9a4b92023-02-15 22:56:18 -06007530 detail::JsonReserveArray(skins, model->skins.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007531 for (unsigned int i = 0; i < model->skins.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007532 detail::json skin;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007533 SerializeGltfSkin(model->skins[i], skin);
David1f9a4b92023-02-15 22:56:18 -06007534 detail::JsonPushBack(skins, std::move(skin));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007535 }
David1f9a4b92023-02-15 22:56:18 -06007536 detail::JsonAddMember(o, "skins", std::move(skins));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007537 }
7538
7539 // TEXTURES
7540 if (model->textures.size()) {
David03ad33c2023-02-15 23:35:51 -06007541 detail::json textures;
David1f9a4b92023-02-15 22:56:18 -06007542 detail::JsonReserveArray(textures, model->textures.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007543 for (unsigned int i = 0; i < model->textures.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007544 detail::json texture;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007545 SerializeGltfTexture(model->textures[i], texture);
David1f9a4b92023-02-15 22:56:18 -06007546 detail::JsonPushBack(textures, std::move(texture));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007547 }
David1f9a4b92023-02-15 22:56:18 -06007548 detail::JsonAddMember(o, "textures", std::move(textures));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007549 }
7550
7551 // SAMPLERS
7552 if (model->samplers.size()) {
David03ad33c2023-02-15 23:35:51 -06007553 detail::json samplers;
David1f9a4b92023-02-15 22:56:18 -06007554 detail::JsonReserveArray(samplers, model->samplers.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007555 for (unsigned int i = 0; i < model->samplers.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007556 detail::json sampler;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007557 SerializeGltfSampler(model->samplers[i], sampler);
David1f9a4b92023-02-15 22:56:18 -06007558 detail::JsonPushBack(samplers, std::move(sampler));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007559 }
David1f9a4b92023-02-15 22:56:18 -06007560 detail::JsonAddMember(o, "samplers", std::move(samplers));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007561 }
7562
7563 // CAMERAS
7564 if (model->cameras.size()) {
David03ad33c2023-02-15 23:35:51 -06007565 detail::json cameras;
David1f9a4b92023-02-15 22:56:18 -06007566 detail::JsonReserveArray(cameras, model->cameras.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007567 for (unsigned int i = 0; i < model->cameras.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007568 detail::json camera;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007569 SerializeGltfCamera(model->cameras[i], camera);
David1f9a4b92023-02-15 22:56:18 -06007570 detail::JsonPushBack(cameras, std::move(camera));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007571 }
David1f9a4b92023-02-15 22:56:18 -06007572 detail::JsonAddMember(o, "cameras", std::move(cameras));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007573 }
7574
David Siegel07616e82023-06-06 01:24:53 +02007575 // EXTRAS & EXTENSIONS
7576 SerializeExtrasAndExtensions(*model, o);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007577
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007578 auto extensionsUsed = model->extensionsUsed;
7579
7580 // LIGHTS as KHR_lights_punctual
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007581 if (model->lights.size()) {
David03ad33c2023-02-15 23:35:51 -06007582 detail::json lights;
David1f9a4b92023-02-15 22:56:18 -06007583 detail::JsonReserveArray(lights, model->lights.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007584 for (unsigned int i = 0; i < model->lights.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007585 detail::json light;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007586 SerializeGltfLight(model->lights[i], light);
David1f9a4b92023-02-15 22:56:18 -06007587 detail::JsonPushBack(lights, std::move(light));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007588 }
David03ad33c2023-02-15 23:35:51 -06007589 detail::json khr_lights_cmn;
David1f9a4b92023-02-15 22:56:18 -06007590 detail::JsonAddMember(khr_lights_cmn, "lights", std::move(lights));
David03ad33c2023-02-15 23:35:51 -06007591 detail::json ext_j;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007592
jrkooncecba5d6c2019-08-29 11:26:22 -05007593 {
David03ad33c2023-02-15 23:35:51 -06007594 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06007595 if (detail::FindMember(o, "extensions", it)) {
7596 detail::JsonAssign(ext_j, detail::GetValue(it));
jrkooncecba5d6c2019-08-29 11:26:22 -05007597 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007598 }
7599
David1f9a4b92023-02-15 22:56:18 -06007600 detail::JsonAddMember(ext_j, "KHR_lights_punctual", std::move(khr_lights_cmn));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007601
David1f9a4b92023-02-15 22:56:18 -06007602 detail::JsonAddMember(o, "extensions", std::move(ext_j));
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007603
7604 // Also add "KHR_lights_punctual" to `extensionsUsed`
7605 {
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04007606 auto has_khr_lights_punctual =
7607 std::find_if(extensionsUsed.begin(), extensionsUsed.end(),
7608 [](const std::string &s) {
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08007609 return (s.compare("KHR_lights_punctual") == 0);
7610 });
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007611
7612 if (has_khr_lights_punctual == extensionsUsed.end()) {
7613 extensionsUsed.push_back("KHR_lights_punctual");
7614 }
7615 }
7616 }
7617
7618 // Extensions used
Selmar1208f052021-02-01 19:24:41 +01007619 if (extensionsUsed.size()) {
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007620 SerializeStringArrayProperty("extensionsUsed", extensionsUsed, o);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007621 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007622}
7623
Johan Bowald52936a02019-07-17 09:06:45 +02007624static bool WriteGltfStream(std::ostream &stream, const std::string &content) {
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007625 stream << content << std::endl;
Marco Langer76586242023-03-12 19:26:05 +01007626 return stream.good();
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007627}
7628
7629static bool WriteGltfFile(const std::string &output,
7630 const std::string &content) {
Harokyangfb256602019-10-30 16:13:52 +08007631#ifdef _WIN32
Syoyo Fujita45cac782019-11-09 20:42:55 +09007632#if defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08007633 std::ofstream gltfFile(UTF8ToWchar(output).c_str());
Syoyo Fujita45cac782019-11-09 20:42:55 +09007634#elif defined(__GLIBCXX__)
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007635 int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
7636 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
7637 __gnu_cxx::stdio_filebuf<char> wfile_buf(
7638 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09007639 std::ostream gltfFile(&wfile_buf);
7640 if (!wfile_buf.is_open()) return false;
Harokyangfb256602019-10-30 16:13:52 +08007641#else
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007642 std::ofstream gltfFile(output.c_str());
7643 if (!gltfFile.is_open()) return false;
Syoyo Fujita45cac782019-11-09 20:42:55 +09007644#endif
7645#else
7646 std::ofstream gltfFile(output.c_str());
7647 if (!gltfFile.is_open()) return false;
7648#endif
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007649 return WriteGltfStream(gltfFile, content);
7650}
7651
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007652static bool WriteBinaryGltfStream(std::ostream &stream,
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007653 const std::string &content,
7654 const std::vector<unsigned char> &binBuffer) {
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007655 const std::string header = "glTF";
7656 const int version = 2;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007657
Alexander Wood190382a2021-10-08 12:19:13 -04007658 const uint32_t content_size = uint32_t(content.size());
7659 const uint32_t binBuffer_size = uint32_t(binBuffer.size());
7660 // determine number of padding bytes required to ensure 4 byte alignment
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09007661 const uint32_t content_padding_size =
7662 content_size % 4 == 0 ? 0 : 4 - content_size % 4;
7663 const uint32_t bin_padding_size =
7664 binBuffer_size % 4 == 0 ? 0 : 4 - binBuffer_size % 4;
Syoyo Fujita1d205202019-11-16 17:00:17 +09007665
7666 // 12 bytes for header, JSON content length, 8 bytes for JSON chunk info.
Alexander Wood190382a2021-10-08 12:19:13 -04007667 // Chunk data must be located at 4-byte boundary, which may require padding
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007668 const uint32_t length =
Alexander Wood9e3d1f62021-10-08 16:57:56 -04007669 12 + 8 + content_size + content_padding_size +
Alexander Wood13803652021-10-08 20:13:07 -04007670 (binBuffer_size ? (8 + binBuffer_size + bin_padding_size) : 0);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007671
Syoyo Fujitacea69e32019-08-20 17:10:30 +09007672 stream.write(header.c_str(), std::streamsize(header.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007673 stream.write(reinterpret_cast<const char *>(&version), sizeof(version));
7674 stream.write(reinterpret_cast<const char *>(&length), sizeof(length));
7675
7676 // JSON chunk info, then JSON data
Alexander Wood9e3d1f62021-10-08 16:57:56 -04007677 const uint32_t model_length = uint32_t(content.size()) + content_padding_size;
Syoyo Fujita72f4a552020-01-08 00:40:41 +09007678 const uint32_t model_format = 0x4E4F534A;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007679 stream.write(reinterpret_cast<const char *>(&model_length),
Johan Bowald52936a02019-07-17 09:06:45 +02007680 sizeof(model_length));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007681 stream.write(reinterpret_cast<const char *>(&model_format),
Johan Bowald52936a02019-07-17 09:06:45 +02007682 sizeof(model_format));
Syoyo Fujitacea69e32019-08-20 17:10:30 +09007683 stream.write(content.c_str(), std::streamsize(content.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007684
7685 // Chunk must be multiplies of 4, so pad with spaces
Alexander Wood9e3d1f62021-10-08 16:57:56 -04007686 if (content_padding_size > 0) {
7687 const std::string padding = std::string(size_t(content_padding_size), ' ');
Syoyo Fujitacea69e32019-08-20 17:10:30 +09007688 stream.write(padding.c_str(), std::streamsize(padding.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007689 }
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007690 if (binBuffer.size() > 0) {
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007691 // BIN chunk info, then BIN data
Syoyo Fujita72f4a552020-01-08 00:40:41 +09007692 const uint32_t bin_length = uint32_t(binBuffer.size()) + bin_padding_size;
7693 const uint32_t bin_format = 0x004e4942;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007694 stream.write(reinterpret_cast<const char *>(&bin_length),
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007695 sizeof(bin_length));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007696 stream.write(reinterpret_cast<const char *>(&bin_format),
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007697 sizeof(bin_format));
7698 stream.write(reinterpret_cast<const char *>(binBuffer.data()),
7699 std::streamsize(binBuffer.size()));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007700 // Chunksize must be multiplies of 4, so pad with zeroes
7701 if (bin_padding_size > 0) {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007702 const std::vector<unsigned char> padding =
7703 std::vector<unsigned char>(size_t(bin_padding_size), 0);
7704 stream.write(reinterpret_cast<const char *>(padding.data()),
7705 std::streamsize(padding.size()));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007706 }
7707 }
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007708
Marco Langer76586242023-03-12 19:26:05 +01007709 stream.flush();
7710 return stream.good();
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007711}
7712
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007713static bool WriteBinaryGltfFile(const std::string &output,
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007714 const std::string &content,
7715 const std::vector<unsigned char> &binBuffer) {
Harokyangfb256602019-10-30 16:13:52 +08007716#ifdef _WIN32
Syoyo Fujita45cac782019-11-09 20:42:55 +09007717#if defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08007718 std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09007719#elif defined(__GLIBCXX__)
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007720 int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
7721 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
7722 __gnu_cxx::stdio_filebuf<char> wfile_buf(
7723 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita4ab03862019-11-10 15:31:17 +09007724 std::ostream gltfFile(&wfile_buf);
Harokyangfb256602019-10-30 16:13:52 +08007725#else
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007726 std::ofstream gltfFile(output.c_str(), std::ios::binary);
Harokyangfb256602019-10-30 16:13:52 +08007727#endif
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007728#else
7729 std::ofstream gltfFile(output.c_str(), std::ios::binary);
jrkooncecba5d6c2019-08-29 11:26:22 -05007730#endif
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007731 return WriteBinaryGltfStream(gltfFile, content, binBuffer);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007732}
7733
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007734bool TinyGLTF::WriteGltfSceneToStream(const Model *model, std::ostream &stream,
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007735 bool prettyPrint = true,
7736 bool writeBinary = false) {
David03ad33c2023-02-15 23:35:51 -06007737 detail::JsonDocument output;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007738
7739 /// Serialize all properties except buffers and images.
7740 SerializeGltfModel(model, output);
7741
7742 // BUFFERS
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007743 std::vector<unsigned char> binBuffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007744 if (model->buffers.size()) {
David03ad33c2023-02-15 23:35:51 -06007745 detail::json buffers;
David1f9a4b92023-02-15 22:56:18 -06007746 detail::JsonReserveArray(buffers, model->buffers.size());
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007747 for (unsigned int i = 0; i < model->buffers.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007748 detail::json buffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007749 if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
7750 SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007751 } else {
7752 SerializeGltfBuffer(model->buffers[i], buffer);
7753 }
David1f9a4b92023-02-15 22:56:18 -06007754 detail::JsonPushBack(buffers, std::move(buffer));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007755 }
David1f9a4b92023-02-15 22:56:18 -06007756 detail::JsonAddMember(output, "buffers", std::move(buffers));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007757 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007758
7759 // IMAGES
7760 if (model->images.size()) {
David03ad33c2023-02-15 23:35:51 -06007761 detail::json images;
David1f9a4b92023-02-15 22:56:18 -06007762 detail::JsonReserveArray(images, model->images.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007763 for (unsigned int i = 0; i < model->images.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007764 detail::json image;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007765
7766 std::string dummystring = "";
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007767 // UpdateImageObject need baseDir but only uses it if embeddedImages is
7768 // enabled, since we won't write separate images when writing to a stream
7769 // we
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007770 std::string uri;
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08007771 if (!UpdateImageObject(model->images[i], dummystring, int(i), true,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08007772 &uri_cb, &this->WriteImageData,
7773 this->write_image_user_data_, &uri)) {
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08007774 return false;
7775 }
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007776 SerializeGltfImage(model->images[i], uri, image);
David1f9a4b92023-02-15 22:56:18 -06007777 detail::JsonPushBack(images, std::move(image));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007778 }
David1f9a4b92023-02-15 22:56:18 -06007779 detail::JsonAddMember(output, "images", std::move(images));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007780 }
7781
7782 if (writeBinary) {
David1f9a4b92023-02-15 22:56:18 -06007783 return WriteBinaryGltfStream(stream, detail::JsonToString(output), binBuffer);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007784 } else {
David1f9a4b92023-02-15 22:56:18 -06007785 return WriteGltfStream(stream, detail::JsonToString(output, prettyPrint ? 2 : -1));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007786 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007787}
7788
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007789bool TinyGLTF::WriteGltfSceneToFile(const Model *model,
7790 const std::string &filename,
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007791 bool embedImages = false,
7792 bool embedBuffers = false,
7793 bool prettyPrint = true,
7794 bool writeBinary = false) {
David03ad33c2023-02-15 23:35:51 -06007795 detail::JsonDocument output;
Selmar Kok7cb31e42018-10-05 16:02:29 +02007796 std::string defaultBinFilename = GetBaseFilename(filename);
7797 std::string defaultBinFileExt = ".bin";
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00007798 std::string::size_type pos =
7799 defaultBinFilename.rfind('.', defaultBinFilename.length());
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007800
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007801 if (pos != std::string::npos) {
Selmar Kok7cb31e42018-10-05 16:02:29 +02007802 defaultBinFilename = defaultBinFilename.substr(0, pos);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007803 }
johan bowald642a3432018-04-01 12:37:18 +02007804 std::string baseDir = GetBaseDir(filename);
7805 if (baseDir.empty()) {
7806 baseDir = "./";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05007807 }
Johan Bowald52936a02019-07-17 09:06:45 +02007808 /// Serialize all properties except buffers and images.
7809 SerializeGltfModel(model, output);
Syoyo Fujitad42767e2018-03-15 21:52:00 -05007810
Selmar Kok7cb31e42018-10-05 16:02:29 +02007811 // BUFFERS
Pyarelal Knowles385946d2023-01-03 18:18:04 -08007812 std::vector<std::string> usedFilenames;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007813 std::vector<unsigned char> binBuffer;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007814 if (model->buffers.size()) {
David03ad33c2023-02-15 23:35:51 -06007815 detail::json buffers;
David1f9a4b92023-02-15 22:56:18 -06007816 detail::JsonReserveArray(buffers, model->buffers.size());
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007817 for (unsigned int i = 0; i < model->buffers.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007818 detail::json buffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007819 if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
7820 SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007821 } else if (embedBuffers) {
7822 SerializeGltfBuffer(model->buffers[i], buffer);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00007823 } else {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007824 std::string binSavePath;
Pyarelal Knowles385946d2023-01-03 18:18:04 -08007825 std::string binFilename;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007826 std::string binUri;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007827 if (!model->buffers[i].uri.empty() &&
7828 !IsDataURI(model->buffers[i].uri)) {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007829 binUri = model->buffers[i].uri;
Pyarelal Knowles385946d2023-01-03 18:18:04 -08007830 if (!uri_cb.decode(binUri, &binFilename, uri_cb.user_data)) {
7831 return false;
7832 }
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007833 } else {
Pyarelal Knowles385946d2023-01-03 18:18:04 -08007834 binFilename = defaultBinFilename + defaultBinFileExt;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007835 bool inUse = true;
7836 int numUsed = 0;
7837 while (inUse) {
7838 inUse = false;
Pyarelal Knowles385946d2023-01-03 18:18:04 -08007839 for (const std::string &usedName : usedFilenames) {
7840 if (binFilename.compare(usedName) != 0) continue;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007841 inUse = true;
Pyarelal Knowles385946d2023-01-03 18:18:04 -08007842 binFilename = defaultBinFilename + std::to_string(numUsed++) +
7843 defaultBinFileExt;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007844 break;
7845 }
Selmar Kokc884e582018-10-05 16:25:54 +02007846 }
Pyarelal Knowles385946d2023-01-03 18:18:04 -08007847
7848 if (uri_cb.encode) {
7849 if (!uri_cb.encode(binFilename, "buffer", &binUri,
7850 uri_cb.user_data)) {
7851 return false;
7852 }
7853 } else {
7854 binUri = binFilename;
7855 }
Selmar Kokc884e582018-10-05 16:25:54 +02007856 }
Pyarelal Knowles385946d2023-01-03 18:18:04 -08007857 usedFilenames.push_back(binFilename);
7858 binSavePath = JoinPath(baseDir, binFilename);
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007859 if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
7860 binUri)) {
7861 return false;
7862 }
Selmar Kokc884e582018-10-05 16:25:54 +02007863 }
David1f9a4b92023-02-15 22:56:18 -06007864 detail::JsonPushBack(buffers, std::move(buffer));
johan bowald30c53472018-03-30 11:49:36 +02007865 }
David1f9a4b92023-02-15 22:56:18 -06007866 detail::JsonAddMember(output, "buffers", std::move(buffers));
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007867 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007868
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007869 // IMAGES
Johan Bowaldfaa27222018-03-28 14:44:45 +02007870 if (model->images.size()) {
David03ad33c2023-02-15 23:35:51 -06007871 detail::json images;
David1f9a4b92023-02-15 22:56:18 -06007872 detail::JsonReserveArray(images, model->images.size());
Johan Bowaldfaa27222018-03-28 14:44:45 +02007873 for (unsigned int i = 0; i < model->images.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007874 detail::json image;
johan bowald642a3432018-04-01 12:37:18 +02007875
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007876 std::string uri;
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08007877 if (!UpdateImageObject(model->images[i], baseDir, int(i), embedImages,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08007878 &uri_cb, &this->WriteImageData,
7879 this->write_image_user_data_, &uri)) {
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08007880 return false;
7881 }
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007882 SerializeGltfImage(model->images[i], uri, image);
David1f9a4b92023-02-15 22:56:18 -06007883 detail::JsonPushBack(images, std::move(image));
Johan Bowaldfaa27222018-03-28 14:44:45 +02007884 }
David1f9a4b92023-02-15 22:56:18 -06007885 detail::JsonAddMember(output, "images", std::move(images));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007886 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007887
David Harmonda9eac22018-08-30 08:06:05 -04007888 if (writeBinary) {
David1f9a4b92023-02-15 22:56:18 -06007889 return WriteBinaryGltfFile(filename, detail::JsonToString(output), binBuffer);
David Harmonda9eac22018-08-30 08:06:05 -04007890 } else {
David1f9a4b92023-02-15 22:56:18 -06007891 return WriteGltfFile(filename, detail::JsonToString(output, (prettyPrint ? 2 : -1)));
David Harmonda9eac22018-08-30 08:06:05 -04007892 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007893}
7894
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09007895} // namespace tinygltf
7896
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09007897#ifdef __clang__
7898#pragma clang diagnostic pop
7899#endif
7900
Syoyo Fujita612e5782022-09-18 21:01:39 +09007901#endif // TINYGLTF_IMPLEMENTATION