blob: 6816ac58e93d3a7a288c3d6ccc420fcfad72ab8f [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
Bryn Lloyd3e98ac42023-06-21 22:15:49 +020051// Auto-detect C++14 standard version
52#if !defined(TINYGLTF_USE_CPP14) && defined(__cplusplus) && \
53 (__cplusplus >= 201402L)
zbendefy69eeea12022-09-05 23:54:57 +020054#define TINYGLTF_USE_CPP14
55#endif
56
Sascha Willems5f9cb242018-12-28 20:53:41 +010057#ifdef __ANDROID__
58#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
59#include <android/asset_manager.h>
60#endif
61#endif
62
Selmar Kok79e3df22019-10-29 16:22:07 +010063#ifdef __GNUC__
64#if (__GNUC__ < 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ <= 8))
Selmar Kokb74fade2019-10-29 16:09:32 +010065#define TINYGLTF_NOEXCEPT
Selmar Kok79e3df22019-10-29 16:22:07 +010066#else
67#define TINYGLTF_NOEXCEPT noexcept
Selmar Kokb74fade2019-10-29 16:09:32 +010068#endif
Selmar Kok79e3df22019-10-29 16:22:07 +010069#else
70#define TINYGLTF_NOEXCEPT noexcept
71#endif
72
Syoyo Fujita6e08b172019-10-30 17:25:38 +090073#define DEFAULT_METHODS(x) \
74 ~x() = default; \
75 x(const x &) = default; \
76 x(x &&) TINYGLTF_NOEXCEPT = default; \
77 x &operator=(const x &) = default; \
78 x &operator=(x &&) TINYGLTF_NOEXCEPT = default;
Selmar Kokb74fade2019-10-29 16:09:32 +010079
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090080namespace tinygltf {
81
82#define TINYGLTF_MODE_POINTS (0)
83#define TINYGLTF_MODE_LINE (1)
84#define TINYGLTF_MODE_LINE_LOOP (2)
Thomas Tissot6c4a0062019-01-30 13:10:51 +010085#define TINYGLTF_MODE_LINE_STRIP (3)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090086#define TINYGLTF_MODE_TRIANGLES (4)
87#define TINYGLTF_MODE_TRIANGLE_STRIP (5)
88#define TINYGLTF_MODE_TRIANGLE_FAN (6)
89
90#define TINYGLTF_COMPONENT_TYPE_BYTE (5120)
91#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE (5121)
92#define TINYGLTF_COMPONENT_TYPE_SHORT (5122)
93#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT (5123)
94#define TINYGLTF_COMPONENT_TYPE_INT (5124)
95#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125)
96#define TINYGLTF_COMPONENT_TYPE_FLOAT (5126)
Syoyo Fujita52ff00a2022-08-16 20:08:45 +090097#define TINYGLTF_COMPONENT_TYPE_DOUBLE \
98 (5130) // OpenGL double type. Note that some of glTF 2.0 validator does not
99 // support double type even the schema seems allow any value of
100 // integer:
101 // https://github.com/KhronosGroup/glTF/blob/b9884a2fd45130b4d673dd6c8a706ee21ee5c5f7/specification/2.0/schema/accessor.schema.json#L22
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900102
Syoyo Fujitac2615632016-06-19 21:56:06 +0900103#define TINYGLTF_TEXTURE_FILTER_NEAREST (9728)
104#define TINYGLTF_TEXTURE_FILTER_LINEAR (9729)
105#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST (9984)
106#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST (9985)
107#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR (9986)
108#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR (9987)
109
Cemalettin Dervis246d8662017-12-07 20:29:51 +0100110#define TINYGLTF_TEXTURE_WRAP_REPEAT (10497)
Syoyo Fujitac2615632016-06-19 21:56:06 +0900111#define TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE (33071)
Luke San Antonio Bialecki904612c2016-08-19 17:28:03 -0400112#define TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT (33648)
Syoyo Fujitac2615632016-06-19 21:56:06 +0900113
Luke San Antoniocdf4cb72016-06-14 21:32:11 -0400114// Redeclarations of the above for technique.parameters.
115#define TINYGLTF_PARAMETER_TYPE_BYTE (5120)
116#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE (5121)
117#define TINYGLTF_PARAMETER_TYPE_SHORT (5122)
118#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT (5123)
119#define TINYGLTF_PARAMETER_TYPE_INT (5124)
120#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT (5125)
121#define TINYGLTF_PARAMETER_TYPE_FLOAT (5126)
122
123#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC2 (35664)
124#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC3 (35665)
125#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC4 (35666)
126
127#define TINYGLTF_PARAMETER_TYPE_INT_VEC2 (35667)
128#define TINYGLTF_PARAMETER_TYPE_INT_VEC3 (35668)
129#define TINYGLTF_PARAMETER_TYPE_INT_VEC4 (35669)
130
131#define TINYGLTF_PARAMETER_TYPE_BOOL (35670)
132#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC2 (35671)
133#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC3 (35672)
134#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC4 (35673)
135
136#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT2 (35674)
137#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT3 (35675)
138#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT4 (35676)
139
140#define TINYGLTF_PARAMETER_TYPE_SAMPLER_2D (35678)
141
142// End parameter types
143
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900144#define TINYGLTF_TYPE_VEC2 (2)
145#define TINYGLTF_TYPE_VEC3 (3)
146#define TINYGLTF_TYPE_VEC4 (4)
147#define TINYGLTF_TYPE_MAT2 (32 + 2)
148#define TINYGLTF_TYPE_MAT3 (32 + 3)
149#define TINYGLTF_TYPE_MAT4 (32 + 4)
150#define TINYGLTF_TYPE_SCALAR (64 + 1)
151#define TINYGLTF_TYPE_VECTOR (64 + 4)
152#define TINYGLTF_TYPE_MATRIX (64 + 16)
153
154#define TINYGLTF_IMAGE_FORMAT_JPEG (0)
155#define TINYGLTF_IMAGE_FORMAT_PNG (1)
156#define TINYGLTF_IMAGE_FORMAT_BMP (2)
157#define TINYGLTF_IMAGE_FORMAT_GIF (3)
158
Luke San Antonio6d616f52016-06-23 14:09:23 -0400159#define TINYGLTF_TEXTURE_FORMAT_ALPHA (6406)
160#define TINYGLTF_TEXTURE_FORMAT_RGB (6407)
Syoyo Fujitabde70212016-02-07 17:38:17 +0900161#define TINYGLTF_TEXTURE_FORMAT_RGBA (6408)
Luke San Antonio6d616f52016-06-23 14:09:23 -0400162#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE (6409)
163#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE_ALPHA (6410)
164
Syoyo Fujitabde70212016-02-07 17:38:17 +0900165#define TINYGLTF_TEXTURE_TARGET_TEXTURE2D (3553)
166#define TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE (5121)
167
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900168#define TINYGLTF_TARGET_ARRAY_BUFFER (34962)
169#define TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER (34963)
170
Luke San Antoniocdf4cb72016-06-14 21:32:11 -0400171#define TINYGLTF_SHADER_TYPE_VERTEX_SHADER (35633)
172#define TINYGLTF_SHADER_TYPE_FRAGMENT_SHADER (35632)
173
Selmar Kok31cb7f92018-10-03 15:39:05 +0200174#define TINYGLTF_DOUBLE_EPS (1.e-12)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900175#define TINYGLTF_DOUBLE_EQUAL(a, b) (std::fabs((b) - (a)) < TINYGLTF_DOUBLE_EPS)
Selmar Kok31cb7f92018-10-03 15:39:05 +0200176
Sascha Willems5f9cb242018-12-28 20:53:41 +0100177#ifdef __ANDROID__
178#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
Serdar Kocdemir264ae4c2022-10-30 22:32:59 +0000179#ifdef TINYGLTF_IMPLEMENTATION
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000180AAssetManager *asset_manager = nullptr;
Serdar Kocdemir264ae4c2022-10-30 22:32:59 +0000181#else
182extern AAssetManager *asset_manager;
183#endif
Sascha Willems5f9cb242018-12-28 20:53:41 +0100184#endif
185#endif
186
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900187typedef enum {
Idriss Chaouch425195b2021-05-20 12:11:00 +0100188 NULL_TYPE,
189 REAL_TYPE,
190 INT_TYPE,
191 BOOL_TYPE,
192 STRING_TYPE,
193 ARRAY_TYPE,
194 BINARY_TYPE,
195 OBJECT_TYPE
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900196} Type;
197
Nyall Dawson8c85d5e2023-08-28 12:56:09 +1000198typedef enum {
Nyall Dawsonbbc1eae2023-09-03 09:05:32 +1000199 Permissive,
200 Strict
Nyall Dawson8c85d5e2023-08-28 12:56:09 +1000201} ParseStrictness;
202
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500203static inline int32_t GetComponentSizeInBytes(uint32_t componentType) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900204 if (componentType == TINYGLTF_COMPONENT_TYPE_BYTE) {
205 return 1;
206 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
207 return 1;
208 } else if (componentType == TINYGLTF_COMPONENT_TYPE_SHORT) {
209 return 2;
210 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
211 return 2;
212 } else if (componentType == TINYGLTF_COMPONENT_TYPE_INT) {
213 return 4;
214 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) {
215 return 4;
216 } else if (componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) {
217 return 4;
218 } else if (componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) {
219 return 8;
220 } else {
imallettd9ce9eb2022-10-07 10:37:09 -0700221 // Unknown component type
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900222 return -1;
223 }
224}
225
DingboDingboDingbo83ccb9f2019-08-29 18:49:15 -0400226static inline int32_t GetNumComponentsInType(uint32_t ty) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900227 if (ty == TINYGLTF_TYPE_SCALAR) {
228 return 1;
229 } else if (ty == TINYGLTF_TYPE_VEC2) {
230 return 2;
231 } else if (ty == TINYGLTF_TYPE_VEC3) {
232 return 3;
233 } else if (ty == TINYGLTF_TYPE_VEC4) {
234 return 4;
235 } else if (ty == TINYGLTF_TYPE_MAT2) {
236 return 4;
237 } else if (ty == TINYGLTF_TYPE_MAT3) {
238 return 9;
239 } else if (ty == TINYGLTF_TYPE_MAT4) {
240 return 16;
241 } else {
imallettd9ce9eb2022-10-07 10:37:09 -0700242 // Unknown component type
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900243 return -1;
244 }
245}
246
Syoyo Fujita150f2432019-07-25 19:22:44 +0900247// TODO(syoyo): Move these functions to TinyGLTF class
Selmar Kok0d0e97e2018-08-22 14:01:57 +0200248bool IsDataURI(const std::string &in);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900249bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
250 const std::string &in, size_t reqBytes, bool checkSize);
Selmar Kok0d0e97e2018-08-22 14:01:57 +0200251
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900252#ifdef __clang__
253#pragma clang diagnostic push
254// Suppress warning for : static Value null_value
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900255#pragma clang diagnostic ignored "-Wexit-time-destructors"
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900256#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900257#endif
258
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900259// Simple class to represent JSON object
260class Value {
261 public:
262 typedef std::vector<Value> Array;
263 typedef std::map<std::string, Value> Object;
264
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200265 Value() = default;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900266
267 explicit Value(bool b) : type_(BOOL_TYPE) { boolean_value_ = b; }
Syoyo Fujitaff515702019-08-24 16:29:14 +0900268 explicit Value(int i) : type_(INT_TYPE) {
269 int_value_ = i;
270 real_value_ = i;
271 }
Syoyo Fujita150f2432019-07-25 19:22:44 +0900272 explicit Value(double n) : type_(REAL_TYPE) { real_value_ = n; }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900273 explicit Value(const std::string &s) : type_(STRING_TYPE) {
274 string_value_ = s;
275 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900276 explicit Value(std::string &&s)
277 : type_(STRING_TYPE), string_value_(std::move(s)) {}
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200278 explicit Value(const char *s) : type_(STRING_TYPE) { string_value_ = s; }
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 {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200524 int sampler{-1}; // required
525 int target_node{-1}; // optional index of the node to target (alternative
Jack Mousseau283b5522023-01-15 11:45:45 -0800526 // 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
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200540 AnimationChannel() = default;
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 {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200546 int input{-1}; // required
547 int output{-1}; // 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
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200557 AnimationSampler() : 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;
Bryn Lloydcb6a7072023-06-21 22:30:14 +0200580 int inverseBindMatrices{-1}; // required here but not in the spec
581 int skeleton{-1}; // 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
Bryn Lloydcb6a7072023-06-21 22:30:14 +0200591 Skin() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100592 DEFAULT_METHODS(Skin)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900593 bool operator==(const Skin &) const;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000594};
595
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000596struct Sampler {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900597 std::string name;
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900598 // glTF 2.0 spec does not define default value for `minFilter` and
599 // `magFilter`. Set -1 in TinyGLTF(issue #186)
600 int minFilter =
601 -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR",
zz8a35b8f2021-05-14 13:49:33 +0800602 // "NEAREST_MIPMAP_NEAREST", "LINEAR_MIPMAP_NEAREST",
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900603 // "NEAREST_MIPMAP_LINEAR", "LINEAR_MIPMAP_LINEAR"]
604 int magFilter =
605 -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR"]
606 int wrapS =
607 TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
608 // "REPEAT"], default "REPEAT"
609 int wrapT =
610 TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
611 // "REPEAT"], default "REPEAT"
Syoyo Fujita52ff00a2022-08-16 20:08:45 +0900612 // int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; // TinyGLTF extension. currently
613 // not used.
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900614
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900615 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900616 ExtensionMap extensions;
617
618 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
619 std::string extras_json_string;
620 std::string extensions_json_string;
Syoyo Fujitac2615632016-06-19 21:56:06 +0900621
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200622 Sampler() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100623 DEFAULT_METHODS(Sampler)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900624 bool operator==(const Sampler &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000625};
626
Syoyo Fujita5b407452017-06-04 17:42:41 +0900627struct Image {
Syoyo Fujitac2615632016-06-19 21:56:06 +0900628 std::string name;
Bryn Lloydcb6a7072023-06-21 22:30:14 +0200629 int width{-1};
630 int height{-1};
631 int component{-1};
632 int bits{-1}; // bit depth per channel. 8(byte), 16 or 32.
633 int pixel_type{-1}; // pixel type(TINYGLTF_COMPONENT_TYPE_***). usually
634 // UBYTE(bits = 8) or USHORT(bits = 16)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900635 std::vector<unsigned char> image;
Bryn Lloydcb6a7072023-06-21 22:30:14 +0200636 int bufferView{-1}; // (required if no uri)
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500637 std::string mimeType; // (required if no uri) ["image/jpeg", "image/png",
638 // "image/bmp", "image/gif"]
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900639 std::string uri; // (required if no mimeType) uri is not decoded(e.g.
640 // whitespace may be represented as %20)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900641 Value extras;
Syoyo Fujita3e53feb2018-09-02 15:36:17 +0900642 ExtensionMap extensions;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900643
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900644 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
645 std::string extras_json_string;
646 std::string extensions_json_string;
647
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900648 // When this flag is true, data is stored to `image` in as-is format(e.g. jpeg
649 // compressed for "image/jpeg" mime) This feature is good if you use custom
650 // image loader function. (e.g. delayed decoding of images for faster glTF
651 // parsing) Default parser for Image does not provide as-is loading feature at
652 // the moment. (You can manipulate this by providing your own LoadImageData
653 // function)
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200654 bool as_is{false};
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900655
Bryn Lloydcb6a7072023-06-21 22:30:14 +0200656 Image() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100657 DEFAULT_METHODS(Image)
jrkoonced1e14722019-08-27 11:51:02 -0500658
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900659 bool operator==(const Image &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000660};
661
662struct Texture {
Vladimír Vondruš239be2c2018-07-24 23:23:56 +0200663 std::string name;
664
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200665 int sampler{-1};
666 int source{-1};
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900667 Value extras;
Selmar Kokfa7022f2018-04-04 18:10:20 +0200668 ExtensionMap extensions;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900669
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900670 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
671 std::string extras_json_string;
672 std::string extensions_json_string;
673
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200674 Texture() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100675 DEFAULT_METHODS(Texture)
jrkoonced1e14722019-08-27 11:51:02 -0500676
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900677 bool operator==(const Texture &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000678};
Syoyo Fujitabde70212016-02-07 17:38:17 +0900679
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900680struct TextureInfo {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200681 int index{-1}; // required.
682 int texCoord{0}; // The set index of texture's TEXCOORD attribute used for
683 // texture coordinate mapping.
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900684
685 Value extras;
686 ExtensionMap extensions;
687
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900688 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
689 std::string extras_json_string;
690 std::string extensions_json_string;
691
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200692 TextureInfo() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100693 DEFAULT_METHODS(TextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900694 bool operator==(const TextureInfo &) const;
695};
696
697struct NormalTextureInfo {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200698 int index{-1}; // required
699 int texCoord{0}; // The set index of texture's TEXCOORD attribute used for
700 // texture coordinate mapping.
701 double scale{
702 1.0}; // scaledNormal = normalize((<sampled normal texture value>
703 // * 2.0 - 1.0) * vec3(<normal scale>, <normal scale>, 1.0))
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900704
705 Value extras;
706 ExtensionMap extensions;
707
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900708 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
709 std::string extras_json_string;
710 std::string extensions_json_string;
711
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200712 NormalTextureInfo() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100713 DEFAULT_METHODS(NormalTextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900714 bool operator==(const NormalTextureInfo &) const;
715};
716
717struct OcclusionTextureInfo {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200718 int index{-1}; // required
719 int texCoord{0}; // The set index of texture's TEXCOORD attribute used for
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900720 // texture coordinate mapping.
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200721 double strength{1.0}; // occludedColor = lerp(color, color * <sampled
722 // occlusion texture value>, <occlusion strength>)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900723
724 Value extras;
725 ExtensionMap extensions;
726
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900727 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
728 std::string extras_json_string;
729 std::string extensions_json_string;
730
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200731 OcclusionTextureInfo() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100732 DEFAULT_METHODS(OcclusionTextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900733 bool operator==(const OcclusionTextureInfo &) const;
734};
735
736// pbrMetallicRoughness class defined in glTF 2.0 spec.
737struct PbrMetallicRoughness {
Thomas Gamper1f42c962023-11-22 15:59:13 +0100738 std::vector<double> baseColorFactor{1.0, 1.0, 1.0, 1.0}; // len = 4. default [1,1,1,1]
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900739 TextureInfo baseColorTexture;
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200740 double metallicFactor{1.0}; // default 1
741 double roughnessFactor{1.0}; // default 1
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900742 TextureInfo metallicRoughnessTexture;
743
744 Value extras;
745 ExtensionMap extensions;
746
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900747 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
748 std::string extras_json_string;
749 std::string extensions_json_string;
750
Thomas Gamper1f42c962023-11-22 15:59:13 +0100751 PbrMetallicRoughness() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100752 DEFAULT_METHODS(PbrMetallicRoughness)
Thomas Gamper1f42c962023-11-22 15:59:13 +0100753
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900754 bool operator==(const PbrMetallicRoughness &) const;
755};
756
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000757// Each extension should be stored in a ParameterMap.
758// members not in the values could be included in the ParameterMap
759// to keep a single material model
760struct Material {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900761 std::string name;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900762
Thomas Gamper1f42c962023-11-22 15:59:13 +0100763 std::vector<double> emissiveFactor{0.0, 0.0, 0.0}; // length 3. default [0, 0, 0]
764 std::string alphaMode{"OPAQUE"}; // default "OPAQUE"
765 double alphaCutoff{0.5}; // default 0.5
766 bool doubleSided{false}; // default false
Thomas Gampera42263b2024-02-05 15:42:49 +0100767 std::vector<int> lods; // level of detail materials (MSFT_lod)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900768
769 PbrMetallicRoughness pbrMetallicRoughness;
770
771 NormalTextureInfo normalTexture;
772 OcclusionTextureInfo occlusionTexture;
773 TextureInfo emissiveTexture;
774
Syoyo Fujita046400b2019-07-24 19:26:48 +0900775 // For backward compatibility
776 // TODO(syoyo): Remove `values` and `additionalValues` in the next release.
777 ParameterMap values;
778 ParameterMap additionalValues;
Selmar09d2ff12018-03-15 17:30:42 +0100779
780 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900781 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200782
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900783 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
784 std::string extras_json_string;
785 std::string extensions_json_string;
786
Thomas Gamper1f42c962023-11-22 15:59:13 +0100787 Material() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100788 DEFAULT_METHODS(Material)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900789
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900790 bool operator==(const Material &) const;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000791};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900792
Syoyo Fujita5b407452017-06-04 17:42:41 +0900793struct BufferView {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900794 std::string name;
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900795 int buffer{-1}; // Required
Syoyo Fujita4e47bc72020-01-02 22:07:25 +0900796 size_t byteOffset{0}; // minimum 0, default 0
797 size_t byteLength{0}; // required, minimum 1. 0 = invalid
798 size_t byteStride{0}; // minimum 4, maximum 252 (multiple of 4), default 0 =
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900799 // understood to be tightly packed
800 int target{0}; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"] for vertex indices
imallettd9ce9eb2022-10-07 10:37:09 -0700801 // or attribs. Could be 0 for other data
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900802 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900803 ExtensionMap extensions;
804
805 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
806 std::string extras_json_string;
807 std::string extensions_json_string;
808
Syoyo Fujita4e47bc72020-01-02 22:07:25 +0900809 bool dracoDecoded{false}; // Flag indicating this has been draco decoded
Syoyo Fujitad17ff662017-05-29 02:53:12 +0900810
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200811 BufferView() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100812 DEFAULT_METHODS(BufferView)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900813 bool operator==(const BufferView &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000814};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900815
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000816struct Accessor {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200817 int bufferView{-1}; // optional in spec but required here since sparse
818 // accessor are not supported
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900819 std::string name;
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200820 size_t byteOffset{0};
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200821 bool normalized{false}; // optional.
822 int componentType{-1}; // (required) One of TINYGLTF_COMPONENT_TYPE_***
823 size_t count{0}; // required
824 int type{-1}; // (required) One of TINYGLTF_TYPE_*** ..
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900825 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900826 ExtensionMap extensions;
827
828 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
829 std::string extras_json_string;
830 std::string extensions_json_string;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000831
Syoyo Fujita7905a5b2021-01-21 20:33:11 +0900832 std::vector<double>
833 minValues; // optional. integer value is promoted to double
834 std::vector<double>
835 maxValues; // optional. integer value is promoted to double
Syoyo Fujitad9d012a2017-07-12 18:29:29 +0900836
David Siegeld852f502023-06-05 23:28:05 +0200837 struct Sparse {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000838 int count;
839 bool isSparse;
840 struct {
emimvi759976e2023-09-02 09:39:53 +0200841 size_t byteOffset;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000842 int bufferView;
843 int componentType; // a TINYGLTF_COMPONENT_TYPE_ value
David Siegeld852f502023-06-05 23:28:05 +0200844 Value extras;
845 ExtensionMap extensions;
846 std::string extras_json_string;
847 std::string extensions_json_string;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000848 } indices;
849 struct {
850 int bufferView;
emimvi759976e2023-09-02 09:39:53 +0200851 size_t byteOffset;
David Siegeld852f502023-06-05 23:28:05 +0200852 Value extras;
853 ExtensionMap extensions;
854 std::string extras_json_string;
855 std::string extensions_json_string;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000856 } values;
David Siegeld852f502023-06-05 23:28:05 +0200857 Value extras;
858 ExtensionMap extensions;
859 std::string extras_json_string;
860 std::string extensions_json_string;
861 };
862
863 Sparse sparse;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000864
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900865 ///
866 /// Utility function to compute byteStride for a given bufferView object.
867 /// Returns -1 upon invalid glTF value or parameter configuration.
868 ///
869 int ByteStride(const BufferView &bufferViewObject) const {
870 if (bufferViewObject.byteStride == 0) {
871 // Assume data is tightly packed.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500872 int componentSizeInBytes =
873 GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900874 if (componentSizeInBytes <= 0) {
875 return -1;
876 }
877
Syoyo Fujita81d75df2019-08-30 19:19:52 +0900878 int numComponents = GetNumComponentsInType(static_cast<uint32_t>(type));
879 if (numComponents <= 0) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900880 return -1;
881 }
882
Syoyo Fujita81d75df2019-08-30 19:19:52 +0900883 return componentSizeInBytes * numComponents;
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900884 } else {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200885 // Check if byteStride is a multiple of the size of the accessor's
886 // component type.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500887 int componentSizeInBytes =
888 GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900889 if (componentSizeInBytes <= 0) {
890 return -1;
891 }
892
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900893 if ((bufferViewObject.byteStride % uint32_t(componentSizeInBytes)) != 0) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900894 return -1;
895 }
Arthur Brainville8ce4e542018-01-10 01:27:12 +0100896 return static_cast<int>(bufferViewObject.byteStride);
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900897 }
898
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900899 // unreachable return 0;
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900900 }
901
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900902 Accessor()
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200903
904 {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000905 sparse.isSparse = false;
906 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100907 DEFAULT_METHODS(Accessor)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900908 bool operator==(const tinygltf::Accessor &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000909};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900910
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900911struct PerspectiveCamera {
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200912 double aspectRatio{0.0}; // min > 0
913 double yfov{0.0}; // required. min > 0
914 double zfar{0.0}; // min > 0
915 double znear{0.0}; // required. min > 0
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900916
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200917 PerspectiveCamera() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100918 DEFAULT_METHODS(PerspectiveCamera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900919 bool operator==(const PerspectiveCamera &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900920
Selmar09d2ff12018-03-15 17:30:42 +0100921 ExtensionMap extensions;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900922 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900923
924 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
925 std::string extras_json_string;
926 std::string extensions_json_string;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900927};
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000928
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900929struct OrthographicCamera {
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200930 double xmag{0.0}; // required. must not be zero.
931 double ymag{0.0}; // required. must not be zero.
932 double zfar{0.0}; // required. `zfar` must be greater than `znear`.
933 double znear{0.0}; // required
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000934
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200935 OrthographicCamera() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100936 DEFAULT_METHODS(OrthographicCamera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900937 bool operator==(const OrthographicCamera &) const;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900938
Selmar09d2ff12018-03-15 17:30:42 +0100939 ExtensionMap extensions;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900940 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900941
942 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
943 std::string extras_json_string;
944 std::string extensions_json_string;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900945};
946
947struct Camera {
948 std::string type; // required. "perspective" or "orthographic"
949 std::string name;
950
951 PerspectiveCamera perspective;
952 OrthographicCamera orthographic;
953
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200954 Camera() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100955 DEFAULT_METHODS(Camera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900956 bool operator==(const Camera &) const;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900957
Selmar09d2ff12018-03-15 17:30:42 +0100958 ExtensionMap extensions;
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000959 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900960
961 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
962 std::string extras_json_string;
963 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900964};
965
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000966struct Primitive {
967 std::map<std::string, int> attributes; // (required) A dictionary object of
968 // integer, where each integer
969 // is the index of the accessor
970 // containing an attribute.
Bryn Lloydcb6a7072023-06-21 22:30:14 +0200971 int material{-1}; // The index of the material to apply to this primitive
972 // when rendering.
973 int indices{-1}; // The index of the accessor that contains the indices.
974 int mode{-1}; // one of TINYGLTF_MODE_***
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900975 std::vector<std::map<std::string, int> > targets; // array of morph targets,
imallettd9ce9eb2022-10-07 10:37:09 -0700976 // where each target is a dict with attributes in ["POSITION, "NORMAL",
Syoyo Fujita5b407452017-06-04 17:42:41 +0900977 // "TANGENT"] pointing
978 // to their corresponding accessors
Alex Wood7319db72019-01-24 15:38:16 -0500979 ExtensionMap extensions;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000980 Value extras;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900981
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900982 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
983 std::string extras_json_string;
984 std::string extensions_json_string;
985
Bryn Lloydcb6a7072023-06-21 22:30:14 +0200986 Primitive() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100987 DEFAULT_METHODS(Primitive)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900988 bool operator==(const Primitive &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000989};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900990
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900991struct Mesh {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900992 std::string name;
993 std::vector<Primitive> primitives;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900994 std::vector<double> weights; // weights to be applied to the Morph Targets
Selmar09d2ff12018-03-15 17:30:42 +0100995 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900996 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200997
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900998 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
999 std::string extras_json_string;
1000 std::string extensions_json_string;
1001
jrkoonced1e14722019-08-27 11:51:02 -05001002 Mesh() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001003 DEFAULT_METHODS(Mesh)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001004 bool operator==(const Mesh &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001005};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001006
1007class Node {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001008 public:
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001009 Node() = default;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001010
Selmar Kokb74fade2019-10-29 16:09:32 +01001011 DEFAULT_METHODS(Node)
Syoyo Fujitad6b0d0a2019-06-29 17:31:13 +09001012
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001013 bool operator==(const Node &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001014
Syoyo Fujita7a570c82023-06-19 21:52:13 +09001015 int camera{-1}; // the index of the camera referenced by this node
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001016
1017 std::string name;
Syoyo Fujita7a570c82023-06-19 21:52:13 +09001018 int skin{-1};
1019 int mesh{-1};
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001020 int light{-1}; // light source index (KHR_lights_punctual)
1021 int emitter{-1}; // audio emitter index (KHR_audio)
Thomas Gampera42263b2024-02-05 15:42:49 +01001022 std::vector<int> lods; // level of detail nodes (MSFT_lod)
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001023 std::vector<int> children;
Syoyo Fujita7c877972016-03-08 01:31:49 +09001024 std::vector<double> rotation; // length must be 0 or 4
1025 std::vector<double> scale; // length must be 0 or 3
1026 std::vector<double> translation; // length must be 0 or 3
1027 std::vector<double> matrix; // length must be 0 or 16
Syoyo Fujita5b407452017-06-04 17:42:41 +09001028 std::vector<double> weights; // The weights of the instantiated Morph Target
Ben Buzbee3b735bb2018-04-24 11:39:30 -07001029
Selmar09d2ff12018-03-15 17:30:42 +01001030 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001031 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001032
1033 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1034 std::string extras_json_string;
1035 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001036};
1037
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001038struct Buffer {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001039 std::string name;
1040 std::vector<unsigned char> data;
Syoyo Fujita5b407452017-06-04 17:42:41 +09001041 std::string
1042 uri; // considered as required here but not in the spec (need to clarify)
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001043 // uri is not decoded(e.g. whitespace may be represented as %20)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001044 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001045 ExtensionMap extensions;
1046
1047 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1048 std::string extras_json_string;
1049 std::string extensions_json_string;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001050
Selmar Kokb74fade2019-10-29 16:09:32 +01001051 Buffer() = default;
1052 DEFAULT_METHODS(Buffer)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001053 bool operator==(const Buffer &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001054};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001055
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001056struct Asset {
Syoyo Fujitab702de72021-03-02 19:08:29 +09001057 std::string version = "2.0"; // required
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001058 std::string generator;
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +00001059 std::string minVersion;
1060 std::string copyright;
Selmar09d2ff12018-03-15 17:30:42 +01001061 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001062 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001063
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001064 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1065 std::string extras_json_string;
1066 std::string extensions_json_string;
1067
jrkoonced1e14722019-08-27 11:51:02 -05001068 Asset() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001069 DEFAULT_METHODS(Asset)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001070 bool operator==(const Asset &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001071};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001072
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001073struct Scene {
1074 std::string name;
1075 std::vector<int> nodes;
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001076 std::vector<int> audioEmitters; // KHR_audio global emitters
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001077
Selmar09d2ff12018-03-15 17:30:42 +01001078 ExtensionMap extensions;
1079 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001080
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001081 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1082 std::string extras_json_string;
1083 std::string extensions_json_string;
1084
jrkoonced1e14722019-08-27 11:51:02 -05001085 Scene() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001086 DEFAULT_METHODS(Scene)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001087 bool operator==(const Scene &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001088};
1089
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001090struct SpotLight {
Bryn Lloyda64f4b42023-06-21 18:40:18 +02001091 double innerConeAngle{0.0};
1092 double outerConeAngle{0.7853981634};
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001093
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001094 SpotLight() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001095 DEFAULT_METHODS(SpotLight)
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001096 bool operator==(const SpotLight &) const;
1097
1098 ExtensionMap extensions;
1099 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001100
1101 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1102 std::string extras_json_string;
1103 std::string extensions_json_string;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001104};
1105
Emanuel Schrade186322b2017-11-06 11:14:41 +01001106struct Light {
1107 std::string name;
1108 std::vector<double> color;
Syoyo Fujita3dad3032020-05-13 21:22:23 +09001109 double intensity{1.0};
Emanuel Schrade186322b2017-11-06 11:14:41 +01001110 std::string type;
imallettd9ce9eb2022-10-07 10:37:09 -07001111 double range{0.0}; // 0.0 = infinite
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001112 SpotLight spot;
1113
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001114 Light() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001115 DEFAULT_METHODS(Light)
Arthur Brainville (Ybalrid)9eeaf202019-09-05 13:02:05 +02001116
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001117 bool operator==(const Light &) const;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001118
1119 ExtensionMap extensions;
1120 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001121
1122 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1123 std::string extras_json_string;
1124 std::string extensions_json_string;
Emanuel Schrade186322b2017-11-06 11:14:41 +01001125};
1126
Baranob_Ilya78864c82023-06-12 10:43:52 +04001127struct PositionalEmitter {
1128 double coneInnerAngle{6.283185307179586};
1129 double coneOuterAngle{6.283185307179586};
1130 double coneOuterGain{0.0};
1131 double maxDistance{100.0};
1132 double refDistance{1.0};
1133 double rolloffFactor{1.0};
1134
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001135 PositionalEmitter() = default;
Baranob_Ilya78864c82023-06-12 10:43:52 +04001136 DEFAULT_METHODS(PositionalEmitter)
1137 bool operator==(const PositionalEmitter &) const;
1138
1139 ExtensionMap extensions;
1140 Value extras;
1141
1142 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1143 std::string extras_json_string;
1144 std::string extensions_json_string;
1145};
1146
1147struct AudioEmitter {
Baranob_Ilya879cb472023-06-12 13:35:05 +04001148 std::string name;
1149 double gain{1.0};
1150 bool loop{false};
1151 bool playing{false};
1152 std::string
1153 type; // positional - Positional audio emitters. Using sound cones, the
1154 // orientation is +Z having the same front side for a glTF asset.
1155 // global - Global audio emitters are not affected by the position
1156 // of audio listeners. coneInnerAngle, coneOuterAngle,
1157 // coneOuterGain, distanceModel, maxDistance, refDistance, and
1158 // rolloffFactor should all be ignored when set.
1159 std::string
1160 distanceModel; // linear - A linear distance model calculating the
1161 // gain induced by the distance according to: 1.0
1162 // - rolloffFactor * (distance - refDistance) /
1163 // (maxDistance - refDistance)
1164 // inverse - (default) An inverse distance model
1165 // calculating the gain induced by the distance according
1166 // to: refDistance / (refDistance + rolloffFactor *
1167 // (Math.max(distance, refDistance) - refDistance))
1168 // exponential - An exponential distance model calculating
1169 // the gain induced by the distance according to:
1170 // pow((Math.max(distance, refDistance) / refDistance,
1171 // -rolloffFactor))
1172 PositionalEmitter positional;
1173 int source{-1};
Baranob_Ilya78864c82023-06-12 10:43:52 +04001174
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001175 AudioEmitter() : type("global"), distanceModel("inverse") {}
Baranob_Ilya879cb472023-06-12 13:35:05 +04001176 DEFAULT_METHODS(AudioEmitter)
Baranob_Ilya78864c82023-06-12 10:43:52 +04001177
Baranob_Ilya879cb472023-06-12 13:35:05 +04001178 bool operator==(const AudioEmitter &) const;
Baranob_Ilya78864c82023-06-12 10:43:52 +04001179
Baranob_Ilya879cb472023-06-12 13:35:05 +04001180 ExtensionMap extensions;
1181 Value extras;
Baranob_Ilya78864c82023-06-12 10:43:52 +04001182
Baranob_Ilya879cb472023-06-12 13:35:05 +04001183 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1184 std::string extras_json_string;
1185 std::string extensions_json_string;
Baranob_Ilya78864c82023-06-12 10:43:52 +04001186};
1187
1188struct AudioSource {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001189 std::string name;
1190 std::string uri;
1191 int bufferView{-1}; // (required if no uri)
1192 std::string
1193 mimeType; // (required if no uri) The audio's MIME type. Required if
1194 // bufferView is defined. Unless specified by another
1195 // extension, the only supported mimeType is audio/mpeg.
Baranob_Ilya78864c82023-06-12 10:43:52 +04001196
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001197 AudioSource() = default;
1198 DEFAULT_METHODS(AudioSource)
Baranob_Ilya78864c82023-06-12 10:43:52 +04001199
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001200 bool operator==(const AudioSource &) const;
Baranob_Ilya78864c82023-06-12 10:43:52 +04001201
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001202 Value extras;
1203 ExtensionMap extensions;
Baranob_Ilya78864c82023-06-12 10:43:52 +04001204
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001205 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1206 std::string extras_json_string;
1207 std::string extensions_json_string;
Baranob_Ilya78864c82023-06-12 10:43:52 +04001208};
1209
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001210class Model {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001211 public:
Selmar Kokff2b1f92019-10-21 17:58:09 +02001212 Model() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001213 DEFAULT_METHODS(Model)
Syoyo Fujitad6b0d0a2019-06-29 17:31:13 +09001214
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001215 bool operator==(const Model &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001216
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001217 std::vector<Accessor> accessors;
1218 std::vector<Animation> animations;
1219 std::vector<Buffer> buffers;
1220 std::vector<BufferView> bufferViews;
1221 std::vector<Material> materials;
1222 std::vector<Mesh> meshes;
1223 std::vector<Node> nodes;
1224 std::vector<Texture> textures;
1225 std::vector<Image> images;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00001226 std::vector<Skin> skins;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001227 std::vector<Sampler> samplers;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09001228 std::vector<Camera> cameras;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001229 std::vector<Scene> scenes;
Emanuel Schrade186322b2017-11-06 11:14:41 +01001230 std::vector<Light> lights;
Baranob_Ilya78864c82023-06-12 10:43:52 +04001231 std::vector<AudioEmitter> audioEmitters;
1232 std::vector<AudioSource> audioSources;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001233
Bryn Lloydcb6a7072023-06-21 22:30:14 +02001234 int defaultScene{-1};
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001235 std::vector<std::string> extensionsUsed;
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00001236 std::vector<std::string> extensionsRequired;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001237
1238 Asset asset;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001239
1240 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001241 ExtensionMap extensions;
1242
1243 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1244 std::string extras_json_string;
1245 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001246};
1247
Syoyo Fujita0614eb82016-10-14 18:50:14 +09001248enum SectionCheck {
Luke San Antonio9e36b612016-06-23 14:10:51 -04001249 NO_REQUIRE = 0x00,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001250 REQUIRE_VERSION = 0x01,
1251 REQUIRE_SCENE = 0x02,
1252 REQUIRE_SCENES = 0x04,
1253 REQUIRE_NODES = 0x08,
1254 REQUIRE_ACCESSORS = 0x10,
1255 REQUIRE_BUFFERS = 0x20,
1256 REQUIRE_BUFFER_VIEWS = 0x40,
1257 REQUIRE_ALL = 0x7f
Luke San Antonio9e36b612016-06-23 14:10:51 -04001258};
1259
Squareysff644d82018-03-13 22:36:18 +01001260///
Pyarelal Knowles385946d2023-01-03 18:18:04 -08001261/// URIEncodeFunction type. Signature for custom URI encoding of external
1262/// resources such as .bin and image files. Used by tinygltf to re-encode the
1263/// final location of saved files. object_type may be used to encode buffer and
1264/// image URIs differently, for example. See
1265/// https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#uris
1266///
1267typedef bool (*URIEncodeFunction)(const std::string &in_uri,
1268 const std::string &object_type,
1269 std::string *out_uri, void *user_data);
1270
1271///
1272/// URIDecodeFunction type. Signature for custom URI decoding of external
1273/// resources such as .bin and image files. Used by tinygltf when computing
1274/// filenames to write resources.
1275///
1276typedef bool (*URIDecodeFunction)(const std::string &in_uri,
1277 std::string *out_uri, void *user_data);
1278
1279// Declaration of default uri decode function
1280bool URIDecode(const std::string &in_uri, std::string *out_uri,
1281 void *user_data);
1282
1283///
1284/// A structure containing URI callbacks and a pointer to their user data.
1285///
1286struct URICallbacks {
1287 URIEncodeFunction encode; // Optional encode method
1288 URIDecodeFunction decode; // Required decode method
1289
1290 void *user_data; // An argument that is passed to all uri callbacks
1291};
1292
1293///
Squareysff644d82018-03-13 22:36:18 +01001294/// LoadImageDataFunction type. Signature for custom image loading callbacks.
1295///
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001296typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *,
1297 std::string *, int, int,
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001298 const unsigned char *, int,
1299 void *user_pointer);
Squareysff644d82018-03-13 22:36:18 +01001300
johan bowald642a3432018-04-01 12:37:18 +02001301///
1302/// WriteImageDataFunction type. Signature for custom image writing callbacks.
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08001303/// The out_uri parameter becomes the URI written to the gltf and may reference
1304/// a file or contain a data URI.
johan bowald642a3432018-04-01 12:37:18 +02001305///
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08001306typedef bool (*WriteImageDataFunction)(const std::string *basepath,
1307 const std::string *filename,
1308 const Image *image, bool embedImages,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08001309 const URICallbacks *uri_cb,
1310 std::string *out_uri,
1311 void *user_pointer);
johan bowald642a3432018-04-01 12:37:18 +02001312
Squareys2d3594d2018-03-13 22:40:53 +01001313#ifndef TINYGLTF_NO_STB_IMAGE
Squareysff644d82018-03-13 22:36:18 +01001314// Declaration of default image loader callback
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001315bool LoadImageData(Image *image, const int image_idx, std::string *err,
1316 std::string *warn, int req_width, int req_height,
1317 const unsigned char *bytes, int size, void *);
Squareys2d3594d2018-03-13 22:40:53 +01001318#endif
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001319
johan bowald642a3432018-04-01 12:37:18 +02001320#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
1321// Declaration of default image writer callback
1322bool WriteImageData(const std::string *basepath, const std::string *filename,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08001323 const Image *image, bool embedImages,
1324 const URICallbacks *uri_cb, std::string *out_uri, void *);
johan bowald642a3432018-04-01 12:37:18 +02001325#endif
1326
Paolo Jovone6601bf2018-07-07 20:43:33 +02001327///
1328/// FilExistsFunction type. Signature for custom filesystem callbacks.
1329///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001330typedef bool (*FileExistsFunction)(const std::string &abs_filename, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001331
1332///
1333/// ExpandFilePathFunction type. Signature for custom filesystem callbacks.
1334///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001335typedef std::string (*ExpandFilePathFunction)(const std::string &, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001336
1337///
1338/// ReadWholeFileFunction type. Signature for custom filesystem callbacks.
1339///
1340typedef bool (*ReadWholeFileFunction)(std::vector<unsigned char> *,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001341 std::string *, const std::string &,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001342 void *);
1343
1344///
1345/// WriteWholeFileFunction type. Signature for custom filesystem callbacks.
1346///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001347typedef bool (*WriteWholeFileFunction)(std::string *, const std::string &,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001348 const std::vector<unsigned char> &,
1349 void *);
1350
1351///
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09001352/// GetFileSizeFunction type. Signature for custom filesystem callbacks.
1353///
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001354typedef bool (*GetFileSizeFunction)(size_t *filesize_out, std::string *err,
1355 const std::string &abs_filename,
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09001356 void *userdata);
1357
1358///
Paolo Jovone6601bf2018-07-07 20:43:33 +02001359/// A structure containing all required filesystem callbacks and a pointer to
1360/// their user data.
1361///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001362struct FsCallbacks {
1363 FileExistsFunction FileExists;
1364 ExpandFilePathFunction ExpandFilePath;
1365 ReadWholeFileFunction ReadWholeFile;
1366 WriteWholeFileFunction WriteWholeFile;
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001367 GetFileSizeFunction GetFileSizeInBytes; // To avoid GetFileSize Win32 API,
1368 // add `InBytes` suffix.
Paolo Jovone6601bf2018-07-07 20:43:33 +02001369
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001370 void *user_data; // An argument that is passed to all fs callbacks
Paolo Jovone6601bf2018-07-07 20:43:33 +02001371};
1372
1373#ifndef TINYGLTF_NO_FS
1374// Declaration of default filesystem callbacks
1375
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001376bool FileExists(const std::string &abs_filename, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001377
Syoyo Fujita3d939fd2020-06-06 18:13:15 +09001378///
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04001379/// Expand file path(e.g. `~` to home directory on posix, `%APPDATA%` to
Syoyo Fujita2c521b32020-12-04 00:50:46 +09001380/// `C:\\Users\\tinygltf\\AppData`)
Syoyo Fujita3d939fd2020-06-06 18:13:15 +09001381///
1382/// @param[in] filepath File path string. Assume UTF-8
1383/// @param[in] userdata User data. Set to `nullptr` if you don't need it.
1384///
1385std::string ExpandFilePath(const std::string &filepath, void *userdata);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001386
1387bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001388 const std::string &filepath, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001389
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001390bool WriteWholeFile(std::string *err, const std::string &filepath,
1391 const std::vector<unsigned char> &contents, void *);
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09001392
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001393bool GetFileSizeInBytes(size_t *filesize_out, std::string *err,
1394 const std::string &filepath, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001395#endif
1396
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001397///
imallettd9ce9eb2022-10-07 10:37:09 -07001398/// glTF Parser/Serializer context.
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001399///
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001400class TinyGLTF {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001401 public:
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001402#ifdef __clang__
1403#pragma clang diagnostic push
1404#pragma clang diagnostic ignored "-Wc++98-compat"
1405#endif
1406
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001407 TinyGLTF() = default;
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001408
1409#ifdef __clang__
1410#pragma clang diagnostic pop
1411#endif
1412
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001413 ~TinyGLTF() = default;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001414
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001415 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001416 /// Loads glTF ASCII asset from a file.
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001417 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001418 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001419 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001420 bool LoadASCIIFromFile(Model *model, std::string *err, std::string *warn,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001421 const std::string &filename,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001422 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001423
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001424 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001425 /// Loads glTF ASCII asset from string(memory).
1426 /// `length` = strlen(str);
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001427 /// `base_dir` is a search path of glTF asset(e.g. images). Path Must be an
1428 /// expanded path (e.g. no tilde(`~`), no environment variables). Set warning
1429 /// message to `warn` for example it fails to load asserts. Returns false and
1430 /// set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001431 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001432 bool LoadASCIIFromString(Model *model, std::string *err, std::string *warn,
1433 const char *str, const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001434 const std::string &base_dir,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001435 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001436
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001437 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001438 /// Loads glTF binary asset from a file.
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001439 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001440 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001441 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001442 bool LoadBinaryFromFile(Model *model, std::string *err, std::string *warn,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001443 const std::string &filename,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001444 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001445
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001446 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001447 /// Loads glTF binary asset from memory.
1448 /// `length` = strlen(str);
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001449 /// `base_dir` is a search path of glTF asset(e.g. images). Path Must be an
1450 /// expanded path (e.g. no tilde(`~`), no environment variables).
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001451 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001452 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001453 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001454 bool LoadBinaryFromMemory(Model *model, std::string *err, std::string *warn,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001455 const unsigned char *bytes,
1456 const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001457 const std::string &base_dir = "",
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001458 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001459
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001460 ///
imallettd9ce9eb2022-10-07 10:37:09 -07001461 /// Write glTF to stream, buffers and images will be embedded
Johan Bowald1af7c1d2019-07-16 14:50:46 +02001462 ///
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08001463 bool WriteGltfSceneToStream(const Model *model, std::ostream &stream,
Johan Bowald1af7c1d2019-07-16 14:50:46 +02001464 bool prettyPrint, bool writeBinary);
1465
1466 ///
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001467 /// Write glTF to file.
1468 ///
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08001469 bool WriteGltfSceneToFile(const Model *model, const std::string &filename,
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001470 bool embedImages, bool embedBuffers,
1471 bool prettyPrint, bool writeBinary);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00001472
Squareysff644d82018-03-13 22:36:18 +01001473 ///
Nyall Dawson8c85d5e2023-08-28 12:56:09 +10001474 /// Sets the parsing strictness.
1475 ///
1476 void SetParseStrictness(ParseStrictness strictness);
1477
1478 ///
Squareysff644d82018-03-13 22:36:18 +01001479 /// Set callback to use for loading image data
1480 ///
1481 void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data);
1482
johan bowald642a3432018-04-01 12:37:18 +02001483 ///
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001484 /// Unset(remove) callback of loading image data
1485 ///
1486 void RemoveImageLoader();
1487
1488 ///
johan bowald642a3432018-04-01 12:37:18 +02001489 /// Set callback to use for writing image data
1490 ///
1491 void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data);
1492
Paolo Jovone6601bf2018-07-07 20:43:33 +02001493 ///
Pyarelal Knowles385946d2023-01-03 18:18:04 -08001494 /// Set callbacks to use for URI encoding and decoding and their user data
1495 ///
1496 void SetURICallbacks(URICallbacks callbacks);
1497
1498 ///
Paolo Jovone6601bf2018-07-07 20:43:33 +02001499 /// Set callbacks to use for filesystem (fs) access and their user data
1500 ///
1501 void SetFsCallbacks(FsCallbacks callbacks);
1502
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001503 ///
1504 /// Set serializing default values(default = false).
1505 /// When true, default values are force serialized to .glTF.
imallettd9ce9eb2022-10-07 10:37:09 -07001506 /// This may be helpful if you want to serialize a full description of glTF
Syoyo Fujitaff515702019-08-24 16:29:14 +09001507 /// data.
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001508 ///
Syoyo Fujitaff515702019-08-24 16:29:14 +09001509 /// TODO(LTE): Supply parsing option as function arguments to
1510 /// `LoadASCIIFromFile()` and others, not by a class method
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001511 ///
1512 void SetSerializeDefaultValues(const bool enabled) {
1513 serialize_default_values_ = enabled;
1514 }
1515
Syoyo Fujitaff515702019-08-24 16:29:14 +09001516 bool GetSerializeDefaultValues() const { return serialize_default_values_; }
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001517
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001518 ///
1519 /// Store original JSON string for `extras` and `extensions`.
1520 /// This feature will be useful when the user want to reconstruct custom data
1521 /// structure from JSON string.
1522 ///
1523 void SetStoreOriginalJSONForExtrasAndExtensions(const bool enabled) {
1524 store_original_json_for_extras_and_extensions_ = enabled;
1525 }
1526
1527 bool GetStoreOriginalJSONForExtrasAndExtensions() const {
1528 return store_original_json_for_extras_and_extensions_;
1529 }
1530
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001531 ///
imallettd9ce9eb2022-10-07 10:37:09 -07001532 /// Specify whether preserve image channels when loading images or not.
1533 /// (Not effective when the user supplies their own LoadImageData callbacks)
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001534 ///
1535 void SetPreserveImageChannels(bool onoff) {
1536 preserve_image_channels_ = onoff;
1537 }
1538
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09001539 ///
1540 /// Set maximum allowed external file size in bytes.
1541 /// Default: 2GB
1542 /// Only effective for built-in ReadWholeFileFunction FS function.
1543 ///
1544 void SetMaxExternalFileSize(size_t max_bytes) {
1545 max_external_file_size_ = max_bytes;
1546 }
1547
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001548 size_t GetMaxExternalFileSize() const { return max_external_file_size_; }
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09001549
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001550 bool GetPreserveImageChannels() const { return preserve_image_channels_; }
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001551
Syoyo Fujitabeded612016-05-01 20:03:43 +09001552 private:
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001553 ///
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001554 /// Loads glTF asset from string(memory).
1555 /// `length` = strlen(str);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001556 /// Set warning message to `warn` for example it fails to load asserts
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001557 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001558 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001559 bool LoadFromString(Model *model, std::string *err, std::string *warn,
1560 const char *str, const unsigned int length,
1561 const std::string &base_dir, unsigned int check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001562
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001563 const unsigned char *bin_data_ = nullptr;
1564 size_t bin_size_ = 0;
1565 bool is_binary_ = false;
1566
Nyall Dawsonbbc1eae2023-09-03 09:05:32 +10001567 ParseStrictness strictness_ = ParseStrictness::Strict;
Nyall Dawson8c85d5e2023-08-28 12:56:09 +10001568
Syoyo Fujitaff515702019-08-24 16:29:14 +09001569 bool serialize_default_values_ = false; ///< Serialize default values?
Squareysff644d82018-03-13 22:36:18 +01001570
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001571 bool store_original_json_for_extras_and_extensions_ = false;
1572
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001573 bool preserve_image_channels_ = false; /// Default false(expand channels to
1574 /// RGBA) for backward compatibility.
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001575
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001576 size_t max_external_file_size_{
1577 size_t((std::numeric_limits<int32_t>::max)())}; // Default 2GB
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09001578
Syoyo Fujita9117abb2022-08-16 20:10:26 +09001579 // Warning & error messages
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09001580 std::string warn_;
1581 std::string err_;
1582
Paolo Jovone6601bf2018-07-07 20:43:33 +02001583 FsCallbacks fs = {
1584#ifndef TINYGLTF_NO_FS
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001585 &tinygltf::FileExists,
1586 &tinygltf::ExpandFilePath,
1587 &tinygltf::ReadWholeFile,
1588 &tinygltf::WriteWholeFile,
1589 &tinygltf::GetFileSizeInBytes,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001590
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001591 nullptr // Fs callback user data
Paolo Jovone6601bf2018-07-07 20:43:33 +02001592#else
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09001593 nullptr, nullptr, nullptr, nullptr, nullptr,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001594
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001595 nullptr // Fs callback user data
Paolo Jovone6601bf2018-07-07 20:43:33 +02001596#endif
1597 };
1598
Pyarelal Knowles385946d2023-01-03 18:18:04 -08001599 URICallbacks uri_cb = {
1600 // Use paths as-is by default. This will use JSON string escaping.
1601 nullptr,
1602 // Decode all URIs before using them as paths as the application may have
1603 // percent encoded them.
1604 &tinygltf::URIDecode,
1605 // URI callback user data
1606 nullptr};
1607
Squareysff644d82018-03-13 22:36:18 +01001608 LoadImageDataFunction LoadImageData =
1609#ifndef TINYGLTF_NO_STB_IMAGE
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001610 &tinygltf::LoadImageData;
Squareysff644d82018-03-13 22:36:18 +01001611#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001612 nullptr;
Squareysff644d82018-03-13 22:36:18 +01001613#endif
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001614 void *load_image_user_data_{nullptr};
1615 bool user_image_loader_{false};
johan bowald642a3432018-04-01 12:37:18 +02001616
1617 WriteImageDataFunction WriteImageData =
1618#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001619 &tinygltf::WriteImageData;
johan bowald642a3432018-04-01 12:37:18 +02001620#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001621 nullptr;
johan bowald642a3432018-04-01 12:37:18 +02001622#endif
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001623 void *write_image_user_data_{nullptr};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001624};
1625
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001626#ifdef __clang__
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001627#pragma clang diagnostic pop // -Wpadded
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001628#endif
1629
Syoyo Fujita7c877972016-03-08 01:31:49 +09001630} // namespace tinygltf
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001631
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001632#endif // TINY_GLTF_H_
Syoyo Fujitac8d3c762017-05-25 01:42:38 +09001633
Selmar Kok31cb7f92018-10-03 15:39:05 +02001634#if defined(TINYGLTF_IMPLEMENTATION) || defined(__INTELLISENSE__)
Syoyo Fujita7c877972016-03-08 01:31:49 +09001635#include <algorithm>
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001636// #include <cassert>
Paolo Jovone6601bf2018-07-07 20:43:33 +02001637#ifndef TINYGLTF_NO_FS
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001638#include <sys/stat.h> // for is_directory check
1639
Syoyo Fujita125d8e52019-11-09 20:52:56 +09001640#include <cstdio>
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001641#include <fstream>
Paolo Jovone6601bf2018-07-07 20:43:33 +02001642#endif
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001643#include <sstream>
Syoyo Fujitabeded612016-05-01 20:03:43 +09001644
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001645#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001646// Disable some warnings for external files.
1647#pragma clang diagnostic push
1648#pragma clang diagnostic ignored "-Wfloat-equal"
1649#pragma clang diagnostic ignored "-Wexit-time-destructors"
1650#pragma clang diagnostic ignored "-Wconversion"
1651#pragma clang diagnostic ignored "-Wold-style-cast"
Syoyo Fujita643ce102016-05-01 17:19:37 +09001652#pragma clang diagnostic ignored "-Wglobal-constructors"
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001653#if __has_warning("-Wreserved-id-macro")
Syoyo Fujita643ce102016-05-01 17:19:37 +09001654#pragma clang diagnostic ignored "-Wreserved-id-macro"
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001655#endif
Syoyo Fujita643ce102016-05-01 17:19:37 +09001656#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
1657#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001658#pragma clang diagnostic ignored "-Wc++98-compat"
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001659#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001660#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
1661#pragma clang diagnostic ignored "-Wswitch-enum"
1662#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001663#pragma clang diagnostic ignored "-Wweak-vtables"
1664#pragma clang diagnostic ignored "-Wcovered-switch-default"
Syoyo Fujitaa8f0b1c2018-08-28 21:33:40 +09001665#if __has_warning("-Wdouble-promotion")
1666#pragma clang diagnostic ignored "-Wdouble-promotion"
1667#endif
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001668#if __has_warning("-Wcomma")
Syoyo Fujitac8d3c762017-05-25 01:42:38 +09001669#pragma clang diagnostic ignored "-Wcomma"
1670#endif
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001671#if __has_warning("-Wzero-as-null-pointer-constant")
1672#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
1673#endif
1674#if __has_warning("-Wcast-qual")
1675#pragma clang diagnostic ignored "-Wcast-qual"
1676#endif
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001677#if __has_warning("-Wmissing-variable-declarations")
1678#pragma clang diagnostic ignored "-Wmissing-variable-declarations"
1679#endif
1680#if __has_warning("-Wmissing-prototypes")
1681#pragma clang diagnostic ignored "-Wmissing-prototypes"
1682#endif
1683#if __has_warning("-Wcast-align")
1684#pragma clang diagnostic ignored "-Wcast-align"
1685#endif
1686#if __has_warning("-Wnewline-eof")
1687#pragma clang diagnostic ignored "-Wnewline-eof"
1688#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001689#if __has_warning("-Wunused-parameter")
1690#pragma clang diagnostic ignored "-Wunused-parameter"
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001691#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001692#if __has_warning("-Wmismatched-tags")
1693#pragma clang diagnostic ignored "-Wmismatched-tags"
1694#endif
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001695#if __has_warning("-Wextra-semi-stmt")
1696#pragma clang diagnostic ignored "-Wextra-semi-stmt"
1697#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001698#endif
1699
imallettd9ce9eb2022-10-07 10:37:09 -07001700// Disable GCC warnings
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001701#ifdef __GNUC__
1702#pragma GCC diagnostic push
1703#pragma GCC diagnostic ignored "-Wtype-limits"
Syoyo Fujitaca56f722019-03-07 21:04:25 +09001704#endif // __GNUC__
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001705
krokofc0116b2019-03-03 08:28:49 +02001706#ifndef TINYGLTF_NO_INCLUDE_JSON
jrkooncecba5d6c2019-08-29 11:26:22 -05001707#ifndef TINYGLTF_USE_RAPIDJSON
Alex Wood7319db72019-01-24 15:38:16 -05001708#include "json.hpp"
jrkooncecba5d6c2019-08-29 11:26:22 -05001709#else
Alvaro Barua3ddf8302021-03-18 00:21:23 +00001710#ifndef TINYGLTF_NO_INCLUDE_RAPIDJSON
jrkooncecba5d6c2019-08-29 11:26:22 -05001711#include "document.h"
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001712#include "prettywriter.h"
1713#include "rapidjson.h"
jrkooncecba5d6c2019-08-29 11:26:22 -05001714#include "stringbuffer.h"
1715#include "writer.h"
jrkooncecba5d6c2019-08-29 11:26:22 -05001716#endif
krokof4b6d112019-03-03 01:11:31 +02001717#endif
Alvaro Barua3ddf8302021-03-18 00:21:23 +00001718#endif
Alex Wood7319db72019-01-24 15:38:16 -05001719
1720#ifdef TINYGLTF_ENABLE_DRACO
Alex Wood7319db72019-01-24 15:38:16 -05001721#include "draco/compression/decode.h"
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001722#include "draco/core/decoder_buffer.h"
Alex Wood7319db72019-01-24 15:38:16 -05001723#endif
Squareys2d3594d2018-03-13 22:40:53 +01001724
1725#ifndef TINYGLTF_NO_STB_IMAGE
krokofc0116b2019-03-03 08:28:49 +02001726#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE
Alex Wood7319db72019-01-24 15:38:16 -05001727#include "stb_image.h"
Squareys2d3594d2018-03-13 22:40:53 +01001728#endif
krokof4b6d112019-03-03 01:11:31 +02001729#endif
Squareys2d3594d2018-03-13 22:40:53 +01001730
johan bowald642a3432018-04-01 12:37:18 +02001731#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
krokofc0116b2019-03-03 08:28:49 +02001732#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE
Alex Wood7319db72019-01-24 15:38:16 -05001733#include "stb_image_write.h"
johan bowald642a3432018-04-01 12:37:18 +02001734#endif
krokof4b6d112019-03-03 01:11:31 +02001735#endif
johan bowald642a3432018-04-01 12:37:18 +02001736
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001737#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001738#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001739#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001740
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001741#ifdef __GNUC__
1742#pragma GCC diagnostic pop
1743#endif
1744
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001745#ifdef _WIN32
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001746
1747// issue 143.
1748// Define NOMINMAX to avoid min/max defines,
imallett3a295882022-10-07 11:20:39 -07001749// but undef it after included Windows.h
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001750#ifndef NOMINMAX
1751#define TINYGLTF_INTERNAL_NOMINMAX
1752#define NOMINMAX
1753#endif
1754
Syoyo Fujita87be0ce2019-02-19 21:36:32 +09001755#ifndef WIN32_LEAN_AND_MEAN
1756#define WIN32_LEAN_AND_MEAN
1757#define TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1758#endif
imallett3a295882022-10-07 11:20:39 -07001759#ifndef __MINGW32__
imallett56e10982022-10-07 10:35:16 -07001760#include <Windows.h> // include API for expanding a file path
imallett3a295882022-10-07 11:20:39 -07001761#else
1762#include <windows.h>
1763#endif
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001764
Syoyo Fujita87be0ce2019-02-19 21:36:32 +09001765#ifdef TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1766#undef WIN32_LEAN_AND_MEAN
1767#endif
1768
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001769#if defined(TINYGLTF_INTERNAL_NOMINMAX)
1770#undef NOMINMAX
1771#endif
1772
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001773#if defined(__GLIBCXX__) // mingw
Syoyo Fujita45cac782019-11-09 20:42:55 +09001774
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001775#include <fcntl.h> // _O_RDONLY
1776
1777#include <ext/stdio_filebuf.h> // fstream (all sorts of IO stuff) + stdio_filebuf (=streambuf)
Syoyo Fujita45cac782019-11-09 20:42:55 +09001778
1779#endif
1780
Julian Smith0598a202021-08-25 12:06:08 +01001781#elif !defined(__ANDROID__) && !defined(__OpenBSD__)
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001782// #include <wordexp.h>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001783#endif
1784
Christopher Sean Morrison2c9b2562022-05-14 19:00:19 -04001785#if defined(__sparcv9) || defined(__powerpc__)
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001786// Big endian
1787#else
1788#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
1789#define TINYGLTF_LITTLE_ENDIAN 1
1790#endif
1791#endif
1792
David03ad33c2023-02-15 23:35:51 -06001793namespace tinygltf {
David1f9a4b92023-02-15 22:56:18 -06001794namespace detail {
jrkooncecba5d6c2019-08-29 11:26:22 -05001795#ifdef TINYGLTF_USE_RAPIDJSON
jrkooncece7fa742019-09-04 13:31:44 -05001796
1797#ifdef TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001798// This uses the RapidJSON CRTAllocator. It is thread safe and multiple
1799// documents may be active at once.
1800using json =
1801 rapidjson::GenericValue<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
David Siegelcfe64fb2023-06-07 15:18:38 +02001802using json_iterator = json::MemberIterator;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001803using json_const_iterator = json::ConstMemberIterator;
1804using json_const_array_iterator = json const *;
1805using JsonDocument =
jrkooncece7fa742019-09-04 13:31:44 -05001806 rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001807rapidjson::CrtAllocator s_CrtAllocator; // stateless and thread safe
1808rapidjson::CrtAllocator &GetAllocator() { return s_CrtAllocator; }
jrkooncece7fa742019-09-04 13:31:44 -05001809#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001810// This uses the default RapidJSON MemoryPoolAllocator. It is very fast, but
1811// not thread safe. Only a single JsonDocument may be active at any one time,
1812// meaning only a single gltf load/save can be active any one time.
1813using json = rapidjson::Value;
David Siegelcfe64fb2023-06-07 15:18:38 +02001814using json_iterator = json::MemberIterator;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001815using json_const_iterator = json::ConstMemberIterator;
1816using json_const_array_iterator = json const *;
1817rapidjson::Document *s_pActiveDocument = nullptr;
1818rapidjson::Document::AllocatorType &GetAllocator() {
1819 assert(s_pActiveDocument); // Root json node must be JsonDocument type
1820 return s_pActiveDocument->GetAllocator();
1821}
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001822
1823#ifdef __clang__
1824#pragma clang diagnostic push
1825// Suppress JsonDocument(JsonDocument &&rhs) noexcept
1826#pragma clang diagnostic ignored "-Wunused-member-function"
1827#endif
1828
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001829struct JsonDocument : public rapidjson::Document {
1830 JsonDocument() {
1831 assert(s_pActiveDocument ==
1832 nullptr); // When using default allocator, only one document can be
1833 // active at a time, if you need multiple active at once,
1834 // define TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
1835 s_pActiveDocument = this;
jrkooncece7fa742019-09-04 13:31:44 -05001836 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001837 JsonDocument(const JsonDocument &) = delete;
1838 JsonDocument(JsonDocument &&rhs) noexcept
jrkooncece7fa742019-09-04 13:31:44 -05001839 : rapidjson::Document(std::move(rhs)) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001840 s_pActiveDocument = this;
1841 rhs.isNil = true;
1842 }
1843 ~JsonDocument() {
1844 if (!isNil) {
1845 s_pActiveDocument = nullptr;
jrkooncecba5d6c2019-08-29 11:26:22 -05001846 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001847 }
jrkooncece7fa742019-09-04 13:31:44 -05001848
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001849 private:
1850 bool isNil = false;
1851};
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001852
1853#ifdef __clang__
1854#pragma clang diagnostic pop
1855#endif
1856
jrkooncece7fa742019-09-04 13:31:44 -05001857#endif // TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001858
jrkooncecba5d6c2019-08-29 11:26:22 -05001859#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001860using nlohmann::json;
David Siegelcfe64fb2023-06-07 15:18:38 +02001861using json_iterator = json::iterator;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001862using json_const_iterator = json::const_iterator;
1863using json_const_array_iterator = json_const_iterator;
1864using JsonDocument = json;
jrkooncecba5d6c2019-08-29 11:26:22 -05001865#endif
1866
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001867void JsonParse(JsonDocument &doc, const char *str, size_t length,
1868 bool throwExc = false) {
jrkooncecba5d6c2019-08-29 11:26:22 -05001869#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001870 (void)throwExc;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001871 doc.Parse(str, length);
jrkooncecba5d6c2019-08-29 11:26:22 -05001872#else
David03ad33c2023-02-15 23:35:51 -06001873 doc = detail::json::parse(str, str + length, nullptr, throwExc);
jrkooncecba5d6c2019-08-29 11:26:22 -05001874#endif
jrkooncecba5d6c2019-08-29 11:26:22 -05001875}
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001876} // namespace detail
1877} // namespace tinygltf
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001878
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001879#ifdef __APPLE__
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001880#include "TargetConditionals.h"
Syoyo Fujitab23f6fe2017-11-15 01:03:09 +09001881#endif
1882
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001883#ifdef __clang__
1884#pragma clang diagnostic push
1885#pragma clang diagnostic ignored "-Wc++98-compat"
1886#endif
1887
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09001888namespace tinygltf {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001889
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001890///
1891/// Internal LoadImageDataOption struct.
1892/// This struct is passed through `user_pointer` in LoadImageData.
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001893/// The struct is not passed when the user supply their own LoadImageData
1894/// callbacks.
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001895///
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001896struct LoadImageDataOption {
1897 // true: preserve image channels(e.g. load as RGB image if the image has RGB
1898 // channels) default `false`(channels are expanded to RGBA for backward
imallettd9ce9eb2022-10-07 10:37:09 -07001899 // compatibility).
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001900 bool preserve_channels{false};
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001901};
1902
Selmar Kok31cb7f92018-10-03 15:39:05 +02001903// Equals function for Value, for recursivity
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001904static bool Equals(const tinygltf::Value &one, const tinygltf::Value &other) {
1905 if (one.Type() != other.Type()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001906
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001907 switch (one.Type()) {
1908 case NULL_TYPE:
1909 return true;
1910 case BOOL_TYPE:
1911 return one.Get<bool>() == other.Get<bool>();
Syoyo Fujita150f2432019-07-25 19:22:44 +09001912 case REAL_TYPE:
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001913 return TINYGLTF_DOUBLE_EQUAL(one.Get<double>(), other.Get<double>());
1914 case INT_TYPE:
1915 return one.Get<int>() == other.Get<int>();
1916 case OBJECT_TYPE: {
1917 auto oneObj = one.Get<tinygltf::Value::Object>();
1918 auto otherObj = other.Get<tinygltf::Value::Object>();
1919 if (oneObj.size() != otherObj.size()) return false;
1920 for (auto &it : oneObj) {
1921 auto otherIt = otherObj.find(it.first);
1922 if (otherIt == otherObj.end()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001923
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001924 if (!Equals(it.second, otherIt->second)) return false;
1925 }
1926 return true;
1927 }
1928 case ARRAY_TYPE: {
1929 if (one.Size() != other.Size()) return false;
1930 for (int i = 0; i < int(one.Size()); ++i)
Selmara63cc632019-04-16 16:57:43 +02001931 if (!Equals(one.Get(i), other.Get(i))) return false;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001932 return true;
1933 }
1934 case STRING_TYPE:
1935 return one.Get<std::string>() == other.Get<std::string>();
1936 case BINARY_TYPE:
1937 return one.Get<std::vector<unsigned char> >() ==
1938 other.Get<std::vector<unsigned char> >();
1939 default: {
1940 // unhandled type
1941 return false;
1942 }
1943 }
Selmar Kok31cb7f92018-10-03 15:39:05 +02001944}
1945
1946// Equals function for std::vector<double> using TINYGLTF_DOUBLE_EPSILON
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001947static bool Equals(const std::vector<double> &one,
1948 const std::vector<double> &other) {
1949 if (one.size() != other.size()) return false;
1950 for (int i = 0; i < int(one.size()); ++i) {
1951 if (!TINYGLTF_DOUBLE_EQUAL(one[size_t(i)], other[size_t(i)])) return false;
1952 }
1953 return true;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001954}
1955
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001956bool Accessor::operator==(const Accessor &other) const {
1957 return this->bufferView == other.bufferView &&
1958 this->byteOffset == other.byteOffset &&
1959 this->componentType == other.componentType &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001960 this->count == other.count && this->extensions == other.extensions &&
1961 this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001962 Equals(this->maxValues, other.maxValues) &&
1963 Equals(this->minValues, other.minValues) && this->name == other.name &&
1964 this->normalized == other.normalized && this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001965}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001966bool Animation::operator==(const Animation &other) const {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001967 return this->channels == other.channels &&
1968 this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001969 this->name == other.name && this->samplers == other.samplers;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001970}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001971bool AnimationChannel::operator==(const AnimationChannel &other) const {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001972 return this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001973 this->target_node == other.target_node &&
1974 this->target_path == other.target_path &&
1975 this->sampler == other.sampler;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001976}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001977bool AnimationSampler::operator==(const AnimationSampler &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001978 return this->extras == other.extras && this->extensions == other.extensions &&
1979 this->input == other.input &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001980 this->interpolation == other.interpolation &&
1981 this->output == other.output;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001982}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001983bool Asset::operator==(const Asset &other) const {
1984 return this->copyright == other.copyright &&
1985 this->extensions == other.extensions && this->extras == other.extras &&
1986 this->generator == other.generator &&
1987 this->minVersion == other.minVersion && this->version == other.version;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001988}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001989bool Buffer::operator==(const Buffer &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001990 return this->data == other.data && this->extensions == other.extensions &&
1991 this->extras == other.extras && this->name == other.name &&
1992 this->uri == other.uri;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001993}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001994bool BufferView::operator==(const BufferView &other) const {
1995 return this->buffer == other.buffer && this->byteLength == other.byteLength &&
1996 this->byteOffset == other.byteOffset &&
1997 this->byteStride == other.byteStride && this->name == other.name &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001998 this->target == other.target && this->extensions == other.extensions &&
1999 this->extras == other.extras &&
Alexander Wood0d77a292019-01-26 08:58:45 -05002000 this->dracoDecoded == other.dracoDecoded;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002001}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002002bool Camera::operator==(const Camera &other) const {
2003 return this->name == other.name && this->extensions == other.extensions &&
2004 this->extras == other.extras &&
2005 this->orthographic == other.orthographic &&
2006 this->perspective == other.perspective && this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002007}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002008bool Image::operator==(const Image &other) const {
2009 return this->bufferView == other.bufferView &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09002010 this->component == other.component &&
2011 this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002012 this->height == other.height && this->image == other.image &&
2013 this->mimeType == other.mimeType && this->name == other.name &&
2014 this->uri == other.uri && this->width == other.width;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002015}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002016bool Light::operator==(const Light &other) const {
2017 return Equals(this->color, other.color) && this->name == other.name &&
2018 this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002019}
Baranob_Ilya78864c82023-06-12 10:43:52 +04002020bool AudioEmitter::operator==(const AudioEmitter &other) const {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02002021 return this->name == other.name &&
2022 TINYGLTF_DOUBLE_EQUAL(this->gain, other.gain) &&
Baranob_Ilya78864c82023-06-12 10:43:52 +04002023 this->loop == other.loop && this->playing == other.playing &&
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02002024 this->type == other.type &&
2025 this->distanceModel == other.distanceModel &&
Baranob_Ilya78864c82023-06-12 10:43:52 +04002026 this->source == other.source;
2027}
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02002028bool AudioSource::operator==(const AudioSource &other) const {
Baranob_Ilya78864c82023-06-12 10:43:52 +04002029 return this->name == other.name && this->uri == other.uri;
2030}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002031bool Material::operator==(const Material &other) const {
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09002032 return (this->pbrMetallicRoughness == other.pbrMetallicRoughness) &&
2033 (this->normalTexture == other.normalTexture) &&
2034 (this->occlusionTexture == other.occlusionTexture) &&
2035 (this->emissiveTexture == other.emissiveTexture) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09002036 Equals(this->emissiveFactor, other.emissiveFactor) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09002037 (this->alphaMode == other.alphaMode) &&
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002038 TINYGLTF_DOUBLE_EQUAL(this->alphaCutoff, other.alphaCutoff) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09002039 (this->doubleSided == other.doubleSided) &&
2040 (this->extensions == other.extensions) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09002041 (this->extras == other.extras) && (this->values == other.values) &&
2042 (this->additionalValues == other.additionalValues) &&
2043 (this->name == other.name);
Selmar Kok31cb7f92018-10-03 15:39:05 +02002044}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002045bool Mesh::operator==(const Mesh &other) const {
2046 return this->extensions == other.extensions && this->extras == other.extras &&
Selmar Kok81b672b2019-10-18 16:08:44 +02002047 this->name == other.name && Equals(this->weights, other.weights) &&
2048 this->primitives == other.primitives;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002049}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002050bool Model::operator==(const Model &other) const {
2051 return this->accessors == other.accessors &&
2052 this->animations == other.animations && this->asset == other.asset &&
2053 this->buffers == other.buffers &&
2054 this->bufferViews == other.bufferViews &&
2055 this->cameras == other.cameras &&
2056 this->defaultScene == other.defaultScene &&
2057 this->extensions == other.extensions &&
2058 this->extensionsRequired == other.extensionsRequired &&
2059 this->extensionsUsed == other.extensionsUsed &&
2060 this->extras == other.extras && this->images == other.images &&
2061 this->lights == other.lights && this->materials == other.materials &&
2062 this->meshes == other.meshes && this->nodes == other.nodes &&
2063 this->samplers == other.samplers && this->scenes == other.scenes &&
2064 this->skins == other.skins && this->textures == other.textures;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002065}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002066bool Node::operator==(const Node &other) const {
2067 return this->camera == other.camera && this->children == other.children &&
2068 this->extensions == other.extensions && this->extras == other.extras &&
2069 Equals(this->matrix, other.matrix) && this->mesh == other.mesh &&
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02002070 (this->light == other.light) && (this->emitter == other.emitter) &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002071 this->name == other.name && Equals(this->rotation, other.rotation) &&
2072 Equals(this->scale, other.scale) && this->skin == other.skin &&
2073 Equals(this->translation, other.translation) &&
2074 Equals(this->weights, other.weights);
Selmar Kok31cb7f92018-10-03 15:39:05 +02002075}
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02002076bool SpotLight::operator==(const SpotLight &other) const {
2077 return this->extensions == other.extensions && this->extras == other.extras &&
2078 TINYGLTF_DOUBLE_EQUAL(this->innerConeAngle, other.innerConeAngle) &&
2079 TINYGLTF_DOUBLE_EQUAL(this->outerConeAngle, other.outerConeAngle);
2080}
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02002081bool PositionalEmitter::operator==(const PositionalEmitter &other) const {
2082 return this->extensions == other.extensions && this->extras == other.extras &&
2083 TINYGLTF_DOUBLE_EQUAL(this->coneInnerAngle, other.coneInnerAngle) &&
2084 TINYGLTF_DOUBLE_EQUAL(this->coneOuterAngle, other.coneOuterAngle) &&
2085 TINYGLTF_DOUBLE_EQUAL(this->coneOuterGain, other.coneOuterGain) &&
2086 TINYGLTF_DOUBLE_EQUAL(this->maxDistance, other.maxDistance) &&
2087 TINYGLTF_DOUBLE_EQUAL(this->refDistance, other.refDistance) &&
2088 TINYGLTF_DOUBLE_EQUAL(this->rolloffFactor, other.rolloffFactor);
Baranob_Ilya78864c82023-06-12 10:43:52 +04002089}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002090bool OrthographicCamera::operator==(const OrthographicCamera &other) const {
2091 return this->extensions == other.extensions && this->extras == other.extras &&
2092 TINYGLTF_DOUBLE_EQUAL(this->xmag, other.xmag) &&
2093 TINYGLTF_DOUBLE_EQUAL(this->ymag, other.ymag) &&
2094 TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
2095 TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
Selmar Kok31cb7f92018-10-03 15:39:05 +02002096}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002097bool Parameter::operator==(const Parameter &other) const {
2098 if (this->bool_value != other.bool_value ||
2099 this->has_number_value != other.has_number_value)
2100 return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002101
Selmar Kok2bda71c2018-10-05 14:36:05 +02002102 if (!TINYGLTF_DOUBLE_EQUAL(this->number_value, other.number_value))
2103 return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002104
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002105 if (this->json_double_value.size() != other.json_double_value.size())
2106 return false;
2107 for (auto &it : this->json_double_value) {
2108 auto otherIt = other.json_double_value.find(it.first);
2109 if (otherIt == other.json_double_value.end()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002110
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002111 if (!TINYGLTF_DOUBLE_EQUAL(it.second, otherIt->second)) return false;
2112 }
2113
2114 if (!Equals(this->number_array, other.number_array)) return false;
2115
2116 if (this->string_value != other.string_value) return false;
2117
2118 return true;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002119}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002120bool PerspectiveCamera::operator==(const PerspectiveCamera &other) const {
2121 return TINYGLTF_DOUBLE_EQUAL(this->aspectRatio, other.aspectRatio) &&
2122 this->extensions == other.extensions && this->extras == other.extras &&
2123 TINYGLTF_DOUBLE_EQUAL(this->yfov, other.yfov) &&
2124 TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
2125 TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
Selmar Kok31cb7f92018-10-03 15:39:05 +02002126}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002127bool Primitive::operator==(const Primitive &other) const {
2128 return this->attributes == other.attributes && this->extras == other.extras &&
2129 this->indices == other.indices && this->material == other.material &&
2130 this->mode == other.mode && this->targets == other.targets;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002131}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002132bool Sampler::operator==(const Sampler &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09002133 return this->extensions == other.extensions && this->extras == other.extras &&
2134 this->magFilter == other.magFilter &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002135 this->minFilter == other.minFilter && this->name == other.name &&
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002136 this->wrapS == other.wrapS && this->wrapT == other.wrapT;
Syoyo Fujita2c521b32020-12-04 00:50:46 +09002137
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002138 // this->wrapR == other.wrapR
Selmar Kok31cb7f92018-10-03 15:39:05 +02002139}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002140bool Scene::operator==(const Scene &other) const {
2141 return this->extensions == other.extensions && this->extras == other.extras &&
2142 this->name == other.name && this->nodes == other.nodes;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002143}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002144bool Skin::operator==(const Skin &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09002145 return this->extensions == other.extensions && this->extras == other.extras &&
2146 this->inverseBindMatrices == other.inverseBindMatrices &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002147 this->joints == other.joints && this->name == other.name &&
2148 this->skeleton == other.skeleton;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002149}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002150bool Texture::operator==(const Texture &other) const {
2151 return this->extensions == other.extensions && this->extras == other.extras &&
2152 this->name == other.name && this->sampler == other.sampler &&
2153 this->source == other.source;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002154}
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09002155bool TextureInfo::operator==(const TextureInfo &other) const {
2156 return this->extensions == other.extensions && this->extras == other.extras &&
2157 this->index == other.index && this->texCoord == other.texCoord;
2158}
2159bool NormalTextureInfo::operator==(const NormalTextureInfo &other) const {
2160 return this->extensions == other.extensions && this->extras == other.extras &&
2161 this->index == other.index && this->texCoord == other.texCoord &&
2162 TINYGLTF_DOUBLE_EQUAL(this->scale, other.scale);
2163}
2164bool OcclusionTextureInfo::operator==(const OcclusionTextureInfo &other) const {
2165 return this->extensions == other.extensions && this->extras == other.extras &&
2166 this->index == other.index && this->texCoord == other.texCoord &&
2167 TINYGLTF_DOUBLE_EQUAL(this->strength, other.strength);
2168}
2169bool PbrMetallicRoughness::operator==(const PbrMetallicRoughness &other) const {
2170 return this->extensions == other.extensions && this->extras == other.extras &&
2171 (this->baseColorTexture == other.baseColorTexture) &&
2172 (this->metallicRoughnessTexture == other.metallicRoughnessTexture) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09002173 Equals(this->baseColorFactor, other.baseColorFactor) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09002174 TINYGLTF_DOUBLE_EQUAL(this->metallicFactor, other.metallicFactor) &&
2175 TINYGLTF_DOUBLE_EQUAL(this->roughnessFactor, other.roughnessFactor);
2176}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002177bool Value::operator==(const Value &other) const {
2178 return Equals(*this, other);
Selmar Kok31cb7f92018-10-03 15:39:05 +02002179}
2180
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002181static void swap4(unsigned int *val) {
2182#ifdef TINYGLTF_LITTLE_ENDIAN
2183 (void)val;
2184#else
2185 unsigned int tmp = *val;
2186 unsigned char *dst = reinterpret_cast<unsigned char *>(val);
2187 unsigned char *src = reinterpret_cast<unsigned char *>(&tmp);
2188
2189 dst[0] = src[3];
2190 dst[1] = src[2];
2191 dst[2] = src[1];
2192 dst[3] = src[0];
2193#endif
2194}
2195
Syoyo Fujitabeded612016-05-01 20:03:43 +09002196static std::string JoinPath(const std::string &path0,
2197 const std::string &path1) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002198 if (path0.empty()) {
2199 return path1;
2200 } else {
2201 // check '/'
2202 char lastChar = *path0.rbegin();
2203 if (lastChar != '/') {
2204 return path0 + std::string("/") + path1;
2205 } else {
2206 return path0 + path1;
2207 }
2208 }
2209}
2210
Syoyo Fujita643ce102016-05-01 17:19:37 +09002211static std::string FindFile(const std::vector<std::string> &paths,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002212 const std::string &filepath, FsCallbacks *fs) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002213 if (fs == nullptr || fs->ExpandFilePath == nullptr ||
2214 fs->FileExists == nullptr) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002215 // Error, fs callback[s] missing
2216 return std::string();
2217 }
2218
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002219 // https://github.com/syoyo/tinygltf/issues/416
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02002220 // Use strlen() since std::string's size/length reports the number of elements
2221 // in the buffer, not the length of string(null-terminated) strip
2222 // null-character in the middle of string.
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002223 size_t slength = strlen(filepath.c_str());
2224 if (slength == 0) {
2225 return std::string();
2226 }
2227
2228 std::string cleaned_filepath = std::string(filepath.c_str());
2229
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002230 for (size_t i = 0; i < paths.size(); i++) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002231 std::string absPath =
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002232 fs->ExpandFilePath(JoinPath(paths[i], cleaned_filepath), fs->user_data);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002233 if (fs->FileExists(absPath, fs->user_data)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002234 return absPath;
2235 }
2236 }
2237
2238 return std::string();
2239}
2240
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09002241static std::string GetFilePathExtension(const std::string &FileName) {
johan bowald642a3432018-04-01 12:37:18 +02002242 if (FileName.find_last_of(".") != std::string::npos)
2243 return FileName.substr(FileName.find_last_of(".") + 1);
2244 return "";
2245}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002246
Syoyo Fujita643ce102016-05-01 17:19:37 +09002247static std::string GetBaseDir(const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002248 if (filepath.find_last_of("/\\") != std::string::npos)
2249 return filepath.substr(0, filepath.find_last_of("/\\"));
2250 return "";
2251}
2252
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002253static std::string GetBaseFilename(const std::string &filepath) {
Martin Gerhardyfae65432022-03-13 18:42:39 +01002254 auto idx = filepath.find_last_of("/\\");
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002255 if (idx != std::string::npos) return filepath.substr(idx + 1);
Alexander Wood190382a2021-10-08 12:19:13 -04002256 return filepath;
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002257}
2258
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002259std::string base64_encode(unsigned char const *, unsigned int len);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002260std::string base64_decode(std::string const &s);
2261
2262/*
2263 base64.cpp and base64.h
2264
2265 Copyright (C) 2004-2008 René Nyffenegger
2266
2267 This source code is provided 'as-is', without any express or implied
2268 warranty. In no event will the author be held liable for any damages
2269 arising from the use of this software.
2270
2271 Permission is granted to anyone to use this software for any purpose,
2272 including commercial applications, and to alter it and redistribute it
2273 freely, subject to the following restrictions:
2274
2275 1. The origin of this source code must not be misrepresented; you must not
2276 claim that you wrote the original source code. If you use this source code
2277 in a product, an acknowledgment in the product documentation would be
2278 appreciated but is not required.
2279
2280 2. Altered source versions must be plainly marked as such, and must not be
2281 misrepresented as being the original source code.
2282
2283 3. This notice may not be removed or altered from any source distribution.
2284
2285 René Nyffenegger rene.nyffenegger@adp-gmbh.ch
2286
2287*/
2288
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002289#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09002290#pragma clang diagnostic push
Syoyo Fujita643ce102016-05-01 17:19:37 +09002291#pragma clang diagnostic ignored "-Wsign-conversion"
2292#pragma clang diagnostic ignored "-Wconversion"
Syoyo Fujita6fd91622016-05-01 20:27:30 +09002293#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002294
2295static inline bool is_base64(unsigned char c) {
2296 return (isalnum(c) || (c == '+') || (c == '/'));
2297}
2298
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002299std::string base64_encode(unsigned char const *bytes_to_encode,
2300 unsigned int in_len) {
johan bowald30c53472018-03-30 11:49:36 +02002301 std::string ret;
2302 int i = 0;
2303 int j = 0;
2304 unsigned char char_array_3[3];
2305 unsigned char char_array_4[4];
2306
Syoyo Fujitaff515702019-08-24 16:29:14 +09002307 const char *base64_chars =
2308 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2309 "abcdefghijklmnopqrstuvwxyz"
2310 "0123456789+/";
2311
johan bowald30c53472018-03-30 11:49:36 +02002312 while (in_len--) {
2313 char_array_3[i++] = *(bytes_to_encode++);
2314 if (i == 3) {
2315 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002316 char_array_4[1] =
2317 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
2318 char_array_4[2] =
2319 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
johan bowald30c53472018-03-30 11:49:36 +02002320 char_array_4[3] = char_array_3[2] & 0x3f;
2321
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002322 for (i = 0; (i < 4); i++) ret += base64_chars[char_array_4[i]];
johan bowald30c53472018-03-30 11:49:36 +02002323 i = 0;
2324 }
2325 }
2326
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002327 if (i) {
2328 for (j = i; j < 3; j++) char_array_3[j] = '\0';
johan bowald30c53472018-03-30 11:49:36 +02002329
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002330 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
2331 char_array_4[1] =
2332 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
2333 char_array_4[2] =
2334 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
johan bowald30c53472018-03-30 11:49:36 +02002335
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002336 for (j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]];
johan bowald30c53472018-03-30 11:49:36 +02002337
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002338 while ((i++ < 3)) ret += '=';
johan bowald30c53472018-03-30 11:49:36 +02002339 }
2340
2341 return ret;
johan bowald30c53472018-03-30 11:49:36 +02002342}
2343
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002344std::string base64_decode(std::string const &encoded_string) {
Syoyo Fujita8c6774c2016-05-01 20:36:29 +09002345 int in_len = static_cast<int>(encoded_string.size());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002346 int i = 0;
2347 int j = 0;
2348 int in_ = 0;
2349 unsigned char char_array_4[4], char_array_3[3];
2350 std::string ret;
2351
Syoyo Fujitaff515702019-08-24 16:29:14 +09002352 const std::string base64_chars =
2353 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2354 "abcdefghijklmnopqrstuvwxyz"
2355 "0123456789+/";
2356
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002357 while (in_len-- && (encoded_string[in_] != '=') &&
2358 is_base64(encoded_string[in_])) {
2359 char_array_4[i++] = encoded_string[in_];
2360 in_++;
2361 if (i == 4) {
2362 for (i = 0; i < 4; i++)
Syoyo Fujitabeded612016-05-01 20:03:43 +09002363 char_array_4[i] =
2364 static_cast<unsigned char>(base64_chars.find(char_array_4[i]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002365
2366 char_array_3[0] =
2367 (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
2368 char_array_3[1] =
2369 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
2370 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
2371
Syoyo Fujita7c877972016-03-08 01:31:49 +09002372 for (i = 0; (i < 3); i++) ret += char_array_3[i];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002373 i = 0;
2374 }
2375 }
2376
2377 if (i) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09002378 for (j = i; j < 4; j++) char_array_4[j] = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002379
2380 for (j = 0; j < 4; j++)
Syoyo Fujitabeded612016-05-01 20:03:43 +09002381 char_array_4[j] =
2382 static_cast<unsigned char>(base64_chars.find(char_array_4[j]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002383
2384 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
2385 char_array_3[1] =
2386 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
2387 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
2388
Syoyo Fujita7c877972016-03-08 01:31:49 +09002389 for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002390 }
2391
2392 return ret;
2393}
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002394#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09002395#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +09002396#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002397
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002398// https://github.com/syoyo/tinygltf/issues/228
2399// TODO(syoyo): Use uriparser https://uriparser.github.io/ for stricter Uri
2400// decoding?
2401//
Alexander Woode4bc6c72021-10-14 08:54:59 -04002402// Uri Decoding from DLIB
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002403// http://dlib.net/dlib/server/server_http.cpp.html
Alexander Woode4bc6c72021-10-14 08:54:59 -04002404// --- dlib begin ------------------------------------------------------------
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002405// Copyright (C) 2003 Davis E. King (davis@dlib.net)
Alexander Woode4bc6c72021-10-14 08:54:59 -04002406// License: Boost Software License
2407// Boost Software License - Version 1.0 - August 17th, 2003
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002408
Alexander Woode4bc6c72021-10-14 08:54:59 -04002409// Permission is hereby granted, free of charge, to any person or organization
2410// obtaining a copy of the software and accompanying documentation covered by
2411// this license (the "Software") to use, reproduce, display, distribute,
2412// execute, and transmit the Software, and to prepare derivative works of the
2413// Software, and to permit third-parties to whom the Software is furnished to
2414// do so, all subject to the following:
2415// The copyright notices in the Software and this entire statement, including
2416// the above license grant, this restriction and the following disclaimer,
2417// must be included in all copies of the Software, in whole or in part, and
2418// all derivative works of the Software, unless such copies or derivative
2419// works are solely in the form of machine-executable object code generated by
2420// a source language processor.
2421// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2422// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2423// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
2424// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
2425// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
2426// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2427// DEALINGS IN THE SOFTWARE.
Syoyo Fujitacbd38852022-02-26 21:19:15 +09002428//
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002429namespace dlib {
2430
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002431inline unsigned char from_hex(unsigned char ch) {
2432 if (ch <= '9' && ch >= '0')
2433 ch -= '0';
2434 else if (ch <= 'f' && ch >= 'a')
2435 ch -= 'a' - 10;
2436 else if (ch <= 'F' && ch >= 'A')
2437 ch -= 'A' - 10;
2438 else
2439 ch = 0;
2440 return ch;
2441}
2442
2443static const std::string urldecode(const std::string &str) {
2444 using namespace std;
2445 string result;
2446 string::size_type i;
2447 for (i = 0; i < str.size(); ++i) {
2448 if (str[i] == '+') {
2449 result += ' ';
2450 } else if (str[i] == '%' && str.size() > i + 2) {
2451 const unsigned char ch1 =
2452 from_hex(static_cast<unsigned char>(str[i + 1]));
2453 const unsigned char ch2 =
2454 from_hex(static_cast<unsigned char>(str[i + 2]));
2455 const unsigned char ch = static_cast<unsigned char>((ch1 << 4) | ch2);
2456 result += static_cast<char>(ch);
2457 i += 2;
2458 } else {
2459 result += str[i];
2460 }
2461 }
2462 return result;
2463}
2464
2465} // namespace dlib
2466// --- dlib end --------------------------------------------------------------
2467
Pyarelal Knowles385946d2023-01-03 18:18:04 -08002468bool URIDecode(const std::string &in_uri, std::string *out_uri,
2469 void *user_data) {
2470 (void)user_data;
2471 *out_uri = dlib::urldecode(in_uri);
2472 return true;
2473}
2474
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002475static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002476 std::string *warn, const std::string &filename,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002477 const std::string &basedir, bool required,
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02002478 size_t reqBytes, bool checkSize,
2479 size_t maxFileSize, FsCallbacks *fs) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002480 if (fs == nullptr || fs->FileExists == nullptr ||
2481 fs->ExpandFilePath == nullptr || fs->ReadWholeFile == nullptr) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002482 // This is a developer error, assert() ?
2483 if (err) {
2484 (*err) += "FS callback[s] not set\n";
2485 }
2486 return false;
2487 }
2488
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002489 std::string *failMsgOut = required ? err : warn;
Selmar Koke3b3fa92018-08-22 19:04:21 +02002490
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002491 out->clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002492
2493 std::vector<std::string> paths;
2494 paths.push_back(basedir);
2495 paths.push_back(".");
2496
Paolo Jovone6601bf2018-07-07 20:43:33 +02002497 std::string filepath = FindFile(paths, filename, fs);
jianghaosenb721fb72017-08-25 21:01:17 +08002498 if (filepath.empty() || filename.empty()) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002499 if (failMsgOut) {
2500 (*failMsgOut) += "File not found : " + filename + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002501 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002502 return false;
2503 }
2504
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002505 // Check file size
2506 if (fs->GetFileSizeInBytes) {
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002507 size_t file_size{0};
2508 std::string _err;
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02002509 bool ok =
2510 fs->GetFileSizeInBytes(&file_size, &_err, filepath, fs->user_data);
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002511 if (!ok) {
2512 if (_err.size()) {
2513 if (failMsgOut) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02002514 (*failMsgOut) += "Getting file size failed : " + filename +
2515 ", err = " + _err + "\n";
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002516 }
2517 }
2518 return false;
2519 }
2520
2521 if (file_size > maxFileSize) {
2522 if (failMsgOut) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02002523 (*failMsgOut) += "File size " + std::to_string(file_size) +
2524 " exceeds maximum allowed file size " +
2525 std::to_string(maxFileSize) + " : " + filepath + "\n";
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002526 }
2527 return false;
2528 }
2529 }
2530
Paolo Jovone6601bf2018-07-07 20:43:33 +02002531 std::vector<unsigned char> buf;
2532 std::string fileReadErr;
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002533 bool fileRead =
2534 fs->ReadWholeFile(&buf, &fileReadErr, filepath, fs->user_data);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002535 if (!fileRead) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002536 if (failMsgOut) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002537 (*failMsgOut) +=
2538 "File read error : " + filepath + " : " + fileReadErr + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002539 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002540 return false;
2541 }
2542
Paolo Jovone6601bf2018-07-07 20:43:33 +02002543 size_t sz = buf.size();
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09002544 if (sz == 0) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002545 if (failMsgOut) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002546 (*failMsgOut) += "File is empty : " + filepath + "\n";
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002547 }
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09002548 return false;
2549 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002550
2551 if (checkSize) {
2552 if (reqBytes == sz) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002553 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002554 return true;
2555 } else {
2556 std::stringstream ss;
2557 ss << "File size mismatch : " << filepath << ", requestedBytes "
2558 << reqBytes << ", but got " << sz << std::endl;
Selmar Koke3b3fa92018-08-22 19:04:21 +02002559 if (failMsgOut) {
2560 (*failMsgOut) += ss.str();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002561 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002562 return false;
2563 }
2564 }
2565
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002566 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002567 return true;
2568}
2569
Nyall Dawson8c85d5e2023-08-28 12:56:09 +10002570void TinyGLTF::SetParseStrictness(ParseStrictness strictness) {
2571 strictness_ = strictness;
2572}
2573
Squareysff644d82018-03-13 22:36:18 +01002574void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002575 LoadImageData = func;
2576 load_image_user_data_ = user_data;
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002577 user_image_loader_ = true;
2578}
2579
2580void TinyGLTF::RemoveImageLoader() {
2581 LoadImageData =
2582#ifndef TINYGLTF_NO_STB_IMAGE
2583 &tinygltf::LoadImageData;
2584#else
2585 nullptr;
2586#endif
2587
2588 load_image_user_data_ = nullptr;
2589 user_image_loader_ = false;
Squareysff644d82018-03-13 22:36:18 +01002590}
2591
Squareys2d3594d2018-03-13 22:40:53 +01002592#ifndef TINYGLTF_NO_STB_IMAGE
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002593bool LoadImageData(Image *image, const int image_idx, std::string *err,
2594 std::string *warn, int req_width, int req_height,
2595 const unsigned char *bytes, int size, void *user_data) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002596 (void)warn;
2597
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002598 LoadImageDataOption option;
2599 if (user_data) {
2600 option = *reinterpret_cast<LoadImageDataOption *>(user_data);
2601 }
2602
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002603 int w = 0, h = 0, comp = 0, req_comp = 0;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002604
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002605 unsigned char *data = nullptr;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002606
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002607 // preserve_channels true: Use channels stored in the image file.
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09002608 // false: force 32-bit textures for common Vulkan compatibility. It appears
2609 // that some GPU drivers do not support 24-bit images for Vulkan
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002610 req_comp = option.preserve_channels ? 0 : 4;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002611 int bits = 8;
2612 int pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002613
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002614 // It is possible that the image we want to load is a 16bit per channel image
2615 // We are going to attempt to load it as 16bit per channel, and if it worked,
imallettd9ce9eb2022-10-07 10:37:09 -07002616 // set the image data accordingly. We are casting the returned pointer into
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002617 // unsigned char, because we are representing "bytes". But we are updating
2618 // the Image metadata to signal that this image uses 2 bytes (16bits) per
2619 // channel:
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002620 if (stbi_is_16_bit_from_memory(bytes, size)) {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002621 data = reinterpret_cast<unsigned char *>(
2622 stbi_load_16_from_memory(bytes, size, &w, &h, &comp, req_comp));
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002623 if (data) {
2624 bits = 16;
2625 pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT;
2626 }
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002627 }
2628
2629 // at this point, if data is still NULL, it means that the image wasn't
2630 // 16bit per channel, we are going to load it as a normal 8bit per channel
imallettd9ce9eb2022-10-07 10:37:09 -07002631 // image as we used to do:
Aurélien Chatelain5cb43462017-05-24 09:55:14 +00002632 // if image cannot be decoded, ignore parsing and keep it by its path
2633 // don't break in this case
Syoyo Fujita5b407452017-06-04 17:42:41 +09002634 // FIXME we should only enter this function if the image is embedded. If
2635 // image->uri references
2636 // an image file, it should be left as it is. Image loading should not be
2637 // mandatory (to support other formats)
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002638 if (!data) data = stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002639 if (!data) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002640 // NOTE: you can use `warn` instead of `err`
Syoyo Fujitabeded612016-05-01 20:03:43 +09002641 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002642 (*err) +=
2643 "Unknown image format. STB cannot decode image data for image[" +
2644 std::to_string(image_idx) + "] name = \"" + image->name + "\".\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002645 }
Omar C. Fd492efc2018-02-10 09:50:35 +02002646 return false;
Syoyo Fujitabeded612016-05-01 20:03:43 +09002647 }
2648
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002649 if ((w < 1) || (h < 1)) {
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002650 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002651 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002652 (*err) += "Invalid image data for image[" + std::to_string(image_idx) +
2653 "] name = \"" + image->name + "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002654 }
Omar C. Fd492efc2018-02-10 09:50:35 +02002655 return false;
Syoyo Fujitabeded612016-05-01 20:03:43 +09002656 }
2657
2658 if (req_width > 0) {
2659 if (req_width != w) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002660 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002661 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002662 (*err) += "Image width mismatch for image[" +
2663 std::to_string(image_idx) + "] name = \"" + image->name +
2664 "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002665 }
2666 return false;
2667 }
2668 }
2669
2670 if (req_height > 0) {
2671 if (req_height != h) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002672 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002673 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002674 (*err) += "Image height mismatch. for image[" +
2675 std::to_string(image_idx) + "] name = \"" + image->name +
2676 "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002677 }
2678 return false;
2679 }
2680 }
2681
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002682 if (req_comp != 0) {
2683 // loaded data has `req_comp` channels(components)
2684 comp = req_comp;
2685 }
2686
Syoyo Fujitabeded612016-05-01 20:03:43 +09002687 image->width = w;
2688 image->height = h;
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002689 image->component = comp;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002690 image->bits = bits;
2691 image->pixel_type = pixel_type;
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002692 image->image.resize(static_cast<size_t>(w * h * comp) * size_t(bits / 8));
2693 std::copy(data, data + w * h * comp * (bits / 8), image->image.begin());
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002694 stbi_image_free(data);
Syoyo Fujita4be2f882016-11-24 16:20:34 +09002695
Syoyo Fujitabeded612016-05-01 20:03:43 +09002696 return true;
2697}
Squareys2d3594d2018-03-13 22:40:53 +01002698#endif
Syoyo Fujitabeded612016-05-01 20:03:43 +09002699
johan bowald642a3432018-04-01 12:37:18 +02002700void TinyGLTF::SetImageWriter(WriteImageDataFunction func, void *user_data) {
2701 WriteImageData = func;
2702 write_image_user_data_ = user_data;
2703}
2704
2705#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
2706static void WriteToMemory_stbi(void *context, void *data, int size) {
2707 std::vector<unsigned char> *buffer =
2708 reinterpret_cast<std::vector<unsigned char> *>(context);
2709
2710 unsigned char *pData = reinterpret_cast<unsigned char *>(data);
2711
2712 buffer->insert(buffer->end(), pData, pData + size);
2713}
2714
2715bool WriteImageData(const std::string *basepath, const std::string *filename,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08002716 const Image *image, bool embedImages,
2717 const URICallbacks *uri_cb, std::string *out_uri,
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08002718 void *fsPtr) {
johan bowald642a3432018-04-01 12:37:18 +02002719 const std::string ext = GetFilePathExtension(*filename);
2720
Paolo Jovone6601bf2018-07-07 20:43:33 +02002721 // Write image to temporary buffer
2722 std::string header;
2723 std::vector<unsigned char> data;
johan bowald642a3432018-04-01 12:37:18 +02002724
Paolo Jovone6601bf2018-07-07 20:43:33 +02002725 if (ext == "png") {
Syoyo Fujitaca56f722019-03-07 21:04:25 +09002726 if ((image->bits != 8) ||
2727 (image->pixel_type != TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)) {
Syoyo Fujitae8a46c42019-03-07 20:50:58 +09002728 // Unsupported pixel format
2729 return false;
2730 }
2731
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002732 if (!stbi_write_png_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002733 image->height, image->component,
2734 &image->image[0], 0)) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002735 return false;
2736 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002737 header = "data:image/png;base64,";
2738 } else if (ext == "jpg") {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002739 if (!stbi_write_jpg_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002740 image->height, image->component,
2741 &image->image[0], 100)) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002742 return false;
2743 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002744 header = "data:image/jpeg;base64,";
2745 } else if (ext == "bmp") {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002746 if (!stbi_write_bmp_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002747 image->height, image->component,
2748 &image->image[0])) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002749 return false;
2750 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002751 header = "data:image/bmp;base64,";
2752 } else if (!embedImages) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002753 // Error: can't output requested format to file
2754 return false;
Paolo Jovone6601bf2018-07-07 20:43:33 +02002755 }
johan bowald642a3432018-04-01 12:37:18 +02002756
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002757 if (embedImages) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002758 // Embed base64-encoded image into URI
johan bowald642a3432018-04-01 12:37:18 +02002759 if (data.size()) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02002760 *out_uri = header + base64_encode(&data[0],
2761 static_cast<unsigned int>(data.size()));
johan bowald642a3432018-04-01 12:37:18 +02002762 } else {
2763 // Throw error?
2764 }
2765 } else {
2766 // Write image to disc
Paolo Jovone6601bf2018-07-07 20:43:33 +02002767 FsCallbacks *fs = reinterpret_cast<FsCallbacks *>(fsPtr);
Syoyo Fujita33514af2018-11-05 13:32:43 +09002768 if ((fs != nullptr) && (fs->WriteWholeFile != nullptr)) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002769 const std::string imagefilepath = JoinPath(*basepath, *filename);
2770 std::string writeError;
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002771 if (!fs->WriteWholeFile(&writeError, imagefilepath, data,
2772 fs->user_data)) {
2773 // Could not write image file to disc; Throw error ?
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002774 return false;
Paolo Jovone6601bf2018-07-07 20:43:33 +02002775 }
johan bowald642a3432018-04-01 12:37:18 +02002776 } else {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002777 // Throw error?
johan bowald642a3432018-04-01 12:37:18 +02002778 }
Pyarelal Knowles385946d2023-01-03 18:18:04 -08002779 if (uri_cb->encode) {
2780 if (!uri_cb->encode(*filename, "image", out_uri, uri_cb->user_data)) {
2781 return false;
2782 }
2783 } else {
2784 *out_uri = *filename;
2785 }
johan bowald642a3432018-04-01 12:37:18 +02002786 }
2787
2788 return true;
2789}
2790#endif
2791
Pyarelal Knowles385946d2023-01-03 18:18:04 -08002792void TinyGLTF::SetURICallbacks(URICallbacks callbacks) {
2793 assert(callbacks.decode);
2794 if (callbacks.decode) {
2795 uri_cb = callbacks;
2796 }
2797}
2798
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002799void TinyGLTF::SetFsCallbacks(FsCallbacks callbacks) { fs = callbacks; }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002800
Harokyangfb256602019-10-30 16:13:52 +08002801#ifdef _WIN32
2802static inline std::wstring UTF8ToWchar(const std::string &str) {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002803 int wstr_size =
2804 MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
imallett56e10982022-10-07 10:35:16 -07002805 std::wstring wstr((size_t)wstr_size, 0);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002806 MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0],
2807 (int)wstr.size());
Harokyangfb256602019-10-30 16:13:52 +08002808 return wstr;
2809}
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002810
2811static inline std::string WcharToUTF8(const std::wstring &wstr) {
2812 int str_size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(),
imallett56e10982022-10-07 10:35:16 -07002813 nullptr, 0, nullptr, nullptr);
2814 std::string str((size_t)str_size, 0);
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002815 WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), &str[0],
imallett56e10982022-10-07 10:35:16 -07002816 (int)str.size(), nullptr, nullptr);
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002817 return str;
2818}
Harokyangfb256602019-10-30 16:13:52 +08002819#endif
2820
Paolo Jovone6601bf2018-07-07 20:43:33 +02002821#ifndef TINYGLTF_NO_FS
2822// Default implementations of filesystem functions
2823
2824bool FileExists(const std::string &abs_filename, void *) {
2825 bool ret;
Sascha Willems5f9cb242018-12-28 20:53:41 +01002826#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002827 if (asset_manager) {
2828 AAsset *asset = AAssetManager_open(asset_manager, abs_filename.c_str(),
2829 AASSET_MODE_STREAMING);
2830 if (!asset) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01002831 return false;
2832 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002833 AAsset_close(asset);
2834 ret = true;
2835 } else {
2836 return false;
2837 }
Sascha Willems5f9cb242018-12-28 20:53:41 +01002838#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002839#ifdef _WIN32
R.E. Kovalev112e3532023-07-05 09:11:57 +03002840#if defined(_MSC_VER) || defined(_LIBCPP_VERSION)
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002841
2842 // First check if a file is a directory.
2843 DWORD result = GetFileAttributesW(UTF8ToWchar(abs_filename).c_str());
2844 if (result == INVALID_FILE_ATTRIBUTES) {
2845 return false;
2846 }
2847 if (result & FILE_ATTRIBUTE_DIRECTORY) {
2848 return false;
2849 }
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002850
Syoyo Fujita45cac782019-11-09 20:42:55 +09002851 FILE *fp = nullptr;
Harokyangfb256602019-10-30 16:13:52 +08002852 errno_t err = _wfopen_s(&fp, UTF8ToWchar(abs_filename).c_str(), L"rb");
Paolo Jovone6601bf2018-07-07 20:43:33 +02002853 if (err != 0) {
2854 return false;
2855 }
R.E. Kovalev112e3532023-07-05 09:11:57 +03002856#elif defined(__GLIBCXX__)
2857 FILE *fp = fopen(abs_filename.c_str(), "rb");
2858 if (!fp) {
2859 return false;
2860 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002861#else
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002862 // TODO: is_directory check
Syoyo Fujita45cac782019-11-09 20:42:55 +09002863 FILE *fp = nullptr;
2864 errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb");
2865 if (err != 0) {
2866 return false;
2867 }
2868#endif
2869
2870#else
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002871 struct stat sb;
2872 if (stat(abs_filename.c_str(), &sb)) {
2873 return false;
2874 }
2875 if (S_ISDIR(sb.st_mode)) {
2876 return false;
2877 }
2878
Paolo Jovone6601bf2018-07-07 20:43:33 +02002879 FILE *fp = fopen(abs_filename.c_str(), "rb");
2880#endif
2881 if (fp) {
2882 ret = true;
2883 fclose(fp);
2884 } else {
2885 ret = false;
2886 }
Sascha Willems5f9cb242018-12-28 20:53:41 +01002887#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002888
2889 return ret;
2890}
2891
2892std::string ExpandFilePath(const std::string &filepath, void *) {
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002893 // https://github.com/syoyo/tinygltf/issues/368
2894 //
2895 // No file path expansion in built-in FS function anymore, since glTF URI
2896 // should not contain tilde('~') and environment variables, and for security
2897 // reason(`wordexp`).
2898 //
2899 // Users need to supply `base_dir`(in `LoadASCIIFromString`,
2900 // `LoadBinaryFromMemory`) in expanded absolute path.
2901
2902 return filepath;
2903
2904#if 0
Paolo Jovone6601bf2018-07-07 20:43:33 +02002905#ifdef _WIN32
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002906 // Assume input `filepath` is encoded in UTF-8
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002907 std::wstring wfilepath = UTF8ToWchar(filepath);
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002908 DWORD wlen = ExpandEnvironmentStringsW(wfilepath.c_str(), nullptr, 0);
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002909 wchar_t *wstr = new wchar_t[wlen];
2910 ExpandEnvironmentStringsW(wfilepath.c_str(), wstr, wlen);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002911
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002912 std::wstring ws(wstr);
2913 delete[] wstr;
2914 return WcharToUTF8(ws);
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002915
Paolo Jovone6601bf2018-07-07 20:43:33 +02002916#else
2917
2918#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \
Julian Smith0598a202021-08-25 12:06:08 +01002919 defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__OpenBSD__)
Paolo Jovone6601bf2018-07-07 20:43:33 +02002920 // no expansion
2921 std::string s = filepath;
2922#else
2923 std::string s;
2924 wordexp_t p;
2925
2926 if (filepath.empty()) {
2927 return "";
2928 }
2929
Frank Galliganaa3c5a12020-01-13 15:06:56 -08002930 // Quote the string to keep any spaces in filepath intact.
2931 std::string quoted_path = "\"" + filepath + "\"";
Paolo Jovone6601bf2018-07-07 20:43:33 +02002932 // char** w;
Frank Galliganaa3c5a12020-01-13 15:06:56 -08002933 int ret = wordexp(quoted_path.c_str(), &p, 0);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002934 if (ret) {
2935 // err
2936 s = filepath;
2937 return s;
2938 }
2939
2940 // Use first element only.
2941 if (p.we_wordv) {
2942 s = std::string(p.we_wordv[0]);
2943 wordfree(&p);
2944 } else {
2945 s = filepath;
2946 }
2947
2948#endif
2949
2950 return s;
2951#endif
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002952#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002953}
2954
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002955bool GetFileSizeInBytes(size_t *filesize_out, std::string *err,
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02002956 const std::string &filepath, void *userdata) {
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002957 (void)userdata;
2958
2959#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
2960 if (asset_manager) {
2961 AAsset *asset = AAssetManager_open(asset_manager, filepath.c_str(),
2962 AASSET_MODE_STREAMING);
2963 if (!asset) {
2964 if (err) {
2965 (*err) += "File open error : " + filepath + "\n";
2966 }
2967 return false;
2968 }
2969 size_t size = AAsset_getLength(asset);
2970
2971 if (size == 0) {
2972 if (err) {
2973 (*err) += "Invalid file size : " + filepath +
2974 " (does the path point to a directory?)";
2975 }
2976 return false;
2977 }
2978
2979 return true;
2980 } else {
2981 if (err) {
2982 (*err) += "No asset manager specified : " + filepath + "\n";
2983 }
2984 return false;
2985 }
2986#else
2987#ifdef _WIN32
2988#if defined(__GLIBCXX__) // mingw
2989 int file_descriptor =
2990 _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY);
2991 __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
2992 std::istream f(&wfile_buf);
2993#elif defined(_MSC_VER) || defined(_LIBCPP_VERSION)
2994 // For libcxx, assume _LIBCPP_HAS_OPEN_WITH_WCHAR is defined to accept
2995 // `wchar_t *`
2996 std::ifstream f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary);
2997#else
2998 // Unknown compiler/runtime
2999 std::ifstream f(filepath.c_str(), std::ifstream::binary);
3000#endif
3001#else
3002 std::ifstream f(filepath.c_str(), std::ifstream::binary);
3003#endif
3004 if (!f) {
3005 if (err) {
3006 (*err) += "File open error : " + filepath + "\n";
3007 }
3008 return false;
3009 }
3010
3011 // For directory(and pipe?), peek() will fail(Posix gnustl/libc++ only)
David Siegel47208b22023-06-05 22:14:30 +02003012 f.peek();
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09003013 if (!f) {
3014 if (err) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02003015 (*err) +=
3016 "File read error. Maybe empty file or invalid file : " + filepath +
3017 "\n";
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09003018 }
3019 return false;
3020 }
3021
3022 f.seekg(0, f.end);
3023 size_t sz = static_cast<size_t>(f.tellg());
3024
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02003025 // std::cout << "sz = " << sz << "\n";
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09003026 f.seekg(0, f.beg);
3027
3028 if (int64_t(sz) < 0) {
3029 if (err) {
3030 (*err) += "Invalid file size : " + filepath +
3031 " (does the path point to a directory?)";
3032 }
3033 return false;
3034 } else if (sz == 0) {
3035 if (err) {
3036 (*err) += "File is empty : " + filepath + "\n";
3037 }
3038 return false;
3039 } else if (sz >= (std::numeric_limits<std::streamoff>::max)()) {
3040 if (err) {
3041 (*err) += "Invalid file size : " + filepath + "\n";
3042 }
3043 return false;
3044 }
3045
3046 (*filesize_out) = sz;
3047 return true;
3048#endif
3049}
3050
Paolo Jovone6601bf2018-07-07 20:43:33 +02003051bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
3052 const std::string &filepath, void *) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01003053#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
3054 if (asset_manager) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003055 AAsset *asset = AAssetManager_open(asset_manager, filepath.c_str(),
3056 AASSET_MODE_STREAMING);
Sascha Willems5f9cb242018-12-28 20:53:41 +01003057 if (!asset) {
3058 if (err) {
3059 (*err) += "File open error : " + filepath + "\n";
3060 }
3061 return false;
3062 }
3063 size_t size = AAsset_getLength(asset);
Syoyo Fujita73c4cce2020-04-28 01:06:34 +09003064 if (size == 0) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01003065 if (err) {
3066 (*err) += "Invalid file size : " + filepath +
3067 " (does the path point to a directory?)";
3068 }
Syoyo Fujita73c4cce2020-04-28 01:06:34 +09003069 return false;
Sascha Willems5f9cb242018-12-28 20:53:41 +01003070 }
3071 out->resize(size);
3072 AAsset_read(asset, reinterpret_cast<char *>(&out->at(0)), size);
3073 AAsset_close(asset);
3074 return true;
3075 } else {
3076 if (err) {
3077 (*err) += "No asset manager specified : " + filepath + "\n";
3078 }
3079 return false;
3080 }
3081#else
Harokyang5cecef22019-10-30 15:16:46 +08003082#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09003083#if defined(__GLIBCXX__) // mingw
3084 int file_descriptor =
3085 _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY);
Syoyo Fujita45cac782019-11-09 20:42:55 +09003086 __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
3087 std::istream f(&wfile_buf);
Syoyo Fujitaaba57bb2020-05-18 20:50:45 +09003088#elif defined(_MSC_VER) || defined(_LIBCPP_VERSION)
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04003089 // For libcxx, assume _LIBCPP_HAS_OPEN_WITH_WCHAR is defined to accept
3090 // `wchar_t *`
Harokyangfb256602019-10-30 16:13:52 +08003091 std::ifstream f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary);
Syoyo Fujitaaba57bb2020-05-18 20:50:45 +09003092#else
3093 // Unknown compiler/runtime
Syoyo Fujita45cac782019-11-09 20:42:55 +09003094 std::ifstream f(filepath.c_str(), std::ifstream::binary);
3095#endif
Harokyang5cecef22019-10-30 15:16:46 +08003096#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02003097 std::ifstream f(filepath.c_str(), std::ifstream::binary);
Harokyang5cecef22019-10-30 15:16:46 +08003098#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02003099 if (!f) {
3100 if (err) {
3101 (*err) += "File open error : " + filepath + "\n";
3102 }
3103 return false;
3104 }
3105
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09003106 // For directory(and pipe?), peek() will fail(Posix gnustl/libc++ only)
David Siegel47208b22023-06-05 22:14:30 +02003107 f.peek();
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09003108 if (!f) {
3109 if (err) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02003110 (*err) +=
3111 "File read error. Maybe empty file or invalid file : " + filepath +
3112 "\n";
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09003113 }
3114 return false;
3115 }
3116
Paolo Jovone6601bf2018-07-07 20:43:33 +02003117 f.seekg(0, f.end);
3118 size_t sz = static_cast<size_t>(f.tellg());
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09003119
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02003120 // std::cout << "sz = " << sz << "\n";
Paolo Jovone6601bf2018-07-07 20:43:33 +02003121 f.seekg(0, f.beg);
3122
Syoyo Fujitae8862472019-10-20 17:47:50 +09003123 if (int64_t(sz) < 0) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02003124 if (err) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003125 (*err) += "Invalid file size : " + filepath +
3126 " (does the path point to a directory?)";
Paolo Jovone6601bf2018-07-07 20:43:33 +02003127 }
3128 return false;
3129 } else if (sz == 0) {
3130 if (err) {
3131 (*err) += "File is empty : " + filepath + "\n";
3132 }
3133 return false;
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09003134 } else if (sz >= (std::numeric_limits<std::streamoff>::max)()) {
3135 if (err) {
3136 (*err) += "Invalid file size : " + filepath + "\n";
3137 }
3138 return false;
Paolo Jovone6601bf2018-07-07 20:43:33 +02003139 }
3140
3141 out->resize(sz);
3142 f.read(reinterpret_cast<char *>(&out->at(0)),
3143 static_cast<std::streamsize>(sz));
Paolo Jovone6601bf2018-07-07 20:43:33 +02003144
3145 return true;
Sascha Willems5f9cb242018-12-28 20:53:41 +01003146#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02003147}
3148
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003149bool WriteWholeFile(std::string *err, const std::string &filepath,
3150 const std::vector<unsigned char> &contents, void *) {
Harokyangfb256602019-10-30 16:13:52 +08003151#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09003152#if defined(__GLIBCXX__) // mingw
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09003153 int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(),
3154 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
3155 __gnu_cxx::stdio_filebuf<char> wfile_buf(
3156 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09003157 std::ostream f(&wfile_buf);
3158#elif defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08003159 std::ofstream f(UTF8ToWchar(filepath).c_str(), std::ofstream::binary);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09003160#else // clang?
Syoyo Fujita45cac782019-11-09 20:42:55 +09003161 std::ofstream f(filepath.c_str(), std::ofstream::binary);
3162#endif
Harokyangfb256602019-10-30 16:13:52 +08003163#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02003164 std::ofstream f(filepath.c_str(), std::ofstream::binary);
Harokyangfb256602019-10-30 16:13:52 +08003165#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02003166 if (!f) {
3167 if (err) {
3168 (*err) += "File open error for writing : " + filepath + "\n";
3169 }
3170 return false;
3171 }
3172
3173 f.write(reinterpret_cast<const char *>(&contents.at(0)),
3174 static_cast<std::streamsize>(contents.size()));
3175 if (!f) {
3176 if (err) {
3177 (*err) += "File write error: " + filepath + "\n";
3178 }
3179 return false;
3180 }
3181
Paolo Jovone6601bf2018-07-07 20:43:33 +02003182 return true;
3183}
3184
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003185#endif // TINYGLTF_NO_FS
Paolo Jovone6601bf2018-07-07 20:43:33 +02003186
johan bowald642a3432018-04-01 12:37:18 +02003187static std::string MimeToExt(const std::string &mimeType) {
3188 if (mimeType == "image/jpeg") {
3189 return "jpg";
3190 } else if (mimeType == "image/png") {
3191 return "png";
3192 } else if (mimeType == "image/bmp") {
3193 return "bmp";
3194 } else if (mimeType == "image/gif") {
3195 return "gif";
3196 }
3197
3198 return "";
3199}
3200
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08003201static bool UpdateImageObject(const Image &image, std::string &baseDir,
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08003202 int index, bool embedImages,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08003203 const URICallbacks *uri_cb,
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08003204 WriteImageDataFunction *WriteImageData,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08003205 void *user_data, std::string *out_uri) {
johan bowald642a3432018-04-01 12:37:18 +02003206 std::string filename;
3207 std::string ext;
imallettd9ce9eb2022-10-07 10:37:09 -07003208 // If image has uri, use it as a filename
johan bowald642a3432018-04-01 12:37:18 +02003209 if (image.uri.size()) {
Pyarelal Knowles385946d2023-01-03 18:18:04 -08003210 std::string decoded_uri;
3211 if (!uri_cb->decode(image.uri, &decoded_uri, uri_cb->user_data)) {
3212 // A decode failure results in a failure to write the gltf.
3213 return false;
3214 }
3215 filename = GetBaseFilename(decoded_uri);
johan bowald642a3432018-04-01 12:37:18 +02003216 ext = GetFilePathExtension(filename);
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09003217 } else if (image.bufferView != -1) {
3218 // If there's no URI and the data exists in a buffer,
3219 // don't change properties or write images
johan bowald642a3432018-04-01 12:37:18 +02003220 } else if (image.name.size()) {
3221 ext = MimeToExt(image.mimeType);
3222 // Otherwise use name as filename
3223 filename = image.name + "." + ext;
3224 } else {
3225 ext = MimeToExt(image.mimeType);
3226 // Fallback to index of image as filename
3227 filename = std::to_string(index) + "." + ext;
3228 }
3229
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08003230 // If callback is set and image data exists, modify image data object. If
3231 // image data does not exist, this is not considered a failure and the
3232 // original uri should be maintained.
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08003233 bool imageWritten = false;
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08003234 if (*WriteImageData != nullptr && !filename.empty() && !image.image.empty()) {
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08003235 imageWritten = (*WriteImageData)(&baseDir, &filename, &image, embedImages,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08003236 uri_cb, out_uri, user_data);
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08003237 if (!imageWritten) {
3238 return false;
3239 }
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08003240 }
3241
3242 // Use the original uri if the image was not written.
3243 if (!imageWritten) {
3244 *out_uri = image.uri;
johan bowald642a3432018-04-01 12:37:18 +02003245 }
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08003246
3247 return true;
johan bowald642a3432018-04-01 12:37:18 +02003248}
3249
Selmar Kok0d0e97e2018-08-22 14:01:57 +02003250bool IsDataURI(const std::string &in) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003251 std::string header = "data:application/octet-stream;base64,";
3252 if (in.find(header) == 0) {
3253 return true;
3254 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09003255
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02003256 header = "data:image/jpeg;base64,";
3257 if (in.find(header) == 0) {
3258 return true;
3259 }
Squareys43374632018-03-13 22:20:48 +01003260
Syoyo Fujita620eed12016-01-02 23:37:12 +09003261 header = "data:image/png;base64,";
3262 if (in.find(header) == 0) {
3263 return true;
3264 }
Squareys43374632018-03-13 22:20:48 +01003265
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02003266 header = "data:image/bmp;base64,";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003267 if (in.find(header) == 0) {
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02003268 return true;
3269 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09003270
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02003271 header = "data:image/gif;base64,";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003272 if (in.find(header) == 0) {
Syoyo Fujita620eed12016-01-02 23:37:12 +09003273 return true;
3274 }
3275
Luke San Antoniocaa24b02016-06-15 02:23:09 -04003276 header = "data:text/plain;base64,";
3277 if (in.find(header) == 0) {
3278 return true;
3279 }
3280
Syoyo Fujita20244e12018-03-15 11:01:05 -05003281 header = "data:application/gltf-buffer;base64,";
3282 if (in.find(header) == 0) {
3283 return true;
3284 }
3285
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003286 return false;
3287}
3288
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09003289bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
3290 const std::string &in, size_t reqBytes, bool checkSize) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003291 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujita620eed12016-01-02 23:37:12 +09003292 std::string data;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003293 if (in.find(header) == 0) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09003294 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003295 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09003296
3297 if (data.empty()) {
3298 header = "data:image/jpeg;base64,";
3299 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02003300 mime_type = "image/jpeg";
Syoyo Fujita7c877972016-03-08 01:31:49 +09003301 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09003302 }
3303 }
3304
3305 if (data.empty()) {
3306 header = "data:image/png;base64,";
3307 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02003308 mime_type = "image/png";
Syoyo Fujita7c877972016-03-08 01:31:49 +09003309 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09003310 }
3311 }
Squareys43374632018-03-13 22:20:48 +01003312
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02003313 if (data.empty()) {
3314 header = "data:image/bmp;base64,";
3315 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02003316 mime_type = "image/bmp";
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02003317 data = base64_decode(in.substr(header.size())); // cut mime string.
3318 }
3319 }
3320
3321 if (data.empty()) {
3322 header = "data:image/gif;base64,";
3323 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02003324 mime_type = "image/gif";
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02003325 data = base64_decode(in.substr(header.size())); // cut mime string.
3326 }
3327 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09003328
3329 if (data.empty()) {
Luke San Antoniocaa24b02016-06-15 02:23:09 -04003330 header = "data:text/plain;base64,";
3331 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02003332 mime_type = "text/plain";
Luke San Antoniocaa24b02016-06-15 02:23:09 -04003333 data = base64_decode(in.substr(header.size()));
3334 }
3335 }
3336
3337 if (data.empty()) {
Syoyo Fujita20244e12018-03-15 11:01:05 -05003338 header = "data:application/gltf-buffer;base64,";
3339 if (in.find(header) == 0) {
3340 data = base64_decode(in.substr(header.size()));
3341 }
3342 }
3343
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09003344 // TODO(syoyo): Allow empty buffer? #229
Syoyo Fujita20244e12018-03-15 11:01:05 -05003345 if (data.empty()) {
Syoyo Fujita620eed12016-01-02 23:37:12 +09003346 return false;
Syoyo Fujitabde70212016-02-07 17:38:17 +09003347 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09003348
3349 if (checkSize) {
3350 if (data.size() != reqBytes) {
3351 return false;
3352 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003353 out->resize(reqBytes);
Syoyo Fujita620eed12016-01-02 23:37:12 +09003354 } else {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003355 out->resize(data.size());
Syoyo Fujita620eed12016-01-02 23:37:12 +09003356 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003357 std::copy(data.begin(), data.end(), out->begin());
Syoyo Fujita620eed12016-01-02 23:37:12 +09003358 return true;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003359}
3360
David1f9a4b92023-02-15 22:56:18 -06003361namespace detail {
David03ad33c2023-02-15 23:35:51 -06003362bool GetInt(const detail::json &o, int &val) {
jrkoonce5cecc412019-08-29 11:45:04 -05003363#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003364 if (!o.IsDouble()) {
3365 if (o.IsInt()) {
3366 val = o.GetInt();
3367 return true;
3368 } else if (o.IsUint()) {
3369 val = static_cast<int>(o.GetUint());
3370 return true;
3371 } else if (o.IsInt64()) {
3372 val = static_cast<int>(o.GetInt64());
3373 return true;
3374 } else if (o.IsUint64()) {
3375 val = static_cast<int>(o.GetUint64());
jrkooncecba5d6c2019-08-29 11:26:22 -05003376 return true;
3377 }
jrkoonce5cecc412019-08-29 11:45:04 -05003378 }
3379
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003380 return false;
jrkoonce5cecc412019-08-29 11:45:04 -05003381#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003382 auto type = o.type();
jrkooncecba5d6c2019-08-29 11:26:22 -05003383
David03ad33c2023-02-15 23:35:51 -06003384 if ((type == detail::json::value_t::number_integer) ||
3385 (type == detail::json::value_t::number_unsigned)) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003386 val = static_cast<int>(o.get<int64_t>());
3387 return true;
jrkoonce5cecc412019-08-29 11:45:04 -05003388 }
3389
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003390 return false;
jrkoonce5cecc412019-08-29 11:45:04 -05003391#endif
jrkooncecba5d6c2019-08-29 11:26:22 -05003392}
3393
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003394#ifdef TINYGLTF_USE_RAPIDJSON
David03ad33c2023-02-15 23:35:51 -06003395bool GetDouble(const detail::json &o, double &val) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003396 if (o.IsDouble()) {
3397 val = o.GetDouble();
3398 return true;
3399 }
3400
3401 return false;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003402}
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003403#endif
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003404
David03ad33c2023-02-15 23:35:51 -06003405bool GetNumber(const detail::json &o, double &val) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003406#ifdef TINYGLTF_USE_RAPIDJSON
3407 if (o.IsNumber()) {
3408 val = o.GetDouble();
3409 return true;
3410 }
3411
3412 return false;
3413#else
3414 if (o.is_number()) {
3415 val = o.get<double>();
3416 return true;
3417 }
3418
3419 return false;
3420#endif
3421}
3422
David03ad33c2023-02-15 23:35:51 -06003423bool GetString(const detail::json &o, std::string &val) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003424#ifdef TINYGLTF_USE_RAPIDJSON
3425 if (o.IsString()) {
3426 val = o.GetString();
3427 return true;
3428 }
3429
3430 return false;
3431#else
David03ad33c2023-02-15 23:35:51 -06003432 if (o.type() == detail::json::value_t::string) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003433 val = o.get<std::string>();
3434 return true;
3435 }
3436
3437 return false;
3438#endif
3439}
3440
David03ad33c2023-02-15 23:35:51 -06003441bool IsArray(const detail::json &o) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003442#ifdef TINYGLTF_USE_RAPIDJSON
3443 return o.IsArray();
3444#else
3445 return o.is_array();
3446#endif
3447}
3448
David03ad33c2023-02-15 23:35:51 -06003449detail::json_const_array_iterator ArrayBegin(const detail::json &o) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003450#ifdef TINYGLTF_USE_RAPIDJSON
3451 return o.Begin();
3452#else
3453 return o.begin();
3454#endif
3455}
3456
David03ad33c2023-02-15 23:35:51 -06003457detail::json_const_array_iterator ArrayEnd(const detail::json &o) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003458#ifdef TINYGLTF_USE_RAPIDJSON
3459 return o.End();
3460#else
3461 return o.end();
3462#endif
3463}
3464
David03ad33c2023-02-15 23:35:51 -06003465bool IsObject(const detail::json &o) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003466#ifdef TINYGLTF_USE_RAPIDJSON
3467 return o.IsObject();
3468#else
3469 return o.is_object();
3470#endif
3471}
3472
David03ad33c2023-02-15 23:35:51 -06003473detail::json_const_iterator ObjectBegin(const detail::json &o) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003474#ifdef TINYGLTF_USE_RAPIDJSON
3475 return o.MemberBegin();
3476#else
3477 return o.begin();
3478#endif
3479}
3480
David03ad33c2023-02-15 23:35:51 -06003481detail::json_const_iterator ObjectEnd(const detail::json &o) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003482#ifdef TINYGLTF_USE_RAPIDJSON
3483 return o.MemberEnd();
3484#else
3485 return o.end();
3486#endif
3487}
3488
Rahul Sheth01d54382020-07-10 14:27:37 -04003489// Making this a const char* results in a pointer to a temporary when
3490// TINYGLTF_USE_RAPIDJSON is off.
David03ad33c2023-02-15 23:35:51 -06003491std::string GetKey(detail::json_const_iterator &it) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003492#ifdef TINYGLTF_USE_RAPIDJSON
3493 return it->name.GetString();
3494#else
3495 return it.key().c_str();
3496#endif
3497}
3498
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02003499bool FindMember(const detail::json &o, const char *member,
3500 detail::json_const_iterator &it) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003501#ifdef TINYGLTF_USE_RAPIDJSON
3502 if (!o.IsObject()) {
3503 return false;
3504 }
3505 it = o.FindMember(member);
3506 return it != o.MemberEnd();
3507#else
3508 it = o.find(member);
3509 return it != o.end();
3510#endif
3511}
3512
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02003513bool FindMember(detail::json &o, const char *member,
3514 detail::json_iterator &it) {
David Siegelcfe64fb2023-06-07 15:18:38 +02003515#ifdef TINYGLTF_USE_RAPIDJSON
3516 if (!o.IsObject()) {
3517 return false;
3518 }
3519 it = o.FindMember(member);
3520 return it != o.MemberEnd();
3521#else
3522 it = o.find(member);
3523 return it != o.end();
3524#endif
3525}
3526
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02003527void Erase(detail::json &o, detail::json_iterator &it) {
David Siegelcfe64fb2023-06-07 15:18:38 +02003528#ifdef TINYGLTF_USE_RAPIDJSON
3529 o.EraseMember(it);
3530#else
3531 o.erase(it);
3532#endif
3533}
3534
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02003535bool IsEmpty(const detail::json &o) {
David Siegelcfe64fb2023-06-07 15:18:38 +02003536#ifdef TINYGLTF_USE_RAPIDJSON
David Siegela1a34cb2023-06-07 15:30:02 +02003537 return o.ObjectEmpty();
David Siegelcfe64fb2023-06-07 15:18:38 +02003538#else
3539 return o.empty();
3540#endif
3541}
3542
David03ad33c2023-02-15 23:35:51 -06003543const detail::json &GetValue(detail::json_const_iterator &it) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003544#ifdef TINYGLTF_USE_RAPIDJSON
3545 return it->value;
3546#else
3547 return it.value();
3548#endif
3549}
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003550
David Siegelcfe64fb2023-06-07 15:18:38 +02003551detail::json &GetValue(detail::json_iterator &it) {
3552#ifdef TINYGLTF_USE_RAPIDJSON
3553 return it->value;
3554#else
3555 return it.value();
3556#endif
3557}
3558
David03ad33c2023-02-15 23:35:51 -06003559std::string JsonToString(const detail::json &o, int spacing = -1) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003560#ifdef TINYGLTF_USE_RAPIDJSON
3561 using namespace rapidjson;
3562 StringBuffer buffer;
3563 if (spacing == -1) {
3564 Writer<StringBuffer> writer(buffer);
Syoyo Fujita544969b2022-07-13 19:03:33 +09003565 // TODO: Better error handling.
3566 // https://github.com/syoyo/tinygltf/issues/332
3567 if (!o.Accept(writer)) {
3568 return "tiny_gltf::JsonToString() failed rapidjson conversion";
3569 }
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003570 } else {
3571 PrettyWriter<StringBuffer> writer(buffer);
3572 writer.SetIndent(' ', uint32_t(spacing));
Syoyo Fujita544969b2022-07-13 19:03:33 +09003573 if (!o.Accept(writer)) {
3574 return "tiny_gltf::JsonToString() failed rapidjson conversion";
3575 }
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003576 }
3577 return buffer.GetString();
3578#else
3579 return o.dump(spacing);
3580#endif
3581}
3582
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02003583} // namespace detail
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003584
David03ad33c2023-02-15 23:35:51 -06003585static bool ParseJsonAsValue(Value *ret, const detail::json &o) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003586 Value val{};
jrkooncecba5d6c2019-08-29 11:26:22 -05003587#ifdef TINYGLTF_USE_RAPIDJSON
3588 using rapidjson::Type;
3589 switch (o.GetType()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003590 case Type::kObjectType: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003591 Value::Object value_object;
jrkooncecba5d6c2019-08-29 11:26:22 -05003592 for (auto it = o.MemberBegin(); it != o.MemberEnd(); ++it) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003593 Value entry;
jrkooncecba5d6c2019-08-29 11:26:22 -05003594 ParseJsonAsValue(&entry, it->value);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003595 if (entry.Type() != NULL_TYPE)
David1f9a4b92023-02-15 22:56:18 -06003596 value_object.emplace(detail::GetKey(it), std::move(entry));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003597 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003598 if (value_object.size() > 0) val = Value(std::move(value_object));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003599 } break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003600 case Type::kArrayType: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003601 Value::Array value_array;
jrkoonce5cecc412019-08-29 11:45:04 -05003602 value_array.reserve(o.Size());
jrkooncecba5d6c2019-08-29 11:26:22 -05003603 for (auto it = o.Begin(); it != o.End(); ++it) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003604 Value entry;
jrkooncecba5d6c2019-08-29 11:26:22 -05003605 ParseJsonAsValue(&entry, *it);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003606 if (entry.Type() != NULL_TYPE)
3607 value_array.emplace_back(std::move(entry));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003608 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003609 if (value_array.size() > 0) val = Value(std::move(value_array));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003610 } break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003611 case Type::kStringType:
3612 val = Value(std::string(o.GetString()));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003613 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003614 case Type::kFalseType:
3615 case Type::kTrueType:
3616 val = Value(o.GetBool());
Selmar Kokfa7022f2018-04-04 18:10:20 +02003617 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003618 case Type::kNumberType:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003619 if (!o.IsDouble()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003620 int i = 0;
David1f9a4b92023-02-15 22:56:18 -06003621 detail::GetInt(o, i);
jrkooncecba5d6c2019-08-29 11:26:22 -05003622 val = Value(i);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003623 } else {
jrkooncecba5d6c2019-08-29 11:26:22 -05003624 double d = 0.0;
David38318022023-02-15 23:21:09 -06003625 detail::GetDouble(o, d);
jrkooncecba5d6c2019-08-29 11:26:22 -05003626 val = Value(d);
3627 }
Selmar Kokfa7022f2018-04-04 18:10:20 +02003628 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003629 case Type::kNullType:
Selmar Kokfa7022f2018-04-04 18:10:20 +02003630 break;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003631 // all types are covered, so no `case default`
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003632 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003633#else
3634 switch (o.type()) {
David03ad33c2023-02-15 23:35:51 -06003635 case detail::json::value_t::object: {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003636 Value::Object value_object;
3637 for (auto it = o.begin(); it != o.end(); it++) {
3638 Value entry;
3639 ParseJsonAsValue(&entry, it.value());
3640 if (entry.Type() != NULL_TYPE)
3641 value_object.emplace(it.key(), std::move(entry));
3642 }
3643 if (value_object.size() > 0) val = Value(std::move(value_object));
3644 } break;
David03ad33c2023-02-15 23:35:51 -06003645 case detail::json::value_t::array: {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003646 Value::Array value_array;
3647 value_array.reserve(o.size());
3648 for (auto it = o.begin(); it != o.end(); it++) {
3649 Value entry;
3650 ParseJsonAsValue(&entry, it.value());
3651 if (entry.Type() != NULL_TYPE)
3652 value_array.emplace_back(std::move(entry));
3653 }
3654 if (value_array.size() > 0) val = Value(std::move(value_array));
3655 } break;
David03ad33c2023-02-15 23:35:51 -06003656 case detail::json::value_t::string:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003657 val = Value(o.get<std::string>());
3658 break;
David03ad33c2023-02-15 23:35:51 -06003659 case detail::json::value_t::boolean:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003660 val = Value(o.get<bool>());
3661 break;
David03ad33c2023-02-15 23:35:51 -06003662 case detail::json::value_t::number_integer:
3663 case detail::json::value_t::number_unsigned:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003664 val = Value(static_cast<int>(o.get<int64_t>()));
3665 break;
David03ad33c2023-02-15 23:35:51 -06003666 case detail::json::value_t::number_float:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003667 val = Value(o.get<double>());
3668 break;
David03ad33c2023-02-15 23:35:51 -06003669 case detail::json::value_t::null:
3670 case detail::json::value_t::discarded:
3671 case detail::json::value_t::binary:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003672 // default:
3673 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003674 }
3675#endif
Marcin Kacprzakf4f5c3c2022-09-02 16:15:54 +02003676 const bool isNotNull = val.Type() != NULL_TYPE;
3677
jrkooncecba5d6c2019-08-29 11:26:22 -05003678 if (ret) *ret = std::move(val);
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003679
Marcin Kacprzakf4f5c3c2022-09-02 16:15:54 +02003680 return isNotNull;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003681}
3682
David03ad33c2023-02-15 23:35:51 -06003683static bool ParseExtrasProperty(Value *ret, const detail::json &o) {
3684 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06003685 if (!detail::FindMember(o, "extras", it)) {
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003686 return false;
3687 }
3688
David1f9a4b92023-02-15 22:56:18 -06003689 return ParseJsonAsValue(ret, detail::GetValue(it));
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003690}
3691
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02003692static bool ParseBooleanProperty(bool *ret, std::string *err,
3693 const detail::json &o,
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09003694 const std::string &property,
3695 const bool required,
3696 const std::string &parent_node = "") {
David03ad33c2023-02-15 23:35:51 -06003697 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06003698 if (!detail::FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003699 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003700 if (err) {
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09003701 (*err) += "'" + property + "' property is missing";
3702 if (!parent_node.empty()) {
3703 (*err) += " in " + parent_node;
3704 }
3705 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003706 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003707 }
3708 return false;
3709 }
3710
David1f9a4b92023-02-15 22:56:18 -06003711 auto &value = detail::GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003712
3713 bool isBoolean;
jrkoonce0d2b6ef2019-09-04 13:46:45 -05003714 bool boolValue = false;
jrkooncecba5d6c2019-08-29 11:26:22 -05003715#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003716 isBoolean = value.IsBool();
3717 if (isBoolean) {
3718 boolValue = value.GetBool();
3719 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003720#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003721 isBoolean = value.is_boolean();
3722 if (isBoolean) {
3723 boolValue = value.get<bool>();
3724 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003725#endif
3726 if (!isBoolean) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003727 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003728 if (err) {
3729 (*err) += "'" + property + "' property is not a bool type.\n";
3730 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003731 }
3732 return false;
3733 }
3734
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003735 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003736 (*ret) = boolValue;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003737 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003738
3739 return true;
3740}
3741
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02003742static bool ParseIntegerProperty(int *ret, std::string *err,
3743 const detail::json &o,
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003744 const std::string &property,
3745 const bool required,
3746 const std::string &parent_node = "") {
David03ad33c2023-02-15 23:35:51 -06003747 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06003748 if (!detail::FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003749 if (required) {
3750 if (err) {
3751 (*err) += "'" + property + "' property is missing";
3752 if (!parent_node.empty()) {
3753 (*err) += " in " + parent_node;
3754 }
3755 (*err) += ".\n";
3756 }
3757 }
3758 return false;
3759 }
3760
jrkooncecba5d6c2019-08-29 11:26:22 -05003761 int intValue;
David1f9a4b92023-02-15 22:56:18 -06003762 bool isInt = detail::GetInt(detail::GetValue(it), intValue);
jrkooncecba5d6c2019-08-29 11:26:22 -05003763 if (!isInt) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003764 if (required) {
3765 if (err) {
3766 (*err) += "'" + property + "' property is not an integer type.\n";
3767 }
3768 }
3769 return false;
3770 }
3771
3772 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003773 (*ret) = intValue;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003774 }
3775
3776 return true;
3777}
3778
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02003779static bool ParseUnsignedProperty(size_t *ret, std::string *err,
3780 const detail::json &o,
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003781 const std::string &property,
3782 const bool required,
3783 const std::string &parent_node = "") {
David03ad33c2023-02-15 23:35:51 -06003784 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06003785 if (!detail::FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003786 if (required) {
3787 if (err) {
3788 (*err) += "'" + property + "' property is missing";
3789 if (!parent_node.empty()) {
3790 (*err) += " in " + parent_node;
3791 }
3792 (*err) += ".\n";
3793 }
3794 }
3795 return false;
3796 }
3797
David1f9a4b92023-02-15 22:56:18 -06003798 auto &value = detail::GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003799
jrkoonce0d2b6ef2019-09-04 13:46:45 -05003800 size_t uValue = 0;
jrkooncecba5d6c2019-08-29 11:26:22 -05003801 bool isUValue;
3802#ifdef TINYGLTF_USE_RAPIDJSON
3803 isUValue = false;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003804 if (value.IsUint()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003805 uValue = value.GetUint();
3806 isUValue = true;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003807 } else if (value.IsUint64()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003808 uValue = value.GetUint64();
3809 isUValue = true;
3810 }
3811#else
3812 isUValue = value.is_number_unsigned();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003813 if (isUValue) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003814 uValue = value.get<size_t>();
3815 }
3816#endif
3817 if (!isUValue) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003818 if (required) {
3819 if (err) {
3820 (*err) += "'" + property + "' property is not a positive integer.\n";
3821 }
3822 }
3823 return false;
3824 }
3825
3826 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003827 (*ret) = uValue;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003828 }
3829
3830 return true;
3831}
3832
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02003833static bool ParseNumberProperty(double *ret, std::string *err,
3834 const detail::json &o,
Syoyo Fujita5b407452017-06-04 17:42:41 +09003835 const std::string &property,
3836 const bool required,
3837 const std::string &parent_node = "") {
David03ad33c2023-02-15 23:35:51 -06003838 detail::json_const_iterator it;
jrkooncecba5d6c2019-08-29 11:26:22 -05003839
David1f9a4b92023-02-15 22:56:18 -06003840 if (!detail::FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003841 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003842 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003843 (*err) += "'" + property + "' property is missing";
3844 if (!parent_node.empty()) {
3845 (*err) += " in " + parent_node;
3846 }
3847 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003848 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003849 }
3850 return false;
3851 }
3852
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003853 double numberValue;
David1f9a4b92023-02-15 22:56:18 -06003854 bool isNumber = detail::GetNumber(detail::GetValue(it), numberValue);
jrkooncecba5d6c2019-08-29 11:26:22 -05003855
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003856 if (!isNumber) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003857 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003858 if (err) {
3859 (*err) += "'" + property + "' property is not a number type.\n";
3860 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003861 }
3862 return false;
3863 }
3864
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003865 if (ret) {
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003866 (*ret) = numberValue;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003867 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003868
3869 return true;
3870}
3871
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003872static bool ParseNumberArrayProperty(std::vector<double> *ret, std::string *err,
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02003873 const detail::json &o,
3874 const std::string &property, bool required,
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003875 const std::string &parent_node = "") {
David03ad33c2023-02-15 23:35:51 -06003876 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06003877 if (!detail::FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003878 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003879 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003880 (*err) += "'" + property + "' property is missing";
3881 if (!parent_node.empty()) {
3882 (*err) += " in " + parent_node;
3883 }
3884 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003885 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003886 }
3887 return false;
3888 }
3889
David1f9a4b92023-02-15 22:56:18 -06003890 if (!detail::IsArray(detail::GetValue(it))) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003891 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003892 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003893 (*err) += "'" + property + "' property is not an array";
3894 if (!parent_node.empty()) {
3895 (*err) += " in " + parent_node;
3896 }
3897 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003898 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003899 }
3900 return false;
3901 }
3902
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003903 ret->clear();
David1f9a4b92023-02-15 22:56:18 -06003904 auto end = detail::ArrayEnd(detail::GetValue(it));
3905 for (auto i = detail::ArrayBegin(detail::GetValue(it)); i != end; ++i) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003906 double numberValue;
David1f9a4b92023-02-15 22:56:18 -06003907 const bool isNumber = detail::GetNumber(*i, numberValue);
jrkooncecba5d6c2019-08-29 11:26:22 -05003908 if (!isNumber) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003909 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003910 if (err) {
3911 (*err) += "'" + property + "' property is not a number.\n";
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003912 if (!parent_node.empty()) {
3913 (*err) += " in " + parent_node;
3914 }
3915 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003916 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003917 }
3918 return false;
3919 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003920 ret->push_back(numberValue);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003921 }
3922
3923 return true;
3924}
3925
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003926static bool ParseIntegerArrayProperty(std::vector<int> *ret, std::string *err,
David03ad33c2023-02-15 23:35:51 -06003927 const detail::json &o,
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003928 const std::string &property,
3929 bool required,
3930 const std::string &parent_node = "") {
David03ad33c2023-02-15 23:35:51 -06003931 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06003932 if (!detail::FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003933 if (required) {
3934 if (err) {
3935 (*err) += "'" + property + "' property is missing";
3936 if (!parent_node.empty()) {
3937 (*err) += " in " + parent_node;
3938 }
3939 (*err) += ".\n";
3940 }
3941 }
3942 return false;
3943 }
3944
David1f9a4b92023-02-15 22:56:18 -06003945 if (!detail::IsArray(detail::GetValue(it))) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003946 if (required) {
3947 if (err) {
3948 (*err) += "'" + property + "' property is not an array";
3949 if (!parent_node.empty()) {
3950 (*err) += " in " + parent_node;
3951 }
3952 (*err) += ".\n";
3953 }
3954 }
3955 return false;
3956 }
3957
3958 ret->clear();
David1f9a4b92023-02-15 22:56:18 -06003959 auto end = detail::ArrayEnd(detail::GetValue(it));
3960 for (auto i = detail::ArrayBegin(detail::GetValue(it)); i != end; ++i) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003961 int numberValue;
David1f9a4b92023-02-15 22:56:18 -06003962 bool isNumber = detail::GetInt(*i, numberValue);
jrkooncecba5d6c2019-08-29 11:26:22 -05003963 if (!isNumber) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003964 if (required) {
3965 if (err) {
3966 (*err) += "'" + property + "' property is not an integer type.\n";
3967 if (!parent_node.empty()) {
3968 (*err) += " in " + parent_node;
3969 }
3970 (*err) += ".\n";
3971 }
3972 }
3973 return false;
3974 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003975 ret->push_back(numberValue);
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003976 }
3977
3978 return true;
3979}
3980
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003981static bool ParseStringProperty(
David03ad33c2023-02-15 23:35:51 -06003982 std::string *ret, std::string *err, const detail::json &o,
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003983 const std::string &property, bool required,
3984 const std::string &parent_node = std::string()) {
David03ad33c2023-02-15 23:35:51 -06003985 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06003986 if (!detail::FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003987 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003988 if (err) {
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003989 (*err) += "'" + property + "' property is missing";
3990 if (parent_node.empty()) {
3991 (*err) += ".\n";
3992 } else {
3993 (*err) += " in `" + parent_node + "'.\n";
3994 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003995 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003996 }
3997 return false;
3998 }
3999
jrkooncecba5d6c2019-08-29 11:26:22 -05004000 std::string strValue;
David1f9a4b92023-02-15 22:56:18 -06004001 if (!detail::GetString(detail::GetValue(it), strValue)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004002 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004003 if (err) {
4004 (*err) += "'" + property + "' property is not a string type.\n";
4005 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004006 }
4007 return false;
4008 }
4009
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004010 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004011 (*ret) = std::move(strValue);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004012 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004013
4014 return true;
4015}
4016
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004017static bool ParseStringIntegerProperty(std::map<std::string, int> *ret,
David03ad33c2023-02-15 23:35:51 -06004018 std::string *err, const detail::json &o,
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004019 const std::string &property,
4020 bool required,
4021 const std::string &parent = "") {
David03ad33c2023-02-15 23:35:51 -06004022 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06004023 if (!detail::FindMember(o, property.c_str(), it)) {
Luke San Antonio19894c72016-06-14 21:19:51 -04004024 if (required) {
4025 if (err) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09004026 if (!parent.empty()) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004027 (*err) +=
4028 "'" + property + "' property is missing in " + parent + ".\n";
Syoyo Fujita57c10182017-10-19 18:48:26 +09004029 } else {
4030 (*err) += "'" + property + "' property is missing.\n";
4031 }
Luke San Antonio19894c72016-06-14 21:19:51 -04004032 }
4033 }
4034 return false;
4035 }
4036
David03ad33c2023-02-15 23:35:51 -06004037 const detail::json &dict = detail::GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05004038
Luke San Antonio19894c72016-06-14 21:19:51 -04004039 // Make sure we are dealing with an object / dictionary.
David1f9a4b92023-02-15 22:56:18 -06004040 if (!detail::IsObject(dict)) {
Luke San Antonio19894c72016-06-14 21:19:51 -04004041 if (required) {
4042 if (err) {
4043 (*err) += "'" + property + "' property is not an object.\n";
4044 }
4045 }
4046 return false;
4047 }
4048
4049 ret->clear();
Luke San Antonio19894c72016-06-14 21:19:51 -04004050
David03ad33c2023-02-15 23:35:51 -06004051 detail::json_const_iterator dictIt(detail::ObjectBegin(dict));
4052 detail::json_const_iterator dictItEnd(detail::ObjectEnd(dict));
Luke San Antonio19894c72016-06-14 21:19:51 -04004053
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09004054 for (; dictIt != dictItEnd; ++dictIt) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004055 int intVal;
David1f9a4b92023-02-15 22:56:18 -06004056 if (!detail::GetInt(detail::GetValue(dictIt), intVal)) {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09004057 if (required) {
4058 if (err) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004059 (*err) += "'" + property + "' value is not an integer type.\n";
Luke San Antonio19894c72016-06-14 21:19:51 -04004060 }
4061 }
4062 return false;
4063 }
4064
4065 // Insert into the list.
David1f9a4b92023-02-15 22:56:18 -06004066 (*ret)[detail::GetKey(dictIt)] = intVal;
Luke San Antonio19894c72016-06-14 21:19:51 -04004067 }
4068 return true;
4069}
4070
Syoyo Fujita5b407452017-06-04 17:42:41 +09004071static bool ParseJSONProperty(std::map<std::string, double> *ret,
David03ad33c2023-02-15 23:35:51 -06004072 std::string *err, const detail::json &o,
Syoyo Fujita5b407452017-06-04 17:42:41 +09004073 const std::string &property, bool required) {
David03ad33c2023-02-15 23:35:51 -06004074 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06004075 if (!detail::FindMember(o, property.c_str(), it)) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00004076 if (required) {
Syoyo Fujita5b407452017-06-04 17:42:41 +09004077 if (err) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00004078 (*err) += "'" + property + "' property is missing. \n'";
4079 }
4080 }
4081 return false;
4082 }
4083
David03ad33c2023-02-15 23:35:51 -06004084 const detail::json &obj = detail::GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05004085
David1f9a4b92023-02-15 22:56:18 -06004086 if (!detail::IsObject(obj)) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00004087 if (required) {
4088 if (err) {
4089 (*err) += "'" + property + "' property is not a JSON object.\n";
4090 }
4091 }
4092 return false;
4093 }
4094
4095 ret->clear();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004096
David03ad33c2023-02-15 23:35:51 -06004097 detail::json_const_iterator it2(detail::ObjectBegin(obj));
4098 detail::json_const_iterator itEnd(detail::ObjectEnd(obj));
jrkooncecba5d6c2019-08-29 11:26:22 -05004099 for (; it2 != itEnd; ++it2) {
4100 double numVal;
David1f9a4b92023-02-15 22:56:18 -06004101 if (detail::GetNumber(detail::GetValue(it2), numVal))
4102 ret->emplace(std::string(detail::GetKey(it2)), numVal);
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00004103 }
4104
4105 return true;
4106}
4107
Selmar09d2ff12018-03-15 17:30:42 +01004108static bool ParseParameterProperty(Parameter *param, std::string *err,
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004109 const detail::json &o,
4110 const std::string &prop, bool required) {
Selmar09d2ff12018-03-15 17:30:42 +01004111 // A parameter value can either be a string or an array of either a boolean or
4112 // a number. Booleans of any kind aren't supported here. Granted, it
4113 // complicates the Parameter structure and breaks it semantically in the sense
4114 // that the client probably works off the assumption that if the string is
4115 // empty the vector is used, etc. Would a tagged union work?
4116 if (ParseStringProperty(&param->string_value, err, o, prop, false)) {
4117 // Found string property.
4118 return true;
4119 } else if (ParseNumberArrayProperty(&param->number_array, err, o, prop,
4120 false)) {
4121 // Found a number array.
4122 return true;
Ben Buzbeef6af2242018-04-25 15:13:05 -07004123 } else if (ParseNumberProperty(&param->number_value, err, o, prop, false)) {
Marcin Kacprzakb12a54e2022-09-02 16:13:11 +02004124 param->has_number_value = true;
4125 return true;
Selmar09d2ff12018-03-15 17:30:42 +01004126 } else if (ParseJSONProperty(&param->json_double_value, err, o, prop,
4127 false)) {
4128 return true;
4129 } else if (ParseBooleanProperty(&param->bool_value, err, o, prop, false)) {
4130 return true;
4131 } else {
4132 if (required) {
4133 if (err) {
4134 (*err) += "parameter must be a string or number / number array.\n";
4135 }
4136 }
4137 return false;
4138 }
4139}
4140
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004141static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err,
David03ad33c2023-02-15 23:35:51 -06004142 const detail::json &o) {
Syoyo Fujita48f6db02018-04-15 18:40:55 +09004143 (void)err;
4144
David03ad33c2023-02-15 23:35:51 -06004145 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06004146 if (!detail::FindMember(o, "extensions", it)) {
Selmar09d2ff12018-03-15 17:30:42 +01004147 return false;
4148 }
jrkooncecba5d6c2019-08-29 11:26:22 -05004149
David1f9a4b92023-02-15 22:56:18 -06004150 auto &obj = detail::GetValue(it);
4151 if (!detail::IsObject(obj)) {
Selmar09d2ff12018-03-15 17:30:42 +01004152 return false;
4153 }
4154 ExtensionMap extensions;
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004155 detail::json_const_iterator extIt =
4156 detail::ObjectBegin(obj); // it.value().begin();
David03ad33c2023-02-15 23:35:51 -06004157 detail::json_const_iterator extEnd = detail::ObjectEnd(obj);
jrkooncecba5d6c2019-08-29 11:26:22 -05004158 for (; extIt != extEnd; ++extIt) {
David1f9a4b92023-02-15 22:56:18 -06004159 auto &itObj = detail::GetValue(extIt);
4160 if (!detail::IsObject(itObj)) continue;
4161 std::string key(detail::GetKey(extIt));
jrkooncecba5d6c2019-08-29 11:26:22 -05004162 if (!ParseJsonAsValue(&extensions[key], itObj)) {
jrkoonce06c30c42019-09-03 15:56:48 -05004163 if (!key.empty()) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004164 // create empty object so that an extension object is still of type
4165 // object
jrkooncecba5d6c2019-08-29 11:26:22 -05004166 extensions[key] = Value{Value::Object{}};
Selmar Kokee3d0662018-10-08 16:20:43 +02004167 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004168 }
Selmar09d2ff12018-03-15 17:30:42 +01004169 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004170 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004171 (*ret) = std::move(extensions);
Selmar09d2ff12018-03-15 17:30:42 +01004172 }
4173 return true;
4174}
4175
David Siegel22cafa12023-06-05 22:18:59 +02004176template <typename GltfType>
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004177static bool ParseExtrasAndExtensions(GltfType *target, std::string *err,
4178 const detail::json &o,
4179 bool store_json_strings) {
David Siegel22cafa12023-06-05 22:18:59 +02004180 ParseExtensionsProperty(&target->extensions, err, o);
4181 ParseExtrasProperty(&target->extras, o);
4182
4183 if (store_json_strings) {
4184 {
4185 detail::json_const_iterator it;
4186 if (detail::FindMember(o, "extensions", it)) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004187 target->extensions_json_string =
4188 detail::JsonToString(detail::GetValue(it));
David Siegel22cafa12023-06-05 22:18:59 +02004189 }
4190 }
4191 {
4192 detail::json_const_iterator it;
4193 if (detail::FindMember(o, "extras", it)) {
4194 target->extras_json_string = detail::JsonToString(detail::GetValue(it));
4195 }
4196 }
4197 }
4198 return true;
4199}
4200
David03ad33c2023-02-15 23:35:51 -06004201static bool ParseAsset(Asset *asset, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004202 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09004203 ParseStringProperty(&asset->version, err, o, "version", true, "Asset");
4204 ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset");
4205 ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset");
Miguel Sousa22bfc842020-02-20 14:16:58 +01004206 ParseStringProperty(&asset->copyright, err, o, "copyright", false, "Asset");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004207
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004208 ParseExtrasAndExtensions(asset, err, o,
4209 store_original_json_for_extras_and_extensions);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004210 return true;
4211}
4212
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004213static bool ParseImage(Image *image, const int image_idx, std::string *err,
David03ad33c2023-02-15 23:35:51 -06004214 std::string *warn, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004215 bool store_original_json_for_extras_and_extensions,
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004216 const std::string &basedir, const size_t max_file_size,
4217 FsCallbacks *fs, const URICallbacks *uri_cb,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004218 LoadImageDataFunction *LoadImageData = nullptr,
Paolo Jovone6601bf2018-07-07 20:43:33 +02004219 void *load_image_user_data = nullptr) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004220 // A glTF image must either reference a bufferView or an image uri
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004221
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004222 // schema says oneOf [`bufferView`, `uri`]
4223 // TODO(syoyo): Check the type of each parameters.
David03ad33c2023-02-15 23:35:51 -06004224 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06004225 bool hasBufferView = detail::FindMember(o, "bufferView", it);
4226 bool hasURI = detail::FindMember(o, "uri", it);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004227
Syoyo Fujitaaf3ebb22019-01-06 18:55:57 +09004228 ParseStringProperty(&image->name, err, o, "name", false);
4229
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004230 if (hasBufferView && hasURI) {
4231 // Should not both defined.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004232 if (err) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09004233 (*err) +=
4234 "Only one of `bufferView` or `uri` should be defined, but both are "
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004235 "defined for image[" +
4236 std::to_string(image_idx) + "] name = \"" + image->name + "\"\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004237 }
4238 return false;
4239 }
4240
4241 if (!hasBufferView && !hasURI) {
4242 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004243 (*err) += "Neither required `bufferView` nor `uri` defined for image[" +
4244 std::to_string(image_idx) + "] name = \"" + image->name +
4245 "\"\n";
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004246 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004247 return false;
4248 }
4249
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004250 ParseExtrasAndExtensions(image, err, o,
4251 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004252
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004253 if (hasBufferView) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004254 int bufferView = -1;
4255 if (!ParseIntegerProperty(&bufferView, err, o, "bufferView", true)) {
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004256 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004257 (*err) += "Failed to parse `bufferView` for image[" +
4258 std::to_string(image_idx) + "] name = \"" + image->name +
4259 "\"\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004260 }
4261 return false;
4262 }
4263
4264 std::string mime_type;
4265 ParseStringProperty(&mime_type, err, o, "mimeType", false);
4266
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004267 int width = 0;
4268 ParseIntegerProperty(&width, err, o, "width", false);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004269
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004270 int height = 0;
4271 ParseIntegerProperty(&height, err, o, "height", false);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004272
4273 // Just only save some information here. Loading actual image data from
4274 // bufferView is done after this `ParseImage` function.
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004275 image->bufferView = bufferView;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004276 image->mimeType = mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004277 image->width = width;
4278 image->height = height;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004279
4280 return true;
4281 }
4282
Syoyo Fujita246654a2018-03-21 20:32:22 +09004283 // Parse URI & Load image data.
4284
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004285 std::string uri;
4286 std::string tmp_err;
Syoyo Fujita246654a2018-03-21 20:32:22 +09004287 if (!ParseStringProperty(&uri, &tmp_err, o, "uri", true)) {
4288 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004289 (*err) += "Failed to parse `uri` for image[" + std::to_string(image_idx) +
4290 "] name = \"" + image->name + "\".\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004291 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09004292 return false;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004293 }
4294
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004295 std::vector<unsigned char> img;
Syoyo Fujitabeded612016-05-01 20:03:43 +09004296
Syoyo Fujita246654a2018-03-21 20:32:22 +09004297 if (IsDataURI(uri)) {
johan bowald642a3432018-04-01 12:37:18 +02004298 if (!DecodeDataURI(&img, image->mimeType, uri, 0, false)) {
Syoyo Fujita246654a2018-03-21 20:32:22 +09004299 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004300 (*err) += "Failed to decode 'uri' for image[" +
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09004301 std::to_string(image_idx) + "] name = \"" + image->name +
4302 "\"\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004303 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09004304 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004305 }
4306 } else {
Syoyo Fujita246654a2018-03-21 20:32:22 +09004307 // Assume external file
4308 // Keep texture path (for textures that cannot be decoded)
4309 image->uri = uri;
Selmar67af3c92018-03-16 11:48:19 +01004310#ifdef TINYGLTF_NO_EXTERNAL_IMAGE
Syoyo Fujita246654a2018-03-21 20:32:22 +09004311 return true;
AlvaroBarua43172232022-09-11 00:41:43 +01004312#else
Pyarelal Knowles385946d2023-01-03 18:18:04 -08004313 std::string decoded_uri;
4314 if (!uri_cb->decode(uri, &decoded_uri, uri_cb->user_data)) {
4315 if (warn) {
4316 (*warn) += "Failed to decode 'uri' for image[" +
Syoyo Fujita877d8562023-04-23 21:47:31 +09004317 std::to_string(image_idx) + "] name = \"" + image->name +
4318 "\"\n";
Pyarelal Knowles385946d2023-01-03 18:18:04 -08004319 }
4320
4321 // Image loading failure is not critical to overall gltf loading.
4322 return true;
4323 }
4324
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004325 if (!LoadExternalFile(&img, err, warn, decoded_uri, basedir,
4326 /* required */ false, /* required bytes */ 0,
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004327 /* checksize */ false,
4328 /* max file size */ max_file_size, fs)) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004329 if (warn) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004330 (*warn) += "Failed to load external 'uri' for image[" +
Syoyo Fujita877d8562023-04-23 21:47:31 +09004331 std::to_string(image_idx) + "] name = \"" + decoded_uri +
4332 "\"\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004333 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09004334 // If the image cannot be loaded, keep uri as image->uri.
4335 return true;
4336 }
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004337
Syoyo Fujita246654a2018-03-21 20:32:22 +09004338 if (img.empty()) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004339 if (warn) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004340 (*warn) += "Image data is empty for image[" +
Syoyo Fujita877d8562023-04-23 21:47:31 +09004341 std::to_string(image_idx) + "] name = \"" + image->name +
4342 "\" \n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09004343 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09004344 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004345 }
AlvaroBarua43172232022-09-11 00:41:43 +01004346#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004347 }
4348
Squareysff644d82018-03-13 22:36:18 +01004349 if (*LoadImageData == nullptr) {
4350 if (err) {
4351 (*err) += "No LoadImageData callback specified.\n";
4352 }
4353 return false;
4354 }
Syoyo Fujitaaf3ebb22019-01-06 18:55:57 +09004355 return (*LoadImageData)(image, image_idx, err, warn, 0, 0, &img.at(0),
Paolo Jovone6601bf2018-07-07 20:43:33 +02004356 static_cast<int>(img.size()), load_image_user_data);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004357}
4358
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004359static bool ParseTexture(Texture *texture, std::string *err,
4360 const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004361 bool store_original_json_for_extras_and_extensions,
Syoyo Fujitabeded612016-05-01 20:03:43 +09004362 const std::string &basedir) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004363 (void)basedir;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004364 int sampler = -1;
4365 int source = -1;
4366 ParseIntegerProperty(&sampler, err, o, "sampler", false);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004367
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004368 ParseIntegerProperty(&source, err, o, "source", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09004369
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004370 texture->sampler = sampler;
4371 texture->source = source;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004372
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004373 ParseExtrasAndExtensions(texture, err, o,
4374 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004375
Vladimír Vondruš239be2c2018-07-24 23:23:56 +02004376 ParseStringProperty(&texture->name, err, o, "name", false);
4377
Syoyo Fujitabde70212016-02-07 17:38:17 +09004378 return true;
4379}
4380
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004381static bool ParseTextureInfo(
David03ad33c2023-02-15 23:35:51 -06004382 TextureInfo *texinfo, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004383 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004384 if (texinfo == nullptr) {
4385 return false;
4386 }
4387
4388 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
4389 /* required */ true, "TextureInfo")) {
4390 return false;
4391 }
4392
4393 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
4394
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004395 ParseExtrasAndExtensions(texinfo, err, o,
4396 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004397
Syoyo Fujita046400b2019-07-24 19:26:48 +09004398 return true;
4399}
4400
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004401static bool ParseNormalTextureInfo(
David03ad33c2023-02-15 23:35:51 -06004402 NormalTextureInfo *texinfo, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004403 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004404 if (texinfo == nullptr) {
4405 return false;
4406 }
4407
4408 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
4409 /* required */ true, "NormalTextureInfo")) {
4410 return false;
4411 }
4412
4413 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
4414 ParseNumberProperty(&texinfo->scale, err, o, "scale", false);
4415
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004416 ParseExtrasAndExtensions(texinfo, err, o,
4417 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004418
Syoyo Fujita046400b2019-07-24 19:26:48 +09004419 return true;
4420}
4421
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004422static bool ParseOcclusionTextureInfo(
David03ad33c2023-02-15 23:35:51 -06004423 OcclusionTextureInfo *texinfo, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004424 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004425 if (texinfo == nullptr) {
4426 return false;
4427 }
4428
4429 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
4430 /* required */ true, "NormalTextureInfo")) {
4431 return false;
4432 }
4433
4434 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
4435 ParseNumberProperty(&texinfo->strength, err, o, "strength", false);
4436
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004437 ParseExtrasAndExtensions(texinfo, err, o,
4438 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004439
Syoyo Fujita046400b2019-07-24 19:26:48 +09004440 return true;
4441}
4442
David03ad33c2023-02-15 23:35:51 -06004443static bool ParseBuffer(Buffer *buffer, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004444 bool store_original_json_for_extras_and_extensions,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08004445 FsCallbacks *fs, const URICallbacks *uri_cb,
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004446 const std::string &basedir,
4447 const size_t max_buffer_size, bool is_binary = false,
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09004448 const unsigned char *bin_data = nullptr,
Syoyo Fujitabeded612016-05-01 20:03:43 +09004449 size_t bin_size = 0) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004450 size_t byteLength;
4451 if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true,
4452 "Buffer")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004453 return false;
4454 }
4455
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004456 // In glTF 2.0, uri is not mandatory anymore
Syoyo Fujita20244e12018-03-15 11:01:05 -05004457 buffer->uri.clear();
4458 ParseStringProperty(&buffer->uri, err, o, "uri", false, "Buffer");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004459
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004460 // having an empty uri for a non embedded image should not be valid
Syoyo Fujita20244e12018-03-15 11:01:05 -05004461 if (!is_binary && buffer->uri.empty()) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004462 if (err) {
4463 (*err) += "'uri' is missing from non binary glTF file buffer.\n";
4464 }
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004465 }
4466
David03ad33c2023-02-15 23:35:51 -06004467 detail::json_const_iterator type;
David1f9a4b92023-02-15 22:56:18 -06004468 if (detail::FindMember(o, "type", type)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004469 std::string typeStr;
David1f9a4b92023-02-15 22:56:18 -06004470 if (detail::GetString(detail::GetValue(type), typeStr)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004471 if (typeStr.compare("arraybuffer") == 0) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004472 // buffer.type = "arraybuffer";
4473 }
4474 }
4475 }
4476
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004477 if (is_binary) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004478 // Still binary glTF accepts external dataURI.
Syoyo Fujita20244e12018-03-15 11:01:05 -05004479 if (!buffer->uri.empty()) {
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004480 // First try embedded data URI.
4481 if (IsDataURI(buffer->uri)) {
4482 std::string mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004483 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004484 true)) {
4485 if (err) {
4486 (*err) +=
4487 "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
4488 }
4489 return false;
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004490 }
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004491 } else {
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004492 // External .bin file.
Pyarelal Knowles385946d2023-01-03 18:18:04 -08004493 std::string decoded_uri;
4494 if (!uri_cb->decode(buffer->uri, &decoded_uri, uri_cb->user_data)) {
4495 return false;
4496 }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09004497 if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr,
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004498 decoded_uri, basedir, /* required */ true,
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004499 byteLength, /* checkSize */ true,
4500 /* max_file_size */ max_buffer_size, fs)) {
Vladimír Vondrušfd84ceb2018-08-16 21:07:56 +02004501 return false;
4502 }
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004503 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004504 } else {
Syoyo Fujitabeded612016-05-01 20:03:43 +09004505 // load data from (embedded) binary data
4506
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09004507 if ((bin_size == 0) || (bin_data == nullptr)) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09004508 if (err) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004509 (*err) +=
4510 "Invalid binary data in `Buffer', or GLB with empty BIN chunk.\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09004511 }
4512 return false;
4513 }
4514
4515 if (byteLength > bin_size) {
4516 if (err) {
4517 std::stringstream ss;
4518 ss << "Invalid `byteLength'. Must be equal or less than binary size: "
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004519 "`byteLength' = "
4520 << byteLength << ", binary size = " << bin_size << std::endl;
Syoyo Fujitabeded612016-05-01 20:03:43 +09004521 (*err) += ss.str();
4522 }
4523 return false;
4524 }
4525
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004526 // Read buffer data
4527 buffer->data.resize(static_cast<size_t>(byteLength));
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004528 memcpy(&(buffer->data.at(0)), bin_data, static_cast<size_t>(byteLength));
Syoyo Fujitabeded612016-05-01 20:03:43 +09004529 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004530
4531 } else {
Syoyo Fujita20244e12018-03-15 11:01:05 -05004532 if (IsDataURI(buffer->uri)) {
johan bowaldef151a42018-04-01 13:44:48 +02004533 std::string mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004534 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
4535 true)) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004536 if (err) {
Syoyo Fujita20244e12018-03-15 11:01:05 -05004537 (*err) += "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004538 }
4539 return false;
4540 }
4541 } else {
4542 // Assume external .bin file.
Pyarelal Knowles385946d2023-01-03 18:18:04 -08004543 std::string decoded_uri;
4544 if (!uri_cb->decode(buffer->uri, &decoded_uri, uri_cb->user_data)) {
4545 return false;
4546 }
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004547 if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, decoded_uri,
4548 basedir, /* required */ true, byteLength,
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004549 /* checkSize */ true,
4550 /* max file size */ max_buffer_size, fs)) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004551 return false;
4552 }
4553 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004554 }
4555
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004556 ParseStringProperty(&buffer->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004557
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004558 ParseExtrasAndExtensions(buffer, err, o,
4559 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004560
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004561 return true;
4562}
4563
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004564static bool ParseBufferView(
David03ad33c2023-02-15 23:35:51 -06004565 BufferView *bufferView, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004566 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004567 int buffer = -1;
4568 if (!ParseIntegerProperty(&buffer, err, o, "buffer", true, "BufferView")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004569 return false;
4570 }
4571
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004572 size_t byteOffset = 0;
4573 ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004574
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004575 size_t byteLength = 1;
4576 if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true,
4577 "BufferView")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004578 return false;
4579 }
4580
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004581 size_t byteStride = 0;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004582 if (!ParseUnsignedProperty(&byteStride, err, o, "byteStride", false)) {
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004583 // Spec says: When byteStride of referenced bufferView is not defined, it
4584 // means that accessor elements are tightly packed, i.e., effective stride
4585 // equals the size of the element.
4586 // We cannot determine the actual byteStride until Accessor are parsed, thus
4587 // set 0(= tightly packed) here(as done in OpenGL's VertexAttribPoiner)
4588 byteStride = 0;
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004589 }
4590
4591 if ((byteStride > 252) || ((byteStride % 4) != 0)) {
4592 if (err) {
4593 std::stringstream ss;
4594 ss << "Invalid `byteStride' value. `byteStride' must be the multiple of "
4595 "4 : "
4596 << byteStride << std::endl;
4597
4598 (*err) += ss.str();
4599 }
4600 return false;
4601 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004602
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004603 int target = 0;
4604 ParseIntegerProperty(&target, err, o, "target", false);
4605 if ((target == TINYGLTF_TARGET_ARRAY_BUFFER) ||
4606 (target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004607 // OK
4608 } else {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004609 target = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004610 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004611 bufferView->target = target;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004612
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004613 ParseStringProperty(&bufferView->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004614
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004615 ParseExtrasAndExtensions(bufferView, err, o,
4616 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004617
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004618 bufferView->buffer = buffer;
4619 bufferView->byteOffset = byteOffset;
4620 bufferView->byteLength = byteLength;
4621 bufferView->byteStride = byteStride;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004622 return true;
4623}
4624
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004625static bool ParseSparseAccessor(
4626 Accessor::Sparse *sparse, std::string *err, const detail::json &o,
4627 bool store_original_json_for_extras_and_extensions) {
David Siegeld852f502023-06-05 23:28:05 +02004628 sparse->isSparse = true;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004629
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004630 int count = 0;
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004631 if (!ParseIntegerProperty(&count, err, o, "count", true, "SparseAccessor")) {
4632 return false;
4633 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004634
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004635 ParseExtrasAndExtensions(sparse, err, o,
4636 store_original_json_for_extras_and_extensions);
David Siegeld852f502023-06-05 23:28:05 +02004637
David03ad33c2023-02-15 23:35:51 -06004638 detail::json_const_iterator indices_iterator;
4639 detail::json_const_iterator values_iterator;
David1f9a4b92023-02-15 22:56:18 -06004640 if (!detail::FindMember(o, "indices", indices_iterator)) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004641 (*err) = "the sparse object of this accessor doesn't have indices";
4642 return false;
4643 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004644
David1f9a4b92023-02-15 22:56:18 -06004645 if (!detail::FindMember(o, "values", values_iterator)) {
imallettd9ce9eb2022-10-07 10:37:09 -07004646 (*err) = "the sparse object of this accessor doesn't have values";
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004647 return false;
4648 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004649
David03ad33c2023-02-15 23:35:51 -06004650 const detail::json &indices_obj = detail::GetValue(indices_iterator);
4651 const detail::json &values_obj = detail::GetValue(values_iterator);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004652
emimvi759976e2023-09-02 09:39:53 +02004653 int indices_buffer_view = 0, component_type = 0;
4654 size_t indices_byte_offset = 0;
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09004655 if (!ParseIntegerProperty(&indices_buffer_view, err, indices_obj,
4656 "bufferView", true, "SparseAccessor")) {
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004657 return false;
4658 }
emimvi759976e2023-09-02 09:39:53 +02004659 ParseUnsignedProperty(&indices_byte_offset, err, indices_obj, "byteOffset",
Jack Mousseau94715172022-02-24 14:25:37 -08004660 false);
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004661 if (!ParseIntegerProperty(&component_type, err, indices_obj, "componentType",
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09004662 true, "SparseAccessor")) {
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004663 return false;
4664 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004665
emimvi759976e2023-09-02 09:39:53 +02004666 int values_buffer_view = 0;
4667 size_t values_byte_offset = 0;
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004668 if (!ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView",
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09004669 true, "SparseAccessor")) {
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004670 return false;
4671 }
emimvi759976e2023-09-02 09:39:53 +02004672 ParseUnsignedProperty(&values_byte_offset, err, values_obj, "byteOffset",
Jack Mousseau94715172022-02-24 14:25:37 -08004673 false);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004674
David Siegeld852f502023-06-05 23:28:05 +02004675 sparse->count = count;
4676 sparse->indices.bufferView = indices_buffer_view;
4677 sparse->indices.byteOffset = indices_byte_offset;
4678 sparse->indices.componentType = component_type;
4679 ParseExtrasAndExtensions(&sparse->indices, err, indices_obj,
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004680 store_original_json_for_extras_and_extensions);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004681
David Siegeld852f502023-06-05 23:28:05 +02004682 sparse->values.bufferView = values_buffer_view;
4683 sparse->values.byteOffset = values_byte_offset;
4684 ParseExtrasAndExtensions(&sparse->values, err, values_obj,
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004685 store_original_json_for_extras_and_extensions);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004686
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004687 return true;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004688}
4689
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004690static bool ParseAccessor(Accessor *accessor, std::string *err,
4691 const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004692 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004693 int bufferView = -1;
4694 ParseIntegerProperty(&bufferView, err, o, "bufferView", false, "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004695
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004696 size_t byteOffset = 0;
4697 ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false, "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004698
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004699 bool normalized = false;
4700 ParseBooleanProperty(&normalized, err, o, "normalized", false, "Accessor");
4701
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004702 size_t componentType = 0;
4703 if (!ParseUnsignedProperty(&componentType, err, o, "componentType", true,
4704 "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004705 return false;
4706 }
4707
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004708 size_t count = 0;
4709 if (!ParseUnsignedProperty(&count, err, o, "count", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004710 return false;
4711 }
4712
4713 std::string type;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09004714 if (!ParseStringProperty(&type, err, o, "type", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004715 return false;
4716 }
4717
4718 if (type.compare("SCALAR") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004719 accessor->type = TINYGLTF_TYPE_SCALAR;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004720 } else if (type.compare("VEC2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004721 accessor->type = TINYGLTF_TYPE_VEC2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004722 } else if (type.compare("VEC3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004723 accessor->type = TINYGLTF_TYPE_VEC3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004724 } else if (type.compare("VEC4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004725 accessor->type = TINYGLTF_TYPE_VEC4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004726 } else if (type.compare("MAT2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004727 accessor->type = TINYGLTF_TYPE_MAT2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004728 } else if (type.compare("MAT3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004729 accessor->type = TINYGLTF_TYPE_MAT3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004730 } else if (type.compare("MAT4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004731 accessor->type = TINYGLTF_TYPE_MAT4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004732 } else {
4733 std::stringstream ss;
4734 ss << "Unsupported `type` for accessor object. Got \"" << type << "\"\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004735 if (err) {
4736 (*err) += ss.str();
4737 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004738 return false;
4739 }
4740
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004741 ParseStringProperty(&accessor->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004742
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004743 accessor->minValues.clear();
4744 accessor->maxValues.clear();
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004745 ParseNumberArrayProperty(&accessor->minValues, err, o, "min", false,
4746 "Accessor");
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004747
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004748 ParseNumberArrayProperty(&accessor->maxValues, err, o, "max", false,
4749 "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004750
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004751 accessor->count = count;
4752 accessor->bufferView = bufferView;
4753 accessor->byteOffset = byteOffset;
jianghaosen4748ad62017-08-29 20:08:37 +08004754 accessor->normalized = normalized;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004755 {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004756 if (componentType >= TINYGLTF_COMPONENT_TYPE_BYTE &&
4757 componentType <= TINYGLTF_COMPONENT_TYPE_DOUBLE) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004758 // OK
Arthur Brainville (Ybalrid)811e1d32019-06-15 07:32:38 +02004759 accessor->componentType = int(componentType);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004760 } else {
4761 std::stringstream ss;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004762 ss << "Invalid `componentType` in accessor. Got " << componentType
4763 << "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004764 if (err) {
4765 (*err) += ss.str();
4766 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004767 return false;
4768 }
4769 }
4770
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004771 ParseExtrasAndExtensions(accessor, err, o,
4772 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004773
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004774 // check if accessor has a "sparse" object:
David03ad33c2023-02-15 23:35:51 -06004775 detail::json_const_iterator iterator;
David1f9a4b92023-02-15 22:56:18 -06004776 if (detail::FindMember(o, "sparse", iterator)) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004777 // here this accessor has a "sparse" subobject
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004778 return ParseSparseAccessor(&accessor->sparse, err,
4779 detail::GetValue(iterator),
4780 store_original_json_for_extras_and_extensions);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004781 }
4782
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004783 return true;
4784}
4785
Alex Wood7319db72019-01-24 15:38:16 -05004786#ifdef TINYGLTF_ENABLE_DRACO
4787
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004788static void DecodeIndexBuffer(draco::Mesh *mesh, size_t componentSize,
4789 std::vector<uint8_t> &outBuffer) {
4790 if (componentSize == 4) {
Alex Wood7319db72019-01-24 15:38:16 -05004791 assert(sizeof(mesh->face(draco::FaceIndex(0))[0]) == componentSize);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004792 memcpy(outBuffer.data(), &mesh->face(draco::FaceIndex(0))[0],
4793 outBuffer.size());
4794 } else {
Alex Wood7319db72019-01-24 15:38:16 -05004795 size_t faceStride = componentSize * 3;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004796 for (draco::FaceIndex f(0); f < mesh->num_faces(); ++f) {
4797 const draco::Mesh::Face &face = mesh->face(f);
4798 if (componentSize == 2) {
4799 uint16_t indices[3] = {(uint16_t)face[0].value(),
4800 (uint16_t)face[1].value(),
4801 (uint16_t)face[2].value()};
4802 memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
4803 faceStride);
4804 } else {
4805 uint8_t indices[3] = {(uint8_t)face[0].value(),
4806 (uint8_t)face[1].value(),
4807 (uint8_t)face[2].value()};
4808 memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
4809 faceStride);
Alex Wood7319db72019-01-24 15:38:16 -05004810 }
4811 }
4812 }
4813}
4814
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004815template <typename T>
4816static bool GetAttributeForAllPoints(draco::Mesh *mesh,
4817 const draco::PointAttribute *pAttribute,
4818 std::vector<uint8_t> &outBuffer) {
Alex Wood7319db72019-01-24 15:38:16 -05004819 size_t byteOffset = 0;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004820 T values[4] = {0, 0, 0, 0};
4821 for (draco::PointIndex i(0); i < mesh->num_points(); ++i) {
Alex Wood7319db72019-01-24 15:38:16 -05004822 const draco::AttributeValueIndex val_index = pAttribute->mapped_index(i);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004823 if (!pAttribute->ConvertValue<T>(val_index, pAttribute->num_components(),
4824 values))
Alex Wood7319db72019-01-24 15:38:16 -05004825 return false;
4826
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004827 memcpy(outBuffer.data() + byteOffset, &values[0],
4828 sizeof(T) * pAttribute->num_components());
Alex Wood7319db72019-01-24 15:38:16 -05004829 byteOffset += sizeof(T) * pAttribute->num_components();
4830 }
4831
4832 return true;
4833}
4834
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004835static bool GetAttributeForAllPoints(uint32_t componentType, draco::Mesh *mesh,
4836 const draco::PointAttribute *pAttribute,
4837 std::vector<uint8_t> &outBuffer) {
Alex Wood7319db72019-01-24 15:38:16 -05004838 bool decodeResult = false;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004839 switch (componentType) {
4840 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
4841 decodeResult =
4842 GetAttributeForAllPoints<uint8_t>(mesh, pAttribute, outBuffer);
4843 break;
4844 case TINYGLTF_COMPONENT_TYPE_BYTE:
4845 decodeResult =
4846 GetAttributeForAllPoints<int8_t>(mesh, pAttribute, outBuffer);
4847 break;
4848 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
4849 decodeResult =
4850 GetAttributeForAllPoints<uint16_t>(mesh, pAttribute, outBuffer);
4851 break;
4852 case TINYGLTF_COMPONENT_TYPE_SHORT:
4853 decodeResult =
4854 GetAttributeForAllPoints<int16_t>(mesh, pAttribute, outBuffer);
4855 break;
4856 case TINYGLTF_COMPONENT_TYPE_INT:
4857 decodeResult =
4858 GetAttributeForAllPoints<int32_t>(mesh, pAttribute, outBuffer);
4859 break;
4860 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
4861 decodeResult =
4862 GetAttributeForAllPoints<uint32_t>(mesh, pAttribute, outBuffer);
4863 break;
4864 case TINYGLTF_COMPONENT_TYPE_FLOAT:
4865 decodeResult =
4866 GetAttributeForAllPoints<float>(mesh, pAttribute, outBuffer);
4867 break;
4868 case TINYGLTF_COMPONENT_TYPE_DOUBLE:
4869 decodeResult =
4870 GetAttributeForAllPoints<double>(mesh, pAttribute, outBuffer);
4871 break;
4872 default:
4873 return false;
Alex Wood7319db72019-01-24 15:38:16 -05004874 }
4875
4876 return decodeResult;
4877}
4878
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004879static bool ParseDracoExtension(Primitive *primitive, Model *model,
Nyall Dawson6e3d6662023-08-24 13:25:34 +10004880 std::string *err, std::string *warn,
4881 const Value &dracoExtensionValue,
4882 ParseStrictness strictness) {
snowapril84e15262021-03-20 19:25:58 +09004883 (void)err;
Alex Wood7319db72019-01-24 15:38:16 -05004884 auto bufferViewValue = dracoExtensionValue.Get("bufferView");
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004885 if (!bufferViewValue.IsInt()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004886 auto attributesValue = dracoExtensionValue.Get("attributes");
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004887 if (!attributesValue.IsObject()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004888
4889 auto attributesObject = attributesValue.Get<Value::Object>();
4890 int bufferView = bufferViewValue.Get<int>();
4891
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004892 BufferView &view = model->bufferViews[bufferView];
4893 Buffer &buffer = model->buffers[view.buffer];
Alex Wood7319db72019-01-24 15:38:16 -05004894 // BufferView has already been decoded
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004895 if (view.dracoDecoded) return true;
Alex Wood7319db72019-01-24 15:38:16 -05004896 view.dracoDecoded = true;
4897
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004898 const char *bufferViewData =
4899 reinterpret_cast<const char *>(buffer.data.data() + view.byteOffset);
Alex Wood7319db72019-01-24 15:38:16 -05004900 size_t bufferViewSize = view.byteLength;
4901
4902 // decode draco
4903 draco::DecoderBuffer decoderBuffer;
4904 decoderBuffer.Init(bufferViewData, bufferViewSize);
4905 draco::Decoder decoder;
4906 auto decodeResult = decoder.DecodeMeshFromBuffer(&decoderBuffer);
4907 if (!decodeResult.ok()) {
4908 return false;
4909 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004910 const std::unique_ptr<draco::Mesh> &mesh = decodeResult.value();
Alex Wood7319db72019-01-24 15:38:16 -05004911
4912 // create new bufferView for indices
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004913 if (primitive->indices >= 0) {
Nyall Dawson0067b4d2023-09-07 08:20:28 +10004914 if (strictness == ParseStrictness::Permissive) {
Nyall Dawson6e3d6662023-08-24 13:25:34 +10004915 const draco::PointIndex::ValueType numPoint = mesh->num_points();
4916 // handle the situation where the stored component type does not match the
4917 // required type for the actual number of stored points
4918 int supposedComponentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
4919 if (numPoint < static_cast<draco::PointIndex::ValueType>(
4920 std::numeric_limits<uint8_t>::max())) {
4921 supposedComponentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
4922 } else if (
4923 numPoint < static_cast<draco::PointIndex::ValueType>(
4924 std::numeric_limits<uint16_t>::max())) {
4925 supposedComponentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT;
4926 } else {
4927 supposedComponentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT;
4928 }
4929
4930 if (supposedComponentType > model->accessors[primitive->indices].componentType) {
Nyall Dawson6e3d6662023-08-24 13:25:34 +10004931 if (warn) {
4932 (*warn) +=
4933 "GLTF component type " + std::to_string(model->accessors[primitive->indices].componentType) +
4934 " is not sufficient for number of stored points,"
4935 " treating as " + std::to_string(supposedComponentType) + "\n";
4936 }
Nyall Dawsonc35819f2023-09-12 08:46:02 +10004937 model->accessors[primitive->indices].componentType = supposedComponentType;
Nyall Dawson6e3d6662023-08-24 13:25:34 +10004938 }
4939 }
4940
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004941 int32_t componentSize = GetComponentSizeInBytes(
4942 model->accessors[primitive->indices].componentType);
Alex Wood7319db72019-01-24 15:38:16 -05004943 Buffer decodedIndexBuffer;
4944 decodedIndexBuffer.data.resize(mesh->num_faces() * 3 * componentSize);
4945
4946 DecodeIndexBuffer(mesh.get(), componentSize, decodedIndexBuffer.data);
4947
4948 model->buffers.emplace_back(std::move(decodedIndexBuffer));
4949
4950 BufferView decodedIndexBufferView;
4951 decodedIndexBufferView.buffer = int(model->buffers.size() - 1);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004952 decodedIndexBufferView.byteLength =
4953 int(mesh->num_faces() * 3 * componentSize);
Alex Wood7319db72019-01-24 15:38:16 -05004954 decodedIndexBufferView.byteOffset = 0;
4955 decodedIndexBufferView.byteStride = 0;
4956 decodedIndexBufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER;
4957 model->bufferViews.emplace_back(std::move(decodedIndexBufferView));
4958
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004959 model->accessors[primitive->indices].bufferView =
4960 int(model->bufferViews.size() - 1);
Alexander Wood0d77a292019-01-26 08:58:45 -05004961 model->accessors[primitive->indices].count = int(mesh->num_faces() * 3);
Alex Wood7319db72019-01-24 15:38:16 -05004962 }
4963
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004964 for (const auto &attribute : attributesObject) {
4965 if (!attribute.second.IsInt()) return false;
Alexander Wood0d77a292019-01-26 08:58:45 -05004966 auto primitiveAttribute = primitive->attributes.find(attribute.first);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004967 if (primitiveAttribute == primitive->attributes.end()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004968
4969 int dracoAttributeIndex = attribute.second.Get<int>();
4970 const auto pAttribute = mesh->GetAttributeByUniqueId(dracoAttributeIndex);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004971 const auto componentType =
4972 model->accessors[primitiveAttribute->second].componentType;
Alex Wood7319db72019-01-24 15:38:16 -05004973
4974 // Create a new buffer for this decoded buffer
4975 Buffer decodedBuffer;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004976 size_t bufferSize = mesh->num_points() * pAttribute->num_components() *
4977 GetComponentSizeInBytes(componentType);
Alex Wood7319db72019-01-24 15:38:16 -05004978 decodedBuffer.data.resize(bufferSize);
4979
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004980 if (!GetAttributeForAllPoints(componentType, mesh.get(), pAttribute,
4981 decodedBuffer.data))
Alex Wood7319db72019-01-24 15:38:16 -05004982 return false;
4983
4984 model->buffers.emplace_back(std::move(decodedBuffer));
4985
4986 BufferView decodedBufferView;
4987 decodedBufferView.buffer = int(model->buffers.size() - 1);
4988 decodedBufferView.byteLength = bufferSize;
4989 decodedBufferView.byteOffset = pAttribute->byte_offset();
4990 decodedBufferView.byteStride = pAttribute->byte_stride();
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004991 decodedBufferView.target = primitive->indices >= 0
4992 ? TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER
4993 : TINYGLTF_TARGET_ARRAY_BUFFER;
Alex Wood7319db72019-01-24 15:38:16 -05004994 model->bufferViews.emplace_back(std::move(decodedBufferView));
4995
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004996 model->accessors[primitiveAttribute->second].bufferView =
4997 int(model->bufferViews.size() - 1);
4998 model->accessors[primitiveAttribute->second].count =
4999 int(mesh->num_points());
Alex Wood7319db72019-01-24 15:38:16 -05005000 }
5001
5002 return true;
5003}
5004#endif
5005
Nyall Dawson6e3d6662023-08-24 13:25:34 +10005006static bool ParsePrimitive(Primitive *primitive, Model *model,
5007 std::string *err, std::string *warn,
David03ad33c2023-02-15 23:35:51 -06005008 const detail::json &o,
Nyall Dawson6e3d6662023-08-24 13:25:34 +10005009 bool store_original_json_for_extras_and_extensions,
5010 ParseStrictness strictness) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005011 int material = -1;
5012 ParseIntegerProperty(&material, err, o, "material", false);
5013 primitive->material = material;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005014
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005015 int mode = TINYGLTF_MODE_TRIANGLES;
5016 ParseIntegerProperty(&mode, err, o, "mode", false);
imallettd9ce9eb2022-10-07 10:37:09 -07005017 primitive->mode = mode; // Why only triangles were supported ?
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005018
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005019 int indices = -1;
5020 ParseIntegerProperty(&indices, err, o, "indices", false);
5021 primitive->indices = indices;
5022 if (!ParseStringIntegerProperty(&primitive->attributes, err, o, "attributes",
5023 true, "Primitive")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005024 return false;
5025 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005026
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00005027 // Look for morph targets
David03ad33c2023-02-15 23:35:51 -06005028 detail::json_const_iterator targetsObject;
David1f9a4b92023-02-15 22:56:18 -06005029 if (detail::FindMember(o, "targets", targetsObject) &&
5030 detail::IsArray(detail::GetValue(targetsObject))) {
5031 auto targetsObjectEnd = detail::ArrayEnd(detail::GetValue(targetsObject));
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005032 for (detail::json_const_array_iterator i =
5033 detail::ArrayBegin(detail::GetValue(targetsObject));
jrkooncecba5d6c2019-08-29 11:26:22 -05005034 i != targetsObjectEnd; ++i) {
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00005035 std::map<std::string, int> targetAttribues;
5036
David03ad33c2023-02-15 23:35:51 -06005037 const detail::json &dict = *i;
David1f9a4b92023-02-15 22:56:18 -06005038 if (detail::IsObject(dict)) {
David03ad33c2023-02-15 23:35:51 -06005039 detail::json_const_iterator dictIt(detail::ObjectBegin(dict));
5040 detail::json_const_iterator dictItEnd(detail::ObjectEnd(dict));
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00005041
jrkooncecba5d6c2019-08-29 11:26:22 -05005042 for (; dictIt != dictItEnd; ++dictIt) {
5043 int iVal;
David1f9a4b92023-02-15 22:56:18 -06005044 if (detail::GetInt(detail::GetValue(dictIt), iVal))
5045 targetAttribues[detail::GetKey(dictIt)] = iVal;
jrkooncecba5d6c2019-08-29 11:26:22 -05005046 }
5047 primitive->targets.emplace_back(std::move(targetAttribues));
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00005048 }
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00005049 }
5050 }
5051
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005052 ParseExtrasAndExtensions(primitive, err, o,
5053 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005054
Alex Wood7319db72019-01-24 15:38:16 -05005055#ifdef TINYGLTF_ENABLE_DRACO
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00005056 auto dracoExtension =
5057 primitive->extensions.find("KHR_draco_mesh_compression");
5058 if (dracoExtension != primitive->extensions.end()) {
Nyall Dawson6e3d6662023-08-24 13:25:34 +10005059 ParseDracoExtension(primitive, model, err, warn, dracoExtension->second, strictness);
Alex Wood7319db72019-01-24 15:38:16 -05005060 }
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09005061#else
5062 (void)model;
Nyall Dawson6e3d6662023-08-24 13:25:34 +10005063 (void)warn;
Nyall Dawson5a7b8272023-09-03 09:04:56 +10005064 (void)strictness;
Alex Wood7319db72019-01-24 15:38:16 -05005065#endif
5066
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005067 return true;
5068}
5069
Nyall Dawson6e3d6662023-08-24 13:25:34 +10005070static bool ParseMesh(Mesh *mesh, Model *model,
5071 std::string *err, std::string *warn,
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005072 const detail::json &o,
Nyall Dawson6e3d6662023-08-24 13:25:34 +10005073 bool store_original_json_for_extras_and_extensions,
5074 ParseStrictness strictness) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005075 ParseStringProperty(&mesh->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005076
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005077 mesh->primitives.clear();
David03ad33c2023-02-15 23:35:51 -06005078 detail::json_const_iterator primObject;
David1f9a4b92023-02-15 22:56:18 -06005079 if (detail::FindMember(o, "primitives", primObject) &&
5080 detail::IsArray(detail::GetValue(primObject))) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005081 detail::json_const_array_iterator primEnd =
5082 detail::ArrayEnd(detail::GetValue(primObject));
5083 for (detail::json_const_array_iterator i =
5084 detail::ArrayBegin(detail::GetValue(primObject));
jrkooncecba5d6c2019-08-29 11:26:22 -05005085 i != primEnd; ++i) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005086 Primitive primitive;
Nyall Dawson6e3d6662023-08-24 13:25:34 +10005087 if (ParsePrimitive(&primitive, model, err, warn, *i,
5088 store_original_json_for_extras_and_extensions,
5089 strictness)) {
Luke San Antonio10a3e1d2016-06-15 00:05:01 -04005090 // Only add the primitive if the parsing succeeds.
jrkoonced1e14722019-08-27 11:51:02 -05005091 mesh->primitives.emplace_back(std::move(primitive));
Luke San Antonio10a3e1d2016-06-15 00:05:01 -04005092 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005093 }
5094 }
5095
Aurélien Chatelain38a6a752017-05-24 09:54:55 +00005096 // Should probably check if has targets and if dimensions fit
5097 ParseNumberArrayProperty(&mesh->weights, err, o, "weights", false);
5098
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005099 ParseExtrasAndExtensions(mesh, err, o,
5100 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005101
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005102 return true;
5103}
5104
David03ad33c2023-02-15 23:35:51 -06005105static bool ParseNode(Node *node, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005106 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005107 ParseStringProperty(&node->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005108
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005109 int skin = -1;
5110 ParseIntegerProperty(&skin, err, o, "skin", false);
5111 node->skin = skin;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005112
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005113 // Matrix and T/R/S are exclusive
Syoyo Fujita5b407452017-06-04 17:42:41 +09005114 if (!ParseNumberArrayProperty(&node->matrix, err, o, "matrix", false)) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005115 ParseNumberArrayProperty(&node->rotation, err, o, "rotation", false);
5116 ParseNumberArrayProperty(&node->scale, err, o, "scale", false);
5117 ParseNumberArrayProperty(&node->translation, err, o, "translation", false);
5118 }
5119
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005120 int camera = -1;
5121 ParseIntegerProperty(&camera, err, o, "camera", false);
5122 node->camera = camera;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005123
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005124 int mesh = -1;
5125 ParseIntegerProperty(&mesh, err, o, "mesh", false);
5126 node->mesh = mesh;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005127
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005128 node->children.clear();
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005129 ParseIntegerArrayProperty(&node->children, err, o, "children", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005130
Benjamin Schmithüsenad63bf72019-08-16 14:19:27 +02005131 ParseNumberArrayProperty(&node->weights, err, o, "weights", false);
5132
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005133 ParseExtrasAndExtensions(node, err, o,
5134 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005135
David Siegelc1648782023-05-09 01:33:57 +02005136 // KHR_lights_punctual: parse light source reference
5137 int light = -1;
5138 if (node->extensions.count("KHR_lights_punctual") != 0) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005139 auto const &light_ext = node->extensions["KHR_lights_punctual"];
David Siegelc1648782023-05-09 01:33:57 +02005140 if (light_ext.Has("light")) {
5141 light = light_ext.Get("light").GetNumberAsInt();
5142 } else {
David Siegel157063f2023-06-06 15:31:58 +02005143 if (err) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005144 *err +=
5145 "Node has extension KHR_lights_punctual, but does not reference "
5146 "a light source.\n";
David Siegel157063f2023-06-06 15:31:58 +02005147 }
David Siegelc1648782023-05-09 01:33:57 +02005148 return false;
5149 }
5150 }
5151 node->light = light;
Baranob_Ilya78864c82023-06-12 10:43:52 +04005152
5153 // KHR_audio: parse audio source reference
5154 int emitter = -1;
5155 if (node->extensions.count("KHR_audio") != 0) {
5156 auto const &audio_ext = node->extensions["KHR_audio"];
5157 if (audio_ext.Has("emitter")) {
5158 emitter = audio_ext.Get("emitter").GetNumberAsInt();
5159 } else {
5160 if (err) {
5161 *err +=
5162 "Node has extension KHR_audio, but does not reference "
5163 "a audio emitter.\n";
5164 }
5165 return false;
5166 }
5167 }
5168 node->emitter = emitter;
Syoyo Fujita7a570c82023-06-19 21:52:13 +09005169
Thomas Gampera42263b2024-02-05 15:42:49 +01005170 node->lods.clear();
5171 if (node->extensions.count("MSFT_lod") != 0) {
5172 auto const &msft_lod_ext = node->extensions["MSFT_lod"];
5173 if (msft_lod_ext.Has("ids")) {
5174 auto idsArr = msft_lod_ext.Get("ids");
5175 for (size_t i = 0; i < idsArr.ArrayLen(); ++i) {
5176 node->lods.emplace_back(idsArr.Get(i).GetNumberAsInt());
5177 }
5178 } else {
5179 if (err) {
5180 *err +=
5181 "Node has extension MSFT_lod, but does not reference "
5182 "other nodes via their ids.\n";
5183 }
5184 return false;
5185 }
5186 }
5187
Emanuel Schrade186322b2017-11-06 11:14:41 +01005188 return true;
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04005189}
5190
Baranob_Ilya879cb472023-06-12 13:35:05 +04005191static bool ParseScene(Scene *scene, std::string *err, const detail::json &o,
5192 bool store_original_json_for_extras_and_extensions) {
5193 ParseStringProperty(&scene->name, err, o, "name", false);
5194 ParseIntegerArrayProperty(&scene->nodes, err, o, "nodes", false);
5195
5196 ParseExtrasAndExtensions(scene, err, o,
5197 store_original_json_for_extras_and_extensions);
5198
5199 // Parse KHR_audio global emitters
5200 if (scene->extensions.count("KHR_audio") != 0) {
5201 auto const &audio_ext = scene->extensions["KHR_audio"];
5202 if (audio_ext.Has("emitters")) {
5203 auto emittersArr = audio_ext.Get("emitters");
5204 for (size_t i = 0; i < emittersArr.ArrayLen(); ++i) {
5205 scene->audioEmitters.emplace_back(emittersArr.Get(i).GetNumberAsInt());
5206 }
5207 } else {
5208 if (err) {
5209 *err +=
5210 "Node has extension KHR_audio, but does not reference "
5211 "a audio emitter.\n";
5212 }
5213 return false;
5214 }
5215 }
5216
5217 return true;
5218}
5219
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005220static bool ParsePbrMetallicRoughness(
David03ad33c2023-02-15 23:35:51 -06005221 PbrMetallicRoughness *pbr, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005222 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09005223 if (pbr == nullptr) {
5224 return false;
5225 }
5226
5227 std::vector<double> baseColorFactor;
5228 if (ParseNumberArrayProperty(&baseColorFactor, err, o, "baseColorFactor",
5229 /* required */ false)) {
5230 if (baseColorFactor.size() != 4) {
5231 if (err) {
5232 (*err) +=
5233 "Array length of `baseColorFactor` parameter in "
5234 "pbrMetallicRoughness must be 4, but got " +
5235 std::to_string(baseColorFactor.size()) + "\n";
5236 }
5237 return false;
5238 }
Selmar Kokc3353e12019-10-18 18:22:35 +02005239 pbr->baseColorFactor = baseColorFactor;
Syoyo Fujita046400b2019-07-24 19:26:48 +09005240 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09005241
5242 {
David03ad33c2023-02-15 23:35:51 -06005243 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06005244 if (detail::FindMember(o, "baseColorTexture", it)) {
5245 ParseTextureInfo(&pbr->baseColorTexture, err, detail::GetValue(it),
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005246 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09005247 }
5248 }
5249
5250 {
David03ad33c2023-02-15 23:35:51 -06005251 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06005252 if (detail::FindMember(o, "metallicRoughnessTexture", it)) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005253 ParseTextureInfo(&pbr->metallicRoughnessTexture, err,
5254 detail::GetValue(it),
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005255 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09005256 }
5257 }
5258
5259 ParseNumberProperty(&pbr->metallicFactor, err, o, "metallicFactor", false);
5260 ParseNumberProperty(&pbr->roughnessFactor, err, o, "roughnessFactor", false);
5261
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005262 ParseExtrasAndExtensions(pbr, err, o,
5263 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005264
Syoyo Fujita046400b2019-07-24 19:26:48 +09005265 return true;
5266}
5267
Nyall Dawson02e8b8d2023-08-24 10:25:20 +10005268static bool ParseMaterial(Material *material, std::string *err, std::string *warn,
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005269 const detail::json &o,
Nyall Dawson8c85d5e2023-08-28 12:56:09 +10005270 bool store_original_json_for_extras_and_extensions,
5271 ParseStrictness strictness) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09005272 ParseStringProperty(&material->name, err, o, "name", /* required */ false);
5273
Syoyo Fujitaff515702019-08-24 16:29:14 +09005274 if (ParseNumberArrayProperty(&material->emissiveFactor, err, o,
5275 "emissiveFactor",
5276 /* required */ false)) {
Nyall Dawsonbbc1eae2023-09-03 09:05:32 +10005277 if (strictness==ParseStrictness::Permissive && material->emissiveFactor.size() == 4) {
Nyall Dawson02e8b8d2023-08-24 10:25:20 +10005278 if (warn) {
5279 (*warn) +=
5280 "Array length of `emissiveFactor` parameter in "
5281 "material must be 3, but got 4\n";
5282 }
5283 material->emissiveFactor.resize(3);
5284 }
5285 else if (material->emissiveFactor.size() != 3) {
Selmar Kok6df800d2019-08-19 11:05:28 +02005286 if (err) {
5287 (*err) +=
5288 "Array length of `emissiveFactor` parameter in "
5289 "material must be 3, but got " +
5290 std::to_string(material->emissiveFactor.size()) + "\n";
5291 }
5292 return false;
5293 }
Syoyo Fujitaff515702019-08-24 16:29:14 +09005294 } else {
Selmar Kok6df800d2019-08-19 11:05:28 +02005295 // fill with default values
Selmar Kok6dba6c62019-08-19 11:23:31 +02005296 material->emissiveFactor = {0.0, 0.0, 0.0};
Selmar Kok6df800d2019-08-19 11:05:28 +02005297 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09005298
5299 ParseStringProperty(&material->alphaMode, err, o, "alphaMode",
5300 /* required */ false);
5301 ParseNumberProperty(&material->alphaCutoff, err, o, "alphaCutoff",
5302 /* required */ false);
5303 ParseBooleanProperty(&material->doubleSided, err, o, "doubleSided",
5304 /* required */ false);
5305
5306 {
David03ad33c2023-02-15 23:35:51 -06005307 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06005308 if (detail::FindMember(o, "pbrMetallicRoughness", it)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09005309 ParsePbrMetallicRoughness(&material->pbrMetallicRoughness, err,
David1f9a4b92023-02-15 22:56:18 -06005310 detail::GetValue(it),
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005311 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09005312 }
5313 }
5314
5315 {
David03ad33c2023-02-15 23:35:51 -06005316 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06005317 if (detail::FindMember(o, "normalTexture", it)) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005318 ParseNormalTextureInfo(&material->normalTexture, err,
5319 detail::GetValue(it),
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005320 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09005321 }
5322 }
5323
5324 {
David03ad33c2023-02-15 23:35:51 -06005325 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06005326 if (detail::FindMember(o, "occlusionTexture", it)) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005327 ParseOcclusionTextureInfo(&material->occlusionTexture, err,
5328 detail::GetValue(it),
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005329 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09005330 }
5331 }
5332
5333 {
David03ad33c2023-02-15 23:35:51 -06005334 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06005335 if (detail::FindMember(o, "emissiveTexture", it)) {
5336 ParseTextureInfo(&material->emissiveTexture, err, detail::GetValue(it),
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005337 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09005338 }
5339 }
5340
5341 // Old code path. For backward compatibility, we still store material values
5342 // as Parameter. This will create duplicated information for
imallettd9ce9eb2022-10-07 10:37:09 -07005343 // example(pbrMetallicRoughness), but should be negligible in terms of memory
Syoyo Fujita046400b2019-07-24 19:26:48 +09005344 // consumption.
5345 // TODO(syoyo): Remove in the next major release.
5346 material->values.clear();
5347 material->additionalValues.clear();
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04005348
David03ad33c2023-02-15 23:35:51 -06005349 detail::json_const_iterator it(detail::ObjectBegin(o));
5350 detail::json_const_iterator itEnd(detail::ObjectEnd(o));
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04005351
jrkooncecba5d6c2019-08-29 11:26:22 -05005352 for (; it != itEnd; ++it) {
David1f9a4b92023-02-15 22:56:18 -06005353 std::string key(detail::GetKey(it));
jrkoonce06c30c42019-09-03 15:56:48 -05005354 if (key == "pbrMetallicRoughness") {
David1f9a4b92023-02-15 22:56:18 -06005355 if (detail::IsObject(detail::GetValue(it))) {
David03ad33c2023-02-15 23:35:51 -06005356 const detail::json &values_object = detail::GetValue(it);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005357
David03ad33c2023-02-15 23:35:51 -06005358 detail::json_const_iterator itVal(detail::ObjectBegin(values_object));
5359 detail::json_const_iterator itValEnd(detail::ObjectEnd(values_object));
jrkoonce06c30c42019-09-03 15:56:48 -05005360
5361 for (; itVal != itValEnd; ++itVal) {
5362 Parameter param;
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005363 if (ParseParameterProperty(&param, err, values_object,
5364 detail::GetKey(itVal), false)) {
David1f9a4b92023-02-15 22:56:18 -06005365 material->values.emplace(detail::GetKey(itVal), std::move(param));
jrkoonce06c30c42019-09-03 15:56:48 -05005366 }
5367 }
5368 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005369 } else if (key == "extensions" || key == "extras") {
5370 // done later, skip, otherwise poorly parsed contents will be saved in the
5371 // parametermap and serialized again later
5372 } else {
jrkoonce06c30c42019-09-03 15:56:48 -05005373 Parameter param;
5374 if (ParseParameterProperty(&param, err, o, key, false)) {
5375 // names of materials have already been parsed. Putting it in this map
imallettd9ce9eb2022-10-07 10:37:09 -07005376 // doesn't correctly reflect the glTF specification
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005377 if (key != "name")
5378 material->additionalValues.emplace(std::move(key), std::move(param));
jrkoonce06c30c42019-09-03 15:56:48 -05005379 }
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00005380 }
Syoyo Fujita5b407452017-06-04 17:42:41 +09005381 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005382
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005383 material->extensions.clear(); // Note(agnat): Why?
5384 ParseExtrasAndExtensions(material, err, o,
5385 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005386
Thomas Gampera42263b2024-02-05 15:42:49 +01005387 material->lods.clear();
5388 if (material->extensions.count("MSFT_lod") != 0) {
5389 auto const &msft_lod_ext = material->extensions["MSFT_lod"];
5390 if (msft_lod_ext.Has("ids")) {
5391 auto idsArr = msft_lod_ext.Get("ids");
5392 for (size_t i = 0; i < idsArr.ArrayLen(); ++i) {
5393 material->lods.emplace_back(idsArr.Get(i).GetNumberAsInt());
5394 }
5395 } else {
5396 if (err) {
5397 *err +=
5398 "Material has extension MSFT_lod, but does not reference "
5399 "other materials via their ids.\n";
5400 }
5401 return false;
5402 }
5403 }
5404
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04005405 return true;
5406}
5407
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005408static bool ParseAnimationChannel(
David03ad33c2023-02-15 23:35:51 -06005409 AnimationChannel *channel, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005410 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005411 int samplerIndex = -1;
5412 int targetIndex = -1;
5413 if (!ParseIntegerProperty(&samplerIndex, err, o, "sampler", true,
5414 "AnimationChannel")) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005415 if (err) {
5416 (*err) += "`sampler` field is missing in animation channels\n";
5417 }
5418 return false;
5419 }
5420
David03ad33c2023-02-15 23:35:51 -06005421 detail::json_const_iterator targetIt;
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005422 if (detail::FindMember(o, "target", targetIt) &&
5423 detail::IsObject(detail::GetValue(targetIt))) {
David03ad33c2023-02-15 23:35:51 -06005424 const detail::json &target_object = detail::GetValue(targetIt);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005425
Jack Mousseau283b5522023-01-15 11:45:45 -08005426 ParseIntegerProperty(&targetIndex, err, target_object, "node", false);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005427
5428 if (!ParseStringProperty(&channel->target_path, err, target_object, "path",
5429 true)) {
5430 if (err) {
5431 (*err) += "`path` field is missing in animation.channels.target\n";
5432 }
5433 return false;
5434 }
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09005435 ParseExtensionsProperty(&channel->target_extensions, err, target_object);
David Siegeld852f502023-06-05 23:28:05 +02005436 ParseExtrasProperty(&channel->target_extras, target_object);
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09005437 if (store_original_json_for_extras_and_extensions) {
David Siegeld852f502023-06-05 23:28:05 +02005438 {
5439 detail::json_const_iterator it;
5440 if (detail::FindMember(target_object, "extensions", it)) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005441 channel->target_extensions_json_string =
5442 detail::JsonToString(detail::GetValue(it));
David Siegeld852f502023-06-05 23:28:05 +02005443 }
5444 }
5445 {
5446 detail::json_const_iterator it;
5447 if (detail::FindMember(target_object, "extras", it)) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005448 channel->target_extras_json_string =
5449 detail::JsonToString(detail::GetValue(it));
David Siegeld852f502023-06-05 23:28:05 +02005450 }
Selmar Kok973d9b32020-01-21 18:45:24 +01005451 }
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09005452 }
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005453 }
5454
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005455 channel->sampler = samplerIndex;
5456 channel->target_node = targetIndex;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005457
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005458 ParseExtrasAndExtensions(channel, err, o,
5459 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005460
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005461 return true;
5462}
5463
5464static bool ParseAnimation(Animation *animation, std::string *err,
David03ad33c2023-02-15 23:35:51 -06005465 const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005466 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005467 {
David03ad33c2023-02-15 23:35:51 -06005468 detail::json_const_iterator channelsIt;
David1f9a4b92023-02-15 22:56:18 -06005469 if (detail::FindMember(o, "channels", channelsIt) &&
5470 detail::IsArray(detail::GetValue(channelsIt))) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005471 detail::json_const_array_iterator channelEnd =
5472 detail::ArrayEnd(detail::GetValue(channelsIt));
5473 for (detail::json_const_array_iterator i =
5474 detail::ArrayBegin(detail::GetValue(channelsIt));
jrkooncecba5d6c2019-08-29 11:26:22 -05005475 i != channelEnd; ++i) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005476 AnimationChannel channel;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005477 if (ParseAnimationChannel(
5478 &channel, err, *i,
5479 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005480 // Only add the channel if the parsing succeeds.
jrkooncecba5d6c2019-08-29 11:26:22 -05005481 animation->channels.emplace_back(std::move(channel));
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005482 }
5483 }
5484 }
5485 }
5486
5487 {
David03ad33c2023-02-15 23:35:51 -06005488 detail::json_const_iterator samplerIt;
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005489 if (detail::FindMember(o, "samplers", samplerIt) &&
5490 detail::IsArray(detail::GetValue(samplerIt))) {
David03ad33c2023-02-15 23:35:51 -06005491 const detail::json &sampler_array = detail::GetValue(samplerIt);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005492
David03ad33c2023-02-15 23:35:51 -06005493 detail::json_const_array_iterator it = detail::ArrayBegin(sampler_array);
5494 detail::json_const_array_iterator itEnd = detail::ArrayEnd(sampler_array);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005495
jrkooncecba5d6c2019-08-29 11:26:22 -05005496 for (; it != itEnd; ++it) {
David03ad33c2023-02-15 23:35:51 -06005497 const detail::json &s = *it;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005498
5499 AnimationSampler sampler;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005500 int inputIndex = -1;
5501 int outputIndex = -1;
5502 if (!ParseIntegerProperty(&inputIndex, err, s, "input", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005503 if (err) {
5504 (*err) += "`input` field is missing in animation.sampler\n";
5505 }
5506 return false;
5507 }
Evan Birenbaum6bdffed2019-02-14 13:30:57 -08005508 ParseStringProperty(&sampler.interpolation, err, s, "interpolation",
5509 false);
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005510 if (!ParseIntegerProperty(&outputIndex, err, s, "output", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005511 if (err) {
5512 (*err) += "`output` field is missing in animation.sampler\n";
5513 }
5514 return false;
5515 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005516 sampler.input = inputIndex;
5517 sampler.output = outputIndex;
David Siegel22cafa12023-06-05 22:18:59 +02005518 ParseExtrasAndExtensions(&sampler, err, o,
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005519 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005520
jrkooncecba5d6c2019-08-29 11:26:22 -05005521 animation->samplers.emplace_back(std::move(sampler));
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005522 }
5523 }
5524 }
5525
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005526 ParseStringProperty(&animation->name, err, o, "name", false);
5527
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005528 ParseExtrasAndExtensions(animation, err, o,
5529 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005530
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005531 return true;
5532}
5533
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005534static bool ParseSampler(Sampler *sampler, std::string *err,
5535 const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005536 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujitac2615632016-06-19 21:56:06 +09005537 ParseStringProperty(&sampler->name, err, o, "name", false);
5538
Syoyo Fujitaee179b22019-08-16 13:11:30 +09005539 int minFilter = -1;
5540 int magFilter = -1;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005541 int wrapS = TINYGLTF_TEXTURE_WRAP_REPEAT;
5542 int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT;
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09005543 // int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005544 ParseIntegerProperty(&minFilter, err, o, "minFilter", false);
5545 ParseIntegerProperty(&magFilter, err, o, "magFilter", false);
5546 ParseIntegerProperty(&wrapS, err, o, "wrapS", false);
5547 ParseIntegerProperty(&wrapT, err, o, "wrapT", false);
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09005548 // ParseIntegerProperty(&wrapR, err, o, "wrapR", false); // tinygltf
5549 // extension
Syoyo Fujitaee179b22019-08-16 13:11:30 +09005550
imallettd9ce9eb2022-10-07 10:37:09 -07005551 // TODO(syoyo): Check the value is allowed one.
Syoyo Fujitaee179b22019-08-16 13:11:30 +09005552 // (e.g. we allow 9728(NEAREST), but don't allow 9727)
Syoyo Fujitac2615632016-06-19 21:56:06 +09005553
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005554 sampler->minFilter = minFilter;
5555 sampler->magFilter = magFilter;
5556 sampler->wrapS = wrapS;
5557 sampler->wrapT = wrapT;
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09005558 // sampler->wrapR = wrapR;
Syoyo Fujitac2615632016-06-19 21:56:06 +09005559
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005560 ParseExtrasAndExtensions(sampler, err, o,
5561 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005562
Syoyo Fujitac2615632016-06-19 21:56:06 +09005563 return true;
5564}
5565
David03ad33c2023-02-15 23:35:51 -06005566static bool ParseSkin(Skin *skin, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005567 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09005568 ParseStringProperty(&skin->name, err, o, "name", false, "Skin");
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005569
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005570 std::vector<int> joints;
5571 if (!ParseIntegerArrayProperty(&joints, err, o, "joints", false, "Skin")) {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005572 return false;
5573 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005574 skin->joints = std::move(joints);
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005575
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005576 int skeleton = -1;
5577 ParseIntegerProperty(&skeleton, err, o, "skeleton", false, "Skin");
5578 skin->skeleton = skeleton;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005579
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005580 int invBind = -1;
5581 ParseIntegerProperty(&invBind, err, o, "inverseBindMatrices", true, "Skin");
5582 skin->inverseBindMatrices = invBind;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005583
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005584 ParseExtrasAndExtensions(skin, err, o,
5585 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005586
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005587 return true;
5588}
5589
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005590static bool ParsePerspectiveCamera(
David03ad33c2023-02-15 23:35:51 -06005591 PerspectiveCamera *camera, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005592 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005593 double yfov = 0.0;
5594 if (!ParseNumberProperty(&yfov, err, o, "yfov", true, "OrthographicCamera")) {
5595 return false;
5596 }
5597
5598 double znear = 0.0;
5599 if (!ParseNumberProperty(&znear, err, o, "znear", true,
5600 "PerspectiveCamera")) {
5601 return false;
5602 }
5603
5604 double aspectRatio = 0.0; // = invalid
5605 ParseNumberProperty(&aspectRatio, err, o, "aspectRatio", false,
5606 "PerspectiveCamera");
5607
5608 double zfar = 0.0; // = invalid
5609 ParseNumberProperty(&zfar, err, o, "zfar", false, "PerspectiveCamera");
5610
Selmar Kok31cb7f92018-10-03 15:39:05 +02005611 camera->aspectRatio = aspectRatio;
5612 camera->zfar = zfar;
5613 camera->yfov = yfov;
5614 camera->znear = znear;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005615
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005616 ParseExtrasAndExtensions(camera, err, o,
5617 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005618
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005619 // TODO(syoyo): Validate parameter values.
5620
5621 return true;
5622}
5623
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005624static bool ParseSpotLight(SpotLight *light, std::string *err,
5625 const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005626 bool store_original_json_for_extras_and_extensions) {
Johan Bowald52936a02019-07-17 09:06:45 +02005627 ParseNumberProperty(&light->innerConeAngle, err, o, "innerConeAngle", false);
5628 ParseNumberProperty(&light->outerConeAngle, err, o, "outerConeAngle", false);
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005629
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005630 ParseExtrasAndExtensions(light, err, o,
5631 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005632
Johan Bowald52936a02019-07-17 09:06:45 +02005633 // TODO(syoyo): Validate parameter values.
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005634
Johan Bowald52936a02019-07-17 09:06:45 +02005635 return true;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005636}
5637
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005638static bool ParseOrthographicCamera(
David03ad33c2023-02-15 23:35:51 -06005639 OrthographicCamera *camera, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005640 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005641 double xmag = 0.0;
5642 if (!ParseNumberProperty(&xmag, err, o, "xmag", true, "OrthographicCamera")) {
5643 return false;
5644 }
5645
5646 double ymag = 0.0;
5647 if (!ParseNumberProperty(&ymag, err, o, "ymag", true, "OrthographicCamera")) {
5648 return false;
5649 }
5650
5651 double zfar = 0.0;
5652 if (!ParseNumberProperty(&zfar, err, o, "zfar", true, "OrthographicCamera")) {
5653 return false;
5654 }
5655
5656 double znear = 0.0;
5657 if (!ParseNumberProperty(&znear, err, o, "znear", true,
5658 "OrthographicCamera")) {
5659 return false;
5660 }
5661
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005662 ParseExtrasAndExtensions(camera, err, o,
5663 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005664
Selmar Kok31cb7f92018-10-03 15:39:05 +02005665 camera->xmag = xmag;
5666 camera->ymag = ymag;
5667 camera->zfar = zfar;
5668 camera->znear = znear;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005669
5670 // TODO(syoyo): Validate parameter values.
5671
5672 return true;
5673}
5674
David03ad33c2023-02-15 23:35:51 -06005675static bool ParseCamera(Camera *camera, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005676 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005677 if (!ParseStringProperty(&camera->type, err, o, "type", true, "Camera")) {
5678 return false;
5679 }
5680
5681 if (camera->type.compare("orthographic") == 0) {
David03ad33c2023-02-15 23:35:51 -06005682 detail::json_const_iterator orthoIt;
David1f9a4b92023-02-15 22:56:18 -06005683 if (!detail::FindMember(o, "orthographic", orthoIt)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005684 if (err) {
5685 std::stringstream ss;
imallettd9ce9eb2022-10-07 10:37:09 -07005686 ss << "Orthographic camera description not found." << std::endl;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005687 (*err) += ss.str();
5688 }
5689 return false;
5690 }
5691
David03ad33c2023-02-15 23:35:51 -06005692 const detail::json &v = detail::GetValue(orthoIt);
David1f9a4b92023-02-15 22:56:18 -06005693 if (!detail::IsObject(v)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005694 if (err) {
5695 std::stringstream ss;
5696 ss << "\"orthographic\" is not a JSON object." << std::endl;
5697 (*err) += ss.str();
5698 }
5699 return false;
5700 }
5701
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005702 if (!ParseOrthographicCamera(
5703 &camera->orthographic, err, v,
5704 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005705 return false;
5706 }
5707 } else if (camera->type.compare("perspective") == 0) {
David03ad33c2023-02-15 23:35:51 -06005708 detail::json_const_iterator perspIt;
David1f9a4b92023-02-15 22:56:18 -06005709 if (!detail::FindMember(o, "perspective", perspIt)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005710 if (err) {
5711 std::stringstream ss;
5712 ss << "Perspective camera description not found." << std::endl;
5713 (*err) += ss.str();
5714 }
5715 return false;
5716 }
5717
David03ad33c2023-02-15 23:35:51 -06005718 const detail::json &v = detail::GetValue(perspIt);
David1f9a4b92023-02-15 22:56:18 -06005719 if (!detail::IsObject(v)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005720 if (err) {
5721 std::stringstream ss;
5722 ss << "\"perspective\" is not a JSON object." << std::endl;
5723 (*err) += ss.str();
5724 }
5725 return false;
5726 }
5727
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005728 if (!ParsePerspectiveCamera(
5729 &camera->perspective, err, v,
5730 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005731 return false;
5732 }
5733 } else {
5734 if (err) {
5735 std::stringstream ss;
5736 ss << "Invalid camera type: \"" << camera->type
5737 << "\". Must be \"perspective\" or \"orthographic\"" << std::endl;
5738 (*err) += ss.str();
5739 }
5740 return false;
5741 }
5742
5743 ParseStringProperty(&camera->name, err, o, "name", false);
5744
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005745 ParseExtrasAndExtensions(camera, err, o,
5746 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005747
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005748 return true;
5749}
5750
David03ad33c2023-02-15 23:35:51 -06005751static bool ParseLight(Light *light, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005752 bool store_original_json_for_extras_and_extensions) {
Johan Bowald52936a02019-07-17 09:06:45 +02005753 if (!ParseStringProperty(&light->type, err, o, "type", true)) {
5754 return false;
5755 }
5756
5757 if (light->type == "spot") {
David03ad33c2023-02-15 23:35:51 -06005758 detail::json_const_iterator spotIt;
David1f9a4b92023-02-15 22:56:18 -06005759 if (!detail::FindMember(o, "spot", spotIt)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005760 if (err) {
5761 std::stringstream ss;
5762 ss << "Spot light description not found." << std::endl;
5763 (*err) += ss.str();
5764 }
5765 return false;
Benjamin Schmithüsen4557b6a2019-07-09 16:55:55 +02005766 }
5767
David03ad33c2023-02-15 23:35:51 -06005768 const detail::json &v = detail::GetValue(spotIt);
David1f9a4b92023-02-15 22:56:18 -06005769 if (!detail::IsObject(v)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005770 if (err) {
5771 std::stringstream ss;
5772 ss << "\"spot\" is not a JSON object." << std::endl;
5773 (*err) += ss.str();
5774 }
5775 return false;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005776 }
5777
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005778 if (!ParseSpotLight(&light->spot, err, v,
5779 store_original_json_for_extras_and_extensions)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005780 return false;
5781 }
5782 }
5783
5784 ParseStringProperty(&light->name, err, o, "name", false);
5785 ParseNumberArrayProperty(&light->color, err, o, "color", false);
5786 ParseNumberProperty(&light->range, err, o, "range", false);
5787 ParseNumberProperty(&light->intensity, err, o, "intensity", false);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005788
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005789 ParseExtrasAndExtensions(light, err, o,
5790 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005791
Johan Bowald52936a02019-07-17 09:06:45 +02005792 return true;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005793}
5794
Baranob_Ilya78864c82023-06-12 10:43:52 +04005795static bool ParsePositionalEmitter(
5796 PositionalEmitter *positional, std::string *err, const detail::json &o,
5797 bool store_original_json_for_extras_and_extensions) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005798 ParseNumberProperty(&positional->coneInnerAngle, err, o, "coneInnerAngle",
5799 false);
5800 ParseNumberProperty(&positional->coneOuterAngle, err, o, "coneOuterAngle",
5801 false);
5802 ParseNumberProperty(&positional->coneOuterGain, err, o, "coneOuterGain",
5803 false);
Baranob_Ilya78864c82023-06-12 10:43:52 +04005804 ParseNumberProperty(&positional->maxDistance, err, o, "maxDistance", false);
5805 ParseNumberProperty(&positional->refDistance, err, o, "refDistance", false);
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005806 ParseNumberProperty(&positional->rolloffFactor, err, o, "rolloffFactor",
5807 false);
Baranob_Ilya78864c82023-06-12 10:43:52 +04005808
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005809 ParseExtrasAndExtensions(positional, err, o,
5810 store_original_json_for_extras_and_extensions);
Baranob_Ilya78864c82023-06-12 10:43:52 +04005811
5812 return true;
5813}
5814
5815static bool ParseAudioEmitter(
5816 AudioEmitter *emitter, std::string *err, const detail::json &o,
5817 bool store_original_json_for_extras_and_extensions) {
5818 if (!ParseStringProperty(&emitter->type, err, o, "type", true)) {
5819 return false;
5820 }
5821
5822 if (emitter->type == "positional") {
5823 detail::json_const_iterator positionalIt;
5824 if (!detail::FindMember(o, "positional", positionalIt)) {
5825 if (err) {
5826 std::stringstream ss;
5827 ss << "Positional emitter description not found." << std::endl;
5828 (*err) += ss.str();
5829 }
5830 return false;
5831 }
5832
5833 const detail::json &v = detail::GetValue(positionalIt);
5834 if (!detail::IsObject(v)) {
5835 if (err) {
5836 std::stringstream ss;
5837 ss << "\"positional\" is not a JSON object." << std::endl;
5838 (*err) += ss.str();
5839 }
5840 return false;
5841 }
5842
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005843 if (!ParsePositionalEmitter(
5844 &emitter->positional, err, v,
5845 store_original_json_for_extras_and_extensions)) {
Baranob_Ilya78864c82023-06-12 10:43:52 +04005846 return false;
5847 }
5848 }
5849
5850 ParseStringProperty(&emitter->name, err, o, "name", false);
5851 ParseNumberProperty(&emitter->gain, err, o, "gain", false);
5852 ParseBooleanProperty(&emitter->loop, err, o, "loop", false);
5853 ParseBooleanProperty(&emitter->playing, err, o, "playing", false);
5854 ParseStringProperty(&emitter->distanceModel, err, o, "distanceModel", false);
5855 ParseIntegerProperty(&emitter->source, err, o, "source", true);
5856
5857 ParseExtrasAndExtensions(emitter, err, o,
5858 store_original_json_for_extras_and_extensions);
5859
5860 return true;
5861}
5862
5863static bool ParseAudioSource(
5864 AudioSource *source, std::string *err, const detail::json &o,
5865 bool store_original_json_for_extras_and_extensions) {
5866 ParseStringProperty(&source->name, err, o, "name", false);
5867 ParseStringProperty(&source->uri, err, o, "uri", false);
5868
5869 if (source->uri.empty()) {
5870 ParseIntegerProperty(&source->bufferView, err, o, "bufferView", true);
5871 ParseStringProperty(&source->mimeType, err, o, "mimeType", true);
5872 }
5873
5874 ParseExtrasAndExtensions(source, err, o,
5875 store_original_json_for_extras_and_extensions);
5876
5877 return true;
5878}
5879
David Siegelbec8a6d2023-06-06 15:36:07 +02005880namespace detail {
5881
5882template <typename Callback>
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005883bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
David Siegelbec8a6d2023-06-06 15:36:07 +02005884 detail::json_const_iterator itm;
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005885 if (detail::FindMember(_v, member, itm) &&
5886 detail::IsArray(detail::GetValue(itm))) {
David Siegelbec8a6d2023-06-06 15:36:07 +02005887 const detail::json &root = detail::GetValue(itm);
5888 auto it = detail::ArrayBegin(root);
5889 auto end = detail::ArrayEnd(root);
5890 for (; it != end; ++it) {
5891 if (!cb(*it)) return false;
5892 }
5893 }
5894 return true;
5895};
5896
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005897} // end of namespace detail
David Siegelbec8a6d2023-06-06 15:36:07 +02005898
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09005899bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005900 const char *json_str,
5901 unsigned int json_str_length,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09005902 const std::string &base_dir,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005903 unsigned int check_sections) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005904 if (json_str_length < 4) {
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09005905 if (err) {
5906 (*err) = "JSON string too short.\n";
5907 }
5908 return false;
5909 }
5910
David03ad33c2023-02-15 23:35:51 -06005911 detail::JsonDocument v;
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005912
Syoyo Fujitad42767e2018-03-15 21:52:00 -05005913#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
5914 defined(_CPPUNWIND)) && \
Syoyo Fujitac4166e42020-01-08 02:38:01 +09005915 !defined(TINYGLTF_NOEXCEPTION)
Syoyo Fujitacdf84902017-08-01 20:12:08 +09005916 try {
David03ad33c2023-02-15 23:35:51 -06005917 detail::JsonParse(v, json_str, json_str_length, true);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005918
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005919 } catch (const std::exception &e) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005920 if (err) {
Syoyo Fujitacdf84902017-08-01 20:12:08 +09005921 (*err) = e.what();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005922 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005923 return false;
5924 }
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005925#else
5926 {
David03ad33c2023-02-15 23:35:51 -06005927 detail::JsonParse(v, json_str, json_str_length);
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005928
David1f9a4b92023-02-15 22:56:18 -06005929 if (!detail::IsObject(v)) {
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005930 // Assume parsing was failed.
5931 if (err) {
5932 (*err) = "Failed to parse JSON object\n";
5933 }
5934 return false;
5935 }
5936 }
5937#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005938
David1f9a4b92023-02-15 22:56:18 -06005939 if (!detail::IsObject(v)) {
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09005940 // root is not an object.
5941 if (err) {
5942 (*err) = "Root element is not a JSON object\n";
5943 }
5944 return false;
5945 }
5946
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02005947 {
5948 bool version_found = false;
David03ad33c2023-02-15 23:35:51 -06005949 detail::json_const_iterator it;
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005950 if (detail::FindMember(v, "asset", it) &&
5951 detail::IsObject(detail::GetValue(it))) {
David1f9a4b92023-02-15 22:56:18 -06005952 auto &itObj = detail::GetValue(it);
David03ad33c2023-02-15 23:35:51 -06005953 detail::json_const_iterator version_it;
jrkooncecba5d6c2019-08-29 11:26:22 -05005954 std::string versionStr;
David1f9a4b92023-02-15 22:56:18 -06005955 if (detail::FindMember(itObj, "version", version_it) &&
5956 detail::GetString(detail::GetValue(version_it), versionStr)) {
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02005957 version_found = true;
5958 }
5959 }
5960 if (version_found) {
5961 // OK
5962 } else if (check_sections & REQUIRE_VERSION) {
5963 if (err) {
5964 (*err) += "\"asset\" object not found in .gltf or not an object type\n";
5965 }
5966 return false;
5967 }
5968 }
5969
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005970 // scene is not mandatory.
Syoyo Fujita5b407452017-06-04 17:42:41 +09005971 // FIXME Maybe a better way to handle it than removing the code
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005972
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005973 auto IsArrayMemberPresent = [](const detail::json &_v,
5974 const char *name) -> bool {
David03ad33c2023-02-15 23:35:51 -06005975 detail::json_const_iterator it;
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005976 return detail::FindMember(_v, name, it) &&
5977 detail::IsArray(detail::GetValue(it));
jrkooncecba5d6c2019-08-29 11:26:22 -05005978 };
5979
Syoyo Fujita83675312017-12-02 21:14:13 +09005980 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005981 if ((check_sections & REQUIRE_SCENES) &&
5982 !IsArrayMemberPresent(v, "scenes")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005983 if (err) {
5984 (*err) += "\"scenes\" object not found in .gltf or not an array type\n";
5985 }
5986 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005987 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005988 }
5989
Syoyo Fujita83675312017-12-02 21:14:13 +09005990 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005991 if ((check_sections & REQUIRE_NODES) && !IsArrayMemberPresent(v, "nodes")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005992 if (err) {
5993 (*err) += "\"nodes\" object not found in .gltf\n";
5994 }
5995 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005996 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005997 }
5998
Syoyo Fujita83675312017-12-02 21:14:13 +09005999 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006000 if ((check_sections & REQUIRE_ACCESSORS) &&
6001 !IsArrayMemberPresent(v, "accessors")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09006002 if (err) {
6003 (*err) += "\"accessors\" object not found in .gltf\n";
6004 }
6005 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09006006 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09006007 }
6008
Syoyo Fujita83675312017-12-02 21:14:13 +09006009 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006010 if ((check_sections & REQUIRE_BUFFERS) &&
6011 !IsArrayMemberPresent(v, "buffers")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09006012 if (err) {
6013 (*err) += "\"buffers\" object not found in .gltf\n";
6014 }
6015 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09006016 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09006017 }
6018
Syoyo Fujita83675312017-12-02 21:14:13 +09006019 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006020 if ((check_sections & REQUIRE_BUFFER_VIEWS) &&
6021 !IsArrayMemberPresent(v, "bufferViews")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09006022 if (err) {
6023 (*err) += "\"bufferViews\" object not found in .gltf\n";
6024 }
6025 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09006026 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09006027 }
6028
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00006029 model->buffers.clear();
6030 model->bufferViews.clear();
6031 model->accessors.clear();
6032 model->meshes.clear();
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09006033 model->cameras.clear();
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00006034 model->nodes.clear();
6035 model->extensionsUsed.clear();
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00006036 model->extensionsRequired.clear();
Selmar09d2ff12018-03-15 17:30:42 +01006037 model->extensions.clear();
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00006038 model->defaultScene = -1;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09006039
Syoyo Fujita83675312017-12-02 21:14:13 +09006040 // 1. Parse Asset
6041 {
David03ad33c2023-02-15 23:35:51 -06006042 detail::json_const_iterator it;
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006043 if (detail::FindMember(v, "asset", it) &&
6044 detail::IsObject(detail::GetValue(it))) {
David03ad33c2023-02-15 23:35:51 -06006045 const detail::json &root = detail::GetValue(it);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09006046
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006047 ParseAsset(&model->asset, err, root,
6048 store_original_json_for_extras_and_extensions_);
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00006049 }
6050 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09006051
David Siegelbec8a6d2023-06-06 15:36:07 +02006052 using detail::ForEachInArray;
jrkooncecba5d6c2019-08-29 11:26:22 -05006053
jrkooncecba5d6c2019-08-29 11:26:22 -05006054 // 2. Parse extensionUsed
6055 {
David03ad33c2023-02-15 23:35:51 -06006056 ForEachInArray(v, "extensionsUsed", [&](const detail::json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006057 std::string str;
David1f9a4b92023-02-15 22:56:18 -06006058 detail::GetString(o, str);
jrkooncecba5d6c2019-08-29 11:26:22 -05006059 model->extensionsUsed.emplace_back(std::move(str));
6060 return true;
6061 });
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09006062 }
6063
Syoyo Fujita83675312017-12-02 21:14:13 +09006064 {
David03ad33c2023-02-15 23:35:51 -06006065 ForEachInArray(v, "extensionsRequired", [&](const detail::json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006066 std::string str;
David1f9a4b92023-02-15 22:56:18 -06006067 detail::GetString(o, str);
jrkooncecba5d6c2019-08-29 11:26:22 -05006068 model->extensionsRequired.emplace_back(std::move(str));
6069 return true;
6070 });
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09006071 }
6072
Syoyo Fujita83675312017-12-02 21:14:13 +09006073 // 3. Parse Buffer
6074 {
David03ad33c2023-02-15 23:35:51 -06006075 bool success = ForEachInArray(v, "buffers", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006076 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006077 if (err) {
6078 (*err) += "`buffers' does not contain an JSON object.";
Syoyo Fujitabeded612016-05-01 20:03:43 +09006079 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006080 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006081 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006082 Buffer buffer;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006083 if (!ParseBuffer(&buffer, err, o,
6084 store_original_json_for_extras_and_extensions_, &fs,
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006085 &uri_cb, base_dir, max_external_file_size_, is_binary_,
6086 bin_data_, bin_size_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006087 return false;
6088 }
6089
6090 model->buffers.emplace_back(std::move(buffer));
6091 return true;
6092 });
6093
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006094 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006095 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09006096 }
6097 }
Syoyo Fujita83675312017-12-02 21:14:13 +09006098 // 4. Parse BufferView
6099 {
David03ad33c2023-02-15 23:35:51 -06006100 bool success = ForEachInArray(v, "bufferViews", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006101 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006102 if (err) {
6103 (*err) += "`bufferViews' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09006104 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006105 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006106 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006107 BufferView bufferView;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006108 if (!ParseBufferView(&bufferView, err, o,
6109 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006110 return false;
6111 }
6112
6113 model->bufferViews.emplace_back(std::move(bufferView));
6114 return true;
6115 });
6116
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006117 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006118 return false;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09006119 }
6120 }
6121
Syoyo Fujita83675312017-12-02 21:14:13 +09006122 // 5. Parse Accessor
6123 {
David03ad33c2023-02-15 23:35:51 -06006124 bool success = ForEachInArray(v, "accessors", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006125 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006126 if (err) {
6127 (*err) += "`accessors' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09006128 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006129 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006130 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006131 Accessor accessor;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006132 if (!ParseAccessor(&accessor, err, o,
6133 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006134 return false;
6135 }
6136
6137 model->accessors.emplace_back(std::move(accessor));
6138 return true;
6139 });
6140
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006141 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006142 return false;
Syoyo Fujitac2615632016-06-19 21:56:06 +09006143 }
6144 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09006145
Syoyo Fujita83675312017-12-02 21:14:13 +09006146 // 6. Parse Mesh
6147 {
David03ad33c2023-02-15 23:35:51 -06006148 bool success = ForEachInArray(v, "meshes", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006149 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006150 if (err) {
6151 (*err) += "`meshes' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09006152 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006153 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006154 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006155 Mesh mesh;
Nyall Dawson6e3d6662023-08-24 13:25:34 +10006156 if (!ParseMesh(&mesh, model, err, warn, o,
6157 store_original_json_for_extras_and_extensions_,
6158 strictness_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006159 return false;
6160 }
6161
6162 model->meshes.emplace_back(std::move(mesh));
6163 return true;
6164 });
6165
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006166 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006167 return false;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09006168 }
6169 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01006170
viperscape9df05802018-12-05 14:11:01 -05006171 // Assign missing bufferView target types
6172 // - Look for missing Mesh indices
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01006173 // - Look for missing Mesh attributes
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006174 for (auto &mesh : model->meshes) {
6175 for (auto &primitive : mesh.primitives) {
6176 if (primitive.indices >
6177 -1) // has indices from parsing step, must be Element Array Buffer
viperscape9df05802018-12-05 14:11:01 -05006178 {
Jeff McGlynn89152522019-04-25 16:33:56 -07006179 if (size_t(primitive.indices) >= model->accessors.size()) {
6180 if (err) {
6181 (*err) += "primitive indices accessor out of bounds";
6182 }
6183 return false;
6184 }
6185
haroonq8098a9e2023-10-05 11:16:52 +01006186 const auto bufferView =
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006187 model->accessors[size_t(primitive.indices)].bufferView;
haroonq8098a9e2023-10-05 11:16:52 +01006188 if (bufferView < 0) {
6189 // skip, bufferView could be null(-1) for certain extensions
Syoyo Fujita8387fdb2023-12-04 22:50:49 +09006190 } else if (size_t(bufferView) >= model->bufferViews.size()) {
Jeff McGlynn89152522019-04-25 16:33:56 -07006191 if (err) {
6192 (*err) += "accessor[" + std::to_string(primitive.indices) +
6193 "] invalid bufferView";
6194 }
6195 return false;
haroonq8098a9e2023-10-05 11:16:52 +01006196 } else {
6197 model->bufferViews[size_t(bufferView)].target =
6198 TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER;
6199 // we could optionally check if accessors' bufferView type is Scalar, as
6200 // it should be
Jeff McGlynn89152522019-04-25 16:33:56 -07006201 }
viperscape9df05802018-12-05 14:11:01 -05006202 }
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01006203
6204 for (auto &attribute : primitive.attributes) {
Nirmal Patele4132162022-09-06 09:16:31 -07006205 const auto accessorsIndex = size_t(attribute.second);
6206 if (accessorsIndex < model->accessors.size()) {
6207 const auto bufferView = model->accessors[accessorsIndex].bufferView;
6208 // bufferView could be null(-1) for sparse morph target
imallett56e10982022-10-07 10:35:16 -07006209 if (bufferView >= 0 && bufferView < (int)model->bufferViews.size()) {
Nirmal Patele4132162022-09-06 09:16:31 -07006210 model->bufferViews[size_t(bufferView)].target =
6211 TINYGLTF_TARGET_ARRAY_BUFFER;
6212 }
6213 }
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01006214 }
Marcin Kacprzakc3d67162020-01-22 13:13:35 +01006215
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09006216 for (auto &target : primitive.targets) {
6217 for (auto &attribute : target) {
Nirmal Patele4132162022-09-06 09:16:31 -07006218 const auto accessorsIndex = size_t(attribute.second);
6219 if (accessorsIndex < model->accessors.size()) {
6220 const auto bufferView = model->accessors[accessorsIndex].bufferView;
6221 // bufferView could be null(-1) for sparse morph target
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006222 if (bufferView >= 0 &&
6223 bufferView < (int)model->bufferViews.size()) {
Nirmal Patele4132162022-09-06 09:16:31 -07006224 model->bufferViews[size_t(bufferView)].target =
6225 TINYGLTF_TARGET_ARRAY_BUFFER;
6226 }
Rahul Sheth125e4a22020-07-13 13:56:50 -04006227 }
Marcin Kacprzakc3d67162020-01-22 13:13:35 +01006228 }
6229 }
viperscape9df05802018-12-05 14:11:01 -05006230 }
6231 }
6232
Syoyo Fujita83675312017-12-02 21:14:13 +09006233 // 7. Parse Node
6234 {
David03ad33c2023-02-15 23:35:51 -06006235 bool success = ForEachInArray(v, "nodes", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006236 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006237 if (err) {
6238 (*err) += "`nodes' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006239 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006240 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006241 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006242 Node node;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006243 if (!ParseNode(&node, err, o,
6244 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006245 return false;
6246 }
6247
6248 model->nodes.emplace_back(std::move(node));
6249 return true;
6250 });
6251
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006252 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006253 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006254 }
6255 }
6256
6257 // 8. Parse scenes.
6258 {
David03ad33c2023-02-15 23:35:51 -06006259 bool success = ForEachInArray(v, "scenes", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006260 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006261 if (err) {
6262 (*err) += "`scenes' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006263 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006264 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006265 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006266
6267 Scene scene;
Baranob_Ilya879cb472023-06-12 13:35:05 +04006268 if (!ParseScene(&scene, err, o,
6269 store_original_json_for_extras_and_extensions_)) {
6270 return false;
6271 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006272
jrkooncecba5d6c2019-08-29 11:26:22 -05006273 model->scenes.emplace_back(std::move(scene));
6274 return true;
6275 });
6276
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006277 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006278 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006279 }
6280 }
6281
6282 // 9. Parse default scenes.
6283 {
David03ad33c2023-02-15 23:35:51 -06006284 detail::json_const_iterator rootIt;
jrkooncecba5d6c2019-08-29 11:26:22 -05006285 int iVal;
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006286 if (detail::FindMember(v, "scene", rootIt) &&
6287 detail::GetInt(detail::GetValue(rootIt), iVal)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006288 model->defaultScene = iVal;
Syoyo Fujita83675312017-12-02 21:14:13 +09006289 }
6290 }
6291
6292 // 10. Parse Material
6293 {
David03ad33c2023-02-15 23:35:51 -06006294 bool success = ForEachInArray(v, "materials", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006295 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006296 if (err) {
6297 (*err) += "`materials' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006298 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006299 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006300 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006301 Material material;
6302 ParseStringProperty(&material.name, err, o, "name", false);
6303
Nyall Dawson02e8b8d2023-08-24 10:25:20 +10006304 if (!ParseMaterial(&material, err, warn, o,
Nyall Dawson8c85d5e2023-08-28 12:56:09 +10006305 store_original_json_for_extras_and_extensions_,
6306 strictness_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006307 return false;
6308 }
6309
6310 model->materials.emplace_back(std::move(material));
6311 return true;
6312 });
6313
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006314 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006315 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006316 }
6317 }
6318
6319 // 11. Parse Image
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09006320 void *load_image_user_data{nullptr};
6321
6322 LoadImageDataOption load_image_option;
6323
6324 if (user_image_loader_) {
6325 // Use user supplied pointer
6326 load_image_user_data = load_image_user_data_;
6327 } else {
6328 load_image_option.preserve_channels = preserve_image_channels_;
6329 load_image_user_data = reinterpret_cast<void *>(&load_image_option);
6330 }
6331
Syoyo Fujita83675312017-12-02 21:14:13 +09006332 {
jrkooncecba5d6c2019-08-29 11:26:22 -05006333 int idx = 0;
David03ad33c2023-02-15 23:35:51 -06006334 bool success = ForEachInArray(v, "images", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006335 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006336 if (err) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006337 (*err) += "image[" + std::to_string(idx) + "] is not a JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006338 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006339 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006340 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006341 Image image;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006342 if (!ParseImage(&image, idx, err, warn, o,
6343 store_original_json_for_extras_and_extensions_, base_dir,
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006344 max_external_file_size_, &fs, &uri_cb,
6345 &this->LoadImageData, load_image_user_data)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006346 return false;
6347 }
6348
6349 if (image.bufferView != -1) {
6350 // Load image from the buffer view.
6351 if (size_t(image.bufferView) >= model->bufferViews.size()) {
6352 if (err) {
6353 std::stringstream ss;
6354 ss << "image[" << idx << "] bufferView \"" << image.bufferView
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006355 << "\" not found in the scene." << std::endl;
jrkooncecba5d6c2019-08-29 11:26:22 -05006356 (*err) += ss.str();
6357 }
6358 return false;
6359 }
6360
6361 const BufferView &bufferView =
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006362 model->bufferViews[size_t(image.bufferView)];
jrkooncecba5d6c2019-08-29 11:26:22 -05006363 if (size_t(bufferView.buffer) >= model->buffers.size()) {
6364 if (err) {
6365 std::stringstream ss;
6366 ss << "image[" << idx << "] buffer \"" << bufferView.buffer
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006367 << "\" not found in the scene." << std::endl;
jrkooncecba5d6c2019-08-29 11:26:22 -05006368 (*err) += ss.str();
6369 }
6370 return false;
6371 }
6372 const Buffer &buffer = model->buffers[size_t(bufferView.buffer)];
6373
6374 if (*LoadImageData == nullptr) {
6375 if (err) {
6376 (*err) += "No LoadImageData callback specified.\n";
6377 }
6378 return false;
6379 }
6380 bool ret = LoadImageData(
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006381 &image, idx, err, warn, image.width, image.height,
6382 &buffer.data[bufferView.byteOffset],
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09006383 static_cast<int>(bufferView.byteLength), load_image_user_data);
jrkooncecba5d6c2019-08-29 11:26:22 -05006384 if (!ret) {
6385 return false;
6386 }
6387 }
6388
6389 model->images.emplace_back(std::move(image));
6390 ++idx;
6391 return true;
6392 });
6393
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006394 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006395 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006396 }
6397 }
6398
6399 // 12. Parse Texture
6400 {
David03ad33c2023-02-15 23:35:51 -06006401 bool success = ForEachInArray(v, "textures", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006402 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006403 if (err) {
6404 (*err) += "`textures' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006405 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006406 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006407 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006408 Texture texture;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006409 if (!ParseTexture(&texture, err, o,
6410 store_original_json_for_extras_and_extensions_,
6411 base_dir)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006412 return false;
6413 }
6414
6415 model->textures.emplace_back(std::move(texture));
6416 return true;
6417 });
6418
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006419 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006420 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006421 }
6422 }
6423
6424 // 13. Parse Animation
6425 {
David03ad33c2023-02-15 23:35:51 -06006426 bool success = ForEachInArray(v, "animations", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006427 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006428 if (err) {
6429 (*err) += "`animations' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006430 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006431 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006432 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006433 Animation animation;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006434 if (!ParseAnimation(&animation, err, o,
6435 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006436 return false;
6437 }
6438
6439 model->animations.emplace_back(std::move(animation));
6440 return true;
6441 });
6442
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006443 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006444 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006445 }
6446 }
6447
6448 // 14. Parse Skin
6449 {
David03ad33c2023-02-15 23:35:51 -06006450 bool success = ForEachInArray(v, "skins", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006451 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006452 if (err) {
6453 (*err) += "`skins' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006454 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006455 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006456 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006457 Skin skin;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006458 if (!ParseSkin(&skin, err, o,
6459 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006460 return false;
6461 }
6462
6463 model->skins.emplace_back(std::move(skin));
6464 return true;
6465 });
6466
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006467 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006468 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006469 }
6470 }
6471
6472 // 15. Parse Sampler
6473 {
David03ad33c2023-02-15 23:35:51 -06006474 bool success = ForEachInArray(v, "samplers", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006475 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006476 if (err) {
6477 (*err) += "`samplers' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006478 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006479 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006480 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006481 Sampler sampler;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006482 if (!ParseSampler(&sampler, err, o,
6483 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006484 return false;
6485 }
6486
6487 model->samplers.emplace_back(std::move(sampler));
6488 return true;
6489 });
6490
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006491 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006492 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006493 }
6494 }
6495
6496 // 16. Parse Camera
6497 {
David03ad33c2023-02-15 23:35:51 -06006498 bool success = ForEachInArray(v, "cameras", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006499 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006500 if (err) {
6501 (*err) += "`cameras' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006502 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006503 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006504 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006505 Camera camera;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006506 if (!ParseCamera(&camera, err, o,
6507 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006508 return false;
6509 }
6510
6511 model->cameras.emplace_back(std::move(camera));
6512 return true;
6513 });
6514
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006515 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006516 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006517 }
6518 }
6519
David Siegel22cafa12023-06-05 22:18:59 +02006520 // 17. Parse Extras & Extensions
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006521 ParseExtrasAndExtensions(model, err, v,
6522 store_original_json_for_extras_and_extensions_);
Selmar09d2ff12018-03-15 17:30:42 +01006523
6524 // 18. Specific extension implementations
Syoyo Fujita83675312017-12-02 21:14:13 +09006525 {
David03ad33c2023-02-15 23:35:51 -06006526 detail::json_const_iterator rootIt;
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006527 if (detail::FindMember(v, "extensions", rootIt) &&
6528 detail::IsObject(detail::GetValue(rootIt))) {
David03ad33c2023-02-15 23:35:51 -06006529 const detail::json &root = detail::GetValue(rootIt);
Syoyo Fujita83675312017-12-02 21:14:13 +09006530
David03ad33c2023-02-15 23:35:51 -06006531 detail::json_const_iterator it(detail::ObjectBegin(root));
6532 detail::json_const_iterator itEnd(detail::ObjectEnd(root));
Syoyo Fujita83675312017-12-02 21:14:13 +09006533 for (; it != itEnd; ++it) {
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02006534 // parse KHR_lights_punctual extension
David1f9a4b92023-02-15 22:56:18 -06006535 std::string key(detail::GetKey(it));
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006536 if ((key == "KHR_lights_punctual") &&
6537 detail::IsObject(detail::GetValue(it))) {
David03ad33c2023-02-15 23:35:51 -06006538 const detail::json &object = detail::GetValue(it);
6539 detail::json_const_iterator itLight;
David1f9a4b92023-02-15 22:56:18 -06006540 if (detail::FindMember(object, "lights", itLight)) {
David03ad33c2023-02-15 23:35:51 -06006541 const detail::json &lights = detail::GetValue(itLight);
David1f9a4b92023-02-15 22:56:18 -06006542 if (!detail::IsArray(lights)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006543 continue;
Syoyo Fujita83675312017-12-02 21:14:13 +09006544 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006545
David1f9a4b92023-02-15 22:56:18 -06006546 auto arrayIt(detail::ArrayBegin(lights));
6547 auto arrayItEnd(detail::ArrayEnd(lights));
jrkooncecba5d6c2019-08-29 11:26:22 -05006548 for (; arrayIt != arrayItEnd; ++arrayIt) {
6549 Light light;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006550 if (!ParseLight(&light, err, *arrayIt,
6551 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006552 return false;
6553 }
6554 model->lights.emplace_back(std::move(light));
6555 }
Syoyo Fujita83675312017-12-02 21:14:13 +09006556 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01006557 }
Baranob_Ilya78864c82023-06-12 10:43:52 +04006558 // parse KHR_audio extension
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006559 if ((key == "KHR_audio") && detail::IsObject(detail::GetValue(it))) {
Baranob_Ilya78864c82023-06-12 10:43:52 +04006560 const detail::json &object = detail::GetValue(it);
6561 detail::json_const_iterator itKhrAudio;
6562 if (detail::FindMember(object, "emitters", itKhrAudio)) {
6563 const detail::json &emitters = detail::GetValue(itKhrAudio);
6564 if (!detail::IsArray(emitters)) {
6565 continue;
6566 }
6567
6568 auto arrayIt(detail::ArrayBegin(emitters));
6569 auto arrayItEnd(detail::ArrayEnd(emitters));
6570 for (; arrayIt != arrayItEnd; ++arrayIt) {
6571 AudioEmitter emitter;
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006572 if (!ParseAudioEmitter(
6573 &emitter, err, *arrayIt,
6574 store_original_json_for_extras_and_extensions_)) {
Baranob_Ilya78864c82023-06-12 10:43:52 +04006575 return false;
6576 }
6577 model->audioEmitters.emplace_back(std::move(emitter));
6578 }
6579 }
6580
6581 if (detail::FindMember(object, "sources", itKhrAudio)) {
6582 const detail::json &sources = detail::GetValue(itKhrAudio);
6583 if (!detail::IsArray(sources)) {
6584 continue;
6585 }
6586
6587 auto arrayIt(detail::ArrayBegin(sources));
6588 auto arrayItEnd(detail::ArrayEnd(sources));
6589 for (; arrayIt != arrayItEnd; ++arrayIt) {
6590 AudioSource source;
6591 if (!ParseAudioSource(
6592 &source, err, *arrayIt,
6593 store_original_json_for_extras_and_extensions_)) {
6594 return false;
6595 }
6596 model->audioSources.emplace_back(std::move(source));
6597 }
6598 }
6599 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01006600 }
6601 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01006602 }
Syoyo Fujita83675312017-12-02 21:14:13 +09006603
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09006604 return true;
6605}
6606
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006607bool TinyGLTF::LoadASCIIFromString(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006608 std::string *warn, const char *str,
6609 unsigned int length,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006610 const std::string &base_dir,
6611 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006612 is_binary_ = false;
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09006613 bin_data_ = nullptr;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006614 bin_size_ = 0;
6615
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006616 return LoadFromString(model, err, warn, str, length, base_dir,
6617 check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006618}
6619
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006620bool TinyGLTF::LoadASCIIFromFile(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006621 std::string *warn, const std::string &filename,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006622 unsigned int check_sections) {
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006623 std::stringstream ss;
6624
Paolo Jovone6601bf2018-07-07 20:43:33 +02006625 if (fs.ReadWholeFile == nullptr) {
6626 // Programmer error, assert() ?
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006627 ss << "Failed to read file: " << filename
6628 << ": one or more FS callback not set" << std::endl;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09006629 if (err) {
6630 (*err) = ss.str();
6631 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006632 return false;
6633 }
6634
Paolo Jovone6601bf2018-07-07 20:43:33 +02006635 std::vector<unsigned char> data;
6636 std::string fileerr;
6637 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006638 if (!fileread) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02006639 ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
6640 if (err) {
6641 (*err) = ss.str();
6642 }
6643 return false;
6644 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006645
Paolo Jovone6601bf2018-07-07 20:43:33 +02006646 size_t sz = data.size();
Luke San Antoniob310b4a2016-06-23 14:17:37 -04006647 if (sz == 0) {
6648 if (err) {
6649 (*err) = "Empty file.";
6650 }
6651 return false;
6652 }
6653
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006654 std::string basedir = GetBaseDir(filename);
6655
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006656 bool ret = LoadASCIIFromString(
6657 model, err, warn, reinterpret_cast<const char *>(&data.at(0)),
6658 static_cast<unsigned int>(data.size()), basedir, check_sections);
Luke San Antonio9e36b612016-06-23 14:10:51 -04006659
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006660 return ret;
6661}
6662
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006663bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006664 std::string *warn,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006665 const unsigned char *bytes,
6666 unsigned int size,
6667 const std::string &base_dir,
6668 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006669 if (size < 20) {
6670 if (err) {
6671 (*err) = "Too short data size for glTF Binary.";
6672 }
6673 return false;
6674 }
6675
Syoyo Fujitabeded612016-05-01 20:03:43 +09006676 if (bytes[0] == 'g' && bytes[1] == 'l' && bytes[2] == 'T' &&
6677 bytes[3] == 'F') {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006678 // ok
6679 } else {
6680 if (err) {
6681 (*err) = "Invalid magic.";
6682 }
6683 return false;
6684 }
6685
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006686 unsigned int version; // 4 bytes
6687 unsigned int length; // 4 bytes
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006688 unsigned int chunk0_length; // 4 bytes
6689 unsigned int chunk0_format; // 4 bytes;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006690
Syoyo Fujitabeded612016-05-01 20:03:43 +09006691 memcpy(&version, bytes + 4, 4);
6692 swap4(&version);
Syoyo Fujita4fea26f2024-01-24 05:43:27 +09006693 memcpy(&length, bytes + 8, 4); // Total glb size, including header and all chunks.
Syoyo Fujitabeded612016-05-01 20:03:43 +09006694 swap4(&length);
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006695 memcpy(&chunk0_length, bytes + 12, 4); // JSON data length
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006696 swap4(&chunk0_length);
6697 memcpy(&chunk0_format, bytes + 16, 4);
6698 swap4(&chunk0_format);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006699
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006700 // https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#binary-gltf-layout
6701 //
Syoyo Fujitae69069d2018-03-15 22:01:18 -05006702 // In case the Bin buffer is not present, the size is exactly 20 + size of
6703 // JSON contents,
6704 // so use "greater than" operator.
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006705 //
6706 // https://github.com/syoyo/tinygltf/issues/372
6707 // Use 64bit uint to avoid integer overflow.
Syoyo Fujitac670f082022-09-17 19:52:25 +09006708 uint64_t header_and_json_size = 20ull + uint64_t(chunk0_length);
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006709
rhiskey8acf8612023-12-04 17:11:59 +03006710 if (header_and_json_size > (std::numeric_limits<uint32_t>::max)()) {
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006711 // Do not allow 4GB or more GLB data.
Syoyo Fujita8387fdb2023-12-04 22:50:49 +09006712 if (err) {
6713 (*err) = "Invalid glTF binary. GLB data exceeds 4GB.";
6714 }
6715 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006716 }
6717
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006718 if ((header_and_json_size > uint64_t(size)) || (chunk0_length < 1) ||
6719 (length > size) || (header_and_json_size > uint64_t(length)) ||
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006720 (chunk0_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006721 if (err) {
6722 (*err) = "Invalid glTF binary.";
6723 }
6724 return false;
6725 }
6726
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006727 // Padding check
6728 // The start and the end of each chunk must be aligned to a 4-byte boundary.
6729 // No padding check for chunk0 start since its 4byte-boundary is ensured.
Syoyo Fujitac670f082022-09-17 19:52:25 +09006730 if ((header_and_json_size % 4) != 0) {
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006731 if (err) {
6732 (*err) = "JSON Chunk end does not aligned to a 4-byte boundary.";
6733 }
Syoyo Fujita8387fdb2023-12-04 22:50:49 +09006734 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006735 }
6736
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006737 // std::cout << "header_and_json_size = " << header_and_json_size << "\n";
6738 // std::cout << "length = " << length << "\n";
Syoyo Fujita612e5782022-09-18 21:01:39 +09006739
Syoyo Fujitac670f082022-09-17 19:52:25 +09006740 // Chunk1(BIN) data
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006741 // The spec says: When the binary buffer is empty or when it is stored by
6742 // other means, this chunk SHOULD be omitted. So when header + JSON data ==
6743 // binary size, Chunk1 is omitted.
Syoyo Fujita612e5782022-09-18 21:01:39 +09006744 if (header_and_json_size == uint64_t(length)) {
Syoyo Fujitac670f082022-09-17 19:52:25 +09006745 bin_data_ = nullptr;
6746 bin_size_ = 0;
6747 } else {
6748 // Read Chunk1 info(BIN data)
Syoyo Fujita4fea26f2024-01-24 05:43:27 +09006749 //
6750 // issue-440:
6751 // 'SHOULD' in glTF spec means 'RECOMMENDED',
6752 // So there is a situation that Chunk1(BIN) is composed of zero-sized BIN data
6753 // (chunksize(0) + binformat(BIN) = 8bytes).
6754 //
6755 if ((header_and_json_size + 8ull) > uint64_t(length)) {
Syoyo Fujitac670f082022-09-17 19:52:25 +09006756 if (err) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006757 (*err) =
6758 "Insufficient storage space for Chunk1(BIN data). At least Chunk1 "
Syoyo Fujita4fea26f2024-01-24 05:43:27 +09006759 "Must have 8 or more bytes, but got " +
Nyall Dawson8e9aadf2023-08-24 11:47:23 +10006760 std::to_string((header_and_json_size + 8ull) - uint64_t(length)) +
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006761 ".\n";
Syoyo Fujitac670f082022-09-17 19:52:25 +09006762 }
6763 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006764 }
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006765
Syoyo Fujita4fea26f2024-01-24 05:43:27 +09006766 unsigned int chunk1_length{0}; // 4 bytes
6767 unsigned int chunk1_format{0}; // 4 bytes;
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006768 memcpy(&chunk1_length, bytes + header_and_json_size,
Syoyo Fujita4fea26f2024-01-24 05:43:27 +09006769 4); // Bin data length
Syoyo Fujitac670f082022-09-17 19:52:25 +09006770 swap4(&chunk1_length);
6771 memcpy(&chunk1_format, bytes + header_and_json_size + 4, 4);
6772 swap4(&chunk1_format);
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006773
Syoyo Fujita4fea26f2024-01-24 05:43:27 +09006774 if (chunk1_format != 0x004e4942) {
Syoyo Fujitac670f082022-09-17 19:52:25 +09006775 if (err) {
Syoyo Fujita4fea26f2024-01-24 05:43:27 +09006776 (*err) = "Invalid chunkType for Chunk1.";
Syoyo Fujitac670f082022-09-17 19:52:25 +09006777 }
6778 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006779 }
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006780
Syoyo Fujita4fea26f2024-01-24 05:43:27 +09006781 if (chunk1_length == 0) {
6782
6783 if (header_and_json_size + 8 > uint64_t(length)) {
Nyall Dawson4d119d72023-09-06 17:35:25 +10006784 if (err) {
Syoyo Fujita4fea26f2024-01-24 05:43:27 +09006785 (*err) = "BIN Chunk header location exceeds the GLB size.";
Nyall Dawson4d119d72023-09-06 17:35:25 +10006786 }
6787 return false;
6788 }
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006789
Syoyo Fujita4fea26f2024-01-24 05:43:27 +09006790 bin_data_ = nullptr;
6791
6792 } else {
6793
6794 // When BIN chunk size is not zero, at least Chunk1 should have 12 bytes(8 bytes(header) + 4 bytes(bin
6795 // payload could be 1~3 bytes, but need to be aligned to 4 bytes)
6796
6797 if (chunk1_length < 4) {
6798 if (err) {
6799 (*err) = "Insufficient Chunk1(BIN) data size.";
6800 }
6801 return false;
Syoyo Fujitac670f082022-09-17 19:52:25 +09006802 }
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006803
Syoyo Fujita4fea26f2024-01-24 05:43:27 +09006804 if ((chunk1_length % 4) != 0) {
6805 if (strictness_==ParseStrictness::Permissive) {
6806 if (warn) {
6807 (*warn) += "BIN Chunk end is not aligned to a 4-byte boundary.\n";
6808 }
6809 }
6810 else {
6811 if (err) {
6812 (*err) = "BIN Chunk end is not aligned to a 4-byte boundary.";
6813 }
6814 return false;
6815 }
Syoyo Fujitac670f082022-09-17 19:52:25 +09006816 }
Syoyo Fujita4fea26f2024-01-24 05:43:27 +09006817
6818 // +8 chunk1 header size.
6819 if (uint64_t(chunk1_length) + header_and_json_size + 8 > uint64_t(length)) {
6820 if (err) {
6821 (*err) = "BIN Chunk data length exceeds the GLB size.";
6822 }
6823 return false;
6824 }
6825
6826 bin_data_ = bytes + header_and_json_size +
6827 8; // 4 bytes (bin_buffer_length) + 4 bytes(bin_buffer_format)
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006828 }
Syoyo Fujitac670f082022-09-17 19:52:25 +09006829
Syoyo Fujitac670f082022-09-17 19:52:25 +09006830 bin_size_ = size_t(chunk1_length);
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006831 }
6832
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006833 is_binary_ = true;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006834
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006835 bool ret = LoadFromString(model, err, warn,
6836 reinterpret_cast<const char *>(&bytes[20]),
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006837 chunk0_length, base_dir, check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006838 if (!ret) {
6839 return ret;
6840 }
6841
6842 return true;
6843}
6844
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006845bool TinyGLTF::LoadBinaryFromFile(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006846 std::string *warn,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006847 const std::string &filename,
6848 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006849 std::stringstream ss;
6850
Paolo Jovone6601bf2018-07-07 20:43:33 +02006851 if (fs.ReadWholeFile == nullptr) {
6852 // Programmer error, assert() ?
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006853 ss << "Failed to read file: " << filename
6854 << ": one or more FS callback not set" << std::endl;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006855 if (err) {
6856 (*err) = ss.str();
6857 }
6858 return false;
6859 }
6860
Paolo Jovone6601bf2018-07-07 20:43:33 +02006861 std::vector<unsigned char> data;
6862 std::string fileerr;
6863 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006864 if (!fileread) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02006865 ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
6866 if (err) {
6867 (*err) = ss.str();
6868 }
6869 return false;
6870 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006871
Syoyo Fujitabeded612016-05-01 20:03:43 +09006872 std::string basedir = GetBaseDir(filename);
6873
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006874 bool ret = LoadBinaryFromMemory(model, err, warn, &data.at(0),
6875 static_cast<unsigned int>(data.size()),
6876 basedir, check_sections);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006877
6878 return ret;
6879}
6880
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006881///////////////////////
6882// GLTF Serialization
6883///////////////////////
David1f9a4b92023-02-15 22:56:18 -06006884namespace detail {
David03ad33c2023-02-15 23:35:51 -06006885detail::json JsonFromString(const char *s) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006886#ifdef TINYGLTF_USE_RAPIDJSON
David03ad33c2023-02-15 23:35:51 -06006887 return detail::json(s, detail::GetAllocator());
jrkooncecba5d6c2019-08-29 11:26:22 -05006888#else
David03ad33c2023-02-15 23:35:51 -06006889 return detail::json(s);
jrkooncecba5d6c2019-08-29 11:26:22 -05006890#endif
jrkoonce63419a12019-09-03 17:06:41 -05006891}
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006892
David03ad33c2023-02-15 23:35:51 -06006893void JsonAssign(detail::json &dest, const detail::json &src) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006894#ifdef TINYGLTF_USE_RAPIDJSON
David03ad33c2023-02-15 23:35:51 -06006895 dest.CopyFrom(src, detail::GetAllocator());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006896#else
6897 dest = src;
6898#endif
6899}
6900
David03ad33c2023-02-15 23:35:51 -06006901void JsonAddMember(detail::json &o, const char *key, detail::json &&value) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006902#ifdef TINYGLTF_USE_RAPIDJSON
6903 if (!o.IsObject()) {
6904 o.SetObject();
6905 }
Syoyo Fujita147a00a2023-06-04 05:45:24 +09006906
6907 // Issue 420.
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006908 // AddMember may create duplicated key, so use [] API when a key already
6909 // exists.
Syoyo Fujita147a00a2023-06-04 05:45:24 +09006910 // https://github.com/Tencent/rapidjson/issues/771#issuecomment-254386863
6911 detail::json_const_iterator it;
6912 if (detail::FindMember(o, key, it)) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006913 o[key] = std::move(value); // replace
Syoyo Fujita147a00a2023-06-04 05:45:24 +09006914 } else {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006915 o.AddMember(detail::json(key, detail::GetAllocator()), std::move(value),
6916 detail::GetAllocator());
Syoyo Fujita147a00a2023-06-04 05:45:24 +09006917 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006918#else
6919 o[key] = std::move(value);
6920#endif
6921}
6922
David03ad33c2023-02-15 23:35:51 -06006923void JsonPushBack(detail::json &o, detail::json &&value) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006924#ifdef TINYGLTF_USE_RAPIDJSON
David03ad33c2023-02-15 23:35:51 -06006925 o.PushBack(std::move(value), detail::GetAllocator());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006926#else
6927 o.push_back(std::move(value));
6928#endif
6929}
6930
David03ad33c2023-02-15 23:35:51 -06006931bool JsonIsNull(const detail::json &o) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006932#ifdef TINYGLTF_USE_RAPIDJSON
6933 return o.IsNull();
6934#else
6935 return o.is_null();
6936#endif
6937}
6938
David03ad33c2023-02-15 23:35:51 -06006939void JsonSetObject(detail::json &o) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006940#ifdef TINYGLTF_USE_RAPIDJSON
6941 o.SetObject();
6942#else
6943 o = o.object({});
6944#endif
6945}
6946
David03ad33c2023-02-15 23:35:51 -06006947void JsonReserveArray(detail::json &o, size_t s) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006948#ifdef TINYGLTF_USE_RAPIDJSON
6949 o.SetArray();
David03ad33c2023-02-15 23:35:51 -06006950 o.Reserve(static_cast<rapidjson::SizeType>(s), detail::GetAllocator());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006951#endif
6952 (void)(o);
6953 (void)(s);
6954}
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006955} // namespace detail
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006956
David03ad33c2023-02-15 23:35:51 -06006957// typedef std::pair<std::string, detail::json> json_object_pair;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006958
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006959template <typename T>
6960static void SerializeNumberProperty(const std::string &key, T number,
David03ad33c2023-02-15 23:35:51 -06006961 detail::json &obj) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006962 // obj.insert(
David03ad33c2023-02-15 23:35:51 -06006963 // json_object_pair(key, detail::json(static_cast<double>(number))));
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006964 // obj[key] = static_cast<double>(number);
David03ad33c2023-02-15 23:35:51 -06006965 detail::JsonAddMember(obj, key.c_str(), detail::json(number));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006966}
6967
Ivo van Dongen272a9df2020-05-29 11:24:11 +03006968#ifdef TINYGLTF_USE_RAPIDJSON
6969template <>
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006970void SerializeNumberProperty(const std::string &key, size_t number,
6971 detail::json &obj) {
6972 detail::JsonAddMember(obj, key.c_str(),
6973 detail::json(static_cast<uint64_t>(number)));
Ivo van Dongen272a9df2020-05-29 11:24:11 +03006974}
6975#endif
6976
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006977template <typename T>
6978static void SerializeNumberArrayProperty(const std::string &key,
6979 const std::vector<T> &value,
David03ad33c2023-02-15 23:35:51 -06006980 detail::json &obj) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006981 if (value.empty()) return;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006982
David03ad33c2023-02-15 23:35:51 -06006983 detail::json ary;
David1f9a4b92023-02-15 22:56:18 -06006984 detail::JsonReserveArray(ary, value.size());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006985 for (const auto &s : value) {
David03ad33c2023-02-15 23:35:51 -06006986 detail::JsonPushBack(ary, detail::json(s));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006987 }
David1f9a4b92023-02-15 22:56:18 -06006988 detail::JsonAddMember(obj, key.c_str(), std::move(ary));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006989}
6990
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006991static void SerializeStringProperty(const std::string &key,
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006992 const std::string &value,
6993 detail::json &obj) {
6994 detail::JsonAddMember(obj, key.c_str(),
6995 detail::JsonFromString(value.c_str()));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006996}
6997
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006998static void SerializeStringArrayProperty(const std::string &key,
6999 const std::vector<std::string> &value,
David03ad33c2023-02-15 23:35:51 -06007000 detail::json &obj) {
7001 detail::json ary;
David1f9a4b92023-02-15 22:56:18 -06007002 detail::JsonReserveArray(ary, value.size());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007003 for (auto &s : value) {
David1f9a4b92023-02-15 22:56:18 -06007004 detail::JsonPushBack(ary, detail::JsonFromString(s.c_str()));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007005 }
David1f9a4b92023-02-15 22:56:18 -06007006 detail::JsonAddMember(obj, key.c_str(), std::move(ary));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007007}
7008
David03ad33c2023-02-15 23:35:51 -06007009static bool ValueToJson(const Value &value, detail::json *ret) {
7010 detail::json obj;
jrkooncecba5d6c2019-08-29 11:26:22 -05007011#ifdef TINYGLTF_USE_RAPIDJSON
7012 switch (value.Type()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007013 case REAL_TYPE:
7014 obj.SetDouble(value.Get<double>());
7015 break;
7016 case INT_TYPE:
7017 obj.SetInt(value.Get<int>());
7018 break;
7019 case BOOL_TYPE:
7020 obj.SetBool(value.Get<bool>());
7021 break;
7022 case STRING_TYPE:
David03ad33c2023-02-15 23:35:51 -06007023 obj.SetString(value.Get<std::string>().c_str(), detail::GetAllocator());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007024 break;
7025 case ARRAY_TYPE: {
7026 obj.SetArray();
7027 obj.Reserve(static_cast<rapidjson::SizeType>(value.ArrayLen()),
David03ad33c2023-02-15 23:35:51 -06007028 detail::GetAllocator());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007029 for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
7030 Value elementValue = value.Get(int(i));
David03ad33c2023-02-15 23:35:51 -06007031 detail::json elementJson;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007032 if (ValueToJson(value.Get(int(i)), &elementJson))
David03ad33c2023-02-15 23:35:51 -06007033 obj.PushBack(std::move(elementJson), detail::GetAllocator());
jrkooncecba5d6c2019-08-29 11:26:22 -05007034 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007035 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05007036 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007037 case BINARY_TYPE:
7038 // TODO
David03ad33c2023-02-15 23:35:51 -06007039 // obj = detail::json(value.Get<std::vector<unsigned char>>());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007040 return false;
7041 break;
7042 case OBJECT_TYPE: {
7043 obj.SetObject();
7044 Value::Object objMap = value.Get<Value::Object>();
7045 for (auto &it : objMap) {
David03ad33c2023-02-15 23:35:51 -06007046 detail::json elementJson;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007047 if (ValueToJson(it.second, &elementJson)) {
David03ad33c2023-02-15 23:35:51 -06007048 obj.AddMember(detail::json(it.first.c_str(), detail::GetAllocator()),
7049 std::move(elementJson), detail::GetAllocator());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007050 }
7051 }
7052 break;
7053 }
7054 case NULL_TYPE:
7055 default:
7056 return false;
jrkooncecba5d6c2019-08-29 11:26:22 -05007057 }
7058#else
Ben Buzbee3b735bb2018-04-24 11:39:30 -07007059 switch (value.Type()) {
Syoyo Fujita150f2432019-07-25 19:22:44 +09007060 case REAL_TYPE:
David03ad33c2023-02-15 23:35:51 -06007061 obj = detail::json(value.Get<double>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07007062 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02007063 case INT_TYPE:
David03ad33c2023-02-15 23:35:51 -06007064 obj = detail::json(value.Get<int>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07007065 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02007066 case BOOL_TYPE:
David03ad33c2023-02-15 23:35:51 -06007067 obj = detail::json(value.Get<bool>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07007068 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02007069 case STRING_TYPE:
David03ad33c2023-02-15 23:35:51 -06007070 obj = detail::json(value.Get<std::string>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07007071 break;
7072 case ARRAY_TYPE: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02007073 for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
7074 Value elementValue = value.Get(int(i));
David03ad33c2023-02-15 23:35:51 -06007075 detail::json elementJson;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07007076 if (ValueToJson(value.Get(int(i)), &elementJson))
Selmar Kokfa7022f2018-04-04 18:10:20 +02007077 obj.push_back(elementJson);
7078 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07007079 break;
7080 }
Selmar Kokfa7022f2018-04-04 18:10:20 +02007081 case BINARY_TYPE:
Ben Buzbee3b735bb2018-04-24 11:39:30 -07007082 // TODO
7083 // obj = json(value.Get<std::vector<unsigned char>>());
Selmar Kokfa7022f2018-04-04 18:10:20 +02007084 return false;
7085 break;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07007086 case OBJECT_TYPE: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02007087 Value::Object objMap = value.Get<Value::Object>();
Ben Buzbee3b735bb2018-04-24 11:39:30 -07007088 for (auto &it : objMap) {
David03ad33c2023-02-15 23:35:51 -06007089 detail::json elementJson;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07007090 if (ValueToJson(it.second, &elementJson)) obj[it.first] = elementJson;
Selmar Kokfa7022f2018-04-04 18:10:20 +02007091 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07007092 break;
7093 }
7094 case NULL_TYPE:
7095 default:
7096 return false;
Selmar Kokfa7022f2018-04-04 18:10:20 +02007097 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007098#endif
7099 if (ret) *ret = std::move(obj);
Selmar Kokfa7022f2018-04-04 18:10:20 +02007100 return true;
7101}
7102
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007103static void SerializeValue(const std::string &key, const Value &value,
David03ad33c2023-02-15 23:35:51 -06007104 detail::json &obj) {
7105 detail::json ret;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007106 if (ValueToJson(value, &ret)) {
David1f9a4b92023-02-15 22:56:18 -06007107 detail::JsonAddMember(obj, key.c_str(), std::move(ret));
jrkooncecba5d6c2019-08-29 11:26:22 -05007108 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007109}
7110
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007111static void SerializeGltfBufferData(const std::vector<unsigned char> &data,
David03ad33c2023-02-15 23:35:51 -06007112 detail::json &o) {
johan bowald30c53472018-03-30 11:49:36 +02007113 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09007114 if (data.size() > 0) {
7115 std::string encodedData =
7116 base64_encode(&data[0], static_cast<unsigned int>(data.size()));
7117 SerializeStringProperty("uri", header + encodedData, o);
7118 } else {
7119 // Issue #229
imallettd9ce9eb2022-10-07 10:37:09 -07007120 // size 0 is allowed. Just emit mime header.
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09007121 SerializeStringProperty("uri", header, o);
7122 }
johan bowald30c53472018-03-30 11:49:36 +02007123}
7124
Selmar Koke4677492018-10-25 16:45:49 +02007125static bool SerializeGltfBufferData(const std::vector<unsigned char> &data,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05007126 const std::string &binFilename) {
Harokyangfb256602019-10-30 16:13:52 +08007127#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007128#if defined(__GLIBCXX__) // mingw
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007129 int file_descriptor = _wopen(UTF8ToWchar(binFilename).c_str(),
7130 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
7131 __gnu_cxx::stdio_filebuf<char> wfile_buf(
7132 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09007133 std::ostream output(&wfile_buf);
7134 if (!wfile_buf.is_open()) return false;
7135#elif defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08007136 std::ofstream output(UTF8ToWchar(binFilename).c_str(), std::ofstream::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09007137 if (!output.is_open()) return false;
Harokyangfb256602019-10-30 16:13:52 +08007138#else
Syoyo Fujitad42767e2018-03-15 21:52:00 -05007139 std::ofstream output(binFilename.c_str(), std::ofstream::binary);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00007140 if (!output.is_open()) return false;
Syoyo Fujita45cac782019-11-09 20:42:55 +09007141#endif
7142#else
7143 std::ofstream output(binFilename.c_str(), std::ofstream::binary);
7144 if (!output.is_open()) return false;
7145#endif
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09007146 if (data.size() > 0) {
7147 output.write(reinterpret_cast<const char *>(&data[0]),
7148 std::streamsize(data.size()));
7149 } else {
7150 // Issue #229
7151 // size 0 will be still valid buffer data.
7152 // write empty file.
7153 }
Selmar Koke4677492018-10-25 16:45:49 +02007154 return true;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007155}
7156
Syoyo Fujitacea69e32019-08-20 17:10:30 +09007157#if 0 // FIXME(syoyo): not used. will be removed in the future release.
David03ad33c2023-02-15 23:35:51 -06007158static void SerializeParameterMap(ParameterMap &param, detail::json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007159 for (ParameterMap::iterator paramIt = param.begin(); paramIt != param.end();
7160 ++paramIt) {
7161 if (paramIt->second.number_array.size()) {
7162 SerializeNumberArrayProperty<double>(paramIt->first,
7163 paramIt->second.number_array, o);
7164 } else if (paramIt->second.json_double_value.size()) {
David03ad33c2023-02-15 23:35:51 -06007165 detail::json json_double_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007166 for (std::map<std::string, double>::iterator it =
7167 paramIt->second.json_double_value.begin();
7168 it != paramIt->second.json_double_value.end(); ++it) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07007169 if (it->first == "index") {
7170 json_double_value[it->first] = paramIt->second.TextureIndex();
7171 } else {
7172 json_double_value[it->first] = it->second;
7173 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007174 }
7175
Syoyo Fujita83675312017-12-02 21:14:13 +09007176 o[paramIt->first] = json_double_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007177 } else if (!paramIt->second.string_value.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007178 SerializeStringProperty(paramIt->first, paramIt->second.string_value, o);
Ben Buzbeef6af2242018-04-25 15:13:05 -07007179 } else if (paramIt->second.has_number_value) {
7180 o[paramIt->first] = paramIt->second.number_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007181 } else {
Syoyo Fujita83675312017-12-02 21:14:13 +09007182 o[paramIt->first] = paramIt->second.bool_value;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007183 }
7184 }
7185}
Syoyo Fujitacea69e32019-08-20 17:10:30 +09007186#endif
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007187
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02007188static void SerializeExtensionMap(const ExtensionMap &extensions,
7189 detail::json &o) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07007190 if (!extensions.size()) return;
Selmar09d2ff12018-03-15 17:30:42 +01007191
David03ad33c2023-02-15 23:35:51 -06007192 detail::json extMap;
Selmar Kok81b672b2019-10-18 16:08:44 +02007193 for (ExtensionMap::const_iterator extIt = extensions.begin();
Ben Buzbee3b735bb2018-04-24 11:39:30 -07007194 extIt != extensions.end(); ++extIt) {
Syoyo Fujita924d86e2018-10-08 21:15:12 +09007195 // Allow an empty object for extension(#97)
David03ad33c2023-02-15 23:35:51 -06007196 detail::json ret;
jrkooncecba5d6c2019-08-29 11:26:22 -05007197 bool isNull = true;
Syoyo Fujita924d86e2018-10-08 21:15:12 +09007198 if (ValueToJson(extIt->second, &ret)) {
David1f9a4b92023-02-15 22:56:18 -06007199 isNull = detail::JsonIsNull(ret);
7200 detail::JsonAddMember(extMap, extIt->first.c_str(), std::move(ret));
Selmar Kokdb7f4e42018-10-10 18:10:58 +02007201 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007202 if (isNull) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00007203 if (!(extIt->first.empty())) { // name should not be empty, but for sure
7204 // create empty object so that an extension name is still included in
7205 // json.
David03ad33c2023-02-15 23:35:51 -06007206 detail::json empty;
David1f9a4b92023-02-15 22:56:18 -06007207 detail::JsonSetObject(empty);
7208 detail::JsonAddMember(extMap, extIt->first.c_str(), std::move(empty));
Syoyo Fujita924d86e2018-10-08 21:15:12 +09007209 }
7210 }
Selmar09d2ff12018-03-15 17:30:42 +01007211 }
David1f9a4b92023-02-15 22:56:18 -06007212 detail::JsonAddMember(o, "extensions", std::move(extMap));
Selmar09d2ff12018-03-15 17:30:42 +01007213}
7214
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02007215static void SerializeExtras(const Value &extras, detail::json &o) {
7216 if (extras.Type() != NULL_TYPE) SerializeValue("extras", extras, o);
David Siegel07616e82023-06-06 01:24:53 +02007217}
7218
7219template <typename GltfType>
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02007220void SerializeExtrasAndExtensions(const GltfType &obj, detail::json &o) {
David Siegel07616e82023-06-06 01:24:53 +02007221 SerializeExtensionMap(obj.extensions, o);
7222 SerializeExtras(obj.extras, o);
7223}
7224
David03ad33c2023-02-15 23:35:51 -06007225static void SerializeGltfAccessor(const Accessor &accessor, detail::json &o) {
Sanjeet Suhag5ecede72020-03-04 17:40:10 -05007226 if (accessor.bufferView >= 0)
7227 SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007228
Syoyo Fujita18f0e202020-04-29 19:16:35 +09007229 if (accessor.byteOffset != 0)
emimvibf7120f2023-09-02 00:11:41 +02007230 SerializeNumberProperty<size_t>("byteOffset", accessor.byteOffset, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007231
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007232 SerializeNumberProperty<int>("componentType", accessor.componentType, o);
7233 SerializeNumberProperty<size_t>("count", accessor.count, o);
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09007234
7235 if ((accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) ||
7236 (accessor.componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE)) {
7237 SerializeNumberArrayProperty<double>("min", accessor.minValues, o);
7238 SerializeNumberArrayProperty<double>("max", accessor.maxValues, o);
7239 } else {
7240 // Issue #301. Serialize as integer.
7241 // Assume int value is within [-2**31-1, 2**31-1]
7242 {
7243 std::vector<int> values;
7244 std::transform(accessor.minValues.begin(), accessor.minValues.end(),
7245 std::back_inserter(values),
7246 [](double v) { return static_cast<int>(v); });
7247
7248 SerializeNumberArrayProperty<int>("min", values, o);
7249 }
7250
7251 {
7252 std::vector<int> values;
7253 std::transform(accessor.maxValues.begin(), accessor.maxValues.end(),
7254 std::back_inserter(values),
7255 [](double v) { return static_cast<int>(v); });
7256
7257 SerializeNumberArrayProperty<int>("max", values, o);
7258 }
7259 }
7260
Eero Pajarre2e8a1152019-11-18 13:09:25 +02007261 if (accessor.normalized)
7262 SerializeValue("normalized", Value(accessor.normalized), o);
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007263 std::string type;
7264 switch (accessor.type) {
7265 case TINYGLTF_TYPE_SCALAR:
7266 type = "SCALAR";
7267 break;
7268 case TINYGLTF_TYPE_VEC2:
7269 type = "VEC2";
7270 break;
7271 case TINYGLTF_TYPE_VEC3:
7272 type = "VEC3";
7273 break;
7274 case TINYGLTF_TYPE_VEC4:
7275 type = "VEC4";
7276 break;
7277 case TINYGLTF_TYPE_MAT2:
7278 type = "MAT2";
7279 break;
7280 case TINYGLTF_TYPE_MAT3:
7281 type = "MAT3";
7282 break;
7283 case TINYGLTF_TYPE_MAT4:
7284 type = "MAT4";
7285 break;
7286 }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09007287
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007288 SerializeStringProperty("type", type, o);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09007289 if (!accessor.name.empty()) SerializeStringProperty("name", accessor.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007290
David Siegel07616e82023-06-06 01:24:53 +02007291 SerializeExtrasAndExtensions(accessor, o);
feiy0b315432022-08-13 10:08:17 +08007292
7293 // sparse
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02007294 if (accessor.sparse.isSparse) {
7295 detail::json sparse;
7296 SerializeNumberProperty<int>("count", accessor.sparse.count, sparse);
7297 {
7298 detail::json indices;
7299 SerializeNumberProperty<int>("bufferView",
7300 accessor.sparse.indices.bufferView, indices);
emimvi759976e2023-09-02 09:39:53 +02007301 SerializeNumberProperty<size_t>("byteOffset",
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02007302 accessor.sparse.indices.byteOffset, indices);
7303 SerializeNumberProperty<int>(
7304 "componentType", accessor.sparse.indices.componentType, indices);
7305 SerializeExtrasAndExtensions(accessor.sparse.indices, indices);
7306 detail::JsonAddMember(sparse, "indices", std::move(indices));
7307 }
7308 {
7309 detail::json values;
7310 SerializeNumberProperty<int>("bufferView",
7311 accessor.sparse.values.bufferView, values);
emimvi759976e2023-09-02 09:39:53 +02007312 SerializeNumberProperty<size_t>("byteOffset",
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02007313 accessor.sparse.values.byteOffset, values);
7314 SerializeExtrasAndExtensions(accessor.sparse.values, values);
7315 detail::JsonAddMember(sparse, "values", std::move(values));
7316 }
7317 SerializeExtrasAndExtensions(accessor.sparse, sparse);
7318 detail::JsonAddMember(o, "sparse", std::move(sparse));
feiy0b315432022-08-13 10:08:17 +08007319 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007320}
7321
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007322static void SerializeGltfAnimationChannel(const AnimationChannel &channel,
David03ad33c2023-02-15 23:35:51 -06007323 detail::json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007324 SerializeNumberProperty("sampler", channel.sampler, o);
jrkooncecba5d6c2019-08-29 11:26:22 -05007325 {
David03ad33c2023-02-15 23:35:51 -06007326 detail::json target;
Jack Mousseau283b5522023-01-15 11:45:45 -08007327
Loïc Escalesa75355b2023-04-18 21:03:39 +02007328 if (channel.target_node >= 0) {
Jack Mousseau283b5522023-01-15 11:45:45 -08007329 SerializeNumberProperty("node", channel.target_node, target);
7330 }
7331
jrkooncecba5d6c2019-08-29 11:26:22 -05007332 SerializeStringProperty("path", channel.target_path, target);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007333
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007334 SerializeExtensionMap(channel.target_extensions, target);
David Siegel07616e82023-06-06 01:24:53 +02007335 SerializeExtras(channel.target_extras, target);
Selmar Kok973d9b32020-01-21 18:45:24 +01007336
David1f9a4b92023-02-15 22:56:18 -06007337 detail::JsonAddMember(o, "target", std::move(target));
jrkooncecba5d6c2019-08-29 11:26:22 -05007338 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007339
David Siegel07616e82023-06-06 01:24:53 +02007340 SerializeExtrasAndExtensions(channel, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007341}
7342
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007343static void SerializeGltfAnimationSampler(const AnimationSampler &sampler,
David03ad33c2023-02-15 23:35:51 -06007344 detail::json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007345 SerializeNumberProperty("input", sampler.input, o);
7346 SerializeNumberProperty("output", sampler.output, o);
7347 SerializeStringProperty("interpolation", sampler.interpolation, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007348
David Siegel07616e82023-06-06 01:24:53 +02007349 SerializeExtrasAndExtensions(sampler, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007350}
7351
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02007352static void SerializeGltfAnimation(const Animation &animation,
7353 detail::json &o) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09007354 if (!animation.name.empty())
7355 SerializeStringProperty("name", animation.name, o);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007356
jrkooncecba5d6c2019-08-29 11:26:22 -05007357 {
David03ad33c2023-02-15 23:35:51 -06007358 detail::json channels;
David1f9a4b92023-02-15 22:56:18 -06007359 detail::JsonReserveArray(channels, animation.channels.size());
jrkooncecba5d6c2019-08-29 11:26:22 -05007360 for (unsigned int i = 0; i < animation.channels.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007361 detail::json channel;
jrkooncecba5d6c2019-08-29 11:26:22 -05007362 AnimationChannel gltfChannel = animation.channels[i];
7363 SerializeGltfAnimationChannel(gltfChannel, channel);
David1f9a4b92023-02-15 22:56:18 -06007364 detail::JsonPushBack(channels, std::move(channel));
jrkooncecba5d6c2019-08-29 11:26:22 -05007365 }
7366
David1f9a4b92023-02-15 22:56:18 -06007367 detail::JsonAddMember(o, "channels", std::move(channels));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007368 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007369
jrkooncecba5d6c2019-08-29 11:26:22 -05007370 {
David03ad33c2023-02-15 23:35:51 -06007371 detail::json samplers;
David1f9a4b92023-02-15 22:56:18 -06007372 detail::JsonReserveArray(samplers, animation.samplers.size());
jrkooncecba5d6c2019-08-29 11:26:22 -05007373 for (unsigned int i = 0; i < animation.samplers.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007374 detail::json sampler;
jrkooncecba5d6c2019-08-29 11:26:22 -05007375 AnimationSampler gltfSampler = animation.samplers[i];
7376 SerializeGltfAnimationSampler(gltfSampler, sampler);
David1f9a4b92023-02-15 22:56:18 -06007377 detail::JsonPushBack(samplers, std::move(sampler));
jrkooncecba5d6c2019-08-29 11:26:22 -05007378 }
David1f9a4b92023-02-15 22:56:18 -06007379 detail::JsonAddMember(o, "samplers", std::move(samplers));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007380 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007381
David Siegel07616e82023-06-06 01:24:53 +02007382 SerializeExtrasAndExtensions(animation, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007383}
7384
David03ad33c2023-02-15 23:35:51 -06007385static void SerializeGltfAsset(const Asset &asset, detail::json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007386 if (!asset.generator.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007387 SerializeStringProperty("generator", asset.generator, o);
7388 }
7389
Christophe820ede82019-07-04 15:21:21 +09007390 if (!asset.copyright.empty()) {
7391 SerializeStringProperty("copyright", asset.copyright, o);
7392 }
7393
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007394 auto version = asset.version;
7395 if (version.empty()) {
Syoyo Fujitab702de72021-03-02 19:08:29 +09007396 // Just in case
7397 // `version` must be defined
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007398 version = "2.0";
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007399 }
7400
Syoyo Fujitab702de72021-03-02 19:08:29 +09007401 // TODO(syoyo): Do we need to check if `version` is greater or equal to 2.0?
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007402 SerializeStringProperty("version", version, o);
Syoyo Fujitab702de72021-03-02 19:08:29 +09007403
David Siegel07616e82023-06-06 01:24:53 +02007404 SerializeExtrasAndExtensions(asset, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007405}
7406
David03ad33c2023-02-15 23:35:51 -06007407static void SerializeGltfBufferBin(const Buffer &buffer, detail::json &o,
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007408 std::vector<unsigned char> &binBuffer) {
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007409 SerializeNumberProperty("byteLength", buffer.data.size(), o);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007410 binBuffer = buffer.data;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007411
7412 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
7413
David Siegel07616e82023-06-06 01:24:53 +02007414 SerializeExtrasAndExtensions(buffer, o);
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007415}
7416
David03ad33c2023-02-15 23:35:51 -06007417static void SerializeGltfBuffer(const Buffer &buffer, detail::json &o) {
johan bowald30c53472018-03-30 11:49:36 +02007418 SerializeNumberProperty("byteLength", buffer.data.size(), o);
7419 SerializeGltfBufferData(buffer.data, o);
7420
7421 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007422
David Siegel07616e82023-06-06 01:24:53 +02007423 SerializeExtrasAndExtensions(buffer, o);
johan bowald30c53472018-03-30 11:49:36 +02007424}
7425
David03ad33c2023-02-15 23:35:51 -06007426static bool SerializeGltfBuffer(const Buffer &buffer, detail::json &o,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05007427 const std::string &binFilename,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08007428 const std::string &binUri) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00007429 if (!SerializeGltfBufferData(buffer.data, binFilename)) return false;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007430 SerializeNumberProperty("byteLength", buffer.data.size(), o);
Pyarelal Knowles385946d2023-01-03 18:18:04 -08007431 SerializeStringProperty("uri", binUri, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007432
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007433 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007434
David Siegel07616e82023-06-06 01:24:53 +02007435 SerializeExtrasAndExtensions(buffer, o);
Selmar Koke4677492018-10-25 16:45:49 +02007436 return true;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007437}
7438
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02007439static void SerializeGltfBufferView(const BufferView &bufferView,
7440 detail::json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007441 SerializeNumberProperty("buffer", bufferView.buffer, o);
7442 SerializeNumberProperty<size_t>("byteLength", bufferView.byteLength, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007443
Johan Bowaldfaa27222018-03-28 14:44:45 +02007444 // byteStride is optional, minimum allowed is 4
7445 if (bufferView.byteStride >= 4) {
7446 SerializeNumberProperty<size_t>("byteStride", bufferView.byteStride, o);
7447 }
7448 // byteOffset is optional, default is 0
7449 if (bufferView.byteOffset > 0) {
7450 SerializeNumberProperty<size_t>("byteOffset", bufferView.byteOffset, o);
7451 }
7452 // Target is optional, check if it contains a valid value
7453 if (bufferView.target == TINYGLTF_TARGET_ARRAY_BUFFER ||
7454 bufferView.target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER) {
7455 SerializeNumberProperty("target", bufferView.target, o);
7456 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007457 if (bufferView.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007458 SerializeStringProperty("name", bufferView.name, o);
7459 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007460
David Siegel07616e82023-06-06 01:24:53 +02007461 SerializeExtrasAndExtensions(bufferView, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007462}
7463
Pyarelal Knowlesa9121552022-12-29 14:12:29 -08007464static void SerializeGltfImage(const Image &image, const std::string &uri,
David03ad33c2023-02-15 23:35:51 -06007465 detail::json &o) {
Syoyo Fujita584f1df2022-12-29 21:05:53 +09007466 // From 2.7.0, we look for `uri` parameter, not `Image.uri`
7467 // if uri is empty, the mimeType and bufferview should be set
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007468 if (uri.empty()) {
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02007469 SerializeStringProperty("mimeType", image.mimeType, o);
7470 SerializeNumberProperty<int>("bufferView", image.bufferView, o);
7471 } else {
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007472 SerializeStringProperty("uri", uri, o);
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02007473 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007474
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007475 if (image.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007476 SerializeStringProperty("name", image.name, o);
7477 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007478
David Siegel07616e82023-06-06 01:24:53 +02007479 SerializeExtrasAndExtensions(image, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007480}
7481
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02007482static void SerializeGltfTextureInfo(const TextureInfo &texinfo,
7483 detail::json &o) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007484 SerializeNumberProperty("index", texinfo.index, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007485
Syoyo Fujita046400b2019-07-24 19:26:48 +09007486 if (texinfo.texCoord != 0) {
7487 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
7488 }
7489
David Siegel07616e82023-06-06 01:24:53 +02007490 SerializeExtrasAndExtensions(texinfo, o);
Syoyo Fujita046400b2019-07-24 19:26:48 +09007491}
7492
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007493static void SerializeGltfNormalTextureInfo(const NormalTextureInfo &texinfo,
David03ad33c2023-02-15 23:35:51 -06007494 detail::json &o) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007495 SerializeNumberProperty("index", texinfo.index, o);
7496
7497 if (texinfo.texCoord != 0) {
7498 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
7499 }
7500
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007501 if (!TINYGLTF_DOUBLE_EQUAL(texinfo.scale, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007502 SerializeNumberProperty("scale", texinfo.scale, o);
7503 }
7504
David Siegel07616e82023-06-06 01:24:53 +02007505 SerializeExtrasAndExtensions(texinfo, o);
Syoyo Fujita046400b2019-07-24 19:26:48 +09007506}
7507
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007508static void SerializeGltfOcclusionTextureInfo(
David03ad33c2023-02-15 23:35:51 -06007509 const OcclusionTextureInfo &texinfo, detail::json &o) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007510 SerializeNumberProperty("index", texinfo.index, o);
7511
7512 if (texinfo.texCoord != 0) {
7513 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
7514 }
7515
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007516 if (!TINYGLTF_DOUBLE_EQUAL(texinfo.strength, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007517 SerializeNumberProperty("strength", texinfo.strength, o);
7518 }
7519
David Siegel07616e82023-06-06 01:24:53 +02007520 SerializeExtrasAndExtensions(texinfo, o);
Syoyo Fujita046400b2019-07-24 19:26:48 +09007521}
7522
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007523static void SerializeGltfPbrMetallicRoughness(const PbrMetallicRoughness &pbr,
David03ad33c2023-02-15 23:35:51 -06007524 detail::json &o) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007525 std::vector<double> default_baseColorFactor = {1.0, 1.0, 1.0, 1.0};
7526 if (!Equals(pbr.baseColorFactor, default_baseColorFactor)) {
7527 SerializeNumberArrayProperty<double>("baseColorFactor", pbr.baseColorFactor,
7528 o);
7529 }
7530
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007531 if (!TINYGLTF_DOUBLE_EQUAL(pbr.metallicFactor, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007532 SerializeNumberProperty("metallicFactor", pbr.metallicFactor, o);
7533 }
7534
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007535 if (!TINYGLTF_DOUBLE_EQUAL(pbr.roughnessFactor, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007536 SerializeNumberProperty("roughnessFactor", pbr.roughnessFactor, o);
7537 }
7538
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007539 if (pbr.baseColorTexture.index > -1) {
David03ad33c2023-02-15 23:35:51 -06007540 detail::json texinfo;
Syoyo Fujita046400b2019-07-24 19:26:48 +09007541 SerializeGltfTextureInfo(pbr.baseColorTexture, texinfo);
David1f9a4b92023-02-15 22:56:18 -06007542 detail::JsonAddMember(o, "baseColorTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007543 }
7544
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007545 if (pbr.metallicRoughnessTexture.index > -1) {
David03ad33c2023-02-15 23:35:51 -06007546 detail::json texinfo;
Syoyo Fujita046400b2019-07-24 19:26:48 +09007547 SerializeGltfTextureInfo(pbr.metallicRoughnessTexture, texinfo);
David1f9a4b92023-02-15 22:56:18 -06007548 detail::JsonAddMember(o, "metallicRoughnessTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007549 }
7550
David Siegel07616e82023-06-06 01:24:53 +02007551 SerializeExtrasAndExtensions(pbr, o);
Syoyo Fujita046400b2019-07-24 19:26:48 +09007552}
7553
David03ad33c2023-02-15 23:35:51 -06007554static void SerializeGltfMaterial(const Material &material, detail::json &o) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007555 if (material.name.size()) {
7556 SerializeStringProperty("name", material.name, o);
7557 }
7558
7559 // QUESTION(syoyo): Write material parameters regardless of its default value?
7560
7561 if (!TINYGLTF_DOUBLE_EQUAL(material.alphaCutoff, 0.5)) {
7562 SerializeNumberProperty("alphaCutoff", material.alphaCutoff, o);
7563 }
7564
Patrick Härtld9a468b2019-08-14 14:14:07 +02007565 if (material.alphaMode.compare("OPAQUE") != 0) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007566 SerializeStringProperty("alphaMode", material.alphaMode, o);
7567 }
7568
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007569 if (material.doubleSided != false)
David03ad33c2023-02-15 23:35:51 -06007570 detail::JsonAddMember(o, "doubleSided", detail::json(material.doubleSided));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007571
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007572 if (material.normalTexture.index > -1) {
David03ad33c2023-02-15 23:35:51 -06007573 detail::json texinfo;
Syoyo Fujita046400b2019-07-24 19:26:48 +09007574 SerializeGltfNormalTextureInfo(material.normalTexture, texinfo);
David1f9a4b92023-02-15 22:56:18 -06007575 detail::JsonAddMember(o, "normalTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007576 }
7577
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007578 if (material.occlusionTexture.index > -1) {
David03ad33c2023-02-15 23:35:51 -06007579 detail::json texinfo;
Syoyo Fujita046400b2019-07-24 19:26:48 +09007580 SerializeGltfOcclusionTextureInfo(material.occlusionTexture, texinfo);
David1f9a4b92023-02-15 22:56:18 -06007581 detail::JsonAddMember(o, "occlusionTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007582 }
7583
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007584 if (material.emissiveTexture.index > -1) {
David03ad33c2023-02-15 23:35:51 -06007585 detail::json texinfo;
Syoyo Fujita046400b2019-07-24 19:26:48 +09007586 SerializeGltfTextureInfo(material.emissiveTexture, texinfo);
David1f9a4b92023-02-15 22:56:18 -06007587 detail::JsonAddMember(o, "emissiveTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007588 }
7589
7590 std::vector<double> default_emissiveFactor = {0.0, 0.0, 0.0};
7591 if (!Equals(material.emissiveFactor, default_emissiveFactor)) {
7592 SerializeNumberArrayProperty<double>("emissiveFactor",
7593 material.emissiveFactor, o);
7594 }
7595
7596 {
David03ad33c2023-02-15 23:35:51 -06007597 detail::json pbrMetallicRoughness;
Syoyo Fujita046400b2019-07-24 19:26:48 +09007598 SerializeGltfPbrMetallicRoughness(material.pbrMetallicRoughness,
7599 pbrMetallicRoughness);
Syoyo Fujita7e009042019-09-13 15:32:22 +09007600 // Issue 204
7601 // Do not serialize `pbrMetallicRoughness` if pbrMetallicRoughness has all
7602 // default values(json is null). Otherwise it will serialize to
7603 // `pbrMetallicRoughness : null`, which cannot be read by other glTF
imallettd9ce9eb2022-10-07 10:37:09 -07007604 // importers (and validators).
Syoyo Fujita7e009042019-09-13 15:32:22 +09007605 //
David1f9a4b92023-02-15 22:56:18 -06007606 if (!detail::JsonIsNull(pbrMetallicRoughness)) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02007607 detail::JsonAddMember(o, "pbrMetallicRoughness",
7608 std::move(pbrMetallicRoughness));
Syoyo Fujita7e009042019-09-13 15:32:22 +09007609 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09007610 }
7611
7612#if 0 // legacy way. just for the record.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007613 if (material.values.size()) {
David03ad33c2023-02-15 23:35:51 -06007614 detail::json pbrMetallicRoughness;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007615 SerializeParameterMap(material.values, pbrMetallicRoughness);
David1f9a4b92023-02-15 22:56:18 -06007616 detail::JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007617 }
7618
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007619 SerializeParameterMap(material.additionalValues, o);
Syoyo Fujita046400b2019-07-24 19:26:48 +09007620#endif
7621
David Siegel07616e82023-06-06 01:24:53 +02007622 SerializeExtrasAndExtensions(material, o);
Thomas Gamperf1bdf432024-02-05 16:50:46 +01007623
7624 // MSFT_lod
7625 if (!material.lods.empty()) {
7626 detail::json_iterator it;
7627 if (!detail::FindMember(o, "extensions", it)) {
7628 detail::json extensions;
7629 detail::JsonSetObject(extensions);
7630 detail::JsonAddMember(o, "extensions", std::move(extensions));
7631 detail::FindMember(o, "extensions", it);
7632 }
7633 auto &extensions = detail::GetValue(it);
7634 if (!detail::FindMember(extensions, "MSFT_lod", it)) {
7635 detail::json lod;
7636 detail::JsonSetObject(lod);
7637 detail::JsonAddMember(extensions, "MSFT_lod", std::move(lod));
7638 detail::FindMember(extensions, "MSFT_lod", it);
7639 }
7640 SerializeNumberArrayProperty<int>("ids", material.lods, detail::GetValue(it));
7641 } else {
7642 detail::json_iterator ext_it;
7643 if (detail::FindMember(o, "extensions", ext_it)) {
7644 auto &extensions = detail::GetValue(ext_it);
7645 detail::json_iterator lp_it;
7646 if (detail::FindMember(extensions, "MSFT_lod", lp_it)) {
7647 detail::Erase(extensions, lp_it);
7648 }
7649 if (detail::IsEmpty(extensions)) {
7650 detail::Erase(o, ext_it);
7651 }
7652 }
7653 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007654}
7655
David03ad33c2023-02-15 23:35:51 -06007656static void SerializeGltfMesh(const Mesh &mesh, detail::json &o) {
7657 detail::json primitives;
David1f9a4b92023-02-15 22:56:18 -06007658 detail::JsonReserveArray(primitives, mesh.primitives.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007659 for (unsigned int i = 0; i < mesh.primitives.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007660 detail::json primitive;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007661 const Primitive &gltfPrimitive = mesh.primitives[i]; // don't make a copy
jrkooncecba5d6c2019-08-29 11:26:22 -05007662 {
David03ad33c2023-02-15 23:35:51 -06007663 detail::json attributes;
jrkooncecba5d6c2019-08-29 11:26:22 -05007664 for (auto attrIt = gltfPrimitive.attributes.begin();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007665 attrIt != gltfPrimitive.attributes.end(); ++attrIt) {
jrkooncecba5d6c2019-08-29 11:26:22 -05007666 SerializeNumberProperty<int>(attrIt->first, attrIt->second, attributes);
7667 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007668
David1f9a4b92023-02-15 22:56:18 -06007669 detail::JsonAddMember(primitive, "attributes", std::move(attributes));
jrkooncecba5d6c2019-08-29 11:26:22 -05007670 }
Johan Bowaldfaa27222018-03-28 14:44:45 +02007671
imallettd9ce9eb2022-10-07 10:37:09 -07007672 // Indices is optional
Johan Bowaldfaa27222018-03-28 14:44:45 +02007673 if (gltfPrimitive.indices > -1) {
7674 SerializeNumberProperty<int>("indices", gltfPrimitive.indices, primitive);
7675 }
7676 // Material is optional
7677 if (gltfPrimitive.material > -1) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09007678 SerializeNumberProperty<int>("material", gltfPrimitive.material,
7679 primitive);
Johan Bowaldfaa27222018-03-28 14:44:45 +02007680 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007681 SerializeNumberProperty<int>("mode", gltfPrimitive.mode, primitive);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007682
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007683 // Morph targets
7684 if (gltfPrimitive.targets.size()) {
David03ad33c2023-02-15 23:35:51 -06007685 detail::json targets;
David1f9a4b92023-02-15 22:56:18 -06007686 detail::JsonReserveArray(targets, gltfPrimitive.targets.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007687 for (unsigned int k = 0; k < gltfPrimitive.targets.size(); ++k) {
David03ad33c2023-02-15 23:35:51 -06007688 detail::json targetAttributes;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007689 std::map<std::string, int> targetData = gltfPrimitive.targets[k];
7690 for (std::map<std::string, int>::iterator attrIt = targetData.begin();
7691 attrIt != targetData.end(); ++attrIt) {
7692 SerializeNumberProperty<int>(attrIt->first, attrIt->second,
7693 targetAttributes);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007694 }
David1f9a4b92023-02-15 22:56:18 -06007695 detail::JsonPushBack(targets, std::move(targetAttributes));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007696 }
David1f9a4b92023-02-15 22:56:18 -06007697 detail::JsonAddMember(primitive, "targets", std::move(targets));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007698 }
7699
KUDELSKI Dimitri69d75572023-07-10 15:13:09 +02007700 SerializeExtrasAndExtensions(gltfPrimitive, primitive);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007701
David1f9a4b92023-02-15 22:56:18 -06007702 detail::JsonPushBack(primitives, std::move(primitive));
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007703 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007704
David1f9a4b92023-02-15 22:56:18 -06007705 detail::JsonAddMember(o, "primitives", std::move(primitives));
jrkooncecba5d6c2019-08-29 11:26:22 -05007706
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007707 if (mesh.weights.size()) {
7708 SerializeNumberArrayProperty<double>("weights", mesh.weights, o);
7709 }
7710
7711 if (mesh.name.size()) {
7712 SerializeStringProperty("name", mesh.name, o);
7713 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007714
David Siegel07616e82023-06-06 01:24:53 +02007715 SerializeExtrasAndExtensions(mesh, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007716}
7717
David03ad33c2023-02-15 23:35:51 -06007718static void SerializeSpotLight(const SpotLight &spot, detail::json &o) {
Johan Bowald52936a02019-07-17 09:06:45 +02007719 SerializeNumberProperty("innerConeAngle", spot.innerConeAngle, o);
7720 SerializeNumberProperty("outerConeAngle", spot.outerConeAngle, o);
David Siegel07616e82023-06-06 01:24:53 +02007721 SerializeExtrasAndExtensions(spot, o);
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007722}
7723
David03ad33c2023-02-15 23:35:51 -06007724static void SerializeGltfLight(const Light &light, detail::json &o) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09007725 if (!light.name.empty()) SerializeStringProperty("name", light.name, o);
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007726 SerializeNumberProperty("intensity", light.intensity, o);
Syoyo Fujita3dad3032020-05-13 21:22:23 +09007727 if (light.range > 0.0) {
7728 SerializeNumberProperty("range", light.range, o);
7729 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01007730 SerializeNumberArrayProperty("color", light.color, o);
7731 SerializeStringProperty("type", light.type, o);
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007732 if (light.type == "spot") {
David03ad33c2023-02-15 23:35:51 -06007733 detail::json spot;
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007734 SerializeSpotLight(light.spot, spot);
David1f9a4b92023-02-15 22:56:18 -06007735 detail::JsonAddMember(o, "spot", std::move(spot));
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007736 }
David Siegel07616e82023-06-06 01:24:53 +02007737 SerializeExtrasAndExtensions(light, o);
Emanuel Schrade186322b2017-11-06 11:14:41 +01007738}
7739
Baranob_Ilya78864c82023-06-12 10:43:52 +04007740static void SerializeGltfPositionalEmitter(const PositionalEmitter &positional,
7741 detail::json &o) {
7742 if (!TINYGLTF_DOUBLE_EQUAL(positional.coneInnerAngle, 6.283185307179586))
7743 SerializeNumberProperty("coneInnerAngle", positional.coneInnerAngle, o);
7744 if (!TINYGLTF_DOUBLE_EQUAL(positional.coneOuterAngle, 6.283185307179586))
7745 SerializeNumberProperty("coneOuterAngle", positional.coneOuterAngle, o);
7746 if (positional.coneOuterGain > 0.0)
7747 SerializeNumberProperty("coneOuterGain", positional.coneOuterGain, o);
7748 if (!TINYGLTF_DOUBLE_EQUAL(positional.maxDistance, 100.0))
7749 SerializeNumberProperty("maxDistance", positional.maxDistance, o);
7750 if (!TINYGLTF_DOUBLE_EQUAL(positional.refDistance, 1.0))
7751 SerializeNumberProperty("refDistance", positional.refDistance, o);
7752 if (!TINYGLTF_DOUBLE_EQUAL(positional.rolloffFactor, 1.0))
7753 SerializeNumberProperty("rolloffFactor", positional.rolloffFactor, o);
7754
7755 SerializeExtrasAndExtensions(positional, o);
7756}
7757
7758static void SerializeGltfAudioEmitter(const AudioEmitter &emitter,
7759 detail::json &o) {
7760 if (!emitter.name.empty()) SerializeStringProperty("name", emitter.name, o);
7761 if (!TINYGLTF_DOUBLE_EQUAL(emitter.gain, 1.0))
7762 SerializeNumberProperty("gain", emitter.gain, o);
7763 if (emitter.loop) SerializeNumberProperty("loop", emitter.loop, o);
7764 if (emitter.playing) SerializeNumberProperty("playing", emitter.playing, o);
7765 if (!emitter.type.empty()) SerializeStringProperty("type", emitter.type, o);
7766 if (!emitter.distanceModel.empty())
7767 SerializeStringProperty("distanceModel", emitter.distanceModel, o);
7768 if (emitter.type == "positional") {
7769 detail::json positional;
7770 SerializeGltfPositionalEmitter(emitter.positional, positional);
7771 detail::JsonAddMember(o, "positional", std::move(positional));
7772 }
7773 SerializeNumberProperty("source", emitter.source, o);
7774 SerializeExtrasAndExtensions(emitter, o);
7775}
7776
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02007777static void SerializeGltfAudioSource(const AudioSource &source,
7778 detail::json &o) {
Baranob_Ilya78864c82023-06-12 10:43:52 +04007779 std::string name;
7780 std::string uri;
Baranob_Ilya78864c82023-06-12 10:43:52 +04007781 std::string mimeType; // (required if no uri) ["audio/mp3", "audio/ogg",
7782 // "audio/wav", "audio/m4a"]
7783
7784 if (!source.name.empty()) SerializeStringProperty("name", source.name, o);
7785 if (source.uri.empty()) {
7786 SerializeStringProperty("mimeType", source.mimeType, o);
7787 SerializeNumberProperty<int>("bufferView", source.bufferView, o);
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02007788 } else {
7789 SerializeStringProperty("uri", source.uri, o);
Baranob_Ilya78864c82023-06-12 10:43:52 +04007790 }
7791 SerializeExtrasAndExtensions(source, o);
7792}
7793
David03ad33c2023-02-15 23:35:51 -06007794static void SerializeGltfNode(const Node &node, detail::json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007795 if (node.translation.size() > 0) {
7796 SerializeNumberArrayProperty<double>("translation", node.translation, o);
7797 }
7798 if (node.rotation.size() > 0) {
7799 SerializeNumberArrayProperty<double>("rotation", node.rotation, o);
7800 }
7801 if (node.scale.size() > 0) {
7802 SerializeNumberArrayProperty<double>("scale", node.scale, o);
7803 }
7804 if (node.matrix.size() > 0) {
7805 SerializeNumberArrayProperty<double>("matrix", node.matrix, o);
7806 }
7807 if (node.mesh != -1) {
7808 SerializeNumberProperty<int>("mesh", node.mesh, o);
7809 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007810
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007811 if (node.skin != -1) {
7812 SerializeNumberProperty<int>("skin", node.skin, o);
7813 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007814
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007815 if (node.camera != -1) {
7816 SerializeNumberProperty<int>("camera", node.camera, o);
7817 }
7818
Benjamin Schmithüsen74c3c102019-08-16 14:24:26 +02007819 if (node.weights.size() > 0) {
7820 SerializeNumberArrayProperty<double>("weights", node.weights, o);
7821 }
7822
David Siegel07616e82023-06-06 01:24:53 +02007823 SerializeExtrasAndExtensions(node, o);
Jens Olssonb96f6962018-05-24 15:29:54 +02007824
David Siegelcfe64fb2023-06-07 15:18:38 +02007825 // Note(agnat): If the asset was loaded from disk, the node may already
7826 // contain the KHR_lights_punctual extension. If it was constructed in
7827 // memory it does not. In any case we update the JSON property using
7828 // the value from the struct. Last, if the node does not have a light
7829 // reference but the extension is still present, we remove it.
7830 if (node.light != -1) {
7831 detail::json_iterator it;
7832 if (!detail::FindMember(o, "extensions", it)) {
7833 detail::json extensions;
7834 detail::JsonSetObject(extensions);
7835 detail::JsonAddMember(o, "extensions", std::move(extensions));
7836 detail::FindMember(o, "extensions", it);
7837 }
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02007838 auto &extensions = detail::GetValue(it);
7839 if (!detail::FindMember(extensions, "KHR_lights_punctual", it)) {
David Siegelcfe64fb2023-06-07 15:18:38 +02007840 detail::json lights_punctual;
7841 detail::JsonSetObject(lights_punctual);
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02007842 detail::JsonAddMember(extensions, "KHR_lights_punctual",
7843 std::move(lights_punctual));
Thomas Gamperafcfb572023-11-23 14:12:54 +01007844 detail::FindMember(extensions, "KHR_lights_punctual", it);
David Siegelcfe64fb2023-06-07 15:18:38 +02007845 }
David Siegel8d5d0b32023-06-07 15:35:35 +02007846 SerializeNumberProperty("light", node.light, detail::GetValue(it));
David Siegelcfe64fb2023-06-07 15:18:38 +02007847 } else {
7848 // node has no light ref (any longer)... so we clean up
7849 detail::json_iterator ext_it;
7850 if (detail::FindMember(o, "extensions", ext_it)) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02007851 auto &extensions = detail::GetValue(ext_it);
David Siegelcfe64fb2023-06-07 15:18:38 +02007852 detail::json_iterator lp_it;
7853 if (detail::FindMember(extensions, "KHR_lights_punctual", lp_it)) {
7854 detail::Erase(extensions, lp_it);
7855 }
7856 if (detail::IsEmpty(extensions)) {
7857 detail::Erase(o, ext_it);
7858 }
7859 }
7860 }
7861
Baranob_Ilya78864c82023-06-12 10:43:52 +04007862 // KHR_audio
7863 if (node.emitter != -1) {
7864 detail::json_iterator it;
7865 if (!detail::FindMember(o, "extensions", it)) {
7866 detail::json extensions;
7867 detail::JsonSetObject(extensions);
7868 detail::JsonAddMember(o, "extensions", std::move(extensions));
7869 detail::FindMember(o, "extensions", it);
7870 }
7871 auto &extensions = detail::GetValue(it);
7872 if (!detail::FindMember(extensions, "KHR_audio", it)) {
7873 detail::json audio;
7874 detail::JsonSetObject(audio);
7875 detail::JsonAddMember(extensions, "KHR_audio", std::move(audio));
Thomas Gamperf1bdf432024-02-05 16:50:46 +01007876 detail::FindMember(extensions, "KHR_audio", it);
Baranob_Ilya78864c82023-06-12 10:43:52 +04007877 }
7878 SerializeNumberProperty("emitter", node.emitter, detail::GetValue(it));
7879 } else {
7880 detail::json_iterator ext_it;
7881 if (detail::FindMember(o, "extensions", ext_it)) {
7882 auto &extensions = detail::GetValue(ext_it);
7883 detail::json_iterator lp_it;
7884 if (detail::FindMember(extensions, "KHR_audio", lp_it)) {
7885 detail::Erase(extensions, lp_it);
7886 }
7887 if (detail::IsEmpty(extensions)) {
7888 detail::Erase(o, ext_it);
7889 }
7890 }
7891 }
7892
Thomas Gamperf1bdf432024-02-05 16:50:46 +01007893 // MSFT_lod
7894 if (!node.lods.empty()) {
7895 detail::json_iterator it;
7896 if (!detail::FindMember(o, "extensions", it)) {
7897 detail::json extensions;
7898 detail::JsonSetObject(extensions);
7899 detail::JsonAddMember(o, "extensions", std::move(extensions));
7900 detail::FindMember(o, "extensions", it);
7901 }
7902 auto &extensions = detail::GetValue(it);
7903 if (!detail::FindMember(extensions, "MSFT_lod", it)) {
7904 detail::json lod;
7905 detail::JsonSetObject(lod);
7906 detail::JsonAddMember(extensions, "MSFT_lod", std::move(lod));
7907 detail::FindMember(extensions, "MSFT_lod", it);
7908 }
7909 SerializeNumberArrayProperty<int>("ids", node.lods, detail::GetValue(it));
7910 } else {
7911 detail::json_iterator ext_it;
7912 if (detail::FindMember(o, "extensions", ext_it)) {
7913 auto &extensions = detail::GetValue(ext_it);
7914 detail::json_iterator lp_it;
7915 if (detail::FindMember(extensions, "MSFT_lod", lp_it)) {
7916 detail::Erase(extensions, lp_it);
7917 }
7918 if (detail::IsEmpty(extensions)) {
7919 detail::Erase(o, ext_it);
7920 }
7921 }
7922 }
7923
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09007924 if (!node.name.empty()) SerializeStringProperty("name", node.name, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007925 SerializeNumberArrayProperty<int>("children", node.children, o);
7926}
7927
David03ad33c2023-02-15 23:35:51 -06007928static void SerializeGltfSampler(const Sampler &sampler, detail::json &o) {
s00665032137a7ca2023-01-13 12:52:08 +07007929 if (!sampler.name.empty()) {
7930 SerializeStringProperty("name", sampler.name, o);
7931 }
Syoyo Fujitaee179b22019-08-16 13:11:30 +09007932 if (sampler.magFilter != -1) {
7933 SerializeNumberProperty("magFilter", sampler.magFilter, o);
7934 }
7935 if (sampler.minFilter != -1) {
7936 SerializeNumberProperty("minFilter", sampler.minFilter, o);
7937 }
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09007938 // SerializeNumberProperty("wrapR", sampler.wrapR, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007939 SerializeNumberProperty("wrapS", sampler.wrapS, o);
7940 SerializeNumberProperty("wrapT", sampler.wrapT, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007941
David Siegel07616e82023-06-06 01:24:53 +02007942 SerializeExtrasAndExtensions(sampler, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007943}
7944
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007945static void SerializeGltfOrthographicCamera(const OrthographicCamera &camera,
David03ad33c2023-02-15 23:35:51 -06007946 detail::json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007947 SerializeNumberProperty("zfar", camera.zfar, o);
7948 SerializeNumberProperty("znear", camera.znear, o);
7949 SerializeNumberProperty("xmag", camera.xmag, o);
7950 SerializeNumberProperty("ymag", camera.ymag, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007951
David Siegel07616e82023-06-06 01:24:53 +02007952 SerializeExtrasAndExtensions(camera, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007953}
7954
7955static void SerializeGltfPerspectiveCamera(const PerspectiveCamera &camera,
David03ad33c2023-02-15 23:35:51 -06007956 detail::json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007957 SerializeNumberProperty("zfar", camera.zfar, o);
7958 SerializeNumberProperty("znear", camera.znear, o);
7959 if (camera.aspectRatio > 0) {
7960 SerializeNumberProperty("aspectRatio", camera.aspectRatio, o);
7961 }
7962
7963 if (camera.yfov > 0) {
7964 SerializeNumberProperty("yfov", camera.yfov, o);
7965 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007966
David Siegel07616e82023-06-06 01:24:53 +02007967 SerializeExtrasAndExtensions(camera, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007968}
7969
David03ad33c2023-02-15 23:35:51 -06007970static void SerializeGltfCamera(const Camera &camera, detail::json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007971 SerializeStringProperty("type", camera.type, o);
7972 if (!camera.name.empty()) {
Selmar Kokee3d0662018-10-08 16:20:43 +02007973 SerializeStringProperty("name", camera.name, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007974 }
7975
7976 if (camera.type.compare("orthographic") == 0) {
David03ad33c2023-02-15 23:35:51 -06007977 detail::json orthographic;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007978 SerializeGltfOrthographicCamera(camera.orthographic, orthographic);
David1f9a4b92023-02-15 22:56:18 -06007979 detail::JsonAddMember(o, "orthographic", std::move(orthographic));
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007980 } else if (camera.type.compare("perspective") == 0) {
David03ad33c2023-02-15 23:35:51 -06007981 detail::json perspective;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007982 SerializeGltfPerspectiveCamera(camera.perspective, perspective);
David1f9a4b92023-02-15 22:56:18 -06007983 detail::JsonAddMember(o, "perspective", std::move(perspective));
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007984 } else {
7985 // ???
7986 }
Syoyofe77cc52020-05-09 02:41:07 +09007987
David Siegel07616e82023-06-06 01:24:53 +02007988 SerializeExtrasAndExtensions(camera, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007989}
7990
David03ad33c2023-02-15 23:35:51 -06007991static void SerializeGltfScene(const Scene &scene, detail::json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007992 SerializeNumberArrayProperty<int>("nodes", scene.nodes, o);
7993
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007994 if (scene.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007995 SerializeStringProperty("name", scene.name, o);
7996 }
David Siegel07616e82023-06-06 01:24:53 +02007997 SerializeExtrasAndExtensions(scene, o);
Baranob_Ilya879cb472023-06-12 13:35:05 +04007998
7999 // KHR_audio
8000 if (!scene.audioEmitters.empty()) {
8001 detail::json_iterator it;
8002 if (!detail::FindMember(o, "extensions", it)) {
8003 detail::json extensions;
8004 detail::JsonSetObject(extensions);
8005 detail::JsonAddMember(o, "extensions", std::move(extensions));
8006 detail::FindMember(o, "extensions", it);
8007 }
8008 auto &extensions = detail::GetValue(it);
8009 if (!detail::FindMember(extensions, "KHR_audio", it)) {
8010 detail::json audio;
8011 detail::JsonSetObject(audio);
8012 detail::JsonAddMember(extensions, "KHR_audio", std::move(audio));
8013 detail::FindMember(o, "KHR_audio", it);
8014 }
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02008015 SerializeNumberArrayProperty("emitters", scene.audioEmitters,
8016 detail::GetValue(it));
Baranob_Ilya879cb472023-06-12 13:35:05 +04008017 } else {
8018 detail::json_iterator ext_it;
8019 if (detail::FindMember(o, "extensions", ext_it)) {
8020 auto &extensions = detail::GetValue(ext_it);
8021 detail::json_iterator lp_it;
8022 if (detail::FindMember(extensions, "KHR_audio", lp_it)) {
8023 detail::Erase(extensions, lp_it);
8024 }
8025 if (detail::IsEmpty(extensions)) {
8026 detail::Erase(o, ext_it);
8027 }
8028 }
8029 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008030}
8031
David03ad33c2023-02-15 23:35:51 -06008032static void SerializeGltfSkin(const Skin &skin, detail::json &o) {
Syoyo Fujitad07b9762021-04-28 19:15:16 +09008033 // required
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008034 SerializeNumberArrayProperty<int>("joints", skin.joints, o);
Syoyo Fujitad07b9762021-04-28 19:15:16 +09008035
8036 if (skin.inverseBindMatrices >= 0) {
8037 SerializeNumberProperty("inverseBindMatrices", skin.inverseBindMatrices, o);
8038 }
8039
8040 if (skin.skeleton >= 0) {
8041 SerializeNumberProperty("skeleton", skin.skeleton, o);
8042 }
8043
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09008044 if (skin.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008045 SerializeStringProperty("name", skin.name, o);
8046 }
David Siegel07616e82023-06-06 01:24:53 +02008047
8048 SerializeExtrasAndExtensions(skin, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008049}
8050
David03ad33c2023-02-15 23:35:51 -06008051static void SerializeGltfTexture(const Texture &texture, detail::json &o) {
johan bowaldb97d34c2018-04-02 07:29:29 +02008052 if (texture.sampler > -1) {
johan bowald642a3432018-04-01 12:37:18 +02008053 SerializeNumberProperty("sampler", texture.sampler, o);
8054 }
Selmar Kok8eb39042018-10-05 14:29:35 +02008055 if (texture.source > -1) {
8056 SerializeNumberProperty("source", texture.source, o);
8057 }
Christophe820ede82019-07-04 15:21:21 +09008058 if (texture.name.size()) {
8059 SerializeStringProperty("name", texture.name, o);
8060 }
David Siegel07616e82023-06-06 01:24:53 +02008061 SerializeExtrasAndExtensions(texture, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008062}
8063
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008064///
8065/// Serialize all properties except buffers and images.
8066///
David03ad33c2023-02-15 23:35:51 -06008067static void SerializeGltfModel(const Model *model, detail::json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09008068 // ACCESSORS
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008069 if (model->accessors.size()) {
David03ad33c2023-02-15 23:35:51 -06008070 detail::json accessors;
David1f9a4b92023-02-15 22:56:18 -06008071 detail::JsonReserveArray(accessors, model->accessors.size());
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008072 for (unsigned int i = 0; i < model->accessors.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06008073 detail::json accessor;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008074 SerializeGltfAccessor(model->accessors[i], accessor);
David1f9a4b92023-02-15 22:56:18 -06008075 detail::JsonPushBack(accessors, std::move(accessor));
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008076 }
David1f9a4b92023-02-15 22:56:18 -06008077 detail::JsonAddMember(o, "accessors", std::move(accessors));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008078 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008079
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09008080 // ANIMATIONS
8081 if (model->animations.size()) {
David03ad33c2023-02-15 23:35:51 -06008082 detail::json animations;
David1f9a4b92023-02-15 22:56:18 -06008083 detail::JsonReserveArray(animations, model->animations.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09008084 for (unsigned int i = 0; i < model->animations.size(); ++i) {
8085 if (model->animations[i].channels.size()) {
David03ad33c2023-02-15 23:35:51 -06008086 detail::json animation;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008087 SerializeGltfAnimation(model->animations[i], animation);
David1f9a4b92023-02-15 22:56:18 -06008088 detail::JsonPushBack(animations, std::move(animation));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008089 }
8090 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09008091
David1f9a4b92023-02-15 22:56:18 -06008092 detail::JsonAddMember(o, "animations", std::move(animations));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008093 }
8094
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09008095 // ASSET
David03ad33c2023-02-15 23:35:51 -06008096 detail::json asset;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008097 SerializeGltfAsset(model->asset, asset);
David1f9a4b92023-02-15 22:56:18 -06008098 detail::JsonAddMember(o, "asset", std::move(asset));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008099
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008100 // BUFFERVIEWS
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008101 if (model->bufferViews.size()) {
David03ad33c2023-02-15 23:35:51 -06008102 detail::json bufferViews;
David1f9a4b92023-02-15 22:56:18 -06008103 detail::JsonReserveArray(bufferViews, model->bufferViews.size());
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008104 for (unsigned int i = 0; i < model->bufferViews.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06008105 detail::json bufferView;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008106 SerializeGltfBufferView(model->bufferViews[i], bufferView);
David1f9a4b92023-02-15 22:56:18 -06008107 detail::JsonPushBack(bufferViews, std::move(bufferView));
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008108 }
David1f9a4b92023-02-15 22:56:18 -06008109 detail::JsonAddMember(o, "bufferViews", std::move(bufferViews));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008110 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008111
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008112 // Extensions required
8113 if (model->extensionsRequired.size()) {
8114 SerializeStringArrayProperty("extensionsRequired",
8115 model->extensionsRequired, o);
8116 }
8117
8118 // MATERIALS
8119 if (model->materials.size()) {
David03ad33c2023-02-15 23:35:51 -06008120 detail::json materials;
David1f9a4b92023-02-15 22:56:18 -06008121 detail::JsonReserveArray(materials, model->materials.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008122 for (unsigned int i = 0; i < model->materials.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06008123 detail::json material;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008124 SerializeGltfMaterial(model->materials[i], material);
Syoyo Fujita68adc4b2020-10-22 22:27:12 +09008125
David1f9a4b92023-02-15 22:56:18 -06008126 if (detail::JsonIsNull(material)) {
Syoyo Fujita68adc4b2020-10-22 22:27:12 +09008127 // Issue 294.
8128 // `material` does not have any required parameters
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09008129 // so the result may be null(unmodified) when all material parameters
8130 // have default value.
Syoyo Fujita68adc4b2020-10-22 22:27:12 +09008131 //
8132 // null is not allowed thus we create an empty JSON object.
David1f9a4b92023-02-15 22:56:18 -06008133 detail::JsonSetObject(material);
Syoyo Fujita68adc4b2020-10-22 22:27:12 +09008134 }
David1f9a4b92023-02-15 22:56:18 -06008135 detail::JsonPushBack(materials, std::move(material));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008136 }
David1f9a4b92023-02-15 22:56:18 -06008137 detail::JsonAddMember(o, "materials", std::move(materials));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008138 }
8139
8140 // MESHES
8141 if (model->meshes.size()) {
David03ad33c2023-02-15 23:35:51 -06008142 detail::json meshes;
David1f9a4b92023-02-15 22:56:18 -06008143 detail::JsonReserveArray(meshes, model->meshes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008144 for (unsigned int i = 0; i < model->meshes.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06008145 detail::json mesh;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008146 SerializeGltfMesh(model->meshes[i], mesh);
David1f9a4b92023-02-15 22:56:18 -06008147 detail::JsonPushBack(meshes, std::move(mesh));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008148 }
David1f9a4b92023-02-15 22:56:18 -06008149 detail::JsonAddMember(o, "meshes", std::move(meshes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008150 }
8151
8152 // NODES
8153 if (model->nodes.size()) {
David03ad33c2023-02-15 23:35:51 -06008154 detail::json nodes;
David1f9a4b92023-02-15 22:56:18 -06008155 detail::JsonReserveArray(nodes, model->nodes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008156 for (unsigned int i = 0; i < model->nodes.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06008157 detail::json node;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008158 SerializeGltfNode(model->nodes[i], node);
Thomas Gamperd4ea67c2023-11-23 11:59:18 +01008159
8160 if (detail::JsonIsNull(node)) {
8161 // Issue 457.
8162 // `node` does not have any required parameters,
8163 // so the result may be null(unmodified) when all node parameters
8164 // have default value.
8165 //
8166 // null is not allowed thus we create an empty JSON object.
8167 detail::JsonSetObject(node);
8168 }
David1f9a4b92023-02-15 22:56:18 -06008169 detail::JsonPushBack(nodes, std::move(node));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008170 }
David1f9a4b92023-02-15 22:56:18 -06008171 detail::JsonAddMember(o, "nodes", std::move(nodes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008172 }
8173
8174 // SCENE
8175 if (model->defaultScene > -1) {
8176 SerializeNumberProperty<int>("scene", model->defaultScene, o);
8177 }
8178
8179 // SCENES
8180 if (model->scenes.size()) {
David03ad33c2023-02-15 23:35:51 -06008181 detail::json scenes;
David1f9a4b92023-02-15 22:56:18 -06008182 detail::JsonReserveArray(scenes, model->scenes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008183 for (unsigned int i = 0; i < model->scenes.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06008184 detail::json currentScene;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008185 SerializeGltfScene(model->scenes[i], currentScene);
Thomas Gamper3203e192023-11-23 15:14:46 +01008186 if (detail::JsonIsNull(currentScene)) {
8187 // Issue 464.
8188 // `scene` does not have any required parameters,
8189 // so the result may be null(unmodified) when all scene parameters
8190 // have default value.
8191 //
8192 // null is not allowed thus we create an empty JSON object.
8193 detail::JsonSetObject(currentScene);
8194 }
David1f9a4b92023-02-15 22:56:18 -06008195 detail::JsonPushBack(scenes, std::move(currentScene));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008196 }
David1f9a4b92023-02-15 22:56:18 -06008197 detail::JsonAddMember(o, "scenes", std::move(scenes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008198 }
8199
8200 // SKINS
8201 if (model->skins.size()) {
David03ad33c2023-02-15 23:35:51 -06008202 detail::json skins;
David1f9a4b92023-02-15 22:56:18 -06008203 detail::JsonReserveArray(skins, model->skins.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008204 for (unsigned int i = 0; i < model->skins.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06008205 detail::json skin;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008206 SerializeGltfSkin(model->skins[i], skin);
David1f9a4b92023-02-15 22:56:18 -06008207 detail::JsonPushBack(skins, std::move(skin));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008208 }
David1f9a4b92023-02-15 22:56:18 -06008209 detail::JsonAddMember(o, "skins", std::move(skins));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008210 }
8211
8212 // TEXTURES
8213 if (model->textures.size()) {
David03ad33c2023-02-15 23:35:51 -06008214 detail::json textures;
David1f9a4b92023-02-15 22:56:18 -06008215 detail::JsonReserveArray(textures, model->textures.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008216 for (unsigned int i = 0; i < model->textures.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06008217 detail::json texture;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008218 SerializeGltfTexture(model->textures[i], texture);
David1f9a4b92023-02-15 22:56:18 -06008219 detail::JsonPushBack(textures, std::move(texture));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008220 }
David1f9a4b92023-02-15 22:56:18 -06008221 detail::JsonAddMember(o, "textures", std::move(textures));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008222 }
8223
8224 // SAMPLERS
8225 if (model->samplers.size()) {
David03ad33c2023-02-15 23:35:51 -06008226 detail::json samplers;
David1f9a4b92023-02-15 22:56:18 -06008227 detail::JsonReserveArray(samplers, model->samplers.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008228 for (unsigned int i = 0; i < model->samplers.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06008229 detail::json sampler;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008230 SerializeGltfSampler(model->samplers[i], sampler);
David1f9a4b92023-02-15 22:56:18 -06008231 detail::JsonPushBack(samplers, std::move(sampler));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008232 }
David1f9a4b92023-02-15 22:56:18 -06008233 detail::JsonAddMember(o, "samplers", std::move(samplers));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008234 }
8235
8236 // CAMERAS
8237 if (model->cameras.size()) {
David03ad33c2023-02-15 23:35:51 -06008238 detail::json cameras;
David1f9a4b92023-02-15 22:56:18 -06008239 detail::JsonReserveArray(cameras, model->cameras.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008240 for (unsigned int i = 0; i < model->cameras.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06008241 detail::json camera;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008242 SerializeGltfCamera(model->cameras[i], camera);
David1f9a4b92023-02-15 22:56:18 -06008243 detail::JsonPushBack(cameras, std::move(camera));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008244 }
David1f9a4b92023-02-15 22:56:18 -06008245 detail::JsonAddMember(o, "cameras", std::move(cameras));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008246 }
8247
David Siegel07616e82023-06-06 01:24:53 +02008248 // EXTRAS & EXTENSIONS
8249 SerializeExtrasAndExtensions(*model, o);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008250
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008251 auto extensionsUsed = model->extensionsUsed;
8252
8253 // LIGHTS as KHR_lights_punctual
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008254 if (model->lights.size()) {
David03ad33c2023-02-15 23:35:51 -06008255 detail::json lights;
David1f9a4b92023-02-15 22:56:18 -06008256 detail::JsonReserveArray(lights, model->lights.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008257 for (unsigned int i = 0; i < model->lights.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06008258 detail::json light;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008259 SerializeGltfLight(model->lights[i], light);
David1f9a4b92023-02-15 22:56:18 -06008260 detail::JsonPushBack(lights, std::move(light));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008261 }
David03ad33c2023-02-15 23:35:51 -06008262 detail::json khr_lights_cmn;
David1f9a4b92023-02-15 22:56:18 -06008263 detail::JsonAddMember(khr_lights_cmn, "lights", std::move(lights));
David03ad33c2023-02-15 23:35:51 -06008264 detail::json ext_j;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008265
jrkooncecba5d6c2019-08-29 11:26:22 -05008266 {
David03ad33c2023-02-15 23:35:51 -06008267 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06008268 if (detail::FindMember(o, "extensions", it)) {
8269 detail::JsonAssign(ext_j, detail::GetValue(it));
jrkooncecba5d6c2019-08-29 11:26:22 -05008270 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008271 }
8272
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02008273 detail::JsonAddMember(ext_j, "KHR_lights_punctual",
8274 std::move(khr_lights_cmn));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008275
David1f9a4b92023-02-15 22:56:18 -06008276 detail::JsonAddMember(o, "extensions", std::move(ext_j));
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008277
8278 // Also add "KHR_lights_punctual" to `extensionsUsed`
8279 {
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04008280 auto has_khr_lights_punctual =
8281 std::find_if(extensionsUsed.begin(), extensionsUsed.end(),
8282 [](const std::string &s) {
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08008283 return (s.compare("KHR_lights_punctual") == 0);
8284 });
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008285
8286 if (has_khr_lights_punctual == extensionsUsed.end()) {
8287 extensionsUsed.push_back("KHR_lights_punctual");
8288 }
8289 }
8290 }
8291
Baranob_Ilya78864c82023-06-12 10:43:52 +04008292 // KHR_audio
8293 if (!model->audioEmitters.empty() || !model->audioSources.empty()) {
8294 detail::json emitters;
8295 detail::JsonReserveArray(emitters, model->audioEmitters.size());
8296 for (unsigned int i = 0; i < model->audioEmitters.size(); ++i) {
8297 detail::json emitter;
8298 SerializeGltfAudioEmitter(model->audioEmitters[i], emitter);
8299 detail::JsonPushBack(emitters, std::move(emitter));
8300 }
8301 detail::json khr_audio_cmn;
8302 detail::JsonAddMember(khr_audio_cmn, "emitters", std::move(emitters));
8303
8304 detail::json sources;
8305 detail::JsonReserveArray(sources, model->audioSources.size());
8306 for (unsigned int i = 0; i < model->audioSources.size(); ++i) {
8307 detail::json source;
8308 SerializeGltfAudioSource(model->audioSources[i], source);
8309 detail::JsonPushBack(sources, std::move(source));
8310 }
8311 detail::JsonAddMember(khr_audio_cmn, "sources", std::move(sources));
8312
8313 detail::json ext_j;
8314 {
8315 detail::json_const_iterator it;
8316 if (detail::FindMember(o, "extensions", it)) {
8317 detail::JsonAssign(ext_j, detail::GetValue(it));
8318 }
8319 }
8320
8321 detail::JsonAddMember(ext_j, "KHR_audio", std::move(khr_audio_cmn));
8322
8323 detail::JsonAddMember(o, "extensions", std::move(ext_j));
Baranob_Ilyac9657be2023-06-12 12:34:34 +04008324
8325 // Also add "KHR_audio" to `extensionsUsed`
8326 {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02008327 auto has_khr_audio = std::find_if(
8328 extensionsUsed.begin(), extensionsUsed.end(),
8329 [](const std::string &s) { return (s.compare("KHR_audio") == 0); });
Baranob_Ilyac9657be2023-06-12 12:34:34 +04008330
8331 if (has_khr_audio == extensionsUsed.end()) {
8332 extensionsUsed.push_back("KHR_audio");
8333 }
8334 }
Baranob_Ilya78864c82023-06-12 10:43:52 +04008335 }
8336
Thomas Gamperb274b342024-03-25 17:00:41 +01008337 // MSFT_lod
8338
8339 // Look if there is a node that employs MSFT_lod
8340 auto msft_lod_nodes_it = std::find_if(
8341 model->nodes.begin(), model->nodes.end(),
8342 [](const Node& node) { return !node.lods.empty(); });
8343
8344 // Look if there is a material that employs MSFT_lod
8345 auto msft_lod_materials_it = std::find_if(
8346 model->materials.begin(), model->materials.end(),
8347 [](const Material& material) {return !material.lods.empty(); });
8348
8349 // If either a node or a material employ MSFT_lod, then we need
8350 // to add MSFT_lod to the list of used extensions.
8351 if (msft_lod_nodes_it != model->nodes.end() || msft_lod_materials_it != model->materials.end()) {
8352 // First check if MSFT_lod is already registered as used extension
8353 auto has_msft_lod = std::find_if(
8354 extensionsUsed.begin(), extensionsUsed.end(),
8355 [](const std::string &s) { return (s.compare("MSFT_lod") == 0); });
8356
8357 // If MSFT_lod is not registered yet, add it
8358 if (has_msft_lod == extensionsUsed.end()) {
8359 extensionsUsed.push_back("MSFT_lod");
8360 }
8361 }
8362
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008363 // Extensions used
Selmar1208f052021-02-01 19:24:41 +01008364 if (extensionsUsed.size()) {
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008365 SerializeStringArrayProperty("extensionsUsed", extensionsUsed, o);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008366 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008367}
8368
Johan Bowald52936a02019-07-17 09:06:45 +02008369static bool WriteGltfStream(std::ostream &stream, const std::string &content) {
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008370 stream << content << std::endl;
Marco Langer76586242023-03-12 19:26:05 +01008371 return stream.good();
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008372}
8373
8374static bool WriteGltfFile(const std::string &output,
8375 const std::string &content) {
Harokyangfb256602019-10-30 16:13:52 +08008376#ifdef _WIN32
Syoyo Fujita45cac782019-11-09 20:42:55 +09008377#if defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08008378 std::ofstream gltfFile(UTF8ToWchar(output).c_str());
Syoyo Fujita45cac782019-11-09 20:42:55 +09008379#elif defined(__GLIBCXX__)
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008380 int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
8381 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
8382 __gnu_cxx::stdio_filebuf<char> wfile_buf(
8383 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09008384 std::ostream gltfFile(&wfile_buf);
8385 if (!wfile_buf.is_open()) return false;
Harokyangfb256602019-10-30 16:13:52 +08008386#else
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008387 std::ofstream gltfFile(output.c_str());
8388 if (!gltfFile.is_open()) return false;
Syoyo Fujita45cac782019-11-09 20:42:55 +09008389#endif
8390#else
8391 std::ofstream gltfFile(output.c_str());
8392 if (!gltfFile.is_open()) return false;
8393#endif
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008394 return WriteGltfStream(gltfFile, content);
8395}
8396
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09008397static bool WriteBinaryGltfStream(std::ostream &stream,
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02008398 const std::string &content,
8399 const std::vector<unsigned char> &binBuffer) {
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008400 const std::string header = "glTF";
8401 const int version = 2;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008402
Alexander Wood190382a2021-10-08 12:19:13 -04008403 const uint32_t content_size = uint32_t(content.size());
8404 const uint32_t binBuffer_size = uint32_t(binBuffer.size());
8405 // determine number of padding bytes required to ensure 4 byte alignment
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09008406 const uint32_t content_padding_size =
8407 content_size % 4 == 0 ? 0 : 4 - content_size % 4;
8408 const uint32_t bin_padding_size =
8409 binBuffer_size % 4 == 0 ? 0 : 4 - binBuffer_size % 4;
Syoyo Fujita1d205202019-11-16 17:00:17 +09008410
8411 // 12 bytes for header, JSON content length, 8 bytes for JSON chunk info.
Alexander Wood190382a2021-10-08 12:19:13 -04008412 // Chunk data must be located at 4-byte boundary, which may require padding
Syoyo Fujitac4166e42020-01-08 02:38:01 +09008413 const uint32_t length =
Alexander Wood9e3d1f62021-10-08 16:57:56 -04008414 12 + 8 + content_size + content_padding_size +
Alexander Wood13803652021-10-08 20:13:07 -04008415 (binBuffer_size ? (8 + binBuffer_size + bin_padding_size) : 0);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008416
Syoyo Fujitacea69e32019-08-20 17:10:30 +09008417 stream.write(header.c_str(), std::streamsize(header.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008418 stream.write(reinterpret_cast<const char *>(&version), sizeof(version));
8419 stream.write(reinterpret_cast<const char *>(&length), sizeof(length));
8420
8421 // JSON chunk info, then JSON data
Alexander Wood9e3d1f62021-10-08 16:57:56 -04008422 const uint32_t model_length = uint32_t(content.size()) + content_padding_size;
Syoyo Fujita72f4a552020-01-08 00:40:41 +09008423 const uint32_t model_format = 0x4E4F534A;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008424 stream.write(reinterpret_cast<const char *>(&model_length),
Johan Bowald52936a02019-07-17 09:06:45 +02008425 sizeof(model_length));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008426 stream.write(reinterpret_cast<const char *>(&model_format),
Johan Bowald52936a02019-07-17 09:06:45 +02008427 sizeof(model_format));
Syoyo Fujitacea69e32019-08-20 17:10:30 +09008428 stream.write(content.c_str(), std::streamsize(content.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008429
8430 // Chunk must be multiplies of 4, so pad with spaces
Alexander Wood9e3d1f62021-10-08 16:57:56 -04008431 if (content_padding_size > 0) {
8432 const std::string padding = std::string(size_t(content_padding_size), ' ');
Syoyo Fujitacea69e32019-08-20 17:10:30 +09008433 stream.write(padding.c_str(), std::streamsize(padding.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008434 }
Syoyo Fujitac4166e42020-01-08 02:38:01 +09008435 if (binBuffer.size() > 0) {
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02008436 // BIN chunk info, then BIN data
Syoyo Fujita72f4a552020-01-08 00:40:41 +09008437 const uint32_t bin_length = uint32_t(binBuffer.size()) + bin_padding_size;
8438 const uint32_t bin_format = 0x004e4942;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02008439 stream.write(reinterpret_cast<const char *>(&bin_length),
Syoyo Fujitac4166e42020-01-08 02:38:01 +09008440 sizeof(bin_length));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02008441 stream.write(reinterpret_cast<const char *>(&bin_format),
Syoyo Fujitac4166e42020-01-08 02:38:01 +09008442 sizeof(bin_format));
8443 stream.write(reinterpret_cast<const char *>(binBuffer.data()),
8444 std::streamsize(binBuffer.size()));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02008445 // Chunksize must be multiplies of 4, so pad with zeroes
8446 if (bin_padding_size > 0) {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09008447 const std::vector<unsigned char> padding =
8448 std::vector<unsigned char>(size_t(bin_padding_size), 0);
8449 stream.write(reinterpret_cast<const char *>(padding.data()),
8450 std::streamsize(padding.size()));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02008451 }
8452 }
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09008453
Marco Langer76586242023-03-12 19:26:05 +01008454 stream.flush();
8455 return stream.good();
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008456}
8457
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09008458static bool WriteBinaryGltfFile(const std::string &output,
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02008459 const std::string &content,
8460 const std::vector<unsigned char> &binBuffer) {
Harokyangfb256602019-10-30 16:13:52 +08008461#ifdef _WIN32
Syoyo Fujita45cac782019-11-09 20:42:55 +09008462#if defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08008463 std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09008464#elif defined(__GLIBCXX__)
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008465 int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
8466 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
8467 __gnu_cxx::stdio_filebuf<char> wfile_buf(
8468 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita4ab03862019-11-10 15:31:17 +09008469 std::ostream gltfFile(&wfile_buf);
Harokyangfb256602019-10-30 16:13:52 +08008470#else
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008471 std::ofstream gltfFile(output.c_str(), std::ios::binary);
Harokyangfb256602019-10-30 16:13:52 +08008472#endif
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008473#else
8474 std::ofstream gltfFile(output.c_str(), std::ios::binary);
jrkooncecba5d6c2019-08-29 11:26:22 -05008475#endif
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09008476 return WriteBinaryGltfStream(gltfFile, content, binBuffer);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008477}
8478
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08008479bool TinyGLTF::WriteGltfSceneToStream(const Model *model, std::ostream &stream,
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008480 bool prettyPrint = true,
8481 bool writeBinary = false) {
David03ad33c2023-02-15 23:35:51 -06008482 detail::JsonDocument output;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008483
8484 /// Serialize all properties except buffers and images.
8485 SerializeGltfModel(model, output);
8486
8487 // BUFFERS
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02008488 std::vector<unsigned char> binBuffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008489 if (model->buffers.size()) {
David03ad33c2023-02-15 23:35:51 -06008490 detail::json buffers;
David1f9a4b92023-02-15 22:56:18 -06008491 detail::JsonReserveArray(buffers, model->buffers.size());
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008492 for (unsigned int i = 0; i < model->buffers.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06008493 detail::json buffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008494 if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
8495 SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008496 } else {
8497 SerializeGltfBuffer(model->buffers[i], buffer);
8498 }
David1f9a4b92023-02-15 22:56:18 -06008499 detail::JsonPushBack(buffers, std::move(buffer));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02008500 }
David1f9a4b92023-02-15 22:56:18 -06008501 detail::JsonAddMember(output, "buffers", std::move(buffers));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008502 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008503
8504 // IMAGES
8505 if (model->images.size()) {
David03ad33c2023-02-15 23:35:51 -06008506 detail::json images;
David1f9a4b92023-02-15 22:56:18 -06008507 detail::JsonReserveArray(images, model->images.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008508 for (unsigned int i = 0; i < model->images.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06008509 detail::json image;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008510
Bryn Lloydc704d732023-06-21 18:42:24 +02008511 std::string dummystring;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008512 // UpdateImageObject need baseDir but only uses it if embeddedImages is
8513 // enabled, since we won't write separate images when writing to a stream
8514 // we
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08008515 std::string uri;
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08008516 if (!UpdateImageObject(model->images[i], dummystring, int(i), true,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08008517 &uri_cb, &this->WriteImageData,
8518 this->write_image_user_data_, &uri)) {
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08008519 return false;
8520 }
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08008521 SerializeGltfImage(model->images[i], uri, image);
David1f9a4b92023-02-15 22:56:18 -06008522 detail::JsonPushBack(images, std::move(image));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008523 }
David1f9a4b92023-02-15 22:56:18 -06008524 detail::JsonAddMember(output, "images", std::move(images));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008525 }
8526
8527 if (writeBinary) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02008528 return WriteBinaryGltfStream(stream, detail::JsonToString(output),
8529 binBuffer);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008530 } else {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02008531 return WriteGltfStream(stream,
8532 detail::JsonToString(output, prettyPrint ? 2 : -1));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008533 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008534}
8535
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08008536bool TinyGLTF::WriteGltfSceneToFile(const Model *model,
8537 const std::string &filename,
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008538 bool embedImages = false,
8539 bool embedBuffers = false,
8540 bool prettyPrint = true,
8541 bool writeBinary = false) {
David03ad33c2023-02-15 23:35:51 -06008542 detail::JsonDocument output;
Selmar Kok7cb31e42018-10-05 16:02:29 +02008543 std::string defaultBinFilename = GetBaseFilename(filename);
8544 std::string defaultBinFileExt = ".bin";
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00008545 std::string::size_type pos =
8546 defaultBinFilename.rfind('.', defaultBinFilename.length());
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008547
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09008548 if (pos != std::string::npos) {
Selmar Kok7cb31e42018-10-05 16:02:29 +02008549 defaultBinFilename = defaultBinFilename.substr(0, pos);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008550 }
johan bowald642a3432018-04-01 12:37:18 +02008551 std::string baseDir = GetBaseDir(filename);
8552 if (baseDir.empty()) {
8553 baseDir = "./";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05008554 }
Johan Bowald52936a02019-07-17 09:06:45 +02008555 /// Serialize all properties except buffers and images.
8556 SerializeGltfModel(model, output);
Syoyo Fujitad42767e2018-03-15 21:52:00 -05008557
Selmar Kok7cb31e42018-10-05 16:02:29 +02008558 // BUFFERS
Pyarelal Knowles385946d2023-01-03 18:18:04 -08008559 std::vector<std::string> usedFilenames;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02008560 std::vector<unsigned char> binBuffer;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008561 if (model->buffers.size()) {
David03ad33c2023-02-15 23:35:51 -06008562 detail::json buffers;
David1f9a4b92023-02-15 22:56:18 -06008563 detail::JsonReserveArray(buffers, model->buffers.size());
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008564 for (unsigned int i = 0; i < model->buffers.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06008565 detail::json buffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008566 if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
8567 SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008568 } else if (embedBuffers) {
8569 SerializeGltfBuffer(model->buffers[i], buffer);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00008570 } else {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008571 std::string binSavePath;
Pyarelal Knowles385946d2023-01-03 18:18:04 -08008572 std::string binFilename;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008573 std::string binUri;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008574 if (!model->buffers[i].uri.empty() &&
8575 !IsDataURI(model->buffers[i].uri)) {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008576 binUri = model->buffers[i].uri;
Pyarelal Knowles385946d2023-01-03 18:18:04 -08008577 if (!uri_cb.decode(binUri, &binFilename, uri_cb.user_data)) {
8578 return false;
8579 }
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008580 } else {
Pyarelal Knowles385946d2023-01-03 18:18:04 -08008581 binFilename = defaultBinFilename + defaultBinFileExt;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008582 bool inUse = true;
8583 int numUsed = 0;
8584 while (inUse) {
8585 inUse = false;
Pyarelal Knowles385946d2023-01-03 18:18:04 -08008586 for (const std::string &usedName : usedFilenames) {
8587 if (binFilename.compare(usedName) != 0) continue;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008588 inUse = true;
Pyarelal Knowles385946d2023-01-03 18:18:04 -08008589 binFilename = defaultBinFilename + std::to_string(numUsed++) +
8590 defaultBinFileExt;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008591 break;
8592 }
Selmar Kokc884e582018-10-05 16:25:54 +02008593 }
Pyarelal Knowles385946d2023-01-03 18:18:04 -08008594
8595 if (uri_cb.encode) {
8596 if (!uri_cb.encode(binFilename, "buffer", &binUri,
8597 uri_cb.user_data)) {
8598 return false;
8599 }
8600 } else {
8601 binUri = binFilename;
8602 }
Selmar Kokc884e582018-10-05 16:25:54 +02008603 }
Pyarelal Knowles385946d2023-01-03 18:18:04 -08008604 usedFilenames.push_back(binFilename);
8605 binSavePath = JoinPath(baseDir, binFilename);
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008606 if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
8607 binUri)) {
8608 return false;
8609 }
Selmar Kokc884e582018-10-05 16:25:54 +02008610 }
David1f9a4b92023-02-15 22:56:18 -06008611 detail::JsonPushBack(buffers, std::move(buffer));
johan bowald30c53472018-03-30 11:49:36 +02008612 }
David1f9a4b92023-02-15 22:56:18 -06008613 detail::JsonAddMember(output, "buffers", std::move(buffers));
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008614 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008615
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09008616 // IMAGES
Johan Bowaldfaa27222018-03-28 14:44:45 +02008617 if (model->images.size()) {
David03ad33c2023-02-15 23:35:51 -06008618 detail::json images;
David1f9a4b92023-02-15 22:56:18 -06008619 detail::JsonReserveArray(images, model->images.size());
Johan Bowaldfaa27222018-03-28 14:44:45 +02008620 for (unsigned int i = 0; i < model->images.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06008621 detail::json image;
johan bowald642a3432018-04-01 12:37:18 +02008622
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08008623 std::string uri;
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08008624 if (!UpdateImageObject(model->images[i], baseDir, int(i), embedImages,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08008625 &uri_cb, &this->WriteImageData,
8626 this->write_image_user_data_, &uri)) {
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08008627 return false;
8628 }
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08008629 SerializeGltfImage(model->images[i], uri, image);
David1f9a4b92023-02-15 22:56:18 -06008630 detail::JsonPushBack(images, std::move(image));
Johan Bowaldfaa27222018-03-28 14:44:45 +02008631 }
David1f9a4b92023-02-15 22:56:18 -06008632 detail::JsonAddMember(output, "images", std::move(images));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008633 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008634
David Harmonda9eac22018-08-30 08:06:05 -04008635 if (writeBinary) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02008636 return WriteBinaryGltfFile(filename, detail::JsonToString(output),
8637 binBuffer);
David Harmonda9eac22018-08-30 08:06:05 -04008638 } else {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02008639 return WriteGltfFile(filename,
8640 detail::JsonToString(output, (prettyPrint ? 2 : -1)));
David Harmonda9eac22018-08-30 08:06:05 -04008641 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008642}
8643
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09008644} // namespace tinygltf
8645
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09008646#ifdef __clang__
8647#pragma clang diagnostic pop
8648#endif
8649
Syoyo Fujita612e5782022-09-18 21:01:39 +09008650#endif // TINYGLTF_IMPLEMENTATION