blob: 153bcec99e7d24041e4d3277d4c400a542dccc38 [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
James Ward9b4e1ea2024-03-25 17:50:33 +0000340 const Value &Get(size_t idx) const {
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900341 static Value null_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900342 assert(IsArray());
James Ward9b4e1ea2024-03-25 17:50:33 +0000343 return (idx < array_value_.size())
344 ? array_value_[idx]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900345 : null_value;
346 }
347
348 // Lookup value from a key-value pair
349 const Value &Get(const std::string &key) const {
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900350 static Value null_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900351 assert(IsObject());
352 Object::const_iterator it = object_value_.find(key);
353 return (it != object_value_.end()) ? it->second : null_value;
354 }
355
356 size_t ArrayLen() const {
357 if (!IsArray()) return 0;
358 return array_value_.size();
359 }
360
361 // Valid only for object type.
362 bool Has(const std::string &key) const {
363 if (!IsObject()) return false;
364 Object::const_iterator it = object_value_.find(key);
365 return (it != object_value_.end()) ? true : false;
366 }
367
368 // List keys
369 std::vector<std::string> Keys() const {
370 std::vector<std::string> keys;
371 if (!IsObject()) return keys; // empty
372
373 for (Object::const_iterator it = object_value_.begin();
374 it != object_value_.end(); ++it) {
375 keys.push_back(it->first);
376 }
377
378 return keys;
379 }
380
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900381 size_t Size() const { return (IsArray() ? ArrayLen() : Keys().size()); }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900382
383 bool operator==(const tinygltf::Value &other) const;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +0000384
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900385 protected:
Syoyo Fujita046400b2019-07-24 19:26:48 +0900386 int type_ = NULL_TYPE;
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900387
Syoyo Fujita046400b2019-07-24 19:26:48 +0900388 int int_value_ = 0;
Syoyo Fujita150f2432019-07-25 19:22:44 +0900389 double real_value_ = 0.0;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900390 std::string string_value_;
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900391 std::vector<unsigned char> binary_value_;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900392 Array array_value_;
393 Object object_value_;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900394 bool boolean_value_ = false;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900395};
396
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900397#ifdef __clang__
398#pragma clang diagnostic pop
399#endif
400
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900401#define TINYGLTF_VALUE_GET(ctype, var) \
402 template <> \
403 inline const ctype &Value::Get<ctype>() const { \
404 return var; \
405 } \
406 template <> \
407 inline ctype &Value::Get<ctype>() { \
408 return var; \
409 }
410TINYGLTF_VALUE_GET(bool, boolean_value_)
Syoyo Fujita150f2432019-07-25 19:22:44 +0900411TINYGLTF_VALUE_GET(double, real_value_)
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900412TINYGLTF_VALUE_GET(int, int_value_)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900413TINYGLTF_VALUE_GET(std::string, string_value_)
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900414TINYGLTF_VALUE_GET(std::vector<unsigned char>, binary_value_)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900415TINYGLTF_VALUE_GET(Value::Array, array_value_)
416TINYGLTF_VALUE_GET(Value::Object, object_value_)
417#undef TINYGLTF_VALUE_GET
418
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900419#ifdef __clang__
420#pragma clang diagnostic push
421#pragma clang diagnostic ignored "-Wc++98-compat"
422#pragma clang diagnostic ignored "-Wpadded"
423#endif
424
imallettd9ce9eb2022-10-07 10:37:09 -0700425/// Aggregate object for representing a color
Arthur Brainville58453192018-01-08 18:25:52 +0100426using ColorValue = std::array<double, 4>;
427
Syoyo Fujita046400b2019-07-24 19:26:48 +0900428// === legacy interface ====
429// TODO(syoyo): Deprecate `Parameter` class.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500430struct Parameter {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200431 bool bool_value = false;
Ben Buzbeef6af2242018-04-25 15:13:05 -0700432 bool has_number_value = false;
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900433 std::string string_value;
434 std::vector<double> number_array;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000435 std::map<std::string, double> json_double_value;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200436 double number_value = 0.0;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900437
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500438 // context sensitive methods. depending the type of the Parameter you are
439 // accessing, these are either valid or not
440 // If this parameter represent a texture map in a material, will return the
441 // texture index
Arthur Brainville58453192018-01-08 18:25:52 +0100442
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500443 /// Return the index of a texture if this Parameter is a texture map.
444 /// Returned value is only valid if the parameter represent a texture from a
445 /// material
Arthur Brainville41fe7722018-01-08 18:32:48 +0100446 int TextureIndex() const {
Arthur Brainville58453192018-01-08 18:25:52 +0100447 const auto it = json_double_value.find("index");
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500448 if (it != std::end(json_double_value)) {
Arthur Brainville58453192018-01-08 18:25:52 +0100449 return int(it->second);
450 }
451 return -1;
452 }
453
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000454 /// Return the index of a texture coordinate set if this Parameter is a
455 /// texture map. Returned value is only valid if the parameter represent a
456 /// texture from a material
Sascha Willemseb011062019-02-23 21:15:45 +0100457 int TextureTexCoord() const {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000458 const auto it = json_double_value.find("texCoord");
459 if (it != std::end(json_double_value)) {
460 return int(it->second);
461 }
imallettd9ce9eb2022-10-07 10:37:09 -0700462 // As per the spec, if texCoord is omitted, this parameter is 0
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000463 return 0;
Sascha Willemseb011062019-02-23 21:15:45 +0100464 }
465
Christophe820ede82019-07-04 15:21:21 +0900466 /// Return the scale of a texture if this Parameter is a normal texture map.
467 /// Returned value is only valid if the parameter represent a normal texture
468 /// from a material
469 double TextureScale() const {
470 const auto it = json_double_value.find("scale");
471 if (it != std::end(json_double_value)) {
472 return it->second;
473 }
imallettd9ce9eb2022-10-07 10:37:09 -0700474 // As per the spec, if scale is omitted, this parameter is 1
Arthur Brainville8a98d982019-07-05 00:26:02 +0200475 return 1;
476 }
Arthur Brianville (Ybalrid)2a9d9de2019-07-05 00:30:47 +0200477
Arthur Brainville8a98d982019-07-05 00:26:02 +0200478 /// Return the strength of a texture if this Parameter is a an occlusion map.
479 /// Returned value is only valid if the parameter represent an occlusion map
480 /// from a material
481 double TextureStrength() const {
482 const auto it = json_double_value.find("strength");
483 if (it != std::end(json_double_value)) {
484 return it->second;
485 }
imallettd9ce9eb2022-10-07 10:37:09 -0700486 // As per the spec, if strength is omitted, this parameter is 1
Arthur Brainville8a98d982019-07-05 00:26:02 +0200487 return 1;
Christophe820ede82019-07-04 15:21:21 +0900488 }
489
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500490 /// Material factor, like the roughness or metalness of a material
491 /// Returned value is only valid if the parameter represent a texture from a
492 /// material
Ben Buzbeef6af2242018-04-25 15:13:05 -0700493 double Factor() const { return number_value; }
Arthur Brainville58453192018-01-08 18:25:52 +0100494
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500495 /// Return the color of a material
496 /// Returned value is only valid if the parameter represent a texture from a
497 /// material
Arthur Brainville95853912018-01-08 18:37:44 +0100498 ColorValue ColorFactor() const {
Arthur Brainville58453192018-01-08 18:25:52 +0100499 return {
imallettd9ce9eb2022-10-07 10:37:09 -0700500 {// this aggregate initialize the std::array object, and uses C++11 RVO.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500501 number_array[0], number_array[1], number_array[2],
502 (number_array.size() > 3 ? number_array[3] : 1.0)}};
Arthur Brainville58453192018-01-08 18:25:52 +0100503 }
Selmar Kok31cb7f92018-10-03 15:39:05 +0200504
Selmar Kokff2b1f92019-10-21 17:58:09 +0200505 Parameter() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100506 DEFAULT_METHODS(Parameter)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900507 bool operator==(const Parameter &) const;
Arthur Brainville58453192018-01-08 18:25:52 +0100508};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900509
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900510#ifdef __clang__
511#pragma clang diagnostic pop
512#endif
513
514#ifdef __clang__
515#pragma clang diagnostic push
516#pragma clang diagnostic ignored "-Wpadded"
517#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900518
Syoyo Fujitabde70212016-02-07 17:38:17 +0900519typedef std::map<std::string, Parameter> ParameterMap;
Selmar Kokfa7022f2018-04-04 18:10:20 +0200520typedef std::map<std::string, Value> ExtensionMap;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900521
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000522struct AnimationChannel {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200523 int sampler{-1}; // required
524 int target_node{-1}; // optional index of the node to target (alternative
Jack Mousseau283b5522023-01-15 11:45:45 -0800525 // target should be provided by extension)
526 std::string target_path; // required with standard values of ["translation",
527 // "rotation", "scale", "weights"]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900528 Value extras;
Selmar Kok4e2988e2019-08-16 14:08:08 +0200529 ExtensionMap extensions;
David Siegeld852f502023-06-05 23:28:05 +0200530 Value target_extras;
Selmar Kok973d9b32020-01-21 18:45:24 +0100531 ExtensionMap target_extensions;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900532
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900533 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
534 std::string extras_json_string;
535 std::string extensions_json_string;
David Siegeld852f502023-06-05 23:28:05 +0200536 std::string target_extras_json_string;
Selmar Kok973d9b32020-01-21 18:45:24 +0100537 std::string target_extensions_json_string;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900538
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200539 AnimationChannel() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100540 DEFAULT_METHODS(AnimationChannel)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900541 bool operator==(const AnimationChannel &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000542};
543
544struct AnimationSampler {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200545 int input{-1}; // required
546 int output{-1}; // required
Arthur Brianville (Ybalrid)2a9d9de2019-07-05 00:30:47 +0200547 std::string interpolation; // "LINEAR", "STEP","CUBICSPLINE" or user defined
548 // string. default "LINEAR"
Jens Olssonb3af2f12018-06-04 10:17:49 +0200549 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900550 ExtensionMap extensions;
551
552 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
553 std::string extras_json_string;
554 std::string extensions_json_string;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000555
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200556 AnimationSampler() : interpolation("LINEAR") {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100557 DEFAULT_METHODS(AnimationSampler)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900558 bool operator==(const AnimationSampler &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000559};
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900560
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900561struct Animation {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900562 std::string name;
563 std::vector<AnimationChannel> channels;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000564 std::vector<AnimationSampler> samplers;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900565 Value extras;
Selmar Kok4e2988e2019-08-16 14:08:08 +0200566 ExtensionMap extensions;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200567
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900568 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
569 std::string extras_json_string;
570 std::string extensions_json_string;
571
Selmar Kokff2b1f92019-10-21 17:58:09 +0200572 Animation() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100573 DEFAULT_METHODS(Animation)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900574 bool operator==(const Animation &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900575};
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900576
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000577struct Skin {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900578 std::string name;
Bryn Lloydcb6a7072023-06-21 22:30:14 +0200579 int inverseBindMatrices{-1}; // required here but not in the spec
580 int skeleton{-1}; // The index of the node used as a skeleton root
581 std::vector<int> joints; // Indices of skeleton nodes
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000582
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900583 Value extras;
584 ExtensionMap extensions;
585
586 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
587 std::string extras_json_string;
588 std::string extensions_json_string;
589
Bryn Lloydcb6a7072023-06-21 22:30:14 +0200590 Skin() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100591 DEFAULT_METHODS(Skin)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900592 bool operator==(const Skin &) const;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000593};
594
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000595struct Sampler {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900596 std::string name;
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900597 // glTF 2.0 spec does not define default value for `minFilter` and
598 // `magFilter`. Set -1 in TinyGLTF(issue #186)
599 int minFilter =
600 -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR",
zz8a35b8f2021-05-14 13:49:33 +0800601 // "NEAREST_MIPMAP_NEAREST", "LINEAR_MIPMAP_NEAREST",
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900602 // "NEAREST_MIPMAP_LINEAR", "LINEAR_MIPMAP_LINEAR"]
603 int magFilter =
604 -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR"]
605 int wrapS =
606 TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
607 // "REPEAT"], default "REPEAT"
608 int wrapT =
609 TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
610 // "REPEAT"], default "REPEAT"
Syoyo Fujita52ff00a2022-08-16 20:08:45 +0900611 // int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; // TinyGLTF extension. currently
612 // not used.
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900613
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900614 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900615 ExtensionMap extensions;
616
617 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
618 std::string extras_json_string;
619 std::string extensions_json_string;
Syoyo Fujitac2615632016-06-19 21:56:06 +0900620
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200621 Sampler() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100622 DEFAULT_METHODS(Sampler)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900623 bool operator==(const Sampler &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000624};
625
Syoyo Fujita5b407452017-06-04 17:42:41 +0900626struct Image {
Syoyo Fujitac2615632016-06-19 21:56:06 +0900627 std::string name;
Bryn Lloydcb6a7072023-06-21 22:30:14 +0200628 int width{-1};
629 int height{-1};
630 int component{-1};
631 int bits{-1}; // bit depth per channel. 8(byte), 16 or 32.
632 int pixel_type{-1}; // pixel type(TINYGLTF_COMPONENT_TYPE_***). usually
633 // UBYTE(bits = 8) or USHORT(bits = 16)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900634 std::vector<unsigned char> image;
Bryn Lloydcb6a7072023-06-21 22:30:14 +0200635 int bufferView{-1}; // (required if no uri)
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500636 std::string mimeType; // (required if no uri) ["image/jpeg", "image/png",
637 // "image/bmp", "image/gif"]
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900638 std::string uri; // (required if no mimeType) uri is not decoded(e.g.
639 // whitespace may be represented as %20)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900640 Value extras;
Syoyo Fujita3e53feb2018-09-02 15:36:17 +0900641 ExtensionMap extensions;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900642
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900643 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
644 std::string extras_json_string;
645 std::string extensions_json_string;
646
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900647 // When this flag is true, data is stored to `image` in as-is format(e.g. jpeg
648 // compressed for "image/jpeg" mime) This feature is good if you use custom
649 // image loader function. (e.g. delayed decoding of images for faster glTF
650 // parsing) Default parser for Image does not provide as-is loading feature at
651 // the moment. (You can manipulate this by providing your own LoadImageData
652 // function)
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200653 bool as_is{false};
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900654
Bryn Lloydcb6a7072023-06-21 22:30:14 +0200655 Image() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100656 DEFAULT_METHODS(Image)
jrkoonced1e14722019-08-27 11:51:02 -0500657
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900658 bool operator==(const Image &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000659};
660
661struct Texture {
Vladimír Vondruš239be2c2018-07-24 23:23:56 +0200662 std::string name;
663
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200664 int sampler{-1};
665 int source{-1};
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900666 Value extras;
Selmar Kokfa7022f2018-04-04 18:10:20 +0200667 ExtensionMap extensions;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900668
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900669 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
670 std::string extras_json_string;
671 std::string extensions_json_string;
672
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200673 Texture() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100674 DEFAULT_METHODS(Texture)
jrkoonced1e14722019-08-27 11:51:02 -0500675
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900676 bool operator==(const Texture &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000677};
Syoyo Fujitabde70212016-02-07 17:38:17 +0900678
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900679struct TextureInfo {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200680 int index{-1}; // required.
681 int texCoord{0}; // The set index of texture's TEXCOORD attribute used for
682 // texture coordinate mapping.
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900683
684 Value extras;
685 ExtensionMap extensions;
686
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900687 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
688 std::string extras_json_string;
689 std::string extensions_json_string;
690
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200691 TextureInfo() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100692 DEFAULT_METHODS(TextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900693 bool operator==(const TextureInfo &) const;
694};
695
696struct NormalTextureInfo {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200697 int index{-1}; // required
698 int texCoord{0}; // The set index of texture's TEXCOORD attribute used for
699 // texture coordinate mapping.
700 double scale{
701 1.0}; // scaledNormal = normalize((<sampled normal texture value>
702 // * 2.0 - 1.0) * vec3(<normal scale>, <normal scale>, 1.0))
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900703
704 Value extras;
705 ExtensionMap extensions;
706
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900707 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
708 std::string extras_json_string;
709 std::string extensions_json_string;
710
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200711 NormalTextureInfo() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100712 DEFAULT_METHODS(NormalTextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900713 bool operator==(const NormalTextureInfo &) const;
714};
715
716struct OcclusionTextureInfo {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200717 int index{-1}; // required
718 int texCoord{0}; // The set index of texture's TEXCOORD attribute used for
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900719 // texture coordinate mapping.
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200720 double strength{1.0}; // occludedColor = lerp(color, color * <sampled
721 // occlusion texture value>, <occlusion strength>)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900722
723 Value extras;
724 ExtensionMap extensions;
725
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900726 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
727 std::string extras_json_string;
728 std::string extensions_json_string;
729
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200730 OcclusionTextureInfo() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100731 DEFAULT_METHODS(OcclusionTextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900732 bool operator==(const OcclusionTextureInfo &) const;
733};
734
735// pbrMetallicRoughness class defined in glTF 2.0 spec.
736struct PbrMetallicRoughness {
Thomas Gamper1f42c962023-11-22 15:59:13 +0100737 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 +0900738 TextureInfo baseColorTexture;
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200739 double metallicFactor{1.0}; // default 1
740 double roughnessFactor{1.0}; // default 1
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900741 TextureInfo metallicRoughnessTexture;
742
743 Value extras;
744 ExtensionMap extensions;
745
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900746 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
747 std::string extras_json_string;
748 std::string extensions_json_string;
749
Thomas Gamper1f42c962023-11-22 15:59:13 +0100750 PbrMetallicRoughness() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100751 DEFAULT_METHODS(PbrMetallicRoughness)
Thomas Gamper1f42c962023-11-22 15:59:13 +0100752
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900753 bool operator==(const PbrMetallicRoughness &) const;
754};
755
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000756// Each extension should be stored in a ParameterMap.
757// members not in the values could be included in the ParameterMap
758// to keep a single material model
759struct Material {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900760 std::string name;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900761
Thomas Gamper1f42c962023-11-22 15:59:13 +0100762 std::vector<double> emissiveFactor{0.0, 0.0, 0.0}; // length 3. default [0, 0, 0]
763 std::string alphaMode{"OPAQUE"}; // default "OPAQUE"
764 double alphaCutoff{0.5}; // default 0.5
765 bool doubleSided{false}; // default false
Thomas Gampera42263b2024-02-05 15:42:49 +0100766 std::vector<int> lods; // level of detail materials (MSFT_lod)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900767
768 PbrMetallicRoughness pbrMetallicRoughness;
769
770 NormalTextureInfo normalTexture;
771 OcclusionTextureInfo occlusionTexture;
772 TextureInfo emissiveTexture;
773
Syoyo Fujita046400b2019-07-24 19:26:48 +0900774 // For backward compatibility
775 // TODO(syoyo): Remove `values` and `additionalValues` in the next release.
776 ParameterMap values;
777 ParameterMap additionalValues;
Selmar09d2ff12018-03-15 17:30:42 +0100778
779 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900780 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200781
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900782 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
783 std::string extras_json_string;
784 std::string extensions_json_string;
785
Thomas Gamper1f42c962023-11-22 15:59:13 +0100786 Material() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100787 DEFAULT_METHODS(Material)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900788
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900789 bool operator==(const Material &) const;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000790};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900791
Syoyo Fujita5b407452017-06-04 17:42:41 +0900792struct BufferView {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900793 std::string name;
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900794 int buffer{-1}; // Required
Syoyo Fujita4e47bc72020-01-02 22:07:25 +0900795 size_t byteOffset{0}; // minimum 0, default 0
796 size_t byteLength{0}; // required, minimum 1. 0 = invalid
797 size_t byteStride{0}; // minimum 4, maximum 252 (multiple of 4), default 0 =
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900798 // understood to be tightly packed
799 int target{0}; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"] for vertex indices
imallettd9ce9eb2022-10-07 10:37:09 -0700800 // or attribs. Could be 0 for other data
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900801 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900802 ExtensionMap extensions;
803
804 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
805 std::string extras_json_string;
806 std::string extensions_json_string;
807
Syoyo Fujita4e47bc72020-01-02 22:07:25 +0900808 bool dracoDecoded{false}; // Flag indicating this has been draco decoded
Syoyo Fujitad17ff662017-05-29 02:53:12 +0900809
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200810 BufferView() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100811 DEFAULT_METHODS(BufferView)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900812 bool operator==(const BufferView &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000813};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900814
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000815struct Accessor {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200816 int bufferView{-1}; // optional in spec but required here since sparse
817 // accessor are not supported
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900818 std::string name;
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200819 size_t byteOffset{0};
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200820 bool normalized{false}; // optional.
821 int componentType{-1}; // (required) One of TINYGLTF_COMPONENT_TYPE_***
822 size_t count{0}; // required
823 int type{-1}; // (required) One of TINYGLTF_TYPE_*** ..
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900824 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900825 ExtensionMap extensions;
826
827 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
828 std::string extras_json_string;
829 std::string extensions_json_string;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000830
Syoyo Fujita7905a5b2021-01-21 20:33:11 +0900831 std::vector<double>
832 minValues; // optional. integer value is promoted to double
833 std::vector<double>
834 maxValues; // optional. integer value is promoted to double
Syoyo Fujitad9d012a2017-07-12 18:29:29 +0900835
David Siegeld852f502023-06-05 23:28:05 +0200836 struct Sparse {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000837 int count;
838 bool isSparse;
839 struct {
emimvi759976e2023-09-02 09:39:53 +0200840 size_t byteOffset;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000841 int bufferView;
842 int componentType; // a TINYGLTF_COMPONENT_TYPE_ value
David Siegeld852f502023-06-05 23:28:05 +0200843 Value extras;
844 ExtensionMap extensions;
845 std::string extras_json_string;
846 std::string extensions_json_string;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000847 } indices;
848 struct {
849 int bufferView;
emimvi759976e2023-09-02 09:39:53 +0200850 size_t byteOffset;
David Siegeld852f502023-06-05 23:28:05 +0200851 Value extras;
852 ExtensionMap extensions;
853 std::string extras_json_string;
854 std::string extensions_json_string;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000855 } values;
David Siegeld852f502023-06-05 23:28:05 +0200856 Value extras;
857 ExtensionMap extensions;
858 std::string extras_json_string;
859 std::string extensions_json_string;
860 };
861
862 Sparse sparse;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000863
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900864 ///
865 /// Utility function to compute byteStride for a given bufferView object.
866 /// Returns -1 upon invalid glTF value or parameter configuration.
867 ///
868 int ByteStride(const BufferView &bufferViewObject) const {
869 if (bufferViewObject.byteStride == 0) {
870 // Assume data is tightly packed.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500871 int componentSizeInBytes =
872 GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900873 if (componentSizeInBytes <= 0) {
874 return -1;
875 }
876
Syoyo Fujita81d75df2019-08-30 19:19:52 +0900877 int numComponents = GetNumComponentsInType(static_cast<uint32_t>(type));
878 if (numComponents <= 0) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900879 return -1;
880 }
881
Syoyo Fujita81d75df2019-08-30 19:19:52 +0900882 return componentSizeInBytes * numComponents;
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900883 } else {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200884 // Check if byteStride is a multiple of the size of the accessor's
885 // component type.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500886 int componentSizeInBytes =
887 GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900888 if (componentSizeInBytes <= 0) {
889 return -1;
890 }
891
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900892 if ((bufferViewObject.byteStride % uint32_t(componentSizeInBytes)) != 0) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900893 return -1;
894 }
Arthur Brainville8ce4e542018-01-10 01:27:12 +0100895 return static_cast<int>(bufferViewObject.byteStride);
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900896 }
897
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900898 // unreachable return 0;
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900899 }
900
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900901 Accessor()
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200902
903 {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000904 sparse.isSparse = false;
905 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100906 DEFAULT_METHODS(Accessor)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900907 bool operator==(const tinygltf::Accessor &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000908};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900909
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900910struct PerspectiveCamera {
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200911 double aspectRatio{0.0}; // min > 0
912 double yfov{0.0}; // required. min > 0
913 double zfar{0.0}; // min > 0
914 double znear{0.0}; // required. min > 0
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900915
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200916 PerspectiveCamera() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100917 DEFAULT_METHODS(PerspectiveCamera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900918 bool operator==(const PerspectiveCamera &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900919
Selmar09d2ff12018-03-15 17:30:42 +0100920 ExtensionMap extensions;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900921 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900922
923 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
924 std::string extras_json_string;
925 std::string extensions_json_string;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900926};
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000927
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900928struct OrthographicCamera {
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200929 double xmag{0.0}; // required. must not be zero.
930 double ymag{0.0}; // required. must not be zero.
931 double zfar{0.0}; // required. `zfar` must be greater than `znear`.
932 double znear{0.0}; // required
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000933
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200934 OrthographicCamera() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100935 DEFAULT_METHODS(OrthographicCamera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900936 bool operator==(const OrthographicCamera &) const;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900937
Selmar09d2ff12018-03-15 17:30:42 +0100938 ExtensionMap extensions;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900939 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900940
941 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
942 std::string extras_json_string;
943 std::string extensions_json_string;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900944};
945
946struct Camera {
947 std::string type; // required. "perspective" or "orthographic"
948 std::string name;
949
950 PerspectiveCamera perspective;
951 OrthographicCamera orthographic;
952
Bryn Lloyd3e98ac42023-06-21 22:15:49 +0200953 Camera() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100954 DEFAULT_METHODS(Camera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900955 bool operator==(const Camera &) const;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900956
Selmar09d2ff12018-03-15 17:30:42 +0100957 ExtensionMap extensions;
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000958 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900959
960 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
961 std::string extras_json_string;
962 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900963};
964
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000965struct Primitive {
966 std::map<std::string, int> attributes; // (required) A dictionary object of
967 // integer, where each integer
968 // is the index of the accessor
969 // containing an attribute.
Bryn Lloydcb6a7072023-06-21 22:30:14 +0200970 int material{-1}; // The index of the material to apply to this primitive
971 // when rendering.
972 int indices{-1}; // The index of the accessor that contains the indices.
973 int mode{-1}; // one of TINYGLTF_MODE_***
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900974 std::vector<std::map<std::string, int> > targets; // array of morph targets,
imallettd9ce9eb2022-10-07 10:37:09 -0700975 // where each target is a dict with attributes in ["POSITION, "NORMAL",
Syoyo Fujita5b407452017-06-04 17:42:41 +0900976 // "TANGENT"] pointing
977 // to their corresponding accessors
Alex Wood7319db72019-01-24 15:38:16 -0500978 ExtensionMap extensions;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000979 Value extras;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900980
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900981 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
982 std::string extras_json_string;
983 std::string extensions_json_string;
984
Bryn Lloydcb6a7072023-06-21 22:30:14 +0200985 Primitive() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100986 DEFAULT_METHODS(Primitive)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900987 bool operator==(const Primitive &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000988};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900989
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900990struct Mesh {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900991 std::string name;
992 std::vector<Primitive> primitives;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900993 std::vector<double> weights; // weights to be applied to the Morph Targets
Selmar09d2ff12018-03-15 17:30:42 +0100994 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900995 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200996
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900997 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
998 std::string extras_json_string;
999 std::string extensions_json_string;
1000
jrkoonced1e14722019-08-27 11:51:02 -05001001 Mesh() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001002 DEFAULT_METHODS(Mesh)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001003 bool operator==(const Mesh &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001004};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001005
1006class Node {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001007 public:
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001008 Node() = default;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001009
Selmar Kokb74fade2019-10-29 16:09:32 +01001010 DEFAULT_METHODS(Node)
Syoyo Fujitad6b0d0a2019-06-29 17:31:13 +09001011
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001012 bool operator==(const Node &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001013
Syoyo Fujita7a570c82023-06-19 21:52:13 +09001014 int camera{-1}; // the index of the camera referenced by this node
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001015
1016 std::string name;
Syoyo Fujita7a570c82023-06-19 21:52:13 +09001017 int skin{-1};
1018 int mesh{-1};
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001019 int light{-1}; // light source index (KHR_lights_punctual)
1020 int emitter{-1}; // audio emitter index (KHR_audio)
Thomas Gampera42263b2024-02-05 15:42:49 +01001021 std::vector<int> lods; // level of detail nodes (MSFT_lod)
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001022 std::vector<int> children;
Syoyo Fujita7c877972016-03-08 01:31:49 +09001023 std::vector<double> rotation; // length must be 0 or 4
1024 std::vector<double> scale; // length must be 0 or 3
1025 std::vector<double> translation; // length must be 0 or 3
1026 std::vector<double> matrix; // length must be 0 or 16
Syoyo Fujita5b407452017-06-04 17:42:41 +09001027 std::vector<double> weights; // The weights of the instantiated Morph Target
Ben Buzbee3b735bb2018-04-24 11:39:30 -07001028
Selmar09d2ff12018-03-15 17:30:42 +01001029 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001030 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001031
1032 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1033 std::string extras_json_string;
1034 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001035};
1036
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001037struct Buffer {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001038 std::string name;
1039 std::vector<unsigned char> data;
Syoyo Fujita5b407452017-06-04 17:42:41 +09001040 std::string
1041 uri; // considered as required here but not in the spec (need to clarify)
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001042 // uri is not decoded(e.g. whitespace may be represented as %20)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001043 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001044 ExtensionMap extensions;
1045
1046 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1047 std::string extras_json_string;
1048 std::string extensions_json_string;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001049
Selmar Kokb74fade2019-10-29 16:09:32 +01001050 Buffer() = default;
1051 DEFAULT_METHODS(Buffer)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001052 bool operator==(const Buffer &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001053};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001054
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001055struct Asset {
Syoyo Fujitab702de72021-03-02 19:08:29 +09001056 std::string version = "2.0"; // required
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001057 std::string generator;
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +00001058 std::string minVersion;
1059 std::string copyright;
Selmar09d2ff12018-03-15 17:30:42 +01001060 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001061 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001062
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001063 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1064 std::string extras_json_string;
1065 std::string extensions_json_string;
1066
jrkoonced1e14722019-08-27 11:51:02 -05001067 Asset() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001068 DEFAULT_METHODS(Asset)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001069 bool operator==(const Asset &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001070};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001071
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001072struct Scene {
1073 std::string name;
1074 std::vector<int> nodes;
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001075 std::vector<int> audioEmitters; // KHR_audio global emitters
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001076
Selmar09d2ff12018-03-15 17:30:42 +01001077 ExtensionMap extensions;
1078 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001079
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001080 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1081 std::string extras_json_string;
1082 std::string extensions_json_string;
1083
jrkoonced1e14722019-08-27 11:51:02 -05001084 Scene() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001085 DEFAULT_METHODS(Scene)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001086 bool operator==(const Scene &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001087};
1088
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001089struct SpotLight {
Bryn Lloyda64f4b42023-06-21 18:40:18 +02001090 double innerConeAngle{0.0};
1091 double outerConeAngle{0.7853981634};
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001092
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001093 SpotLight() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001094 DEFAULT_METHODS(SpotLight)
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001095 bool operator==(const SpotLight &) const;
1096
1097 ExtensionMap extensions;
1098 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001099
1100 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1101 std::string extras_json_string;
1102 std::string extensions_json_string;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001103};
1104
Emanuel Schrade186322b2017-11-06 11:14:41 +01001105struct Light {
1106 std::string name;
1107 std::vector<double> color;
Syoyo Fujita3dad3032020-05-13 21:22:23 +09001108 double intensity{1.0};
Emanuel Schrade186322b2017-11-06 11:14:41 +01001109 std::string type;
imallettd9ce9eb2022-10-07 10:37:09 -07001110 double range{0.0}; // 0.0 = infinite
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001111 SpotLight spot;
1112
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001113 Light() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001114 DEFAULT_METHODS(Light)
Arthur Brainville (Ybalrid)9eeaf202019-09-05 13:02:05 +02001115
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001116 bool operator==(const Light &) const;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001117
1118 ExtensionMap extensions;
1119 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001120
1121 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1122 std::string extras_json_string;
1123 std::string extensions_json_string;
Emanuel Schrade186322b2017-11-06 11:14:41 +01001124};
1125
Baranob_Ilya78864c82023-06-12 10:43:52 +04001126struct PositionalEmitter {
1127 double coneInnerAngle{6.283185307179586};
1128 double coneOuterAngle{6.283185307179586};
1129 double coneOuterGain{0.0};
1130 double maxDistance{100.0};
1131 double refDistance{1.0};
1132 double rolloffFactor{1.0};
1133
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001134 PositionalEmitter() = default;
Baranob_Ilya78864c82023-06-12 10:43:52 +04001135 DEFAULT_METHODS(PositionalEmitter)
1136 bool operator==(const PositionalEmitter &) const;
1137
1138 ExtensionMap extensions;
1139 Value extras;
1140
1141 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1142 std::string extras_json_string;
1143 std::string extensions_json_string;
1144};
1145
1146struct AudioEmitter {
Baranob_Ilya879cb472023-06-12 13:35:05 +04001147 std::string name;
1148 double gain{1.0};
1149 bool loop{false};
1150 bool playing{false};
1151 std::string
1152 type; // positional - Positional audio emitters. Using sound cones, the
1153 // orientation is +Z having the same front side for a glTF asset.
1154 // global - Global audio emitters are not affected by the position
1155 // of audio listeners. coneInnerAngle, coneOuterAngle,
1156 // coneOuterGain, distanceModel, maxDistance, refDistance, and
1157 // rolloffFactor should all be ignored when set.
1158 std::string
1159 distanceModel; // linear - A linear distance model calculating the
1160 // gain induced by the distance according to: 1.0
1161 // - rolloffFactor * (distance - refDistance) /
1162 // (maxDistance - refDistance)
1163 // inverse - (default) An inverse distance model
1164 // calculating the gain induced by the distance according
1165 // to: refDistance / (refDistance + rolloffFactor *
1166 // (Math.max(distance, refDistance) - refDistance))
1167 // exponential - An exponential distance model calculating
1168 // the gain induced by the distance according to:
1169 // pow((Math.max(distance, refDistance) / refDistance,
1170 // -rolloffFactor))
1171 PositionalEmitter positional;
1172 int source{-1};
Baranob_Ilya78864c82023-06-12 10:43:52 +04001173
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001174 AudioEmitter() : type("global"), distanceModel("inverse") {}
Baranob_Ilya879cb472023-06-12 13:35:05 +04001175 DEFAULT_METHODS(AudioEmitter)
Baranob_Ilya78864c82023-06-12 10:43:52 +04001176
Baranob_Ilya879cb472023-06-12 13:35:05 +04001177 bool operator==(const AudioEmitter &) const;
Baranob_Ilya78864c82023-06-12 10:43:52 +04001178
Baranob_Ilya879cb472023-06-12 13:35:05 +04001179 ExtensionMap extensions;
1180 Value extras;
Baranob_Ilya78864c82023-06-12 10:43:52 +04001181
Baranob_Ilya879cb472023-06-12 13:35:05 +04001182 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1183 std::string extras_json_string;
1184 std::string extensions_json_string;
Baranob_Ilya78864c82023-06-12 10:43:52 +04001185};
1186
1187struct AudioSource {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001188 std::string name;
1189 std::string uri;
1190 int bufferView{-1}; // (required if no uri)
1191 std::string
1192 mimeType; // (required if no uri) The audio's MIME type. Required if
1193 // bufferView is defined. Unless specified by another
1194 // extension, the only supported mimeType is audio/mpeg.
Baranob_Ilya78864c82023-06-12 10:43:52 +04001195
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001196 AudioSource() = default;
1197 DEFAULT_METHODS(AudioSource)
Baranob_Ilya78864c82023-06-12 10:43:52 +04001198
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001199 bool operator==(const AudioSource &) const;
Baranob_Ilya78864c82023-06-12 10:43:52 +04001200
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001201 Value extras;
1202 ExtensionMap extensions;
Baranob_Ilya78864c82023-06-12 10:43:52 +04001203
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001204 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1205 std::string extras_json_string;
1206 std::string extensions_json_string;
Baranob_Ilya78864c82023-06-12 10:43:52 +04001207};
1208
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001209class Model {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001210 public:
Selmar Kokff2b1f92019-10-21 17:58:09 +02001211 Model() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001212 DEFAULT_METHODS(Model)
Syoyo Fujitad6b0d0a2019-06-29 17:31:13 +09001213
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001214 bool operator==(const Model &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001215
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001216 std::vector<Accessor> accessors;
1217 std::vector<Animation> animations;
1218 std::vector<Buffer> buffers;
1219 std::vector<BufferView> bufferViews;
1220 std::vector<Material> materials;
1221 std::vector<Mesh> meshes;
1222 std::vector<Node> nodes;
1223 std::vector<Texture> textures;
1224 std::vector<Image> images;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00001225 std::vector<Skin> skins;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001226 std::vector<Sampler> samplers;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09001227 std::vector<Camera> cameras;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001228 std::vector<Scene> scenes;
Emanuel Schrade186322b2017-11-06 11:14:41 +01001229 std::vector<Light> lights;
Baranob_Ilya78864c82023-06-12 10:43:52 +04001230 std::vector<AudioEmitter> audioEmitters;
1231 std::vector<AudioSource> audioSources;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001232
Bryn Lloydcb6a7072023-06-21 22:30:14 +02001233 int defaultScene{-1};
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001234 std::vector<std::string> extensionsUsed;
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00001235 std::vector<std::string> extensionsRequired;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001236
1237 Asset asset;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001238
1239 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001240 ExtensionMap extensions;
1241
1242 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1243 std::string extras_json_string;
1244 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001245};
1246
Syoyo Fujita0614eb82016-10-14 18:50:14 +09001247enum SectionCheck {
Luke San Antonio9e36b612016-06-23 14:10:51 -04001248 NO_REQUIRE = 0x00,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001249 REQUIRE_VERSION = 0x01,
1250 REQUIRE_SCENE = 0x02,
1251 REQUIRE_SCENES = 0x04,
1252 REQUIRE_NODES = 0x08,
1253 REQUIRE_ACCESSORS = 0x10,
1254 REQUIRE_BUFFERS = 0x20,
1255 REQUIRE_BUFFER_VIEWS = 0x40,
1256 REQUIRE_ALL = 0x7f
Luke San Antonio9e36b612016-06-23 14:10:51 -04001257};
1258
Squareysff644d82018-03-13 22:36:18 +01001259///
Pyarelal Knowles385946d2023-01-03 18:18:04 -08001260/// URIEncodeFunction type. Signature for custom URI encoding of external
1261/// resources such as .bin and image files. Used by tinygltf to re-encode the
1262/// final location of saved files. object_type may be used to encode buffer and
1263/// image URIs differently, for example. See
1264/// https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#uris
1265///
1266typedef bool (*URIEncodeFunction)(const std::string &in_uri,
1267 const std::string &object_type,
1268 std::string *out_uri, void *user_data);
1269
1270///
1271/// URIDecodeFunction type. Signature for custom URI decoding of external
1272/// resources such as .bin and image files. Used by tinygltf when computing
1273/// filenames to write resources.
1274///
1275typedef bool (*URIDecodeFunction)(const std::string &in_uri,
1276 std::string *out_uri, void *user_data);
1277
1278// Declaration of default uri decode function
1279bool URIDecode(const std::string &in_uri, std::string *out_uri,
1280 void *user_data);
1281
1282///
1283/// A structure containing URI callbacks and a pointer to their user data.
1284///
1285struct URICallbacks {
1286 URIEncodeFunction encode; // Optional encode method
1287 URIDecodeFunction decode; // Required decode method
1288
1289 void *user_data; // An argument that is passed to all uri callbacks
1290};
1291
1292///
Squareysff644d82018-03-13 22:36:18 +01001293/// LoadImageDataFunction type. Signature for custom image loading callbacks.
1294///
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001295typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *,
1296 std::string *, int, int,
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001297 const unsigned char *, int,
1298 void *user_pointer);
Squareysff644d82018-03-13 22:36:18 +01001299
johan bowald642a3432018-04-01 12:37:18 +02001300///
1301/// WriteImageDataFunction type. Signature for custom image writing callbacks.
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08001302/// The out_uri parameter becomes the URI written to the gltf and may reference
1303/// a file or contain a data URI.
johan bowald642a3432018-04-01 12:37:18 +02001304///
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08001305typedef bool (*WriteImageDataFunction)(const std::string *basepath,
1306 const std::string *filename,
1307 const Image *image, bool embedImages,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08001308 const URICallbacks *uri_cb,
1309 std::string *out_uri,
1310 void *user_pointer);
johan bowald642a3432018-04-01 12:37:18 +02001311
Squareys2d3594d2018-03-13 22:40:53 +01001312#ifndef TINYGLTF_NO_STB_IMAGE
Squareysff644d82018-03-13 22:36:18 +01001313// Declaration of default image loader callback
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001314bool LoadImageData(Image *image, const int image_idx, std::string *err,
1315 std::string *warn, int req_width, int req_height,
1316 const unsigned char *bytes, int size, void *);
Squareys2d3594d2018-03-13 22:40:53 +01001317#endif
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001318
johan bowald642a3432018-04-01 12:37:18 +02001319#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
1320// Declaration of default image writer callback
1321bool WriteImageData(const std::string *basepath, const std::string *filename,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08001322 const Image *image, bool embedImages,
1323 const URICallbacks *uri_cb, std::string *out_uri, void *);
johan bowald642a3432018-04-01 12:37:18 +02001324#endif
1325
Paolo Jovone6601bf2018-07-07 20:43:33 +02001326///
1327/// FilExistsFunction type. Signature for custom filesystem callbacks.
1328///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001329typedef bool (*FileExistsFunction)(const std::string &abs_filename, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001330
1331///
1332/// ExpandFilePathFunction type. Signature for custom filesystem callbacks.
1333///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001334typedef std::string (*ExpandFilePathFunction)(const std::string &, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001335
1336///
1337/// ReadWholeFileFunction type. Signature for custom filesystem callbacks.
1338///
1339typedef bool (*ReadWholeFileFunction)(std::vector<unsigned char> *,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001340 std::string *, const std::string &,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001341 void *);
1342
1343///
1344/// WriteWholeFileFunction type. Signature for custom filesystem callbacks.
1345///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001346typedef bool (*WriteWholeFileFunction)(std::string *, const std::string &,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001347 const std::vector<unsigned char> &,
1348 void *);
1349
1350///
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09001351/// GetFileSizeFunction type. Signature for custom filesystem callbacks.
1352///
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001353typedef bool (*GetFileSizeFunction)(size_t *filesize_out, std::string *err,
1354 const std::string &abs_filename,
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09001355 void *userdata);
1356
1357///
Paolo Jovone6601bf2018-07-07 20:43:33 +02001358/// A structure containing all required filesystem callbacks and a pointer to
1359/// their user data.
1360///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001361struct FsCallbacks {
1362 FileExistsFunction FileExists;
1363 ExpandFilePathFunction ExpandFilePath;
1364 ReadWholeFileFunction ReadWholeFile;
1365 WriteWholeFileFunction WriteWholeFile;
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001366 GetFileSizeFunction GetFileSizeInBytes; // To avoid GetFileSize Win32 API,
1367 // add `InBytes` suffix.
Paolo Jovone6601bf2018-07-07 20:43:33 +02001368
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001369 void *user_data; // An argument that is passed to all fs callbacks
Paolo Jovone6601bf2018-07-07 20:43:33 +02001370};
1371
1372#ifndef TINYGLTF_NO_FS
1373// Declaration of default filesystem callbacks
1374
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001375bool FileExists(const std::string &abs_filename, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001376
Syoyo Fujita3d939fd2020-06-06 18:13:15 +09001377///
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04001378/// Expand file path(e.g. `~` to home directory on posix, `%APPDATA%` to
Syoyo Fujita2c521b32020-12-04 00:50:46 +09001379/// `C:\\Users\\tinygltf\\AppData`)
Syoyo Fujita3d939fd2020-06-06 18:13:15 +09001380///
1381/// @param[in] filepath File path string. Assume UTF-8
1382/// @param[in] userdata User data. Set to `nullptr` if you don't need it.
1383///
1384std::string ExpandFilePath(const std::string &filepath, void *userdata);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001385
1386bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001387 const std::string &filepath, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001388
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001389bool WriteWholeFile(std::string *err, const std::string &filepath,
1390 const std::vector<unsigned char> &contents, void *);
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09001391
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001392bool GetFileSizeInBytes(size_t *filesize_out, std::string *err,
1393 const std::string &filepath, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001394#endif
1395
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001396///
imallettd9ce9eb2022-10-07 10:37:09 -07001397/// glTF Parser/Serializer context.
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001398///
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001399class TinyGLTF {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001400 public:
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001401#ifdef __clang__
1402#pragma clang diagnostic push
1403#pragma clang diagnostic ignored "-Wc++98-compat"
1404#endif
1405
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001406 TinyGLTF() = default;
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001407
1408#ifdef __clang__
1409#pragma clang diagnostic pop
1410#endif
1411
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001412 ~TinyGLTF() = default;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001413
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001414 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001415 /// Loads glTF ASCII asset from a file.
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001416 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001417 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001418 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001419 bool LoadASCIIFromFile(Model *model, std::string *err, std::string *warn,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001420 const std::string &filename,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001421 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001422
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001423 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001424 /// Loads glTF ASCII asset from string(memory).
1425 /// `length` = strlen(str);
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001426 /// `base_dir` is a search path of glTF asset(e.g. images). Path Must be an
1427 /// expanded path (e.g. no tilde(`~`), no environment variables). Set warning
1428 /// message to `warn` for example it fails to load asserts. Returns false and
1429 /// set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001430 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001431 bool LoadASCIIFromString(Model *model, std::string *err, std::string *warn,
1432 const char *str, const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001433 const std::string &base_dir,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001434 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001435
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001436 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001437 /// Loads glTF binary asset from a file.
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001438 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001439 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001440 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001441 bool LoadBinaryFromFile(Model *model, std::string *err, std::string *warn,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001442 const std::string &filename,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001443 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001444
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001445 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001446 /// Loads glTF binary asset from memory.
1447 /// `length` = strlen(str);
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001448 /// `base_dir` is a search path of glTF asset(e.g. images). Path Must be an
1449 /// expanded path (e.g. no tilde(`~`), no environment variables).
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001450 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001451 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001452 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001453 bool LoadBinaryFromMemory(Model *model, std::string *err, std::string *warn,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001454 const unsigned char *bytes,
1455 const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001456 const std::string &base_dir = "",
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001457 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001458
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001459 ///
imallettd9ce9eb2022-10-07 10:37:09 -07001460 /// Write glTF to stream, buffers and images will be embedded
Johan Bowald1af7c1d2019-07-16 14:50:46 +02001461 ///
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08001462 bool WriteGltfSceneToStream(const Model *model, std::ostream &stream,
Johan Bowald1af7c1d2019-07-16 14:50:46 +02001463 bool prettyPrint, bool writeBinary);
1464
1465 ///
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001466 /// Write glTF to file.
1467 ///
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08001468 bool WriteGltfSceneToFile(const Model *model, const std::string &filename,
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001469 bool embedImages, bool embedBuffers,
1470 bool prettyPrint, bool writeBinary);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00001471
Squareysff644d82018-03-13 22:36:18 +01001472 ///
Nyall Dawson8c85d5e2023-08-28 12:56:09 +10001473 /// Sets the parsing strictness.
1474 ///
1475 void SetParseStrictness(ParseStrictness strictness);
1476
1477 ///
Squareysff644d82018-03-13 22:36:18 +01001478 /// Set callback to use for loading image data
1479 ///
1480 void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data);
1481
johan bowald642a3432018-04-01 12:37:18 +02001482 ///
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001483 /// Unset(remove) callback of loading image data
1484 ///
1485 void RemoveImageLoader();
1486
1487 ///
johan bowald642a3432018-04-01 12:37:18 +02001488 /// Set callback to use for writing image data
1489 ///
1490 void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data);
1491
Paolo Jovone6601bf2018-07-07 20:43:33 +02001492 ///
Pyarelal Knowles385946d2023-01-03 18:18:04 -08001493 /// Set callbacks to use for URI encoding and decoding and their user data
1494 ///
1495 void SetURICallbacks(URICallbacks callbacks);
1496
1497 ///
Paolo Jovone6601bf2018-07-07 20:43:33 +02001498 /// Set callbacks to use for filesystem (fs) access and their user data
1499 ///
1500 void SetFsCallbacks(FsCallbacks callbacks);
1501
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001502 ///
1503 /// Set serializing default values(default = false).
1504 /// When true, default values are force serialized to .glTF.
imallettd9ce9eb2022-10-07 10:37:09 -07001505 /// This may be helpful if you want to serialize a full description of glTF
Syoyo Fujitaff515702019-08-24 16:29:14 +09001506 /// data.
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001507 ///
Syoyo Fujitaff515702019-08-24 16:29:14 +09001508 /// TODO(LTE): Supply parsing option as function arguments to
1509 /// `LoadASCIIFromFile()` and others, not by a class method
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001510 ///
1511 void SetSerializeDefaultValues(const bool enabled) {
1512 serialize_default_values_ = enabled;
1513 }
1514
Syoyo Fujitaff515702019-08-24 16:29:14 +09001515 bool GetSerializeDefaultValues() const { return serialize_default_values_; }
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001516
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001517 ///
1518 /// Store original JSON string for `extras` and `extensions`.
1519 /// This feature will be useful when the user want to reconstruct custom data
1520 /// structure from JSON string.
1521 ///
1522 void SetStoreOriginalJSONForExtrasAndExtensions(const bool enabled) {
1523 store_original_json_for_extras_and_extensions_ = enabled;
1524 }
1525
1526 bool GetStoreOriginalJSONForExtrasAndExtensions() const {
1527 return store_original_json_for_extras_and_extensions_;
1528 }
1529
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001530 ///
imallettd9ce9eb2022-10-07 10:37:09 -07001531 /// Specify whether preserve image channels when loading images or not.
1532 /// (Not effective when the user supplies their own LoadImageData callbacks)
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001533 ///
1534 void SetPreserveImageChannels(bool onoff) {
1535 preserve_image_channels_ = onoff;
1536 }
1537
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09001538 ///
1539 /// Set maximum allowed external file size in bytes.
1540 /// Default: 2GB
1541 /// Only effective for built-in ReadWholeFileFunction FS function.
1542 ///
1543 void SetMaxExternalFileSize(size_t max_bytes) {
1544 max_external_file_size_ = max_bytes;
1545 }
1546
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001547 size_t GetMaxExternalFileSize() const { return max_external_file_size_; }
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09001548
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001549 bool GetPreserveImageChannels() const { return preserve_image_channels_; }
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001550
Syoyo Fujitabeded612016-05-01 20:03:43 +09001551 private:
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001552 ///
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001553 /// Loads glTF asset from string(memory).
1554 /// `length` = strlen(str);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001555 /// Set warning message to `warn` for example it fails to load asserts
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001556 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001557 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001558 bool LoadFromString(Model *model, std::string *err, std::string *warn,
1559 const char *str, const unsigned int length,
1560 const std::string &base_dir, unsigned int check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001561
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001562 const unsigned char *bin_data_ = nullptr;
1563 size_t bin_size_ = 0;
1564 bool is_binary_ = false;
1565
Nyall Dawsonbbc1eae2023-09-03 09:05:32 +10001566 ParseStrictness strictness_ = ParseStrictness::Strict;
Nyall Dawson8c85d5e2023-08-28 12:56:09 +10001567
Syoyo Fujitaff515702019-08-24 16:29:14 +09001568 bool serialize_default_values_ = false; ///< Serialize default values?
Squareysff644d82018-03-13 22:36:18 +01001569
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001570 bool store_original_json_for_extras_and_extensions_ = false;
1571
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001572 bool preserve_image_channels_ = false; /// Default false(expand channels to
1573 /// RGBA) for backward compatibility.
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001574
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001575 size_t max_external_file_size_{
1576 size_t((std::numeric_limits<int32_t>::max)())}; // Default 2GB
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09001577
Syoyo Fujita9117abb2022-08-16 20:10:26 +09001578 // Warning & error messages
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09001579 std::string warn_;
1580 std::string err_;
1581
Paolo Jovone6601bf2018-07-07 20:43:33 +02001582 FsCallbacks fs = {
1583#ifndef TINYGLTF_NO_FS
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001584 &tinygltf::FileExists,
1585 &tinygltf::ExpandFilePath,
1586 &tinygltf::ReadWholeFile,
1587 &tinygltf::WriteWholeFile,
1588 &tinygltf::GetFileSizeInBytes,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001589
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001590 nullptr // Fs callback user data
Paolo Jovone6601bf2018-07-07 20:43:33 +02001591#else
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09001592 nullptr, nullptr, nullptr, nullptr, nullptr,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001593
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001594 nullptr // Fs callback user data
Paolo Jovone6601bf2018-07-07 20:43:33 +02001595#endif
1596 };
1597
Pyarelal Knowles385946d2023-01-03 18:18:04 -08001598 URICallbacks uri_cb = {
1599 // Use paths as-is by default. This will use JSON string escaping.
1600 nullptr,
1601 // Decode all URIs before using them as paths as the application may have
1602 // percent encoded them.
1603 &tinygltf::URIDecode,
1604 // URI callback user data
1605 nullptr};
1606
Squareysff644d82018-03-13 22:36:18 +01001607 LoadImageDataFunction LoadImageData =
1608#ifndef TINYGLTF_NO_STB_IMAGE
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001609 &tinygltf::LoadImageData;
Squareysff644d82018-03-13 22:36:18 +01001610#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001611 nullptr;
Squareysff644d82018-03-13 22:36:18 +01001612#endif
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001613 void *load_image_user_data_{nullptr};
1614 bool user_image_loader_{false};
johan bowald642a3432018-04-01 12:37:18 +02001615
1616 WriteImageDataFunction WriteImageData =
1617#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001618 &tinygltf::WriteImageData;
johan bowald642a3432018-04-01 12:37:18 +02001619#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001620 nullptr;
johan bowald642a3432018-04-01 12:37:18 +02001621#endif
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001622 void *write_image_user_data_{nullptr};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001623};
1624
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001625#ifdef __clang__
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001626#pragma clang diagnostic pop // -Wpadded
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001627#endif
1628
Syoyo Fujita7c877972016-03-08 01:31:49 +09001629} // namespace tinygltf
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001630
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001631#endif // TINY_GLTF_H_
Syoyo Fujitac8d3c762017-05-25 01:42:38 +09001632
Selmar Kok31cb7f92018-10-03 15:39:05 +02001633#if defined(TINYGLTF_IMPLEMENTATION) || defined(__INTELLISENSE__)
Syoyo Fujita7c877972016-03-08 01:31:49 +09001634#include <algorithm>
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001635// #include <cassert>
Paolo Jovone6601bf2018-07-07 20:43:33 +02001636#ifndef TINYGLTF_NO_FS
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001637#include <sys/stat.h> // for is_directory check
1638
Syoyo Fujita125d8e52019-11-09 20:52:56 +09001639#include <cstdio>
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001640#include <fstream>
Paolo Jovone6601bf2018-07-07 20:43:33 +02001641#endif
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001642#include <sstream>
Syoyo Fujitabeded612016-05-01 20:03:43 +09001643
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001644#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001645// Disable some warnings for external files.
1646#pragma clang diagnostic push
1647#pragma clang diagnostic ignored "-Wfloat-equal"
1648#pragma clang diagnostic ignored "-Wexit-time-destructors"
1649#pragma clang diagnostic ignored "-Wconversion"
1650#pragma clang diagnostic ignored "-Wold-style-cast"
Syoyo Fujita643ce102016-05-01 17:19:37 +09001651#pragma clang diagnostic ignored "-Wglobal-constructors"
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001652#if __has_warning("-Wreserved-id-macro")
Syoyo Fujita643ce102016-05-01 17:19:37 +09001653#pragma clang diagnostic ignored "-Wreserved-id-macro"
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001654#endif
Syoyo Fujita643ce102016-05-01 17:19:37 +09001655#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
1656#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001657#pragma clang diagnostic ignored "-Wc++98-compat"
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001658#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001659#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
1660#pragma clang diagnostic ignored "-Wswitch-enum"
1661#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001662#pragma clang diagnostic ignored "-Wweak-vtables"
1663#pragma clang diagnostic ignored "-Wcovered-switch-default"
Syoyo Fujitaa8f0b1c2018-08-28 21:33:40 +09001664#if __has_warning("-Wdouble-promotion")
1665#pragma clang diagnostic ignored "-Wdouble-promotion"
1666#endif
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001667#if __has_warning("-Wcomma")
Syoyo Fujitac8d3c762017-05-25 01:42:38 +09001668#pragma clang diagnostic ignored "-Wcomma"
1669#endif
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001670#if __has_warning("-Wzero-as-null-pointer-constant")
1671#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
1672#endif
1673#if __has_warning("-Wcast-qual")
1674#pragma clang diagnostic ignored "-Wcast-qual"
1675#endif
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001676#if __has_warning("-Wmissing-variable-declarations")
1677#pragma clang diagnostic ignored "-Wmissing-variable-declarations"
1678#endif
1679#if __has_warning("-Wmissing-prototypes")
1680#pragma clang diagnostic ignored "-Wmissing-prototypes"
1681#endif
1682#if __has_warning("-Wcast-align")
1683#pragma clang diagnostic ignored "-Wcast-align"
1684#endif
1685#if __has_warning("-Wnewline-eof")
1686#pragma clang diagnostic ignored "-Wnewline-eof"
1687#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001688#if __has_warning("-Wunused-parameter")
1689#pragma clang diagnostic ignored "-Wunused-parameter"
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001690#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001691#if __has_warning("-Wmismatched-tags")
1692#pragma clang diagnostic ignored "-Wmismatched-tags"
1693#endif
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001694#if __has_warning("-Wextra-semi-stmt")
1695#pragma clang diagnostic ignored "-Wextra-semi-stmt"
1696#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001697#endif
1698
imallettd9ce9eb2022-10-07 10:37:09 -07001699// Disable GCC warnings
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001700#ifdef __GNUC__
1701#pragma GCC diagnostic push
1702#pragma GCC diagnostic ignored "-Wtype-limits"
Syoyo Fujitaca56f722019-03-07 21:04:25 +09001703#endif // __GNUC__
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001704
krokofc0116b2019-03-03 08:28:49 +02001705#ifndef TINYGLTF_NO_INCLUDE_JSON
jrkooncecba5d6c2019-08-29 11:26:22 -05001706#ifndef TINYGLTF_USE_RAPIDJSON
Alex Wood7319db72019-01-24 15:38:16 -05001707#include "json.hpp"
jrkooncecba5d6c2019-08-29 11:26:22 -05001708#else
Alvaro Barua3ddf8302021-03-18 00:21:23 +00001709#ifndef TINYGLTF_NO_INCLUDE_RAPIDJSON
jrkooncecba5d6c2019-08-29 11:26:22 -05001710#include "document.h"
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001711#include "prettywriter.h"
1712#include "rapidjson.h"
jrkooncecba5d6c2019-08-29 11:26:22 -05001713#include "stringbuffer.h"
1714#include "writer.h"
jrkooncecba5d6c2019-08-29 11:26:22 -05001715#endif
krokof4b6d112019-03-03 01:11:31 +02001716#endif
Alvaro Barua3ddf8302021-03-18 00:21:23 +00001717#endif
Alex Wood7319db72019-01-24 15:38:16 -05001718
1719#ifdef TINYGLTF_ENABLE_DRACO
Alex Wood7319db72019-01-24 15:38:16 -05001720#include "draco/compression/decode.h"
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001721#include "draco/core/decoder_buffer.h"
Alex Wood7319db72019-01-24 15:38:16 -05001722#endif
Squareys2d3594d2018-03-13 22:40:53 +01001723
1724#ifndef TINYGLTF_NO_STB_IMAGE
krokofc0116b2019-03-03 08:28:49 +02001725#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE
Alex Wood7319db72019-01-24 15:38:16 -05001726#include "stb_image.h"
Squareys2d3594d2018-03-13 22:40:53 +01001727#endif
krokof4b6d112019-03-03 01:11:31 +02001728#endif
Squareys2d3594d2018-03-13 22:40:53 +01001729
johan bowald642a3432018-04-01 12:37:18 +02001730#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
krokofc0116b2019-03-03 08:28:49 +02001731#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE
Alex Wood7319db72019-01-24 15:38:16 -05001732#include "stb_image_write.h"
johan bowald642a3432018-04-01 12:37:18 +02001733#endif
krokof4b6d112019-03-03 01:11:31 +02001734#endif
johan bowald642a3432018-04-01 12:37:18 +02001735
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001736#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001737#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001738#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001739
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001740#ifdef __GNUC__
1741#pragma GCC diagnostic pop
1742#endif
1743
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001744#ifdef _WIN32
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001745
1746// issue 143.
1747// Define NOMINMAX to avoid min/max defines,
imallett3a295882022-10-07 11:20:39 -07001748// but undef it after included Windows.h
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001749#ifndef NOMINMAX
1750#define TINYGLTF_INTERNAL_NOMINMAX
1751#define NOMINMAX
1752#endif
1753
Syoyo Fujita87be0ce2019-02-19 21:36:32 +09001754#ifndef WIN32_LEAN_AND_MEAN
1755#define WIN32_LEAN_AND_MEAN
1756#define TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1757#endif
imallett3a295882022-10-07 11:20:39 -07001758#ifndef __MINGW32__
imallett56e10982022-10-07 10:35:16 -07001759#include <Windows.h> // include API for expanding a file path
imallett3a295882022-10-07 11:20:39 -07001760#else
1761#include <windows.h>
1762#endif
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001763
Syoyo Fujita87be0ce2019-02-19 21:36:32 +09001764#ifdef TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1765#undef WIN32_LEAN_AND_MEAN
1766#endif
1767
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001768#if defined(TINYGLTF_INTERNAL_NOMINMAX)
1769#undef NOMINMAX
1770#endif
1771
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001772#if defined(__GLIBCXX__) // mingw
Syoyo Fujita45cac782019-11-09 20:42:55 +09001773
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001774#include <fcntl.h> // _O_RDONLY
1775
1776#include <ext/stdio_filebuf.h> // fstream (all sorts of IO stuff) + stdio_filebuf (=streambuf)
Syoyo Fujita45cac782019-11-09 20:42:55 +09001777
1778#endif
1779
Julian Smith0598a202021-08-25 12:06:08 +01001780#elif !defined(__ANDROID__) && !defined(__OpenBSD__)
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001781// #include <wordexp.h>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001782#endif
1783
Christopher Sean Morrison2c9b2562022-05-14 19:00:19 -04001784#if defined(__sparcv9) || defined(__powerpc__)
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001785// Big endian
1786#else
1787#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
1788#define TINYGLTF_LITTLE_ENDIAN 1
1789#endif
1790#endif
1791
David03ad33c2023-02-15 23:35:51 -06001792namespace tinygltf {
David1f9a4b92023-02-15 22:56:18 -06001793namespace detail {
jrkooncecba5d6c2019-08-29 11:26:22 -05001794#ifdef TINYGLTF_USE_RAPIDJSON
jrkooncece7fa742019-09-04 13:31:44 -05001795
1796#ifdef TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001797// This uses the RapidJSON CRTAllocator. It is thread safe and multiple
1798// documents may be active at once.
1799using json =
1800 rapidjson::GenericValue<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
David Siegelcfe64fb2023-06-07 15:18:38 +02001801using json_iterator = json::MemberIterator;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001802using json_const_iterator = json::ConstMemberIterator;
1803using json_const_array_iterator = json const *;
1804using JsonDocument =
jrkooncece7fa742019-09-04 13:31:44 -05001805 rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001806rapidjson::CrtAllocator s_CrtAllocator; // stateless and thread safe
1807rapidjson::CrtAllocator &GetAllocator() { return s_CrtAllocator; }
jrkooncece7fa742019-09-04 13:31:44 -05001808#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001809// This uses the default RapidJSON MemoryPoolAllocator. It is very fast, but
1810// not thread safe. Only a single JsonDocument may be active at any one time,
1811// meaning only a single gltf load/save can be active any one time.
1812using json = rapidjson::Value;
David Siegelcfe64fb2023-06-07 15:18:38 +02001813using json_iterator = json::MemberIterator;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001814using json_const_iterator = json::ConstMemberIterator;
1815using json_const_array_iterator = json const *;
1816rapidjson::Document *s_pActiveDocument = nullptr;
1817rapidjson::Document::AllocatorType &GetAllocator() {
1818 assert(s_pActiveDocument); // Root json node must be JsonDocument type
1819 return s_pActiveDocument->GetAllocator();
1820}
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001821
1822#ifdef __clang__
1823#pragma clang diagnostic push
1824// Suppress JsonDocument(JsonDocument &&rhs) noexcept
1825#pragma clang diagnostic ignored "-Wunused-member-function"
1826#endif
1827
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001828struct JsonDocument : public rapidjson::Document {
1829 JsonDocument() {
1830 assert(s_pActiveDocument ==
1831 nullptr); // When using default allocator, only one document can be
1832 // active at a time, if you need multiple active at once,
1833 // define TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
1834 s_pActiveDocument = this;
jrkooncece7fa742019-09-04 13:31:44 -05001835 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001836 JsonDocument(const JsonDocument &) = delete;
1837 JsonDocument(JsonDocument &&rhs) noexcept
jrkooncece7fa742019-09-04 13:31:44 -05001838 : rapidjson::Document(std::move(rhs)) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001839 s_pActiveDocument = this;
1840 rhs.isNil = true;
1841 }
1842 ~JsonDocument() {
1843 if (!isNil) {
1844 s_pActiveDocument = nullptr;
jrkooncecba5d6c2019-08-29 11:26:22 -05001845 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001846 }
jrkooncece7fa742019-09-04 13:31:44 -05001847
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001848 private:
1849 bool isNil = false;
1850};
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001851
1852#ifdef __clang__
1853#pragma clang diagnostic pop
1854#endif
1855
jrkooncece7fa742019-09-04 13:31:44 -05001856#endif // TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001857
jrkooncecba5d6c2019-08-29 11:26:22 -05001858#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001859using nlohmann::json;
David Siegelcfe64fb2023-06-07 15:18:38 +02001860using json_iterator = json::iterator;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001861using json_const_iterator = json::const_iterator;
1862using json_const_array_iterator = json_const_iterator;
1863using JsonDocument = json;
jrkooncecba5d6c2019-08-29 11:26:22 -05001864#endif
1865
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001866void JsonParse(JsonDocument &doc, const char *str, size_t length,
1867 bool throwExc = false) {
jrkooncecba5d6c2019-08-29 11:26:22 -05001868#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001869 (void)throwExc;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001870 doc.Parse(str, length);
jrkooncecba5d6c2019-08-29 11:26:22 -05001871#else
David03ad33c2023-02-15 23:35:51 -06001872 doc = detail::json::parse(str, str + length, nullptr, throwExc);
jrkooncecba5d6c2019-08-29 11:26:22 -05001873#endif
jrkooncecba5d6c2019-08-29 11:26:22 -05001874}
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02001875} // namespace detail
1876} // namespace tinygltf
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001877
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001878#ifdef __APPLE__
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001879#include "TargetConditionals.h"
Syoyo Fujitab23f6fe2017-11-15 01:03:09 +09001880#endif
1881
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001882#ifdef __clang__
1883#pragma clang diagnostic push
1884#pragma clang diagnostic ignored "-Wc++98-compat"
1885#endif
1886
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09001887namespace tinygltf {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001888
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001889///
1890/// Internal LoadImageDataOption struct.
1891/// This struct is passed through `user_pointer` in LoadImageData.
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001892/// The struct is not passed when the user supply their own LoadImageData
1893/// callbacks.
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001894///
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001895struct LoadImageDataOption {
1896 // true: preserve image channels(e.g. load as RGB image if the image has RGB
1897 // channels) default `false`(channels are expanded to RGBA for backward
imallettd9ce9eb2022-10-07 10:37:09 -07001898 // compatibility).
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001899 bool preserve_channels{false};
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001900};
1901
Selmar Kok31cb7f92018-10-03 15:39:05 +02001902// Equals function for Value, for recursivity
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001903static bool Equals(const tinygltf::Value &one, const tinygltf::Value &other) {
1904 if (one.Type() != other.Type()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001905
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001906 switch (one.Type()) {
1907 case NULL_TYPE:
1908 return true;
1909 case BOOL_TYPE:
1910 return one.Get<bool>() == other.Get<bool>();
Syoyo Fujita150f2432019-07-25 19:22:44 +09001911 case REAL_TYPE:
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001912 return TINYGLTF_DOUBLE_EQUAL(one.Get<double>(), other.Get<double>());
1913 case INT_TYPE:
1914 return one.Get<int>() == other.Get<int>();
1915 case OBJECT_TYPE: {
1916 auto oneObj = one.Get<tinygltf::Value::Object>();
1917 auto otherObj = other.Get<tinygltf::Value::Object>();
1918 if (oneObj.size() != otherObj.size()) return false;
1919 for (auto &it : oneObj) {
1920 auto otherIt = otherObj.find(it.first);
1921 if (otherIt == otherObj.end()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001922
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001923 if (!Equals(it.second, otherIt->second)) return false;
1924 }
1925 return true;
1926 }
1927 case ARRAY_TYPE: {
1928 if (one.Size() != other.Size()) return false;
James Ward9b4e1ea2024-03-25 17:50:33 +00001929 for (size_t i = 0; i < one.Size(); ++i)
Selmara63cc632019-04-16 16:57:43 +02001930 if (!Equals(one.Get(i), other.Get(i))) return false;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001931 return true;
1932 }
1933 case STRING_TYPE:
1934 return one.Get<std::string>() == other.Get<std::string>();
1935 case BINARY_TYPE:
1936 return one.Get<std::vector<unsigned char> >() ==
1937 other.Get<std::vector<unsigned char> >();
1938 default: {
1939 // unhandled type
1940 return false;
1941 }
1942 }
Selmar Kok31cb7f92018-10-03 15:39:05 +02001943}
1944
1945// Equals function for std::vector<double> using TINYGLTF_DOUBLE_EPSILON
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001946static bool Equals(const std::vector<double> &one,
1947 const std::vector<double> &other) {
1948 if (one.size() != other.size()) return false;
1949 for (int i = 0; i < int(one.size()); ++i) {
1950 if (!TINYGLTF_DOUBLE_EQUAL(one[size_t(i)], other[size_t(i)])) return false;
1951 }
1952 return true;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001953}
1954
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001955bool Accessor::operator==(const Accessor &other) const {
1956 return this->bufferView == other.bufferView &&
1957 this->byteOffset == other.byteOffset &&
1958 this->componentType == other.componentType &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001959 this->count == other.count && this->extensions == other.extensions &&
1960 this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001961 Equals(this->maxValues, other.maxValues) &&
1962 Equals(this->minValues, other.minValues) && this->name == other.name &&
1963 this->normalized == other.normalized && this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001964}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001965bool Animation::operator==(const Animation &other) const {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001966 return this->channels == other.channels &&
1967 this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001968 this->name == other.name && this->samplers == other.samplers;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001969}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001970bool AnimationChannel::operator==(const AnimationChannel &other) const {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001971 return this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001972 this->target_node == other.target_node &&
1973 this->target_path == other.target_path &&
1974 this->sampler == other.sampler;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001975}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001976bool AnimationSampler::operator==(const AnimationSampler &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001977 return this->extras == other.extras && this->extensions == other.extensions &&
1978 this->input == other.input &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001979 this->interpolation == other.interpolation &&
1980 this->output == other.output;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001981}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001982bool Asset::operator==(const Asset &other) const {
1983 return this->copyright == other.copyright &&
1984 this->extensions == other.extensions && this->extras == other.extras &&
1985 this->generator == other.generator &&
1986 this->minVersion == other.minVersion && this->version == other.version;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001987}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001988bool Buffer::operator==(const Buffer &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001989 return this->data == other.data && this->extensions == other.extensions &&
1990 this->extras == other.extras && this->name == other.name &&
1991 this->uri == other.uri;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001992}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001993bool BufferView::operator==(const BufferView &other) const {
1994 return this->buffer == other.buffer && this->byteLength == other.byteLength &&
1995 this->byteOffset == other.byteOffset &&
1996 this->byteStride == other.byteStride && this->name == other.name &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001997 this->target == other.target && this->extensions == other.extensions &&
1998 this->extras == other.extras &&
Alexander Wood0d77a292019-01-26 08:58:45 -05001999 this->dracoDecoded == other.dracoDecoded;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002000}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002001bool Camera::operator==(const Camera &other) const {
2002 return this->name == other.name && this->extensions == other.extensions &&
2003 this->extras == other.extras &&
2004 this->orthographic == other.orthographic &&
2005 this->perspective == other.perspective && this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002006}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002007bool Image::operator==(const Image &other) const {
2008 return this->bufferView == other.bufferView &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09002009 this->component == other.component &&
2010 this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002011 this->height == other.height && this->image == other.image &&
2012 this->mimeType == other.mimeType && this->name == other.name &&
2013 this->uri == other.uri && this->width == other.width;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002014}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002015bool Light::operator==(const Light &other) const {
2016 return Equals(this->color, other.color) && this->name == other.name &&
2017 this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002018}
Baranob_Ilya78864c82023-06-12 10:43:52 +04002019bool AudioEmitter::operator==(const AudioEmitter &other) const {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02002020 return this->name == other.name &&
2021 TINYGLTF_DOUBLE_EQUAL(this->gain, other.gain) &&
Baranob_Ilya78864c82023-06-12 10:43:52 +04002022 this->loop == other.loop && this->playing == other.playing &&
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02002023 this->type == other.type &&
2024 this->distanceModel == other.distanceModel &&
Baranob_Ilya78864c82023-06-12 10:43:52 +04002025 this->source == other.source;
2026}
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02002027bool AudioSource::operator==(const AudioSource &other) const {
Baranob_Ilya78864c82023-06-12 10:43:52 +04002028 return this->name == other.name && this->uri == other.uri;
2029}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002030bool Material::operator==(const Material &other) const {
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09002031 return (this->pbrMetallicRoughness == other.pbrMetallicRoughness) &&
2032 (this->normalTexture == other.normalTexture) &&
2033 (this->occlusionTexture == other.occlusionTexture) &&
2034 (this->emissiveTexture == other.emissiveTexture) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09002035 Equals(this->emissiveFactor, other.emissiveFactor) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09002036 (this->alphaMode == other.alphaMode) &&
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002037 TINYGLTF_DOUBLE_EQUAL(this->alphaCutoff, other.alphaCutoff) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09002038 (this->doubleSided == other.doubleSided) &&
2039 (this->extensions == other.extensions) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09002040 (this->extras == other.extras) && (this->values == other.values) &&
2041 (this->additionalValues == other.additionalValues) &&
2042 (this->name == other.name);
Selmar Kok31cb7f92018-10-03 15:39:05 +02002043}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002044bool Mesh::operator==(const Mesh &other) const {
2045 return this->extensions == other.extensions && this->extras == other.extras &&
Selmar Kok81b672b2019-10-18 16:08:44 +02002046 this->name == other.name && Equals(this->weights, other.weights) &&
2047 this->primitives == other.primitives;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002048}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002049bool Model::operator==(const Model &other) const {
2050 return this->accessors == other.accessors &&
2051 this->animations == other.animations && this->asset == other.asset &&
2052 this->buffers == other.buffers &&
2053 this->bufferViews == other.bufferViews &&
2054 this->cameras == other.cameras &&
2055 this->defaultScene == other.defaultScene &&
2056 this->extensions == other.extensions &&
2057 this->extensionsRequired == other.extensionsRequired &&
2058 this->extensionsUsed == other.extensionsUsed &&
2059 this->extras == other.extras && this->images == other.images &&
2060 this->lights == other.lights && this->materials == other.materials &&
2061 this->meshes == other.meshes && this->nodes == other.nodes &&
2062 this->samplers == other.samplers && this->scenes == other.scenes &&
2063 this->skins == other.skins && this->textures == other.textures;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002064}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002065bool Node::operator==(const Node &other) const {
2066 return this->camera == other.camera && this->children == other.children &&
2067 this->extensions == other.extensions && this->extras == other.extras &&
2068 Equals(this->matrix, other.matrix) && this->mesh == other.mesh &&
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02002069 (this->light == other.light) && (this->emitter == other.emitter) &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002070 this->name == other.name && Equals(this->rotation, other.rotation) &&
2071 Equals(this->scale, other.scale) && this->skin == other.skin &&
2072 Equals(this->translation, other.translation) &&
2073 Equals(this->weights, other.weights);
Selmar Kok31cb7f92018-10-03 15:39:05 +02002074}
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02002075bool SpotLight::operator==(const SpotLight &other) const {
2076 return this->extensions == other.extensions && this->extras == other.extras &&
2077 TINYGLTF_DOUBLE_EQUAL(this->innerConeAngle, other.innerConeAngle) &&
2078 TINYGLTF_DOUBLE_EQUAL(this->outerConeAngle, other.outerConeAngle);
2079}
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02002080bool PositionalEmitter::operator==(const PositionalEmitter &other) const {
2081 return this->extensions == other.extensions && this->extras == other.extras &&
2082 TINYGLTF_DOUBLE_EQUAL(this->coneInnerAngle, other.coneInnerAngle) &&
2083 TINYGLTF_DOUBLE_EQUAL(this->coneOuterAngle, other.coneOuterAngle) &&
2084 TINYGLTF_DOUBLE_EQUAL(this->coneOuterGain, other.coneOuterGain) &&
2085 TINYGLTF_DOUBLE_EQUAL(this->maxDistance, other.maxDistance) &&
2086 TINYGLTF_DOUBLE_EQUAL(this->refDistance, other.refDistance) &&
2087 TINYGLTF_DOUBLE_EQUAL(this->rolloffFactor, other.rolloffFactor);
Baranob_Ilya78864c82023-06-12 10:43:52 +04002088}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002089bool OrthographicCamera::operator==(const OrthographicCamera &other) const {
2090 return this->extensions == other.extensions && this->extras == other.extras &&
2091 TINYGLTF_DOUBLE_EQUAL(this->xmag, other.xmag) &&
2092 TINYGLTF_DOUBLE_EQUAL(this->ymag, other.ymag) &&
2093 TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
2094 TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
Selmar Kok31cb7f92018-10-03 15:39:05 +02002095}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002096bool Parameter::operator==(const Parameter &other) const {
2097 if (this->bool_value != other.bool_value ||
2098 this->has_number_value != other.has_number_value)
2099 return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002100
Selmar Kok2bda71c2018-10-05 14:36:05 +02002101 if (!TINYGLTF_DOUBLE_EQUAL(this->number_value, other.number_value))
2102 return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002103
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002104 if (this->json_double_value.size() != other.json_double_value.size())
2105 return false;
2106 for (auto &it : this->json_double_value) {
2107 auto otherIt = other.json_double_value.find(it.first);
2108 if (otherIt == other.json_double_value.end()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002109
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002110 if (!TINYGLTF_DOUBLE_EQUAL(it.second, otherIt->second)) return false;
2111 }
2112
2113 if (!Equals(this->number_array, other.number_array)) return false;
2114
2115 if (this->string_value != other.string_value) return false;
2116
2117 return true;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002118}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002119bool PerspectiveCamera::operator==(const PerspectiveCamera &other) const {
2120 return TINYGLTF_DOUBLE_EQUAL(this->aspectRatio, other.aspectRatio) &&
2121 this->extensions == other.extensions && this->extras == other.extras &&
2122 TINYGLTF_DOUBLE_EQUAL(this->yfov, other.yfov) &&
2123 TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
2124 TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
Selmar Kok31cb7f92018-10-03 15:39:05 +02002125}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002126bool Primitive::operator==(const Primitive &other) const {
2127 return this->attributes == other.attributes && this->extras == other.extras &&
2128 this->indices == other.indices && this->material == other.material &&
2129 this->mode == other.mode && this->targets == other.targets;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002130}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002131bool Sampler::operator==(const Sampler &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09002132 return this->extensions == other.extensions && this->extras == other.extras &&
2133 this->magFilter == other.magFilter &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002134 this->minFilter == other.minFilter && this->name == other.name &&
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002135 this->wrapS == other.wrapS && this->wrapT == other.wrapT;
Syoyo Fujita2c521b32020-12-04 00:50:46 +09002136
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002137 // this->wrapR == other.wrapR
Selmar Kok31cb7f92018-10-03 15:39:05 +02002138}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002139bool Scene::operator==(const Scene &other) const {
2140 return this->extensions == other.extensions && this->extras == other.extras &&
2141 this->name == other.name && this->nodes == other.nodes;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002142}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002143bool Skin::operator==(const Skin &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09002144 return this->extensions == other.extensions && this->extras == other.extras &&
2145 this->inverseBindMatrices == other.inverseBindMatrices &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002146 this->joints == other.joints && this->name == other.name &&
2147 this->skeleton == other.skeleton;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002148}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002149bool Texture::operator==(const Texture &other) const {
2150 return this->extensions == other.extensions && this->extras == other.extras &&
2151 this->name == other.name && this->sampler == other.sampler &&
2152 this->source == other.source;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002153}
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09002154bool TextureInfo::operator==(const TextureInfo &other) const {
2155 return this->extensions == other.extensions && this->extras == other.extras &&
2156 this->index == other.index && this->texCoord == other.texCoord;
2157}
2158bool NormalTextureInfo::operator==(const NormalTextureInfo &other) const {
2159 return this->extensions == other.extensions && this->extras == other.extras &&
2160 this->index == other.index && this->texCoord == other.texCoord &&
2161 TINYGLTF_DOUBLE_EQUAL(this->scale, other.scale);
2162}
2163bool OcclusionTextureInfo::operator==(const OcclusionTextureInfo &other) const {
2164 return this->extensions == other.extensions && this->extras == other.extras &&
2165 this->index == other.index && this->texCoord == other.texCoord &&
2166 TINYGLTF_DOUBLE_EQUAL(this->strength, other.strength);
2167}
2168bool PbrMetallicRoughness::operator==(const PbrMetallicRoughness &other) const {
2169 return this->extensions == other.extensions && this->extras == other.extras &&
2170 (this->baseColorTexture == other.baseColorTexture) &&
2171 (this->metallicRoughnessTexture == other.metallicRoughnessTexture) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09002172 Equals(this->baseColorFactor, other.baseColorFactor) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09002173 TINYGLTF_DOUBLE_EQUAL(this->metallicFactor, other.metallicFactor) &&
2174 TINYGLTF_DOUBLE_EQUAL(this->roughnessFactor, other.roughnessFactor);
2175}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002176bool Value::operator==(const Value &other) const {
2177 return Equals(*this, other);
Selmar Kok31cb7f92018-10-03 15:39:05 +02002178}
2179
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002180static void swap4(unsigned int *val) {
2181#ifdef TINYGLTF_LITTLE_ENDIAN
2182 (void)val;
2183#else
2184 unsigned int tmp = *val;
2185 unsigned char *dst = reinterpret_cast<unsigned char *>(val);
2186 unsigned char *src = reinterpret_cast<unsigned char *>(&tmp);
2187
2188 dst[0] = src[3];
2189 dst[1] = src[2];
2190 dst[2] = src[1];
2191 dst[3] = src[0];
2192#endif
2193}
2194
Syoyo Fujitabeded612016-05-01 20:03:43 +09002195static std::string JoinPath(const std::string &path0,
2196 const std::string &path1) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002197 if (path0.empty()) {
2198 return path1;
2199 } else {
2200 // check '/'
2201 char lastChar = *path0.rbegin();
2202 if (lastChar != '/') {
2203 return path0 + std::string("/") + path1;
2204 } else {
2205 return path0 + path1;
2206 }
2207 }
2208}
2209
Syoyo Fujita643ce102016-05-01 17:19:37 +09002210static std::string FindFile(const std::vector<std::string> &paths,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002211 const std::string &filepath, FsCallbacks *fs) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002212 if (fs == nullptr || fs->ExpandFilePath == nullptr ||
2213 fs->FileExists == nullptr) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002214 // Error, fs callback[s] missing
2215 return std::string();
2216 }
2217
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002218 // https://github.com/syoyo/tinygltf/issues/416
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02002219 // Use strlen() since std::string's size/length reports the number of elements
2220 // in the buffer, not the length of string(null-terminated) strip
2221 // null-character in the middle of string.
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002222 size_t slength = strlen(filepath.c_str());
2223 if (slength == 0) {
2224 return std::string();
2225 }
2226
2227 std::string cleaned_filepath = std::string(filepath.c_str());
2228
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002229 for (size_t i = 0; i < paths.size(); i++) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002230 std::string absPath =
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002231 fs->ExpandFilePath(JoinPath(paths[i], cleaned_filepath), fs->user_data);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002232 if (fs->FileExists(absPath, fs->user_data)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002233 return absPath;
2234 }
2235 }
2236
2237 return std::string();
2238}
2239
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09002240static std::string GetFilePathExtension(const std::string &FileName) {
johan bowald642a3432018-04-01 12:37:18 +02002241 if (FileName.find_last_of(".") != std::string::npos)
2242 return FileName.substr(FileName.find_last_of(".") + 1);
2243 return "";
2244}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002245
Syoyo Fujita643ce102016-05-01 17:19:37 +09002246static std::string GetBaseDir(const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002247 if (filepath.find_last_of("/\\") != std::string::npos)
Patrick Mc Gartoll1bdd4042024-05-16 18:11:26 -07002248 return filepath.substr(0, filepath.find_last_of("/\\") + 1);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002249 return "";
2250}
2251
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002252static std::string GetBaseFilename(const std::string &filepath) {
Martin Gerhardyfae65432022-03-13 18:42:39 +01002253 auto idx = filepath.find_last_of("/\\");
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002254 if (idx != std::string::npos) return filepath.substr(idx + 1);
Alexander Wood190382a2021-10-08 12:19:13 -04002255 return filepath;
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002256}
2257
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002258std::string base64_encode(unsigned char const *, unsigned int len);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002259std::string base64_decode(std::string const &s);
2260
2261/*
2262 base64.cpp and base64.h
2263
2264 Copyright (C) 2004-2008 René Nyffenegger
2265
2266 This source code is provided 'as-is', without any express or implied
2267 warranty. In no event will the author be held liable for any damages
2268 arising from the use of this software.
2269
2270 Permission is granted to anyone to use this software for any purpose,
2271 including commercial applications, and to alter it and redistribute it
2272 freely, subject to the following restrictions:
2273
2274 1. The origin of this source code must not be misrepresented; you must not
2275 claim that you wrote the original source code. If you use this source code
2276 in a product, an acknowledgment in the product documentation would be
2277 appreciated but is not required.
2278
2279 2. Altered source versions must be plainly marked as such, and must not be
2280 misrepresented as being the original source code.
2281
2282 3. This notice may not be removed or altered from any source distribution.
2283
2284 René Nyffenegger rene.nyffenegger@adp-gmbh.ch
2285
2286*/
2287
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002288#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09002289#pragma clang diagnostic push
Syoyo Fujita643ce102016-05-01 17:19:37 +09002290#pragma clang diagnostic ignored "-Wsign-conversion"
2291#pragma clang diagnostic ignored "-Wconversion"
Syoyo Fujita6fd91622016-05-01 20:27:30 +09002292#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002293
2294static inline bool is_base64(unsigned char c) {
2295 return (isalnum(c) || (c == '+') || (c == '/'));
2296}
2297
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002298std::string base64_encode(unsigned char const *bytes_to_encode,
2299 unsigned int in_len) {
johan bowald30c53472018-03-30 11:49:36 +02002300 std::string ret;
2301 int i = 0;
2302 int j = 0;
2303 unsigned char char_array_3[3];
2304 unsigned char char_array_4[4];
2305
Syoyo Fujitaff515702019-08-24 16:29:14 +09002306 const char *base64_chars =
2307 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2308 "abcdefghijklmnopqrstuvwxyz"
2309 "0123456789+/";
2310
johan bowald30c53472018-03-30 11:49:36 +02002311 while (in_len--) {
2312 char_array_3[i++] = *(bytes_to_encode++);
2313 if (i == 3) {
2314 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002315 char_array_4[1] =
2316 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
2317 char_array_4[2] =
2318 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
johan bowald30c53472018-03-30 11:49:36 +02002319 char_array_4[3] = char_array_3[2] & 0x3f;
2320
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002321 for (i = 0; (i < 4); i++) ret += base64_chars[char_array_4[i]];
johan bowald30c53472018-03-30 11:49:36 +02002322 i = 0;
2323 }
2324 }
2325
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002326 if (i) {
2327 for (j = i; j < 3; j++) char_array_3[j] = '\0';
johan bowald30c53472018-03-30 11:49:36 +02002328
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002329 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
2330 char_array_4[1] =
2331 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
2332 char_array_4[2] =
2333 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
johan bowald30c53472018-03-30 11:49:36 +02002334
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002335 for (j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]];
johan bowald30c53472018-03-30 11:49:36 +02002336
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002337 while ((i++ < 3)) ret += '=';
johan bowald30c53472018-03-30 11:49:36 +02002338 }
2339
2340 return ret;
johan bowald30c53472018-03-30 11:49:36 +02002341}
2342
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002343std::string base64_decode(std::string const &encoded_string) {
Syoyo Fujita8c6774c2016-05-01 20:36:29 +09002344 int in_len = static_cast<int>(encoded_string.size());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002345 int i = 0;
2346 int j = 0;
2347 int in_ = 0;
2348 unsigned char char_array_4[4], char_array_3[3];
2349 std::string ret;
2350
Syoyo Fujitaff515702019-08-24 16:29:14 +09002351 const std::string base64_chars =
2352 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2353 "abcdefghijklmnopqrstuvwxyz"
2354 "0123456789+/";
2355
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002356 while (in_len-- && (encoded_string[in_] != '=') &&
2357 is_base64(encoded_string[in_])) {
2358 char_array_4[i++] = encoded_string[in_];
2359 in_++;
2360 if (i == 4) {
2361 for (i = 0; i < 4; i++)
Syoyo Fujitabeded612016-05-01 20:03:43 +09002362 char_array_4[i] =
2363 static_cast<unsigned char>(base64_chars.find(char_array_4[i]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002364
2365 char_array_3[0] =
2366 (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
2367 char_array_3[1] =
2368 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
2369 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
2370
Syoyo Fujita7c877972016-03-08 01:31:49 +09002371 for (i = 0; (i < 3); i++) ret += char_array_3[i];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002372 i = 0;
2373 }
2374 }
2375
2376 if (i) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09002377 for (j = i; j < 4; j++) char_array_4[j] = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002378
2379 for (j = 0; j < 4; j++)
Syoyo Fujitabeded612016-05-01 20:03:43 +09002380 char_array_4[j] =
2381 static_cast<unsigned char>(base64_chars.find(char_array_4[j]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002382
2383 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
2384 char_array_3[1] =
2385 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
2386 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
2387
Syoyo Fujita7c877972016-03-08 01:31:49 +09002388 for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002389 }
2390
2391 return ret;
2392}
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002393#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09002394#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +09002395#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002396
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002397// https://github.com/syoyo/tinygltf/issues/228
2398// TODO(syoyo): Use uriparser https://uriparser.github.io/ for stricter Uri
2399// decoding?
2400//
Alexander Woode4bc6c72021-10-14 08:54:59 -04002401// Uri Decoding from DLIB
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002402// http://dlib.net/dlib/server/server_http.cpp.html
Alexander Woode4bc6c72021-10-14 08:54:59 -04002403// --- dlib begin ------------------------------------------------------------
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002404// Copyright (C) 2003 Davis E. King (davis@dlib.net)
Alexander Woode4bc6c72021-10-14 08:54:59 -04002405// License: Boost Software License
2406// Boost Software License - Version 1.0 - August 17th, 2003
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002407
Alexander Woode4bc6c72021-10-14 08:54:59 -04002408// Permission is hereby granted, free of charge, to any person or organization
2409// obtaining a copy of the software and accompanying documentation covered by
2410// this license (the "Software") to use, reproduce, display, distribute,
2411// execute, and transmit the Software, and to prepare derivative works of the
2412// Software, and to permit third-parties to whom the Software is furnished to
2413// do so, all subject to the following:
2414// The copyright notices in the Software and this entire statement, including
2415// the above license grant, this restriction and the following disclaimer,
2416// must be included in all copies of the Software, in whole or in part, and
2417// all derivative works of the Software, unless such copies or derivative
2418// works are solely in the form of machine-executable object code generated by
2419// a source language processor.
2420// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2421// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2422// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
2423// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
2424// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
2425// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2426// DEALINGS IN THE SOFTWARE.
Syoyo Fujitacbd38852022-02-26 21:19:15 +09002427//
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002428namespace dlib {
2429
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002430inline unsigned char from_hex(unsigned char ch) {
2431 if (ch <= '9' && ch >= '0')
2432 ch -= '0';
2433 else if (ch <= 'f' && ch >= 'a')
2434 ch -= 'a' - 10;
2435 else if (ch <= 'F' && ch >= 'A')
2436 ch -= 'A' - 10;
2437 else
2438 ch = 0;
2439 return ch;
2440}
2441
2442static const std::string urldecode(const std::string &str) {
2443 using namespace std;
2444 string result;
2445 string::size_type i;
2446 for (i = 0; i < str.size(); ++i) {
2447 if (str[i] == '+') {
2448 result += ' ';
2449 } else if (str[i] == '%' && str.size() > i + 2) {
2450 const unsigned char ch1 =
2451 from_hex(static_cast<unsigned char>(str[i + 1]));
2452 const unsigned char ch2 =
2453 from_hex(static_cast<unsigned char>(str[i + 2]));
2454 const unsigned char ch = static_cast<unsigned char>((ch1 << 4) | ch2);
2455 result += static_cast<char>(ch);
2456 i += 2;
2457 } else {
2458 result += str[i];
2459 }
2460 }
2461 return result;
2462}
2463
2464} // namespace dlib
2465// --- dlib end --------------------------------------------------------------
2466
Pyarelal Knowles385946d2023-01-03 18:18:04 -08002467bool URIDecode(const std::string &in_uri, std::string *out_uri,
2468 void *user_data) {
2469 (void)user_data;
2470 *out_uri = dlib::urldecode(in_uri);
2471 return true;
2472}
2473
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002474static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002475 std::string *warn, const std::string &filename,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002476 const std::string &basedir, bool required,
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02002477 size_t reqBytes, bool checkSize,
2478 size_t maxFileSize, FsCallbacks *fs) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002479 if (fs == nullptr || fs->FileExists == nullptr ||
2480 fs->ExpandFilePath == nullptr || fs->ReadWholeFile == nullptr) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002481 // This is a developer error, assert() ?
2482 if (err) {
2483 (*err) += "FS callback[s] not set\n";
2484 }
2485 return false;
2486 }
2487
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002488 std::string *failMsgOut = required ? err : warn;
Selmar Koke3b3fa92018-08-22 19:04:21 +02002489
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002490 out->clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002491
2492 std::vector<std::string> paths;
2493 paths.push_back(basedir);
2494 paths.push_back(".");
2495
Paolo Jovone6601bf2018-07-07 20:43:33 +02002496 std::string filepath = FindFile(paths, filename, fs);
jianghaosenb721fb72017-08-25 21:01:17 +08002497 if (filepath.empty() || filename.empty()) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002498 if (failMsgOut) {
2499 (*failMsgOut) += "File not found : " + filename + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002500 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002501 return false;
2502 }
2503
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002504 // Check file size
2505 if (fs->GetFileSizeInBytes) {
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002506 size_t file_size{0};
2507 std::string _err;
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02002508 bool ok =
2509 fs->GetFileSizeInBytes(&file_size, &_err, filepath, fs->user_data);
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002510 if (!ok) {
2511 if (_err.size()) {
2512 if (failMsgOut) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02002513 (*failMsgOut) += "Getting file size failed : " + filename +
2514 ", err = " + _err + "\n";
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002515 }
2516 }
2517 return false;
2518 }
2519
2520 if (file_size > maxFileSize) {
2521 if (failMsgOut) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02002522 (*failMsgOut) += "File size " + std::to_string(file_size) +
2523 " exceeds maximum allowed file size " +
2524 std::to_string(maxFileSize) + " : " + filepath + "\n";
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002525 }
2526 return false;
2527 }
2528 }
2529
Paolo Jovone6601bf2018-07-07 20:43:33 +02002530 std::vector<unsigned char> buf;
2531 std::string fileReadErr;
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002532 bool fileRead =
2533 fs->ReadWholeFile(&buf, &fileReadErr, filepath, fs->user_data);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002534 if (!fileRead) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002535 if (failMsgOut) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002536 (*failMsgOut) +=
2537 "File read error : " + filepath + " : " + fileReadErr + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002538 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002539 return false;
2540 }
2541
Paolo Jovone6601bf2018-07-07 20:43:33 +02002542 size_t sz = buf.size();
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09002543 if (sz == 0) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002544 if (failMsgOut) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002545 (*failMsgOut) += "File is empty : " + filepath + "\n";
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002546 }
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09002547 return false;
2548 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002549
2550 if (checkSize) {
2551 if (reqBytes == sz) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002552 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002553 return true;
2554 } else {
2555 std::stringstream ss;
2556 ss << "File size mismatch : " << filepath << ", requestedBytes "
2557 << reqBytes << ", but got " << sz << std::endl;
Selmar Koke3b3fa92018-08-22 19:04:21 +02002558 if (failMsgOut) {
2559 (*failMsgOut) += ss.str();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002560 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002561 return false;
2562 }
2563 }
2564
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002565 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002566 return true;
2567}
2568
Nyall Dawson8c85d5e2023-08-28 12:56:09 +10002569void TinyGLTF::SetParseStrictness(ParseStrictness strictness) {
2570 strictness_ = strictness;
2571}
2572
Squareysff644d82018-03-13 22:36:18 +01002573void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002574 LoadImageData = func;
2575 load_image_user_data_ = user_data;
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002576 user_image_loader_ = true;
2577}
2578
2579void TinyGLTF::RemoveImageLoader() {
2580 LoadImageData =
2581#ifndef TINYGLTF_NO_STB_IMAGE
2582 &tinygltf::LoadImageData;
2583#else
2584 nullptr;
2585#endif
2586
2587 load_image_user_data_ = nullptr;
2588 user_image_loader_ = false;
Squareysff644d82018-03-13 22:36:18 +01002589}
2590
Squareys2d3594d2018-03-13 22:40:53 +01002591#ifndef TINYGLTF_NO_STB_IMAGE
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002592bool LoadImageData(Image *image, const int image_idx, std::string *err,
2593 std::string *warn, int req_width, int req_height,
2594 const unsigned char *bytes, int size, void *user_data) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002595 (void)warn;
2596
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002597 LoadImageDataOption option;
2598 if (user_data) {
2599 option = *reinterpret_cast<LoadImageDataOption *>(user_data);
2600 }
2601
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002602 int w = 0, h = 0, comp = 0, req_comp = 0;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002603
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002604 unsigned char *data = nullptr;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002605
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002606 // preserve_channels true: Use channels stored in the image file.
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09002607 // false: force 32-bit textures for common Vulkan compatibility. It appears
2608 // that some GPU drivers do not support 24-bit images for Vulkan
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002609 req_comp = option.preserve_channels ? 0 : 4;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002610 int bits = 8;
2611 int pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002612
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002613 // It is possible that the image we want to load is a 16bit per channel image
2614 // We are going to attempt to load it as 16bit per channel, and if it worked,
imallettd9ce9eb2022-10-07 10:37:09 -07002615 // set the image data accordingly. We are casting the returned pointer into
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002616 // unsigned char, because we are representing "bytes". But we are updating
2617 // the Image metadata to signal that this image uses 2 bytes (16bits) per
2618 // channel:
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002619 if (stbi_is_16_bit_from_memory(bytes, size)) {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002620 data = reinterpret_cast<unsigned char *>(
2621 stbi_load_16_from_memory(bytes, size, &w, &h, &comp, req_comp));
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002622 if (data) {
2623 bits = 16;
2624 pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT;
2625 }
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002626 }
2627
2628 // at this point, if data is still NULL, it means that the image wasn't
2629 // 16bit per channel, we are going to load it as a normal 8bit per channel
imallettd9ce9eb2022-10-07 10:37:09 -07002630 // image as we used to do:
Aurélien Chatelain5cb43462017-05-24 09:55:14 +00002631 // if image cannot be decoded, ignore parsing and keep it by its path
2632 // don't break in this case
Syoyo Fujita5b407452017-06-04 17:42:41 +09002633 // FIXME we should only enter this function if the image is embedded. If
2634 // image->uri references
2635 // an image file, it should be left as it is. Image loading should not be
2636 // mandatory (to support other formats)
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002637 if (!data) data = stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002638 if (!data) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002639 // NOTE: you can use `warn` instead of `err`
Syoyo Fujitabeded612016-05-01 20:03:43 +09002640 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002641 (*err) +=
2642 "Unknown image format. STB cannot decode image data for image[" +
2643 std::to_string(image_idx) + "] name = \"" + image->name + "\".\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002644 }
Omar C. Fd492efc2018-02-10 09:50:35 +02002645 return false;
Syoyo Fujitabeded612016-05-01 20:03:43 +09002646 }
2647
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002648 if ((w < 1) || (h < 1)) {
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002649 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002650 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002651 (*err) += "Invalid image data for image[" + std::to_string(image_idx) +
2652 "] name = \"" + image->name + "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002653 }
Omar C. Fd492efc2018-02-10 09:50:35 +02002654 return false;
Syoyo Fujitabeded612016-05-01 20:03:43 +09002655 }
2656
2657 if (req_width > 0) {
2658 if (req_width != w) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002659 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002660 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002661 (*err) += "Image width mismatch for image[" +
2662 std::to_string(image_idx) + "] name = \"" + image->name +
2663 "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002664 }
2665 return false;
2666 }
2667 }
2668
2669 if (req_height > 0) {
2670 if (req_height != h) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002671 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002672 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002673 (*err) += "Image height mismatch. for image[" +
2674 std::to_string(image_idx) + "] name = \"" + image->name +
2675 "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002676 }
2677 return false;
2678 }
2679 }
2680
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002681 if (req_comp != 0) {
2682 // loaded data has `req_comp` channels(components)
2683 comp = req_comp;
2684 }
2685
Syoyo Fujitabeded612016-05-01 20:03:43 +09002686 image->width = w;
2687 image->height = h;
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002688 image->component = comp;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002689 image->bits = bits;
2690 image->pixel_type = pixel_type;
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002691 image->image.resize(static_cast<size_t>(w * h * comp) * size_t(bits / 8));
2692 std::copy(data, data + w * h * comp * (bits / 8), image->image.begin());
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002693 stbi_image_free(data);
Syoyo Fujita4be2f882016-11-24 16:20:34 +09002694
Syoyo Fujitabeded612016-05-01 20:03:43 +09002695 return true;
2696}
Squareys2d3594d2018-03-13 22:40:53 +01002697#endif
Syoyo Fujitabeded612016-05-01 20:03:43 +09002698
johan bowald642a3432018-04-01 12:37:18 +02002699void TinyGLTF::SetImageWriter(WriteImageDataFunction func, void *user_data) {
2700 WriteImageData = func;
2701 write_image_user_data_ = user_data;
2702}
2703
2704#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
2705static void WriteToMemory_stbi(void *context, void *data, int size) {
2706 std::vector<unsigned char> *buffer =
2707 reinterpret_cast<std::vector<unsigned char> *>(context);
2708
2709 unsigned char *pData = reinterpret_cast<unsigned char *>(data);
2710
2711 buffer->insert(buffer->end(), pData, pData + size);
2712}
2713
2714bool WriteImageData(const std::string *basepath, const std::string *filename,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08002715 const Image *image, bool embedImages,
2716 const URICallbacks *uri_cb, std::string *out_uri,
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08002717 void *fsPtr) {
johan bowald642a3432018-04-01 12:37:18 +02002718 const std::string ext = GetFilePathExtension(*filename);
2719
Paolo Jovone6601bf2018-07-07 20:43:33 +02002720 // Write image to temporary buffer
2721 std::string header;
2722 std::vector<unsigned char> data;
johan bowald642a3432018-04-01 12:37:18 +02002723
Paolo Jovone6601bf2018-07-07 20:43:33 +02002724 if (ext == "png") {
Syoyo Fujitaca56f722019-03-07 21:04:25 +09002725 if ((image->bits != 8) ||
2726 (image->pixel_type != TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)) {
Syoyo Fujitae8a46c42019-03-07 20:50:58 +09002727 // Unsupported pixel format
2728 return false;
2729 }
2730
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002731 if (!stbi_write_png_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002732 image->height, image->component,
2733 &image->image[0], 0)) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002734 return false;
2735 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002736 header = "data:image/png;base64,";
2737 } else if (ext == "jpg") {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002738 if (!stbi_write_jpg_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002739 image->height, image->component,
2740 &image->image[0], 100)) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002741 return false;
2742 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002743 header = "data:image/jpeg;base64,";
2744 } else if (ext == "bmp") {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002745 if (!stbi_write_bmp_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002746 image->height, image->component,
2747 &image->image[0])) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002748 return false;
2749 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002750 header = "data:image/bmp;base64,";
2751 } else if (!embedImages) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002752 // Error: can't output requested format to file
2753 return false;
Paolo Jovone6601bf2018-07-07 20:43:33 +02002754 }
johan bowald642a3432018-04-01 12:37:18 +02002755
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002756 if (embedImages) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002757 // Embed base64-encoded image into URI
johan bowald642a3432018-04-01 12:37:18 +02002758 if (data.size()) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02002759 *out_uri = header + base64_encode(&data[0],
2760 static_cast<unsigned int>(data.size()));
johan bowald642a3432018-04-01 12:37:18 +02002761 } else {
2762 // Throw error?
2763 }
2764 } else {
2765 // Write image to disc
Paolo Jovone6601bf2018-07-07 20:43:33 +02002766 FsCallbacks *fs = reinterpret_cast<FsCallbacks *>(fsPtr);
Syoyo Fujita33514af2018-11-05 13:32:43 +09002767 if ((fs != nullptr) && (fs->WriteWholeFile != nullptr)) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002768 const std::string imagefilepath = JoinPath(*basepath, *filename);
2769 std::string writeError;
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002770 if (!fs->WriteWholeFile(&writeError, imagefilepath, data,
2771 fs->user_data)) {
2772 // Could not write image file to disc; Throw error ?
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002773 return false;
Paolo Jovone6601bf2018-07-07 20:43:33 +02002774 }
johan bowald642a3432018-04-01 12:37:18 +02002775 } else {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002776 // Throw error?
johan bowald642a3432018-04-01 12:37:18 +02002777 }
Pyarelal Knowles385946d2023-01-03 18:18:04 -08002778 if (uri_cb->encode) {
2779 if (!uri_cb->encode(*filename, "image", out_uri, uri_cb->user_data)) {
2780 return false;
2781 }
2782 } else {
2783 *out_uri = *filename;
2784 }
johan bowald642a3432018-04-01 12:37:18 +02002785 }
2786
2787 return true;
2788}
2789#endif
2790
Pyarelal Knowles385946d2023-01-03 18:18:04 -08002791void TinyGLTF::SetURICallbacks(URICallbacks callbacks) {
2792 assert(callbacks.decode);
2793 if (callbacks.decode) {
2794 uri_cb = callbacks;
2795 }
2796}
2797
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002798void TinyGLTF::SetFsCallbacks(FsCallbacks callbacks) { fs = callbacks; }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002799
Harokyangfb256602019-10-30 16:13:52 +08002800#ifdef _WIN32
2801static inline std::wstring UTF8ToWchar(const std::string &str) {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002802 int wstr_size =
2803 MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
imallett56e10982022-10-07 10:35:16 -07002804 std::wstring wstr((size_t)wstr_size, 0);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002805 MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0],
2806 (int)wstr.size());
Harokyangfb256602019-10-30 16:13:52 +08002807 return wstr;
2808}
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002809
2810static inline std::string WcharToUTF8(const std::wstring &wstr) {
2811 int str_size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(),
imallett56e10982022-10-07 10:35:16 -07002812 nullptr, 0, nullptr, nullptr);
2813 std::string str((size_t)str_size, 0);
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002814 WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), &str[0],
imallett56e10982022-10-07 10:35:16 -07002815 (int)str.size(), nullptr, nullptr);
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002816 return str;
2817}
Harokyangfb256602019-10-30 16:13:52 +08002818#endif
2819
Paolo Jovone6601bf2018-07-07 20:43:33 +02002820#ifndef TINYGLTF_NO_FS
2821// Default implementations of filesystem functions
2822
2823bool FileExists(const std::string &abs_filename, void *) {
2824 bool ret;
Sascha Willems5f9cb242018-12-28 20:53:41 +01002825#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002826 if (asset_manager) {
2827 AAsset *asset = AAssetManager_open(asset_manager, abs_filename.c_str(),
2828 AASSET_MODE_STREAMING);
2829 if (!asset) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01002830 return false;
2831 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002832 AAsset_close(asset);
2833 ret = true;
2834 } else {
2835 return false;
2836 }
Sascha Willems5f9cb242018-12-28 20:53:41 +01002837#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002838#ifdef _WIN32
R.E. Kovalev112e3532023-07-05 09:11:57 +03002839#if defined(_MSC_VER) || defined(_LIBCPP_VERSION)
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002840
2841 // First check if a file is a directory.
2842 DWORD result = GetFileAttributesW(UTF8ToWchar(abs_filename).c_str());
2843 if (result == INVALID_FILE_ATTRIBUTES) {
2844 return false;
2845 }
2846 if (result & FILE_ATTRIBUTE_DIRECTORY) {
2847 return false;
2848 }
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002849
Syoyo Fujita45cac782019-11-09 20:42:55 +09002850 FILE *fp = nullptr;
Harokyangfb256602019-10-30 16:13:52 +08002851 errno_t err = _wfopen_s(&fp, UTF8ToWchar(abs_filename).c_str(), L"rb");
Paolo Jovone6601bf2018-07-07 20:43:33 +02002852 if (err != 0) {
2853 return false;
2854 }
R.E. Kovalev112e3532023-07-05 09:11:57 +03002855#elif defined(__GLIBCXX__)
2856 FILE *fp = fopen(abs_filename.c_str(), "rb");
2857 if (!fp) {
2858 return false;
2859 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002860#else
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002861 // TODO: is_directory check
Syoyo Fujita45cac782019-11-09 20:42:55 +09002862 FILE *fp = nullptr;
2863 errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb");
2864 if (err != 0) {
2865 return false;
2866 }
2867#endif
2868
2869#else
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002870 struct stat sb;
2871 if (stat(abs_filename.c_str(), &sb)) {
2872 return false;
2873 }
2874 if (S_ISDIR(sb.st_mode)) {
2875 return false;
2876 }
2877
Paolo Jovone6601bf2018-07-07 20:43:33 +02002878 FILE *fp = fopen(abs_filename.c_str(), "rb");
2879#endif
2880 if (fp) {
2881 ret = true;
2882 fclose(fp);
2883 } else {
2884 ret = false;
2885 }
Sascha Willems5f9cb242018-12-28 20:53:41 +01002886#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002887
2888 return ret;
2889}
2890
2891std::string ExpandFilePath(const std::string &filepath, void *) {
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002892 // https://github.com/syoyo/tinygltf/issues/368
2893 //
2894 // No file path expansion in built-in FS function anymore, since glTF URI
2895 // should not contain tilde('~') and environment variables, and for security
2896 // reason(`wordexp`).
2897 //
2898 // Users need to supply `base_dir`(in `LoadASCIIFromString`,
2899 // `LoadBinaryFromMemory`) in expanded absolute path.
2900
2901 return filepath;
2902
2903#if 0
Paolo Jovone6601bf2018-07-07 20:43:33 +02002904#ifdef _WIN32
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002905 // Assume input `filepath` is encoded in UTF-8
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002906 std::wstring wfilepath = UTF8ToWchar(filepath);
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002907 DWORD wlen = ExpandEnvironmentStringsW(wfilepath.c_str(), nullptr, 0);
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002908 wchar_t *wstr = new wchar_t[wlen];
2909 ExpandEnvironmentStringsW(wfilepath.c_str(), wstr, wlen);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002910
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002911 std::wstring ws(wstr);
2912 delete[] wstr;
2913 return WcharToUTF8(ws);
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002914
Paolo Jovone6601bf2018-07-07 20:43:33 +02002915#else
2916
2917#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \
Julian Smith0598a202021-08-25 12:06:08 +01002918 defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__OpenBSD__)
Paolo Jovone6601bf2018-07-07 20:43:33 +02002919 // no expansion
2920 std::string s = filepath;
2921#else
2922 std::string s;
2923 wordexp_t p;
2924
2925 if (filepath.empty()) {
2926 return "";
2927 }
2928
Frank Galliganaa3c5a12020-01-13 15:06:56 -08002929 // Quote the string to keep any spaces in filepath intact.
2930 std::string quoted_path = "\"" + filepath + "\"";
Paolo Jovone6601bf2018-07-07 20:43:33 +02002931 // char** w;
Frank Galliganaa3c5a12020-01-13 15:06:56 -08002932 int ret = wordexp(quoted_path.c_str(), &p, 0);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002933 if (ret) {
2934 // err
2935 s = filepath;
2936 return s;
2937 }
2938
2939 // Use first element only.
2940 if (p.we_wordv) {
2941 s = std::string(p.we_wordv[0]);
2942 wordfree(&p);
2943 } else {
2944 s = filepath;
2945 }
2946
2947#endif
2948
2949 return s;
2950#endif
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002951#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002952}
2953
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002954bool GetFileSizeInBytes(size_t *filesize_out, std::string *err,
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02002955 const std::string &filepath, void *userdata) {
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002956 (void)userdata;
2957
2958#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
2959 if (asset_manager) {
2960 AAsset *asset = AAssetManager_open(asset_manager, filepath.c_str(),
2961 AASSET_MODE_STREAMING);
2962 if (!asset) {
2963 if (err) {
2964 (*err) += "File open error : " + filepath + "\n";
2965 }
2966 return false;
2967 }
2968 size_t size = AAsset_getLength(asset);
2969
2970 if (size == 0) {
2971 if (err) {
2972 (*err) += "Invalid file size : " + filepath +
2973 " (does the path point to a directory?)";
2974 }
2975 return false;
2976 }
2977
2978 return true;
2979 } else {
2980 if (err) {
2981 (*err) += "No asset manager specified : " + filepath + "\n";
2982 }
2983 return false;
2984 }
2985#else
2986#ifdef _WIN32
2987#if defined(__GLIBCXX__) // mingw
2988 int file_descriptor =
2989 _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY);
2990 __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
2991 std::istream f(&wfile_buf);
2992#elif defined(_MSC_VER) || defined(_LIBCPP_VERSION)
2993 // For libcxx, assume _LIBCPP_HAS_OPEN_WITH_WCHAR is defined to accept
2994 // `wchar_t *`
2995 std::ifstream f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary);
2996#else
2997 // Unknown compiler/runtime
2998 std::ifstream f(filepath.c_str(), std::ifstream::binary);
2999#endif
3000#else
3001 std::ifstream f(filepath.c_str(), std::ifstream::binary);
3002#endif
3003 if (!f) {
3004 if (err) {
3005 (*err) += "File open error : " + filepath + "\n";
3006 }
3007 return false;
3008 }
3009
3010 // For directory(and pipe?), peek() will fail(Posix gnustl/libc++ only)
David Siegel47208b22023-06-05 22:14:30 +02003011 f.peek();
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09003012 if (!f) {
3013 if (err) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02003014 (*err) +=
3015 "File read error. Maybe empty file or invalid file : " + filepath +
3016 "\n";
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09003017 }
3018 return false;
3019 }
3020
3021 f.seekg(0, f.end);
jamesvertf57d18a2024-03-26 11:06:16 +00003022 const auto sz = f.tellg();
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09003023
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02003024 // std::cout << "sz = " << sz << "\n";
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09003025 f.seekg(0, f.beg);
3026
jamesvertf57d18a2024-03-26 11:06:16 +00003027 if (sz < 0) {
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09003028 if (err) {
3029 (*err) += "Invalid file size : " + filepath +
3030 " (does the path point to a directory?)";
3031 }
3032 return false;
jamesverte3f9a7d2024-03-26 11:32:43 +00003033 } else if (sz == std::streamoff(0)) {
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09003034 if (err) {
3035 (*err) += "File is empty : " + filepath + "\n";
3036 }
3037 return false;
3038 } else if (sz >= (std::numeric_limits<std::streamoff>::max)()) {
3039 if (err) {
3040 (*err) += "Invalid file size : " + filepath + "\n";
3041 }
3042 return false;
3043 }
3044
3045 (*filesize_out) = sz;
3046 return true;
3047#endif
3048}
3049
Paolo Jovone6601bf2018-07-07 20:43:33 +02003050bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
3051 const std::string &filepath, void *) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01003052#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
3053 if (asset_manager) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003054 AAsset *asset = AAssetManager_open(asset_manager, filepath.c_str(),
3055 AASSET_MODE_STREAMING);
Sascha Willems5f9cb242018-12-28 20:53:41 +01003056 if (!asset) {
3057 if (err) {
3058 (*err) += "File open error : " + filepath + "\n";
3059 }
3060 return false;
3061 }
3062 size_t size = AAsset_getLength(asset);
Syoyo Fujita73c4cce2020-04-28 01:06:34 +09003063 if (size == 0) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01003064 if (err) {
3065 (*err) += "Invalid file size : " + filepath +
3066 " (does the path point to a directory?)";
3067 }
Syoyo Fujita73c4cce2020-04-28 01:06:34 +09003068 return false;
Sascha Willems5f9cb242018-12-28 20:53:41 +01003069 }
3070 out->resize(size);
3071 AAsset_read(asset, reinterpret_cast<char *>(&out->at(0)), size);
3072 AAsset_close(asset);
3073 return true;
3074 } else {
3075 if (err) {
3076 (*err) += "No asset manager specified : " + filepath + "\n";
3077 }
3078 return false;
3079 }
3080#else
Harokyang5cecef22019-10-30 15:16:46 +08003081#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09003082#if defined(__GLIBCXX__) // mingw
3083 int file_descriptor =
3084 _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY);
Syoyo Fujita45cac782019-11-09 20:42:55 +09003085 __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
3086 std::istream f(&wfile_buf);
Syoyo Fujitaaba57bb2020-05-18 20:50:45 +09003087#elif defined(_MSC_VER) || defined(_LIBCPP_VERSION)
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04003088 // For libcxx, assume _LIBCPP_HAS_OPEN_WITH_WCHAR is defined to accept
3089 // `wchar_t *`
Harokyangfb256602019-10-30 16:13:52 +08003090 std::ifstream f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary);
Syoyo Fujitaaba57bb2020-05-18 20:50:45 +09003091#else
3092 // Unknown compiler/runtime
Syoyo Fujita45cac782019-11-09 20:42:55 +09003093 std::ifstream f(filepath.c_str(), std::ifstream::binary);
3094#endif
Harokyang5cecef22019-10-30 15:16:46 +08003095#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02003096 std::ifstream f(filepath.c_str(), std::ifstream::binary);
Harokyang5cecef22019-10-30 15:16:46 +08003097#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02003098 if (!f) {
3099 if (err) {
3100 (*err) += "File open error : " + filepath + "\n";
3101 }
3102 return false;
3103 }
3104
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09003105 // For directory(and pipe?), peek() will fail(Posix gnustl/libc++ only)
David Siegel47208b22023-06-05 22:14:30 +02003106 f.peek();
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09003107 if (!f) {
3108 if (err) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02003109 (*err) +=
3110 "File read error. Maybe empty file or invalid file : " + filepath +
3111 "\n";
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09003112 }
3113 return false;
3114 }
3115
Paolo Jovone6601bf2018-07-07 20:43:33 +02003116 f.seekg(0, f.end);
jamesvertf57d18a2024-03-26 11:06:16 +00003117 const auto sz = f.tellg();
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09003118
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02003119 // std::cout << "sz = " << sz << "\n";
Paolo Jovone6601bf2018-07-07 20:43:33 +02003120 f.seekg(0, f.beg);
3121
jamesvertf57d18a2024-03-26 11:06:16 +00003122 if (sz < 0) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02003123 if (err) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003124 (*err) += "Invalid file size : " + filepath +
3125 " (does the path point to a directory?)";
Paolo Jovone6601bf2018-07-07 20:43:33 +02003126 }
3127 return false;
jamesverte3f9a7d2024-03-26 11:32:43 +00003128 } else if (sz == std::streamoff(0)) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02003129 if (err) {
3130 (*err) += "File is empty : " + filepath + "\n";
3131 }
3132 return false;
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09003133 } else if (sz >= (std::numeric_limits<std::streamoff>::max)()) {
3134 if (err) {
3135 (*err) += "Invalid file size : " + filepath + "\n";
3136 }
3137 return false;
Paolo Jovone6601bf2018-07-07 20:43:33 +02003138 }
3139
3140 out->resize(sz);
3141 f.read(reinterpret_cast<char *>(&out->at(0)),
3142 static_cast<std::streamsize>(sz));
Paolo Jovone6601bf2018-07-07 20:43:33 +02003143
3144 return true;
Sascha Willems5f9cb242018-12-28 20:53:41 +01003145#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02003146}
3147
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003148bool WriteWholeFile(std::string *err, const std::string &filepath,
3149 const std::vector<unsigned char> &contents, void *) {
Harokyangfb256602019-10-30 16:13:52 +08003150#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09003151#if defined(__GLIBCXX__) // mingw
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09003152 int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(),
TheDev22dfeab2024-03-25 18:21:20 +03003153 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY, _S_IWRITE);
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09003154 __gnu_cxx::stdio_filebuf<char> wfile_buf(
3155 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09003156 std::ostream f(&wfile_buf);
3157#elif defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08003158 std::ofstream f(UTF8ToWchar(filepath).c_str(), std::ofstream::binary);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09003159#else // clang?
Syoyo Fujita45cac782019-11-09 20:42:55 +09003160 std::ofstream f(filepath.c_str(), std::ofstream::binary);
3161#endif
Harokyangfb256602019-10-30 16:13:52 +08003162#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02003163 std::ofstream f(filepath.c_str(), std::ofstream::binary);
Harokyangfb256602019-10-30 16:13:52 +08003164#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02003165 if (!f) {
3166 if (err) {
3167 (*err) += "File open error for writing : " + filepath + "\n";
3168 }
3169 return false;
3170 }
3171
3172 f.write(reinterpret_cast<const char *>(&contents.at(0)),
3173 static_cast<std::streamsize>(contents.size()));
3174 if (!f) {
3175 if (err) {
3176 (*err) += "File write error: " + filepath + "\n";
3177 }
3178 return false;
3179 }
3180
Paolo Jovone6601bf2018-07-07 20:43:33 +02003181 return true;
3182}
3183
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003184#endif // TINYGLTF_NO_FS
Paolo Jovone6601bf2018-07-07 20:43:33 +02003185
johan bowald642a3432018-04-01 12:37:18 +02003186static std::string MimeToExt(const std::string &mimeType) {
3187 if (mimeType == "image/jpeg") {
3188 return "jpg";
3189 } else if (mimeType == "image/png") {
3190 return "png";
3191 } else if (mimeType == "image/bmp") {
3192 return "bmp";
3193 } else if (mimeType == "image/gif") {
3194 return "gif";
3195 }
3196
3197 return "";
3198}
3199
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08003200static bool UpdateImageObject(const Image &image, std::string &baseDir,
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08003201 int index, bool embedImages,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08003202 const URICallbacks *uri_cb,
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08003203 WriteImageDataFunction *WriteImageData,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08003204 void *user_data, std::string *out_uri) {
johan bowald642a3432018-04-01 12:37:18 +02003205 std::string filename;
3206 std::string ext;
imallettd9ce9eb2022-10-07 10:37:09 -07003207 // If image has uri, use it as a filename
johan bowald642a3432018-04-01 12:37:18 +02003208 if (image.uri.size()) {
Pyarelal Knowles385946d2023-01-03 18:18:04 -08003209 std::string decoded_uri;
3210 if (!uri_cb->decode(image.uri, &decoded_uri, uri_cb->user_data)) {
3211 // A decode failure results in a failure to write the gltf.
3212 return false;
3213 }
3214 filename = GetBaseFilename(decoded_uri);
johan bowald642a3432018-04-01 12:37:18 +02003215 ext = GetFilePathExtension(filename);
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09003216 } else if (image.bufferView != -1) {
3217 // If there's no URI and the data exists in a buffer,
3218 // don't change properties or write images
johan bowald642a3432018-04-01 12:37:18 +02003219 } else if (image.name.size()) {
3220 ext = MimeToExt(image.mimeType);
3221 // Otherwise use name as filename
3222 filename = image.name + "." + ext;
3223 } else {
3224 ext = MimeToExt(image.mimeType);
3225 // Fallback to index of image as filename
3226 filename = std::to_string(index) + "." + ext;
3227 }
3228
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08003229 // If callback is set and image data exists, modify image data object. If
3230 // image data does not exist, this is not considered a failure and the
3231 // original uri should be maintained.
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08003232 bool imageWritten = false;
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08003233 if (*WriteImageData != nullptr && !filename.empty() && !image.image.empty()) {
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08003234 imageWritten = (*WriteImageData)(&baseDir, &filename, &image, embedImages,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08003235 uri_cb, out_uri, user_data);
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08003236 if (!imageWritten) {
3237 return false;
3238 }
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08003239 }
3240
3241 // Use the original uri if the image was not written.
3242 if (!imageWritten) {
3243 *out_uri = image.uri;
johan bowald642a3432018-04-01 12:37:18 +02003244 }
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08003245
3246 return true;
johan bowald642a3432018-04-01 12:37:18 +02003247}
3248
Selmar Kok0d0e97e2018-08-22 14:01:57 +02003249bool IsDataURI(const std::string &in) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003250 std::string header = "data:application/octet-stream;base64,";
3251 if (in.find(header) == 0) {
3252 return true;
3253 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09003254
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02003255 header = "data:image/jpeg;base64,";
3256 if (in.find(header) == 0) {
3257 return true;
3258 }
Squareys43374632018-03-13 22:20:48 +01003259
Syoyo Fujita620eed12016-01-02 23:37:12 +09003260 header = "data:image/png;base64,";
3261 if (in.find(header) == 0) {
3262 return true;
3263 }
Squareys43374632018-03-13 22:20:48 +01003264
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02003265 header = "data:image/bmp;base64,";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003266 if (in.find(header) == 0) {
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02003267 return true;
3268 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09003269
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02003270 header = "data:image/gif;base64,";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003271 if (in.find(header) == 0) {
Syoyo Fujita620eed12016-01-02 23:37:12 +09003272 return true;
3273 }
3274
Luke San Antoniocaa24b02016-06-15 02:23:09 -04003275 header = "data:text/plain;base64,";
3276 if (in.find(header) == 0) {
3277 return true;
3278 }
3279
Syoyo Fujita20244e12018-03-15 11:01:05 -05003280 header = "data:application/gltf-buffer;base64,";
3281 if (in.find(header) == 0) {
3282 return true;
3283 }
3284
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003285 return false;
3286}
3287
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09003288bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
3289 const std::string &in, size_t reqBytes, bool checkSize) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003290 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujita620eed12016-01-02 23:37:12 +09003291 std::string data;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003292 if (in.find(header) == 0) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09003293 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003294 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09003295
3296 if (data.empty()) {
3297 header = "data:image/jpeg;base64,";
3298 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02003299 mime_type = "image/jpeg";
Syoyo Fujita7c877972016-03-08 01:31:49 +09003300 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09003301 }
3302 }
3303
3304 if (data.empty()) {
3305 header = "data:image/png;base64,";
3306 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02003307 mime_type = "image/png";
Syoyo Fujita7c877972016-03-08 01:31:49 +09003308 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09003309 }
3310 }
Squareys43374632018-03-13 22:20:48 +01003311
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02003312 if (data.empty()) {
3313 header = "data:image/bmp;base64,";
3314 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02003315 mime_type = "image/bmp";
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02003316 data = base64_decode(in.substr(header.size())); // cut mime string.
3317 }
3318 }
3319
3320 if (data.empty()) {
3321 header = "data:image/gif;base64,";
3322 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02003323 mime_type = "image/gif";
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02003324 data = base64_decode(in.substr(header.size())); // cut mime string.
3325 }
3326 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09003327
3328 if (data.empty()) {
Luke San Antoniocaa24b02016-06-15 02:23:09 -04003329 header = "data:text/plain;base64,";
3330 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02003331 mime_type = "text/plain";
Luke San Antoniocaa24b02016-06-15 02:23:09 -04003332 data = base64_decode(in.substr(header.size()));
3333 }
3334 }
3335
3336 if (data.empty()) {
Syoyo Fujita20244e12018-03-15 11:01:05 -05003337 header = "data:application/gltf-buffer;base64,";
3338 if (in.find(header) == 0) {
3339 data = base64_decode(in.substr(header.size()));
3340 }
3341 }
3342
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09003343 // TODO(syoyo): Allow empty buffer? #229
Syoyo Fujita20244e12018-03-15 11:01:05 -05003344 if (data.empty()) {
Syoyo Fujita620eed12016-01-02 23:37:12 +09003345 return false;
Syoyo Fujitabde70212016-02-07 17:38:17 +09003346 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09003347
3348 if (checkSize) {
3349 if (data.size() != reqBytes) {
3350 return false;
3351 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003352 out->resize(reqBytes);
Syoyo Fujita620eed12016-01-02 23:37:12 +09003353 } else {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003354 out->resize(data.size());
Syoyo Fujita620eed12016-01-02 23:37:12 +09003355 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003356 std::copy(data.begin(), data.end(), out->begin());
Syoyo Fujita620eed12016-01-02 23:37:12 +09003357 return true;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003358}
3359
David1f9a4b92023-02-15 22:56:18 -06003360namespace detail {
David03ad33c2023-02-15 23:35:51 -06003361bool GetInt(const detail::json &o, int &val) {
jrkoonce5cecc412019-08-29 11:45:04 -05003362#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003363 if (!o.IsDouble()) {
3364 if (o.IsInt()) {
3365 val = o.GetInt();
3366 return true;
3367 } else if (o.IsUint()) {
3368 val = static_cast<int>(o.GetUint());
3369 return true;
3370 } else if (o.IsInt64()) {
3371 val = static_cast<int>(o.GetInt64());
3372 return true;
3373 } else if (o.IsUint64()) {
3374 val = static_cast<int>(o.GetUint64());
jrkooncecba5d6c2019-08-29 11:26:22 -05003375 return true;
3376 }
jrkoonce5cecc412019-08-29 11:45:04 -05003377 }
3378
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003379 return false;
jrkoonce5cecc412019-08-29 11:45:04 -05003380#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003381 auto type = o.type();
jrkooncecba5d6c2019-08-29 11:26:22 -05003382
David03ad33c2023-02-15 23:35:51 -06003383 if ((type == detail::json::value_t::number_integer) ||
3384 (type == detail::json::value_t::number_unsigned)) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003385 val = static_cast<int>(o.get<int64_t>());
3386 return true;
jrkoonce5cecc412019-08-29 11:45:04 -05003387 }
3388
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003389 return false;
jrkoonce5cecc412019-08-29 11:45:04 -05003390#endif
jrkooncecba5d6c2019-08-29 11:26:22 -05003391}
3392
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003393#ifdef TINYGLTF_USE_RAPIDJSON
David03ad33c2023-02-15 23:35:51 -06003394bool GetDouble(const detail::json &o, double &val) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003395 if (o.IsDouble()) {
3396 val = o.GetDouble();
3397 return true;
3398 }
3399
3400 return false;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003401}
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003402#endif
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003403
David03ad33c2023-02-15 23:35:51 -06003404bool GetNumber(const detail::json &o, double &val) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003405#ifdef TINYGLTF_USE_RAPIDJSON
3406 if (o.IsNumber()) {
3407 val = o.GetDouble();
3408 return true;
3409 }
3410
3411 return false;
3412#else
3413 if (o.is_number()) {
3414 val = o.get<double>();
3415 return true;
3416 }
3417
3418 return false;
3419#endif
3420}
3421
David03ad33c2023-02-15 23:35:51 -06003422bool GetString(const detail::json &o, std::string &val) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003423#ifdef TINYGLTF_USE_RAPIDJSON
3424 if (o.IsString()) {
3425 val = o.GetString();
3426 return true;
3427 }
3428
3429 return false;
3430#else
David03ad33c2023-02-15 23:35:51 -06003431 if (o.type() == detail::json::value_t::string) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003432 val = o.get<std::string>();
3433 return true;
3434 }
3435
3436 return false;
3437#endif
3438}
3439
David03ad33c2023-02-15 23:35:51 -06003440bool IsArray(const detail::json &o) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003441#ifdef TINYGLTF_USE_RAPIDJSON
3442 return o.IsArray();
3443#else
3444 return o.is_array();
3445#endif
3446}
3447
David03ad33c2023-02-15 23:35:51 -06003448detail::json_const_array_iterator ArrayBegin(const detail::json &o) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003449#ifdef TINYGLTF_USE_RAPIDJSON
3450 return o.Begin();
3451#else
3452 return o.begin();
3453#endif
3454}
3455
David03ad33c2023-02-15 23:35:51 -06003456detail::json_const_array_iterator ArrayEnd(const detail::json &o) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003457#ifdef TINYGLTF_USE_RAPIDJSON
3458 return o.End();
3459#else
3460 return o.end();
3461#endif
3462}
3463
David03ad33c2023-02-15 23:35:51 -06003464bool IsObject(const detail::json &o) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003465#ifdef TINYGLTF_USE_RAPIDJSON
3466 return o.IsObject();
3467#else
3468 return o.is_object();
3469#endif
3470}
3471
David03ad33c2023-02-15 23:35:51 -06003472detail::json_const_iterator ObjectBegin(const detail::json &o) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003473#ifdef TINYGLTF_USE_RAPIDJSON
3474 return o.MemberBegin();
3475#else
3476 return o.begin();
3477#endif
3478}
3479
David03ad33c2023-02-15 23:35:51 -06003480detail::json_const_iterator ObjectEnd(const detail::json &o) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003481#ifdef TINYGLTF_USE_RAPIDJSON
3482 return o.MemberEnd();
3483#else
3484 return o.end();
3485#endif
3486}
3487
Rahul Sheth01d54382020-07-10 14:27:37 -04003488// Making this a const char* results in a pointer to a temporary when
3489// TINYGLTF_USE_RAPIDJSON is off.
David03ad33c2023-02-15 23:35:51 -06003490std::string GetKey(detail::json_const_iterator &it) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003491#ifdef TINYGLTF_USE_RAPIDJSON
3492 return it->name.GetString();
3493#else
3494 return it.key().c_str();
3495#endif
3496}
3497
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02003498bool FindMember(const detail::json &o, const char *member,
3499 detail::json_const_iterator &it) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003500#ifdef TINYGLTF_USE_RAPIDJSON
3501 if (!o.IsObject()) {
3502 return false;
3503 }
3504 it = o.FindMember(member);
3505 return it != o.MemberEnd();
3506#else
3507 it = o.find(member);
3508 return it != o.end();
3509#endif
3510}
3511
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02003512bool FindMember(detail::json &o, const char *member,
3513 detail::json_iterator &it) {
David Siegelcfe64fb2023-06-07 15:18:38 +02003514#ifdef TINYGLTF_USE_RAPIDJSON
3515 if (!o.IsObject()) {
3516 return false;
3517 }
3518 it = o.FindMember(member);
3519 return it != o.MemberEnd();
3520#else
3521 it = o.find(member);
3522 return it != o.end();
3523#endif
3524}
3525
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02003526void Erase(detail::json &o, detail::json_iterator &it) {
David Siegelcfe64fb2023-06-07 15:18:38 +02003527#ifdef TINYGLTF_USE_RAPIDJSON
3528 o.EraseMember(it);
3529#else
3530 o.erase(it);
3531#endif
3532}
3533
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02003534bool IsEmpty(const detail::json &o) {
David Siegelcfe64fb2023-06-07 15:18:38 +02003535#ifdef TINYGLTF_USE_RAPIDJSON
David Siegela1a34cb2023-06-07 15:30:02 +02003536 return o.ObjectEmpty();
David Siegelcfe64fb2023-06-07 15:18:38 +02003537#else
3538 return o.empty();
3539#endif
3540}
3541
David03ad33c2023-02-15 23:35:51 -06003542const detail::json &GetValue(detail::json_const_iterator &it) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003543#ifdef TINYGLTF_USE_RAPIDJSON
3544 return it->value;
3545#else
3546 return it.value();
3547#endif
3548}
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003549
David Siegelcfe64fb2023-06-07 15:18:38 +02003550detail::json &GetValue(detail::json_iterator &it) {
3551#ifdef TINYGLTF_USE_RAPIDJSON
3552 return it->value;
3553#else
3554 return it.value();
3555#endif
3556}
3557
David03ad33c2023-02-15 23:35:51 -06003558std::string JsonToString(const detail::json &o, int spacing = -1) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003559#ifdef TINYGLTF_USE_RAPIDJSON
3560 using namespace rapidjson;
3561 StringBuffer buffer;
3562 if (spacing == -1) {
3563 Writer<StringBuffer> writer(buffer);
Syoyo Fujita544969b2022-07-13 19:03:33 +09003564 // TODO: Better error handling.
3565 // https://github.com/syoyo/tinygltf/issues/332
3566 if (!o.Accept(writer)) {
3567 return "tiny_gltf::JsonToString() failed rapidjson conversion";
3568 }
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003569 } else {
3570 PrettyWriter<StringBuffer> writer(buffer);
3571 writer.SetIndent(' ', uint32_t(spacing));
Syoyo Fujita544969b2022-07-13 19:03:33 +09003572 if (!o.Accept(writer)) {
3573 return "tiny_gltf::JsonToString() failed rapidjson conversion";
3574 }
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003575 }
3576 return buffer.GetString();
3577#else
3578 return o.dump(spacing);
3579#endif
3580}
3581
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02003582} // namespace detail
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003583
David03ad33c2023-02-15 23:35:51 -06003584static bool ParseJsonAsValue(Value *ret, const detail::json &o) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003585 Value val{};
jrkooncecba5d6c2019-08-29 11:26:22 -05003586#ifdef TINYGLTF_USE_RAPIDJSON
3587 using rapidjson::Type;
3588 switch (o.GetType()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003589 case Type::kObjectType: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003590 Value::Object value_object;
jrkooncecba5d6c2019-08-29 11:26:22 -05003591 for (auto it = o.MemberBegin(); it != o.MemberEnd(); ++it) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003592 Value entry;
jrkooncecba5d6c2019-08-29 11:26:22 -05003593 ParseJsonAsValue(&entry, it->value);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003594 if (entry.Type() != NULL_TYPE)
David1f9a4b92023-02-15 22:56:18 -06003595 value_object.emplace(detail::GetKey(it), std::move(entry));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003596 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003597 if (value_object.size() > 0) val = Value(std::move(value_object));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003598 } break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003599 case Type::kArrayType: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003600 Value::Array value_array;
jrkoonce5cecc412019-08-29 11:45:04 -05003601 value_array.reserve(o.Size());
jrkooncecba5d6c2019-08-29 11:26:22 -05003602 for (auto it = o.Begin(); it != o.End(); ++it) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003603 Value entry;
jrkooncecba5d6c2019-08-29 11:26:22 -05003604 ParseJsonAsValue(&entry, *it);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003605 if (entry.Type() != NULL_TYPE)
3606 value_array.emplace_back(std::move(entry));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003607 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003608 if (value_array.size() > 0) val = Value(std::move(value_array));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003609 } break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003610 case Type::kStringType:
3611 val = Value(std::string(o.GetString()));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003612 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003613 case Type::kFalseType:
3614 case Type::kTrueType:
3615 val = Value(o.GetBool());
Selmar Kokfa7022f2018-04-04 18:10:20 +02003616 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003617 case Type::kNumberType:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003618 if (!o.IsDouble()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003619 int i = 0;
David1f9a4b92023-02-15 22:56:18 -06003620 detail::GetInt(o, i);
jrkooncecba5d6c2019-08-29 11:26:22 -05003621 val = Value(i);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003622 } else {
jrkooncecba5d6c2019-08-29 11:26:22 -05003623 double d = 0.0;
David38318022023-02-15 23:21:09 -06003624 detail::GetDouble(o, d);
jrkooncecba5d6c2019-08-29 11:26:22 -05003625 val = Value(d);
3626 }
Selmar Kokfa7022f2018-04-04 18:10:20 +02003627 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003628 case Type::kNullType:
Selmar Kokfa7022f2018-04-04 18:10:20 +02003629 break;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003630 // all types are covered, so no `case default`
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003631 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003632#else
3633 switch (o.type()) {
David03ad33c2023-02-15 23:35:51 -06003634 case detail::json::value_t::object: {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003635 Value::Object value_object;
3636 for (auto it = o.begin(); it != o.end(); it++) {
3637 Value entry;
3638 ParseJsonAsValue(&entry, it.value());
3639 if (entry.Type() != NULL_TYPE)
3640 value_object.emplace(it.key(), std::move(entry));
3641 }
3642 if (value_object.size() > 0) val = Value(std::move(value_object));
3643 } break;
David03ad33c2023-02-15 23:35:51 -06003644 case detail::json::value_t::array: {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003645 Value::Array value_array;
3646 value_array.reserve(o.size());
3647 for (auto it = o.begin(); it != o.end(); it++) {
3648 Value entry;
3649 ParseJsonAsValue(&entry, it.value());
3650 if (entry.Type() != NULL_TYPE)
3651 value_array.emplace_back(std::move(entry));
3652 }
3653 if (value_array.size() > 0) val = Value(std::move(value_array));
3654 } break;
David03ad33c2023-02-15 23:35:51 -06003655 case detail::json::value_t::string:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003656 val = Value(o.get<std::string>());
3657 break;
David03ad33c2023-02-15 23:35:51 -06003658 case detail::json::value_t::boolean:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003659 val = Value(o.get<bool>());
3660 break;
David03ad33c2023-02-15 23:35:51 -06003661 case detail::json::value_t::number_integer:
3662 case detail::json::value_t::number_unsigned:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003663 val = Value(static_cast<int>(o.get<int64_t>()));
3664 break;
David03ad33c2023-02-15 23:35:51 -06003665 case detail::json::value_t::number_float:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003666 val = Value(o.get<double>());
3667 break;
David03ad33c2023-02-15 23:35:51 -06003668 case detail::json::value_t::null:
3669 case detail::json::value_t::discarded:
3670 case detail::json::value_t::binary:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003671 // default:
3672 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003673 }
3674#endif
Marcin Kacprzakf4f5c3c2022-09-02 16:15:54 +02003675 const bool isNotNull = val.Type() != NULL_TYPE;
3676
jrkooncecba5d6c2019-08-29 11:26:22 -05003677 if (ret) *ret = std::move(val);
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003678
Marcin Kacprzakf4f5c3c2022-09-02 16:15:54 +02003679 return isNotNull;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003680}
3681
David03ad33c2023-02-15 23:35:51 -06003682static bool ParseExtrasProperty(Value *ret, const detail::json &o) {
3683 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06003684 if (!detail::FindMember(o, "extras", it)) {
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003685 return false;
3686 }
3687
David1f9a4b92023-02-15 22:56:18 -06003688 return ParseJsonAsValue(ret, detail::GetValue(it));
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003689}
3690
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02003691static bool ParseBooleanProperty(bool *ret, std::string *err,
3692 const detail::json &o,
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09003693 const std::string &property,
3694 const bool required,
3695 const std::string &parent_node = "") {
David03ad33c2023-02-15 23:35:51 -06003696 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06003697 if (!detail::FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003698 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003699 if (err) {
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09003700 (*err) += "'" + property + "' property is missing";
3701 if (!parent_node.empty()) {
3702 (*err) += " in " + parent_node;
3703 }
3704 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003705 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003706 }
3707 return false;
3708 }
3709
David1f9a4b92023-02-15 22:56:18 -06003710 auto &value = detail::GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003711
3712 bool isBoolean;
jrkoonce0d2b6ef2019-09-04 13:46:45 -05003713 bool boolValue = false;
jrkooncecba5d6c2019-08-29 11:26:22 -05003714#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003715 isBoolean = value.IsBool();
3716 if (isBoolean) {
3717 boolValue = value.GetBool();
3718 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003719#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003720 isBoolean = value.is_boolean();
3721 if (isBoolean) {
3722 boolValue = value.get<bool>();
3723 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003724#endif
3725 if (!isBoolean) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003726 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003727 if (err) {
3728 (*err) += "'" + property + "' property is not a bool type.\n";
3729 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003730 }
3731 return false;
3732 }
3733
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003734 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003735 (*ret) = boolValue;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003736 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003737
3738 return true;
3739}
3740
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02003741static bool ParseIntegerProperty(int *ret, std::string *err,
3742 const detail::json &o,
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003743 const std::string &property,
3744 const bool required,
3745 const std::string &parent_node = "") {
David03ad33c2023-02-15 23:35:51 -06003746 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06003747 if (!detail::FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003748 if (required) {
3749 if (err) {
3750 (*err) += "'" + property + "' property is missing";
3751 if (!parent_node.empty()) {
3752 (*err) += " in " + parent_node;
3753 }
3754 (*err) += ".\n";
3755 }
3756 }
3757 return false;
3758 }
3759
jrkooncecba5d6c2019-08-29 11:26:22 -05003760 int intValue;
David1f9a4b92023-02-15 22:56:18 -06003761 bool isInt = detail::GetInt(detail::GetValue(it), intValue);
jrkooncecba5d6c2019-08-29 11:26:22 -05003762 if (!isInt) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003763 if (required) {
3764 if (err) {
3765 (*err) += "'" + property + "' property is not an integer type.\n";
3766 }
3767 }
3768 return false;
3769 }
3770
3771 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003772 (*ret) = intValue;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003773 }
3774
3775 return true;
3776}
3777
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02003778static bool ParseUnsignedProperty(size_t *ret, std::string *err,
3779 const detail::json &o,
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003780 const std::string &property,
3781 const bool required,
3782 const std::string &parent_node = "") {
David03ad33c2023-02-15 23:35:51 -06003783 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06003784 if (!detail::FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003785 if (required) {
3786 if (err) {
3787 (*err) += "'" + property + "' property is missing";
3788 if (!parent_node.empty()) {
3789 (*err) += " in " + parent_node;
3790 }
3791 (*err) += ".\n";
3792 }
3793 }
3794 return false;
3795 }
3796
David1f9a4b92023-02-15 22:56:18 -06003797 auto &value = detail::GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003798
jrkoonce0d2b6ef2019-09-04 13:46:45 -05003799 size_t uValue = 0;
jrkooncecba5d6c2019-08-29 11:26:22 -05003800 bool isUValue;
3801#ifdef TINYGLTF_USE_RAPIDJSON
3802 isUValue = false;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003803 if (value.IsUint()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003804 uValue = value.GetUint();
3805 isUValue = true;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003806 } else if (value.IsUint64()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003807 uValue = value.GetUint64();
3808 isUValue = true;
3809 }
3810#else
3811 isUValue = value.is_number_unsigned();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003812 if (isUValue) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003813 uValue = value.get<size_t>();
3814 }
3815#endif
3816 if (!isUValue) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003817 if (required) {
3818 if (err) {
3819 (*err) += "'" + property + "' property is not a positive integer.\n";
3820 }
3821 }
3822 return false;
3823 }
3824
3825 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003826 (*ret) = uValue;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003827 }
3828
3829 return true;
3830}
3831
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02003832static bool ParseNumberProperty(double *ret, std::string *err,
3833 const detail::json &o,
Syoyo Fujita5b407452017-06-04 17:42:41 +09003834 const std::string &property,
3835 const bool required,
3836 const std::string &parent_node = "") {
David03ad33c2023-02-15 23:35:51 -06003837 detail::json_const_iterator it;
jrkooncecba5d6c2019-08-29 11:26:22 -05003838
David1f9a4b92023-02-15 22:56:18 -06003839 if (!detail::FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003840 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003841 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003842 (*err) += "'" + property + "' property is missing";
3843 if (!parent_node.empty()) {
3844 (*err) += " in " + parent_node;
3845 }
3846 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003847 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003848 }
3849 return false;
3850 }
3851
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003852 double numberValue;
David1f9a4b92023-02-15 22:56:18 -06003853 bool isNumber = detail::GetNumber(detail::GetValue(it), numberValue);
jrkooncecba5d6c2019-08-29 11:26:22 -05003854
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003855 if (!isNumber) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003856 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003857 if (err) {
3858 (*err) += "'" + property + "' property is not a number type.\n";
3859 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003860 }
3861 return false;
3862 }
3863
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003864 if (ret) {
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003865 (*ret) = numberValue;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003866 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003867
3868 return true;
3869}
3870
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003871static bool ParseNumberArrayProperty(std::vector<double> *ret, std::string *err,
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02003872 const detail::json &o,
3873 const std::string &property, bool required,
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003874 const std::string &parent_node = "") {
David03ad33c2023-02-15 23:35:51 -06003875 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06003876 if (!detail::FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003877 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003878 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003879 (*err) += "'" + property + "' property is missing";
3880 if (!parent_node.empty()) {
3881 (*err) += " in " + parent_node;
3882 }
3883 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003884 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003885 }
3886 return false;
3887 }
3888
David1f9a4b92023-02-15 22:56:18 -06003889 if (!detail::IsArray(detail::GetValue(it))) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003890 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003891 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003892 (*err) += "'" + property + "' property is not an array";
3893 if (!parent_node.empty()) {
3894 (*err) += " in " + parent_node;
3895 }
3896 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003897 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003898 }
3899 return false;
3900 }
3901
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003902 ret->clear();
David1f9a4b92023-02-15 22:56:18 -06003903 auto end = detail::ArrayEnd(detail::GetValue(it));
3904 for (auto i = detail::ArrayBegin(detail::GetValue(it)); i != end; ++i) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003905 double numberValue;
David1f9a4b92023-02-15 22:56:18 -06003906 const bool isNumber = detail::GetNumber(*i, numberValue);
jrkooncecba5d6c2019-08-29 11:26:22 -05003907 if (!isNumber) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003908 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003909 if (err) {
3910 (*err) += "'" + property + "' property is not a number.\n";
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003911 if (!parent_node.empty()) {
3912 (*err) += " in " + parent_node;
3913 }
3914 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003915 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003916 }
3917 return false;
3918 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003919 ret->push_back(numberValue);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003920 }
3921
3922 return true;
3923}
3924
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003925static bool ParseIntegerArrayProperty(std::vector<int> *ret, std::string *err,
David03ad33c2023-02-15 23:35:51 -06003926 const detail::json &o,
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003927 const std::string &property,
3928 bool required,
3929 const std::string &parent_node = "") {
David03ad33c2023-02-15 23:35:51 -06003930 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06003931 if (!detail::FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003932 if (required) {
3933 if (err) {
3934 (*err) += "'" + property + "' property is missing";
3935 if (!parent_node.empty()) {
3936 (*err) += " in " + parent_node;
3937 }
3938 (*err) += ".\n";
3939 }
3940 }
3941 return false;
3942 }
3943
David1f9a4b92023-02-15 22:56:18 -06003944 if (!detail::IsArray(detail::GetValue(it))) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003945 if (required) {
3946 if (err) {
3947 (*err) += "'" + property + "' property is not an array";
3948 if (!parent_node.empty()) {
3949 (*err) += " in " + parent_node;
3950 }
3951 (*err) += ".\n";
3952 }
3953 }
3954 return false;
3955 }
3956
3957 ret->clear();
David1f9a4b92023-02-15 22:56:18 -06003958 auto end = detail::ArrayEnd(detail::GetValue(it));
3959 for (auto i = detail::ArrayBegin(detail::GetValue(it)); i != end; ++i) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003960 int numberValue;
David1f9a4b92023-02-15 22:56:18 -06003961 bool isNumber = detail::GetInt(*i, numberValue);
jrkooncecba5d6c2019-08-29 11:26:22 -05003962 if (!isNumber) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003963 if (required) {
3964 if (err) {
3965 (*err) += "'" + property + "' property is not an integer type.\n";
3966 if (!parent_node.empty()) {
3967 (*err) += " in " + parent_node;
3968 }
3969 (*err) += ".\n";
3970 }
3971 }
3972 return false;
3973 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003974 ret->push_back(numberValue);
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003975 }
3976
3977 return true;
3978}
3979
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003980static bool ParseStringProperty(
David03ad33c2023-02-15 23:35:51 -06003981 std::string *ret, std::string *err, const detail::json &o,
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003982 const std::string &property, bool required,
3983 const std::string &parent_node = std::string()) {
David03ad33c2023-02-15 23:35:51 -06003984 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06003985 if (!detail::FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003986 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003987 if (err) {
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003988 (*err) += "'" + property + "' property is missing";
3989 if (parent_node.empty()) {
3990 (*err) += ".\n";
3991 } else {
3992 (*err) += " in `" + parent_node + "'.\n";
3993 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003994 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003995 }
3996 return false;
3997 }
3998
jrkooncecba5d6c2019-08-29 11:26:22 -05003999 std::string strValue;
David1f9a4b92023-02-15 22:56:18 -06004000 if (!detail::GetString(detail::GetValue(it), strValue)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004001 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004002 if (err) {
4003 (*err) += "'" + property + "' property is not a string type.\n";
4004 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004005 }
4006 return false;
4007 }
4008
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004009 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004010 (*ret) = std::move(strValue);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004011 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004012
4013 return true;
4014}
4015
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004016static bool ParseStringIntegerProperty(std::map<std::string, int> *ret,
David03ad33c2023-02-15 23:35:51 -06004017 std::string *err, const detail::json &o,
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004018 const std::string &property,
4019 bool required,
4020 const std::string &parent = "") {
David03ad33c2023-02-15 23:35:51 -06004021 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06004022 if (!detail::FindMember(o, property.c_str(), it)) {
Luke San Antonio19894c72016-06-14 21:19:51 -04004023 if (required) {
4024 if (err) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09004025 if (!parent.empty()) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004026 (*err) +=
4027 "'" + property + "' property is missing in " + parent + ".\n";
Syoyo Fujita57c10182017-10-19 18:48:26 +09004028 } else {
4029 (*err) += "'" + property + "' property is missing.\n";
4030 }
Luke San Antonio19894c72016-06-14 21:19:51 -04004031 }
4032 }
4033 return false;
4034 }
4035
David03ad33c2023-02-15 23:35:51 -06004036 const detail::json &dict = detail::GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05004037
Luke San Antonio19894c72016-06-14 21:19:51 -04004038 // Make sure we are dealing with an object / dictionary.
David1f9a4b92023-02-15 22:56:18 -06004039 if (!detail::IsObject(dict)) {
Luke San Antonio19894c72016-06-14 21:19:51 -04004040 if (required) {
4041 if (err) {
4042 (*err) += "'" + property + "' property is not an object.\n";
4043 }
4044 }
4045 return false;
4046 }
4047
4048 ret->clear();
Luke San Antonio19894c72016-06-14 21:19:51 -04004049
David03ad33c2023-02-15 23:35:51 -06004050 detail::json_const_iterator dictIt(detail::ObjectBegin(dict));
4051 detail::json_const_iterator dictItEnd(detail::ObjectEnd(dict));
Luke San Antonio19894c72016-06-14 21:19:51 -04004052
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09004053 for (; dictIt != dictItEnd; ++dictIt) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004054 int intVal;
David1f9a4b92023-02-15 22:56:18 -06004055 if (!detail::GetInt(detail::GetValue(dictIt), intVal)) {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09004056 if (required) {
4057 if (err) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004058 (*err) += "'" + property + "' value is not an integer type.\n";
Luke San Antonio19894c72016-06-14 21:19:51 -04004059 }
4060 }
4061 return false;
4062 }
4063
4064 // Insert into the list.
David1f9a4b92023-02-15 22:56:18 -06004065 (*ret)[detail::GetKey(dictIt)] = intVal;
Luke San Antonio19894c72016-06-14 21:19:51 -04004066 }
4067 return true;
4068}
4069
Syoyo Fujita5b407452017-06-04 17:42:41 +09004070static bool ParseJSONProperty(std::map<std::string, double> *ret,
David03ad33c2023-02-15 23:35:51 -06004071 std::string *err, const detail::json &o,
Syoyo Fujita5b407452017-06-04 17:42:41 +09004072 const std::string &property, bool required) {
David03ad33c2023-02-15 23:35:51 -06004073 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06004074 if (!detail::FindMember(o, property.c_str(), it)) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00004075 if (required) {
Syoyo Fujita5b407452017-06-04 17:42:41 +09004076 if (err) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00004077 (*err) += "'" + property + "' property is missing. \n'";
4078 }
4079 }
4080 return false;
4081 }
4082
David03ad33c2023-02-15 23:35:51 -06004083 const detail::json &obj = detail::GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05004084
David1f9a4b92023-02-15 22:56:18 -06004085 if (!detail::IsObject(obj)) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00004086 if (required) {
4087 if (err) {
4088 (*err) += "'" + property + "' property is not a JSON object.\n";
4089 }
4090 }
4091 return false;
4092 }
4093
4094 ret->clear();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004095
David03ad33c2023-02-15 23:35:51 -06004096 detail::json_const_iterator it2(detail::ObjectBegin(obj));
4097 detail::json_const_iterator itEnd(detail::ObjectEnd(obj));
jrkooncecba5d6c2019-08-29 11:26:22 -05004098 for (; it2 != itEnd; ++it2) {
4099 double numVal;
David1f9a4b92023-02-15 22:56:18 -06004100 if (detail::GetNumber(detail::GetValue(it2), numVal))
4101 ret->emplace(std::string(detail::GetKey(it2)), numVal);
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00004102 }
4103
4104 return true;
4105}
4106
Selmar09d2ff12018-03-15 17:30:42 +01004107static bool ParseParameterProperty(Parameter *param, std::string *err,
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004108 const detail::json &o,
4109 const std::string &prop, bool required) {
Selmar09d2ff12018-03-15 17:30:42 +01004110 // A parameter value can either be a string or an array of either a boolean or
4111 // a number. Booleans of any kind aren't supported here. Granted, it
4112 // complicates the Parameter structure and breaks it semantically in the sense
4113 // that the client probably works off the assumption that if the string is
4114 // empty the vector is used, etc. Would a tagged union work?
4115 if (ParseStringProperty(&param->string_value, err, o, prop, false)) {
4116 // Found string property.
4117 return true;
4118 } else if (ParseNumberArrayProperty(&param->number_array, err, o, prop,
4119 false)) {
4120 // Found a number array.
4121 return true;
Ben Buzbeef6af2242018-04-25 15:13:05 -07004122 } else if (ParseNumberProperty(&param->number_value, err, o, prop, false)) {
Marcin Kacprzakb12a54e2022-09-02 16:13:11 +02004123 param->has_number_value = true;
4124 return true;
Selmar09d2ff12018-03-15 17:30:42 +01004125 } else if (ParseJSONProperty(&param->json_double_value, err, o, prop,
4126 false)) {
4127 return true;
4128 } else if (ParseBooleanProperty(&param->bool_value, err, o, prop, false)) {
4129 return true;
4130 } else {
4131 if (required) {
4132 if (err) {
4133 (*err) += "parameter must be a string or number / number array.\n";
4134 }
4135 }
4136 return false;
4137 }
4138}
4139
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004140static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err,
David03ad33c2023-02-15 23:35:51 -06004141 const detail::json &o) {
Syoyo Fujita48f6db02018-04-15 18:40:55 +09004142 (void)err;
4143
David03ad33c2023-02-15 23:35:51 -06004144 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06004145 if (!detail::FindMember(o, "extensions", it)) {
Selmar09d2ff12018-03-15 17:30:42 +01004146 return false;
4147 }
jrkooncecba5d6c2019-08-29 11:26:22 -05004148
David1f9a4b92023-02-15 22:56:18 -06004149 auto &obj = detail::GetValue(it);
4150 if (!detail::IsObject(obj)) {
Selmar09d2ff12018-03-15 17:30:42 +01004151 return false;
4152 }
4153 ExtensionMap extensions;
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004154 detail::json_const_iterator extIt =
4155 detail::ObjectBegin(obj); // it.value().begin();
David03ad33c2023-02-15 23:35:51 -06004156 detail::json_const_iterator extEnd = detail::ObjectEnd(obj);
jrkooncecba5d6c2019-08-29 11:26:22 -05004157 for (; extIt != extEnd; ++extIt) {
David1f9a4b92023-02-15 22:56:18 -06004158 auto &itObj = detail::GetValue(extIt);
4159 if (!detail::IsObject(itObj)) continue;
4160 std::string key(detail::GetKey(extIt));
jrkooncecba5d6c2019-08-29 11:26:22 -05004161 if (!ParseJsonAsValue(&extensions[key], itObj)) {
jrkoonce06c30c42019-09-03 15:56:48 -05004162 if (!key.empty()) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004163 // create empty object so that an extension object is still of type
4164 // object
jrkooncecba5d6c2019-08-29 11:26:22 -05004165 extensions[key] = Value{Value::Object{}};
Selmar Kokee3d0662018-10-08 16:20:43 +02004166 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004167 }
Selmar09d2ff12018-03-15 17:30:42 +01004168 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004169 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004170 (*ret) = std::move(extensions);
Selmar09d2ff12018-03-15 17:30:42 +01004171 }
4172 return true;
4173}
4174
David Siegel22cafa12023-06-05 22:18:59 +02004175template <typename GltfType>
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004176static bool ParseExtrasAndExtensions(GltfType *target, std::string *err,
4177 const detail::json &o,
4178 bool store_json_strings) {
David Siegel22cafa12023-06-05 22:18:59 +02004179 ParseExtensionsProperty(&target->extensions, err, o);
4180 ParseExtrasProperty(&target->extras, o);
4181
4182 if (store_json_strings) {
4183 {
4184 detail::json_const_iterator it;
4185 if (detail::FindMember(o, "extensions", it)) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004186 target->extensions_json_string =
4187 detail::JsonToString(detail::GetValue(it));
David Siegel22cafa12023-06-05 22:18:59 +02004188 }
4189 }
4190 {
4191 detail::json_const_iterator it;
4192 if (detail::FindMember(o, "extras", it)) {
4193 target->extras_json_string = detail::JsonToString(detail::GetValue(it));
4194 }
4195 }
4196 }
4197 return true;
4198}
4199
David03ad33c2023-02-15 23:35:51 -06004200static bool ParseAsset(Asset *asset, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004201 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09004202 ParseStringProperty(&asset->version, err, o, "version", true, "Asset");
4203 ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset");
4204 ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset");
Miguel Sousa22bfc842020-02-20 14:16:58 +01004205 ParseStringProperty(&asset->copyright, err, o, "copyright", false, "Asset");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004206
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004207 ParseExtrasAndExtensions(asset, err, o,
4208 store_original_json_for_extras_and_extensions);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004209 return true;
4210}
4211
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004212static bool ParseImage(Image *image, const int image_idx, std::string *err,
David03ad33c2023-02-15 23:35:51 -06004213 std::string *warn, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004214 bool store_original_json_for_extras_and_extensions,
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004215 const std::string &basedir, const size_t max_file_size,
4216 FsCallbacks *fs, const URICallbacks *uri_cb,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004217 LoadImageDataFunction *LoadImageData = nullptr,
Paolo Jovone6601bf2018-07-07 20:43:33 +02004218 void *load_image_user_data = nullptr) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004219 // A glTF image must either reference a bufferView or an image uri
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004220
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004221 // schema says oneOf [`bufferView`, `uri`]
4222 // TODO(syoyo): Check the type of each parameters.
David03ad33c2023-02-15 23:35:51 -06004223 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06004224 bool hasBufferView = detail::FindMember(o, "bufferView", it);
4225 bool hasURI = detail::FindMember(o, "uri", it);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004226
Syoyo Fujitaaf3ebb22019-01-06 18:55:57 +09004227 ParseStringProperty(&image->name, err, o, "name", false);
4228
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004229 if (hasBufferView && hasURI) {
4230 // Should not both defined.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004231 if (err) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09004232 (*err) +=
4233 "Only one of `bufferView` or `uri` should be defined, but both are "
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004234 "defined for image[" +
4235 std::to_string(image_idx) + "] name = \"" + image->name + "\"\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004236 }
4237 return false;
4238 }
4239
4240 if (!hasBufferView && !hasURI) {
4241 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004242 (*err) += "Neither required `bufferView` nor `uri` defined for image[" +
4243 std::to_string(image_idx) + "] name = \"" + image->name +
4244 "\"\n";
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004245 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004246 return false;
4247 }
4248
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004249 ParseExtrasAndExtensions(image, err, o,
4250 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004251
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004252 if (hasBufferView) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004253 int bufferView = -1;
4254 if (!ParseIntegerProperty(&bufferView, err, o, "bufferView", true)) {
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004255 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004256 (*err) += "Failed to parse `bufferView` for image[" +
4257 std::to_string(image_idx) + "] name = \"" + image->name +
4258 "\"\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004259 }
4260 return false;
4261 }
4262
4263 std::string mime_type;
4264 ParseStringProperty(&mime_type, err, o, "mimeType", false);
4265
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004266 int width = 0;
4267 ParseIntegerProperty(&width, err, o, "width", false);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004268
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004269 int height = 0;
4270 ParseIntegerProperty(&height, err, o, "height", false);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004271
4272 // Just only save some information here. Loading actual image data from
4273 // bufferView is done after this `ParseImage` function.
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004274 image->bufferView = bufferView;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004275 image->mimeType = mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004276 image->width = width;
4277 image->height = height;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004278
4279 return true;
4280 }
4281
Syoyo Fujita246654a2018-03-21 20:32:22 +09004282 // Parse URI & Load image data.
4283
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004284 std::string uri;
4285 std::string tmp_err;
Syoyo Fujita246654a2018-03-21 20:32:22 +09004286 if (!ParseStringProperty(&uri, &tmp_err, o, "uri", true)) {
4287 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004288 (*err) += "Failed to parse `uri` for image[" + std::to_string(image_idx) +
4289 "] name = \"" + image->name + "\".\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004290 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09004291 return false;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004292 }
4293
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004294 std::vector<unsigned char> img;
Syoyo Fujitabeded612016-05-01 20:03:43 +09004295
Syoyo Fujita246654a2018-03-21 20:32:22 +09004296 if (IsDataURI(uri)) {
johan bowald642a3432018-04-01 12:37:18 +02004297 if (!DecodeDataURI(&img, image->mimeType, uri, 0, false)) {
Syoyo Fujita246654a2018-03-21 20:32:22 +09004298 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004299 (*err) += "Failed to decode 'uri' for image[" +
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09004300 std::to_string(image_idx) + "] name = \"" + image->name +
4301 "\"\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004302 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09004303 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004304 }
4305 } else {
Syoyo Fujita246654a2018-03-21 20:32:22 +09004306 // Assume external file
4307 // Keep texture path (for textures that cannot be decoded)
4308 image->uri = uri;
Selmar67af3c92018-03-16 11:48:19 +01004309#ifdef TINYGLTF_NO_EXTERNAL_IMAGE
Syoyo Fujita246654a2018-03-21 20:32:22 +09004310 return true;
AlvaroBarua43172232022-09-11 00:41:43 +01004311#else
Pyarelal Knowles385946d2023-01-03 18:18:04 -08004312 std::string decoded_uri;
4313 if (!uri_cb->decode(uri, &decoded_uri, uri_cb->user_data)) {
4314 if (warn) {
4315 (*warn) += "Failed to decode 'uri' for image[" +
Syoyo Fujita877d8562023-04-23 21:47:31 +09004316 std::to_string(image_idx) + "] name = \"" + image->name +
4317 "\"\n";
Pyarelal Knowles385946d2023-01-03 18:18:04 -08004318 }
4319
4320 // Image loading failure is not critical to overall gltf loading.
4321 return true;
4322 }
4323
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004324 if (!LoadExternalFile(&img, err, warn, decoded_uri, basedir,
4325 /* required */ false, /* required bytes */ 0,
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004326 /* checksize */ false,
4327 /* max file size */ max_file_size, fs)) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004328 if (warn) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004329 (*warn) += "Failed to load external 'uri' for image[" +
Syoyo Fujita877d8562023-04-23 21:47:31 +09004330 std::to_string(image_idx) + "] name = \"" + decoded_uri +
4331 "\"\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004332 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09004333 // If the image cannot be loaded, keep uri as image->uri.
4334 return true;
4335 }
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004336
Syoyo Fujita246654a2018-03-21 20:32:22 +09004337 if (img.empty()) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004338 if (warn) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004339 (*warn) += "Image data is empty for image[" +
Syoyo Fujita877d8562023-04-23 21:47:31 +09004340 std::to_string(image_idx) + "] name = \"" + image->name +
4341 "\" \n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09004342 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09004343 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004344 }
AlvaroBarua43172232022-09-11 00:41:43 +01004345#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004346 }
4347
Squareysff644d82018-03-13 22:36:18 +01004348 if (*LoadImageData == nullptr) {
4349 if (err) {
4350 (*err) += "No LoadImageData callback specified.\n";
4351 }
4352 return false;
4353 }
Syoyo Fujitaaf3ebb22019-01-06 18:55:57 +09004354 return (*LoadImageData)(image, image_idx, err, warn, 0, 0, &img.at(0),
Paolo Jovone6601bf2018-07-07 20:43:33 +02004355 static_cast<int>(img.size()), load_image_user_data);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004356}
4357
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004358static bool ParseTexture(Texture *texture, std::string *err,
4359 const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004360 bool store_original_json_for_extras_and_extensions,
Syoyo Fujitabeded612016-05-01 20:03:43 +09004361 const std::string &basedir) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004362 (void)basedir;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004363 int sampler = -1;
4364 int source = -1;
4365 ParseIntegerProperty(&sampler, err, o, "sampler", false);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004366
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004367 ParseIntegerProperty(&source, err, o, "source", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09004368
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004369 texture->sampler = sampler;
4370 texture->source = source;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004371
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004372 ParseExtrasAndExtensions(texture, err, o,
4373 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004374
Vladimír Vondruš239be2c2018-07-24 23:23:56 +02004375 ParseStringProperty(&texture->name, err, o, "name", false);
4376
Syoyo Fujitabde70212016-02-07 17:38:17 +09004377 return true;
4378}
4379
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004380static bool ParseTextureInfo(
David03ad33c2023-02-15 23:35:51 -06004381 TextureInfo *texinfo, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004382 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004383 if (texinfo == nullptr) {
4384 return false;
4385 }
4386
4387 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
4388 /* required */ true, "TextureInfo")) {
4389 return false;
4390 }
4391
4392 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
4393
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004394 ParseExtrasAndExtensions(texinfo, err, o,
4395 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004396
Syoyo Fujita046400b2019-07-24 19:26:48 +09004397 return true;
4398}
4399
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004400static bool ParseNormalTextureInfo(
David03ad33c2023-02-15 23:35:51 -06004401 NormalTextureInfo *texinfo, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004402 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004403 if (texinfo == nullptr) {
4404 return false;
4405 }
4406
4407 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
4408 /* required */ true, "NormalTextureInfo")) {
4409 return false;
4410 }
4411
4412 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
4413 ParseNumberProperty(&texinfo->scale, err, o, "scale", false);
4414
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004415 ParseExtrasAndExtensions(texinfo, err, o,
4416 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004417
Syoyo Fujita046400b2019-07-24 19:26:48 +09004418 return true;
4419}
4420
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004421static bool ParseOcclusionTextureInfo(
David03ad33c2023-02-15 23:35:51 -06004422 OcclusionTextureInfo *texinfo, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004423 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004424 if (texinfo == nullptr) {
4425 return false;
4426 }
4427
4428 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
4429 /* required */ true, "NormalTextureInfo")) {
4430 return false;
4431 }
4432
4433 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
4434 ParseNumberProperty(&texinfo->strength, err, o, "strength", false);
4435
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004436 ParseExtrasAndExtensions(texinfo, err, o,
4437 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004438
Syoyo Fujita046400b2019-07-24 19:26:48 +09004439 return true;
4440}
4441
David03ad33c2023-02-15 23:35:51 -06004442static bool ParseBuffer(Buffer *buffer, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004443 bool store_original_json_for_extras_and_extensions,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08004444 FsCallbacks *fs, const URICallbacks *uri_cb,
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004445 const std::string &basedir,
4446 const size_t max_buffer_size, bool is_binary = false,
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09004447 const unsigned char *bin_data = nullptr,
Syoyo Fujitabeded612016-05-01 20:03:43 +09004448 size_t bin_size = 0) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004449 size_t byteLength;
4450 if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true,
4451 "Buffer")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004452 return false;
4453 }
4454
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004455 // In glTF 2.0, uri is not mandatory anymore
Syoyo Fujita20244e12018-03-15 11:01:05 -05004456 buffer->uri.clear();
4457 ParseStringProperty(&buffer->uri, err, o, "uri", false, "Buffer");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004458
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004459 // having an empty uri for a non embedded image should not be valid
Syoyo Fujita20244e12018-03-15 11:01:05 -05004460 if (!is_binary && buffer->uri.empty()) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004461 if (err) {
4462 (*err) += "'uri' is missing from non binary glTF file buffer.\n";
4463 }
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004464 }
4465
David03ad33c2023-02-15 23:35:51 -06004466 detail::json_const_iterator type;
David1f9a4b92023-02-15 22:56:18 -06004467 if (detail::FindMember(o, "type", type)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004468 std::string typeStr;
David1f9a4b92023-02-15 22:56:18 -06004469 if (detail::GetString(detail::GetValue(type), typeStr)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004470 if (typeStr.compare("arraybuffer") == 0) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004471 // buffer.type = "arraybuffer";
4472 }
4473 }
4474 }
4475
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004476 if (is_binary) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004477 // Still binary glTF accepts external dataURI.
Syoyo Fujita20244e12018-03-15 11:01:05 -05004478 if (!buffer->uri.empty()) {
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004479 // First try embedded data URI.
4480 if (IsDataURI(buffer->uri)) {
4481 std::string mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004482 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004483 true)) {
4484 if (err) {
4485 (*err) +=
4486 "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
4487 }
4488 return false;
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004489 }
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004490 } else {
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004491 // External .bin file.
Pyarelal Knowles385946d2023-01-03 18:18:04 -08004492 std::string decoded_uri;
4493 if (!uri_cb->decode(buffer->uri, &decoded_uri, uri_cb->user_data)) {
4494 return false;
4495 }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09004496 if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr,
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004497 decoded_uri, basedir, /* required */ true,
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004498 byteLength, /* checkSize */ true,
4499 /* max_file_size */ max_buffer_size, fs)) {
Vladimír Vondrušfd84ceb2018-08-16 21:07:56 +02004500 return false;
4501 }
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004502 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004503 } else {
Syoyo Fujitabeded612016-05-01 20:03:43 +09004504 // load data from (embedded) binary data
4505
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09004506 if ((bin_size == 0) || (bin_data == nullptr)) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09004507 if (err) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004508 (*err) +=
4509 "Invalid binary data in `Buffer', or GLB with empty BIN chunk.\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09004510 }
4511 return false;
4512 }
4513
4514 if (byteLength > bin_size) {
4515 if (err) {
4516 std::stringstream ss;
4517 ss << "Invalid `byteLength'. Must be equal or less than binary size: "
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004518 "`byteLength' = "
4519 << byteLength << ", binary size = " << bin_size << std::endl;
Syoyo Fujitabeded612016-05-01 20:03:43 +09004520 (*err) += ss.str();
4521 }
4522 return false;
4523 }
4524
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004525 // Read buffer data
4526 buffer->data.resize(static_cast<size_t>(byteLength));
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004527 memcpy(&(buffer->data.at(0)), bin_data, static_cast<size_t>(byteLength));
Syoyo Fujitabeded612016-05-01 20:03:43 +09004528 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004529
4530 } else {
Syoyo Fujita20244e12018-03-15 11:01:05 -05004531 if (IsDataURI(buffer->uri)) {
johan bowaldef151a42018-04-01 13:44:48 +02004532 std::string mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004533 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
4534 true)) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004535 if (err) {
Syoyo Fujita20244e12018-03-15 11:01:05 -05004536 (*err) += "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004537 }
4538 return false;
4539 }
4540 } else {
4541 // Assume external .bin file.
Pyarelal Knowles385946d2023-01-03 18:18:04 -08004542 std::string decoded_uri;
4543 if (!uri_cb->decode(buffer->uri, &decoded_uri, uri_cb->user_data)) {
4544 return false;
4545 }
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004546 if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, decoded_uri,
4547 basedir, /* required */ true, byteLength,
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004548 /* checkSize */ true,
4549 /* max file size */ max_buffer_size, fs)) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004550 return false;
4551 }
4552 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004553 }
4554
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004555 ParseStringProperty(&buffer->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004556
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004557 ParseExtrasAndExtensions(buffer, err, o,
4558 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004559
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004560 return true;
4561}
4562
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004563static bool ParseBufferView(
David03ad33c2023-02-15 23:35:51 -06004564 BufferView *bufferView, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004565 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004566 int buffer = -1;
4567 if (!ParseIntegerProperty(&buffer, err, o, "buffer", true, "BufferView")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004568 return false;
4569 }
4570
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004571 size_t byteOffset = 0;
4572 ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004573
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004574 size_t byteLength = 1;
4575 if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true,
4576 "BufferView")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004577 return false;
4578 }
4579
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004580 size_t byteStride = 0;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004581 if (!ParseUnsignedProperty(&byteStride, err, o, "byteStride", false)) {
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004582 // Spec says: When byteStride of referenced bufferView is not defined, it
4583 // means that accessor elements are tightly packed, i.e., effective stride
4584 // equals the size of the element.
4585 // We cannot determine the actual byteStride until Accessor are parsed, thus
4586 // set 0(= tightly packed) here(as done in OpenGL's VertexAttribPoiner)
4587 byteStride = 0;
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004588 }
4589
4590 if ((byteStride > 252) || ((byteStride % 4) != 0)) {
4591 if (err) {
4592 std::stringstream ss;
4593 ss << "Invalid `byteStride' value. `byteStride' must be the multiple of "
4594 "4 : "
4595 << byteStride << std::endl;
4596
4597 (*err) += ss.str();
4598 }
4599 return false;
4600 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004601
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004602 int target = 0;
4603 ParseIntegerProperty(&target, err, o, "target", false);
4604 if ((target == TINYGLTF_TARGET_ARRAY_BUFFER) ||
4605 (target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004606 // OK
4607 } else {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004608 target = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004609 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004610 bufferView->target = target;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004611
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004612 ParseStringProperty(&bufferView->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004613
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004614 ParseExtrasAndExtensions(bufferView, err, o,
4615 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004616
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004617 bufferView->buffer = buffer;
4618 bufferView->byteOffset = byteOffset;
4619 bufferView->byteLength = byteLength;
4620 bufferView->byteStride = byteStride;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004621 return true;
4622}
4623
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004624static bool ParseSparseAccessor(
4625 Accessor::Sparse *sparse, std::string *err, const detail::json &o,
4626 bool store_original_json_for_extras_and_extensions) {
David Siegeld852f502023-06-05 23:28:05 +02004627 sparse->isSparse = true;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004628
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004629 int count = 0;
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004630 if (!ParseIntegerProperty(&count, err, o, "count", true, "SparseAccessor")) {
4631 return false;
4632 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004633
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004634 ParseExtrasAndExtensions(sparse, err, o,
4635 store_original_json_for_extras_and_extensions);
David Siegeld852f502023-06-05 23:28:05 +02004636
David03ad33c2023-02-15 23:35:51 -06004637 detail::json_const_iterator indices_iterator;
4638 detail::json_const_iterator values_iterator;
David1f9a4b92023-02-15 22:56:18 -06004639 if (!detail::FindMember(o, "indices", indices_iterator)) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004640 (*err) = "the sparse object of this accessor doesn't have indices";
4641 return false;
4642 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004643
David1f9a4b92023-02-15 22:56:18 -06004644 if (!detail::FindMember(o, "values", values_iterator)) {
imallettd9ce9eb2022-10-07 10:37:09 -07004645 (*err) = "the sparse object of this accessor doesn't have values";
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004646 return false;
4647 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004648
David03ad33c2023-02-15 23:35:51 -06004649 const detail::json &indices_obj = detail::GetValue(indices_iterator);
4650 const detail::json &values_obj = detail::GetValue(values_iterator);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004651
emimvi759976e2023-09-02 09:39:53 +02004652 int indices_buffer_view = 0, component_type = 0;
4653 size_t indices_byte_offset = 0;
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09004654 if (!ParseIntegerProperty(&indices_buffer_view, err, indices_obj,
4655 "bufferView", true, "SparseAccessor")) {
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004656 return false;
4657 }
emimvi759976e2023-09-02 09:39:53 +02004658 ParseUnsignedProperty(&indices_byte_offset, err, indices_obj, "byteOffset",
Jack Mousseau94715172022-02-24 14:25:37 -08004659 false);
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004660 if (!ParseIntegerProperty(&component_type, err, indices_obj, "componentType",
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09004661 true, "SparseAccessor")) {
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004662 return false;
4663 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004664
emimvi759976e2023-09-02 09:39:53 +02004665 int values_buffer_view = 0;
4666 size_t values_byte_offset = 0;
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004667 if (!ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView",
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09004668 true, "SparseAccessor")) {
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004669 return false;
4670 }
emimvi759976e2023-09-02 09:39:53 +02004671 ParseUnsignedProperty(&values_byte_offset, err, values_obj, "byteOffset",
Jack Mousseau94715172022-02-24 14:25:37 -08004672 false);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004673
David Siegeld852f502023-06-05 23:28:05 +02004674 sparse->count = count;
4675 sparse->indices.bufferView = indices_buffer_view;
4676 sparse->indices.byteOffset = indices_byte_offset;
4677 sparse->indices.componentType = component_type;
4678 ParseExtrasAndExtensions(&sparse->indices, err, indices_obj,
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004679 store_original_json_for_extras_and_extensions);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004680
David Siegeld852f502023-06-05 23:28:05 +02004681 sparse->values.bufferView = values_buffer_view;
4682 sparse->values.byteOffset = values_byte_offset;
4683 ParseExtrasAndExtensions(&sparse->values, err, values_obj,
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004684 store_original_json_for_extras_and_extensions);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004685
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004686 return true;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004687}
4688
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004689static bool ParseAccessor(Accessor *accessor, std::string *err,
4690 const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004691 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004692 int bufferView = -1;
4693 ParseIntegerProperty(&bufferView, err, o, "bufferView", false, "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004694
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004695 size_t byteOffset = 0;
4696 ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false, "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004697
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004698 bool normalized = false;
4699 ParseBooleanProperty(&normalized, err, o, "normalized", false, "Accessor");
4700
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004701 size_t componentType = 0;
4702 if (!ParseUnsignedProperty(&componentType, err, o, "componentType", true,
4703 "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004704 return false;
4705 }
4706
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004707 size_t count = 0;
4708 if (!ParseUnsignedProperty(&count, err, o, "count", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004709 return false;
4710 }
4711
4712 std::string type;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09004713 if (!ParseStringProperty(&type, err, o, "type", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004714 return false;
4715 }
4716
4717 if (type.compare("SCALAR") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004718 accessor->type = TINYGLTF_TYPE_SCALAR;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004719 } else if (type.compare("VEC2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004720 accessor->type = TINYGLTF_TYPE_VEC2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004721 } else if (type.compare("VEC3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004722 accessor->type = TINYGLTF_TYPE_VEC3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004723 } else if (type.compare("VEC4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004724 accessor->type = TINYGLTF_TYPE_VEC4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004725 } else if (type.compare("MAT2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004726 accessor->type = TINYGLTF_TYPE_MAT2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004727 } else if (type.compare("MAT3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004728 accessor->type = TINYGLTF_TYPE_MAT3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004729 } else if (type.compare("MAT4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004730 accessor->type = TINYGLTF_TYPE_MAT4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004731 } else {
4732 std::stringstream ss;
4733 ss << "Unsupported `type` for accessor object. Got \"" << type << "\"\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004734 if (err) {
4735 (*err) += ss.str();
4736 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004737 return false;
4738 }
4739
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004740 ParseStringProperty(&accessor->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004741
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004742 accessor->minValues.clear();
4743 accessor->maxValues.clear();
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004744 ParseNumberArrayProperty(&accessor->minValues, err, o, "min", false,
4745 "Accessor");
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004746
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004747 ParseNumberArrayProperty(&accessor->maxValues, err, o, "max", false,
4748 "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004749
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004750 accessor->count = count;
4751 accessor->bufferView = bufferView;
4752 accessor->byteOffset = byteOffset;
jianghaosen4748ad62017-08-29 20:08:37 +08004753 accessor->normalized = normalized;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004754 {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004755 if (componentType >= TINYGLTF_COMPONENT_TYPE_BYTE &&
4756 componentType <= TINYGLTF_COMPONENT_TYPE_DOUBLE) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004757 // OK
Arthur Brainville (Ybalrid)811e1d32019-06-15 07:32:38 +02004758 accessor->componentType = int(componentType);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004759 } else {
4760 std::stringstream ss;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004761 ss << "Invalid `componentType` in accessor. Got " << componentType
4762 << "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004763 if (err) {
4764 (*err) += ss.str();
4765 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004766 return false;
4767 }
4768 }
4769
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004770 ParseExtrasAndExtensions(accessor, err, o,
4771 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004772
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004773 // check if accessor has a "sparse" object:
David03ad33c2023-02-15 23:35:51 -06004774 detail::json_const_iterator iterator;
David1f9a4b92023-02-15 22:56:18 -06004775 if (detail::FindMember(o, "sparse", iterator)) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004776 // here this accessor has a "sparse" subobject
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02004777 return ParseSparseAccessor(&accessor->sparse, err,
4778 detail::GetValue(iterator),
4779 store_original_json_for_extras_and_extensions);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004780 }
4781
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004782 return true;
4783}
4784
Alex Wood7319db72019-01-24 15:38:16 -05004785#ifdef TINYGLTF_ENABLE_DRACO
4786
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004787static void DecodeIndexBuffer(draco::Mesh *mesh, size_t componentSize,
4788 std::vector<uint8_t> &outBuffer) {
4789 if (componentSize == 4) {
Alex Wood7319db72019-01-24 15:38:16 -05004790 assert(sizeof(mesh->face(draco::FaceIndex(0))[0]) == componentSize);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004791 memcpy(outBuffer.data(), &mesh->face(draco::FaceIndex(0))[0],
4792 outBuffer.size());
4793 } else {
Alex Wood7319db72019-01-24 15:38:16 -05004794 size_t faceStride = componentSize * 3;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004795 for (draco::FaceIndex f(0); f < mesh->num_faces(); ++f) {
4796 const draco::Mesh::Face &face = mesh->face(f);
4797 if (componentSize == 2) {
4798 uint16_t indices[3] = {(uint16_t)face[0].value(),
4799 (uint16_t)face[1].value(),
4800 (uint16_t)face[2].value()};
4801 memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
4802 faceStride);
4803 } else {
4804 uint8_t indices[3] = {(uint8_t)face[0].value(),
4805 (uint8_t)face[1].value(),
4806 (uint8_t)face[2].value()};
4807 memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
4808 faceStride);
Alex Wood7319db72019-01-24 15:38:16 -05004809 }
4810 }
4811 }
4812}
4813
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004814template <typename T>
4815static bool GetAttributeForAllPoints(draco::Mesh *mesh,
4816 const draco::PointAttribute *pAttribute,
4817 std::vector<uint8_t> &outBuffer) {
Alex Wood7319db72019-01-24 15:38:16 -05004818 size_t byteOffset = 0;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004819 T values[4] = {0, 0, 0, 0};
4820 for (draco::PointIndex i(0); i < mesh->num_points(); ++i) {
Alex Wood7319db72019-01-24 15:38:16 -05004821 const draco::AttributeValueIndex val_index = pAttribute->mapped_index(i);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004822 if (!pAttribute->ConvertValue<T>(val_index, pAttribute->num_components(),
4823 values))
Alex Wood7319db72019-01-24 15:38:16 -05004824 return false;
4825
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004826 memcpy(outBuffer.data() + byteOffset, &values[0],
4827 sizeof(T) * pAttribute->num_components());
Alex Wood7319db72019-01-24 15:38:16 -05004828 byteOffset += sizeof(T) * pAttribute->num_components();
4829 }
4830
4831 return true;
4832}
4833
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004834static bool GetAttributeForAllPoints(uint32_t componentType, draco::Mesh *mesh,
4835 const draco::PointAttribute *pAttribute,
4836 std::vector<uint8_t> &outBuffer) {
Alex Wood7319db72019-01-24 15:38:16 -05004837 bool decodeResult = false;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004838 switch (componentType) {
4839 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
4840 decodeResult =
4841 GetAttributeForAllPoints<uint8_t>(mesh, pAttribute, outBuffer);
4842 break;
4843 case TINYGLTF_COMPONENT_TYPE_BYTE:
4844 decodeResult =
4845 GetAttributeForAllPoints<int8_t>(mesh, pAttribute, outBuffer);
4846 break;
4847 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
4848 decodeResult =
4849 GetAttributeForAllPoints<uint16_t>(mesh, pAttribute, outBuffer);
4850 break;
4851 case TINYGLTF_COMPONENT_TYPE_SHORT:
4852 decodeResult =
4853 GetAttributeForAllPoints<int16_t>(mesh, pAttribute, outBuffer);
4854 break;
4855 case TINYGLTF_COMPONENT_TYPE_INT:
4856 decodeResult =
4857 GetAttributeForAllPoints<int32_t>(mesh, pAttribute, outBuffer);
4858 break;
4859 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
4860 decodeResult =
4861 GetAttributeForAllPoints<uint32_t>(mesh, pAttribute, outBuffer);
4862 break;
4863 case TINYGLTF_COMPONENT_TYPE_FLOAT:
4864 decodeResult =
4865 GetAttributeForAllPoints<float>(mesh, pAttribute, outBuffer);
4866 break;
4867 case TINYGLTF_COMPONENT_TYPE_DOUBLE:
4868 decodeResult =
4869 GetAttributeForAllPoints<double>(mesh, pAttribute, outBuffer);
4870 break;
4871 default:
4872 return false;
Alex Wood7319db72019-01-24 15:38:16 -05004873 }
4874
4875 return decodeResult;
4876}
4877
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004878static bool ParseDracoExtension(Primitive *primitive, Model *model,
Nyall Dawson6e3d6662023-08-24 13:25:34 +10004879 std::string *err, std::string *warn,
4880 const Value &dracoExtensionValue,
4881 ParseStrictness strictness) {
snowapril84e15262021-03-20 19:25:58 +09004882 (void)err;
Alex Wood7319db72019-01-24 15:38:16 -05004883 auto bufferViewValue = dracoExtensionValue.Get("bufferView");
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004884 if (!bufferViewValue.IsInt()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004885 auto attributesValue = dracoExtensionValue.Get("attributes");
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004886 if (!attributesValue.IsObject()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004887
4888 auto attributesObject = attributesValue.Get<Value::Object>();
4889 int bufferView = bufferViewValue.Get<int>();
4890
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004891 BufferView &view = model->bufferViews[bufferView];
4892 Buffer &buffer = model->buffers[view.buffer];
Alex Wood7319db72019-01-24 15:38:16 -05004893 // BufferView has already been decoded
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004894 if (view.dracoDecoded) return true;
Alex Wood7319db72019-01-24 15:38:16 -05004895 view.dracoDecoded = true;
4896
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004897 const char *bufferViewData =
4898 reinterpret_cast<const char *>(buffer.data.data() + view.byteOffset);
Alex Wood7319db72019-01-24 15:38:16 -05004899 size_t bufferViewSize = view.byteLength;
4900
4901 // decode draco
4902 draco::DecoderBuffer decoderBuffer;
4903 decoderBuffer.Init(bufferViewData, bufferViewSize);
4904 draco::Decoder decoder;
4905 auto decodeResult = decoder.DecodeMeshFromBuffer(&decoderBuffer);
4906 if (!decodeResult.ok()) {
4907 return false;
4908 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004909 const std::unique_ptr<draco::Mesh> &mesh = decodeResult.value();
Alex Wood7319db72019-01-24 15:38:16 -05004910
4911 // create new bufferView for indices
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004912 if (primitive->indices >= 0) {
Nyall Dawson0067b4d2023-09-07 08:20:28 +10004913 if (strictness == ParseStrictness::Permissive) {
Nyall Dawson6e3d6662023-08-24 13:25:34 +10004914 const draco::PointIndex::ValueType numPoint = mesh->num_points();
4915 // handle the situation where the stored component type does not match the
4916 // required type for the actual number of stored points
4917 int supposedComponentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
4918 if (numPoint < static_cast<draco::PointIndex::ValueType>(
4919 std::numeric_limits<uint8_t>::max())) {
4920 supposedComponentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
4921 } else if (
4922 numPoint < static_cast<draco::PointIndex::ValueType>(
4923 std::numeric_limits<uint16_t>::max())) {
4924 supposedComponentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT;
4925 } else {
4926 supposedComponentType = TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT;
4927 }
4928
4929 if (supposedComponentType > model->accessors[primitive->indices].componentType) {
Nyall Dawson6e3d6662023-08-24 13:25:34 +10004930 if (warn) {
4931 (*warn) +=
4932 "GLTF component type " + std::to_string(model->accessors[primitive->indices].componentType) +
4933 " is not sufficient for number of stored points,"
4934 " treating as " + std::to_string(supposedComponentType) + "\n";
4935 }
Nyall Dawsonc35819f2023-09-12 08:46:02 +10004936 model->accessors[primitive->indices].componentType = supposedComponentType;
Nyall Dawson6e3d6662023-08-24 13:25:34 +10004937 }
4938 }
4939
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004940 int32_t componentSize = GetComponentSizeInBytes(
4941 model->accessors[primitive->indices].componentType);
Alex Wood7319db72019-01-24 15:38:16 -05004942 Buffer decodedIndexBuffer;
4943 decodedIndexBuffer.data.resize(mesh->num_faces() * 3 * componentSize);
4944
4945 DecodeIndexBuffer(mesh.get(), componentSize, decodedIndexBuffer.data);
4946
4947 model->buffers.emplace_back(std::move(decodedIndexBuffer));
4948
4949 BufferView decodedIndexBufferView;
4950 decodedIndexBufferView.buffer = int(model->buffers.size() - 1);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004951 decodedIndexBufferView.byteLength =
4952 int(mesh->num_faces() * 3 * componentSize);
Alex Wood7319db72019-01-24 15:38:16 -05004953 decodedIndexBufferView.byteOffset = 0;
4954 decodedIndexBufferView.byteStride = 0;
4955 decodedIndexBufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER;
4956 model->bufferViews.emplace_back(std::move(decodedIndexBufferView));
4957
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004958 model->accessors[primitive->indices].bufferView =
4959 int(model->bufferViews.size() - 1);
Alexander Wood0d77a292019-01-26 08:58:45 -05004960 model->accessors[primitive->indices].count = int(mesh->num_faces() * 3);
Alex Wood7319db72019-01-24 15:38:16 -05004961 }
4962
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004963 for (const auto &attribute : attributesObject) {
4964 if (!attribute.second.IsInt()) return false;
Alexander Wood0d77a292019-01-26 08:58:45 -05004965 auto primitiveAttribute = primitive->attributes.find(attribute.first);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004966 if (primitiveAttribute == primitive->attributes.end()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004967
4968 int dracoAttributeIndex = attribute.second.Get<int>();
4969 const auto pAttribute = mesh->GetAttributeByUniqueId(dracoAttributeIndex);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004970 const auto componentType =
4971 model->accessors[primitiveAttribute->second].componentType;
Alex Wood7319db72019-01-24 15:38:16 -05004972
4973 // Create a new buffer for this decoded buffer
4974 Buffer decodedBuffer;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004975 size_t bufferSize = mesh->num_points() * pAttribute->num_components() *
4976 GetComponentSizeInBytes(componentType);
Alex Wood7319db72019-01-24 15:38:16 -05004977 decodedBuffer.data.resize(bufferSize);
4978
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004979 if (!GetAttributeForAllPoints(componentType, mesh.get(), pAttribute,
4980 decodedBuffer.data))
Alex Wood7319db72019-01-24 15:38:16 -05004981 return false;
4982
4983 model->buffers.emplace_back(std::move(decodedBuffer));
4984
4985 BufferView decodedBufferView;
4986 decodedBufferView.buffer = int(model->buffers.size() - 1);
4987 decodedBufferView.byteLength = bufferSize;
4988 decodedBufferView.byteOffset = pAttribute->byte_offset();
4989 decodedBufferView.byteStride = pAttribute->byte_stride();
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004990 decodedBufferView.target = primitive->indices >= 0
4991 ? TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER
4992 : TINYGLTF_TARGET_ARRAY_BUFFER;
Alex Wood7319db72019-01-24 15:38:16 -05004993 model->bufferViews.emplace_back(std::move(decodedBufferView));
4994
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004995 model->accessors[primitiveAttribute->second].bufferView =
4996 int(model->bufferViews.size() - 1);
4997 model->accessors[primitiveAttribute->second].count =
4998 int(mesh->num_points());
Alex Wood7319db72019-01-24 15:38:16 -05004999 }
5000
5001 return true;
5002}
5003#endif
5004
Nyall Dawson6e3d6662023-08-24 13:25:34 +10005005static bool ParsePrimitive(Primitive *primitive, Model *model,
5006 std::string *err, std::string *warn,
David03ad33c2023-02-15 23:35:51 -06005007 const detail::json &o,
Nyall Dawson6e3d6662023-08-24 13:25:34 +10005008 bool store_original_json_for_extras_and_extensions,
5009 ParseStrictness strictness) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005010 int material = -1;
5011 ParseIntegerProperty(&material, err, o, "material", false);
5012 primitive->material = material;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005013
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005014 int mode = TINYGLTF_MODE_TRIANGLES;
5015 ParseIntegerProperty(&mode, err, o, "mode", false);
imallettd9ce9eb2022-10-07 10:37:09 -07005016 primitive->mode = mode; // Why only triangles were supported ?
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005017
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005018 int indices = -1;
5019 ParseIntegerProperty(&indices, err, o, "indices", false);
5020 primitive->indices = indices;
5021 if (!ParseStringIntegerProperty(&primitive->attributes, err, o, "attributes",
5022 true, "Primitive")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005023 return false;
5024 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005025
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00005026 // Look for morph targets
David03ad33c2023-02-15 23:35:51 -06005027 detail::json_const_iterator targetsObject;
David1f9a4b92023-02-15 22:56:18 -06005028 if (detail::FindMember(o, "targets", targetsObject) &&
5029 detail::IsArray(detail::GetValue(targetsObject))) {
5030 auto targetsObjectEnd = detail::ArrayEnd(detail::GetValue(targetsObject));
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005031 for (detail::json_const_array_iterator i =
5032 detail::ArrayBegin(detail::GetValue(targetsObject));
jrkooncecba5d6c2019-08-29 11:26:22 -05005033 i != targetsObjectEnd; ++i) {
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00005034 std::map<std::string, int> targetAttribues;
5035
David03ad33c2023-02-15 23:35:51 -06005036 const detail::json &dict = *i;
David1f9a4b92023-02-15 22:56:18 -06005037 if (detail::IsObject(dict)) {
David03ad33c2023-02-15 23:35:51 -06005038 detail::json_const_iterator dictIt(detail::ObjectBegin(dict));
5039 detail::json_const_iterator dictItEnd(detail::ObjectEnd(dict));
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00005040
jrkooncecba5d6c2019-08-29 11:26:22 -05005041 for (; dictIt != dictItEnd; ++dictIt) {
5042 int iVal;
David1f9a4b92023-02-15 22:56:18 -06005043 if (detail::GetInt(detail::GetValue(dictIt), iVal))
5044 targetAttribues[detail::GetKey(dictIt)] = iVal;
jrkooncecba5d6c2019-08-29 11:26:22 -05005045 }
5046 primitive->targets.emplace_back(std::move(targetAttribues));
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00005047 }
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00005048 }
5049 }
5050
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005051 ParseExtrasAndExtensions(primitive, err, o,
5052 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005053
Alex Wood7319db72019-01-24 15:38:16 -05005054#ifdef TINYGLTF_ENABLE_DRACO
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00005055 auto dracoExtension =
5056 primitive->extensions.find("KHR_draco_mesh_compression");
5057 if (dracoExtension != primitive->extensions.end()) {
Nyall Dawson6e3d6662023-08-24 13:25:34 +10005058 ParseDracoExtension(primitive, model, err, warn, dracoExtension->second, strictness);
Alex Wood7319db72019-01-24 15:38:16 -05005059 }
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09005060#else
5061 (void)model;
Nyall Dawson6e3d6662023-08-24 13:25:34 +10005062 (void)warn;
Nyall Dawson5a7b8272023-09-03 09:04:56 +10005063 (void)strictness;
Alex Wood7319db72019-01-24 15:38:16 -05005064#endif
5065
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005066 return true;
5067}
5068
Nyall Dawson6e3d6662023-08-24 13:25:34 +10005069static bool ParseMesh(Mesh *mesh, Model *model,
5070 std::string *err, std::string *warn,
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005071 const detail::json &o,
Nyall Dawson6e3d6662023-08-24 13:25:34 +10005072 bool store_original_json_for_extras_and_extensions,
5073 ParseStrictness strictness) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005074 ParseStringProperty(&mesh->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005075
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005076 mesh->primitives.clear();
David03ad33c2023-02-15 23:35:51 -06005077 detail::json_const_iterator primObject;
David1f9a4b92023-02-15 22:56:18 -06005078 if (detail::FindMember(o, "primitives", primObject) &&
5079 detail::IsArray(detail::GetValue(primObject))) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005080 detail::json_const_array_iterator primEnd =
5081 detail::ArrayEnd(detail::GetValue(primObject));
5082 for (detail::json_const_array_iterator i =
5083 detail::ArrayBegin(detail::GetValue(primObject));
jrkooncecba5d6c2019-08-29 11:26:22 -05005084 i != primEnd; ++i) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005085 Primitive primitive;
Nyall Dawson6e3d6662023-08-24 13:25:34 +10005086 if (ParsePrimitive(&primitive, model, err, warn, *i,
5087 store_original_json_for_extras_and_extensions,
5088 strictness)) {
Luke San Antonio10a3e1d2016-06-15 00:05:01 -04005089 // Only add the primitive if the parsing succeeds.
jrkoonced1e14722019-08-27 11:51:02 -05005090 mesh->primitives.emplace_back(std::move(primitive));
Luke San Antonio10a3e1d2016-06-15 00:05:01 -04005091 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005092 }
5093 }
5094
Aurélien Chatelain38a6a752017-05-24 09:54:55 +00005095 // Should probably check if has targets and if dimensions fit
5096 ParseNumberArrayProperty(&mesh->weights, err, o, "weights", false);
5097
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005098 ParseExtrasAndExtensions(mesh, err, o,
5099 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005100
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005101 return true;
5102}
5103
David03ad33c2023-02-15 23:35:51 -06005104static bool ParseNode(Node *node, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005105 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005106 ParseStringProperty(&node->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005107
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005108 int skin = -1;
5109 ParseIntegerProperty(&skin, err, o, "skin", false);
5110 node->skin = skin;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005111
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005112 // Matrix and T/R/S are exclusive
Syoyo Fujita5b407452017-06-04 17:42:41 +09005113 if (!ParseNumberArrayProperty(&node->matrix, err, o, "matrix", false)) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005114 ParseNumberArrayProperty(&node->rotation, err, o, "rotation", false);
5115 ParseNumberArrayProperty(&node->scale, err, o, "scale", false);
5116 ParseNumberArrayProperty(&node->translation, err, o, "translation", false);
5117 }
5118
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005119 int camera = -1;
5120 ParseIntegerProperty(&camera, err, o, "camera", false);
5121 node->camera = camera;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005122
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005123 int mesh = -1;
5124 ParseIntegerProperty(&mesh, err, o, "mesh", false);
5125 node->mesh = mesh;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005126
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005127 node->children.clear();
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005128 ParseIntegerArrayProperty(&node->children, err, o, "children", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005129
Benjamin Schmithüsenad63bf72019-08-16 14:19:27 +02005130 ParseNumberArrayProperty(&node->weights, err, o, "weights", false);
5131
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005132 ParseExtrasAndExtensions(node, err, o,
5133 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005134
David Siegelc1648782023-05-09 01:33:57 +02005135 // KHR_lights_punctual: parse light source reference
5136 int light = -1;
5137 if (node->extensions.count("KHR_lights_punctual") != 0) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005138 auto const &light_ext = node->extensions["KHR_lights_punctual"];
David Siegelc1648782023-05-09 01:33:57 +02005139 if (light_ext.Has("light")) {
5140 light = light_ext.Get("light").GetNumberAsInt();
5141 } else {
David Siegel157063f2023-06-06 15:31:58 +02005142 if (err) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005143 *err +=
5144 "Node has extension KHR_lights_punctual, but does not reference "
5145 "a light source.\n";
David Siegel157063f2023-06-06 15:31:58 +02005146 }
David Siegelc1648782023-05-09 01:33:57 +02005147 return false;
5148 }
5149 }
5150 node->light = light;
Baranob_Ilya78864c82023-06-12 10:43:52 +04005151
5152 // KHR_audio: parse audio source reference
5153 int emitter = -1;
5154 if (node->extensions.count("KHR_audio") != 0) {
5155 auto const &audio_ext = node->extensions["KHR_audio"];
5156 if (audio_ext.Has("emitter")) {
5157 emitter = audio_ext.Get("emitter").GetNumberAsInt();
5158 } else {
5159 if (err) {
5160 *err +=
5161 "Node has extension KHR_audio, but does not reference "
5162 "a audio emitter.\n";
5163 }
5164 return false;
5165 }
5166 }
5167 node->emitter = emitter;
Syoyo Fujita7a570c82023-06-19 21:52:13 +09005168
Thomas Gampera42263b2024-02-05 15:42:49 +01005169 node->lods.clear();
5170 if (node->extensions.count("MSFT_lod") != 0) {
5171 auto const &msft_lod_ext = node->extensions["MSFT_lod"];
5172 if (msft_lod_ext.Has("ids")) {
5173 auto idsArr = msft_lod_ext.Get("ids");
5174 for (size_t i = 0; i < idsArr.ArrayLen(); ++i) {
5175 node->lods.emplace_back(idsArr.Get(i).GetNumberAsInt());
5176 }
5177 } else {
5178 if (err) {
5179 *err +=
5180 "Node has extension MSFT_lod, but does not reference "
5181 "other nodes via their ids.\n";
5182 }
5183 return false;
5184 }
5185 }
5186
Emanuel Schrade186322b2017-11-06 11:14:41 +01005187 return true;
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04005188}
5189
Baranob_Ilya879cb472023-06-12 13:35:05 +04005190static bool ParseScene(Scene *scene, std::string *err, const detail::json &o,
5191 bool store_original_json_for_extras_and_extensions) {
5192 ParseStringProperty(&scene->name, err, o, "name", false);
5193 ParseIntegerArrayProperty(&scene->nodes, err, o, "nodes", false);
5194
5195 ParseExtrasAndExtensions(scene, err, o,
5196 store_original_json_for_extras_and_extensions);
5197
5198 // Parse KHR_audio global emitters
5199 if (scene->extensions.count("KHR_audio") != 0) {
5200 auto const &audio_ext = scene->extensions["KHR_audio"];
5201 if (audio_ext.Has("emitters")) {
5202 auto emittersArr = audio_ext.Get("emitters");
5203 for (size_t i = 0; i < emittersArr.ArrayLen(); ++i) {
5204 scene->audioEmitters.emplace_back(emittersArr.Get(i).GetNumberAsInt());
5205 }
5206 } else {
5207 if (err) {
5208 *err +=
5209 "Node has extension KHR_audio, but does not reference "
5210 "a audio emitter.\n";
5211 }
5212 return false;
5213 }
5214 }
5215
5216 return true;
5217}
5218
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005219static bool ParsePbrMetallicRoughness(
David03ad33c2023-02-15 23:35:51 -06005220 PbrMetallicRoughness *pbr, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005221 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09005222 if (pbr == nullptr) {
5223 return false;
5224 }
5225
5226 std::vector<double> baseColorFactor;
5227 if (ParseNumberArrayProperty(&baseColorFactor, err, o, "baseColorFactor",
5228 /* required */ false)) {
5229 if (baseColorFactor.size() != 4) {
5230 if (err) {
5231 (*err) +=
5232 "Array length of `baseColorFactor` parameter in "
5233 "pbrMetallicRoughness must be 4, but got " +
5234 std::to_string(baseColorFactor.size()) + "\n";
5235 }
5236 return false;
5237 }
Selmar Kokc3353e12019-10-18 18:22:35 +02005238 pbr->baseColorFactor = baseColorFactor;
Syoyo Fujita046400b2019-07-24 19:26:48 +09005239 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09005240
5241 {
David03ad33c2023-02-15 23:35:51 -06005242 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06005243 if (detail::FindMember(o, "baseColorTexture", it)) {
5244 ParseTextureInfo(&pbr->baseColorTexture, err, detail::GetValue(it),
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005245 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09005246 }
5247 }
5248
5249 {
David03ad33c2023-02-15 23:35:51 -06005250 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06005251 if (detail::FindMember(o, "metallicRoughnessTexture", it)) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005252 ParseTextureInfo(&pbr->metallicRoughnessTexture, err,
5253 detail::GetValue(it),
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005254 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09005255 }
5256 }
5257
5258 ParseNumberProperty(&pbr->metallicFactor, err, o, "metallicFactor", false);
5259 ParseNumberProperty(&pbr->roughnessFactor, err, o, "roughnessFactor", false);
5260
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005261 ParseExtrasAndExtensions(pbr, err, o,
5262 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005263
Syoyo Fujita046400b2019-07-24 19:26:48 +09005264 return true;
5265}
5266
Nyall Dawson02e8b8d2023-08-24 10:25:20 +10005267static bool ParseMaterial(Material *material, std::string *err, std::string *warn,
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005268 const detail::json &o,
Nyall Dawson8c85d5e2023-08-28 12:56:09 +10005269 bool store_original_json_for_extras_and_extensions,
5270 ParseStrictness strictness) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09005271 ParseStringProperty(&material->name, err, o, "name", /* required */ false);
5272
Syoyo Fujitaff515702019-08-24 16:29:14 +09005273 if (ParseNumberArrayProperty(&material->emissiveFactor, err, o,
5274 "emissiveFactor",
5275 /* required */ false)) {
Nyall Dawsonbbc1eae2023-09-03 09:05:32 +10005276 if (strictness==ParseStrictness::Permissive && material->emissiveFactor.size() == 4) {
Nyall Dawson02e8b8d2023-08-24 10:25:20 +10005277 if (warn) {
5278 (*warn) +=
5279 "Array length of `emissiveFactor` parameter in "
5280 "material must be 3, but got 4\n";
5281 }
5282 material->emissiveFactor.resize(3);
5283 }
5284 else if (material->emissiveFactor.size() != 3) {
Selmar Kok6df800d2019-08-19 11:05:28 +02005285 if (err) {
5286 (*err) +=
5287 "Array length of `emissiveFactor` parameter in "
5288 "material must be 3, but got " +
5289 std::to_string(material->emissiveFactor.size()) + "\n";
5290 }
5291 return false;
5292 }
Syoyo Fujitaff515702019-08-24 16:29:14 +09005293 } else {
Selmar Kok6df800d2019-08-19 11:05:28 +02005294 // fill with default values
Selmar Kok6dba6c62019-08-19 11:23:31 +02005295 material->emissiveFactor = {0.0, 0.0, 0.0};
Selmar Kok6df800d2019-08-19 11:05:28 +02005296 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09005297
5298 ParseStringProperty(&material->alphaMode, err, o, "alphaMode",
5299 /* required */ false);
5300 ParseNumberProperty(&material->alphaCutoff, err, o, "alphaCutoff",
5301 /* required */ false);
5302 ParseBooleanProperty(&material->doubleSided, err, o, "doubleSided",
5303 /* required */ false);
5304
5305 {
David03ad33c2023-02-15 23:35:51 -06005306 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06005307 if (detail::FindMember(o, "pbrMetallicRoughness", it)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09005308 ParsePbrMetallicRoughness(&material->pbrMetallicRoughness, err,
David1f9a4b92023-02-15 22:56:18 -06005309 detail::GetValue(it),
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005310 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09005311 }
5312 }
5313
5314 {
David03ad33c2023-02-15 23:35:51 -06005315 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06005316 if (detail::FindMember(o, "normalTexture", it)) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005317 ParseNormalTextureInfo(&material->normalTexture, err,
5318 detail::GetValue(it),
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005319 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09005320 }
5321 }
5322
5323 {
David03ad33c2023-02-15 23:35:51 -06005324 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06005325 if (detail::FindMember(o, "occlusionTexture", it)) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005326 ParseOcclusionTextureInfo(&material->occlusionTexture, err,
5327 detail::GetValue(it),
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005328 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09005329 }
5330 }
5331
5332 {
David03ad33c2023-02-15 23:35:51 -06005333 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06005334 if (detail::FindMember(o, "emissiveTexture", it)) {
5335 ParseTextureInfo(&material->emissiveTexture, err, detail::GetValue(it),
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005336 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09005337 }
5338 }
5339
5340 // Old code path. For backward compatibility, we still store material values
5341 // as Parameter. This will create duplicated information for
imallettd9ce9eb2022-10-07 10:37:09 -07005342 // example(pbrMetallicRoughness), but should be negligible in terms of memory
Syoyo Fujita046400b2019-07-24 19:26:48 +09005343 // consumption.
5344 // TODO(syoyo): Remove in the next major release.
5345 material->values.clear();
5346 material->additionalValues.clear();
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04005347
David03ad33c2023-02-15 23:35:51 -06005348 detail::json_const_iterator it(detail::ObjectBegin(o));
5349 detail::json_const_iterator itEnd(detail::ObjectEnd(o));
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04005350
jrkooncecba5d6c2019-08-29 11:26:22 -05005351 for (; it != itEnd; ++it) {
David1f9a4b92023-02-15 22:56:18 -06005352 std::string key(detail::GetKey(it));
jrkoonce06c30c42019-09-03 15:56:48 -05005353 if (key == "pbrMetallicRoughness") {
David1f9a4b92023-02-15 22:56:18 -06005354 if (detail::IsObject(detail::GetValue(it))) {
David03ad33c2023-02-15 23:35:51 -06005355 const detail::json &values_object = detail::GetValue(it);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005356
David03ad33c2023-02-15 23:35:51 -06005357 detail::json_const_iterator itVal(detail::ObjectBegin(values_object));
5358 detail::json_const_iterator itValEnd(detail::ObjectEnd(values_object));
jrkoonce06c30c42019-09-03 15:56:48 -05005359
5360 for (; itVal != itValEnd; ++itVal) {
5361 Parameter param;
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005362 if (ParseParameterProperty(&param, err, values_object,
5363 detail::GetKey(itVal), false)) {
David1f9a4b92023-02-15 22:56:18 -06005364 material->values.emplace(detail::GetKey(itVal), std::move(param));
jrkoonce06c30c42019-09-03 15:56:48 -05005365 }
5366 }
5367 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005368 } else if (key == "extensions" || key == "extras") {
5369 // done later, skip, otherwise poorly parsed contents will be saved in the
5370 // parametermap and serialized again later
5371 } else {
jrkoonce06c30c42019-09-03 15:56:48 -05005372 Parameter param;
5373 if (ParseParameterProperty(&param, err, o, key, false)) {
5374 // names of materials have already been parsed. Putting it in this map
imallettd9ce9eb2022-10-07 10:37:09 -07005375 // doesn't correctly reflect the glTF specification
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005376 if (key != "name")
5377 material->additionalValues.emplace(std::move(key), std::move(param));
jrkoonce06c30c42019-09-03 15:56:48 -05005378 }
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00005379 }
Syoyo Fujita5b407452017-06-04 17:42:41 +09005380 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005381
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005382 material->extensions.clear(); // Note(agnat): Why?
5383 ParseExtrasAndExtensions(material, err, o,
5384 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005385
Thomas Gampera42263b2024-02-05 15:42:49 +01005386 material->lods.clear();
5387 if (material->extensions.count("MSFT_lod") != 0) {
5388 auto const &msft_lod_ext = material->extensions["MSFT_lod"];
5389 if (msft_lod_ext.Has("ids")) {
5390 auto idsArr = msft_lod_ext.Get("ids");
5391 for (size_t i = 0; i < idsArr.ArrayLen(); ++i) {
5392 material->lods.emplace_back(idsArr.Get(i).GetNumberAsInt());
5393 }
5394 } else {
5395 if (err) {
5396 *err +=
5397 "Material has extension MSFT_lod, but does not reference "
5398 "other materials via their ids.\n";
5399 }
5400 return false;
5401 }
5402 }
5403
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04005404 return true;
5405}
5406
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005407static bool ParseAnimationChannel(
David03ad33c2023-02-15 23:35:51 -06005408 AnimationChannel *channel, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005409 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005410 int samplerIndex = -1;
5411 int targetIndex = -1;
5412 if (!ParseIntegerProperty(&samplerIndex, err, o, "sampler", true,
5413 "AnimationChannel")) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005414 if (err) {
5415 (*err) += "`sampler` field is missing in animation channels\n";
5416 }
5417 return false;
5418 }
5419
David03ad33c2023-02-15 23:35:51 -06005420 detail::json_const_iterator targetIt;
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005421 if (detail::FindMember(o, "target", targetIt) &&
5422 detail::IsObject(detail::GetValue(targetIt))) {
David03ad33c2023-02-15 23:35:51 -06005423 const detail::json &target_object = detail::GetValue(targetIt);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005424
Jack Mousseau283b5522023-01-15 11:45:45 -08005425 ParseIntegerProperty(&targetIndex, err, target_object, "node", false);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005426
5427 if (!ParseStringProperty(&channel->target_path, err, target_object, "path",
5428 true)) {
5429 if (err) {
5430 (*err) += "`path` field is missing in animation.channels.target\n";
5431 }
5432 return false;
5433 }
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09005434 ParseExtensionsProperty(&channel->target_extensions, err, target_object);
David Siegeld852f502023-06-05 23:28:05 +02005435 ParseExtrasProperty(&channel->target_extras, target_object);
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09005436 if (store_original_json_for_extras_and_extensions) {
David Siegeld852f502023-06-05 23:28:05 +02005437 {
5438 detail::json_const_iterator it;
5439 if (detail::FindMember(target_object, "extensions", it)) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005440 channel->target_extensions_json_string =
5441 detail::JsonToString(detail::GetValue(it));
David Siegeld852f502023-06-05 23:28:05 +02005442 }
5443 }
5444 {
5445 detail::json_const_iterator it;
5446 if (detail::FindMember(target_object, "extras", it)) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005447 channel->target_extras_json_string =
5448 detail::JsonToString(detail::GetValue(it));
David Siegeld852f502023-06-05 23:28:05 +02005449 }
Selmar Kok973d9b32020-01-21 18:45:24 +01005450 }
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09005451 }
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005452 }
5453
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005454 channel->sampler = samplerIndex;
5455 channel->target_node = targetIndex;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005456
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005457 ParseExtrasAndExtensions(channel, err, o,
5458 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005459
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005460 return true;
5461}
5462
5463static bool ParseAnimation(Animation *animation, std::string *err,
David03ad33c2023-02-15 23:35:51 -06005464 const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005465 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005466 {
David03ad33c2023-02-15 23:35:51 -06005467 detail::json_const_iterator channelsIt;
David1f9a4b92023-02-15 22:56:18 -06005468 if (detail::FindMember(o, "channels", channelsIt) &&
5469 detail::IsArray(detail::GetValue(channelsIt))) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005470 detail::json_const_array_iterator channelEnd =
5471 detail::ArrayEnd(detail::GetValue(channelsIt));
5472 for (detail::json_const_array_iterator i =
5473 detail::ArrayBegin(detail::GetValue(channelsIt));
jrkooncecba5d6c2019-08-29 11:26:22 -05005474 i != channelEnd; ++i) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005475 AnimationChannel channel;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005476 if (ParseAnimationChannel(
5477 &channel, err, *i,
5478 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005479 // Only add the channel if the parsing succeeds.
jrkooncecba5d6c2019-08-29 11:26:22 -05005480 animation->channels.emplace_back(std::move(channel));
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005481 }
5482 }
5483 }
5484 }
5485
5486 {
David03ad33c2023-02-15 23:35:51 -06005487 detail::json_const_iterator samplerIt;
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005488 if (detail::FindMember(o, "samplers", samplerIt) &&
5489 detail::IsArray(detail::GetValue(samplerIt))) {
David03ad33c2023-02-15 23:35:51 -06005490 const detail::json &sampler_array = detail::GetValue(samplerIt);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005491
David03ad33c2023-02-15 23:35:51 -06005492 detail::json_const_array_iterator it = detail::ArrayBegin(sampler_array);
5493 detail::json_const_array_iterator itEnd = detail::ArrayEnd(sampler_array);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005494
jrkooncecba5d6c2019-08-29 11:26:22 -05005495 for (; it != itEnd; ++it) {
David03ad33c2023-02-15 23:35:51 -06005496 const detail::json &s = *it;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005497
5498 AnimationSampler sampler;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005499 int inputIndex = -1;
5500 int outputIndex = -1;
5501 if (!ParseIntegerProperty(&inputIndex, err, s, "input", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005502 if (err) {
5503 (*err) += "`input` field is missing in animation.sampler\n";
5504 }
5505 return false;
5506 }
Evan Birenbaum6bdffed2019-02-14 13:30:57 -08005507 ParseStringProperty(&sampler.interpolation, err, s, "interpolation",
5508 false);
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005509 if (!ParseIntegerProperty(&outputIndex, err, s, "output", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005510 if (err) {
5511 (*err) += "`output` field is missing in animation.sampler\n";
5512 }
5513 return false;
5514 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005515 sampler.input = inputIndex;
5516 sampler.output = outputIndex;
David Siegel22cafa12023-06-05 22:18:59 +02005517 ParseExtrasAndExtensions(&sampler, err, o,
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005518 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005519
jrkooncecba5d6c2019-08-29 11:26:22 -05005520 animation->samplers.emplace_back(std::move(sampler));
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005521 }
5522 }
5523 }
5524
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005525 ParseStringProperty(&animation->name, err, o, "name", false);
5526
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005527 ParseExtrasAndExtensions(animation, err, o,
5528 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005529
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005530 return true;
5531}
5532
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005533static bool ParseSampler(Sampler *sampler, std::string *err,
5534 const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005535 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujitac2615632016-06-19 21:56:06 +09005536 ParseStringProperty(&sampler->name, err, o, "name", false);
5537
Syoyo Fujitaee179b22019-08-16 13:11:30 +09005538 int minFilter = -1;
5539 int magFilter = -1;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005540 int wrapS = TINYGLTF_TEXTURE_WRAP_REPEAT;
5541 int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT;
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09005542 // int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005543 ParseIntegerProperty(&minFilter, err, o, "minFilter", false);
5544 ParseIntegerProperty(&magFilter, err, o, "magFilter", false);
5545 ParseIntegerProperty(&wrapS, err, o, "wrapS", false);
5546 ParseIntegerProperty(&wrapT, err, o, "wrapT", false);
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09005547 // ParseIntegerProperty(&wrapR, err, o, "wrapR", false); // tinygltf
5548 // extension
Syoyo Fujitaee179b22019-08-16 13:11:30 +09005549
imallettd9ce9eb2022-10-07 10:37:09 -07005550 // TODO(syoyo): Check the value is allowed one.
Syoyo Fujitaee179b22019-08-16 13:11:30 +09005551 // (e.g. we allow 9728(NEAREST), but don't allow 9727)
Syoyo Fujitac2615632016-06-19 21:56:06 +09005552
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005553 sampler->minFilter = minFilter;
5554 sampler->magFilter = magFilter;
5555 sampler->wrapS = wrapS;
5556 sampler->wrapT = wrapT;
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09005557 // sampler->wrapR = wrapR;
Syoyo Fujitac2615632016-06-19 21:56:06 +09005558
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005559 ParseExtrasAndExtensions(sampler, err, o,
5560 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005561
Syoyo Fujitac2615632016-06-19 21:56:06 +09005562 return true;
5563}
5564
David03ad33c2023-02-15 23:35:51 -06005565static bool ParseSkin(Skin *skin, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005566 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09005567 ParseStringProperty(&skin->name, err, o, "name", false, "Skin");
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005568
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005569 std::vector<int> joints;
5570 if (!ParseIntegerArrayProperty(&joints, err, o, "joints", false, "Skin")) {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005571 return false;
5572 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005573 skin->joints = std::move(joints);
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005574
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005575 int skeleton = -1;
5576 ParseIntegerProperty(&skeleton, err, o, "skeleton", false, "Skin");
5577 skin->skeleton = skeleton;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005578
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005579 int invBind = -1;
5580 ParseIntegerProperty(&invBind, err, o, "inverseBindMatrices", true, "Skin");
5581 skin->inverseBindMatrices = invBind;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005582
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005583 ParseExtrasAndExtensions(skin, err, o,
5584 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005585
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005586 return true;
5587}
5588
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005589static bool ParsePerspectiveCamera(
David03ad33c2023-02-15 23:35:51 -06005590 PerspectiveCamera *camera, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005591 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005592 double yfov = 0.0;
5593 if (!ParseNumberProperty(&yfov, err, o, "yfov", true, "OrthographicCamera")) {
5594 return false;
5595 }
5596
5597 double znear = 0.0;
5598 if (!ParseNumberProperty(&znear, err, o, "znear", true,
5599 "PerspectiveCamera")) {
5600 return false;
5601 }
5602
5603 double aspectRatio = 0.0; // = invalid
5604 ParseNumberProperty(&aspectRatio, err, o, "aspectRatio", false,
5605 "PerspectiveCamera");
5606
5607 double zfar = 0.0; // = invalid
5608 ParseNumberProperty(&zfar, err, o, "zfar", false, "PerspectiveCamera");
5609
Selmar Kok31cb7f92018-10-03 15:39:05 +02005610 camera->aspectRatio = aspectRatio;
5611 camera->zfar = zfar;
5612 camera->yfov = yfov;
5613 camera->znear = znear;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005614
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005615 ParseExtrasAndExtensions(camera, err, o,
5616 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005617
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005618 // TODO(syoyo): Validate parameter values.
5619
5620 return true;
5621}
5622
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005623static bool ParseSpotLight(SpotLight *light, std::string *err,
5624 const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005625 bool store_original_json_for_extras_and_extensions) {
Johan Bowald52936a02019-07-17 09:06:45 +02005626 ParseNumberProperty(&light->innerConeAngle, err, o, "innerConeAngle", false);
5627 ParseNumberProperty(&light->outerConeAngle, err, o, "outerConeAngle", false);
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005628
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005629 ParseExtrasAndExtensions(light, err, o,
5630 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005631
Johan Bowald52936a02019-07-17 09:06:45 +02005632 // TODO(syoyo): Validate parameter values.
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005633
Johan Bowald52936a02019-07-17 09:06:45 +02005634 return true;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005635}
5636
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005637static bool ParseOrthographicCamera(
David03ad33c2023-02-15 23:35:51 -06005638 OrthographicCamera *camera, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005639 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005640 double xmag = 0.0;
5641 if (!ParseNumberProperty(&xmag, err, o, "xmag", true, "OrthographicCamera")) {
5642 return false;
5643 }
5644
5645 double ymag = 0.0;
5646 if (!ParseNumberProperty(&ymag, err, o, "ymag", true, "OrthographicCamera")) {
5647 return false;
5648 }
5649
5650 double zfar = 0.0;
5651 if (!ParseNumberProperty(&zfar, err, o, "zfar", true, "OrthographicCamera")) {
5652 return false;
5653 }
5654
5655 double znear = 0.0;
5656 if (!ParseNumberProperty(&znear, err, o, "znear", true,
5657 "OrthographicCamera")) {
5658 return false;
5659 }
5660
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005661 ParseExtrasAndExtensions(camera, err, o,
5662 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005663
Selmar Kok31cb7f92018-10-03 15:39:05 +02005664 camera->xmag = xmag;
5665 camera->ymag = ymag;
5666 camera->zfar = zfar;
5667 camera->znear = znear;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005668
5669 // TODO(syoyo): Validate parameter values.
5670
5671 return true;
5672}
5673
David03ad33c2023-02-15 23:35:51 -06005674static bool ParseCamera(Camera *camera, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005675 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005676 if (!ParseStringProperty(&camera->type, err, o, "type", true, "Camera")) {
5677 return false;
5678 }
5679
5680 if (camera->type.compare("orthographic") == 0) {
David03ad33c2023-02-15 23:35:51 -06005681 detail::json_const_iterator orthoIt;
David1f9a4b92023-02-15 22:56:18 -06005682 if (!detail::FindMember(o, "orthographic", orthoIt)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005683 if (err) {
5684 std::stringstream ss;
imallettd9ce9eb2022-10-07 10:37:09 -07005685 ss << "Orthographic camera description not found." << std::endl;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005686 (*err) += ss.str();
5687 }
5688 return false;
5689 }
5690
David03ad33c2023-02-15 23:35:51 -06005691 const detail::json &v = detail::GetValue(orthoIt);
David1f9a4b92023-02-15 22:56:18 -06005692 if (!detail::IsObject(v)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005693 if (err) {
5694 std::stringstream ss;
5695 ss << "\"orthographic\" is not a JSON object." << std::endl;
5696 (*err) += ss.str();
5697 }
5698 return false;
5699 }
5700
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005701 if (!ParseOrthographicCamera(
5702 &camera->orthographic, err, v,
5703 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005704 return false;
5705 }
5706 } else if (camera->type.compare("perspective") == 0) {
David03ad33c2023-02-15 23:35:51 -06005707 detail::json_const_iterator perspIt;
David1f9a4b92023-02-15 22:56:18 -06005708 if (!detail::FindMember(o, "perspective", perspIt)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005709 if (err) {
5710 std::stringstream ss;
5711 ss << "Perspective camera description not found." << std::endl;
5712 (*err) += ss.str();
5713 }
5714 return false;
5715 }
5716
David03ad33c2023-02-15 23:35:51 -06005717 const detail::json &v = detail::GetValue(perspIt);
David1f9a4b92023-02-15 22:56:18 -06005718 if (!detail::IsObject(v)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005719 if (err) {
5720 std::stringstream ss;
5721 ss << "\"perspective\" is not a JSON object." << std::endl;
5722 (*err) += ss.str();
5723 }
5724 return false;
5725 }
5726
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005727 if (!ParsePerspectiveCamera(
5728 &camera->perspective, err, v,
5729 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005730 return false;
5731 }
5732 } else {
5733 if (err) {
5734 std::stringstream ss;
5735 ss << "Invalid camera type: \"" << camera->type
5736 << "\". Must be \"perspective\" or \"orthographic\"" << std::endl;
5737 (*err) += ss.str();
5738 }
5739 return false;
5740 }
5741
5742 ParseStringProperty(&camera->name, err, o, "name", false);
5743
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005744 ParseExtrasAndExtensions(camera, err, o,
5745 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005746
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005747 return true;
5748}
5749
David03ad33c2023-02-15 23:35:51 -06005750static bool ParseLight(Light *light, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005751 bool store_original_json_for_extras_and_extensions) {
Johan Bowald52936a02019-07-17 09:06:45 +02005752 if (!ParseStringProperty(&light->type, err, o, "type", true)) {
5753 return false;
5754 }
5755
5756 if (light->type == "spot") {
David03ad33c2023-02-15 23:35:51 -06005757 detail::json_const_iterator spotIt;
David1f9a4b92023-02-15 22:56:18 -06005758 if (!detail::FindMember(o, "spot", spotIt)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005759 if (err) {
5760 std::stringstream ss;
5761 ss << "Spot light description not found." << std::endl;
5762 (*err) += ss.str();
5763 }
5764 return false;
Benjamin Schmithüsen4557b6a2019-07-09 16:55:55 +02005765 }
5766
David03ad33c2023-02-15 23:35:51 -06005767 const detail::json &v = detail::GetValue(spotIt);
David1f9a4b92023-02-15 22:56:18 -06005768 if (!detail::IsObject(v)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005769 if (err) {
5770 std::stringstream ss;
5771 ss << "\"spot\" is not a JSON object." << std::endl;
5772 (*err) += ss.str();
5773 }
5774 return false;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005775 }
5776
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005777 if (!ParseSpotLight(&light->spot, err, v,
5778 store_original_json_for_extras_and_extensions)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005779 return false;
5780 }
5781 }
5782
5783 ParseStringProperty(&light->name, err, o, "name", false);
5784 ParseNumberArrayProperty(&light->color, err, o, "color", false);
5785 ParseNumberProperty(&light->range, err, o, "range", false);
5786 ParseNumberProperty(&light->intensity, err, o, "intensity", false);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005787
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005788 ParseExtrasAndExtensions(light, err, o,
5789 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005790
Johan Bowald52936a02019-07-17 09:06:45 +02005791 return true;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005792}
5793
Baranob_Ilya78864c82023-06-12 10:43:52 +04005794static bool ParsePositionalEmitter(
5795 PositionalEmitter *positional, std::string *err, const detail::json &o,
5796 bool store_original_json_for_extras_and_extensions) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005797 ParseNumberProperty(&positional->coneInnerAngle, err, o, "coneInnerAngle",
5798 false);
5799 ParseNumberProperty(&positional->coneOuterAngle, err, o, "coneOuterAngle",
5800 false);
5801 ParseNumberProperty(&positional->coneOuterGain, err, o, "coneOuterGain",
5802 false);
Baranob_Ilya78864c82023-06-12 10:43:52 +04005803 ParseNumberProperty(&positional->maxDistance, err, o, "maxDistance", false);
5804 ParseNumberProperty(&positional->refDistance, err, o, "refDistance", false);
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005805 ParseNumberProperty(&positional->rolloffFactor, err, o, "rolloffFactor",
5806 false);
Baranob_Ilya78864c82023-06-12 10:43:52 +04005807
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005808 ParseExtrasAndExtensions(positional, err, o,
5809 store_original_json_for_extras_and_extensions);
Baranob_Ilya78864c82023-06-12 10:43:52 +04005810
5811 return true;
5812}
5813
5814static bool ParseAudioEmitter(
5815 AudioEmitter *emitter, std::string *err, const detail::json &o,
5816 bool store_original_json_for_extras_and_extensions) {
5817 if (!ParseStringProperty(&emitter->type, err, o, "type", true)) {
5818 return false;
5819 }
5820
5821 if (emitter->type == "positional") {
5822 detail::json_const_iterator positionalIt;
5823 if (!detail::FindMember(o, "positional", positionalIt)) {
5824 if (err) {
5825 std::stringstream ss;
5826 ss << "Positional emitter description not found." << std::endl;
5827 (*err) += ss.str();
5828 }
5829 return false;
5830 }
5831
5832 const detail::json &v = detail::GetValue(positionalIt);
5833 if (!detail::IsObject(v)) {
5834 if (err) {
5835 std::stringstream ss;
5836 ss << "\"positional\" is not a JSON object." << std::endl;
5837 (*err) += ss.str();
5838 }
5839 return false;
5840 }
5841
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005842 if (!ParsePositionalEmitter(
5843 &emitter->positional, err, v,
5844 store_original_json_for_extras_and_extensions)) {
Baranob_Ilya78864c82023-06-12 10:43:52 +04005845 return false;
5846 }
5847 }
5848
5849 ParseStringProperty(&emitter->name, err, o, "name", false);
5850 ParseNumberProperty(&emitter->gain, err, o, "gain", false);
5851 ParseBooleanProperty(&emitter->loop, err, o, "loop", false);
5852 ParseBooleanProperty(&emitter->playing, err, o, "playing", false);
5853 ParseStringProperty(&emitter->distanceModel, err, o, "distanceModel", false);
5854 ParseIntegerProperty(&emitter->source, err, o, "source", true);
5855
5856 ParseExtrasAndExtensions(emitter, err, o,
5857 store_original_json_for_extras_and_extensions);
5858
5859 return true;
5860}
5861
5862static bool ParseAudioSource(
5863 AudioSource *source, std::string *err, const detail::json &o,
5864 bool store_original_json_for_extras_and_extensions) {
5865 ParseStringProperty(&source->name, err, o, "name", false);
5866 ParseStringProperty(&source->uri, err, o, "uri", false);
5867
5868 if (source->uri.empty()) {
5869 ParseIntegerProperty(&source->bufferView, err, o, "bufferView", true);
5870 ParseStringProperty(&source->mimeType, err, o, "mimeType", true);
5871 }
5872
5873 ParseExtrasAndExtensions(source, err, o,
5874 store_original_json_for_extras_and_extensions);
5875
5876 return true;
5877}
5878
David Siegelbec8a6d2023-06-06 15:36:07 +02005879namespace detail {
5880
5881template <typename Callback>
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005882bool ForEachInArray(const detail::json &_v, const char *member, Callback &&cb) {
David Siegelbec8a6d2023-06-06 15:36:07 +02005883 detail::json_const_iterator itm;
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005884 if (detail::FindMember(_v, member, itm) &&
5885 detail::IsArray(detail::GetValue(itm))) {
David Siegelbec8a6d2023-06-06 15:36:07 +02005886 const detail::json &root = detail::GetValue(itm);
5887 auto it = detail::ArrayBegin(root);
5888 auto end = detail::ArrayEnd(root);
5889 for (; it != end; ++it) {
5890 if (!cb(*it)) return false;
5891 }
5892 }
5893 return true;
5894};
5895
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005896} // end of namespace detail
David Siegelbec8a6d2023-06-06 15:36:07 +02005897
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09005898bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005899 const char *json_str,
5900 unsigned int json_str_length,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09005901 const std::string &base_dir,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005902 unsigned int check_sections) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005903 if (json_str_length < 4) {
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09005904 if (err) {
5905 (*err) = "JSON string too short.\n";
5906 }
5907 return false;
5908 }
5909
David03ad33c2023-02-15 23:35:51 -06005910 detail::JsonDocument v;
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005911
Syoyo Fujitad42767e2018-03-15 21:52:00 -05005912#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
5913 defined(_CPPUNWIND)) && \
Syoyo Fujitac4166e42020-01-08 02:38:01 +09005914 !defined(TINYGLTF_NOEXCEPTION)
Syoyo Fujitacdf84902017-08-01 20:12:08 +09005915 try {
David03ad33c2023-02-15 23:35:51 -06005916 detail::JsonParse(v, json_str, json_str_length, true);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005917
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005918 } catch (const std::exception &e) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005919 if (err) {
Syoyo Fujitacdf84902017-08-01 20:12:08 +09005920 (*err) = e.what();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005921 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005922 return false;
5923 }
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005924#else
5925 {
David03ad33c2023-02-15 23:35:51 -06005926 detail::JsonParse(v, json_str, json_str_length);
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005927
David1f9a4b92023-02-15 22:56:18 -06005928 if (!detail::IsObject(v)) {
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005929 // Assume parsing was failed.
5930 if (err) {
5931 (*err) = "Failed to parse JSON object\n";
5932 }
5933 return false;
5934 }
5935 }
5936#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005937
David1f9a4b92023-02-15 22:56:18 -06005938 if (!detail::IsObject(v)) {
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09005939 // root is not an object.
5940 if (err) {
5941 (*err) = "Root element is not a JSON object\n";
5942 }
5943 return false;
5944 }
5945
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02005946 {
5947 bool version_found = false;
David03ad33c2023-02-15 23:35:51 -06005948 detail::json_const_iterator it;
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005949 if (detail::FindMember(v, "asset", it) &&
5950 detail::IsObject(detail::GetValue(it))) {
David1f9a4b92023-02-15 22:56:18 -06005951 auto &itObj = detail::GetValue(it);
David03ad33c2023-02-15 23:35:51 -06005952 detail::json_const_iterator version_it;
jrkooncecba5d6c2019-08-29 11:26:22 -05005953 std::string versionStr;
David1f9a4b92023-02-15 22:56:18 -06005954 if (detail::FindMember(itObj, "version", version_it) &&
5955 detail::GetString(detail::GetValue(version_it), versionStr)) {
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02005956 version_found = true;
5957 }
5958 }
5959 if (version_found) {
5960 // OK
5961 } else if (check_sections & REQUIRE_VERSION) {
5962 if (err) {
5963 (*err) += "\"asset\" object not found in .gltf or not an object type\n";
5964 }
5965 return false;
5966 }
5967 }
5968
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005969 // scene is not mandatory.
Syoyo Fujita5b407452017-06-04 17:42:41 +09005970 // FIXME Maybe a better way to handle it than removing the code
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005971
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005972 auto IsArrayMemberPresent = [](const detail::json &_v,
5973 const char *name) -> bool {
David03ad33c2023-02-15 23:35:51 -06005974 detail::json_const_iterator it;
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02005975 return detail::FindMember(_v, name, it) &&
5976 detail::IsArray(detail::GetValue(it));
jrkooncecba5d6c2019-08-29 11:26:22 -05005977 };
5978
Syoyo Fujita83675312017-12-02 21:14:13 +09005979 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005980 if ((check_sections & REQUIRE_SCENES) &&
5981 !IsArrayMemberPresent(v, "scenes")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005982 if (err) {
5983 (*err) += "\"scenes\" object not found in .gltf or not an array type\n";
5984 }
5985 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005986 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005987 }
5988
Syoyo Fujita83675312017-12-02 21:14:13 +09005989 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005990 if ((check_sections & REQUIRE_NODES) && !IsArrayMemberPresent(v, "nodes")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005991 if (err) {
5992 (*err) += "\"nodes\" object not found in .gltf\n";
5993 }
5994 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005995 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005996 }
5997
Syoyo Fujita83675312017-12-02 21:14:13 +09005998 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005999 if ((check_sections & REQUIRE_ACCESSORS) &&
6000 !IsArrayMemberPresent(v, "accessors")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09006001 if (err) {
6002 (*err) += "\"accessors\" object not found in .gltf\n";
6003 }
6004 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09006005 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09006006 }
6007
Syoyo Fujita83675312017-12-02 21:14:13 +09006008 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006009 if ((check_sections & REQUIRE_BUFFERS) &&
6010 !IsArrayMemberPresent(v, "buffers")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09006011 if (err) {
6012 (*err) += "\"buffers\" object not found in .gltf\n";
6013 }
6014 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09006015 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09006016 }
6017
Syoyo Fujita83675312017-12-02 21:14:13 +09006018 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006019 if ((check_sections & REQUIRE_BUFFER_VIEWS) &&
6020 !IsArrayMemberPresent(v, "bufferViews")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09006021 if (err) {
6022 (*err) += "\"bufferViews\" object not found in .gltf\n";
6023 }
6024 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09006025 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09006026 }
6027
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00006028 model->buffers.clear();
6029 model->bufferViews.clear();
6030 model->accessors.clear();
6031 model->meshes.clear();
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09006032 model->cameras.clear();
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00006033 model->nodes.clear();
6034 model->extensionsUsed.clear();
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00006035 model->extensionsRequired.clear();
Selmar09d2ff12018-03-15 17:30:42 +01006036 model->extensions.clear();
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00006037 model->defaultScene = -1;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09006038
Syoyo Fujita83675312017-12-02 21:14:13 +09006039 // 1. Parse Asset
6040 {
David03ad33c2023-02-15 23:35:51 -06006041 detail::json_const_iterator it;
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006042 if (detail::FindMember(v, "asset", it) &&
6043 detail::IsObject(detail::GetValue(it))) {
David03ad33c2023-02-15 23:35:51 -06006044 const detail::json &root = detail::GetValue(it);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09006045
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006046 ParseAsset(&model->asset, err, root,
6047 store_original_json_for_extras_and_extensions_);
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00006048 }
6049 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09006050
David Siegelbec8a6d2023-06-06 15:36:07 +02006051 using detail::ForEachInArray;
jrkooncecba5d6c2019-08-29 11:26:22 -05006052
jrkooncecba5d6c2019-08-29 11:26:22 -05006053 // 2. Parse extensionUsed
6054 {
David03ad33c2023-02-15 23:35:51 -06006055 ForEachInArray(v, "extensionsUsed", [&](const detail::json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006056 std::string str;
David1f9a4b92023-02-15 22:56:18 -06006057 detail::GetString(o, str);
jrkooncecba5d6c2019-08-29 11:26:22 -05006058 model->extensionsUsed.emplace_back(std::move(str));
6059 return true;
6060 });
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09006061 }
6062
Syoyo Fujita83675312017-12-02 21:14:13 +09006063 {
David03ad33c2023-02-15 23:35:51 -06006064 ForEachInArray(v, "extensionsRequired", [&](const detail::json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006065 std::string str;
David1f9a4b92023-02-15 22:56:18 -06006066 detail::GetString(o, str);
jrkooncecba5d6c2019-08-29 11:26:22 -05006067 model->extensionsRequired.emplace_back(std::move(str));
6068 return true;
6069 });
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09006070 }
6071
Syoyo Fujita83675312017-12-02 21:14:13 +09006072 // 3. Parse Buffer
6073 {
David03ad33c2023-02-15 23:35:51 -06006074 bool success = ForEachInArray(v, "buffers", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006075 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006076 if (err) {
6077 (*err) += "`buffers' does not contain an JSON object.";
Syoyo Fujitabeded612016-05-01 20:03:43 +09006078 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006079 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006080 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006081 Buffer buffer;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006082 if (!ParseBuffer(&buffer, err, o,
6083 store_original_json_for_extras_and_extensions_, &fs,
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006084 &uri_cb, base_dir, max_external_file_size_, is_binary_,
6085 bin_data_, bin_size_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006086 return false;
6087 }
6088
6089 model->buffers.emplace_back(std::move(buffer));
6090 return true;
6091 });
6092
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006093 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006094 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09006095 }
6096 }
Syoyo Fujita83675312017-12-02 21:14:13 +09006097 // 4. Parse BufferView
6098 {
David03ad33c2023-02-15 23:35:51 -06006099 bool success = ForEachInArray(v, "bufferViews", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006100 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006101 if (err) {
6102 (*err) += "`bufferViews' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09006103 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006104 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006105 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006106 BufferView bufferView;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006107 if (!ParseBufferView(&bufferView, err, o,
6108 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006109 return false;
6110 }
6111
6112 model->bufferViews.emplace_back(std::move(bufferView));
6113 return true;
6114 });
6115
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006116 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006117 return false;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09006118 }
6119 }
6120
Syoyo Fujita83675312017-12-02 21:14:13 +09006121 // 5. Parse Accessor
6122 {
David03ad33c2023-02-15 23:35:51 -06006123 bool success = ForEachInArray(v, "accessors", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006124 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006125 if (err) {
6126 (*err) += "`accessors' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09006127 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006128 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006129 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006130 Accessor accessor;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006131 if (!ParseAccessor(&accessor, err, o,
6132 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006133 return false;
6134 }
6135
6136 model->accessors.emplace_back(std::move(accessor));
6137 return true;
6138 });
6139
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006140 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006141 return false;
Syoyo Fujitac2615632016-06-19 21:56:06 +09006142 }
6143 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09006144
Syoyo Fujita83675312017-12-02 21:14:13 +09006145 // 6. Parse Mesh
6146 {
David03ad33c2023-02-15 23:35:51 -06006147 bool success = ForEachInArray(v, "meshes", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006148 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006149 if (err) {
6150 (*err) += "`meshes' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09006151 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006152 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006153 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006154 Mesh mesh;
Nyall Dawson6e3d6662023-08-24 13:25:34 +10006155 if (!ParseMesh(&mesh, model, err, warn, o,
6156 store_original_json_for_extras_and_extensions_,
6157 strictness_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006158 return false;
6159 }
6160
6161 model->meshes.emplace_back(std::move(mesh));
6162 return true;
6163 });
6164
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006165 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006166 return false;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09006167 }
6168 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01006169
viperscape9df05802018-12-05 14:11:01 -05006170 // Assign missing bufferView target types
6171 // - Look for missing Mesh indices
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01006172 // - Look for missing Mesh attributes
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006173 for (auto &mesh : model->meshes) {
6174 for (auto &primitive : mesh.primitives) {
6175 if (primitive.indices >
6176 -1) // has indices from parsing step, must be Element Array Buffer
viperscape9df05802018-12-05 14:11:01 -05006177 {
Jeff McGlynn89152522019-04-25 16:33:56 -07006178 if (size_t(primitive.indices) >= model->accessors.size()) {
6179 if (err) {
6180 (*err) += "primitive indices accessor out of bounds";
6181 }
6182 return false;
6183 }
6184
haroonq8098a9e2023-10-05 11:16:52 +01006185 const auto bufferView =
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006186 model->accessors[size_t(primitive.indices)].bufferView;
haroonq8098a9e2023-10-05 11:16:52 +01006187 if (bufferView < 0) {
6188 // skip, bufferView could be null(-1) for certain extensions
Syoyo Fujita8387fdb2023-12-04 22:50:49 +09006189 } else if (size_t(bufferView) >= model->bufferViews.size()) {
Jeff McGlynn89152522019-04-25 16:33:56 -07006190 if (err) {
6191 (*err) += "accessor[" + std::to_string(primitive.indices) +
6192 "] invalid bufferView";
6193 }
6194 return false;
haroonq8098a9e2023-10-05 11:16:52 +01006195 } else {
6196 model->bufferViews[size_t(bufferView)].target =
6197 TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER;
6198 // we could optionally check if accessors' bufferView type is Scalar, as
6199 // it should be
Jeff McGlynn89152522019-04-25 16:33:56 -07006200 }
viperscape9df05802018-12-05 14:11:01 -05006201 }
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01006202
6203 for (auto &attribute : primitive.attributes) {
Nirmal Patele4132162022-09-06 09:16:31 -07006204 const auto accessorsIndex = size_t(attribute.second);
6205 if (accessorsIndex < model->accessors.size()) {
6206 const auto bufferView = model->accessors[accessorsIndex].bufferView;
6207 // bufferView could be null(-1) for sparse morph target
imallett56e10982022-10-07 10:35:16 -07006208 if (bufferView >= 0 && bufferView < (int)model->bufferViews.size()) {
Nirmal Patele4132162022-09-06 09:16:31 -07006209 model->bufferViews[size_t(bufferView)].target =
6210 TINYGLTF_TARGET_ARRAY_BUFFER;
6211 }
6212 }
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01006213 }
Marcin Kacprzakc3d67162020-01-22 13:13:35 +01006214
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09006215 for (auto &target : primitive.targets) {
6216 for (auto &attribute : target) {
Nirmal Patele4132162022-09-06 09:16:31 -07006217 const auto accessorsIndex = size_t(attribute.second);
6218 if (accessorsIndex < model->accessors.size()) {
6219 const auto bufferView = model->accessors[accessorsIndex].bufferView;
6220 // bufferView could be null(-1) for sparse morph target
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006221 if (bufferView >= 0 &&
6222 bufferView < (int)model->bufferViews.size()) {
Nirmal Patele4132162022-09-06 09:16:31 -07006223 model->bufferViews[size_t(bufferView)].target =
6224 TINYGLTF_TARGET_ARRAY_BUFFER;
6225 }
Rahul Sheth125e4a22020-07-13 13:56:50 -04006226 }
Marcin Kacprzakc3d67162020-01-22 13:13:35 +01006227 }
6228 }
viperscape9df05802018-12-05 14:11:01 -05006229 }
6230 }
6231
Syoyo Fujita83675312017-12-02 21:14:13 +09006232 // 7. Parse Node
6233 {
David03ad33c2023-02-15 23:35:51 -06006234 bool success = ForEachInArray(v, "nodes", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006235 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006236 if (err) {
6237 (*err) += "`nodes' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006238 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006239 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006240 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006241 Node node;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006242 if (!ParseNode(&node, err, o,
6243 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006244 return false;
6245 }
6246
6247 model->nodes.emplace_back(std::move(node));
6248 return true;
6249 });
6250
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006251 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006252 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006253 }
6254 }
6255
6256 // 8. Parse scenes.
6257 {
David03ad33c2023-02-15 23:35:51 -06006258 bool success = ForEachInArray(v, "scenes", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006259 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006260 if (err) {
6261 (*err) += "`scenes' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006262 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006263 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006264 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006265
6266 Scene scene;
Baranob_Ilya879cb472023-06-12 13:35:05 +04006267 if (!ParseScene(&scene, err, o,
6268 store_original_json_for_extras_and_extensions_)) {
6269 return false;
6270 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006271
jrkooncecba5d6c2019-08-29 11:26:22 -05006272 model->scenes.emplace_back(std::move(scene));
6273 return true;
6274 });
6275
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006276 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006277 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006278 }
6279 }
6280
6281 // 9. Parse default scenes.
6282 {
David03ad33c2023-02-15 23:35:51 -06006283 detail::json_const_iterator rootIt;
jrkooncecba5d6c2019-08-29 11:26:22 -05006284 int iVal;
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006285 if (detail::FindMember(v, "scene", rootIt) &&
6286 detail::GetInt(detail::GetValue(rootIt), iVal)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006287 model->defaultScene = iVal;
Syoyo Fujita83675312017-12-02 21:14:13 +09006288 }
6289 }
6290
6291 // 10. Parse Material
6292 {
David03ad33c2023-02-15 23:35:51 -06006293 bool success = ForEachInArray(v, "materials", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006294 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006295 if (err) {
6296 (*err) += "`materials' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006297 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006298 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006299 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006300 Material material;
6301 ParseStringProperty(&material.name, err, o, "name", false);
6302
Nyall Dawson02e8b8d2023-08-24 10:25:20 +10006303 if (!ParseMaterial(&material, err, warn, o,
Nyall Dawson8c85d5e2023-08-28 12:56:09 +10006304 store_original_json_for_extras_and_extensions_,
6305 strictness_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006306 return false;
6307 }
6308
6309 model->materials.emplace_back(std::move(material));
6310 return true;
6311 });
6312
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006313 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006314 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006315 }
6316 }
6317
6318 // 11. Parse Image
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09006319 void *load_image_user_data{nullptr};
6320
6321 LoadImageDataOption load_image_option;
6322
6323 if (user_image_loader_) {
6324 // Use user supplied pointer
6325 load_image_user_data = load_image_user_data_;
6326 } else {
6327 load_image_option.preserve_channels = preserve_image_channels_;
6328 load_image_user_data = reinterpret_cast<void *>(&load_image_option);
6329 }
6330
Syoyo Fujita83675312017-12-02 21:14:13 +09006331 {
jrkooncecba5d6c2019-08-29 11:26:22 -05006332 int idx = 0;
David03ad33c2023-02-15 23:35:51 -06006333 bool success = ForEachInArray(v, "images", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006334 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006335 if (err) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006336 (*err) += "image[" + std::to_string(idx) + "] is not a JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006337 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006338 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006339 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006340 Image image;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006341 if (!ParseImage(&image, idx, err, warn, o,
6342 store_original_json_for_extras_and_extensions_, base_dir,
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006343 max_external_file_size_, &fs, &uri_cb,
6344 &this->LoadImageData, load_image_user_data)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006345 return false;
6346 }
6347
6348 if (image.bufferView != -1) {
6349 // Load image from the buffer view.
6350 if (size_t(image.bufferView) >= model->bufferViews.size()) {
6351 if (err) {
6352 std::stringstream ss;
6353 ss << "image[" << idx << "] bufferView \"" << image.bufferView
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006354 << "\" not found in the scene." << std::endl;
jrkooncecba5d6c2019-08-29 11:26:22 -05006355 (*err) += ss.str();
6356 }
6357 return false;
6358 }
6359
6360 const BufferView &bufferView =
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006361 model->bufferViews[size_t(image.bufferView)];
jrkooncecba5d6c2019-08-29 11:26:22 -05006362 if (size_t(bufferView.buffer) >= model->buffers.size()) {
6363 if (err) {
6364 std::stringstream ss;
6365 ss << "image[" << idx << "] buffer \"" << bufferView.buffer
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006366 << "\" not found in the scene." << std::endl;
jrkooncecba5d6c2019-08-29 11:26:22 -05006367 (*err) += ss.str();
6368 }
6369 return false;
6370 }
6371 const Buffer &buffer = model->buffers[size_t(bufferView.buffer)];
6372
6373 if (*LoadImageData == nullptr) {
6374 if (err) {
6375 (*err) += "No LoadImageData callback specified.\n";
6376 }
6377 return false;
6378 }
6379 bool ret = LoadImageData(
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006380 &image, idx, err, warn, image.width, image.height,
6381 &buffer.data[bufferView.byteOffset],
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09006382 static_cast<int>(bufferView.byteLength), load_image_user_data);
jrkooncecba5d6c2019-08-29 11:26:22 -05006383 if (!ret) {
6384 return false;
6385 }
6386 }
6387
6388 model->images.emplace_back(std::move(image));
6389 ++idx;
6390 return true;
6391 });
6392
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006393 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006394 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006395 }
6396 }
6397
6398 // 12. Parse Texture
6399 {
David03ad33c2023-02-15 23:35:51 -06006400 bool success = ForEachInArray(v, "textures", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006401 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006402 if (err) {
6403 (*err) += "`textures' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006404 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006405 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006406 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006407 Texture texture;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006408 if (!ParseTexture(&texture, err, o,
6409 store_original_json_for_extras_and_extensions_,
6410 base_dir)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006411 return false;
6412 }
6413
6414 model->textures.emplace_back(std::move(texture));
6415 return true;
6416 });
6417
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006418 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006419 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006420 }
6421 }
6422
6423 // 13. Parse Animation
6424 {
David03ad33c2023-02-15 23:35:51 -06006425 bool success = ForEachInArray(v, "animations", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006426 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006427 if (err) {
6428 (*err) += "`animations' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006429 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006430 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006431 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006432 Animation animation;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006433 if (!ParseAnimation(&animation, err, o,
6434 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006435 return false;
6436 }
6437
6438 model->animations.emplace_back(std::move(animation));
6439 return true;
6440 });
6441
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006442 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006443 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006444 }
6445 }
6446
6447 // 14. Parse Skin
6448 {
David03ad33c2023-02-15 23:35:51 -06006449 bool success = ForEachInArray(v, "skins", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006450 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006451 if (err) {
6452 (*err) += "`skins' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006453 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006454 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006455 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006456 Skin skin;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006457 if (!ParseSkin(&skin, err, o,
6458 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006459 return false;
6460 }
6461
6462 model->skins.emplace_back(std::move(skin));
6463 return true;
6464 });
6465
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006466 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006467 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006468 }
6469 }
6470
6471 // 15. Parse Sampler
6472 {
David03ad33c2023-02-15 23:35:51 -06006473 bool success = ForEachInArray(v, "samplers", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006474 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006475 if (err) {
6476 (*err) += "`samplers' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006477 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006478 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006479 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006480 Sampler sampler;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006481 if (!ParseSampler(&sampler, err, o,
6482 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006483 return false;
6484 }
6485
6486 model->samplers.emplace_back(std::move(sampler));
6487 return true;
6488 });
6489
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006490 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006491 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006492 }
6493 }
6494
6495 // 16. Parse Camera
6496 {
David03ad33c2023-02-15 23:35:51 -06006497 bool success = ForEachInArray(v, "cameras", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006498 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006499 if (err) {
6500 (*err) += "`cameras' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006501 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006502 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006503 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006504 Camera camera;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006505 if (!ParseCamera(&camera, err, o,
6506 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006507 return false;
6508 }
6509
6510 model->cameras.emplace_back(std::move(camera));
6511 return true;
6512 });
6513
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006514 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006515 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006516 }
6517 }
6518
David Siegel22cafa12023-06-05 22:18:59 +02006519 // 17. Parse Extras & Extensions
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006520 ParseExtrasAndExtensions(model, err, v,
6521 store_original_json_for_extras_and_extensions_);
Selmar09d2ff12018-03-15 17:30:42 +01006522
6523 // 18. Specific extension implementations
Syoyo Fujita83675312017-12-02 21:14:13 +09006524 {
David03ad33c2023-02-15 23:35:51 -06006525 detail::json_const_iterator rootIt;
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006526 if (detail::FindMember(v, "extensions", rootIt) &&
6527 detail::IsObject(detail::GetValue(rootIt))) {
David03ad33c2023-02-15 23:35:51 -06006528 const detail::json &root = detail::GetValue(rootIt);
Syoyo Fujita83675312017-12-02 21:14:13 +09006529
David03ad33c2023-02-15 23:35:51 -06006530 detail::json_const_iterator it(detail::ObjectBegin(root));
6531 detail::json_const_iterator itEnd(detail::ObjectEnd(root));
Syoyo Fujita83675312017-12-02 21:14:13 +09006532 for (; it != itEnd; ++it) {
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02006533 // parse KHR_lights_punctual extension
David1f9a4b92023-02-15 22:56:18 -06006534 std::string key(detail::GetKey(it));
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006535 if ((key == "KHR_lights_punctual") &&
6536 detail::IsObject(detail::GetValue(it))) {
David03ad33c2023-02-15 23:35:51 -06006537 const detail::json &object = detail::GetValue(it);
6538 detail::json_const_iterator itLight;
David1f9a4b92023-02-15 22:56:18 -06006539 if (detail::FindMember(object, "lights", itLight)) {
David03ad33c2023-02-15 23:35:51 -06006540 const detail::json &lights = detail::GetValue(itLight);
David1f9a4b92023-02-15 22:56:18 -06006541 if (!detail::IsArray(lights)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006542 continue;
Syoyo Fujita83675312017-12-02 21:14:13 +09006543 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006544
David1f9a4b92023-02-15 22:56:18 -06006545 auto arrayIt(detail::ArrayBegin(lights));
6546 auto arrayItEnd(detail::ArrayEnd(lights));
jrkooncecba5d6c2019-08-29 11:26:22 -05006547 for (; arrayIt != arrayItEnd; ++arrayIt) {
6548 Light light;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006549 if (!ParseLight(&light, err, *arrayIt,
6550 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006551 return false;
6552 }
6553 model->lights.emplace_back(std::move(light));
6554 }
Syoyo Fujita83675312017-12-02 21:14:13 +09006555 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01006556 }
Baranob_Ilya78864c82023-06-12 10:43:52 +04006557 // parse KHR_audio extension
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006558 if ((key == "KHR_audio") && detail::IsObject(detail::GetValue(it))) {
Baranob_Ilya78864c82023-06-12 10:43:52 +04006559 const detail::json &object = detail::GetValue(it);
6560 detail::json_const_iterator itKhrAudio;
6561 if (detail::FindMember(object, "emitters", itKhrAudio)) {
6562 const detail::json &emitters = detail::GetValue(itKhrAudio);
6563 if (!detail::IsArray(emitters)) {
6564 continue;
6565 }
6566
6567 auto arrayIt(detail::ArrayBegin(emitters));
6568 auto arrayItEnd(detail::ArrayEnd(emitters));
6569 for (; arrayIt != arrayItEnd; ++arrayIt) {
6570 AudioEmitter emitter;
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006571 if (!ParseAudioEmitter(
6572 &emitter, err, *arrayIt,
6573 store_original_json_for_extras_and_extensions_)) {
Baranob_Ilya78864c82023-06-12 10:43:52 +04006574 return false;
6575 }
6576 model->audioEmitters.emplace_back(std::move(emitter));
6577 }
6578 }
6579
6580 if (detail::FindMember(object, "sources", itKhrAudio)) {
6581 const detail::json &sources = detail::GetValue(itKhrAudio);
6582 if (!detail::IsArray(sources)) {
6583 continue;
6584 }
6585
6586 auto arrayIt(detail::ArrayBegin(sources));
6587 auto arrayItEnd(detail::ArrayEnd(sources));
6588 for (; arrayIt != arrayItEnd; ++arrayIt) {
6589 AudioSource source;
6590 if (!ParseAudioSource(
6591 &source, err, *arrayIt,
6592 store_original_json_for_extras_and_extensions_)) {
6593 return false;
6594 }
6595 model->audioSources.emplace_back(std::move(source));
6596 }
6597 }
6598 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01006599 }
6600 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01006601 }
Syoyo Fujita83675312017-12-02 21:14:13 +09006602
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09006603 return true;
6604}
6605
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006606bool TinyGLTF::LoadASCIIFromString(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006607 std::string *warn, const char *str,
6608 unsigned int length,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006609 const std::string &base_dir,
6610 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006611 is_binary_ = false;
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09006612 bin_data_ = nullptr;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006613 bin_size_ = 0;
6614
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006615 return LoadFromString(model, err, warn, str, length, base_dir,
6616 check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006617}
6618
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006619bool TinyGLTF::LoadASCIIFromFile(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006620 std::string *warn, const std::string &filename,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006621 unsigned int check_sections) {
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006622 std::stringstream ss;
6623
Paolo Jovone6601bf2018-07-07 20:43:33 +02006624 if (fs.ReadWholeFile == nullptr) {
6625 // Programmer error, assert() ?
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006626 ss << "Failed to read file: " << filename
6627 << ": one or more FS callback not set" << std::endl;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09006628 if (err) {
6629 (*err) = ss.str();
6630 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006631 return false;
6632 }
6633
Paolo Jovone6601bf2018-07-07 20:43:33 +02006634 std::vector<unsigned char> data;
6635 std::string fileerr;
6636 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006637 if (!fileread) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02006638 ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
6639 if (err) {
6640 (*err) = ss.str();
6641 }
6642 return false;
6643 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006644
Paolo Jovone6601bf2018-07-07 20:43:33 +02006645 size_t sz = data.size();
Luke San Antoniob310b4a2016-06-23 14:17:37 -04006646 if (sz == 0) {
6647 if (err) {
6648 (*err) = "Empty file.";
6649 }
6650 return false;
6651 }
6652
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006653 std::string basedir = GetBaseDir(filename);
6654
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006655 bool ret = LoadASCIIFromString(
6656 model, err, warn, reinterpret_cast<const char *>(&data.at(0)),
6657 static_cast<unsigned int>(data.size()), basedir, check_sections);
Luke San Antonio9e36b612016-06-23 14:10:51 -04006658
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006659 return ret;
6660}
6661
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006662bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006663 std::string *warn,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006664 const unsigned char *bytes,
6665 unsigned int size,
6666 const std::string &base_dir,
6667 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006668 if (size < 20) {
6669 if (err) {
6670 (*err) = "Too short data size for glTF Binary.";
6671 }
6672 return false;
6673 }
6674
Syoyo Fujitabeded612016-05-01 20:03:43 +09006675 if (bytes[0] == 'g' && bytes[1] == 'l' && bytes[2] == 'T' &&
6676 bytes[3] == 'F') {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006677 // ok
6678 } else {
6679 if (err) {
6680 (*err) = "Invalid magic.";
6681 }
6682 return false;
6683 }
6684
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006685 unsigned int version; // 4 bytes
6686 unsigned int length; // 4 bytes
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006687 unsigned int chunk0_length; // 4 bytes
6688 unsigned int chunk0_format; // 4 bytes;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006689
Syoyo Fujitabeded612016-05-01 20:03:43 +09006690 memcpy(&version, bytes + 4, 4);
6691 swap4(&version);
Syoyo Fujita4fea26f2024-01-24 05:43:27 +09006692 memcpy(&length, bytes + 8, 4); // Total glb size, including header and all chunks.
Syoyo Fujitabeded612016-05-01 20:03:43 +09006693 swap4(&length);
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006694 memcpy(&chunk0_length, bytes + 12, 4); // JSON data length
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006695 swap4(&chunk0_length);
6696 memcpy(&chunk0_format, bytes + 16, 4);
6697 swap4(&chunk0_format);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006698
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006699 // https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#binary-gltf-layout
6700 //
Syoyo Fujitae69069d2018-03-15 22:01:18 -05006701 // In case the Bin buffer is not present, the size is exactly 20 + size of
6702 // JSON contents,
6703 // so use "greater than" operator.
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006704 //
6705 // https://github.com/syoyo/tinygltf/issues/372
6706 // Use 64bit uint to avoid integer overflow.
Syoyo Fujitac670f082022-09-17 19:52:25 +09006707 uint64_t header_and_json_size = 20ull + uint64_t(chunk0_length);
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006708
rhiskey8acf8612023-12-04 17:11:59 +03006709 if (header_and_json_size > (std::numeric_limits<uint32_t>::max)()) {
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006710 // Do not allow 4GB or more GLB data.
Syoyo Fujita8387fdb2023-12-04 22:50:49 +09006711 if (err) {
6712 (*err) = "Invalid glTF binary. GLB data exceeds 4GB.";
6713 }
6714 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006715 }
6716
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006717 if ((header_and_json_size > uint64_t(size)) || (chunk0_length < 1) ||
6718 (length > size) || (header_and_json_size > uint64_t(length)) ||
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006719 (chunk0_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006720 if (err) {
6721 (*err) = "Invalid glTF binary.";
6722 }
6723 return false;
6724 }
6725
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006726 // Padding check
6727 // The start and the end of each chunk must be aligned to a 4-byte boundary.
6728 // No padding check for chunk0 start since its 4byte-boundary is ensured.
Syoyo Fujitac670f082022-09-17 19:52:25 +09006729 if ((header_and_json_size % 4) != 0) {
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006730 if (err) {
6731 (*err) = "JSON Chunk end does not aligned to a 4-byte boundary.";
6732 }
Syoyo Fujita8387fdb2023-12-04 22:50:49 +09006733 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006734 }
6735
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006736 // std::cout << "header_and_json_size = " << header_and_json_size << "\n";
6737 // std::cout << "length = " << length << "\n";
Syoyo Fujita612e5782022-09-18 21:01:39 +09006738
Syoyo Fujitac670f082022-09-17 19:52:25 +09006739 // Chunk1(BIN) data
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006740 // The spec says: When the binary buffer is empty or when it is stored by
6741 // other means, this chunk SHOULD be omitted. So when header + JSON data ==
6742 // binary size, Chunk1 is omitted.
Syoyo Fujita612e5782022-09-18 21:01:39 +09006743 if (header_and_json_size == uint64_t(length)) {
Syoyo Fujitac670f082022-09-17 19:52:25 +09006744 bin_data_ = nullptr;
6745 bin_size_ = 0;
6746 } else {
6747 // Read Chunk1 info(BIN data)
Syoyo Fujita4fea26f2024-01-24 05:43:27 +09006748 //
6749 // issue-440:
6750 // 'SHOULD' in glTF spec means 'RECOMMENDED',
6751 // So there is a situation that Chunk1(BIN) is composed of zero-sized BIN data
6752 // (chunksize(0) + binformat(BIN) = 8bytes).
6753 //
6754 if ((header_and_json_size + 8ull) > uint64_t(length)) {
Syoyo Fujitac670f082022-09-17 19:52:25 +09006755 if (err) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006756 (*err) =
6757 "Insufficient storage space for Chunk1(BIN data). At least Chunk1 "
Syoyo Fujita4fea26f2024-01-24 05:43:27 +09006758 "Must have 8 or more bytes, but got " +
Nyall Dawson8e9aadf2023-08-24 11:47:23 +10006759 std::to_string((header_and_json_size + 8ull) - uint64_t(length)) +
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006760 ".\n";
Syoyo Fujitac670f082022-09-17 19:52:25 +09006761 }
6762 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006763 }
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006764
Syoyo Fujita4fea26f2024-01-24 05:43:27 +09006765 unsigned int chunk1_length{0}; // 4 bytes
6766 unsigned int chunk1_format{0}; // 4 bytes;
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006767 memcpy(&chunk1_length, bytes + header_and_json_size,
Syoyo Fujita4fea26f2024-01-24 05:43:27 +09006768 4); // Bin data length
Syoyo Fujitac670f082022-09-17 19:52:25 +09006769 swap4(&chunk1_length);
6770 memcpy(&chunk1_format, bytes + header_and_json_size + 4, 4);
6771 swap4(&chunk1_format);
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006772
Syoyo Fujita4fea26f2024-01-24 05:43:27 +09006773 if (chunk1_format != 0x004e4942) {
Syoyo Fujitac670f082022-09-17 19:52:25 +09006774 if (err) {
Syoyo Fujita4fea26f2024-01-24 05:43:27 +09006775 (*err) = "Invalid chunkType for Chunk1.";
Syoyo Fujitac670f082022-09-17 19:52:25 +09006776 }
6777 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006778 }
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006779
Syoyo Fujita4fea26f2024-01-24 05:43:27 +09006780 if (chunk1_length == 0) {
6781
6782 if (header_and_json_size + 8 > uint64_t(length)) {
Nyall Dawson4d119d72023-09-06 17:35:25 +10006783 if (err) {
Syoyo Fujita4fea26f2024-01-24 05:43:27 +09006784 (*err) = "BIN Chunk header location exceeds the GLB size.";
Nyall Dawson4d119d72023-09-06 17:35:25 +10006785 }
6786 return false;
6787 }
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006788
Syoyo Fujita4fea26f2024-01-24 05:43:27 +09006789 bin_data_ = nullptr;
6790
6791 } else {
6792
6793 // When BIN chunk size is not zero, at least Chunk1 should have 12 bytes(8 bytes(header) + 4 bytes(bin
6794 // payload could be 1~3 bytes, but need to be aligned to 4 bytes)
6795
6796 if (chunk1_length < 4) {
6797 if (err) {
6798 (*err) = "Insufficient Chunk1(BIN) data size.";
6799 }
6800 return false;
Syoyo Fujitac670f082022-09-17 19:52:25 +09006801 }
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006802
Syoyo Fujita4fea26f2024-01-24 05:43:27 +09006803 if ((chunk1_length % 4) != 0) {
6804 if (strictness_==ParseStrictness::Permissive) {
6805 if (warn) {
6806 (*warn) += "BIN Chunk end is not aligned to a 4-byte boundary.\n";
6807 }
6808 }
6809 else {
6810 if (err) {
6811 (*err) = "BIN Chunk end is not aligned to a 4-byte boundary.";
6812 }
6813 return false;
6814 }
Syoyo Fujitac670f082022-09-17 19:52:25 +09006815 }
Syoyo Fujita4fea26f2024-01-24 05:43:27 +09006816
6817 // +8 chunk1 header size.
6818 if (uint64_t(chunk1_length) + header_and_json_size + 8 > uint64_t(length)) {
6819 if (err) {
6820 (*err) = "BIN Chunk data length exceeds the GLB size.";
6821 }
6822 return false;
6823 }
6824
6825 bin_data_ = bytes + header_and_json_size +
6826 8; // 4 bytes (bin_buffer_length) + 4 bytes(bin_buffer_format)
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006827 }
Syoyo Fujitac670f082022-09-17 19:52:25 +09006828
Syoyo Fujitac670f082022-09-17 19:52:25 +09006829 bin_size_ = size_t(chunk1_length);
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006830 }
6831
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006832 is_binary_ = true;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006833
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006834 bool ret = LoadFromString(model, err, warn,
6835 reinterpret_cast<const char *>(&bytes[20]),
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006836 chunk0_length, base_dir, check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006837 if (!ret) {
6838 return ret;
6839 }
6840
6841 return true;
6842}
6843
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006844bool TinyGLTF::LoadBinaryFromFile(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006845 std::string *warn,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006846 const std::string &filename,
6847 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006848 std::stringstream ss;
6849
Paolo Jovone6601bf2018-07-07 20:43:33 +02006850 if (fs.ReadWholeFile == nullptr) {
6851 // Programmer error, assert() ?
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006852 ss << "Failed to read file: " << filename
6853 << ": one or more FS callback not set" << std::endl;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006854 if (err) {
6855 (*err) = ss.str();
6856 }
6857 return false;
6858 }
6859
Paolo Jovone6601bf2018-07-07 20:43:33 +02006860 std::vector<unsigned char> data;
6861 std::string fileerr;
6862 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006863 if (!fileread) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02006864 ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
6865 if (err) {
6866 (*err) = ss.str();
6867 }
6868 return false;
6869 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006870
Syoyo Fujitabeded612016-05-01 20:03:43 +09006871 std::string basedir = GetBaseDir(filename);
6872
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006873 bool ret = LoadBinaryFromMemory(model, err, warn, &data.at(0),
6874 static_cast<unsigned int>(data.size()),
6875 basedir, check_sections);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006876
6877 return ret;
6878}
6879
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006880///////////////////////
6881// GLTF Serialization
6882///////////////////////
David1f9a4b92023-02-15 22:56:18 -06006883namespace detail {
David03ad33c2023-02-15 23:35:51 -06006884detail::json JsonFromString(const char *s) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006885#ifdef TINYGLTF_USE_RAPIDJSON
David03ad33c2023-02-15 23:35:51 -06006886 return detail::json(s, detail::GetAllocator());
jrkooncecba5d6c2019-08-29 11:26:22 -05006887#else
David03ad33c2023-02-15 23:35:51 -06006888 return detail::json(s);
jrkooncecba5d6c2019-08-29 11:26:22 -05006889#endif
jrkoonce63419a12019-09-03 17:06:41 -05006890}
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006891
David03ad33c2023-02-15 23:35:51 -06006892void JsonAssign(detail::json &dest, const detail::json &src) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006893#ifdef TINYGLTF_USE_RAPIDJSON
David03ad33c2023-02-15 23:35:51 -06006894 dest.CopyFrom(src, detail::GetAllocator());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006895#else
6896 dest = src;
6897#endif
6898}
6899
David03ad33c2023-02-15 23:35:51 -06006900void JsonAddMember(detail::json &o, const char *key, detail::json &&value) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006901#ifdef TINYGLTF_USE_RAPIDJSON
6902 if (!o.IsObject()) {
6903 o.SetObject();
6904 }
Syoyo Fujita147a00a2023-06-04 05:45:24 +09006905
6906 // Issue 420.
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006907 // AddMember may create duplicated key, so use [] API when a key already
6908 // exists.
Syoyo Fujita147a00a2023-06-04 05:45:24 +09006909 // https://github.com/Tencent/rapidjson/issues/771#issuecomment-254386863
6910 detail::json_const_iterator it;
6911 if (detail::FindMember(o, key, it)) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006912 o[key] = std::move(value); // replace
Syoyo Fujita147a00a2023-06-04 05:45:24 +09006913 } else {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006914 o.AddMember(detail::json(key, detail::GetAllocator()), std::move(value),
6915 detail::GetAllocator());
Syoyo Fujita147a00a2023-06-04 05:45:24 +09006916 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006917#else
6918 o[key] = std::move(value);
6919#endif
6920}
6921
David03ad33c2023-02-15 23:35:51 -06006922void JsonPushBack(detail::json &o, detail::json &&value) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006923#ifdef TINYGLTF_USE_RAPIDJSON
David03ad33c2023-02-15 23:35:51 -06006924 o.PushBack(std::move(value), detail::GetAllocator());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006925#else
6926 o.push_back(std::move(value));
6927#endif
6928}
6929
David03ad33c2023-02-15 23:35:51 -06006930bool JsonIsNull(const detail::json &o) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006931#ifdef TINYGLTF_USE_RAPIDJSON
6932 return o.IsNull();
6933#else
6934 return o.is_null();
6935#endif
6936}
6937
David03ad33c2023-02-15 23:35:51 -06006938void JsonSetObject(detail::json &o) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006939#ifdef TINYGLTF_USE_RAPIDJSON
6940 o.SetObject();
6941#else
6942 o = o.object({});
6943#endif
6944}
6945
David03ad33c2023-02-15 23:35:51 -06006946void JsonReserveArray(detail::json &o, size_t s) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006947#ifdef TINYGLTF_USE_RAPIDJSON
6948 o.SetArray();
David03ad33c2023-02-15 23:35:51 -06006949 o.Reserve(static_cast<rapidjson::SizeType>(s), detail::GetAllocator());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006950#endif
6951 (void)(o);
6952 (void)(s);
6953}
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006954} // namespace detail
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006955
David03ad33c2023-02-15 23:35:51 -06006956// typedef std::pair<std::string, detail::json> json_object_pair;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006957
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006958template <typename T>
6959static void SerializeNumberProperty(const std::string &key, T number,
David03ad33c2023-02-15 23:35:51 -06006960 detail::json &obj) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006961 // obj.insert(
David03ad33c2023-02-15 23:35:51 -06006962 // json_object_pair(key, detail::json(static_cast<double>(number))));
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006963 // obj[key] = static_cast<double>(number);
David03ad33c2023-02-15 23:35:51 -06006964 detail::JsonAddMember(obj, key.c_str(), detail::json(number));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006965}
6966
Ivo van Dongen272a9df2020-05-29 11:24:11 +03006967#ifdef TINYGLTF_USE_RAPIDJSON
6968template <>
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006969void SerializeNumberProperty(const std::string &key, size_t number,
6970 detail::json &obj) {
6971 detail::JsonAddMember(obj, key.c_str(),
6972 detail::json(static_cast<uint64_t>(number)));
Ivo van Dongen272a9df2020-05-29 11:24:11 +03006973}
6974#endif
6975
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006976template <typename T>
6977static void SerializeNumberArrayProperty(const std::string &key,
6978 const std::vector<T> &value,
David03ad33c2023-02-15 23:35:51 -06006979 detail::json &obj) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006980 if (value.empty()) return;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006981
David03ad33c2023-02-15 23:35:51 -06006982 detail::json ary;
David1f9a4b92023-02-15 22:56:18 -06006983 detail::JsonReserveArray(ary, value.size());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006984 for (const auto &s : value) {
David03ad33c2023-02-15 23:35:51 -06006985 detail::JsonPushBack(ary, detail::json(s));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006986 }
David1f9a4b92023-02-15 22:56:18 -06006987 detail::JsonAddMember(obj, key.c_str(), std::move(ary));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006988}
6989
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006990static void SerializeStringProperty(const std::string &key,
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02006991 const std::string &value,
6992 detail::json &obj) {
6993 detail::JsonAddMember(obj, key.c_str(),
6994 detail::JsonFromString(value.c_str()));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006995}
6996
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006997static void SerializeStringArrayProperty(const std::string &key,
6998 const std::vector<std::string> &value,
David03ad33c2023-02-15 23:35:51 -06006999 detail::json &obj) {
7000 detail::json ary;
David1f9a4b92023-02-15 22:56:18 -06007001 detail::JsonReserveArray(ary, value.size());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007002 for (auto &s : value) {
David1f9a4b92023-02-15 22:56:18 -06007003 detail::JsonPushBack(ary, detail::JsonFromString(s.c_str()));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007004 }
David1f9a4b92023-02-15 22:56:18 -06007005 detail::JsonAddMember(obj, key.c_str(), std::move(ary));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007006}
7007
David03ad33c2023-02-15 23:35:51 -06007008static bool ValueToJson(const Value &value, detail::json *ret) {
7009 detail::json obj;
jrkooncecba5d6c2019-08-29 11:26:22 -05007010#ifdef TINYGLTF_USE_RAPIDJSON
7011 switch (value.Type()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007012 case REAL_TYPE:
7013 obj.SetDouble(value.Get<double>());
7014 break;
7015 case INT_TYPE:
7016 obj.SetInt(value.Get<int>());
7017 break;
7018 case BOOL_TYPE:
7019 obj.SetBool(value.Get<bool>());
7020 break;
7021 case STRING_TYPE:
David03ad33c2023-02-15 23:35:51 -06007022 obj.SetString(value.Get<std::string>().c_str(), detail::GetAllocator());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007023 break;
7024 case ARRAY_TYPE: {
7025 obj.SetArray();
7026 obj.Reserve(static_cast<rapidjson::SizeType>(value.ArrayLen()),
David03ad33c2023-02-15 23:35:51 -06007027 detail::GetAllocator());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007028 for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
7029 Value elementValue = value.Get(int(i));
David03ad33c2023-02-15 23:35:51 -06007030 detail::json elementJson;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007031 if (ValueToJson(value.Get(int(i)), &elementJson))
David03ad33c2023-02-15 23:35:51 -06007032 obj.PushBack(std::move(elementJson), detail::GetAllocator());
jrkooncecba5d6c2019-08-29 11:26:22 -05007033 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007034 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05007035 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007036 case BINARY_TYPE:
7037 // TODO
David03ad33c2023-02-15 23:35:51 -06007038 // obj = detail::json(value.Get<std::vector<unsigned char>>());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007039 return false;
7040 break;
7041 case OBJECT_TYPE: {
7042 obj.SetObject();
7043 Value::Object objMap = value.Get<Value::Object>();
7044 for (auto &it : objMap) {
David03ad33c2023-02-15 23:35:51 -06007045 detail::json elementJson;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007046 if (ValueToJson(it.second, &elementJson)) {
David03ad33c2023-02-15 23:35:51 -06007047 obj.AddMember(detail::json(it.first.c_str(), detail::GetAllocator()),
7048 std::move(elementJson), detail::GetAllocator());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007049 }
7050 }
7051 break;
7052 }
7053 case NULL_TYPE:
7054 default:
7055 return false;
jrkooncecba5d6c2019-08-29 11:26:22 -05007056 }
7057#else
Ben Buzbee3b735bb2018-04-24 11:39:30 -07007058 switch (value.Type()) {
Syoyo Fujita150f2432019-07-25 19:22:44 +09007059 case REAL_TYPE:
David03ad33c2023-02-15 23:35:51 -06007060 obj = detail::json(value.Get<double>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07007061 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02007062 case INT_TYPE:
David03ad33c2023-02-15 23:35:51 -06007063 obj = detail::json(value.Get<int>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07007064 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02007065 case BOOL_TYPE:
David03ad33c2023-02-15 23:35:51 -06007066 obj = detail::json(value.Get<bool>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07007067 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02007068 case STRING_TYPE:
David03ad33c2023-02-15 23:35:51 -06007069 obj = detail::json(value.Get<std::string>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07007070 break;
7071 case ARRAY_TYPE: {
James Ward9b4e1ea2024-03-25 17:50:33 +00007072 for (size_t i = 0; i < value.ArrayLen(); ++i) {
7073 Value elementValue = value.Get(i);
David03ad33c2023-02-15 23:35:51 -06007074 detail::json elementJson;
James Ward9b4e1ea2024-03-25 17:50:33 +00007075 if (ValueToJson(value.Get(i), &elementJson))
Selmar Kokfa7022f2018-04-04 18:10:20 +02007076 obj.push_back(elementJson);
7077 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07007078 break;
7079 }
Selmar Kokfa7022f2018-04-04 18:10:20 +02007080 case BINARY_TYPE:
Ben Buzbee3b735bb2018-04-24 11:39:30 -07007081 // TODO
7082 // obj = json(value.Get<std::vector<unsigned char>>());
Selmar Kokfa7022f2018-04-04 18:10:20 +02007083 return false;
7084 break;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07007085 case OBJECT_TYPE: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02007086 Value::Object objMap = value.Get<Value::Object>();
Ben Buzbee3b735bb2018-04-24 11:39:30 -07007087 for (auto &it : objMap) {
David03ad33c2023-02-15 23:35:51 -06007088 detail::json elementJson;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07007089 if (ValueToJson(it.second, &elementJson)) obj[it.first] = elementJson;
Selmar Kokfa7022f2018-04-04 18:10:20 +02007090 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07007091 break;
7092 }
7093 case NULL_TYPE:
7094 default:
7095 return false;
Selmar Kokfa7022f2018-04-04 18:10:20 +02007096 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007097#endif
7098 if (ret) *ret = std::move(obj);
Selmar Kokfa7022f2018-04-04 18:10:20 +02007099 return true;
7100}
7101
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007102static void SerializeValue(const std::string &key, const Value &value,
David03ad33c2023-02-15 23:35:51 -06007103 detail::json &obj) {
7104 detail::json ret;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007105 if (ValueToJson(value, &ret)) {
David1f9a4b92023-02-15 22:56:18 -06007106 detail::JsonAddMember(obj, key.c_str(), std::move(ret));
jrkooncecba5d6c2019-08-29 11:26:22 -05007107 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007108}
7109
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007110static void SerializeGltfBufferData(const std::vector<unsigned char> &data,
David03ad33c2023-02-15 23:35:51 -06007111 detail::json &o) {
johan bowald30c53472018-03-30 11:49:36 +02007112 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09007113 if (data.size() > 0) {
7114 std::string encodedData =
7115 base64_encode(&data[0], static_cast<unsigned int>(data.size()));
7116 SerializeStringProperty("uri", header + encodedData, o);
7117 } else {
7118 // Issue #229
imallettd9ce9eb2022-10-07 10:37:09 -07007119 // size 0 is allowed. Just emit mime header.
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09007120 SerializeStringProperty("uri", header, o);
7121 }
johan bowald30c53472018-03-30 11:49:36 +02007122}
7123
Selmar Koke4677492018-10-25 16:45:49 +02007124static bool SerializeGltfBufferData(const std::vector<unsigned char> &data,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05007125 const std::string &binFilename) {
Harokyangfb256602019-10-30 16:13:52 +08007126#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007127#if defined(__GLIBCXX__) // mingw
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007128 int file_descriptor = _wopen(UTF8ToWchar(binFilename).c_str(),
TheDev22dfeab2024-03-25 18:21:20 +03007129 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY, _S_IWRITE);
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007130 __gnu_cxx::stdio_filebuf<char> wfile_buf(
7131 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09007132 std::ostream output(&wfile_buf);
7133 if (!wfile_buf.is_open()) return false;
7134#elif defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08007135 std::ofstream output(UTF8ToWchar(binFilename).c_str(), std::ofstream::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09007136 if (!output.is_open()) return false;
Harokyangfb256602019-10-30 16:13:52 +08007137#else
Syoyo Fujitad42767e2018-03-15 21:52:00 -05007138 std::ofstream output(binFilename.c_str(), std::ofstream::binary);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00007139 if (!output.is_open()) return false;
Syoyo Fujita45cac782019-11-09 20:42:55 +09007140#endif
7141#else
7142 std::ofstream output(binFilename.c_str(), std::ofstream::binary);
7143 if (!output.is_open()) return false;
7144#endif
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09007145 if (data.size() > 0) {
7146 output.write(reinterpret_cast<const char *>(&data[0]),
7147 std::streamsize(data.size()));
7148 } else {
7149 // Issue #229
7150 // size 0 will be still valid buffer data.
7151 // write empty file.
7152 }
Selmar Koke4677492018-10-25 16:45:49 +02007153 return true;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007154}
7155
Syoyo Fujitacea69e32019-08-20 17:10:30 +09007156#if 0 // FIXME(syoyo): not used. will be removed in the future release.
David03ad33c2023-02-15 23:35:51 -06007157static void SerializeParameterMap(ParameterMap &param, detail::json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007158 for (ParameterMap::iterator paramIt = param.begin(); paramIt != param.end();
7159 ++paramIt) {
7160 if (paramIt->second.number_array.size()) {
7161 SerializeNumberArrayProperty<double>(paramIt->first,
7162 paramIt->second.number_array, o);
7163 } else if (paramIt->second.json_double_value.size()) {
David03ad33c2023-02-15 23:35:51 -06007164 detail::json json_double_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007165 for (std::map<std::string, double>::iterator it =
7166 paramIt->second.json_double_value.begin();
7167 it != paramIt->second.json_double_value.end(); ++it) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07007168 if (it->first == "index") {
7169 json_double_value[it->first] = paramIt->second.TextureIndex();
7170 } else {
7171 json_double_value[it->first] = it->second;
7172 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007173 }
7174
Syoyo Fujita83675312017-12-02 21:14:13 +09007175 o[paramIt->first] = json_double_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007176 } else if (!paramIt->second.string_value.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007177 SerializeStringProperty(paramIt->first, paramIt->second.string_value, o);
Ben Buzbeef6af2242018-04-25 15:13:05 -07007178 } else if (paramIt->second.has_number_value) {
7179 o[paramIt->first] = paramIt->second.number_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007180 } else {
Syoyo Fujita83675312017-12-02 21:14:13 +09007181 o[paramIt->first] = paramIt->second.bool_value;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007182 }
7183 }
7184}
Syoyo Fujitacea69e32019-08-20 17:10:30 +09007185#endif
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007186
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02007187static void SerializeExtensionMap(const ExtensionMap &extensions,
7188 detail::json &o) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07007189 if (!extensions.size()) return;
Selmar09d2ff12018-03-15 17:30:42 +01007190
David03ad33c2023-02-15 23:35:51 -06007191 detail::json extMap;
Selmar Kok81b672b2019-10-18 16:08:44 +02007192 for (ExtensionMap::const_iterator extIt = extensions.begin();
Ben Buzbee3b735bb2018-04-24 11:39:30 -07007193 extIt != extensions.end(); ++extIt) {
Syoyo Fujita924d86e2018-10-08 21:15:12 +09007194 // Allow an empty object for extension(#97)
David03ad33c2023-02-15 23:35:51 -06007195 detail::json ret;
jrkooncecba5d6c2019-08-29 11:26:22 -05007196 bool isNull = true;
Syoyo Fujita924d86e2018-10-08 21:15:12 +09007197 if (ValueToJson(extIt->second, &ret)) {
David1f9a4b92023-02-15 22:56:18 -06007198 isNull = detail::JsonIsNull(ret);
7199 detail::JsonAddMember(extMap, extIt->first.c_str(), std::move(ret));
Selmar Kokdb7f4e42018-10-10 18:10:58 +02007200 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007201 if (isNull) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00007202 if (!(extIt->first.empty())) { // name should not be empty, but for sure
7203 // create empty object so that an extension name is still included in
7204 // json.
David03ad33c2023-02-15 23:35:51 -06007205 detail::json empty;
David1f9a4b92023-02-15 22:56:18 -06007206 detail::JsonSetObject(empty);
7207 detail::JsonAddMember(extMap, extIt->first.c_str(), std::move(empty));
Syoyo Fujita924d86e2018-10-08 21:15:12 +09007208 }
7209 }
Selmar09d2ff12018-03-15 17:30:42 +01007210 }
David1f9a4b92023-02-15 22:56:18 -06007211 detail::JsonAddMember(o, "extensions", std::move(extMap));
Selmar09d2ff12018-03-15 17:30:42 +01007212}
7213
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02007214static void SerializeExtras(const Value &extras, detail::json &o) {
7215 if (extras.Type() != NULL_TYPE) SerializeValue("extras", extras, o);
David Siegel07616e82023-06-06 01:24:53 +02007216}
7217
7218template <typename GltfType>
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02007219void SerializeExtrasAndExtensions(const GltfType &obj, detail::json &o) {
David Siegel07616e82023-06-06 01:24:53 +02007220 SerializeExtensionMap(obj.extensions, o);
7221 SerializeExtras(obj.extras, o);
7222}
7223
David03ad33c2023-02-15 23:35:51 -06007224static void SerializeGltfAccessor(const Accessor &accessor, detail::json &o) {
Sanjeet Suhag5ecede72020-03-04 17:40:10 -05007225 if (accessor.bufferView >= 0)
7226 SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007227
Syoyo Fujita18f0e202020-04-29 19:16:35 +09007228 if (accessor.byteOffset != 0)
emimvibf7120f2023-09-02 00:11:41 +02007229 SerializeNumberProperty<size_t>("byteOffset", accessor.byteOffset, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007230
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007231 SerializeNumberProperty<int>("componentType", accessor.componentType, o);
7232 SerializeNumberProperty<size_t>("count", accessor.count, o);
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09007233
7234 if ((accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) ||
7235 (accessor.componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE)) {
7236 SerializeNumberArrayProperty<double>("min", accessor.minValues, o);
7237 SerializeNumberArrayProperty<double>("max", accessor.maxValues, o);
7238 } else {
7239 // Issue #301. Serialize as integer.
7240 // Assume int value is within [-2**31-1, 2**31-1]
7241 {
7242 std::vector<int> values;
7243 std::transform(accessor.minValues.begin(), accessor.minValues.end(),
7244 std::back_inserter(values),
7245 [](double v) { return static_cast<int>(v); });
7246
7247 SerializeNumberArrayProperty<int>("min", values, o);
7248 }
7249
7250 {
7251 std::vector<int> values;
7252 std::transform(accessor.maxValues.begin(), accessor.maxValues.end(),
7253 std::back_inserter(values),
7254 [](double v) { return static_cast<int>(v); });
7255
7256 SerializeNumberArrayProperty<int>("max", values, o);
7257 }
7258 }
7259
Eero Pajarre2e8a1152019-11-18 13:09:25 +02007260 if (accessor.normalized)
7261 SerializeValue("normalized", Value(accessor.normalized), o);
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007262 std::string type;
7263 switch (accessor.type) {
7264 case TINYGLTF_TYPE_SCALAR:
7265 type = "SCALAR";
7266 break;
7267 case TINYGLTF_TYPE_VEC2:
7268 type = "VEC2";
7269 break;
7270 case TINYGLTF_TYPE_VEC3:
7271 type = "VEC3";
7272 break;
7273 case TINYGLTF_TYPE_VEC4:
7274 type = "VEC4";
7275 break;
7276 case TINYGLTF_TYPE_MAT2:
7277 type = "MAT2";
7278 break;
7279 case TINYGLTF_TYPE_MAT3:
7280 type = "MAT3";
7281 break;
7282 case TINYGLTF_TYPE_MAT4:
7283 type = "MAT4";
7284 break;
7285 }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09007286
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007287 SerializeStringProperty("type", type, o);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09007288 if (!accessor.name.empty()) SerializeStringProperty("name", accessor.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007289
David Siegel07616e82023-06-06 01:24:53 +02007290 SerializeExtrasAndExtensions(accessor, o);
feiy0b315432022-08-13 10:08:17 +08007291
7292 // sparse
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02007293 if (accessor.sparse.isSparse) {
7294 detail::json sparse;
7295 SerializeNumberProperty<int>("count", accessor.sparse.count, sparse);
7296 {
7297 detail::json indices;
7298 SerializeNumberProperty<int>("bufferView",
7299 accessor.sparse.indices.bufferView, indices);
emimvi759976e2023-09-02 09:39:53 +02007300 SerializeNumberProperty<size_t>("byteOffset",
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02007301 accessor.sparse.indices.byteOffset, indices);
7302 SerializeNumberProperty<int>(
7303 "componentType", accessor.sparse.indices.componentType, indices);
7304 SerializeExtrasAndExtensions(accessor.sparse.indices, indices);
7305 detail::JsonAddMember(sparse, "indices", std::move(indices));
7306 }
7307 {
7308 detail::json values;
7309 SerializeNumberProperty<int>("bufferView",
7310 accessor.sparse.values.bufferView, values);
emimvi759976e2023-09-02 09:39:53 +02007311 SerializeNumberProperty<size_t>("byteOffset",
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02007312 accessor.sparse.values.byteOffset, values);
7313 SerializeExtrasAndExtensions(accessor.sparse.values, values);
7314 detail::JsonAddMember(sparse, "values", std::move(values));
7315 }
7316 SerializeExtrasAndExtensions(accessor.sparse, sparse);
7317 detail::JsonAddMember(o, "sparse", std::move(sparse));
feiy0b315432022-08-13 10:08:17 +08007318 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007319}
7320
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007321static void SerializeGltfAnimationChannel(const AnimationChannel &channel,
David03ad33c2023-02-15 23:35:51 -06007322 detail::json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007323 SerializeNumberProperty("sampler", channel.sampler, o);
jrkooncecba5d6c2019-08-29 11:26:22 -05007324 {
David03ad33c2023-02-15 23:35:51 -06007325 detail::json target;
Jack Mousseau283b5522023-01-15 11:45:45 -08007326
Loïc Escalesa75355b2023-04-18 21:03:39 +02007327 if (channel.target_node >= 0) {
Jack Mousseau283b5522023-01-15 11:45:45 -08007328 SerializeNumberProperty("node", channel.target_node, target);
7329 }
7330
jrkooncecba5d6c2019-08-29 11:26:22 -05007331 SerializeStringProperty("path", channel.target_path, target);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007332
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007333 SerializeExtensionMap(channel.target_extensions, target);
David Siegel07616e82023-06-06 01:24:53 +02007334 SerializeExtras(channel.target_extras, target);
Selmar Kok973d9b32020-01-21 18:45:24 +01007335
David1f9a4b92023-02-15 22:56:18 -06007336 detail::JsonAddMember(o, "target", std::move(target));
jrkooncecba5d6c2019-08-29 11:26:22 -05007337 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007338
David Siegel07616e82023-06-06 01:24:53 +02007339 SerializeExtrasAndExtensions(channel, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007340}
7341
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007342static void SerializeGltfAnimationSampler(const AnimationSampler &sampler,
David03ad33c2023-02-15 23:35:51 -06007343 detail::json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007344 SerializeNumberProperty("input", sampler.input, o);
7345 SerializeNumberProperty("output", sampler.output, o);
7346 SerializeStringProperty("interpolation", sampler.interpolation, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007347
David Siegel07616e82023-06-06 01:24:53 +02007348 SerializeExtrasAndExtensions(sampler, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007349}
7350
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02007351static void SerializeGltfAnimation(const Animation &animation,
7352 detail::json &o) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09007353 if (!animation.name.empty())
7354 SerializeStringProperty("name", animation.name, o);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007355
jrkooncecba5d6c2019-08-29 11:26:22 -05007356 {
David03ad33c2023-02-15 23:35:51 -06007357 detail::json channels;
David1f9a4b92023-02-15 22:56:18 -06007358 detail::JsonReserveArray(channels, animation.channels.size());
jrkooncecba5d6c2019-08-29 11:26:22 -05007359 for (unsigned int i = 0; i < animation.channels.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007360 detail::json channel;
jrkooncecba5d6c2019-08-29 11:26:22 -05007361 AnimationChannel gltfChannel = animation.channels[i];
7362 SerializeGltfAnimationChannel(gltfChannel, channel);
David1f9a4b92023-02-15 22:56:18 -06007363 detail::JsonPushBack(channels, std::move(channel));
jrkooncecba5d6c2019-08-29 11:26:22 -05007364 }
7365
David1f9a4b92023-02-15 22:56:18 -06007366 detail::JsonAddMember(o, "channels", std::move(channels));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007367 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007368
jrkooncecba5d6c2019-08-29 11:26:22 -05007369 {
David03ad33c2023-02-15 23:35:51 -06007370 detail::json samplers;
David1f9a4b92023-02-15 22:56:18 -06007371 detail::JsonReserveArray(samplers, animation.samplers.size());
jrkooncecba5d6c2019-08-29 11:26:22 -05007372 for (unsigned int i = 0; i < animation.samplers.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007373 detail::json sampler;
jrkooncecba5d6c2019-08-29 11:26:22 -05007374 AnimationSampler gltfSampler = animation.samplers[i];
7375 SerializeGltfAnimationSampler(gltfSampler, sampler);
David1f9a4b92023-02-15 22:56:18 -06007376 detail::JsonPushBack(samplers, std::move(sampler));
jrkooncecba5d6c2019-08-29 11:26:22 -05007377 }
David1f9a4b92023-02-15 22:56:18 -06007378 detail::JsonAddMember(o, "samplers", std::move(samplers));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007379 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007380
David Siegel07616e82023-06-06 01:24:53 +02007381 SerializeExtrasAndExtensions(animation, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007382}
7383
David03ad33c2023-02-15 23:35:51 -06007384static void SerializeGltfAsset(const Asset &asset, detail::json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007385 if (!asset.generator.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007386 SerializeStringProperty("generator", asset.generator, o);
7387 }
7388
Christophe820ede82019-07-04 15:21:21 +09007389 if (!asset.copyright.empty()) {
7390 SerializeStringProperty("copyright", asset.copyright, o);
7391 }
7392
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007393 auto version = asset.version;
7394 if (version.empty()) {
Syoyo Fujitab702de72021-03-02 19:08:29 +09007395 // Just in case
7396 // `version` must be defined
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007397 version = "2.0";
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007398 }
7399
Syoyo Fujitab702de72021-03-02 19:08:29 +09007400 // TODO(syoyo): Do we need to check if `version` is greater or equal to 2.0?
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007401 SerializeStringProperty("version", version, o);
Syoyo Fujitab702de72021-03-02 19:08:29 +09007402
David Siegel07616e82023-06-06 01:24:53 +02007403 SerializeExtrasAndExtensions(asset, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007404}
7405
David03ad33c2023-02-15 23:35:51 -06007406static void SerializeGltfBufferBin(const Buffer &buffer, detail::json &o,
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007407 std::vector<unsigned char> &binBuffer) {
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007408 SerializeNumberProperty("byteLength", buffer.data.size(), o);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007409 binBuffer = buffer.data;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007410
7411 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
7412
David Siegel07616e82023-06-06 01:24:53 +02007413 SerializeExtrasAndExtensions(buffer, o);
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007414}
7415
David03ad33c2023-02-15 23:35:51 -06007416static void SerializeGltfBuffer(const Buffer &buffer, detail::json &o) {
johan bowald30c53472018-03-30 11:49:36 +02007417 SerializeNumberProperty("byteLength", buffer.data.size(), o);
7418 SerializeGltfBufferData(buffer.data, o);
7419
7420 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007421
David Siegel07616e82023-06-06 01:24:53 +02007422 SerializeExtrasAndExtensions(buffer, o);
johan bowald30c53472018-03-30 11:49:36 +02007423}
7424
David03ad33c2023-02-15 23:35:51 -06007425static bool SerializeGltfBuffer(const Buffer &buffer, detail::json &o,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05007426 const std::string &binFilename,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08007427 const std::string &binUri) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00007428 if (!SerializeGltfBufferData(buffer.data, binFilename)) return false;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007429 SerializeNumberProperty("byteLength", buffer.data.size(), o);
Pyarelal Knowles385946d2023-01-03 18:18:04 -08007430 SerializeStringProperty("uri", binUri, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007431
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007432 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007433
David Siegel07616e82023-06-06 01:24:53 +02007434 SerializeExtrasAndExtensions(buffer, o);
Selmar Koke4677492018-10-25 16:45:49 +02007435 return true;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007436}
7437
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02007438static void SerializeGltfBufferView(const BufferView &bufferView,
7439 detail::json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007440 SerializeNumberProperty("buffer", bufferView.buffer, o);
7441 SerializeNumberProperty<size_t>("byteLength", bufferView.byteLength, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007442
Johan Bowaldfaa27222018-03-28 14:44:45 +02007443 // byteStride is optional, minimum allowed is 4
7444 if (bufferView.byteStride >= 4) {
7445 SerializeNumberProperty<size_t>("byteStride", bufferView.byteStride, o);
7446 }
7447 // byteOffset is optional, default is 0
7448 if (bufferView.byteOffset > 0) {
7449 SerializeNumberProperty<size_t>("byteOffset", bufferView.byteOffset, o);
7450 }
7451 // Target is optional, check if it contains a valid value
7452 if (bufferView.target == TINYGLTF_TARGET_ARRAY_BUFFER ||
7453 bufferView.target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER) {
7454 SerializeNumberProperty("target", bufferView.target, o);
7455 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007456 if (bufferView.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007457 SerializeStringProperty("name", bufferView.name, o);
7458 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007459
David Siegel07616e82023-06-06 01:24:53 +02007460 SerializeExtrasAndExtensions(bufferView, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007461}
7462
Pyarelal Knowlesa9121552022-12-29 14:12:29 -08007463static void SerializeGltfImage(const Image &image, const std::string &uri,
David03ad33c2023-02-15 23:35:51 -06007464 detail::json &o) {
Syoyo Fujita584f1df2022-12-29 21:05:53 +09007465 // From 2.7.0, we look for `uri` parameter, not `Image.uri`
7466 // if uri is empty, the mimeType and bufferview should be set
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007467 if (uri.empty()) {
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02007468 SerializeStringProperty("mimeType", image.mimeType, o);
7469 SerializeNumberProperty<int>("bufferView", image.bufferView, o);
7470 } else {
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007471 SerializeStringProperty("uri", uri, o);
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02007472 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007473
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007474 if (image.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007475 SerializeStringProperty("name", image.name, o);
7476 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007477
David Siegel07616e82023-06-06 01:24:53 +02007478 SerializeExtrasAndExtensions(image, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007479}
7480
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02007481static void SerializeGltfTextureInfo(const TextureInfo &texinfo,
7482 detail::json &o) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007483 SerializeNumberProperty("index", texinfo.index, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007484
Syoyo Fujita046400b2019-07-24 19:26:48 +09007485 if (texinfo.texCoord != 0) {
7486 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
7487 }
7488
David Siegel07616e82023-06-06 01:24:53 +02007489 SerializeExtrasAndExtensions(texinfo, o);
Syoyo Fujita046400b2019-07-24 19:26:48 +09007490}
7491
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007492static void SerializeGltfNormalTextureInfo(const NormalTextureInfo &texinfo,
David03ad33c2023-02-15 23:35:51 -06007493 detail::json &o) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007494 SerializeNumberProperty("index", texinfo.index, o);
7495
7496 if (texinfo.texCoord != 0) {
7497 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
7498 }
7499
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007500 if (!TINYGLTF_DOUBLE_EQUAL(texinfo.scale, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007501 SerializeNumberProperty("scale", texinfo.scale, o);
7502 }
7503
David Siegel07616e82023-06-06 01:24:53 +02007504 SerializeExtrasAndExtensions(texinfo, o);
Syoyo Fujita046400b2019-07-24 19:26:48 +09007505}
7506
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007507static void SerializeGltfOcclusionTextureInfo(
David03ad33c2023-02-15 23:35:51 -06007508 const OcclusionTextureInfo &texinfo, detail::json &o) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007509 SerializeNumberProperty("index", texinfo.index, o);
7510
7511 if (texinfo.texCoord != 0) {
7512 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
7513 }
7514
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007515 if (!TINYGLTF_DOUBLE_EQUAL(texinfo.strength, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007516 SerializeNumberProperty("strength", texinfo.strength, o);
7517 }
7518
David Siegel07616e82023-06-06 01:24:53 +02007519 SerializeExtrasAndExtensions(texinfo, o);
Syoyo Fujita046400b2019-07-24 19:26:48 +09007520}
7521
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007522static void SerializeGltfPbrMetallicRoughness(const PbrMetallicRoughness &pbr,
David03ad33c2023-02-15 23:35:51 -06007523 detail::json &o) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007524 std::vector<double> default_baseColorFactor = {1.0, 1.0, 1.0, 1.0};
7525 if (!Equals(pbr.baseColorFactor, default_baseColorFactor)) {
7526 SerializeNumberArrayProperty<double>("baseColorFactor", pbr.baseColorFactor,
7527 o);
7528 }
7529
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007530 if (!TINYGLTF_DOUBLE_EQUAL(pbr.metallicFactor, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007531 SerializeNumberProperty("metallicFactor", pbr.metallicFactor, o);
7532 }
7533
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007534 if (!TINYGLTF_DOUBLE_EQUAL(pbr.roughnessFactor, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007535 SerializeNumberProperty("roughnessFactor", pbr.roughnessFactor, o);
7536 }
7537
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007538 if (pbr.baseColorTexture.index > -1) {
David03ad33c2023-02-15 23:35:51 -06007539 detail::json texinfo;
Syoyo Fujita046400b2019-07-24 19:26:48 +09007540 SerializeGltfTextureInfo(pbr.baseColorTexture, texinfo);
David1f9a4b92023-02-15 22:56:18 -06007541 detail::JsonAddMember(o, "baseColorTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007542 }
7543
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007544 if (pbr.metallicRoughnessTexture.index > -1) {
David03ad33c2023-02-15 23:35:51 -06007545 detail::json texinfo;
Syoyo Fujita046400b2019-07-24 19:26:48 +09007546 SerializeGltfTextureInfo(pbr.metallicRoughnessTexture, texinfo);
David1f9a4b92023-02-15 22:56:18 -06007547 detail::JsonAddMember(o, "metallicRoughnessTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007548 }
7549
David Siegel07616e82023-06-06 01:24:53 +02007550 SerializeExtrasAndExtensions(pbr, o);
Syoyo Fujita046400b2019-07-24 19:26:48 +09007551}
7552
David03ad33c2023-02-15 23:35:51 -06007553static void SerializeGltfMaterial(const Material &material, detail::json &o) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007554 if (material.name.size()) {
7555 SerializeStringProperty("name", material.name, o);
7556 }
7557
7558 // QUESTION(syoyo): Write material parameters regardless of its default value?
7559
7560 if (!TINYGLTF_DOUBLE_EQUAL(material.alphaCutoff, 0.5)) {
7561 SerializeNumberProperty("alphaCutoff", material.alphaCutoff, o);
7562 }
7563
Patrick Härtld9a468b2019-08-14 14:14:07 +02007564 if (material.alphaMode.compare("OPAQUE") != 0) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007565 SerializeStringProperty("alphaMode", material.alphaMode, o);
7566 }
7567
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007568 if (material.doubleSided != false)
David03ad33c2023-02-15 23:35:51 -06007569 detail::JsonAddMember(o, "doubleSided", detail::json(material.doubleSided));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007570
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007571 if (material.normalTexture.index > -1) {
David03ad33c2023-02-15 23:35:51 -06007572 detail::json texinfo;
Syoyo Fujita046400b2019-07-24 19:26:48 +09007573 SerializeGltfNormalTextureInfo(material.normalTexture, texinfo);
David1f9a4b92023-02-15 22:56:18 -06007574 detail::JsonAddMember(o, "normalTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007575 }
7576
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007577 if (material.occlusionTexture.index > -1) {
David03ad33c2023-02-15 23:35:51 -06007578 detail::json texinfo;
Syoyo Fujita046400b2019-07-24 19:26:48 +09007579 SerializeGltfOcclusionTextureInfo(material.occlusionTexture, texinfo);
David1f9a4b92023-02-15 22:56:18 -06007580 detail::JsonAddMember(o, "occlusionTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007581 }
7582
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007583 if (material.emissiveTexture.index > -1) {
David03ad33c2023-02-15 23:35:51 -06007584 detail::json texinfo;
Syoyo Fujita046400b2019-07-24 19:26:48 +09007585 SerializeGltfTextureInfo(material.emissiveTexture, texinfo);
David1f9a4b92023-02-15 22:56:18 -06007586 detail::JsonAddMember(o, "emissiveTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007587 }
7588
7589 std::vector<double> default_emissiveFactor = {0.0, 0.0, 0.0};
7590 if (!Equals(material.emissiveFactor, default_emissiveFactor)) {
7591 SerializeNumberArrayProperty<double>("emissiveFactor",
7592 material.emissiveFactor, o);
7593 }
7594
7595 {
David03ad33c2023-02-15 23:35:51 -06007596 detail::json pbrMetallicRoughness;
Syoyo Fujita046400b2019-07-24 19:26:48 +09007597 SerializeGltfPbrMetallicRoughness(material.pbrMetallicRoughness,
7598 pbrMetallicRoughness);
Syoyo Fujita7e009042019-09-13 15:32:22 +09007599 // Issue 204
7600 // Do not serialize `pbrMetallicRoughness` if pbrMetallicRoughness has all
7601 // default values(json is null). Otherwise it will serialize to
7602 // `pbrMetallicRoughness : null`, which cannot be read by other glTF
imallettd9ce9eb2022-10-07 10:37:09 -07007603 // importers (and validators).
Syoyo Fujita7e009042019-09-13 15:32:22 +09007604 //
David1f9a4b92023-02-15 22:56:18 -06007605 if (!detail::JsonIsNull(pbrMetallicRoughness)) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02007606 detail::JsonAddMember(o, "pbrMetallicRoughness",
7607 std::move(pbrMetallicRoughness));
Syoyo Fujita7e009042019-09-13 15:32:22 +09007608 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09007609 }
7610
7611#if 0 // legacy way. just for the record.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007612 if (material.values.size()) {
David03ad33c2023-02-15 23:35:51 -06007613 detail::json pbrMetallicRoughness;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007614 SerializeParameterMap(material.values, pbrMetallicRoughness);
David1f9a4b92023-02-15 22:56:18 -06007615 detail::JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007616 }
7617
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007618 SerializeParameterMap(material.additionalValues, o);
Syoyo Fujita046400b2019-07-24 19:26:48 +09007619#endif
7620
David Siegel07616e82023-06-06 01:24:53 +02007621 SerializeExtrasAndExtensions(material, o);
Thomas Gamperf1bdf432024-02-05 16:50:46 +01007622
7623 // MSFT_lod
7624 if (!material.lods.empty()) {
7625 detail::json_iterator it;
7626 if (!detail::FindMember(o, "extensions", it)) {
7627 detail::json extensions;
7628 detail::JsonSetObject(extensions);
7629 detail::JsonAddMember(o, "extensions", std::move(extensions));
7630 detail::FindMember(o, "extensions", it);
7631 }
7632 auto &extensions = detail::GetValue(it);
7633 if (!detail::FindMember(extensions, "MSFT_lod", it)) {
7634 detail::json lod;
7635 detail::JsonSetObject(lod);
7636 detail::JsonAddMember(extensions, "MSFT_lod", std::move(lod));
7637 detail::FindMember(extensions, "MSFT_lod", it);
7638 }
7639 SerializeNumberArrayProperty<int>("ids", material.lods, detail::GetValue(it));
7640 } else {
7641 detail::json_iterator ext_it;
7642 if (detail::FindMember(o, "extensions", ext_it)) {
7643 auto &extensions = detail::GetValue(ext_it);
7644 detail::json_iterator lp_it;
7645 if (detail::FindMember(extensions, "MSFT_lod", lp_it)) {
7646 detail::Erase(extensions, lp_it);
7647 }
7648 if (detail::IsEmpty(extensions)) {
7649 detail::Erase(o, ext_it);
7650 }
7651 }
7652 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007653}
7654
David03ad33c2023-02-15 23:35:51 -06007655static void SerializeGltfMesh(const Mesh &mesh, detail::json &o) {
7656 detail::json primitives;
David1f9a4b92023-02-15 22:56:18 -06007657 detail::JsonReserveArray(primitives, mesh.primitives.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007658 for (unsigned int i = 0; i < mesh.primitives.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007659 detail::json primitive;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007660 const Primitive &gltfPrimitive = mesh.primitives[i]; // don't make a copy
jrkooncecba5d6c2019-08-29 11:26:22 -05007661 {
David03ad33c2023-02-15 23:35:51 -06007662 detail::json attributes;
jrkooncecba5d6c2019-08-29 11:26:22 -05007663 for (auto attrIt = gltfPrimitive.attributes.begin();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007664 attrIt != gltfPrimitive.attributes.end(); ++attrIt) {
jrkooncecba5d6c2019-08-29 11:26:22 -05007665 SerializeNumberProperty<int>(attrIt->first, attrIt->second, attributes);
7666 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007667
David1f9a4b92023-02-15 22:56:18 -06007668 detail::JsonAddMember(primitive, "attributes", std::move(attributes));
jrkooncecba5d6c2019-08-29 11:26:22 -05007669 }
Johan Bowaldfaa27222018-03-28 14:44:45 +02007670
imallettd9ce9eb2022-10-07 10:37:09 -07007671 // Indices is optional
Johan Bowaldfaa27222018-03-28 14:44:45 +02007672 if (gltfPrimitive.indices > -1) {
7673 SerializeNumberProperty<int>("indices", gltfPrimitive.indices, primitive);
7674 }
7675 // Material is optional
7676 if (gltfPrimitive.material > -1) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09007677 SerializeNumberProperty<int>("material", gltfPrimitive.material,
7678 primitive);
Johan Bowaldfaa27222018-03-28 14:44:45 +02007679 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007680 SerializeNumberProperty<int>("mode", gltfPrimitive.mode, primitive);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007681
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007682 // Morph targets
7683 if (gltfPrimitive.targets.size()) {
David03ad33c2023-02-15 23:35:51 -06007684 detail::json targets;
David1f9a4b92023-02-15 22:56:18 -06007685 detail::JsonReserveArray(targets, gltfPrimitive.targets.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007686 for (unsigned int k = 0; k < gltfPrimitive.targets.size(); ++k) {
David03ad33c2023-02-15 23:35:51 -06007687 detail::json targetAttributes;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007688 std::map<std::string, int> targetData = gltfPrimitive.targets[k];
7689 for (std::map<std::string, int>::iterator attrIt = targetData.begin();
7690 attrIt != targetData.end(); ++attrIt) {
7691 SerializeNumberProperty<int>(attrIt->first, attrIt->second,
7692 targetAttributes);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007693 }
David1f9a4b92023-02-15 22:56:18 -06007694 detail::JsonPushBack(targets, std::move(targetAttributes));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007695 }
David1f9a4b92023-02-15 22:56:18 -06007696 detail::JsonAddMember(primitive, "targets", std::move(targets));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007697 }
7698
KUDELSKI Dimitri69d75572023-07-10 15:13:09 +02007699 SerializeExtrasAndExtensions(gltfPrimitive, primitive);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007700
David1f9a4b92023-02-15 22:56:18 -06007701 detail::JsonPushBack(primitives, std::move(primitive));
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007702 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007703
David1f9a4b92023-02-15 22:56:18 -06007704 detail::JsonAddMember(o, "primitives", std::move(primitives));
jrkooncecba5d6c2019-08-29 11:26:22 -05007705
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007706 if (mesh.weights.size()) {
7707 SerializeNumberArrayProperty<double>("weights", mesh.weights, o);
7708 }
7709
7710 if (mesh.name.size()) {
7711 SerializeStringProperty("name", mesh.name, o);
7712 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007713
David Siegel07616e82023-06-06 01:24:53 +02007714 SerializeExtrasAndExtensions(mesh, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007715}
7716
David03ad33c2023-02-15 23:35:51 -06007717static void SerializeSpotLight(const SpotLight &spot, detail::json &o) {
Johan Bowald52936a02019-07-17 09:06:45 +02007718 SerializeNumberProperty("innerConeAngle", spot.innerConeAngle, o);
7719 SerializeNumberProperty("outerConeAngle", spot.outerConeAngle, o);
David Siegel07616e82023-06-06 01:24:53 +02007720 SerializeExtrasAndExtensions(spot, o);
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007721}
7722
David03ad33c2023-02-15 23:35:51 -06007723static void SerializeGltfLight(const Light &light, detail::json &o) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09007724 if (!light.name.empty()) SerializeStringProperty("name", light.name, o);
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007725 SerializeNumberProperty("intensity", light.intensity, o);
Syoyo Fujita3dad3032020-05-13 21:22:23 +09007726 if (light.range > 0.0) {
7727 SerializeNumberProperty("range", light.range, o);
7728 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01007729 SerializeNumberArrayProperty("color", light.color, o);
7730 SerializeStringProperty("type", light.type, o);
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007731 if (light.type == "spot") {
David03ad33c2023-02-15 23:35:51 -06007732 detail::json spot;
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007733 SerializeSpotLight(light.spot, spot);
David1f9a4b92023-02-15 22:56:18 -06007734 detail::JsonAddMember(o, "spot", std::move(spot));
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007735 }
David Siegel07616e82023-06-06 01:24:53 +02007736 SerializeExtrasAndExtensions(light, o);
Emanuel Schrade186322b2017-11-06 11:14:41 +01007737}
7738
Baranob_Ilya78864c82023-06-12 10:43:52 +04007739static void SerializeGltfPositionalEmitter(const PositionalEmitter &positional,
7740 detail::json &o) {
7741 if (!TINYGLTF_DOUBLE_EQUAL(positional.coneInnerAngle, 6.283185307179586))
7742 SerializeNumberProperty("coneInnerAngle", positional.coneInnerAngle, o);
7743 if (!TINYGLTF_DOUBLE_EQUAL(positional.coneOuterAngle, 6.283185307179586))
7744 SerializeNumberProperty("coneOuterAngle", positional.coneOuterAngle, o);
7745 if (positional.coneOuterGain > 0.0)
7746 SerializeNumberProperty("coneOuterGain", positional.coneOuterGain, o);
7747 if (!TINYGLTF_DOUBLE_EQUAL(positional.maxDistance, 100.0))
7748 SerializeNumberProperty("maxDistance", positional.maxDistance, o);
7749 if (!TINYGLTF_DOUBLE_EQUAL(positional.refDistance, 1.0))
7750 SerializeNumberProperty("refDistance", positional.refDistance, o);
7751 if (!TINYGLTF_DOUBLE_EQUAL(positional.rolloffFactor, 1.0))
7752 SerializeNumberProperty("rolloffFactor", positional.rolloffFactor, o);
7753
7754 SerializeExtrasAndExtensions(positional, o);
7755}
7756
7757static void SerializeGltfAudioEmitter(const AudioEmitter &emitter,
7758 detail::json &o) {
7759 if (!emitter.name.empty()) SerializeStringProperty("name", emitter.name, o);
7760 if (!TINYGLTF_DOUBLE_EQUAL(emitter.gain, 1.0))
7761 SerializeNumberProperty("gain", emitter.gain, o);
7762 if (emitter.loop) SerializeNumberProperty("loop", emitter.loop, o);
7763 if (emitter.playing) SerializeNumberProperty("playing", emitter.playing, o);
7764 if (!emitter.type.empty()) SerializeStringProperty("type", emitter.type, o);
7765 if (!emitter.distanceModel.empty())
7766 SerializeStringProperty("distanceModel", emitter.distanceModel, o);
7767 if (emitter.type == "positional") {
7768 detail::json positional;
7769 SerializeGltfPositionalEmitter(emitter.positional, positional);
7770 detail::JsonAddMember(o, "positional", std::move(positional));
7771 }
7772 SerializeNumberProperty("source", emitter.source, o);
7773 SerializeExtrasAndExtensions(emitter, o);
7774}
7775
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02007776static void SerializeGltfAudioSource(const AudioSource &source,
7777 detail::json &o) {
Baranob_Ilya78864c82023-06-12 10:43:52 +04007778 std::string name;
7779 std::string uri;
Baranob_Ilya78864c82023-06-12 10:43:52 +04007780 std::string mimeType; // (required if no uri) ["audio/mp3", "audio/ogg",
7781 // "audio/wav", "audio/m4a"]
7782
7783 if (!source.name.empty()) SerializeStringProperty("name", source.name, o);
7784 if (source.uri.empty()) {
7785 SerializeStringProperty("mimeType", source.mimeType, o);
7786 SerializeNumberProperty<int>("bufferView", source.bufferView, o);
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02007787 } else {
7788 SerializeStringProperty("uri", source.uri, o);
Baranob_Ilya78864c82023-06-12 10:43:52 +04007789 }
7790 SerializeExtrasAndExtensions(source, o);
7791}
7792
David03ad33c2023-02-15 23:35:51 -06007793static void SerializeGltfNode(const Node &node, detail::json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007794 if (node.translation.size() > 0) {
7795 SerializeNumberArrayProperty<double>("translation", node.translation, o);
7796 }
7797 if (node.rotation.size() > 0) {
7798 SerializeNumberArrayProperty<double>("rotation", node.rotation, o);
7799 }
7800 if (node.scale.size() > 0) {
7801 SerializeNumberArrayProperty<double>("scale", node.scale, o);
7802 }
7803 if (node.matrix.size() > 0) {
7804 SerializeNumberArrayProperty<double>("matrix", node.matrix, o);
7805 }
7806 if (node.mesh != -1) {
7807 SerializeNumberProperty<int>("mesh", node.mesh, o);
7808 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007809
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007810 if (node.skin != -1) {
7811 SerializeNumberProperty<int>("skin", node.skin, o);
7812 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007813
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007814 if (node.camera != -1) {
7815 SerializeNumberProperty<int>("camera", node.camera, o);
7816 }
7817
Benjamin Schmithüsen74c3c102019-08-16 14:24:26 +02007818 if (node.weights.size() > 0) {
7819 SerializeNumberArrayProperty<double>("weights", node.weights, o);
7820 }
7821
David Siegel07616e82023-06-06 01:24:53 +02007822 SerializeExtrasAndExtensions(node, o);
Jens Olssonb96f6962018-05-24 15:29:54 +02007823
David Siegelcfe64fb2023-06-07 15:18:38 +02007824 // Note(agnat): If the asset was loaded from disk, the node may already
7825 // contain the KHR_lights_punctual extension. If it was constructed in
7826 // memory it does not. In any case we update the JSON property using
7827 // the value from the struct. Last, if the node does not have a light
7828 // reference but the extension is still present, we remove it.
7829 if (node.light != -1) {
7830 detail::json_iterator it;
7831 if (!detail::FindMember(o, "extensions", it)) {
7832 detail::json extensions;
7833 detail::JsonSetObject(extensions);
7834 detail::JsonAddMember(o, "extensions", std::move(extensions));
7835 detail::FindMember(o, "extensions", it);
7836 }
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02007837 auto &extensions = detail::GetValue(it);
7838 if (!detail::FindMember(extensions, "KHR_lights_punctual", it)) {
David Siegelcfe64fb2023-06-07 15:18:38 +02007839 detail::json lights_punctual;
7840 detail::JsonSetObject(lights_punctual);
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02007841 detail::JsonAddMember(extensions, "KHR_lights_punctual",
7842 std::move(lights_punctual));
Thomas Gamperafcfb572023-11-23 14:12:54 +01007843 detail::FindMember(extensions, "KHR_lights_punctual", it);
David Siegelcfe64fb2023-06-07 15:18:38 +02007844 }
David Siegel8d5d0b32023-06-07 15:35:35 +02007845 SerializeNumberProperty("light", node.light, detail::GetValue(it));
David Siegelcfe64fb2023-06-07 15:18:38 +02007846 } else {
7847 // node has no light ref (any longer)... so we clean up
7848 detail::json_iterator ext_it;
7849 if (detail::FindMember(o, "extensions", ext_it)) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02007850 auto &extensions = detail::GetValue(ext_it);
David Siegelcfe64fb2023-06-07 15:18:38 +02007851 detail::json_iterator lp_it;
7852 if (detail::FindMember(extensions, "KHR_lights_punctual", lp_it)) {
7853 detail::Erase(extensions, lp_it);
7854 }
7855 if (detail::IsEmpty(extensions)) {
7856 detail::Erase(o, ext_it);
7857 }
7858 }
7859 }
7860
Baranob_Ilya78864c82023-06-12 10:43:52 +04007861 // KHR_audio
7862 if (node.emitter != -1) {
7863 detail::json_iterator it;
7864 if (!detail::FindMember(o, "extensions", it)) {
7865 detail::json extensions;
7866 detail::JsonSetObject(extensions);
7867 detail::JsonAddMember(o, "extensions", std::move(extensions));
7868 detail::FindMember(o, "extensions", it);
7869 }
7870 auto &extensions = detail::GetValue(it);
7871 if (!detail::FindMember(extensions, "KHR_audio", it)) {
7872 detail::json audio;
7873 detail::JsonSetObject(audio);
7874 detail::JsonAddMember(extensions, "KHR_audio", std::move(audio));
Thomas Gamperf1bdf432024-02-05 16:50:46 +01007875 detail::FindMember(extensions, "KHR_audio", it);
Baranob_Ilya78864c82023-06-12 10:43:52 +04007876 }
7877 SerializeNumberProperty("emitter", node.emitter, detail::GetValue(it));
7878 } else {
7879 detail::json_iterator ext_it;
7880 if (detail::FindMember(o, "extensions", ext_it)) {
7881 auto &extensions = detail::GetValue(ext_it);
7882 detail::json_iterator lp_it;
7883 if (detail::FindMember(extensions, "KHR_audio", lp_it)) {
7884 detail::Erase(extensions, lp_it);
7885 }
7886 if (detail::IsEmpty(extensions)) {
7887 detail::Erase(o, ext_it);
7888 }
7889 }
7890 }
7891
Thomas Gamperf1bdf432024-02-05 16:50:46 +01007892 // MSFT_lod
7893 if (!node.lods.empty()) {
7894 detail::json_iterator it;
7895 if (!detail::FindMember(o, "extensions", it)) {
7896 detail::json extensions;
7897 detail::JsonSetObject(extensions);
7898 detail::JsonAddMember(o, "extensions", std::move(extensions));
7899 detail::FindMember(o, "extensions", it);
7900 }
7901 auto &extensions = detail::GetValue(it);
7902 if (!detail::FindMember(extensions, "MSFT_lod", it)) {
7903 detail::json lod;
7904 detail::JsonSetObject(lod);
7905 detail::JsonAddMember(extensions, "MSFT_lod", std::move(lod));
7906 detail::FindMember(extensions, "MSFT_lod", it);
7907 }
7908 SerializeNumberArrayProperty<int>("ids", node.lods, detail::GetValue(it));
7909 } else {
7910 detail::json_iterator ext_it;
7911 if (detail::FindMember(o, "extensions", ext_it)) {
7912 auto &extensions = detail::GetValue(ext_it);
7913 detail::json_iterator lp_it;
7914 if (detail::FindMember(extensions, "MSFT_lod", lp_it)) {
7915 detail::Erase(extensions, lp_it);
7916 }
7917 if (detail::IsEmpty(extensions)) {
7918 detail::Erase(o, ext_it);
7919 }
7920 }
7921 }
7922
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09007923 if (!node.name.empty()) SerializeStringProperty("name", node.name, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007924 SerializeNumberArrayProperty<int>("children", node.children, o);
7925}
7926
David03ad33c2023-02-15 23:35:51 -06007927static void SerializeGltfSampler(const Sampler &sampler, detail::json &o) {
s00665032137a7ca2023-01-13 12:52:08 +07007928 if (!sampler.name.empty()) {
7929 SerializeStringProperty("name", sampler.name, o);
7930 }
Syoyo Fujitaee179b22019-08-16 13:11:30 +09007931 if (sampler.magFilter != -1) {
7932 SerializeNumberProperty("magFilter", sampler.magFilter, o);
7933 }
7934 if (sampler.minFilter != -1) {
7935 SerializeNumberProperty("minFilter", sampler.minFilter, o);
7936 }
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09007937 // SerializeNumberProperty("wrapR", sampler.wrapR, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007938 SerializeNumberProperty("wrapS", sampler.wrapS, o);
7939 SerializeNumberProperty("wrapT", sampler.wrapT, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007940
David Siegel07616e82023-06-06 01:24:53 +02007941 SerializeExtrasAndExtensions(sampler, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007942}
7943
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007944static void SerializeGltfOrthographicCamera(const OrthographicCamera &camera,
David03ad33c2023-02-15 23:35:51 -06007945 detail::json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007946 SerializeNumberProperty("zfar", camera.zfar, o);
7947 SerializeNumberProperty("znear", camera.znear, o);
7948 SerializeNumberProperty("xmag", camera.xmag, o);
7949 SerializeNumberProperty("ymag", camera.ymag, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007950
David Siegel07616e82023-06-06 01:24:53 +02007951 SerializeExtrasAndExtensions(camera, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007952}
7953
7954static void SerializeGltfPerspectiveCamera(const PerspectiveCamera &camera,
David03ad33c2023-02-15 23:35:51 -06007955 detail::json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007956 SerializeNumberProperty("zfar", camera.zfar, o);
7957 SerializeNumberProperty("znear", camera.znear, o);
7958 if (camera.aspectRatio > 0) {
7959 SerializeNumberProperty("aspectRatio", camera.aspectRatio, o);
7960 }
7961
7962 if (camera.yfov > 0) {
7963 SerializeNumberProperty("yfov", camera.yfov, o);
7964 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007965
David Siegel07616e82023-06-06 01:24:53 +02007966 SerializeExtrasAndExtensions(camera, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007967}
7968
David03ad33c2023-02-15 23:35:51 -06007969static void SerializeGltfCamera(const Camera &camera, detail::json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007970 SerializeStringProperty("type", camera.type, o);
7971 if (!camera.name.empty()) {
Selmar Kokee3d0662018-10-08 16:20:43 +02007972 SerializeStringProperty("name", camera.name, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007973 }
7974
7975 if (camera.type.compare("orthographic") == 0) {
David03ad33c2023-02-15 23:35:51 -06007976 detail::json orthographic;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007977 SerializeGltfOrthographicCamera(camera.orthographic, orthographic);
David1f9a4b92023-02-15 22:56:18 -06007978 detail::JsonAddMember(o, "orthographic", std::move(orthographic));
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007979 } else if (camera.type.compare("perspective") == 0) {
David03ad33c2023-02-15 23:35:51 -06007980 detail::json perspective;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007981 SerializeGltfPerspectiveCamera(camera.perspective, perspective);
David1f9a4b92023-02-15 22:56:18 -06007982 detail::JsonAddMember(o, "perspective", std::move(perspective));
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007983 } else {
7984 // ???
7985 }
Syoyofe77cc52020-05-09 02:41:07 +09007986
David Siegel07616e82023-06-06 01:24:53 +02007987 SerializeExtrasAndExtensions(camera, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007988}
7989
David03ad33c2023-02-15 23:35:51 -06007990static void SerializeGltfScene(const Scene &scene, detail::json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007991 SerializeNumberArrayProperty<int>("nodes", scene.nodes, o);
7992
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007993 if (scene.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007994 SerializeStringProperty("name", scene.name, o);
7995 }
David Siegel07616e82023-06-06 01:24:53 +02007996 SerializeExtrasAndExtensions(scene, o);
Baranob_Ilya879cb472023-06-12 13:35:05 +04007997
7998 // KHR_audio
7999 if (!scene.audioEmitters.empty()) {
8000 detail::json_iterator it;
8001 if (!detail::FindMember(o, "extensions", it)) {
8002 detail::json extensions;
8003 detail::JsonSetObject(extensions);
8004 detail::JsonAddMember(o, "extensions", std::move(extensions));
8005 detail::FindMember(o, "extensions", it);
8006 }
8007 auto &extensions = detail::GetValue(it);
8008 if (!detail::FindMember(extensions, "KHR_audio", it)) {
8009 detail::json audio;
8010 detail::JsonSetObject(audio);
8011 detail::JsonAddMember(extensions, "KHR_audio", std::move(audio));
8012 detail::FindMember(o, "KHR_audio", it);
8013 }
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02008014 SerializeNumberArrayProperty("emitters", scene.audioEmitters,
8015 detail::GetValue(it));
Baranob_Ilya879cb472023-06-12 13:35:05 +04008016 } else {
8017 detail::json_iterator ext_it;
8018 if (detail::FindMember(o, "extensions", ext_it)) {
8019 auto &extensions = detail::GetValue(ext_it);
8020 detail::json_iterator lp_it;
8021 if (detail::FindMember(extensions, "KHR_audio", lp_it)) {
8022 detail::Erase(extensions, lp_it);
8023 }
8024 if (detail::IsEmpty(extensions)) {
8025 detail::Erase(o, ext_it);
8026 }
8027 }
8028 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008029}
8030
David03ad33c2023-02-15 23:35:51 -06008031static void SerializeGltfSkin(const Skin &skin, detail::json &o) {
Syoyo Fujitad07b9762021-04-28 19:15:16 +09008032 // required
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008033 SerializeNumberArrayProperty<int>("joints", skin.joints, o);
Syoyo Fujitad07b9762021-04-28 19:15:16 +09008034
8035 if (skin.inverseBindMatrices >= 0) {
8036 SerializeNumberProperty("inverseBindMatrices", skin.inverseBindMatrices, o);
8037 }
8038
8039 if (skin.skeleton >= 0) {
8040 SerializeNumberProperty("skeleton", skin.skeleton, o);
8041 }
8042
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09008043 if (skin.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008044 SerializeStringProperty("name", skin.name, o);
8045 }
David Siegel07616e82023-06-06 01:24:53 +02008046
8047 SerializeExtrasAndExtensions(skin, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008048}
8049
David03ad33c2023-02-15 23:35:51 -06008050static void SerializeGltfTexture(const Texture &texture, detail::json &o) {
johan bowaldb97d34c2018-04-02 07:29:29 +02008051 if (texture.sampler > -1) {
johan bowald642a3432018-04-01 12:37:18 +02008052 SerializeNumberProperty("sampler", texture.sampler, o);
8053 }
Selmar Kok8eb39042018-10-05 14:29:35 +02008054 if (texture.source > -1) {
8055 SerializeNumberProperty("source", texture.source, o);
8056 }
Christophe820ede82019-07-04 15:21:21 +09008057 if (texture.name.size()) {
8058 SerializeStringProperty("name", texture.name, o);
8059 }
David Siegel07616e82023-06-06 01:24:53 +02008060 SerializeExtrasAndExtensions(texture, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008061}
8062
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008063///
8064/// Serialize all properties except buffers and images.
8065///
David03ad33c2023-02-15 23:35:51 -06008066static void SerializeGltfModel(const Model *model, detail::json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09008067 // ACCESSORS
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008068 if (model->accessors.size()) {
David03ad33c2023-02-15 23:35:51 -06008069 detail::json accessors;
David1f9a4b92023-02-15 22:56:18 -06008070 detail::JsonReserveArray(accessors, model->accessors.size());
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008071 for (unsigned int i = 0; i < model->accessors.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06008072 detail::json accessor;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008073 SerializeGltfAccessor(model->accessors[i], accessor);
David1f9a4b92023-02-15 22:56:18 -06008074 detail::JsonPushBack(accessors, std::move(accessor));
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008075 }
David1f9a4b92023-02-15 22:56:18 -06008076 detail::JsonAddMember(o, "accessors", std::move(accessors));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008077 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008078
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09008079 // ANIMATIONS
8080 if (model->animations.size()) {
David03ad33c2023-02-15 23:35:51 -06008081 detail::json animations;
David1f9a4b92023-02-15 22:56:18 -06008082 detail::JsonReserveArray(animations, model->animations.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09008083 for (unsigned int i = 0; i < model->animations.size(); ++i) {
8084 if (model->animations[i].channels.size()) {
David03ad33c2023-02-15 23:35:51 -06008085 detail::json animation;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008086 SerializeGltfAnimation(model->animations[i], animation);
David1f9a4b92023-02-15 22:56:18 -06008087 detail::JsonPushBack(animations, std::move(animation));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008088 }
8089 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09008090
David1f9a4b92023-02-15 22:56:18 -06008091 detail::JsonAddMember(o, "animations", std::move(animations));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008092 }
8093
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09008094 // ASSET
David03ad33c2023-02-15 23:35:51 -06008095 detail::json asset;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008096 SerializeGltfAsset(model->asset, asset);
David1f9a4b92023-02-15 22:56:18 -06008097 detail::JsonAddMember(o, "asset", std::move(asset));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008098
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008099 // BUFFERVIEWS
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008100 if (model->bufferViews.size()) {
David03ad33c2023-02-15 23:35:51 -06008101 detail::json bufferViews;
David1f9a4b92023-02-15 22:56:18 -06008102 detail::JsonReserveArray(bufferViews, model->bufferViews.size());
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008103 for (unsigned int i = 0; i < model->bufferViews.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06008104 detail::json bufferView;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008105 SerializeGltfBufferView(model->bufferViews[i], bufferView);
David1f9a4b92023-02-15 22:56:18 -06008106 detail::JsonPushBack(bufferViews, std::move(bufferView));
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008107 }
David1f9a4b92023-02-15 22:56:18 -06008108 detail::JsonAddMember(o, "bufferViews", std::move(bufferViews));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008109 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008110
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008111 // Extensions required
8112 if (model->extensionsRequired.size()) {
8113 SerializeStringArrayProperty("extensionsRequired",
8114 model->extensionsRequired, o);
8115 }
8116
8117 // MATERIALS
8118 if (model->materials.size()) {
David03ad33c2023-02-15 23:35:51 -06008119 detail::json materials;
David1f9a4b92023-02-15 22:56:18 -06008120 detail::JsonReserveArray(materials, model->materials.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008121 for (unsigned int i = 0; i < model->materials.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06008122 detail::json material;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008123 SerializeGltfMaterial(model->materials[i], material);
Syoyo Fujita68adc4b2020-10-22 22:27:12 +09008124
David1f9a4b92023-02-15 22:56:18 -06008125 if (detail::JsonIsNull(material)) {
Syoyo Fujita68adc4b2020-10-22 22:27:12 +09008126 // Issue 294.
8127 // `material` does not have any required parameters
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09008128 // so the result may be null(unmodified) when all material parameters
8129 // have default value.
Syoyo Fujita68adc4b2020-10-22 22:27:12 +09008130 //
8131 // null is not allowed thus we create an empty JSON object.
David1f9a4b92023-02-15 22:56:18 -06008132 detail::JsonSetObject(material);
Syoyo Fujita68adc4b2020-10-22 22:27:12 +09008133 }
David1f9a4b92023-02-15 22:56:18 -06008134 detail::JsonPushBack(materials, std::move(material));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008135 }
David1f9a4b92023-02-15 22:56:18 -06008136 detail::JsonAddMember(o, "materials", std::move(materials));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008137 }
8138
8139 // MESHES
8140 if (model->meshes.size()) {
David03ad33c2023-02-15 23:35:51 -06008141 detail::json meshes;
David1f9a4b92023-02-15 22:56:18 -06008142 detail::JsonReserveArray(meshes, model->meshes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008143 for (unsigned int i = 0; i < model->meshes.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06008144 detail::json mesh;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008145 SerializeGltfMesh(model->meshes[i], mesh);
David1f9a4b92023-02-15 22:56:18 -06008146 detail::JsonPushBack(meshes, std::move(mesh));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008147 }
David1f9a4b92023-02-15 22:56:18 -06008148 detail::JsonAddMember(o, "meshes", std::move(meshes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008149 }
8150
8151 // NODES
8152 if (model->nodes.size()) {
David03ad33c2023-02-15 23:35:51 -06008153 detail::json nodes;
David1f9a4b92023-02-15 22:56:18 -06008154 detail::JsonReserveArray(nodes, model->nodes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008155 for (unsigned int i = 0; i < model->nodes.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06008156 detail::json node;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008157 SerializeGltfNode(model->nodes[i], node);
Thomas Gamperd4ea67c2023-11-23 11:59:18 +01008158
8159 if (detail::JsonIsNull(node)) {
8160 // Issue 457.
8161 // `node` does not have any required parameters,
8162 // so the result may be null(unmodified) when all node parameters
8163 // have default value.
8164 //
8165 // null is not allowed thus we create an empty JSON object.
8166 detail::JsonSetObject(node);
8167 }
David1f9a4b92023-02-15 22:56:18 -06008168 detail::JsonPushBack(nodes, std::move(node));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008169 }
David1f9a4b92023-02-15 22:56:18 -06008170 detail::JsonAddMember(o, "nodes", std::move(nodes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008171 }
8172
8173 // SCENE
8174 if (model->defaultScene > -1) {
8175 SerializeNumberProperty<int>("scene", model->defaultScene, o);
8176 }
8177
8178 // SCENES
8179 if (model->scenes.size()) {
David03ad33c2023-02-15 23:35:51 -06008180 detail::json scenes;
David1f9a4b92023-02-15 22:56:18 -06008181 detail::JsonReserveArray(scenes, model->scenes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008182 for (unsigned int i = 0; i < model->scenes.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06008183 detail::json currentScene;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008184 SerializeGltfScene(model->scenes[i], currentScene);
Thomas Gamper3203e192023-11-23 15:14:46 +01008185 if (detail::JsonIsNull(currentScene)) {
8186 // Issue 464.
8187 // `scene` does not have any required parameters,
8188 // so the result may be null(unmodified) when all scene parameters
8189 // have default value.
8190 //
8191 // null is not allowed thus we create an empty JSON object.
8192 detail::JsonSetObject(currentScene);
8193 }
David1f9a4b92023-02-15 22:56:18 -06008194 detail::JsonPushBack(scenes, std::move(currentScene));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008195 }
David1f9a4b92023-02-15 22:56:18 -06008196 detail::JsonAddMember(o, "scenes", std::move(scenes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008197 }
8198
8199 // SKINS
8200 if (model->skins.size()) {
David03ad33c2023-02-15 23:35:51 -06008201 detail::json skins;
David1f9a4b92023-02-15 22:56:18 -06008202 detail::JsonReserveArray(skins, model->skins.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008203 for (unsigned int i = 0; i < model->skins.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06008204 detail::json skin;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008205 SerializeGltfSkin(model->skins[i], skin);
David1f9a4b92023-02-15 22:56:18 -06008206 detail::JsonPushBack(skins, std::move(skin));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008207 }
David1f9a4b92023-02-15 22:56:18 -06008208 detail::JsonAddMember(o, "skins", std::move(skins));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008209 }
8210
8211 // TEXTURES
8212 if (model->textures.size()) {
David03ad33c2023-02-15 23:35:51 -06008213 detail::json textures;
David1f9a4b92023-02-15 22:56:18 -06008214 detail::JsonReserveArray(textures, model->textures.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008215 for (unsigned int i = 0; i < model->textures.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06008216 detail::json texture;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008217 SerializeGltfTexture(model->textures[i], texture);
David1f9a4b92023-02-15 22:56:18 -06008218 detail::JsonPushBack(textures, std::move(texture));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008219 }
David1f9a4b92023-02-15 22:56:18 -06008220 detail::JsonAddMember(o, "textures", std::move(textures));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008221 }
8222
8223 // SAMPLERS
8224 if (model->samplers.size()) {
David03ad33c2023-02-15 23:35:51 -06008225 detail::json samplers;
David1f9a4b92023-02-15 22:56:18 -06008226 detail::JsonReserveArray(samplers, model->samplers.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008227 for (unsigned int i = 0; i < model->samplers.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06008228 detail::json sampler;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008229 SerializeGltfSampler(model->samplers[i], sampler);
David1f9a4b92023-02-15 22:56:18 -06008230 detail::JsonPushBack(samplers, std::move(sampler));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008231 }
David1f9a4b92023-02-15 22:56:18 -06008232 detail::JsonAddMember(o, "samplers", std::move(samplers));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008233 }
8234
8235 // CAMERAS
8236 if (model->cameras.size()) {
David03ad33c2023-02-15 23:35:51 -06008237 detail::json cameras;
David1f9a4b92023-02-15 22:56:18 -06008238 detail::JsonReserveArray(cameras, model->cameras.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008239 for (unsigned int i = 0; i < model->cameras.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06008240 detail::json camera;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008241 SerializeGltfCamera(model->cameras[i], camera);
David1f9a4b92023-02-15 22:56:18 -06008242 detail::JsonPushBack(cameras, std::move(camera));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008243 }
David1f9a4b92023-02-15 22:56:18 -06008244 detail::JsonAddMember(o, "cameras", std::move(cameras));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008245 }
8246
David Siegel07616e82023-06-06 01:24:53 +02008247 // EXTRAS & EXTENSIONS
8248 SerializeExtrasAndExtensions(*model, o);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008249
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008250 auto extensionsUsed = model->extensionsUsed;
8251
8252 // LIGHTS as KHR_lights_punctual
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008253 if (model->lights.size()) {
David03ad33c2023-02-15 23:35:51 -06008254 detail::json lights;
David1f9a4b92023-02-15 22:56:18 -06008255 detail::JsonReserveArray(lights, model->lights.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008256 for (unsigned int i = 0; i < model->lights.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06008257 detail::json light;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008258 SerializeGltfLight(model->lights[i], light);
David1f9a4b92023-02-15 22:56:18 -06008259 detail::JsonPushBack(lights, std::move(light));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008260 }
David03ad33c2023-02-15 23:35:51 -06008261 detail::json khr_lights_cmn;
David1f9a4b92023-02-15 22:56:18 -06008262 detail::JsonAddMember(khr_lights_cmn, "lights", std::move(lights));
David03ad33c2023-02-15 23:35:51 -06008263 detail::json ext_j;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008264
jrkooncecba5d6c2019-08-29 11:26:22 -05008265 {
David03ad33c2023-02-15 23:35:51 -06008266 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06008267 if (detail::FindMember(o, "extensions", it)) {
8268 detail::JsonAssign(ext_j, detail::GetValue(it));
jrkooncecba5d6c2019-08-29 11:26:22 -05008269 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008270 }
8271
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02008272 detail::JsonAddMember(ext_j, "KHR_lights_punctual",
8273 std::move(khr_lights_cmn));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008274
David1f9a4b92023-02-15 22:56:18 -06008275 detail::JsonAddMember(o, "extensions", std::move(ext_j));
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008276
8277 // Also add "KHR_lights_punctual" to `extensionsUsed`
8278 {
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04008279 auto has_khr_lights_punctual =
8280 std::find_if(extensionsUsed.begin(), extensionsUsed.end(),
8281 [](const std::string &s) {
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08008282 return (s.compare("KHR_lights_punctual") == 0);
8283 });
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008284
8285 if (has_khr_lights_punctual == extensionsUsed.end()) {
8286 extensionsUsed.push_back("KHR_lights_punctual");
8287 }
8288 }
8289 }
8290
Baranob_Ilya78864c82023-06-12 10:43:52 +04008291 // KHR_audio
8292 if (!model->audioEmitters.empty() || !model->audioSources.empty()) {
8293 detail::json emitters;
8294 detail::JsonReserveArray(emitters, model->audioEmitters.size());
8295 for (unsigned int i = 0; i < model->audioEmitters.size(); ++i) {
8296 detail::json emitter;
8297 SerializeGltfAudioEmitter(model->audioEmitters[i], emitter);
8298 detail::JsonPushBack(emitters, std::move(emitter));
8299 }
8300 detail::json khr_audio_cmn;
8301 detail::JsonAddMember(khr_audio_cmn, "emitters", std::move(emitters));
8302
8303 detail::json sources;
8304 detail::JsonReserveArray(sources, model->audioSources.size());
8305 for (unsigned int i = 0; i < model->audioSources.size(); ++i) {
8306 detail::json source;
8307 SerializeGltfAudioSource(model->audioSources[i], source);
8308 detail::JsonPushBack(sources, std::move(source));
8309 }
8310 detail::JsonAddMember(khr_audio_cmn, "sources", std::move(sources));
8311
8312 detail::json ext_j;
8313 {
8314 detail::json_const_iterator it;
8315 if (detail::FindMember(o, "extensions", it)) {
8316 detail::JsonAssign(ext_j, detail::GetValue(it));
8317 }
8318 }
8319
8320 detail::JsonAddMember(ext_j, "KHR_audio", std::move(khr_audio_cmn));
8321
8322 detail::JsonAddMember(o, "extensions", std::move(ext_j));
Baranob_Ilyac9657be2023-06-12 12:34:34 +04008323
8324 // Also add "KHR_audio" to `extensionsUsed`
8325 {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02008326 auto has_khr_audio = std::find_if(
8327 extensionsUsed.begin(), extensionsUsed.end(),
8328 [](const std::string &s) { return (s.compare("KHR_audio") == 0); });
Baranob_Ilyac9657be2023-06-12 12:34:34 +04008329
8330 if (has_khr_audio == extensionsUsed.end()) {
8331 extensionsUsed.push_back("KHR_audio");
8332 }
8333 }
Baranob_Ilya78864c82023-06-12 10:43:52 +04008334 }
8335
Thomas Gamperb274b342024-03-25 17:00:41 +01008336 // MSFT_lod
8337
8338 // Look if there is a node that employs MSFT_lod
8339 auto msft_lod_nodes_it = std::find_if(
8340 model->nodes.begin(), model->nodes.end(),
8341 [](const Node& node) { return !node.lods.empty(); });
8342
8343 // Look if there is a material that employs MSFT_lod
8344 auto msft_lod_materials_it = std::find_if(
8345 model->materials.begin(), model->materials.end(),
8346 [](const Material& material) {return !material.lods.empty(); });
8347
8348 // If either a node or a material employ MSFT_lod, then we need
8349 // to add MSFT_lod to the list of used extensions.
8350 if (msft_lod_nodes_it != model->nodes.end() || msft_lod_materials_it != model->materials.end()) {
8351 // First check if MSFT_lod is already registered as used extension
8352 auto has_msft_lod = std::find_if(
8353 extensionsUsed.begin(), extensionsUsed.end(),
8354 [](const std::string &s) { return (s.compare("MSFT_lod") == 0); });
8355
8356 // If MSFT_lod is not registered yet, add it
8357 if (has_msft_lod == extensionsUsed.end()) {
8358 extensionsUsed.push_back("MSFT_lod");
8359 }
8360 }
8361
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008362 // Extensions used
Selmar1208f052021-02-01 19:24:41 +01008363 if (extensionsUsed.size()) {
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008364 SerializeStringArrayProperty("extensionsUsed", extensionsUsed, o);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008365 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008366}
8367
Johan Bowald52936a02019-07-17 09:06:45 +02008368static bool WriteGltfStream(std::ostream &stream, const std::string &content) {
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008369 stream << content << std::endl;
Marco Langer76586242023-03-12 19:26:05 +01008370 return stream.good();
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008371}
8372
8373static bool WriteGltfFile(const std::string &output,
8374 const std::string &content) {
Harokyangfb256602019-10-30 16:13:52 +08008375#ifdef _WIN32
Syoyo Fujita45cac782019-11-09 20:42:55 +09008376#if defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08008377 std::ofstream gltfFile(UTF8ToWchar(output).c_str());
Syoyo Fujita45cac782019-11-09 20:42:55 +09008378#elif defined(__GLIBCXX__)
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008379 int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
TheDev22dfeab2024-03-25 18:21:20 +03008380 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY, _S_IWRITE);
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008381 __gnu_cxx::stdio_filebuf<char> wfile_buf(
8382 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09008383 std::ostream gltfFile(&wfile_buf);
8384 if (!wfile_buf.is_open()) return false;
Harokyangfb256602019-10-30 16:13:52 +08008385#else
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008386 std::ofstream gltfFile(output.c_str());
8387 if (!gltfFile.is_open()) return false;
Syoyo Fujita45cac782019-11-09 20:42:55 +09008388#endif
8389#else
8390 std::ofstream gltfFile(output.c_str());
8391 if (!gltfFile.is_open()) return false;
8392#endif
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008393 return WriteGltfStream(gltfFile, content);
8394}
8395
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09008396static bool WriteBinaryGltfStream(std::ostream &stream,
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02008397 const std::string &content,
8398 const std::vector<unsigned char> &binBuffer) {
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008399 const std::string header = "glTF";
8400 const int version = 2;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008401
Alexander Wood190382a2021-10-08 12:19:13 -04008402 const uint32_t content_size = uint32_t(content.size());
8403 const uint32_t binBuffer_size = uint32_t(binBuffer.size());
8404 // determine number of padding bytes required to ensure 4 byte alignment
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09008405 const uint32_t content_padding_size =
8406 content_size % 4 == 0 ? 0 : 4 - content_size % 4;
8407 const uint32_t bin_padding_size =
8408 binBuffer_size % 4 == 0 ? 0 : 4 - binBuffer_size % 4;
Syoyo Fujita1d205202019-11-16 17:00:17 +09008409
8410 // 12 bytes for header, JSON content length, 8 bytes for JSON chunk info.
Alexander Wood190382a2021-10-08 12:19:13 -04008411 // Chunk data must be located at 4-byte boundary, which may require padding
Syoyo Fujitac4166e42020-01-08 02:38:01 +09008412 const uint32_t length =
Alexander Wood9e3d1f62021-10-08 16:57:56 -04008413 12 + 8 + content_size + content_padding_size +
Alexander Wood13803652021-10-08 20:13:07 -04008414 (binBuffer_size ? (8 + binBuffer_size + bin_padding_size) : 0);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008415
Syoyo Fujitacea69e32019-08-20 17:10:30 +09008416 stream.write(header.c_str(), std::streamsize(header.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008417 stream.write(reinterpret_cast<const char *>(&version), sizeof(version));
8418 stream.write(reinterpret_cast<const char *>(&length), sizeof(length));
8419
8420 // JSON chunk info, then JSON data
Alexander Wood9e3d1f62021-10-08 16:57:56 -04008421 const uint32_t model_length = uint32_t(content.size()) + content_padding_size;
Syoyo Fujita72f4a552020-01-08 00:40:41 +09008422 const uint32_t model_format = 0x4E4F534A;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008423 stream.write(reinterpret_cast<const char *>(&model_length),
Johan Bowald52936a02019-07-17 09:06:45 +02008424 sizeof(model_length));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008425 stream.write(reinterpret_cast<const char *>(&model_format),
Johan Bowald52936a02019-07-17 09:06:45 +02008426 sizeof(model_format));
Syoyo Fujitacea69e32019-08-20 17:10:30 +09008427 stream.write(content.c_str(), std::streamsize(content.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008428
8429 // Chunk must be multiplies of 4, so pad with spaces
Alexander Wood9e3d1f62021-10-08 16:57:56 -04008430 if (content_padding_size > 0) {
8431 const std::string padding = std::string(size_t(content_padding_size), ' ');
Syoyo Fujitacea69e32019-08-20 17:10:30 +09008432 stream.write(padding.c_str(), std::streamsize(padding.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008433 }
Syoyo Fujitac4166e42020-01-08 02:38:01 +09008434 if (binBuffer.size() > 0) {
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02008435 // BIN chunk info, then BIN data
Syoyo Fujita72f4a552020-01-08 00:40:41 +09008436 const uint32_t bin_length = uint32_t(binBuffer.size()) + bin_padding_size;
8437 const uint32_t bin_format = 0x004e4942;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02008438 stream.write(reinterpret_cast<const char *>(&bin_length),
Syoyo Fujitac4166e42020-01-08 02:38:01 +09008439 sizeof(bin_length));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02008440 stream.write(reinterpret_cast<const char *>(&bin_format),
Syoyo Fujitac4166e42020-01-08 02:38:01 +09008441 sizeof(bin_format));
8442 stream.write(reinterpret_cast<const char *>(binBuffer.data()),
8443 std::streamsize(binBuffer.size()));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02008444 // Chunksize must be multiplies of 4, so pad with zeroes
8445 if (bin_padding_size > 0) {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09008446 const std::vector<unsigned char> padding =
8447 std::vector<unsigned char>(size_t(bin_padding_size), 0);
8448 stream.write(reinterpret_cast<const char *>(padding.data()),
8449 std::streamsize(padding.size()));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02008450 }
8451 }
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09008452
Marco Langer76586242023-03-12 19:26:05 +01008453 stream.flush();
8454 return stream.good();
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008455}
8456
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09008457static bool WriteBinaryGltfFile(const std::string &output,
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02008458 const std::string &content,
8459 const std::vector<unsigned char> &binBuffer) {
Harokyangfb256602019-10-30 16:13:52 +08008460#ifdef _WIN32
Syoyo Fujita45cac782019-11-09 20:42:55 +09008461#if defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08008462 std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09008463#elif defined(__GLIBCXX__)
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008464 int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
TheDev22dfeab2024-03-25 18:21:20 +03008465 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY, _S_IWRITE);
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008466 __gnu_cxx::stdio_filebuf<char> wfile_buf(
8467 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita4ab03862019-11-10 15:31:17 +09008468 std::ostream gltfFile(&wfile_buf);
Harokyangfb256602019-10-30 16:13:52 +08008469#else
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008470 std::ofstream gltfFile(output.c_str(), std::ios::binary);
Harokyangfb256602019-10-30 16:13:52 +08008471#endif
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008472#else
8473 std::ofstream gltfFile(output.c_str(), std::ios::binary);
jrkooncecba5d6c2019-08-29 11:26:22 -05008474#endif
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09008475 return WriteBinaryGltfStream(gltfFile, content, binBuffer);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008476}
8477
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08008478bool TinyGLTF::WriteGltfSceneToStream(const Model *model, std::ostream &stream,
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008479 bool prettyPrint = true,
8480 bool writeBinary = false) {
David03ad33c2023-02-15 23:35:51 -06008481 detail::JsonDocument output;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008482
8483 /// Serialize all properties except buffers and images.
8484 SerializeGltfModel(model, output);
8485
8486 // BUFFERS
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02008487 std::vector<unsigned char> binBuffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008488 if (model->buffers.size()) {
David03ad33c2023-02-15 23:35:51 -06008489 detail::json buffers;
David1f9a4b92023-02-15 22:56:18 -06008490 detail::JsonReserveArray(buffers, model->buffers.size());
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008491 for (unsigned int i = 0; i < model->buffers.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06008492 detail::json buffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008493 if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
8494 SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008495 } else {
8496 SerializeGltfBuffer(model->buffers[i], buffer);
8497 }
David1f9a4b92023-02-15 22:56:18 -06008498 detail::JsonPushBack(buffers, std::move(buffer));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02008499 }
David1f9a4b92023-02-15 22:56:18 -06008500 detail::JsonAddMember(output, "buffers", std::move(buffers));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008501 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008502
8503 // IMAGES
8504 if (model->images.size()) {
David03ad33c2023-02-15 23:35:51 -06008505 detail::json images;
David1f9a4b92023-02-15 22:56:18 -06008506 detail::JsonReserveArray(images, model->images.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008507 for (unsigned int i = 0; i < model->images.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06008508 detail::json image;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008509
Bryn Lloydc704d732023-06-21 18:42:24 +02008510 std::string dummystring;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008511 // UpdateImageObject need baseDir but only uses it if embeddedImages is
8512 // enabled, since we won't write separate images when writing to a stream
8513 // we
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08008514 std::string uri;
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08008515 if (!UpdateImageObject(model->images[i], dummystring, int(i), true,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08008516 &uri_cb, &this->WriteImageData,
8517 this->write_image_user_data_, &uri)) {
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08008518 return false;
8519 }
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08008520 SerializeGltfImage(model->images[i], uri, image);
David1f9a4b92023-02-15 22:56:18 -06008521 detail::JsonPushBack(images, std::move(image));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008522 }
David1f9a4b92023-02-15 22:56:18 -06008523 detail::JsonAddMember(output, "images", std::move(images));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008524 }
8525
8526 if (writeBinary) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02008527 return WriteBinaryGltfStream(stream, detail::JsonToString(output),
8528 binBuffer);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008529 } else {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02008530 return WriteGltfStream(stream,
8531 detail::JsonToString(output, prettyPrint ? 2 : -1));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008532 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008533}
8534
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08008535bool TinyGLTF::WriteGltfSceneToFile(const Model *model,
8536 const std::string &filename,
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008537 bool embedImages = false,
8538 bool embedBuffers = false,
8539 bool prettyPrint = true,
8540 bool writeBinary = false) {
David03ad33c2023-02-15 23:35:51 -06008541 detail::JsonDocument output;
Selmar Kok7cb31e42018-10-05 16:02:29 +02008542 std::string defaultBinFilename = GetBaseFilename(filename);
8543 std::string defaultBinFileExt = ".bin";
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00008544 std::string::size_type pos =
8545 defaultBinFilename.rfind('.', defaultBinFilename.length());
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008546
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09008547 if (pos != std::string::npos) {
Selmar Kok7cb31e42018-10-05 16:02:29 +02008548 defaultBinFilename = defaultBinFilename.substr(0, pos);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008549 }
johan bowald642a3432018-04-01 12:37:18 +02008550 std::string baseDir = GetBaseDir(filename);
8551 if (baseDir.empty()) {
8552 baseDir = "./";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05008553 }
Johan Bowald52936a02019-07-17 09:06:45 +02008554 /// Serialize all properties except buffers and images.
8555 SerializeGltfModel(model, output);
Syoyo Fujitad42767e2018-03-15 21:52:00 -05008556
Selmar Kok7cb31e42018-10-05 16:02:29 +02008557 // BUFFERS
Pyarelal Knowles385946d2023-01-03 18:18:04 -08008558 std::vector<std::string> usedFilenames;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02008559 std::vector<unsigned char> binBuffer;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008560 if (model->buffers.size()) {
David03ad33c2023-02-15 23:35:51 -06008561 detail::json buffers;
David1f9a4b92023-02-15 22:56:18 -06008562 detail::JsonReserveArray(buffers, model->buffers.size());
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008563 for (unsigned int i = 0; i < model->buffers.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06008564 detail::json buffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008565 if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
8566 SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008567 } else if (embedBuffers) {
8568 SerializeGltfBuffer(model->buffers[i], buffer);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00008569 } else {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008570 std::string binSavePath;
Pyarelal Knowles385946d2023-01-03 18:18:04 -08008571 std::string binFilename;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008572 std::string binUri;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008573 if (!model->buffers[i].uri.empty() &&
8574 !IsDataURI(model->buffers[i].uri)) {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008575 binUri = model->buffers[i].uri;
Pyarelal Knowles385946d2023-01-03 18:18:04 -08008576 if (!uri_cb.decode(binUri, &binFilename, uri_cb.user_data)) {
8577 return false;
8578 }
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008579 } else {
Pyarelal Knowles385946d2023-01-03 18:18:04 -08008580 binFilename = defaultBinFilename + defaultBinFileExt;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008581 bool inUse = true;
8582 int numUsed = 0;
8583 while (inUse) {
8584 inUse = false;
Pyarelal Knowles385946d2023-01-03 18:18:04 -08008585 for (const std::string &usedName : usedFilenames) {
8586 if (binFilename.compare(usedName) != 0) continue;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008587 inUse = true;
Pyarelal Knowles385946d2023-01-03 18:18:04 -08008588 binFilename = defaultBinFilename + std::to_string(numUsed++) +
8589 defaultBinFileExt;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008590 break;
8591 }
Selmar Kokc884e582018-10-05 16:25:54 +02008592 }
Pyarelal Knowles385946d2023-01-03 18:18:04 -08008593
8594 if (uri_cb.encode) {
8595 if (!uri_cb.encode(binFilename, "buffer", &binUri,
8596 uri_cb.user_data)) {
8597 return false;
8598 }
8599 } else {
8600 binUri = binFilename;
8601 }
Selmar Kokc884e582018-10-05 16:25:54 +02008602 }
Pyarelal Knowles385946d2023-01-03 18:18:04 -08008603 usedFilenames.push_back(binFilename);
8604 binSavePath = JoinPath(baseDir, binFilename);
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008605 if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
8606 binUri)) {
8607 return false;
8608 }
Selmar Kokc884e582018-10-05 16:25:54 +02008609 }
David1f9a4b92023-02-15 22:56:18 -06008610 detail::JsonPushBack(buffers, std::move(buffer));
johan bowald30c53472018-03-30 11:49:36 +02008611 }
David1f9a4b92023-02-15 22:56:18 -06008612 detail::JsonAddMember(output, "buffers", std::move(buffers));
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008613 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008614
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09008615 // IMAGES
Johan Bowaldfaa27222018-03-28 14:44:45 +02008616 if (model->images.size()) {
David03ad33c2023-02-15 23:35:51 -06008617 detail::json images;
David1f9a4b92023-02-15 22:56:18 -06008618 detail::JsonReserveArray(images, model->images.size());
Johan Bowaldfaa27222018-03-28 14:44:45 +02008619 for (unsigned int i = 0; i < model->images.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06008620 detail::json image;
johan bowald642a3432018-04-01 12:37:18 +02008621
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08008622 std::string uri;
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08008623 if (!UpdateImageObject(model->images[i], baseDir, int(i), embedImages,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08008624 &uri_cb, &this->WriteImageData,
8625 this->write_image_user_data_, &uri)) {
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08008626 return false;
8627 }
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08008628 SerializeGltfImage(model->images[i], uri, image);
David1f9a4b92023-02-15 22:56:18 -06008629 detail::JsonPushBack(images, std::move(image));
Johan Bowaldfaa27222018-03-28 14:44:45 +02008630 }
David1f9a4b92023-02-15 22:56:18 -06008631 detail::JsonAddMember(output, "images", std::move(images));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008632 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008633
David Harmonda9eac22018-08-30 08:06:05 -04008634 if (writeBinary) {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02008635 return WriteBinaryGltfFile(filename, detail::JsonToString(output),
8636 binBuffer);
David Harmonda9eac22018-08-30 08:06:05 -04008637 } else {
Bryn Lloyd3e98ac42023-06-21 22:15:49 +02008638 return WriteGltfFile(filename,
8639 detail::JsonToString(output, (prettyPrint ? 2 : -1)));
David Harmonda9eac22018-08-30 08:06:05 -04008640 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008641}
8642
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09008643} // namespace tinygltf
8644
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09008645#ifdef __clang__
8646#pragma clang diagnostic pop
8647#endif
8648
Syoyo Fujita612e5782022-09-18 21:01:39 +09008649#endif // TINYGLTF_IMPLEMENTATION