blob: dd57060bc3f1877214e176112c2413ac348edb0e [file] [log] [blame]
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001//
Syoyo Fujita9a1ea7e2017-06-20 02:17:28 +09002// Header-only tiny glTF 2.0 loader and serializer.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003//
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004//
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09005// The MIT License (MIT)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09006//
Syoyo Fujitab702de72021-03-02 19:08:29 +09007// Copyright (c) 2015 - Present Syoyo Fujita, Aurélien Chatelain and many
Syoyo Fujita5b407452017-06-04 17:42:41 +09008// contributors.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09009//
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090010// Permission is hereby granted, free of charge, to any person obtaining a copy
11// of this software and associated documentation files (the "Software"), to deal
12// in the Software without restriction, including without limitation the rights
13// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14// copies of the Software, and to permit persons to whom the Software is
15// furnished to do so, subject to the following conditions:
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090016//
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090017// The above copyright notice and this permission notice shall be included in
18// all copies or substantial portions of the Software.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090019//
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090020// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26// THE SOFTWARE.
27
Syoyo Fujita5a6df342023-06-04 19:07:00 +090028// Version: - v2.8.10
29// See https://github.com/syoyo/tinygltf/releases for release history.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090030//
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090031// Tiny glTF loader is using following third party libraries:
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090032//
Syoyo Fujita2e21be72017-11-05 17:13:01 +090033// - jsonhpp: C++ JSON library.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090034// - base64: base64 decode/encode library.
35// - stb_image: Image loading library.
36//
Syoyo Fujita2840a0e2017-06-21 02:52:11 +090037#ifndef TINY_GLTF_H_
38#define TINY_GLTF_H_
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090039
Syoyo Fujitad42767e2018-03-15 21:52:00 -050040#include <array>
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090041#include <cassert>
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +090042#include <cmath> // std::fabs
Syoyo Fujitad42767e2018-03-15 21:52:00 -050043#include <cstdint>
Selmar Kok31cb7f92018-10-03 15:39:05 +020044#include <cstdlib>
Syoyo Fujita641b3cc2018-10-04 15:43:33 +090045#include <cstring>
Syoyo Fujita046400b2019-07-24 19:26:48 +090046#include <limits>
Syoyo Fujita523c9bf2016-04-20 14:06:56 +090047#include <map>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090048#include <string>
49#include <vector>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090050
zbendefy69eeea12022-09-05 23:54:57 +020051//Auto-detect C++14 standard version
52#if !defined(TINYGLTF_USE_CPP14) && defined(__cplusplus) && (__cplusplus >= 201402L)
53#define TINYGLTF_USE_CPP14
54#endif
55
Sascha Willems5f9cb242018-12-28 20:53:41 +010056#ifdef __ANDROID__
57#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
58#include <android/asset_manager.h>
59#endif
60#endif
61
Selmar Kok79e3df22019-10-29 16:22:07 +010062#ifdef __GNUC__
63#if (__GNUC__ < 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ <= 8))
Selmar Kokb74fade2019-10-29 16:09:32 +010064#define TINYGLTF_NOEXCEPT
Selmar Kok79e3df22019-10-29 16:22:07 +010065#else
66#define TINYGLTF_NOEXCEPT noexcept
Selmar Kokb74fade2019-10-29 16:09:32 +010067#endif
Selmar Kok79e3df22019-10-29 16:22:07 +010068#else
69#define TINYGLTF_NOEXCEPT noexcept
70#endif
71
Syoyo Fujita6e08b172019-10-30 17:25:38 +090072#define DEFAULT_METHODS(x) \
73 ~x() = default; \
74 x(const x &) = default; \
75 x(x &&) TINYGLTF_NOEXCEPT = default; \
76 x &operator=(const x &) = default; \
77 x &operator=(x &&) TINYGLTF_NOEXCEPT = default;
Selmar Kokb74fade2019-10-29 16:09:32 +010078
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090079namespace tinygltf {
80
81#define TINYGLTF_MODE_POINTS (0)
82#define TINYGLTF_MODE_LINE (1)
83#define TINYGLTF_MODE_LINE_LOOP (2)
Thomas Tissot6c4a0062019-01-30 13:10:51 +010084#define TINYGLTF_MODE_LINE_STRIP (3)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090085#define TINYGLTF_MODE_TRIANGLES (4)
86#define TINYGLTF_MODE_TRIANGLE_STRIP (5)
87#define TINYGLTF_MODE_TRIANGLE_FAN (6)
88
89#define TINYGLTF_COMPONENT_TYPE_BYTE (5120)
90#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE (5121)
91#define TINYGLTF_COMPONENT_TYPE_SHORT (5122)
92#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT (5123)
93#define TINYGLTF_COMPONENT_TYPE_INT (5124)
94#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125)
95#define TINYGLTF_COMPONENT_TYPE_FLOAT (5126)
Syoyo Fujita52ff00a2022-08-16 20:08:45 +090096#define TINYGLTF_COMPONENT_TYPE_DOUBLE \
97 (5130) // OpenGL double type. Note that some of glTF 2.0 validator does not
98 // support double type even the schema seems allow any value of
99 // integer:
100 // https://github.com/KhronosGroup/glTF/blob/b9884a2fd45130b4d673dd6c8a706ee21ee5c5f7/specification/2.0/schema/accessor.schema.json#L22
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900101
Syoyo Fujitac2615632016-06-19 21:56:06 +0900102#define TINYGLTF_TEXTURE_FILTER_NEAREST (9728)
103#define TINYGLTF_TEXTURE_FILTER_LINEAR (9729)
104#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST (9984)
105#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST (9985)
106#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR (9986)
107#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR (9987)
108
Cemalettin Dervis246d8662017-12-07 20:29:51 +0100109#define TINYGLTF_TEXTURE_WRAP_REPEAT (10497)
Syoyo Fujitac2615632016-06-19 21:56:06 +0900110#define TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE (33071)
Luke San Antonio Bialecki904612c2016-08-19 17:28:03 -0400111#define TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT (33648)
Syoyo Fujitac2615632016-06-19 21:56:06 +0900112
Luke San Antoniocdf4cb72016-06-14 21:32:11 -0400113// Redeclarations of the above for technique.parameters.
114#define TINYGLTF_PARAMETER_TYPE_BYTE (5120)
115#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE (5121)
116#define TINYGLTF_PARAMETER_TYPE_SHORT (5122)
117#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT (5123)
118#define TINYGLTF_PARAMETER_TYPE_INT (5124)
119#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT (5125)
120#define TINYGLTF_PARAMETER_TYPE_FLOAT (5126)
121
122#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC2 (35664)
123#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC3 (35665)
124#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC4 (35666)
125
126#define TINYGLTF_PARAMETER_TYPE_INT_VEC2 (35667)
127#define TINYGLTF_PARAMETER_TYPE_INT_VEC3 (35668)
128#define TINYGLTF_PARAMETER_TYPE_INT_VEC4 (35669)
129
130#define TINYGLTF_PARAMETER_TYPE_BOOL (35670)
131#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC2 (35671)
132#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC3 (35672)
133#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC4 (35673)
134
135#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT2 (35674)
136#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT3 (35675)
137#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT4 (35676)
138
139#define TINYGLTF_PARAMETER_TYPE_SAMPLER_2D (35678)
140
141// End parameter types
142
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900143#define TINYGLTF_TYPE_VEC2 (2)
144#define TINYGLTF_TYPE_VEC3 (3)
145#define TINYGLTF_TYPE_VEC4 (4)
146#define TINYGLTF_TYPE_MAT2 (32 + 2)
147#define TINYGLTF_TYPE_MAT3 (32 + 3)
148#define TINYGLTF_TYPE_MAT4 (32 + 4)
149#define TINYGLTF_TYPE_SCALAR (64 + 1)
150#define TINYGLTF_TYPE_VECTOR (64 + 4)
151#define TINYGLTF_TYPE_MATRIX (64 + 16)
152
153#define TINYGLTF_IMAGE_FORMAT_JPEG (0)
154#define TINYGLTF_IMAGE_FORMAT_PNG (1)
155#define TINYGLTF_IMAGE_FORMAT_BMP (2)
156#define TINYGLTF_IMAGE_FORMAT_GIF (3)
157
Luke San Antonio6d616f52016-06-23 14:09:23 -0400158#define TINYGLTF_TEXTURE_FORMAT_ALPHA (6406)
159#define TINYGLTF_TEXTURE_FORMAT_RGB (6407)
Syoyo Fujitabde70212016-02-07 17:38:17 +0900160#define TINYGLTF_TEXTURE_FORMAT_RGBA (6408)
Luke San Antonio6d616f52016-06-23 14:09:23 -0400161#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE (6409)
162#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE_ALPHA (6410)
163
Syoyo Fujitabde70212016-02-07 17:38:17 +0900164#define TINYGLTF_TEXTURE_TARGET_TEXTURE2D (3553)
165#define TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE (5121)
166
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900167#define TINYGLTF_TARGET_ARRAY_BUFFER (34962)
168#define TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER (34963)
169
Luke San Antoniocdf4cb72016-06-14 21:32:11 -0400170#define TINYGLTF_SHADER_TYPE_VERTEX_SHADER (35633)
171#define TINYGLTF_SHADER_TYPE_FRAGMENT_SHADER (35632)
172
Selmar Kok31cb7f92018-10-03 15:39:05 +0200173#define TINYGLTF_DOUBLE_EPS (1.e-12)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900174#define TINYGLTF_DOUBLE_EQUAL(a, b) (std::fabs((b) - (a)) < TINYGLTF_DOUBLE_EPS)
Selmar Kok31cb7f92018-10-03 15:39:05 +0200175
Sascha Willems5f9cb242018-12-28 20:53:41 +0100176#ifdef __ANDROID__
177#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
Serdar Kocdemir264ae4c2022-10-30 22:32:59 +0000178#ifdef TINYGLTF_IMPLEMENTATION
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000179AAssetManager *asset_manager = nullptr;
Serdar Kocdemir264ae4c2022-10-30 22:32:59 +0000180#else
181extern AAssetManager *asset_manager;
182#endif
Sascha Willems5f9cb242018-12-28 20:53:41 +0100183#endif
184#endif
185
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900186typedef enum {
Idriss Chaouch425195b2021-05-20 12:11:00 +0100187 NULL_TYPE,
188 REAL_TYPE,
189 INT_TYPE,
190 BOOL_TYPE,
191 STRING_TYPE,
192 ARRAY_TYPE,
193 BINARY_TYPE,
194 OBJECT_TYPE
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900195} Type;
196
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500197static inline int32_t GetComponentSizeInBytes(uint32_t componentType) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900198 if (componentType == TINYGLTF_COMPONENT_TYPE_BYTE) {
199 return 1;
200 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
201 return 1;
202 } else if (componentType == TINYGLTF_COMPONENT_TYPE_SHORT) {
203 return 2;
204 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
205 return 2;
206 } else if (componentType == TINYGLTF_COMPONENT_TYPE_INT) {
207 return 4;
208 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) {
209 return 4;
210 } else if (componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) {
211 return 4;
212 } else if (componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) {
213 return 8;
214 } else {
imallettd9ce9eb2022-10-07 10:37:09 -0700215 // Unknown component type
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900216 return -1;
217 }
218}
219
DingboDingboDingbo83ccb9f2019-08-29 18:49:15 -0400220static inline int32_t GetNumComponentsInType(uint32_t ty) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900221 if (ty == TINYGLTF_TYPE_SCALAR) {
222 return 1;
223 } else if (ty == TINYGLTF_TYPE_VEC2) {
224 return 2;
225 } else if (ty == TINYGLTF_TYPE_VEC3) {
226 return 3;
227 } else if (ty == TINYGLTF_TYPE_VEC4) {
228 return 4;
229 } else if (ty == TINYGLTF_TYPE_MAT2) {
230 return 4;
231 } else if (ty == TINYGLTF_TYPE_MAT3) {
232 return 9;
233 } else if (ty == TINYGLTF_TYPE_MAT4) {
234 return 16;
235 } else {
imallettd9ce9eb2022-10-07 10:37:09 -0700236 // Unknown component type
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900237 return -1;
238 }
239}
240
Syoyo Fujita150f2432019-07-25 19:22:44 +0900241// TODO(syoyo): Move these functions to TinyGLTF class
Selmar Kok0d0e97e2018-08-22 14:01:57 +0200242bool IsDataURI(const std::string &in);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900243bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
244 const std::string &in, size_t reqBytes, bool checkSize);
Selmar Kok0d0e97e2018-08-22 14:01:57 +0200245
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900246#ifdef __clang__
247#pragma clang diagnostic push
248// Suppress warning for : static Value null_value
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900249#pragma clang diagnostic ignored "-Wexit-time-destructors"
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900250#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900251#endif
252
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900253// Simple class to represent JSON object
254class Value {
255 public:
256 typedef std::vector<Value> Array;
257 typedef std::map<std::string, Value> Object;
258
Syoyo Fujita046400b2019-07-24 19:26:48 +0900259 Value()
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200260
261 {}
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900262
263 explicit Value(bool b) : type_(BOOL_TYPE) { boolean_value_ = b; }
Syoyo Fujitaff515702019-08-24 16:29:14 +0900264 explicit Value(int i) : type_(INT_TYPE) {
265 int_value_ = i;
266 real_value_ = i;
267 }
Syoyo Fujita150f2432019-07-25 19:22:44 +0900268 explicit Value(double n) : type_(REAL_TYPE) { real_value_ = n; }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900269 explicit Value(const std::string &s) : type_(STRING_TYPE) {
270 string_value_ = s;
271 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900272 explicit Value(std::string &&s)
273 : type_(STRING_TYPE), string_value_(std::move(s)) {}
David Siegel49caa652023-04-08 23:44:53 +0200274 explicit Value(const char *s) : type_(STRING_TYPE) {
275 string_value_ = s;
276 }
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900277 explicit Value(const unsigned char *p, size_t n) : type_(BINARY_TYPE) {
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900278 binary_value_.resize(n);
279 memcpy(binary_value_.data(), p, n);
280 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900281 explicit Value(std::vector<unsigned char> &&v) noexcept
282 : type_(BINARY_TYPE),
283 binary_value_(std::move(v)) {}
284 explicit Value(const Array &a) : type_(ARRAY_TYPE) { array_value_ = a; }
285 explicit Value(Array &&a) noexcept : type_(ARRAY_TYPE),
286 array_value_(std::move(a)) {}
jrkoonced1e14722019-08-27 11:51:02 -0500287
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900288 explicit Value(const Object &o) : type_(OBJECT_TYPE) { object_value_ = o; }
289 explicit Value(Object &&o) noexcept : type_(OBJECT_TYPE),
290 object_value_(std::move(o)) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100291
292 DEFAULT_METHODS(Value)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900293
Hill Mad1e32862021-02-20 22:30:44 -0800294 char Type() const { return static_cast<char>(type_); }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900295
296 bool IsBool() const { return (type_ == BOOL_TYPE); }
297
298 bool IsInt() const { return (type_ == INT_TYPE); }
299
Syoyo Fujita150f2432019-07-25 19:22:44 +0900300 bool IsNumber() const { return (type_ == REAL_TYPE) || (type_ == INT_TYPE); }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900301
Syoyo Fujita150f2432019-07-25 19:22:44 +0900302 bool IsReal() const { return (type_ == REAL_TYPE); }
Syoyo Fujita046400b2019-07-24 19:26:48 +0900303
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900304 bool IsString() const { return (type_ == STRING_TYPE); }
305
306 bool IsBinary() const { return (type_ == BINARY_TYPE); }
307
308 bool IsArray() const { return (type_ == ARRAY_TYPE); }
309
310 bool IsObject() const { return (type_ == OBJECT_TYPE); }
311
Syoyo Fujita150f2432019-07-25 19:22:44 +0900312 // Use this function if you want to have number value as double.
313 double GetNumberAsDouble() const {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900314 if (type_ == INT_TYPE) {
315 return double(int_value_);
Syoyo Fujita150f2432019-07-25 19:22:44 +0900316 } else {
317 return real_value_;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900318 }
Syoyo Fujita150f2432019-07-25 19:22:44 +0900319 }
Syoyo Fujita046400b2019-07-24 19:26:48 +0900320
Syoyo Fujita150f2432019-07-25 19:22:44 +0900321 // Use this function if you want to have number value as int.
Syoyo Fujitaff0a2e92020-05-11 19:07:00 +0900322 // TODO(syoyo): Support int value larger than 32 bits
323 int GetNumberAsInt() const {
Syoyo Fujita150f2432019-07-25 19:22:44 +0900324 if (type_ == REAL_TYPE) {
325 return int(real_value_);
326 } else {
327 return int_value_;
328 }
Syoyo Fujita046400b2019-07-24 19:26:48 +0900329 }
330
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900331 // Accessor
332 template <typename T>
333 const T &Get() const;
334 template <typename T>
335 T &Get();
336
337 // Lookup value from an array
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900338 const Value &Get(int idx) const {
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900339 static Value null_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900340 assert(IsArray());
341 assert(idx >= 0);
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900342 return (static_cast<size_t>(idx) < array_value_.size())
343 ? array_value_[static_cast<size_t>(idx)]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900344 : null_value;
345 }
346
347 // Lookup value from a key-value pair
348 const Value &Get(const std::string &key) const {
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900349 static Value null_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900350 assert(IsObject());
351 Object::const_iterator it = object_value_.find(key);
352 return (it != object_value_.end()) ? it->second : null_value;
353 }
354
355 size_t ArrayLen() const {
356 if (!IsArray()) return 0;
357 return array_value_.size();
358 }
359
360 // Valid only for object type.
361 bool Has(const std::string &key) const {
362 if (!IsObject()) return false;
363 Object::const_iterator it = object_value_.find(key);
364 return (it != object_value_.end()) ? true : false;
365 }
366
367 // List keys
368 std::vector<std::string> Keys() const {
369 std::vector<std::string> keys;
370 if (!IsObject()) return keys; // empty
371
372 for (Object::const_iterator it = object_value_.begin();
373 it != object_value_.end(); ++it) {
374 keys.push_back(it->first);
375 }
376
377 return keys;
378 }
379
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900380 size_t Size() const { return (IsArray() ? ArrayLen() : Keys().size()); }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900381
382 bool operator==(const tinygltf::Value &other) const;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +0000383
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900384 protected:
Syoyo Fujita046400b2019-07-24 19:26:48 +0900385 int type_ = NULL_TYPE;
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900386
Syoyo Fujita046400b2019-07-24 19:26:48 +0900387 int int_value_ = 0;
Syoyo Fujita150f2432019-07-25 19:22:44 +0900388 double real_value_ = 0.0;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900389 std::string string_value_;
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900390 std::vector<unsigned char> binary_value_;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900391 Array array_value_;
392 Object object_value_;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900393 bool boolean_value_ = false;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900394};
395
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900396#ifdef __clang__
397#pragma clang diagnostic pop
398#endif
399
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900400#define TINYGLTF_VALUE_GET(ctype, var) \
401 template <> \
402 inline const ctype &Value::Get<ctype>() const { \
403 return var; \
404 } \
405 template <> \
406 inline ctype &Value::Get<ctype>() { \
407 return var; \
408 }
409TINYGLTF_VALUE_GET(bool, boolean_value_)
Syoyo Fujita150f2432019-07-25 19:22:44 +0900410TINYGLTF_VALUE_GET(double, real_value_)
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900411TINYGLTF_VALUE_GET(int, int_value_)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900412TINYGLTF_VALUE_GET(std::string, string_value_)
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900413TINYGLTF_VALUE_GET(std::vector<unsigned char>, binary_value_)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900414TINYGLTF_VALUE_GET(Value::Array, array_value_)
415TINYGLTF_VALUE_GET(Value::Object, object_value_)
416#undef TINYGLTF_VALUE_GET
417
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900418#ifdef __clang__
419#pragma clang diagnostic push
420#pragma clang diagnostic ignored "-Wc++98-compat"
421#pragma clang diagnostic ignored "-Wpadded"
422#endif
423
imallettd9ce9eb2022-10-07 10:37:09 -0700424/// Aggregate object for representing a color
Arthur Brainville58453192018-01-08 18:25:52 +0100425using ColorValue = std::array<double, 4>;
426
Syoyo Fujita046400b2019-07-24 19:26:48 +0900427// === legacy interface ====
428// TODO(syoyo): Deprecate `Parameter` class.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500429struct Parameter {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200430 bool bool_value = false;
Ben Buzbeef6af2242018-04-25 15:13:05 -0700431 bool has_number_value = false;
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900432 std::string string_value;
433 std::vector<double> number_array;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000434 std::map<std::string, double> json_double_value;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200435 double number_value = 0.0;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900436
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500437 // context sensitive methods. depending the type of the Parameter you are
438 // accessing, these are either valid or not
439 // If this parameter represent a texture map in a material, will return the
440 // texture index
Arthur Brainville58453192018-01-08 18:25:52 +0100441
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500442 /// Return the index of a texture if this Parameter is a texture map.
443 /// Returned value is only valid if the parameter represent a texture from a
444 /// material
Arthur Brainville41fe7722018-01-08 18:32:48 +0100445 int TextureIndex() const {
Arthur Brainville58453192018-01-08 18:25:52 +0100446 const auto it = json_double_value.find("index");
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500447 if (it != std::end(json_double_value)) {
Arthur Brainville58453192018-01-08 18:25:52 +0100448 return int(it->second);
449 }
450 return -1;
451 }
452
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000453 /// Return the index of a texture coordinate set if this Parameter is a
454 /// texture map. Returned value is only valid if the parameter represent a
455 /// texture from a material
Sascha Willemseb011062019-02-23 21:15:45 +0100456 int TextureTexCoord() const {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000457 const auto it = json_double_value.find("texCoord");
458 if (it != std::end(json_double_value)) {
459 return int(it->second);
460 }
imallettd9ce9eb2022-10-07 10:37:09 -0700461 // As per the spec, if texCoord is omitted, this parameter is 0
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000462 return 0;
Sascha Willemseb011062019-02-23 21:15:45 +0100463 }
464
Christophe820ede82019-07-04 15:21:21 +0900465 /// Return the scale of a texture if this Parameter is a normal texture map.
466 /// Returned value is only valid if the parameter represent a normal texture
467 /// from a material
468 double TextureScale() const {
469 const auto it = json_double_value.find("scale");
470 if (it != std::end(json_double_value)) {
471 return it->second;
472 }
imallettd9ce9eb2022-10-07 10:37:09 -0700473 // As per the spec, if scale is omitted, this parameter is 1
Arthur Brainville8a98d982019-07-05 00:26:02 +0200474 return 1;
475 }
Arthur Brianville (Ybalrid)2a9d9de2019-07-05 00:30:47 +0200476
Arthur Brainville8a98d982019-07-05 00:26:02 +0200477 /// Return the strength of a texture if this Parameter is a an occlusion map.
478 /// Returned value is only valid if the parameter represent an occlusion map
479 /// from a material
480 double TextureStrength() const {
481 const auto it = json_double_value.find("strength");
482 if (it != std::end(json_double_value)) {
483 return it->second;
484 }
imallettd9ce9eb2022-10-07 10:37:09 -0700485 // As per the spec, if strength is omitted, this parameter is 1
Arthur Brainville8a98d982019-07-05 00:26:02 +0200486 return 1;
Christophe820ede82019-07-04 15:21:21 +0900487 }
488
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500489 /// Material factor, like the roughness or metalness of a material
490 /// Returned value is only valid if the parameter represent a texture from a
491 /// material
Ben Buzbeef6af2242018-04-25 15:13:05 -0700492 double Factor() const { return number_value; }
Arthur Brainville58453192018-01-08 18:25:52 +0100493
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500494 /// Return the color of a material
495 /// Returned value is only valid if the parameter represent a texture from a
496 /// material
Arthur Brainville95853912018-01-08 18:37:44 +0100497 ColorValue ColorFactor() const {
Arthur Brainville58453192018-01-08 18:25:52 +0100498 return {
imallettd9ce9eb2022-10-07 10:37:09 -0700499 {// this aggregate initialize the std::array object, and uses C++11 RVO.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500500 number_array[0], number_array[1], number_array[2],
501 (number_array.size() > 3 ? number_array[3] : 1.0)}};
Arthur Brainville58453192018-01-08 18:25:52 +0100502 }
Selmar Kok31cb7f92018-10-03 15:39:05 +0200503
Selmar Kokff2b1f92019-10-21 17:58:09 +0200504 Parameter() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100505 DEFAULT_METHODS(Parameter)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900506 bool operator==(const Parameter &) const;
Arthur Brainville58453192018-01-08 18:25:52 +0100507};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900508
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900509#ifdef __clang__
510#pragma clang diagnostic pop
511#endif
512
513#ifdef __clang__
514#pragma clang diagnostic push
515#pragma clang diagnostic ignored "-Wpadded"
516#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900517
Syoyo Fujitabde70212016-02-07 17:38:17 +0900518typedef std::map<std::string, Parameter> ParameterMap;
Selmar Kokfa7022f2018-04-04 18:10:20 +0200519typedef std::map<std::string, Value> ExtensionMap;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900520
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000521struct AnimationChannel {
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200522 int sampler{-1}; // required
523 int target_node{-1}; // optional index of the node to target (alternative
Jack Mousseau283b5522023-01-15 11:45:45 -0800524 // target should be provided by extension)
525 std::string target_path; // required with standard values of ["translation",
526 // "rotation", "scale", "weights"]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900527 Value extras;
Selmar Kok4e2988e2019-08-16 14:08:08 +0200528 ExtensionMap extensions;
David Siegeld852f502023-06-05 23:28:05 +0200529 Value target_extras;
Selmar Kok973d9b32020-01-21 18:45:24 +0100530 ExtensionMap target_extensions;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900531
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900532 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
533 std::string extras_json_string;
534 std::string extensions_json_string;
David Siegeld852f502023-06-05 23:28:05 +0200535 std::string target_extras_json_string;
Selmar Kok973d9b32020-01-21 18:45:24 +0100536 std::string target_extensions_json_string;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900537
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200538 AnimationChannel() {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100539 DEFAULT_METHODS(AnimationChannel)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900540 bool operator==(const AnimationChannel &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000541};
542
543struct AnimationSampler {
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200544 int input{-1}; // required
545 int output{-1}; // required
Arthur Brianville (Ybalrid)2a9d9de2019-07-05 00:30:47 +0200546 std::string interpolation; // "LINEAR", "STEP","CUBICSPLINE" or user defined
547 // string. default "LINEAR"
Jens Olssonb3af2f12018-06-04 10:17:49 +0200548 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900549 ExtensionMap extensions;
550
551 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
552 std::string extras_json_string;
553 std::string extensions_json_string;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000554
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200555 AnimationSampler() : interpolation("LINEAR") {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100556 DEFAULT_METHODS(AnimationSampler)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900557 bool operator==(const AnimationSampler &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000558};
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900559
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900560struct Animation {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900561 std::string name;
562 std::vector<AnimationChannel> channels;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000563 std::vector<AnimationSampler> samplers;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900564 Value extras;
Selmar Kok4e2988e2019-08-16 14:08:08 +0200565 ExtensionMap extensions;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200566
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900567 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
568 std::string extras_json_string;
569 std::string extensions_json_string;
570
Selmar Kokff2b1f92019-10-21 17:58:09 +0200571 Animation() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100572 DEFAULT_METHODS(Animation)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900573 bool operator==(const Animation &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900574};
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900575
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000576struct Skin {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900577 std::string name;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900578 int inverseBindMatrices; // required here but not in the spec
579 int skeleton; // The index of the node used as a skeleton root
580 std::vector<int> joints; // Indices of skeleton nodes
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000581
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900582 Value extras;
583 ExtensionMap extensions;
584
585 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
586 std::string extras_json_string;
587 std::string extensions_json_string;
588
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900589 Skin() {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000590 inverseBindMatrices = -1;
Aurélien Chatelain8d5e0a72017-06-19 13:52:08 +0000591 skeleton = -1;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000592 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100593 DEFAULT_METHODS(Skin)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900594 bool operator==(const Skin &) const;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000595};
596
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000597struct Sampler {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900598 std::string name;
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900599 // glTF 2.0 spec does not define default value for `minFilter` and
600 // `magFilter`. Set -1 in TinyGLTF(issue #186)
601 int minFilter =
602 -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR",
zz8a35b8f2021-05-14 13:49:33 +0800603 // "NEAREST_MIPMAP_NEAREST", "LINEAR_MIPMAP_NEAREST",
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900604 // "NEAREST_MIPMAP_LINEAR", "LINEAR_MIPMAP_LINEAR"]
605 int magFilter =
606 -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR"]
607 int wrapS =
608 TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
609 // "REPEAT"], default "REPEAT"
610 int wrapT =
611 TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
612 // "REPEAT"], default "REPEAT"
Syoyo Fujita52ff00a2022-08-16 20:08:45 +0900613 // int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; // TinyGLTF extension. currently
614 // not used.
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900615
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900616 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900617 ExtensionMap extensions;
618
619 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
620 std::string extras_json_string;
621 std::string extensions_json_string;
Syoyo Fujitac2615632016-06-19 21:56:06 +0900622
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000623 Sampler()
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200624
625 {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100626 DEFAULT_METHODS(Sampler)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900627 bool operator==(const Sampler &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000628};
629
Syoyo Fujita5b407452017-06-04 17:42:41 +0900630struct Image {
Syoyo Fujitac2615632016-06-19 21:56:06 +0900631 std::string name;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900632 int width;
633 int height;
634 int component;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000635 int bits; // bit depth per channel. 8(byte), 16 or 32.
636 int pixel_type; // pixel type(TINYGLTF_COMPONENT_TYPE_***). usually
637 // UBYTE(bits = 8) or USHORT(bits = 16)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900638 std::vector<unsigned char> image;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900639 int bufferView; // (required if no uri)
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500640 std::string mimeType; // (required if no uri) ["image/jpeg", "image/png",
641 // "image/bmp", "image/gif"]
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900642 std::string uri; // (required if no mimeType) uri is not decoded(e.g.
643 // whitespace may be represented as %20)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900644 Value extras;
Syoyo Fujita3e53feb2018-09-02 15:36:17 +0900645 ExtensionMap extensions;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900646
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900647 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
648 std::string extras_json_string;
649 std::string extensions_json_string;
650
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900651 // When this flag is true, data is stored to `image` in as-is format(e.g. jpeg
652 // compressed for "image/jpeg" mime) This feature is good if you use custom
653 // image loader function. (e.g. delayed decoding of images for faster glTF
654 // parsing) Default parser for Image does not provide as-is loading feature at
655 // the moment. (You can manipulate this by providing your own LoadImageData
656 // function)
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200657 bool as_is{false};
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900658
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200659 Image() {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900660 bufferView = -1;
661 width = -1;
662 height = -1;
663 component = -1;
daemyung jang1739d022020-07-03 13:27:39 +0900664 bits = -1;
665 pixel_type = -1;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900666 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100667 DEFAULT_METHODS(Image)
jrkoonced1e14722019-08-27 11:51:02 -0500668
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900669 bool operator==(const Image &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000670};
671
672struct Texture {
Vladimír Vondruš239be2c2018-07-24 23:23:56 +0200673 std::string name;
674
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200675 int sampler{-1};
676 int source{-1};
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900677 Value extras;
Selmar Kokfa7022f2018-04-04 18:10:20 +0200678 ExtensionMap extensions;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900679
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900680 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
681 std::string extras_json_string;
682 std::string extensions_json_string;
683
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200684 Texture() {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100685 DEFAULT_METHODS(Texture)
jrkoonced1e14722019-08-27 11:51:02 -0500686
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900687 bool operator==(const Texture &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000688};
Syoyo Fujitabde70212016-02-07 17:38:17 +0900689
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900690struct TextureInfo {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900691 int index = -1; // required.
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200692 int texCoord{0}; // The set index of texture's TEXCOORD attribute used for
Syoyo Fujita046400b2019-07-24 19:26:48 +0900693 // texture coordinate mapping.
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900694
695 Value extras;
696 ExtensionMap extensions;
697
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900698 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
699 std::string extras_json_string;
700 std::string extensions_json_string;
701
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200702 TextureInfo() {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100703 DEFAULT_METHODS(TextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900704 bool operator==(const TextureInfo &) const;
705};
706
707struct NormalTextureInfo {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900708 int index = -1; // required
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200709 int texCoord{0}; // The set index of texture's TEXCOORD attribute used for
Syoyo Fujita046400b2019-07-24 19:26:48 +0900710 // texture coordinate mapping.
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200711 double scale{1.0}; // scaledNormal = normalize((<sampled normal texture value>
Syoyo Fujita046400b2019-07-24 19:26:48 +0900712 // * 2.0 - 1.0) * vec3(<normal scale>, <normal scale>, 1.0))
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900713
714 Value extras;
715 ExtensionMap extensions;
716
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900717 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
718 std::string extras_json_string;
719 std::string extensions_json_string;
720
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200721 NormalTextureInfo() {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100722 DEFAULT_METHODS(NormalTextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900723 bool operator==(const NormalTextureInfo &) const;
724};
725
726struct OcclusionTextureInfo {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900727 int index = -1; // required
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200728 int texCoord{0}; // The set index of texture's TEXCOORD attribute used for
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900729 // texture coordinate mapping.
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200730 double strength{1.0}; // occludedColor = lerp(color, color * <sampled occlusion
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900731 // texture value>, <occlusion strength>)
732
733 Value extras;
734 ExtensionMap extensions;
735
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900736 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
737 std::string extras_json_string;
738 std::string extensions_json_string;
739
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200740 OcclusionTextureInfo() {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100741 DEFAULT_METHODS(OcclusionTextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900742 bool operator==(const OcclusionTextureInfo &) const;
743};
744
745// pbrMetallicRoughness class defined in glTF 2.0 spec.
746struct PbrMetallicRoughness {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900747 std::vector<double> baseColorFactor; // len = 4. default [1,1,1,1]
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900748 TextureInfo baseColorTexture;
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200749 double metallicFactor{1.0}; // default 1
750 double roughnessFactor{1.0}; // default 1
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900751 TextureInfo metallicRoughnessTexture;
752
753 Value extras;
754 ExtensionMap extensions;
755
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900756 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
757 std::string extras_json_string;
758 std::string extensions_json_string;
759
760 PbrMetallicRoughness()
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200761 : baseColorFactor(std::vector<double>{1.0, 1.0, 1.0, 1.0})
762 {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100763 DEFAULT_METHODS(PbrMetallicRoughness)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900764 bool operator==(const PbrMetallicRoughness &) const;
765};
766
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000767// Each extension should be stored in a ParameterMap.
768// members not in the values could be included in the ParameterMap
769// to keep a single material model
770struct Material {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900771 std::string name;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900772
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900773 std::vector<double> emissiveFactor; // length 3. default [0, 0, 0]
Syoyo Fujita046400b2019-07-24 19:26:48 +0900774 std::string alphaMode; // default "OPAQUE"
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200775 double alphaCutoff{0.5}; // default 0.5
776 bool doubleSided{false}; // default false;
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900777
778 PbrMetallicRoughness pbrMetallicRoughness;
779
780 NormalTextureInfo normalTexture;
781 OcclusionTextureInfo occlusionTexture;
782 TextureInfo emissiveTexture;
783
Syoyo Fujita046400b2019-07-24 19:26:48 +0900784 // For backward compatibility
785 // TODO(syoyo): Remove `values` and `additionalValues` in the next release.
786 ParameterMap values;
787 ParameterMap additionalValues;
Selmar09d2ff12018-03-15 17:30:42 +0100788
789 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900790 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200791
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900792 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
793 std::string extras_json_string;
794 std::string extensions_json_string;
795
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200796 Material() : alphaMode("OPAQUE") {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100797 DEFAULT_METHODS(Material)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900798
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900799 bool operator==(const Material &) const;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000800};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900801
Syoyo Fujita5b407452017-06-04 17:42:41 +0900802struct BufferView {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900803 std::string name;
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900804 int buffer{-1}; // Required
Syoyo Fujita4e47bc72020-01-02 22:07:25 +0900805 size_t byteOffset{0}; // minimum 0, default 0
806 size_t byteLength{0}; // required, minimum 1. 0 = invalid
807 size_t byteStride{0}; // minimum 4, maximum 252 (multiple of 4), default 0 =
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900808 // understood to be tightly packed
809 int target{0}; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"] for vertex indices
imallettd9ce9eb2022-10-07 10:37:09 -0700810 // or attribs. Could be 0 for other data
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900811 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900812 ExtensionMap extensions;
813
814 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
815 std::string extras_json_string;
816 std::string extensions_json_string;
817
Syoyo Fujita4e47bc72020-01-02 22:07:25 +0900818 bool dracoDecoded{false}; // Flag indicating this has been draco decoded
Syoyo Fujitad17ff662017-05-29 02:53:12 +0900819
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900820 BufferView()
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200821
822 {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100823 DEFAULT_METHODS(BufferView)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900824 bool operator==(const BufferView &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000825};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900826
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000827struct Accessor {
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200828 int bufferView{-1}; // optional in spec but required here since sparse accessor
Syoyo Fujita5b407452017-06-04 17:42:41 +0900829 // are not supported
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900830 std::string name;
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200831 size_t byteOffset{0};
832 bool normalized{false}; // optional.
833 int componentType{-1}; // (required) One of TINYGLTF_COMPONENT_TYPE_***
834 size_t count{0}; // required
835 int type{-1}; // (required) One of TINYGLTF_TYPE_*** ..
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900836 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900837 ExtensionMap extensions;
838
839 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
840 std::string extras_json_string;
841 std::string extensions_json_string;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000842
Syoyo Fujita7905a5b2021-01-21 20:33:11 +0900843 std::vector<double>
844 minValues; // optional. integer value is promoted to double
845 std::vector<double>
846 maxValues; // optional. integer value is promoted to double
Syoyo Fujitad9d012a2017-07-12 18:29:29 +0900847
David Siegeld852f502023-06-05 23:28:05 +0200848 struct Sparse {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000849 int count;
850 bool isSparse;
851 struct {
852 int byteOffset;
853 int bufferView;
854 int componentType; // a TINYGLTF_COMPONENT_TYPE_ value
David Siegeld852f502023-06-05 23:28:05 +0200855 Value extras;
856 ExtensionMap extensions;
857 std::string extras_json_string;
858 std::string extensions_json_string;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000859 } indices;
860 struct {
861 int bufferView;
862 int byteOffset;
David Siegeld852f502023-06-05 23:28:05 +0200863 Value extras;
864 ExtensionMap extensions;
865 std::string extras_json_string;
866 std::string extensions_json_string;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000867 } values;
David Siegeld852f502023-06-05 23:28:05 +0200868 Value extras;
869 ExtensionMap extensions;
870 std::string extras_json_string;
871 std::string extensions_json_string;
872 };
873
874 Sparse sparse;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000875
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900876 ///
877 /// Utility function to compute byteStride for a given bufferView object.
878 /// Returns -1 upon invalid glTF value or parameter configuration.
879 ///
880 int ByteStride(const BufferView &bufferViewObject) const {
881 if (bufferViewObject.byteStride == 0) {
882 // Assume data is tightly packed.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500883 int componentSizeInBytes =
884 GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900885 if (componentSizeInBytes <= 0) {
886 return -1;
887 }
888
Syoyo Fujita81d75df2019-08-30 19:19:52 +0900889 int numComponents = GetNumComponentsInType(static_cast<uint32_t>(type));
890 if (numComponents <= 0) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900891 return -1;
892 }
893
Syoyo Fujita81d75df2019-08-30 19:19:52 +0900894 return componentSizeInBytes * numComponents;
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900895 } else {
imallettd9ce9eb2022-10-07 10:37:09 -0700896 // Check if byteStride is a multiple of the size of the accessor's component
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500897 // type.
898 int componentSizeInBytes =
899 GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900900 if (componentSizeInBytes <= 0) {
901 return -1;
902 }
903
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900904 if ((bufferViewObject.byteStride % uint32_t(componentSizeInBytes)) != 0) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900905 return -1;
906 }
Arthur Brainville8ce4e542018-01-10 01:27:12 +0100907 return static_cast<int>(bufferViewObject.byteStride);
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900908 }
909
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900910 // unreachable return 0;
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900911 }
912
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900913 Accessor()
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200914
915 {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000916 sparse.isSparse = false;
917 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100918 DEFAULT_METHODS(Accessor)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900919 bool operator==(const tinygltf::Accessor &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000920};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900921
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900922struct PerspectiveCamera {
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200923 double aspectRatio{0.0}; // min > 0
924 double yfov{0.0}; // required. min > 0
925 double zfar{0.0}; // min > 0
926 double znear{0.0}; // required. min > 0
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900927
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900928 PerspectiveCamera()
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200929
930 {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100931 DEFAULT_METHODS(PerspectiveCamera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900932 bool operator==(const PerspectiveCamera &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900933
Selmar09d2ff12018-03-15 17:30:42 +0100934 ExtensionMap extensions;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900935 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900936
937 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
938 std::string extras_json_string;
939 std::string extensions_json_string;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900940};
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000941
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900942struct OrthographicCamera {
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200943 double xmag{0.0}; // required. must not be zero.
944 double ymag{0.0}; // required. must not be zero.
945 double zfar{0.0}; // required. `zfar` must be greater than `znear`.
946 double znear{0.0}; // required
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000947
Bryn Lloyda64f4b42023-06-21 18:40:18 +0200948 OrthographicCamera() {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100949 DEFAULT_METHODS(OrthographicCamera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900950 bool operator==(const OrthographicCamera &) const;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900951
Selmar09d2ff12018-03-15 17:30:42 +0100952 ExtensionMap extensions;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900953 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900954
955 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
956 std::string extras_json_string;
957 std::string extensions_json_string;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900958};
959
960struct Camera {
961 std::string type; // required. "perspective" or "orthographic"
962 std::string name;
963
964 PerspectiveCamera perspective;
965 OrthographicCamera orthographic;
966
967 Camera() {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100968 DEFAULT_METHODS(Camera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900969 bool operator==(const Camera &) const;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900970
Selmar09d2ff12018-03-15 17:30:42 +0100971 ExtensionMap extensions;
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000972 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900973
974 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
975 std::string extras_json_string;
976 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900977};
978
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000979struct Primitive {
980 std::map<std::string, int> attributes; // (required) A dictionary object of
981 // integer, where each integer
982 // is the index of the accessor
983 // containing an attribute.
984 int material; // The index of the material to apply to this primitive
Syoyo Fujita5b407452017-06-04 17:42:41 +0900985 // when rendering.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000986 int indices; // The index of the accessor that contains the indices.
987 int mode; // one of TINYGLTF_MODE_***
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900988 std::vector<std::map<std::string, int> > targets; // array of morph targets,
imallettd9ce9eb2022-10-07 10:37:09 -0700989 // where each target is a dict with attributes in ["POSITION, "NORMAL",
Syoyo Fujita5b407452017-06-04 17:42:41 +0900990 // "TANGENT"] pointing
991 // to their corresponding accessors
Alex Wood7319db72019-01-24 15:38:16 -0500992 ExtensionMap extensions;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000993 Value extras;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900994
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900995 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
996 std::string extras_json_string;
997 std::string extensions_json_string;
998
Syoyo Fujita5b407452017-06-04 17:42:41 +0900999 Primitive() {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001000 material = -1;
1001 indices = -1;
daemyung jang1739d022020-07-03 13:27:39 +09001002 mode = -1;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001003 }
Selmar Kokb74fade2019-10-29 16:09:32 +01001004 DEFAULT_METHODS(Primitive)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001005 bool operator==(const Primitive &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001006};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001007
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001008struct Mesh {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001009 std::string name;
1010 std::vector<Primitive> primitives;
Syoyo Fujita5b407452017-06-04 17:42:41 +09001011 std::vector<double> weights; // weights to be applied to the Morph Targets
Selmar09d2ff12018-03-15 17:30:42 +01001012 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001013 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001014
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001015 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1016 std::string extras_json_string;
1017 std::string extensions_json_string;
1018
jrkoonced1e14722019-08-27 11:51:02 -05001019 Mesh() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001020 DEFAULT_METHODS(Mesh)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001021 bool operator==(const Mesh &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001022};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001023
1024class Node {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001025 public:
Bryn Lloyda64f4b42023-06-21 18:40:18 +02001026 Node() {}
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001027
Selmar Kokb74fade2019-10-29 16:09:32 +01001028 DEFAULT_METHODS(Node)
Syoyo Fujitad6b0d0a2019-06-29 17:31:13 +09001029
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001030 bool operator==(const Node &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001031
Syoyo Fujita7a570c82023-06-19 21:52:13 +09001032 int camera{-1}; // the index of the camera referenced by this node
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001033
1034 std::string name;
Syoyo Fujita7a570c82023-06-19 21:52:13 +09001035 int skin{-1};
1036 int mesh{-1};
1037 int light{-1}; // light source index (KHR_lights_punctual)
1038 int emitter{-1}; // audio emitter index (KHR_audio)
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001039 std::vector<int> children;
Syoyo Fujita7c877972016-03-08 01:31:49 +09001040 std::vector<double> rotation; // length must be 0 or 4
1041 std::vector<double> scale; // length must be 0 or 3
1042 std::vector<double> translation; // length must be 0 or 3
1043 std::vector<double> matrix; // length must be 0 or 16
Syoyo Fujita5b407452017-06-04 17:42:41 +09001044 std::vector<double> weights; // The weights of the instantiated Morph Target
Ben Buzbee3b735bb2018-04-24 11:39:30 -07001045
Selmar09d2ff12018-03-15 17:30:42 +01001046 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001047 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001048
1049 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1050 std::string extras_json_string;
1051 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001052};
1053
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001054struct Buffer {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001055 std::string name;
1056 std::vector<unsigned char> data;
Syoyo Fujita5b407452017-06-04 17:42:41 +09001057 std::string
1058 uri; // considered as required here but not in the spec (need to clarify)
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001059 // uri is not decoded(e.g. whitespace may be represented as %20)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001060 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001061 ExtensionMap extensions;
1062
1063 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1064 std::string extras_json_string;
1065 std::string extensions_json_string;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001066
Selmar Kokb74fade2019-10-29 16:09:32 +01001067 Buffer() = default;
1068 DEFAULT_METHODS(Buffer)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001069 bool operator==(const Buffer &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001070};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001071
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001072struct Asset {
Syoyo Fujitab702de72021-03-02 19:08:29 +09001073 std::string version = "2.0"; // required
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001074 std::string generator;
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +00001075 std::string minVersion;
1076 std::string copyright;
Selmar09d2ff12018-03-15 17:30:42 +01001077 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001078 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 Asset() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001085 DEFAULT_METHODS(Asset)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001086 bool operator==(const Asset &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001087};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001088
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001089struct Scene {
1090 std::string name;
1091 std::vector<int> nodes;
Baranob_Ilya879cb472023-06-12 13:35:05 +04001092 std::vector<int> audioEmitters; // KHR_audio global emitters
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001093
Selmar09d2ff12018-03-15 17:30:42 +01001094 ExtensionMap extensions;
1095 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001096
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001097 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1098 std::string extras_json_string;
1099 std::string extensions_json_string;
1100
jrkoonced1e14722019-08-27 11:51:02 -05001101 Scene() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001102 DEFAULT_METHODS(Scene)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001103 bool operator==(const Scene &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001104};
1105
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001106struct SpotLight {
Bryn Lloyda64f4b42023-06-21 18:40:18 +02001107 double innerConeAngle{0.0};
1108 double outerConeAngle{0.7853981634};
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001109
Bryn Lloyda64f4b42023-06-21 18:40:18 +02001110 SpotLight() {}
Selmar Kokb74fade2019-10-29 16:09:32 +01001111 DEFAULT_METHODS(SpotLight)
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001112 bool operator==(const SpotLight &) const;
1113
1114 ExtensionMap extensions;
1115 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001116
1117 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1118 std::string extras_json_string;
1119 std::string extensions_json_string;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001120};
1121
Emanuel Schrade186322b2017-11-06 11:14:41 +01001122struct Light {
1123 std::string name;
1124 std::vector<double> color;
Syoyo Fujita3dad3032020-05-13 21:22:23 +09001125 double intensity{1.0};
Emanuel Schrade186322b2017-11-06 11:14:41 +01001126 std::string type;
imallettd9ce9eb2022-10-07 10:37:09 -07001127 double range{0.0}; // 0.0 = infinite
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001128 SpotLight spot;
1129
Bryn Lloyda64f4b42023-06-21 18:40:18 +02001130 Light() {}
Selmar Kokb74fade2019-10-29 16:09:32 +01001131 DEFAULT_METHODS(Light)
Arthur Brainville (Ybalrid)9eeaf202019-09-05 13:02:05 +02001132
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001133 bool operator==(const Light &) const;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001134
1135 ExtensionMap extensions;
1136 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001137
1138 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1139 std::string extras_json_string;
1140 std::string extensions_json_string;
Emanuel Schrade186322b2017-11-06 11:14:41 +01001141};
1142
Baranob_Ilya78864c82023-06-12 10:43:52 +04001143struct PositionalEmitter {
1144 double coneInnerAngle{6.283185307179586};
1145 double coneOuterAngle{6.283185307179586};
1146 double coneOuterGain{0.0};
1147 double maxDistance{100.0};
1148 double refDistance{1.0};
1149 double rolloffFactor{1.0};
1150
Bryn Lloyda64f4b42023-06-21 18:40:18 +02001151 PositionalEmitter() {}
Baranob_Ilya78864c82023-06-12 10:43:52 +04001152 DEFAULT_METHODS(PositionalEmitter)
1153 bool operator==(const PositionalEmitter &) const;
1154
1155 ExtensionMap extensions;
1156 Value extras;
1157
1158 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1159 std::string extras_json_string;
1160 std::string extensions_json_string;
1161};
1162
1163struct AudioEmitter {
Baranob_Ilya879cb472023-06-12 13:35:05 +04001164 std::string name;
1165 double gain{1.0};
1166 bool loop{false};
1167 bool playing{false};
1168 std::string
1169 type; // positional - Positional audio emitters. Using sound cones, the
1170 // orientation is +Z having the same front side for a glTF asset.
1171 // global - Global audio emitters are not affected by the position
1172 // of audio listeners. coneInnerAngle, coneOuterAngle,
1173 // coneOuterGain, distanceModel, maxDistance, refDistance, and
1174 // rolloffFactor should all be ignored when set.
1175 std::string
1176 distanceModel; // linear - A linear distance model calculating the
1177 // gain induced by the distance according to: 1.0
1178 // - rolloffFactor * (distance - refDistance) /
1179 // (maxDistance - refDistance)
1180 // inverse - (default) An inverse distance model
1181 // calculating the gain induced by the distance according
1182 // to: refDistance / (refDistance + rolloffFactor *
1183 // (Math.max(distance, refDistance) - refDistance))
1184 // exponential - An exponential distance model calculating
1185 // the gain induced by the distance according to:
1186 // pow((Math.max(distance, refDistance) / refDistance,
1187 // -rolloffFactor))
1188 PositionalEmitter positional;
1189 int source{-1};
Baranob_Ilya78864c82023-06-12 10:43:52 +04001190
Baranob_Ilya879cb472023-06-12 13:35:05 +04001191 AudioEmitter()
Bryn Lloyda64f4b42023-06-21 18:40:18 +02001192 :
Baranob_Ilya879cb472023-06-12 13:35:05 +04001193 type("global"),
1194 distanceModel("inverse") {}
1195 DEFAULT_METHODS(AudioEmitter)
Baranob_Ilya78864c82023-06-12 10:43:52 +04001196
Baranob_Ilya879cb472023-06-12 13:35:05 +04001197 bool operator==(const AudioEmitter &) const;
Baranob_Ilya78864c82023-06-12 10:43:52 +04001198
Baranob_Ilya879cb472023-06-12 13:35:05 +04001199 ExtensionMap extensions;
1200 Value extras;
Baranob_Ilya78864c82023-06-12 10:43:52 +04001201
Baranob_Ilya879cb472023-06-12 13:35:05 +04001202 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1203 std::string extras_json_string;
1204 std::string extensions_json_string;
Baranob_Ilya78864c82023-06-12 10:43:52 +04001205};
1206
1207struct AudioSource {
1208 std::string name;
1209 std::string uri;
Baranob_Ilya879cb472023-06-12 13:35:05 +04001210 int bufferView{-1}; // (required if no uri)
1211 std::string mimeType; // (required if no uri) The audio's MIME type. Required if
1212 // bufferView is defined. Unless specified by another
1213 // extension, the only supported mimeType is audio/mpeg.
Baranob_Ilya78864c82023-06-12 10:43:52 +04001214
1215 AudioSource() {}
1216 DEFAULT_METHODS(AudioSource)
1217
1218 bool operator==(const AudioSource &) const;
1219
1220 Value extras;
1221 ExtensionMap extensions;
1222
1223 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1224 std::string extras_json_string;
1225 std::string extensions_json_string;
1226};
1227
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001228class Model {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001229 public:
Selmar Kokff2b1f92019-10-21 17:58:09 +02001230 Model() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001231 DEFAULT_METHODS(Model)
Syoyo Fujitad6b0d0a2019-06-29 17:31:13 +09001232
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001233 bool operator==(const Model &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001234
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001235 std::vector<Accessor> accessors;
1236 std::vector<Animation> animations;
1237 std::vector<Buffer> buffers;
1238 std::vector<BufferView> bufferViews;
1239 std::vector<Material> materials;
1240 std::vector<Mesh> meshes;
1241 std::vector<Node> nodes;
1242 std::vector<Texture> textures;
1243 std::vector<Image> images;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00001244 std::vector<Skin> skins;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001245 std::vector<Sampler> samplers;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09001246 std::vector<Camera> cameras;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001247 std::vector<Scene> scenes;
Emanuel Schrade186322b2017-11-06 11:14:41 +01001248 std::vector<Light> lights;
Baranob_Ilya78864c82023-06-12 10:43:52 +04001249 std::vector<AudioEmitter> audioEmitters;
1250 std::vector<AudioSource> audioSources;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001251
sammyKhana0a62bd2020-01-17 13:41:16 +01001252 int defaultScene = -1;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001253 std::vector<std::string> extensionsUsed;
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00001254 std::vector<std::string> extensionsRequired;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001255
1256 Asset asset;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001257
1258 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001259 ExtensionMap extensions;
1260
1261 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1262 std::string extras_json_string;
1263 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001264};
1265
Syoyo Fujita0614eb82016-10-14 18:50:14 +09001266enum SectionCheck {
Luke San Antonio9e36b612016-06-23 14:10:51 -04001267 NO_REQUIRE = 0x00,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001268 REQUIRE_VERSION = 0x01,
1269 REQUIRE_SCENE = 0x02,
1270 REQUIRE_SCENES = 0x04,
1271 REQUIRE_NODES = 0x08,
1272 REQUIRE_ACCESSORS = 0x10,
1273 REQUIRE_BUFFERS = 0x20,
1274 REQUIRE_BUFFER_VIEWS = 0x40,
1275 REQUIRE_ALL = 0x7f
Luke San Antonio9e36b612016-06-23 14:10:51 -04001276};
1277
Squareysff644d82018-03-13 22:36:18 +01001278///
Pyarelal Knowles385946d2023-01-03 18:18:04 -08001279/// URIEncodeFunction type. Signature for custom URI encoding of external
1280/// resources such as .bin and image files. Used by tinygltf to re-encode the
1281/// final location of saved files. object_type may be used to encode buffer and
1282/// image URIs differently, for example. See
1283/// https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#uris
1284///
1285typedef bool (*URIEncodeFunction)(const std::string &in_uri,
1286 const std::string &object_type,
1287 std::string *out_uri, void *user_data);
1288
1289///
1290/// URIDecodeFunction type. Signature for custom URI decoding of external
1291/// resources such as .bin and image files. Used by tinygltf when computing
1292/// filenames to write resources.
1293///
1294typedef bool (*URIDecodeFunction)(const std::string &in_uri,
1295 std::string *out_uri, void *user_data);
1296
1297// Declaration of default uri decode function
1298bool URIDecode(const std::string &in_uri, std::string *out_uri,
1299 void *user_data);
1300
1301///
1302/// A structure containing URI callbacks and a pointer to their user data.
1303///
1304struct URICallbacks {
1305 URIEncodeFunction encode; // Optional encode method
1306 URIDecodeFunction decode; // Required decode method
1307
1308 void *user_data; // An argument that is passed to all uri callbacks
1309};
1310
1311///
Squareysff644d82018-03-13 22:36:18 +01001312/// LoadImageDataFunction type. Signature for custom image loading callbacks.
1313///
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001314typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *,
1315 std::string *, int, int,
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001316 const unsigned char *, int,
1317 void *user_pointer);
Squareysff644d82018-03-13 22:36:18 +01001318
johan bowald642a3432018-04-01 12:37:18 +02001319///
1320/// WriteImageDataFunction type. Signature for custom image writing callbacks.
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08001321/// The out_uri parameter becomes the URI written to the gltf and may reference
1322/// a file or contain a data URI.
johan bowald642a3432018-04-01 12:37:18 +02001323///
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08001324typedef bool (*WriteImageDataFunction)(const std::string *basepath,
1325 const std::string *filename,
1326 const Image *image, bool embedImages,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08001327 const URICallbacks *uri_cb,
1328 std::string *out_uri,
1329 void *user_pointer);
johan bowald642a3432018-04-01 12:37:18 +02001330
Squareys2d3594d2018-03-13 22:40:53 +01001331#ifndef TINYGLTF_NO_STB_IMAGE
Squareysff644d82018-03-13 22:36:18 +01001332// Declaration of default image loader callback
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001333bool LoadImageData(Image *image, const int image_idx, std::string *err,
1334 std::string *warn, int req_width, int req_height,
1335 const unsigned char *bytes, int size, void *);
Squareys2d3594d2018-03-13 22:40:53 +01001336#endif
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001337
johan bowald642a3432018-04-01 12:37:18 +02001338#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
1339// Declaration of default image writer callback
1340bool WriteImageData(const std::string *basepath, const std::string *filename,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08001341 const Image *image, bool embedImages,
1342 const URICallbacks *uri_cb, std::string *out_uri, void *);
johan bowald642a3432018-04-01 12:37:18 +02001343#endif
1344
Paolo Jovone6601bf2018-07-07 20:43:33 +02001345///
1346/// FilExistsFunction type. Signature for custom filesystem callbacks.
1347///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001348typedef bool (*FileExistsFunction)(const std::string &abs_filename, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001349
1350///
1351/// ExpandFilePathFunction type. Signature for custom filesystem callbacks.
1352///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001353typedef std::string (*ExpandFilePathFunction)(const std::string &, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001354
1355///
1356/// ReadWholeFileFunction type. Signature for custom filesystem callbacks.
1357///
1358typedef bool (*ReadWholeFileFunction)(std::vector<unsigned char> *,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001359 std::string *, const std::string &,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001360 void *);
1361
1362///
1363/// WriteWholeFileFunction type. Signature for custom filesystem callbacks.
1364///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001365typedef bool (*WriteWholeFileFunction)(std::string *, const std::string &,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001366 const std::vector<unsigned char> &,
1367 void *);
1368
1369///
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09001370/// GetFileSizeFunction type. Signature for custom filesystem callbacks.
1371///
1372typedef bool (*GetFileSizeFunction)(size_t *filesize_out, std::string *err, const std::string &abs_filename,
1373 void *userdata);
1374
1375///
Paolo Jovone6601bf2018-07-07 20:43:33 +02001376/// A structure containing all required filesystem callbacks and a pointer to
1377/// their user data.
1378///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001379struct FsCallbacks {
1380 FileExistsFunction FileExists;
1381 ExpandFilePathFunction ExpandFilePath;
1382 ReadWholeFileFunction ReadWholeFile;
1383 WriteWholeFileFunction WriteWholeFile;
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09001384 GetFileSizeFunction GetFileSizeInBytes; // To avoid GetFileSize Win32 API, add `InBytes` suffix.
Paolo Jovone6601bf2018-07-07 20:43:33 +02001385
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001386 void *user_data; // An argument that is passed to all fs callbacks
Paolo Jovone6601bf2018-07-07 20:43:33 +02001387};
1388
1389#ifndef TINYGLTF_NO_FS
1390// Declaration of default filesystem callbacks
1391
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001392bool FileExists(const std::string &abs_filename, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001393
Syoyo Fujita3d939fd2020-06-06 18:13:15 +09001394///
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04001395/// Expand file path(e.g. `~` to home directory on posix, `%APPDATA%` to
Syoyo Fujita2c521b32020-12-04 00:50:46 +09001396/// `C:\\Users\\tinygltf\\AppData`)
Syoyo Fujita3d939fd2020-06-06 18:13:15 +09001397///
1398/// @param[in] filepath File path string. Assume UTF-8
1399/// @param[in] userdata User data. Set to `nullptr` if you don't need it.
1400///
1401std::string ExpandFilePath(const std::string &filepath, void *userdata);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001402
1403bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001404 const std::string &filepath, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001405
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001406bool WriteWholeFile(std::string *err, const std::string &filepath,
1407 const std::vector<unsigned char> &contents, void *);
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09001408
1409bool GetFileSizeInBytes(size_t *filesize_out, std::string *err, const std::string &filepath,
1410 void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001411#endif
1412
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001413///
imallettd9ce9eb2022-10-07 10:37:09 -07001414/// glTF Parser/Serializer context.
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001415///
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001416class TinyGLTF {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001417 public:
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001418#ifdef __clang__
1419#pragma clang diagnostic push
1420#pragma clang diagnostic ignored "-Wc++98-compat"
1421#endif
1422
Bryn Lloyda64f4b42023-06-21 18:40:18 +02001423 TinyGLTF() {}
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001424
1425#ifdef __clang__
1426#pragma clang diagnostic pop
1427#endif
1428
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001429 ~TinyGLTF() {}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001430
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001431 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001432 /// Loads glTF ASCII asset from a file.
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001433 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001434 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001435 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001436 bool LoadASCIIFromFile(Model *model, std::string *err, std::string *warn,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001437 const std::string &filename,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001438 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001439
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001440 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001441 /// Loads glTF ASCII asset from string(memory).
1442 /// `length` = strlen(str);
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001443 /// `base_dir` is a search path of glTF asset(e.g. images). Path Must be an
1444 /// expanded path (e.g. no tilde(`~`), no environment variables). Set warning
1445 /// message to `warn` for example it fails to load asserts. Returns false and
1446 /// set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001447 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001448 bool LoadASCIIFromString(Model *model, std::string *err, std::string *warn,
1449 const char *str, const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001450 const std::string &base_dir,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001451 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001452
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001453 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001454 /// Loads glTF binary asset from a file.
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001455 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001456 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001457 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001458 bool LoadBinaryFromFile(Model *model, std::string *err, std::string *warn,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001459 const std::string &filename,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001460 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001461
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001462 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001463 /// Loads glTF binary asset from memory.
1464 /// `length` = strlen(str);
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001465 /// `base_dir` is a search path of glTF asset(e.g. images). Path Must be an
1466 /// expanded path (e.g. no tilde(`~`), no environment variables).
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001467 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001468 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001469 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001470 bool LoadBinaryFromMemory(Model *model, std::string *err, std::string *warn,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001471 const unsigned char *bytes,
1472 const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001473 const std::string &base_dir = "",
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001474 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001475
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001476 ///
imallettd9ce9eb2022-10-07 10:37:09 -07001477 /// Write glTF to stream, buffers and images will be embedded
Johan Bowald1af7c1d2019-07-16 14:50:46 +02001478 ///
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08001479 bool WriteGltfSceneToStream(const Model *model, std::ostream &stream,
Johan Bowald1af7c1d2019-07-16 14:50:46 +02001480 bool prettyPrint, bool writeBinary);
1481
1482 ///
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001483 /// Write glTF to file.
1484 ///
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08001485 bool WriteGltfSceneToFile(const Model *model, const std::string &filename,
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001486 bool embedImages, bool embedBuffers,
1487 bool prettyPrint, bool writeBinary);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00001488
Squareysff644d82018-03-13 22:36:18 +01001489 ///
1490 /// Set callback to use for loading image data
1491 ///
1492 void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data);
1493
johan bowald642a3432018-04-01 12:37:18 +02001494 ///
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001495 /// Unset(remove) callback of loading image data
1496 ///
1497 void RemoveImageLoader();
1498
1499 ///
johan bowald642a3432018-04-01 12:37:18 +02001500 /// Set callback to use for writing image data
1501 ///
1502 void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data);
1503
Paolo Jovone6601bf2018-07-07 20:43:33 +02001504 ///
Pyarelal Knowles385946d2023-01-03 18:18:04 -08001505 /// Set callbacks to use for URI encoding and decoding and their user data
1506 ///
1507 void SetURICallbacks(URICallbacks callbacks);
1508
1509 ///
Paolo Jovone6601bf2018-07-07 20:43:33 +02001510 /// Set callbacks to use for filesystem (fs) access and their user data
1511 ///
1512 void SetFsCallbacks(FsCallbacks callbacks);
1513
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001514 ///
1515 /// Set serializing default values(default = false).
1516 /// When true, default values are force serialized to .glTF.
imallettd9ce9eb2022-10-07 10:37:09 -07001517 /// This may be helpful if you want to serialize a full description of glTF
Syoyo Fujitaff515702019-08-24 16:29:14 +09001518 /// data.
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001519 ///
Syoyo Fujitaff515702019-08-24 16:29:14 +09001520 /// TODO(LTE): Supply parsing option as function arguments to
1521 /// `LoadASCIIFromFile()` and others, not by a class method
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001522 ///
1523 void SetSerializeDefaultValues(const bool enabled) {
1524 serialize_default_values_ = enabled;
1525 }
1526
Syoyo Fujitaff515702019-08-24 16:29:14 +09001527 bool GetSerializeDefaultValues() const { return serialize_default_values_; }
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001528
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001529 ///
1530 /// Store original JSON string for `extras` and `extensions`.
1531 /// This feature will be useful when the user want to reconstruct custom data
1532 /// structure from JSON string.
1533 ///
1534 void SetStoreOriginalJSONForExtrasAndExtensions(const bool enabled) {
1535 store_original_json_for_extras_and_extensions_ = enabled;
1536 }
1537
1538 bool GetStoreOriginalJSONForExtrasAndExtensions() const {
1539 return store_original_json_for_extras_and_extensions_;
1540 }
1541
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001542 ///
imallettd9ce9eb2022-10-07 10:37:09 -07001543 /// Specify whether preserve image channels when loading images or not.
1544 /// (Not effective when the user supplies their own LoadImageData callbacks)
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001545 ///
1546 void SetPreserveImageChannels(bool onoff) {
1547 preserve_image_channels_ = onoff;
1548 }
1549
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09001550 ///
1551 /// Set maximum allowed external file size in bytes.
1552 /// Default: 2GB
1553 /// Only effective for built-in ReadWholeFileFunction FS function.
1554 ///
1555 void SetMaxExternalFileSize(size_t max_bytes) {
1556 max_external_file_size_ = max_bytes;
1557 }
1558
1559 size_t GetMaxExternalFileSize() const {
1560 return max_external_file_size_;
1561 }
1562
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001563 bool GetPreserveImageChannels() const { return preserve_image_channels_; }
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001564
Syoyo Fujitabeded612016-05-01 20:03:43 +09001565 private:
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001566 ///
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001567 /// Loads glTF asset from string(memory).
1568 /// `length` = strlen(str);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001569 /// Set warning message to `warn` for example it fails to load asserts
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001570 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001571 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001572 bool LoadFromString(Model *model, std::string *err, std::string *warn,
1573 const char *str, const unsigned int length,
1574 const std::string &base_dir, unsigned int check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001575
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001576 const unsigned char *bin_data_ = nullptr;
1577 size_t bin_size_ = 0;
1578 bool is_binary_ = false;
1579
Syoyo Fujitaff515702019-08-24 16:29:14 +09001580 bool serialize_default_values_ = false; ///< Serialize default values?
Squareysff644d82018-03-13 22:36:18 +01001581
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001582 bool store_original_json_for_extras_and_extensions_ = false;
1583
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001584 bool preserve_image_channels_ = false; /// Default false(expand channels to
1585 /// RGBA) for backward compatibility.
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001586
Syoyo Fujita1a5046e2023-04-23 23:08:41 +09001587 size_t max_external_file_size_{size_t((std::numeric_limits<int32_t>::max)())}; // Default 2GB
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09001588
Syoyo Fujita9117abb2022-08-16 20:10:26 +09001589 // Warning & error messages
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09001590 std::string warn_;
1591 std::string err_;
1592
Paolo Jovone6601bf2018-07-07 20:43:33 +02001593 FsCallbacks fs = {
1594#ifndef TINYGLTF_NO_FS
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001595 &tinygltf::FileExists, &tinygltf::ExpandFilePath,
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09001596 &tinygltf::ReadWholeFile, &tinygltf::WriteWholeFile, &tinygltf::GetFileSizeInBytes,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001597
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001598 nullptr // Fs callback user data
Paolo Jovone6601bf2018-07-07 20:43:33 +02001599#else
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09001600 nullptr, nullptr, nullptr, nullptr, nullptr,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001601
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001602 nullptr // Fs callback user data
Paolo Jovone6601bf2018-07-07 20:43:33 +02001603#endif
1604 };
1605
Pyarelal Knowles385946d2023-01-03 18:18:04 -08001606 URICallbacks uri_cb = {
1607 // Use paths as-is by default. This will use JSON string escaping.
1608 nullptr,
1609 // Decode all URIs before using them as paths as the application may have
1610 // percent encoded them.
1611 &tinygltf::URIDecode,
1612 // URI callback user data
1613 nullptr};
1614
Squareysff644d82018-03-13 22:36:18 +01001615 LoadImageDataFunction LoadImageData =
1616#ifndef TINYGLTF_NO_STB_IMAGE
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001617 &tinygltf::LoadImageData;
Squareysff644d82018-03-13 22:36:18 +01001618#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001619 nullptr;
Squareysff644d82018-03-13 22:36:18 +01001620#endif
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001621 void *load_image_user_data_{nullptr};
1622 bool user_image_loader_{false};
johan bowald642a3432018-04-01 12:37:18 +02001623
1624 WriteImageDataFunction WriteImageData =
1625#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001626 &tinygltf::WriteImageData;
johan bowald642a3432018-04-01 12:37:18 +02001627#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001628 nullptr;
johan bowald642a3432018-04-01 12:37:18 +02001629#endif
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001630 void *write_image_user_data_{nullptr};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001631};
1632
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001633#ifdef __clang__
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001634#pragma clang diagnostic pop // -Wpadded
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001635#endif
1636
Syoyo Fujita7c877972016-03-08 01:31:49 +09001637} // namespace tinygltf
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001638
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001639#endif // TINY_GLTF_H_
Syoyo Fujitac8d3c762017-05-25 01:42:38 +09001640
Selmar Kok31cb7f92018-10-03 15:39:05 +02001641#if defined(TINYGLTF_IMPLEMENTATION) || defined(__INTELLISENSE__)
Syoyo Fujita7c877972016-03-08 01:31:49 +09001642#include <algorithm>
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001643//#include <cassert>
Paolo Jovone6601bf2018-07-07 20:43:33 +02001644#ifndef TINYGLTF_NO_FS
Syoyo Fujita125d8e52019-11-09 20:52:56 +09001645#include <cstdio>
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001646#include <fstream>
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09001647#include <sys/stat.h> // for is_directory check
Paolo Jovone6601bf2018-07-07 20:43:33 +02001648#endif
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001649#include <sstream>
Syoyo Fujitabeded612016-05-01 20:03:43 +09001650
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001651#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001652// Disable some warnings for external files.
1653#pragma clang diagnostic push
1654#pragma clang diagnostic ignored "-Wfloat-equal"
1655#pragma clang diagnostic ignored "-Wexit-time-destructors"
1656#pragma clang diagnostic ignored "-Wconversion"
1657#pragma clang diagnostic ignored "-Wold-style-cast"
Syoyo Fujita643ce102016-05-01 17:19:37 +09001658#pragma clang diagnostic ignored "-Wglobal-constructors"
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001659#if __has_warning("-Wreserved-id-macro")
Syoyo Fujita643ce102016-05-01 17:19:37 +09001660#pragma clang diagnostic ignored "-Wreserved-id-macro"
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001661#endif
Syoyo Fujita643ce102016-05-01 17:19:37 +09001662#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
1663#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001664#pragma clang diagnostic ignored "-Wc++98-compat"
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001665#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001666#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
1667#pragma clang diagnostic ignored "-Wswitch-enum"
1668#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001669#pragma clang diagnostic ignored "-Wweak-vtables"
1670#pragma clang diagnostic ignored "-Wcovered-switch-default"
Syoyo Fujitaa8f0b1c2018-08-28 21:33:40 +09001671#if __has_warning("-Wdouble-promotion")
1672#pragma clang diagnostic ignored "-Wdouble-promotion"
1673#endif
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001674#if __has_warning("-Wcomma")
Syoyo Fujitac8d3c762017-05-25 01:42:38 +09001675#pragma clang diagnostic ignored "-Wcomma"
1676#endif
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001677#if __has_warning("-Wzero-as-null-pointer-constant")
1678#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
1679#endif
1680#if __has_warning("-Wcast-qual")
1681#pragma clang diagnostic ignored "-Wcast-qual"
1682#endif
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001683#if __has_warning("-Wmissing-variable-declarations")
1684#pragma clang diagnostic ignored "-Wmissing-variable-declarations"
1685#endif
1686#if __has_warning("-Wmissing-prototypes")
1687#pragma clang diagnostic ignored "-Wmissing-prototypes"
1688#endif
1689#if __has_warning("-Wcast-align")
1690#pragma clang diagnostic ignored "-Wcast-align"
1691#endif
1692#if __has_warning("-Wnewline-eof")
1693#pragma clang diagnostic ignored "-Wnewline-eof"
1694#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001695#if __has_warning("-Wunused-parameter")
1696#pragma clang diagnostic ignored "-Wunused-parameter"
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001697#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001698#if __has_warning("-Wmismatched-tags")
1699#pragma clang diagnostic ignored "-Wmismatched-tags"
1700#endif
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001701#if __has_warning("-Wextra-semi-stmt")
1702#pragma clang diagnostic ignored "-Wextra-semi-stmt"
1703#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001704#endif
1705
imallettd9ce9eb2022-10-07 10:37:09 -07001706// Disable GCC warnings
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001707#ifdef __GNUC__
1708#pragma GCC diagnostic push
1709#pragma GCC diagnostic ignored "-Wtype-limits"
Syoyo Fujitaca56f722019-03-07 21:04:25 +09001710#endif // __GNUC__
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001711
krokofc0116b2019-03-03 08:28:49 +02001712#ifndef TINYGLTF_NO_INCLUDE_JSON
jrkooncecba5d6c2019-08-29 11:26:22 -05001713#ifndef TINYGLTF_USE_RAPIDJSON
Alex Wood7319db72019-01-24 15:38:16 -05001714#include "json.hpp"
jrkooncecba5d6c2019-08-29 11:26:22 -05001715#else
Alvaro Barua3ddf8302021-03-18 00:21:23 +00001716#ifndef TINYGLTF_NO_INCLUDE_RAPIDJSON
jrkooncecba5d6c2019-08-29 11:26:22 -05001717#include "document.h"
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001718#include "prettywriter.h"
1719#include "rapidjson.h"
jrkooncecba5d6c2019-08-29 11:26:22 -05001720#include "stringbuffer.h"
1721#include "writer.h"
jrkooncecba5d6c2019-08-29 11:26:22 -05001722#endif
krokof4b6d112019-03-03 01:11:31 +02001723#endif
Alvaro Barua3ddf8302021-03-18 00:21:23 +00001724#endif
Alex Wood7319db72019-01-24 15:38:16 -05001725
1726#ifdef TINYGLTF_ENABLE_DRACO
Alex Wood7319db72019-01-24 15:38:16 -05001727#include "draco/compression/decode.h"
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001728#include "draco/core/decoder_buffer.h"
Alex Wood7319db72019-01-24 15:38:16 -05001729#endif
Squareys2d3594d2018-03-13 22:40:53 +01001730
1731#ifndef TINYGLTF_NO_STB_IMAGE
krokofc0116b2019-03-03 08:28:49 +02001732#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE
Alex Wood7319db72019-01-24 15:38:16 -05001733#include "stb_image.h"
Squareys2d3594d2018-03-13 22:40:53 +01001734#endif
krokof4b6d112019-03-03 01:11:31 +02001735#endif
Squareys2d3594d2018-03-13 22:40:53 +01001736
johan bowald642a3432018-04-01 12:37:18 +02001737#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
krokofc0116b2019-03-03 08:28:49 +02001738#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE
Alex Wood7319db72019-01-24 15:38:16 -05001739#include "stb_image_write.h"
johan bowald642a3432018-04-01 12:37:18 +02001740#endif
krokof4b6d112019-03-03 01:11:31 +02001741#endif
johan bowald642a3432018-04-01 12:37:18 +02001742
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001743#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001744#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001745#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001746
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001747#ifdef __GNUC__
1748#pragma GCC diagnostic pop
1749#endif
1750
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001751#ifdef _WIN32
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001752
1753// issue 143.
1754// Define NOMINMAX to avoid min/max defines,
imallett3a295882022-10-07 11:20:39 -07001755// but undef it after included Windows.h
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001756#ifndef NOMINMAX
1757#define TINYGLTF_INTERNAL_NOMINMAX
1758#define NOMINMAX
1759#endif
1760
Syoyo Fujita87be0ce2019-02-19 21:36:32 +09001761#ifndef WIN32_LEAN_AND_MEAN
1762#define WIN32_LEAN_AND_MEAN
1763#define TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1764#endif
imallett3a295882022-10-07 11:20:39 -07001765#ifndef __MINGW32__
imallett56e10982022-10-07 10:35:16 -07001766#include <Windows.h> // include API for expanding a file path
imallett3a295882022-10-07 11:20:39 -07001767#else
1768#include <windows.h>
1769#endif
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001770
Syoyo Fujita87be0ce2019-02-19 21:36:32 +09001771#ifdef TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1772#undef WIN32_LEAN_AND_MEAN
1773#endif
1774
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001775#if defined(TINYGLTF_INTERNAL_NOMINMAX)
1776#undef NOMINMAX
1777#endif
1778
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001779#if defined(__GLIBCXX__) // mingw
Syoyo Fujita45cac782019-11-09 20:42:55 +09001780
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001781#include <fcntl.h> // _O_RDONLY
1782
1783#include <ext/stdio_filebuf.h> // fstream (all sorts of IO stuff) + stdio_filebuf (=streambuf)
Syoyo Fujita45cac782019-11-09 20:42:55 +09001784
1785#endif
1786
Julian Smith0598a202021-08-25 12:06:08 +01001787#elif !defined(__ANDROID__) && !defined(__OpenBSD__)
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001788//#include <wordexp.h>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001789#endif
1790
Christopher Sean Morrison2c9b2562022-05-14 19:00:19 -04001791#if defined(__sparcv9) || defined(__powerpc__)
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001792// Big endian
1793#else
1794#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
1795#define TINYGLTF_LITTLE_ENDIAN 1
1796#endif
1797#endif
1798
David03ad33c2023-02-15 23:35:51 -06001799namespace tinygltf {
David1f9a4b92023-02-15 22:56:18 -06001800namespace detail {
jrkooncecba5d6c2019-08-29 11:26:22 -05001801#ifdef TINYGLTF_USE_RAPIDJSON
jrkooncece7fa742019-09-04 13:31:44 -05001802
1803#ifdef TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001804// This uses the RapidJSON CRTAllocator. It is thread safe and multiple
1805// documents may be active at once.
1806using json =
1807 rapidjson::GenericValue<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
David Siegelcfe64fb2023-06-07 15:18:38 +02001808using json_iterator = json::MemberIterator;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001809using json_const_iterator = json::ConstMemberIterator;
1810using json_const_array_iterator = json const *;
1811using JsonDocument =
jrkooncece7fa742019-09-04 13:31:44 -05001812 rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001813rapidjson::CrtAllocator s_CrtAllocator; // stateless and thread safe
1814rapidjson::CrtAllocator &GetAllocator() { return s_CrtAllocator; }
jrkooncece7fa742019-09-04 13:31:44 -05001815#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001816// This uses the default RapidJSON MemoryPoolAllocator. It is very fast, but
1817// not thread safe. Only a single JsonDocument may be active at any one time,
1818// meaning only a single gltf load/save can be active any one time.
1819using json = rapidjson::Value;
David Siegelcfe64fb2023-06-07 15:18:38 +02001820using json_iterator = json::MemberIterator;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001821using json_const_iterator = json::ConstMemberIterator;
1822using json_const_array_iterator = json const *;
1823rapidjson::Document *s_pActiveDocument = nullptr;
1824rapidjson::Document::AllocatorType &GetAllocator() {
1825 assert(s_pActiveDocument); // Root json node must be JsonDocument type
1826 return s_pActiveDocument->GetAllocator();
1827}
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001828
1829#ifdef __clang__
1830#pragma clang diagnostic push
1831// Suppress JsonDocument(JsonDocument &&rhs) noexcept
1832#pragma clang diagnostic ignored "-Wunused-member-function"
1833#endif
1834
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001835struct JsonDocument : public rapidjson::Document {
1836 JsonDocument() {
1837 assert(s_pActiveDocument ==
1838 nullptr); // When using default allocator, only one document can be
1839 // active at a time, if you need multiple active at once,
1840 // define TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
1841 s_pActiveDocument = this;
jrkooncece7fa742019-09-04 13:31:44 -05001842 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001843 JsonDocument(const JsonDocument &) = delete;
1844 JsonDocument(JsonDocument &&rhs) noexcept
jrkooncece7fa742019-09-04 13:31:44 -05001845 : rapidjson::Document(std::move(rhs)) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001846 s_pActiveDocument = this;
1847 rhs.isNil = true;
1848 }
1849 ~JsonDocument() {
1850 if (!isNil) {
1851 s_pActiveDocument = nullptr;
jrkooncecba5d6c2019-08-29 11:26:22 -05001852 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001853 }
jrkooncece7fa742019-09-04 13:31:44 -05001854
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001855 private:
1856 bool isNil = false;
1857};
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001858
1859#ifdef __clang__
1860#pragma clang diagnostic pop
1861#endif
1862
jrkooncece7fa742019-09-04 13:31:44 -05001863#endif // TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001864
jrkooncecba5d6c2019-08-29 11:26:22 -05001865#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001866using nlohmann::json;
David Siegelcfe64fb2023-06-07 15:18:38 +02001867using json_iterator = json::iterator;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001868using json_const_iterator = json::const_iterator;
1869using json_const_array_iterator = json_const_iterator;
1870using JsonDocument = json;
jrkooncecba5d6c2019-08-29 11:26:22 -05001871#endif
1872
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001873void JsonParse(JsonDocument &doc, const char *str, size_t length,
1874 bool throwExc = false) {
jrkooncecba5d6c2019-08-29 11:26:22 -05001875#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001876 (void)throwExc;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001877 doc.Parse(str, length);
jrkooncecba5d6c2019-08-29 11:26:22 -05001878#else
David03ad33c2023-02-15 23:35:51 -06001879 doc = detail::json::parse(str, str + length, nullptr, throwExc);
jrkooncecba5d6c2019-08-29 11:26:22 -05001880#endif
jrkooncecba5d6c2019-08-29 11:26:22 -05001881}
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001882} // namespace
David03ad33c2023-02-15 23:35:51 -06001883}
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001884
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001885#ifdef __APPLE__
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001886#include "TargetConditionals.h"
Syoyo Fujitab23f6fe2017-11-15 01:03:09 +09001887#endif
1888
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001889#ifdef __clang__
1890#pragma clang diagnostic push
1891#pragma clang diagnostic ignored "-Wc++98-compat"
1892#endif
1893
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09001894namespace tinygltf {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001895
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001896///
1897/// Internal LoadImageDataOption struct.
1898/// This struct is passed through `user_pointer` in LoadImageData.
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001899/// The struct is not passed when the user supply their own LoadImageData
1900/// callbacks.
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001901///
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001902struct LoadImageDataOption {
1903 // true: preserve image channels(e.g. load as RGB image if the image has RGB
1904 // channels) default `false`(channels are expanded to RGBA for backward
imallettd9ce9eb2022-10-07 10:37:09 -07001905 // compatibility).
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001906 bool preserve_channels{false};
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001907};
1908
Selmar Kok31cb7f92018-10-03 15:39:05 +02001909// Equals function for Value, for recursivity
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001910static bool Equals(const tinygltf::Value &one, const tinygltf::Value &other) {
1911 if (one.Type() != other.Type()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001912
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001913 switch (one.Type()) {
1914 case NULL_TYPE:
1915 return true;
1916 case BOOL_TYPE:
1917 return one.Get<bool>() == other.Get<bool>();
Syoyo Fujita150f2432019-07-25 19:22:44 +09001918 case REAL_TYPE:
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001919 return TINYGLTF_DOUBLE_EQUAL(one.Get<double>(), other.Get<double>());
1920 case INT_TYPE:
1921 return one.Get<int>() == other.Get<int>();
1922 case OBJECT_TYPE: {
1923 auto oneObj = one.Get<tinygltf::Value::Object>();
1924 auto otherObj = other.Get<tinygltf::Value::Object>();
1925 if (oneObj.size() != otherObj.size()) return false;
1926 for (auto &it : oneObj) {
1927 auto otherIt = otherObj.find(it.first);
1928 if (otherIt == otherObj.end()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001929
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001930 if (!Equals(it.second, otherIt->second)) return false;
1931 }
1932 return true;
1933 }
1934 case ARRAY_TYPE: {
1935 if (one.Size() != other.Size()) return false;
1936 for (int i = 0; i < int(one.Size()); ++i)
Selmara63cc632019-04-16 16:57:43 +02001937 if (!Equals(one.Get(i), other.Get(i))) return false;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001938 return true;
1939 }
1940 case STRING_TYPE:
1941 return one.Get<std::string>() == other.Get<std::string>();
1942 case BINARY_TYPE:
1943 return one.Get<std::vector<unsigned char> >() ==
1944 other.Get<std::vector<unsigned char> >();
1945 default: {
1946 // unhandled type
1947 return false;
1948 }
1949 }
Selmar Kok31cb7f92018-10-03 15:39:05 +02001950}
1951
1952// Equals function for std::vector<double> using TINYGLTF_DOUBLE_EPSILON
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001953static bool Equals(const std::vector<double> &one,
1954 const std::vector<double> &other) {
1955 if (one.size() != other.size()) return false;
1956 for (int i = 0; i < int(one.size()); ++i) {
1957 if (!TINYGLTF_DOUBLE_EQUAL(one[size_t(i)], other[size_t(i)])) return false;
1958 }
1959 return true;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001960}
1961
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001962bool Accessor::operator==(const Accessor &other) const {
1963 return this->bufferView == other.bufferView &&
1964 this->byteOffset == other.byteOffset &&
1965 this->componentType == other.componentType &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001966 this->count == other.count && this->extensions == other.extensions &&
1967 this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001968 Equals(this->maxValues, other.maxValues) &&
1969 Equals(this->minValues, other.minValues) && this->name == other.name &&
1970 this->normalized == other.normalized && this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001971}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001972bool Animation::operator==(const Animation &other) const {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001973 return this->channels == other.channels &&
1974 this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001975 this->name == other.name && this->samplers == other.samplers;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001976}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001977bool AnimationChannel::operator==(const AnimationChannel &other) const {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001978 return this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001979 this->target_node == other.target_node &&
1980 this->target_path == other.target_path &&
1981 this->sampler == other.sampler;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001982}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001983bool AnimationSampler::operator==(const AnimationSampler &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001984 return this->extras == other.extras && this->extensions == other.extensions &&
1985 this->input == other.input &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001986 this->interpolation == other.interpolation &&
1987 this->output == other.output;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001988}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001989bool Asset::operator==(const Asset &other) const {
1990 return this->copyright == other.copyright &&
1991 this->extensions == other.extensions && this->extras == other.extras &&
1992 this->generator == other.generator &&
1993 this->minVersion == other.minVersion && this->version == other.version;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001994}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001995bool Buffer::operator==(const Buffer &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001996 return this->data == other.data && this->extensions == other.extensions &&
1997 this->extras == other.extras && this->name == other.name &&
1998 this->uri == other.uri;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001999}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002000bool BufferView::operator==(const BufferView &other) const {
2001 return this->buffer == other.buffer && this->byteLength == other.byteLength &&
2002 this->byteOffset == other.byteOffset &&
2003 this->byteStride == other.byteStride && this->name == other.name &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09002004 this->target == other.target && this->extensions == other.extensions &&
2005 this->extras == other.extras &&
Alexander Wood0d77a292019-01-26 08:58:45 -05002006 this->dracoDecoded == other.dracoDecoded;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002007}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002008bool Camera::operator==(const Camera &other) const {
2009 return this->name == other.name && this->extensions == other.extensions &&
2010 this->extras == other.extras &&
2011 this->orthographic == other.orthographic &&
2012 this->perspective == other.perspective && this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002013}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002014bool Image::operator==(const Image &other) const {
2015 return this->bufferView == other.bufferView &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09002016 this->component == other.component &&
2017 this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002018 this->height == other.height && this->image == other.image &&
2019 this->mimeType == other.mimeType && this->name == other.name &&
2020 this->uri == other.uri && this->width == other.width;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002021}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002022bool Light::operator==(const Light &other) const {
2023 return Equals(this->color, other.color) && this->name == other.name &&
2024 this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002025}
Baranob_Ilya78864c82023-06-12 10:43:52 +04002026bool AudioEmitter::operator==(const AudioEmitter &other) const {
2027 return this->name == other.name && TINYGLTF_DOUBLE_EQUAL(this->gain, other.gain) &&
2028 this->loop == other.loop && this->playing == other.playing &&
2029 this->type == other.type && this->distanceModel == other.distanceModel &&
2030 this->source == other.source;
2031}
2032bool AudioSource::operator == (const AudioSource &other) const {
2033 return this->name == other.name && this->uri == other.uri;
2034}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002035bool Material::operator==(const Material &other) const {
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09002036 return (this->pbrMetallicRoughness == other.pbrMetallicRoughness) &&
2037 (this->normalTexture == other.normalTexture) &&
2038 (this->occlusionTexture == other.occlusionTexture) &&
2039 (this->emissiveTexture == other.emissiveTexture) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09002040 Equals(this->emissiveFactor, other.emissiveFactor) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09002041 (this->alphaMode == other.alphaMode) &&
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002042 TINYGLTF_DOUBLE_EQUAL(this->alphaCutoff, other.alphaCutoff) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09002043 (this->doubleSided == other.doubleSided) &&
2044 (this->extensions == other.extensions) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09002045 (this->extras == other.extras) && (this->values == other.values) &&
2046 (this->additionalValues == other.additionalValues) &&
2047 (this->name == other.name);
Selmar Kok31cb7f92018-10-03 15:39:05 +02002048}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002049bool Mesh::operator==(const Mesh &other) const {
2050 return this->extensions == other.extensions && this->extras == other.extras &&
Selmar Kok81b672b2019-10-18 16:08:44 +02002051 this->name == other.name && Equals(this->weights, other.weights) &&
2052 this->primitives == other.primitives;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002053}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002054bool Model::operator==(const Model &other) const {
2055 return this->accessors == other.accessors &&
2056 this->animations == other.animations && this->asset == other.asset &&
2057 this->buffers == other.buffers &&
2058 this->bufferViews == other.bufferViews &&
2059 this->cameras == other.cameras &&
2060 this->defaultScene == other.defaultScene &&
2061 this->extensions == other.extensions &&
2062 this->extensionsRequired == other.extensionsRequired &&
2063 this->extensionsUsed == other.extensionsUsed &&
2064 this->extras == other.extras && this->images == other.images &&
2065 this->lights == other.lights && this->materials == other.materials &&
2066 this->meshes == other.meshes && this->nodes == other.nodes &&
2067 this->samplers == other.samplers && this->scenes == other.scenes &&
2068 this->skins == other.skins && this->textures == other.textures;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002069}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002070bool Node::operator==(const Node &other) const {
2071 return this->camera == other.camera && this->children == other.children &&
2072 this->extensions == other.extensions && this->extras == other.extras &&
2073 Equals(this->matrix, other.matrix) && this->mesh == other.mesh &&
Syoyo Fujita7a570c82023-06-19 21:52:13 +09002074 (this->light == other.light) &&
2075 (this->emitter == other.emitter) &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002076 this->name == other.name && Equals(this->rotation, other.rotation) &&
2077 Equals(this->scale, other.scale) && this->skin == other.skin &&
2078 Equals(this->translation, other.translation) &&
2079 Equals(this->weights, other.weights);
Selmar Kok31cb7f92018-10-03 15:39:05 +02002080}
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02002081bool SpotLight::operator==(const SpotLight &other) const {
2082 return this->extensions == other.extensions && this->extras == other.extras &&
2083 TINYGLTF_DOUBLE_EQUAL(this->innerConeAngle, other.innerConeAngle) &&
2084 TINYGLTF_DOUBLE_EQUAL(this->outerConeAngle, other.outerConeAngle);
2085}
Baranob_Ilya78864c82023-06-12 10:43:52 +04002086bool PositionalEmitter::operator == (const PositionalEmitter& other) const {
2087 return this->extensions == other.extensions && this->extras == other.extras &&
2088 TINYGLTF_DOUBLE_EQUAL(this->coneInnerAngle, other.coneInnerAngle) &&
2089 TINYGLTF_DOUBLE_EQUAL(this->coneOuterAngle, other.coneOuterAngle) &&
2090 TINYGLTF_DOUBLE_EQUAL(this->coneOuterGain, other.coneOuterGain) &&
2091 TINYGLTF_DOUBLE_EQUAL(this->maxDistance, other.maxDistance) &&
2092 TINYGLTF_DOUBLE_EQUAL(this->refDistance, other.refDistance) &&
2093 TINYGLTF_DOUBLE_EQUAL(this->rolloffFactor, other.rolloffFactor);
2094}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002095bool OrthographicCamera::operator==(const OrthographicCamera &other) const {
2096 return this->extensions == other.extensions && this->extras == other.extras &&
2097 TINYGLTF_DOUBLE_EQUAL(this->xmag, other.xmag) &&
2098 TINYGLTF_DOUBLE_EQUAL(this->ymag, other.ymag) &&
2099 TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
2100 TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
Selmar Kok31cb7f92018-10-03 15:39:05 +02002101}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002102bool Parameter::operator==(const Parameter &other) const {
2103 if (this->bool_value != other.bool_value ||
2104 this->has_number_value != other.has_number_value)
2105 return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002106
Selmar Kok2bda71c2018-10-05 14:36:05 +02002107 if (!TINYGLTF_DOUBLE_EQUAL(this->number_value, other.number_value))
2108 return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002109
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002110 if (this->json_double_value.size() != other.json_double_value.size())
2111 return false;
2112 for (auto &it : this->json_double_value) {
2113 auto otherIt = other.json_double_value.find(it.first);
2114 if (otherIt == other.json_double_value.end()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002115
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002116 if (!TINYGLTF_DOUBLE_EQUAL(it.second, otherIt->second)) return false;
2117 }
2118
2119 if (!Equals(this->number_array, other.number_array)) return false;
2120
2121 if (this->string_value != other.string_value) return false;
2122
2123 return true;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002124}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002125bool PerspectiveCamera::operator==(const PerspectiveCamera &other) const {
2126 return TINYGLTF_DOUBLE_EQUAL(this->aspectRatio, other.aspectRatio) &&
2127 this->extensions == other.extensions && this->extras == other.extras &&
2128 TINYGLTF_DOUBLE_EQUAL(this->yfov, other.yfov) &&
2129 TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
2130 TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
Selmar Kok31cb7f92018-10-03 15:39:05 +02002131}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002132bool Primitive::operator==(const Primitive &other) const {
2133 return this->attributes == other.attributes && this->extras == other.extras &&
2134 this->indices == other.indices && this->material == other.material &&
2135 this->mode == other.mode && this->targets == other.targets;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002136}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002137bool Sampler::operator==(const Sampler &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09002138 return this->extensions == other.extensions && this->extras == other.extras &&
2139 this->magFilter == other.magFilter &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002140 this->minFilter == other.minFilter && this->name == other.name &&
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002141 this->wrapS == other.wrapS && this->wrapT == other.wrapT;
Syoyo Fujita2c521b32020-12-04 00:50:46 +09002142
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002143 // this->wrapR == other.wrapR
Selmar Kok31cb7f92018-10-03 15:39:05 +02002144}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002145bool Scene::operator==(const Scene &other) const {
2146 return this->extensions == other.extensions && this->extras == other.extras &&
2147 this->name == other.name && this->nodes == other.nodes;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002148}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002149bool Skin::operator==(const Skin &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09002150 return this->extensions == other.extensions && this->extras == other.extras &&
2151 this->inverseBindMatrices == other.inverseBindMatrices &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002152 this->joints == other.joints && this->name == other.name &&
2153 this->skeleton == other.skeleton;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002154}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002155bool Texture::operator==(const Texture &other) const {
2156 return this->extensions == other.extensions && this->extras == other.extras &&
2157 this->name == other.name && this->sampler == other.sampler &&
2158 this->source == other.source;
Selmar Kok31cb7f92018-10-03 15:39:05 +02002159}
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09002160bool TextureInfo::operator==(const TextureInfo &other) const {
2161 return this->extensions == other.extensions && this->extras == other.extras &&
2162 this->index == other.index && this->texCoord == other.texCoord;
2163}
2164bool NormalTextureInfo::operator==(const NormalTextureInfo &other) const {
2165 return this->extensions == other.extensions && this->extras == other.extras &&
2166 this->index == other.index && this->texCoord == other.texCoord &&
2167 TINYGLTF_DOUBLE_EQUAL(this->scale, other.scale);
2168}
2169bool OcclusionTextureInfo::operator==(const OcclusionTextureInfo &other) const {
2170 return this->extensions == other.extensions && this->extras == other.extras &&
2171 this->index == other.index && this->texCoord == other.texCoord &&
2172 TINYGLTF_DOUBLE_EQUAL(this->strength, other.strength);
2173}
2174bool PbrMetallicRoughness::operator==(const PbrMetallicRoughness &other) const {
2175 return this->extensions == other.extensions && this->extras == other.extras &&
2176 (this->baseColorTexture == other.baseColorTexture) &&
2177 (this->metallicRoughnessTexture == other.metallicRoughnessTexture) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09002178 Equals(this->baseColorFactor, other.baseColorFactor) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09002179 TINYGLTF_DOUBLE_EQUAL(this->metallicFactor, other.metallicFactor) &&
2180 TINYGLTF_DOUBLE_EQUAL(this->roughnessFactor, other.roughnessFactor);
2181}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002182bool Value::operator==(const Value &other) const {
2183 return Equals(*this, other);
Selmar Kok31cb7f92018-10-03 15:39:05 +02002184}
2185
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002186static void swap4(unsigned int *val) {
2187#ifdef TINYGLTF_LITTLE_ENDIAN
2188 (void)val;
2189#else
2190 unsigned int tmp = *val;
2191 unsigned char *dst = reinterpret_cast<unsigned char *>(val);
2192 unsigned char *src = reinterpret_cast<unsigned char *>(&tmp);
2193
2194 dst[0] = src[3];
2195 dst[1] = src[2];
2196 dst[2] = src[1];
2197 dst[3] = src[0];
2198#endif
2199}
2200
Syoyo Fujitabeded612016-05-01 20:03:43 +09002201static std::string JoinPath(const std::string &path0,
2202 const std::string &path1) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002203 if (path0.empty()) {
2204 return path1;
2205 } else {
2206 // check '/'
2207 char lastChar = *path0.rbegin();
2208 if (lastChar != '/') {
2209 return path0 + std::string("/") + path1;
2210 } else {
2211 return path0 + path1;
2212 }
2213 }
2214}
2215
Syoyo Fujita643ce102016-05-01 17:19:37 +09002216static std::string FindFile(const std::vector<std::string> &paths,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002217 const std::string &filepath, FsCallbacks *fs) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002218 if (fs == nullptr || fs->ExpandFilePath == nullptr ||
2219 fs->FileExists == nullptr) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002220 // Error, fs callback[s] missing
2221 return std::string();
2222 }
2223
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002224 // https://github.com/syoyo/tinygltf/issues/416
2225 // Use strlen() since std::string's size/length reports the number of elements in the buffer, not the length of string(null-terminated)
2226 // strip null-character in the middle of string.
2227 size_t slength = strlen(filepath.c_str());
2228 if (slength == 0) {
2229 return std::string();
2230 }
2231
2232 std::string cleaned_filepath = std::string(filepath.c_str());
2233
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002234 for (size_t i = 0; i < paths.size(); i++) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002235 std::string absPath =
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002236 fs->ExpandFilePath(JoinPath(paths[i], cleaned_filepath), fs->user_data);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002237 if (fs->FileExists(absPath, fs->user_data)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002238 return absPath;
2239 }
2240 }
2241
2242 return std::string();
2243}
2244
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09002245static std::string GetFilePathExtension(const std::string &FileName) {
johan bowald642a3432018-04-01 12:37:18 +02002246 if (FileName.find_last_of(".") != std::string::npos)
2247 return FileName.substr(FileName.find_last_of(".") + 1);
2248 return "";
2249}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002250
Syoyo Fujita643ce102016-05-01 17:19:37 +09002251static std::string GetBaseDir(const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002252 if (filepath.find_last_of("/\\") != std::string::npos)
2253 return filepath.substr(0, filepath.find_last_of("/\\"));
2254 return "";
2255}
2256
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002257static std::string GetBaseFilename(const std::string &filepath) {
Martin Gerhardyfae65432022-03-13 18:42:39 +01002258 auto idx = filepath.find_last_of("/\\");
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002259 if (idx != std::string::npos) return filepath.substr(idx + 1);
Alexander Wood190382a2021-10-08 12:19:13 -04002260 return filepath;
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002261}
2262
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002263std::string base64_encode(unsigned char const *, unsigned int len);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002264std::string base64_decode(std::string const &s);
2265
2266/*
2267 base64.cpp and base64.h
2268
2269 Copyright (C) 2004-2008 René Nyffenegger
2270
2271 This source code is provided 'as-is', without any express or implied
2272 warranty. In no event will the author be held liable for any damages
2273 arising from the use of this software.
2274
2275 Permission is granted to anyone to use this software for any purpose,
2276 including commercial applications, and to alter it and redistribute it
2277 freely, subject to the following restrictions:
2278
2279 1. The origin of this source code must not be misrepresented; you must not
2280 claim that you wrote the original source code. If you use this source code
2281 in a product, an acknowledgment in the product documentation would be
2282 appreciated but is not required.
2283
2284 2. Altered source versions must be plainly marked as such, and must not be
2285 misrepresented as being the original source code.
2286
2287 3. This notice may not be removed or altered from any source distribution.
2288
2289 René Nyffenegger rene.nyffenegger@adp-gmbh.ch
2290
2291*/
2292
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002293#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09002294#pragma clang diagnostic push
Syoyo Fujita643ce102016-05-01 17:19:37 +09002295#pragma clang diagnostic ignored "-Wsign-conversion"
2296#pragma clang diagnostic ignored "-Wconversion"
Syoyo Fujita6fd91622016-05-01 20:27:30 +09002297#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002298
2299static inline bool is_base64(unsigned char c) {
2300 return (isalnum(c) || (c == '+') || (c == '/'));
2301}
2302
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002303std::string base64_encode(unsigned char const *bytes_to_encode,
2304 unsigned int in_len) {
johan bowald30c53472018-03-30 11:49:36 +02002305 std::string ret;
2306 int i = 0;
2307 int j = 0;
2308 unsigned char char_array_3[3];
2309 unsigned char char_array_4[4];
2310
Syoyo Fujitaff515702019-08-24 16:29:14 +09002311 const char *base64_chars =
2312 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2313 "abcdefghijklmnopqrstuvwxyz"
2314 "0123456789+/";
2315
johan bowald30c53472018-03-30 11:49:36 +02002316 while (in_len--) {
2317 char_array_3[i++] = *(bytes_to_encode++);
2318 if (i == 3) {
2319 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002320 char_array_4[1] =
2321 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
2322 char_array_4[2] =
2323 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
johan bowald30c53472018-03-30 11:49:36 +02002324 char_array_4[3] = char_array_3[2] & 0x3f;
2325
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002326 for (i = 0; (i < 4); i++) ret += base64_chars[char_array_4[i]];
johan bowald30c53472018-03-30 11:49:36 +02002327 i = 0;
2328 }
2329 }
2330
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002331 if (i) {
2332 for (j = i; j < 3; j++) char_array_3[j] = '\0';
johan bowald30c53472018-03-30 11:49:36 +02002333
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002334 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
2335 char_array_4[1] =
2336 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
2337 char_array_4[2] =
2338 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
johan bowald30c53472018-03-30 11:49:36 +02002339
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002340 for (j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]];
johan bowald30c53472018-03-30 11:49:36 +02002341
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002342 while ((i++ < 3)) ret += '=';
johan bowald30c53472018-03-30 11:49:36 +02002343 }
2344
2345 return ret;
johan bowald30c53472018-03-30 11:49:36 +02002346}
2347
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002348std::string base64_decode(std::string const &encoded_string) {
Syoyo Fujita8c6774c2016-05-01 20:36:29 +09002349 int in_len = static_cast<int>(encoded_string.size());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002350 int i = 0;
2351 int j = 0;
2352 int in_ = 0;
2353 unsigned char char_array_4[4], char_array_3[3];
2354 std::string ret;
2355
Syoyo Fujitaff515702019-08-24 16:29:14 +09002356 const std::string base64_chars =
2357 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2358 "abcdefghijklmnopqrstuvwxyz"
2359 "0123456789+/";
2360
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002361 while (in_len-- && (encoded_string[in_] != '=') &&
2362 is_base64(encoded_string[in_])) {
2363 char_array_4[i++] = encoded_string[in_];
2364 in_++;
2365 if (i == 4) {
2366 for (i = 0; i < 4; i++)
Syoyo Fujitabeded612016-05-01 20:03:43 +09002367 char_array_4[i] =
2368 static_cast<unsigned char>(base64_chars.find(char_array_4[i]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002369
2370 char_array_3[0] =
2371 (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
2372 char_array_3[1] =
2373 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
2374 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
2375
Syoyo Fujita7c877972016-03-08 01:31:49 +09002376 for (i = 0; (i < 3); i++) ret += char_array_3[i];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002377 i = 0;
2378 }
2379 }
2380
2381 if (i) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09002382 for (j = i; j < 4; j++) char_array_4[j] = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002383
2384 for (j = 0; j < 4; j++)
Syoyo Fujitabeded612016-05-01 20:03:43 +09002385 char_array_4[j] =
2386 static_cast<unsigned char>(base64_chars.find(char_array_4[j]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002387
2388 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
2389 char_array_3[1] =
2390 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
2391 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
2392
Syoyo Fujita7c877972016-03-08 01:31:49 +09002393 for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002394 }
2395
2396 return ret;
2397}
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002398#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09002399#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +09002400#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002401
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002402// https://github.com/syoyo/tinygltf/issues/228
2403// TODO(syoyo): Use uriparser https://uriparser.github.io/ for stricter Uri
2404// decoding?
2405//
Alexander Woode4bc6c72021-10-14 08:54:59 -04002406// Uri Decoding from DLIB
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002407// http://dlib.net/dlib/server/server_http.cpp.html
Alexander Woode4bc6c72021-10-14 08:54:59 -04002408// --- dlib begin ------------------------------------------------------------
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002409// Copyright (C) 2003 Davis E. King (davis@dlib.net)
Alexander Woode4bc6c72021-10-14 08:54:59 -04002410// License: Boost Software License
2411// Boost Software License - Version 1.0 - August 17th, 2003
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002412
Alexander Woode4bc6c72021-10-14 08:54:59 -04002413// Permission is hereby granted, free of charge, to any person or organization
2414// obtaining a copy of the software and accompanying documentation covered by
2415// this license (the "Software") to use, reproduce, display, distribute,
2416// execute, and transmit the Software, and to prepare derivative works of the
2417// Software, and to permit third-parties to whom the Software is furnished to
2418// do so, all subject to the following:
2419// The copyright notices in the Software and this entire statement, including
2420// the above license grant, this restriction and the following disclaimer,
2421// must be included in all copies of the Software, in whole or in part, and
2422// all derivative works of the Software, unless such copies or derivative
2423// works are solely in the form of machine-executable object code generated by
2424// a source language processor.
2425// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2426// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2427// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
2428// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
2429// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
2430// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2431// DEALINGS IN THE SOFTWARE.
Syoyo Fujitacbd38852022-02-26 21:19:15 +09002432//
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002433namespace dlib {
2434
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002435inline unsigned char from_hex(unsigned char ch) {
2436 if (ch <= '9' && ch >= '0')
2437 ch -= '0';
2438 else if (ch <= 'f' && ch >= 'a')
2439 ch -= 'a' - 10;
2440 else if (ch <= 'F' && ch >= 'A')
2441 ch -= 'A' - 10;
2442 else
2443 ch = 0;
2444 return ch;
2445}
2446
2447static const std::string urldecode(const std::string &str) {
2448 using namespace std;
2449 string result;
2450 string::size_type i;
2451 for (i = 0; i < str.size(); ++i) {
2452 if (str[i] == '+') {
2453 result += ' ';
2454 } else if (str[i] == '%' && str.size() > i + 2) {
2455 const unsigned char ch1 =
2456 from_hex(static_cast<unsigned char>(str[i + 1]));
2457 const unsigned char ch2 =
2458 from_hex(static_cast<unsigned char>(str[i + 2]));
2459 const unsigned char ch = static_cast<unsigned char>((ch1 << 4) | ch2);
2460 result += static_cast<char>(ch);
2461 i += 2;
2462 } else {
2463 result += str[i];
2464 }
2465 }
2466 return result;
2467}
2468
2469} // namespace dlib
2470// --- dlib end --------------------------------------------------------------
2471
Pyarelal Knowles385946d2023-01-03 18:18:04 -08002472bool URIDecode(const std::string &in_uri, std::string *out_uri,
2473 void *user_data) {
2474 (void)user_data;
2475 *out_uri = dlib::urldecode(in_uri);
2476 return true;
2477}
2478
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002479static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002480 std::string *warn, const std::string &filename,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002481 const std::string &basedir, bool required,
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002482 size_t reqBytes, bool checkSize, size_t maxFileSize, FsCallbacks *fs) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002483 if (fs == nullptr || fs->FileExists == nullptr ||
2484 fs->ExpandFilePath == nullptr || fs->ReadWholeFile == nullptr) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002485 // This is a developer error, assert() ?
2486 if (err) {
2487 (*err) += "FS callback[s] not set\n";
2488 }
2489 return false;
2490 }
2491
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002492 std::string *failMsgOut = required ? err : warn;
Selmar Koke3b3fa92018-08-22 19:04:21 +02002493
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002494 out->clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002495
2496 std::vector<std::string> paths;
2497 paths.push_back(basedir);
2498 paths.push_back(".");
2499
Paolo Jovone6601bf2018-07-07 20:43:33 +02002500 std::string filepath = FindFile(paths, filename, fs);
jianghaosenb721fb72017-08-25 21:01:17 +08002501 if (filepath.empty() || filename.empty()) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002502 if (failMsgOut) {
2503 (*failMsgOut) += "File not found : " + filename + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002504 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002505 return false;
2506 }
2507
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002508 // Check file size
2509 if (fs->GetFileSizeInBytes) {
2510
2511 size_t file_size{0};
2512 std::string _err;
David Siegelcc93e1f2023-04-26 12:13:41 +02002513 bool ok = fs->GetFileSizeInBytes(&file_size, &_err, filepath, fs->user_data);
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002514 if (!ok) {
2515 if (_err.size()) {
2516 if (failMsgOut) {
2517 (*failMsgOut) += "Getting file size failed : " + filename + ", err = " + _err + "\n";
2518 }
2519 }
2520 return false;
2521 }
2522
2523 if (file_size > maxFileSize) {
2524 if (failMsgOut) {
2525 (*failMsgOut) += "File size " + std::to_string(file_size) + " exceeds maximum allowed file size " + std::to_string(maxFileSize) + " : " + filepath + "\n";
2526 }
2527 return false;
2528 }
2529 }
2530
Paolo Jovone6601bf2018-07-07 20:43:33 +02002531 std::vector<unsigned char> buf;
2532 std::string fileReadErr;
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002533 bool fileRead =
2534 fs->ReadWholeFile(&buf, &fileReadErr, filepath, fs->user_data);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002535 if (!fileRead) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002536 if (failMsgOut) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002537 (*failMsgOut) +=
2538 "File read error : " + filepath + " : " + fileReadErr + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002539 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002540 return false;
2541 }
2542
Paolo Jovone6601bf2018-07-07 20:43:33 +02002543 size_t sz = buf.size();
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09002544 if (sz == 0) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002545 if (failMsgOut) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002546 (*failMsgOut) += "File is empty : " + filepath + "\n";
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002547 }
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09002548 return false;
2549 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002550
2551 if (checkSize) {
2552 if (reqBytes == sz) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002553 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002554 return true;
2555 } else {
2556 std::stringstream ss;
2557 ss << "File size mismatch : " << filepath << ", requestedBytes "
2558 << reqBytes << ", but got " << sz << std::endl;
Selmar Koke3b3fa92018-08-22 19:04:21 +02002559 if (failMsgOut) {
2560 (*failMsgOut) += ss.str();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002561 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002562 return false;
2563 }
2564 }
2565
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002566 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002567 return true;
2568}
2569
Squareysff644d82018-03-13 22:36:18 +01002570void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002571 LoadImageData = func;
2572 load_image_user_data_ = user_data;
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002573 user_image_loader_ = true;
2574}
2575
2576void TinyGLTF::RemoveImageLoader() {
2577 LoadImageData =
2578#ifndef TINYGLTF_NO_STB_IMAGE
2579 &tinygltf::LoadImageData;
2580#else
2581 nullptr;
2582#endif
2583
2584 load_image_user_data_ = nullptr;
2585 user_image_loader_ = false;
Squareysff644d82018-03-13 22:36:18 +01002586}
2587
Squareys2d3594d2018-03-13 22:40:53 +01002588#ifndef TINYGLTF_NO_STB_IMAGE
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002589bool LoadImageData(Image *image, const int image_idx, std::string *err,
2590 std::string *warn, int req_width, int req_height,
2591 const unsigned char *bytes, int size, void *user_data) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002592 (void)warn;
2593
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002594 LoadImageDataOption option;
2595 if (user_data) {
2596 option = *reinterpret_cast<LoadImageDataOption *>(user_data);
2597 }
2598
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002599 int w = 0, h = 0, comp = 0, req_comp = 0;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002600
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002601 unsigned char *data = nullptr;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002602
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002603 // preserve_channels true: Use channels stored in the image file.
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09002604 // false: force 32-bit textures for common Vulkan compatibility. It appears
2605 // that some GPU drivers do not support 24-bit images for Vulkan
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002606 req_comp = option.preserve_channels ? 0 : 4;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002607 int bits = 8;
2608 int pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002609
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002610 // It is possible that the image we want to load is a 16bit per channel image
2611 // We are going to attempt to load it as 16bit per channel, and if it worked,
imallettd9ce9eb2022-10-07 10:37:09 -07002612 // set the image data accordingly. We are casting the returned pointer into
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002613 // unsigned char, because we are representing "bytes". But we are updating
2614 // the Image metadata to signal that this image uses 2 bytes (16bits) per
2615 // channel:
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002616 if (stbi_is_16_bit_from_memory(bytes, size)) {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002617 data = reinterpret_cast<unsigned char *>(
2618 stbi_load_16_from_memory(bytes, size, &w, &h, &comp, req_comp));
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002619 if (data) {
2620 bits = 16;
2621 pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT;
2622 }
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002623 }
2624
2625 // at this point, if data is still NULL, it means that the image wasn't
2626 // 16bit per channel, we are going to load it as a normal 8bit per channel
imallettd9ce9eb2022-10-07 10:37:09 -07002627 // image as we used to do:
Aurélien Chatelain5cb43462017-05-24 09:55:14 +00002628 // if image cannot be decoded, ignore parsing and keep it by its path
2629 // don't break in this case
Syoyo Fujita5b407452017-06-04 17:42:41 +09002630 // FIXME we should only enter this function if the image is embedded. If
2631 // image->uri references
2632 // an image file, it should be left as it is. Image loading should not be
2633 // mandatory (to support other formats)
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002634 if (!data) data = stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002635 if (!data) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002636 // NOTE: you can use `warn` instead of `err`
Syoyo Fujitabeded612016-05-01 20:03:43 +09002637 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002638 (*err) +=
2639 "Unknown image format. STB cannot decode image data for image[" +
2640 std::to_string(image_idx) + "] name = \"" + image->name + "\".\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002641 }
Omar C. Fd492efc2018-02-10 09:50:35 +02002642 return false;
Syoyo Fujitabeded612016-05-01 20:03:43 +09002643 }
2644
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002645 if ((w < 1) || (h < 1)) {
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002646 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002647 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002648 (*err) += "Invalid image data for image[" + std::to_string(image_idx) +
2649 "] name = \"" + image->name + "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002650 }
Omar C. Fd492efc2018-02-10 09:50:35 +02002651 return false;
Syoyo Fujitabeded612016-05-01 20:03:43 +09002652 }
2653
2654 if (req_width > 0) {
2655 if (req_width != w) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002656 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002657 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002658 (*err) += "Image width mismatch for image[" +
2659 std::to_string(image_idx) + "] name = \"" + image->name +
2660 "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002661 }
2662 return false;
2663 }
2664 }
2665
2666 if (req_height > 0) {
2667 if (req_height != h) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002668 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002669 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002670 (*err) += "Image height mismatch. for image[" +
2671 std::to_string(image_idx) + "] name = \"" + image->name +
2672 "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002673 }
2674 return false;
2675 }
2676 }
2677
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002678 if (req_comp != 0) {
2679 // loaded data has `req_comp` channels(components)
2680 comp = req_comp;
2681 }
2682
Syoyo Fujitabeded612016-05-01 20:03:43 +09002683 image->width = w;
2684 image->height = h;
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002685 image->component = comp;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002686 image->bits = bits;
2687 image->pixel_type = pixel_type;
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002688 image->image.resize(static_cast<size_t>(w * h * comp) * size_t(bits / 8));
2689 std::copy(data, data + w * h * comp * (bits / 8), image->image.begin());
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002690 stbi_image_free(data);
Syoyo Fujita4be2f882016-11-24 16:20:34 +09002691
Syoyo Fujitabeded612016-05-01 20:03:43 +09002692 return true;
2693}
Squareys2d3594d2018-03-13 22:40:53 +01002694#endif
Syoyo Fujitabeded612016-05-01 20:03:43 +09002695
johan bowald642a3432018-04-01 12:37:18 +02002696void TinyGLTF::SetImageWriter(WriteImageDataFunction func, void *user_data) {
2697 WriteImageData = func;
2698 write_image_user_data_ = user_data;
2699}
2700
2701#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
2702static void WriteToMemory_stbi(void *context, void *data, int size) {
2703 std::vector<unsigned char> *buffer =
2704 reinterpret_cast<std::vector<unsigned char> *>(context);
2705
2706 unsigned char *pData = reinterpret_cast<unsigned char *>(data);
2707
2708 buffer->insert(buffer->end(), pData, pData + size);
2709}
2710
2711bool WriteImageData(const std::string *basepath, const std::string *filename,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08002712 const Image *image, bool embedImages,
2713 const URICallbacks *uri_cb, std::string *out_uri,
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08002714 void *fsPtr) {
johan bowald642a3432018-04-01 12:37:18 +02002715 const std::string ext = GetFilePathExtension(*filename);
2716
Paolo Jovone6601bf2018-07-07 20:43:33 +02002717 // Write image to temporary buffer
2718 std::string header;
2719 std::vector<unsigned char> data;
johan bowald642a3432018-04-01 12:37:18 +02002720
Paolo Jovone6601bf2018-07-07 20:43:33 +02002721 if (ext == "png") {
Syoyo Fujitaca56f722019-03-07 21:04:25 +09002722 if ((image->bits != 8) ||
2723 (image->pixel_type != TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)) {
Syoyo Fujitae8a46c42019-03-07 20:50:58 +09002724 // Unsupported pixel format
2725 return false;
2726 }
2727
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002728 if (!stbi_write_png_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002729 image->height, image->component,
2730 &image->image[0], 0)) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002731 return false;
2732 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002733 header = "data:image/png;base64,";
2734 } else if (ext == "jpg") {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002735 if (!stbi_write_jpg_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002736 image->height, image->component,
2737 &image->image[0], 100)) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002738 return false;
2739 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002740 header = "data:image/jpeg;base64,";
2741 } else if (ext == "bmp") {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002742 if (!stbi_write_bmp_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002743 image->height, image->component,
2744 &image->image[0])) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002745 return false;
2746 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002747 header = "data:image/bmp;base64,";
2748 } else if (!embedImages) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002749 // Error: can't output requested format to file
2750 return false;
Paolo Jovone6601bf2018-07-07 20:43:33 +02002751 }
johan bowald642a3432018-04-01 12:37:18 +02002752
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002753 if (embedImages) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002754 // Embed base64-encoded image into URI
johan bowald642a3432018-04-01 12:37:18 +02002755 if (data.size()) {
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08002756 *out_uri = header +
2757 base64_encode(&data[0], static_cast<unsigned int>(data.size()));
johan bowald642a3432018-04-01 12:37:18 +02002758 } else {
2759 // Throw error?
2760 }
2761 } else {
2762 // Write image to disc
Paolo Jovone6601bf2018-07-07 20:43:33 +02002763 FsCallbacks *fs = reinterpret_cast<FsCallbacks *>(fsPtr);
Syoyo Fujita33514af2018-11-05 13:32:43 +09002764 if ((fs != nullptr) && (fs->WriteWholeFile != nullptr)) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002765 const std::string imagefilepath = JoinPath(*basepath, *filename);
2766 std::string writeError;
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002767 if (!fs->WriteWholeFile(&writeError, imagefilepath, data,
2768 fs->user_data)) {
2769 // Could not write image file to disc; Throw error ?
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002770 return false;
Paolo Jovone6601bf2018-07-07 20:43:33 +02002771 }
johan bowald642a3432018-04-01 12:37:18 +02002772 } else {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002773 // Throw error?
johan bowald642a3432018-04-01 12:37:18 +02002774 }
Pyarelal Knowles385946d2023-01-03 18:18:04 -08002775 if (uri_cb->encode) {
2776 if (!uri_cb->encode(*filename, "image", out_uri, uri_cb->user_data)) {
2777 return false;
2778 }
2779 } else {
2780 *out_uri = *filename;
2781 }
johan bowald642a3432018-04-01 12:37:18 +02002782 }
2783
2784 return true;
2785}
2786#endif
2787
Pyarelal Knowles385946d2023-01-03 18:18:04 -08002788void TinyGLTF::SetURICallbacks(URICallbacks callbacks) {
2789 assert(callbacks.decode);
2790 if (callbacks.decode) {
2791 uri_cb = callbacks;
2792 }
2793}
2794
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002795void TinyGLTF::SetFsCallbacks(FsCallbacks callbacks) { fs = callbacks; }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002796
Harokyangfb256602019-10-30 16:13:52 +08002797#ifdef _WIN32
2798static inline std::wstring UTF8ToWchar(const std::string &str) {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002799 int wstr_size =
2800 MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
imallett56e10982022-10-07 10:35:16 -07002801 std::wstring wstr((size_t)wstr_size, 0);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002802 MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0],
2803 (int)wstr.size());
Harokyangfb256602019-10-30 16:13:52 +08002804 return wstr;
2805}
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002806
2807static inline std::string WcharToUTF8(const std::wstring &wstr) {
2808 int str_size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(),
imallett56e10982022-10-07 10:35:16 -07002809 nullptr, 0, nullptr, nullptr);
2810 std::string str((size_t)str_size, 0);
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002811 WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), &str[0],
imallett56e10982022-10-07 10:35:16 -07002812 (int)str.size(), nullptr, nullptr);
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002813 return str;
2814}
Harokyangfb256602019-10-30 16:13:52 +08002815#endif
2816
Paolo Jovone6601bf2018-07-07 20:43:33 +02002817#ifndef TINYGLTF_NO_FS
2818// Default implementations of filesystem functions
2819
2820bool FileExists(const std::string &abs_filename, void *) {
2821 bool ret;
Sascha Willems5f9cb242018-12-28 20:53:41 +01002822#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002823 if (asset_manager) {
2824 AAsset *asset = AAssetManager_open(asset_manager, abs_filename.c_str(),
2825 AASSET_MODE_STREAMING);
2826 if (!asset) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01002827 return false;
2828 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002829 AAsset_close(asset);
2830 ret = true;
2831 } else {
2832 return false;
2833 }
Sascha Willems5f9cb242018-12-28 20:53:41 +01002834#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002835#ifdef _WIN32
operatios1668d1e2022-09-24 22:37:14 +03002836#if defined(_MSC_VER) || defined(__GLIBCXX__) || defined(_LIBCPP_VERSION)
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002837
2838 // First check if a file is a directory.
2839 DWORD result = GetFileAttributesW(UTF8ToWchar(abs_filename).c_str());
2840 if (result == INVALID_FILE_ATTRIBUTES) {
2841 return false;
2842 }
2843 if (result & FILE_ATTRIBUTE_DIRECTORY) {
2844 return false;
2845 }
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002846
Syoyo Fujita45cac782019-11-09 20:42:55 +09002847 FILE *fp = nullptr;
Harokyangfb256602019-10-30 16:13:52 +08002848 errno_t err = _wfopen_s(&fp, UTF8ToWchar(abs_filename).c_str(), L"rb");
Paolo Jovone6601bf2018-07-07 20:43:33 +02002849 if (err != 0) {
2850 return false;
2851 }
2852#else
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002853 // TODO: is_directory check
Syoyo Fujita45cac782019-11-09 20:42:55 +09002854 FILE *fp = nullptr;
2855 errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb");
2856 if (err != 0) {
2857 return false;
2858 }
2859#endif
2860
2861#else
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002862 struct stat sb;
2863 if (stat(abs_filename.c_str(), &sb)) {
2864 return false;
2865 }
2866 if (S_ISDIR(sb.st_mode)) {
2867 return false;
2868 }
2869
Paolo Jovone6601bf2018-07-07 20:43:33 +02002870 FILE *fp = fopen(abs_filename.c_str(), "rb");
2871#endif
2872 if (fp) {
2873 ret = true;
2874 fclose(fp);
2875 } else {
2876 ret = false;
2877 }
Sascha Willems5f9cb242018-12-28 20:53:41 +01002878#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002879
2880 return ret;
2881}
2882
2883std::string ExpandFilePath(const std::string &filepath, void *) {
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002884 // https://github.com/syoyo/tinygltf/issues/368
2885 //
2886 // No file path expansion in built-in FS function anymore, since glTF URI
2887 // should not contain tilde('~') and environment variables, and for security
2888 // reason(`wordexp`).
2889 //
2890 // Users need to supply `base_dir`(in `LoadASCIIFromString`,
2891 // `LoadBinaryFromMemory`) in expanded absolute path.
2892
2893 return filepath;
2894
2895#if 0
Paolo Jovone6601bf2018-07-07 20:43:33 +02002896#ifdef _WIN32
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002897 // Assume input `filepath` is encoded in UTF-8
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002898 std::wstring wfilepath = UTF8ToWchar(filepath);
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002899 DWORD wlen = ExpandEnvironmentStringsW(wfilepath.c_str(), nullptr, 0);
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002900 wchar_t *wstr = new wchar_t[wlen];
2901 ExpandEnvironmentStringsW(wfilepath.c_str(), wstr, wlen);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002902
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002903 std::wstring ws(wstr);
2904 delete[] wstr;
2905 return WcharToUTF8(ws);
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002906
Paolo Jovone6601bf2018-07-07 20:43:33 +02002907#else
2908
2909#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \
Julian Smith0598a202021-08-25 12:06:08 +01002910 defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__OpenBSD__)
Paolo Jovone6601bf2018-07-07 20:43:33 +02002911 // no expansion
2912 std::string s = filepath;
2913#else
2914 std::string s;
2915 wordexp_t p;
2916
2917 if (filepath.empty()) {
2918 return "";
2919 }
2920
Frank Galliganaa3c5a12020-01-13 15:06:56 -08002921 // Quote the string to keep any spaces in filepath intact.
2922 std::string quoted_path = "\"" + filepath + "\"";
Paolo Jovone6601bf2018-07-07 20:43:33 +02002923 // char** w;
Frank Galliganaa3c5a12020-01-13 15:06:56 -08002924 int ret = wordexp(quoted_path.c_str(), &p, 0);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002925 if (ret) {
2926 // err
2927 s = filepath;
2928 return s;
2929 }
2930
2931 // Use first element only.
2932 if (p.we_wordv) {
2933 s = std::string(p.we_wordv[0]);
2934 wordfree(&p);
2935 } else {
2936 s = filepath;
2937 }
2938
2939#endif
2940
2941 return s;
2942#endif
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002943#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002944}
2945
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09002946bool GetFileSizeInBytes(size_t *filesize_out, std::string *err,
2947 const std::string &filepath, void *userdata) {
2948 (void)userdata;
2949
2950#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
2951 if (asset_manager) {
2952 AAsset *asset = AAssetManager_open(asset_manager, filepath.c_str(),
2953 AASSET_MODE_STREAMING);
2954 if (!asset) {
2955 if (err) {
2956 (*err) += "File open error : " + filepath + "\n";
2957 }
2958 return false;
2959 }
2960 size_t size = AAsset_getLength(asset);
2961
2962 if (size == 0) {
2963 if (err) {
2964 (*err) += "Invalid file size : " + filepath +
2965 " (does the path point to a directory?)";
2966 }
2967 return false;
2968 }
2969
2970 return true;
2971 } else {
2972 if (err) {
2973 (*err) += "No asset manager specified : " + filepath + "\n";
2974 }
2975 return false;
2976 }
2977#else
2978#ifdef _WIN32
2979#if defined(__GLIBCXX__) // mingw
2980 int file_descriptor =
2981 _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY);
2982 __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
2983 std::istream f(&wfile_buf);
2984#elif defined(_MSC_VER) || defined(_LIBCPP_VERSION)
2985 // For libcxx, assume _LIBCPP_HAS_OPEN_WITH_WCHAR is defined to accept
2986 // `wchar_t *`
2987 std::ifstream f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary);
2988#else
2989 // Unknown compiler/runtime
2990 std::ifstream f(filepath.c_str(), std::ifstream::binary);
2991#endif
2992#else
2993 std::ifstream f(filepath.c_str(), std::ifstream::binary);
2994#endif
2995 if (!f) {
2996 if (err) {
2997 (*err) += "File open error : " + filepath + "\n";
2998 }
2999 return false;
3000 }
3001
3002 // For directory(and pipe?), peek() will fail(Posix gnustl/libc++ only)
David Siegel47208b22023-06-05 22:14:30 +02003003 f.peek();
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09003004 if (!f) {
3005 if (err) {
3006 (*err) += "File read error. Maybe empty file or invalid file : " + filepath + "\n";
3007 }
3008 return false;
3009 }
3010
3011 f.seekg(0, f.end);
3012 size_t sz = static_cast<size_t>(f.tellg());
3013
3014 //std::cout << "sz = " << sz << "\n";
3015 f.seekg(0, f.beg);
3016
3017 if (int64_t(sz) < 0) {
3018 if (err) {
3019 (*err) += "Invalid file size : " + filepath +
3020 " (does the path point to a directory?)";
3021 }
3022 return false;
3023 } else if (sz == 0) {
3024 if (err) {
3025 (*err) += "File is empty : " + filepath + "\n";
3026 }
3027 return false;
3028 } else if (sz >= (std::numeric_limits<std::streamoff>::max)()) {
3029 if (err) {
3030 (*err) += "Invalid file size : " + filepath + "\n";
3031 }
3032 return false;
3033 }
3034
3035 (*filesize_out) = sz;
3036 return true;
3037#endif
3038}
3039
Paolo Jovone6601bf2018-07-07 20:43:33 +02003040bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
3041 const std::string &filepath, void *) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01003042#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
3043 if (asset_manager) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003044 AAsset *asset = AAssetManager_open(asset_manager, filepath.c_str(),
3045 AASSET_MODE_STREAMING);
Sascha Willems5f9cb242018-12-28 20:53:41 +01003046 if (!asset) {
3047 if (err) {
3048 (*err) += "File open error : " + filepath + "\n";
3049 }
3050 return false;
3051 }
3052 size_t size = AAsset_getLength(asset);
Syoyo Fujita73c4cce2020-04-28 01:06:34 +09003053 if (size == 0) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01003054 if (err) {
3055 (*err) += "Invalid file size : " + filepath +
3056 " (does the path point to a directory?)";
3057 }
Syoyo Fujita73c4cce2020-04-28 01:06:34 +09003058 return false;
Sascha Willems5f9cb242018-12-28 20:53:41 +01003059 }
3060 out->resize(size);
3061 AAsset_read(asset, reinterpret_cast<char *>(&out->at(0)), size);
3062 AAsset_close(asset);
3063 return true;
3064 } else {
3065 if (err) {
3066 (*err) += "No asset manager specified : " + filepath + "\n";
3067 }
3068 return false;
3069 }
3070#else
Harokyang5cecef22019-10-30 15:16:46 +08003071#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09003072#if defined(__GLIBCXX__) // mingw
3073 int file_descriptor =
3074 _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY);
Syoyo Fujita45cac782019-11-09 20:42:55 +09003075 __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
3076 std::istream f(&wfile_buf);
Syoyo Fujitaaba57bb2020-05-18 20:50:45 +09003077#elif defined(_MSC_VER) || defined(_LIBCPP_VERSION)
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04003078 // For libcxx, assume _LIBCPP_HAS_OPEN_WITH_WCHAR is defined to accept
3079 // `wchar_t *`
Harokyangfb256602019-10-30 16:13:52 +08003080 std::ifstream f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary);
Syoyo Fujitaaba57bb2020-05-18 20:50:45 +09003081#else
3082 // Unknown compiler/runtime
Syoyo Fujita45cac782019-11-09 20:42:55 +09003083 std::ifstream f(filepath.c_str(), std::ifstream::binary);
3084#endif
Harokyang5cecef22019-10-30 15:16:46 +08003085#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02003086 std::ifstream f(filepath.c_str(), std::ifstream::binary);
Harokyang5cecef22019-10-30 15:16:46 +08003087#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02003088 if (!f) {
3089 if (err) {
3090 (*err) += "File open error : " + filepath + "\n";
3091 }
3092 return false;
3093 }
3094
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09003095 // For directory(and pipe?), peek() will fail(Posix gnustl/libc++ only)
David Siegel47208b22023-06-05 22:14:30 +02003096 f.peek();
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09003097 if (!f) {
3098 if (err) {
3099 (*err) += "File read error. Maybe empty file or invalid file : " + filepath + "\n";
3100 }
3101 return false;
3102 }
3103
Paolo Jovone6601bf2018-07-07 20:43:33 +02003104 f.seekg(0, f.end);
3105 size_t sz = static_cast<size_t>(f.tellg());
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09003106
3107 //std::cout << "sz = " << sz << "\n";
Paolo Jovone6601bf2018-07-07 20:43:33 +02003108 f.seekg(0, f.beg);
3109
Syoyo Fujitae8862472019-10-20 17:47:50 +09003110 if (int64_t(sz) < 0) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02003111 if (err) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003112 (*err) += "Invalid file size : " + filepath +
3113 " (does the path point to a directory?)";
Paolo Jovone6601bf2018-07-07 20:43:33 +02003114 }
3115 return false;
3116 } else if (sz == 0) {
3117 if (err) {
3118 (*err) += "File is empty : " + filepath + "\n";
3119 }
3120 return false;
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09003121 } else if (sz >= (std::numeric_limits<std::streamoff>::max)()) {
3122 if (err) {
3123 (*err) += "Invalid file size : " + filepath + "\n";
3124 }
3125 return false;
Paolo Jovone6601bf2018-07-07 20:43:33 +02003126 }
3127
3128 out->resize(sz);
3129 f.read(reinterpret_cast<char *>(&out->at(0)),
3130 static_cast<std::streamsize>(sz));
Paolo Jovone6601bf2018-07-07 20:43:33 +02003131
3132 return true;
Sascha Willems5f9cb242018-12-28 20:53:41 +01003133#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02003134}
3135
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003136bool WriteWholeFile(std::string *err, const std::string &filepath,
3137 const std::vector<unsigned char> &contents, void *) {
Harokyangfb256602019-10-30 16:13:52 +08003138#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09003139#if defined(__GLIBCXX__) // mingw
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09003140 int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(),
3141 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
3142 __gnu_cxx::stdio_filebuf<char> wfile_buf(
3143 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09003144 std::ostream f(&wfile_buf);
3145#elif defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08003146 std::ofstream f(UTF8ToWchar(filepath).c_str(), std::ofstream::binary);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09003147#else // clang?
Syoyo Fujita45cac782019-11-09 20:42:55 +09003148 std::ofstream f(filepath.c_str(), std::ofstream::binary);
3149#endif
Harokyangfb256602019-10-30 16:13:52 +08003150#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02003151 std::ofstream f(filepath.c_str(), std::ofstream::binary);
Harokyangfb256602019-10-30 16:13:52 +08003152#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02003153 if (!f) {
3154 if (err) {
3155 (*err) += "File open error for writing : " + filepath + "\n";
3156 }
3157 return false;
3158 }
3159
3160 f.write(reinterpret_cast<const char *>(&contents.at(0)),
3161 static_cast<std::streamsize>(contents.size()));
3162 if (!f) {
3163 if (err) {
3164 (*err) += "File write error: " + filepath + "\n";
3165 }
3166 return false;
3167 }
3168
Paolo Jovone6601bf2018-07-07 20:43:33 +02003169 return true;
3170}
3171
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003172#endif // TINYGLTF_NO_FS
Paolo Jovone6601bf2018-07-07 20:43:33 +02003173
johan bowald642a3432018-04-01 12:37:18 +02003174static std::string MimeToExt(const std::string &mimeType) {
3175 if (mimeType == "image/jpeg") {
3176 return "jpg";
3177 } else if (mimeType == "image/png") {
3178 return "png";
3179 } else if (mimeType == "image/bmp") {
3180 return "bmp";
3181 } else if (mimeType == "image/gif") {
3182 return "gif";
3183 }
3184
3185 return "";
3186}
3187
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08003188static bool UpdateImageObject(const Image &image, std::string &baseDir,
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08003189 int index, bool embedImages,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08003190 const URICallbacks *uri_cb,
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08003191 WriteImageDataFunction *WriteImageData,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08003192 void *user_data, std::string *out_uri) {
johan bowald642a3432018-04-01 12:37:18 +02003193 std::string filename;
3194 std::string ext;
imallettd9ce9eb2022-10-07 10:37:09 -07003195 // If image has uri, use it as a filename
johan bowald642a3432018-04-01 12:37:18 +02003196 if (image.uri.size()) {
Pyarelal Knowles385946d2023-01-03 18:18:04 -08003197 std::string decoded_uri;
3198 if (!uri_cb->decode(image.uri, &decoded_uri, uri_cb->user_data)) {
3199 // A decode failure results in a failure to write the gltf.
3200 return false;
3201 }
3202 filename = GetBaseFilename(decoded_uri);
johan bowald642a3432018-04-01 12:37:18 +02003203 ext = GetFilePathExtension(filename);
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09003204 } else if (image.bufferView != -1) {
3205 // If there's no URI and the data exists in a buffer,
3206 // don't change properties or write images
johan bowald642a3432018-04-01 12:37:18 +02003207 } else if (image.name.size()) {
3208 ext = MimeToExt(image.mimeType);
3209 // Otherwise use name as filename
3210 filename = image.name + "." + ext;
3211 } else {
3212 ext = MimeToExt(image.mimeType);
3213 // Fallback to index of image as filename
3214 filename = std::to_string(index) + "." + ext;
3215 }
3216
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08003217 // If callback is set and image data exists, modify image data object. If
3218 // image data does not exist, this is not considered a failure and the
3219 // original uri should be maintained.
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08003220 bool imageWritten = false;
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08003221 if (*WriteImageData != nullptr && !filename.empty() && !image.image.empty()) {
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08003222 imageWritten = (*WriteImageData)(&baseDir, &filename, &image, embedImages,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08003223 uri_cb, out_uri, user_data);
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08003224 if (!imageWritten) {
3225 return false;
3226 }
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08003227 }
3228
3229 // Use the original uri if the image was not written.
3230 if (!imageWritten) {
3231 *out_uri = image.uri;
johan bowald642a3432018-04-01 12:37:18 +02003232 }
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08003233
3234 return true;
johan bowald642a3432018-04-01 12:37:18 +02003235}
3236
Selmar Kok0d0e97e2018-08-22 14:01:57 +02003237bool IsDataURI(const std::string &in) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003238 std::string header = "data:application/octet-stream;base64,";
3239 if (in.find(header) == 0) {
3240 return true;
3241 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09003242
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02003243 header = "data:image/jpeg;base64,";
3244 if (in.find(header) == 0) {
3245 return true;
3246 }
Squareys43374632018-03-13 22:20:48 +01003247
Syoyo Fujita620eed12016-01-02 23:37:12 +09003248 header = "data:image/png;base64,";
3249 if (in.find(header) == 0) {
3250 return true;
3251 }
Squareys43374632018-03-13 22:20:48 +01003252
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02003253 header = "data:image/bmp;base64,";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003254 if (in.find(header) == 0) {
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02003255 return true;
3256 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09003257
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02003258 header = "data:image/gif;base64,";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003259 if (in.find(header) == 0) {
Syoyo Fujita620eed12016-01-02 23:37:12 +09003260 return true;
3261 }
3262
Luke San Antoniocaa24b02016-06-15 02:23:09 -04003263 header = "data:text/plain;base64,";
3264 if (in.find(header) == 0) {
3265 return true;
3266 }
3267
Syoyo Fujita20244e12018-03-15 11:01:05 -05003268 header = "data:application/gltf-buffer;base64,";
3269 if (in.find(header) == 0) {
3270 return true;
3271 }
3272
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003273 return false;
3274}
3275
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09003276bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
3277 const std::string &in, size_t reqBytes, bool checkSize) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003278 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujita620eed12016-01-02 23:37:12 +09003279 std::string data;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003280 if (in.find(header) == 0) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09003281 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003282 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09003283
3284 if (data.empty()) {
3285 header = "data:image/jpeg;base64,";
3286 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02003287 mime_type = "image/jpeg";
Syoyo Fujita7c877972016-03-08 01:31:49 +09003288 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09003289 }
3290 }
3291
3292 if (data.empty()) {
3293 header = "data:image/png;base64,";
3294 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02003295 mime_type = "image/png";
Syoyo Fujita7c877972016-03-08 01:31:49 +09003296 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09003297 }
3298 }
Squareys43374632018-03-13 22:20:48 +01003299
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02003300 if (data.empty()) {
3301 header = "data:image/bmp;base64,";
3302 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02003303 mime_type = "image/bmp";
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02003304 data = base64_decode(in.substr(header.size())); // cut mime string.
3305 }
3306 }
3307
3308 if (data.empty()) {
3309 header = "data:image/gif;base64,";
3310 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02003311 mime_type = "image/gif";
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02003312 data = base64_decode(in.substr(header.size())); // cut mime string.
3313 }
3314 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09003315
3316 if (data.empty()) {
Luke San Antoniocaa24b02016-06-15 02:23:09 -04003317 header = "data:text/plain;base64,";
3318 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02003319 mime_type = "text/plain";
Luke San Antoniocaa24b02016-06-15 02:23:09 -04003320 data = base64_decode(in.substr(header.size()));
3321 }
3322 }
3323
3324 if (data.empty()) {
Syoyo Fujita20244e12018-03-15 11:01:05 -05003325 header = "data:application/gltf-buffer;base64,";
3326 if (in.find(header) == 0) {
3327 data = base64_decode(in.substr(header.size()));
3328 }
3329 }
3330
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09003331 // TODO(syoyo): Allow empty buffer? #229
Syoyo Fujita20244e12018-03-15 11:01:05 -05003332 if (data.empty()) {
Syoyo Fujita620eed12016-01-02 23:37:12 +09003333 return false;
Syoyo Fujitabde70212016-02-07 17:38:17 +09003334 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09003335
3336 if (checkSize) {
3337 if (data.size() != reqBytes) {
3338 return false;
3339 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003340 out->resize(reqBytes);
Syoyo Fujita620eed12016-01-02 23:37:12 +09003341 } else {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003342 out->resize(data.size());
Syoyo Fujita620eed12016-01-02 23:37:12 +09003343 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003344 std::copy(data.begin(), data.end(), out->begin());
Syoyo Fujita620eed12016-01-02 23:37:12 +09003345 return true;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003346}
3347
David1f9a4b92023-02-15 22:56:18 -06003348namespace detail {
David03ad33c2023-02-15 23:35:51 -06003349bool GetInt(const detail::json &o, int &val) {
jrkoonce5cecc412019-08-29 11:45:04 -05003350#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003351 if (!o.IsDouble()) {
3352 if (o.IsInt()) {
3353 val = o.GetInt();
3354 return true;
3355 } else if (o.IsUint()) {
3356 val = static_cast<int>(o.GetUint());
3357 return true;
3358 } else if (o.IsInt64()) {
3359 val = static_cast<int>(o.GetInt64());
3360 return true;
3361 } else if (o.IsUint64()) {
3362 val = static_cast<int>(o.GetUint64());
jrkooncecba5d6c2019-08-29 11:26:22 -05003363 return true;
3364 }
jrkoonce5cecc412019-08-29 11:45:04 -05003365 }
3366
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003367 return false;
jrkoonce5cecc412019-08-29 11:45:04 -05003368#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003369 auto type = o.type();
jrkooncecba5d6c2019-08-29 11:26:22 -05003370
David03ad33c2023-02-15 23:35:51 -06003371 if ((type == detail::json::value_t::number_integer) ||
3372 (type == detail::json::value_t::number_unsigned)) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003373 val = static_cast<int>(o.get<int64_t>());
3374 return true;
jrkoonce5cecc412019-08-29 11:45:04 -05003375 }
3376
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003377 return false;
jrkoonce5cecc412019-08-29 11:45:04 -05003378#endif
jrkooncecba5d6c2019-08-29 11:26:22 -05003379}
3380
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003381#ifdef TINYGLTF_USE_RAPIDJSON
David03ad33c2023-02-15 23:35:51 -06003382bool GetDouble(const detail::json &o, double &val) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003383 if (o.IsDouble()) {
3384 val = o.GetDouble();
3385 return true;
3386 }
3387
3388 return false;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003389}
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003390#endif
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003391
David03ad33c2023-02-15 23:35:51 -06003392bool GetNumber(const detail::json &o, double &val) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003393#ifdef TINYGLTF_USE_RAPIDJSON
3394 if (o.IsNumber()) {
3395 val = o.GetDouble();
3396 return true;
3397 }
3398
3399 return false;
3400#else
3401 if (o.is_number()) {
3402 val = o.get<double>();
3403 return true;
3404 }
3405
3406 return false;
3407#endif
3408}
3409
David03ad33c2023-02-15 23:35:51 -06003410bool GetString(const detail::json &o, std::string &val) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003411#ifdef TINYGLTF_USE_RAPIDJSON
3412 if (o.IsString()) {
3413 val = o.GetString();
3414 return true;
3415 }
3416
3417 return false;
3418#else
David03ad33c2023-02-15 23:35:51 -06003419 if (o.type() == detail::json::value_t::string) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003420 val = o.get<std::string>();
3421 return true;
3422 }
3423
3424 return false;
3425#endif
3426}
3427
David03ad33c2023-02-15 23:35:51 -06003428bool IsArray(const detail::json &o) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003429#ifdef TINYGLTF_USE_RAPIDJSON
3430 return o.IsArray();
3431#else
3432 return o.is_array();
3433#endif
3434}
3435
David03ad33c2023-02-15 23:35:51 -06003436detail::json_const_array_iterator ArrayBegin(const detail::json &o) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003437#ifdef TINYGLTF_USE_RAPIDJSON
3438 return o.Begin();
3439#else
3440 return o.begin();
3441#endif
3442}
3443
David03ad33c2023-02-15 23:35:51 -06003444detail::json_const_array_iterator ArrayEnd(const detail::json &o) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003445#ifdef TINYGLTF_USE_RAPIDJSON
3446 return o.End();
3447#else
3448 return o.end();
3449#endif
3450}
3451
David03ad33c2023-02-15 23:35:51 -06003452bool IsObject(const detail::json &o) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003453#ifdef TINYGLTF_USE_RAPIDJSON
3454 return o.IsObject();
3455#else
3456 return o.is_object();
3457#endif
3458}
3459
David03ad33c2023-02-15 23:35:51 -06003460detail::json_const_iterator ObjectBegin(const detail::json &o) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003461#ifdef TINYGLTF_USE_RAPIDJSON
3462 return o.MemberBegin();
3463#else
3464 return o.begin();
3465#endif
3466}
3467
David03ad33c2023-02-15 23:35:51 -06003468detail::json_const_iterator ObjectEnd(const detail::json &o) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003469#ifdef TINYGLTF_USE_RAPIDJSON
3470 return o.MemberEnd();
3471#else
3472 return o.end();
3473#endif
3474}
3475
Rahul Sheth01d54382020-07-10 14:27:37 -04003476// Making this a const char* results in a pointer to a temporary when
3477// TINYGLTF_USE_RAPIDJSON is off.
David03ad33c2023-02-15 23:35:51 -06003478std::string GetKey(detail::json_const_iterator &it) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003479#ifdef TINYGLTF_USE_RAPIDJSON
3480 return it->name.GetString();
3481#else
3482 return it.key().c_str();
3483#endif
3484}
3485
David03ad33c2023-02-15 23:35:51 -06003486bool FindMember(const detail::json &o, const char *member, detail::json_const_iterator &it) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003487#ifdef TINYGLTF_USE_RAPIDJSON
3488 if (!o.IsObject()) {
3489 return false;
3490 }
3491 it = o.FindMember(member);
3492 return it != o.MemberEnd();
3493#else
3494 it = o.find(member);
3495 return it != o.end();
3496#endif
3497}
3498
David Siegelcfe64fb2023-06-07 15:18:38 +02003499bool FindMember(detail::json &o, const char *member, detail::json_iterator &it) {
3500#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
3512void Erase(detail::json & o, detail::json_iterator &it) {
3513#ifdef TINYGLTF_USE_RAPIDJSON
3514 o.EraseMember(it);
3515#else
3516 o.erase(it);
3517#endif
3518}
3519
3520bool IsEmpty(const detail::json & o) {
3521#ifdef TINYGLTF_USE_RAPIDJSON
David Siegela1a34cb2023-06-07 15:30:02 +02003522 return o.ObjectEmpty();
David Siegelcfe64fb2023-06-07 15:18:38 +02003523#else
3524 return o.empty();
3525#endif
3526}
3527
David03ad33c2023-02-15 23:35:51 -06003528const detail::json &GetValue(detail::json_const_iterator &it) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003529#ifdef TINYGLTF_USE_RAPIDJSON
3530 return it->value;
3531#else
3532 return it.value();
3533#endif
3534}
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003535
David Siegelcfe64fb2023-06-07 15:18:38 +02003536detail::json &GetValue(detail::json_iterator &it) {
3537#ifdef TINYGLTF_USE_RAPIDJSON
3538 return it->value;
3539#else
3540 return it.value();
3541#endif
3542}
3543
David03ad33c2023-02-15 23:35:51 -06003544std::string JsonToString(const detail::json &o, int spacing = -1) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003545#ifdef TINYGLTF_USE_RAPIDJSON
3546 using namespace rapidjson;
3547 StringBuffer buffer;
3548 if (spacing == -1) {
3549 Writer<StringBuffer> writer(buffer);
Syoyo Fujita544969b2022-07-13 19:03:33 +09003550 // TODO: Better error handling.
3551 // https://github.com/syoyo/tinygltf/issues/332
3552 if (!o.Accept(writer)) {
3553 return "tiny_gltf::JsonToString() failed rapidjson conversion";
3554 }
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003555 } else {
3556 PrettyWriter<StringBuffer> writer(buffer);
3557 writer.SetIndent(' ', uint32_t(spacing));
Syoyo Fujita544969b2022-07-13 19:03:33 +09003558 if (!o.Accept(writer)) {
3559 return "tiny_gltf::JsonToString() failed rapidjson conversion";
3560 }
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003561 }
3562 return buffer.GetString();
3563#else
3564 return o.dump(spacing);
3565#endif
3566}
3567
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003568} // namespace
3569
David03ad33c2023-02-15 23:35:51 -06003570static bool ParseJsonAsValue(Value *ret, const detail::json &o) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003571 Value val{};
jrkooncecba5d6c2019-08-29 11:26:22 -05003572#ifdef TINYGLTF_USE_RAPIDJSON
3573 using rapidjson::Type;
3574 switch (o.GetType()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003575 case Type::kObjectType: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003576 Value::Object value_object;
jrkooncecba5d6c2019-08-29 11:26:22 -05003577 for (auto it = o.MemberBegin(); it != o.MemberEnd(); ++it) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003578 Value entry;
jrkooncecba5d6c2019-08-29 11:26:22 -05003579 ParseJsonAsValue(&entry, it->value);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003580 if (entry.Type() != NULL_TYPE)
David1f9a4b92023-02-15 22:56:18 -06003581 value_object.emplace(detail::GetKey(it), std::move(entry));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003582 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003583 if (value_object.size() > 0) val = Value(std::move(value_object));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003584 } break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003585 case Type::kArrayType: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003586 Value::Array value_array;
jrkoonce5cecc412019-08-29 11:45:04 -05003587 value_array.reserve(o.Size());
jrkooncecba5d6c2019-08-29 11:26:22 -05003588 for (auto it = o.Begin(); it != o.End(); ++it) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003589 Value entry;
jrkooncecba5d6c2019-08-29 11:26:22 -05003590 ParseJsonAsValue(&entry, *it);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003591 if (entry.Type() != NULL_TYPE)
3592 value_array.emplace_back(std::move(entry));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003593 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003594 if (value_array.size() > 0) val = Value(std::move(value_array));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003595 } break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003596 case Type::kStringType:
3597 val = Value(std::string(o.GetString()));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003598 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003599 case Type::kFalseType:
3600 case Type::kTrueType:
3601 val = Value(o.GetBool());
Selmar Kokfa7022f2018-04-04 18:10:20 +02003602 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003603 case Type::kNumberType:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003604 if (!o.IsDouble()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003605 int i = 0;
David1f9a4b92023-02-15 22:56:18 -06003606 detail::GetInt(o, i);
jrkooncecba5d6c2019-08-29 11:26:22 -05003607 val = Value(i);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003608 } else {
jrkooncecba5d6c2019-08-29 11:26:22 -05003609 double d = 0.0;
David38318022023-02-15 23:21:09 -06003610 detail::GetDouble(o, d);
jrkooncecba5d6c2019-08-29 11:26:22 -05003611 val = Value(d);
3612 }
Selmar Kokfa7022f2018-04-04 18:10:20 +02003613 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003614 case Type::kNullType:
Selmar Kokfa7022f2018-04-04 18:10:20 +02003615 break;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003616 // all types are covered, so no `case default`
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003617 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003618#else
3619 switch (o.type()) {
David03ad33c2023-02-15 23:35:51 -06003620 case detail::json::value_t::object: {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003621 Value::Object value_object;
3622 for (auto it = o.begin(); it != o.end(); it++) {
3623 Value entry;
3624 ParseJsonAsValue(&entry, it.value());
3625 if (entry.Type() != NULL_TYPE)
3626 value_object.emplace(it.key(), std::move(entry));
3627 }
3628 if (value_object.size() > 0) val = Value(std::move(value_object));
3629 } break;
David03ad33c2023-02-15 23:35:51 -06003630 case detail::json::value_t::array: {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003631 Value::Array value_array;
3632 value_array.reserve(o.size());
3633 for (auto it = o.begin(); it != o.end(); it++) {
3634 Value entry;
3635 ParseJsonAsValue(&entry, it.value());
3636 if (entry.Type() != NULL_TYPE)
3637 value_array.emplace_back(std::move(entry));
3638 }
3639 if (value_array.size() > 0) val = Value(std::move(value_array));
3640 } break;
David03ad33c2023-02-15 23:35:51 -06003641 case detail::json::value_t::string:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003642 val = Value(o.get<std::string>());
3643 break;
David03ad33c2023-02-15 23:35:51 -06003644 case detail::json::value_t::boolean:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003645 val = Value(o.get<bool>());
3646 break;
David03ad33c2023-02-15 23:35:51 -06003647 case detail::json::value_t::number_integer:
3648 case detail::json::value_t::number_unsigned:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003649 val = Value(static_cast<int>(o.get<int64_t>()));
3650 break;
David03ad33c2023-02-15 23:35:51 -06003651 case detail::json::value_t::number_float:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003652 val = Value(o.get<double>());
3653 break;
David03ad33c2023-02-15 23:35:51 -06003654 case detail::json::value_t::null:
3655 case detail::json::value_t::discarded:
3656 case detail::json::value_t::binary:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003657 // default:
3658 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003659 }
3660#endif
Marcin Kacprzakf4f5c3c2022-09-02 16:15:54 +02003661 const bool isNotNull = val.Type() != NULL_TYPE;
3662
jrkooncecba5d6c2019-08-29 11:26:22 -05003663 if (ret) *ret = std::move(val);
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003664
Marcin Kacprzakf4f5c3c2022-09-02 16:15:54 +02003665 return isNotNull;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003666}
3667
David03ad33c2023-02-15 23:35:51 -06003668static bool ParseExtrasProperty(Value *ret, const detail::json &o) {
3669 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06003670 if (!detail::FindMember(o, "extras", it)) {
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003671 return false;
3672 }
3673
David1f9a4b92023-02-15 22:56:18 -06003674 return ParseJsonAsValue(ret, detail::GetValue(it));
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003675}
3676
David03ad33c2023-02-15 23:35:51 -06003677static bool ParseBooleanProperty(bool *ret, std::string *err, const detail::json &o,
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09003678 const std::string &property,
3679 const bool required,
3680 const std::string &parent_node = "") {
David03ad33c2023-02-15 23:35:51 -06003681 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06003682 if (!detail::FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003683 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003684 if (err) {
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09003685 (*err) += "'" + property + "' property is missing";
3686 if (!parent_node.empty()) {
3687 (*err) += " in " + parent_node;
3688 }
3689 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003690 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003691 }
3692 return false;
3693 }
3694
David1f9a4b92023-02-15 22:56:18 -06003695 auto &value = detail::GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003696
3697 bool isBoolean;
jrkoonce0d2b6ef2019-09-04 13:46:45 -05003698 bool boolValue = false;
jrkooncecba5d6c2019-08-29 11:26:22 -05003699#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003700 isBoolean = value.IsBool();
3701 if (isBoolean) {
3702 boolValue = value.GetBool();
3703 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003704#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003705 isBoolean = value.is_boolean();
3706 if (isBoolean) {
3707 boolValue = value.get<bool>();
3708 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003709#endif
3710 if (!isBoolean) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003711 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003712 if (err) {
3713 (*err) += "'" + property + "' property is not a bool type.\n";
3714 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003715 }
3716 return false;
3717 }
3718
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003719 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003720 (*ret) = boolValue;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003721 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003722
3723 return true;
3724}
3725
David03ad33c2023-02-15 23:35:51 -06003726static bool ParseIntegerProperty(int *ret, std::string *err, const detail::json &o,
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003727 const std::string &property,
3728 const bool required,
3729 const std::string &parent_node = "") {
David03ad33c2023-02-15 23:35:51 -06003730 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06003731 if (!detail::FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003732 if (required) {
3733 if (err) {
3734 (*err) += "'" + property + "' property is missing";
3735 if (!parent_node.empty()) {
3736 (*err) += " in " + parent_node;
3737 }
3738 (*err) += ".\n";
3739 }
3740 }
3741 return false;
3742 }
3743
jrkooncecba5d6c2019-08-29 11:26:22 -05003744 int intValue;
David1f9a4b92023-02-15 22:56:18 -06003745 bool isInt = detail::GetInt(detail::GetValue(it), intValue);
jrkooncecba5d6c2019-08-29 11:26:22 -05003746 if (!isInt) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003747 if (required) {
3748 if (err) {
3749 (*err) += "'" + property + "' property is not an integer type.\n";
3750 }
3751 }
3752 return false;
3753 }
3754
3755 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003756 (*ret) = intValue;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003757 }
3758
3759 return true;
3760}
3761
David03ad33c2023-02-15 23:35:51 -06003762static bool ParseUnsignedProperty(size_t *ret, std::string *err, const detail::json &o,
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003763 const std::string &property,
3764 const bool required,
3765 const std::string &parent_node = "") {
David03ad33c2023-02-15 23:35:51 -06003766 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06003767 if (!detail::FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003768 if (required) {
3769 if (err) {
3770 (*err) += "'" + property + "' property is missing";
3771 if (!parent_node.empty()) {
3772 (*err) += " in " + parent_node;
3773 }
3774 (*err) += ".\n";
3775 }
3776 }
3777 return false;
3778 }
3779
David1f9a4b92023-02-15 22:56:18 -06003780 auto &value = detail::GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003781
jrkoonce0d2b6ef2019-09-04 13:46:45 -05003782 size_t uValue = 0;
jrkooncecba5d6c2019-08-29 11:26:22 -05003783 bool isUValue;
3784#ifdef TINYGLTF_USE_RAPIDJSON
3785 isUValue = false;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003786 if (value.IsUint()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003787 uValue = value.GetUint();
3788 isUValue = true;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003789 } else if (value.IsUint64()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003790 uValue = value.GetUint64();
3791 isUValue = true;
3792 }
3793#else
3794 isUValue = value.is_number_unsigned();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003795 if (isUValue) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003796 uValue = value.get<size_t>();
3797 }
3798#endif
3799 if (!isUValue) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003800 if (required) {
3801 if (err) {
3802 (*err) += "'" + property + "' property is not a positive integer.\n";
3803 }
3804 }
3805 return false;
3806 }
3807
3808 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003809 (*ret) = uValue;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003810 }
3811
3812 return true;
3813}
3814
David03ad33c2023-02-15 23:35:51 -06003815static bool ParseNumberProperty(double *ret, std::string *err, const detail::json &o,
Syoyo Fujita5b407452017-06-04 17:42:41 +09003816 const std::string &property,
3817 const bool required,
3818 const std::string &parent_node = "") {
David03ad33c2023-02-15 23:35:51 -06003819 detail::json_const_iterator it;
jrkooncecba5d6c2019-08-29 11:26:22 -05003820
David1f9a4b92023-02-15 22:56:18 -06003821 if (!detail::FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003822 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003823 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003824 (*err) += "'" + property + "' property is missing";
3825 if (!parent_node.empty()) {
3826 (*err) += " in " + parent_node;
3827 }
3828 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003829 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003830 }
3831 return false;
3832 }
3833
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003834 double numberValue;
David1f9a4b92023-02-15 22:56:18 -06003835 bool isNumber = detail::GetNumber(detail::GetValue(it), numberValue);
jrkooncecba5d6c2019-08-29 11:26:22 -05003836
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003837 if (!isNumber) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003838 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003839 if (err) {
3840 (*err) += "'" + property + "' property is not a number type.\n";
3841 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003842 }
3843 return false;
3844 }
3845
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003846 if (ret) {
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003847 (*ret) = numberValue;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003848 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003849
3850 return true;
3851}
3852
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003853static bool ParseNumberArrayProperty(std::vector<double> *ret, std::string *err,
David03ad33c2023-02-15 23:35:51 -06003854 const detail::json &o, const std::string &property,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003855 bool required,
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003856 const std::string &parent_node = "") {
David03ad33c2023-02-15 23:35:51 -06003857 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06003858 if (!detail::FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003859 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003860 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003861 (*err) += "'" + property + "' property is missing";
3862 if (!parent_node.empty()) {
3863 (*err) += " in " + parent_node;
3864 }
3865 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003866 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003867 }
3868 return false;
3869 }
3870
David1f9a4b92023-02-15 22:56:18 -06003871 if (!detail::IsArray(detail::GetValue(it))) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003872 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003873 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003874 (*err) += "'" + property + "' property is not an array";
3875 if (!parent_node.empty()) {
3876 (*err) += " in " + parent_node;
3877 }
3878 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003879 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003880 }
3881 return false;
3882 }
3883
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003884 ret->clear();
David1f9a4b92023-02-15 22:56:18 -06003885 auto end = detail::ArrayEnd(detail::GetValue(it));
3886 for (auto i = detail::ArrayBegin(detail::GetValue(it)); i != end; ++i) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003887 double numberValue;
David1f9a4b92023-02-15 22:56:18 -06003888 const bool isNumber = detail::GetNumber(*i, numberValue);
jrkooncecba5d6c2019-08-29 11:26:22 -05003889 if (!isNumber) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003890 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003891 if (err) {
3892 (*err) += "'" + property + "' property is not a number.\n";
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003893 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 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003901 ret->push_back(numberValue);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003902 }
3903
3904 return true;
3905}
3906
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003907static bool ParseIntegerArrayProperty(std::vector<int> *ret, std::string *err,
David03ad33c2023-02-15 23:35:51 -06003908 const detail::json &o,
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003909 const std::string &property,
3910 bool required,
3911 const std::string &parent_node = "") {
David03ad33c2023-02-15 23:35:51 -06003912 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06003913 if (!detail::FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003914 if (required) {
3915 if (err) {
3916 (*err) += "'" + property + "' property is missing";
3917 if (!parent_node.empty()) {
3918 (*err) += " in " + parent_node;
3919 }
3920 (*err) += ".\n";
3921 }
3922 }
3923 return false;
3924 }
3925
David1f9a4b92023-02-15 22:56:18 -06003926 if (!detail::IsArray(detail::GetValue(it))) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003927 if (required) {
3928 if (err) {
3929 (*err) += "'" + property + "' property is not an array";
3930 if (!parent_node.empty()) {
3931 (*err) += " in " + parent_node;
3932 }
3933 (*err) += ".\n";
3934 }
3935 }
3936 return false;
3937 }
3938
3939 ret->clear();
David1f9a4b92023-02-15 22:56:18 -06003940 auto end = detail::ArrayEnd(detail::GetValue(it));
3941 for (auto i = detail::ArrayBegin(detail::GetValue(it)); i != end; ++i) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003942 int numberValue;
David1f9a4b92023-02-15 22:56:18 -06003943 bool isNumber = detail::GetInt(*i, numberValue);
jrkooncecba5d6c2019-08-29 11:26:22 -05003944 if (!isNumber) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003945 if (required) {
3946 if (err) {
3947 (*err) += "'" + property + "' property is not an integer type.\n";
3948 if (!parent_node.empty()) {
3949 (*err) += " in " + parent_node;
3950 }
3951 (*err) += ".\n";
3952 }
3953 }
3954 return false;
3955 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003956 ret->push_back(numberValue);
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003957 }
3958
3959 return true;
3960}
3961
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003962static bool ParseStringProperty(
David03ad33c2023-02-15 23:35:51 -06003963 std::string *ret, std::string *err, const detail::json &o,
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003964 const std::string &property, bool required,
3965 const std::string &parent_node = std::string()) {
David03ad33c2023-02-15 23:35:51 -06003966 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06003967 if (!detail::FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003968 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003969 if (err) {
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003970 (*err) += "'" + property + "' property is missing";
3971 if (parent_node.empty()) {
3972 (*err) += ".\n";
3973 } else {
3974 (*err) += " in `" + parent_node + "'.\n";
3975 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003976 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003977 }
3978 return false;
3979 }
3980
jrkooncecba5d6c2019-08-29 11:26:22 -05003981 std::string strValue;
David1f9a4b92023-02-15 22:56:18 -06003982 if (!detail::GetString(detail::GetValue(it), strValue)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003983 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003984 if (err) {
3985 (*err) += "'" + property + "' property is not a string type.\n";
3986 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003987 }
3988 return false;
3989 }
3990
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003991 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003992 (*ret) = std::move(strValue);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003993 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003994
3995 return true;
3996}
3997
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003998static bool ParseStringIntegerProperty(std::map<std::string, int> *ret,
David03ad33c2023-02-15 23:35:51 -06003999 std::string *err, const detail::json &o,
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004000 const std::string &property,
4001 bool required,
4002 const std::string &parent = "") {
David03ad33c2023-02-15 23:35:51 -06004003 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06004004 if (!detail::FindMember(o, property.c_str(), it)) {
Luke San Antonio19894c72016-06-14 21:19:51 -04004005 if (required) {
4006 if (err) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09004007 if (!parent.empty()) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004008 (*err) +=
4009 "'" + property + "' property is missing in " + parent + ".\n";
Syoyo Fujita57c10182017-10-19 18:48:26 +09004010 } else {
4011 (*err) += "'" + property + "' property is missing.\n";
4012 }
Luke San Antonio19894c72016-06-14 21:19:51 -04004013 }
4014 }
4015 return false;
4016 }
4017
David03ad33c2023-02-15 23:35:51 -06004018 const detail::json &dict = detail::GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05004019
Luke San Antonio19894c72016-06-14 21:19:51 -04004020 // Make sure we are dealing with an object / dictionary.
David1f9a4b92023-02-15 22:56:18 -06004021 if (!detail::IsObject(dict)) {
Luke San Antonio19894c72016-06-14 21:19:51 -04004022 if (required) {
4023 if (err) {
4024 (*err) += "'" + property + "' property is not an object.\n";
4025 }
4026 }
4027 return false;
4028 }
4029
4030 ret->clear();
Luke San Antonio19894c72016-06-14 21:19:51 -04004031
David03ad33c2023-02-15 23:35:51 -06004032 detail::json_const_iterator dictIt(detail::ObjectBegin(dict));
4033 detail::json_const_iterator dictItEnd(detail::ObjectEnd(dict));
Luke San Antonio19894c72016-06-14 21:19:51 -04004034
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09004035 for (; dictIt != dictItEnd; ++dictIt) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004036 int intVal;
David1f9a4b92023-02-15 22:56:18 -06004037 if (!detail::GetInt(detail::GetValue(dictIt), intVal)) {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09004038 if (required) {
4039 if (err) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004040 (*err) += "'" + property + "' value is not an integer type.\n";
Luke San Antonio19894c72016-06-14 21:19:51 -04004041 }
4042 }
4043 return false;
4044 }
4045
4046 // Insert into the list.
David1f9a4b92023-02-15 22:56:18 -06004047 (*ret)[detail::GetKey(dictIt)] = intVal;
Luke San Antonio19894c72016-06-14 21:19:51 -04004048 }
4049 return true;
4050}
4051
Syoyo Fujita5b407452017-06-04 17:42:41 +09004052static bool ParseJSONProperty(std::map<std::string, double> *ret,
David03ad33c2023-02-15 23:35:51 -06004053 std::string *err, const detail::json &o,
Syoyo Fujita5b407452017-06-04 17:42:41 +09004054 const std::string &property, bool required) {
David03ad33c2023-02-15 23:35:51 -06004055 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06004056 if (!detail::FindMember(o, property.c_str(), it)) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00004057 if (required) {
Syoyo Fujita5b407452017-06-04 17:42:41 +09004058 if (err) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00004059 (*err) += "'" + property + "' property is missing. \n'";
4060 }
4061 }
4062 return false;
4063 }
4064
David03ad33c2023-02-15 23:35:51 -06004065 const detail::json &obj = detail::GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05004066
David1f9a4b92023-02-15 22:56:18 -06004067 if (!detail::IsObject(obj)) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00004068 if (required) {
4069 if (err) {
4070 (*err) += "'" + property + "' property is not a JSON object.\n";
4071 }
4072 }
4073 return false;
4074 }
4075
4076 ret->clear();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004077
David03ad33c2023-02-15 23:35:51 -06004078 detail::json_const_iterator it2(detail::ObjectBegin(obj));
4079 detail::json_const_iterator itEnd(detail::ObjectEnd(obj));
jrkooncecba5d6c2019-08-29 11:26:22 -05004080 for (; it2 != itEnd; ++it2) {
4081 double numVal;
David1f9a4b92023-02-15 22:56:18 -06004082 if (detail::GetNumber(detail::GetValue(it2), numVal))
4083 ret->emplace(std::string(detail::GetKey(it2)), numVal);
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00004084 }
4085
4086 return true;
4087}
4088
Selmar09d2ff12018-03-15 17:30:42 +01004089static bool ParseParameterProperty(Parameter *param, std::string *err,
David03ad33c2023-02-15 23:35:51 -06004090 const detail::json &o, const std::string &prop,
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004091 bool required) {
Selmar09d2ff12018-03-15 17:30:42 +01004092 // A parameter value can either be a string or an array of either a boolean or
4093 // a number. Booleans of any kind aren't supported here. Granted, it
4094 // complicates the Parameter structure and breaks it semantically in the sense
4095 // that the client probably works off the assumption that if the string is
4096 // empty the vector is used, etc. Would a tagged union work?
4097 if (ParseStringProperty(&param->string_value, err, o, prop, false)) {
4098 // Found string property.
4099 return true;
4100 } else if (ParseNumberArrayProperty(&param->number_array, err, o, prop,
4101 false)) {
4102 // Found a number array.
4103 return true;
Ben Buzbeef6af2242018-04-25 15:13:05 -07004104 } else if (ParseNumberProperty(&param->number_value, err, o, prop, false)) {
Marcin Kacprzakb12a54e2022-09-02 16:13:11 +02004105 param->has_number_value = true;
4106 return true;
Selmar09d2ff12018-03-15 17:30:42 +01004107 } else if (ParseJSONProperty(&param->json_double_value, err, o, prop,
4108 false)) {
4109 return true;
4110 } else if (ParseBooleanProperty(&param->bool_value, err, o, prop, false)) {
4111 return true;
4112 } else {
4113 if (required) {
4114 if (err) {
4115 (*err) += "parameter must be a string or number / number array.\n";
4116 }
4117 }
4118 return false;
4119 }
4120}
4121
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004122static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err,
David03ad33c2023-02-15 23:35:51 -06004123 const detail::json &o) {
Syoyo Fujita48f6db02018-04-15 18:40:55 +09004124 (void)err;
4125
David03ad33c2023-02-15 23:35:51 -06004126 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06004127 if (!detail::FindMember(o, "extensions", it)) {
Selmar09d2ff12018-03-15 17:30:42 +01004128 return false;
4129 }
jrkooncecba5d6c2019-08-29 11:26:22 -05004130
David1f9a4b92023-02-15 22:56:18 -06004131 auto &obj = detail::GetValue(it);
4132 if (!detail::IsObject(obj)) {
Selmar09d2ff12018-03-15 17:30:42 +01004133 return false;
4134 }
4135 ExtensionMap extensions;
David03ad33c2023-02-15 23:35:51 -06004136 detail::json_const_iterator extIt = detail::ObjectBegin(obj); // it.value().begin();
4137 detail::json_const_iterator extEnd = detail::ObjectEnd(obj);
jrkooncecba5d6c2019-08-29 11:26:22 -05004138 for (; extIt != extEnd; ++extIt) {
David1f9a4b92023-02-15 22:56:18 -06004139 auto &itObj = detail::GetValue(extIt);
4140 if (!detail::IsObject(itObj)) continue;
4141 std::string key(detail::GetKey(extIt));
jrkooncecba5d6c2019-08-29 11:26:22 -05004142 if (!ParseJsonAsValue(&extensions[key], itObj)) {
jrkoonce06c30c42019-09-03 15:56:48 -05004143 if (!key.empty()) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004144 // create empty object so that an extension object is still of type
4145 // object
jrkooncecba5d6c2019-08-29 11:26:22 -05004146 extensions[key] = Value{Value::Object{}};
Selmar Kokee3d0662018-10-08 16:20:43 +02004147 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004148 }
Selmar09d2ff12018-03-15 17:30:42 +01004149 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004150 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004151 (*ret) = std::move(extensions);
Selmar09d2ff12018-03-15 17:30:42 +01004152 }
4153 return true;
4154}
4155
David Siegel22cafa12023-06-05 22:18:59 +02004156template <typename GltfType>
4157static bool ParseExtrasAndExtensions(GltfType * target, std::string *err,
4158 const detail::json & o, bool store_json_strings) {
Syoyo Fujita7a570c82023-06-19 21:52:13 +09004159
David Siegel22cafa12023-06-05 22:18:59 +02004160 ParseExtensionsProperty(&target->extensions, err, o);
4161 ParseExtrasProperty(&target->extras, o);
4162
4163 if (store_json_strings) {
4164 {
4165 detail::json_const_iterator it;
4166 if (detail::FindMember(o, "extensions", it)) {
4167 target->extensions_json_string = detail::JsonToString(detail::GetValue(it));
4168 }
4169 }
4170 {
4171 detail::json_const_iterator it;
4172 if (detail::FindMember(o, "extras", it)) {
4173 target->extras_json_string = detail::JsonToString(detail::GetValue(it));
4174 }
4175 }
4176 }
4177 return true;
4178}
4179
David03ad33c2023-02-15 23:35:51 -06004180static bool ParseAsset(Asset *asset, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004181 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09004182 ParseStringProperty(&asset->version, err, o, "version", true, "Asset");
4183 ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset");
4184 ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset");
Miguel Sousa22bfc842020-02-20 14:16:58 +01004185 ParseStringProperty(&asset->copyright, err, o, "copyright", false, "Asset");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004186
David Siegel22cafa12023-06-05 22:18:59 +02004187 ParseExtrasAndExtensions(asset, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004188 return true;
4189}
4190
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004191static bool ParseImage(Image *image, const int image_idx, std::string *err,
David03ad33c2023-02-15 23:35:51 -06004192 std::string *warn, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004193 bool store_original_json_for_extras_and_extensions,
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09004194 const std::string &basedir, const size_t max_file_size, FsCallbacks *fs,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08004195 const URICallbacks *uri_cb,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004196 LoadImageDataFunction *LoadImageData = nullptr,
Paolo Jovone6601bf2018-07-07 20:43:33 +02004197 void *load_image_user_data = nullptr) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004198 // A glTF image must either reference a bufferView or an image uri
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004199
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004200 // schema says oneOf [`bufferView`, `uri`]
4201 // TODO(syoyo): Check the type of each parameters.
David03ad33c2023-02-15 23:35:51 -06004202 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06004203 bool hasBufferView = detail::FindMember(o, "bufferView", it);
4204 bool hasURI = detail::FindMember(o, "uri", it);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004205
Syoyo Fujitaaf3ebb22019-01-06 18:55:57 +09004206 ParseStringProperty(&image->name, err, o, "name", false);
4207
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004208 if (hasBufferView && hasURI) {
4209 // Should not both defined.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004210 if (err) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09004211 (*err) +=
4212 "Only one of `bufferView` or `uri` should be defined, but both are "
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004213 "defined for image[" +
4214 std::to_string(image_idx) + "] name = \"" + image->name + "\"\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004215 }
4216 return false;
4217 }
4218
4219 if (!hasBufferView && !hasURI) {
4220 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004221 (*err) += "Neither required `bufferView` nor `uri` defined for image[" +
4222 std::to_string(image_idx) + "] name = \"" + image->name +
4223 "\"\n";
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004224 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004225 return false;
4226 }
4227
David Siegel22cafa12023-06-05 22:18:59 +02004228 ParseExtrasAndExtensions(image, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004229
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004230 if (hasBufferView) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004231 int bufferView = -1;
4232 if (!ParseIntegerProperty(&bufferView, err, o, "bufferView", true)) {
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004233 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004234 (*err) += "Failed to parse `bufferView` for image[" +
4235 std::to_string(image_idx) + "] name = \"" + image->name +
4236 "\"\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004237 }
4238 return false;
4239 }
4240
4241 std::string mime_type;
4242 ParseStringProperty(&mime_type, err, o, "mimeType", false);
4243
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004244 int width = 0;
4245 ParseIntegerProperty(&width, err, o, "width", false);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004246
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004247 int height = 0;
4248 ParseIntegerProperty(&height, err, o, "height", false);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004249
4250 // Just only save some information here. Loading actual image data from
4251 // bufferView is done after this `ParseImage` function.
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004252 image->bufferView = bufferView;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004253 image->mimeType = mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004254 image->width = width;
4255 image->height = height;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004256
4257 return true;
4258 }
4259
Syoyo Fujita246654a2018-03-21 20:32:22 +09004260 // Parse URI & Load image data.
4261
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004262 std::string uri;
4263 std::string tmp_err;
Syoyo Fujita246654a2018-03-21 20:32:22 +09004264 if (!ParseStringProperty(&uri, &tmp_err, o, "uri", true)) {
4265 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004266 (*err) += "Failed to parse `uri` for image[" + std::to_string(image_idx) +
4267 "] name = \"" + image->name + "\".\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004268 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09004269 return false;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004270 }
4271
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004272 std::vector<unsigned char> img;
Syoyo Fujitabeded612016-05-01 20:03:43 +09004273
Syoyo Fujita246654a2018-03-21 20:32:22 +09004274 if (IsDataURI(uri)) {
johan bowald642a3432018-04-01 12:37:18 +02004275 if (!DecodeDataURI(&img, image->mimeType, uri, 0, false)) {
Syoyo Fujita246654a2018-03-21 20:32:22 +09004276 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004277 (*err) += "Failed to decode 'uri' for image[" +
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09004278 std::to_string(image_idx) + "] name = \"" + image->name +
4279 "\"\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004280 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09004281 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004282 }
4283 } else {
Syoyo Fujita246654a2018-03-21 20:32:22 +09004284 // Assume external file
4285 // Keep texture path (for textures that cannot be decoded)
4286 image->uri = uri;
Selmar67af3c92018-03-16 11:48:19 +01004287#ifdef TINYGLTF_NO_EXTERNAL_IMAGE
Syoyo Fujita246654a2018-03-21 20:32:22 +09004288 return true;
AlvaroBarua43172232022-09-11 00:41:43 +01004289#else
Pyarelal Knowles385946d2023-01-03 18:18:04 -08004290 std::string decoded_uri;
4291 if (!uri_cb->decode(uri, &decoded_uri, uri_cb->user_data)) {
4292 if (warn) {
4293 (*warn) += "Failed to decode 'uri' for image[" +
Syoyo Fujita877d8562023-04-23 21:47:31 +09004294 std::to_string(image_idx) + "] name = \"" + image->name +
4295 "\"\n";
Pyarelal Knowles385946d2023-01-03 18:18:04 -08004296 }
4297
4298 // Image loading failure is not critical to overall gltf loading.
4299 return true;
4300 }
4301
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004302 if (!LoadExternalFile(&img, err, warn, decoded_uri, basedir,
4303 /* required */ false, /* required bytes */ 0,
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09004304 /* checksize */ false, /* max file size */ max_file_size, fs)) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004305 if (warn) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004306 (*warn) += "Failed to load external 'uri' for image[" +
Syoyo Fujita877d8562023-04-23 21:47:31 +09004307 std::to_string(image_idx) + "] name = \"" + decoded_uri +
4308 "\"\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004309 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09004310 // If the image cannot be loaded, keep uri as image->uri.
4311 return true;
4312 }
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09004313
Syoyo Fujita246654a2018-03-21 20:32:22 +09004314 if (img.empty()) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004315 if (warn) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004316 (*warn) += "Image data is empty for image[" +
Syoyo Fujita877d8562023-04-23 21:47:31 +09004317 std::to_string(image_idx) + "] name = \"" + image->name +
4318 "\" \n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09004319 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09004320 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004321 }
AlvaroBarua43172232022-09-11 00:41:43 +01004322#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004323 }
4324
Squareysff644d82018-03-13 22:36:18 +01004325 if (*LoadImageData == nullptr) {
4326 if (err) {
4327 (*err) += "No LoadImageData callback specified.\n";
4328 }
4329 return false;
4330 }
Syoyo Fujitaaf3ebb22019-01-06 18:55:57 +09004331 return (*LoadImageData)(image, image_idx, err, warn, 0, 0, &img.at(0),
Paolo Jovone6601bf2018-07-07 20:43:33 +02004332 static_cast<int>(img.size()), load_image_user_data);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004333}
4334
David03ad33c2023-02-15 23:35:51 -06004335static bool ParseTexture(Texture *texture, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004336 bool store_original_json_for_extras_and_extensions,
Syoyo Fujitabeded612016-05-01 20:03:43 +09004337 const std::string &basedir) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004338 (void)basedir;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004339 int sampler = -1;
4340 int source = -1;
4341 ParseIntegerProperty(&sampler, err, o, "sampler", false);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004342
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004343 ParseIntegerProperty(&source, err, o, "source", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09004344
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004345 texture->sampler = sampler;
4346 texture->source = source;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07004347
David Siegel22cafa12023-06-05 22:18:59 +02004348 ParseExtrasAndExtensions(texture, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004349
Vladimír Vondruš239be2c2018-07-24 23:23:56 +02004350 ParseStringProperty(&texture->name, err, o, "name", false);
4351
Syoyo Fujitabde70212016-02-07 17:38:17 +09004352 return true;
4353}
4354
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004355static bool ParseTextureInfo(
David03ad33c2023-02-15 23:35:51 -06004356 TextureInfo *texinfo, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004357 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004358 if (texinfo == nullptr) {
4359 return false;
4360 }
4361
4362 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
4363 /* required */ true, "TextureInfo")) {
4364 return false;
4365 }
4366
4367 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
4368
David Siegel22cafa12023-06-05 22:18:59 +02004369 ParseExtrasAndExtensions(texinfo, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004370
Syoyo Fujita046400b2019-07-24 19:26:48 +09004371 return true;
4372}
4373
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004374static bool ParseNormalTextureInfo(
David03ad33c2023-02-15 23:35:51 -06004375 NormalTextureInfo *texinfo, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004376 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004377 if (texinfo == nullptr) {
4378 return false;
4379 }
4380
4381 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
4382 /* required */ true, "NormalTextureInfo")) {
4383 return false;
4384 }
4385
4386 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
4387 ParseNumberProperty(&texinfo->scale, err, o, "scale", false);
4388
David Siegel22cafa12023-06-05 22:18:59 +02004389 ParseExtrasAndExtensions(texinfo, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004390
Syoyo Fujita046400b2019-07-24 19:26:48 +09004391 return true;
4392}
4393
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004394static bool ParseOcclusionTextureInfo(
David03ad33c2023-02-15 23:35:51 -06004395 OcclusionTextureInfo *texinfo, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004396 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004397 if (texinfo == nullptr) {
4398 return false;
4399 }
4400
4401 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
4402 /* required */ true, "NormalTextureInfo")) {
4403 return false;
4404 }
4405
4406 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
4407 ParseNumberProperty(&texinfo->strength, err, o, "strength", false);
4408
David Siegel22cafa12023-06-05 22:18:59 +02004409 ParseExtrasAndExtensions(texinfo, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004410
Syoyo Fujita046400b2019-07-24 19:26:48 +09004411 return true;
4412}
4413
David03ad33c2023-02-15 23:35:51 -06004414static bool ParseBuffer(Buffer *buffer, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004415 bool store_original_json_for_extras_and_extensions,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08004416 FsCallbacks *fs, const URICallbacks *uri_cb,
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09004417 const std::string &basedir, const size_t max_buffer_size, bool is_binary = false,
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09004418 const unsigned char *bin_data = nullptr,
Syoyo Fujitabeded612016-05-01 20:03:43 +09004419 size_t bin_size = 0) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004420 size_t byteLength;
4421 if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true,
4422 "Buffer")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004423 return false;
4424 }
4425
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004426 // In glTF 2.0, uri is not mandatory anymore
Syoyo Fujita20244e12018-03-15 11:01:05 -05004427 buffer->uri.clear();
4428 ParseStringProperty(&buffer->uri, err, o, "uri", false, "Buffer");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004429
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004430 // having an empty uri for a non embedded image should not be valid
Syoyo Fujita20244e12018-03-15 11:01:05 -05004431 if (!is_binary && buffer->uri.empty()) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004432 if (err) {
4433 (*err) += "'uri' is missing from non binary glTF file buffer.\n";
4434 }
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004435 }
4436
David03ad33c2023-02-15 23:35:51 -06004437 detail::json_const_iterator type;
David1f9a4b92023-02-15 22:56:18 -06004438 if (detail::FindMember(o, "type", type)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004439 std::string typeStr;
David1f9a4b92023-02-15 22:56:18 -06004440 if (detail::GetString(detail::GetValue(type), typeStr)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004441 if (typeStr.compare("arraybuffer") == 0) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004442 // buffer.type = "arraybuffer";
4443 }
4444 }
4445 }
4446
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004447 if (is_binary) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004448 // Still binary glTF accepts external dataURI.
Syoyo Fujita20244e12018-03-15 11:01:05 -05004449 if (!buffer->uri.empty()) {
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004450 // First try embedded data URI.
4451 if (IsDataURI(buffer->uri)) {
4452 std::string mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004453 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004454 true)) {
4455 if (err) {
4456 (*err) +=
4457 "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
4458 }
4459 return false;
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004460 }
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004461 } else {
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004462 // External .bin file.
Pyarelal Knowles385946d2023-01-03 18:18:04 -08004463 std::string decoded_uri;
4464 if (!uri_cb->decode(buffer->uri, &decoded_uri, uri_cb->user_data)) {
4465 return false;
4466 }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09004467 if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr,
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004468 decoded_uri, basedir, /* required */ true,
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09004469 byteLength, /* checkSize */ true, /* max_file_size */max_buffer_size, fs)) {
Vladimír Vondrušfd84ceb2018-08-16 21:07:56 +02004470 return false;
4471 }
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004472 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004473 } else {
Syoyo Fujitabeded612016-05-01 20:03:43 +09004474 // load data from (embedded) binary data
4475
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09004476 if ((bin_size == 0) || (bin_data == nullptr)) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09004477 if (err) {
Syoyo Fujitac670f082022-09-17 19:52:25 +09004478 (*err) += "Invalid binary data in `Buffer', or GLB with empty BIN chunk.\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09004479 }
4480 return false;
4481 }
4482
4483 if (byteLength > bin_size) {
4484 if (err) {
4485 std::stringstream ss;
4486 ss << "Invalid `byteLength'. Must be equal or less than binary size: "
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004487 "`byteLength' = "
4488 << byteLength << ", binary size = " << bin_size << std::endl;
Syoyo Fujitabeded612016-05-01 20:03:43 +09004489 (*err) += ss.str();
4490 }
4491 return false;
4492 }
4493
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004494 // Read buffer data
4495 buffer->data.resize(static_cast<size_t>(byteLength));
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004496 memcpy(&(buffer->data.at(0)), bin_data, static_cast<size_t>(byteLength));
Syoyo Fujitabeded612016-05-01 20:03:43 +09004497 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004498
4499 } else {
Syoyo Fujita20244e12018-03-15 11:01:05 -05004500 if (IsDataURI(buffer->uri)) {
johan bowaldef151a42018-04-01 13:44:48 +02004501 std::string mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004502 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
4503 true)) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004504 if (err) {
Syoyo Fujita20244e12018-03-15 11:01:05 -05004505 (*err) += "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004506 }
4507 return false;
4508 }
4509 } else {
4510 // Assume external .bin file.
Pyarelal Knowles385946d2023-01-03 18:18:04 -08004511 std::string decoded_uri;
4512 if (!uri_cb->decode(buffer->uri, &decoded_uri, uri_cb->user_data)) {
4513 return false;
4514 }
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004515 if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, decoded_uri,
4516 basedir, /* required */ true, byteLength,
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09004517 /* checkSize */ true, /* max file size */max_buffer_size, fs)) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004518 return false;
4519 }
4520 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004521 }
4522
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004523 ParseStringProperty(&buffer->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004524
David Siegel22cafa12023-06-05 22:18:59 +02004525 ParseExtrasAndExtensions(buffer, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004526
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004527 return true;
4528}
4529
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004530static bool ParseBufferView(
David03ad33c2023-02-15 23:35:51 -06004531 BufferView *bufferView, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004532 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004533 int buffer = -1;
4534 if (!ParseIntegerProperty(&buffer, err, o, "buffer", true, "BufferView")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004535 return false;
4536 }
4537
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004538 size_t byteOffset = 0;
4539 ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004540
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004541 size_t byteLength = 1;
4542 if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true,
4543 "BufferView")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004544 return false;
4545 }
4546
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004547 size_t byteStride = 0;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004548 if (!ParseUnsignedProperty(&byteStride, err, o, "byteStride", false)) {
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004549 // Spec says: When byteStride of referenced bufferView is not defined, it
4550 // means that accessor elements are tightly packed, i.e., effective stride
4551 // equals the size of the element.
4552 // We cannot determine the actual byteStride until Accessor are parsed, thus
4553 // set 0(= tightly packed) here(as done in OpenGL's VertexAttribPoiner)
4554 byteStride = 0;
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004555 }
4556
4557 if ((byteStride > 252) || ((byteStride % 4) != 0)) {
4558 if (err) {
4559 std::stringstream ss;
4560 ss << "Invalid `byteStride' value. `byteStride' must be the multiple of "
4561 "4 : "
4562 << byteStride << std::endl;
4563
4564 (*err) += ss.str();
4565 }
4566 return false;
4567 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004568
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004569 int target = 0;
4570 ParseIntegerProperty(&target, err, o, "target", false);
4571 if ((target == TINYGLTF_TARGET_ARRAY_BUFFER) ||
4572 (target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004573 // OK
4574 } else {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004575 target = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004576 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004577 bufferView->target = target;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004578
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004579 ParseStringProperty(&bufferView->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004580
David Siegel22cafa12023-06-05 22:18:59 +02004581 ParseExtrasAndExtensions(bufferView, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004582
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004583 bufferView->buffer = buffer;
4584 bufferView->byteOffset = byteOffset;
4585 bufferView->byteLength = byteLength;
4586 bufferView->byteStride = byteStride;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004587 return true;
4588}
4589
David Siegeld852f502023-06-05 23:28:05 +02004590static bool ParseSparseAccessor(Accessor::Sparse *sparse, std::string *err,
4591 const detail::json &o,
4592 bool store_original_json_for_extras_and_extensions) {
4593 sparse->isSparse = true;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004594
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004595 int count = 0;
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004596 if (!ParseIntegerProperty(&count, err, o, "count", true, "SparseAccessor")) {
4597 return false;
4598 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004599
David Siegeld852f502023-06-05 23:28:05 +02004600 ParseExtrasAndExtensions(sparse, err, o, store_original_json_for_extras_and_extensions);
4601
David03ad33c2023-02-15 23:35:51 -06004602 detail::json_const_iterator indices_iterator;
4603 detail::json_const_iterator values_iterator;
David1f9a4b92023-02-15 22:56:18 -06004604 if (!detail::FindMember(o, "indices", indices_iterator)) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004605 (*err) = "the sparse object of this accessor doesn't have indices";
4606 return false;
4607 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004608
David1f9a4b92023-02-15 22:56:18 -06004609 if (!detail::FindMember(o, "values", values_iterator)) {
imallettd9ce9eb2022-10-07 10:37:09 -07004610 (*err) = "the sparse object of this accessor doesn't have values";
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004611 return false;
4612 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004613
David03ad33c2023-02-15 23:35:51 -06004614 const detail::json &indices_obj = detail::GetValue(indices_iterator);
4615 const detail::json &values_obj = detail::GetValue(values_iterator);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004616
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004617 int indices_buffer_view = 0, indices_byte_offset = 0, component_type = 0;
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09004618 if (!ParseIntegerProperty(&indices_buffer_view, err, indices_obj,
4619 "bufferView", true, "SparseAccessor")) {
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004620 return false;
4621 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004622 ParseIntegerProperty(&indices_byte_offset, err, indices_obj, "byteOffset",
Jack Mousseau94715172022-02-24 14:25:37 -08004623 false);
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004624 if (!ParseIntegerProperty(&component_type, err, indices_obj, "componentType",
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09004625 true, "SparseAccessor")) {
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004626 return false;
4627 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004628
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004629 int values_buffer_view = 0, values_byte_offset = 0;
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004630 if (!ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView",
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09004631 true, "SparseAccessor")) {
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004632 return false;
4633 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004634 ParseIntegerProperty(&values_byte_offset, err, values_obj, "byteOffset",
Jack Mousseau94715172022-02-24 14:25:37 -08004635 false);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004636
David Siegeld852f502023-06-05 23:28:05 +02004637 sparse->count = count;
4638 sparse->indices.bufferView = indices_buffer_view;
4639 sparse->indices.byteOffset = indices_byte_offset;
4640 sparse->indices.componentType = component_type;
4641 ParseExtrasAndExtensions(&sparse->indices, err, indices_obj,
4642 store_original_json_for_extras_and_extensions);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004643
David Siegeld852f502023-06-05 23:28:05 +02004644 sparse->values.bufferView = values_buffer_view;
4645 sparse->values.byteOffset = values_byte_offset;
4646 ParseExtrasAndExtensions(&sparse->values, err, values_obj,
4647 store_original_json_for_extras_and_extensions);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004648
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004649 return true;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004650}
4651
David03ad33c2023-02-15 23:35:51 -06004652static bool ParseAccessor(Accessor *accessor, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004653 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004654 int bufferView = -1;
4655 ParseIntegerProperty(&bufferView, err, o, "bufferView", false, "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004656
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004657 size_t byteOffset = 0;
4658 ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false, "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004659
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004660 bool normalized = false;
4661 ParseBooleanProperty(&normalized, err, o, "normalized", false, "Accessor");
4662
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004663 size_t componentType = 0;
4664 if (!ParseUnsignedProperty(&componentType, err, o, "componentType", true,
4665 "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004666 return false;
4667 }
4668
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004669 size_t count = 0;
4670 if (!ParseUnsignedProperty(&count, err, o, "count", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004671 return false;
4672 }
4673
4674 std::string type;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09004675 if (!ParseStringProperty(&type, err, o, "type", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004676 return false;
4677 }
4678
4679 if (type.compare("SCALAR") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004680 accessor->type = TINYGLTF_TYPE_SCALAR;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004681 } else if (type.compare("VEC2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004682 accessor->type = TINYGLTF_TYPE_VEC2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004683 } else if (type.compare("VEC3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004684 accessor->type = TINYGLTF_TYPE_VEC3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004685 } else if (type.compare("VEC4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004686 accessor->type = TINYGLTF_TYPE_VEC4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004687 } else if (type.compare("MAT2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004688 accessor->type = TINYGLTF_TYPE_MAT2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004689 } else if (type.compare("MAT3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004690 accessor->type = TINYGLTF_TYPE_MAT3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004691 } else if (type.compare("MAT4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004692 accessor->type = TINYGLTF_TYPE_MAT4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004693 } else {
4694 std::stringstream ss;
4695 ss << "Unsupported `type` for accessor object. Got \"" << type << "\"\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004696 if (err) {
4697 (*err) += ss.str();
4698 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004699 return false;
4700 }
4701
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004702 ParseStringProperty(&accessor->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004703
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004704 accessor->minValues.clear();
4705 accessor->maxValues.clear();
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004706 ParseNumberArrayProperty(&accessor->minValues, err, o, "min", false,
4707 "Accessor");
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004708
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004709 ParseNumberArrayProperty(&accessor->maxValues, err, o, "max", false,
4710 "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004711
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004712 accessor->count = count;
4713 accessor->bufferView = bufferView;
4714 accessor->byteOffset = byteOffset;
jianghaosen4748ad62017-08-29 20:08:37 +08004715 accessor->normalized = normalized;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004716 {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004717 if (componentType >= TINYGLTF_COMPONENT_TYPE_BYTE &&
4718 componentType <= TINYGLTF_COMPONENT_TYPE_DOUBLE) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004719 // OK
Arthur Brainville (Ybalrid)811e1d32019-06-15 07:32:38 +02004720 accessor->componentType = int(componentType);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004721 } else {
4722 std::stringstream ss;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004723 ss << "Invalid `componentType` in accessor. Got " << componentType
4724 << "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004725 if (err) {
4726 (*err) += ss.str();
4727 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004728 return false;
4729 }
4730 }
4731
David Siegel22cafa12023-06-05 22:18:59 +02004732 ParseExtrasAndExtensions(accessor, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004733
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004734 // check if accessor has a "sparse" object:
David03ad33c2023-02-15 23:35:51 -06004735 detail::json_const_iterator iterator;
David1f9a4b92023-02-15 22:56:18 -06004736 if (detail::FindMember(o, "sparse", iterator)) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004737 // here this accessor has a "sparse" subobject
David Siegeld852f502023-06-05 23:28:05 +02004738 return ParseSparseAccessor(&accessor->sparse, err, detail::GetValue(iterator),
4739 store_original_json_for_extras_and_extensions);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004740 }
4741
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004742 return true;
4743}
4744
Alex Wood7319db72019-01-24 15:38:16 -05004745#ifdef TINYGLTF_ENABLE_DRACO
4746
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004747static void DecodeIndexBuffer(draco::Mesh *mesh, size_t componentSize,
4748 std::vector<uint8_t> &outBuffer) {
4749 if (componentSize == 4) {
Alex Wood7319db72019-01-24 15:38:16 -05004750 assert(sizeof(mesh->face(draco::FaceIndex(0))[0]) == componentSize);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004751 memcpy(outBuffer.data(), &mesh->face(draco::FaceIndex(0))[0],
4752 outBuffer.size());
4753 } else {
Alex Wood7319db72019-01-24 15:38:16 -05004754 size_t faceStride = componentSize * 3;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004755 for (draco::FaceIndex f(0); f < mesh->num_faces(); ++f) {
4756 const draco::Mesh::Face &face = mesh->face(f);
4757 if (componentSize == 2) {
4758 uint16_t indices[3] = {(uint16_t)face[0].value(),
4759 (uint16_t)face[1].value(),
4760 (uint16_t)face[2].value()};
4761 memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
4762 faceStride);
4763 } else {
4764 uint8_t indices[3] = {(uint8_t)face[0].value(),
4765 (uint8_t)face[1].value(),
4766 (uint8_t)face[2].value()};
4767 memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
4768 faceStride);
Alex Wood7319db72019-01-24 15:38:16 -05004769 }
4770 }
4771 }
4772}
4773
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004774template <typename T>
4775static bool GetAttributeForAllPoints(draco::Mesh *mesh,
4776 const draco::PointAttribute *pAttribute,
4777 std::vector<uint8_t> &outBuffer) {
Alex Wood7319db72019-01-24 15:38:16 -05004778 size_t byteOffset = 0;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004779 T values[4] = {0, 0, 0, 0};
4780 for (draco::PointIndex i(0); i < mesh->num_points(); ++i) {
Alex Wood7319db72019-01-24 15:38:16 -05004781 const draco::AttributeValueIndex val_index = pAttribute->mapped_index(i);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004782 if (!pAttribute->ConvertValue<T>(val_index, pAttribute->num_components(),
4783 values))
Alex Wood7319db72019-01-24 15:38:16 -05004784 return false;
4785
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004786 memcpy(outBuffer.data() + byteOffset, &values[0],
4787 sizeof(T) * pAttribute->num_components());
Alex Wood7319db72019-01-24 15:38:16 -05004788 byteOffset += sizeof(T) * pAttribute->num_components();
4789 }
4790
4791 return true;
4792}
4793
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004794static bool GetAttributeForAllPoints(uint32_t componentType, draco::Mesh *mesh,
4795 const draco::PointAttribute *pAttribute,
4796 std::vector<uint8_t> &outBuffer) {
Alex Wood7319db72019-01-24 15:38:16 -05004797 bool decodeResult = false;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004798 switch (componentType) {
4799 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
4800 decodeResult =
4801 GetAttributeForAllPoints<uint8_t>(mesh, pAttribute, outBuffer);
4802 break;
4803 case TINYGLTF_COMPONENT_TYPE_BYTE:
4804 decodeResult =
4805 GetAttributeForAllPoints<int8_t>(mesh, pAttribute, outBuffer);
4806 break;
4807 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
4808 decodeResult =
4809 GetAttributeForAllPoints<uint16_t>(mesh, pAttribute, outBuffer);
4810 break;
4811 case TINYGLTF_COMPONENT_TYPE_SHORT:
4812 decodeResult =
4813 GetAttributeForAllPoints<int16_t>(mesh, pAttribute, outBuffer);
4814 break;
4815 case TINYGLTF_COMPONENT_TYPE_INT:
4816 decodeResult =
4817 GetAttributeForAllPoints<int32_t>(mesh, pAttribute, outBuffer);
4818 break;
4819 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
4820 decodeResult =
4821 GetAttributeForAllPoints<uint32_t>(mesh, pAttribute, outBuffer);
4822 break;
4823 case TINYGLTF_COMPONENT_TYPE_FLOAT:
4824 decodeResult =
4825 GetAttributeForAllPoints<float>(mesh, pAttribute, outBuffer);
4826 break;
4827 case TINYGLTF_COMPONENT_TYPE_DOUBLE:
4828 decodeResult =
4829 GetAttributeForAllPoints<double>(mesh, pAttribute, outBuffer);
4830 break;
4831 default:
4832 return false;
Alex Wood7319db72019-01-24 15:38:16 -05004833 }
4834
4835 return decodeResult;
4836}
4837
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004838static bool ParseDracoExtension(Primitive *primitive, Model *model,
4839 std::string *err,
4840 const Value &dracoExtensionValue) {
snowapril84e15262021-03-20 19:25:58 +09004841 (void)err;
Alex Wood7319db72019-01-24 15:38:16 -05004842 auto bufferViewValue = dracoExtensionValue.Get("bufferView");
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004843 if (!bufferViewValue.IsInt()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004844 auto attributesValue = dracoExtensionValue.Get("attributes");
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004845 if (!attributesValue.IsObject()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004846
4847 auto attributesObject = attributesValue.Get<Value::Object>();
4848 int bufferView = bufferViewValue.Get<int>();
4849
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004850 BufferView &view = model->bufferViews[bufferView];
4851 Buffer &buffer = model->buffers[view.buffer];
Alex Wood7319db72019-01-24 15:38:16 -05004852 // BufferView has already been decoded
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004853 if (view.dracoDecoded) return true;
Alex Wood7319db72019-01-24 15:38:16 -05004854 view.dracoDecoded = true;
4855
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004856 const char *bufferViewData =
4857 reinterpret_cast<const char *>(buffer.data.data() + view.byteOffset);
Alex Wood7319db72019-01-24 15:38:16 -05004858 size_t bufferViewSize = view.byteLength;
4859
4860 // decode draco
4861 draco::DecoderBuffer decoderBuffer;
4862 decoderBuffer.Init(bufferViewData, bufferViewSize);
4863 draco::Decoder decoder;
4864 auto decodeResult = decoder.DecodeMeshFromBuffer(&decoderBuffer);
4865 if (!decodeResult.ok()) {
4866 return false;
4867 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004868 const std::unique_ptr<draco::Mesh> &mesh = decodeResult.value();
Alex Wood7319db72019-01-24 15:38:16 -05004869
4870 // create new bufferView for indices
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004871 if (primitive->indices >= 0) {
4872 int32_t componentSize = GetComponentSizeInBytes(
4873 model->accessors[primitive->indices].componentType);
Alex Wood7319db72019-01-24 15:38:16 -05004874 Buffer decodedIndexBuffer;
4875 decodedIndexBuffer.data.resize(mesh->num_faces() * 3 * componentSize);
4876
4877 DecodeIndexBuffer(mesh.get(), componentSize, decodedIndexBuffer.data);
4878
4879 model->buffers.emplace_back(std::move(decodedIndexBuffer));
4880
4881 BufferView decodedIndexBufferView;
4882 decodedIndexBufferView.buffer = int(model->buffers.size() - 1);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004883 decodedIndexBufferView.byteLength =
4884 int(mesh->num_faces() * 3 * componentSize);
Alex Wood7319db72019-01-24 15:38:16 -05004885 decodedIndexBufferView.byteOffset = 0;
4886 decodedIndexBufferView.byteStride = 0;
4887 decodedIndexBufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER;
4888 model->bufferViews.emplace_back(std::move(decodedIndexBufferView));
4889
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004890 model->accessors[primitive->indices].bufferView =
4891 int(model->bufferViews.size() - 1);
Alexander Wood0d77a292019-01-26 08:58:45 -05004892 model->accessors[primitive->indices].count = int(mesh->num_faces() * 3);
Alex Wood7319db72019-01-24 15:38:16 -05004893 }
4894
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004895 for (const auto &attribute : attributesObject) {
4896 if (!attribute.second.IsInt()) return false;
Alexander Wood0d77a292019-01-26 08:58:45 -05004897 auto primitiveAttribute = primitive->attributes.find(attribute.first);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004898 if (primitiveAttribute == primitive->attributes.end()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004899
4900 int dracoAttributeIndex = attribute.second.Get<int>();
4901 const auto pAttribute = mesh->GetAttributeByUniqueId(dracoAttributeIndex);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004902 const auto componentType =
4903 model->accessors[primitiveAttribute->second].componentType;
Alex Wood7319db72019-01-24 15:38:16 -05004904
4905 // Create a new buffer for this decoded buffer
4906 Buffer decodedBuffer;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004907 size_t bufferSize = mesh->num_points() * pAttribute->num_components() *
4908 GetComponentSizeInBytes(componentType);
Alex Wood7319db72019-01-24 15:38:16 -05004909 decodedBuffer.data.resize(bufferSize);
4910
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004911 if (!GetAttributeForAllPoints(componentType, mesh.get(), pAttribute,
4912 decodedBuffer.data))
Alex Wood7319db72019-01-24 15:38:16 -05004913 return false;
4914
4915 model->buffers.emplace_back(std::move(decodedBuffer));
4916
4917 BufferView decodedBufferView;
4918 decodedBufferView.buffer = int(model->buffers.size() - 1);
4919 decodedBufferView.byteLength = bufferSize;
4920 decodedBufferView.byteOffset = pAttribute->byte_offset();
4921 decodedBufferView.byteStride = pAttribute->byte_stride();
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004922 decodedBufferView.target = primitive->indices >= 0
4923 ? TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER
4924 : TINYGLTF_TARGET_ARRAY_BUFFER;
Alex Wood7319db72019-01-24 15:38:16 -05004925 model->bufferViews.emplace_back(std::move(decodedBufferView));
4926
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004927 model->accessors[primitiveAttribute->second].bufferView =
4928 int(model->bufferViews.size() - 1);
4929 model->accessors[primitiveAttribute->second].count =
4930 int(mesh->num_points());
Alex Wood7319db72019-01-24 15:38:16 -05004931 }
4932
4933 return true;
4934}
4935#endif
4936
4937static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err,
David03ad33c2023-02-15 23:35:51 -06004938 const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004939 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004940 int material = -1;
4941 ParseIntegerProperty(&material, err, o, "material", false);
4942 primitive->material = material;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004943
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004944 int mode = TINYGLTF_MODE_TRIANGLES;
4945 ParseIntegerProperty(&mode, err, o, "mode", false);
imallettd9ce9eb2022-10-07 10:37:09 -07004946 primitive->mode = mode; // Why only triangles were supported ?
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004947
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004948 int indices = -1;
4949 ParseIntegerProperty(&indices, err, o, "indices", false);
4950 primitive->indices = indices;
4951 if (!ParseStringIntegerProperty(&primitive->attributes, err, o, "attributes",
4952 true, "Primitive")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004953 return false;
4954 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004955
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004956 // Look for morph targets
David03ad33c2023-02-15 23:35:51 -06004957 detail::json_const_iterator targetsObject;
David1f9a4b92023-02-15 22:56:18 -06004958 if (detail::FindMember(o, "targets", targetsObject) &&
4959 detail::IsArray(detail::GetValue(targetsObject))) {
4960 auto targetsObjectEnd = detail::ArrayEnd(detail::GetValue(targetsObject));
David03ad33c2023-02-15 23:35:51 -06004961 for (detail::json_const_array_iterator i = detail::ArrayBegin(detail::GetValue(targetsObject));
jrkooncecba5d6c2019-08-29 11:26:22 -05004962 i != targetsObjectEnd; ++i) {
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004963 std::map<std::string, int> targetAttribues;
4964
David03ad33c2023-02-15 23:35:51 -06004965 const detail::json &dict = *i;
David1f9a4b92023-02-15 22:56:18 -06004966 if (detail::IsObject(dict)) {
David03ad33c2023-02-15 23:35:51 -06004967 detail::json_const_iterator dictIt(detail::ObjectBegin(dict));
4968 detail::json_const_iterator dictItEnd(detail::ObjectEnd(dict));
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004969
jrkooncecba5d6c2019-08-29 11:26:22 -05004970 for (; dictIt != dictItEnd; ++dictIt) {
4971 int iVal;
David1f9a4b92023-02-15 22:56:18 -06004972 if (detail::GetInt(detail::GetValue(dictIt), iVal))
4973 targetAttribues[detail::GetKey(dictIt)] = iVal;
jrkooncecba5d6c2019-08-29 11:26:22 -05004974 }
4975 primitive->targets.emplace_back(std::move(targetAttribues));
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004976 }
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004977 }
4978 }
4979
David Siegel22cafa12023-06-05 22:18:59 +02004980 ParseExtrasAndExtensions(primitive, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004981
Alex Wood7319db72019-01-24 15:38:16 -05004982#ifdef TINYGLTF_ENABLE_DRACO
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004983 auto dracoExtension =
4984 primitive->extensions.find("KHR_draco_mesh_compression");
4985 if (dracoExtension != primitive->extensions.end()) {
4986 ParseDracoExtension(primitive, model, err, dracoExtension->second);
Alex Wood7319db72019-01-24 15:38:16 -05004987 }
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09004988#else
4989 (void)model;
Alex Wood7319db72019-01-24 15:38:16 -05004990#endif
4991
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004992 return true;
4993}
4994
David03ad33c2023-02-15 23:35:51 -06004995static bool ParseMesh(Mesh *mesh, Model *model, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004996 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004997 ParseStringProperty(&mesh->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004998
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004999 mesh->primitives.clear();
David03ad33c2023-02-15 23:35:51 -06005000 detail::json_const_iterator primObject;
David1f9a4b92023-02-15 22:56:18 -06005001 if (detail::FindMember(o, "primitives", primObject) &&
5002 detail::IsArray(detail::GetValue(primObject))) {
David03ad33c2023-02-15 23:35:51 -06005003 detail::json_const_array_iterator primEnd = detail::ArrayEnd(detail::GetValue(primObject));
5004 for (detail::json_const_array_iterator i = detail::ArrayBegin(detail::GetValue(primObject));
jrkooncecba5d6c2019-08-29 11:26:22 -05005005 i != primEnd; ++i) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005006 Primitive primitive;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005007 if (ParsePrimitive(&primitive, model, err, *i,
5008 store_original_json_for_extras_and_extensions)) {
Luke San Antonio10a3e1d2016-06-15 00:05:01 -04005009 // Only add the primitive if the parsing succeeds.
jrkoonced1e14722019-08-27 11:51:02 -05005010 mesh->primitives.emplace_back(std::move(primitive));
Luke San Antonio10a3e1d2016-06-15 00:05:01 -04005011 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005012 }
5013 }
5014
Aurélien Chatelain38a6a752017-05-24 09:54:55 +00005015 // Should probably check if has targets and if dimensions fit
5016 ParseNumberArrayProperty(&mesh->weights, err, o, "weights", false);
5017
David Siegel22cafa12023-06-05 22:18:59 +02005018 ParseExtrasAndExtensions(mesh, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005019
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005020 return true;
5021}
5022
David03ad33c2023-02-15 23:35:51 -06005023static bool ParseNode(Node *node, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005024 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005025 ParseStringProperty(&node->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005026
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005027 int skin = -1;
5028 ParseIntegerProperty(&skin, err, o, "skin", false);
5029 node->skin = skin;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005030
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005031 // Matrix and T/R/S are exclusive
Syoyo Fujita5b407452017-06-04 17:42:41 +09005032 if (!ParseNumberArrayProperty(&node->matrix, err, o, "matrix", false)) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005033 ParseNumberArrayProperty(&node->rotation, err, o, "rotation", false);
5034 ParseNumberArrayProperty(&node->scale, err, o, "scale", false);
5035 ParseNumberArrayProperty(&node->translation, err, o, "translation", false);
5036 }
5037
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005038 int camera = -1;
5039 ParseIntegerProperty(&camera, err, o, "camera", false);
5040 node->camera = camera;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005041
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005042 int mesh = -1;
5043 ParseIntegerProperty(&mesh, err, o, "mesh", false);
5044 node->mesh = mesh;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005045
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005046 node->children.clear();
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005047 ParseIntegerArrayProperty(&node->children, err, o, "children", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005048
Benjamin Schmithüsenad63bf72019-08-16 14:19:27 +02005049 ParseNumberArrayProperty(&node->weights, err, o, "weights", false);
5050
David Siegel22cafa12023-06-05 22:18:59 +02005051 ParseExtrasAndExtensions(node, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005052
David Siegelc1648782023-05-09 01:33:57 +02005053 // KHR_lights_punctual: parse light source reference
5054 int light = -1;
5055 if (node->extensions.count("KHR_lights_punctual") != 0) {
5056 auto const& light_ext = node->extensions["KHR_lights_punctual"];
5057 if (light_ext.Has("light")) {
5058 light = light_ext.Get("light").GetNumberAsInt();
5059 } else {
David Siegel157063f2023-06-06 15:31:58 +02005060 if (err) {
5061 *err += "Node has extension KHR_lights_punctual, but does not reference "
5062 "a light source.\n";
5063 }
David Siegelc1648782023-05-09 01:33:57 +02005064 return false;
5065 }
5066 }
5067 node->light = light;
Baranob_Ilya78864c82023-06-12 10:43:52 +04005068
5069 // KHR_audio: parse audio source reference
5070 int emitter = -1;
5071 if (node->extensions.count("KHR_audio") != 0) {
5072 auto const &audio_ext = node->extensions["KHR_audio"];
5073 if (audio_ext.Has("emitter")) {
5074 emitter = audio_ext.Get("emitter").GetNumberAsInt();
5075 } else {
5076 if (err) {
5077 *err +=
5078 "Node has extension KHR_audio, but does not reference "
5079 "a audio emitter.\n";
5080 }
5081 return false;
5082 }
5083 }
5084 node->emitter = emitter;
Syoyo Fujita7a570c82023-06-19 21:52:13 +09005085
Emanuel Schrade186322b2017-11-06 11:14:41 +01005086 return true;
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04005087}
5088
Baranob_Ilya879cb472023-06-12 13:35:05 +04005089static bool ParseScene(Scene *scene, std::string *err, const detail::json &o,
5090 bool store_original_json_for_extras_and_extensions) {
5091 ParseStringProperty(&scene->name, err, o, "name", false);
5092 ParseIntegerArrayProperty(&scene->nodes, err, o, "nodes", false);
5093
5094 ParseExtrasAndExtensions(scene, err, o,
5095 store_original_json_for_extras_and_extensions);
5096
5097 // Parse KHR_audio global emitters
5098 if (scene->extensions.count("KHR_audio") != 0) {
5099 auto const &audio_ext = scene->extensions["KHR_audio"];
5100 if (audio_ext.Has("emitters")) {
5101 auto emittersArr = audio_ext.Get("emitters");
5102 for (size_t i = 0; i < emittersArr.ArrayLen(); ++i) {
5103 scene->audioEmitters.emplace_back(emittersArr.Get(i).GetNumberAsInt());
5104 }
5105 } else {
5106 if (err) {
5107 *err +=
5108 "Node has extension KHR_audio, but does not reference "
5109 "a audio emitter.\n";
5110 }
5111 return false;
5112 }
5113 }
5114
5115 return true;
5116}
5117
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005118static bool ParsePbrMetallicRoughness(
David03ad33c2023-02-15 23:35:51 -06005119 PbrMetallicRoughness *pbr, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005120 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09005121 if (pbr == nullptr) {
5122 return false;
5123 }
5124
5125 std::vector<double> baseColorFactor;
5126 if (ParseNumberArrayProperty(&baseColorFactor, err, o, "baseColorFactor",
5127 /* required */ false)) {
5128 if (baseColorFactor.size() != 4) {
5129 if (err) {
5130 (*err) +=
5131 "Array length of `baseColorFactor` parameter in "
5132 "pbrMetallicRoughness must be 4, but got " +
5133 std::to_string(baseColorFactor.size()) + "\n";
5134 }
5135 return false;
5136 }
Selmar Kokc3353e12019-10-18 18:22:35 +02005137 pbr->baseColorFactor = baseColorFactor;
Syoyo Fujita046400b2019-07-24 19:26:48 +09005138 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09005139
5140 {
David03ad33c2023-02-15 23:35:51 -06005141 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06005142 if (detail::FindMember(o, "baseColorTexture", it)) {
5143 ParseTextureInfo(&pbr->baseColorTexture, err, detail::GetValue(it),
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005144 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09005145 }
5146 }
5147
5148 {
David03ad33c2023-02-15 23:35:51 -06005149 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06005150 if (detail::FindMember(o, "metallicRoughnessTexture", it)) {
5151 ParseTextureInfo(&pbr->metallicRoughnessTexture, err, detail::GetValue(it),
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005152 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09005153 }
5154 }
5155
5156 ParseNumberProperty(&pbr->metallicFactor, err, o, "metallicFactor", false);
5157 ParseNumberProperty(&pbr->roughnessFactor, err, o, "roughnessFactor", false);
5158
David Siegel22cafa12023-06-05 22:18:59 +02005159 ParseExtrasAndExtensions(pbr, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005160
Syoyo Fujita046400b2019-07-24 19:26:48 +09005161 return true;
5162}
5163
David03ad33c2023-02-15 23:35:51 -06005164static bool ParseMaterial(Material *material, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005165 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09005166 ParseStringProperty(&material->name, err, o, "name", /* required */ false);
5167
Syoyo Fujitaff515702019-08-24 16:29:14 +09005168 if (ParseNumberArrayProperty(&material->emissiveFactor, err, o,
5169 "emissiveFactor",
5170 /* required */ false)) {
Selmar Kok6df800d2019-08-19 11:05:28 +02005171 if (material->emissiveFactor.size() != 3) {
5172 if (err) {
5173 (*err) +=
5174 "Array length of `emissiveFactor` parameter in "
5175 "material must be 3, but got " +
5176 std::to_string(material->emissiveFactor.size()) + "\n";
5177 }
5178 return false;
5179 }
Syoyo Fujitaff515702019-08-24 16:29:14 +09005180 } else {
Selmar Kok6df800d2019-08-19 11:05:28 +02005181 // fill with default values
Selmar Kok6dba6c62019-08-19 11:23:31 +02005182 material->emissiveFactor = {0.0, 0.0, 0.0};
Selmar Kok6df800d2019-08-19 11:05:28 +02005183 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09005184
5185 ParseStringProperty(&material->alphaMode, err, o, "alphaMode",
5186 /* required */ false);
5187 ParseNumberProperty(&material->alphaCutoff, err, o, "alphaCutoff",
5188 /* required */ false);
5189 ParseBooleanProperty(&material->doubleSided, err, o, "doubleSided",
5190 /* required */ false);
5191
5192 {
David03ad33c2023-02-15 23:35:51 -06005193 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06005194 if (detail::FindMember(o, "pbrMetallicRoughness", it)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09005195 ParsePbrMetallicRoughness(&material->pbrMetallicRoughness, err,
David1f9a4b92023-02-15 22:56:18 -06005196 detail::GetValue(it),
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005197 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09005198 }
5199 }
5200
5201 {
David03ad33c2023-02-15 23:35:51 -06005202 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06005203 if (detail::FindMember(o, "normalTexture", it)) {
5204 ParseNormalTextureInfo(&material->normalTexture, err, detail::GetValue(it),
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005205 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09005206 }
5207 }
5208
5209 {
David03ad33c2023-02-15 23:35:51 -06005210 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06005211 if (detail::FindMember(o, "occlusionTexture", it)) {
5212 ParseOcclusionTextureInfo(&material->occlusionTexture, err, detail::GetValue(it),
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005213 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09005214 }
5215 }
5216
5217 {
David03ad33c2023-02-15 23:35:51 -06005218 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06005219 if (detail::FindMember(o, "emissiveTexture", it)) {
5220 ParseTextureInfo(&material->emissiveTexture, err, detail::GetValue(it),
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005221 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09005222 }
5223 }
5224
5225 // Old code path. For backward compatibility, we still store material values
5226 // as Parameter. This will create duplicated information for
imallettd9ce9eb2022-10-07 10:37:09 -07005227 // example(pbrMetallicRoughness), but should be negligible in terms of memory
Syoyo Fujita046400b2019-07-24 19:26:48 +09005228 // consumption.
5229 // TODO(syoyo): Remove in the next major release.
5230 material->values.clear();
5231 material->additionalValues.clear();
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04005232
David03ad33c2023-02-15 23:35:51 -06005233 detail::json_const_iterator it(detail::ObjectBegin(o));
5234 detail::json_const_iterator itEnd(detail::ObjectEnd(o));
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04005235
jrkooncecba5d6c2019-08-29 11:26:22 -05005236 for (; it != itEnd; ++it) {
David1f9a4b92023-02-15 22:56:18 -06005237 std::string key(detail::GetKey(it));
jrkoonce06c30c42019-09-03 15:56:48 -05005238 if (key == "pbrMetallicRoughness") {
David1f9a4b92023-02-15 22:56:18 -06005239 if (detail::IsObject(detail::GetValue(it))) {
David03ad33c2023-02-15 23:35:51 -06005240 const detail::json &values_object = detail::GetValue(it);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005241
David03ad33c2023-02-15 23:35:51 -06005242 detail::json_const_iterator itVal(detail::ObjectBegin(values_object));
5243 detail::json_const_iterator itValEnd(detail::ObjectEnd(values_object));
jrkoonce06c30c42019-09-03 15:56:48 -05005244
5245 for (; itVal != itValEnd; ++itVal) {
5246 Parameter param;
David1f9a4b92023-02-15 22:56:18 -06005247 if (ParseParameterProperty(&param, err, values_object, detail::GetKey(itVal),
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005248 false)) {
David1f9a4b92023-02-15 22:56:18 -06005249 material->values.emplace(detail::GetKey(itVal), std::move(param));
jrkoonce06c30c42019-09-03 15:56:48 -05005250 }
5251 }
5252 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005253 } else if (key == "extensions" || key == "extras") {
5254 // done later, skip, otherwise poorly parsed contents will be saved in the
5255 // parametermap and serialized again later
5256 } else {
jrkoonce06c30c42019-09-03 15:56:48 -05005257 Parameter param;
5258 if (ParseParameterProperty(&param, err, o, key, false)) {
5259 // names of materials have already been parsed. Putting it in this map
imallettd9ce9eb2022-10-07 10:37:09 -07005260 // doesn't correctly reflect the glTF specification
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005261 if (key != "name")
5262 material->additionalValues.emplace(std::move(key), std::move(param));
jrkoonce06c30c42019-09-03 15:56:48 -05005263 }
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00005264 }
Syoyo Fujita5b407452017-06-04 17:42:41 +09005265 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005266
David Siegel22cafa12023-06-05 22:18:59 +02005267 material->extensions.clear(); // Note(agnat): Why?
5268 ParseExtrasAndExtensions(material, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005269
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04005270 return true;
5271}
5272
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005273static bool ParseAnimationChannel(
David03ad33c2023-02-15 23:35:51 -06005274 AnimationChannel *channel, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005275 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005276 int samplerIndex = -1;
5277 int targetIndex = -1;
5278 if (!ParseIntegerProperty(&samplerIndex, err, o, "sampler", true,
5279 "AnimationChannel")) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005280 if (err) {
5281 (*err) += "`sampler` field is missing in animation channels\n";
5282 }
5283 return false;
5284 }
5285
David03ad33c2023-02-15 23:35:51 -06005286 detail::json_const_iterator targetIt;
David1f9a4b92023-02-15 22:56:18 -06005287 if (detail::FindMember(o, "target", targetIt) && detail::IsObject(detail::GetValue(targetIt))) {
David03ad33c2023-02-15 23:35:51 -06005288 const detail::json &target_object = detail::GetValue(targetIt);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005289
Jack Mousseau283b5522023-01-15 11:45:45 -08005290 ParseIntegerProperty(&targetIndex, err, target_object, "node", false);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005291
5292 if (!ParseStringProperty(&channel->target_path, err, target_object, "path",
5293 true)) {
5294 if (err) {
5295 (*err) += "`path` field is missing in animation.channels.target\n";
5296 }
5297 return false;
5298 }
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09005299 ParseExtensionsProperty(&channel->target_extensions, err, target_object);
David Siegeld852f502023-06-05 23:28:05 +02005300 ParseExtrasProperty(&channel->target_extras, target_object);
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09005301 if (store_original_json_for_extras_and_extensions) {
David Siegeld852f502023-06-05 23:28:05 +02005302 {
5303 detail::json_const_iterator it;
5304 if (detail::FindMember(target_object, "extensions", it)) {
5305 channel->target_extensions_json_string = detail::JsonToString(detail::GetValue(it));
5306 }
5307 }
5308 {
5309 detail::json_const_iterator it;
5310 if (detail::FindMember(target_object, "extras", it)) {
5311 channel->target_extras_json_string = detail::JsonToString(detail::GetValue(it));
5312 }
Selmar Kok973d9b32020-01-21 18:45:24 +01005313 }
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09005314 }
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005315 }
5316
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005317 channel->sampler = samplerIndex;
5318 channel->target_node = targetIndex;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005319
David Siegel22cafa12023-06-05 22:18:59 +02005320 ParseExtrasAndExtensions(channel, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005321
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005322 return true;
5323}
5324
5325static bool ParseAnimation(Animation *animation, std::string *err,
David03ad33c2023-02-15 23:35:51 -06005326 const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005327 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005328 {
David03ad33c2023-02-15 23:35:51 -06005329 detail::json_const_iterator channelsIt;
David1f9a4b92023-02-15 22:56:18 -06005330 if (detail::FindMember(o, "channels", channelsIt) &&
5331 detail::IsArray(detail::GetValue(channelsIt))) {
David03ad33c2023-02-15 23:35:51 -06005332 detail::json_const_array_iterator channelEnd = detail::ArrayEnd(detail::GetValue(channelsIt));
5333 for (detail::json_const_array_iterator i = detail::ArrayBegin(detail::GetValue(channelsIt));
jrkooncecba5d6c2019-08-29 11:26:22 -05005334 i != channelEnd; ++i) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005335 AnimationChannel channel;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005336 if (ParseAnimationChannel(
5337 &channel, err, *i,
5338 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005339 // Only add the channel if the parsing succeeds.
jrkooncecba5d6c2019-08-29 11:26:22 -05005340 animation->channels.emplace_back(std::move(channel));
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005341 }
5342 }
5343 }
5344 }
5345
5346 {
David03ad33c2023-02-15 23:35:51 -06005347 detail::json_const_iterator samplerIt;
David1f9a4b92023-02-15 22:56:18 -06005348 if (detail::FindMember(o, "samplers", samplerIt) && detail::IsArray(detail::GetValue(samplerIt))) {
David03ad33c2023-02-15 23:35:51 -06005349 const detail::json &sampler_array = detail::GetValue(samplerIt);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005350
David03ad33c2023-02-15 23:35:51 -06005351 detail::json_const_array_iterator it = detail::ArrayBegin(sampler_array);
5352 detail::json_const_array_iterator itEnd = detail::ArrayEnd(sampler_array);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005353
jrkooncecba5d6c2019-08-29 11:26:22 -05005354 for (; it != itEnd; ++it) {
David03ad33c2023-02-15 23:35:51 -06005355 const detail::json &s = *it;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005356
5357 AnimationSampler sampler;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005358 int inputIndex = -1;
5359 int outputIndex = -1;
5360 if (!ParseIntegerProperty(&inputIndex, err, s, "input", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005361 if (err) {
5362 (*err) += "`input` field is missing in animation.sampler\n";
5363 }
5364 return false;
5365 }
Evan Birenbaum6bdffed2019-02-14 13:30:57 -08005366 ParseStringProperty(&sampler.interpolation, err, s, "interpolation",
5367 false);
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005368 if (!ParseIntegerProperty(&outputIndex, err, s, "output", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005369 if (err) {
5370 (*err) += "`output` field is missing in animation.sampler\n";
5371 }
5372 return false;
5373 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005374 sampler.input = inputIndex;
5375 sampler.output = outputIndex;
David Siegel22cafa12023-06-05 22:18:59 +02005376 ParseExtrasAndExtensions(&sampler, err, o,
5377 store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005378
jrkooncecba5d6c2019-08-29 11:26:22 -05005379 animation->samplers.emplace_back(std::move(sampler));
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005380 }
5381 }
5382 }
5383
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005384 ParseStringProperty(&animation->name, err, o, "name", false);
5385
David Siegel22cafa12023-06-05 22:18:59 +02005386 ParseExtrasAndExtensions(animation, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005387
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005388 return true;
5389}
5390
David03ad33c2023-02-15 23:35:51 -06005391static bool ParseSampler(Sampler *sampler, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005392 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujitac2615632016-06-19 21:56:06 +09005393 ParseStringProperty(&sampler->name, err, o, "name", false);
5394
Syoyo Fujitaee179b22019-08-16 13:11:30 +09005395 int minFilter = -1;
5396 int magFilter = -1;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005397 int wrapS = TINYGLTF_TEXTURE_WRAP_REPEAT;
5398 int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT;
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09005399 // int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005400 ParseIntegerProperty(&minFilter, err, o, "minFilter", false);
5401 ParseIntegerProperty(&magFilter, err, o, "magFilter", false);
5402 ParseIntegerProperty(&wrapS, err, o, "wrapS", false);
5403 ParseIntegerProperty(&wrapT, err, o, "wrapT", false);
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09005404 // ParseIntegerProperty(&wrapR, err, o, "wrapR", false); // tinygltf
5405 // extension
Syoyo Fujitaee179b22019-08-16 13:11:30 +09005406
imallettd9ce9eb2022-10-07 10:37:09 -07005407 // TODO(syoyo): Check the value is allowed one.
Syoyo Fujitaee179b22019-08-16 13:11:30 +09005408 // (e.g. we allow 9728(NEAREST), but don't allow 9727)
Syoyo Fujitac2615632016-06-19 21:56:06 +09005409
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005410 sampler->minFilter = minFilter;
5411 sampler->magFilter = magFilter;
5412 sampler->wrapS = wrapS;
5413 sampler->wrapT = wrapT;
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09005414 // sampler->wrapR = wrapR;
Syoyo Fujitac2615632016-06-19 21:56:06 +09005415
David Siegel22cafa12023-06-05 22:18:59 +02005416 ParseExtrasAndExtensions(sampler, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005417
Syoyo Fujitac2615632016-06-19 21:56:06 +09005418 return true;
5419}
5420
David03ad33c2023-02-15 23:35:51 -06005421static bool ParseSkin(Skin *skin, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005422 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09005423 ParseStringProperty(&skin->name, err, o, "name", false, "Skin");
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005424
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005425 std::vector<int> joints;
5426 if (!ParseIntegerArrayProperty(&joints, err, o, "joints", false, "Skin")) {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005427 return false;
5428 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005429 skin->joints = std::move(joints);
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005430
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005431 int skeleton = -1;
5432 ParseIntegerProperty(&skeleton, err, o, "skeleton", false, "Skin");
5433 skin->skeleton = skeleton;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005434
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005435 int invBind = -1;
5436 ParseIntegerProperty(&invBind, err, o, "inverseBindMatrices", true, "Skin");
5437 skin->inverseBindMatrices = invBind;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005438
David Siegel22cafa12023-06-05 22:18:59 +02005439 ParseExtrasAndExtensions(skin, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005440
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005441 return true;
5442}
5443
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005444static bool ParsePerspectiveCamera(
David03ad33c2023-02-15 23:35:51 -06005445 PerspectiveCamera *camera, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005446 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005447 double yfov = 0.0;
5448 if (!ParseNumberProperty(&yfov, err, o, "yfov", true, "OrthographicCamera")) {
5449 return false;
5450 }
5451
5452 double znear = 0.0;
5453 if (!ParseNumberProperty(&znear, err, o, "znear", true,
5454 "PerspectiveCamera")) {
5455 return false;
5456 }
5457
5458 double aspectRatio = 0.0; // = invalid
5459 ParseNumberProperty(&aspectRatio, err, o, "aspectRatio", false,
5460 "PerspectiveCamera");
5461
5462 double zfar = 0.0; // = invalid
5463 ParseNumberProperty(&zfar, err, o, "zfar", false, "PerspectiveCamera");
5464
Selmar Kok31cb7f92018-10-03 15:39:05 +02005465 camera->aspectRatio = aspectRatio;
5466 camera->zfar = zfar;
5467 camera->yfov = yfov;
5468 camera->znear = znear;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005469
David Siegel22cafa12023-06-05 22:18:59 +02005470 ParseExtrasAndExtensions(camera, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005471
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005472 // TODO(syoyo): Validate parameter values.
5473
5474 return true;
5475}
5476
David03ad33c2023-02-15 23:35:51 -06005477static bool ParseSpotLight(SpotLight *light, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005478 bool store_original_json_for_extras_and_extensions) {
Johan Bowald52936a02019-07-17 09:06:45 +02005479 ParseNumberProperty(&light->innerConeAngle, err, o, "innerConeAngle", false);
5480 ParseNumberProperty(&light->outerConeAngle, err, o, "outerConeAngle", false);
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005481
David Siegel22cafa12023-06-05 22:18:59 +02005482 ParseExtrasAndExtensions(light, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005483
Johan Bowald52936a02019-07-17 09:06:45 +02005484 // TODO(syoyo): Validate parameter values.
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005485
Johan Bowald52936a02019-07-17 09:06:45 +02005486 return true;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005487}
5488
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005489static bool ParseOrthographicCamera(
David03ad33c2023-02-15 23:35:51 -06005490 OrthographicCamera *camera, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005491 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005492 double xmag = 0.0;
5493 if (!ParseNumberProperty(&xmag, err, o, "xmag", true, "OrthographicCamera")) {
5494 return false;
5495 }
5496
5497 double ymag = 0.0;
5498 if (!ParseNumberProperty(&ymag, err, o, "ymag", true, "OrthographicCamera")) {
5499 return false;
5500 }
5501
5502 double zfar = 0.0;
5503 if (!ParseNumberProperty(&zfar, err, o, "zfar", true, "OrthographicCamera")) {
5504 return false;
5505 }
5506
5507 double znear = 0.0;
5508 if (!ParseNumberProperty(&znear, err, o, "znear", true,
5509 "OrthographicCamera")) {
5510 return false;
5511 }
5512
David Siegel22cafa12023-06-05 22:18:59 +02005513 ParseExtrasAndExtensions(camera, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005514
Selmar Kok31cb7f92018-10-03 15:39:05 +02005515 camera->xmag = xmag;
5516 camera->ymag = ymag;
5517 camera->zfar = zfar;
5518 camera->znear = znear;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005519
5520 // TODO(syoyo): Validate parameter values.
5521
5522 return true;
5523}
5524
David03ad33c2023-02-15 23:35:51 -06005525static bool ParseCamera(Camera *camera, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005526 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005527 if (!ParseStringProperty(&camera->type, err, o, "type", true, "Camera")) {
5528 return false;
5529 }
5530
5531 if (camera->type.compare("orthographic") == 0) {
David03ad33c2023-02-15 23:35:51 -06005532 detail::json_const_iterator orthoIt;
David1f9a4b92023-02-15 22:56:18 -06005533 if (!detail::FindMember(o, "orthographic", orthoIt)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005534 if (err) {
5535 std::stringstream ss;
imallettd9ce9eb2022-10-07 10:37:09 -07005536 ss << "Orthographic camera description not found." << std::endl;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005537 (*err) += ss.str();
5538 }
5539 return false;
5540 }
5541
David03ad33c2023-02-15 23:35:51 -06005542 const detail::json &v = detail::GetValue(orthoIt);
David1f9a4b92023-02-15 22:56:18 -06005543 if (!detail::IsObject(v)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005544 if (err) {
5545 std::stringstream ss;
5546 ss << "\"orthographic\" is not a JSON object." << std::endl;
5547 (*err) += ss.str();
5548 }
5549 return false;
5550 }
5551
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005552 if (!ParseOrthographicCamera(
5553 &camera->orthographic, err, v,
5554 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005555 return false;
5556 }
5557 } else if (camera->type.compare("perspective") == 0) {
David03ad33c2023-02-15 23:35:51 -06005558 detail::json_const_iterator perspIt;
David1f9a4b92023-02-15 22:56:18 -06005559 if (!detail::FindMember(o, "perspective", perspIt)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005560 if (err) {
5561 std::stringstream ss;
5562 ss << "Perspective camera description not found." << std::endl;
5563 (*err) += ss.str();
5564 }
5565 return false;
5566 }
5567
David03ad33c2023-02-15 23:35:51 -06005568 const detail::json &v = detail::GetValue(perspIt);
David1f9a4b92023-02-15 22:56:18 -06005569 if (!detail::IsObject(v)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005570 if (err) {
5571 std::stringstream ss;
5572 ss << "\"perspective\" is not a JSON object." << std::endl;
5573 (*err) += ss.str();
5574 }
5575 return false;
5576 }
5577
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005578 if (!ParsePerspectiveCamera(
5579 &camera->perspective, err, v,
5580 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005581 return false;
5582 }
5583 } else {
5584 if (err) {
5585 std::stringstream ss;
5586 ss << "Invalid camera type: \"" << camera->type
5587 << "\". Must be \"perspective\" or \"orthographic\"" << std::endl;
5588 (*err) += ss.str();
5589 }
5590 return false;
5591 }
5592
5593 ParseStringProperty(&camera->name, err, o, "name", false);
5594
David Siegel22cafa12023-06-05 22:18:59 +02005595 ParseExtrasAndExtensions(camera, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005596
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005597 return true;
5598}
5599
David03ad33c2023-02-15 23:35:51 -06005600static bool ParseLight(Light *light, std::string *err, const detail::json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005601 bool store_original_json_for_extras_and_extensions) {
Johan Bowald52936a02019-07-17 09:06:45 +02005602 if (!ParseStringProperty(&light->type, err, o, "type", true)) {
5603 return false;
5604 }
5605
5606 if (light->type == "spot") {
David03ad33c2023-02-15 23:35:51 -06005607 detail::json_const_iterator spotIt;
David1f9a4b92023-02-15 22:56:18 -06005608 if (!detail::FindMember(o, "spot", spotIt)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005609 if (err) {
5610 std::stringstream ss;
5611 ss << "Spot light description not found." << std::endl;
5612 (*err) += ss.str();
5613 }
5614 return false;
Benjamin Schmithüsen4557b6a2019-07-09 16:55:55 +02005615 }
5616
David03ad33c2023-02-15 23:35:51 -06005617 const detail::json &v = detail::GetValue(spotIt);
David1f9a4b92023-02-15 22:56:18 -06005618 if (!detail::IsObject(v)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005619 if (err) {
5620 std::stringstream ss;
5621 ss << "\"spot\" is not a JSON object." << std::endl;
5622 (*err) += ss.str();
5623 }
5624 return false;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005625 }
5626
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005627 if (!ParseSpotLight(&light->spot, err, v,
5628 store_original_json_for_extras_and_extensions)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005629 return false;
5630 }
5631 }
5632
5633 ParseStringProperty(&light->name, err, o, "name", false);
5634 ParseNumberArrayProperty(&light->color, err, o, "color", false);
5635 ParseNumberProperty(&light->range, err, o, "range", false);
5636 ParseNumberProperty(&light->intensity, err, o, "intensity", false);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005637
David Siegel22cafa12023-06-05 22:18:59 +02005638 ParseExtrasAndExtensions(light, err, o, store_original_json_for_extras_and_extensions);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005639
Johan Bowald52936a02019-07-17 09:06:45 +02005640 return true;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005641}
5642
Baranob_Ilya78864c82023-06-12 10:43:52 +04005643static bool ParsePositionalEmitter(
5644 PositionalEmitter *positional, std::string *err, const detail::json &o,
5645 bool store_original_json_for_extras_and_extensions) {
5646 ParseNumberProperty(&positional->coneInnerAngle, err, o, "coneInnerAngle", false);
5647 ParseNumberProperty(&positional->coneOuterAngle, err, o, "coneOuterAngle", false);
5648 ParseNumberProperty(&positional->coneOuterGain, err, o, "coneOuterGain", false);
5649 ParseNumberProperty(&positional->maxDistance, err, o, "maxDistance", false);
5650 ParseNumberProperty(&positional->refDistance, err, o, "refDistance", false);
5651 ParseNumberProperty(&positional->rolloffFactor, err, o, "rolloffFactor", false);
5652
5653 ParseExtrasAndExtensions(positional, err, o, store_original_json_for_extras_and_extensions);
5654
5655 return true;
5656}
5657
5658static bool ParseAudioEmitter(
5659 AudioEmitter *emitter, std::string *err, const detail::json &o,
5660 bool store_original_json_for_extras_and_extensions) {
5661 if (!ParseStringProperty(&emitter->type, err, o, "type", true)) {
5662 return false;
5663 }
5664
5665 if (emitter->type == "positional") {
5666 detail::json_const_iterator positionalIt;
5667 if (!detail::FindMember(o, "positional", positionalIt)) {
5668 if (err) {
5669 std::stringstream ss;
5670 ss << "Positional emitter description not found." << std::endl;
5671 (*err) += ss.str();
5672 }
5673 return false;
5674 }
5675
5676 const detail::json &v = detail::GetValue(positionalIt);
5677 if (!detail::IsObject(v)) {
5678 if (err) {
5679 std::stringstream ss;
5680 ss << "\"positional\" is not a JSON object." << std::endl;
5681 (*err) += ss.str();
5682 }
5683 return false;
5684 }
5685
5686 if (!ParsePositionalEmitter(&emitter->positional, err, v,
5687 store_original_json_for_extras_and_extensions)) {
5688 return false;
5689 }
5690 }
5691
5692 ParseStringProperty(&emitter->name, err, o, "name", false);
5693 ParseNumberProperty(&emitter->gain, err, o, "gain", false);
5694 ParseBooleanProperty(&emitter->loop, err, o, "loop", false);
5695 ParseBooleanProperty(&emitter->playing, err, o, "playing", false);
5696 ParseStringProperty(&emitter->distanceModel, err, o, "distanceModel", false);
5697 ParseIntegerProperty(&emitter->source, err, o, "source", true);
5698
5699 ParseExtrasAndExtensions(emitter, err, o,
5700 store_original_json_for_extras_and_extensions);
5701
5702 return true;
5703}
5704
5705static bool ParseAudioSource(
5706 AudioSource *source, std::string *err, const detail::json &o,
5707 bool store_original_json_for_extras_and_extensions) {
5708 ParseStringProperty(&source->name, err, o, "name", false);
5709 ParseStringProperty(&source->uri, err, o, "uri", false);
5710
5711 if (source->uri.empty()) {
5712 ParseIntegerProperty(&source->bufferView, err, o, "bufferView", true);
5713 ParseStringProperty(&source->mimeType, err, o, "mimeType", true);
5714 }
5715
5716 ParseExtrasAndExtensions(source, err, o,
5717 store_original_json_for_extras_and_extensions);
5718
5719 return true;
5720}
5721
David Siegelbec8a6d2023-06-06 15:36:07 +02005722namespace detail {
5723
5724template <typename Callback>
5725bool ForEachInArray(const detail::json &_v, const char *member, Callback && cb) {
5726 detail::json_const_iterator itm;
5727 if (detail::FindMember(_v, member, itm) && detail::IsArray(detail::GetValue(itm))) {
5728 const detail::json &root = detail::GetValue(itm);
5729 auto it = detail::ArrayBegin(root);
5730 auto end = detail::ArrayEnd(root);
5731 for (; it != end; ++it) {
5732 if (!cb(*it)) return false;
5733 }
5734 }
5735 return true;
5736};
5737
5738} // end of namespace detail
5739
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09005740bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005741 const char *json_str,
5742 unsigned int json_str_length,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09005743 const std::string &base_dir,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005744 unsigned int check_sections) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005745 if (json_str_length < 4) {
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09005746 if (err) {
5747 (*err) = "JSON string too short.\n";
5748 }
5749 return false;
5750 }
5751
David03ad33c2023-02-15 23:35:51 -06005752 detail::JsonDocument v;
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005753
Syoyo Fujitad42767e2018-03-15 21:52:00 -05005754#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
5755 defined(_CPPUNWIND)) && \
Syoyo Fujitac4166e42020-01-08 02:38:01 +09005756 !defined(TINYGLTF_NOEXCEPTION)
Syoyo Fujitacdf84902017-08-01 20:12:08 +09005757 try {
David03ad33c2023-02-15 23:35:51 -06005758 detail::JsonParse(v, json_str, json_str_length, true);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005759
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005760 } catch (const std::exception &e) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005761 if (err) {
Syoyo Fujitacdf84902017-08-01 20:12:08 +09005762 (*err) = e.what();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005763 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005764 return false;
5765 }
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005766#else
5767 {
David03ad33c2023-02-15 23:35:51 -06005768 detail::JsonParse(v, json_str, json_str_length);
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005769
David1f9a4b92023-02-15 22:56:18 -06005770 if (!detail::IsObject(v)) {
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005771 // Assume parsing was failed.
5772 if (err) {
5773 (*err) = "Failed to parse JSON object\n";
5774 }
5775 return false;
5776 }
5777 }
5778#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005779
David1f9a4b92023-02-15 22:56:18 -06005780 if (!detail::IsObject(v)) {
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09005781 // root is not an object.
5782 if (err) {
5783 (*err) = "Root element is not a JSON object\n";
5784 }
5785 return false;
5786 }
5787
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02005788 {
5789 bool version_found = false;
David03ad33c2023-02-15 23:35:51 -06005790 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06005791 if (detail::FindMember(v, "asset", it) && detail::IsObject(detail::GetValue(it))) {
5792 auto &itObj = detail::GetValue(it);
David03ad33c2023-02-15 23:35:51 -06005793 detail::json_const_iterator version_it;
jrkooncecba5d6c2019-08-29 11:26:22 -05005794 std::string versionStr;
David1f9a4b92023-02-15 22:56:18 -06005795 if (detail::FindMember(itObj, "version", version_it) &&
5796 detail::GetString(detail::GetValue(version_it), versionStr)) {
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02005797 version_found = true;
5798 }
5799 }
5800 if (version_found) {
5801 // OK
5802 } else if (check_sections & REQUIRE_VERSION) {
5803 if (err) {
5804 (*err) += "\"asset\" object not found in .gltf or not an object type\n";
5805 }
5806 return false;
5807 }
5808 }
5809
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005810 // scene is not mandatory.
Syoyo Fujita5b407452017-06-04 17:42:41 +09005811 // FIXME Maybe a better way to handle it than removing the code
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005812
David03ad33c2023-02-15 23:35:51 -06005813 auto IsArrayMemberPresent = [](const detail::json &_v, const char *name) -> bool {
5814 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06005815 return detail::FindMember(_v, name, it) && detail::IsArray(detail::GetValue(it));
jrkooncecba5d6c2019-08-29 11:26:22 -05005816 };
5817
Syoyo Fujita83675312017-12-02 21:14:13 +09005818 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005819 if ((check_sections & REQUIRE_SCENES) &&
5820 !IsArrayMemberPresent(v, "scenes")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005821 if (err) {
5822 (*err) += "\"scenes\" object not found in .gltf or not an array type\n";
5823 }
5824 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005825 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005826 }
5827
Syoyo Fujita83675312017-12-02 21:14:13 +09005828 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005829 if ((check_sections & REQUIRE_NODES) && !IsArrayMemberPresent(v, "nodes")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005830 if (err) {
5831 (*err) += "\"nodes\" object not found in .gltf\n";
5832 }
5833 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005834 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005835 }
5836
Syoyo Fujita83675312017-12-02 21:14:13 +09005837 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005838 if ((check_sections & REQUIRE_ACCESSORS) &&
5839 !IsArrayMemberPresent(v, "accessors")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005840 if (err) {
5841 (*err) += "\"accessors\" object not found in .gltf\n";
5842 }
5843 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005844 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005845 }
5846
Syoyo Fujita83675312017-12-02 21:14:13 +09005847 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005848 if ((check_sections & REQUIRE_BUFFERS) &&
5849 !IsArrayMemberPresent(v, "buffers")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005850 if (err) {
5851 (*err) += "\"buffers\" object not found in .gltf\n";
5852 }
5853 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005854 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005855 }
5856
Syoyo Fujita83675312017-12-02 21:14:13 +09005857 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005858 if ((check_sections & REQUIRE_BUFFER_VIEWS) &&
5859 !IsArrayMemberPresent(v, "bufferViews")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005860 if (err) {
5861 (*err) += "\"bufferViews\" object not found in .gltf\n";
5862 }
5863 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005864 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005865 }
5866
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005867 model->buffers.clear();
5868 model->bufferViews.clear();
5869 model->accessors.clear();
5870 model->meshes.clear();
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005871 model->cameras.clear();
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005872 model->nodes.clear();
5873 model->extensionsUsed.clear();
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00005874 model->extensionsRequired.clear();
Selmar09d2ff12018-03-15 17:30:42 +01005875 model->extensions.clear();
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005876 model->defaultScene = -1;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005877
Syoyo Fujita83675312017-12-02 21:14:13 +09005878 // 1. Parse Asset
5879 {
David03ad33c2023-02-15 23:35:51 -06005880 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06005881 if (detail::FindMember(v, "asset", it) && detail::IsObject(detail::GetValue(it))) {
David03ad33c2023-02-15 23:35:51 -06005882 const detail::json &root = detail::GetValue(it);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005883
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005884 ParseAsset(&model->asset, err, root,
5885 store_original_json_for_extras_and_extensions_);
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00005886 }
5887 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005888
David Siegelbec8a6d2023-06-06 15:36:07 +02005889 using detail::ForEachInArray;
jrkooncecba5d6c2019-08-29 11:26:22 -05005890
jrkooncecba5d6c2019-08-29 11:26:22 -05005891 // 2. Parse extensionUsed
5892 {
David03ad33c2023-02-15 23:35:51 -06005893 ForEachInArray(v, "extensionsUsed", [&](const detail::json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005894 std::string str;
David1f9a4b92023-02-15 22:56:18 -06005895 detail::GetString(o, str);
jrkooncecba5d6c2019-08-29 11:26:22 -05005896 model->extensionsUsed.emplace_back(std::move(str));
5897 return true;
5898 });
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005899 }
5900
Syoyo Fujita83675312017-12-02 21:14:13 +09005901 {
David03ad33c2023-02-15 23:35:51 -06005902 ForEachInArray(v, "extensionsRequired", [&](const detail::json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005903 std::string str;
David1f9a4b92023-02-15 22:56:18 -06005904 detail::GetString(o, str);
jrkooncecba5d6c2019-08-29 11:26:22 -05005905 model->extensionsRequired.emplace_back(std::move(str));
5906 return true;
5907 });
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005908 }
5909
Syoyo Fujita83675312017-12-02 21:14:13 +09005910 // 3. Parse Buffer
5911 {
David03ad33c2023-02-15 23:35:51 -06005912 bool success = ForEachInArray(v, "buffers", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06005913 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005914 if (err) {
5915 (*err) += "`buffers' does not contain an JSON object.";
Syoyo Fujitabeded612016-05-01 20:03:43 +09005916 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005917 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005918 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005919 Buffer buffer;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005920 if (!ParseBuffer(&buffer, err, o,
5921 store_original_json_for_extras_and_extensions_, &fs,
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09005922 &uri_cb, base_dir, max_external_file_size_, is_binary_, bin_data_, bin_size_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005923 return false;
5924 }
5925
5926 model->buffers.emplace_back(std::move(buffer));
5927 return true;
5928 });
5929
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005930 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005931 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005932 }
5933 }
Syoyo Fujita83675312017-12-02 21:14:13 +09005934 // 4. Parse BufferView
5935 {
David03ad33c2023-02-15 23:35:51 -06005936 bool success = ForEachInArray(v, "bufferViews", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06005937 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005938 if (err) {
5939 (*err) += "`bufferViews' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09005940 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005941 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005942 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005943 BufferView bufferView;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005944 if (!ParseBufferView(&bufferView, err, o,
5945 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005946 return false;
5947 }
5948
5949 model->bufferViews.emplace_back(std::move(bufferView));
5950 return true;
5951 });
5952
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005953 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005954 return false;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005955 }
5956 }
5957
Syoyo Fujita83675312017-12-02 21:14:13 +09005958 // 5. Parse Accessor
5959 {
David03ad33c2023-02-15 23:35:51 -06005960 bool success = ForEachInArray(v, "accessors", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06005961 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005962 if (err) {
5963 (*err) += "`accessors' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09005964 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005965 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005966 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005967 Accessor accessor;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005968 if (!ParseAccessor(&accessor, err, o,
5969 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005970 return false;
5971 }
5972
5973 model->accessors.emplace_back(std::move(accessor));
5974 return true;
5975 });
5976
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005977 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005978 return false;
Syoyo Fujitac2615632016-06-19 21:56:06 +09005979 }
5980 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005981
Syoyo Fujita83675312017-12-02 21:14:13 +09005982 // 6. Parse Mesh
5983 {
David03ad33c2023-02-15 23:35:51 -06005984 bool success = ForEachInArray(v, "meshes", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06005985 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005986 if (err) {
5987 (*err) += "`meshes' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09005988 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005989 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005990 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005991 Mesh mesh;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005992 if (!ParseMesh(&mesh, model, err, o,
5993 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005994 return false;
5995 }
5996
5997 model->meshes.emplace_back(std::move(mesh));
5998 return true;
5999 });
6000
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006001 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006002 return false;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09006003 }
6004 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01006005
viperscape9df05802018-12-05 14:11:01 -05006006 // Assign missing bufferView target types
6007 // - Look for missing Mesh indices
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01006008 // - Look for missing Mesh attributes
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006009 for (auto &mesh : model->meshes) {
6010 for (auto &primitive : mesh.primitives) {
6011 if (primitive.indices >
6012 -1) // has indices from parsing step, must be Element Array Buffer
viperscape9df05802018-12-05 14:11:01 -05006013 {
Jeff McGlynn89152522019-04-25 16:33:56 -07006014 if (size_t(primitive.indices) >= model->accessors.size()) {
6015 if (err) {
6016 (*err) += "primitive indices accessor out of bounds";
6017 }
6018 return false;
6019 }
6020
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006021 auto bufferView =
6022 model->accessors[size_t(primitive.indices)].bufferView;
Jeff McGlynn89152522019-04-25 16:33:56 -07006023 if (bufferView < 0 || size_t(bufferView) >= model->bufferViews.size()) {
6024 if (err) {
6025 (*err) += "accessor[" + std::to_string(primitive.indices) +
6026 "] invalid bufferView";
6027 }
6028 return false;
6029 }
6030
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006031 model->bufferViews[size_t(bufferView)].target =
Jeff McGlynn89152522019-04-25 16:33:56 -07006032 TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER;
imallettd9ce9eb2022-10-07 10:37:09 -07006033 // we could optionally check if accessors' bufferView type is Scalar, as
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006034 // it should be
viperscape9df05802018-12-05 14:11:01 -05006035 }
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01006036
6037 for (auto &attribute : primitive.attributes) {
Nirmal Patele4132162022-09-06 09:16:31 -07006038 const auto accessorsIndex = size_t(attribute.second);
6039 if (accessorsIndex < model->accessors.size()) {
6040 const auto bufferView = model->accessors[accessorsIndex].bufferView;
6041 // bufferView could be null(-1) for sparse morph target
imallett56e10982022-10-07 10:35:16 -07006042 if (bufferView >= 0 && bufferView < (int)model->bufferViews.size()) {
Nirmal Patele4132162022-09-06 09:16:31 -07006043 model->bufferViews[size_t(bufferView)].target =
6044 TINYGLTF_TARGET_ARRAY_BUFFER;
6045 }
6046 }
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01006047 }
Marcin Kacprzakc3d67162020-01-22 13:13:35 +01006048
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09006049 for (auto &target : primitive.targets) {
6050 for (auto &attribute : target) {
Nirmal Patele4132162022-09-06 09:16:31 -07006051 const auto accessorsIndex = size_t(attribute.second);
6052 if (accessorsIndex < model->accessors.size()) {
6053 const auto bufferView = model->accessors[accessorsIndex].bufferView;
6054 // bufferView could be null(-1) for sparse morph target
imallett56e10982022-10-07 10:35:16 -07006055 if (bufferView >= 0 && bufferView < (int)model->bufferViews.size()) {
Nirmal Patele4132162022-09-06 09:16:31 -07006056 model->bufferViews[size_t(bufferView)].target =
6057 TINYGLTF_TARGET_ARRAY_BUFFER;
6058 }
Rahul Sheth125e4a22020-07-13 13:56:50 -04006059 }
Marcin Kacprzakc3d67162020-01-22 13:13:35 +01006060 }
6061 }
viperscape9df05802018-12-05 14:11:01 -05006062 }
6063 }
6064
Syoyo Fujita83675312017-12-02 21:14:13 +09006065 // 7. Parse Node
6066 {
David03ad33c2023-02-15 23:35:51 -06006067 bool success = ForEachInArray(v, "nodes", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006068 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006069 if (err) {
6070 (*err) += "`nodes' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006071 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006072 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006073 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006074 Node node;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006075 if (!ParseNode(&node, err, o,
6076 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006077 return false;
6078 }
6079
6080 model->nodes.emplace_back(std::move(node));
6081 return true;
6082 });
6083
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006084 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006085 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006086 }
6087 }
6088
6089 // 8. Parse scenes.
6090 {
David03ad33c2023-02-15 23:35:51 -06006091 bool success = ForEachInArray(v, "scenes", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006092 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006093 if (err) {
6094 (*err) += "`scenes' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006095 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006096 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006097 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006098
6099 Scene scene;
Baranob_Ilya879cb472023-06-12 13:35:05 +04006100 if (!ParseScene(&scene, err, o,
6101 store_original_json_for_extras_and_extensions_)) {
6102 return false;
6103 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006104
jrkooncecba5d6c2019-08-29 11:26:22 -05006105 model->scenes.emplace_back(std::move(scene));
6106 return true;
6107 });
6108
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006109 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006110 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006111 }
6112 }
6113
6114 // 9. Parse default scenes.
6115 {
David03ad33c2023-02-15 23:35:51 -06006116 detail::json_const_iterator rootIt;
jrkooncecba5d6c2019-08-29 11:26:22 -05006117 int iVal;
David1f9a4b92023-02-15 22:56:18 -06006118 if (detail::FindMember(v, "scene", rootIt) && detail::GetInt(detail::GetValue(rootIt), iVal)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006119 model->defaultScene = iVal;
Syoyo Fujita83675312017-12-02 21:14:13 +09006120 }
6121 }
6122
6123 // 10. Parse Material
6124 {
David03ad33c2023-02-15 23:35:51 -06006125 bool success = ForEachInArray(v, "materials", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006126 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006127 if (err) {
6128 (*err) += "`materials' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006129 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006130 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006131 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006132 Material material;
6133 ParseStringProperty(&material.name, err, o, "name", false);
6134
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006135 if (!ParseMaterial(&material, err, o,
6136 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006137 return false;
6138 }
6139
6140 model->materials.emplace_back(std::move(material));
6141 return true;
6142 });
6143
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006144 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006145 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006146 }
6147 }
6148
6149 // 11. Parse Image
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09006150 void *load_image_user_data{nullptr};
6151
6152 LoadImageDataOption load_image_option;
6153
6154 if (user_image_loader_) {
6155 // Use user supplied pointer
6156 load_image_user_data = load_image_user_data_;
6157 } else {
6158 load_image_option.preserve_channels = preserve_image_channels_;
6159 load_image_user_data = reinterpret_cast<void *>(&load_image_option);
6160 }
6161
Syoyo Fujita83675312017-12-02 21:14:13 +09006162 {
jrkooncecba5d6c2019-08-29 11:26:22 -05006163 int idx = 0;
David03ad33c2023-02-15 23:35:51 -06006164 bool success = ForEachInArray(v, "images", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006165 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006166 if (err) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006167 (*err) += "image[" + std::to_string(idx) + "] is not a JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006168 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006169 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006170 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006171 Image image;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006172 if (!ParseImage(&image, idx, err, warn, o,
6173 store_original_json_for_extras_and_extensions_, base_dir,
Syoyo Fujitaecfd37d2023-04-23 21:31:30 +09006174 max_external_file_size_, &fs, &uri_cb, &this->LoadImageData,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08006175 load_image_user_data)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006176 return false;
6177 }
6178
6179 if (image.bufferView != -1) {
6180 // Load image from the buffer view.
6181 if (size_t(image.bufferView) >= model->bufferViews.size()) {
6182 if (err) {
6183 std::stringstream ss;
6184 ss << "image[" << idx << "] bufferView \"" << image.bufferView
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006185 << "\" not found in the scene." << std::endl;
jrkooncecba5d6c2019-08-29 11:26:22 -05006186 (*err) += ss.str();
6187 }
6188 return false;
6189 }
6190
6191 const BufferView &bufferView =
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006192 model->bufferViews[size_t(image.bufferView)];
jrkooncecba5d6c2019-08-29 11:26:22 -05006193 if (size_t(bufferView.buffer) >= model->buffers.size()) {
6194 if (err) {
6195 std::stringstream ss;
6196 ss << "image[" << idx << "] buffer \"" << bufferView.buffer
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006197 << "\" not found in the scene." << std::endl;
jrkooncecba5d6c2019-08-29 11:26:22 -05006198 (*err) += ss.str();
6199 }
6200 return false;
6201 }
6202 const Buffer &buffer = model->buffers[size_t(bufferView.buffer)];
6203
6204 if (*LoadImageData == nullptr) {
6205 if (err) {
6206 (*err) += "No LoadImageData callback specified.\n";
6207 }
6208 return false;
6209 }
6210 bool ret = LoadImageData(
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006211 &image, idx, err, warn, image.width, image.height,
6212 &buffer.data[bufferView.byteOffset],
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09006213 static_cast<int>(bufferView.byteLength), load_image_user_data);
jrkooncecba5d6c2019-08-29 11:26:22 -05006214 if (!ret) {
6215 return false;
6216 }
6217 }
6218
6219 model->images.emplace_back(std::move(image));
6220 ++idx;
6221 return true;
6222 });
6223
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006224 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006225 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006226 }
6227 }
6228
6229 // 12. Parse Texture
6230 {
David03ad33c2023-02-15 23:35:51 -06006231 bool success = ForEachInArray(v, "textures", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006232 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006233 if (err) {
6234 (*err) += "`textures' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006235 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006236 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006237 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006238 Texture texture;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006239 if (!ParseTexture(&texture, err, o,
6240 store_original_json_for_extras_and_extensions_,
6241 base_dir)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006242 return false;
6243 }
6244
6245 model->textures.emplace_back(std::move(texture));
6246 return true;
6247 });
6248
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006249 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006250 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006251 }
6252 }
6253
6254 // 13. Parse Animation
6255 {
David03ad33c2023-02-15 23:35:51 -06006256 bool success = ForEachInArray(v, "animations", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006257 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006258 if (err) {
6259 (*err) += "`animations' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006260 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006261 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006262 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006263 Animation animation;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006264 if (!ParseAnimation(&animation, err, o,
6265 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006266 return false;
6267 }
6268
6269 model->animations.emplace_back(std::move(animation));
6270 return true;
6271 });
6272
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006273 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006274 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006275 }
6276 }
6277
6278 // 14. Parse Skin
6279 {
David03ad33c2023-02-15 23:35:51 -06006280 bool success = ForEachInArray(v, "skins", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006281 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006282 if (err) {
6283 (*err) += "`skins' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006284 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006285 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006286 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006287 Skin skin;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006288 if (!ParseSkin(&skin, err, o,
6289 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006290 return false;
6291 }
6292
6293 model->skins.emplace_back(std::move(skin));
6294 return true;
6295 });
6296
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006297 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006298 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006299 }
6300 }
6301
6302 // 15. Parse Sampler
6303 {
David03ad33c2023-02-15 23:35:51 -06006304 bool success = ForEachInArray(v, "samplers", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006305 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006306 if (err) {
6307 (*err) += "`samplers' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006308 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006309 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006310 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006311 Sampler sampler;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006312 if (!ParseSampler(&sampler, err, o,
6313 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006314 return false;
6315 }
6316
6317 model->samplers.emplace_back(std::move(sampler));
6318 return true;
6319 });
6320
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006321 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006322 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006323 }
6324 }
6325
6326 // 16. Parse Camera
6327 {
David03ad33c2023-02-15 23:35:51 -06006328 bool success = ForEachInArray(v, "cameras", [&](const detail::json &o) {
David1f9a4b92023-02-15 22:56:18 -06006329 if (!detail::IsObject(o)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006330 if (err) {
6331 (*err) += "`cameras' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006332 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006333 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006334 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006335 Camera camera;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006336 if (!ParseCamera(&camera, err, o,
6337 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006338 return false;
6339 }
6340
6341 model->cameras.emplace_back(std::move(camera));
6342 return true;
6343 });
6344
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006345 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006346 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006347 }
6348 }
6349
David Siegel22cafa12023-06-05 22:18:59 +02006350 // 17. Parse Extras & Extensions
6351 ParseExtrasAndExtensions(model, err, v, store_original_json_for_extras_and_extensions_);
Selmar09d2ff12018-03-15 17:30:42 +01006352
6353 // 18. Specific extension implementations
Syoyo Fujita83675312017-12-02 21:14:13 +09006354 {
David03ad33c2023-02-15 23:35:51 -06006355 detail::json_const_iterator rootIt;
David1f9a4b92023-02-15 22:56:18 -06006356 if (detail::FindMember(v, "extensions", rootIt) && detail::IsObject(detail::GetValue(rootIt))) {
David03ad33c2023-02-15 23:35:51 -06006357 const detail::json &root = detail::GetValue(rootIt);
Syoyo Fujita83675312017-12-02 21:14:13 +09006358
David03ad33c2023-02-15 23:35:51 -06006359 detail::json_const_iterator it(detail::ObjectBegin(root));
6360 detail::json_const_iterator itEnd(detail::ObjectEnd(root));
Syoyo Fujita83675312017-12-02 21:14:13 +09006361 for (; it != itEnd; ++it) {
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02006362 // parse KHR_lights_punctual extension
David1f9a4b92023-02-15 22:56:18 -06006363 std::string key(detail::GetKey(it));
6364 if ((key == "KHR_lights_punctual") && detail::IsObject(detail::GetValue(it))) {
David03ad33c2023-02-15 23:35:51 -06006365 const detail::json &object = detail::GetValue(it);
6366 detail::json_const_iterator itLight;
David1f9a4b92023-02-15 22:56:18 -06006367 if (detail::FindMember(object, "lights", itLight)) {
David03ad33c2023-02-15 23:35:51 -06006368 const detail::json &lights = detail::GetValue(itLight);
David1f9a4b92023-02-15 22:56:18 -06006369 if (!detail::IsArray(lights)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006370 continue;
Syoyo Fujita83675312017-12-02 21:14:13 +09006371 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006372
David1f9a4b92023-02-15 22:56:18 -06006373 auto arrayIt(detail::ArrayBegin(lights));
6374 auto arrayItEnd(detail::ArrayEnd(lights));
jrkooncecba5d6c2019-08-29 11:26:22 -05006375 for (; arrayIt != arrayItEnd; ++arrayIt) {
6376 Light light;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006377 if (!ParseLight(&light, err, *arrayIt,
6378 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006379 return false;
6380 }
6381 model->lights.emplace_back(std::move(light));
6382 }
Syoyo Fujita83675312017-12-02 21:14:13 +09006383 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01006384 }
Baranob_Ilya78864c82023-06-12 10:43:52 +04006385 // parse KHR_audio extension
6386 if ((key == "KHR_audio") &&
6387 detail::IsObject(detail::GetValue(it))) {
6388 const detail::json &object = detail::GetValue(it);
6389 detail::json_const_iterator itKhrAudio;
6390 if (detail::FindMember(object, "emitters", itKhrAudio)) {
6391 const detail::json &emitters = detail::GetValue(itKhrAudio);
6392 if (!detail::IsArray(emitters)) {
6393 continue;
6394 }
6395
6396 auto arrayIt(detail::ArrayBegin(emitters));
6397 auto arrayItEnd(detail::ArrayEnd(emitters));
6398 for (; arrayIt != arrayItEnd; ++arrayIt) {
6399 AudioEmitter emitter;
6400 if (!ParseAudioEmitter(&emitter, err, *arrayIt,
6401 store_original_json_for_extras_and_extensions_)) {
6402 return false;
6403 }
6404 model->audioEmitters.emplace_back(std::move(emitter));
6405 }
6406 }
6407
6408 if (detail::FindMember(object, "sources", itKhrAudio)) {
6409 const detail::json &sources = detail::GetValue(itKhrAudio);
6410 if (!detail::IsArray(sources)) {
6411 continue;
6412 }
6413
6414 auto arrayIt(detail::ArrayBegin(sources));
6415 auto arrayItEnd(detail::ArrayEnd(sources));
6416 for (; arrayIt != arrayItEnd; ++arrayIt) {
6417 AudioSource source;
6418 if (!ParseAudioSource(
6419 &source, err, *arrayIt,
6420 store_original_json_for_extras_and_extensions_)) {
6421 return false;
6422 }
6423 model->audioSources.emplace_back(std::move(source));
6424 }
6425 }
6426 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01006427 }
6428 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01006429 }
Syoyo Fujita83675312017-12-02 21:14:13 +09006430
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09006431 return true;
6432}
6433
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006434bool TinyGLTF::LoadASCIIFromString(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006435 std::string *warn, const char *str,
6436 unsigned int length,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006437 const std::string &base_dir,
6438 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006439 is_binary_ = false;
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09006440 bin_data_ = nullptr;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006441 bin_size_ = 0;
6442
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006443 return LoadFromString(model, err, warn, str, length, base_dir,
6444 check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006445}
6446
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006447bool TinyGLTF::LoadASCIIFromFile(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006448 std::string *warn, const std::string &filename,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006449 unsigned int check_sections) {
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006450 std::stringstream ss;
6451
Paolo Jovone6601bf2018-07-07 20:43:33 +02006452 if (fs.ReadWholeFile == nullptr) {
6453 // Programmer error, assert() ?
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006454 ss << "Failed to read file: " << filename
6455 << ": one or more FS callback not set" << std::endl;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09006456 if (err) {
6457 (*err) = ss.str();
6458 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006459 return false;
6460 }
6461
Paolo Jovone6601bf2018-07-07 20:43:33 +02006462 std::vector<unsigned char> data;
6463 std::string fileerr;
6464 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006465 if (!fileread) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02006466 ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
6467 if (err) {
6468 (*err) = ss.str();
6469 }
6470 return false;
6471 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006472
Paolo Jovone6601bf2018-07-07 20:43:33 +02006473 size_t sz = data.size();
Luke San Antoniob310b4a2016-06-23 14:17:37 -04006474 if (sz == 0) {
6475 if (err) {
6476 (*err) = "Empty file.";
6477 }
6478 return false;
6479 }
6480
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006481 std::string basedir = GetBaseDir(filename);
6482
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006483 bool ret = LoadASCIIFromString(
6484 model, err, warn, reinterpret_cast<const char *>(&data.at(0)),
6485 static_cast<unsigned int>(data.size()), basedir, check_sections);
Luke San Antonio9e36b612016-06-23 14:10:51 -04006486
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006487 return ret;
6488}
6489
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006490bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006491 std::string *warn,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006492 const unsigned char *bytes,
6493 unsigned int size,
6494 const std::string &base_dir,
6495 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006496 if (size < 20) {
6497 if (err) {
6498 (*err) = "Too short data size for glTF Binary.";
6499 }
6500 return false;
6501 }
6502
Syoyo Fujitabeded612016-05-01 20:03:43 +09006503 if (bytes[0] == 'g' && bytes[1] == 'l' && bytes[2] == 'T' &&
6504 bytes[3] == 'F') {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006505 // ok
6506 } else {
6507 if (err) {
6508 (*err) = "Invalid magic.";
6509 }
6510 return false;
6511 }
6512
Syoyo Fujitabeded612016-05-01 20:03:43 +09006513 unsigned int version; // 4 bytes
6514 unsigned int length; // 4 bytes
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006515 unsigned int chunk0_length; // 4 bytes
6516 unsigned int chunk0_format; // 4 bytes;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006517
Syoyo Fujitabeded612016-05-01 20:03:43 +09006518 memcpy(&version, bytes + 4, 4);
6519 swap4(&version);
6520 memcpy(&length, bytes + 8, 4);
6521 swap4(&length);
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006522 memcpy(&chunk0_length, bytes + 12, 4); // JSON data length
6523 swap4(&chunk0_length);
6524 memcpy(&chunk0_format, bytes + 16, 4);
6525 swap4(&chunk0_format);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006526
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006527 // https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#binary-gltf-layout
6528 //
Syoyo Fujitae69069d2018-03-15 22:01:18 -05006529 // In case the Bin buffer is not present, the size is exactly 20 + size of
6530 // JSON contents,
6531 // so use "greater than" operator.
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006532 //
6533 // https://github.com/syoyo/tinygltf/issues/372
6534 // Use 64bit uint to avoid integer overflow.
Syoyo Fujitac670f082022-09-17 19:52:25 +09006535 uint64_t header_and_json_size = 20ull + uint64_t(chunk0_length);
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006536
Syoyo Fujitac670f082022-09-17 19:52:25 +09006537 if (header_and_json_size > std::numeric_limits<uint32_t>::max()) {
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006538 // Do not allow 4GB or more GLB data.
6539 (*err) = "Invalid glTF binary. GLB data exceeds 4GB.";
6540 }
6541
Syoyo Fujitac670f082022-09-17 19:52:25 +09006542 if ((header_and_json_size > uint64_t(size)) || (chunk0_length < 1) || (length > size) ||
6543 (header_and_json_size > uint64_t(length)) ||
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006544 (chunk0_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006545 if (err) {
6546 (*err) = "Invalid glTF binary.";
6547 }
6548 return false;
6549 }
6550
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006551 // Padding check
6552 // The start and the end of each chunk must be aligned to a 4-byte boundary.
6553 // No padding check for chunk0 start since its 4byte-boundary is ensured.
Syoyo Fujitac670f082022-09-17 19:52:25 +09006554 if ((header_and_json_size % 4) != 0) {
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006555 if (err) {
6556 (*err) = "JSON Chunk end does not aligned to a 4-byte boundary.";
6557 }
6558 }
6559
Syoyo Fujita612e5782022-09-18 21:01:39 +09006560 //std::cout << "header_and_json_size = " << header_and_json_size << "\n";
6561 //std::cout << "length = " << length << "\n";
6562
Syoyo Fujitac670f082022-09-17 19:52:25 +09006563 // Chunk1(BIN) data
6564 // The spec says: When the binary buffer is empty or when it is stored by other means, this chunk SHOULD be omitted.
Syoyo Fujita612e5782022-09-18 21:01:39 +09006565 // So when header + JSON data == binary size, Chunk1 is omitted.
6566 if (header_and_json_size == uint64_t(length)) {
Syoyo Fujitac670f082022-09-17 19:52:25 +09006567
Syoyo Fujitac670f082022-09-17 19:52:25 +09006568 bin_data_ = nullptr;
6569 bin_size_ = 0;
6570 } else {
6571 // Read Chunk1 info(BIN data)
imallettd9ce9eb2022-10-07 10:37:09 -07006572 // At least Chunk1 should have 12 bytes(8 bytes(header) + 4 bytes(bin payload could be 1~3 bytes, but need to be aligned to 4 bytes)
Syoyo Fujita612e5782022-09-18 21:01:39 +09006573 if ((header_and_json_size + 12ull) > uint64_t(length)) {
Syoyo Fujitac670f082022-09-17 19:52:25 +09006574 if (err) {
Syoyo Fujita612e5782022-09-18 21:01:39 +09006575 (*err) = "Insufficient storage space for Chunk1(BIN data). At least Chunk1 Must have 4 bytes or more bytes, but got " + std::to_string((header_and_json_size + 12ull) - uint64_t(length)) + ".\n";
Syoyo Fujitac670f082022-09-17 19:52:25 +09006576 }
6577 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006578 }
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006579
Syoyo Fujitac670f082022-09-17 19:52:25 +09006580 unsigned int chunk1_length; // 4 bytes
6581 unsigned int chunk1_format; // 4 bytes;
6582 memcpy(&chunk1_length, bytes + header_and_json_size, 4); // JSON data length
6583 swap4(&chunk1_length);
6584 memcpy(&chunk1_format, bytes + header_and_json_size + 4, 4);
6585 swap4(&chunk1_format);
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006586
Syoyo Fujita612e5782022-09-18 21:01:39 +09006587 //std::cout << "chunk1_length = " << chunk1_length << "\n";
6588
Syoyo Fujitac670f082022-09-17 19:52:25 +09006589 if (chunk1_length < 4) {
6590 if (err) {
6591 (*err) = "Insufficient Chunk1(BIN) data size.";
6592 }
6593 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006594 }
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006595
Syoyo Fujitac670f082022-09-17 19:52:25 +09006596 if ((chunk1_length % 4) != 0) {
6597 if (err) {
6598 (*err) = "BIN Chunk end does not aligned to a 4-byte boundary.";
6599 }
6600 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006601 }
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006602
Syoyo Fujitac670f082022-09-17 19:52:25 +09006603 if (uint64_t(chunk1_length) + header_and_json_size > uint64_t(length)) {
6604 if (err) {
6605 (*err) = "BIN Chunk data length exceeds the GLB size.";
6606 }
6607 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006608 }
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006609
Syoyo Fujitac670f082022-09-17 19:52:25 +09006610 if (chunk1_format != 0x004e4942) {
6611 if (err) {
imallettd9ce9eb2022-10-07 10:37:09 -07006612 (*err) = "Invalid type for chunk1 data.";
Syoyo Fujitac670f082022-09-17 19:52:25 +09006613 }
6614 return false;
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006615 }
Syoyo Fujitac670f082022-09-17 19:52:25 +09006616
Syoyo Fujita612e5782022-09-18 21:01:39 +09006617 //std::cout << "chunk1_length = " << chunk1_length << "\n";
6618
Syoyo Fujitac670f082022-09-17 19:52:25 +09006619 bin_data_ = bytes + header_and_json_size +
6620 8; // 4 bytes (bin_buffer_length) + 4 bytes(bin_buffer_format)
6621
6622 bin_size_ = size_t(chunk1_length);
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006623 }
6624
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006625 // Extract JSON string.
Syoyo Fujitabeded612016-05-01 20:03:43 +09006626 std::string jsonString(reinterpret_cast<const char *>(&bytes[20]),
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006627 chunk0_length);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006628
6629 is_binary_ = true;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006630
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006631 bool ret = LoadFromString(model, err, warn,
6632 reinterpret_cast<const char *>(&bytes[20]),
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006633 chunk0_length, base_dir, check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006634 if (!ret) {
6635 return ret;
6636 }
6637
6638 return true;
6639}
6640
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006641bool TinyGLTF::LoadBinaryFromFile(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006642 std::string *warn,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006643 const std::string &filename,
6644 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006645 std::stringstream ss;
6646
Paolo Jovone6601bf2018-07-07 20:43:33 +02006647 if (fs.ReadWholeFile == nullptr) {
6648 // Programmer error, assert() ?
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006649 ss << "Failed to read file: " << filename
6650 << ": one or more FS callback not set" << std::endl;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006651 if (err) {
6652 (*err) = ss.str();
6653 }
6654 return false;
6655 }
6656
Paolo Jovone6601bf2018-07-07 20:43:33 +02006657 std::vector<unsigned char> data;
6658 std::string fileerr;
6659 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006660 if (!fileread) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02006661 ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
6662 if (err) {
6663 (*err) = ss.str();
6664 }
6665 return false;
6666 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006667
Syoyo Fujitabeded612016-05-01 20:03:43 +09006668 std::string basedir = GetBaseDir(filename);
6669
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006670 bool ret = LoadBinaryFromMemory(model, err, warn, &data.at(0),
6671 static_cast<unsigned int>(data.size()),
6672 basedir, check_sections);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006673
6674 return ret;
6675}
6676
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006677///////////////////////
6678// GLTF Serialization
6679///////////////////////
David1f9a4b92023-02-15 22:56:18 -06006680namespace detail {
David03ad33c2023-02-15 23:35:51 -06006681detail::json JsonFromString(const char *s) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006682#ifdef TINYGLTF_USE_RAPIDJSON
David03ad33c2023-02-15 23:35:51 -06006683 return detail::json(s, detail::GetAllocator());
jrkooncecba5d6c2019-08-29 11:26:22 -05006684#else
David03ad33c2023-02-15 23:35:51 -06006685 return detail::json(s);
jrkooncecba5d6c2019-08-29 11:26:22 -05006686#endif
jrkoonce63419a12019-09-03 17:06:41 -05006687}
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006688
David03ad33c2023-02-15 23:35:51 -06006689void JsonAssign(detail::json &dest, const detail::json &src) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006690#ifdef TINYGLTF_USE_RAPIDJSON
David03ad33c2023-02-15 23:35:51 -06006691 dest.CopyFrom(src, detail::GetAllocator());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006692#else
6693 dest = src;
6694#endif
6695}
6696
David03ad33c2023-02-15 23:35:51 -06006697void JsonAddMember(detail::json &o, const char *key, detail::json &&value) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006698#ifdef TINYGLTF_USE_RAPIDJSON
6699 if (!o.IsObject()) {
6700 o.SetObject();
6701 }
Syoyo Fujita147a00a2023-06-04 05:45:24 +09006702
6703 // Issue 420.
6704 // AddMember may create duplicated key, so use [] API when a key already exists.
6705 // https://github.com/Tencent/rapidjson/issues/771#issuecomment-254386863
6706 detail::json_const_iterator it;
6707 if (detail::FindMember(o, key, it)) {
6708 o[key] = std::move(value); // replace
6709 } else {
6710 o.AddMember(detail::json(key, detail::GetAllocator()), std::move(value), detail::GetAllocator());
6711 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006712#else
6713 o[key] = std::move(value);
6714#endif
6715}
6716
David03ad33c2023-02-15 23:35:51 -06006717void JsonPushBack(detail::json &o, detail::json &&value) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006718#ifdef TINYGLTF_USE_RAPIDJSON
David03ad33c2023-02-15 23:35:51 -06006719 o.PushBack(std::move(value), detail::GetAllocator());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006720#else
6721 o.push_back(std::move(value));
6722#endif
6723}
6724
David03ad33c2023-02-15 23:35:51 -06006725bool JsonIsNull(const detail::json &o) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006726#ifdef TINYGLTF_USE_RAPIDJSON
6727 return o.IsNull();
6728#else
6729 return o.is_null();
6730#endif
6731}
6732
David03ad33c2023-02-15 23:35:51 -06006733void JsonSetObject(detail::json &o) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006734#ifdef TINYGLTF_USE_RAPIDJSON
6735 o.SetObject();
6736#else
6737 o = o.object({});
6738#endif
6739}
6740
David03ad33c2023-02-15 23:35:51 -06006741void JsonReserveArray(detail::json &o, size_t s) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006742#ifdef TINYGLTF_USE_RAPIDJSON
6743 o.SetArray();
David03ad33c2023-02-15 23:35:51 -06006744 o.Reserve(static_cast<rapidjson::SizeType>(s), detail::GetAllocator());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006745#endif
6746 (void)(o);
6747 (void)(s);
6748}
6749} // namespace
6750
David03ad33c2023-02-15 23:35:51 -06006751// typedef std::pair<std::string, detail::json> json_object_pair;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006752
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006753template <typename T>
6754static void SerializeNumberProperty(const std::string &key, T number,
David03ad33c2023-02-15 23:35:51 -06006755 detail::json &obj) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006756 // obj.insert(
David03ad33c2023-02-15 23:35:51 -06006757 // json_object_pair(key, detail::json(static_cast<double>(number))));
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006758 // obj[key] = static_cast<double>(number);
David03ad33c2023-02-15 23:35:51 -06006759 detail::JsonAddMember(obj, key.c_str(), detail::json(number));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006760}
6761
Ivo van Dongen272a9df2020-05-29 11:24:11 +03006762#ifdef TINYGLTF_USE_RAPIDJSON
6763template <>
David03ad33c2023-02-15 23:35:51 -06006764void SerializeNumberProperty(const std::string &key, size_t number, detail::json &obj) {
6765 detail::JsonAddMember(obj, key.c_str(), detail::json(static_cast<uint64_t>(number)));
Ivo van Dongen272a9df2020-05-29 11:24:11 +03006766}
6767#endif
6768
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006769template <typename T>
6770static void SerializeNumberArrayProperty(const std::string &key,
6771 const std::vector<T> &value,
David03ad33c2023-02-15 23:35:51 -06006772 detail::json &obj) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006773 if (value.empty()) return;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006774
David03ad33c2023-02-15 23:35:51 -06006775 detail::json ary;
David1f9a4b92023-02-15 22:56:18 -06006776 detail::JsonReserveArray(ary, value.size());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006777 for (const auto &s : value) {
David03ad33c2023-02-15 23:35:51 -06006778 detail::JsonPushBack(ary, detail::json(s));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006779 }
David1f9a4b92023-02-15 22:56:18 -06006780 detail::JsonAddMember(obj, key.c_str(), std::move(ary));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006781}
6782
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006783static void SerializeStringProperty(const std::string &key,
David03ad33c2023-02-15 23:35:51 -06006784 const std::string &value, detail::json &obj) {
David1f9a4b92023-02-15 22:56:18 -06006785 detail::JsonAddMember(obj, key.c_str(), detail::JsonFromString(value.c_str()));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006786}
6787
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006788static void SerializeStringArrayProperty(const std::string &key,
6789 const std::vector<std::string> &value,
David03ad33c2023-02-15 23:35:51 -06006790 detail::json &obj) {
6791 detail::json ary;
David1f9a4b92023-02-15 22:56:18 -06006792 detail::JsonReserveArray(ary, value.size());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006793 for (auto &s : value) {
David1f9a4b92023-02-15 22:56:18 -06006794 detail::JsonPushBack(ary, detail::JsonFromString(s.c_str()));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006795 }
David1f9a4b92023-02-15 22:56:18 -06006796 detail::JsonAddMember(obj, key.c_str(), std::move(ary));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006797}
6798
David03ad33c2023-02-15 23:35:51 -06006799static bool ValueToJson(const Value &value, detail::json *ret) {
6800 detail::json obj;
jrkooncecba5d6c2019-08-29 11:26:22 -05006801#ifdef TINYGLTF_USE_RAPIDJSON
6802 switch (value.Type()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006803 case REAL_TYPE:
6804 obj.SetDouble(value.Get<double>());
6805 break;
6806 case INT_TYPE:
6807 obj.SetInt(value.Get<int>());
6808 break;
6809 case BOOL_TYPE:
6810 obj.SetBool(value.Get<bool>());
6811 break;
6812 case STRING_TYPE:
David03ad33c2023-02-15 23:35:51 -06006813 obj.SetString(value.Get<std::string>().c_str(), detail::GetAllocator());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006814 break;
6815 case ARRAY_TYPE: {
6816 obj.SetArray();
6817 obj.Reserve(static_cast<rapidjson::SizeType>(value.ArrayLen()),
David03ad33c2023-02-15 23:35:51 -06006818 detail::GetAllocator());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006819 for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
6820 Value elementValue = value.Get(int(i));
David03ad33c2023-02-15 23:35:51 -06006821 detail::json elementJson;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006822 if (ValueToJson(value.Get(int(i)), &elementJson))
David03ad33c2023-02-15 23:35:51 -06006823 obj.PushBack(std::move(elementJson), detail::GetAllocator());
jrkooncecba5d6c2019-08-29 11:26:22 -05006824 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006825 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05006826 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006827 case BINARY_TYPE:
6828 // TODO
David03ad33c2023-02-15 23:35:51 -06006829 // obj = detail::json(value.Get<std::vector<unsigned char>>());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006830 return false;
6831 break;
6832 case OBJECT_TYPE: {
6833 obj.SetObject();
6834 Value::Object objMap = value.Get<Value::Object>();
6835 for (auto &it : objMap) {
David03ad33c2023-02-15 23:35:51 -06006836 detail::json elementJson;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006837 if (ValueToJson(it.second, &elementJson)) {
David03ad33c2023-02-15 23:35:51 -06006838 obj.AddMember(detail::json(it.first.c_str(), detail::GetAllocator()),
6839 std::move(elementJson), detail::GetAllocator());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006840 }
6841 }
6842 break;
6843 }
6844 case NULL_TYPE:
6845 default:
6846 return false;
jrkooncecba5d6c2019-08-29 11:26:22 -05006847 }
6848#else
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006849 switch (value.Type()) {
Syoyo Fujita150f2432019-07-25 19:22:44 +09006850 case REAL_TYPE:
David03ad33c2023-02-15 23:35:51 -06006851 obj = detail::json(value.Get<double>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006852 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006853 case INT_TYPE:
David03ad33c2023-02-15 23:35:51 -06006854 obj = detail::json(value.Get<int>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006855 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006856 case BOOL_TYPE:
David03ad33c2023-02-15 23:35:51 -06006857 obj = detail::json(value.Get<bool>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006858 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006859 case STRING_TYPE:
David03ad33c2023-02-15 23:35:51 -06006860 obj = detail::json(value.Get<std::string>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006861 break;
6862 case ARRAY_TYPE: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006863 for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
6864 Value elementValue = value.Get(int(i));
David03ad33c2023-02-15 23:35:51 -06006865 detail::json elementJson;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006866 if (ValueToJson(value.Get(int(i)), &elementJson))
Selmar Kokfa7022f2018-04-04 18:10:20 +02006867 obj.push_back(elementJson);
6868 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006869 break;
6870 }
Selmar Kokfa7022f2018-04-04 18:10:20 +02006871 case BINARY_TYPE:
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006872 // TODO
6873 // obj = json(value.Get<std::vector<unsigned char>>());
Selmar Kokfa7022f2018-04-04 18:10:20 +02006874 return false;
6875 break;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006876 case OBJECT_TYPE: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006877 Value::Object objMap = value.Get<Value::Object>();
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006878 for (auto &it : objMap) {
David03ad33c2023-02-15 23:35:51 -06006879 detail::json elementJson;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006880 if (ValueToJson(it.second, &elementJson)) obj[it.first] = elementJson;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006881 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006882 break;
6883 }
6884 case NULL_TYPE:
6885 default:
6886 return false;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006887 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006888#endif
6889 if (ret) *ret = std::move(obj);
Selmar Kokfa7022f2018-04-04 18:10:20 +02006890 return true;
6891}
6892
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006893static void SerializeValue(const std::string &key, const Value &value,
David03ad33c2023-02-15 23:35:51 -06006894 detail::json &obj) {
6895 detail::json ret;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006896 if (ValueToJson(value, &ret)) {
David1f9a4b92023-02-15 22:56:18 -06006897 detail::JsonAddMember(obj, key.c_str(), std::move(ret));
jrkooncecba5d6c2019-08-29 11:26:22 -05006898 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006899}
6900
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006901static void SerializeGltfBufferData(const std::vector<unsigned char> &data,
David03ad33c2023-02-15 23:35:51 -06006902 detail::json &o) {
johan bowald30c53472018-03-30 11:49:36 +02006903 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09006904 if (data.size() > 0) {
6905 std::string encodedData =
6906 base64_encode(&data[0], static_cast<unsigned int>(data.size()));
6907 SerializeStringProperty("uri", header + encodedData, o);
6908 } else {
6909 // Issue #229
imallettd9ce9eb2022-10-07 10:37:09 -07006910 // size 0 is allowed. Just emit mime header.
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09006911 SerializeStringProperty("uri", header, o);
6912 }
johan bowald30c53472018-03-30 11:49:36 +02006913}
6914
Selmar Koke4677492018-10-25 16:45:49 +02006915static bool SerializeGltfBufferData(const std::vector<unsigned char> &data,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006916 const std::string &binFilename) {
Harokyangfb256602019-10-30 16:13:52 +08006917#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09006918#if defined(__GLIBCXX__) // mingw
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09006919 int file_descriptor = _wopen(UTF8ToWchar(binFilename).c_str(),
6920 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
6921 __gnu_cxx::stdio_filebuf<char> wfile_buf(
6922 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09006923 std::ostream output(&wfile_buf);
6924 if (!wfile_buf.is_open()) return false;
6925#elif defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08006926 std::ofstream output(UTF8ToWchar(binFilename).c_str(), std::ofstream::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09006927 if (!output.is_open()) return false;
Harokyangfb256602019-10-30 16:13:52 +08006928#else
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006929 std::ofstream output(binFilename.c_str(), std::ofstream::binary);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006930 if (!output.is_open()) return false;
Syoyo Fujita45cac782019-11-09 20:42:55 +09006931#endif
6932#else
6933 std::ofstream output(binFilename.c_str(), std::ofstream::binary);
6934 if (!output.is_open()) return false;
6935#endif
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09006936 if (data.size() > 0) {
6937 output.write(reinterpret_cast<const char *>(&data[0]),
6938 std::streamsize(data.size()));
6939 } else {
6940 // Issue #229
6941 // size 0 will be still valid buffer data.
6942 // write empty file.
6943 }
Selmar Koke4677492018-10-25 16:45:49 +02006944 return true;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006945}
6946
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006947#if 0 // FIXME(syoyo): not used. will be removed in the future release.
David03ad33c2023-02-15 23:35:51 -06006948static void SerializeParameterMap(ParameterMap &param, detail::json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006949 for (ParameterMap::iterator paramIt = param.begin(); paramIt != param.end();
6950 ++paramIt) {
6951 if (paramIt->second.number_array.size()) {
6952 SerializeNumberArrayProperty<double>(paramIt->first,
6953 paramIt->second.number_array, o);
6954 } else if (paramIt->second.json_double_value.size()) {
David03ad33c2023-02-15 23:35:51 -06006955 detail::json json_double_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006956 for (std::map<std::string, double>::iterator it =
6957 paramIt->second.json_double_value.begin();
6958 it != paramIt->second.json_double_value.end(); ++it) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006959 if (it->first == "index") {
6960 json_double_value[it->first] = paramIt->second.TextureIndex();
6961 } else {
6962 json_double_value[it->first] = it->second;
6963 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006964 }
6965
Syoyo Fujita83675312017-12-02 21:14:13 +09006966 o[paramIt->first] = json_double_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006967 } else if (!paramIt->second.string_value.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006968 SerializeStringProperty(paramIt->first, paramIt->second.string_value, o);
Ben Buzbeef6af2242018-04-25 15:13:05 -07006969 } else if (paramIt->second.has_number_value) {
6970 o[paramIt->first] = paramIt->second.number_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006971 } else {
Syoyo Fujita83675312017-12-02 21:14:13 +09006972 o[paramIt->first] = paramIt->second.bool_value;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006973 }
6974 }
6975}
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006976#endif
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006977
David03ad33c2023-02-15 23:35:51 -06006978static void SerializeExtensionMap(const ExtensionMap &extensions, detail::json &o) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006979 if (!extensions.size()) return;
Selmar09d2ff12018-03-15 17:30:42 +01006980
David03ad33c2023-02-15 23:35:51 -06006981 detail::json extMap;
Selmar Kok81b672b2019-10-18 16:08:44 +02006982 for (ExtensionMap::const_iterator extIt = extensions.begin();
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006983 extIt != extensions.end(); ++extIt) {
Syoyo Fujita924d86e2018-10-08 21:15:12 +09006984 // Allow an empty object for extension(#97)
David03ad33c2023-02-15 23:35:51 -06006985 detail::json ret;
jrkooncecba5d6c2019-08-29 11:26:22 -05006986 bool isNull = true;
Syoyo Fujita924d86e2018-10-08 21:15:12 +09006987 if (ValueToJson(extIt->second, &ret)) {
David1f9a4b92023-02-15 22:56:18 -06006988 isNull = detail::JsonIsNull(ret);
6989 detail::JsonAddMember(extMap, extIt->first.c_str(), std::move(ret));
Selmar Kokdb7f4e42018-10-10 18:10:58 +02006990 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006991 if (isNull) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006992 if (!(extIt->first.empty())) { // name should not be empty, but for sure
6993 // create empty object so that an extension name is still included in
6994 // json.
David03ad33c2023-02-15 23:35:51 -06006995 detail::json empty;
David1f9a4b92023-02-15 22:56:18 -06006996 detail::JsonSetObject(empty);
6997 detail::JsonAddMember(extMap, extIt->first.c_str(), std::move(empty));
Syoyo Fujita924d86e2018-10-08 21:15:12 +09006998 }
6999 }
Selmar09d2ff12018-03-15 17:30:42 +01007000 }
David1f9a4b92023-02-15 22:56:18 -06007001 detail::JsonAddMember(o, "extensions", std::move(extMap));
Selmar09d2ff12018-03-15 17:30:42 +01007002}
7003
David Siegel07616e82023-06-06 01:24:53 +02007004static void SerializeExtras(const Value & extras, detail::json & o) {
Syoyo Fujita7a570c82023-06-19 21:52:13 +09007005 if (extras.Type() != NULL_TYPE)
David Siegel07616e82023-06-06 01:24:53 +02007006 SerializeValue("extras", extras, o);
7007}
7008
7009template <typename GltfType>
7010void SerializeExtrasAndExtensions(const GltfType & obj, detail::json & o) {
7011 SerializeExtensionMap(obj.extensions, o);
7012 SerializeExtras(obj.extras, o);
7013}
7014
David03ad33c2023-02-15 23:35:51 -06007015static void SerializeGltfAccessor(const Accessor &accessor, detail::json &o) {
Sanjeet Suhag5ecede72020-03-04 17:40:10 -05007016 if (accessor.bufferView >= 0)
7017 SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007018
Syoyo Fujita18f0e202020-04-29 19:16:35 +09007019 if (accessor.byteOffset != 0)
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007020 SerializeNumberProperty<int>("byteOffset", int(accessor.byteOffset), o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007021
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007022 SerializeNumberProperty<int>("componentType", accessor.componentType, o);
7023 SerializeNumberProperty<size_t>("count", accessor.count, o);
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09007024
7025 if ((accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) ||
7026 (accessor.componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE)) {
7027 SerializeNumberArrayProperty<double>("min", accessor.minValues, o);
7028 SerializeNumberArrayProperty<double>("max", accessor.maxValues, o);
7029 } else {
7030 // Issue #301. Serialize as integer.
7031 // Assume int value is within [-2**31-1, 2**31-1]
7032 {
7033 std::vector<int> values;
7034 std::transform(accessor.minValues.begin(), accessor.minValues.end(),
7035 std::back_inserter(values),
7036 [](double v) { return static_cast<int>(v); });
7037
7038 SerializeNumberArrayProperty<int>("min", values, o);
7039 }
7040
7041 {
7042 std::vector<int> values;
7043 std::transform(accessor.maxValues.begin(), accessor.maxValues.end(),
7044 std::back_inserter(values),
7045 [](double v) { return static_cast<int>(v); });
7046
7047 SerializeNumberArrayProperty<int>("max", values, o);
7048 }
7049 }
7050
Eero Pajarre2e8a1152019-11-18 13:09:25 +02007051 if (accessor.normalized)
7052 SerializeValue("normalized", Value(accessor.normalized), o);
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007053 std::string type;
7054 switch (accessor.type) {
7055 case TINYGLTF_TYPE_SCALAR:
7056 type = "SCALAR";
7057 break;
7058 case TINYGLTF_TYPE_VEC2:
7059 type = "VEC2";
7060 break;
7061 case TINYGLTF_TYPE_VEC3:
7062 type = "VEC3";
7063 break;
7064 case TINYGLTF_TYPE_VEC4:
7065 type = "VEC4";
7066 break;
7067 case TINYGLTF_TYPE_MAT2:
7068 type = "MAT2";
7069 break;
7070 case TINYGLTF_TYPE_MAT3:
7071 type = "MAT3";
7072 break;
7073 case TINYGLTF_TYPE_MAT4:
7074 type = "MAT4";
7075 break;
7076 }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09007077
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007078 SerializeStringProperty("type", type, o);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09007079 if (!accessor.name.empty()) SerializeStringProperty("name", accessor.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007080
David Siegel07616e82023-06-06 01:24:53 +02007081 SerializeExtrasAndExtensions(accessor, o);
feiy0b315432022-08-13 10:08:17 +08007082
7083 // sparse
7084 if (accessor.sparse.isSparse)
7085 {
David03ad33c2023-02-15 23:35:51 -06007086 detail::json sparse;
feiy0b315432022-08-13 10:08:17 +08007087 SerializeNumberProperty<int>("count", accessor.sparse.count, sparse);
7088 {
David03ad33c2023-02-15 23:35:51 -06007089 detail::json indices;
feiy0b315432022-08-13 10:08:17 +08007090 SerializeNumberProperty<int>("bufferView", accessor.sparse.indices.bufferView, indices);
7091 SerializeNumberProperty<int>("byteOffset", accessor.sparse.indices.byteOffset, indices);
7092 SerializeNumberProperty<int>("componentType", accessor.sparse.indices.componentType, indices);
David Siegel07616e82023-06-06 01:24:53 +02007093 SerializeExtrasAndExtensions(accessor.sparse.indices, indices);
David1f9a4b92023-02-15 22:56:18 -06007094 detail::JsonAddMember(sparse, "indices", std::move(indices));
feiy0b315432022-08-13 10:08:17 +08007095 }
7096 {
David03ad33c2023-02-15 23:35:51 -06007097 detail::json values;
feiy0b315432022-08-13 10:08:17 +08007098 SerializeNumberProperty<int>("bufferView", accessor.sparse.values.bufferView, values);
feiy1a8814d2022-08-14 19:49:07 +08007099 SerializeNumberProperty<int>("byteOffset", accessor.sparse.values.byteOffset, values);
David Siegel07616e82023-06-06 01:24:53 +02007100 SerializeExtrasAndExtensions(accessor.sparse.values, values);
David1f9a4b92023-02-15 22:56:18 -06007101 detail::JsonAddMember(sparse, "values", std::move(values));
feiy0b315432022-08-13 10:08:17 +08007102 }
David Siegel07616e82023-06-06 01:24:53 +02007103 SerializeExtrasAndExtensions(accessor.sparse, sparse);
David1f9a4b92023-02-15 22:56:18 -06007104 detail::JsonAddMember(o, "sparse", std::move(sparse));
feiy0b315432022-08-13 10:08:17 +08007105 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007106}
7107
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007108static void SerializeGltfAnimationChannel(const AnimationChannel &channel,
David03ad33c2023-02-15 23:35:51 -06007109 detail::json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007110 SerializeNumberProperty("sampler", channel.sampler, o);
jrkooncecba5d6c2019-08-29 11:26:22 -05007111 {
David03ad33c2023-02-15 23:35:51 -06007112 detail::json target;
Jack Mousseau283b5522023-01-15 11:45:45 -08007113
Loïc Escalesa75355b2023-04-18 21:03:39 +02007114 if (channel.target_node >= 0) {
Jack Mousseau283b5522023-01-15 11:45:45 -08007115 SerializeNumberProperty("node", channel.target_node, target);
7116 }
7117
jrkooncecba5d6c2019-08-29 11:26:22 -05007118 SerializeStringProperty("path", channel.target_path, target);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007119
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007120 SerializeExtensionMap(channel.target_extensions, target);
David Siegel07616e82023-06-06 01:24:53 +02007121 SerializeExtras(channel.target_extras, target);
Selmar Kok973d9b32020-01-21 18:45:24 +01007122
David1f9a4b92023-02-15 22:56:18 -06007123 detail::JsonAddMember(o, "target", std::move(target));
jrkooncecba5d6c2019-08-29 11:26:22 -05007124 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007125
David Siegel07616e82023-06-06 01:24:53 +02007126 SerializeExtrasAndExtensions(channel, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007127}
7128
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007129static void SerializeGltfAnimationSampler(const AnimationSampler &sampler,
David03ad33c2023-02-15 23:35:51 -06007130 detail::json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007131 SerializeNumberProperty("input", sampler.input, o);
7132 SerializeNumberProperty("output", sampler.output, o);
7133 SerializeStringProperty("interpolation", sampler.interpolation, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007134
David Siegel07616e82023-06-06 01:24:53 +02007135 SerializeExtrasAndExtensions(sampler, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007136}
7137
David03ad33c2023-02-15 23:35:51 -06007138static void SerializeGltfAnimation(const Animation &animation, detail::json &o) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09007139 if (!animation.name.empty())
7140 SerializeStringProperty("name", animation.name, o);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007141
jrkooncecba5d6c2019-08-29 11:26:22 -05007142 {
David03ad33c2023-02-15 23:35:51 -06007143 detail::json channels;
David1f9a4b92023-02-15 22:56:18 -06007144 detail::JsonReserveArray(channels, animation.channels.size());
jrkooncecba5d6c2019-08-29 11:26:22 -05007145 for (unsigned int i = 0; i < animation.channels.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007146 detail::json channel;
jrkooncecba5d6c2019-08-29 11:26:22 -05007147 AnimationChannel gltfChannel = animation.channels[i];
7148 SerializeGltfAnimationChannel(gltfChannel, channel);
David1f9a4b92023-02-15 22:56:18 -06007149 detail::JsonPushBack(channels, std::move(channel));
jrkooncecba5d6c2019-08-29 11:26:22 -05007150 }
7151
David1f9a4b92023-02-15 22:56:18 -06007152 detail::JsonAddMember(o, "channels", std::move(channels));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007153 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007154
jrkooncecba5d6c2019-08-29 11:26:22 -05007155 {
David03ad33c2023-02-15 23:35:51 -06007156 detail::json samplers;
David1f9a4b92023-02-15 22:56:18 -06007157 detail::JsonReserveArray(samplers, animation.samplers.size());
jrkooncecba5d6c2019-08-29 11:26:22 -05007158 for (unsigned int i = 0; i < animation.samplers.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007159 detail::json sampler;
jrkooncecba5d6c2019-08-29 11:26:22 -05007160 AnimationSampler gltfSampler = animation.samplers[i];
7161 SerializeGltfAnimationSampler(gltfSampler, sampler);
David1f9a4b92023-02-15 22:56:18 -06007162 detail::JsonPushBack(samplers, std::move(sampler));
jrkooncecba5d6c2019-08-29 11:26:22 -05007163 }
David1f9a4b92023-02-15 22:56:18 -06007164 detail::JsonAddMember(o, "samplers", std::move(samplers));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007165 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007166
David Siegel07616e82023-06-06 01:24:53 +02007167 SerializeExtrasAndExtensions(animation, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007168}
7169
David03ad33c2023-02-15 23:35:51 -06007170static void SerializeGltfAsset(const Asset &asset, detail::json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007171 if (!asset.generator.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007172 SerializeStringProperty("generator", asset.generator, o);
7173 }
7174
Christophe820ede82019-07-04 15:21:21 +09007175 if (!asset.copyright.empty()) {
7176 SerializeStringProperty("copyright", asset.copyright, o);
7177 }
7178
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007179 auto version = asset.version;
7180 if (version.empty()) {
Syoyo Fujitab702de72021-03-02 19:08:29 +09007181 // Just in case
7182 // `version` must be defined
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007183 version = "2.0";
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007184 }
7185
Syoyo Fujitab702de72021-03-02 19:08:29 +09007186 // TODO(syoyo): Do we need to check if `version` is greater or equal to 2.0?
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007187 SerializeStringProperty("version", version, o);
Syoyo Fujitab702de72021-03-02 19:08:29 +09007188
David Siegel07616e82023-06-06 01:24:53 +02007189 SerializeExtrasAndExtensions(asset, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007190}
7191
David03ad33c2023-02-15 23:35:51 -06007192static void SerializeGltfBufferBin(const Buffer &buffer, detail::json &o,
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007193 std::vector<unsigned char> &binBuffer) {
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007194 SerializeNumberProperty("byteLength", buffer.data.size(), o);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007195 binBuffer = buffer.data;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007196
7197 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
7198
David Siegel07616e82023-06-06 01:24:53 +02007199 SerializeExtrasAndExtensions(buffer, o);
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007200}
7201
David03ad33c2023-02-15 23:35:51 -06007202static void SerializeGltfBuffer(const Buffer &buffer, detail::json &o) {
johan bowald30c53472018-03-30 11:49:36 +02007203 SerializeNumberProperty("byteLength", buffer.data.size(), o);
7204 SerializeGltfBufferData(buffer.data, o);
7205
7206 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007207
David Siegel07616e82023-06-06 01:24:53 +02007208 SerializeExtrasAndExtensions(buffer, o);
johan bowald30c53472018-03-30 11:49:36 +02007209}
7210
David03ad33c2023-02-15 23:35:51 -06007211static bool SerializeGltfBuffer(const Buffer &buffer, detail::json &o,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05007212 const std::string &binFilename,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08007213 const std::string &binUri) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00007214 if (!SerializeGltfBufferData(buffer.data, binFilename)) return false;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007215 SerializeNumberProperty("byteLength", buffer.data.size(), o);
Pyarelal Knowles385946d2023-01-03 18:18:04 -08007216 SerializeStringProperty("uri", binUri, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007217
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007218 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007219
David Siegel07616e82023-06-06 01:24:53 +02007220 SerializeExtrasAndExtensions(buffer, o);
Selmar Koke4677492018-10-25 16:45:49 +02007221 return true;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007222}
7223
David03ad33c2023-02-15 23:35:51 -06007224static void SerializeGltfBufferView(const BufferView &bufferView, detail::json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007225 SerializeNumberProperty("buffer", bufferView.buffer, o);
7226 SerializeNumberProperty<size_t>("byteLength", bufferView.byteLength, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007227
Johan Bowaldfaa27222018-03-28 14:44:45 +02007228 // byteStride is optional, minimum allowed is 4
7229 if (bufferView.byteStride >= 4) {
7230 SerializeNumberProperty<size_t>("byteStride", bufferView.byteStride, o);
7231 }
7232 // byteOffset is optional, default is 0
7233 if (bufferView.byteOffset > 0) {
7234 SerializeNumberProperty<size_t>("byteOffset", bufferView.byteOffset, o);
7235 }
7236 // Target is optional, check if it contains a valid value
7237 if (bufferView.target == TINYGLTF_TARGET_ARRAY_BUFFER ||
7238 bufferView.target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER) {
7239 SerializeNumberProperty("target", bufferView.target, o);
7240 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007241 if (bufferView.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007242 SerializeStringProperty("name", bufferView.name, o);
7243 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007244
David Siegel07616e82023-06-06 01:24:53 +02007245 SerializeExtrasAndExtensions(bufferView, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007246}
7247
Pyarelal Knowlesa9121552022-12-29 14:12:29 -08007248static void SerializeGltfImage(const Image &image, const std::string &uri,
David03ad33c2023-02-15 23:35:51 -06007249 detail::json &o) {
Syoyo Fujita584f1df2022-12-29 21:05:53 +09007250 // From 2.7.0, we look for `uri` parameter, not `Image.uri`
7251 // if uri is empty, the mimeType and bufferview should be set
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007252 if (uri.empty()) {
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02007253 SerializeStringProperty("mimeType", image.mimeType, o);
7254 SerializeNumberProperty<int>("bufferView", image.bufferView, o);
7255 } else {
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007256 SerializeStringProperty("uri", uri, o);
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02007257 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007258
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007259 if (image.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007260 SerializeStringProperty("name", image.name, o);
7261 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007262
David Siegel07616e82023-06-06 01:24:53 +02007263 SerializeExtrasAndExtensions(image, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007264}
7265
David03ad33c2023-02-15 23:35:51 -06007266static void SerializeGltfTextureInfo(const TextureInfo &texinfo, detail::json &o) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007267 SerializeNumberProperty("index", texinfo.index, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007268
Syoyo Fujita046400b2019-07-24 19:26:48 +09007269 if (texinfo.texCoord != 0) {
7270 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
7271 }
7272
David Siegel07616e82023-06-06 01:24:53 +02007273 SerializeExtrasAndExtensions(texinfo, o);
Syoyo Fujita046400b2019-07-24 19:26:48 +09007274}
7275
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007276static void SerializeGltfNormalTextureInfo(const NormalTextureInfo &texinfo,
David03ad33c2023-02-15 23:35:51 -06007277 detail::json &o) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007278 SerializeNumberProperty("index", texinfo.index, o);
7279
7280 if (texinfo.texCoord != 0) {
7281 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
7282 }
7283
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007284 if (!TINYGLTF_DOUBLE_EQUAL(texinfo.scale, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007285 SerializeNumberProperty("scale", texinfo.scale, o);
7286 }
7287
David Siegel07616e82023-06-06 01:24:53 +02007288 SerializeExtrasAndExtensions(texinfo, o);
Syoyo Fujita046400b2019-07-24 19:26:48 +09007289}
7290
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007291static void SerializeGltfOcclusionTextureInfo(
David03ad33c2023-02-15 23:35:51 -06007292 const OcclusionTextureInfo &texinfo, detail::json &o) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007293 SerializeNumberProperty("index", texinfo.index, o);
7294
7295 if (texinfo.texCoord != 0) {
7296 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
7297 }
7298
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007299 if (!TINYGLTF_DOUBLE_EQUAL(texinfo.strength, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007300 SerializeNumberProperty("strength", texinfo.strength, o);
7301 }
7302
David Siegel07616e82023-06-06 01:24:53 +02007303 SerializeExtrasAndExtensions(texinfo, o);
Syoyo Fujita046400b2019-07-24 19:26:48 +09007304}
7305
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08007306static void SerializeGltfPbrMetallicRoughness(const PbrMetallicRoughness &pbr,
David03ad33c2023-02-15 23:35:51 -06007307 detail::json &o) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007308 std::vector<double> default_baseColorFactor = {1.0, 1.0, 1.0, 1.0};
7309 if (!Equals(pbr.baseColorFactor, default_baseColorFactor)) {
7310 SerializeNumberArrayProperty<double>("baseColorFactor", pbr.baseColorFactor,
7311 o);
7312 }
7313
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007314 if (!TINYGLTF_DOUBLE_EQUAL(pbr.metallicFactor, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007315 SerializeNumberProperty("metallicFactor", pbr.metallicFactor, o);
7316 }
7317
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007318 if (!TINYGLTF_DOUBLE_EQUAL(pbr.roughnessFactor, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007319 SerializeNumberProperty("roughnessFactor", pbr.roughnessFactor, o);
7320 }
7321
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007322 if (pbr.baseColorTexture.index > -1) {
David03ad33c2023-02-15 23:35:51 -06007323 detail::json texinfo;
Syoyo Fujita046400b2019-07-24 19:26:48 +09007324 SerializeGltfTextureInfo(pbr.baseColorTexture, texinfo);
David1f9a4b92023-02-15 22:56:18 -06007325 detail::JsonAddMember(o, "baseColorTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007326 }
7327
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007328 if (pbr.metallicRoughnessTexture.index > -1) {
David03ad33c2023-02-15 23:35:51 -06007329 detail::json texinfo;
Syoyo Fujita046400b2019-07-24 19:26:48 +09007330 SerializeGltfTextureInfo(pbr.metallicRoughnessTexture, texinfo);
David1f9a4b92023-02-15 22:56:18 -06007331 detail::JsonAddMember(o, "metallicRoughnessTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007332 }
7333
David Siegel07616e82023-06-06 01:24:53 +02007334 SerializeExtrasAndExtensions(pbr, o);
Syoyo Fujita046400b2019-07-24 19:26:48 +09007335}
7336
David03ad33c2023-02-15 23:35:51 -06007337static void SerializeGltfMaterial(const Material &material, detail::json &o) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007338 if (material.name.size()) {
7339 SerializeStringProperty("name", material.name, o);
7340 }
7341
7342 // QUESTION(syoyo): Write material parameters regardless of its default value?
7343
7344 if (!TINYGLTF_DOUBLE_EQUAL(material.alphaCutoff, 0.5)) {
7345 SerializeNumberProperty("alphaCutoff", material.alphaCutoff, o);
7346 }
7347
Patrick Härtld9a468b2019-08-14 14:14:07 +02007348 if (material.alphaMode.compare("OPAQUE") != 0) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007349 SerializeStringProperty("alphaMode", material.alphaMode, o);
7350 }
7351
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007352 if (material.doubleSided != false)
David03ad33c2023-02-15 23:35:51 -06007353 detail::JsonAddMember(o, "doubleSided", detail::json(material.doubleSided));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007354
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007355 if (material.normalTexture.index > -1) {
David03ad33c2023-02-15 23:35:51 -06007356 detail::json texinfo;
Syoyo Fujita046400b2019-07-24 19:26:48 +09007357 SerializeGltfNormalTextureInfo(material.normalTexture, texinfo);
David1f9a4b92023-02-15 22:56:18 -06007358 detail::JsonAddMember(o, "normalTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007359 }
7360
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007361 if (material.occlusionTexture.index > -1) {
David03ad33c2023-02-15 23:35:51 -06007362 detail::json texinfo;
Syoyo Fujita046400b2019-07-24 19:26:48 +09007363 SerializeGltfOcclusionTextureInfo(material.occlusionTexture, texinfo);
David1f9a4b92023-02-15 22:56:18 -06007364 detail::JsonAddMember(o, "occlusionTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007365 }
7366
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007367 if (material.emissiveTexture.index > -1) {
David03ad33c2023-02-15 23:35:51 -06007368 detail::json texinfo;
Syoyo Fujita046400b2019-07-24 19:26:48 +09007369 SerializeGltfTextureInfo(material.emissiveTexture, texinfo);
David1f9a4b92023-02-15 22:56:18 -06007370 detail::JsonAddMember(o, "emissiveTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007371 }
7372
7373 std::vector<double> default_emissiveFactor = {0.0, 0.0, 0.0};
7374 if (!Equals(material.emissiveFactor, default_emissiveFactor)) {
7375 SerializeNumberArrayProperty<double>("emissiveFactor",
7376 material.emissiveFactor, o);
7377 }
7378
7379 {
David03ad33c2023-02-15 23:35:51 -06007380 detail::json pbrMetallicRoughness;
Syoyo Fujita046400b2019-07-24 19:26:48 +09007381 SerializeGltfPbrMetallicRoughness(material.pbrMetallicRoughness,
7382 pbrMetallicRoughness);
Syoyo Fujita7e009042019-09-13 15:32:22 +09007383 // Issue 204
7384 // Do not serialize `pbrMetallicRoughness` if pbrMetallicRoughness has all
7385 // default values(json is null). Otherwise it will serialize to
7386 // `pbrMetallicRoughness : null`, which cannot be read by other glTF
imallettd9ce9eb2022-10-07 10:37:09 -07007387 // importers (and validators).
Syoyo Fujita7e009042019-09-13 15:32:22 +09007388 //
David1f9a4b92023-02-15 22:56:18 -06007389 if (!detail::JsonIsNull(pbrMetallicRoughness)) {
7390 detail::JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness));
Syoyo Fujita7e009042019-09-13 15:32:22 +09007391 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09007392 }
7393
7394#if 0 // legacy way. just for the record.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007395 if (material.values.size()) {
David03ad33c2023-02-15 23:35:51 -06007396 detail::json pbrMetallicRoughness;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007397 SerializeParameterMap(material.values, pbrMetallicRoughness);
David1f9a4b92023-02-15 22:56:18 -06007398 detail::JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007399 }
7400
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007401 SerializeParameterMap(material.additionalValues, o);
Syoyo Fujita046400b2019-07-24 19:26:48 +09007402#else
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007403
Syoyo Fujita046400b2019-07-24 19:26:48 +09007404#endif
7405
David Siegel07616e82023-06-06 01:24:53 +02007406 SerializeExtrasAndExtensions(material, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007407}
7408
David03ad33c2023-02-15 23:35:51 -06007409static void SerializeGltfMesh(const Mesh &mesh, detail::json &o) {
7410 detail::json primitives;
David1f9a4b92023-02-15 22:56:18 -06007411 detail::JsonReserveArray(primitives, mesh.primitives.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007412 for (unsigned int i = 0; i < mesh.primitives.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007413 detail::json primitive;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007414 const Primitive &gltfPrimitive = mesh.primitives[i]; // don't make a copy
jrkooncecba5d6c2019-08-29 11:26:22 -05007415 {
David03ad33c2023-02-15 23:35:51 -06007416 detail::json attributes;
jrkooncecba5d6c2019-08-29 11:26:22 -05007417 for (auto attrIt = gltfPrimitive.attributes.begin();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007418 attrIt != gltfPrimitive.attributes.end(); ++attrIt) {
jrkooncecba5d6c2019-08-29 11:26:22 -05007419 SerializeNumberProperty<int>(attrIt->first, attrIt->second, attributes);
7420 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007421
David1f9a4b92023-02-15 22:56:18 -06007422 detail::JsonAddMember(primitive, "attributes", std::move(attributes));
jrkooncecba5d6c2019-08-29 11:26:22 -05007423 }
Johan Bowaldfaa27222018-03-28 14:44:45 +02007424
imallettd9ce9eb2022-10-07 10:37:09 -07007425 // Indices is optional
Johan Bowaldfaa27222018-03-28 14:44:45 +02007426 if (gltfPrimitive.indices > -1) {
7427 SerializeNumberProperty<int>("indices", gltfPrimitive.indices, primitive);
7428 }
7429 // Material is optional
7430 if (gltfPrimitive.material > -1) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09007431 SerializeNumberProperty<int>("material", gltfPrimitive.material,
7432 primitive);
Johan Bowaldfaa27222018-03-28 14:44:45 +02007433 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007434 SerializeNumberProperty<int>("mode", gltfPrimitive.mode, primitive);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007435
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007436 // Morph targets
7437 if (gltfPrimitive.targets.size()) {
David03ad33c2023-02-15 23:35:51 -06007438 detail::json targets;
David1f9a4b92023-02-15 22:56:18 -06007439 detail::JsonReserveArray(targets, gltfPrimitive.targets.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007440 for (unsigned int k = 0; k < gltfPrimitive.targets.size(); ++k) {
David03ad33c2023-02-15 23:35:51 -06007441 detail::json targetAttributes;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007442 std::map<std::string, int> targetData = gltfPrimitive.targets[k];
7443 for (std::map<std::string, int>::iterator attrIt = targetData.begin();
7444 attrIt != targetData.end(); ++attrIt) {
7445 SerializeNumberProperty<int>(attrIt->first, attrIt->second,
7446 targetAttributes);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007447 }
David1f9a4b92023-02-15 22:56:18 -06007448 detail::JsonPushBack(targets, std::move(targetAttributes));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007449 }
David1f9a4b92023-02-15 22:56:18 -06007450 detail::JsonAddMember(primitive, "targets", std::move(targets));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007451 }
7452
David Siegel07616e82023-06-06 01:24:53 +02007453 SerializeExtrasAndExtensions(gltfPrimitive, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007454
David1f9a4b92023-02-15 22:56:18 -06007455 detail::JsonPushBack(primitives, std::move(primitive));
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007456 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007457
David1f9a4b92023-02-15 22:56:18 -06007458 detail::JsonAddMember(o, "primitives", std::move(primitives));
jrkooncecba5d6c2019-08-29 11:26:22 -05007459
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007460 if (mesh.weights.size()) {
7461 SerializeNumberArrayProperty<double>("weights", mesh.weights, o);
7462 }
7463
7464 if (mesh.name.size()) {
7465 SerializeStringProperty("name", mesh.name, o);
7466 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007467
David Siegel07616e82023-06-06 01:24:53 +02007468 SerializeExtrasAndExtensions(mesh, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007469}
7470
David03ad33c2023-02-15 23:35:51 -06007471static void SerializeSpotLight(const SpotLight &spot, detail::json &o) {
Johan Bowald52936a02019-07-17 09:06:45 +02007472 SerializeNumberProperty("innerConeAngle", spot.innerConeAngle, o);
7473 SerializeNumberProperty("outerConeAngle", spot.outerConeAngle, o);
David Siegel07616e82023-06-06 01:24:53 +02007474 SerializeExtrasAndExtensions(spot, o);
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007475}
7476
David03ad33c2023-02-15 23:35:51 -06007477static void SerializeGltfLight(const Light &light, detail::json &o) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09007478 if (!light.name.empty()) SerializeStringProperty("name", light.name, o);
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007479 SerializeNumberProperty("intensity", light.intensity, o);
Syoyo Fujita3dad3032020-05-13 21:22:23 +09007480 if (light.range > 0.0) {
7481 SerializeNumberProperty("range", light.range, o);
7482 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01007483 SerializeNumberArrayProperty("color", light.color, o);
7484 SerializeStringProperty("type", light.type, o);
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007485 if (light.type == "spot") {
David03ad33c2023-02-15 23:35:51 -06007486 detail::json spot;
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007487 SerializeSpotLight(light.spot, spot);
David1f9a4b92023-02-15 22:56:18 -06007488 detail::JsonAddMember(o, "spot", std::move(spot));
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007489 }
David Siegel07616e82023-06-06 01:24:53 +02007490 SerializeExtrasAndExtensions(light, o);
Emanuel Schrade186322b2017-11-06 11:14:41 +01007491}
7492
Baranob_Ilya78864c82023-06-12 10:43:52 +04007493static void SerializeGltfPositionalEmitter(const PositionalEmitter &positional,
7494 detail::json &o) {
7495 if (!TINYGLTF_DOUBLE_EQUAL(positional.coneInnerAngle, 6.283185307179586))
7496 SerializeNumberProperty("coneInnerAngle", positional.coneInnerAngle, o);
7497 if (!TINYGLTF_DOUBLE_EQUAL(positional.coneOuterAngle, 6.283185307179586))
7498 SerializeNumberProperty("coneOuterAngle", positional.coneOuterAngle, o);
7499 if (positional.coneOuterGain > 0.0)
7500 SerializeNumberProperty("coneOuterGain", positional.coneOuterGain, o);
7501 if (!TINYGLTF_DOUBLE_EQUAL(positional.maxDistance, 100.0))
7502 SerializeNumberProperty("maxDistance", positional.maxDistance, o);
7503 if (!TINYGLTF_DOUBLE_EQUAL(positional.refDistance, 1.0))
7504 SerializeNumberProperty("refDistance", positional.refDistance, o);
7505 if (!TINYGLTF_DOUBLE_EQUAL(positional.rolloffFactor, 1.0))
7506 SerializeNumberProperty("rolloffFactor", positional.rolloffFactor, o);
7507
7508 SerializeExtrasAndExtensions(positional, o);
7509}
7510
7511static void SerializeGltfAudioEmitter(const AudioEmitter &emitter,
7512 detail::json &o) {
7513 if (!emitter.name.empty()) SerializeStringProperty("name", emitter.name, o);
7514 if (!TINYGLTF_DOUBLE_EQUAL(emitter.gain, 1.0))
7515 SerializeNumberProperty("gain", emitter.gain, o);
7516 if (emitter.loop) SerializeNumberProperty("loop", emitter.loop, o);
7517 if (emitter.playing) SerializeNumberProperty("playing", emitter.playing, o);
7518 if (!emitter.type.empty()) SerializeStringProperty("type", emitter.type, o);
7519 if (!emitter.distanceModel.empty())
7520 SerializeStringProperty("distanceModel", emitter.distanceModel, o);
7521 if (emitter.type == "positional") {
7522 detail::json positional;
7523 SerializeGltfPositionalEmitter(emitter.positional, positional);
7524 detail::JsonAddMember(o, "positional", std::move(positional));
7525 }
7526 SerializeNumberProperty("source", emitter.source, o);
7527 SerializeExtrasAndExtensions(emitter, o);
7528}
7529
7530static void SerializeGltfAudioSource(const AudioSource& source, detail::json& o) {
7531 std::string name;
7532 std::string uri;
7533 int bufferView; // (required if no uri)
7534 std::string mimeType; // (required if no uri) ["audio/mp3", "audio/ogg",
7535 // "audio/wav", "audio/m4a"]
7536
7537 if (!source.name.empty()) SerializeStringProperty("name", source.name, o);
7538 if (source.uri.empty()) {
7539 SerializeStringProperty("mimeType", source.mimeType, o);
7540 SerializeNumberProperty<int>("bufferView", source.bufferView, o);
7541 }
7542 else {
7543 SerializeStringProperty("uri", source.uri, o);
7544 }
7545 SerializeExtrasAndExtensions(source, o);
7546}
7547
David03ad33c2023-02-15 23:35:51 -06007548static void SerializeGltfNode(const Node &node, detail::json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007549 if (node.translation.size() > 0) {
7550 SerializeNumberArrayProperty<double>("translation", node.translation, o);
7551 }
7552 if (node.rotation.size() > 0) {
7553 SerializeNumberArrayProperty<double>("rotation", node.rotation, o);
7554 }
7555 if (node.scale.size() > 0) {
7556 SerializeNumberArrayProperty<double>("scale", node.scale, o);
7557 }
7558 if (node.matrix.size() > 0) {
7559 SerializeNumberArrayProperty<double>("matrix", node.matrix, o);
7560 }
7561 if (node.mesh != -1) {
7562 SerializeNumberProperty<int>("mesh", node.mesh, o);
7563 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007564
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007565 if (node.skin != -1) {
7566 SerializeNumberProperty<int>("skin", node.skin, o);
7567 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007568
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007569 if (node.camera != -1) {
7570 SerializeNumberProperty<int>("camera", node.camera, o);
7571 }
7572
Benjamin Schmithüsen74c3c102019-08-16 14:24:26 +02007573 if (node.weights.size() > 0) {
7574 SerializeNumberArrayProperty<double>("weights", node.weights, o);
7575 }
7576
David Siegel07616e82023-06-06 01:24:53 +02007577 SerializeExtrasAndExtensions(node, o);
Jens Olssonb96f6962018-05-24 15:29:54 +02007578
David Siegelcfe64fb2023-06-07 15:18:38 +02007579 // Note(agnat): If the asset was loaded from disk, the node may already
7580 // contain the KHR_lights_punctual extension. If it was constructed in
7581 // memory it does not. In any case we update the JSON property using
7582 // the value from the struct. Last, if the node does not have a light
7583 // reference but the extension is still present, we remove it.
7584 if (node.light != -1) {
7585 detail::json_iterator it;
7586 if (!detail::FindMember(o, "extensions", it)) {
7587 detail::json extensions;
7588 detail::JsonSetObject(extensions);
7589 detail::JsonAddMember(o, "extensions", std::move(extensions));
7590 detail::FindMember(o, "extensions", it);
7591 }
7592 auto & extensions = detail::GetValue(it);
7593 if ( ! detail::FindMember(extensions, "KHR_lights_punctual", it)) {
7594 detail::json lights_punctual;
7595 detail::JsonSetObject(lights_punctual);
7596 detail::JsonAddMember(extensions, "KHR_lights_punctual", std::move(lights_punctual));
7597 detail::FindMember(o, "KHR_lights_punctual", it);
7598 }
David Siegel8d5d0b32023-06-07 15:35:35 +02007599 SerializeNumberProperty("light", node.light, detail::GetValue(it));
David Siegelcfe64fb2023-06-07 15:18:38 +02007600 } else {
7601 // node has no light ref (any longer)... so we clean up
7602 detail::json_iterator ext_it;
7603 if (detail::FindMember(o, "extensions", ext_it)) {
7604 auto & extensions = detail::GetValue(ext_it);
7605 detail::json_iterator lp_it;
7606 if (detail::FindMember(extensions, "KHR_lights_punctual", lp_it)) {
7607 detail::Erase(extensions, lp_it);
7608 }
7609 if (detail::IsEmpty(extensions)) {
7610 detail::Erase(o, ext_it);
7611 }
7612 }
7613 }
7614
Baranob_Ilya78864c82023-06-12 10:43:52 +04007615 // KHR_audio
7616 if (node.emitter != -1) {
7617 detail::json_iterator it;
7618 if (!detail::FindMember(o, "extensions", it)) {
7619 detail::json extensions;
7620 detail::JsonSetObject(extensions);
7621 detail::JsonAddMember(o, "extensions", std::move(extensions));
7622 detail::FindMember(o, "extensions", it);
7623 }
7624 auto &extensions = detail::GetValue(it);
7625 if (!detail::FindMember(extensions, "KHR_audio", it)) {
7626 detail::json audio;
7627 detail::JsonSetObject(audio);
7628 detail::JsonAddMember(extensions, "KHR_audio", std::move(audio));
7629 detail::FindMember(o, "KHR_audio", it);
7630 }
7631 SerializeNumberProperty("emitter", node.emitter, detail::GetValue(it));
7632 } else {
7633 detail::json_iterator ext_it;
7634 if (detail::FindMember(o, "extensions", ext_it)) {
7635 auto &extensions = detail::GetValue(ext_it);
7636 detail::json_iterator lp_it;
7637 if (detail::FindMember(extensions, "KHR_audio", lp_it)) {
7638 detail::Erase(extensions, lp_it);
7639 }
7640 if (detail::IsEmpty(extensions)) {
7641 detail::Erase(o, ext_it);
7642 }
7643 }
7644 }
7645
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09007646 if (!node.name.empty()) SerializeStringProperty("name", node.name, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007647 SerializeNumberArrayProperty<int>("children", node.children, o);
7648}
7649
David03ad33c2023-02-15 23:35:51 -06007650static void SerializeGltfSampler(const Sampler &sampler, detail::json &o) {
s00665032137a7ca2023-01-13 12:52:08 +07007651 if (!sampler.name.empty()) {
7652 SerializeStringProperty("name", sampler.name, o);
7653 }
Syoyo Fujitaee179b22019-08-16 13:11:30 +09007654 if (sampler.magFilter != -1) {
7655 SerializeNumberProperty("magFilter", sampler.magFilter, o);
7656 }
7657 if (sampler.minFilter != -1) {
7658 SerializeNumberProperty("minFilter", sampler.minFilter, o);
7659 }
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09007660 // SerializeNumberProperty("wrapR", sampler.wrapR, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007661 SerializeNumberProperty("wrapS", sampler.wrapS, o);
7662 SerializeNumberProperty("wrapT", sampler.wrapT, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007663
David Siegel07616e82023-06-06 01:24:53 +02007664 SerializeExtrasAndExtensions(sampler, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007665}
7666
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007667static void SerializeGltfOrthographicCamera(const OrthographicCamera &camera,
David03ad33c2023-02-15 23:35:51 -06007668 detail::json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007669 SerializeNumberProperty("zfar", camera.zfar, o);
7670 SerializeNumberProperty("znear", camera.znear, o);
7671 SerializeNumberProperty("xmag", camera.xmag, o);
7672 SerializeNumberProperty("ymag", camera.ymag, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007673
David Siegel07616e82023-06-06 01:24:53 +02007674 SerializeExtrasAndExtensions(camera, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007675}
7676
7677static void SerializeGltfPerspectiveCamera(const PerspectiveCamera &camera,
David03ad33c2023-02-15 23:35:51 -06007678 detail::json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007679 SerializeNumberProperty("zfar", camera.zfar, o);
7680 SerializeNumberProperty("znear", camera.znear, o);
7681 if (camera.aspectRatio > 0) {
7682 SerializeNumberProperty("aspectRatio", camera.aspectRatio, o);
7683 }
7684
7685 if (camera.yfov > 0) {
7686 SerializeNumberProperty("yfov", camera.yfov, o);
7687 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007688
David Siegel07616e82023-06-06 01:24:53 +02007689 SerializeExtrasAndExtensions(camera, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007690}
7691
David03ad33c2023-02-15 23:35:51 -06007692static void SerializeGltfCamera(const Camera &camera, detail::json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007693 SerializeStringProperty("type", camera.type, o);
7694 if (!camera.name.empty()) {
Selmar Kokee3d0662018-10-08 16:20:43 +02007695 SerializeStringProperty("name", camera.name, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007696 }
7697
7698 if (camera.type.compare("orthographic") == 0) {
David03ad33c2023-02-15 23:35:51 -06007699 detail::json orthographic;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007700 SerializeGltfOrthographicCamera(camera.orthographic, orthographic);
David1f9a4b92023-02-15 22:56:18 -06007701 detail::JsonAddMember(o, "orthographic", std::move(orthographic));
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007702 } else if (camera.type.compare("perspective") == 0) {
David03ad33c2023-02-15 23:35:51 -06007703 detail::json perspective;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007704 SerializeGltfPerspectiveCamera(camera.perspective, perspective);
David1f9a4b92023-02-15 22:56:18 -06007705 detail::JsonAddMember(o, "perspective", std::move(perspective));
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007706 } else {
7707 // ???
7708 }
Syoyofe77cc52020-05-09 02:41:07 +09007709
David Siegel07616e82023-06-06 01:24:53 +02007710 SerializeExtrasAndExtensions(camera, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007711}
7712
David03ad33c2023-02-15 23:35:51 -06007713static void SerializeGltfScene(const Scene &scene, detail::json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007714 SerializeNumberArrayProperty<int>("nodes", scene.nodes, o);
7715
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007716 if (scene.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007717 SerializeStringProperty("name", scene.name, o);
7718 }
David Siegel07616e82023-06-06 01:24:53 +02007719 SerializeExtrasAndExtensions(scene, o);
Baranob_Ilya879cb472023-06-12 13:35:05 +04007720
7721 // KHR_audio
7722 if (!scene.audioEmitters.empty()) {
7723 detail::json_iterator it;
7724 if (!detail::FindMember(o, "extensions", it)) {
7725 detail::json extensions;
7726 detail::JsonSetObject(extensions);
7727 detail::JsonAddMember(o, "extensions", std::move(extensions));
7728 detail::FindMember(o, "extensions", it);
7729 }
7730 auto &extensions = detail::GetValue(it);
7731 if (!detail::FindMember(extensions, "KHR_audio", it)) {
7732 detail::json audio;
7733 detail::JsonSetObject(audio);
7734 detail::JsonAddMember(extensions, "KHR_audio", std::move(audio));
7735 detail::FindMember(o, "KHR_audio", it);
7736 }
7737 SerializeNumberArrayProperty("emitters", scene.audioEmitters, detail::GetValue(it));
7738 } else {
7739 detail::json_iterator ext_it;
7740 if (detail::FindMember(o, "extensions", ext_it)) {
7741 auto &extensions = detail::GetValue(ext_it);
7742 detail::json_iterator lp_it;
7743 if (detail::FindMember(extensions, "KHR_audio", lp_it)) {
7744 detail::Erase(extensions, lp_it);
7745 }
7746 if (detail::IsEmpty(extensions)) {
7747 detail::Erase(o, ext_it);
7748 }
7749 }
7750 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007751}
7752
David03ad33c2023-02-15 23:35:51 -06007753static void SerializeGltfSkin(const Skin &skin, detail::json &o) {
Syoyo Fujitad07b9762021-04-28 19:15:16 +09007754 // required
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007755 SerializeNumberArrayProperty<int>("joints", skin.joints, o);
Syoyo Fujitad07b9762021-04-28 19:15:16 +09007756
7757 if (skin.inverseBindMatrices >= 0) {
7758 SerializeNumberProperty("inverseBindMatrices", skin.inverseBindMatrices, o);
7759 }
7760
7761 if (skin.skeleton >= 0) {
7762 SerializeNumberProperty("skeleton", skin.skeleton, o);
7763 }
7764
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007765 if (skin.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007766 SerializeStringProperty("name", skin.name, o);
7767 }
David Siegel07616e82023-06-06 01:24:53 +02007768
7769 SerializeExtrasAndExtensions(skin, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007770}
7771
David03ad33c2023-02-15 23:35:51 -06007772static void SerializeGltfTexture(const Texture &texture, detail::json &o) {
johan bowaldb97d34c2018-04-02 07:29:29 +02007773 if (texture.sampler > -1) {
johan bowald642a3432018-04-01 12:37:18 +02007774 SerializeNumberProperty("sampler", texture.sampler, o);
7775 }
Selmar Kok8eb39042018-10-05 14:29:35 +02007776 if (texture.source > -1) {
7777 SerializeNumberProperty("source", texture.source, o);
7778 }
Christophe820ede82019-07-04 15:21:21 +09007779 if (texture.name.size()) {
7780 SerializeStringProperty("name", texture.name, o);
7781 }
David Siegel07616e82023-06-06 01:24:53 +02007782 SerializeExtrasAndExtensions(texture, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007783}
7784
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007785///
7786/// Serialize all properties except buffers and images.
7787///
David03ad33c2023-02-15 23:35:51 -06007788static void SerializeGltfModel(const Model *model, detail::json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007789 // ACCESSORS
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007790 if (model->accessors.size()) {
David03ad33c2023-02-15 23:35:51 -06007791 detail::json accessors;
David1f9a4b92023-02-15 22:56:18 -06007792 detail::JsonReserveArray(accessors, model->accessors.size());
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007793 for (unsigned int i = 0; i < model->accessors.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007794 detail::json accessor;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007795 SerializeGltfAccessor(model->accessors[i], accessor);
David1f9a4b92023-02-15 22:56:18 -06007796 detail::JsonPushBack(accessors, std::move(accessor));
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007797 }
David1f9a4b92023-02-15 22:56:18 -06007798 detail::JsonAddMember(o, "accessors", std::move(accessors));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007799 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007800
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007801 // ANIMATIONS
7802 if (model->animations.size()) {
David03ad33c2023-02-15 23:35:51 -06007803 detail::json animations;
David1f9a4b92023-02-15 22:56:18 -06007804 detail::JsonReserveArray(animations, model->animations.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007805 for (unsigned int i = 0; i < model->animations.size(); ++i) {
7806 if (model->animations[i].channels.size()) {
David03ad33c2023-02-15 23:35:51 -06007807 detail::json animation;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007808 SerializeGltfAnimation(model->animations[i], animation);
David1f9a4b92023-02-15 22:56:18 -06007809 detail::JsonPushBack(animations, std::move(animation));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007810 }
7811 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007812
David1f9a4b92023-02-15 22:56:18 -06007813 detail::JsonAddMember(o, "animations", std::move(animations));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007814 }
7815
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007816 // ASSET
David03ad33c2023-02-15 23:35:51 -06007817 detail::json asset;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007818 SerializeGltfAsset(model->asset, asset);
David1f9a4b92023-02-15 22:56:18 -06007819 detail::JsonAddMember(o, "asset", std::move(asset));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007820
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007821 // BUFFERVIEWS
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007822 if (model->bufferViews.size()) {
David03ad33c2023-02-15 23:35:51 -06007823 detail::json bufferViews;
David1f9a4b92023-02-15 22:56:18 -06007824 detail::JsonReserveArray(bufferViews, model->bufferViews.size());
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007825 for (unsigned int i = 0; i < model->bufferViews.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007826 detail::json bufferView;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007827 SerializeGltfBufferView(model->bufferViews[i], bufferView);
David1f9a4b92023-02-15 22:56:18 -06007828 detail::JsonPushBack(bufferViews, std::move(bufferView));
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007829 }
David1f9a4b92023-02-15 22:56:18 -06007830 detail::JsonAddMember(o, "bufferViews", std::move(bufferViews));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007831 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007832
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007833 // Extensions required
7834 if (model->extensionsRequired.size()) {
7835 SerializeStringArrayProperty("extensionsRequired",
7836 model->extensionsRequired, o);
7837 }
7838
7839 // MATERIALS
7840 if (model->materials.size()) {
David03ad33c2023-02-15 23:35:51 -06007841 detail::json materials;
David1f9a4b92023-02-15 22:56:18 -06007842 detail::JsonReserveArray(materials, model->materials.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007843 for (unsigned int i = 0; i < model->materials.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007844 detail::json material;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007845 SerializeGltfMaterial(model->materials[i], material);
Syoyo Fujita68adc4b2020-10-22 22:27:12 +09007846
David1f9a4b92023-02-15 22:56:18 -06007847 if (detail::JsonIsNull(material)) {
Syoyo Fujita68adc4b2020-10-22 22:27:12 +09007848 // Issue 294.
7849 // `material` does not have any required parameters
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09007850 // so the result may be null(unmodified) when all material parameters
7851 // have default value.
Syoyo Fujita68adc4b2020-10-22 22:27:12 +09007852 //
7853 // null is not allowed thus we create an empty JSON object.
David1f9a4b92023-02-15 22:56:18 -06007854 detail::JsonSetObject(material);
Syoyo Fujita68adc4b2020-10-22 22:27:12 +09007855 }
David1f9a4b92023-02-15 22:56:18 -06007856 detail::JsonPushBack(materials, std::move(material));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007857 }
David1f9a4b92023-02-15 22:56:18 -06007858 detail::JsonAddMember(o, "materials", std::move(materials));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007859 }
7860
7861 // MESHES
7862 if (model->meshes.size()) {
David03ad33c2023-02-15 23:35:51 -06007863 detail::json meshes;
David1f9a4b92023-02-15 22:56:18 -06007864 detail::JsonReserveArray(meshes, model->meshes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007865 for (unsigned int i = 0; i < model->meshes.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007866 detail::json mesh;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007867 SerializeGltfMesh(model->meshes[i], mesh);
David1f9a4b92023-02-15 22:56:18 -06007868 detail::JsonPushBack(meshes, std::move(mesh));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007869 }
David1f9a4b92023-02-15 22:56:18 -06007870 detail::JsonAddMember(o, "meshes", std::move(meshes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007871 }
7872
7873 // NODES
7874 if (model->nodes.size()) {
David03ad33c2023-02-15 23:35:51 -06007875 detail::json nodes;
David1f9a4b92023-02-15 22:56:18 -06007876 detail::JsonReserveArray(nodes, model->nodes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007877 for (unsigned int i = 0; i < model->nodes.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007878 detail::json node;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007879 SerializeGltfNode(model->nodes[i], node);
David1f9a4b92023-02-15 22:56:18 -06007880 detail::JsonPushBack(nodes, std::move(node));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007881 }
David1f9a4b92023-02-15 22:56:18 -06007882 detail::JsonAddMember(o, "nodes", std::move(nodes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007883 }
7884
7885 // SCENE
7886 if (model->defaultScene > -1) {
7887 SerializeNumberProperty<int>("scene", model->defaultScene, o);
7888 }
7889
7890 // SCENES
7891 if (model->scenes.size()) {
David03ad33c2023-02-15 23:35:51 -06007892 detail::json scenes;
David1f9a4b92023-02-15 22:56:18 -06007893 detail::JsonReserveArray(scenes, model->scenes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007894 for (unsigned int i = 0; i < model->scenes.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007895 detail::json currentScene;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007896 SerializeGltfScene(model->scenes[i], currentScene);
David1f9a4b92023-02-15 22:56:18 -06007897 detail::JsonPushBack(scenes, std::move(currentScene));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007898 }
David1f9a4b92023-02-15 22:56:18 -06007899 detail::JsonAddMember(o, "scenes", std::move(scenes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007900 }
7901
7902 // SKINS
7903 if (model->skins.size()) {
David03ad33c2023-02-15 23:35:51 -06007904 detail::json skins;
David1f9a4b92023-02-15 22:56:18 -06007905 detail::JsonReserveArray(skins, model->skins.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007906 for (unsigned int i = 0; i < model->skins.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007907 detail::json skin;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007908 SerializeGltfSkin(model->skins[i], skin);
David1f9a4b92023-02-15 22:56:18 -06007909 detail::JsonPushBack(skins, std::move(skin));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007910 }
David1f9a4b92023-02-15 22:56:18 -06007911 detail::JsonAddMember(o, "skins", std::move(skins));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007912 }
7913
7914 // TEXTURES
7915 if (model->textures.size()) {
David03ad33c2023-02-15 23:35:51 -06007916 detail::json textures;
David1f9a4b92023-02-15 22:56:18 -06007917 detail::JsonReserveArray(textures, model->textures.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007918 for (unsigned int i = 0; i < model->textures.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007919 detail::json texture;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007920 SerializeGltfTexture(model->textures[i], texture);
David1f9a4b92023-02-15 22:56:18 -06007921 detail::JsonPushBack(textures, std::move(texture));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007922 }
David1f9a4b92023-02-15 22:56:18 -06007923 detail::JsonAddMember(o, "textures", std::move(textures));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007924 }
7925
7926 // SAMPLERS
7927 if (model->samplers.size()) {
David03ad33c2023-02-15 23:35:51 -06007928 detail::json samplers;
David1f9a4b92023-02-15 22:56:18 -06007929 detail::JsonReserveArray(samplers, model->samplers.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007930 for (unsigned int i = 0; i < model->samplers.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007931 detail::json sampler;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007932 SerializeGltfSampler(model->samplers[i], sampler);
David1f9a4b92023-02-15 22:56:18 -06007933 detail::JsonPushBack(samplers, std::move(sampler));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007934 }
David1f9a4b92023-02-15 22:56:18 -06007935 detail::JsonAddMember(o, "samplers", std::move(samplers));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007936 }
7937
7938 // CAMERAS
7939 if (model->cameras.size()) {
David03ad33c2023-02-15 23:35:51 -06007940 detail::json cameras;
David1f9a4b92023-02-15 22:56:18 -06007941 detail::JsonReserveArray(cameras, model->cameras.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007942 for (unsigned int i = 0; i < model->cameras.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007943 detail::json camera;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007944 SerializeGltfCamera(model->cameras[i], camera);
David1f9a4b92023-02-15 22:56:18 -06007945 detail::JsonPushBack(cameras, std::move(camera));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007946 }
David1f9a4b92023-02-15 22:56:18 -06007947 detail::JsonAddMember(o, "cameras", std::move(cameras));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007948 }
7949
David Siegel07616e82023-06-06 01:24:53 +02007950 // EXTRAS & EXTENSIONS
7951 SerializeExtrasAndExtensions(*model, o);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007952
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007953 auto extensionsUsed = model->extensionsUsed;
7954
7955 // LIGHTS as KHR_lights_punctual
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007956 if (model->lights.size()) {
David03ad33c2023-02-15 23:35:51 -06007957 detail::json lights;
David1f9a4b92023-02-15 22:56:18 -06007958 detail::JsonReserveArray(lights, model->lights.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007959 for (unsigned int i = 0; i < model->lights.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06007960 detail::json light;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007961 SerializeGltfLight(model->lights[i], light);
David1f9a4b92023-02-15 22:56:18 -06007962 detail::JsonPushBack(lights, std::move(light));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007963 }
David03ad33c2023-02-15 23:35:51 -06007964 detail::json khr_lights_cmn;
David1f9a4b92023-02-15 22:56:18 -06007965 detail::JsonAddMember(khr_lights_cmn, "lights", std::move(lights));
David03ad33c2023-02-15 23:35:51 -06007966 detail::json ext_j;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007967
jrkooncecba5d6c2019-08-29 11:26:22 -05007968 {
David03ad33c2023-02-15 23:35:51 -06007969 detail::json_const_iterator it;
David1f9a4b92023-02-15 22:56:18 -06007970 if (detail::FindMember(o, "extensions", it)) {
7971 detail::JsonAssign(ext_j, detail::GetValue(it));
jrkooncecba5d6c2019-08-29 11:26:22 -05007972 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007973 }
7974
David1f9a4b92023-02-15 22:56:18 -06007975 detail::JsonAddMember(ext_j, "KHR_lights_punctual", std::move(khr_lights_cmn));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007976
David1f9a4b92023-02-15 22:56:18 -06007977 detail::JsonAddMember(o, "extensions", std::move(ext_j));
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007978
7979 // Also add "KHR_lights_punctual" to `extensionsUsed`
7980 {
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04007981 auto has_khr_lights_punctual =
7982 std::find_if(extensionsUsed.begin(), extensionsUsed.end(),
7983 [](const std::string &s) {
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08007984 return (s.compare("KHR_lights_punctual") == 0);
7985 });
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007986
7987 if (has_khr_lights_punctual == extensionsUsed.end()) {
7988 extensionsUsed.push_back("KHR_lights_punctual");
7989 }
7990 }
7991 }
7992
Baranob_Ilya78864c82023-06-12 10:43:52 +04007993 // KHR_audio
7994 if (!model->audioEmitters.empty() || !model->audioSources.empty()) {
7995 detail::json emitters;
7996 detail::JsonReserveArray(emitters, model->audioEmitters.size());
7997 for (unsigned int i = 0; i < model->audioEmitters.size(); ++i) {
7998 detail::json emitter;
7999 SerializeGltfAudioEmitter(model->audioEmitters[i], emitter);
8000 detail::JsonPushBack(emitters, std::move(emitter));
8001 }
8002 detail::json khr_audio_cmn;
8003 detail::JsonAddMember(khr_audio_cmn, "emitters", std::move(emitters));
8004
8005 detail::json sources;
8006 detail::JsonReserveArray(sources, model->audioSources.size());
8007 for (unsigned int i = 0; i < model->audioSources.size(); ++i) {
8008 detail::json source;
8009 SerializeGltfAudioSource(model->audioSources[i], source);
8010 detail::JsonPushBack(sources, std::move(source));
8011 }
8012 detail::JsonAddMember(khr_audio_cmn, "sources", std::move(sources));
8013
8014 detail::json ext_j;
8015 {
8016 detail::json_const_iterator it;
8017 if (detail::FindMember(o, "extensions", it)) {
8018 detail::JsonAssign(ext_j, detail::GetValue(it));
8019 }
8020 }
8021
8022 detail::JsonAddMember(ext_j, "KHR_audio", std::move(khr_audio_cmn));
8023
8024 detail::JsonAddMember(o, "extensions", std::move(ext_j));
Baranob_Ilyac9657be2023-06-12 12:34:34 +04008025
8026 // Also add "KHR_audio" to `extensionsUsed`
8027 {
8028 auto has_khr_audio =
8029 std::find_if(extensionsUsed.begin(), extensionsUsed.end(),
8030 [](const std::string &s) {
8031 return (s.compare("KHR_audio") == 0);
8032 });
8033
8034 if (has_khr_audio == extensionsUsed.end()) {
8035 extensionsUsed.push_back("KHR_audio");
8036 }
8037 }
Baranob_Ilya78864c82023-06-12 10:43:52 +04008038 }
8039
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008040 // Extensions used
Selmar1208f052021-02-01 19:24:41 +01008041 if (extensionsUsed.size()) {
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008042 SerializeStringArrayProperty("extensionsUsed", extensionsUsed, o);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008043 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008044}
8045
Johan Bowald52936a02019-07-17 09:06:45 +02008046static bool WriteGltfStream(std::ostream &stream, const std::string &content) {
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008047 stream << content << std::endl;
Marco Langer76586242023-03-12 19:26:05 +01008048 return stream.good();
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008049}
8050
8051static bool WriteGltfFile(const std::string &output,
8052 const std::string &content) {
Harokyangfb256602019-10-30 16:13:52 +08008053#ifdef _WIN32
Syoyo Fujita45cac782019-11-09 20:42:55 +09008054#if defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08008055 std::ofstream gltfFile(UTF8ToWchar(output).c_str());
Syoyo Fujita45cac782019-11-09 20:42:55 +09008056#elif defined(__GLIBCXX__)
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008057 int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
8058 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
8059 __gnu_cxx::stdio_filebuf<char> wfile_buf(
8060 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09008061 std::ostream gltfFile(&wfile_buf);
8062 if (!wfile_buf.is_open()) return false;
Harokyangfb256602019-10-30 16:13:52 +08008063#else
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008064 std::ofstream gltfFile(output.c_str());
8065 if (!gltfFile.is_open()) return false;
Syoyo Fujita45cac782019-11-09 20:42:55 +09008066#endif
8067#else
8068 std::ofstream gltfFile(output.c_str());
8069 if (!gltfFile.is_open()) return false;
8070#endif
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008071 return WriteGltfStream(gltfFile, content);
8072}
8073
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09008074static bool WriteBinaryGltfStream(std::ostream &stream,
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02008075 const std::string &content,
8076 const std::vector<unsigned char> &binBuffer) {
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008077 const std::string header = "glTF";
8078 const int version = 2;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008079
Alexander Wood190382a2021-10-08 12:19:13 -04008080 const uint32_t content_size = uint32_t(content.size());
8081 const uint32_t binBuffer_size = uint32_t(binBuffer.size());
8082 // determine number of padding bytes required to ensure 4 byte alignment
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09008083 const uint32_t content_padding_size =
8084 content_size % 4 == 0 ? 0 : 4 - content_size % 4;
8085 const uint32_t bin_padding_size =
8086 binBuffer_size % 4 == 0 ? 0 : 4 - binBuffer_size % 4;
Syoyo Fujita1d205202019-11-16 17:00:17 +09008087
8088 // 12 bytes for header, JSON content length, 8 bytes for JSON chunk info.
Alexander Wood190382a2021-10-08 12:19:13 -04008089 // Chunk data must be located at 4-byte boundary, which may require padding
Syoyo Fujitac4166e42020-01-08 02:38:01 +09008090 const uint32_t length =
Alexander Wood9e3d1f62021-10-08 16:57:56 -04008091 12 + 8 + content_size + content_padding_size +
Alexander Wood13803652021-10-08 20:13:07 -04008092 (binBuffer_size ? (8 + binBuffer_size + bin_padding_size) : 0);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008093
Syoyo Fujitacea69e32019-08-20 17:10:30 +09008094 stream.write(header.c_str(), std::streamsize(header.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008095 stream.write(reinterpret_cast<const char *>(&version), sizeof(version));
8096 stream.write(reinterpret_cast<const char *>(&length), sizeof(length));
8097
8098 // JSON chunk info, then JSON data
Alexander Wood9e3d1f62021-10-08 16:57:56 -04008099 const uint32_t model_length = uint32_t(content.size()) + content_padding_size;
Syoyo Fujita72f4a552020-01-08 00:40:41 +09008100 const uint32_t model_format = 0x4E4F534A;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008101 stream.write(reinterpret_cast<const char *>(&model_length),
Johan Bowald52936a02019-07-17 09:06:45 +02008102 sizeof(model_length));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008103 stream.write(reinterpret_cast<const char *>(&model_format),
Johan Bowald52936a02019-07-17 09:06:45 +02008104 sizeof(model_format));
Syoyo Fujitacea69e32019-08-20 17:10:30 +09008105 stream.write(content.c_str(), std::streamsize(content.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008106
8107 // Chunk must be multiplies of 4, so pad with spaces
Alexander Wood9e3d1f62021-10-08 16:57:56 -04008108 if (content_padding_size > 0) {
8109 const std::string padding = std::string(size_t(content_padding_size), ' ');
Syoyo Fujitacea69e32019-08-20 17:10:30 +09008110 stream.write(padding.c_str(), std::streamsize(padding.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008111 }
Syoyo Fujitac4166e42020-01-08 02:38:01 +09008112 if (binBuffer.size() > 0) {
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02008113 // BIN chunk info, then BIN data
Syoyo Fujita72f4a552020-01-08 00:40:41 +09008114 const uint32_t bin_length = uint32_t(binBuffer.size()) + bin_padding_size;
8115 const uint32_t bin_format = 0x004e4942;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02008116 stream.write(reinterpret_cast<const char *>(&bin_length),
Syoyo Fujitac4166e42020-01-08 02:38:01 +09008117 sizeof(bin_length));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02008118 stream.write(reinterpret_cast<const char *>(&bin_format),
Syoyo Fujitac4166e42020-01-08 02:38:01 +09008119 sizeof(bin_format));
8120 stream.write(reinterpret_cast<const char *>(binBuffer.data()),
8121 std::streamsize(binBuffer.size()));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02008122 // Chunksize must be multiplies of 4, so pad with zeroes
8123 if (bin_padding_size > 0) {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09008124 const std::vector<unsigned char> padding =
8125 std::vector<unsigned char>(size_t(bin_padding_size), 0);
8126 stream.write(reinterpret_cast<const char *>(padding.data()),
8127 std::streamsize(padding.size()));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02008128 }
8129 }
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09008130
Marco Langer76586242023-03-12 19:26:05 +01008131 stream.flush();
8132 return stream.good();
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008133}
8134
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09008135static bool WriteBinaryGltfFile(const std::string &output,
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02008136 const std::string &content,
8137 const std::vector<unsigned char> &binBuffer) {
Harokyangfb256602019-10-30 16:13:52 +08008138#ifdef _WIN32
Syoyo Fujita45cac782019-11-09 20:42:55 +09008139#if defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08008140 std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09008141#elif defined(__GLIBCXX__)
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008142 int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
8143 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
8144 __gnu_cxx::stdio_filebuf<char> wfile_buf(
8145 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita4ab03862019-11-10 15:31:17 +09008146 std::ostream gltfFile(&wfile_buf);
Harokyangfb256602019-10-30 16:13:52 +08008147#else
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008148 std::ofstream gltfFile(output.c_str(), std::ios::binary);
Harokyangfb256602019-10-30 16:13:52 +08008149#endif
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008150#else
8151 std::ofstream gltfFile(output.c_str(), std::ios::binary);
jrkooncecba5d6c2019-08-29 11:26:22 -05008152#endif
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09008153 return WriteBinaryGltfStream(gltfFile, content, binBuffer);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008154}
8155
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08008156bool TinyGLTF::WriteGltfSceneToStream(const Model *model, std::ostream &stream,
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008157 bool prettyPrint = true,
8158 bool writeBinary = false) {
David03ad33c2023-02-15 23:35:51 -06008159 detail::JsonDocument output;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008160
8161 /// Serialize all properties except buffers and images.
8162 SerializeGltfModel(model, output);
8163
8164 // BUFFERS
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02008165 std::vector<unsigned char> binBuffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008166 if (model->buffers.size()) {
David03ad33c2023-02-15 23:35:51 -06008167 detail::json buffers;
David1f9a4b92023-02-15 22:56:18 -06008168 detail::JsonReserveArray(buffers, model->buffers.size());
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008169 for (unsigned int i = 0; i < model->buffers.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06008170 detail::json buffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008171 if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
8172 SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008173 } else {
8174 SerializeGltfBuffer(model->buffers[i], buffer);
8175 }
David1f9a4b92023-02-15 22:56:18 -06008176 detail::JsonPushBack(buffers, std::move(buffer));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02008177 }
David1f9a4b92023-02-15 22:56:18 -06008178 detail::JsonAddMember(output, "buffers", std::move(buffers));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008179 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008180
8181 // IMAGES
8182 if (model->images.size()) {
David03ad33c2023-02-15 23:35:51 -06008183 detail::json images;
David1f9a4b92023-02-15 22:56:18 -06008184 detail::JsonReserveArray(images, model->images.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008185 for (unsigned int i = 0; i < model->images.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06008186 detail::json image;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008187
8188 std::string dummystring = "";
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008189 // UpdateImageObject need baseDir but only uses it if embeddedImages is
8190 // enabled, since we won't write separate images when writing to a stream
8191 // we
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08008192 std::string uri;
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08008193 if (!UpdateImageObject(model->images[i], dummystring, int(i), true,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08008194 &uri_cb, &this->WriteImageData,
8195 this->write_image_user_data_, &uri)) {
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08008196 return false;
8197 }
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08008198 SerializeGltfImage(model->images[i], uri, image);
David1f9a4b92023-02-15 22:56:18 -06008199 detail::JsonPushBack(images, std::move(image));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008200 }
David1f9a4b92023-02-15 22:56:18 -06008201 detail::JsonAddMember(output, "images", std::move(images));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008202 }
8203
8204 if (writeBinary) {
David1f9a4b92023-02-15 22:56:18 -06008205 return WriteBinaryGltfStream(stream, detail::JsonToString(output), binBuffer);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008206 } else {
David1f9a4b92023-02-15 22:56:18 -06008207 return WriteGltfStream(stream, detail::JsonToString(output, prettyPrint ? 2 : -1));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008208 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008209}
8210
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08008211bool TinyGLTF::WriteGltfSceneToFile(const Model *model,
8212 const std::string &filename,
Johan Bowald1af7c1d2019-07-16 14:50:46 +02008213 bool embedImages = false,
8214 bool embedBuffers = false,
8215 bool prettyPrint = true,
8216 bool writeBinary = false) {
David03ad33c2023-02-15 23:35:51 -06008217 detail::JsonDocument output;
Selmar Kok7cb31e42018-10-05 16:02:29 +02008218 std::string defaultBinFilename = GetBaseFilename(filename);
8219 std::string defaultBinFileExt = ".bin";
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00008220 std::string::size_type pos =
8221 defaultBinFilename.rfind('.', defaultBinFilename.length());
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008222
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09008223 if (pos != std::string::npos) {
Selmar Kok7cb31e42018-10-05 16:02:29 +02008224 defaultBinFilename = defaultBinFilename.substr(0, pos);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008225 }
johan bowald642a3432018-04-01 12:37:18 +02008226 std::string baseDir = GetBaseDir(filename);
8227 if (baseDir.empty()) {
8228 baseDir = "./";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05008229 }
Johan Bowald52936a02019-07-17 09:06:45 +02008230 /// Serialize all properties except buffers and images.
8231 SerializeGltfModel(model, output);
Syoyo Fujitad42767e2018-03-15 21:52:00 -05008232
Selmar Kok7cb31e42018-10-05 16:02:29 +02008233 // BUFFERS
Pyarelal Knowles385946d2023-01-03 18:18:04 -08008234 std::vector<std::string> usedFilenames;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02008235 std::vector<unsigned char> binBuffer;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008236 if (model->buffers.size()) {
David03ad33c2023-02-15 23:35:51 -06008237 detail::json buffers;
David1f9a4b92023-02-15 22:56:18 -06008238 detail::JsonReserveArray(buffers, model->buffers.size());
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008239 for (unsigned int i = 0; i < model->buffers.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06008240 detail::json buffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008241 if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
8242 SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008243 } else if (embedBuffers) {
8244 SerializeGltfBuffer(model->buffers[i], buffer);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00008245 } else {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008246 std::string binSavePath;
Pyarelal Knowles385946d2023-01-03 18:18:04 -08008247 std::string binFilename;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008248 std::string binUri;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09008249 if (!model->buffers[i].uri.empty() &&
8250 !IsDataURI(model->buffers[i].uri)) {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008251 binUri = model->buffers[i].uri;
Pyarelal Knowles385946d2023-01-03 18:18:04 -08008252 if (!uri_cb.decode(binUri, &binFilename, uri_cb.user_data)) {
8253 return false;
8254 }
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008255 } else {
Pyarelal Knowles385946d2023-01-03 18:18:04 -08008256 binFilename = defaultBinFilename + defaultBinFileExt;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008257 bool inUse = true;
8258 int numUsed = 0;
8259 while (inUse) {
8260 inUse = false;
Pyarelal Knowles385946d2023-01-03 18:18:04 -08008261 for (const std::string &usedName : usedFilenames) {
8262 if (binFilename.compare(usedName) != 0) continue;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008263 inUse = true;
Pyarelal Knowles385946d2023-01-03 18:18:04 -08008264 binFilename = defaultBinFilename + std::to_string(numUsed++) +
8265 defaultBinFileExt;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008266 break;
8267 }
Selmar Kokc884e582018-10-05 16:25:54 +02008268 }
Pyarelal Knowles385946d2023-01-03 18:18:04 -08008269
8270 if (uri_cb.encode) {
8271 if (!uri_cb.encode(binFilename, "buffer", &binUri,
8272 uri_cb.user_data)) {
8273 return false;
8274 }
8275 } else {
8276 binUri = binFilename;
8277 }
Selmar Kokc884e582018-10-05 16:25:54 +02008278 }
Pyarelal Knowles385946d2023-01-03 18:18:04 -08008279 usedFilenames.push_back(binFilename);
8280 binSavePath = JoinPath(baseDir, binFilename);
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008281 if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
8282 binUri)) {
8283 return false;
8284 }
Selmar Kokc884e582018-10-05 16:25:54 +02008285 }
David1f9a4b92023-02-15 22:56:18 -06008286 detail::JsonPushBack(buffers, std::move(buffer));
johan bowald30c53472018-03-30 11:49:36 +02008287 }
David1f9a4b92023-02-15 22:56:18 -06008288 detail::JsonAddMember(output, "buffers", std::move(buffers));
Selmar Kokeb9d29c2020-01-28 13:45:38 +01008289 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008290
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09008291 // IMAGES
Johan Bowaldfaa27222018-03-28 14:44:45 +02008292 if (model->images.size()) {
David03ad33c2023-02-15 23:35:51 -06008293 detail::json images;
David1f9a4b92023-02-15 22:56:18 -06008294 detail::JsonReserveArray(images, model->images.size());
Johan Bowaldfaa27222018-03-28 14:44:45 +02008295 for (unsigned int i = 0; i < model->images.size(); ++i) {
David03ad33c2023-02-15 23:35:51 -06008296 detail::json image;
johan bowald642a3432018-04-01 12:37:18 +02008297
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08008298 std::string uri;
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08008299 if (!UpdateImageObject(model->images[i], baseDir, int(i), embedImages,
Pyarelal Knowles385946d2023-01-03 18:18:04 -08008300 &uri_cb, &this->WriteImageData,
8301 this->write_image_user_data_, &uri)) {
Pyarelal Knowlesd2b0af62022-12-27 16:38:48 -08008302 return false;
8303 }
Pyarelal Knowlesde75d872022-12-13 15:50:41 -08008304 SerializeGltfImage(model->images[i], uri, image);
David1f9a4b92023-02-15 22:56:18 -06008305 detail::JsonPushBack(images, std::move(image));
Johan Bowaldfaa27222018-03-28 14:44:45 +02008306 }
David1f9a4b92023-02-15 22:56:18 -06008307 detail::JsonAddMember(output, "images", std::move(images));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008308 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008309
David Harmonda9eac22018-08-30 08:06:05 -04008310 if (writeBinary) {
David1f9a4b92023-02-15 22:56:18 -06008311 return WriteBinaryGltfFile(filename, detail::JsonToString(output), binBuffer);
David Harmonda9eac22018-08-30 08:06:05 -04008312 } else {
David1f9a4b92023-02-15 22:56:18 -06008313 return WriteGltfFile(filename, detail::JsonToString(output, (prettyPrint ? 2 : -1)));
David Harmonda9eac22018-08-30 08:06:05 -04008314 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00008315}
8316
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09008317} // namespace tinygltf
8318
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09008319#ifdef __clang__
8320#pragma clang diagnostic pop
8321#endif
8322
Syoyo Fujita612e5782022-09-18 21:01:39 +09008323#endif // TINYGLTF_IMPLEMENTATION