blob: a4aa9967c6a3b5b3304a367152e012151cffa8c8 [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 Fujitaa4d26882015-12-20 20:27:54 +090028// Version:
Syoyo Fujita3bddc092022-08-19 18:21:24 +090029// - v2.6.0 Support serializing sparse accessor(Thanks to @fynv).
30// Disable expanding file path for security(no use of awkward `wordexp` anymore).
Syoyo Fujita010ee9c2020-10-31 19:35:55 +090031// - v2.5.0 Add SetPreserveImageChannels() option to load image data as is.
Syoyo Fujita7905a5b2021-01-21 20:33:11 +090032// - v2.4.3 Fix null object output when when material has all default
33// parameters.
Syoyo Fujitac4166e42020-01-08 02:38:01 +090034// - v2.4.2 Decode percent-encoded URI.
Syoyo Fujita6e08b172019-10-30 17:25:38 +090035// - v2.4.1 Fix some glTF object class does not have `extensions` and/or
36// `extras` property.
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +090037// - v2.4.0 Experimental RapidJSON and C++14 support(Thanks to @jrkoone).
Syoyo Fujitaee179b22019-08-16 13:11:30 +090038// - v2.3.1 Set default value of minFilter and magFilter in Sampler to -1.
Syoyo Fujita046400b2019-07-24 19:26:48 +090039// - v2.3.0 Modified Material representation according to glTF 2.0 schema
40// (and introduced TextureInfo class)
Syoyo Fujita150f2432019-07-25 19:22:44 +090041// Change the behavior of `Value::IsNumber`. It return true either the
42// value is int or real.
Syoyo Fujitaca56f722019-03-07 21:04:25 +090043// - v2.2.0 Add loading 16bit PNG support. Add Sparse accessor support(Thanks
44// to @Ybalrid)
Syoyo Fujita7ae71102019-01-19 03:03:22 +090045// - v2.1.0 Add draco compression.
Syoyo Fujita0820d832018-10-04 15:45:13 +090046// - v2.0.1 Add comparsion feature(Thanks to @Selmar).
Syoyo Fujitaf6120152017-05-27 23:51:23 +090047// - v2.0.0 glTF 2.0!.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090048//
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090049// Tiny glTF loader is using following third party libraries:
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090050//
Syoyo Fujita2e21be72017-11-05 17:13:01 +090051// - jsonhpp: C++ JSON library.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090052// - base64: base64 decode/encode library.
53// - stb_image: Image loading library.
54//
Syoyo Fujita2840a0e2017-06-21 02:52:11 +090055#ifndef TINY_GLTF_H_
56#define TINY_GLTF_H_
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090057
Syoyo Fujitad42767e2018-03-15 21:52:00 -050058#include <array>
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090059#include <cassert>
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +090060#include <cmath> // std::fabs
Syoyo Fujitad42767e2018-03-15 21:52:00 -050061#include <cstdint>
Selmar Kok31cb7f92018-10-03 15:39:05 +020062#include <cstdlib>
Syoyo Fujita641b3cc2018-10-04 15:43:33 +090063#include <cstring>
Syoyo Fujita046400b2019-07-24 19:26:48 +090064#include <limits>
Syoyo Fujita523c9bf2016-04-20 14:06:56 +090065#include <map>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090066#include <string>
67#include <vector>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090068
zbendefy69eeea12022-09-05 23:54:57 +020069//Auto-detect C++14 standard version
70#if !defined(TINYGLTF_USE_CPP14) && defined(__cplusplus) && (__cplusplus >= 201402L)
71#define TINYGLTF_USE_CPP14
72#endif
73
jrkoonce51453942019-09-03 09:48:30 -050074#ifndef TINYGLTF_USE_CPP14
75#include <functional>
76#endif
77
Sascha Willems5f9cb242018-12-28 20:53:41 +010078#ifdef __ANDROID__
79#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
80#include <android/asset_manager.h>
81#endif
82#endif
83
Selmar Kok79e3df22019-10-29 16:22:07 +010084#ifdef __GNUC__
85#if (__GNUC__ < 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ <= 8))
Selmar Kokb74fade2019-10-29 16:09:32 +010086#define TINYGLTF_NOEXCEPT
Selmar Kok79e3df22019-10-29 16:22:07 +010087#else
88#define TINYGLTF_NOEXCEPT noexcept
Selmar Kokb74fade2019-10-29 16:09:32 +010089#endif
Selmar Kok79e3df22019-10-29 16:22:07 +010090#else
91#define TINYGLTF_NOEXCEPT noexcept
92#endif
93
Syoyo Fujita6e08b172019-10-30 17:25:38 +090094#define DEFAULT_METHODS(x) \
95 ~x() = default; \
96 x(const x &) = default; \
97 x(x &&) TINYGLTF_NOEXCEPT = default; \
98 x &operator=(const x &) = default; \
99 x &operator=(x &&) TINYGLTF_NOEXCEPT = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100100
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900101namespace tinygltf {
102
103#define TINYGLTF_MODE_POINTS (0)
104#define TINYGLTF_MODE_LINE (1)
105#define TINYGLTF_MODE_LINE_LOOP (2)
Thomas Tissot6c4a0062019-01-30 13:10:51 +0100106#define TINYGLTF_MODE_LINE_STRIP (3)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900107#define TINYGLTF_MODE_TRIANGLES (4)
108#define TINYGLTF_MODE_TRIANGLE_STRIP (5)
109#define TINYGLTF_MODE_TRIANGLE_FAN (6)
110
111#define TINYGLTF_COMPONENT_TYPE_BYTE (5120)
112#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE (5121)
113#define TINYGLTF_COMPONENT_TYPE_SHORT (5122)
114#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT (5123)
115#define TINYGLTF_COMPONENT_TYPE_INT (5124)
116#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125)
117#define TINYGLTF_COMPONENT_TYPE_FLOAT (5126)
Syoyo Fujita52ff00a2022-08-16 20:08:45 +0900118#define TINYGLTF_COMPONENT_TYPE_DOUBLE \
119 (5130) // OpenGL double type. Note that some of glTF 2.0 validator does not
120 // support double type even the schema seems allow any value of
121 // integer:
122 // https://github.com/KhronosGroup/glTF/blob/b9884a2fd45130b4d673dd6c8a706ee21ee5c5f7/specification/2.0/schema/accessor.schema.json#L22
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900123
Syoyo Fujitac2615632016-06-19 21:56:06 +0900124#define TINYGLTF_TEXTURE_FILTER_NEAREST (9728)
125#define TINYGLTF_TEXTURE_FILTER_LINEAR (9729)
126#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST (9984)
127#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST (9985)
128#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR (9986)
129#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR (9987)
130
Cemalettin Dervis246d8662017-12-07 20:29:51 +0100131#define TINYGLTF_TEXTURE_WRAP_REPEAT (10497)
Syoyo Fujitac2615632016-06-19 21:56:06 +0900132#define TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE (33071)
Luke San Antonio Bialecki904612c2016-08-19 17:28:03 -0400133#define TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT (33648)
Syoyo Fujitac2615632016-06-19 21:56:06 +0900134
Luke San Antoniocdf4cb72016-06-14 21:32:11 -0400135// Redeclarations of the above for technique.parameters.
136#define TINYGLTF_PARAMETER_TYPE_BYTE (5120)
137#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE (5121)
138#define TINYGLTF_PARAMETER_TYPE_SHORT (5122)
139#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT (5123)
140#define TINYGLTF_PARAMETER_TYPE_INT (5124)
141#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT (5125)
142#define TINYGLTF_PARAMETER_TYPE_FLOAT (5126)
143
144#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC2 (35664)
145#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC3 (35665)
146#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC4 (35666)
147
148#define TINYGLTF_PARAMETER_TYPE_INT_VEC2 (35667)
149#define TINYGLTF_PARAMETER_TYPE_INT_VEC3 (35668)
150#define TINYGLTF_PARAMETER_TYPE_INT_VEC4 (35669)
151
152#define TINYGLTF_PARAMETER_TYPE_BOOL (35670)
153#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC2 (35671)
154#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC3 (35672)
155#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC4 (35673)
156
157#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT2 (35674)
158#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT3 (35675)
159#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT4 (35676)
160
161#define TINYGLTF_PARAMETER_TYPE_SAMPLER_2D (35678)
162
163// End parameter types
164
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900165#define TINYGLTF_TYPE_VEC2 (2)
166#define TINYGLTF_TYPE_VEC3 (3)
167#define TINYGLTF_TYPE_VEC4 (4)
168#define TINYGLTF_TYPE_MAT2 (32 + 2)
169#define TINYGLTF_TYPE_MAT3 (32 + 3)
170#define TINYGLTF_TYPE_MAT4 (32 + 4)
171#define TINYGLTF_TYPE_SCALAR (64 + 1)
172#define TINYGLTF_TYPE_VECTOR (64 + 4)
173#define TINYGLTF_TYPE_MATRIX (64 + 16)
174
175#define TINYGLTF_IMAGE_FORMAT_JPEG (0)
176#define TINYGLTF_IMAGE_FORMAT_PNG (1)
177#define TINYGLTF_IMAGE_FORMAT_BMP (2)
178#define TINYGLTF_IMAGE_FORMAT_GIF (3)
179
Luke San Antonio6d616f52016-06-23 14:09:23 -0400180#define TINYGLTF_TEXTURE_FORMAT_ALPHA (6406)
181#define TINYGLTF_TEXTURE_FORMAT_RGB (6407)
Syoyo Fujitabde70212016-02-07 17:38:17 +0900182#define TINYGLTF_TEXTURE_FORMAT_RGBA (6408)
Luke San Antonio6d616f52016-06-23 14:09:23 -0400183#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE (6409)
184#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE_ALPHA (6410)
185
Syoyo Fujitabde70212016-02-07 17:38:17 +0900186#define TINYGLTF_TEXTURE_TARGET_TEXTURE2D (3553)
187#define TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE (5121)
188
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900189#define TINYGLTF_TARGET_ARRAY_BUFFER (34962)
190#define TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER (34963)
191
Luke San Antoniocdf4cb72016-06-14 21:32:11 -0400192#define TINYGLTF_SHADER_TYPE_VERTEX_SHADER (35633)
193#define TINYGLTF_SHADER_TYPE_FRAGMENT_SHADER (35632)
194
Selmar Kok31cb7f92018-10-03 15:39:05 +0200195#define TINYGLTF_DOUBLE_EPS (1.e-12)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900196#define TINYGLTF_DOUBLE_EQUAL(a, b) (std::fabs((b) - (a)) < TINYGLTF_DOUBLE_EPS)
Selmar Kok31cb7f92018-10-03 15:39:05 +0200197
Sascha Willems5f9cb242018-12-28 20:53:41 +0100198#ifdef __ANDROID__
199#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000200AAssetManager *asset_manager = nullptr;
Sascha Willems5f9cb242018-12-28 20:53:41 +0100201#endif
202#endif
203
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900204typedef enum {
Idriss Chaouch425195b2021-05-20 12:11:00 +0100205 NULL_TYPE,
206 REAL_TYPE,
207 INT_TYPE,
208 BOOL_TYPE,
209 STRING_TYPE,
210 ARRAY_TYPE,
211 BINARY_TYPE,
212 OBJECT_TYPE
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900213} Type;
214
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500215static inline int32_t GetComponentSizeInBytes(uint32_t componentType) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900216 if (componentType == TINYGLTF_COMPONENT_TYPE_BYTE) {
217 return 1;
218 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
219 return 1;
220 } else if (componentType == TINYGLTF_COMPONENT_TYPE_SHORT) {
221 return 2;
222 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
223 return 2;
224 } else if (componentType == TINYGLTF_COMPONENT_TYPE_INT) {
225 return 4;
226 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) {
227 return 4;
228 } else if (componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) {
229 return 4;
230 } else if (componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) {
231 return 8;
232 } else {
233 // Unknown componenty type
234 return -1;
235 }
236}
237
DingboDingboDingbo83ccb9f2019-08-29 18:49:15 -0400238static inline int32_t GetNumComponentsInType(uint32_t ty) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900239 if (ty == TINYGLTF_TYPE_SCALAR) {
240 return 1;
241 } else if (ty == TINYGLTF_TYPE_VEC2) {
242 return 2;
243 } else if (ty == TINYGLTF_TYPE_VEC3) {
244 return 3;
245 } else if (ty == TINYGLTF_TYPE_VEC4) {
246 return 4;
247 } else if (ty == TINYGLTF_TYPE_MAT2) {
248 return 4;
249 } else if (ty == TINYGLTF_TYPE_MAT3) {
250 return 9;
251 } else if (ty == TINYGLTF_TYPE_MAT4) {
252 return 16;
253 } else {
254 // Unknown componenty type
255 return -1;
256 }
257}
258
Syoyo Fujita150f2432019-07-25 19:22:44 +0900259// TODO(syoyo): Move these functions to TinyGLTF class
Selmar Kok0d0e97e2018-08-22 14:01:57 +0200260bool IsDataURI(const std::string &in);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900261bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
262 const std::string &in, size_t reqBytes, bool checkSize);
Selmar Kok0d0e97e2018-08-22 14:01:57 +0200263
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900264#ifdef __clang__
265#pragma clang diagnostic push
266// Suppress warning for : static Value null_value
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900267#pragma clang diagnostic ignored "-Wexit-time-destructors"
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900268#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900269#endif
270
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900271// Simple class to represent JSON object
272class Value {
273 public:
274 typedef std::vector<Value> Array;
275 typedef std::map<std::string, Value> Object;
276
Syoyo Fujita046400b2019-07-24 19:26:48 +0900277 Value()
278 : type_(NULL_TYPE),
279 int_value_(0),
Syoyo Fujita150f2432019-07-25 19:22:44 +0900280 real_value_(0.0),
Syoyo Fujita046400b2019-07-24 19:26:48 +0900281 boolean_value_(false) {}
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900282
283 explicit Value(bool b) : type_(BOOL_TYPE) { boolean_value_ = b; }
Syoyo Fujitaff515702019-08-24 16:29:14 +0900284 explicit Value(int i) : type_(INT_TYPE) {
285 int_value_ = i;
286 real_value_ = i;
287 }
Syoyo Fujita150f2432019-07-25 19:22:44 +0900288 explicit Value(double n) : type_(REAL_TYPE) { real_value_ = n; }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900289 explicit Value(const std::string &s) : type_(STRING_TYPE) {
290 string_value_ = s;
291 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900292 explicit Value(std::string &&s)
293 : type_(STRING_TYPE), string_value_(std::move(s)) {}
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900294 explicit Value(const unsigned char *p, size_t n) : type_(BINARY_TYPE) {
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900295 binary_value_.resize(n);
296 memcpy(binary_value_.data(), p, n);
297 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900298 explicit Value(std::vector<unsigned char> &&v) noexcept
299 : type_(BINARY_TYPE),
300 binary_value_(std::move(v)) {}
301 explicit Value(const Array &a) : type_(ARRAY_TYPE) { array_value_ = a; }
302 explicit Value(Array &&a) noexcept : type_(ARRAY_TYPE),
303 array_value_(std::move(a)) {}
jrkoonced1e14722019-08-27 11:51:02 -0500304
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900305 explicit Value(const Object &o) : type_(OBJECT_TYPE) { object_value_ = o; }
306 explicit Value(Object &&o) noexcept : type_(OBJECT_TYPE),
307 object_value_(std::move(o)) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100308
309 DEFAULT_METHODS(Value)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900310
Hill Mad1e32862021-02-20 22:30:44 -0800311 char Type() const { return static_cast<char>(type_); }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900312
313 bool IsBool() const { return (type_ == BOOL_TYPE); }
314
315 bool IsInt() const { return (type_ == INT_TYPE); }
316
Syoyo Fujita150f2432019-07-25 19:22:44 +0900317 bool IsNumber() const { return (type_ == REAL_TYPE) || (type_ == INT_TYPE); }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900318
Syoyo Fujita150f2432019-07-25 19:22:44 +0900319 bool IsReal() const { return (type_ == REAL_TYPE); }
Syoyo Fujita046400b2019-07-24 19:26:48 +0900320
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900321 bool IsString() const { return (type_ == STRING_TYPE); }
322
323 bool IsBinary() const { return (type_ == BINARY_TYPE); }
324
325 bool IsArray() const { return (type_ == ARRAY_TYPE); }
326
327 bool IsObject() const { return (type_ == OBJECT_TYPE); }
328
Syoyo Fujita150f2432019-07-25 19:22:44 +0900329 // Use this function if you want to have number value as double.
330 double GetNumberAsDouble() const {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900331 if (type_ == INT_TYPE) {
332 return double(int_value_);
Syoyo Fujita150f2432019-07-25 19:22:44 +0900333 } else {
334 return real_value_;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900335 }
Syoyo Fujita150f2432019-07-25 19:22:44 +0900336 }
Syoyo Fujita046400b2019-07-24 19:26:48 +0900337
Syoyo Fujita150f2432019-07-25 19:22:44 +0900338 // Use this function if you want to have number value as int.
Syoyo Fujitaff0a2e92020-05-11 19:07:00 +0900339 // TODO(syoyo): Support int value larger than 32 bits
340 int GetNumberAsInt() const {
Syoyo Fujita150f2432019-07-25 19:22:44 +0900341 if (type_ == REAL_TYPE) {
342 return int(real_value_);
343 } else {
344 return int_value_;
345 }
Syoyo Fujita046400b2019-07-24 19:26:48 +0900346 }
347
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900348 // Accessor
349 template <typename T>
350 const T &Get() const;
351 template <typename T>
352 T &Get();
353
354 // Lookup value from an array
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900355 const Value &Get(int idx) const {
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900356 static Value null_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900357 assert(IsArray());
358 assert(idx >= 0);
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900359 return (static_cast<size_t>(idx) < array_value_.size())
360 ? array_value_[static_cast<size_t>(idx)]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900361 : null_value;
362 }
363
364 // Lookup value from a key-value pair
365 const Value &Get(const std::string &key) const {
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900366 static Value null_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900367 assert(IsObject());
368 Object::const_iterator it = object_value_.find(key);
369 return (it != object_value_.end()) ? it->second : null_value;
370 }
371
372 size_t ArrayLen() const {
373 if (!IsArray()) return 0;
374 return array_value_.size();
375 }
376
377 // Valid only for object type.
378 bool Has(const std::string &key) const {
379 if (!IsObject()) return false;
380 Object::const_iterator it = object_value_.find(key);
381 return (it != object_value_.end()) ? true : false;
382 }
383
384 // List keys
385 std::vector<std::string> Keys() const {
386 std::vector<std::string> keys;
387 if (!IsObject()) return keys; // empty
388
389 for (Object::const_iterator it = object_value_.begin();
390 it != object_value_.end(); ++it) {
391 keys.push_back(it->first);
392 }
393
394 return keys;
395 }
396
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900397 size_t Size() const { return (IsArray() ? ArrayLen() : Keys().size()); }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900398
399 bool operator==(const tinygltf::Value &other) const;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +0000400
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900401 protected:
Syoyo Fujita046400b2019-07-24 19:26:48 +0900402 int type_ = NULL_TYPE;
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900403
Syoyo Fujita046400b2019-07-24 19:26:48 +0900404 int int_value_ = 0;
Syoyo Fujita150f2432019-07-25 19:22:44 +0900405 double real_value_ = 0.0;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900406 std::string string_value_;
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900407 std::vector<unsigned char> binary_value_;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900408 Array array_value_;
409 Object object_value_;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900410 bool boolean_value_ = false;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900411};
412
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900413#ifdef __clang__
414#pragma clang diagnostic pop
415#endif
416
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900417#define TINYGLTF_VALUE_GET(ctype, var) \
418 template <> \
419 inline const ctype &Value::Get<ctype>() const { \
420 return var; \
421 } \
422 template <> \
423 inline ctype &Value::Get<ctype>() { \
424 return var; \
425 }
426TINYGLTF_VALUE_GET(bool, boolean_value_)
Syoyo Fujita150f2432019-07-25 19:22:44 +0900427TINYGLTF_VALUE_GET(double, real_value_)
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900428TINYGLTF_VALUE_GET(int, int_value_)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900429TINYGLTF_VALUE_GET(std::string, string_value_)
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900430TINYGLTF_VALUE_GET(std::vector<unsigned char>, binary_value_)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900431TINYGLTF_VALUE_GET(Value::Array, array_value_)
432TINYGLTF_VALUE_GET(Value::Object, object_value_)
433#undef TINYGLTF_VALUE_GET
434
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900435#ifdef __clang__
436#pragma clang diagnostic push
437#pragma clang diagnostic ignored "-Wc++98-compat"
438#pragma clang diagnostic ignored "-Wpadded"
439#endif
440
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500441/// Agregate object for representing a color
Arthur Brainville58453192018-01-08 18:25:52 +0100442using ColorValue = std::array<double, 4>;
443
Syoyo Fujita046400b2019-07-24 19:26:48 +0900444// === legacy interface ====
445// TODO(syoyo): Deprecate `Parameter` class.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500446struct Parameter {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200447 bool bool_value = false;
Ben Buzbeef6af2242018-04-25 15:13:05 -0700448 bool has_number_value = false;
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900449 std::string string_value;
450 std::vector<double> number_array;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000451 std::map<std::string, double> json_double_value;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200452 double number_value = 0.0;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900453
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500454 // context sensitive methods. depending the type of the Parameter you are
455 // accessing, these are either valid or not
456 // If this parameter represent a texture map in a material, will return the
457 // texture index
Arthur Brainville58453192018-01-08 18:25:52 +0100458
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500459 /// Return the index of a texture if this Parameter is a texture map.
460 /// Returned value is only valid if the parameter represent a texture from a
461 /// material
Arthur Brainville41fe7722018-01-08 18:32:48 +0100462 int TextureIndex() const {
Arthur Brainville58453192018-01-08 18:25:52 +0100463 const auto it = json_double_value.find("index");
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500464 if (it != std::end(json_double_value)) {
Arthur Brainville58453192018-01-08 18:25:52 +0100465 return int(it->second);
466 }
467 return -1;
468 }
469
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000470 /// Return the index of a texture coordinate set if this Parameter is a
471 /// texture map. Returned value is only valid if the parameter represent a
472 /// texture from a material
Sascha Willemseb011062019-02-23 21:15:45 +0100473 int TextureTexCoord() const {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000474 const auto it = json_double_value.find("texCoord");
475 if (it != std::end(json_double_value)) {
476 return int(it->second);
477 }
Arthur Brainville8a98d982019-07-05 00:26:02 +0200478 // As per the spec, if texCoord is ommited, this parameter is 0
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000479 return 0;
Sascha Willemseb011062019-02-23 21:15:45 +0100480 }
481
Christophe820ede82019-07-04 15:21:21 +0900482 /// Return the scale of a texture if this Parameter is a normal texture map.
483 /// Returned value is only valid if the parameter represent a normal texture
484 /// from a material
485 double TextureScale() const {
486 const auto it = json_double_value.find("scale");
487 if (it != std::end(json_double_value)) {
488 return it->second;
489 }
Arthur Brainville8a98d982019-07-05 00:26:02 +0200490 // As per the spec, if scale is ommited, this paramter is 1
491 return 1;
492 }
Arthur Brianville (Ybalrid)2a9d9de2019-07-05 00:30:47 +0200493
Arthur Brainville8a98d982019-07-05 00:26:02 +0200494 /// Return the strength of a texture if this Parameter is a an occlusion map.
495 /// Returned value is only valid if the parameter represent an occlusion map
496 /// from a material
497 double TextureStrength() const {
498 const auto it = json_double_value.find("strength");
499 if (it != std::end(json_double_value)) {
500 return it->second;
501 }
502 // As per the spec, if strenghth is ommited, this parameter is 1
503 return 1;
Christophe820ede82019-07-04 15:21:21 +0900504 }
505
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500506 /// Material factor, like the roughness or metalness of a material
507 /// Returned value is only valid if the parameter represent a texture from a
508 /// material
Ben Buzbeef6af2242018-04-25 15:13:05 -0700509 double Factor() const { return number_value; }
Arthur Brainville58453192018-01-08 18:25:52 +0100510
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500511 /// Return the color of a material
512 /// Returned value is only valid if the parameter represent a texture from a
513 /// material
Arthur Brainville95853912018-01-08 18:37:44 +0100514 ColorValue ColorFactor() const {
Arthur Brainville58453192018-01-08 18:25:52 +0100515 return {
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500516 {// this agregate intialize the std::array object, and uses C++11 RVO.
517 number_array[0], number_array[1], number_array[2],
518 (number_array.size() > 3 ? number_array[3] : 1.0)}};
Arthur Brainville58453192018-01-08 18:25:52 +0100519 }
Selmar Kok31cb7f92018-10-03 15:39:05 +0200520
Selmar Kokff2b1f92019-10-21 17:58:09 +0200521 Parameter() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100522 DEFAULT_METHODS(Parameter)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900523 bool operator==(const Parameter &) const;
Arthur Brainville58453192018-01-08 18:25:52 +0100524};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900525
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900526#ifdef __clang__
527#pragma clang diagnostic pop
528#endif
529
530#ifdef __clang__
531#pragma clang diagnostic push
532#pragma clang diagnostic ignored "-Wpadded"
533#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900534
Syoyo Fujitabde70212016-02-07 17:38:17 +0900535typedef std::map<std::string, Parameter> ParameterMap;
Selmar Kokfa7022f2018-04-04 18:10:20 +0200536typedef std::map<std::string, Value> ExtensionMap;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900537
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000538struct AnimationChannel {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900539 int sampler; // required
540 int target_node; // required (index of the node to target)
541 std::string target_path; // required in ["translation", "rotation", "scale",
542 // "weights"]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900543 Value extras;
Selmar Kok4e2988e2019-08-16 14:08:08 +0200544 ExtensionMap extensions;
Selmar Kok973d9b32020-01-21 18:45:24 +0100545 ExtensionMap target_extensions;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900546
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900547 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
548 std::string extras_json_string;
549 std::string extensions_json_string;
Selmar Kok973d9b32020-01-21 18:45:24 +0100550 std::string target_extensions_json_string;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900551
Syoyo Fujita5b407452017-06-04 17:42:41 +0900552 AnimationChannel() : sampler(-1), target_node(-1) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100553 DEFAULT_METHODS(AnimationChannel)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900554 bool operator==(const AnimationChannel &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000555};
556
557struct AnimationSampler {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900558 int input; // required
559 int output; // required
Arthur Brianville (Ybalrid)2a9d9de2019-07-05 00:30:47 +0200560 std::string interpolation; // "LINEAR", "STEP","CUBICSPLINE" or user defined
561 // string. default "LINEAR"
Jens Olssonb3af2f12018-06-04 10:17:49 +0200562 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900563 ExtensionMap extensions;
564
565 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
566 std::string extras_json_string;
567 std::string extensions_json_string;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000568
Syoyo Fujita5b407452017-06-04 17:42:41 +0900569 AnimationSampler() : input(-1), output(-1), interpolation("LINEAR") {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100570 DEFAULT_METHODS(AnimationSampler)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900571 bool operator==(const AnimationSampler &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000572};
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900573
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900574struct Animation {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900575 std::string name;
576 std::vector<AnimationChannel> channels;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000577 std::vector<AnimationSampler> samplers;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900578 Value extras;
Selmar Kok4e2988e2019-08-16 14:08:08 +0200579 ExtensionMap extensions;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200580
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900581 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
582 std::string extras_json_string;
583 std::string extensions_json_string;
584
Selmar Kokff2b1f92019-10-21 17:58:09 +0200585 Animation() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100586 DEFAULT_METHODS(Animation)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900587 bool operator==(const Animation &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900588};
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900589
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000590struct Skin {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900591 std::string name;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900592 int inverseBindMatrices; // required here but not in the spec
593 int skeleton; // The index of the node used as a skeleton root
594 std::vector<int> joints; // Indices of skeleton nodes
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000595
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900596 Value extras;
597 ExtensionMap extensions;
598
599 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
600 std::string extras_json_string;
601 std::string extensions_json_string;
602
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900603 Skin() {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000604 inverseBindMatrices = -1;
Aurélien Chatelain8d5e0a72017-06-19 13:52:08 +0000605 skeleton = -1;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000606 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100607 DEFAULT_METHODS(Skin)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900608 bool operator==(const Skin &) const;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000609};
610
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000611struct Sampler {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900612 std::string name;
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900613 // glTF 2.0 spec does not define default value for `minFilter` and
614 // `magFilter`. Set -1 in TinyGLTF(issue #186)
615 int minFilter =
616 -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR",
zz8a35b8f2021-05-14 13:49:33 +0800617 // "NEAREST_MIPMAP_NEAREST", "LINEAR_MIPMAP_NEAREST",
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900618 // "NEAREST_MIPMAP_LINEAR", "LINEAR_MIPMAP_LINEAR"]
619 int magFilter =
620 -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR"]
621 int wrapS =
622 TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
623 // "REPEAT"], default "REPEAT"
624 int wrapT =
625 TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
626 // "REPEAT"], default "REPEAT"
Syoyo Fujita52ff00a2022-08-16 20:08:45 +0900627 // int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; // TinyGLTF extension. currently
628 // not used.
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900629
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900630 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900631 ExtensionMap extensions;
632
633 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
634 std::string extras_json_string;
635 std::string extensions_json_string;
Syoyo Fujitac2615632016-06-19 21:56:06 +0900636
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000637 Sampler()
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900638 : minFilter(-1),
639 magFilter(-1),
timmmeh73584ba2019-01-30 18:38:46 -0800640 wrapS(TINYGLTF_TEXTURE_WRAP_REPEAT),
Syoyo Fujita2c521b32020-12-04 00:50:46 +0900641 wrapT(TINYGLTF_TEXTURE_WRAP_REPEAT) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100642 DEFAULT_METHODS(Sampler)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900643 bool operator==(const Sampler &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000644};
645
Syoyo Fujita5b407452017-06-04 17:42:41 +0900646struct Image {
Syoyo Fujitac2615632016-06-19 21:56:06 +0900647 std::string name;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900648 int width;
649 int height;
650 int component;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000651 int bits; // bit depth per channel. 8(byte), 16 or 32.
652 int pixel_type; // pixel type(TINYGLTF_COMPONENT_TYPE_***). usually
653 // UBYTE(bits = 8) or USHORT(bits = 16)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900654 std::vector<unsigned char> image;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900655 int bufferView; // (required if no uri)
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500656 std::string mimeType; // (required if no uri) ["image/jpeg", "image/png",
657 // "image/bmp", "image/gif"]
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900658 std::string uri; // (required if no mimeType) uri is not decoded(e.g.
659 // whitespace may be represented as %20)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900660 Value extras;
Syoyo Fujita3e53feb2018-09-02 15:36:17 +0900661 ExtensionMap extensions;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900662
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900663 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
664 std::string extras_json_string;
665 std::string extensions_json_string;
666
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900667 // When this flag is true, data is stored to `image` in as-is format(e.g. jpeg
668 // compressed for "image/jpeg" mime) This feature is good if you use custom
669 // image loader function. (e.g. delayed decoding of images for faster glTF
670 // parsing) Default parser for Image does not provide as-is loading feature at
671 // the moment. (You can manipulate this by providing your own LoadImageData
672 // function)
Selmar Kokfa0a9982018-10-03 15:46:23 +0200673 bool as_is;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900674
675 Image() : as_is(false) {
676 bufferView = -1;
677 width = -1;
678 height = -1;
679 component = -1;
daemyung jang1739d022020-07-03 13:27:39 +0900680 bits = -1;
681 pixel_type = -1;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900682 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100683 DEFAULT_METHODS(Image)
jrkoonced1e14722019-08-27 11:51:02 -0500684
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900685 bool operator==(const Image &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000686};
687
688struct Texture {
Vladimír Vondruš239be2c2018-07-24 23:23:56 +0200689 std::string name;
690
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000691 int sampler;
Selmar Kok8eb39042018-10-05 14:29:35 +0200692 int source;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900693 Value extras;
Selmar Kokfa7022f2018-04-04 18:10:20 +0200694 ExtensionMap extensions;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900695
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900696 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
697 std::string extras_json_string;
698 std::string extensions_json_string;
699
Syoyo Fujita5b407452017-06-04 17:42:41 +0900700 Texture() : sampler(-1), source(-1) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100701 DEFAULT_METHODS(Texture)
jrkoonced1e14722019-08-27 11:51:02 -0500702
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900703 bool operator==(const Texture &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000704};
Syoyo Fujitabde70212016-02-07 17:38:17 +0900705
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900706struct TextureInfo {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900707 int index = -1; // required.
708 int texCoord; // The set index of texture's TEXCOORD attribute used for
709 // texture coordinate mapping.
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900710
711 Value extras;
712 ExtensionMap extensions;
713
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900714 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
715 std::string extras_json_string;
716 std::string extensions_json_string;
717
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900718 TextureInfo() : index(-1), texCoord(0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100719 DEFAULT_METHODS(TextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900720 bool operator==(const TextureInfo &) const;
721};
722
723struct NormalTextureInfo {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900724 int index = -1; // required
725 int texCoord; // The set index of texture's TEXCOORD attribute used for
726 // texture coordinate mapping.
727 double scale; // scaledNormal = normalize((<sampled normal texture value>
728 // * 2.0 - 1.0) * vec3(<normal scale>, <normal scale>, 1.0))
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900729
730 Value extras;
731 ExtensionMap extensions;
732
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900733 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
734 std::string extras_json_string;
735 std::string extensions_json_string;
736
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900737 NormalTextureInfo() : index(-1), texCoord(0), scale(1.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100738 DEFAULT_METHODS(NormalTextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900739 bool operator==(const NormalTextureInfo &) const;
740};
741
742struct OcclusionTextureInfo {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900743 int index = -1; // required
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900744 int texCoord; // The set index of texture's TEXCOORD attribute used for
745 // texture coordinate mapping.
746 double strength; // occludedColor = lerp(color, color * <sampled occlusion
747 // texture value>, <occlusion strength>)
748
749 Value extras;
750 ExtensionMap extensions;
751
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900752 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
753 std::string extras_json_string;
754 std::string extensions_json_string;
755
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900756 OcclusionTextureInfo() : index(-1), texCoord(0), strength(1.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100757 DEFAULT_METHODS(OcclusionTextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900758 bool operator==(const OcclusionTextureInfo &) const;
759};
760
761// pbrMetallicRoughness class defined in glTF 2.0 spec.
762struct PbrMetallicRoughness {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900763 std::vector<double> baseColorFactor; // len = 4. default [1,1,1,1]
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900764 TextureInfo baseColorTexture;
765 double metallicFactor; // default 1
766 double roughnessFactor; // default 1
767 TextureInfo metallicRoughnessTexture;
768
769 Value extras;
770 ExtensionMap extensions;
771
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900772 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
773 std::string extras_json_string;
774 std::string extensions_json_string;
775
776 PbrMetallicRoughness()
777 : baseColorFactor(std::vector<double>{1.0, 1.0, 1.0, 1.0}),
778 metallicFactor(1.0),
779 roughnessFactor(1.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100780 DEFAULT_METHODS(PbrMetallicRoughness)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900781 bool operator==(const PbrMetallicRoughness &) const;
782};
783
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000784// Each extension should be stored in a ParameterMap.
785// members not in the values could be included in the ParameterMap
786// to keep a single material model
787struct Material {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900788 std::string name;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900789
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900790 std::vector<double> emissiveFactor; // length 3. default [0, 0, 0]
Syoyo Fujita046400b2019-07-24 19:26:48 +0900791 std::string alphaMode; // default "OPAQUE"
792 double alphaCutoff; // default 0.5
793 bool doubleSided; // default false;
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900794
795 PbrMetallicRoughness pbrMetallicRoughness;
796
797 NormalTextureInfo normalTexture;
798 OcclusionTextureInfo occlusionTexture;
799 TextureInfo emissiveTexture;
800
Syoyo Fujita046400b2019-07-24 19:26:48 +0900801 // For backward compatibility
802 // TODO(syoyo): Remove `values` and `additionalValues` in the next release.
803 ParameterMap values;
804 ParameterMap additionalValues;
Selmar09d2ff12018-03-15 17:30:42 +0100805
806 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900807 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200808
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900809 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
810 std::string extras_json_string;
811 std::string extensions_json_string;
812
Syoyo Fujita046400b2019-07-24 19:26:48 +0900813 Material() : alphaMode("OPAQUE"), alphaCutoff(0.5), doubleSided(false) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100814 DEFAULT_METHODS(Material)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900815
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900816 bool operator==(const Material &) const;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000817};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900818
Syoyo Fujita5b407452017-06-04 17:42:41 +0900819struct BufferView {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900820 std::string name;
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900821 int buffer{-1}; // Required
Syoyo Fujita4e47bc72020-01-02 22:07:25 +0900822 size_t byteOffset{0}; // minimum 0, default 0
823 size_t byteLength{0}; // required, minimum 1. 0 = invalid
824 size_t byteStride{0}; // minimum 4, maximum 252 (multiple of 4), default 0 =
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900825 // understood to be tightly packed
826 int target{0}; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"] for vertex indices
827 // or atttribs. Could be 0 for other data
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900828 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900829 ExtensionMap extensions;
830
831 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
832 std::string extras_json_string;
833 std::string extensions_json_string;
834
Syoyo Fujita4e47bc72020-01-02 22:07:25 +0900835 bool dracoDecoded{false}; // Flag indicating this has been draco decoded
Syoyo Fujitad17ff662017-05-29 02:53:12 +0900836
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900837 BufferView()
838 : buffer(-1),
839 byteOffset(0),
840 byteLength(0),
841 byteStride(0),
842 target(0),
843 dracoDecoded(false) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100844 DEFAULT_METHODS(BufferView)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900845 bool operator==(const BufferView &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000846};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900847
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000848struct Accessor {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900849 int bufferView; // optional in spec but required here since sparse accessor
850 // are not supported
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900851 std::string name;
852 size_t byteOffset;
Fabien Freling9056aee2019-03-21 17:03:18 +0100853 bool normalized; // optional.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000854 int componentType; // (required) One of TINYGLTF_COMPONENT_TYPE_***
Syoyo Fujita5b407452017-06-04 17:42:41 +0900855 size_t count; // required
856 int type; // (required) One of TINYGLTF_TYPE_*** ..
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900857 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900858 ExtensionMap extensions;
859
860 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
861 std::string extras_json_string;
862 std::string extensions_json_string;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000863
Syoyo Fujita7905a5b2021-01-21 20:33:11 +0900864 std::vector<double>
865 minValues; // optional. integer value is promoted to double
866 std::vector<double>
867 maxValues; // optional. integer value is promoted to double
Syoyo Fujitad9d012a2017-07-12 18:29:29 +0900868
Arthur Brainville (Ybalrid)1ccb4ff2019-03-06 11:30:00 +0100869 struct {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000870 int count;
871 bool isSparse;
872 struct {
873 int byteOffset;
874 int bufferView;
875 int componentType; // a TINYGLTF_COMPONENT_TYPE_ value
876 } indices;
877 struct {
878 int bufferView;
879 int byteOffset;
880 } values;
Arthur Brainville (Ybalrid)1ccb4ff2019-03-06 11:30:00 +0100881 } sparse;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000882
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900883 ///
884 /// Utility function to compute byteStride for a given bufferView object.
885 /// Returns -1 upon invalid glTF value or parameter configuration.
886 ///
887 int ByteStride(const BufferView &bufferViewObject) const {
888 if (bufferViewObject.byteStride == 0) {
889 // Assume data is tightly packed.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500890 int componentSizeInBytes =
891 GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900892 if (componentSizeInBytes <= 0) {
893 return -1;
894 }
895
Syoyo Fujita81d75df2019-08-30 19:19:52 +0900896 int numComponents = GetNumComponentsInType(static_cast<uint32_t>(type));
897 if (numComponents <= 0) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900898 return -1;
899 }
900
Syoyo Fujita81d75df2019-08-30 19:19:52 +0900901 return componentSizeInBytes * numComponents;
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900902 } else {
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500903 // Check if byteStride is a mulple of the size of the accessor's component
904 // type.
905 int componentSizeInBytes =
906 GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900907 if (componentSizeInBytes <= 0) {
908 return -1;
909 }
910
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900911 if ((bufferViewObject.byteStride % uint32_t(componentSizeInBytes)) != 0) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900912 return -1;
913 }
Arthur Brainville8ce4e542018-01-10 01:27:12 +0100914 return static_cast<int>(bufferViewObject.byteStride);
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900915 }
916
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900917 // unreachable return 0;
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900918 }
919
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900920 Accessor()
921 : bufferView(-1),
922 byteOffset(0),
923 normalized(false),
924 componentType(-1),
925 count(0),
926 type(-1) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000927 sparse.isSparse = false;
928 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100929 DEFAULT_METHODS(Accessor)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900930 bool operator==(const tinygltf::Accessor &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000931};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900932
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900933struct PerspectiveCamera {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200934 double aspectRatio; // min > 0
935 double yfov; // required. min > 0
936 double zfar; // min > 0
937 double znear; // required. min > 0
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900938
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900939 PerspectiveCamera()
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900940 : aspectRatio(0.0),
941 yfov(0.0),
942 zfar(0.0) // 0 = use infinite projecton matrix
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900943 ,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900944 znear(0.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100945 DEFAULT_METHODS(PerspectiveCamera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900946 bool operator==(const PerspectiveCamera &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900947
Selmar09d2ff12018-03-15 17:30:42 +0100948 ExtensionMap extensions;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900949 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900950
951 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
952 std::string extras_json_string;
953 std::string extensions_json_string;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900954};
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000955
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900956struct OrthographicCamera {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200957 double xmag; // required. must not be zero.
958 double ymag; // required. must not be zero.
959 double zfar; // required. `zfar` must be greater than `znear`.
960 double znear; // required
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000961
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900962 OrthographicCamera() : xmag(0.0), ymag(0.0), zfar(0.0), znear(0.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100963 DEFAULT_METHODS(OrthographicCamera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900964 bool operator==(const OrthographicCamera &) const;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900965
Selmar09d2ff12018-03-15 17:30:42 +0100966 ExtensionMap extensions;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900967 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900968
969 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
970 std::string extras_json_string;
971 std::string extensions_json_string;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900972};
973
974struct Camera {
975 std::string type; // required. "perspective" or "orthographic"
976 std::string name;
977
978 PerspectiveCamera perspective;
979 OrthographicCamera orthographic;
980
981 Camera() {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100982 DEFAULT_METHODS(Camera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900983 bool operator==(const Camera &) const;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900984
Selmar09d2ff12018-03-15 17:30:42 +0100985 ExtensionMap extensions;
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000986 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900987
988 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
989 std::string extras_json_string;
990 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900991};
992
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000993struct Primitive {
994 std::map<std::string, int> attributes; // (required) A dictionary object of
995 // integer, where each integer
996 // is the index of the accessor
997 // containing an attribute.
998 int material; // The index of the material to apply to this primitive
Syoyo Fujita5b407452017-06-04 17:42:41 +0900999 // when rendering.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001000 int indices; // The index of the accessor that contains the indices.
1001 int mode; // one of TINYGLTF_MODE_***
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001002 std::vector<std::map<std::string, int> > targets; // array of morph targets,
Syoyo Fujita5b407452017-06-04 17:42:41 +09001003 // where each target is a dict with attribues in ["POSITION, "NORMAL",
1004 // "TANGENT"] pointing
1005 // to their corresponding accessors
Alex Wood7319db72019-01-24 15:38:16 -05001006 ExtensionMap extensions;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001007 Value extras;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001008
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001009 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1010 std::string extras_json_string;
1011 std::string extensions_json_string;
1012
Syoyo Fujita5b407452017-06-04 17:42:41 +09001013 Primitive() {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001014 material = -1;
1015 indices = -1;
daemyung jang1739d022020-07-03 13:27:39 +09001016 mode = -1;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001017 }
Selmar Kokb74fade2019-10-29 16:09:32 +01001018 DEFAULT_METHODS(Primitive)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001019 bool operator==(const Primitive &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001020};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001021
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001022struct Mesh {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001023 std::string name;
1024 std::vector<Primitive> primitives;
Syoyo Fujita5b407452017-06-04 17:42:41 +09001025 std::vector<double> weights; // weights to be applied to the Morph Targets
Selmar09d2ff12018-03-15 17:30:42 +01001026 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001027 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001028
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001029 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1030 std::string extras_json_string;
1031 std::string extensions_json_string;
1032
jrkoonced1e14722019-08-27 11:51:02 -05001033 Mesh() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001034 DEFAULT_METHODS(Mesh)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001035 bool operator==(const Mesh &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001036};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001037
1038class Node {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001039 public:
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09001040 Node() : camera(-1), skin(-1), mesh(-1) {}
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001041
Selmar Kokb74fade2019-10-29 16:09:32 +01001042 DEFAULT_METHODS(Node)
Syoyo Fujitad6b0d0a2019-06-29 17:31:13 +09001043
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001044 bool operator==(const Node &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001045
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001046 int camera; // the index of the camera referenced by this node
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001047
1048 std::string name;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00001049 int skin;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001050 int mesh;
1051 std::vector<int> children;
Syoyo Fujita7c877972016-03-08 01:31:49 +09001052 std::vector<double> rotation; // length must be 0 or 4
1053 std::vector<double> scale; // length must be 0 or 3
1054 std::vector<double> translation; // length must be 0 or 3
1055 std::vector<double> matrix; // length must be 0 or 16
Syoyo Fujita5b407452017-06-04 17:42:41 +09001056 std::vector<double> weights; // The weights of the instantiated Morph Target
Ben Buzbee3b735bb2018-04-24 11:39:30 -07001057
Selmar09d2ff12018-03-15 17:30:42 +01001058 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001059 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001060
1061 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1062 std::string extras_json_string;
1063 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001064};
1065
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001066struct Buffer {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001067 std::string name;
1068 std::vector<unsigned char> data;
Syoyo Fujita5b407452017-06-04 17:42:41 +09001069 std::string
1070 uri; // considered as required here but not in the spec (need to clarify)
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001071 // uri is not decoded(e.g. whitespace may be represented as %20)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001072 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001073 ExtensionMap extensions;
1074
1075 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1076 std::string extras_json_string;
1077 std::string extensions_json_string;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001078
Selmar Kokb74fade2019-10-29 16:09:32 +01001079 Buffer() = default;
1080 DEFAULT_METHODS(Buffer)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001081 bool operator==(const Buffer &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001082};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001083
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001084struct Asset {
Syoyo Fujitab702de72021-03-02 19:08:29 +09001085 std::string version = "2.0"; // required
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001086 std::string generator;
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +00001087 std::string minVersion;
1088 std::string copyright;
Selmar09d2ff12018-03-15 17:30:42 +01001089 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001090 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001091
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001092 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1093 std::string extras_json_string;
1094 std::string extensions_json_string;
1095
jrkoonced1e14722019-08-27 11:51:02 -05001096 Asset() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001097 DEFAULT_METHODS(Asset)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001098 bool operator==(const Asset &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001099};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001100
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001101struct Scene {
1102 std::string name;
1103 std::vector<int> nodes;
1104
Selmar09d2ff12018-03-15 17:30:42 +01001105 ExtensionMap extensions;
1106 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001107
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001108 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1109 std::string extras_json_string;
1110 std::string extensions_json_string;
1111
jrkoonced1e14722019-08-27 11:51:02 -05001112 Scene() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001113 DEFAULT_METHODS(Scene)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001114 bool operator==(const Scene &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001115};
1116
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001117struct SpotLight {
1118 double innerConeAngle;
1119 double outerConeAngle;
1120
Johan Bowald52936a02019-07-17 09:06:45 +02001121 SpotLight() : innerConeAngle(0.0), outerConeAngle(0.7853981634) {}
Selmar Kokb74fade2019-10-29 16:09:32 +01001122 DEFAULT_METHODS(SpotLight)
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001123 bool operator==(const SpotLight &) const;
1124
1125 ExtensionMap extensions;
1126 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001127
1128 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1129 std::string extras_json_string;
1130 std::string extensions_json_string;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001131};
1132
Emanuel Schrade186322b2017-11-06 11:14:41 +01001133struct Light {
1134 std::string name;
1135 std::vector<double> color;
Syoyo Fujita3dad3032020-05-13 21:22:23 +09001136 double intensity{1.0};
Emanuel Schrade186322b2017-11-06 11:14:41 +01001137 std::string type;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09001138 double range{0.0}; // 0.0 = inifinite
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001139 SpotLight spot;
1140
Benjamin Schmithüsenb2d7d882019-07-09 16:32:42 +02001141 Light() : intensity(1.0), range(0.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +01001142 DEFAULT_METHODS(Light)
Arthur Brainville (Ybalrid)9eeaf202019-09-05 13:02:05 +02001143
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001144 bool operator==(const Light &) const;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001145
1146 ExtensionMap extensions;
1147 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001148
1149 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1150 std::string extras_json_string;
1151 std::string extensions_json_string;
Emanuel Schrade186322b2017-11-06 11:14:41 +01001152};
1153
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001154class Model {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001155 public:
Selmar Kokff2b1f92019-10-21 17:58:09 +02001156 Model() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001157 DEFAULT_METHODS(Model)
Syoyo Fujitad6b0d0a2019-06-29 17:31:13 +09001158
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001159 bool operator==(const Model &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001160
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001161 std::vector<Accessor> accessors;
1162 std::vector<Animation> animations;
1163 std::vector<Buffer> buffers;
1164 std::vector<BufferView> bufferViews;
1165 std::vector<Material> materials;
1166 std::vector<Mesh> meshes;
1167 std::vector<Node> nodes;
1168 std::vector<Texture> textures;
1169 std::vector<Image> images;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00001170 std::vector<Skin> skins;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001171 std::vector<Sampler> samplers;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09001172 std::vector<Camera> cameras;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001173 std::vector<Scene> scenes;
Emanuel Schrade186322b2017-11-06 11:14:41 +01001174 std::vector<Light> lights;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001175
sammyKhana0a62bd2020-01-17 13:41:16 +01001176 int defaultScene = -1;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001177 std::vector<std::string> extensionsUsed;
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00001178 std::vector<std::string> extensionsRequired;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001179
1180 Asset asset;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001181
1182 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001183 ExtensionMap extensions;
1184
1185 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1186 std::string extras_json_string;
1187 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001188};
1189
Syoyo Fujita0614eb82016-10-14 18:50:14 +09001190enum SectionCheck {
Luke San Antonio9e36b612016-06-23 14:10:51 -04001191 NO_REQUIRE = 0x00,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001192 REQUIRE_VERSION = 0x01,
1193 REQUIRE_SCENE = 0x02,
1194 REQUIRE_SCENES = 0x04,
1195 REQUIRE_NODES = 0x08,
1196 REQUIRE_ACCESSORS = 0x10,
1197 REQUIRE_BUFFERS = 0x20,
1198 REQUIRE_BUFFER_VIEWS = 0x40,
1199 REQUIRE_ALL = 0x7f
Luke San Antonio9e36b612016-06-23 14:10:51 -04001200};
1201
Squareysff644d82018-03-13 22:36:18 +01001202///
1203/// LoadImageDataFunction type. Signature for custom image loading callbacks.
1204///
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001205typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *,
1206 std::string *, int, int,
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001207 const unsigned char *, int,
1208 void *user_pointer);
Squareysff644d82018-03-13 22:36:18 +01001209
johan bowald642a3432018-04-01 12:37:18 +02001210///
1211/// WriteImageDataFunction type. Signature for custom image writing callbacks.
1212///
1213typedef bool (*WriteImageDataFunction)(const std::string *, const std::string *,
1214 Image *, bool, void *);
1215
Squareys2d3594d2018-03-13 22:40:53 +01001216#ifndef TINYGLTF_NO_STB_IMAGE
Squareysff644d82018-03-13 22:36:18 +01001217// Declaration of default image loader callback
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001218bool LoadImageData(Image *image, const int image_idx, std::string *err,
1219 std::string *warn, int req_width, int req_height,
1220 const unsigned char *bytes, int size, void *);
Squareys2d3594d2018-03-13 22:40:53 +01001221#endif
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001222
johan bowald642a3432018-04-01 12:37:18 +02001223#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
1224// Declaration of default image writer callback
1225bool WriteImageData(const std::string *basepath, const std::string *filename,
1226 Image *image, bool embedImages, void *);
1227#endif
1228
Paolo Jovone6601bf2018-07-07 20:43:33 +02001229///
1230/// FilExistsFunction type. Signature for custom filesystem callbacks.
1231///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001232typedef bool (*FileExistsFunction)(const std::string &abs_filename, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001233
1234///
1235/// ExpandFilePathFunction type. Signature for custom filesystem callbacks.
1236///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001237typedef std::string (*ExpandFilePathFunction)(const std::string &, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001238
1239///
1240/// ReadWholeFileFunction type. Signature for custom filesystem callbacks.
1241///
1242typedef bool (*ReadWholeFileFunction)(std::vector<unsigned char> *,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001243 std::string *, const std::string &,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001244 void *);
1245
1246///
1247/// WriteWholeFileFunction type. Signature for custom filesystem callbacks.
1248///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001249typedef bool (*WriteWholeFileFunction)(std::string *, const std::string &,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001250 const std::vector<unsigned char> &,
1251 void *);
1252
1253///
1254/// A structure containing all required filesystem callbacks and a pointer to
1255/// their user data.
1256///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001257struct FsCallbacks {
1258 FileExistsFunction FileExists;
1259 ExpandFilePathFunction ExpandFilePath;
1260 ReadWholeFileFunction ReadWholeFile;
1261 WriteWholeFileFunction WriteWholeFile;
Paolo Jovone6601bf2018-07-07 20:43:33 +02001262
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001263 void *user_data; // An argument that is passed to all fs callbacks
Paolo Jovone6601bf2018-07-07 20:43:33 +02001264};
1265
1266#ifndef TINYGLTF_NO_FS
1267// Declaration of default filesystem callbacks
1268
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001269bool FileExists(const std::string &abs_filename, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001270
Syoyo Fujita3d939fd2020-06-06 18:13:15 +09001271///
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04001272/// Expand file path(e.g. `~` to home directory on posix, `%APPDATA%` to
Syoyo Fujita2c521b32020-12-04 00:50:46 +09001273/// `C:\\Users\\tinygltf\\AppData`)
Syoyo Fujita3d939fd2020-06-06 18:13:15 +09001274///
1275/// @param[in] filepath File path string. Assume UTF-8
1276/// @param[in] userdata User data. Set to `nullptr` if you don't need it.
1277///
1278std::string ExpandFilePath(const std::string &filepath, void *userdata);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001279
1280bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001281 const std::string &filepath, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001282
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001283bool WriteWholeFile(std::string *err, const std::string &filepath,
1284 const std::vector<unsigned char> &contents, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001285#endif
1286
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001287///
1288/// glTF Parser/Serialier context.
1289///
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001290class TinyGLTF {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001291 public:
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001292#ifdef __clang__
1293#pragma clang diagnostic push
1294#pragma clang diagnostic ignored "-Wc++98-compat"
1295#endif
1296
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001297 TinyGLTF() : bin_data_(nullptr), bin_size_(0), is_binary_(false) {}
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001298
1299#ifdef __clang__
1300#pragma clang diagnostic pop
1301#endif
1302
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001303 ~TinyGLTF() {}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001304
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001305 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001306 /// Loads glTF ASCII asset from a file.
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001307 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001308 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001309 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001310 bool LoadASCIIFromFile(Model *model, std::string *err, std::string *warn,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001311 const std::string &filename,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001312 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001313
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001314 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001315 /// Loads glTF ASCII asset from string(memory).
1316 /// `length` = strlen(str);
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001317 /// `base_dir` is a search path of glTF asset(e.g. images). Path Must be an
1318 /// expanded path (e.g. no tilde(`~`), no environment variables). Set warning
1319 /// message to `warn` for example it fails to load asserts. Returns false and
1320 /// set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001321 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001322 bool LoadASCIIFromString(Model *model, std::string *err, std::string *warn,
1323 const char *str, const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001324 const std::string &base_dir,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001325 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001326
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001327 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001328 /// Loads glTF binary asset from a file.
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001329 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001330 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001331 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001332 bool LoadBinaryFromFile(Model *model, std::string *err, std::string *warn,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001333 const std::string &filename,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001334 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001335
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001336 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001337 /// Loads glTF binary asset from memory.
1338 /// `length` = strlen(str);
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001339 /// `base_dir` is a search path of glTF asset(e.g. images). Path Must be an
1340 /// expanded path (e.g. no tilde(`~`), no environment variables).
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001341 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001342 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001343 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001344 bool LoadBinaryFromMemory(Model *model, std::string *err, std::string *warn,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001345 const unsigned char *bytes,
1346 const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001347 const std::string &base_dir = "",
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001348 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001349
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001350 ///
Johan Bowald1af7c1d2019-07-16 14:50:46 +02001351 /// Write glTF to stream, buffers and images will be embeded
1352 ///
1353 bool WriteGltfSceneToStream(Model *model, std::ostream &stream,
1354 bool prettyPrint, bool writeBinary);
1355
1356 ///
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001357 /// Write glTF to file.
1358 ///
johan bowald642a3432018-04-01 12:37:18 +02001359 bool WriteGltfSceneToFile(Model *model, const std::string &filename,
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001360 bool embedImages, bool embedBuffers,
1361 bool prettyPrint, bool writeBinary);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00001362
Squareysff644d82018-03-13 22:36:18 +01001363 ///
1364 /// Set callback to use for loading image data
1365 ///
1366 void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data);
1367
johan bowald642a3432018-04-01 12:37:18 +02001368 ///
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001369 /// Unset(remove) callback of loading image data
1370 ///
1371 void RemoveImageLoader();
1372
1373 ///
johan bowald642a3432018-04-01 12:37:18 +02001374 /// Set callback to use for writing image data
1375 ///
1376 void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data);
1377
Paolo Jovone6601bf2018-07-07 20:43:33 +02001378 ///
1379 /// Set callbacks to use for filesystem (fs) access and their user data
1380 ///
1381 void SetFsCallbacks(FsCallbacks callbacks);
1382
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001383 ///
1384 /// Set serializing default values(default = false).
1385 /// When true, default values are force serialized to .glTF.
Syoyo Fujitaff515702019-08-24 16:29:14 +09001386 /// This may be helpfull if you want to serialize a full description of glTF
1387 /// data.
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001388 ///
Syoyo Fujitaff515702019-08-24 16:29:14 +09001389 /// TODO(LTE): Supply parsing option as function arguments to
1390 /// `LoadASCIIFromFile()` and others, not by a class method
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001391 ///
1392 void SetSerializeDefaultValues(const bool enabled) {
1393 serialize_default_values_ = enabled;
1394 }
1395
Syoyo Fujitaff515702019-08-24 16:29:14 +09001396 bool GetSerializeDefaultValues() const { return serialize_default_values_; }
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001397
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001398 ///
1399 /// Store original JSON string for `extras` and `extensions`.
1400 /// This feature will be useful when the user want to reconstruct custom data
1401 /// structure from JSON string.
1402 ///
1403 void SetStoreOriginalJSONForExtrasAndExtensions(const bool enabled) {
1404 store_original_json_for_extras_and_extensions_ = enabled;
1405 }
1406
1407 bool GetStoreOriginalJSONForExtrasAndExtensions() const {
1408 return store_original_json_for_extras_and_extensions_;
1409 }
1410
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001411 ///
1412 /// Specify whether preserve image channales when loading images or not.
1413 /// (Not effective when the user suppy their own LoadImageData callbacks)
1414 ///
1415 void SetPreserveImageChannels(bool onoff) {
1416 preserve_image_channels_ = onoff;
1417 }
1418
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001419 bool GetPreserveImageChannels() const { return preserve_image_channels_; }
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001420
Syoyo Fujitabeded612016-05-01 20:03:43 +09001421 private:
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001422 ///
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001423 /// Loads glTF asset from string(memory).
1424 /// `length` = strlen(str);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001425 /// Set warning message to `warn` for example it fails to load asserts
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001426 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001427 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001428 bool LoadFromString(Model *model, std::string *err, std::string *warn,
1429 const char *str, const unsigned int length,
1430 const std::string &base_dir, unsigned int check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001431
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001432 const unsigned char *bin_data_ = nullptr;
1433 size_t bin_size_ = 0;
1434 bool is_binary_ = false;
1435
Syoyo Fujitaff515702019-08-24 16:29:14 +09001436 bool serialize_default_values_ = false; ///< Serialize default values?
Squareysff644d82018-03-13 22:36:18 +01001437
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001438 bool store_original_json_for_extras_and_extensions_ = false;
1439
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001440 bool preserve_image_channels_ = false; /// Default false(expand channels to
1441 /// RGBA) for backward compatibility.
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001442
Syoyo Fujita9117abb2022-08-16 20:10:26 +09001443 // Warning & error messages
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09001444 std::string warn_;
1445 std::string err_;
1446
Paolo Jovone6601bf2018-07-07 20:43:33 +02001447 FsCallbacks fs = {
1448#ifndef TINYGLTF_NO_FS
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001449 &tinygltf::FileExists, &tinygltf::ExpandFilePath,
1450 &tinygltf::ReadWholeFile, &tinygltf::WriteWholeFile,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001451
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001452 nullptr // Fs callback user data
Paolo Jovone6601bf2018-07-07 20:43:33 +02001453#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001454 nullptr, nullptr, nullptr, nullptr,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001455
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001456 nullptr // Fs callback user data
Paolo Jovone6601bf2018-07-07 20:43:33 +02001457#endif
1458 };
1459
Squareysff644d82018-03-13 22:36:18 +01001460 LoadImageDataFunction LoadImageData =
1461#ifndef TINYGLTF_NO_STB_IMAGE
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001462 &tinygltf::LoadImageData;
Squareysff644d82018-03-13 22:36:18 +01001463#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001464 nullptr;
Squareysff644d82018-03-13 22:36:18 +01001465#endif
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001466 void *load_image_user_data_{nullptr};
1467 bool user_image_loader_{false};
johan bowald642a3432018-04-01 12:37:18 +02001468
1469 WriteImageDataFunction WriteImageData =
1470#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001471 &tinygltf::WriteImageData;
johan bowald642a3432018-04-01 12:37:18 +02001472#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001473 nullptr;
johan bowald642a3432018-04-01 12:37:18 +02001474#endif
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001475 void *write_image_user_data_{nullptr};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001476};
1477
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001478#ifdef __clang__
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001479#pragma clang diagnostic pop // -Wpadded
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001480#endif
1481
Syoyo Fujita7c877972016-03-08 01:31:49 +09001482} // namespace tinygltf
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001483
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001484#endif // TINY_GLTF_H_
Syoyo Fujitac8d3c762017-05-25 01:42:38 +09001485
Selmar Kok31cb7f92018-10-03 15:39:05 +02001486#if defined(TINYGLTF_IMPLEMENTATION) || defined(__INTELLISENSE__)
Syoyo Fujita7c877972016-03-08 01:31:49 +09001487#include <algorithm>
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001488//#include <cassert>
Paolo Jovone6601bf2018-07-07 20:43:33 +02001489#ifndef TINYGLTF_NO_FS
Syoyo Fujita125d8e52019-11-09 20:52:56 +09001490#include <cstdio>
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001491#include <fstream>
Paolo Jovone6601bf2018-07-07 20:43:33 +02001492#endif
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001493#include <sstream>
Syoyo Fujitabeded612016-05-01 20:03:43 +09001494
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001495#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001496// Disable some warnings for external files.
1497#pragma clang diagnostic push
1498#pragma clang diagnostic ignored "-Wfloat-equal"
1499#pragma clang diagnostic ignored "-Wexit-time-destructors"
1500#pragma clang diagnostic ignored "-Wconversion"
1501#pragma clang diagnostic ignored "-Wold-style-cast"
Syoyo Fujita643ce102016-05-01 17:19:37 +09001502#pragma clang diagnostic ignored "-Wglobal-constructors"
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001503#if __has_warning("-Wreserved-id-macro")
Syoyo Fujita643ce102016-05-01 17:19:37 +09001504#pragma clang diagnostic ignored "-Wreserved-id-macro"
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001505#endif
Syoyo Fujita643ce102016-05-01 17:19:37 +09001506#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
1507#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001508#pragma clang diagnostic ignored "-Wc++98-compat"
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001509#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001510#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
1511#pragma clang diagnostic ignored "-Wswitch-enum"
1512#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001513#pragma clang diagnostic ignored "-Wweak-vtables"
1514#pragma clang diagnostic ignored "-Wcovered-switch-default"
Syoyo Fujitaa8f0b1c2018-08-28 21:33:40 +09001515#if __has_warning("-Wdouble-promotion")
1516#pragma clang diagnostic ignored "-Wdouble-promotion"
1517#endif
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001518#if __has_warning("-Wcomma")
Syoyo Fujitac8d3c762017-05-25 01:42:38 +09001519#pragma clang diagnostic ignored "-Wcomma"
1520#endif
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001521#if __has_warning("-Wzero-as-null-pointer-constant")
1522#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
1523#endif
1524#if __has_warning("-Wcast-qual")
1525#pragma clang diagnostic ignored "-Wcast-qual"
1526#endif
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001527#if __has_warning("-Wmissing-variable-declarations")
1528#pragma clang diagnostic ignored "-Wmissing-variable-declarations"
1529#endif
1530#if __has_warning("-Wmissing-prototypes")
1531#pragma clang diagnostic ignored "-Wmissing-prototypes"
1532#endif
1533#if __has_warning("-Wcast-align")
1534#pragma clang diagnostic ignored "-Wcast-align"
1535#endif
1536#if __has_warning("-Wnewline-eof")
1537#pragma clang diagnostic ignored "-Wnewline-eof"
1538#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001539#if __has_warning("-Wunused-parameter")
1540#pragma clang diagnostic ignored "-Wunused-parameter"
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001541#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001542#if __has_warning("-Wmismatched-tags")
1543#pragma clang diagnostic ignored "-Wmismatched-tags"
1544#endif
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001545#if __has_warning("-Wextra-semi-stmt")
1546#pragma clang diagnostic ignored "-Wextra-semi-stmt"
1547#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001548#endif
1549
1550// Disable GCC warnigs
1551#ifdef __GNUC__
1552#pragma GCC diagnostic push
1553#pragma GCC diagnostic ignored "-Wtype-limits"
Syoyo Fujitaca56f722019-03-07 21:04:25 +09001554#endif // __GNUC__
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001555
krokofc0116b2019-03-03 08:28:49 +02001556#ifndef TINYGLTF_NO_INCLUDE_JSON
jrkooncecba5d6c2019-08-29 11:26:22 -05001557#ifndef TINYGLTF_USE_RAPIDJSON
Alex Wood7319db72019-01-24 15:38:16 -05001558#include "json.hpp"
jrkooncecba5d6c2019-08-29 11:26:22 -05001559#else
Alvaro Barua3ddf8302021-03-18 00:21:23 +00001560#ifndef TINYGLTF_NO_INCLUDE_RAPIDJSON
jrkooncecba5d6c2019-08-29 11:26:22 -05001561#include "document.h"
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001562#include "prettywriter.h"
1563#include "rapidjson.h"
jrkooncecba5d6c2019-08-29 11:26:22 -05001564#include "stringbuffer.h"
1565#include "writer.h"
jrkooncecba5d6c2019-08-29 11:26:22 -05001566#endif
krokof4b6d112019-03-03 01:11:31 +02001567#endif
Alvaro Barua3ddf8302021-03-18 00:21:23 +00001568#endif
Alex Wood7319db72019-01-24 15:38:16 -05001569
1570#ifdef TINYGLTF_ENABLE_DRACO
Alex Wood7319db72019-01-24 15:38:16 -05001571#include "draco/compression/decode.h"
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001572#include "draco/core/decoder_buffer.h"
Alex Wood7319db72019-01-24 15:38:16 -05001573#endif
Squareys2d3594d2018-03-13 22:40:53 +01001574
1575#ifndef TINYGLTF_NO_STB_IMAGE
krokofc0116b2019-03-03 08:28:49 +02001576#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE
Alex Wood7319db72019-01-24 15:38:16 -05001577#include "stb_image.h"
Squareys2d3594d2018-03-13 22:40:53 +01001578#endif
krokof4b6d112019-03-03 01:11:31 +02001579#endif
Squareys2d3594d2018-03-13 22:40:53 +01001580
johan bowald642a3432018-04-01 12:37:18 +02001581#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
krokofc0116b2019-03-03 08:28:49 +02001582#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE
Alex Wood7319db72019-01-24 15:38:16 -05001583#include "stb_image_write.h"
johan bowald642a3432018-04-01 12:37:18 +02001584#endif
krokof4b6d112019-03-03 01:11:31 +02001585#endif
johan bowald642a3432018-04-01 12:37:18 +02001586
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001587#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001588#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001589#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001590
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001591#ifdef __GNUC__
1592#pragma GCC diagnostic pop
1593#endif
1594
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001595#ifdef _WIN32
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001596
1597// issue 143.
1598// Define NOMINMAX to avoid min/max defines,
1599// but undef it after included windows.h
1600#ifndef NOMINMAX
1601#define TINYGLTF_INTERNAL_NOMINMAX
1602#define NOMINMAX
1603#endif
1604
Syoyo Fujita87be0ce2019-02-19 21:36:32 +09001605#ifndef WIN32_LEAN_AND_MEAN
1606#define WIN32_LEAN_AND_MEAN
1607#define TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1608#endif
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001609#include <windows.h> // include API for expanding a file path
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001610
Syoyo Fujita87be0ce2019-02-19 21:36:32 +09001611#ifdef TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1612#undef WIN32_LEAN_AND_MEAN
1613#endif
1614
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001615#if defined(TINYGLTF_INTERNAL_NOMINMAX)
1616#undef NOMINMAX
1617#endif
1618
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001619#if defined(__GLIBCXX__) // mingw
Syoyo Fujita45cac782019-11-09 20:42:55 +09001620
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001621#include <fcntl.h> // _O_RDONLY
1622
1623#include <ext/stdio_filebuf.h> // fstream (all sorts of IO stuff) + stdio_filebuf (=streambuf)
Syoyo Fujita45cac782019-11-09 20:42:55 +09001624
1625#endif
1626
Julian Smith0598a202021-08-25 12:06:08 +01001627#elif !defined(__ANDROID__) && !defined(__OpenBSD__)
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001628//#include <wordexp.h>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001629#endif
1630
Christopher Sean Morrison2c9b2562022-05-14 19:00:19 -04001631#if defined(__sparcv9) || defined(__powerpc__)
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001632// Big endian
1633#else
1634#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
1635#define TINYGLTF_LITTLE_ENDIAN 1
1636#endif
1637#endif
1638
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001639namespace {
jrkooncecba5d6c2019-08-29 11:26:22 -05001640#ifdef TINYGLTF_USE_RAPIDJSON
jrkooncece7fa742019-09-04 13:31:44 -05001641
1642#ifdef TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001643// This uses the RapidJSON CRTAllocator. It is thread safe and multiple
1644// documents may be active at once.
1645using json =
1646 rapidjson::GenericValue<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
1647using json_const_iterator = json::ConstMemberIterator;
1648using json_const_array_iterator = json const *;
1649using JsonDocument =
jrkooncece7fa742019-09-04 13:31:44 -05001650 rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001651rapidjson::CrtAllocator s_CrtAllocator; // stateless and thread safe
1652rapidjson::CrtAllocator &GetAllocator() { return s_CrtAllocator; }
jrkooncece7fa742019-09-04 13:31:44 -05001653#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001654// This uses the default RapidJSON MemoryPoolAllocator. It is very fast, but
1655// not thread safe. Only a single JsonDocument may be active at any one time,
1656// meaning only a single gltf load/save can be active any one time.
1657using json = rapidjson::Value;
1658using json_const_iterator = json::ConstMemberIterator;
1659using json_const_array_iterator = json const *;
1660rapidjson::Document *s_pActiveDocument = nullptr;
1661rapidjson::Document::AllocatorType &GetAllocator() {
1662 assert(s_pActiveDocument); // Root json node must be JsonDocument type
1663 return s_pActiveDocument->GetAllocator();
1664}
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001665
1666#ifdef __clang__
1667#pragma clang diagnostic push
1668// Suppress JsonDocument(JsonDocument &&rhs) noexcept
1669#pragma clang diagnostic ignored "-Wunused-member-function"
1670#endif
1671
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001672struct JsonDocument : public rapidjson::Document {
1673 JsonDocument() {
1674 assert(s_pActiveDocument ==
1675 nullptr); // When using default allocator, only one document can be
1676 // active at a time, if you need multiple active at once,
1677 // define TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
1678 s_pActiveDocument = this;
jrkooncece7fa742019-09-04 13:31:44 -05001679 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001680 JsonDocument(const JsonDocument &) = delete;
1681 JsonDocument(JsonDocument &&rhs) noexcept
jrkooncece7fa742019-09-04 13:31:44 -05001682 : rapidjson::Document(std::move(rhs)) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001683 s_pActiveDocument = this;
1684 rhs.isNil = true;
1685 }
1686 ~JsonDocument() {
1687 if (!isNil) {
1688 s_pActiveDocument = nullptr;
jrkooncecba5d6c2019-08-29 11:26:22 -05001689 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001690 }
jrkooncece7fa742019-09-04 13:31:44 -05001691
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001692 private:
1693 bool isNil = false;
1694};
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001695
1696#ifdef __clang__
1697#pragma clang diagnostic pop
1698#endif
1699
jrkooncece7fa742019-09-04 13:31:44 -05001700#endif // TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001701
jrkooncecba5d6c2019-08-29 11:26:22 -05001702#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001703using nlohmann::json;
1704using json_const_iterator = json::const_iterator;
1705using json_const_array_iterator = json_const_iterator;
1706using JsonDocument = json;
jrkooncecba5d6c2019-08-29 11:26:22 -05001707#endif
1708
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001709void JsonParse(JsonDocument &doc, const char *str, size_t length,
1710 bool throwExc = false) {
jrkooncecba5d6c2019-08-29 11:26:22 -05001711#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001712 (void)throwExc;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001713 doc.Parse(str, length);
jrkooncecba5d6c2019-08-29 11:26:22 -05001714#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001715 doc = json::parse(str, str + length, nullptr, throwExc);
jrkooncecba5d6c2019-08-29 11:26:22 -05001716#endif
jrkooncecba5d6c2019-08-29 11:26:22 -05001717}
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001718} // namespace
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001719
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001720#ifdef __APPLE__
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001721#include "TargetConditionals.h"
Syoyo Fujitab23f6fe2017-11-15 01:03:09 +09001722#endif
1723
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001724#ifdef __clang__
1725#pragma clang diagnostic push
1726#pragma clang diagnostic ignored "-Wc++98-compat"
1727#endif
1728
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09001729namespace tinygltf {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001730
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001731///
1732/// Internal LoadImageDataOption struct.
1733/// This struct is passed through `user_pointer` in LoadImageData.
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001734/// The struct is not passed when the user supply their own LoadImageData
1735/// callbacks.
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001736///
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001737struct LoadImageDataOption {
1738 // true: preserve image channels(e.g. load as RGB image if the image has RGB
1739 // channels) default `false`(channels are expanded to RGBA for backward
1740 // compatiblity).
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001741 bool preserve_channels{false};
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001742};
1743
Selmar Kok31cb7f92018-10-03 15:39:05 +02001744// Equals function for Value, for recursivity
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001745static bool Equals(const tinygltf::Value &one, const tinygltf::Value &other) {
1746 if (one.Type() != other.Type()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001747
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001748 switch (one.Type()) {
1749 case NULL_TYPE:
1750 return true;
1751 case BOOL_TYPE:
1752 return one.Get<bool>() == other.Get<bool>();
Syoyo Fujita150f2432019-07-25 19:22:44 +09001753 case REAL_TYPE:
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001754 return TINYGLTF_DOUBLE_EQUAL(one.Get<double>(), other.Get<double>());
1755 case INT_TYPE:
1756 return one.Get<int>() == other.Get<int>();
1757 case OBJECT_TYPE: {
1758 auto oneObj = one.Get<tinygltf::Value::Object>();
1759 auto otherObj = other.Get<tinygltf::Value::Object>();
1760 if (oneObj.size() != otherObj.size()) return false;
1761 for (auto &it : oneObj) {
1762 auto otherIt = otherObj.find(it.first);
1763 if (otherIt == otherObj.end()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001764
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001765 if (!Equals(it.second, otherIt->second)) return false;
1766 }
1767 return true;
1768 }
1769 case ARRAY_TYPE: {
1770 if (one.Size() != other.Size()) return false;
1771 for (int i = 0; i < int(one.Size()); ++i)
Selmara63cc632019-04-16 16:57:43 +02001772 if (!Equals(one.Get(i), other.Get(i))) return false;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001773 return true;
1774 }
1775 case STRING_TYPE:
1776 return one.Get<std::string>() == other.Get<std::string>();
1777 case BINARY_TYPE:
1778 return one.Get<std::vector<unsigned char> >() ==
1779 other.Get<std::vector<unsigned char> >();
1780 default: {
1781 // unhandled type
1782 return false;
1783 }
1784 }
Selmar Kok31cb7f92018-10-03 15:39:05 +02001785}
1786
1787// Equals function for std::vector<double> using TINYGLTF_DOUBLE_EPSILON
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001788static bool Equals(const std::vector<double> &one,
1789 const std::vector<double> &other) {
1790 if (one.size() != other.size()) return false;
1791 for (int i = 0; i < int(one.size()); ++i) {
1792 if (!TINYGLTF_DOUBLE_EQUAL(one[size_t(i)], other[size_t(i)])) return false;
1793 }
1794 return true;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001795}
1796
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001797bool Accessor::operator==(const Accessor &other) const {
1798 return this->bufferView == other.bufferView &&
1799 this->byteOffset == other.byteOffset &&
1800 this->componentType == other.componentType &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001801 this->count == other.count && this->extensions == other.extensions &&
1802 this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001803 Equals(this->maxValues, other.maxValues) &&
1804 Equals(this->minValues, other.minValues) && this->name == other.name &&
1805 this->normalized == other.normalized && this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001806}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001807bool Animation::operator==(const Animation &other) const {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001808 return this->channels == other.channels &&
1809 this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001810 this->name == other.name && this->samplers == other.samplers;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001811}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001812bool AnimationChannel::operator==(const AnimationChannel &other) const {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001813 return this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001814 this->target_node == other.target_node &&
1815 this->target_path == other.target_path &&
1816 this->sampler == other.sampler;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001817}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001818bool AnimationSampler::operator==(const AnimationSampler &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001819 return this->extras == other.extras && this->extensions == other.extensions &&
1820 this->input == other.input &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001821 this->interpolation == other.interpolation &&
1822 this->output == other.output;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001823}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001824bool Asset::operator==(const Asset &other) const {
1825 return this->copyright == other.copyright &&
1826 this->extensions == other.extensions && this->extras == other.extras &&
1827 this->generator == other.generator &&
1828 this->minVersion == other.minVersion && this->version == other.version;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001829}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001830bool Buffer::operator==(const Buffer &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001831 return this->data == other.data && this->extensions == other.extensions &&
1832 this->extras == other.extras && this->name == other.name &&
1833 this->uri == other.uri;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001834}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001835bool BufferView::operator==(const BufferView &other) const {
1836 return this->buffer == other.buffer && this->byteLength == other.byteLength &&
1837 this->byteOffset == other.byteOffset &&
1838 this->byteStride == other.byteStride && this->name == other.name &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001839 this->target == other.target && this->extensions == other.extensions &&
1840 this->extras == other.extras &&
Alexander Wood0d77a292019-01-26 08:58:45 -05001841 this->dracoDecoded == other.dracoDecoded;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001842}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001843bool Camera::operator==(const Camera &other) const {
1844 return this->name == other.name && this->extensions == other.extensions &&
1845 this->extras == other.extras &&
1846 this->orthographic == other.orthographic &&
1847 this->perspective == other.perspective && this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001848}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001849bool Image::operator==(const Image &other) const {
1850 return this->bufferView == other.bufferView &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001851 this->component == other.component &&
1852 this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001853 this->height == other.height && this->image == other.image &&
1854 this->mimeType == other.mimeType && this->name == other.name &&
1855 this->uri == other.uri && this->width == other.width;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001856}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001857bool Light::operator==(const Light &other) const {
1858 return Equals(this->color, other.color) && this->name == other.name &&
1859 this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001860}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001861bool Material::operator==(const Material &other) const {
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001862 return (this->pbrMetallicRoughness == other.pbrMetallicRoughness) &&
1863 (this->normalTexture == other.normalTexture) &&
1864 (this->occlusionTexture == other.occlusionTexture) &&
1865 (this->emissiveTexture == other.emissiveTexture) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09001866 Equals(this->emissiveFactor, other.emissiveFactor) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001867 (this->alphaMode == other.alphaMode) &&
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001868 TINYGLTF_DOUBLE_EQUAL(this->alphaCutoff, other.alphaCutoff) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001869 (this->doubleSided == other.doubleSided) &&
1870 (this->extensions == other.extensions) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09001871 (this->extras == other.extras) && (this->values == other.values) &&
1872 (this->additionalValues == other.additionalValues) &&
1873 (this->name == other.name);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001874}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001875bool Mesh::operator==(const Mesh &other) const {
1876 return this->extensions == other.extensions && this->extras == other.extras &&
Selmar Kok81b672b2019-10-18 16:08:44 +02001877 this->name == other.name && Equals(this->weights, other.weights) &&
1878 this->primitives == other.primitives;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001879}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001880bool Model::operator==(const Model &other) const {
1881 return this->accessors == other.accessors &&
1882 this->animations == other.animations && this->asset == other.asset &&
1883 this->buffers == other.buffers &&
1884 this->bufferViews == other.bufferViews &&
1885 this->cameras == other.cameras &&
1886 this->defaultScene == other.defaultScene &&
1887 this->extensions == other.extensions &&
1888 this->extensionsRequired == other.extensionsRequired &&
1889 this->extensionsUsed == other.extensionsUsed &&
1890 this->extras == other.extras && this->images == other.images &&
1891 this->lights == other.lights && this->materials == other.materials &&
1892 this->meshes == other.meshes && this->nodes == other.nodes &&
1893 this->samplers == other.samplers && this->scenes == other.scenes &&
1894 this->skins == other.skins && this->textures == other.textures;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001895}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001896bool Node::operator==(const Node &other) const {
1897 return this->camera == other.camera && this->children == other.children &&
1898 this->extensions == other.extensions && this->extras == other.extras &&
1899 Equals(this->matrix, other.matrix) && this->mesh == other.mesh &&
1900 this->name == other.name && Equals(this->rotation, other.rotation) &&
1901 Equals(this->scale, other.scale) && this->skin == other.skin &&
1902 Equals(this->translation, other.translation) &&
1903 Equals(this->weights, other.weights);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001904}
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001905bool SpotLight::operator==(const SpotLight &other) const {
1906 return this->extensions == other.extensions && this->extras == other.extras &&
1907 TINYGLTF_DOUBLE_EQUAL(this->innerConeAngle, other.innerConeAngle) &&
1908 TINYGLTF_DOUBLE_EQUAL(this->outerConeAngle, other.outerConeAngle);
1909}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001910bool OrthographicCamera::operator==(const OrthographicCamera &other) const {
1911 return this->extensions == other.extensions && this->extras == other.extras &&
1912 TINYGLTF_DOUBLE_EQUAL(this->xmag, other.xmag) &&
1913 TINYGLTF_DOUBLE_EQUAL(this->ymag, other.ymag) &&
1914 TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
1915 TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001916}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001917bool Parameter::operator==(const Parameter &other) const {
1918 if (this->bool_value != other.bool_value ||
1919 this->has_number_value != other.has_number_value)
1920 return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001921
Selmar Kok2bda71c2018-10-05 14:36:05 +02001922 if (!TINYGLTF_DOUBLE_EQUAL(this->number_value, other.number_value))
1923 return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001924
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001925 if (this->json_double_value.size() != other.json_double_value.size())
1926 return false;
1927 for (auto &it : this->json_double_value) {
1928 auto otherIt = other.json_double_value.find(it.first);
1929 if (otherIt == other.json_double_value.end()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001930
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001931 if (!TINYGLTF_DOUBLE_EQUAL(it.second, otherIt->second)) return false;
1932 }
1933
1934 if (!Equals(this->number_array, other.number_array)) return false;
1935
1936 if (this->string_value != other.string_value) return false;
1937
1938 return true;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001939}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001940bool PerspectiveCamera::operator==(const PerspectiveCamera &other) const {
1941 return TINYGLTF_DOUBLE_EQUAL(this->aspectRatio, other.aspectRatio) &&
1942 this->extensions == other.extensions && this->extras == other.extras &&
1943 TINYGLTF_DOUBLE_EQUAL(this->yfov, other.yfov) &&
1944 TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
1945 TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001946}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001947bool Primitive::operator==(const Primitive &other) const {
1948 return this->attributes == other.attributes && this->extras == other.extras &&
1949 this->indices == other.indices && this->material == other.material &&
1950 this->mode == other.mode && this->targets == other.targets;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001951}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001952bool Sampler::operator==(const Sampler &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001953 return this->extensions == other.extensions && this->extras == other.extras &&
1954 this->magFilter == other.magFilter &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001955 this->minFilter == other.minFilter && this->name == other.name &&
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001956 this->wrapS == other.wrapS && this->wrapT == other.wrapT;
Syoyo Fujita2c521b32020-12-04 00:50:46 +09001957
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001958 // this->wrapR == other.wrapR
Selmar Kok31cb7f92018-10-03 15:39:05 +02001959}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001960bool Scene::operator==(const Scene &other) const {
1961 return this->extensions == other.extensions && this->extras == other.extras &&
1962 this->name == other.name && this->nodes == other.nodes;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001963}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001964bool Skin::operator==(const Skin &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001965 return this->extensions == other.extensions && this->extras == other.extras &&
1966 this->inverseBindMatrices == other.inverseBindMatrices &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001967 this->joints == other.joints && this->name == other.name &&
1968 this->skeleton == other.skeleton;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001969}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001970bool Texture::operator==(const Texture &other) const {
1971 return this->extensions == other.extensions && this->extras == other.extras &&
1972 this->name == other.name && this->sampler == other.sampler &&
1973 this->source == other.source;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001974}
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001975bool TextureInfo::operator==(const TextureInfo &other) const {
1976 return this->extensions == other.extensions && this->extras == other.extras &&
1977 this->index == other.index && this->texCoord == other.texCoord;
1978}
1979bool NormalTextureInfo::operator==(const NormalTextureInfo &other) const {
1980 return this->extensions == other.extensions && this->extras == other.extras &&
1981 this->index == other.index && this->texCoord == other.texCoord &&
1982 TINYGLTF_DOUBLE_EQUAL(this->scale, other.scale);
1983}
1984bool OcclusionTextureInfo::operator==(const OcclusionTextureInfo &other) const {
1985 return this->extensions == other.extensions && this->extras == other.extras &&
1986 this->index == other.index && this->texCoord == other.texCoord &&
1987 TINYGLTF_DOUBLE_EQUAL(this->strength, other.strength);
1988}
1989bool PbrMetallicRoughness::operator==(const PbrMetallicRoughness &other) const {
1990 return this->extensions == other.extensions && this->extras == other.extras &&
1991 (this->baseColorTexture == other.baseColorTexture) &&
1992 (this->metallicRoughnessTexture == other.metallicRoughnessTexture) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09001993 Equals(this->baseColorFactor, other.baseColorFactor) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001994 TINYGLTF_DOUBLE_EQUAL(this->metallicFactor, other.metallicFactor) &&
1995 TINYGLTF_DOUBLE_EQUAL(this->roughnessFactor, other.roughnessFactor);
1996}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001997bool Value::operator==(const Value &other) const {
1998 return Equals(*this, other);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001999}
2000
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002001static void swap4(unsigned int *val) {
2002#ifdef TINYGLTF_LITTLE_ENDIAN
2003 (void)val;
2004#else
2005 unsigned int tmp = *val;
2006 unsigned char *dst = reinterpret_cast<unsigned char *>(val);
2007 unsigned char *src = reinterpret_cast<unsigned char *>(&tmp);
2008
2009 dst[0] = src[3];
2010 dst[1] = src[2];
2011 dst[2] = src[1];
2012 dst[3] = src[0];
2013#endif
2014}
2015
Syoyo Fujitabeded612016-05-01 20:03:43 +09002016static std::string JoinPath(const std::string &path0,
2017 const std::string &path1) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002018 if (path0.empty()) {
2019 return path1;
2020 } else {
2021 // check '/'
2022 char lastChar = *path0.rbegin();
2023 if (lastChar != '/') {
2024 return path0 + std::string("/") + path1;
2025 } else {
2026 return path0 + path1;
2027 }
2028 }
2029}
2030
Syoyo Fujita643ce102016-05-01 17:19:37 +09002031static std::string FindFile(const std::vector<std::string> &paths,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002032 const std::string &filepath, FsCallbacks *fs) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002033 if (fs == nullptr || fs->ExpandFilePath == nullptr ||
2034 fs->FileExists == nullptr) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002035 // Error, fs callback[s] missing
2036 return std::string();
2037 }
2038
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002039 for (size_t i = 0; i < paths.size(); i++) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002040 std::string absPath =
2041 fs->ExpandFilePath(JoinPath(paths[i], filepath), fs->user_data);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002042 if (fs->FileExists(absPath, fs->user_data)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002043 return absPath;
2044 }
2045 }
2046
2047 return std::string();
2048}
2049
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09002050static std::string GetFilePathExtension(const std::string &FileName) {
johan bowald642a3432018-04-01 12:37:18 +02002051 if (FileName.find_last_of(".") != std::string::npos)
2052 return FileName.substr(FileName.find_last_of(".") + 1);
2053 return "";
2054}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002055
Syoyo Fujita643ce102016-05-01 17:19:37 +09002056static std::string GetBaseDir(const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002057 if (filepath.find_last_of("/\\") != std::string::npos)
2058 return filepath.substr(0, filepath.find_last_of("/\\"));
2059 return "";
2060}
2061
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002062static std::string GetBaseFilename(const std::string &filepath) {
Martin Gerhardyfae65432022-03-13 18:42:39 +01002063 auto idx = filepath.find_last_of("/\\");
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002064 if (idx != std::string::npos) return filepath.substr(idx + 1);
Alexander Wood190382a2021-10-08 12:19:13 -04002065 return filepath;
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002066}
2067
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002068std::string base64_encode(unsigned char const *, unsigned int len);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002069std::string base64_decode(std::string const &s);
2070
2071/*
2072 base64.cpp and base64.h
2073
2074 Copyright (C) 2004-2008 René Nyffenegger
2075
2076 This source code is provided 'as-is', without any express or implied
2077 warranty. In no event will the author be held liable for any damages
2078 arising from the use of this software.
2079
2080 Permission is granted to anyone to use this software for any purpose,
2081 including commercial applications, and to alter it and redistribute it
2082 freely, subject to the following restrictions:
2083
2084 1. The origin of this source code must not be misrepresented; you must not
2085 claim that you wrote the original source code. If you use this source code
2086 in a product, an acknowledgment in the product documentation would be
2087 appreciated but is not required.
2088
2089 2. Altered source versions must be plainly marked as such, and must not be
2090 misrepresented as being the original source code.
2091
2092 3. This notice may not be removed or altered from any source distribution.
2093
2094 René Nyffenegger rene.nyffenegger@adp-gmbh.ch
2095
2096*/
2097
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002098#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09002099#pragma clang diagnostic push
Syoyo Fujita643ce102016-05-01 17:19:37 +09002100#pragma clang diagnostic ignored "-Wsign-conversion"
2101#pragma clang diagnostic ignored "-Wconversion"
Syoyo Fujita6fd91622016-05-01 20:27:30 +09002102#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002103
2104static inline bool is_base64(unsigned char c) {
2105 return (isalnum(c) || (c == '+') || (c == '/'));
2106}
2107
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002108std::string base64_encode(unsigned char const *bytes_to_encode,
2109 unsigned int in_len) {
johan bowald30c53472018-03-30 11:49:36 +02002110 std::string ret;
2111 int i = 0;
2112 int j = 0;
2113 unsigned char char_array_3[3];
2114 unsigned char char_array_4[4];
2115
Syoyo Fujitaff515702019-08-24 16:29:14 +09002116 const char *base64_chars =
2117 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2118 "abcdefghijklmnopqrstuvwxyz"
2119 "0123456789+/";
2120
johan bowald30c53472018-03-30 11:49:36 +02002121 while (in_len--) {
2122 char_array_3[i++] = *(bytes_to_encode++);
2123 if (i == 3) {
2124 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002125 char_array_4[1] =
2126 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
2127 char_array_4[2] =
2128 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
johan bowald30c53472018-03-30 11:49:36 +02002129 char_array_4[3] = char_array_3[2] & 0x3f;
2130
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002131 for (i = 0; (i < 4); i++) ret += base64_chars[char_array_4[i]];
johan bowald30c53472018-03-30 11:49:36 +02002132 i = 0;
2133 }
2134 }
2135
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002136 if (i) {
2137 for (j = i; j < 3; j++) char_array_3[j] = '\0';
johan bowald30c53472018-03-30 11:49:36 +02002138
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002139 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
2140 char_array_4[1] =
2141 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
2142 char_array_4[2] =
2143 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
johan bowald30c53472018-03-30 11:49:36 +02002144
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002145 for (j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]];
johan bowald30c53472018-03-30 11:49:36 +02002146
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002147 while ((i++ < 3)) ret += '=';
johan bowald30c53472018-03-30 11:49:36 +02002148 }
2149
2150 return ret;
johan bowald30c53472018-03-30 11:49:36 +02002151}
2152
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002153std::string base64_decode(std::string const &encoded_string) {
Syoyo Fujita8c6774c2016-05-01 20:36:29 +09002154 int in_len = static_cast<int>(encoded_string.size());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002155 int i = 0;
2156 int j = 0;
2157 int in_ = 0;
2158 unsigned char char_array_4[4], char_array_3[3];
2159 std::string ret;
2160
Syoyo Fujitaff515702019-08-24 16:29:14 +09002161 const std::string base64_chars =
2162 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2163 "abcdefghijklmnopqrstuvwxyz"
2164 "0123456789+/";
2165
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002166 while (in_len-- && (encoded_string[in_] != '=') &&
2167 is_base64(encoded_string[in_])) {
2168 char_array_4[i++] = encoded_string[in_];
2169 in_++;
2170 if (i == 4) {
2171 for (i = 0; i < 4; i++)
Syoyo Fujitabeded612016-05-01 20:03:43 +09002172 char_array_4[i] =
2173 static_cast<unsigned char>(base64_chars.find(char_array_4[i]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002174
2175 char_array_3[0] =
2176 (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
2177 char_array_3[1] =
2178 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
2179 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
2180
Syoyo Fujita7c877972016-03-08 01:31:49 +09002181 for (i = 0; (i < 3); i++) ret += char_array_3[i];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002182 i = 0;
2183 }
2184 }
2185
2186 if (i) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09002187 for (j = i; j < 4; j++) char_array_4[j] = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002188
2189 for (j = 0; j < 4; j++)
Syoyo Fujitabeded612016-05-01 20:03:43 +09002190 char_array_4[j] =
2191 static_cast<unsigned char>(base64_chars.find(char_array_4[j]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002192
2193 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
2194 char_array_3[1] =
2195 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
2196 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
2197
Syoyo Fujita7c877972016-03-08 01:31:49 +09002198 for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002199 }
2200
2201 return ret;
2202}
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002203#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09002204#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +09002205#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002206
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002207// https://github.com/syoyo/tinygltf/issues/228
2208// TODO(syoyo): Use uriparser https://uriparser.github.io/ for stricter Uri
2209// decoding?
2210//
Alexander Woode4bc6c72021-10-14 08:54:59 -04002211// Uri Decoding from DLIB
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002212// http://dlib.net/dlib/server/server_http.cpp.html
Alexander Woode4bc6c72021-10-14 08:54:59 -04002213// --- dlib begin ------------------------------------------------------------
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002214// Copyright (C) 2003 Davis E. King (davis@dlib.net)
Alexander Woode4bc6c72021-10-14 08:54:59 -04002215// License: Boost Software License
2216// Boost Software License - Version 1.0 - August 17th, 2003
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002217
Alexander Woode4bc6c72021-10-14 08:54:59 -04002218// Permission is hereby granted, free of charge, to any person or organization
2219// obtaining a copy of the software and accompanying documentation covered by
2220// this license (the "Software") to use, reproduce, display, distribute,
2221// execute, and transmit the Software, and to prepare derivative works of the
2222// Software, and to permit third-parties to whom the Software is furnished to
2223// do so, all subject to the following:
2224// The copyright notices in the Software and this entire statement, including
2225// the above license grant, this restriction and the following disclaimer,
2226// must be included in all copies of the Software, in whole or in part, and
2227// all derivative works of the Software, unless such copies or derivative
2228// works are solely in the form of machine-executable object code generated by
2229// a source language processor.
2230// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2231// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2232// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
2233// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
2234// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
2235// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2236// DEALINGS IN THE SOFTWARE.
Syoyo Fujitacbd38852022-02-26 21:19:15 +09002237//
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002238namespace dlib {
2239
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002240inline unsigned char from_hex(unsigned char ch) {
2241 if (ch <= '9' && ch >= '0')
2242 ch -= '0';
2243 else if (ch <= 'f' && ch >= 'a')
2244 ch -= 'a' - 10;
2245 else if (ch <= 'F' && ch >= 'A')
2246 ch -= 'A' - 10;
2247 else
2248 ch = 0;
2249 return ch;
2250}
2251
2252static const std::string urldecode(const std::string &str) {
2253 using namespace std;
2254 string result;
2255 string::size_type i;
2256 for (i = 0; i < str.size(); ++i) {
2257 if (str[i] == '+') {
2258 result += ' ';
2259 } else if (str[i] == '%' && str.size() > i + 2) {
2260 const unsigned char ch1 =
2261 from_hex(static_cast<unsigned char>(str[i + 1]));
2262 const unsigned char ch2 =
2263 from_hex(static_cast<unsigned char>(str[i + 2]));
2264 const unsigned char ch = static_cast<unsigned char>((ch1 << 4) | ch2);
2265 result += static_cast<char>(ch);
2266 i += 2;
2267 } else {
2268 result += str[i];
2269 }
2270 }
2271 return result;
2272}
2273
2274} // namespace dlib
2275// --- dlib end --------------------------------------------------------------
2276
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002277static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002278 std::string *warn, const std::string &filename,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002279 const std::string &basedir, bool required,
2280 size_t reqBytes, bool checkSize, FsCallbacks *fs) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002281 if (fs == nullptr || fs->FileExists == nullptr ||
2282 fs->ExpandFilePath == nullptr || fs->ReadWholeFile == nullptr) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002283 // This is a developer error, assert() ?
2284 if (err) {
2285 (*err) += "FS callback[s] not set\n";
2286 }
2287 return false;
2288 }
2289
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002290 std::string *failMsgOut = required ? err : warn;
Selmar Koke3b3fa92018-08-22 19:04:21 +02002291
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002292 out->clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002293
2294 std::vector<std::string> paths;
2295 paths.push_back(basedir);
2296 paths.push_back(".");
2297
Paolo Jovone6601bf2018-07-07 20:43:33 +02002298 std::string filepath = FindFile(paths, filename, fs);
jianghaosenb721fb72017-08-25 21:01:17 +08002299 if (filepath.empty() || filename.empty()) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002300 if (failMsgOut) {
2301 (*failMsgOut) += "File not found : " + filename + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002302 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002303 return false;
2304 }
2305
Paolo Jovone6601bf2018-07-07 20:43:33 +02002306 std::vector<unsigned char> buf;
2307 std::string fileReadErr;
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002308 bool fileRead =
2309 fs->ReadWholeFile(&buf, &fileReadErr, filepath, fs->user_data);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002310 if (!fileRead) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002311 if (failMsgOut) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002312 (*failMsgOut) +=
2313 "File read error : " + filepath + " : " + fileReadErr + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002314 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002315 return false;
2316 }
2317
Paolo Jovone6601bf2018-07-07 20:43:33 +02002318 size_t sz = buf.size();
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09002319 if (sz == 0) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002320 if (failMsgOut) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002321 (*failMsgOut) += "File is empty : " + filepath + "\n";
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002322 }
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09002323 return false;
2324 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002325
2326 if (checkSize) {
2327 if (reqBytes == sz) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002328 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002329 return true;
2330 } else {
2331 std::stringstream ss;
2332 ss << "File size mismatch : " << filepath << ", requestedBytes "
2333 << reqBytes << ", but got " << sz << std::endl;
Selmar Koke3b3fa92018-08-22 19:04:21 +02002334 if (failMsgOut) {
2335 (*failMsgOut) += ss.str();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002336 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002337 return false;
2338 }
2339 }
2340
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002341 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002342 return true;
2343}
2344
Squareysff644d82018-03-13 22:36:18 +01002345void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002346 LoadImageData = func;
2347 load_image_user_data_ = user_data;
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002348 user_image_loader_ = true;
2349}
2350
2351void TinyGLTF::RemoveImageLoader() {
2352 LoadImageData =
2353#ifndef TINYGLTF_NO_STB_IMAGE
2354 &tinygltf::LoadImageData;
2355#else
2356 nullptr;
2357#endif
2358
2359 load_image_user_data_ = nullptr;
2360 user_image_loader_ = false;
Squareysff644d82018-03-13 22:36:18 +01002361}
2362
Squareys2d3594d2018-03-13 22:40:53 +01002363#ifndef TINYGLTF_NO_STB_IMAGE
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002364bool LoadImageData(Image *image, const int image_idx, std::string *err,
2365 std::string *warn, int req_width, int req_height,
2366 const unsigned char *bytes, int size, void *user_data) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002367 (void)warn;
2368
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002369 LoadImageDataOption option;
2370 if (user_data) {
2371 option = *reinterpret_cast<LoadImageDataOption *>(user_data);
2372 }
2373
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002374 int w = 0, h = 0, comp = 0, req_comp = 0;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002375
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002376 unsigned char *data = nullptr;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002377
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002378 // preserve_channels true: Use channels stored in the image file.
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09002379 // false: force 32-bit textures for common Vulkan compatibility. It appears
2380 // that some GPU drivers do not support 24-bit images for Vulkan
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002381 req_comp = option.preserve_channels ? 0 : 4;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002382 int bits = 8;
2383 int pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002384
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002385 // It is possible that the image we want to load is a 16bit per channel image
2386 // We are going to attempt to load it as 16bit per channel, and if it worked,
2387 // set the image data accodingly. We are casting the returned pointer into
2388 // unsigned char, because we are representing "bytes". But we are updating
2389 // the Image metadata to signal that this image uses 2 bytes (16bits) per
2390 // channel:
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002391 if (stbi_is_16_bit_from_memory(bytes, size)) {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002392 data = reinterpret_cast<unsigned char *>(
2393 stbi_load_16_from_memory(bytes, size, &w, &h, &comp, req_comp));
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002394 if (data) {
2395 bits = 16;
2396 pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT;
2397 }
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002398 }
2399
2400 // at this point, if data is still NULL, it means that the image wasn't
2401 // 16bit per channel, we are going to load it as a normal 8bit per channel
2402 // mage as we used to do:
Aurélien Chatelain5cb43462017-05-24 09:55:14 +00002403 // if image cannot be decoded, ignore parsing and keep it by its path
2404 // don't break in this case
Syoyo Fujita5b407452017-06-04 17:42:41 +09002405 // FIXME we should only enter this function if the image is embedded. If
2406 // image->uri references
2407 // an image file, it should be left as it is. Image loading should not be
2408 // mandatory (to support other formats)
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002409 if (!data) data = stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002410 if (!data) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002411 // NOTE: you can use `warn` instead of `err`
Syoyo Fujitabeded612016-05-01 20:03:43 +09002412 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002413 (*err) +=
2414 "Unknown image format. STB cannot decode image data for image[" +
2415 std::to_string(image_idx) + "] name = \"" + image->name + "\".\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002416 }
Omar C. Fd492efc2018-02-10 09:50:35 +02002417 return false;
Syoyo Fujitabeded612016-05-01 20:03:43 +09002418 }
2419
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002420 if ((w < 1) || (h < 1)) {
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002421 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002422 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002423 (*err) += "Invalid image data for image[" + std::to_string(image_idx) +
2424 "] name = \"" + image->name + "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002425 }
Omar C. Fd492efc2018-02-10 09:50:35 +02002426 return false;
Syoyo Fujitabeded612016-05-01 20:03:43 +09002427 }
2428
2429 if (req_width > 0) {
2430 if (req_width != w) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002431 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002432 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002433 (*err) += "Image width mismatch for image[" +
2434 std::to_string(image_idx) + "] name = \"" + image->name +
2435 "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002436 }
2437 return false;
2438 }
2439 }
2440
2441 if (req_height > 0) {
2442 if (req_height != h) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002443 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002444 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002445 (*err) += "Image height mismatch. for image[" +
2446 std::to_string(image_idx) + "] name = \"" + image->name +
2447 "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002448 }
2449 return false;
2450 }
2451 }
2452
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002453 if (req_comp != 0) {
2454 // loaded data has `req_comp` channels(components)
2455 comp = req_comp;
2456 }
2457
Syoyo Fujitabeded612016-05-01 20:03:43 +09002458 image->width = w;
2459 image->height = h;
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002460 image->component = comp;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002461 image->bits = bits;
2462 image->pixel_type = pixel_type;
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002463 image->image.resize(static_cast<size_t>(w * h * comp) * size_t(bits / 8));
2464 std::copy(data, data + w * h * comp * (bits / 8), image->image.begin());
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002465 stbi_image_free(data);
Syoyo Fujita4be2f882016-11-24 16:20:34 +09002466
Syoyo Fujitabeded612016-05-01 20:03:43 +09002467 return true;
2468}
Squareys2d3594d2018-03-13 22:40:53 +01002469#endif
Syoyo Fujitabeded612016-05-01 20:03:43 +09002470
johan bowald642a3432018-04-01 12:37:18 +02002471void TinyGLTF::SetImageWriter(WriteImageDataFunction func, void *user_data) {
2472 WriteImageData = func;
2473 write_image_user_data_ = user_data;
2474}
2475
2476#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
2477static void WriteToMemory_stbi(void *context, void *data, int size) {
2478 std::vector<unsigned char> *buffer =
2479 reinterpret_cast<std::vector<unsigned char> *>(context);
2480
2481 unsigned char *pData = reinterpret_cast<unsigned char *>(data);
2482
2483 buffer->insert(buffer->end(), pData, pData + size);
2484}
2485
2486bool WriteImageData(const std::string *basepath, const std::string *filename,
Paolo Jovone6601bf2018-07-07 20:43:33 +02002487 Image *image, bool embedImages, void *fsPtr) {
johan bowald642a3432018-04-01 12:37:18 +02002488 const std::string ext = GetFilePathExtension(*filename);
2489
Paolo Jovone6601bf2018-07-07 20:43:33 +02002490 // Write image to temporary buffer
2491 std::string header;
2492 std::vector<unsigned char> data;
johan bowald642a3432018-04-01 12:37:18 +02002493
Paolo Jovone6601bf2018-07-07 20:43:33 +02002494 if (ext == "png") {
Syoyo Fujitaca56f722019-03-07 21:04:25 +09002495 if ((image->bits != 8) ||
2496 (image->pixel_type != TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)) {
Syoyo Fujitae8a46c42019-03-07 20:50:58 +09002497 // Unsupported pixel format
2498 return false;
2499 }
2500
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002501 if (!stbi_write_png_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002502 image->height, image->component,
2503 &image->image[0], 0)) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002504 return false;
2505 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002506 header = "data:image/png;base64,";
2507 } else if (ext == "jpg") {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002508 if (!stbi_write_jpg_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002509 image->height, image->component,
2510 &image->image[0], 100)) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002511 return false;
2512 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002513 header = "data:image/jpeg;base64,";
2514 } else if (ext == "bmp") {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002515 if (!stbi_write_bmp_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002516 image->height, image->component,
2517 &image->image[0])) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002518 return false;
2519 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002520 header = "data:image/bmp;base64,";
2521 } else if (!embedImages) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002522 // Error: can't output requested format to file
2523 return false;
Paolo Jovone6601bf2018-07-07 20:43:33 +02002524 }
johan bowald642a3432018-04-01 12:37:18 +02002525
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002526 if (embedImages) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002527 // Embed base64-encoded image into URI
johan bowald642a3432018-04-01 12:37:18 +02002528 if (data.size()) {
2529 image->uri =
2530 header +
2531 base64_encode(&data[0], static_cast<unsigned int>(data.size()));
2532 } else {
2533 // Throw error?
2534 }
2535 } else {
2536 // Write image to disc
Paolo Jovone6601bf2018-07-07 20:43:33 +02002537 FsCallbacks *fs = reinterpret_cast<FsCallbacks *>(fsPtr);
Syoyo Fujita33514af2018-11-05 13:32:43 +09002538 if ((fs != nullptr) && (fs->WriteWholeFile != nullptr)) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002539 const std::string imagefilepath = JoinPath(*basepath, *filename);
2540 std::string writeError;
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002541 if (!fs->WriteWholeFile(&writeError, imagefilepath, data,
2542 fs->user_data)) {
2543 // Could not write image file to disc; Throw error ?
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002544 return false;
Paolo Jovone6601bf2018-07-07 20:43:33 +02002545 }
johan bowald642a3432018-04-01 12:37:18 +02002546 } else {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002547 // Throw error?
johan bowald642a3432018-04-01 12:37:18 +02002548 }
2549 image->uri = *filename;
2550 }
2551
2552 return true;
2553}
2554#endif
2555
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002556void TinyGLTF::SetFsCallbacks(FsCallbacks callbacks) { fs = callbacks; }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002557
Harokyangfb256602019-10-30 16:13:52 +08002558#ifdef _WIN32
2559static inline std::wstring UTF8ToWchar(const std::string &str) {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002560 int wstr_size =
2561 MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
Harokyangfb256602019-10-30 16:13:52 +08002562 std::wstring wstr(wstr_size, 0);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002563 MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0],
2564 (int)wstr.size());
Harokyangfb256602019-10-30 16:13:52 +08002565 return wstr;
2566}
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002567
2568static inline std::string WcharToUTF8(const std::wstring &wstr) {
2569 int str_size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(),
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04002570 nullptr, 0, NULL, NULL);
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002571 std::string str(str_size, 0);
2572 WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), &str[0],
2573 (int)str.size(), NULL, NULL);
2574 return str;
2575}
Harokyangfb256602019-10-30 16:13:52 +08002576#endif
2577
Paolo Jovone6601bf2018-07-07 20:43:33 +02002578#ifndef TINYGLTF_NO_FS
2579// Default implementations of filesystem functions
2580
2581bool FileExists(const std::string &abs_filename, void *) {
2582 bool ret;
Sascha Willems5f9cb242018-12-28 20:53:41 +01002583#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002584 if (asset_manager) {
2585 AAsset *asset = AAssetManager_open(asset_manager, abs_filename.c_str(),
2586 AASSET_MODE_STREAMING);
2587 if (!asset) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01002588 return false;
2589 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002590 AAsset_close(asset);
2591 ret = true;
2592 } else {
2593 return false;
2594 }
Sascha Willems5f9cb242018-12-28 20:53:41 +01002595#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002596#ifdef _WIN32
Syoyo Fujita45cac782019-11-09 20:42:55 +09002597#if defined(_MSC_VER) || defined(__GLIBCXX__)
2598 FILE *fp = nullptr;
Harokyangfb256602019-10-30 16:13:52 +08002599 errno_t err = _wfopen_s(&fp, UTF8ToWchar(abs_filename).c_str(), L"rb");
Paolo Jovone6601bf2018-07-07 20:43:33 +02002600 if (err != 0) {
2601 return false;
2602 }
2603#else
Syoyo Fujita45cac782019-11-09 20:42:55 +09002604 FILE *fp = nullptr;
2605 errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb");
2606 if (err != 0) {
2607 return false;
2608 }
2609#endif
2610
2611#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002612 FILE *fp = fopen(abs_filename.c_str(), "rb");
2613#endif
2614 if (fp) {
2615 ret = true;
2616 fclose(fp);
2617 } else {
2618 ret = false;
2619 }
Sascha Willems5f9cb242018-12-28 20:53:41 +01002620#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002621
2622 return ret;
2623}
2624
2625std::string ExpandFilePath(const std::string &filepath, void *) {
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002626 // https://github.com/syoyo/tinygltf/issues/368
2627 //
2628 // No file path expansion in built-in FS function anymore, since glTF URI
2629 // should not contain tilde('~') and environment variables, and for security
2630 // reason(`wordexp`).
2631 //
2632 // Users need to supply `base_dir`(in `LoadASCIIFromString`,
2633 // `LoadBinaryFromMemory`) in expanded absolute path.
2634
2635 return filepath;
2636
2637#if 0
Paolo Jovone6601bf2018-07-07 20:43:33 +02002638#ifdef _WIN32
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002639 // Assume input `filepath` is encoded in UTF-8
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002640 std::wstring wfilepath = UTF8ToWchar(filepath);
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002641 DWORD wlen = ExpandEnvironmentStringsW(wfilepath.c_str(), nullptr, 0);
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002642 wchar_t *wstr = new wchar_t[wlen];
2643 ExpandEnvironmentStringsW(wfilepath.c_str(), wstr, wlen);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002644
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002645 std::wstring ws(wstr);
2646 delete[] wstr;
2647 return WcharToUTF8(ws);
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002648
Paolo Jovone6601bf2018-07-07 20:43:33 +02002649#else
2650
2651#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \
Julian Smith0598a202021-08-25 12:06:08 +01002652 defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__OpenBSD__)
Paolo Jovone6601bf2018-07-07 20:43:33 +02002653 // no expansion
2654 std::string s = filepath;
2655#else
2656 std::string s;
2657 wordexp_t p;
2658
2659 if (filepath.empty()) {
2660 return "";
2661 }
2662
Frank Galliganaa3c5a12020-01-13 15:06:56 -08002663 // Quote the string to keep any spaces in filepath intact.
2664 std::string quoted_path = "\"" + filepath + "\"";
Paolo Jovone6601bf2018-07-07 20:43:33 +02002665 // char** w;
Frank Galliganaa3c5a12020-01-13 15:06:56 -08002666 int ret = wordexp(quoted_path.c_str(), &p, 0);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002667 if (ret) {
2668 // err
2669 s = filepath;
2670 return s;
2671 }
2672
2673 // Use first element only.
2674 if (p.we_wordv) {
2675 s = std::string(p.we_wordv[0]);
2676 wordfree(&p);
2677 } else {
2678 s = filepath;
2679 }
2680
2681#endif
2682
2683 return s;
2684#endif
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002685#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002686}
2687
2688bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
2689 const std::string &filepath, void *) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01002690#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
2691 if (asset_manager) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002692 AAsset *asset = AAssetManager_open(asset_manager, filepath.c_str(),
2693 AASSET_MODE_STREAMING);
Sascha Willems5f9cb242018-12-28 20:53:41 +01002694 if (!asset) {
2695 if (err) {
2696 (*err) += "File open error : " + filepath + "\n";
2697 }
2698 return false;
2699 }
2700 size_t size = AAsset_getLength(asset);
Syoyo Fujita73c4cce2020-04-28 01:06:34 +09002701 if (size == 0) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01002702 if (err) {
2703 (*err) += "Invalid file size : " + filepath +
2704 " (does the path point to a directory?)";
2705 }
Syoyo Fujita73c4cce2020-04-28 01:06:34 +09002706 return false;
Sascha Willems5f9cb242018-12-28 20:53:41 +01002707 }
2708 out->resize(size);
2709 AAsset_read(asset, reinterpret_cast<char *>(&out->at(0)), size);
2710 AAsset_close(asset);
2711 return true;
2712 } else {
2713 if (err) {
2714 (*err) += "No asset manager specified : " + filepath + "\n";
2715 }
2716 return false;
2717 }
2718#else
Harokyang5cecef22019-10-30 15:16:46 +08002719#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002720#if defined(__GLIBCXX__) // mingw
2721 int file_descriptor =
2722 _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY);
Syoyo Fujita45cac782019-11-09 20:42:55 +09002723 __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
2724 std::istream f(&wfile_buf);
Syoyo Fujitaaba57bb2020-05-18 20:50:45 +09002725#elif defined(_MSC_VER) || defined(_LIBCPP_VERSION)
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04002726 // For libcxx, assume _LIBCPP_HAS_OPEN_WITH_WCHAR is defined to accept
2727 // `wchar_t *`
Harokyangfb256602019-10-30 16:13:52 +08002728 std::ifstream f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary);
Syoyo Fujitaaba57bb2020-05-18 20:50:45 +09002729#else
2730 // Unknown compiler/runtime
Syoyo Fujita45cac782019-11-09 20:42:55 +09002731 std::ifstream f(filepath.c_str(), std::ifstream::binary);
2732#endif
Harokyang5cecef22019-10-30 15:16:46 +08002733#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002734 std::ifstream f(filepath.c_str(), std::ifstream::binary);
Harokyang5cecef22019-10-30 15:16:46 +08002735#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002736 if (!f) {
2737 if (err) {
2738 (*err) += "File open error : " + filepath + "\n";
2739 }
2740 return false;
2741 }
2742
2743 f.seekg(0, f.end);
2744 size_t sz = static_cast<size_t>(f.tellg());
2745 f.seekg(0, f.beg);
2746
Syoyo Fujitae8862472019-10-20 17:47:50 +09002747 if (int64_t(sz) < 0) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002748 if (err) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002749 (*err) += "Invalid file size : " + filepath +
2750 " (does the path point to a directory?)";
Paolo Jovone6601bf2018-07-07 20:43:33 +02002751 }
2752 return false;
2753 } else if (sz == 0) {
2754 if (err) {
2755 (*err) += "File is empty : " + filepath + "\n";
2756 }
2757 return false;
2758 }
2759
2760 out->resize(sz);
2761 f.read(reinterpret_cast<char *>(&out->at(0)),
2762 static_cast<std::streamsize>(sz));
Paolo Jovone6601bf2018-07-07 20:43:33 +02002763
2764 return true;
Sascha Willems5f9cb242018-12-28 20:53:41 +01002765#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002766}
2767
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002768bool WriteWholeFile(std::string *err, const std::string &filepath,
2769 const std::vector<unsigned char> &contents, void *) {
Harokyangfb256602019-10-30 16:13:52 +08002770#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002771#if defined(__GLIBCXX__) // mingw
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09002772 int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(),
2773 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
2774 __gnu_cxx::stdio_filebuf<char> wfile_buf(
2775 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09002776 std::ostream f(&wfile_buf);
2777#elif defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08002778 std::ofstream f(UTF8ToWchar(filepath).c_str(), std::ofstream::binary);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002779#else // clang?
Syoyo Fujita45cac782019-11-09 20:42:55 +09002780 std::ofstream f(filepath.c_str(), std::ofstream::binary);
2781#endif
Harokyangfb256602019-10-30 16:13:52 +08002782#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002783 std::ofstream f(filepath.c_str(), std::ofstream::binary);
Harokyangfb256602019-10-30 16:13:52 +08002784#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002785 if (!f) {
2786 if (err) {
2787 (*err) += "File open error for writing : " + filepath + "\n";
2788 }
2789 return false;
2790 }
2791
2792 f.write(reinterpret_cast<const char *>(&contents.at(0)),
2793 static_cast<std::streamsize>(contents.size()));
2794 if (!f) {
2795 if (err) {
2796 (*err) += "File write error: " + filepath + "\n";
2797 }
2798 return false;
2799 }
2800
Paolo Jovone6601bf2018-07-07 20:43:33 +02002801 return true;
2802}
2803
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002804#endif // TINYGLTF_NO_FS
Paolo Jovone6601bf2018-07-07 20:43:33 +02002805
johan bowald642a3432018-04-01 12:37:18 +02002806static std::string MimeToExt(const std::string &mimeType) {
2807 if (mimeType == "image/jpeg") {
2808 return "jpg";
2809 } else if (mimeType == "image/png") {
2810 return "png";
2811 } else if (mimeType == "image/bmp") {
2812 return "bmp";
2813 } else if (mimeType == "image/gif") {
2814 return "gif";
2815 }
2816
2817 return "";
2818}
2819
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09002820static void UpdateImageObject(Image &image, std::string &baseDir, int index,
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002821 bool embedImages,
2822 WriteImageDataFunction *WriteImageData = nullptr,
2823 void *user_data = nullptr) {
johan bowald642a3432018-04-01 12:37:18 +02002824 std::string filename;
2825 std::string ext;
FsiGuy00015623db855c62020-03-09 16:57:21 -05002826 // If image has uri, use it it as a filename
johan bowald642a3432018-04-01 12:37:18 +02002827 if (image.uri.size()) {
2828 filename = GetBaseFilename(image.uri);
2829 ext = GetFilePathExtension(filename);
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09002830 } else if (image.bufferView != -1) {
2831 // If there's no URI and the data exists in a buffer,
2832 // don't change properties or write images
johan bowald642a3432018-04-01 12:37:18 +02002833 } else if (image.name.size()) {
2834 ext = MimeToExt(image.mimeType);
2835 // Otherwise use name as filename
2836 filename = image.name + "." + ext;
2837 } else {
2838 ext = MimeToExt(image.mimeType);
2839 // Fallback to index of image as filename
2840 filename = std::to_string(index) + "." + ext;
2841 }
2842
2843 // If callback is set, modify image data object
FsiGuy00015623db855c62020-03-09 16:57:21 -05002844 if (*WriteImageData != nullptr && !filename.empty()) {
johan bowald642a3432018-04-01 12:37:18 +02002845 std::string uri;
2846 (*WriteImageData)(&baseDir, &filename, &image, embedImages, user_data);
2847 }
2848}
2849
Selmar Kok0d0e97e2018-08-22 14:01:57 +02002850bool IsDataURI(const std::string &in) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002851 std::string header = "data:application/octet-stream;base64,";
2852 if (in.find(header) == 0) {
2853 return true;
2854 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002855
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002856 header = "data:image/jpeg;base64,";
2857 if (in.find(header) == 0) {
2858 return true;
2859 }
Squareys43374632018-03-13 22:20:48 +01002860
Syoyo Fujita620eed12016-01-02 23:37:12 +09002861 header = "data:image/png;base64,";
2862 if (in.find(header) == 0) {
2863 return true;
2864 }
Squareys43374632018-03-13 22:20:48 +01002865
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002866 header = "data:image/bmp;base64,";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002867 if (in.find(header) == 0) {
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002868 return true;
2869 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002870
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002871 header = "data:image/gif;base64,";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002872 if (in.find(header) == 0) {
Syoyo Fujita620eed12016-01-02 23:37:12 +09002873 return true;
2874 }
2875
Luke San Antoniocaa24b02016-06-15 02:23:09 -04002876 header = "data:text/plain;base64,";
2877 if (in.find(header) == 0) {
2878 return true;
2879 }
2880
Syoyo Fujita20244e12018-03-15 11:01:05 -05002881 header = "data:application/gltf-buffer;base64,";
2882 if (in.find(header) == 0) {
2883 return true;
2884 }
2885
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002886 return false;
2887}
2888
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002889bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
2890 const std::string &in, size_t reqBytes, bool checkSize) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002891 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujita620eed12016-01-02 23:37:12 +09002892 std::string data;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002893 if (in.find(header) == 0) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09002894 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002895 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002896
2897 if (data.empty()) {
2898 header = "data:image/jpeg;base64,";
2899 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002900 mime_type = "image/jpeg";
Syoyo Fujita7c877972016-03-08 01:31:49 +09002901 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09002902 }
2903 }
2904
2905 if (data.empty()) {
2906 header = "data:image/png;base64,";
2907 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002908 mime_type = "image/png";
Syoyo Fujita7c877972016-03-08 01:31:49 +09002909 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09002910 }
2911 }
Squareys43374632018-03-13 22:20:48 +01002912
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002913 if (data.empty()) {
2914 header = "data:image/bmp;base64,";
2915 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002916 mime_type = "image/bmp";
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002917 data = base64_decode(in.substr(header.size())); // cut mime string.
2918 }
2919 }
2920
2921 if (data.empty()) {
2922 header = "data:image/gif;base64,";
2923 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002924 mime_type = "image/gif";
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002925 data = base64_decode(in.substr(header.size())); // cut mime string.
2926 }
2927 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002928
2929 if (data.empty()) {
Luke San Antoniocaa24b02016-06-15 02:23:09 -04002930 header = "data:text/plain;base64,";
2931 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002932 mime_type = "text/plain";
Luke San Antoniocaa24b02016-06-15 02:23:09 -04002933 data = base64_decode(in.substr(header.size()));
2934 }
2935 }
2936
2937 if (data.empty()) {
Syoyo Fujita20244e12018-03-15 11:01:05 -05002938 header = "data:application/gltf-buffer;base64,";
2939 if (in.find(header) == 0) {
2940 data = base64_decode(in.substr(header.size()));
2941 }
2942 }
2943
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09002944 // TODO(syoyo): Allow empty buffer? #229
Syoyo Fujita20244e12018-03-15 11:01:05 -05002945 if (data.empty()) {
Syoyo Fujita620eed12016-01-02 23:37:12 +09002946 return false;
Syoyo Fujitabde70212016-02-07 17:38:17 +09002947 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002948
2949 if (checkSize) {
2950 if (data.size() != reqBytes) {
2951 return false;
2952 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002953 out->resize(reqBytes);
Syoyo Fujita620eed12016-01-02 23:37:12 +09002954 } else {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002955 out->resize(data.size());
Syoyo Fujita620eed12016-01-02 23:37:12 +09002956 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002957 std::copy(data.begin(), data.end(), out->begin());
Syoyo Fujita620eed12016-01-02 23:37:12 +09002958 return true;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002959}
2960
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002961namespace {
2962bool GetInt(const json &o, int &val) {
jrkoonce5cecc412019-08-29 11:45:04 -05002963#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002964 if (!o.IsDouble()) {
2965 if (o.IsInt()) {
2966 val = o.GetInt();
2967 return true;
2968 } else if (o.IsUint()) {
2969 val = static_cast<int>(o.GetUint());
2970 return true;
2971 } else if (o.IsInt64()) {
2972 val = static_cast<int>(o.GetInt64());
2973 return true;
2974 } else if (o.IsUint64()) {
2975 val = static_cast<int>(o.GetUint64());
jrkooncecba5d6c2019-08-29 11:26:22 -05002976 return true;
2977 }
jrkoonce5cecc412019-08-29 11:45:04 -05002978 }
2979
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002980 return false;
jrkoonce5cecc412019-08-29 11:45:04 -05002981#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002982 auto type = o.type();
jrkooncecba5d6c2019-08-29 11:26:22 -05002983
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002984 if ((type == json::value_t::number_integer) ||
2985 (type == json::value_t::number_unsigned)) {
2986 val = static_cast<int>(o.get<int64_t>());
2987 return true;
jrkoonce5cecc412019-08-29 11:45:04 -05002988 }
2989
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002990 return false;
jrkoonce5cecc412019-08-29 11:45:04 -05002991#endif
jrkooncecba5d6c2019-08-29 11:26:22 -05002992}
2993
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002994#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09002995bool GetDouble(const json &o, double &val) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002996 if (o.IsDouble()) {
2997 val = o.GetDouble();
2998 return true;
2999 }
3000
3001 return false;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003002}
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003003#endif
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003004
3005bool GetNumber(const json &o, double &val) {
3006#ifdef TINYGLTF_USE_RAPIDJSON
3007 if (o.IsNumber()) {
3008 val = o.GetDouble();
3009 return true;
3010 }
3011
3012 return false;
3013#else
3014 if (o.is_number()) {
3015 val = o.get<double>();
3016 return true;
3017 }
3018
3019 return false;
3020#endif
3021}
3022
3023bool GetString(const json &o, std::string &val) {
3024#ifdef TINYGLTF_USE_RAPIDJSON
3025 if (o.IsString()) {
3026 val = o.GetString();
3027 return true;
3028 }
3029
3030 return false;
3031#else
3032 if (o.type() == json::value_t::string) {
3033 val = o.get<std::string>();
3034 return true;
3035 }
3036
3037 return false;
3038#endif
3039}
3040
3041bool IsArray(const json &o) {
3042#ifdef TINYGLTF_USE_RAPIDJSON
3043 return o.IsArray();
3044#else
3045 return o.is_array();
3046#endif
3047}
3048
3049json_const_array_iterator ArrayBegin(const json &o) {
3050#ifdef TINYGLTF_USE_RAPIDJSON
3051 return o.Begin();
3052#else
3053 return o.begin();
3054#endif
3055}
3056
3057json_const_array_iterator ArrayEnd(const json &o) {
3058#ifdef TINYGLTF_USE_RAPIDJSON
3059 return o.End();
3060#else
3061 return o.end();
3062#endif
3063}
3064
3065bool IsObject(const json &o) {
3066#ifdef TINYGLTF_USE_RAPIDJSON
3067 return o.IsObject();
3068#else
3069 return o.is_object();
3070#endif
3071}
3072
3073json_const_iterator ObjectBegin(const json &o) {
3074#ifdef TINYGLTF_USE_RAPIDJSON
3075 return o.MemberBegin();
3076#else
3077 return o.begin();
3078#endif
3079}
3080
3081json_const_iterator ObjectEnd(const json &o) {
3082#ifdef TINYGLTF_USE_RAPIDJSON
3083 return o.MemberEnd();
3084#else
3085 return o.end();
3086#endif
3087}
3088
Rahul Sheth01d54382020-07-10 14:27:37 -04003089// Making this a const char* results in a pointer to a temporary when
3090// TINYGLTF_USE_RAPIDJSON is off.
3091std::string GetKey(json_const_iterator &it) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003092#ifdef TINYGLTF_USE_RAPIDJSON
3093 return it->name.GetString();
3094#else
3095 return it.key().c_str();
3096#endif
3097}
3098
3099bool FindMember(const json &o, const char *member, json_const_iterator &it) {
3100#ifdef TINYGLTF_USE_RAPIDJSON
3101 if (!o.IsObject()) {
3102 return false;
3103 }
3104 it = o.FindMember(member);
3105 return it != o.MemberEnd();
3106#else
3107 it = o.find(member);
3108 return it != o.end();
3109#endif
3110}
3111
3112const json &GetValue(json_const_iterator &it) {
3113#ifdef TINYGLTF_USE_RAPIDJSON
3114 return it->value;
3115#else
3116 return it.value();
3117#endif
3118}
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003119
3120std::string JsonToString(const json &o, int spacing = -1) {
3121#ifdef TINYGLTF_USE_RAPIDJSON
3122 using namespace rapidjson;
3123 StringBuffer buffer;
3124 if (spacing == -1) {
3125 Writer<StringBuffer> writer(buffer);
Syoyo Fujita544969b2022-07-13 19:03:33 +09003126 // TODO: Better error handling.
3127 // https://github.com/syoyo/tinygltf/issues/332
3128 if (!o.Accept(writer)) {
3129 return "tiny_gltf::JsonToString() failed rapidjson conversion";
3130 }
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003131 } else {
3132 PrettyWriter<StringBuffer> writer(buffer);
3133 writer.SetIndent(' ', uint32_t(spacing));
Syoyo Fujita544969b2022-07-13 19:03:33 +09003134 if (!o.Accept(writer)) {
3135 return "tiny_gltf::JsonToString() failed rapidjson conversion";
3136 }
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003137 }
3138 return buffer.GetString();
3139#else
3140 return o.dump(spacing);
3141#endif
3142}
3143
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003144} // namespace
3145
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003146static bool ParseJsonAsValue(Value *ret, const json &o) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003147 Value val{};
jrkooncecba5d6c2019-08-29 11:26:22 -05003148#ifdef TINYGLTF_USE_RAPIDJSON
3149 using rapidjson::Type;
3150 switch (o.GetType()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003151 case Type::kObjectType: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003152 Value::Object value_object;
jrkooncecba5d6c2019-08-29 11:26:22 -05003153 for (auto it = o.MemberBegin(); it != o.MemberEnd(); ++it) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003154 Value entry;
jrkooncecba5d6c2019-08-29 11:26:22 -05003155 ParseJsonAsValue(&entry, it->value);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003156 if (entry.Type() != NULL_TYPE)
3157 value_object.emplace(GetKey(it), std::move(entry));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003158 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003159 if (value_object.size() > 0) val = Value(std::move(value_object));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003160 } break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003161 case Type::kArrayType: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003162 Value::Array value_array;
jrkoonce5cecc412019-08-29 11:45:04 -05003163 value_array.reserve(o.Size());
jrkooncecba5d6c2019-08-29 11:26:22 -05003164 for (auto it = o.Begin(); it != o.End(); ++it) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003165 Value entry;
jrkooncecba5d6c2019-08-29 11:26:22 -05003166 ParseJsonAsValue(&entry, *it);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003167 if (entry.Type() != NULL_TYPE)
3168 value_array.emplace_back(std::move(entry));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003169 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003170 if (value_array.size() > 0) val = Value(std::move(value_array));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003171 } break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003172 case Type::kStringType:
3173 val = Value(std::string(o.GetString()));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003174 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003175 case Type::kFalseType:
3176 case Type::kTrueType:
3177 val = Value(o.GetBool());
Selmar Kokfa7022f2018-04-04 18:10:20 +02003178 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003179 case Type::kNumberType:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003180 if (!o.IsDouble()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003181 int i = 0;
3182 GetInt(o, i);
3183 val = Value(i);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003184 } else {
jrkooncecba5d6c2019-08-29 11:26:22 -05003185 double d = 0.0;
3186 GetDouble(o, d);
3187 val = Value(d);
3188 }
Selmar Kokfa7022f2018-04-04 18:10:20 +02003189 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003190 case Type::kNullType:
Selmar Kokfa7022f2018-04-04 18:10:20 +02003191 break;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003192 // all types are covered, so no `case default`
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003193 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003194#else
3195 switch (o.type()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003196 case json::value_t::object: {
3197 Value::Object value_object;
3198 for (auto it = o.begin(); it != o.end(); it++) {
3199 Value entry;
3200 ParseJsonAsValue(&entry, it.value());
3201 if (entry.Type() != NULL_TYPE)
3202 value_object.emplace(it.key(), std::move(entry));
3203 }
3204 if (value_object.size() > 0) val = Value(std::move(value_object));
3205 } break;
3206 case json::value_t::array: {
3207 Value::Array value_array;
3208 value_array.reserve(o.size());
3209 for (auto it = o.begin(); it != o.end(); it++) {
3210 Value entry;
3211 ParseJsonAsValue(&entry, it.value());
3212 if (entry.Type() != NULL_TYPE)
3213 value_array.emplace_back(std::move(entry));
3214 }
3215 if (value_array.size() > 0) val = Value(std::move(value_array));
3216 } break;
3217 case json::value_t::string:
3218 val = Value(o.get<std::string>());
3219 break;
3220 case json::value_t::boolean:
3221 val = Value(o.get<bool>());
3222 break;
3223 case json::value_t::number_integer:
3224 case json::value_t::number_unsigned:
3225 val = Value(static_cast<int>(o.get<int64_t>()));
3226 break;
3227 case json::value_t::number_float:
3228 val = Value(o.get<double>());
3229 break;
3230 case json::value_t::null:
3231 case json::value_t::discarded:
Christopher Sean Morrison0bfcb4f2022-02-23 10:02:49 -05003232 case json::value_t::binary:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003233 // default:
3234 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003235 }
3236#endif
Marcin Kacprzakf4f5c3c2022-09-02 16:15:54 +02003237 const bool isNotNull = val.Type() != NULL_TYPE;
3238
jrkooncecba5d6c2019-08-29 11:26:22 -05003239 if (ret) *ret = std::move(val);
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003240
Marcin Kacprzakf4f5c3c2022-09-02 16:15:54 +02003241 return isNotNull;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003242}
3243
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003244static bool ParseExtrasProperty(Value *ret, const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003245 json_const_iterator it;
3246 if (!FindMember(o, "extras", it)) {
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003247 return false;
3248 }
3249
jrkooncecba5d6c2019-08-29 11:26:22 -05003250 return ParseJsonAsValue(ret, GetValue(it));
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003251}
3252
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003253static bool ParseBooleanProperty(bool *ret, std::string *err, const json &o,
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09003254 const std::string &property,
3255 const bool required,
3256 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003257 json_const_iterator it;
3258 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003259 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003260 if (err) {
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09003261 (*err) += "'" + property + "' property is missing";
3262 if (!parent_node.empty()) {
3263 (*err) += " in " + parent_node;
3264 }
3265 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003266 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003267 }
3268 return false;
3269 }
3270
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003271 auto &value = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003272
3273 bool isBoolean;
jrkoonce0d2b6ef2019-09-04 13:46:45 -05003274 bool boolValue = false;
jrkooncecba5d6c2019-08-29 11:26:22 -05003275#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003276 isBoolean = value.IsBool();
3277 if (isBoolean) {
3278 boolValue = value.GetBool();
3279 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003280#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003281 isBoolean = value.is_boolean();
3282 if (isBoolean) {
3283 boolValue = value.get<bool>();
3284 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003285#endif
3286 if (!isBoolean) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003287 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003288 if (err) {
3289 (*err) += "'" + property + "' property is not a bool type.\n";
3290 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003291 }
3292 return false;
3293 }
3294
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003295 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003296 (*ret) = boolValue;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003297 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003298
3299 return true;
3300}
3301
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003302static bool ParseIntegerProperty(int *ret, std::string *err, const json &o,
3303 const std::string &property,
3304 const bool required,
3305 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003306 json_const_iterator it;
3307 if (!FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003308 if (required) {
3309 if (err) {
3310 (*err) += "'" + property + "' property is missing";
3311 if (!parent_node.empty()) {
3312 (*err) += " in " + parent_node;
3313 }
3314 (*err) += ".\n";
3315 }
3316 }
3317 return false;
3318 }
3319
jrkooncecba5d6c2019-08-29 11:26:22 -05003320 int intValue;
3321 bool isInt = GetInt(GetValue(it), intValue);
3322 if (!isInt) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003323 if (required) {
3324 if (err) {
3325 (*err) += "'" + property + "' property is not an integer type.\n";
3326 }
3327 }
3328 return false;
3329 }
3330
3331 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003332 (*ret) = intValue;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003333 }
3334
3335 return true;
3336}
3337
3338static bool ParseUnsignedProperty(size_t *ret, std::string *err, const json &o,
3339 const std::string &property,
3340 const bool required,
3341 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003342 json_const_iterator it;
3343 if (!FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003344 if (required) {
3345 if (err) {
3346 (*err) += "'" + property + "' property is missing";
3347 if (!parent_node.empty()) {
3348 (*err) += " in " + parent_node;
3349 }
3350 (*err) += ".\n";
3351 }
3352 }
3353 return false;
3354 }
3355
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003356 auto &value = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003357
jrkoonce0d2b6ef2019-09-04 13:46:45 -05003358 size_t uValue = 0;
jrkooncecba5d6c2019-08-29 11:26:22 -05003359 bool isUValue;
3360#ifdef TINYGLTF_USE_RAPIDJSON
3361 isUValue = false;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003362 if (value.IsUint()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003363 uValue = value.GetUint();
3364 isUValue = true;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003365 } else if (value.IsUint64()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003366 uValue = value.GetUint64();
3367 isUValue = true;
3368 }
3369#else
3370 isUValue = value.is_number_unsigned();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003371 if (isUValue) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003372 uValue = value.get<size_t>();
3373 }
3374#endif
3375 if (!isUValue) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003376 if (required) {
3377 if (err) {
3378 (*err) += "'" + property + "' property is not a positive integer.\n";
3379 }
3380 }
3381 return false;
3382 }
3383
3384 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003385 (*ret) = uValue;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003386 }
3387
3388 return true;
3389}
3390
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003391static bool ParseNumberProperty(double *ret, std::string *err, const json &o,
Syoyo Fujita5b407452017-06-04 17:42:41 +09003392 const std::string &property,
3393 const bool required,
3394 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003395 json_const_iterator it;
3396
3397 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003398 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003399 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003400 (*err) += "'" + property + "' property is missing";
3401 if (!parent_node.empty()) {
3402 (*err) += " in " + parent_node;
3403 }
3404 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003405 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003406 }
3407 return false;
3408 }
3409
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003410 double numberValue;
3411 bool isNumber = GetNumber(GetValue(it), numberValue);
jrkooncecba5d6c2019-08-29 11:26:22 -05003412
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003413 if (!isNumber) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003414 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003415 if (err) {
3416 (*err) += "'" + property + "' property is not a number type.\n";
3417 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003418 }
3419 return false;
3420 }
3421
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003422 if (ret) {
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003423 (*ret) = numberValue;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003424 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003425
3426 return true;
3427}
3428
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003429static bool ParseNumberArrayProperty(std::vector<double> *ret, std::string *err,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003430 const json &o, const std::string &property,
3431 bool required,
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003432 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003433 json_const_iterator it;
3434 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003435 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003436 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003437 (*err) += "'" + property + "' property is missing";
3438 if (!parent_node.empty()) {
3439 (*err) += " in " + parent_node;
3440 }
3441 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003442 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003443 }
3444 return false;
3445 }
3446
jrkooncecba5d6c2019-08-29 11:26:22 -05003447 if (!IsArray(GetValue(it))) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003448 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003449 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003450 (*err) += "'" + property + "' property is not an array";
3451 if (!parent_node.empty()) {
3452 (*err) += " in " + parent_node;
3453 }
3454 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003455 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003456 }
3457 return false;
3458 }
3459
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003460 ret->clear();
jrkooncecba5d6c2019-08-29 11:26:22 -05003461 auto end = ArrayEnd(GetValue(it));
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003462 for (auto i = ArrayBegin(GetValue(it)); i != end; ++i) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003463 double numberValue;
jrkoonce9b6f52e2019-08-29 13:56:58 -05003464 const bool isNumber = GetNumber(*i, numberValue);
jrkooncecba5d6c2019-08-29 11:26:22 -05003465 if (!isNumber) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003466 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003467 if (err) {
3468 (*err) += "'" + property + "' property is not a number.\n";
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003469 if (!parent_node.empty()) {
3470 (*err) += " in " + parent_node;
3471 }
3472 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003473 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003474 }
3475 return false;
3476 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003477 ret->push_back(numberValue);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003478 }
3479
3480 return true;
3481}
3482
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003483static bool ParseIntegerArrayProperty(std::vector<int> *ret, std::string *err,
3484 const json &o,
3485 const std::string &property,
3486 bool required,
3487 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003488 json_const_iterator it;
3489 if (!FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003490 if (required) {
3491 if (err) {
3492 (*err) += "'" + property + "' property is missing";
3493 if (!parent_node.empty()) {
3494 (*err) += " in " + parent_node;
3495 }
3496 (*err) += ".\n";
3497 }
3498 }
3499 return false;
3500 }
3501
jrkooncecba5d6c2019-08-29 11:26:22 -05003502 if (!IsArray(GetValue(it))) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003503 if (required) {
3504 if (err) {
3505 (*err) += "'" + property + "' property is not an array";
3506 if (!parent_node.empty()) {
3507 (*err) += " in " + parent_node;
3508 }
3509 (*err) += ".\n";
3510 }
3511 }
3512 return false;
3513 }
3514
3515 ret->clear();
jrkooncecba5d6c2019-08-29 11:26:22 -05003516 auto end = ArrayEnd(GetValue(it));
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003517 for (auto i = ArrayBegin(GetValue(it)); i != end; ++i) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003518 int numberValue;
3519 bool isNumber = GetInt(*i, numberValue);
3520 if (!isNumber) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003521 if (required) {
3522 if (err) {
3523 (*err) += "'" + property + "' property is not an integer type.\n";
3524 if (!parent_node.empty()) {
3525 (*err) += " in " + parent_node;
3526 }
3527 (*err) += ".\n";
3528 }
3529 }
3530 return false;
3531 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003532 ret->push_back(numberValue);
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003533 }
3534
3535 return true;
3536}
3537
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003538static bool ParseStringProperty(
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003539 std::string *ret, std::string *err, const json &o,
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003540 const std::string &property, bool required,
3541 const std::string &parent_node = std::string()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003542 json_const_iterator it;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003543 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003544 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003545 if (err) {
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003546 (*err) += "'" + property + "' property is missing";
3547 if (parent_node.empty()) {
3548 (*err) += ".\n";
3549 } else {
3550 (*err) += " in `" + parent_node + "'.\n";
3551 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003552 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003553 }
3554 return false;
3555 }
3556
jrkooncecba5d6c2019-08-29 11:26:22 -05003557 std::string strValue;
3558 if (!GetString(GetValue(it), strValue)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003559 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003560 if (err) {
3561 (*err) += "'" + property + "' property is not a string type.\n";
3562 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003563 }
3564 return false;
3565 }
3566
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003567 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003568 (*ret) = std::move(strValue);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003569 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003570
3571 return true;
3572}
3573
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003574static bool ParseStringIntegerProperty(std::map<std::string, int> *ret,
3575 std::string *err, const json &o,
3576 const std::string &property,
3577 bool required,
3578 const std::string &parent = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003579 json_const_iterator it;
3580 if (!FindMember(o, property.c_str(), it)) {
Luke San Antonio19894c72016-06-14 21:19:51 -04003581 if (required) {
3582 if (err) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09003583 if (!parent.empty()) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003584 (*err) +=
3585 "'" + property + "' property is missing in " + parent + ".\n";
Syoyo Fujita57c10182017-10-19 18:48:26 +09003586 } else {
3587 (*err) += "'" + property + "' property is missing.\n";
3588 }
Luke San Antonio19894c72016-06-14 21:19:51 -04003589 }
3590 }
3591 return false;
3592 }
3593
jrkooncecba5d6c2019-08-29 11:26:22 -05003594 const json &dict = GetValue(it);
3595
Luke San Antonio19894c72016-06-14 21:19:51 -04003596 // Make sure we are dealing with an object / dictionary.
jrkooncecba5d6c2019-08-29 11:26:22 -05003597 if (!IsObject(dict)) {
Luke San Antonio19894c72016-06-14 21:19:51 -04003598 if (required) {
3599 if (err) {
3600 (*err) += "'" + property + "' property is not an object.\n";
3601 }
3602 }
3603 return false;
3604 }
3605
3606 ret->clear();
Luke San Antonio19894c72016-06-14 21:19:51 -04003607
jrkooncecba5d6c2019-08-29 11:26:22 -05003608 json_const_iterator dictIt(ObjectBegin(dict));
3609 json_const_iterator dictItEnd(ObjectEnd(dict));
Luke San Antonio19894c72016-06-14 21:19:51 -04003610
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09003611 for (; dictIt != dictItEnd; ++dictIt) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003612 int intVal;
3613 if (!GetInt(GetValue(dictIt), intVal)) {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09003614 if (required) {
3615 if (err) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003616 (*err) += "'" + property + "' value is not an integer type.\n";
Luke San Antonio19894c72016-06-14 21:19:51 -04003617 }
3618 }
3619 return false;
3620 }
3621
3622 // Insert into the list.
jrkooncecba5d6c2019-08-29 11:26:22 -05003623 (*ret)[GetKey(dictIt)] = intVal;
Luke San Antonio19894c72016-06-14 21:19:51 -04003624 }
3625 return true;
3626}
3627
Syoyo Fujita5b407452017-06-04 17:42:41 +09003628static bool ParseJSONProperty(std::map<std::string, double> *ret,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003629 std::string *err, const json &o,
Syoyo Fujita5b407452017-06-04 17:42:41 +09003630 const std::string &property, bool required) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003631 json_const_iterator it;
3632 if (!FindMember(o, property.c_str(), it)) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003633 if (required) {
Syoyo Fujita5b407452017-06-04 17:42:41 +09003634 if (err) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003635 (*err) += "'" + property + "' property is missing. \n'";
3636 }
3637 }
3638 return false;
3639 }
3640
jrkooncecba5d6c2019-08-29 11:26:22 -05003641 const json &obj = GetValue(it);
3642
3643 if (!IsObject(obj)) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003644 if (required) {
3645 if (err) {
3646 (*err) += "'" + property + "' property is not a JSON object.\n";
3647 }
3648 }
3649 return false;
3650 }
3651
3652 ret->clear();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003653
jrkooncecba5d6c2019-08-29 11:26:22 -05003654 json_const_iterator it2(ObjectBegin(obj));
3655 json_const_iterator itEnd(ObjectEnd(obj));
3656 for (; it2 != itEnd; ++it2) {
3657 double numVal;
3658 if (GetNumber(GetValue(it2), numVal))
3659 ret->emplace(std::string(GetKey(it2)), numVal);
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003660 }
3661
3662 return true;
3663}
3664
Selmar09d2ff12018-03-15 17:30:42 +01003665static bool ParseParameterProperty(Parameter *param, std::string *err,
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003666 const json &o, const std::string &prop,
3667 bool required) {
Selmar09d2ff12018-03-15 17:30:42 +01003668 // A parameter value can either be a string or an array of either a boolean or
3669 // a number. Booleans of any kind aren't supported here. Granted, it
3670 // complicates the Parameter structure and breaks it semantically in the sense
3671 // that the client probably works off the assumption that if the string is
3672 // empty the vector is used, etc. Would a tagged union work?
3673 if (ParseStringProperty(&param->string_value, err, o, prop, false)) {
3674 // Found string property.
3675 return true;
3676 } else if (ParseNumberArrayProperty(&param->number_array, err, o, prop,
3677 false)) {
3678 // Found a number array.
3679 return true;
Ben Buzbeef6af2242018-04-25 15:13:05 -07003680 } else if (ParseNumberProperty(&param->number_value, err, o, prop, false)) {
Marcin Kacprzakb12a54e2022-09-02 16:13:11 +02003681 param->has_number_value = true;
3682 return true;
Selmar09d2ff12018-03-15 17:30:42 +01003683 } else if (ParseJSONProperty(&param->json_double_value, err, o, prop,
3684 false)) {
3685 return true;
3686 } else if (ParseBooleanProperty(&param->bool_value, err, o, prop, false)) {
3687 return true;
3688 } else {
3689 if (required) {
3690 if (err) {
3691 (*err) += "parameter must be a string or number / number array.\n";
3692 }
3693 }
3694 return false;
3695 }
3696}
3697
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003698static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err,
3699 const json &o) {
Syoyo Fujita48f6db02018-04-15 18:40:55 +09003700 (void)err;
3701
jrkooncecba5d6c2019-08-29 11:26:22 -05003702 json_const_iterator it;
3703 if (!FindMember(o, "extensions", it)) {
Selmar09d2ff12018-03-15 17:30:42 +01003704 return false;
3705 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003706
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003707 auto &obj = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003708 if (!IsObject(obj)) {
Selmar09d2ff12018-03-15 17:30:42 +01003709 return false;
3710 }
3711 ExtensionMap extensions;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003712 json_const_iterator extIt = ObjectBegin(obj); // it.value().begin();
jrkooncecba5d6c2019-08-29 11:26:22 -05003713 json_const_iterator extEnd = ObjectEnd(obj);
3714 for (; extIt != extEnd; ++extIt) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003715 auto &itObj = GetValue(extIt);
jrkooncecba5d6c2019-08-29 11:26:22 -05003716 if (!IsObject(itObj)) continue;
3717 std::string key(GetKey(extIt));
3718 if (!ParseJsonAsValue(&extensions[key], itObj)) {
jrkoonce06c30c42019-09-03 15:56:48 -05003719 if (!key.empty()) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003720 // create empty object so that an extension object is still of type
3721 // object
jrkooncecba5d6c2019-08-29 11:26:22 -05003722 extensions[key] = Value{Value::Object{}};
Selmar Kokee3d0662018-10-08 16:20:43 +02003723 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003724 }
Selmar09d2ff12018-03-15 17:30:42 +01003725 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003726 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003727 (*ret) = std::move(extensions);
Selmar09d2ff12018-03-15 17:30:42 +01003728 }
3729 return true;
3730}
3731
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003732static bool ParseAsset(Asset *asset, std::string *err, const json &o,
3733 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09003734 ParseStringProperty(&asset->version, err, o, "version", true, "Asset");
3735 ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset");
3736 ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset");
Miguel Sousa22bfc842020-02-20 14:16:58 +01003737 ParseStringProperty(&asset->copyright, err, o, "copyright", false, "Asset");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003738
Selmar09d2ff12018-03-15 17:30:42 +01003739 ParseExtensionsProperty(&asset->extensions, err, o);
3740
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +00003741 // Unity exporter version is added as extra here
3742 ParseExtrasProperty(&(asset->extras), o);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003743
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003744 if (store_original_json_for_extras_and_extensions) {
3745 {
3746 json_const_iterator it;
3747 if (FindMember(o, "extensions", it)) {
3748 asset->extensions_json_string = JsonToString(GetValue(it));
3749 }
3750 }
3751 {
3752 json_const_iterator it;
3753 if (FindMember(o, "extras", it)) {
3754 asset->extras_json_string = JsonToString(GetValue(it));
3755 }
3756 }
3757 }
3758
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003759 return true;
3760}
3761
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003762static bool ParseImage(Image *image, const int image_idx, std::string *err,
3763 std::string *warn, const json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003764 bool store_original_json_for_extras_and_extensions,
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003765 const std::string &basedir, FsCallbacks *fs,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003766 LoadImageDataFunction *LoadImageData = nullptr,
Paolo Jovone6601bf2018-07-07 20:43:33 +02003767 void *load_image_user_data = nullptr) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003768 // A glTF image must either reference a bufferView or an image uri
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003769
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003770 // schema says oneOf [`bufferView`, `uri`]
3771 // TODO(syoyo): Check the type of each parameters.
jrkooncecba5d6c2019-08-29 11:26:22 -05003772 json_const_iterator it;
3773 bool hasBufferView = FindMember(o, "bufferView", it);
3774 bool hasURI = FindMember(o, "uri", it);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003775
Syoyo Fujitaaf3ebb22019-01-06 18:55:57 +09003776 ParseStringProperty(&image->name, err, o, "name", false);
3777
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003778 if (hasBufferView && hasURI) {
3779 // Should not both defined.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003780 if (err) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09003781 (*err) +=
3782 "Only one of `bufferView` or `uri` should be defined, but both are "
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003783 "defined for image[" +
3784 std::to_string(image_idx) + "] name = \"" + image->name + "\"\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003785 }
3786 return false;
3787 }
3788
3789 if (!hasBufferView && !hasURI) {
3790 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003791 (*err) += "Neither required `bufferView` nor `uri` defined for image[" +
3792 std::to_string(image_idx) + "] name = \"" + image->name +
3793 "\"\n";
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003794 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003795 return false;
3796 }
3797
Syoyo Fujita3e53feb2018-09-02 15:36:17 +09003798 ParseExtensionsProperty(&image->extensions, err, o);
Selmar Kok8eb39042018-10-05 14:29:35 +02003799 ParseExtrasProperty(&image->extras, o);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003800
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003801 if (store_original_json_for_extras_and_extensions) {
3802 {
3803 json_const_iterator eit;
3804 if (FindMember(o, "extensions", eit)) {
3805 image->extensions_json_string = JsonToString(GetValue(eit));
3806 }
3807 }
3808 {
3809 json_const_iterator eit;
3810 if (FindMember(o, "extras", eit)) {
3811 image->extras_json_string = JsonToString(GetValue(eit));
3812 }
3813 }
3814 }
3815
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003816 if (hasBufferView) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003817 int bufferView = -1;
3818 if (!ParseIntegerProperty(&bufferView, err, o, "bufferView", true)) {
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003819 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003820 (*err) += "Failed to parse `bufferView` for image[" +
3821 std::to_string(image_idx) + "] name = \"" + image->name +
3822 "\"\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003823 }
3824 return false;
3825 }
3826
3827 std::string mime_type;
3828 ParseStringProperty(&mime_type, err, o, "mimeType", false);
3829
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003830 int width = 0;
3831 ParseIntegerProperty(&width, err, o, "width", false);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003832
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003833 int height = 0;
3834 ParseIntegerProperty(&height, err, o, "height", false);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003835
3836 // Just only save some information here. Loading actual image data from
3837 // bufferView is done after this `ParseImage` function.
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003838 image->bufferView = bufferView;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003839 image->mimeType = mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003840 image->width = width;
3841 image->height = height;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003842
3843 return true;
3844 }
3845
Syoyo Fujita246654a2018-03-21 20:32:22 +09003846 // Parse URI & Load image data.
3847
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003848 std::string uri;
3849 std::string tmp_err;
Syoyo Fujita246654a2018-03-21 20:32:22 +09003850 if (!ParseStringProperty(&uri, &tmp_err, o, "uri", true)) {
3851 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003852 (*err) += "Failed to parse `uri` for image[" + std::to_string(image_idx) +
3853 "] name = \"" + image->name + "\".\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003854 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003855 return false;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003856 }
3857
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003858 std::vector<unsigned char> img;
Syoyo Fujitabeded612016-05-01 20:03:43 +09003859
Syoyo Fujita246654a2018-03-21 20:32:22 +09003860 if (IsDataURI(uri)) {
johan bowald642a3432018-04-01 12:37:18 +02003861 if (!DecodeDataURI(&img, image->mimeType, uri, 0, false)) {
Syoyo Fujita246654a2018-03-21 20:32:22 +09003862 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003863 (*err) += "Failed to decode 'uri' for image[" +
3864 std::to_string(image_idx) + "] name = [" + image->name +
3865 "]\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003866 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003867 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003868 }
3869 } else {
Syoyo Fujita246654a2018-03-21 20:32:22 +09003870 // Assume external file
3871 // Keep texture path (for textures that cannot be decoded)
3872 image->uri = uri;
Selmar67af3c92018-03-16 11:48:19 +01003873#ifdef TINYGLTF_NO_EXTERNAL_IMAGE
Syoyo Fujita246654a2018-03-21 20:32:22 +09003874 return true;
Selmar67af3c92018-03-16 11:48:19 +01003875#endif
Syoyo Fujitac4166e42020-01-08 02:38:01 +09003876 std::string decoded_uri = dlib::urldecode(uri);
3877 if (!LoadExternalFile(&img, err, warn, decoded_uri, basedir,
3878 /* required */ false, /* required bytes */ 0,
3879 /* checksize */ false, fs)) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003880 if (warn) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003881 (*warn) += "Failed to load external 'uri' for image[" +
3882 std::to_string(image_idx) + "] name = [" + image->name +
3883 "]\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003884 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003885 // If the image cannot be loaded, keep uri as image->uri.
3886 return true;
3887 }
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003888
Syoyo Fujita246654a2018-03-21 20:32:22 +09003889 if (img.empty()) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003890 if (warn) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003891 (*warn) += "Image data is empty for image[" +
3892 std::to_string(image_idx) + "] name = [" + image->name +
3893 "] \n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09003894 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003895 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003896 }
3897 }
3898
Squareysff644d82018-03-13 22:36:18 +01003899 if (*LoadImageData == nullptr) {
3900 if (err) {
3901 (*err) += "No LoadImageData callback specified.\n";
3902 }
3903 return false;
3904 }
Syoyo Fujitaaf3ebb22019-01-06 18:55:57 +09003905 return (*LoadImageData)(image, image_idx, err, warn, 0, 0, &img.at(0),
Paolo Jovone6601bf2018-07-07 20:43:33 +02003906 static_cast<int>(img.size()), load_image_user_data);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003907}
3908
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003909static bool ParseTexture(Texture *texture, std::string *err, const json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003910 bool store_original_json_for_extras_and_extensions,
Syoyo Fujitabeded612016-05-01 20:03:43 +09003911 const std::string &basedir) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003912 (void)basedir;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003913 int sampler = -1;
3914 int source = -1;
3915 ParseIntegerProperty(&sampler, err, o, "sampler", false);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003916
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003917 ParseIntegerProperty(&source, err, o, "source", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09003918
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003919 texture->sampler = sampler;
3920 texture->source = source;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003921
Selmar Kokfa7022f2018-04-04 18:10:20 +02003922 ParseExtensionsProperty(&texture->extensions, err, o);
3923 ParseExtrasProperty(&texture->extras, o);
Syoyo Fujitabde70212016-02-07 17:38:17 +09003924
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003925 if (store_original_json_for_extras_and_extensions) {
3926 {
3927 json_const_iterator it;
3928 if (FindMember(o, "extensions", it)) {
3929 texture->extensions_json_string = JsonToString(GetValue(it));
3930 }
3931 }
3932 {
3933 json_const_iterator it;
3934 if (FindMember(o, "extras", it)) {
3935 texture->extras_json_string = JsonToString(GetValue(it));
3936 }
3937 }
3938 }
3939
Vladimír Vondruš239be2c2018-07-24 23:23:56 +02003940 ParseStringProperty(&texture->name, err, o, "name", false);
3941
Syoyo Fujitabde70212016-02-07 17:38:17 +09003942 return true;
3943}
3944
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003945static bool ParseTextureInfo(
3946 TextureInfo *texinfo, std::string *err, const json &o,
3947 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09003948 if (texinfo == nullptr) {
3949 return false;
3950 }
3951
3952 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
3953 /* required */ true, "TextureInfo")) {
3954 return false;
3955 }
3956
3957 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
3958
3959 ParseExtensionsProperty(&texinfo->extensions, err, o);
3960 ParseExtrasProperty(&texinfo->extras, o);
3961
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003962 if (store_original_json_for_extras_and_extensions) {
3963 {
3964 json_const_iterator it;
3965 if (FindMember(o, "extensions", it)) {
3966 texinfo->extensions_json_string = JsonToString(GetValue(it));
3967 }
3968 }
3969 {
3970 json_const_iterator it;
3971 if (FindMember(o, "extras", it)) {
3972 texinfo->extras_json_string = JsonToString(GetValue(it));
3973 }
3974 }
3975 }
3976
Syoyo Fujita046400b2019-07-24 19:26:48 +09003977 return true;
3978}
3979
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003980static bool ParseNormalTextureInfo(
3981 NormalTextureInfo *texinfo, std::string *err, const json &o,
3982 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09003983 if (texinfo == nullptr) {
3984 return false;
3985 }
3986
3987 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
3988 /* required */ true, "NormalTextureInfo")) {
3989 return false;
3990 }
3991
3992 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
3993 ParseNumberProperty(&texinfo->scale, err, o, "scale", false);
3994
3995 ParseExtensionsProperty(&texinfo->extensions, err, o);
3996 ParseExtrasProperty(&texinfo->extras, o);
3997
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003998 if (store_original_json_for_extras_and_extensions) {
3999 {
4000 json_const_iterator it;
4001 if (FindMember(o, "extensions", it)) {
4002 texinfo->extensions_json_string = JsonToString(GetValue(it));
4003 }
4004 }
4005 {
4006 json_const_iterator it;
4007 if (FindMember(o, "extras", it)) {
4008 texinfo->extras_json_string = JsonToString(GetValue(it));
4009 }
4010 }
4011 }
4012
Syoyo Fujita046400b2019-07-24 19:26:48 +09004013 return true;
4014}
4015
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004016static bool ParseOcclusionTextureInfo(
4017 OcclusionTextureInfo *texinfo, std::string *err, const json &o,
4018 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004019 if (texinfo == nullptr) {
4020 return false;
4021 }
4022
4023 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
4024 /* required */ true, "NormalTextureInfo")) {
4025 return false;
4026 }
4027
4028 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
4029 ParseNumberProperty(&texinfo->strength, err, o, "strength", false);
4030
4031 ParseExtensionsProperty(&texinfo->extensions, err, o);
4032 ParseExtrasProperty(&texinfo->extras, o);
4033
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004034 if (store_original_json_for_extras_and_extensions) {
4035 {
4036 json_const_iterator it;
4037 if (FindMember(o, "extensions", it)) {
4038 texinfo->extensions_json_string = JsonToString(GetValue(it));
4039 }
4040 }
4041 {
4042 json_const_iterator it;
4043 if (FindMember(o, "extras", it)) {
4044 texinfo->extras_json_string = JsonToString(GetValue(it));
4045 }
4046 }
4047 }
4048
Syoyo Fujita046400b2019-07-24 19:26:48 +09004049 return true;
4050}
4051
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004052static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004053 bool store_original_json_for_extras_and_extensions,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004054 FsCallbacks *fs, const std::string &basedir,
4055 bool is_binary = false,
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09004056 const unsigned char *bin_data = nullptr,
Syoyo Fujitabeded612016-05-01 20:03:43 +09004057 size_t bin_size = 0) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004058 size_t byteLength;
4059 if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true,
4060 "Buffer")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004061 return false;
4062 }
4063
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004064 // In glTF 2.0, uri is not mandatory anymore
Syoyo Fujita20244e12018-03-15 11:01:05 -05004065 buffer->uri.clear();
4066 ParseStringProperty(&buffer->uri, err, o, "uri", false, "Buffer");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004067
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004068 // having an empty uri for a non embedded image should not be valid
Syoyo Fujita20244e12018-03-15 11:01:05 -05004069 if (!is_binary && buffer->uri.empty()) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004070 if (err) {
4071 (*err) += "'uri' is missing from non binary glTF file buffer.\n";
4072 }
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004073 }
4074
jrkooncecba5d6c2019-08-29 11:26:22 -05004075 json_const_iterator type;
4076 if (FindMember(o, "type", type)) {
4077 std::string typeStr;
4078 if (GetString(GetValue(type), typeStr)) {
4079 if (typeStr.compare("arraybuffer") == 0) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004080 // buffer.type = "arraybuffer";
4081 }
4082 }
4083 }
4084
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004085 if (is_binary) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004086 // Still binary glTF accepts external dataURI.
Syoyo Fujita20244e12018-03-15 11:01:05 -05004087 if (!buffer->uri.empty()) {
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004088 // First try embedded data URI.
4089 if (IsDataURI(buffer->uri)) {
4090 std::string mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004091 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004092 true)) {
4093 if (err) {
4094 (*err) +=
4095 "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
4096 }
4097 return false;
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004098 }
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004099 } else {
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004100 // External .bin file.
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004101 std::string decoded_uri = dlib::urldecode(buffer->uri);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09004102 if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr,
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004103 decoded_uri, basedir, /* required */ true,
4104 byteLength, /* checkSize */ true, fs)) {
Vladimír Vondrušfd84ceb2018-08-16 21:07:56 +02004105 return false;
4106 }
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004107 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004108 } else {
Syoyo Fujitabeded612016-05-01 20:03:43 +09004109 // load data from (embedded) binary data
4110
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09004111 if ((bin_size == 0) || (bin_data == nullptr)) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09004112 if (err) {
Syoyo Fujita40c4a0a2017-05-30 01:21:36 +09004113 (*err) += "Invalid binary data in `Buffer'.\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09004114 }
4115 return false;
4116 }
4117
4118 if (byteLength > bin_size) {
4119 if (err) {
4120 std::stringstream ss;
4121 ss << "Invalid `byteLength'. Must be equal or less than binary size: "
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004122 "`byteLength' = "
4123 << byteLength << ", binary size = " << bin_size << std::endl;
Syoyo Fujitabeded612016-05-01 20:03:43 +09004124 (*err) += ss.str();
4125 }
4126 return false;
4127 }
4128
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004129 // Read buffer data
4130 buffer->data.resize(static_cast<size_t>(byteLength));
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004131 memcpy(&(buffer->data.at(0)), bin_data, static_cast<size_t>(byteLength));
Syoyo Fujitabeded612016-05-01 20:03:43 +09004132 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004133
4134 } else {
Syoyo Fujita20244e12018-03-15 11:01:05 -05004135 if (IsDataURI(buffer->uri)) {
johan bowaldef151a42018-04-01 13:44:48 +02004136 std::string mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004137 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
4138 true)) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004139 if (err) {
Syoyo Fujita20244e12018-03-15 11:01:05 -05004140 (*err) += "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004141 }
4142 return false;
4143 }
4144 } else {
4145 // Assume external .bin file.
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004146 std::string decoded_uri = dlib::urldecode(buffer->uri);
4147 if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, decoded_uri,
4148 basedir, /* required */ true, byteLength,
4149 /* checkSize */ true, fs)) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004150 return false;
4151 }
4152 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004153 }
4154
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004155 ParseStringProperty(&buffer->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004156
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004157 ParseExtensionsProperty(&buffer->extensions, err, o);
4158 ParseExtrasProperty(&buffer->extras, o);
4159
4160 if (store_original_json_for_extras_and_extensions) {
4161 {
4162 json_const_iterator it;
4163 if (FindMember(o, "extensions", it)) {
4164 buffer->extensions_json_string = JsonToString(GetValue(it));
4165 }
4166 }
4167 {
4168 json_const_iterator it;
4169 if (FindMember(o, "extras", it)) {
4170 buffer->extras_json_string = JsonToString(GetValue(it));
4171 }
4172 }
4173 }
4174
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004175 return true;
4176}
4177
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004178static bool ParseBufferView(
4179 BufferView *bufferView, std::string *err, const json &o,
4180 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004181 int buffer = -1;
4182 if (!ParseIntegerProperty(&buffer, err, o, "buffer", true, "BufferView")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004183 return false;
4184 }
4185
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004186 size_t byteOffset = 0;
4187 ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004188
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004189 size_t byteLength = 1;
4190 if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true,
4191 "BufferView")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004192 return false;
4193 }
4194
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004195 size_t byteStride = 0;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004196 if (!ParseUnsignedProperty(&byteStride, err, o, "byteStride", false)) {
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004197 // Spec says: When byteStride of referenced bufferView is not defined, it
4198 // means that accessor elements are tightly packed, i.e., effective stride
4199 // equals the size of the element.
4200 // We cannot determine the actual byteStride until Accessor are parsed, thus
4201 // set 0(= tightly packed) here(as done in OpenGL's VertexAttribPoiner)
4202 byteStride = 0;
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004203 }
4204
4205 if ((byteStride > 252) || ((byteStride % 4) != 0)) {
4206 if (err) {
4207 std::stringstream ss;
4208 ss << "Invalid `byteStride' value. `byteStride' must be the multiple of "
4209 "4 : "
4210 << byteStride << std::endl;
4211
4212 (*err) += ss.str();
4213 }
4214 return false;
4215 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004216
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004217 int target = 0;
4218 ParseIntegerProperty(&target, err, o, "target", false);
4219 if ((target == TINYGLTF_TARGET_ARRAY_BUFFER) ||
4220 (target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004221 // OK
4222 } else {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004223 target = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004224 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004225 bufferView->target = target;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004226
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004227 ParseStringProperty(&bufferView->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004228
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004229 ParseExtensionsProperty(&bufferView->extensions, err, o);
4230 ParseExtrasProperty(&bufferView->extras, o);
4231
4232 if (store_original_json_for_extras_and_extensions) {
4233 {
4234 json_const_iterator it;
4235 if (FindMember(o, "extensions", it)) {
4236 bufferView->extensions_json_string = JsonToString(GetValue(it));
4237 }
4238 }
4239 {
4240 json_const_iterator it;
4241 if (FindMember(o, "extras", it)) {
4242 bufferView->extras_json_string = JsonToString(GetValue(it));
4243 }
4244 }
4245 }
4246
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004247 bufferView->buffer = buffer;
4248 bufferView->byteOffset = byteOffset;
4249 bufferView->byteLength = byteLength;
4250 bufferView->byteStride = byteStride;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004251 return true;
4252}
4253
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004254static bool ParseSparseAccessor(Accessor *accessor, std::string *err,
4255 const json &o) {
4256 accessor->sparse.isSparse = true;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004257
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004258 int count = 0;
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004259 if (!ParseIntegerProperty(&count, err, o, "count", true, "SparseAccessor")) {
4260 return false;
4261 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004262
jrkooncecba5d6c2019-08-29 11:26:22 -05004263 json_const_iterator indices_iterator;
4264 json_const_iterator values_iterator;
4265 if (!FindMember(o, "indices", indices_iterator)) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004266 (*err) = "the sparse object of this accessor doesn't have indices";
4267 return false;
4268 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004269
jrkooncecba5d6c2019-08-29 11:26:22 -05004270 if (!FindMember(o, "values", values_iterator)) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004271 (*err) = "the sparse object ob ths accessor doesn't have values";
4272 return false;
4273 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004274
jrkooncecba5d6c2019-08-29 11:26:22 -05004275 const json &indices_obj = GetValue(indices_iterator);
4276 const json &values_obj = GetValue(values_iterator);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004277
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004278 int indices_buffer_view = 0, indices_byte_offset = 0, component_type = 0;
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09004279 if (!ParseIntegerProperty(&indices_buffer_view, err, indices_obj,
4280 "bufferView", true, "SparseAccessor")) {
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004281 return false;
4282 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004283 ParseIntegerProperty(&indices_byte_offset, err, indices_obj, "byteOffset",
Jack Mousseau94715172022-02-24 14:25:37 -08004284 false);
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004285 if (!ParseIntegerProperty(&component_type, err, indices_obj, "componentType",
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09004286 true, "SparseAccessor")) {
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004287 return false;
4288 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004289
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004290 int values_buffer_view = 0, values_byte_offset = 0;
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004291 if (!ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView",
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09004292 true, "SparseAccessor")) {
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004293 return false;
4294 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004295 ParseIntegerProperty(&values_byte_offset, err, values_obj, "byteOffset",
Jack Mousseau94715172022-02-24 14:25:37 -08004296 false);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004297
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004298 accessor->sparse.count = count;
4299 accessor->sparse.indices.bufferView = indices_buffer_view;
4300 accessor->sparse.indices.byteOffset = indices_byte_offset;
4301 accessor->sparse.indices.componentType = component_type;
4302 accessor->sparse.values.bufferView = values_buffer_view;
4303 accessor->sparse.values.byteOffset = values_byte_offset;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004304
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004305 return true;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004306}
4307
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004308static bool ParseAccessor(Accessor *accessor, std::string *err, const json &o,
4309 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004310 int bufferView = -1;
4311 ParseIntegerProperty(&bufferView, err, o, "bufferView", false, "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004312
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004313 size_t byteOffset = 0;
4314 ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false, "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004315
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004316 bool normalized = false;
4317 ParseBooleanProperty(&normalized, err, o, "normalized", false, "Accessor");
4318
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004319 size_t componentType = 0;
4320 if (!ParseUnsignedProperty(&componentType, err, o, "componentType", true,
4321 "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004322 return false;
4323 }
4324
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004325 size_t count = 0;
4326 if (!ParseUnsignedProperty(&count, err, o, "count", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004327 return false;
4328 }
4329
4330 std::string type;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09004331 if (!ParseStringProperty(&type, err, o, "type", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004332 return false;
4333 }
4334
4335 if (type.compare("SCALAR") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004336 accessor->type = TINYGLTF_TYPE_SCALAR;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004337 } else if (type.compare("VEC2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004338 accessor->type = TINYGLTF_TYPE_VEC2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004339 } else if (type.compare("VEC3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004340 accessor->type = TINYGLTF_TYPE_VEC3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004341 } else if (type.compare("VEC4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004342 accessor->type = TINYGLTF_TYPE_VEC4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004343 } else if (type.compare("MAT2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004344 accessor->type = TINYGLTF_TYPE_MAT2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004345 } else if (type.compare("MAT3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004346 accessor->type = TINYGLTF_TYPE_MAT3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004347 } else if (type.compare("MAT4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004348 accessor->type = TINYGLTF_TYPE_MAT4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004349 } else {
4350 std::stringstream ss;
4351 ss << "Unsupported `type` for accessor object. Got \"" << type << "\"\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004352 if (err) {
4353 (*err) += ss.str();
4354 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004355 return false;
4356 }
4357
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004358 ParseStringProperty(&accessor->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004359
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004360 accessor->minValues.clear();
4361 accessor->maxValues.clear();
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004362 ParseNumberArrayProperty(&accessor->minValues, err, o, "min", false,
4363 "Accessor");
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004364
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004365 ParseNumberArrayProperty(&accessor->maxValues, err, o, "max", false,
4366 "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004367
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004368 accessor->count = count;
4369 accessor->bufferView = bufferView;
4370 accessor->byteOffset = byteOffset;
jianghaosen4748ad62017-08-29 20:08:37 +08004371 accessor->normalized = normalized;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004372 {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004373 if (componentType >= TINYGLTF_COMPONENT_TYPE_BYTE &&
4374 componentType <= TINYGLTF_COMPONENT_TYPE_DOUBLE) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004375 // OK
Arthur Brainville (Ybalrid)811e1d32019-06-15 07:32:38 +02004376 accessor->componentType = int(componentType);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004377 } else {
4378 std::stringstream ss;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004379 ss << "Invalid `componentType` in accessor. Got " << componentType
4380 << "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004381 if (err) {
4382 (*err) += ss.str();
4383 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004384 return false;
4385 }
4386 }
4387
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004388 ParseExtensionsProperty(&(accessor->extensions), err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004389 ParseExtrasProperty(&(accessor->extras), o);
4390
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004391 if (store_original_json_for_extras_and_extensions) {
4392 {
4393 json_const_iterator it;
4394 if (FindMember(o, "extensions", it)) {
4395 accessor->extensions_json_string = JsonToString(GetValue(it));
4396 }
4397 }
4398 {
4399 json_const_iterator it;
4400 if (FindMember(o, "extras", it)) {
4401 accessor->extras_json_string = JsonToString(GetValue(it));
4402 }
4403 }
4404 }
4405
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004406 // check if accessor has a "sparse" object:
jrkooncecba5d6c2019-08-29 11:26:22 -05004407 json_const_iterator iterator;
4408 if (FindMember(o, "sparse", iterator)) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004409 // here this accessor has a "sparse" subobject
jrkooncecba5d6c2019-08-29 11:26:22 -05004410 return ParseSparseAccessor(accessor, err, GetValue(iterator));
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004411 }
4412
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004413 return true;
4414}
4415
Alex Wood7319db72019-01-24 15:38:16 -05004416#ifdef TINYGLTF_ENABLE_DRACO
4417
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004418static void DecodeIndexBuffer(draco::Mesh *mesh, size_t componentSize,
4419 std::vector<uint8_t> &outBuffer) {
4420 if (componentSize == 4) {
Alex Wood7319db72019-01-24 15:38:16 -05004421 assert(sizeof(mesh->face(draco::FaceIndex(0))[0]) == componentSize);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004422 memcpy(outBuffer.data(), &mesh->face(draco::FaceIndex(0))[0],
4423 outBuffer.size());
4424 } else {
Alex Wood7319db72019-01-24 15:38:16 -05004425 size_t faceStride = componentSize * 3;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004426 for (draco::FaceIndex f(0); f < mesh->num_faces(); ++f) {
4427 const draco::Mesh::Face &face = mesh->face(f);
4428 if (componentSize == 2) {
4429 uint16_t indices[3] = {(uint16_t)face[0].value(),
4430 (uint16_t)face[1].value(),
4431 (uint16_t)face[2].value()};
4432 memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
4433 faceStride);
4434 } else {
4435 uint8_t indices[3] = {(uint8_t)face[0].value(),
4436 (uint8_t)face[1].value(),
4437 (uint8_t)face[2].value()};
4438 memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
4439 faceStride);
Alex Wood7319db72019-01-24 15:38:16 -05004440 }
4441 }
4442 }
4443}
4444
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004445template <typename T>
4446static bool GetAttributeForAllPoints(draco::Mesh *mesh,
4447 const draco::PointAttribute *pAttribute,
4448 std::vector<uint8_t> &outBuffer) {
Alex Wood7319db72019-01-24 15:38:16 -05004449 size_t byteOffset = 0;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004450 T values[4] = {0, 0, 0, 0};
4451 for (draco::PointIndex i(0); i < mesh->num_points(); ++i) {
Alex Wood7319db72019-01-24 15:38:16 -05004452 const draco::AttributeValueIndex val_index = pAttribute->mapped_index(i);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004453 if (!pAttribute->ConvertValue<T>(val_index, pAttribute->num_components(),
4454 values))
Alex Wood7319db72019-01-24 15:38:16 -05004455 return false;
4456
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004457 memcpy(outBuffer.data() + byteOffset, &values[0],
4458 sizeof(T) * pAttribute->num_components());
Alex Wood7319db72019-01-24 15:38:16 -05004459 byteOffset += sizeof(T) * pAttribute->num_components();
4460 }
4461
4462 return true;
4463}
4464
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004465static bool GetAttributeForAllPoints(uint32_t componentType, draco::Mesh *mesh,
4466 const draco::PointAttribute *pAttribute,
4467 std::vector<uint8_t> &outBuffer) {
Alex Wood7319db72019-01-24 15:38:16 -05004468 bool decodeResult = false;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004469 switch (componentType) {
4470 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
4471 decodeResult =
4472 GetAttributeForAllPoints<uint8_t>(mesh, pAttribute, outBuffer);
4473 break;
4474 case TINYGLTF_COMPONENT_TYPE_BYTE:
4475 decodeResult =
4476 GetAttributeForAllPoints<int8_t>(mesh, pAttribute, outBuffer);
4477 break;
4478 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
4479 decodeResult =
4480 GetAttributeForAllPoints<uint16_t>(mesh, pAttribute, outBuffer);
4481 break;
4482 case TINYGLTF_COMPONENT_TYPE_SHORT:
4483 decodeResult =
4484 GetAttributeForAllPoints<int16_t>(mesh, pAttribute, outBuffer);
4485 break;
4486 case TINYGLTF_COMPONENT_TYPE_INT:
4487 decodeResult =
4488 GetAttributeForAllPoints<int32_t>(mesh, pAttribute, outBuffer);
4489 break;
4490 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
4491 decodeResult =
4492 GetAttributeForAllPoints<uint32_t>(mesh, pAttribute, outBuffer);
4493 break;
4494 case TINYGLTF_COMPONENT_TYPE_FLOAT:
4495 decodeResult =
4496 GetAttributeForAllPoints<float>(mesh, pAttribute, outBuffer);
4497 break;
4498 case TINYGLTF_COMPONENT_TYPE_DOUBLE:
4499 decodeResult =
4500 GetAttributeForAllPoints<double>(mesh, pAttribute, outBuffer);
4501 break;
4502 default:
4503 return false;
Alex Wood7319db72019-01-24 15:38:16 -05004504 }
4505
4506 return decodeResult;
4507}
4508
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004509static bool ParseDracoExtension(Primitive *primitive, Model *model,
4510 std::string *err,
4511 const Value &dracoExtensionValue) {
snowapril84e15262021-03-20 19:25:58 +09004512 (void)err;
Alex Wood7319db72019-01-24 15:38:16 -05004513 auto bufferViewValue = dracoExtensionValue.Get("bufferView");
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004514 if (!bufferViewValue.IsInt()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004515 auto attributesValue = dracoExtensionValue.Get("attributes");
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004516 if (!attributesValue.IsObject()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004517
4518 auto attributesObject = attributesValue.Get<Value::Object>();
4519 int bufferView = bufferViewValue.Get<int>();
4520
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004521 BufferView &view = model->bufferViews[bufferView];
4522 Buffer &buffer = model->buffers[view.buffer];
Alex Wood7319db72019-01-24 15:38:16 -05004523 // BufferView has already been decoded
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004524 if (view.dracoDecoded) return true;
Alex Wood7319db72019-01-24 15:38:16 -05004525 view.dracoDecoded = true;
4526
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004527 const char *bufferViewData =
4528 reinterpret_cast<const char *>(buffer.data.data() + view.byteOffset);
Alex Wood7319db72019-01-24 15:38:16 -05004529 size_t bufferViewSize = view.byteLength;
4530
4531 // decode draco
4532 draco::DecoderBuffer decoderBuffer;
4533 decoderBuffer.Init(bufferViewData, bufferViewSize);
4534 draco::Decoder decoder;
4535 auto decodeResult = decoder.DecodeMeshFromBuffer(&decoderBuffer);
4536 if (!decodeResult.ok()) {
4537 return false;
4538 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004539 const std::unique_ptr<draco::Mesh> &mesh = decodeResult.value();
Alex Wood7319db72019-01-24 15:38:16 -05004540
4541 // create new bufferView for indices
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004542 if (primitive->indices >= 0) {
4543 int32_t componentSize = GetComponentSizeInBytes(
4544 model->accessors[primitive->indices].componentType);
Alex Wood7319db72019-01-24 15:38:16 -05004545 Buffer decodedIndexBuffer;
4546 decodedIndexBuffer.data.resize(mesh->num_faces() * 3 * componentSize);
4547
4548 DecodeIndexBuffer(mesh.get(), componentSize, decodedIndexBuffer.data);
4549
4550 model->buffers.emplace_back(std::move(decodedIndexBuffer));
4551
4552 BufferView decodedIndexBufferView;
4553 decodedIndexBufferView.buffer = int(model->buffers.size() - 1);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004554 decodedIndexBufferView.byteLength =
4555 int(mesh->num_faces() * 3 * componentSize);
Alex Wood7319db72019-01-24 15:38:16 -05004556 decodedIndexBufferView.byteOffset = 0;
4557 decodedIndexBufferView.byteStride = 0;
4558 decodedIndexBufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER;
4559 model->bufferViews.emplace_back(std::move(decodedIndexBufferView));
4560
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004561 model->accessors[primitive->indices].bufferView =
4562 int(model->bufferViews.size() - 1);
Alexander Wood0d77a292019-01-26 08:58:45 -05004563 model->accessors[primitive->indices].count = int(mesh->num_faces() * 3);
Alex Wood7319db72019-01-24 15:38:16 -05004564 }
4565
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004566 for (const auto &attribute : attributesObject) {
4567 if (!attribute.second.IsInt()) return false;
Alexander Wood0d77a292019-01-26 08:58:45 -05004568 auto primitiveAttribute = primitive->attributes.find(attribute.first);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004569 if (primitiveAttribute == primitive->attributes.end()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004570
4571 int dracoAttributeIndex = attribute.second.Get<int>();
4572 const auto pAttribute = mesh->GetAttributeByUniqueId(dracoAttributeIndex);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004573 const auto componentType =
4574 model->accessors[primitiveAttribute->second].componentType;
Alex Wood7319db72019-01-24 15:38:16 -05004575
4576 // Create a new buffer for this decoded buffer
4577 Buffer decodedBuffer;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004578 size_t bufferSize = mesh->num_points() * pAttribute->num_components() *
4579 GetComponentSizeInBytes(componentType);
Alex Wood7319db72019-01-24 15:38:16 -05004580 decodedBuffer.data.resize(bufferSize);
4581
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004582 if (!GetAttributeForAllPoints(componentType, mesh.get(), pAttribute,
4583 decodedBuffer.data))
Alex Wood7319db72019-01-24 15:38:16 -05004584 return false;
4585
4586 model->buffers.emplace_back(std::move(decodedBuffer));
4587
4588 BufferView decodedBufferView;
4589 decodedBufferView.buffer = int(model->buffers.size() - 1);
4590 decodedBufferView.byteLength = bufferSize;
4591 decodedBufferView.byteOffset = pAttribute->byte_offset();
4592 decodedBufferView.byteStride = pAttribute->byte_stride();
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004593 decodedBufferView.target = primitive->indices >= 0
4594 ? TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER
4595 : TINYGLTF_TARGET_ARRAY_BUFFER;
Alex Wood7319db72019-01-24 15:38:16 -05004596 model->bufferViews.emplace_back(std::move(decodedBufferView));
4597
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004598 model->accessors[primitiveAttribute->second].bufferView =
4599 int(model->bufferViews.size() - 1);
4600 model->accessors[primitiveAttribute->second].count =
4601 int(mesh->num_points());
Alex Wood7319db72019-01-24 15:38:16 -05004602 }
4603
4604 return true;
4605}
4606#endif
4607
4608static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004609 const json &o,
4610 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004611 int material = -1;
4612 ParseIntegerProperty(&material, err, o, "material", false);
4613 primitive->material = material;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004614
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004615 int mode = TINYGLTF_MODE_TRIANGLES;
4616 ParseIntegerProperty(&mode, err, o, "mode", false);
4617 primitive->mode = mode; // Why only triangled were supported ?
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004618
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004619 int indices = -1;
4620 ParseIntegerProperty(&indices, err, o, "indices", false);
4621 primitive->indices = indices;
4622 if (!ParseStringIntegerProperty(&primitive->attributes, err, o, "attributes",
4623 true, "Primitive")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004624 return false;
4625 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004626
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004627 // Look for morph targets
jrkooncecba5d6c2019-08-29 11:26:22 -05004628 json_const_iterator targetsObject;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004629 if (FindMember(o, "targets", targetsObject) &&
4630 IsArray(GetValue(targetsObject))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004631 auto targetsObjectEnd = ArrayEnd(GetValue(targetsObject));
4632 for (json_const_array_iterator i = ArrayBegin(GetValue(targetsObject));
4633 i != targetsObjectEnd; ++i) {
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004634 std::map<std::string, int> targetAttribues;
4635
jrkooncecba5d6c2019-08-29 11:26:22 -05004636 const json &dict = *i;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004637 if (IsObject(dict)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004638 json_const_iterator dictIt(ObjectBegin(dict));
4639 json_const_iterator dictItEnd(ObjectEnd(dict));
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004640
jrkooncecba5d6c2019-08-29 11:26:22 -05004641 for (; dictIt != dictItEnd; ++dictIt) {
4642 int iVal;
4643 if (GetInt(GetValue(dictIt), iVal))
4644 targetAttribues[GetKey(dictIt)] = iVal;
4645 }
4646 primitive->targets.emplace_back(std::move(targetAttribues));
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004647 }
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004648 }
4649 }
4650
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004651 ParseExtrasProperty(&(primitive->extras), o);
Alex Wood7319db72019-01-24 15:38:16 -05004652 ParseExtensionsProperty(&primitive->extensions, err, o);
4653
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004654 if (store_original_json_for_extras_and_extensions) {
4655 {
4656 json_const_iterator it;
4657 if (FindMember(o, "extensions", it)) {
4658 primitive->extensions_json_string = JsonToString(GetValue(it));
4659 }
4660 }
4661 {
4662 json_const_iterator it;
4663 if (FindMember(o, "extras", it)) {
4664 primitive->extras_json_string = JsonToString(GetValue(it));
4665 }
4666 }
4667 }
4668
Alex Wood7319db72019-01-24 15:38:16 -05004669#ifdef TINYGLTF_ENABLE_DRACO
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004670 auto dracoExtension =
4671 primitive->extensions.find("KHR_draco_mesh_compression");
4672 if (dracoExtension != primitive->extensions.end()) {
4673 ParseDracoExtension(primitive, model, err, dracoExtension->second);
Alex Wood7319db72019-01-24 15:38:16 -05004674 }
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09004675#else
4676 (void)model;
Alex Wood7319db72019-01-24 15:38:16 -05004677#endif
4678
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004679 return true;
4680}
4681
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004682static bool ParseMesh(Mesh *mesh, Model *model, std::string *err, const json &o,
4683 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004684 ParseStringProperty(&mesh->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004685
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004686 mesh->primitives.clear();
jrkooncecba5d6c2019-08-29 11:26:22 -05004687 json_const_iterator primObject;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004688 if (FindMember(o, "primitives", primObject) &&
4689 IsArray(GetValue(primObject))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004690 json_const_array_iterator primEnd = ArrayEnd(GetValue(primObject));
4691 for (json_const_array_iterator i = ArrayBegin(GetValue(primObject));
4692 i != primEnd; ++i) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004693 Primitive primitive;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004694 if (ParsePrimitive(&primitive, model, err, *i,
4695 store_original_json_for_extras_and_extensions)) {
Luke San Antonio10a3e1d2016-06-15 00:05:01 -04004696 // Only add the primitive if the parsing succeeds.
jrkoonced1e14722019-08-27 11:51:02 -05004697 mesh->primitives.emplace_back(std::move(primitive));
Luke San Antonio10a3e1d2016-06-15 00:05:01 -04004698 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004699 }
4700 }
4701
Aurélien Chatelain38a6a752017-05-24 09:54:55 +00004702 // Should probably check if has targets and if dimensions fit
4703 ParseNumberArrayProperty(&mesh->weights, err, o, "weights", false);
4704
Selmar09d2ff12018-03-15 17:30:42 +01004705 ParseExtensionsProperty(&mesh->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004706 ParseExtrasProperty(&(mesh->extras), o);
4707
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004708 if (store_original_json_for_extras_and_extensions) {
4709 {
4710 json_const_iterator it;
4711 if (FindMember(o, "extensions", it)) {
4712 mesh->extensions_json_string = JsonToString(GetValue(it));
4713 }
4714 }
4715 {
4716 json_const_iterator it;
4717 if (FindMember(o, "extras", it)) {
4718 mesh->extras_json_string = JsonToString(GetValue(it));
4719 }
4720 }
4721 }
4722
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004723 return true;
4724}
4725
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004726static bool ParseNode(Node *node, std::string *err, const json &o,
4727 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004728 ParseStringProperty(&node->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004729
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004730 int skin = -1;
4731 ParseIntegerProperty(&skin, err, o, "skin", false);
4732 node->skin = skin;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00004733
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004734 // Matrix and T/R/S are exclusive
Syoyo Fujita5b407452017-06-04 17:42:41 +09004735 if (!ParseNumberArrayProperty(&node->matrix, err, o, "matrix", false)) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004736 ParseNumberArrayProperty(&node->rotation, err, o, "rotation", false);
4737 ParseNumberArrayProperty(&node->scale, err, o, "scale", false);
4738 ParseNumberArrayProperty(&node->translation, err, o, "translation", false);
4739 }
4740
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004741 int camera = -1;
4742 ParseIntegerProperty(&camera, err, o, "camera", false);
4743 node->camera = camera;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004744
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004745 int mesh = -1;
4746 ParseIntegerProperty(&mesh, err, o, "mesh", false);
4747 node->mesh = mesh;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004748
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004749 node->children.clear();
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004750 ParseIntegerArrayProperty(&node->children, err, o, "children", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004751
Benjamin Schmithüsenad63bf72019-08-16 14:19:27 +02004752 ParseNumberArrayProperty(&node->weights, err, o, "weights", false);
4753
Selmar09d2ff12018-03-15 17:30:42 +01004754 ParseExtensionsProperty(&node->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004755 ParseExtrasProperty(&(node->extras), o);
4756
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004757 if (store_original_json_for_extras_and_extensions) {
4758 {
4759 json_const_iterator it;
4760 if (FindMember(o, "extensions", it)) {
4761 node->extensions_json_string = JsonToString(GetValue(it));
4762 }
4763 }
4764 {
4765 json_const_iterator it;
4766 if (FindMember(o, "extras", it)) {
4767 node->extras_json_string = JsonToString(GetValue(it));
4768 }
4769 }
4770 }
4771
Emanuel Schrade186322b2017-11-06 11:14:41 +01004772 return true;
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004773}
4774
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004775static bool ParsePbrMetallicRoughness(
4776 PbrMetallicRoughness *pbr, std::string *err, const json &o,
4777 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004778 if (pbr == nullptr) {
4779 return false;
4780 }
4781
4782 std::vector<double> baseColorFactor;
4783 if (ParseNumberArrayProperty(&baseColorFactor, err, o, "baseColorFactor",
4784 /* required */ false)) {
4785 if (baseColorFactor.size() != 4) {
4786 if (err) {
4787 (*err) +=
4788 "Array length of `baseColorFactor` parameter in "
4789 "pbrMetallicRoughness must be 4, but got " +
4790 std::to_string(baseColorFactor.size()) + "\n";
4791 }
4792 return false;
4793 }
Selmar Kokc3353e12019-10-18 18:22:35 +02004794 pbr->baseColorFactor = baseColorFactor;
Syoyo Fujita046400b2019-07-24 19:26:48 +09004795 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09004796
4797 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004798 json_const_iterator it;
4799 if (FindMember(o, "baseColorTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004800 ParseTextureInfo(&pbr->baseColorTexture, err, GetValue(it),
4801 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004802 }
4803 }
4804
4805 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004806 json_const_iterator it;
4807 if (FindMember(o, "metallicRoughnessTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004808 ParseTextureInfo(&pbr->metallicRoughnessTexture, err, GetValue(it),
4809 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004810 }
4811 }
4812
4813 ParseNumberProperty(&pbr->metallicFactor, err, o, "metallicFactor", false);
4814 ParseNumberProperty(&pbr->roughnessFactor, err, o, "roughnessFactor", false);
4815
4816 ParseExtensionsProperty(&pbr->extensions, err, o);
4817 ParseExtrasProperty(&pbr->extras, o);
4818
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004819 if (store_original_json_for_extras_and_extensions) {
4820 {
4821 json_const_iterator it;
4822 if (FindMember(o, "extensions", it)) {
4823 pbr->extensions_json_string = JsonToString(GetValue(it));
4824 }
4825 }
4826 {
4827 json_const_iterator it;
4828 if (FindMember(o, "extras", it)) {
4829 pbr->extras_json_string = JsonToString(GetValue(it));
4830 }
4831 }
4832 }
4833
Syoyo Fujita046400b2019-07-24 19:26:48 +09004834 return true;
4835}
4836
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004837static bool ParseMaterial(Material *material, std::string *err, const json &o,
4838 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004839 ParseStringProperty(&material->name, err, o, "name", /* required */ false);
4840
Syoyo Fujitaff515702019-08-24 16:29:14 +09004841 if (ParseNumberArrayProperty(&material->emissiveFactor, err, o,
4842 "emissiveFactor",
4843 /* required */ false)) {
Selmar Kok6df800d2019-08-19 11:05:28 +02004844 if (material->emissiveFactor.size() != 3) {
4845 if (err) {
4846 (*err) +=
4847 "Array length of `emissiveFactor` parameter in "
4848 "material must be 3, but got " +
4849 std::to_string(material->emissiveFactor.size()) + "\n";
4850 }
4851 return false;
4852 }
Syoyo Fujitaff515702019-08-24 16:29:14 +09004853 } else {
Selmar Kok6df800d2019-08-19 11:05:28 +02004854 // fill with default values
Selmar Kok6dba6c62019-08-19 11:23:31 +02004855 material->emissiveFactor = {0.0, 0.0, 0.0};
Selmar Kok6df800d2019-08-19 11:05:28 +02004856 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09004857
4858 ParseStringProperty(&material->alphaMode, err, o, "alphaMode",
4859 /* required */ false);
4860 ParseNumberProperty(&material->alphaCutoff, err, o, "alphaCutoff",
4861 /* required */ false);
4862 ParseBooleanProperty(&material->doubleSided, err, o, "doubleSided",
4863 /* required */ false);
4864
4865 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004866 json_const_iterator it;
4867 if (FindMember(o, "pbrMetallicRoughness", it)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004868 ParsePbrMetallicRoughness(&material->pbrMetallicRoughness, err,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004869 GetValue(it),
4870 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004871 }
4872 }
4873
4874 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004875 json_const_iterator it;
4876 if (FindMember(o, "normalTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004877 ParseNormalTextureInfo(&material->normalTexture, err, GetValue(it),
4878 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004879 }
4880 }
4881
4882 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004883 json_const_iterator it;
4884 if (FindMember(o, "occlusionTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004885 ParseOcclusionTextureInfo(&material->occlusionTexture, err, GetValue(it),
4886 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004887 }
4888 }
4889
4890 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004891 json_const_iterator it;
4892 if (FindMember(o, "emissiveTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004893 ParseTextureInfo(&material->emissiveTexture, err, GetValue(it),
4894 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004895 }
4896 }
4897
4898 // Old code path. For backward compatibility, we still store material values
4899 // as Parameter. This will create duplicated information for
4900 // example(pbrMetallicRoughness), but should be neglible in terms of memory
4901 // consumption.
4902 // TODO(syoyo): Remove in the next major release.
4903 material->values.clear();
4904 material->additionalValues.clear();
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004905
jrkooncecba5d6c2019-08-29 11:26:22 -05004906 json_const_iterator it(ObjectBegin(o));
4907 json_const_iterator itEnd(ObjectEnd(o));
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004908
jrkooncecba5d6c2019-08-29 11:26:22 -05004909 for (; it != itEnd; ++it) {
4910 std::string key(GetKey(it));
jrkoonce06c30c42019-09-03 15:56:48 -05004911 if (key == "pbrMetallicRoughness") {
4912 if (IsObject(GetValue(it))) {
4913 const json &values_object = GetValue(it);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004914
jrkoonce06c30c42019-09-03 15:56:48 -05004915 json_const_iterator itVal(ObjectBegin(values_object));
4916 json_const_iterator itValEnd(ObjectEnd(values_object));
4917
4918 for (; itVal != itValEnd; ++itVal) {
4919 Parameter param;
4920 if (ParseParameterProperty(&param, err, values_object, GetKey(itVal),
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004921 false)) {
jrkoonce06c30c42019-09-03 15:56:48 -05004922 material->values.emplace(GetKey(itVal), std::move(param));
4923 }
4924 }
4925 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004926 } else if (key == "extensions" || key == "extras") {
4927 // done later, skip, otherwise poorly parsed contents will be saved in the
4928 // parametermap and serialized again later
4929 } else {
jrkoonce06c30c42019-09-03 15:56:48 -05004930 Parameter param;
4931 if (ParseParameterProperty(&param, err, o, key, false)) {
4932 // names of materials have already been parsed. Putting it in this map
4933 // doesn't correctly reflext the glTF specification
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004934 if (key != "name")
4935 material->additionalValues.emplace(std::move(key), std::move(param));
jrkoonce06c30c42019-09-03 15:56:48 -05004936 }
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00004937 }
Syoyo Fujita5b407452017-06-04 17:42:41 +09004938 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004939
Syoyo Fujita046400b2019-07-24 19:26:48 +09004940 material->extensions.clear();
Selmar09d2ff12018-03-15 17:30:42 +01004941 ParseExtensionsProperty(&material->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004942 ParseExtrasProperty(&(material->extras), o);
4943
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004944 if (store_original_json_for_extras_and_extensions) {
4945 {
4946 json_const_iterator eit;
4947 if (FindMember(o, "extensions", eit)) {
4948 material->extensions_json_string = JsonToString(GetValue(eit));
4949 }
4950 }
4951 {
4952 json_const_iterator eit;
4953 if (FindMember(o, "extras", eit)) {
4954 material->extras_json_string = JsonToString(GetValue(eit));
4955 }
4956 }
4957 }
4958
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004959 return true;
4960}
4961
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004962static bool ParseAnimationChannel(
4963 AnimationChannel *channel, std::string *err, const json &o,
4964 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004965 int samplerIndex = -1;
4966 int targetIndex = -1;
4967 if (!ParseIntegerProperty(&samplerIndex, err, o, "sampler", true,
4968 "AnimationChannel")) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004969 if (err) {
4970 (*err) += "`sampler` field is missing in animation channels\n";
4971 }
4972 return false;
4973 }
4974
jrkooncecba5d6c2019-08-29 11:26:22 -05004975 json_const_iterator targetIt;
4976 if (FindMember(o, "target", targetIt) && IsObject(GetValue(targetIt))) {
4977 const json &target_object = GetValue(targetIt);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004978
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004979 if (!ParseIntegerProperty(&targetIndex, err, target_object, "node", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004980 if (err) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09004981 (*err) += "`node` field is missing in animation.channels.target\n";
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004982 }
4983 return false;
4984 }
4985
4986 if (!ParseStringProperty(&channel->target_path, err, target_object, "path",
4987 true)) {
4988 if (err) {
4989 (*err) += "`path` field is missing in animation.channels.target\n";
4990 }
4991 return false;
4992 }
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09004993 ParseExtensionsProperty(&channel->target_extensions, err, target_object);
4994 if (store_original_json_for_extras_and_extensions) {
Selmar Kok973d9b32020-01-21 18:45:24 +01004995 json_const_iterator it;
4996 if (FindMember(target_object, "extensions", it)) {
4997 channel->target_extensions_json_string = JsonToString(GetValue(it));
4998 }
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09004999 }
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005000 }
5001
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005002 channel->sampler = samplerIndex;
5003 channel->target_node = targetIndex;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005004
Selmar Kok4e2988e2019-08-16 14:08:08 +02005005 ParseExtensionsProperty(&channel->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09005006 ParseExtrasProperty(&(channel->extras), o);
5007
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005008 if (store_original_json_for_extras_and_extensions) {
5009 {
5010 json_const_iterator it;
5011 if (FindMember(o, "extensions", it)) {
5012 channel->extensions_json_string = JsonToString(GetValue(it));
5013 }
5014 }
5015 {
5016 json_const_iterator it;
5017 if (FindMember(o, "extras", it)) {
5018 channel->extras_json_string = JsonToString(GetValue(it));
5019 }
5020 }
5021 }
5022
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005023 return true;
5024}
5025
5026static bool ParseAnimation(Animation *animation, std::string *err,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005027 const json &o,
5028 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005029 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005030 json_const_iterator channelsIt;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005031 if (FindMember(o, "channels", channelsIt) &&
5032 IsArray(GetValue(channelsIt))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005033 json_const_array_iterator channelEnd = ArrayEnd(GetValue(channelsIt));
5034 for (json_const_array_iterator i = ArrayBegin(GetValue(channelsIt));
5035 i != channelEnd; ++i) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005036 AnimationChannel channel;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005037 if (ParseAnimationChannel(
5038 &channel, err, *i,
5039 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005040 // Only add the channel if the parsing succeeds.
jrkooncecba5d6c2019-08-29 11:26:22 -05005041 animation->channels.emplace_back(std::move(channel));
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005042 }
5043 }
5044 }
5045 }
5046
5047 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005048 json_const_iterator samplerIt;
5049 if (FindMember(o, "samplers", samplerIt) && IsArray(GetValue(samplerIt))) {
5050 const json &sampler_array = GetValue(samplerIt);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005051
jrkooncecba5d6c2019-08-29 11:26:22 -05005052 json_const_array_iterator it = ArrayBegin(sampler_array);
5053 json_const_array_iterator itEnd = ArrayEnd(sampler_array);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005054
jrkooncecba5d6c2019-08-29 11:26:22 -05005055 for (; it != itEnd; ++it) {
5056 const json &s = *it;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005057
5058 AnimationSampler sampler;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005059 int inputIndex = -1;
5060 int outputIndex = -1;
5061 if (!ParseIntegerProperty(&inputIndex, err, s, "input", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005062 if (err) {
5063 (*err) += "`input` field is missing in animation.sampler\n";
5064 }
5065 return false;
5066 }
Evan Birenbaum6bdffed2019-02-14 13:30:57 -08005067 ParseStringProperty(&sampler.interpolation, err, s, "interpolation",
5068 false);
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005069 if (!ParseIntegerProperty(&outputIndex, err, s, "output", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005070 if (err) {
5071 (*err) += "`output` field is missing in animation.sampler\n";
5072 }
5073 return false;
5074 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005075 sampler.input = inputIndex;
5076 sampler.output = outputIndex;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005077 ParseExtensionsProperty(&(sampler.extensions), err, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02005078 ParseExtrasProperty(&(sampler.extras), s);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005079
5080 if (store_original_json_for_extras_and_extensions) {
5081 {
5082 json_const_iterator eit;
5083 if (FindMember(o, "extensions", eit)) {
5084 sampler.extensions_json_string = JsonToString(GetValue(eit));
5085 }
5086 }
5087 {
5088 json_const_iterator eit;
5089 if (FindMember(o, "extras", eit)) {
5090 sampler.extras_json_string = JsonToString(GetValue(eit));
5091 }
5092 }
5093 }
5094
jrkooncecba5d6c2019-08-29 11:26:22 -05005095 animation->samplers.emplace_back(std::move(sampler));
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005096 }
5097 }
5098 }
5099
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005100 ParseStringProperty(&animation->name, err, o, "name", false);
5101
Selmar Kok4e2988e2019-08-16 14:08:08 +02005102 ParseExtensionsProperty(&animation->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09005103 ParseExtrasProperty(&(animation->extras), o);
5104
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005105 if (store_original_json_for_extras_and_extensions) {
5106 {
5107 json_const_iterator it;
5108 if (FindMember(o, "extensions", it)) {
5109 animation->extensions_json_string = JsonToString(GetValue(it));
5110 }
5111 }
5112 {
5113 json_const_iterator it;
5114 if (FindMember(o, "extras", it)) {
5115 animation->extras_json_string = JsonToString(GetValue(it));
5116 }
5117 }
5118 }
5119
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005120 return true;
5121}
5122
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005123static bool ParseSampler(Sampler *sampler, std::string *err, const json &o,
5124 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujitac2615632016-06-19 21:56:06 +09005125 ParseStringProperty(&sampler->name, err, o, "name", false);
5126
Syoyo Fujitaee179b22019-08-16 13:11:30 +09005127 int minFilter = -1;
5128 int magFilter = -1;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005129 int wrapS = TINYGLTF_TEXTURE_WRAP_REPEAT;
5130 int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT;
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09005131 // int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005132 ParseIntegerProperty(&minFilter, err, o, "minFilter", false);
5133 ParseIntegerProperty(&magFilter, err, o, "magFilter", false);
5134 ParseIntegerProperty(&wrapS, err, o, "wrapS", false);
5135 ParseIntegerProperty(&wrapT, err, o, "wrapT", false);
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09005136 // ParseIntegerProperty(&wrapR, err, o, "wrapR", false); // tinygltf
5137 // extension
Syoyo Fujitaee179b22019-08-16 13:11:30 +09005138
5139 // TODO(syoyo): Check the value is alloed one.
5140 // (e.g. we allow 9728(NEAREST), but don't allow 9727)
Syoyo Fujitac2615632016-06-19 21:56:06 +09005141
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005142 sampler->minFilter = minFilter;
5143 sampler->magFilter = magFilter;
5144 sampler->wrapS = wrapS;
5145 sampler->wrapT = wrapT;
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09005146 // sampler->wrapR = wrapR;
Syoyo Fujitac2615632016-06-19 21:56:06 +09005147
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005148 ParseExtensionsProperty(&(sampler->extensions), err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09005149 ParseExtrasProperty(&(sampler->extras), o);
5150
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005151 if (store_original_json_for_extras_and_extensions) {
5152 {
5153 json_const_iterator it;
5154 if (FindMember(o, "extensions", it)) {
5155 sampler->extensions_json_string = JsonToString(GetValue(it));
5156 }
5157 }
5158 {
5159 json_const_iterator it;
5160 if (FindMember(o, "extras", it)) {
5161 sampler->extras_json_string = JsonToString(GetValue(it));
5162 }
5163 }
5164 }
5165
Syoyo Fujitac2615632016-06-19 21:56:06 +09005166 return true;
5167}
5168
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005169static bool ParseSkin(Skin *skin, std::string *err, const json &o,
5170 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09005171 ParseStringProperty(&skin->name, err, o, "name", false, "Skin");
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005172
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005173 std::vector<int> joints;
5174 if (!ParseIntegerArrayProperty(&joints, err, o, "joints", false, "Skin")) {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005175 return false;
5176 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005177 skin->joints = std::move(joints);
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005178
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005179 int skeleton = -1;
5180 ParseIntegerProperty(&skeleton, err, o, "skeleton", false, "Skin");
5181 skin->skeleton = skeleton;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005182
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005183 int invBind = -1;
5184 ParseIntegerProperty(&invBind, err, o, "inverseBindMatrices", true, "Skin");
5185 skin->inverseBindMatrices = invBind;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005186
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005187 ParseExtensionsProperty(&(skin->extensions), err, o);
5188 ParseExtrasProperty(&(skin->extras), o);
5189
5190 if (store_original_json_for_extras_and_extensions) {
5191 {
5192 json_const_iterator it;
5193 if (FindMember(o, "extensions", it)) {
5194 skin->extensions_json_string = JsonToString(GetValue(it));
5195 }
5196 }
5197 {
5198 json_const_iterator it;
5199 if (FindMember(o, "extras", it)) {
5200 skin->extras_json_string = JsonToString(GetValue(it));
5201 }
5202 }
5203 }
5204
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005205 return true;
5206}
5207
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005208static bool ParsePerspectiveCamera(
5209 PerspectiveCamera *camera, std::string *err, const json &o,
5210 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005211 double yfov = 0.0;
5212 if (!ParseNumberProperty(&yfov, err, o, "yfov", true, "OrthographicCamera")) {
5213 return false;
5214 }
5215
5216 double znear = 0.0;
5217 if (!ParseNumberProperty(&znear, err, o, "znear", true,
5218 "PerspectiveCamera")) {
5219 return false;
5220 }
5221
5222 double aspectRatio = 0.0; // = invalid
5223 ParseNumberProperty(&aspectRatio, err, o, "aspectRatio", false,
5224 "PerspectiveCamera");
5225
5226 double zfar = 0.0; // = invalid
5227 ParseNumberProperty(&zfar, err, o, "zfar", false, "PerspectiveCamera");
5228
Selmar Kok31cb7f92018-10-03 15:39:05 +02005229 camera->aspectRatio = aspectRatio;
5230 camera->zfar = zfar;
5231 camera->yfov = yfov;
5232 camera->znear = znear;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005233
Selmar09d2ff12018-03-15 17:30:42 +01005234 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005235 ParseExtrasProperty(&(camera->extras), o);
5236
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005237 if (store_original_json_for_extras_and_extensions) {
5238 {
5239 json_const_iterator it;
5240 if (FindMember(o, "extensions", it)) {
5241 camera->extensions_json_string = JsonToString(GetValue(it));
5242 }
5243 }
5244 {
5245 json_const_iterator it;
5246 if (FindMember(o, "extras", it)) {
5247 camera->extras_json_string = JsonToString(GetValue(it));
5248 }
5249 }
5250 }
5251
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005252 // TODO(syoyo): Validate parameter values.
5253
5254 return true;
5255}
5256
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005257static bool ParseSpotLight(SpotLight *light, std::string *err, const json &o,
5258 bool store_original_json_for_extras_and_extensions) {
Johan Bowald52936a02019-07-17 09:06:45 +02005259 ParseNumberProperty(&light->innerConeAngle, err, o, "innerConeAngle", false);
5260 ParseNumberProperty(&light->outerConeAngle, err, o, "outerConeAngle", false);
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005261
Johan Bowald52936a02019-07-17 09:06:45 +02005262 ParseExtensionsProperty(&light->extensions, err, o);
5263 ParseExtrasProperty(&light->extras, o);
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005264
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005265 if (store_original_json_for_extras_and_extensions) {
5266 {
5267 json_const_iterator it;
5268 if (FindMember(o, "extensions", it)) {
5269 light->extensions_json_string = JsonToString(GetValue(it));
5270 }
5271 }
5272 {
5273 json_const_iterator it;
5274 if (FindMember(o, "extras", it)) {
5275 light->extras_json_string = JsonToString(GetValue(it));
5276 }
5277 }
5278 }
5279
Johan Bowald52936a02019-07-17 09:06:45 +02005280 // TODO(syoyo): Validate parameter values.
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005281
Johan Bowald52936a02019-07-17 09:06:45 +02005282 return true;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005283}
5284
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005285static bool ParseOrthographicCamera(
5286 OrthographicCamera *camera, std::string *err, const json &o,
5287 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005288 double xmag = 0.0;
5289 if (!ParseNumberProperty(&xmag, err, o, "xmag", true, "OrthographicCamera")) {
5290 return false;
5291 }
5292
5293 double ymag = 0.0;
5294 if (!ParseNumberProperty(&ymag, err, o, "ymag", true, "OrthographicCamera")) {
5295 return false;
5296 }
5297
5298 double zfar = 0.0;
5299 if (!ParseNumberProperty(&zfar, err, o, "zfar", true, "OrthographicCamera")) {
5300 return false;
5301 }
5302
5303 double znear = 0.0;
5304 if (!ParseNumberProperty(&znear, err, o, "znear", true,
5305 "OrthographicCamera")) {
5306 return false;
5307 }
5308
Selmar09d2ff12018-03-15 17:30:42 +01005309 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005310 ParseExtrasProperty(&(camera->extras), o);
5311
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005312 if (store_original_json_for_extras_and_extensions) {
5313 {
5314 json_const_iterator it;
5315 if (FindMember(o, "extensions", it)) {
5316 camera->extensions_json_string = JsonToString(GetValue(it));
5317 }
5318 }
5319 {
5320 json_const_iterator it;
5321 if (FindMember(o, "extras", it)) {
5322 camera->extras_json_string = JsonToString(GetValue(it));
5323 }
5324 }
5325 }
5326
Selmar Kok31cb7f92018-10-03 15:39:05 +02005327 camera->xmag = xmag;
5328 camera->ymag = ymag;
5329 camera->zfar = zfar;
5330 camera->znear = znear;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005331
5332 // TODO(syoyo): Validate parameter values.
5333
5334 return true;
5335}
5336
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005337static bool ParseCamera(Camera *camera, std::string *err, const json &o,
5338 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005339 if (!ParseStringProperty(&camera->type, err, o, "type", true, "Camera")) {
5340 return false;
5341 }
5342
5343 if (camera->type.compare("orthographic") == 0) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005344 json_const_iterator orthoIt;
5345 if (!FindMember(o, "orthographic", orthoIt)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005346 if (err) {
5347 std::stringstream ss;
5348 ss << "Orhographic camera description not found." << std::endl;
5349 (*err) += ss.str();
5350 }
5351 return false;
5352 }
5353
jrkooncecba5d6c2019-08-29 11:26:22 -05005354 const json &v = GetValue(orthoIt);
5355 if (!IsObject(v)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005356 if (err) {
5357 std::stringstream ss;
5358 ss << "\"orthographic\" is not a JSON object." << std::endl;
5359 (*err) += ss.str();
5360 }
5361 return false;
5362 }
5363
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005364 if (!ParseOrthographicCamera(
5365 &camera->orthographic, err, v,
5366 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005367 return false;
5368 }
5369 } else if (camera->type.compare("perspective") == 0) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005370 json_const_iterator perspIt;
5371 if (!FindMember(o, "perspective", perspIt)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005372 if (err) {
5373 std::stringstream ss;
5374 ss << "Perspective camera description not found." << std::endl;
5375 (*err) += ss.str();
5376 }
5377 return false;
5378 }
5379
jrkooncecba5d6c2019-08-29 11:26:22 -05005380 const json &v = GetValue(perspIt);
5381 if (!IsObject(v)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005382 if (err) {
5383 std::stringstream ss;
5384 ss << "\"perspective\" is not a JSON object." << std::endl;
5385 (*err) += ss.str();
5386 }
5387 return false;
5388 }
5389
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005390 if (!ParsePerspectiveCamera(
5391 &camera->perspective, err, v,
5392 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005393 return false;
5394 }
5395 } else {
5396 if (err) {
5397 std::stringstream ss;
5398 ss << "Invalid camera type: \"" << camera->type
5399 << "\". Must be \"perspective\" or \"orthographic\"" << std::endl;
5400 (*err) += ss.str();
5401 }
5402 return false;
5403 }
5404
5405 ParseStringProperty(&camera->name, err, o, "name", false);
5406
Selmar09d2ff12018-03-15 17:30:42 +01005407 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005408 ParseExtrasProperty(&(camera->extras), o);
5409
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005410 if (store_original_json_for_extras_and_extensions) {
5411 {
5412 json_const_iterator it;
5413 if (FindMember(o, "extensions", it)) {
5414 camera->extensions_json_string = JsonToString(GetValue(it));
5415 }
5416 }
5417 {
5418 json_const_iterator it;
5419 if (FindMember(o, "extras", it)) {
5420 camera->extras_json_string = JsonToString(GetValue(it));
5421 }
5422 }
5423 }
5424
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005425 return true;
5426}
5427
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005428static bool ParseLight(Light *light, std::string *err, const json &o,
5429 bool store_original_json_for_extras_and_extensions) {
Johan Bowald52936a02019-07-17 09:06:45 +02005430 if (!ParseStringProperty(&light->type, err, o, "type", true)) {
5431 return false;
5432 }
5433
5434 if (light->type == "spot") {
jrkooncecba5d6c2019-08-29 11:26:22 -05005435 json_const_iterator spotIt;
5436 if (!FindMember(o, "spot", spotIt)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005437 if (err) {
5438 std::stringstream ss;
5439 ss << "Spot light description not found." << std::endl;
5440 (*err) += ss.str();
5441 }
5442 return false;
Benjamin Schmithüsen4557b6a2019-07-09 16:55:55 +02005443 }
5444
jrkooncecba5d6c2019-08-29 11:26:22 -05005445 const json &v = GetValue(spotIt);
5446 if (!IsObject(v)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005447 if (err) {
5448 std::stringstream ss;
5449 ss << "\"spot\" is not a JSON object." << std::endl;
5450 (*err) += ss.str();
5451 }
5452 return false;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005453 }
5454
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005455 if (!ParseSpotLight(&light->spot, err, v,
5456 store_original_json_for_extras_and_extensions)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005457 return false;
5458 }
5459 }
5460
5461 ParseStringProperty(&light->name, err, o, "name", false);
5462 ParseNumberArrayProperty(&light->color, err, o, "color", false);
5463 ParseNumberProperty(&light->range, err, o, "range", false);
5464 ParseNumberProperty(&light->intensity, err, o, "intensity", false);
5465 ParseExtensionsProperty(&light->extensions, err, o);
5466 ParseExtrasProperty(&(light->extras), o);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005467
5468 if (store_original_json_for_extras_and_extensions) {
5469 {
5470 json_const_iterator it;
5471 if (FindMember(o, "extensions", it)) {
5472 light->extensions_json_string = JsonToString(GetValue(it));
5473 }
5474 }
5475 {
5476 json_const_iterator it;
5477 if (FindMember(o, "extras", it)) {
5478 light->extras_json_string = JsonToString(GetValue(it));
5479 }
5480 }
5481 }
5482
Johan Bowald52936a02019-07-17 09:06:45 +02005483 return true;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005484}
5485
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09005486bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005487 const char *json_str,
5488 unsigned int json_str_length,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09005489 const std::string &base_dir,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005490 unsigned int check_sections) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005491 if (json_str_length < 4) {
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09005492 if (err) {
5493 (*err) = "JSON string too short.\n";
5494 }
5495 return false;
5496 }
5497
jrkooncecba5d6c2019-08-29 11:26:22 -05005498 JsonDocument v;
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005499
Syoyo Fujitad42767e2018-03-15 21:52:00 -05005500#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
5501 defined(_CPPUNWIND)) && \
Syoyo Fujitac4166e42020-01-08 02:38:01 +09005502 !defined(TINYGLTF_NOEXCEPTION)
Syoyo Fujitacdf84902017-08-01 20:12:08 +09005503 try {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005504 JsonParse(v, json_str, json_str_length, true);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005505
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005506 } catch (const std::exception &e) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005507 if (err) {
Syoyo Fujitacdf84902017-08-01 20:12:08 +09005508 (*err) = e.what();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005509 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005510 return false;
5511 }
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005512#else
5513 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005514 JsonParse(v, json_str, json_str_length);
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005515
jrkooncecba5d6c2019-08-29 11:26:22 -05005516 if (!IsObject(v)) {
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005517 // Assume parsing was failed.
5518 if (err) {
5519 (*err) = "Failed to parse JSON object\n";
5520 }
5521 return false;
5522 }
5523 }
5524#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005525
jrkooncecba5d6c2019-08-29 11:26:22 -05005526 if (!IsObject(v)) {
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09005527 // root is not an object.
5528 if (err) {
5529 (*err) = "Root element is not a JSON object\n";
5530 }
5531 return false;
5532 }
5533
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02005534 {
5535 bool version_found = false;
jrkooncecba5d6c2019-08-29 11:26:22 -05005536 json_const_iterator it;
5537 if (FindMember(v, "asset", it) && IsObject(GetValue(it))) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005538 auto &itObj = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05005539 json_const_iterator version_it;
5540 std::string versionStr;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005541 if (FindMember(itObj, "version", version_it) &&
5542 GetString(GetValue(version_it), versionStr)) {
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02005543 version_found = true;
5544 }
5545 }
5546 if (version_found) {
5547 // OK
5548 } else if (check_sections & REQUIRE_VERSION) {
5549 if (err) {
5550 (*err) += "\"asset\" object not found in .gltf or not an object type\n";
5551 }
5552 return false;
5553 }
5554 }
5555
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005556 // scene is not mandatory.
Syoyo Fujita5b407452017-06-04 17:42:41 +09005557 // FIXME Maybe a better way to handle it than removing the code
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005558
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005559 auto IsArrayMemberPresent = [](const json &_v, const char *name) -> bool {
jrkooncecba5d6c2019-08-29 11:26:22 -05005560 json_const_iterator it;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005561 return FindMember(_v, name, it) && IsArray(GetValue(it));
jrkooncecba5d6c2019-08-29 11:26:22 -05005562 };
5563
Syoyo Fujita83675312017-12-02 21:14:13 +09005564 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005565 if ((check_sections & REQUIRE_SCENES) &&
5566 !IsArrayMemberPresent(v, "scenes")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005567 if (err) {
5568 (*err) += "\"scenes\" object not found in .gltf or not an array type\n";
5569 }
5570 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005571 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005572 }
5573
Syoyo Fujita83675312017-12-02 21:14:13 +09005574 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005575 if ((check_sections & REQUIRE_NODES) && !IsArrayMemberPresent(v, "nodes")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005576 if (err) {
5577 (*err) += "\"nodes\" object not found in .gltf\n";
5578 }
5579 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005580 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005581 }
5582
Syoyo Fujita83675312017-12-02 21:14:13 +09005583 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005584 if ((check_sections & REQUIRE_ACCESSORS) &&
5585 !IsArrayMemberPresent(v, "accessors")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005586 if (err) {
5587 (*err) += "\"accessors\" object not found in .gltf\n";
5588 }
5589 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005590 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005591 }
5592
Syoyo Fujita83675312017-12-02 21:14:13 +09005593 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005594 if ((check_sections & REQUIRE_BUFFERS) &&
5595 !IsArrayMemberPresent(v, "buffers")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005596 if (err) {
5597 (*err) += "\"buffers\" object not found in .gltf\n";
5598 }
5599 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005600 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005601 }
5602
Syoyo Fujita83675312017-12-02 21:14:13 +09005603 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005604 if ((check_sections & REQUIRE_BUFFER_VIEWS) &&
5605 !IsArrayMemberPresent(v, "bufferViews")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005606 if (err) {
5607 (*err) += "\"bufferViews\" object not found in .gltf\n";
5608 }
5609 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005610 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005611 }
5612
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005613 model->buffers.clear();
5614 model->bufferViews.clear();
5615 model->accessors.clear();
5616 model->meshes.clear();
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005617 model->cameras.clear();
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005618 model->nodes.clear();
5619 model->extensionsUsed.clear();
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00005620 model->extensionsRequired.clear();
Selmar09d2ff12018-03-15 17:30:42 +01005621 model->extensions.clear();
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005622 model->defaultScene = -1;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005623
Syoyo Fujita83675312017-12-02 21:14:13 +09005624 // 1. Parse Asset
5625 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005626 json_const_iterator it;
5627 if (FindMember(v, "asset", it) && IsObject(GetValue(it))) {
5628 const json &root = GetValue(it);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005629
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005630 ParseAsset(&model->asset, err, root,
5631 store_original_json_for_extras_and_extensions_);
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00005632 }
5633 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005634
jrkoonce51453942019-09-03 09:48:30 -05005635#ifdef TINYGLTF_USE_CPP14
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005636 auto ForEachInArray = [](const json &_v, const char *member,
5637 const auto &cb) -> bool
jrkoonce51453942019-09-03 09:48:30 -05005638#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005639 // The std::function<> implementation can be less efficient because it will
5640 // allocate heap when the size of the captured lambda is above 16 bytes with
5641 // clang and gcc, but it does not require C++14.
5642 auto ForEachInArray = [](const json &_v, const char *member,
5643 const std::function<bool(const json &)> &cb) -> bool
jrkoonce51453942019-09-03 09:48:30 -05005644#endif
Syoyo Fujita83675312017-12-02 21:14:13 +09005645 {
jrkoonce0d2b6ef2019-09-04 13:46:45 -05005646 json_const_iterator itm;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005647 if (FindMember(_v, member, itm) && IsArray(GetValue(itm))) {
jrkoonce0d2b6ef2019-09-04 13:46:45 -05005648 const json &root = GetValue(itm);
jrkooncecba5d6c2019-08-29 11:26:22 -05005649 auto it = ArrayBegin(root);
5650 auto end = ArrayEnd(root);
5651 for (; it != end; ++it) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005652 if (!cb(*it)) return false;
Syoyo Fujita59130d12017-08-01 20:23:11 +09005653 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005654 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005655 return true;
5656 };
5657
jrkooncecba5d6c2019-08-29 11:26:22 -05005658 // 2. Parse extensionUsed
5659 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005660 ForEachInArray(v, "extensionsUsed", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005661 std::string str;
5662 GetString(o, str);
5663 model->extensionsUsed.emplace_back(std::move(str));
5664 return true;
5665 });
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005666 }
5667
Syoyo Fujita83675312017-12-02 21:14:13 +09005668 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005669 ForEachInArray(v, "extensionsRequired", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005670 std::string str;
5671 GetString(o, str);
5672 model->extensionsRequired.emplace_back(std::move(str));
5673 return true;
5674 });
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005675 }
5676
Syoyo Fujita83675312017-12-02 21:14:13 +09005677 // 3. Parse Buffer
5678 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005679 bool success = ForEachInArray(v, "buffers", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005680 if (!IsObject(o)) {
5681 if (err) {
5682 (*err) += "`buffers' does not contain an JSON object.";
Syoyo Fujitabeded612016-05-01 20:03:43 +09005683 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005684 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005685 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005686 Buffer buffer;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005687 if (!ParseBuffer(&buffer, err, o,
5688 store_original_json_for_extras_and_extensions_, &fs,
5689 base_dir, is_binary_, bin_data_, bin_size_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005690 return false;
5691 }
5692
5693 model->buffers.emplace_back(std::move(buffer));
5694 return true;
5695 });
5696
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005697 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005698 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005699 }
5700 }
Syoyo Fujita83675312017-12-02 21:14:13 +09005701 // 4. Parse BufferView
5702 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005703 bool success = ForEachInArray(v, "bufferViews", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005704 if (!IsObject(o)) {
5705 if (err) {
5706 (*err) += "`bufferViews' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09005707 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005708 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005709 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005710 BufferView bufferView;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005711 if (!ParseBufferView(&bufferView, err, o,
5712 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005713 return false;
5714 }
5715
5716 model->bufferViews.emplace_back(std::move(bufferView));
5717 return true;
5718 });
5719
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005720 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005721 return false;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005722 }
5723 }
5724
Syoyo Fujita83675312017-12-02 21:14:13 +09005725 // 5. Parse Accessor
5726 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005727 bool success = ForEachInArray(v, "accessors", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005728 if (!IsObject(o)) {
5729 if (err) {
5730 (*err) += "`accessors' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09005731 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005732 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005733 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005734 Accessor accessor;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005735 if (!ParseAccessor(&accessor, err, o,
5736 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005737 return false;
5738 }
5739
5740 model->accessors.emplace_back(std::move(accessor));
5741 return true;
5742 });
5743
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005744 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005745 return false;
Syoyo Fujitac2615632016-06-19 21:56:06 +09005746 }
5747 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005748
Syoyo Fujita83675312017-12-02 21:14:13 +09005749 // 6. Parse Mesh
5750 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005751 bool success = ForEachInArray(v, "meshes", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005752 if (!IsObject(o)) {
5753 if (err) {
5754 (*err) += "`meshes' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09005755 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005756 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005757 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005758 Mesh mesh;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005759 if (!ParseMesh(&mesh, model, err, o,
5760 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005761 return false;
5762 }
5763
5764 model->meshes.emplace_back(std::move(mesh));
5765 return true;
5766 });
5767
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005768 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005769 return false;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005770 }
5771 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01005772
viperscape9df05802018-12-05 14:11:01 -05005773 // Assign missing bufferView target types
5774 // - Look for missing Mesh indices
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01005775 // - Look for missing Mesh attributes
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00005776 for (auto &mesh : model->meshes) {
5777 for (auto &primitive : mesh.primitives) {
5778 if (primitive.indices >
5779 -1) // has indices from parsing step, must be Element Array Buffer
viperscape9df05802018-12-05 14:11:01 -05005780 {
Jeff McGlynn89152522019-04-25 16:33:56 -07005781 if (size_t(primitive.indices) >= model->accessors.size()) {
5782 if (err) {
5783 (*err) += "primitive indices accessor out of bounds";
5784 }
5785 return false;
5786 }
5787
Syoyo Fujitacea69e32019-08-20 17:10:30 +09005788 auto bufferView =
5789 model->accessors[size_t(primitive.indices)].bufferView;
Jeff McGlynn89152522019-04-25 16:33:56 -07005790 if (bufferView < 0 || size_t(bufferView) >= model->bufferViews.size()) {
5791 if (err) {
5792 (*err) += "accessor[" + std::to_string(primitive.indices) +
5793 "] invalid bufferView";
5794 }
5795 return false;
5796 }
5797
Syoyo Fujitacea69e32019-08-20 17:10:30 +09005798 model->bufferViews[size_t(bufferView)].target =
Jeff McGlynn89152522019-04-25 16:33:56 -07005799 TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00005800 // we could optionally check if acessors' bufferView type is Scalar, as
5801 // it should be
viperscape9df05802018-12-05 14:11:01 -05005802 }
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01005803
5804 for (auto &attribute : primitive.attributes) {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09005805 model
5806 ->bufferViews[size_t(
5807 model->accessors[size_t(attribute.second)].bufferView)]
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01005808 .target = TINYGLTF_TARGET_ARRAY_BUFFER;
5809 }
Marcin Kacprzakc3d67162020-01-22 13:13:35 +01005810
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09005811 for (auto &target : primitive.targets) {
5812 for (auto &attribute : target) {
Rahul Sheth125e4a22020-07-13 13:56:50 -04005813 auto bufferView =
5814 model->accessors[size_t(attribute.second)].bufferView;
Syoyo Fujita91da2992020-07-15 13:52:39 +09005815 // bufferView could be null(-1) for sparse morph target
5816 if (bufferView >= 0) {
Rahul Sheth125e4a22020-07-13 13:56:50 -04005817 model->bufferViews[size_t(bufferView)].target =
5818 TINYGLTF_TARGET_ARRAY_BUFFER;
5819 }
Marcin Kacprzakc3d67162020-01-22 13:13:35 +01005820 }
5821 }
viperscape9df05802018-12-05 14:11:01 -05005822 }
5823 }
5824
Syoyo Fujita83675312017-12-02 21:14:13 +09005825 // 7. Parse Node
5826 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005827 bool success = ForEachInArray(v, "nodes", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005828 if (!IsObject(o)) {
5829 if (err) {
5830 (*err) += "`nodes' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005831 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005832 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005833 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005834 Node node;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005835 if (!ParseNode(&node, err, o,
5836 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005837 return false;
5838 }
5839
5840 model->nodes.emplace_back(std::move(node));
5841 return true;
5842 });
5843
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005844 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005845 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005846 }
5847 }
5848
5849 // 8. Parse scenes.
5850 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005851 bool success = ForEachInArray(v, "scenes", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005852 if (!IsObject(o)) {
5853 if (err) {
5854 (*err) += "`scenes' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005855 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005856 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005857 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005858 std::vector<int> nodes;
jrkooncea3b8b352019-09-03 10:30:19 -05005859 ParseIntegerArrayProperty(&nodes, err, o, "nodes", false);
jrkooncecba5d6c2019-08-29 11:26:22 -05005860
5861 Scene scene;
5862 scene.nodes = std::move(nodes);
5863
5864 ParseStringProperty(&scene.name, err, o, "name", false);
5865
5866 ParseExtensionsProperty(&scene.extensions, err, o);
5867 ParseExtrasProperty(&scene.extras, o);
5868
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005869 if (store_original_json_for_extras_and_extensions_) {
5870 {
5871 json_const_iterator it;
5872 if (FindMember(o, "extensions", it)) {
Mio Nilssone29ba7c2021-05-11 11:50:35 +02005873 scene.extensions_json_string = JsonToString(GetValue(it));
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005874 }
5875 }
5876 {
5877 json_const_iterator it;
5878 if (FindMember(o, "extras", it)) {
Mio Nilssone29ba7c2021-05-11 11:50:35 +02005879 scene.extras_json_string = JsonToString(GetValue(it));
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005880 }
5881 }
5882 }
5883
jrkooncecba5d6c2019-08-29 11:26:22 -05005884 model->scenes.emplace_back(std::move(scene));
5885 return true;
5886 });
5887
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005888 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005889 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005890 }
5891 }
5892
5893 // 9. Parse default scenes.
5894 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005895 json_const_iterator rootIt;
5896 int iVal;
5897 if (FindMember(v, "scene", rootIt) && GetInt(GetValue(rootIt), iVal)) {
5898 model->defaultScene = iVal;
Syoyo Fujita83675312017-12-02 21:14:13 +09005899 }
5900 }
5901
5902 // 10. Parse Material
5903 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005904 bool success = ForEachInArray(v, "materials", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005905 if (!IsObject(o)) {
5906 if (err) {
5907 (*err) += "`materials' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005908 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005909 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005910 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005911 Material material;
5912 ParseStringProperty(&material.name, err, o, "name", false);
5913
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005914 if (!ParseMaterial(&material, err, o,
5915 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005916 return false;
5917 }
5918
5919 model->materials.emplace_back(std::move(material));
5920 return true;
5921 });
5922
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005923 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005924 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005925 }
5926 }
5927
5928 // 11. Parse Image
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09005929 void *load_image_user_data{nullptr};
5930
5931 LoadImageDataOption load_image_option;
5932
5933 if (user_image_loader_) {
5934 // Use user supplied pointer
5935 load_image_user_data = load_image_user_data_;
5936 } else {
5937 load_image_option.preserve_channels = preserve_image_channels_;
5938 load_image_user_data = reinterpret_cast<void *>(&load_image_option);
5939 }
5940
Syoyo Fujita83675312017-12-02 21:14:13 +09005941 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005942 int idx = 0;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005943 bool success = ForEachInArray(v, "images", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005944 if (!IsObject(o)) {
5945 if (err) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005946 (*err) += "image[" + std::to_string(idx) + "] is not a JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005947 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005948 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005949 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005950 Image image;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005951 if (!ParseImage(&image, idx, err, warn, o,
5952 store_original_json_for_extras_and_extensions_, base_dir,
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09005953 &fs, &this->LoadImageData, load_image_user_data)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005954 return false;
5955 }
5956
5957 if (image.bufferView != -1) {
5958 // Load image from the buffer view.
5959 if (size_t(image.bufferView) >= model->bufferViews.size()) {
5960 if (err) {
5961 std::stringstream ss;
5962 ss << "image[" << idx << "] bufferView \"" << image.bufferView
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005963 << "\" not found in the scene." << std::endl;
jrkooncecba5d6c2019-08-29 11:26:22 -05005964 (*err) += ss.str();
5965 }
5966 return false;
5967 }
5968
5969 const BufferView &bufferView =
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005970 model->bufferViews[size_t(image.bufferView)];
jrkooncecba5d6c2019-08-29 11:26:22 -05005971 if (size_t(bufferView.buffer) >= model->buffers.size()) {
5972 if (err) {
5973 std::stringstream ss;
5974 ss << "image[" << idx << "] buffer \"" << bufferView.buffer
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005975 << "\" not found in the scene." << std::endl;
jrkooncecba5d6c2019-08-29 11:26:22 -05005976 (*err) += ss.str();
5977 }
5978 return false;
5979 }
5980 const Buffer &buffer = model->buffers[size_t(bufferView.buffer)];
5981
5982 if (*LoadImageData == nullptr) {
5983 if (err) {
5984 (*err) += "No LoadImageData callback specified.\n";
5985 }
5986 return false;
5987 }
5988 bool ret = LoadImageData(
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005989 &image, idx, err, warn, image.width, image.height,
5990 &buffer.data[bufferView.byteOffset],
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09005991 static_cast<int>(bufferView.byteLength), load_image_user_data);
jrkooncecba5d6c2019-08-29 11:26:22 -05005992 if (!ret) {
5993 return false;
5994 }
5995 }
5996
5997 model->images.emplace_back(std::move(image));
5998 ++idx;
5999 return true;
6000 });
6001
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006002 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006003 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006004 }
6005 }
6006
6007 // 12. Parse Texture
6008 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006009 bool success = ForEachInArray(v, "textures", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006010 if (!IsObject(o)) {
6011 if (err) {
6012 (*err) += "`textures' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006013 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006014 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006015 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006016 Texture texture;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006017 if (!ParseTexture(&texture, err, o,
6018 store_original_json_for_extras_and_extensions_,
6019 base_dir)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006020 return false;
6021 }
6022
6023 model->textures.emplace_back(std::move(texture));
6024 return true;
6025 });
6026
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006027 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006028 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006029 }
6030 }
6031
6032 // 13. Parse Animation
6033 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006034 bool success = ForEachInArray(v, "animations", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006035 if (!IsObject(o)) {
6036 if (err) {
6037 (*err) += "`animations' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006038 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006039 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006040 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006041 Animation animation;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006042 if (!ParseAnimation(&animation, err, o,
6043 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006044 return false;
6045 }
6046
6047 model->animations.emplace_back(std::move(animation));
6048 return true;
6049 });
6050
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006051 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006052 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006053 }
6054 }
6055
6056 // 14. Parse Skin
6057 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006058 bool success = ForEachInArray(v, "skins", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006059 if (!IsObject(o)) {
6060 if (err) {
6061 (*err) += "`skins' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006062 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006063 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006064 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006065 Skin skin;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006066 if (!ParseSkin(&skin, err, o,
6067 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006068 return false;
6069 }
6070
6071 model->skins.emplace_back(std::move(skin));
6072 return true;
6073 });
6074
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006075 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006076 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006077 }
6078 }
6079
6080 // 15. Parse Sampler
6081 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006082 bool success = ForEachInArray(v, "samplers", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006083 if (!IsObject(o)) {
6084 if (err) {
6085 (*err) += "`samplers' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006086 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006087 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006088 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006089 Sampler sampler;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006090 if (!ParseSampler(&sampler, err, o,
6091 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006092 return false;
6093 }
6094
6095 model->samplers.emplace_back(std::move(sampler));
6096 return true;
6097 });
6098
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006099 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006100 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006101 }
6102 }
6103
6104 // 16. Parse Camera
6105 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006106 bool success = ForEachInArray(v, "cameras", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006107 if (!IsObject(o)) {
6108 if (err) {
6109 (*err) += "`cameras' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006110 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006111 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006112 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006113 Camera camera;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006114 if (!ParseCamera(&camera, err, o,
6115 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006116 return false;
6117 }
6118
6119 model->cameras.emplace_back(std::move(camera));
6120 return true;
6121 });
6122
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006123 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006124 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006125 }
6126 }
6127
6128 // 17. Parse Extensions
Selmar09d2ff12018-03-15 17:30:42 +01006129 ParseExtensionsProperty(&model->extensions, err, v);
6130
6131 // 18. Specific extension implementations
Syoyo Fujita83675312017-12-02 21:14:13 +09006132 {
jrkooncecba5d6c2019-08-29 11:26:22 -05006133 json_const_iterator rootIt;
6134 if (FindMember(v, "extensions", rootIt) && IsObject(GetValue(rootIt))) {
6135 const json &root = GetValue(rootIt);
Syoyo Fujita83675312017-12-02 21:14:13 +09006136
jrkooncecba5d6c2019-08-29 11:26:22 -05006137 json_const_iterator it(ObjectBegin(root));
6138 json_const_iterator itEnd(ObjectEnd(root));
Syoyo Fujita83675312017-12-02 21:14:13 +09006139 for (; it != itEnd; ++it) {
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02006140 // parse KHR_lights_punctual extension
jrkooncecba5d6c2019-08-29 11:26:22 -05006141 std::string key(GetKey(it));
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006142 if ((key == "KHR_lights_punctual") && IsObject(GetValue(it))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006143 const json &object = GetValue(it);
6144 json_const_iterator itLight;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006145 if (FindMember(object, "lights", itLight)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006146 const json &lights = GetValue(itLight);
6147 if (!IsArray(lights)) {
6148 continue;
Syoyo Fujita83675312017-12-02 21:14:13 +09006149 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006150
6151 auto arrayIt(ArrayBegin(lights));
6152 auto arrayItEnd(ArrayEnd(lights));
6153 for (; arrayIt != arrayItEnd; ++arrayIt) {
6154 Light light;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006155 if (!ParseLight(&light, err, *arrayIt,
6156 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006157 return false;
6158 }
6159 model->lights.emplace_back(std::move(light));
6160 }
Syoyo Fujita83675312017-12-02 21:14:13 +09006161 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01006162 }
6163 }
6164 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01006165 }
Syoyo Fujita83675312017-12-02 21:14:13 +09006166
mynzc0d4d1c2018-06-28 23:06:00 +09006167 // 19. Parse Extras
6168 ParseExtrasProperty(&model->extras, v);
6169
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006170 if (store_original_json_for_extras_and_extensions_) {
6171 model->extras_json_string = JsonToString(v["extras"]);
6172 model->extensions_json_string = JsonToString(v["extensions"]);
6173 }
6174
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09006175 return true;
6176}
6177
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006178bool TinyGLTF::LoadASCIIFromString(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006179 std::string *warn, const char *str,
6180 unsigned int length,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006181 const std::string &base_dir,
6182 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006183 is_binary_ = false;
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09006184 bin_data_ = nullptr;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006185 bin_size_ = 0;
6186
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006187 return LoadFromString(model, err, warn, str, length, base_dir,
6188 check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006189}
6190
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006191bool TinyGLTF::LoadASCIIFromFile(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006192 std::string *warn, const std::string &filename,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006193 unsigned int check_sections) {
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006194 std::stringstream ss;
6195
Paolo Jovone6601bf2018-07-07 20:43:33 +02006196 if (fs.ReadWholeFile == nullptr) {
6197 // Programmer error, assert() ?
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006198 ss << "Failed to read file: " << filename
6199 << ": one or more FS callback not set" << std::endl;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09006200 if (err) {
6201 (*err) = ss.str();
6202 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006203 return false;
6204 }
6205
Paolo Jovone6601bf2018-07-07 20:43:33 +02006206 std::vector<unsigned char> data;
6207 std::string fileerr;
6208 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006209 if (!fileread) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02006210 ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
6211 if (err) {
6212 (*err) = ss.str();
6213 }
6214 return false;
6215 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006216
Paolo Jovone6601bf2018-07-07 20:43:33 +02006217 size_t sz = data.size();
Luke San Antoniob310b4a2016-06-23 14:17:37 -04006218 if (sz == 0) {
6219 if (err) {
6220 (*err) = "Empty file.";
6221 }
6222 return false;
6223 }
6224
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006225 std::string basedir = GetBaseDir(filename);
6226
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006227 bool ret = LoadASCIIFromString(
6228 model, err, warn, reinterpret_cast<const char *>(&data.at(0)),
6229 static_cast<unsigned int>(data.size()), basedir, check_sections);
Luke San Antonio9e36b612016-06-23 14:10:51 -04006230
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006231 return ret;
6232}
6233
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006234bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006235 std::string *warn,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006236 const unsigned char *bytes,
6237 unsigned int size,
6238 const std::string &base_dir,
6239 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006240 if (size < 20) {
6241 if (err) {
6242 (*err) = "Too short data size for glTF Binary.";
6243 }
6244 return false;
6245 }
6246
Syoyo Fujitabeded612016-05-01 20:03:43 +09006247 if (bytes[0] == 'g' && bytes[1] == 'l' && bytes[2] == 'T' &&
6248 bytes[3] == 'F') {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006249 // ok
6250 } else {
6251 if (err) {
6252 (*err) = "Invalid magic.";
6253 }
6254 return false;
6255 }
6256
Syoyo Fujitabeded612016-05-01 20:03:43 +09006257 unsigned int version; // 4 bytes
6258 unsigned int length; // 4 bytes
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006259 unsigned int chunk0_length; // 4 bytes
6260 unsigned int chunk0_format; // 4 bytes;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006261
Syoyo Fujitabeded612016-05-01 20:03:43 +09006262 memcpy(&version, bytes + 4, 4);
6263 swap4(&version);
6264 memcpy(&length, bytes + 8, 4);
6265 swap4(&length);
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006266 memcpy(&chunk0_length, bytes + 12, 4); // JSON data length
6267 swap4(&chunk0_length);
6268 memcpy(&chunk0_format, bytes + 16, 4);
6269 swap4(&chunk0_format);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006270
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006271 // https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#binary-gltf-layout
6272 //
Syoyo Fujitae69069d2018-03-15 22:01:18 -05006273 // In case the Bin buffer is not present, the size is exactly 20 + size of
6274 // JSON contents,
6275 // so use "greater than" operator.
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006276 //
6277 // https://github.com/syoyo/tinygltf/issues/372
6278 // Use 64bit uint to avoid integer overflow.
6279 uint64_t json_size = 20ull + uint64_t(chunk0_length);
6280
6281 if (json_size > std::numeric_limits<uint32_t>::max()) {
6282 // Do not allow 4GB or more GLB data.
6283 (*err) = "Invalid glTF binary. GLB data exceeds 4GB.";
6284 }
6285
6286 if ((json_size > uint64_t(size)) || (chunk0_length < 1) || (length > size) ||
6287 (json_size > uint64_t(length)) ||
6288 (chunk0_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006289 if (err) {
6290 (*err) = "Invalid glTF binary.";
6291 }
6292 return false;
6293 }
6294
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006295 // Padding check
6296 // The start and the end of each chunk must be aligned to a 4-byte boundary.
6297 // No padding check for chunk0 start since its 4byte-boundary is ensured.
6298 if ((json_size % 4) != 0) {
6299 if (err) {
6300 (*err) = "JSON Chunk end does not aligned to a 4-byte boundary.";
6301 }
6302 }
6303
6304 // Read Chunk1 info(BIN data)
6305 if ((json_size + 8ull) > uint64_t(length)) {
6306 if (err) {
6307 (*err) = "Insufficient storage space for Chunk1(BIN data).";
6308 }
6309 return false;
6310 }
6311
6312 unsigned int chunk1_length; // 4 bytes
6313 unsigned int chunk1_format; // 4 bytes;
6314 memcpy(&chunk1_length, bytes + json_size, 4); // JSON data length
6315 swap4(&chunk1_length);
6316 memcpy(&chunk1_format, bytes + json_size + 4, 4);
6317 swap4(&chunk1_format);
6318
6319 if (chunk1_length < 4) {
6320 // TODO: Do we allow 0byte BIN data?
6321 if (err) {
6322 (*err) = "Insufficient Chunk1(BIN) data size.";
6323 }
6324 return false;
6325 }
6326
6327 if ((chunk1_length % 4) != 0) {
6328 if (err) {
6329 (*err) = "BIN Chunk end does not aligned to a 4-byte boundary.";
6330 }
6331 return false;
6332 }
6333
6334 if (uint64_t(chunk1_length) + json_size > uint64_t(length)) {
6335 if (err) {
6336 (*err) = "BIN Chunk data length exceeds the GLB size.";
6337 }
6338 return false;
6339 }
6340
6341 if (chunk1_format != 0x004e4942) {
6342 if (err) {
6343 (*err) = "Invlid type for chunk1 data.";
6344 }
6345 return false;
6346 }
6347
6348 bin_data_ = bytes + json_size +
6349 8; // 4 bytes (bin_buffer_length) + 4 bytes(bin_buffer_format)
6350
6351 bin_size_ = size_t(chunk1_length);
6352
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006353 // Extract JSON string.
Syoyo Fujitabeded612016-05-01 20:03:43 +09006354 std::string jsonString(reinterpret_cast<const char *>(&bytes[20]),
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006355 chunk0_length);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006356
6357 is_binary_ = true;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006358
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006359 bool ret = LoadFromString(model, err, warn,
6360 reinterpret_cast<const char *>(&bytes[20]),
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006361 chunk0_length, base_dir, check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006362 if (!ret) {
6363 return ret;
6364 }
6365
6366 return true;
6367}
6368
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006369bool TinyGLTF::LoadBinaryFromFile(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006370 std::string *warn,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006371 const std::string &filename,
6372 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006373 std::stringstream ss;
6374
Paolo Jovone6601bf2018-07-07 20:43:33 +02006375 if (fs.ReadWholeFile == nullptr) {
6376 // Programmer error, assert() ?
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006377 ss << "Failed to read file: " << filename
6378 << ": one or more FS callback not set" << std::endl;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006379 if (err) {
6380 (*err) = ss.str();
6381 }
6382 return false;
6383 }
6384
Paolo Jovone6601bf2018-07-07 20:43:33 +02006385 std::vector<unsigned char> data;
6386 std::string fileerr;
6387 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006388 if (!fileread) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02006389 ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
6390 if (err) {
6391 (*err) = ss.str();
6392 }
6393 return false;
6394 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006395
Syoyo Fujitabeded612016-05-01 20:03:43 +09006396 std::string basedir = GetBaseDir(filename);
6397
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006398 bool ret = LoadBinaryFromMemory(model, err, warn, &data.at(0),
6399 static_cast<unsigned int>(data.size()),
6400 basedir, check_sections);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006401
6402 return ret;
6403}
6404
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006405///////////////////////
6406// GLTF Serialization
6407///////////////////////
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006408namespace {
6409json JsonFromString(const char *s) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006410#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006411 return json(s, GetAllocator());
jrkooncecba5d6c2019-08-29 11:26:22 -05006412#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006413 return json(s);
jrkooncecba5d6c2019-08-29 11:26:22 -05006414#endif
jrkoonce63419a12019-09-03 17:06:41 -05006415}
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006416
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006417void JsonAssign(json &dest, const json &src) {
6418#ifdef TINYGLTF_USE_RAPIDJSON
6419 dest.CopyFrom(src, GetAllocator());
6420#else
6421 dest = src;
6422#endif
6423}
6424
6425void JsonAddMember(json &o, const char *key, json &&value) {
6426#ifdef TINYGLTF_USE_RAPIDJSON
6427 if (!o.IsObject()) {
6428 o.SetObject();
6429 }
6430 o.AddMember(json(key, GetAllocator()), std::move(value), GetAllocator());
6431#else
6432 o[key] = std::move(value);
6433#endif
6434}
6435
6436void JsonPushBack(json &o, json &&value) {
6437#ifdef TINYGLTF_USE_RAPIDJSON
6438 o.PushBack(std::move(value), GetAllocator());
6439#else
6440 o.push_back(std::move(value));
6441#endif
6442}
6443
6444bool JsonIsNull(const json &o) {
6445#ifdef TINYGLTF_USE_RAPIDJSON
6446 return o.IsNull();
6447#else
6448 return o.is_null();
6449#endif
6450}
6451
6452void JsonSetObject(json &o) {
6453#ifdef TINYGLTF_USE_RAPIDJSON
6454 o.SetObject();
6455#else
6456 o = o.object({});
6457#endif
6458}
6459
6460void JsonReserveArray(json &o, size_t s) {
6461#ifdef TINYGLTF_USE_RAPIDJSON
6462 o.SetArray();
6463 o.Reserve(static_cast<rapidjson::SizeType>(s), GetAllocator());
6464#endif
6465 (void)(o);
6466 (void)(s);
6467}
6468} // namespace
6469
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006470// typedef std::pair<std::string, json> json_object_pair;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006471
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006472template <typename T>
6473static void SerializeNumberProperty(const std::string &key, T number,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006474 json &obj) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006475 // obj.insert(
Syoyo Fujita83675312017-12-02 21:14:13 +09006476 // json_object_pair(key, json(static_cast<double>(number))));
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006477 // obj[key] = static_cast<double>(number);
jrkooncecba5d6c2019-08-29 11:26:22 -05006478 JsonAddMember(obj, key.c_str(), json(number));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006479}
6480
Ivo van Dongen272a9df2020-05-29 11:24:11 +03006481#ifdef TINYGLTF_USE_RAPIDJSON
6482template <>
6483void SerializeNumberProperty(const std::string &key, size_t number, json &obj) {
6484 JsonAddMember(obj, key.c_str(), json(static_cast<uint64_t>(number)));
6485}
6486#endif
6487
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006488template <typename T>
6489static void SerializeNumberArrayProperty(const std::string &key,
6490 const std::vector<T> &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006491 json &obj) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006492 if (value.empty()) return;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006493
jrkooncecba5d6c2019-08-29 11:26:22 -05006494 json ary;
6495 JsonReserveArray(ary, value.size());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006496 for (const auto &s : value) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006497 JsonPushBack(ary, json(s));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006498 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006499 JsonAddMember(obj, key.c_str(), std::move(ary));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006500}
6501
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006502static void SerializeStringProperty(const std::string &key,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006503 const std::string &value, json &obj) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006504 JsonAddMember(obj, key.c_str(), JsonFromString(value.c_str()));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006505}
6506
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006507static void SerializeStringArrayProperty(const std::string &key,
6508 const std::vector<std::string> &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006509 json &obj) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006510 json ary;
6511 JsonReserveArray(ary, value.size());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006512 for (auto &s : value) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006513 JsonPushBack(ary, JsonFromString(s.c_str()));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006514 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006515 JsonAddMember(obj, key.c_str(), std::move(ary));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006516}
6517
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006518static bool ValueToJson(const Value &value, json *ret) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006519 json obj;
jrkooncecba5d6c2019-08-29 11:26:22 -05006520#ifdef TINYGLTF_USE_RAPIDJSON
6521 switch (value.Type()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006522 case REAL_TYPE:
6523 obj.SetDouble(value.Get<double>());
6524 break;
6525 case INT_TYPE:
6526 obj.SetInt(value.Get<int>());
6527 break;
6528 case BOOL_TYPE:
6529 obj.SetBool(value.Get<bool>());
6530 break;
6531 case STRING_TYPE:
6532 obj.SetString(value.Get<std::string>().c_str(), GetAllocator());
6533 break;
6534 case ARRAY_TYPE: {
6535 obj.SetArray();
6536 obj.Reserve(static_cast<rapidjson::SizeType>(value.ArrayLen()),
6537 GetAllocator());
6538 for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
6539 Value elementValue = value.Get(int(i));
6540 json elementJson;
6541 if (ValueToJson(value.Get(int(i)), &elementJson))
6542 obj.PushBack(std::move(elementJson), GetAllocator());
jrkooncecba5d6c2019-08-29 11:26:22 -05006543 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006544 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05006545 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006546 case BINARY_TYPE:
6547 // TODO
6548 // obj = json(value.Get<std::vector<unsigned char>>());
6549 return false;
6550 break;
6551 case OBJECT_TYPE: {
6552 obj.SetObject();
6553 Value::Object objMap = value.Get<Value::Object>();
6554 for (auto &it : objMap) {
6555 json elementJson;
6556 if (ValueToJson(it.second, &elementJson)) {
6557 obj.AddMember(json(it.first.c_str(), GetAllocator()),
6558 std::move(elementJson), GetAllocator());
6559 }
6560 }
6561 break;
6562 }
6563 case NULL_TYPE:
6564 default:
6565 return false;
jrkooncecba5d6c2019-08-29 11:26:22 -05006566 }
6567#else
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006568 switch (value.Type()) {
Syoyo Fujita150f2432019-07-25 19:22:44 +09006569 case REAL_TYPE:
Selmar Kokfa7022f2018-04-04 18:10:20 +02006570 obj = json(value.Get<double>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006571 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006572 case INT_TYPE:
6573 obj = json(value.Get<int>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006574 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006575 case BOOL_TYPE:
6576 obj = json(value.Get<bool>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006577 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006578 case STRING_TYPE:
6579 obj = json(value.Get<std::string>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006580 break;
6581 case ARRAY_TYPE: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006582 for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
6583 Value elementValue = value.Get(int(i));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006584 json elementJson;
6585 if (ValueToJson(value.Get(int(i)), &elementJson))
Selmar Kokfa7022f2018-04-04 18:10:20 +02006586 obj.push_back(elementJson);
6587 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006588 break;
6589 }
Selmar Kokfa7022f2018-04-04 18:10:20 +02006590 case BINARY_TYPE:
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006591 // TODO
6592 // obj = json(value.Get<std::vector<unsigned char>>());
Selmar Kokfa7022f2018-04-04 18:10:20 +02006593 return false;
6594 break;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006595 case OBJECT_TYPE: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006596 Value::Object objMap = value.Get<Value::Object>();
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006597 for (auto &it : objMap) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006598 json elementJson;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006599 if (ValueToJson(it.second, &elementJson)) obj[it.first] = elementJson;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006600 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006601 break;
6602 }
6603 case NULL_TYPE:
6604 default:
6605 return false;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006606 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006607#endif
6608 if (ret) *ret = std::move(obj);
Selmar Kokfa7022f2018-04-04 18:10:20 +02006609 return true;
6610}
6611
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006612static void SerializeValue(const std::string &key, const Value &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006613 json &obj) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006614 json ret;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006615 if (ValueToJson(value, &ret)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006616 JsonAddMember(obj, key.c_str(), std::move(ret));
6617 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006618}
6619
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006620static void SerializeGltfBufferData(const std::vector<unsigned char> &data,
johan bowald30c53472018-03-30 11:49:36 +02006621 json &o) {
6622 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09006623 if (data.size() > 0) {
6624 std::string encodedData =
6625 base64_encode(&data[0], static_cast<unsigned int>(data.size()));
6626 SerializeStringProperty("uri", header + encodedData, o);
6627 } else {
6628 // Issue #229
6629 // size 0 is allowd. Just emit mime header.
6630 SerializeStringProperty("uri", header, o);
6631 }
johan bowald30c53472018-03-30 11:49:36 +02006632}
6633
Selmar Koke4677492018-10-25 16:45:49 +02006634static bool SerializeGltfBufferData(const std::vector<unsigned char> &data,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006635 const std::string &binFilename) {
Harokyangfb256602019-10-30 16:13:52 +08006636#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09006637#if defined(__GLIBCXX__) // mingw
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09006638 int file_descriptor = _wopen(UTF8ToWchar(binFilename).c_str(),
6639 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
6640 __gnu_cxx::stdio_filebuf<char> wfile_buf(
6641 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09006642 std::ostream output(&wfile_buf);
6643 if (!wfile_buf.is_open()) return false;
6644#elif defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08006645 std::ofstream output(UTF8ToWchar(binFilename).c_str(), std::ofstream::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09006646 if (!output.is_open()) return false;
Harokyangfb256602019-10-30 16:13:52 +08006647#else
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006648 std::ofstream output(binFilename.c_str(), std::ofstream::binary);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006649 if (!output.is_open()) return false;
Syoyo Fujita45cac782019-11-09 20:42:55 +09006650#endif
6651#else
6652 std::ofstream output(binFilename.c_str(), std::ofstream::binary);
6653 if (!output.is_open()) return false;
6654#endif
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09006655 if (data.size() > 0) {
6656 output.write(reinterpret_cast<const char *>(&data[0]),
6657 std::streamsize(data.size()));
6658 } else {
6659 // Issue #229
6660 // size 0 will be still valid buffer data.
6661 // write empty file.
6662 }
Selmar Koke4677492018-10-25 16:45:49 +02006663 return true;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006664}
6665
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006666#if 0 // FIXME(syoyo): not used. will be removed in the future release.
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006667static void SerializeParameterMap(ParameterMap &param, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006668 for (ParameterMap::iterator paramIt = param.begin(); paramIt != param.end();
6669 ++paramIt) {
6670 if (paramIt->second.number_array.size()) {
6671 SerializeNumberArrayProperty<double>(paramIt->first,
6672 paramIt->second.number_array, o);
6673 } else if (paramIt->second.json_double_value.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006674 json json_double_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006675 for (std::map<std::string, double>::iterator it =
6676 paramIt->second.json_double_value.begin();
6677 it != paramIt->second.json_double_value.end(); ++it) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006678 if (it->first == "index") {
6679 json_double_value[it->first] = paramIt->second.TextureIndex();
6680 } else {
6681 json_double_value[it->first] = it->second;
6682 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006683 }
6684
Syoyo Fujita83675312017-12-02 21:14:13 +09006685 o[paramIt->first] = json_double_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006686 } else if (!paramIt->second.string_value.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006687 SerializeStringProperty(paramIt->first, paramIt->second.string_value, o);
Ben Buzbeef6af2242018-04-25 15:13:05 -07006688 } else if (paramIt->second.has_number_value) {
6689 o[paramIt->first] = paramIt->second.number_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006690 } else {
Syoyo Fujita83675312017-12-02 21:14:13 +09006691 o[paramIt->first] = paramIt->second.bool_value;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006692 }
6693 }
6694}
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006695#endif
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006696
Selmar Kok81b672b2019-10-18 16:08:44 +02006697static void SerializeExtensionMap(const ExtensionMap &extensions, json &o) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006698 if (!extensions.size()) return;
Selmar09d2ff12018-03-15 17:30:42 +01006699
6700 json extMap;
Selmar Kok81b672b2019-10-18 16:08:44 +02006701 for (ExtensionMap::const_iterator extIt = extensions.begin();
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006702 extIt != extensions.end(); ++extIt) {
Syoyo Fujita924d86e2018-10-08 21:15:12 +09006703 // Allow an empty object for extension(#97)
6704 json ret;
jrkooncecba5d6c2019-08-29 11:26:22 -05006705 bool isNull = true;
Syoyo Fujita924d86e2018-10-08 21:15:12 +09006706 if (ValueToJson(extIt->second, &ret)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006707 isNull = JsonIsNull(ret);
6708 JsonAddMember(extMap, extIt->first.c_str(), std::move(ret));
Selmar Kokdb7f4e42018-10-10 18:10:58 +02006709 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006710 if (isNull) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006711 if (!(extIt->first.empty())) { // name should not be empty, but for sure
6712 // create empty object so that an extension name is still included in
6713 // json.
jrkooncecba5d6c2019-08-29 11:26:22 -05006714 json empty;
jrkoonce06c30c42019-09-03 15:56:48 -05006715 JsonSetObject(empty);
jrkooncecba5d6c2019-08-29 11:26:22 -05006716 JsonAddMember(extMap, extIt->first.c_str(), std::move(empty));
Syoyo Fujita924d86e2018-10-08 21:15:12 +09006717 }
6718 }
Selmar09d2ff12018-03-15 17:30:42 +01006719 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006720 JsonAddMember(o, "extensions", std::move(extMap));
Selmar09d2ff12018-03-15 17:30:42 +01006721}
6722
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006723static void SerializeGltfAccessor(Accessor &accessor, json &o) {
Sanjeet Suhag5ecede72020-03-04 17:40:10 -05006724 if (accessor.bufferView >= 0)
6725 SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006726
Syoyo Fujita18f0e202020-04-29 19:16:35 +09006727 if (accessor.byteOffset != 0)
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006728 SerializeNumberProperty<int>("byteOffset", int(accessor.byteOffset), o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006729
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006730 SerializeNumberProperty<int>("componentType", accessor.componentType, o);
6731 SerializeNumberProperty<size_t>("count", accessor.count, o);
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09006732
6733 if ((accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) ||
6734 (accessor.componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE)) {
6735 SerializeNumberArrayProperty<double>("min", accessor.minValues, o);
6736 SerializeNumberArrayProperty<double>("max", accessor.maxValues, o);
6737 } else {
6738 // Issue #301. Serialize as integer.
6739 // Assume int value is within [-2**31-1, 2**31-1]
6740 {
6741 std::vector<int> values;
6742 std::transform(accessor.minValues.begin(), accessor.minValues.end(),
6743 std::back_inserter(values),
6744 [](double v) { return static_cast<int>(v); });
6745
6746 SerializeNumberArrayProperty<int>("min", values, o);
6747 }
6748
6749 {
6750 std::vector<int> values;
6751 std::transform(accessor.maxValues.begin(), accessor.maxValues.end(),
6752 std::back_inserter(values),
6753 [](double v) { return static_cast<int>(v); });
6754
6755 SerializeNumberArrayProperty<int>("max", values, o);
6756 }
6757 }
6758
Eero Pajarre2e8a1152019-11-18 13:09:25 +02006759 if (accessor.normalized)
6760 SerializeValue("normalized", Value(accessor.normalized), o);
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006761 std::string type;
6762 switch (accessor.type) {
6763 case TINYGLTF_TYPE_SCALAR:
6764 type = "SCALAR";
6765 break;
6766 case TINYGLTF_TYPE_VEC2:
6767 type = "VEC2";
6768 break;
6769 case TINYGLTF_TYPE_VEC3:
6770 type = "VEC3";
6771 break;
6772 case TINYGLTF_TYPE_VEC4:
6773 type = "VEC4";
6774 break;
6775 case TINYGLTF_TYPE_MAT2:
6776 type = "MAT2";
6777 break;
6778 case TINYGLTF_TYPE_MAT3:
6779 type = "MAT3";
6780 break;
6781 case TINYGLTF_TYPE_MAT4:
6782 type = "MAT4";
6783 break;
6784 }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09006785
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006786 SerializeStringProperty("type", type, o);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09006787 if (!accessor.name.empty()) SerializeStringProperty("name", accessor.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006788
6789 if (accessor.extras.Type() != NULL_TYPE) {
6790 SerializeValue("extras", accessor.extras, o);
6791 }
feiy0b315432022-08-13 10:08:17 +08006792
6793 // sparse
6794 if (accessor.sparse.isSparse)
6795 {
6796 json sparse;
6797 SerializeNumberProperty<int>("count", accessor.sparse.count, sparse);
6798 {
6799 json indices;
6800 SerializeNumberProperty<int>("bufferView", accessor.sparse.indices.bufferView, indices);
6801 SerializeNumberProperty<int>("byteOffset", accessor.sparse.indices.byteOffset, indices);
6802 SerializeNumberProperty<int>("componentType", accessor.sparse.indices.componentType, indices);
6803 JsonAddMember(sparse, "indices", std::move(indices));
6804 }
6805 {
6806 json values;
6807 SerializeNumberProperty<int>("bufferView", accessor.sparse.values.bufferView, values);
feiy1a8814d2022-08-14 19:49:07 +08006808 SerializeNumberProperty<int>("byteOffset", accessor.sparse.values.byteOffset, values);
feiy0b315432022-08-13 10:08:17 +08006809 JsonAddMember(sparse, "values", std::move(values));
6810 }
6811 JsonAddMember(o, "sparse", std::move(sparse));
6812 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006813}
6814
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006815static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006816 SerializeNumberProperty("sampler", channel.sampler, o);
jrkooncecba5d6c2019-08-29 11:26:22 -05006817 {
6818 json target;
6819 SerializeNumberProperty("node", channel.target_node, target);
6820 SerializeStringProperty("path", channel.target_path, target);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006821
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09006822 SerializeExtensionMap(channel.target_extensions, target);
Selmar Kok973d9b32020-01-21 18:45:24 +01006823
jrkooncecba5d6c2019-08-29 11:26:22 -05006824 JsonAddMember(o, "target", std::move(target));
6825 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02006826
6827 if (channel.extras.Type() != NULL_TYPE) {
6828 SerializeValue("extras", channel.extras, o);
6829 }
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006830
Selmar Kok4e2988e2019-08-16 14:08:08 +02006831 SerializeExtensionMap(channel.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006832}
6833
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006834static void SerializeGltfAnimationSampler(AnimationSampler &sampler, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006835 SerializeNumberProperty("input", sampler.input, o);
6836 SerializeNumberProperty("output", sampler.output, o);
6837 SerializeStringProperty("interpolation", sampler.interpolation, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006838
6839 if (sampler.extras.Type() != NULL_TYPE) {
6840 SerializeValue("extras", sampler.extras, o);
6841 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006842}
6843
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006844static void SerializeGltfAnimation(Animation &animation, json &o) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09006845 if (!animation.name.empty())
6846 SerializeStringProperty("name", animation.name, o);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006847
jrkooncecba5d6c2019-08-29 11:26:22 -05006848 {
6849 json channels;
6850 JsonReserveArray(channels, animation.channels.size());
6851 for (unsigned int i = 0; i < animation.channels.size(); ++i) {
6852 json channel;
6853 AnimationChannel gltfChannel = animation.channels[i];
6854 SerializeGltfAnimationChannel(gltfChannel, channel);
6855 JsonPushBack(channels, std::move(channel));
6856 }
6857
6858 JsonAddMember(o, "channels", std::move(channels));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006859 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006860
jrkooncecba5d6c2019-08-29 11:26:22 -05006861 {
6862 json samplers;
Jacek1da4e5d2020-01-06 14:36:57 -06006863 JsonReserveArray(samplers, animation.samplers.size());
jrkooncecba5d6c2019-08-29 11:26:22 -05006864 for (unsigned int i = 0; i < animation.samplers.size(); ++i) {
6865 json sampler;
6866 AnimationSampler gltfSampler = animation.samplers[i];
6867 SerializeGltfAnimationSampler(gltfSampler, sampler);
6868 JsonPushBack(samplers, std::move(sampler));
6869 }
6870 JsonAddMember(o, "samplers", std::move(samplers));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006871 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006872
Jens Olssonb3af2f12018-06-04 10:17:49 +02006873 if (animation.extras.Type() != NULL_TYPE) {
6874 SerializeValue("extras", animation.extras, o);
6875 }
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006876
Selmar Kok4e2988e2019-08-16 14:08:08 +02006877 SerializeExtensionMap(animation.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006878}
6879
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006880static void SerializeGltfAsset(Asset &asset, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006881 if (!asset.generator.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006882 SerializeStringProperty("generator", asset.generator, o);
6883 }
6884
Christophe820ede82019-07-04 15:21:21 +09006885 if (!asset.copyright.empty()) {
6886 SerializeStringProperty("copyright", asset.copyright, o);
6887 }
6888
Syoyo Fujitab702de72021-03-02 19:08:29 +09006889 if (asset.version.empty()) {
6890 // Just in case
6891 // `version` must be defined
6892 asset.version = "2.0";
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006893 }
6894
Syoyo Fujitab702de72021-03-02 19:08:29 +09006895 // TODO(syoyo): Do we need to check if `version` is greater or equal to 2.0?
6896 SerializeStringProperty("version", asset.version, o);
6897
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006898 if (asset.extras.Keys().size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006899 SerializeValue("extras", asset.extras, o);
6900 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006901
Selmar09d2ff12018-03-15 17:30:42 +01006902 SerializeExtensionMap(asset.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006903}
6904
Syoyo Fujitac4166e42020-01-08 02:38:01 +09006905static void SerializeGltfBufferBin(Buffer &buffer, json &o,
6906 std::vector<unsigned char> &binBuffer) {
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02006907 SerializeNumberProperty("byteLength", buffer.data.size(), o);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09006908 binBuffer = buffer.data;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02006909
6910 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
6911
6912 if (buffer.extras.Type() != NULL_TYPE) {
6913 SerializeValue("extras", buffer.extras, o);
6914 }
6915}
6916
johan bowald30c53472018-03-30 11:49:36 +02006917static void SerializeGltfBuffer(Buffer &buffer, json &o) {
6918 SerializeNumberProperty("byteLength", buffer.data.size(), o);
6919 SerializeGltfBufferData(buffer.data, o);
6920
6921 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006922
6923 if (buffer.extras.Type() != NULL_TYPE) {
6924 SerializeValue("extras", buffer.extras, o);
6925 }
johan bowald30c53472018-03-30 11:49:36 +02006926}
6927
Selmar Koke4677492018-10-25 16:45:49 +02006928static bool SerializeGltfBuffer(Buffer &buffer, json &o,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006929 const std::string &binFilename,
6930 const std::string &binBaseFilename) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006931 if (!SerializeGltfBufferData(buffer.data, binFilename)) return false;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006932 SerializeNumberProperty("byteLength", buffer.data.size(), o);
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006933 SerializeStringProperty("uri", binBaseFilename, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006934
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006935 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006936
6937 if (buffer.extras.Type() != NULL_TYPE) {
6938 SerializeValue("extras", buffer.extras, o);
6939 }
Selmar Koke4677492018-10-25 16:45:49 +02006940 return true;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006941}
6942
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006943static void SerializeGltfBufferView(BufferView &bufferView, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006944 SerializeNumberProperty("buffer", bufferView.buffer, o);
6945 SerializeNumberProperty<size_t>("byteLength", bufferView.byteLength, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006946
Johan Bowaldfaa27222018-03-28 14:44:45 +02006947 // byteStride is optional, minimum allowed is 4
6948 if (bufferView.byteStride >= 4) {
6949 SerializeNumberProperty<size_t>("byteStride", bufferView.byteStride, o);
6950 }
6951 // byteOffset is optional, default is 0
6952 if (bufferView.byteOffset > 0) {
6953 SerializeNumberProperty<size_t>("byteOffset", bufferView.byteOffset, o);
6954 }
6955 // Target is optional, check if it contains a valid value
6956 if (bufferView.target == TINYGLTF_TARGET_ARRAY_BUFFER ||
6957 bufferView.target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER) {
6958 SerializeNumberProperty("target", bufferView.target, o);
6959 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006960 if (bufferView.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006961 SerializeStringProperty("name", bufferView.name, o);
6962 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02006963
6964 if (bufferView.extras.Type() != NULL_TYPE) {
6965 SerializeValue("extras", bufferView.extras, o);
6966 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006967}
6968
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006969static void SerializeGltfImage(Image &image, json &o) {
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02006970 // if uri empty, the mimeType and bufferview should be set
rainliang00062be8d02019-04-29 09:54:27 +08006971 if (image.uri.empty()) {
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02006972 SerializeStringProperty("mimeType", image.mimeType, o);
6973 SerializeNumberProperty<int>("bufferView", image.bufferView, o);
6974 } else {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09006975 // TODO(syoyo): dlib::urilencode?
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02006976 SerializeStringProperty("uri", image.uri, o);
6977 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006978
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006979 if (image.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006980 SerializeStringProperty("name", image.name, o);
6981 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02006982
6983 if (image.extras.Type() != NULL_TYPE) {
6984 SerializeValue("extras", image.extras, o);
6985 }
Syoyo Fujita3e53feb2018-09-02 15:36:17 +09006986
6987 SerializeExtensionMap(image.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006988}
6989
Syoyo Fujita046400b2019-07-24 19:26:48 +09006990static void SerializeGltfTextureInfo(TextureInfo &texinfo, json &o) {
6991 SerializeNumberProperty("index", texinfo.index, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006992
Syoyo Fujita046400b2019-07-24 19:26:48 +09006993 if (texinfo.texCoord != 0) {
6994 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
6995 }
6996
6997 if (texinfo.extras.Type() != NULL_TYPE) {
6998 SerializeValue("extras", texinfo.extras, o);
6999 }
7000
7001 SerializeExtensionMap(texinfo.extensions, o);
7002}
7003
7004static void SerializeGltfNormalTextureInfo(NormalTextureInfo &texinfo,
7005 json &o) {
7006 SerializeNumberProperty("index", texinfo.index, o);
7007
7008 if (texinfo.texCoord != 0) {
7009 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
7010 }
7011
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007012 if (!TINYGLTF_DOUBLE_EQUAL(texinfo.scale, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007013 SerializeNumberProperty("scale", texinfo.scale, o);
7014 }
7015
7016 if (texinfo.extras.Type() != NULL_TYPE) {
7017 SerializeValue("extras", texinfo.extras, o);
7018 }
7019
7020 SerializeExtensionMap(texinfo.extensions, o);
7021}
7022
7023static void SerializeGltfOcclusionTextureInfo(OcclusionTextureInfo &texinfo,
7024 json &o) {
7025 SerializeNumberProperty("index", texinfo.index, o);
7026
7027 if (texinfo.texCoord != 0) {
7028 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
7029 }
7030
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007031 if (!TINYGLTF_DOUBLE_EQUAL(texinfo.strength, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007032 SerializeNumberProperty("strength", texinfo.strength, o);
7033 }
7034
7035 if (texinfo.extras.Type() != NULL_TYPE) {
7036 SerializeValue("extras", texinfo.extras, o);
7037 }
7038
7039 SerializeExtensionMap(texinfo.extensions, o);
7040}
7041
7042static void SerializeGltfPbrMetallicRoughness(PbrMetallicRoughness &pbr,
7043 json &o) {
7044 std::vector<double> default_baseColorFactor = {1.0, 1.0, 1.0, 1.0};
7045 if (!Equals(pbr.baseColorFactor, default_baseColorFactor)) {
7046 SerializeNumberArrayProperty<double>("baseColorFactor", pbr.baseColorFactor,
7047 o);
7048 }
7049
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007050 if (!TINYGLTF_DOUBLE_EQUAL(pbr.metallicFactor, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007051 SerializeNumberProperty("metallicFactor", pbr.metallicFactor, o);
7052 }
7053
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007054 if (!TINYGLTF_DOUBLE_EQUAL(pbr.roughnessFactor, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007055 SerializeNumberProperty("roughnessFactor", pbr.roughnessFactor, o);
7056 }
7057
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007058 if (pbr.baseColorTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007059 json texinfo;
7060 SerializeGltfTextureInfo(pbr.baseColorTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05007061 JsonAddMember(o, "baseColorTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007062 }
7063
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007064 if (pbr.metallicRoughnessTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007065 json texinfo;
7066 SerializeGltfTextureInfo(pbr.metallicRoughnessTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05007067 JsonAddMember(o, "metallicRoughnessTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007068 }
7069
7070 SerializeExtensionMap(pbr.extensions, o);
7071
7072 if (pbr.extras.Type() != NULL_TYPE) {
7073 SerializeValue("extras", pbr.extras, o);
7074 }
7075}
7076
7077static void SerializeGltfMaterial(Material &material, json &o) {
7078 if (material.name.size()) {
7079 SerializeStringProperty("name", material.name, o);
7080 }
7081
7082 // QUESTION(syoyo): Write material parameters regardless of its default value?
7083
7084 if (!TINYGLTF_DOUBLE_EQUAL(material.alphaCutoff, 0.5)) {
7085 SerializeNumberProperty("alphaCutoff", material.alphaCutoff, o);
7086 }
7087
Patrick Härtld9a468b2019-08-14 14:14:07 +02007088 if (material.alphaMode.compare("OPAQUE") != 0) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007089 SerializeStringProperty("alphaMode", material.alphaMode, o);
7090 }
7091
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007092 if (material.doubleSided != false)
7093 JsonAddMember(o, "doubleSided", json(material.doubleSided));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007094
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007095 if (material.normalTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007096 json texinfo;
7097 SerializeGltfNormalTextureInfo(material.normalTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05007098 JsonAddMember(o, "normalTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007099 }
7100
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007101 if (material.occlusionTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007102 json texinfo;
7103 SerializeGltfOcclusionTextureInfo(material.occlusionTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05007104 JsonAddMember(o, "occlusionTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007105 }
7106
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007107 if (material.emissiveTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007108 json texinfo;
7109 SerializeGltfTextureInfo(material.emissiveTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05007110 JsonAddMember(o, "emissiveTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007111 }
7112
7113 std::vector<double> default_emissiveFactor = {0.0, 0.0, 0.0};
7114 if (!Equals(material.emissiveFactor, default_emissiveFactor)) {
7115 SerializeNumberArrayProperty<double>("emissiveFactor",
7116 material.emissiveFactor, o);
7117 }
7118
7119 {
7120 json pbrMetallicRoughness;
7121 SerializeGltfPbrMetallicRoughness(material.pbrMetallicRoughness,
7122 pbrMetallicRoughness);
Syoyo Fujita7e009042019-09-13 15:32:22 +09007123 // Issue 204
7124 // Do not serialize `pbrMetallicRoughness` if pbrMetallicRoughness has all
7125 // default values(json is null). Otherwise it will serialize to
7126 // `pbrMetallicRoughness : null`, which cannot be read by other glTF
7127 // importers(and validators).
7128 //
7129 if (!JsonIsNull(pbrMetallicRoughness)) {
7130 JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness));
7131 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09007132 }
7133
7134#if 0 // legacy way. just for the record.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007135 if (material.values.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007136 json pbrMetallicRoughness;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007137 SerializeParameterMap(material.values, pbrMetallicRoughness);
jrkooncecba5d6c2019-08-29 11:26:22 -05007138 JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007139 }
7140
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007141 SerializeParameterMap(material.additionalValues, o);
Syoyo Fujita046400b2019-07-24 19:26:48 +09007142#else
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007143
Syoyo Fujita046400b2019-07-24 19:26:48 +09007144#endif
7145
7146 SerializeExtensionMap(material.extensions, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007147
7148 if (material.extras.Type() != NULL_TYPE) {
7149 SerializeValue("extras", material.extras, o);
7150 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007151}
7152
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007153static void SerializeGltfMesh(Mesh &mesh, json &o) {
Syoyo Fujita83675312017-12-02 21:14:13 +09007154 json primitives;
jrkooncecba5d6c2019-08-29 11:26:22 -05007155 JsonReserveArray(primitives, mesh.primitives.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007156 for (unsigned int i = 0; i < mesh.primitives.size(); ++i) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007157 json primitive;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007158 const Primitive &gltfPrimitive = mesh.primitives[i]; // don't make a copy
jrkooncecba5d6c2019-08-29 11:26:22 -05007159 {
7160 json attributes;
7161 for (auto attrIt = gltfPrimitive.attributes.begin();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007162 attrIt != gltfPrimitive.attributes.end(); ++attrIt) {
jrkooncecba5d6c2019-08-29 11:26:22 -05007163 SerializeNumberProperty<int>(attrIt->first, attrIt->second, attributes);
7164 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007165
jrkooncecba5d6c2019-08-29 11:26:22 -05007166 JsonAddMember(primitive, "attributes", std::move(attributes));
7167 }
Johan Bowaldfaa27222018-03-28 14:44:45 +02007168
7169 // Indicies is optional
7170 if (gltfPrimitive.indices > -1) {
7171 SerializeNumberProperty<int>("indices", gltfPrimitive.indices, primitive);
7172 }
7173 // Material is optional
7174 if (gltfPrimitive.material > -1) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09007175 SerializeNumberProperty<int>("material", gltfPrimitive.material,
7176 primitive);
Johan Bowaldfaa27222018-03-28 14:44:45 +02007177 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007178 SerializeNumberProperty<int>("mode", gltfPrimitive.mode, primitive);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007179
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007180 // Morph targets
7181 if (gltfPrimitive.targets.size()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09007182 json targets;
jrkooncecba5d6c2019-08-29 11:26:22 -05007183 JsonReserveArray(targets, gltfPrimitive.targets.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007184 for (unsigned int k = 0; k < gltfPrimitive.targets.size(); ++k) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007185 json targetAttributes;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007186 std::map<std::string, int> targetData = gltfPrimitive.targets[k];
7187 for (std::map<std::string, int>::iterator attrIt = targetData.begin();
7188 attrIt != targetData.end(); ++attrIt) {
7189 SerializeNumberProperty<int>(attrIt->first, attrIt->second,
7190 targetAttributes);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007191 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007192 JsonPushBack(targets, std::move(targetAttributes));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007193 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007194 JsonAddMember(primitive, "targets", std::move(targets));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007195 }
7196
zhaozhiquan3eb65e22019-12-18 11:28:57 +08007197 SerializeExtensionMap(gltfPrimitive.extensions, primitive);
Selmar Kok81b672b2019-10-18 16:08:44 +02007198
Jens Olssonb3af2f12018-06-04 10:17:49 +02007199 if (gltfPrimitive.extras.Type() != NULL_TYPE) {
7200 SerializeValue("extras", gltfPrimitive.extras, primitive);
7201 }
7202
jrkooncecba5d6c2019-08-29 11:26:22 -05007203 JsonPushBack(primitives, std::move(primitive));
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007204 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007205
jrkooncecba5d6c2019-08-29 11:26:22 -05007206 JsonAddMember(o, "primitives", std::move(primitives));
7207
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007208 if (mesh.weights.size()) {
7209 SerializeNumberArrayProperty<double>("weights", mesh.weights, o);
7210 }
7211
7212 if (mesh.name.size()) {
7213 SerializeStringProperty("name", mesh.name, o);
7214 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007215
Selmar Kok81b672b2019-10-18 16:08:44 +02007216 SerializeExtensionMap(mesh.extensions, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007217 if (mesh.extras.Type() != NULL_TYPE) {
7218 SerializeValue("extras", mesh.extras, o);
7219 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007220}
7221
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007222static void SerializeSpotLight(SpotLight &spot, json &o) {
Johan Bowald52936a02019-07-17 09:06:45 +02007223 SerializeNumberProperty("innerConeAngle", spot.innerConeAngle, o);
7224 SerializeNumberProperty("outerConeAngle", spot.outerConeAngle, o);
7225 SerializeExtensionMap(spot.extensions, o);
Selmar Kok81b672b2019-10-18 16:08:44 +02007226 if (spot.extras.Type() != NULL_TYPE) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09007227 SerializeValue("extras", spot.extras, o);
Selmar Kok81b672b2019-10-18 16:08:44 +02007228 }
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007229}
7230
Syoyo Fujita83675312017-12-02 21:14:13 +09007231static void SerializeGltfLight(Light &light, json &o) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09007232 if (!light.name.empty()) SerializeStringProperty("name", light.name, o);
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007233 SerializeNumberProperty("intensity", light.intensity, o);
Syoyo Fujita3dad3032020-05-13 21:22:23 +09007234 if (light.range > 0.0) {
7235 SerializeNumberProperty("range", light.range, o);
7236 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01007237 SerializeNumberArrayProperty("color", light.color, o);
7238 SerializeStringProperty("type", light.type, o);
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007239 if (light.type == "spot") {
7240 json spot;
7241 SerializeSpotLight(light.spot, spot);
jrkooncecba5d6c2019-08-29 11:26:22 -05007242 JsonAddMember(o, "spot", std::move(spot));
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007243 }
Selmar Kok81b672b2019-10-18 16:08:44 +02007244 SerializeExtensionMap(light.extensions, o);
7245 if (light.extras.Type() != NULL_TYPE) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09007246 SerializeValue("extras", light.extras, o);
Selmar Kok81b672b2019-10-18 16:08:44 +02007247 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01007248}
7249
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007250static void SerializeGltfNode(Node &node, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007251 if (node.translation.size() > 0) {
7252 SerializeNumberArrayProperty<double>("translation", node.translation, o);
7253 }
7254 if (node.rotation.size() > 0) {
7255 SerializeNumberArrayProperty<double>("rotation", node.rotation, o);
7256 }
7257 if (node.scale.size() > 0) {
7258 SerializeNumberArrayProperty<double>("scale", node.scale, o);
7259 }
7260 if (node.matrix.size() > 0) {
7261 SerializeNumberArrayProperty<double>("matrix", node.matrix, o);
7262 }
7263 if (node.mesh != -1) {
7264 SerializeNumberProperty<int>("mesh", node.mesh, o);
7265 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007266
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007267 if (node.skin != -1) {
7268 SerializeNumberProperty<int>("skin", node.skin, o);
7269 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007270
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007271 if (node.camera != -1) {
7272 SerializeNumberProperty<int>("camera", node.camera, o);
7273 }
7274
Benjamin Schmithüsen74c3c102019-08-16 14:24:26 +02007275 if (node.weights.size() > 0) {
7276 SerializeNumberArrayProperty<double>("weights", node.weights, o);
7277 }
7278
Jens Olssona9718662018-05-24 15:48:49 +02007279 if (node.extras.Type() != NULL_TYPE) {
Jens Olssonb96f6962018-05-24 15:29:54 +02007280 SerializeValue("extras", node.extras, o);
7281 }
7282
Selmar09d2ff12018-03-15 17:30:42 +01007283 SerializeExtensionMap(node.extensions, o);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09007284 if (!node.name.empty()) SerializeStringProperty("name", node.name, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007285 SerializeNumberArrayProperty<int>("children", node.children, o);
7286}
7287
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007288static void SerializeGltfSampler(Sampler &sampler, json &o) {
Syoyo Fujitaee179b22019-08-16 13:11:30 +09007289 if (sampler.magFilter != -1) {
7290 SerializeNumberProperty("magFilter", sampler.magFilter, o);
7291 }
7292 if (sampler.minFilter != -1) {
7293 SerializeNumberProperty("minFilter", sampler.minFilter, o);
7294 }
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09007295 // SerializeNumberProperty("wrapR", sampler.wrapR, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007296 SerializeNumberProperty("wrapS", sampler.wrapS, o);
7297 SerializeNumberProperty("wrapT", sampler.wrapT, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007298
7299 if (sampler.extras.Type() != NULL_TYPE) {
7300 SerializeValue("extras", sampler.extras, o);
7301 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007302}
7303
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007304static void SerializeGltfOrthographicCamera(const OrthographicCamera &camera,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007305 json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007306 SerializeNumberProperty("zfar", camera.zfar, o);
7307 SerializeNumberProperty("znear", camera.znear, o);
7308 SerializeNumberProperty("xmag", camera.xmag, o);
7309 SerializeNumberProperty("ymag", camera.ymag, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007310
7311 if (camera.extras.Type() != NULL_TYPE) {
7312 SerializeValue("extras", camera.extras, o);
7313 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007314}
7315
7316static void SerializeGltfPerspectiveCamera(const PerspectiveCamera &camera,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007317 json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007318 SerializeNumberProperty("zfar", camera.zfar, o);
7319 SerializeNumberProperty("znear", camera.znear, o);
7320 if (camera.aspectRatio > 0) {
7321 SerializeNumberProperty("aspectRatio", camera.aspectRatio, o);
7322 }
7323
7324 if (camera.yfov > 0) {
7325 SerializeNumberProperty("yfov", camera.yfov, o);
7326 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007327
7328 if (camera.extras.Type() != NULL_TYPE) {
7329 SerializeValue("extras", camera.extras, o);
7330 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007331}
7332
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007333static void SerializeGltfCamera(const Camera &camera, json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007334 SerializeStringProperty("type", camera.type, o);
7335 if (!camera.name.empty()) {
Selmar Kokee3d0662018-10-08 16:20:43 +02007336 SerializeStringProperty("name", camera.name, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007337 }
7338
7339 if (camera.type.compare("orthographic") == 0) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007340 json orthographic;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007341 SerializeGltfOrthographicCamera(camera.orthographic, orthographic);
jrkooncecba5d6c2019-08-29 11:26:22 -05007342 JsonAddMember(o, "orthographic", std::move(orthographic));
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007343 } else if (camera.type.compare("perspective") == 0) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007344 json perspective;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007345 SerializeGltfPerspectiveCamera(camera.perspective, perspective);
jrkooncecba5d6c2019-08-29 11:26:22 -05007346 JsonAddMember(o, "perspective", std::move(perspective));
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007347 } else {
7348 // ???
7349 }
Syoyofe77cc52020-05-09 02:41:07 +09007350
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007351 if (camera.extras.Type() != NULL_TYPE) {
Syoyofe77cc52020-05-09 02:41:07 +09007352 SerializeValue("extras", camera.extras, o);
7353 }
7354 SerializeExtensionMap(camera.extensions, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007355}
7356
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007357static void SerializeGltfScene(Scene &scene, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007358 SerializeNumberArrayProperty<int>("nodes", scene.nodes, o);
7359
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007360 if (scene.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007361 SerializeStringProperty("name", scene.name, o);
7362 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007363 if (scene.extras.Type() != NULL_TYPE) {
Selmar09d2ff12018-03-15 17:30:42 +01007364 SerializeValue("extras", scene.extras, o);
7365 }
7366 SerializeExtensionMap(scene.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007367}
7368
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007369static void SerializeGltfSkin(Skin &skin, json &o) {
Syoyo Fujitad07b9762021-04-28 19:15:16 +09007370 // required
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007371 SerializeNumberArrayProperty<int>("joints", skin.joints, o);
Syoyo Fujitad07b9762021-04-28 19:15:16 +09007372
7373 if (skin.inverseBindMatrices >= 0) {
7374 SerializeNumberProperty("inverseBindMatrices", skin.inverseBindMatrices, o);
7375 }
7376
7377 if (skin.skeleton >= 0) {
7378 SerializeNumberProperty("skeleton", skin.skeleton, o);
7379 }
7380
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007381 if (skin.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007382 SerializeStringProperty("name", skin.name, o);
7383 }
7384}
7385
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007386static void SerializeGltfTexture(Texture &texture, json &o) {
johan bowaldb97d34c2018-04-02 07:29:29 +02007387 if (texture.sampler > -1) {
johan bowald642a3432018-04-01 12:37:18 +02007388 SerializeNumberProperty("sampler", texture.sampler, o);
7389 }
Selmar Kok8eb39042018-10-05 14:29:35 +02007390 if (texture.source > -1) {
7391 SerializeNumberProperty("source", texture.source, o);
7392 }
Christophe820ede82019-07-04 15:21:21 +09007393 if (texture.name.size()) {
7394 SerializeStringProperty("name", texture.name, o);
7395 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007396 if (texture.extras.Type() != NULL_TYPE) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007397 SerializeValue("extras", texture.extras, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007398 }
Selmar Kok9eae1102018-04-04 18:10:37 +02007399 SerializeExtensionMap(texture.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007400}
7401
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007402///
7403/// Serialize all properties except buffers and images.
7404///
7405static void SerializeGltfModel(Model *model, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007406 // ACCESSORS
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007407 if (model->accessors.size()) {
7408 json accessors;
7409 JsonReserveArray(accessors, model->accessors.size());
7410 for (unsigned int i = 0; i < model->accessors.size(); ++i) {
7411 json accessor;
7412 SerializeGltfAccessor(model->accessors[i], accessor);
7413 JsonPushBack(accessors, std::move(accessor));
7414 }
7415 JsonAddMember(o, "accessors", std::move(accessors));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007416 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007417
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007418 // ANIMATIONS
7419 if (model->animations.size()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09007420 json animations;
jrkooncecba5d6c2019-08-29 11:26:22 -05007421 JsonReserveArray(animations, model->animations.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007422 for (unsigned int i = 0; i < model->animations.size(); ++i) {
7423 if (model->animations[i].channels.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007424 json animation;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007425 SerializeGltfAnimation(model->animations[i], animation);
jrkooncecba5d6c2019-08-29 11:26:22 -05007426 JsonPushBack(animations, std::move(animation));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007427 }
7428 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007429
jrkooncecba5d6c2019-08-29 11:26:22 -05007430 JsonAddMember(o, "animations", std::move(animations));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007431 }
7432
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007433 // ASSET
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007434 json asset;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007435 SerializeGltfAsset(model->asset, asset);
jrkooncecba5d6c2019-08-29 11:26:22 -05007436 JsonAddMember(o, "asset", std::move(asset));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007437
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007438 // BUFFERVIEWS
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007439 if (model->bufferViews.size()) {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007440 json bufferViews;
7441 JsonReserveArray(bufferViews, model->bufferViews.size());
7442 for (unsigned int i = 0; i < model->bufferViews.size(); ++i) {
7443 json bufferView;
7444 SerializeGltfBufferView(model->bufferViews[i], bufferView);
7445 JsonPushBack(bufferViews, std::move(bufferView));
7446 }
7447 JsonAddMember(o, "bufferViews", std::move(bufferViews));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007448 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007449
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007450 // Extensions required
7451 if (model->extensionsRequired.size()) {
7452 SerializeStringArrayProperty("extensionsRequired",
7453 model->extensionsRequired, o);
7454 }
7455
7456 // MATERIALS
7457 if (model->materials.size()) {
7458 json materials;
jrkooncecba5d6c2019-08-29 11:26:22 -05007459 JsonReserveArray(materials, model->materials.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007460 for (unsigned int i = 0; i < model->materials.size(); ++i) {
7461 json material;
7462 SerializeGltfMaterial(model->materials[i], material);
Syoyo Fujita68adc4b2020-10-22 22:27:12 +09007463
7464 if (JsonIsNull(material)) {
7465 // Issue 294.
7466 // `material` does not have any required parameters
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09007467 // so the result may be null(unmodified) when all material parameters
7468 // have default value.
Syoyo Fujita68adc4b2020-10-22 22:27:12 +09007469 //
7470 // null is not allowed thus we create an empty JSON object.
7471 JsonSetObject(material);
7472 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007473 JsonPushBack(materials, std::move(material));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007474 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007475 JsonAddMember(o, "materials", std::move(materials));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007476 }
7477
7478 // MESHES
7479 if (model->meshes.size()) {
7480 json meshes;
jrkooncecba5d6c2019-08-29 11:26:22 -05007481 JsonReserveArray(meshes, model->meshes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007482 for (unsigned int i = 0; i < model->meshes.size(); ++i) {
7483 json mesh;
7484 SerializeGltfMesh(model->meshes[i], mesh);
jrkooncecba5d6c2019-08-29 11:26:22 -05007485 JsonPushBack(meshes, std::move(mesh));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007486 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007487 JsonAddMember(o, "meshes", std::move(meshes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007488 }
7489
7490 // NODES
7491 if (model->nodes.size()) {
7492 json nodes;
jrkooncecba5d6c2019-08-29 11:26:22 -05007493 JsonReserveArray(nodes, model->nodes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007494 for (unsigned int i = 0; i < model->nodes.size(); ++i) {
7495 json node;
7496 SerializeGltfNode(model->nodes[i], node);
jrkooncecba5d6c2019-08-29 11:26:22 -05007497 JsonPushBack(nodes, std::move(node));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007498 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007499 JsonAddMember(o, "nodes", std::move(nodes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007500 }
7501
7502 // SCENE
7503 if (model->defaultScene > -1) {
7504 SerializeNumberProperty<int>("scene", model->defaultScene, o);
7505 }
7506
7507 // SCENES
7508 if (model->scenes.size()) {
7509 json scenes;
jrkooncecba5d6c2019-08-29 11:26:22 -05007510 JsonReserveArray(scenes, model->scenes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007511 for (unsigned int i = 0; i < model->scenes.size(); ++i) {
7512 json currentScene;
7513 SerializeGltfScene(model->scenes[i], currentScene);
jrkooncecba5d6c2019-08-29 11:26:22 -05007514 JsonPushBack(scenes, std::move(currentScene));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007515 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007516 JsonAddMember(o, "scenes", std::move(scenes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007517 }
7518
7519 // SKINS
7520 if (model->skins.size()) {
7521 json skins;
jrkooncecba5d6c2019-08-29 11:26:22 -05007522 JsonReserveArray(skins, model->skins.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007523 for (unsigned int i = 0; i < model->skins.size(); ++i) {
7524 json skin;
7525 SerializeGltfSkin(model->skins[i], skin);
jrkooncecba5d6c2019-08-29 11:26:22 -05007526 JsonPushBack(skins, std::move(skin));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007527 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007528 JsonAddMember(o, "skins", std::move(skins));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007529 }
7530
7531 // TEXTURES
7532 if (model->textures.size()) {
7533 json textures;
jrkooncecba5d6c2019-08-29 11:26:22 -05007534 JsonReserveArray(textures, model->textures.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007535 for (unsigned int i = 0; i < model->textures.size(); ++i) {
7536 json texture;
7537 SerializeGltfTexture(model->textures[i], texture);
jrkooncecba5d6c2019-08-29 11:26:22 -05007538 JsonPushBack(textures, std::move(texture));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007539 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007540 JsonAddMember(o, "textures", std::move(textures));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007541 }
7542
7543 // SAMPLERS
7544 if (model->samplers.size()) {
7545 json samplers;
jrkooncecba5d6c2019-08-29 11:26:22 -05007546 JsonReserveArray(samplers, model->samplers.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007547 for (unsigned int i = 0; i < model->samplers.size(); ++i) {
7548 json sampler;
7549 SerializeGltfSampler(model->samplers[i], sampler);
jrkooncecba5d6c2019-08-29 11:26:22 -05007550 JsonPushBack(samplers, std::move(sampler));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007551 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007552 JsonAddMember(o, "samplers", std::move(samplers));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007553 }
7554
7555 // CAMERAS
7556 if (model->cameras.size()) {
7557 json cameras;
jrkooncecba5d6c2019-08-29 11:26:22 -05007558 JsonReserveArray(cameras, model->cameras.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007559 for (unsigned int i = 0; i < model->cameras.size(); ++i) {
7560 json camera;
7561 SerializeGltfCamera(model->cameras[i], camera);
jrkooncecba5d6c2019-08-29 11:26:22 -05007562 JsonPushBack(cameras, std::move(camera));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007563 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007564 JsonAddMember(o, "cameras", std::move(cameras));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007565 }
7566
7567 // EXTENSIONS
7568 SerializeExtensionMap(model->extensions, o);
7569
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007570 auto extensionsUsed = model->extensionsUsed;
7571
7572 // LIGHTS as KHR_lights_punctual
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007573 if (model->lights.size()) {
7574 json lights;
jrkooncecba5d6c2019-08-29 11:26:22 -05007575 JsonReserveArray(lights, model->lights.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007576 for (unsigned int i = 0; i < model->lights.size(); ++i) {
7577 json light;
7578 SerializeGltfLight(model->lights[i], light);
jrkooncecba5d6c2019-08-29 11:26:22 -05007579 JsonPushBack(lights, std::move(light));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007580 }
7581 json khr_lights_cmn;
jrkooncecba5d6c2019-08-29 11:26:22 -05007582 JsonAddMember(khr_lights_cmn, "lights", std::move(lights));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007583 json ext_j;
7584
jrkooncecba5d6c2019-08-29 11:26:22 -05007585 {
7586 json_const_iterator it;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007587 if (FindMember(o, "extensions", it)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05007588 JsonAssign(ext_j, GetValue(it));
7589 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007590 }
7591
jrkooncecba5d6c2019-08-29 11:26:22 -05007592 JsonAddMember(ext_j, "KHR_lights_punctual", std::move(khr_lights_cmn));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007593
jrkooncecba5d6c2019-08-29 11:26:22 -05007594 JsonAddMember(o, "extensions", std::move(ext_j));
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007595
7596 // Also add "KHR_lights_punctual" to `extensionsUsed`
7597 {
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04007598 auto has_khr_lights_punctual =
7599 std::find_if(extensionsUsed.begin(), extensionsUsed.end(),
7600 [](const std::string &s) {
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08007601 return (s.compare("KHR_lights_punctual") == 0);
7602 });
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007603
7604 if (has_khr_lights_punctual == extensionsUsed.end()) {
7605 extensionsUsed.push_back("KHR_lights_punctual");
7606 }
7607 }
7608 }
7609
7610 // Extensions used
Selmar1208f052021-02-01 19:24:41 +01007611 if (extensionsUsed.size()) {
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007612 SerializeStringArrayProperty("extensionsUsed", extensionsUsed, o);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007613 }
7614
7615 // EXTRAS
7616 if (model->extras.Type() != NULL_TYPE) {
7617 SerializeValue("extras", model->extras, o);
7618 }
7619}
7620
Johan Bowald52936a02019-07-17 09:06:45 +02007621static bool WriteGltfStream(std::ostream &stream, const std::string &content) {
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007622 stream << content << std::endl;
7623 return true;
7624}
7625
7626static bool WriteGltfFile(const std::string &output,
7627 const std::string &content) {
Harokyangfb256602019-10-30 16:13:52 +08007628#ifdef _WIN32
Syoyo Fujita45cac782019-11-09 20:42:55 +09007629#if defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08007630 std::ofstream gltfFile(UTF8ToWchar(output).c_str());
Syoyo Fujita45cac782019-11-09 20:42:55 +09007631#elif defined(__GLIBCXX__)
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007632 int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
7633 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
7634 __gnu_cxx::stdio_filebuf<char> wfile_buf(
7635 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09007636 std::ostream gltfFile(&wfile_buf);
7637 if (!wfile_buf.is_open()) return false;
Harokyangfb256602019-10-30 16:13:52 +08007638#else
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007639 std::ofstream gltfFile(output.c_str());
7640 if (!gltfFile.is_open()) return false;
Syoyo Fujita45cac782019-11-09 20:42:55 +09007641#endif
7642#else
7643 std::ofstream gltfFile(output.c_str());
7644 if (!gltfFile.is_open()) return false;
7645#endif
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007646 return WriteGltfStream(gltfFile, content);
7647}
7648
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007649static bool WriteBinaryGltfStream(std::ostream &stream,
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007650 const std::string &content,
7651 const std::vector<unsigned char> &binBuffer) {
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007652 const std::string header = "glTF";
7653 const int version = 2;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007654
Alexander Wood190382a2021-10-08 12:19:13 -04007655 const uint32_t content_size = uint32_t(content.size());
7656 const uint32_t binBuffer_size = uint32_t(binBuffer.size());
7657 // determine number of padding bytes required to ensure 4 byte alignment
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09007658 const uint32_t content_padding_size =
7659 content_size % 4 == 0 ? 0 : 4 - content_size % 4;
7660 const uint32_t bin_padding_size =
7661 binBuffer_size % 4 == 0 ? 0 : 4 - binBuffer_size % 4;
Syoyo Fujita1d205202019-11-16 17:00:17 +09007662
7663 // 12 bytes for header, JSON content length, 8 bytes for JSON chunk info.
Alexander Wood190382a2021-10-08 12:19:13 -04007664 // Chunk data must be located at 4-byte boundary, which may require padding
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007665 const uint32_t length =
Alexander Wood9e3d1f62021-10-08 16:57:56 -04007666 12 + 8 + content_size + content_padding_size +
Alexander Wood13803652021-10-08 20:13:07 -04007667 (binBuffer_size ? (8 + binBuffer_size + bin_padding_size) : 0);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007668
Syoyo Fujitacea69e32019-08-20 17:10:30 +09007669 stream.write(header.c_str(), std::streamsize(header.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007670 stream.write(reinterpret_cast<const char *>(&version), sizeof(version));
7671 stream.write(reinterpret_cast<const char *>(&length), sizeof(length));
7672
7673 // JSON chunk info, then JSON data
Alexander Wood9e3d1f62021-10-08 16:57:56 -04007674 const uint32_t model_length = uint32_t(content.size()) + content_padding_size;
Syoyo Fujita72f4a552020-01-08 00:40:41 +09007675 const uint32_t model_format = 0x4E4F534A;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007676 stream.write(reinterpret_cast<const char *>(&model_length),
Johan Bowald52936a02019-07-17 09:06:45 +02007677 sizeof(model_length));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007678 stream.write(reinterpret_cast<const char *>(&model_format),
Johan Bowald52936a02019-07-17 09:06:45 +02007679 sizeof(model_format));
Syoyo Fujitacea69e32019-08-20 17:10:30 +09007680 stream.write(content.c_str(), std::streamsize(content.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007681
7682 // Chunk must be multiplies of 4, so pad with spaces
Alexander Wood9e3d1f62021-10-08 16:57:56 -04007683 if (content_padding_size > 0) {
7684 const std::string padding = std::string(size_t(content_padding_size), ' ');
Syoyo Fujitacea69e32019-08-20 17:10:30 +09007685 stream.write(padding.c_str(), std::streamsize(padding.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007686 }
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007687 if (binBuffer.size() > 0) {
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007688 // BIN chunk info, then BIN data
Syoyo Fujita72f4a552020-01-08 00:40:41 +09007689 const uint32_t bin_length = uint32_t(binBuffer.size()) + bin_padding_size;
7690 const uint32_t bin_format = 0x004e4942;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007691 stream.write(reinterpret_cast<const char *>(&bin_length),
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007692 sizeof(bin_length));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007693 stream.write(reinterpret_cast<const char *>(&bin_format),
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007694 sizeof(bin_format));
7695 stream.write(reinterpret_cast<const char *>(binBuffer.data()),
7696 std::streamsize(binBuffer.size()));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007697 // Chunksize must be multiplies of 4, so pad with zeroes
7698 if (bin_padding_size > 0) {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007699 const std::vector<unsigned char> padding =
7700 std::vector<unsigned char>(size_t(bin_padding_size), 0);
7701 stream.write(reinterpret_cast<const char *>(padding.data()),
7702 std::streamsize(padding.size()));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007703 }
7704 }
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007705
7706 // TODO: Check error on stream.write
7707 return true;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007708}
7709
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007710static bool WriteBinaryGltfFile(const std::string &output,
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007711 const std::string &content,
7712 const std::vector<unsigned char> &binBuffer) {
Harokyangfb256602019-10-30 16:13:52 +08007713#ifdef _WIN32
Syoyo Fujita45cac782019-11-09 20:42:55 +09007714#if defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08007715 std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09007716#elif defined(__GLIBCXX__)
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007717 int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
7718 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
7719 __gnu_cxx::stdio_filebuf<char> wfile_buf(
7720 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita4ab03862019-11-10 15:31:17 +09007721 std::ostream gltfFile(&wfile_buf);
Harokyangfb256602019-10-30 16:13:52 +08007722#else
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007723 std::ofstream gltfFile(output.c_str(), std::ios::binary);
Harokyangfb256602019-10-30 16:13:52 +08007724#endif
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007725#else
7726 std::ofstream gltfFile(output.c_str(), std::ios::binary);
jrkooncecba5d6c2019-08-29 11:26:22 -05007727#endif
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007728 return WriteBinaryGltfStream(gltfFile, content, binBuffer);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007729}
7730
7731bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
7732 bool prettyPrint = true,
7733 bool writeBinary = false) {
7734 JsonDocument output;
7735
7736 /// Serialize all properties except buffers and images.
7737 SerializeGltfModel(model, output);
7738
7739 // BUFFERS
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007740 std::vector<unsigned char> binBuffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007741 if (model->buffers.size()) {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007742 json buffers;
7743 JsonReserveArray(buffers, model->buffers.size());
7744 for (unsigned int i = 0; i < model->buffers.size(); ++i) {
7745 json buffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007746 if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
7747 SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007748 } else {
7749 SerializeGltfBuffer(model->buffers[i], buffer);
7750 }
7751 JsonPushBack(buffers, std::move(buffer));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007752 }
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007753 JsonAddMember(output, "buffers", std::move(buffers));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007754 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007755
7756 // IMAGES
7757 if (model->images.size()) {
7758 json images;
jrkooncecba5d6c2019-08-29 11:26:22 -05007759 JsonReserveArray(images, model->images.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007760 for (unsigned int i = 0; i < model->images.size(); ++i) {
7761 json image;
7762
7763 std::string dummystring = "";
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007764 // UpdateImageObject need baseDir but only uses it if embeddedImages is
7765 // enabled, since we won't write separate images when writing to a stream
7766 // we
Hendrik Schwanekamp41e11022022-06-29 12:22:40 +02007767 UpdateImageObject(model->images[i], dummystring, int(i), true,
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007768 &this->WriteImageData, this->write_image_user_data_);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007769 SerializeGltfImage(model->images[i], image);
jrkooncecba5d6c2019-08-29 11:26:22 -05007770 JsonPushBack(images, std::move(image));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007771 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007772 JsonAddMember(output, "images", std::move(images));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007773 }
7774
7775 if (writeBinary) {
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007776 return WriteBinaryGltfStream(stream, JsonToString(output), binBuffer);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007777 } else {
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007778 return WriteGltfStream(stream, JsonToString(output, prettyPrint ? 2 : -1));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007779 }
7780
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007781}
7782
7783bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
7784 bool embedImages = false,
7785 bool embedBuffers = false,
7786 bool prettyPrint = true,
7787 bool writeBinary = false) {
jrkooncecba5d6c2019-08-29 11:26:22 -05007788 JsonDocument output;
Selmar Kok7cb31e42018-10-05 16:02:29 +02007789 std::string defaultBinFilename = GetBaseFilename(filename);
7790 std::string defaultBinFileExt = ".bin";
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00007791 std::string::size_type pos =
7792 defaultBinFilename.rfind('.', defaultBinFilename.length());
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007793
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007794 if (pos != std::string::npos) {
Selmar Kok7cb31e42018-10-05 16:02:29 +02007795 defaultBinFilename = defaultBinFilename.substr(0, pos);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007796 }
johan bowald642a3432018-04-01 12:37:18 +02007797 std::string baseDir = GetBaseDir(filename);
7798 if (baseDir.empty()) {
7799 baseDir = "./";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05007800 }
Johan Bowald52936a02019-07-17 09:06:45 +02007801 /// Serialize all properties except buffers and images.
7802 SerializeGltfModel(model, output);
Syoyo Fujitad42767e2018-03-15 21:52:00 -05007803
Selmar Kok7cb31e42018-10-05 16:02:29 +02007804 // BUFFERS
Selmar Kokc884e582018-10-05 16:25:54 +02007805 std::vector<std::string> usedUris;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007806 std::vector<unsigned char> binBuffer;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007807 if (model->buffers.size()) {
7808 json buffers;
7809 JsonReserveArray(buffers, model->buffers.size());
7810 for (unsigned int i = 0; i < model->buffers.size(); ++i) {
7811 json buffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007812 if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
7813 SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007814 } else if (embedBuffers) {
7815 SerializeGltfBuffer(model->buffers[i], buffer);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00007816 } else {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007817 std::string binSavePath;
7818 std::string binUri;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007819 if (!model->buffers[i].uri.empty() &&
7820 !IsDataURI(model->buffers[i].uri)) {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007821 binUri = model->buffers[i].uri;
7822 } else {
7823 binUri = defaultBinFilename + defaultBinFileExt;
7824 bool inUse = true;
7825 int numUsed = 0;
7826 while (inUse) {
7827 inUse = false;
7828 for (const std::string &usedName : usedUris) {
7829 if (binUri.compare(usedName) != 0) continue;
7830 inUse = true;
7831 binUri = defaultBinFilename + std::to_string(numUsed++) +
7832 defaultBinFileExt;
7833 break;
7834 }
Selmar Kokc884e582018-10-05 16:25:54 +02007835 }
7836 }
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007837 usedUris.push_back(binUri);
7838 binSavePath = JoinPath(baseDir, binUri);
7839 if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
7840 binUri)) {
7841 return false;
7842 }
Selmar Kokc884e582018-10-05 16:25:54 +02007843 }
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007844 JsonPushBack(buffers, std::move(buffer));
johan bowald30c53472018-03-30 11:49:36 +02007845 }
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007846 JsonAddMember(output, "buffers", std::move(buffers));
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007847 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007848
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007849 // IMAGES
Johan Bowaldfaa27222018-03-28 14:44:45 +02007850 if (model->images.size()) {
7851 json images;
jrkooncecba5d6c2019-08-29 11:26:22 -05007852 JsonReserveArray(images, model->images.size());
Johan Bowaldfaa27222018-03-28 14:44:45 +02007853 for (unsigned int i = 0; i < model->images.size(); ++i) {
7854 json image;
johan bowald642a3432018-04-01 12:37:18 +02007855
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09007856 UpdateImageObject(model->images[i], baseDir, int(i), embedImages,
Johan Bowald77decfa2018-11-06 14:28:20 +01007857 &this->WriteImageData, this->write_image_user_data_);
Johan Bowaldfaa27222018-03-28 14:44:45 +02007858 SerializeGltfImage(model->images[i], image);
jrkooncecba5d6c2019-08-29 11:26:22 -05007859 JsonPushBack(images, std::move(image));
Johan Bowaldfaa27222018-03-28 14:44:45 +02007860 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007861 JsonAddMember(output, "images", std::move(images));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007862 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007863
David Harmonda9eac22018-08-30 08:06:05 -04007864 if (writeBinary) {
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007865 return WriteBinaryGltfFile(filename, JsonToString(output), binBuffer);
David Harmonda9eac22018-08-30 08:06:05 -04007866 } else {
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007867 return WriteGltfFile(filename, JsonToString(output, (prettyPrint ? 2 : -1)));
David Harmonda9eac22018-08-30 08:06:05 -04007868 }
7869
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007870}
7871
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09007872} // namespace tinygltf
7873
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09007874#ifdef __clang__
7875#pragma clang diagnostic pop
7876#endif
7877
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007878#endif // TINYGLTF_IMPLEMENTATION