blob: fdd0679b5591db6b4f9c6d1d560b7066f67491c1 [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
jrkoonce51453942019-09-03 09:48:30 -050069#ifndef TINYGLTF_USE_CPP14
70#include <functional>
71#endif
72
Sascha Willems5f9cb242018-12-28 20:53:41 +010073#ifdef __ANDROID__
74#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
75#include <android/asset_manager.h>
76#endif
77#endif
78
Selmar Kok79e3df22019-10-29 16:22:07 +010079#ifdef __GNUC__
80#if (__GNUC__ < 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ <= 8))
Selmar Kokb74fade2019-10-29 16:09:32 +010081#define TINYGLTF_NOEXCEPT
Selmar Kok79e3df22019-10-29 16:22:07 +010082#else
83#define TINYGLTF_NOEXCEPT noexcept
Selmar Kokb74fade2019-10-29 16:09:32 +010084#endif
Selmar Kok79e3df22019-10-29 16:22:07 +010085#else
86#define TINYGLTF_NOEXCEPT noexcept
87#endif
88
Syoyo Fujita6e08b172019-10-30 17:25:38 +090089#define DEFAULT_METHODS(x) \
90 ~x() = default; \
91 x(const x &) = default; \
92 x(x &&) TINYGLTF_NOEXCEPT = default; \
93 x &operator=(const x &) = default; \
94 x &operator=(x &&) TINYGLTF_NOEXCEPT = default;
Selmar Kokb74fade2019-10-29 16:09:32 +010095
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090096namespace tinygltf {
97
98#define TINYGLTF_MODE_POINTS (0)
99#define TINYGLTF_MODE_LINE (1)
100#define TINYGLTF_MODE_LINE_LOOP (2)
Thomas Tissot6c4a0062019-01-30 13:10:51 +0100101#define TINYGLTF_MODE_LINE_STRIP (3)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900102#define TINYGLTF_MODE_TRIANGLES (4)
103#define TINYGLTF_MODE_TRIANGLE_STRIP (5)
104#define TINYGLTF_MODE_TRIANGLE_FAN (6)
105
106#define TINYGLTF_COMPONENT_TYPE_BYTE (5120)
107#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE (5121)
108#define TINYGLTF_COMPONENT_TYPE_SHORT (5122)
109#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT (5123)
110#define TINYGLTF_COMPONENT_TYPE_INT (5124)
111#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125)
112#define TINYGLTF_COMPONENT_TYPE_FLOAT (5126)
Syoyo Fujita52ff00a2022-08-16 20:08:45 +0900113#define TINYGLTF_COMPONENT_TYPE_DOUBLE \
114 (5130) // OpenGL double type. Note that some of glTF 2.0 validator does not
115 // support double type even the schema seems allow any value of
116 // integer:
117 // https://github.com/KhronosGroup/glTF/blob/b9884a2fd45130b4d673dd6c8a706ee21ee5c5f7/specification/2.0/schema/accessor.schema.json#L22
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900118
Syoyo Fujitac2615632016-06-19 21:56:06 +0900119#define TINYGLTF_TEXTURE_FILTER_NEAREST (9728)
120#define TINYGLTF_TEXTURE_FILTER_LINEAR (9729)
121#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST (9984)
122#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST (9985)
123#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR (9986)
124#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR (9987)
125
Cemalettin Dervis246d8662017-12-07 20:29:51 +0100126#define TINYGLTF_TEXTURE_WRAP_REPEAT (10497)
Syoyo Fujitac2615632016-06-19 21:56:06 +0900127#define TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE (33071)
Luke San Antonio Bialecki904612c2016-08-19 17:28:03 -0400128#define TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT (33648)
Syoyo Fujitac2615632016-06-19 21:56:06 +0900129
Luke San Antoniocdf4cb72016-06-14 21:32:11 -0400130// Redeclarations of the above for technique.parameters.
131#define TINYGLTF_PARAMETER_TYPE_BYTE (5120)
132#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE (5121)
133#define TINYGLTF_PARAMETER_TYPE_SHORT (5122)
134#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT (5123)
135#define TINYGLTF_PARAMETER_TYPE_INT (5124)
136#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT (5125)
137#define TINYGLTF_PARAMETER_TYPE_FLOAT (5126)
138
139#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC2 (35664)
140#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC3 (35665)
141#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC4 (35666)
142
143#define TINYGLTF_PARAMETER_TYPE_INT_VEC2 (35667)
144#define TINYGLTF_PARAMETER_TYPE_INT_VEC3 (35668)
145#define TINYGLTF_PARAMETER_TYPE_INT_VEC4 (35669)
146
147#define TINYGLTF_PARAMETER_TYPE_BOOL (35670)
148#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC2 (35671)
149#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC3 (35672)
150#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC4 (35673)
151
152#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT2 (35674)
153#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT3 (35675)
154#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT4 (35676)
155
156#define TINYGLTF_PARAMETER_TYPE_SAMPLER_2D (35678)
157
158// End parameter types
159
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900160#define TINYGLTF_TYPE_VEC2 (2)
161#define TINYGLTF_TYPE_VEC3 (3)
162#define TINYGLTF_TYPE_VEC4 (4)
163#define TINYGLTF_TYPE_MAT2 (32 + 2)
164#define TINYGLTF_TYPE_MAT3 (32 + 3)
165#define TINYGLTF_TYPE_MAT4 (32 + 4)
166#define TINYGLTF_TYPE_SCALAR (64 + 1)
167#define TINYGLTF_TYPE_VECTOR (64 + 4)
168#define TINYGLTF_TYPE_MATRIX (64 + 16)
169
170#define TINYGLTF_IMAGE_FORMAT_JPEG (0)
171#define TINYGLTF_IMAGE_FORMAT_PNG (1)
172#define TINYGLTF_IMAGE_FORMAT_BMP (2)
173#define TINYGLTF_IMAGE_FORMAT_GIF (3)
174
Luke San Antonio6d616f52016-06-23 14:09:23 -0400175#define TINYGLTF_TEXTURE_FORMAT_ALPHA (6406)
176#define TINYGLTF_TEXTURE_FORMAT_RGB (6407)
Syoyo Fujitabde70212016-02-07 17:38:17 +0900177#define TINYGLTF_TEXTURE_FORMAT_RGBA (6408)
Luke San Antonio6d616f52016-06-23 14:09:23 -0400178#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE (6409)
179#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE_ALPHA (6410)
180
Syoyo Fujitabde70212016-02-07 17:38:17 +0900181#define TINYGLTF_TEXTURE_TARGET_TEXTURE2D (3553)
182#define TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE (5121)
183
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900184#define TINYGLTF_TARGET_ARRAY_BUFFER (34962)
185#define TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER (34963)
186
Luke San Antoniocdf4cb72016-06-14 21:32:11 -0400187#define TINYGLTF_SHADER_TYPE_VERTEX_SHADER (35633)
188#define TINYGLTF_SHADER_TYPE_FRAGMENT_SHADER (35632)
189
Selmar Kok31cb7f92018-10-03 15:39:05 +0200190#define TINYGLTF_DOUBLE_EPS (1.e-12)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900191#define TINYGLTF_DOUBLE_EQUAL(a, b) (std::fabs((b) - (a)) < TINYGLTF_DOUBLE_EPS)
Selmar Kok31cb7f92018-10-03 15:39:05 +0200192
Sascha Willems5f9cb242018-12-28 20:53:41 +0100193#ifdef __ANDROID__
194#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000195AAssetManager *asset_manager = nullptr;
Sascha Willems5f9cb242018-12-28 20:53:41 +0100196#endif
197#endif
198
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900199typedef enum {
Idriss Chaouch425195b2021-05-20 12:11:00 +0100200 NULL_TYPE,
201 REAL_TYPE,
202 INT_TYPE,
203 BOOL_TYPE,
204 STRING_TYPE,
205 ARRAY_TYPE,
206 BINARY_TYPE,
207 OBJECT_TYPE
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900208} Type;
209
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500210static inline int32_t GetComponentSizeInBytes(uint32_t componentType) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900211 if (componentType == TINYGLTF_COMPONENT_TYPE_BYTE) {
212 return 1;
213 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
214 return 1;
215 } else if (componentType == TINYGLTF_COMPONENT_TYPE_SHORT) {
216 return 2;
217 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
218 return 2;
219 } else if (componentType == TINYGLTF_COMPONENT_TYPE_INT) {
220 return 4;
221 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) {
222 return 4;
223 } else if (componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) {
224 return 4;
225 } else if (componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) {
226 return 8;
227 } else {
228 // Unknown componenty type
229 return -1;
230 }
231}
232
DingboDingboDingbo83ccb9f2019-08-29 18:49:15 -0400233static inline int32_t GetNumComponentsInType(uint32_t ty) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900234 if (ty == TINYGLTF_TYPE_SCALAR) {
235 return 1;
236 } else if (ty == TINYGLTF_TYPE_VEC2) {
237 return 2;
238 } else if (ty == TINYGLTF_TYPE_VEC3) {
239 return 3;
240 } else if (ty == TINYGLTF_TYPE_VEC4) {
241 return 4;
242 } else if (ty == TINYGLTF_TYPE_MAT2) {
243 return 4;
244 } else if (ty == TINYGLTF_TYPE_MAT3) {
245 return 9;
246 } else if (ty == TINYGLTF_TYPE_MAT4) {
247 return 16;
248 } else {
249 // Unknown componenty type
250 return -1;
251 }
252}
253
Syoyo Fujita150f2432019-07-25 19:22:44 +0900254// TODO(syoyo): Move these functions to TinyGLTF class
Selmar Kok0d0e97e2018-08-22 14:01:57 +0200255bool IsDataURI(const std::string &in);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900256bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
257 const std::string &in, size_t reqBytes, bool checkSize);
Selmar Kok0d0e97e2018-08-22 14:01:57 +0200258
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900259#ifdef __clang__
260#pragma clang diagnostic push
261// Suppress warning for : static Value null_value
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900262#pragma clang diagnostic ignored "-Wexit-time-destructors"
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900263#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900264#endif
265
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900266// Simple class to represent JSON object
267class Value {
268 public:
269 typedef std::vector<Value> Array;
270 typedef std::map<std::string, Value> Object;
271
Syoyo Fujita046400b2019-07-24 19:26:48 +0900272 Value()
273 : type_(NULL_TYPE),
274 int_value_(0),
Syoyo Fujita150f2432019-07-25 19:22:44 +0900275 real_value_(0.0),
Syoyo Fujita046400b2019-07-24 19:26:48 +0900276 boolean_value_(false) {}
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900277
278 explicit Value(bool b) : type_(BOOL_TYPE) { boolean_value_ = b; }
Syoyo Fujitaff515702019-08-24 16:29:14 +0900279 explicit Value(int i) : type_(INT_TYPE) {
280 int_value_ = i;
281 real_value_ = i;
282 }
Syoyo Fujita150f2432019-07-25 19:22:44 +0900283 explicit Value(double n) : type_(REAL_TYPE) { real_value_ = n; }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900284 explicit Value(const std::string &s) : type_(STRING_TYPE) {
285 string_value_ = s;
286 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900287 explicit Value(std::string &&s)
288 : type_(STRING_TYPE), string_value_(std::move(s)) {}
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900289 explicit Value(const unsigned char *p, size_t n) : type_(BINARY_TYPE) {
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900290 binary_value_.resize(n);
291 memcpy(binary_value_.data(), p, n);
292 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900293 explicit Value(std::vector<unsigned char> &&v) noexcept
294 : type_(BINARY_TYPE),
295 binary_value_(std::move(v)) {}
296 explicit Value(const Array &a) : type_(ARRAY_TYPE) { array_value_ = a; }
297 explicit Value(Array &&a) noexcept : type_(ARRAY_TYPE),
298 array_value_(std::move(a)) {}
jrkoonced1e14722019-08-27 11:51:02 -0500299
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900300 explicit Value(const Object &o) : type_(OBJECT_TYPE) { object_value_ = o; }
301 explicit Value(Object &&o) noexcept : type_(OBJECT_TYPE),
302 object_value_(std::move(o)) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100303
304 DEFAULT_METHODS(Value)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900305
Hill Mad1e32862021-02-20 22:30:44 -0800306 char Type() const { return static_cast<char>(type_); }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900307
308 bool IsBool() const { return (type_ == BOOL_TYPE); }
309
310 bool IsInt() const { return (type_ == INT_TYPE); }
311
Syoyo Fujita150f2432019-07-25 19:22:44 +0900312 bool IsNumber() const { return (type_ == REAL_TYPE) || (type_ == INT_TYPE); }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900313
Syoyo Fujita150f2432019-07-25 19:22:44 +0900314 bool IsReal() const { return (type_ == REAL_TYPE); }
Syoyo Fujita046400b2019-07-24 19:26:48 +0900315
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900316 bool IsString() const { return (type_ == STRING_TYPE); }
317
318 bool IsBinary() const { return (type_ == BINARY_TYPE); }
319
320 bool IsArray() const { return (type_ == ARRAY_TYPE); }
321
322 bool IsObject() const { return (type_ == OBJECT_TYPE); }
323
Syoyo Fujita150f2432019-07-25 19:22:44 +0900324 // Use this function if you want to have number value as double.
325 double GetNumberAsDouble() const {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900326 if (type_ == INT_TYPE) {
327 return double(int_value_);
Syoyo Fujita150f2432019-07-25 19:22:44 +0900328 } else {
329 return real_value_;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900330 }
Syoyo Fujita150f2432019-07-25 19:22:44 +0900331 }
Syoyo Fujita046400b2019-07-24 19:26:48 +0900332
Syoyo Fujita150f2432019-07-25 19:22:44 +0900333 // Use this function if you want to have number value as int.
Syoyo Fujitaff0a2e92020-05-11 19:07:00 +0900334 // TODO(syoyo): Support int value larger than 32 bits
335 int GetNumberAsInt() const {
Syoyo Fujita150f2432019-07-25 19:22:44 +0900336 if (type_ == REAL_TYPE) {
337 return int(real_value_);
338 } else {
339 return int_value_;
340 }
Syoyo Fujita046400b2019-07-24 19:26:48 +0900341 }
342
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900343 // Accessor
344 template <typename T>
345 const T &Get() const;
346 template <typename T>
347 T &Get();
348
349 // Lookup value from an array
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900350 const Value &Get(int idx) const {
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900351 static Value null_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900352 assert(IsArray());
353 assert(idx >= 0);
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900354 return (static_cast<size_t>(idx) < array_value_.size())
355 ? array_value_[static_cast<size_t>(idx)]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900356 : null_value;
357 }
358
359 // Lookup value from a key-value pair
360 const Value &Get(const std::string &key) const {
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900361 static Value null_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900362 assert(IsObject());
363 Object::const_iterator it = object_value_.find(key);
364 return (it != object_value_.end()) ? it->second : null_value;
365 }
366
367 size_t ArrayLen() const {
368 if (!IsArray()) return 0;
369 return array_value_.size();
370 }
371
372 // Valid only for object type.
373 bool Has(const std::string &key) const {
374 if (!IsObject()) return false;
375 Object::const_iterator it = object_value_.find(key);
376 return (it != object_value_.end()) ? true : false;
377 }
378
379 // List keys
380 std::vector<std::string> Keys() const {
381 std::vector<std::string> keys;
382 if (!IsObject()) return keys; // empty
383
384 for (Object::const_iterator it = object_value_.begin();
385 it != object_value_.end(); ++it) {
386 keys.push_back(it->first);
387 }
388
389 return keys;
390 }
391
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900392 size_t Size() const { return (IsArray() ? ArrayLen() : Keys().size()); }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900393
394 bool operator==(const tinygltf::Value &other) const;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +0000395
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900396 protected:
Syoyo Fujita046400b2019-07-24 19:26:48 +0900397 int type_ = NULL_TYPE;
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900398
Syoyo Fujita046400b2019-07-24 19:26:48 +0900399 int int_value_ = 0;
Syoyo Fujita150f2432019-07-25 19:22:44 +0900400 double real_value_ = 0.0;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900401 std::string string_value_;
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900402 std::vector<unsigned char> binary_value_;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900403 Array array_value_;
404 Object object_value_;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900405 bool boolean_value_ = false;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900406};
407
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900408#ifdef __clang__
409#pragma clang diagnostic pop
410#endif
411
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900412#define TINYGLTF_VALUE_GET(ctype, var) \
413 template <> \
414 inline const ctype &Value::Get<ctype>() const { \
415 return var; \
416 } \
417 template <> \
418 inline ctype &Value::Get<ctype>() { \
419 return var; \
420 }
421TINYGLTF_VALUE_GET(bool, boolean_value_)
Syoyo Fujita150f2432019-07-25 19:22:44 +0900422TINYGLTF_VALUE_GET(double, real_value_)
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900423TINYGLTF_VALUE_GET(int, int_value_)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900424TINYGLTF_VALUE_GET(std::string, string_value_)
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900425TINYGLTF_VALUE_GET(std::vector<unsigned char>, binary_value_)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900426TINYGLTF_VALUE_GET(Value::Array, array_value_)
427TINYGLTF_VALUE_GET(Value::Object, object_value_)
428#undef TINYGLTF_VALUE_GET
429
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900430#ifdef __clang__
431#pragma clang diagnostic push
432#pragma clang diagnostic ignored "-Wc++98-compat"
433#pragma clang diagnostic ignored "-Wpadded"
434#endif
435
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500436/// Agregate object for representing a color
Arthur Brainville58453192018-01-08 18:25:52 +0100437using ColorValue = std::array<double, 4>;
438
Syoyo Fujita046400b2019-07-24 19:26:48 +0900439// === legacy interface ====
440// TODO(syoyo): Deprecate `Parameter` class.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500441struct Parameter {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200442 bool bool_value = false;
Ben Buzbeef6af2242018-04-25 15:13:05 -0700443 bool has_number_value = false;
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900444 std::string string_value;
445 std::vector<double> number_array;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000446 std::map<std::string, double> json_double_value;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200447 double number_value = 0.0;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900448
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500449 // context sensitive methods. depending the type of the Parameter you are
450 // accessing, these are either valid or not
451 // If this parameter represent a texture map in a material, will return the
452 // texture index
Arthur Brainville58453192018-01-08 18:25:52 +0100453
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500454 /// Return the index of a texture if this Parameter is a texture map.
455 /// Returned value is only valid if the parameter represent a texture from a
456 /// material
Arthur Brainville41fe7722018-01-08 18:32:48 +0100457 int TextureIndex() const {
Arthur Brainville58453192018-01-08 18:25:52 +0100458 const auto it = json_double_value.find("index");
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500459 if (it != std::end(json_double_value)) {
Arthur Brainville58453192018-01-08 18:25:52 +0100460 return int(it->second);
461 }
462 return -1;
463 }
464
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000465 /// Return the index of a texture coordinate set if this Parameter is a
466 /// texture map. Returned value is only valid if the parameter represent a
467 /// texture from a material
Sascha Willemseb011062019-02-23 21:15:45 +0100468 int TextureTexCoord() const {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000469 const auto it = json_double_value.find("texCoord");
470 if (it != std::end(json_double_value)) {
471 return int(it->second);
472 }
Arthur Brainville8a98d982019-07-05 00:26:02 +0200473 // As per the spec, if texCoord is ommited, this parameter is 0
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000474 return 0;
Sascha Willemseb011062019-02-23 21:15:45 +0100475 }
476
Christophe820ede82019-07-04 15:21:21 +0900477 /// Return the scale of a texture if this Parameter is a normal texture map.
478 /// Returned value is only valid if the parameter represent a normal texture
479 /// from a material
480 double TextureScale() const {
481 const auto it = json_double_value.find("scale");
482 if (it != std::end(json_double_value)) {
483 return it->second;
484 }
Arthur Brainville8a98d982019-07-05 00:26:02 +0200485 // As per the spec, if scale is ommited, this paramter is 1
486 return 1;
487 }
Arthur Brianville (Ybalrid)2a9d9de2019-07-05 00:30:47 +0200488
Arthur Brainville8a98d982019-07-05 00:26:02 +0200489 /// Return the strength of a texture if this Parameter is a an occlusion map.
490 /// Returned value is only valid if the parameter represent an occlusion map
491 /// from a material
492 double TextureStrength() const {
493 const auto it = json_double_value.find("strength");
494 if (it != std::end(json_double_value)) {
495 return it->second;
496 }
497 // As per the spec, if strenghth is ommited, this parameter is 1
498 return 1;
Christophe820ede82019-07-04 15:21:21 +0900499 }
500
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500501 /// Material factor, like the roughness or metalness of a material
502 /// Returned value is only valid if the parameter represent a texture from a
503 /// material
Ben Buzbeef6af2242018-04-25 15:13:05 -0700504 double Factor() const { return number_value; }
Arthur Brainville58453192018-01-08 18:25:52 +0100505
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500506 /// Return the color of a material
507 /// Returned value is only valid if the parameter represent a texture from a
508 /// material
Arthur Brainville95853912018-01-08 18:37:44 +0100509 ColorValue ColorFactor() const {
Arthur Brainville58453192018-01-08 18:25:52 +0100510 return {
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500511 {// this agregate intialize the std::array object, and uses C++11 RVO.
512 number_array[0], number_array[1], number_array[2],
513 (number_array.size() > 3 ? number_array[3] : 1.0)}};
Arthur Brainville58453192018-01-08 18:25:52 +0100514 }
Selmar Kok31cb7f92018-10-03 15:39:05 +0200515
Selmar Kokff2b1f92019-10-21 17:58:09 +0200516 Parameter() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100517 DEFAULT_METHODS(Parameter)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900518 bool operator==(const Parameter &) const;
Arthur Brainville58453192018-01-08 18:25:52 +0100519};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900520
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900521#ifdef __clang__
522#pragma clang diagnostic pop
523#endif
524
525#ifdef __clang__
526#pragma clang diagnostic push
527#pragma clang diagnostic ignored "-Wpadded"
528#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900529
Syoyo Fujitabde70212016-02-07 17:38:17 +0900530typedef std::map<std::string, Parameter> ParameterMap;
Selmar Kokfa7022f2018-04-04 18:10:20 +0200531typedef std::map<std::string, Value> ExtensionMap;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900532
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000533struct AnimationChannel {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900534 int sampler; // required
535 int target_node; // required (index of the node to target)
536 std::string target_path; // required in ["translation", "rotation", "scale",
537 // "weights"]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900538 Value extras;
Selmar Kok4e2988e2019-08-16 14:08:08 +0200539 ExtensionMap extensions;
Selmar Kok973d9b32020-01-21 18:45:24 +0100540 ExtensionMap target_extensions;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900541
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900542 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
543 std::string extras_json_string;
544 std::string extensions_json_string;
Selmar Kok973d9b32020-01-21 18:45:24 +0100545 std::string target_extensions_json_string;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900546
Syoyo Fujita5b407452017-06-04 17:42:41 +0900547 AnimationChannel() : sampler(-1), target_node(-1) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100548 DEFAULT_METHODS(AnimationChannel)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900549 bool operator==(const AnimationChannel &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000550};
551
552struct AnimationSampler {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900553 int input; // required
554 int output; // required
Arthur Brianville (Ybalrid)2a9d9de2019-07-05 00:30:47 +0200555 std::string interpolation; // "LINEAR", "STEP","CUBICSPLINE" or user defined
556 // string. default "LINEAR"
Jens Olssonb3af2f12018-06-04 10:17:49 +0200557 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900558 ExtensionMap extensions;
559
560 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
561 std::string extras_json_string;
562 std::string extensions_json_string;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000563
Syoyo Fujita5b407452017-06-04 17:42:41 +0900564 AnimationSampler() : input(-1), output(-1), interpolation("LINEAR") {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100565 DEFAULT_METHODS(AnimationSampler)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900566 bool operator==(const AnimationSampler &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000567};
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900568
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900569struct Animation {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900570 std::string name;
571 std::vector<AnimationChannel> channels;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000572 std::vector<AnimationSampler> samplers;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900573 Value extras;
Selmar Kok4e2988e2019-08-16 14:08:08 +0200574 ExtensionMap extensions;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200575
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900576 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
577 std::string extras_json_string;
578 std::string extensions_json_string;
579
Selmar Kokff2b1f92019-10-21 17:58:09 +0200580 Animation() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100581 DEFAULT_METHODS(Animation)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900582 bool operator==(const Animation &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900583};
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900584
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000585struct Skin {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900586 std::string name;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900587 int inverseBindMatrices; // required here but not in the spec
588 int skeleton; // The index of the node used as a skeleton root
589 std::vector<int> joints; // Indices of skeleton nodes
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000590
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900591 Value extras;
592 ExtensionMap extensions;
593
594 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
595 std::string extras_json_string;
596 std::string extensions_json_string;
597
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900598 Skin() {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000599 inverseBindMatrices = -1;
Aurélien Chatelain8d5e0a72017-06-19 13:52:08 +0000600 skeleton = -1;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000601 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100602 DEFAULT_METHODS(Skin)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900603 bool operator==(const Skin &) const;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000604};
605
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000606struct Sampler {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900607 std::string name;
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900608 // glTF 2.0 spec does not define default value for `minFilter` and
609 // `magFilter`. Set -1 in TinyGLTF(issue #186)
610 int minFilter =
611 -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR",
zz8a35b8f2021-05-14 13:49:33 +0800612 // "NEAREST_MIPMAP_NEAREST", "LINEAR_MIPMAP_NEAREST",
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900613 // "NEAREST_MIPMAP_LINEAR", "LINEAR_MIPMAP_LINEAR"]
614 int magFilter =
615 -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR"]
616 int wrapS =
617 TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
618 // "REPEAT"], default "REPEAT"
619 int wrapT =
620 TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
621 // "REPEAT"], default "REPEAT"
Syoyo Fujita52ff00a2022-08-16 20:08:45 +0900622 // int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; // TinyGLTF extension. currently
623 // not used.
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900624
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900625 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900626 ExtensionMap extensions;
627
628 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
629 std::string extras_json_string;
630 std::string extensions_json_string;
Syoyo Fujitac2615632016-06-19 21:56:06 +0900631
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000632 Sampler()
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900633 : minFilter(-1),
634 magFilter(-1),
timmmeh73584ba2019-01-30 18:38:46 -0800635 wrapS(TINYGLTF_TEXTURE_WRAP_REPEAT),
Syoyo Fujita2c521b32020-12-04 00:50:46 +0900636 wrapT(TINYGLTF_TEXTURE_WRAP_REPEAT) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100637 DEFAULT_METHODS(Sampler)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900638 bool operator==(const Sampler &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000639};
640
Syoyo Fujita5b407452017-06-04 17:42:41 +0900641struct Image {
Syoyo Fujitac2615632016-06-19 21:56:06 +0900642 std::string name;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900643 int width;
644 int height;
645 int component;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000646 int bits; // bit depth per channel. 8(byte), 16 or 32.
647 int pixel_type; // pixel type(TINYGLTF_COMPONENT_TYPE_***). usually
648 // UBYTE(bits = 8) or USHORT(bits = 16)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900649 std::vector<unsigned char> image;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900650 int bufferView; // (required if no uri)
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500651 std::string mimeType; // (required if no uri) ["image/jpeg", "image/png",
652 // "image/bmp", "image/gif"]
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900653 std::string uri; // (required if no mimeType) uri is not decoded(e.g.
654 // whitespace may be represented as %20)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900655 Value extras;
Syoyo Fujita3e53feb2018-09-02 15:36:17 +0900656 ExtensionMap extensions;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900657
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900658 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
659 std::string extras_json_string;
660 std::string extensions_json_string;
661
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900662 // When this flag is true, data is stored to `image` in as-is format(e.g. jpeg
663 // compressed for "image/jpeg" mime) This feature is good if you use custom
664 // image loader function. (e.g. delayed decoding of images for faster glTF
665 // parsing) Default parser for Image does not provide as-is loading feature at
666 // the moment. (You can manipulate this by providing your own LoadImageData
667 // function)
Selmar Kokfa0a9982018-10-03 15:46:23 +0200668 bool as_is;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900669
670 Image() : as_is(false) {
671 bufferView = -1;
672 width = -1;
673 height = -1;
674 component = -1;
daemyung jang1739d022020-07-03 13:27:39 +0900675 bits = -1;
676 pixel_type = -1;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900677 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100678 DEFAULT_METHODS(Image)
jrkoonced1e14722019-08-27 11:51:02 -0500679
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900680 bool operator==(const Image &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000681};
682
683struct Texture {
Vladimír Vondruš239be2c2018-07-24 23:23:56 +0200684 std::string name;
685
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000686 int sampler;
Selmar Kok8eb39042018-10-05 14:29:35 +0200687 int source;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900688 Value extras;
Selmar Kokfa7022f2018-04-04 18:10:20 +0200689 ExtensionMap extensions;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900690
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900691 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
692 std::string extras_json_string;
693 std::string extensions_json_string;
694
Syoyo Fujita5b407452017-06-04 17:42:41 +0900695 Texture() : sampler(-1), source(-1) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100696 DEFAULT_METHODS(Texture)
jrkoonced1e14722019-08-27 11:51:02 -0500697
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900698 bool operator==(const Texture &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000699};
Syoyo Fujitabde70212016-02-07 17:38:17 +0900700
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900701struct TextureInfo {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900702 int index = -1; // required.
703 int texCoord; // The set index of texture's TEXCOORD attribute used for
704 // texture coordinate mapping.
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900705
706 Value extras;
707 ExtensionMap extensions;
708
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900709 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
710 std::string extras_json_string;
711 std::string extensions_json_string;
712
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900713 TextureInfo() : index(-1), texCoord(0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100714 DEFAULT_METHODS(TextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900715 bool operator==(const TextureInfo &) const;
716};
717
718struct NormalTextureInfo {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900719 int index = -1; // required
720 int texCoord; // The set index of texture's TEXCOORD attribute used for
721 // texture coordinate mapping.
722 double scale; // scaledNormal = normalize((<sampled normal texture value>
723 // * 2.0 - 1.0) * vec3(<normal scale>, <normal scale>, 1.0))
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900724
725 Value extras;
726 ExtensionMap extensions;
727
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900728 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
729 std::string extras_json_string;
730 std::string extensions_json_string;
731
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900732 NormalTextureInfo() : index(-1), texCoord(0), scale(1.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100733 DEFAULT_METHODS(NormalTextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900734 bool operator==(const NormalTextureInfo &) const;
735};
736
737struct OcclusionTextureInfo {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900738 int index = -1; // required
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900739 int texCoord; // The set index of texture's TEXCOORD attribute used for
740 // texture coordinate mapping.
741 double strength; // occludedColor = lerp(color, color * <sampled occlusion
742 // texture value>, <occlusion strength>)
743
744 Value extras;
745 ExtensionMap extensions;
746
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900747 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
748 std::string extras_json_string;
749 std::string extensions_json_string;
750
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900751 OcclusionTextureInfo() : index(-1), texCoord(0), strength(1.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100752 DEFAULT_METHODS(OcclusionTextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900753 bool operator==(const OcclusionTextureInfo &) const;
754};
755
756// pbrMetallicRoughness class defined in glTF 2.0 spec.
757struct PbrMetallicRoughness {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900758 std::vector<double> baseColorFactor; // len = 4. default [1,1,1,1]
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900759 TextureInfo baseColorTexture;
760 double metallicFactor; // default 1
761 double roughnessFactor; // default 1
762 TextureInfo metallicRoughnessTexture;
763
764 Value extras;
765 ExtensionMap extensions;
766
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900767 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
768 std::string extras_json_string;
769 std::string extensions_json_string;
770
771 PbrMetallicRoughness()
772 : baseColorFactor(std::vector<double>{1.0, 1.0, 1.0, 1.0}),
773 metallicFactor(1.0),
774 roughnessFactor(1.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100775 DEFAULT_METHODS(PbrMetallicRoughness)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900776 bool operator==(const PbrMetallicRoughness &) const;
777};
778
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000779// Each extension should be stored in a ParameterMap.
780// members not in the values could be included in the ParameterMap
781// to keep a single material model
782struct Material {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900783 std::string name;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900784
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900785 std::vector<double> emissiveFactor; // length 3. default [0, 0, 0]
Syoyo Fujita046400b2019-07-24 19:26:48 +0900786 std::string alphaMode; // default "OPAQUE"
787 double alphaCutoff; // default 0.5
788 bool doubleSided; // default false;
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900789
790 PbrMetallicRoughness pbrMetallicRoughness;
791
792 NormalTextureInfo normalTexture;
793 OcclusionTextureInfo occlusionTexture;
794 TextureInfo emissiveTexture;
795
Syoyo Fujita046400b2019-07-24 19:26:48 +0900796 // For backward compatibility
797 // TODO(syoyo): Remove `values` and `additionalValues` in the next release.
798 ParameterMap values;
799 ParameterMap additionalValues;
Selmar09d2ff12018-03-15 17:30:42 +0100800
801 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900802 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200803
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900804 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
805 std::string extras_json_string;
806 std::string extensions_json_string;
807
Syoyo Fujita046400b2019-07-24 19:26:48 +0900808 Material() : alphaMode("OPAQUE"), alphaCutoff(0.5), doubleSided(false) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100809 DEFAULT_METHODS(Material)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900810
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900811 bool operator==(const Material &) const;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000812};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900813
Syoyo Fujita5b407452017-06-04 17:42:41 +0900814struct BufferView {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900815 std::string name;
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900816 int buffer{-1}; // Required
Syoyo Fujita4e47bc72020-01-02 22:07:25 +0900817 size_t byteOffset{0}; // minimum 0, default 0
818 size_t byteLength{0}; // required, minimum 1. 0 = invalid
819 size_t byteStride{0}; // minimum 4, maximum 252 (multiple of 4), default 0 =
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900820 // understood to be tightly packed
821 int target{0}; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"] for vertex indices
822 // or atttribs. Could be 0 for other data
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900823 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900824 ExtensionMap extensions;
825
826 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
827 std::string extras_json_string;
828 std::string extensions_json_string;
829
Syoyo Fujita4e47bc72020-01-02 22:07:25 +0900830 bool dracoDecoded{false}; // Flag indicating this has been draco decoded
Syoyo Fujitad17ff662017-05-29 02:53:12 +0900831
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900832 BufferView()
833 : buffer(-1),
834 byteOffset(0),
835 byteLength(0),
836 byteStride(0),
837 target(0),
838 dracoDecoded(false) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100839 DEFAULT_METHODS(BufferView)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900840 bool operator==(const BufferView &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000841};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900842
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000843struct Accessor {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900844 int bufferView; // optional in spec but required here since sparse accessor
845 // are not supported
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900846 std::string name;
847 size_t byteOffset;
Fabien Freling9056aee2019-03-21 17:03:18 +0100848 bool normalized; // optional.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000849 int componentType; // (required) One of TINYGLTF_COMPONENT_TYPE_***
Syoyo Fujita5b407452017-06-04 17:42:41 +0900850 size_t count; // required
851 int type; // (required) One of TINYGLTF_TYPE_*** ..
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900852 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900853 ExtensionMap extensions;
854
855 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
856 std::string extras_json_string;
857 std::string extensions_json_string;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000858
Syoyo Fujita7905a5b2021-01-21 20:33:11 +0900859 std::vector<double>
860 minValues; // optional. integer value is promoted to double
861 std::vector<double>
862 maxValues; // optional. integer value is promoted to double
Syoyo Fujitad9d012a2017-07-12 18:29:29 +0900863
Arthur Brainville (Ybalrid)1ccb4ff2019-03-06 11:30:00 +0100864 struct {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000865 int count;
866 bool isSparse;
867 struct {
868 int byteOffset;
869 int bufferView;
870 int componentType; // a TINYGLTF_COMPONENT_TYPE_ value
871 } indices;
872 struct {
873 int bufferView;
874 int byteOffset;
875 } values;
Arthur Brainville (Ybalrid)1ccb4ff2019-03-06 11:30:00 +0100876 } sparse;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000877
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900878 ///
879 /// Utility function to compute byteStride for a given bufferView object.
880 /// Returns -1 upon invalid glTF value or parameter configuration.
881 ///
882 int ByteStride(const BufferView &bufferViewObject) const {
883 if (bufferViewObject.byteStride == 0) {
884 // Assume data is tightly packed.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500885 int componentSizeInBytes =
886 GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900887 if (componentSizeInBytes <= 0) {
888 return -1;
889 }
890
Syoyo Fujita81d75df2019-08-30 19:19:52 +0900891 int numComponents = GetNumComponentsInType(static_cast<uint32_t>(type));
892 if (numComponents <= 0) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900893 return -1;
894 }
895
Syoyo Fujita81d75df2019-08-30 19:19:52 +0900896 return componentSizeInBytes * numComponents;
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900897 } else {
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500898 // Check if byteStride is a mulple of the size of the accessor's component
899 // type.
900 int componentSizeInBytes =
901 GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900902 if (componentSizeInBytes <= 0) {
903 return -1;
904 }
905
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900906 if ((bufferViewObject.byteStride % uint32_t(componentSizeInBytes)) != 0) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900907 return -1;
908 }
Arthur Brainville8ce4e542018-01-10 01:27:12 +0100909 return static_cast<int>(bufferViewObject.byteStride);
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900910 }
911
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900912 // unreachable return 0;
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900913 }
914
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900915 Accessor()
916 : bufferView(-1),
917 byteOffset(0),
918 normalized(false),
919 componentType(-1),
920 count(0),
921 type(-1) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000922 sparse.isSparse = false;
923 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100924 DEFAULT_METHODS(Accessor)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900925 bool operator==(const tinygltf::Accessor &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000926};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900927
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900928struct PerspectiveCamera {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200929 double aspectRatio; // min > 0
930 double yfov; // required. min > 0
931 double zfar; // min > 0
932 double znear; // required. min > 0
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900933
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900934 PerspectiveCamera()
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900935 : aspectRatio(0.0),
936 yfov(0.0),
937 zfar(0.0) // 0 = use infinite projecton matrix
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900938 ,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900939 znear(0.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100940 DEFAULT_METHODS(PerspectiveCamera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900941 bool operator==(const PerspectiveCamera &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900942
Selmar09d2ff12018-03-15 17:30:42 +0100943 ExtensionMap extensions;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900944 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900945
946 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
947 std::string extras_json_string;
948 std::string extensions_json_string;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900949};
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000950
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900951struct OrthographicCamera {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200952 double xmag; // required. must not be zero.
953 double ymag; // required. must not be zero.
954 double zfar; // required. `zfar` must be greater than `znear`.
955 double znear; // required
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000956
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900957 OrthographicCamera() : xmag(0.0), ymag(0.0), zfar(0.0), znear(0.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100958 DEFAULT_METHODS(OrthographicCamera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900959 bool operator==(const OrthographicCamera &) const;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900960
Selmar09d2ff12018-03-15 17:30:42 +0100961 ExtensionMap extensions;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900962 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900963
964 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
965 std::string extras_json_string;
966 std::string extensions_json_string;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900967};
968
969struct Camera {
970 std::string type; // required. "perspective" or "orthographic"
971 std::string name;
972
973 PerspectiveCamera perspective;
974 OrthographicCamera orthographic;
975
976 Camera() {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100977 DEFAULT_METHODS(Camera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900978 bool operator==(const Camera &) const;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900979
Selmar09d2ff12018-03-15 17:30:42 +0100980 ExtensionMap extensions;
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000981 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900982
983 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
984 std::string extras_json_string;
985 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900986};
987
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000988struct Primitive {
989 std::map<std::string, int> attributes; // (required) A dictionary object of
990 // integer, where each integer
991 // is the index of the accessor
992 // containing an attribute.
993 int material; // The index of the material to apply to this primitive
Syoyo Fujita5b407452017-06-04 17:42:41 +0900994 // when rendering.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000995 int indices; // The index of the accessor that contains the indices.
996 int mode; // one of TINYGLTF_MODE_***
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900997 std::vector<std::map<std::string, int> > targets; // array of morph targets,
Syoyo Fujita5b407452017-06-04 17:42:41 +0900998 // where each target is a dict with attribues in ["POSITION, "NORMAL",
999 // "TANGENT"] pointing
1000 // to their corresponding accessors
Alex Wood7319db72019-01-24 15:38:16 -05001001 ExtensionMap extensions;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001002 Value extras;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001003
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001004 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1005 std::string extras_json_string;
1006 std::string extensions_json_string;
1007
Syoyo Fujita5b407452017-06-04 17:42:41 +09001008 Primitive() {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001009 material = -1;
1010 indices = -1;
daemyung jang1739d022020-07-03 13:27:39 +09001011 mode = -1;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001012 }
Selmar Kokb74fade2019-10-29 16:09:32 +01001013 DEFAULT_METHODS(Primitive)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001014 bool operator==(const Primitive &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001015};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001016
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001017struct Mesh {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001018 std::string name;
1019 std::vector<Primitive> primitives;
Syoyo Fujita5b407452017-06-04 17:42:41 +09001020 std::vector<double> weights; // weights to be applied to the Morph Targets
Selmar09d2ff12018-03-15 17:30:42 +01001021 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001022 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001023
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001024 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1025 std::string extras_json_string;
1026 std::string extensions_json_string;
1027
jrkoonced1e14722019-08-27 11:51:02 -05001028 Mesh() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001029 DEFAULT_METHODS(Mesh)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001030 bool operator==(const Mesh &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001031};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001032
1033class Node {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001034 public:
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09001035 Node() : camera(-1), skin(-1), mesh(-1) {}
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001036
Selmar Kokb74fade2019-10-29 16:09:32 +01001037 DEFAULT_METHODS(Node)
Syoyo Fujitad6b0d0a2019-06-29 17:31:13 +09001038
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001039 bool operator==(const Node &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001040
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001041 int camera; // the index of the camera referenced by this node
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001042
1043 std::string name;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00001044 int skin;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001045 int mesh;
1046 std::vector<int> children;
Syoyo Fujita7c877972016-03-08 01:31:49 +09001047 std::vector<double> rotation; // length must be 0 or 4
1048 std::vector<double> scale; // length must be 0 or 3
1049 std::vector<double> translation; // length must be 0 or 3
1050 std::vector<double> matrix; // length must be 0 or 16
Syoyo Fujita5b407452017-06-04 17:42:41 +09001051 std::vector<double> weights; // The weights of the instantiated Morph Target
Ben Buzbee3b735bb2018-04-24 11:39:30 -07001052
Selmar09d2ff12018-03-15 17:30:42 +01001053 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001054 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001055
1056 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1057 std::string extras_json_string;
1058 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001059};
1060
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001061struct Buffer {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001062 std::string name;
1063 std::vector<unsigned char> data;
Syoyo Fujita5b407452017-06-04 17:42:41 +09001064 std::string
1065 uri; // considered as required here but not in the spec (need to clarify)
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001066 // uri is not decoded(e.g. whitespace may be represented as %20)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001067 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001068 ExtensionMap extensions;
1069
1070 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1071 std::string extras_json_string;
1072 std::string extensions_json_string;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001073
Selmar Kokb74fade2019-10-29 16:09:32 +01001074 Buffer() = default;
1075 DEFAULT_METHODS(Buffer)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001076 bool operator==(const Buffer &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001077};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001078
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001079struct Asset {
Syoyo Fujitab702de72021-03-02 19:08:29 +09001080 std::string version = "2.0"; // required
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001081 std::string generator;
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +00001082 std::string minVersion;
1083 std::string copyright;
Selmar09d2ff12018-03-15 17:30:42 +01001084 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001085 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001086
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001087 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1088 std::string extras_json_string;
1089 std::string extensions_json_string;
1090
jrkoonced1e14722019-08-27 11:51:02 -05001091 Asset() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001092 DEFAULT_METHODS(Asset)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001093 bool operator==(const Asset &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001094};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001095
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001096struct Scene {
1097 std::string name;
1098 std::vector<int> nodes;
1099
Selmar09d2ff12018-03-15 17:30:42 +01001100 ExtensionMap extensions;
1101 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001102
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001103 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1104 std::string extras_json_string;
1105 std::string extensions_json_string;
1106
jrkoonced1e14722019-08-27 11:51:02 -05001107 Scene() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001108 DEFAULT_METHODS(Scene)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001109 bool operator==(const Scene &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001110};
1111
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001112struct SpotLight {
1113 double innerConeAngle;
1114 double outerConeAngle;
1115
Johan Bowald52936a02019-07-17 09:06:45 +02001116 SpotLight() : innerConeAngle(0.0), outerConeAngle(0.7853981634) {}
Selmar Kokb74fade2019-10-29 16:09:32 +01001117 DEFAULT_METHODS(SpotLight)
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001118 bool operator==(const SpotLight &) const;
1119
1120 ExtensionMap extensions;
1121 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001122
1123 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1124 std::string extras_json_string;
1125 std::string extensions_json_string;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001126};
1127
Emanuel Schrade186322b2017-11-06 11:14:41 +01001128struct Light {
1129 std::string name;
1130 std::vector<double> color;
Syoyo Fujita3dad3032020-05-13 21:22:23 +09001131 double intensity{1.0};
Emanuel Schrade186322b2017-11-06 11:14:41 +01001132 std::string type;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09001133 double range{0.0}; // 0.0 = inifinite
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001134 SpotLight spot;
1135
Benjamin Schmithüsenb2d7d882019-07-09 16:32:42 +02001136 Light() : intensity(1.0), range(0.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +01001137 DEFAULT_METHODS(Light)
Arthur Brainville (Ybalrid)9eeaf202019-09-05 13:02:05 +02001138
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001139 bool operator==(const Light &) const;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001140
1141 ExtensionMap extensions;
1142 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001143
1144 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1145 std::string extras_json_string;
1146 std::string extensions_json_string;
Emanuel Schrade186322b2017-11-06 11:14:41 +01001147};
1148
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001149class Model {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001150 public:
Selmar Kokff2b1f92019-10-21 17:58:09 +02001151 Model() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001152 DEFAULT_METHODS(Model)
Syoyo Fujitad6b0d0a2019-06-29 17:31:13 +09001153
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001154 bool operator==(const Model &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001155
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001156 std::vector<Accessor> accessors;
1157 std::vector<Animation> animations;
1158 std::vector<Buffer> buffers;
1159 std::vector<BufferView> bufferViews;
1160 std::vector<Material> materials;
1161 std::vector<Mesh> meshes;
1162 std::vector<Node> nodes;
1163 std::vector<Texture> textures;
1164 std::vector<Image> images;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00001165 std::vector<Skin> skins;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001166 std::vector<Sampler> samplers;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09001167 std::vector<Camera> cameras;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001168 std::vector<Scene> scenes;
Emanuel Schrade186322b2017-11-06 11:14:41 +01001169 std::vector<Light> lights;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001170
sammyKhana0a62bd2020-01-17 13:41:16 +01001171 int defaultScene = -1;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001172 std::vector<std::string> extensionsUsed;
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00001173 std::vector<std::string> extensionsRequired;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001174
1175 Asset asset;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001176
1177 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001178 ExtensionMap extensions;
1179
1180 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1181 std::string extras_json_string;
1182 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001183};
1184
Syoyo Fujita0614eb82016-10-14 18:50:14 +09001185enum SectionCheck {
Luke San Antonio9e36b612016-06-23 14:10:51 -04001186 NO_REQUIRE = 0x00,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001187 REQUIRE_VERSION = 0x01,
1188 REQUIRE_SCENE = 0x02,
1189 REQUIRE_SCENES = 0x04,
1190 REQUIRE_NODES = 0x08,
1191 REQUIRE_ACCESSORS = 0x10,
1192 REQUIRE_BUFFERS = 0x20,
1193 REQUIRE_BUFFER_VIEWS = 0x40,
1194 REQUIRE_ALL = 0x7f
Luke San Antonio9e36b612016-06-23 14:10:51 -04001195};
1196
Squareysff644d82018-03-13 22:36:18 +01001197///
1198/// LoadImageDataFunction type. Signature for custom image loading callbacks.
1199///
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001200typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *,
1201 std::string *, int, int,
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001202 const unsigned char *, int,
1203 void *user_pointer);
Squareysff644d82018-03-13 22:36:18 +01001204
johan bowald642a3432018-04-01 12:37:18 +02001205///
1206/// WriteImageDataFunction type. Signature for custom image writing callbacks.
1207///
1208typedef bool (*WriteImageDataFunction)(const std::string *, const std::string *,
1209 Image *, bool, void *);
1210
Squareys2d3594d2018-03-13 22:40:53 +01001211#ifndef TINYGLTF_NO_STB_IMAGE
Squareysff644d82018-03-13 22:36:18 +01001212// Declaration of default image loader callback
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001213bool LoadImageData(Image *image, const int image_idx, std::string *err,
1214 std::string *warn, int req_width, int req_height,
1215 const unsigned char *bytes, int size, void *);
Squareys2d3594d2018-03-13 22:40:53 +01001216#endif
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001217
johan bowald642a3432018-04-01 12:37:18 +02001218#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
1219// Declaration of default image writer callback
1220bool WriteImageData(const std::string *basepath, const std::string *filename,
1221 Image *image, bool embedImages, void *);
1222#endif
1223
Paolo Jovone6601bf2018-07-07 20:43:33 +02001224///
1225/// FilExistsFunction type. Signature for custom filesystem callbacks.
1226///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001227typedef bool (*FileExistsFunction)(const std::string &abs_filename, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001228
1229///
1230/// ExpandFilePathFunction type. Signature for custom filesystem callbacks.
1231///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001232typedef std::string (*ExpandFilePathFunction)(const std::string &, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001233
1234///
1235/// ReadWholeFileFunction type. Signature for custom filesystem callbacks.
1236///
1237typedef bool (*ReadWholeFileFunction)(std::vector<unsigned char> *,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001238 std::string *, const std::string &,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001239 void *);
1240
1241///
1242/// WriteWholeFileFunction type. Signature for custom filesystem callbacks.
1243///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001244typedef bool (*WriteWholeFileFunction)(std::string *, const std::string &,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001245 const std::vector<unsigned char> &,
1246 void *);
1247
1248///
1249/// A structure containing all required filesystem callbacks and a pointer to
1250/// their user data.
1251///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001252struct FsCallbacks {
1253 FileExistsFunction FileExists;
1254 ExpandFilePathFunction ExpandFilePath;
1255 ReadWholeFileFunction ReadWholeFile;
1256 WriteWholeFileFunction WriteWholeFile;
Paolo Jovone6601bf2018-07-07 20:43:33 +02001257
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001258 void *user_data; // An argument that is passed to all fs callbacks
Paolo Jovone6601bf2018-07-07 20:43:33 +02001259};
1260
1261#ifndef TINYGLTF_NO_FS
1262// Declaration of default filesystem callbacks
1263
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001264bool FileExists(const std::string &abs_filename, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001265
Syoyo Fujita3d939fd2020-06-06 18:13:15 +09001266///
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04001267/// Expand file path(e.g. `~` to home directory on posix, `%APPDATA%` to
Syoyo Fujita2c521b32020-12-04 00:50:46 +09001268/// `C:\\Users\\tinygltf\\AppData`)
Syoyo Fujita3d939fd2020-06-06 18:13:15 +09001269///
1270/// @param[in] filepath File path string. Assume UTF-8
1271/// @param[in] userdata User data. Set to `nullptr` if you don't need it.
1272///
1273std::string ExpandFilePath(const std::string &filepath, void *userdata);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001274
1275bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001276 const std::string &filepath, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001277
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001278bool WriteWholeFile(std::string *err, const std::string &filepath,
1279 const std::vector<unsigned char> &contents, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001280#endif
1281
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001282///
1283/// glTF Parser/Serialier context.
1284///
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001285class TinyGLTF {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001286 public:
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001287#ifdef __clang__
1288#pragma clang diagnostic push
1289#pragma clang diagnostic ignored "-Wc++98-compat"
1290#endif
1291
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001292 TinyGLTF() : bin_data_(nullptr), bin_size_(0), is_binary_(false) {}
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001293
1294#ifdef __clang__
1295#pragma clang diagnostic pop
1296#endif
1297
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001298 ~TinyGLTF() {}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001299
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001300 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001301 /// Loads glTF ASCII asset from a file.
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001302 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001303 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001304 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001305 bool LoadASCIIFromFile(Model *model, std::string *err, std::string *warn,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001306 const std::string &filename,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001307 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001308
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001309 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001310 /// Loads glTF ASCII asset from string(memory).
1311 /// `length` = strlen(str);
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001312 /// `base_dir` is a search path of glTF asset(e.g. images). Path Must be an
1313 /// expanded path (e.g. no tilde(`~`), no environment variables). Set warning
1314 /// message to `warn` for example it fails to load asserts. Returns false and
1315 /// set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001316 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001317 bool LoadASCIIFromString(Model *model, std::string *err, std::string *warn,
1318 const char *str, const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001319 const std::string &base_dir,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001320 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001321
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001322 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001323 /// Loads glTF binary asset from a file.
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001324 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001325 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001326 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001327 bool LoadBinaryFromFile(Model *model, std::string *err, std::string *warn,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001328 const std::string &filename,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001329 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001330
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001331 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001332 /// Loads glTF binary asset from memory.
1333 /// `length` = strlen(str);
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001334 /// `base_dir` is a search path of glTF asset(e.g. images). Path Must be an
1335 /// expanded path (e.g. no tilde(`~`), no environment variables).
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001336 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001337 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001338 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001339 bool LoadBinaryFromMemory(Model *model, std::string *err, std::string *warn,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001340 const unsigned char *bytes,
1341 const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001342 const std::string &base_dir = "",
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001343 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001344
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001345 ///
Johan Bowald1af7c1d2019-07-16 14:50:46 +02001346 /// Write glTF to stream, buffers and images will be embeded
1347 ///
1348 bool WriteGltfSceneToStream(Model *model, std::ostream &stream,
1349 bool prettyPrint, bool writeBinary);
1350
1351 ///
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001352 /// Write glTF to file.
1353 ///
johan bowald642a3432018-04-01 12:37:18 +02001354 bool WriteGltfSceneToFile(Model *model, const std::string &filename,
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001355 bool embedImages, bool embedBuffers,
1356 bool prettyPrint, bool writeBinary);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00001357
Squareysff644d82018-03-13 22:36:18 +01001358 ///
1359 /// Set callback to use for loading image data
1360 ///
1361 void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data);
1362
johan bowald642a3432018-04-01 12:37:18 +02001363 ///
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001364 /// Unset(remove) callback of loading image data
1365 ///
1366 void RemoveImageLoader();
1367
1368 ///
johan bowald642a3432018-04-01 12:37:18 +02001369 /// Set callback to use for writing image data
1370 ///
1371 void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data);
1372
Paolo Jovone6601bf2018-07-07 20:43:33 +02001373 ///
1374 /// Set callbacks to use for filesystem (fs) access and their user data
1375 ///
1376 void SetFsCallbacks(FsCallbacks callbacks);
1377
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001378 ///
1379 /// Set serializing default values(default = false).
1380 /// When true, default values are force serialized to .glTF.
Syoyo Fujitaff515702019-08-24 16:29:14 +09001381 /// This may be helpfull if you want to serialize a full description of glTF
1382 /// data.
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001383 ///
Syoyo Fujitaff515702019-08-24 16:29:14 +09001384 /// TODO(LTE): Supply parsing option as function arguments to
1385 /// `LoadASCIIFromFile()` and others, not by a class method
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001386 ///
1387 void SetSerializeDefaultValues(const bool enabled) {
1388 serialize_default_values_ = enabled;
1389 }
1390
Syoyo Fujitaff515702019-08-24 16:29:14 +09001391 bool GetSerializeDefaultValues() const { return serialize_default_values_; }
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001392
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001393 ///
1394 /// Store original JSON string for `extras` and `extensions`.
1395 /// This feature will be useful when the user want to reconstruct custom data
1396 /// structure from JSON string.
1397 ///
1398 void SetStoreOriginalJSONForExtrasAndExtensions(const bool enabled) {
1399 store_original_json_for_extras_and_extensions_ = enabled;
1400 }
1401
1402 bool GetStoreOriginalJSONForExtrasAndExtensions() const {
1403 return store_original_json_for_extras_and_extensions_;
1404 }
1405
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001406 ///
1407 /// Specify whether preserve image channales when loading images or not.
1408 /// (Not effective when the user suppy their own LoadImageData callbacks)
1409 ///
1410 void SetPreserveImageChannels(bool onoff) {
1411 preserve_image_channels_ = onoff;
1412 }
1413
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001414 bool GetPreserveImageChannels() const { return preserve_image_channels_; }
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001415
Syoyo Fujitabeded612016-05-01 20:03:43 +09001416 private:
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001417 ///
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001418 /// Loads glTF asset from string(memory).
1419 /// `length` = strlen(str);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001420 /// Set warning message to `warn` for example it fails to load asserts
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001421 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001422 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001423 bool LoadFromString(Model *model, std::string *err, std::string *warn,
1424 const char *str, const unsigned int length,
1425 const std::string &base_dir, unsigned int check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001426
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001427 const unsigned char *bin_data_ = nullptr;
1428 size_t bin_size_ = 0;
1429 bool is_binary_ = false;
1430
Syoyo Fujitaff515702019-08-24 16:29:14 +09001431 bool serialize_default_values_ = false; ///< Serialize default values?
Squareysff644d82018-03-13 22:36:18 +01001432
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001433 bool store_original_json_for_extras_and_extensions_ = false;
1434
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001435 bool preserve_image_channels_ = false; /// Default false(expand channels to
1436 /// RGBA) for backward compatibility.
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001437
Syoyo Fujita9117abb2022-08-16 20:10:26 +09001438 // Warning & error messages
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09001439 std::string warn_;
1440 std::string err_;
1441
Paolo Jovone6601bf2018-07-07 20:43:33 +02001442 FsCallbacks fs = {
1443#ifndef TINYGLTF_NO_FS
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001444 &tinygltf::FileExists, &tinygltf::ExpandFilePath,
1445 &tinygltf::ReadWholeFile, &tinygltf::WriteWholeFile,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001446
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001447 nullptr // Fs callback user data
Paolo Jovone6601bf2018-07-07 20:43:33 +02001448#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001449 nullptr, nullptr, nullptr, nullptr,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001450
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001451 nullptr // Fs callback user data
Paolo Jovone6601bf2018-07-07 20:43:33 +02001452#endif
1453 };
1454
Squareysff644d82018-03-13 22:36:18 +01001455 LoadImageDataFunction LoadImageData =
1456#ifndef TINYGLTF_NO_STB_IMAGE
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001457 &tinygltf::LoadImageData;
Squareysff644d82018-03-13 22:36:18 +01001458#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001459 nullptr;
Squareysff644d82018-03-13 22:36:18 +01001460#endif
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001461 void *load_image_user_data_{nullptr};
1462 bool user_image_loader_{false};
johan bowald642a3432018-04-01 12:37:18 +02001463
1464 WriteImageDataFunction WriteImageData =
1465#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001466 &tinygltf::WriteImageData;
johan bowald642a3432018-04-01 12:37:18 +02001467#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001468 nullptr;
johan bowald642a3432018-04-01 12:37:18 +02001469#endif
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001470 void *write_image_user_data_{nullptr};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001471};
1472
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001473#ifdef __clang__
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001474#pragma clang diagnostic pop // -Wpadded
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001475#endif
1476
Syoyo Fujita7c877972016-03-08 01:31:49 +09001477} // namespace tinygltf
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001478
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001479#endif // TINY_GLTF_H_
Syoyo Fujitac8d3c762017-05-25 01:42:38 +09001480
Selmar Kok31cb7f92018-10-03 15:39:05 +02001481#if defined(TINYGLTF_IMPLEMENTATION) || defined(__INTELLISENSE__)
Syoyo Fujita7c877972016-03-08 01:31:49 +09001482#include <algorithm>
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001483//#include <cassert>
Paolo Jovone6601bf2018-07-07 20:43:33 +02001484#ifndef TINYGLTF_NO_FS
Syoyo Fujita125d8e52019-11-09 20:52:56 +09001485#include <cstdio>
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001486#include <fstream>
Paolo Jovone6601bf2018-07-07 20:43:33 +02001487#endif
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001488#include <sstream>
Syoyo Fujitabeded612016-05-01 20:03:43 +09001489
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001490#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001491// Disable some warnings for external files.
1492#pragma clang diagnostic push
1493#pragma clang diagnostic ignored "-Wfloat-equal"
1494#pragma clang diagnostic ignored "-Wexit-time-destructors"
1495#pragma clang diagnostic ignored "-Wconversion"
1496#pragma clang diagnostic ignored "-Wold-style-cast"
Syoyo Fujita643ce102016-05-01 17:19:37 +09001497#pragma clang diagnostic ignored "-Wglobal-constructors"
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001498#if __has_warning("-Wreserved-id-macro")
Syoyo Fujita643ce102016-05-01 17:19:37 +09001499#pragma clang diagnostic ignored "-Wreserved-id-macro"
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001500#endif
Syoyo Fujita643ce102016-05-01 17:19:37 +09001501#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
1502#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001503#pragma clang diagnostic ignored "-Wc++98-compat"
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001504#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001505#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
1506#pragma clang diagnostic ignored "-Wswitch-enum"
1507#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001508#pragma clang diagnostic ignored "-Wweak-vtables"
1509#pragma clang diagnostic ignored "-Wcovered-switch-default"
Syoyo Fujitaa8f0b1c2018-08-28 21:33:40 +09001510#if __has_warning("-Wdouble-promotion")
1511#pragma clang diagnostic ignored "-Wdouble-promotion"
1512#endif
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001513#if __has_warning("-Wcomma")
Syoyo Fujitac8d3c762017-05-25 01:42:38 +09001514#pragma clang diagnostic ignored "-Wcomma"
1515#endif
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001516#if __has_warning("-Wzero-as-null-pointer-constant")
1517#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
1518#endif
1519#if __has_warning("-Wcast-qual")
1520#pragma clang diagnostic ignored "-Wcast-qual"
1521#endif
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001522#if __has_warning("-Wmissing-variable-declarations")
1523#pragma clang diagnostic ignored "-Wmissing-variable-declarations"
1524#endif
1525#if __has_warning("-Wmissing-prototypes")
1526#pragma clang diagnostic ignored "-Wmissing-prototypes"
1527#endif
1528#if __has_warning("-Wcast-align")
1529#pragma clang diagnostic ignored "-Wcast-align"
1530#endif
1531#if __has_warning("-Wnewline-eof")
1532#pragma clang diagnostic ignored "-Wnewline-eof"
1533#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001534#if __has_warning("-Wunused-parameter")
1535#pragma clang diagnostic ignored "-Wunused-parameter"
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001536#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001537#if __has_warning("-Wmismatched-tags")
1538#pragma clang diagnostic ignored "-Wmismatched-tags"
1539#endif
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001540#if __has_warning("-Wextra-semi-stmt")
1541#pragma clang diagnostic ignored "-Wextra-semi-stmt"
1542#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001543#endif
1544
1545// Disable GCC warnigs
1546#ifdef __GNUC__
1547#pragma GCC diagnostic push
1548#pragma GCC diagnostic ignored "-Wtype-limits"
Syoyo Fujitaca56f722019-03-07 21:04:25 +09001549#endif // __GNUC__
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001550
krokofc0116b2019-03-03 08:28:49 +02001551#ifndef TINYGLTF_NO_INCLUDE_JSON
jrkooncecba5d6c2019-08-29 11:26:22 -05001552#ifndef TINYGLTF_USE_RAPIDJSON
Alex Wood7319db72019-01-24 15:38:16 -05001553#include "json.hpp"
jrkooncecba5d6c2019-08-29 11:26:22 -05001554#else
Alvaro Barua3ddf8302021-03-18 00:21:23 +00001555#ifndef TINYGLTF_NO_INCLUDE_RAPIDJSON
jrkooncecba5d6c2019-08-29 11:26:22 -05001556#include "document.h"
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001557#include "prettywriter.h"
1558#include "rapidjson.h"
jrkooncecba5d6c2019-08-29 11:26:22 -05001559#include "stringbuffer.h"
1560#include "writer.h"
jrkooncecba5d6c2019-08-29 11:26:22 -05001561#endif
krokof4b6d112019-03-03 01:11:31 +02001562#endif
Alvaro Barua3ddf8302021-03-18 00:21:23 +00001563#endif
Alex Wood7319db72019-01-24 15:38:16 -05001564
1565#ifdef TINYGLTF_ENABLE_DRACO
Alex Wood7319db72019-01-24 15:38:16 -05001566#include "draco/compression/decode.h"
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001567#include "draco/core/decoder_buffer.h"
Alex Wood7319db72019-01-24 15:38:16 -05001568#endif
Squareys2d3594d2018-03-13 22:40:53 +01001569
1570#ifndef TINYGLTF_NO_STB_IMAGE
krokofc0116b2019-03-03 08:28:49 +02001571#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE
Alex Wood7319db72019-01-24 15:38:16 -05001572#include "stb_image.h"
Squareys2d3594d2018-03-13 22:40:53 +01001573#endif
krokof4b6d112019-03-03 01:11:31 +02001574#endif
Squareys2d3594d2018-03-13 22:40:53 +01001575
johan bowald642a3432018-04-01 12:37:18 +02001576#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
krokofc0116b2019-03-03 08:28:49 +02001577#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE
Alex Wood7319db72019-01-24 15:38:16 -05001578#include "stb_image_write.h"
johan bowald642a3432018-04-01 12:37:18 +02001579#endif
krokof4b6d112019-03-03 01:11:31 +02001580#endif
johan bowald642a3432018-04-01 12:37:18 +02001581
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001582#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001583#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001584#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001585
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001586#ifdef __GNUC__
1587#pragma GCC diagnostic pop
1588#endif
1589
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001590#ifdef _WIN32
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001591
1592// issue 143.
1593// Define NOMINMAX to avoid min/max defines,
1594// but undef it after included windows.h
1595#ifndef NOMINMAX
1596#define TINYGLTF_INTERNAL_NOMINMAX
1597#define NOMINMAX
1598#endif
1599
Syoyo Fujita87be0ce2019-02-19 21:36:32 +09001600#ifndef WIN32_LEAN_AND_MEAN
1601#define WIN32_LEAN_AND_MEAN
1602#define TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1603#endif
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001604#include <windows.h> // include API for expanding a file path
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001605
Syoyo Fujita87be0ce2019-02-19 21:36:32 +09001606#ifdef TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1607#undef WIN32_LEAN_AND_MEAN
1608#endif
1609
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001610#if defined(TINYGLTF_INTERNAL_NOMINMAX)
1611#undef NOMINMAX
1612#endif
1613
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001614#if defined(__GLIBCXX__) // mingw
Syoyo Fujita45cac782019-11-09 20:42:55 +09001615
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001616#include <fcntl.h> // _O_RDONLY
1617
1618#include <ext/stdio_filebuf.h> // fstream (all sorts of IO stuff) + stdio_filebuf (=streambuf)
Syoyo Fujita45cac782019-11-09 20:42:55 +09001619
1620#endif
1621
Julian Smith0598a202021-08-25 12:06:08 +01001622#elif !defined(__ANDROID__) && !defined(__OpenBSD__)
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001623//#include <wordexp.h>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001624#endif
1625
Christopher Sean Morrison2c9b2562022-05-14 19:00:19 -04001626#if defined(__sparcv9) || defined(__powerpc__)
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001627// Big endian
1628#else
1629#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
1630#define TINYGLTF_LITTLE_ENDIAN 1
1631#endif
1632#endif
1633
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001634namespace {
jrkooncecba5d6c2019-08-29 11:26:22 -05001635#ifdef TINYGLTF_USE_RAPIDJSON
jrkooncece7fa742019-09-04 13:31:44 -05001636
1637#ifdef TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001638// This uses the RapidJSON CRTAllocator. It is thread safe and multiple
1639// documents may be active at once.
1640using json =
1641 rapidjson::GenericValue<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
1642using json_const_iterator = json::ConstMemberIterator;
1643using json_const_array_iterator = json const *;
1644using JsonDocument =
jrkooncece7fa742019-09-04 13:31:44 -05001645 rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001646rapidjson::CrtAllocator s_CrtAllocator; // stateless and thread safe
1647rapidjson::CrtAllocator &GetAllocator() { return s_CrtAllocator; }
jrkooncece7fa742019-09-04 13:31:44 -05001648#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001649// This uses the default RapidJSON MemoryPoolAllocator. It is very fast, but
1650// not thread safe. Only a single JsonDocument may be active at any one time,
1651// meaning only a single gltf load/save can be active any one time.
1652using json = rapidjson::Value;
1653using json_const_iterator = json::ConstMemberIterator;
1654using json_const_array_iterator = json const *;
1655rapidjson::Document *s_pActiveDocument = nullptr;
1656rapidjson::Document::AllocatorType &GetAllocator() {
1657 assert(s_pActiveDocument); // Root json node must be JsonDocument type
1658 return s_pActiveDocument->GetAllocator();
1659}
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001660
1661#ifdef __clang__
1662#pragma clang diagnostic push
1663// Suppress JsonDocument(JsonDocument &&rhs) noexcept
1664#pragma clang diagnostic ignored "-Wunused-member-function"
1665#endif
1666
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001667struct JsonDocument : public rapidjson::Document {
1668 JsonDocument() {
1669 assert(s_pActiveDocument ==
1670 nullptr); // When using default allocator, only one document can be
1671 // active at a time, if you need multiple active at once,
1672 // define TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
1673 s_pActiveDocument = this;
jrkooncece7fa742019-09-04 13:31:44 -05001674 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001675 JsonDocument(const JsonDocument &) = delete;
1676 JsonDocument(JsonDocument &&rhs) noexcept
jrkooncece7fa742019-09-04 13:31:44 -05001677 : rapidjson::Document(std::move(rhs)) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001678 s_pActiveDocument = this;
1679 rhs.isNil = true;
1680 }
1681 ~JsonDocument() {
1682 if (!isNil) {
1683 s_pActiveDocument = nullptr;
jrkooncecba5d6c2019-08-29 11:26:22 -05001684 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001685 }
jrkooncece7fa742019-09-04 13:31:44 -05001686
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001687 private:
1688 bool isNil = false;
1689};
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001690
1691#ifdef __clang__
1692#pragma clang diagnostic pop
1693#endif
1694
jrkooncece7fa742019-09-04 13:31:44 -05001695#endif // TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001696
jrkooncecba5d6c2019-08-29 11:26:22 -05001697#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001698using nlohmann::json;
1699using json_const_iterator = json::const_iterator;
1700using json_const_array_iterator = json_const_iterator;
1701using JsonDocument = json;
jrkooncecba5d6c2019-08-29 11:26:22 -05001702#endif
1703
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001704void JsonParse(JsonDocument &doc, const char *str, size_t length,
1705 bool throwExc = false) {
jrkooncecba5d6c2019-08-29 11:26:22 -05001706#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001707 (void)throwExc;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001708 doc.Parse(str, length);
jrkooncecba5d6c2019-08-29 11:26:22 -05001709#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001710 doc = json::parse(str, str + length, nullptr, throwExc);
jrkooncecba5d6c2019-08-29 11:26:22 -05001711#endif
jrkooncecba5d6c2019-08-29 11:26:22 -05001712}
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001713} // namespace
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001714
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001715#ifdef __APPLE__
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001716#include "TargetConditionals.h"
Syoyo Fujitab23f6fe2017-11-15 01:03:09 +09001717#endif
1718
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001719#ifdef __clang__
1720#pragma clang diagnostic push
1721#pragma clang diagnostic ignored "-Wc++98-compat"
1722#endif
1723
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09001724namespace tinygltf {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001725
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001726///
1727/// Internal LoadImageDataOption struct.
1728/// This struct is passed through `user_pointer` in LoadImageData.
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001729/// The struct is not passed when the user supply their own LoadImageData
1730/// callbacks.
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001731///
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001732struct LoadImageDataOption {
1733 // true: preserve image channels(e.g. load as RGB image if the image has RGB
1734 // channels) default `false`(channels are expanded to RGBA for backward
1735 // compatiblity).
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001736 bool preserve_channels{false};
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001737};
1738
Selmar Kok31cb7f92018-10-03 15:39:05 +02001739// Equals function for Value, for recursivity
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001740static bool Equals(const tinygltf::Value &one, const tinygltf::Value &other) {
1741 if (one.Type() != other.Type()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001742
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001743 switch (one.Type()) {
1744 case NULL_TYPE:
1745 return true;
1746 case BOOL_TYPE:
1747 return one.Get<bool>() == other.Get<bool>();
Syoyo Fujita150f2432019-07-25 19:22:44 +09001748 case REAL_TYPE:
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001749 return TINYGLTF_DOUBLE_EQUAL(one.Get<double>(), other.Get<double>());
1750 case INT_TYPE:
1751 return one.Get<int>() == other.Get<int>();
1752 case OBJECT_TYPE: {
1753 auto oneObj = one.Get<tinygltf::Value::Object>();
1754 auto otherObj = other.Get<tinygltf::Value::Object>();
1755 if (oneObj.size() != otherObj.size()) return false;
1756 for (auto &it : oneObj) {
1757 auto otherIt = otherObj.find(it.first);
1758 if (otherIt == otherObj.end()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001759
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001760 if (!Equals(it.second, otherIt->second)) return false;
1761 }
1762 return true;
1763 }
1764 case ARRAY_TYPE: {
1765 if (one.Size() != other.Size()) return false;
1766 for (int i = 0; i < int(one.Size()); ++i)
Selmara63cc632019-04-16 16:57:43 +02001767 if (!Equals(one.Get(i), other.Get(i))) return false;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001768 return true;
1769 }
1770 case STRING_TYPE:
1771 return one.Get<std::string>() == other.Get<std::string>();
1772 case BINARY_TYPE:
1773 return one.Get<std::vector<unsigned char> >() ==
1774 other.Get<std::vector<unsigned char> >();
1775 default: {
1776 // unhandled type
1777 return false;
1778 }
1779 }
Selmar Kok31cb7f92018-10-03 15:39:05 +02001780}
1781
1782// Equals function for std::vector<double> using TINYGLTF_DOUBLE_EPSILON
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001783static bool Equals(const std::vector<double> &one,
1784 const std::vector<double> &other) {
1785 if (one.size() != other.size()) return false;
1786 for (int i = 0; i < int(one.size()); ++i) {
1787 if (!TINYGLTF_DOUBLE_EQUAL(one[size_t(i)], other[size_t(i)])) return false;
1788 }
1789 return true;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001790}
1791
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001792bool Accessor::operator==(const Accessor &other) const {
1793 return this->bufferView == other.bufferView &&
1794 this->byteOffset == other.byteOffset &&
1795 this->componentType == other.componentType &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001796 this->count == other.count && this->extensions == other.extensions &&
1797 this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001798 Equals(this->maxValues, other.maxValues) &&
1799 Equals(this->minValues, other.minValues) && this->name == other.name &&
1800 this->normalized == other.normalized && this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001801}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001802bool Animation::operator==(const Animation &other) const {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001803 return this->channels == other.channels &&
1804 this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001805 this->name == other.name && this->samplers == other.samplers;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001806}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001807bool AnimationChannel::operator==(const AnimationChannel &other) const {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001808 return this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001809 this->target_node == other.target_node &&
1810 this->target_path == other.target_path &&
1811 this->sampler == other.sampler;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001812}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001813bool AnimationSampler::operator==(const AnimationSampler &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001814 return this->extras == other.extras && this->extensions == other.extensions &&
1815 this->input == other.input &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001816 this->interpolation == other.interpolation &&
1817 this->output == other.output;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001818}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001819bool Asset::operator==(const Asset &other) const {
1820 return this->copyright == other.copyright &&
1821 this->extensions == other.extensions && this->extras == other.extras &&
1822 this->generator == other.generator &&
1823 this->minVersion == other.minVersion && this->version == other.version;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001824}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001825bool Buffer::operator==(const Buffer &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001826 return this->data == other.data && this->extensions == other.extensions &&
1827 this->extras == other.extras && this->name == other.name &&
1828 this->uri == other.uri;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001829}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001830bool BufferView::operator==(const BufferView &other) const {
1831 return this->buffer == other.buffer && this->byteLength == other.byteLength &&
1832 this->byteOffset == other.byteOffset &&
1833 this->byteStride == other.byteStride && this->name == other.name &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001834 this->target == other.target && this->extensions == other.extensions &&
1835 this->extras == other.extras &&
Alexander Wood0d77a292019-01-26 08:58:45 -05001836 this->dracoDecoded == other.dracoDecoded;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001837}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001838bool Camera::operator==(const Camera &other) const {
1839 return this->name == other.name && this->extensions == other.extensions &&
1840 this->extras == other.extras &&
1841 this->orthographic == other.orthographic &&
1842 this->perspective == other.perspective && this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001843}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001844bool Image::operator==(const Image &other) const {
1845 return this->bufferView == other.bufferView &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001846 this->component == other.component &&
1847 this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001848 this->height == other.height && this->image == other.image &&
1849 this->mimeType == other.mimeType && this->name == other.name &&
1850 this->uri == other.uri && this->width == other.width;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001851}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001852bool Light::operator==(const Light &other) const {
1853 return Equals(this->color, other.color) && this->name == other.name &&
1854 this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001855}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001856bool Material::operator==(const Material &other) const {
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001857 return (this->pbrMetallicRoughness == other.pbrMetallicRoughness) &&
1858 (this->normalTexture == other.normalTexture) &&
1859 (this->occlusionTexture == other.occlusionTexture) &&
1860 (this->emissiveTexture == other.emissiveTexture) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09001861 Equals(this->emissiveFactor, other.emissiveFactor) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001862 (this->alphaMode == other.alphaMode) &&
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001863 TINYGLTF_DOUBLE_EQUAL(this->alphaCutoff, other.alphaCutoff) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001864 (this->doubleSided == other.doubleSided) &&
1865 (this->extensions == other.extensions) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09001866 (this->extras == other.extras) && (this->values == other.values) &&
1867 (this->additionalValues == other.additionalValues) &&
1868 (this->name == other.name);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001869}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001870bool Mesh::operator==(const Mesh &other) const {
1871 return this->extensions == other.extensions && this->extras == other.extras &&
Selmar Kok81b672b2019-10-18 16:08:44 +02001872 this->name == other.name && Equals(this->weights, other.weights) &&
1873 this->primitives == other.primitives;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001874}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001875bool Model::operator==(const Model &other) const {
1876 return this->accessors == other.accessors &&
1877 this->animations == other.animations && this->asset == other.asset &&
1878 this->buffers == other.buffers &&
1879 this->bufferViews == other.bufferViews &&
1880 this->cameras == other.cameras &&
1881 this->defaultScene == other.defaultScene &&
1882 this->extensions == other.extensions &&
1883 this->extensionsRequired == other.extensionsRequired &&
1884 this->extensionsUsed == other.extensionsUsed &&
1885 this->extras == other.extras && this->images == other.images &&
1886 this->lights == other.lights && this->materials == other.materials &&
1887 this->meshes == other.meshes && this->nodes == other.nodes &&
1888 this->samplers == other.samplers && this->scenes == other.scenes &&
1889 this->skins == other.skins && this->textures == other.textures;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001890}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001891bool Node::operator==(const Node &other) const {
1892 return this->camera == other.camera && this->children == other.children &&
1893 this->extensions == other.extensions && this->extras == other.extras &&
1894 Equals(this->matrix, other.matrix) && this->mesh == other.mesh &&
1895 this->name == other.name && Equals(this->rotation, other.rotation) &&
1896 Equals(this->scale, other.scale) && this->skin == other.skin &&
1897 Equals(this->translation, other.translation) &&
1898 Equals(this->weights, other.weights);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001899}
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001900bool SpotLight::operator==(const SpotLight &other) const {
1901 return this->extensions == other.extensions && this->extras == other.extras &&
1902 TINYGLTF_DOUBLE_EQUAL(this->innerConeAngle, other.innerConeAngle) &&
1903 TINYGLTF_DOUBLE_EQUAL(this->outerConeAngle, other.outerConeAngle);
1904}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001905bool OrthographicCamera::operator==(const OrthographicCamera &other) const {
1906 return this->extensions == other.extensions && this->extras == other.extras &&
1907 TINYGLTF_DOUBLE_EQUAL(this->xmag, other.xmag) &&
1908 TINYGLTF_DOUBLE_EQUAL(this->ymag, other.ymag) &&
1909 TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
1910 TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001911}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001912bool Parameter::operator==(const Parameter &other) const {
1913 if (this->bool_value != other.bool_value ||
1914 this->has_number_value != other.has_number_value)
1915 return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001916
Selmar Kok2bda71c2018-10-05 14:36:05 +02001917 if (!TINYGLTF_DOUBLE_EQUAL(this->number_value, other.number_value))
1918 return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001919
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001920 if (this->json_double_value.size() != other.json_double_value.size())
1921 return false;
1922 for (auto &it : this->json_double_value) {
1923 auto otherIt = other.json_double_value.find(it.first);
1924 if (otherIt == other.json_double_value.end()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001925
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001926 if (!TINYGLTF_DOUBLE_EQUAL(it.second, otherIt->second)) return false;
1927 }
1928
1929 if (!Equals(this->number_array, other.number_array)) return false;
1930
1931 if (this->string_value != other.string_value) return false;
1932
1933 return true;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001934}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001935bool PerspectiveCamera::operator==(const PerspectiveCamera &other) const {
1936 return TINYGLTF_DOUBLE_EQUAL(this->aspectRatio, other.aspectRatio) &&
1937 this->extensions == other.extensions && this->extras == other.extras &&
1938 TINYGLTF_DOUBLE_EQUAL(this->yfov, other.yfov) &&
1939 TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
1940 TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001941}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001942bool Primitive::operator==(const Primitive &other) const {
1943 return this->attributes == other.attributes && this->extras == other.extras &&
1944 this->indices == other.indices && this->material == other.material &&
1945 this->mode == other.mode && this->targets == other.targets;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001946}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001947bool Sampler::operator==(const Sampler &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001948 return this->extensions == other.extensions && this->extras == other.extras &&
1949 this->magFilter == other.magFilter &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001950 this->minFilter == other.minFilter && this->name == other.name &&
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001951 this->wrapS == other.wrapS && this->wrapT == other.wrapT;
Syoyo Fujita2c521b32020-12-04 00:50:46 +09001952
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09001953 // this->wrapR == other.wrapR
Selmar Kok31cb7f92018-10-03 15:39:05 +02001954}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001955bool Scene::operator==(const Scene &other) const {
1956 return this->extensions == other.extensions && this->extras == other.extras &&
1957 this->name == other.name && this->nodes == other.nodes;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001958}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001959bool Skin::operator==(const Skin &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001960 return this->extensions == other.extensions && this->extras == other.extras &&
1961 this->inverseBindMatrices == other.inverseBindMatrices &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001962 this->joints == other.joints && this->name == other.name &&
1963 this->skeleton == other.skeleton;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001964}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001965bool Texture::operator==(const Texture &other) const {
1966 return this->extensions == other.extensions && this->extras == other.extras &&
1967 this->name == other.name && this->sampler == other.sampler &&
1968 this->source == other.source;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001969}
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001970bool TextureInfo::operator==(const TextureInfo &other) const {
1971 return this->extensions == other.extensions && this->extras == other.extras &&
1972 this->index == other.index && this->texCoord == other.texCoord;
1973}
1974bool NormalTextureInfo::operator==(const NormalTextureInfo &other) const {
1975 return this->extensions == other.extensions && this->extras == other.extras &&
1976 this->index == other.index && this->texCoord == other.texCoord &&
1977 TINYGLTF_DOUBLE_EQUAL(this->scale, other.scale);
1978}
1979bool OcclusionTextureInfo::operator==(const OcclusionTextureInfo &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->strength, other.strength);
1983}
1984bool PbrMetallicRoughness::operator==(const PbrMetallicRoughness &other) const {
1985 return this->extensions == other.extensions && this->extras == other.extras &&
1986 (this->baseColorTexture == other.baseColorTexture) &&
1987 (this->metallicRoughnessTexture == other.metallicRoughnessTexture) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09001988 Equals(this->baseColorFactor, other.baseColorFactor) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001989 TINYGLTF_DOUBLE_EQUAL(this->metallicFactor, other.metallicFactor) &&
1990 TINYGLTF_DOUBLE_EQUAL(this->roughnessFactor, other.roughnessFactor);
1991}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001992bool Value::operator==(const Value &other) const {
1993 return Equals(*this, other);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001994}
1995
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001996static void swap4(unsigned int *val) {
1997#ifdef TINYGLTF_LITTLE_ENDIAN
1998 (void)val;
1999#else
2000 unsigned int tmp = *val;
2001 unsigned char *dst = reinterpret_cast<unsigned char *>(val);
2002 unsigned char *src = reinterpret_cast<unsigned char *>(&tmp);
2003
2004 dst[0] = src[3];
2005 dst[1] = src[2];
2006 dst[2] = src[1];
2007 dst[3] = src[0];
2008#endif
2009}
2010
Syoyo Fujitabeded612016-05-01 20:03:43 +09002011static std::string JoinPath(const std::string &path0,
2012 const std::string &path1) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002013 if (path0.empty()) {
2014 return path1;
2015 } else {
2016 // check '/'
2017 char lastChar = *path0.rbegin();
2018 if (lastChar != '/') {
2019 return path0 + std::string("/") + path1;
2020 } else {
2021 return path0 + path1;
2022 }
2023 }
2024}
2025
Syoyo Fujita643ce102016-05-01 17:19:37 +09002026static std::string FindFile(const std::vector<std::string> &paths,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002027 const std::string &filepath, FsCallbacks *fs) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002028 if (fs == nullptr || fs->ExpandFilePath == nullptr ||
2029 fs->FileExists == nullptr) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002030 // Error, fs callback[s] missing
2031 return std::string();
2032 }
2033
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002034 for (size_t i = 0; i < paths.size(); i++) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002035 std::string absPath =
2036 fs->ExpandFilePath(JoinPath(paths[i], filepath), fs->user_data);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002037 if (fs->FileExists(absPath, fs->user_data)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002038 return absPath;
2039 }
2040 }
2041
2042 return std::string();
2043}
2044
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09002045static std::string GetFilePathExtension(const std::string &FileName) {
johan bowald642a3432018-04-01 12:37:18 +02002046 if (FileName.find_last_of(".") != std::string::npos)
2047 return FileName.substr(FileName.find_last_of(".") + 1);
2048 return "";
2049}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002050
Syoyo Fujita643ce102016-05-01 17:19:37 +09002051static std::string GetBaseDir(const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002052 if (filepath.find_last_of("/\\") != std::string::npos)
2053 return filepath.substr(0, filepath.find_last_of("/\\"));
2054 return "";
2055}
2056
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002057static std::string GetBaseFilename(const std::string &filepath) {
Martin Gerhardyfae65432022-03-13 18:42:39 +01002058 auto idx = filepath.find_last_of("/\\");
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002059 if (idx != std::string::npos) return filepath.substr(idx + 1);
Alexander Wood190382a2021-10-08 12:19:13 -04002060 return filepath;
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002061}
2062
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002063std::string base64_encode(unsigned char const *, unsigned int len);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002064std::string base64_decode(std::string const &s);
2065
2066/*
2067 base64.cpp and base64.h
2068
2069 Copyright (C) 2004-2008 René Nyffenegger
2070
2071 This source code is provided 'as-is', without any express or implied
2072 warranty. In no event will the author be held liable for any damages
2073 arising from the use of this software.
2074
2075 Permission is granted to anyone to use this software for any purpose,
2076 including commercial applications, and to alter it and redistribute it
2077 freely, subject to the following restrictions:
2078
2079 1. The origin of this source code must not be misrepresented; you must not
2080 claim that you wrote the original source code. If you use this source code
2081 in a product, an acknowledgment in the product documentation would be
2082 appreciated but is not required.
2083
2084 2. Altered source versions must be plainly marked as such, and must not be
2085 misrepresented as being the original source code.
2086
2087 3. This notice may not be removed or altered from any source distribution.
2088
2089 René Nyffenegger rene.nyffenegger@adp-gmbh.ch
2090
2091*/
2092
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002093#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09002094#pragma clang diagnostic push
Syoyo Fujita643ce102016-05-01 17:19:37 +09002095#pragma clang diagnostic ignored "-Wsign-conversion"
2096#pragma clang diagnostic ignored "-Wconversion"
Syoyo Fujita6fd91622016-05-01 20:27:30 +09002097#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002098
2099static inline bool is_base64(unsigned char c) {
2100 return (isalnum(c) || (c == '+') || (c == '/'));
2101}
2102
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002103std::string base64_encode(unsigned char const *bytes_to_encode,
2104 unsigned int in_len) {
johan bowald30c53472018-03-30 11:49:36 +02002105 std::string ret;
2106 int i = 0;
2107 int j = 0;
2108 unsigned char char_array_3[3];
2109 unsigned char char_array_4[4];
2110
Syoyo Fujitaff515702019-08-24 16:29:14 +09002111 const char *base64_chars =
2112 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2113 "abcdefghijklmnopqrstuvwxyz"
2114 "0123456789+/";
2115
johan bowald30c53472018-03-30 11:49:36 +02002116 while (in_len--) {
2117 char_array_3[i++] = *(bytes_to_encode++);
2118 if (i == 3) {
2119 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002120 char_array_4[1] =
2121 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
2122 char_array_4[2] =
2123 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
johan bowald30c53472018-03-30 11:49:36 +02002124 char_array_4[3] = char_array_3[2] & 0x3f;
2125
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002126 for (i = 0; (i < 4); i++) ret += base64_chars[char_array_4[i]];
johan bowald30c53472018-03-30 11:49:36 +02002127 i = 0;
2128 }
2129 }
2130
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002131 if (i) {
2132 for (j = i; j < 3; j++) char_array_3[j] = '\0';
johan bowald30c53472018-03-30 11:49:36 +02002133
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002134 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
2135 char_array_4[1] =
2136 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
2137 char_array_4[2] =
2138 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
johan bowald30c53472018-03-30 11:49:36 +02002139
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002140 for (j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]];
johan bowald30c53472018-03-30 11:49:36 +02002141
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002142 while ((i++ < 3)) ret += '=';
johan bowald30c53472018-03-30 11:49:36 +02002143 }
2144
2145 return ret;
johan bowald30c53472018-03-30 11:49:36 +02002146}
2147
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002148std::string base64_decode(std::string const &encoded_string) {
Syoyo Fujita8c6774c2016-05-01 20:36:29 +09002149 int in_len = static_cast<int>(encoded_string.size());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002150 int i = 0;
2151 int j = 0;
2152 int in_ = 0;
2153 unsigned char char_array_4[4], char_array_3[3];
2154 std::string ret;
2155
Syoyo Fujitaff515702019-08-24 16:29:14 +09002156 const std::string base64_chars =
2157 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2158 "abcdefghijklmnopqrstuvwxyz"
2159 "0123456789+/";
2160
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002161 while (in_len-- && (encoded_string[in_] != '=') &&
2162 is_base64(encoded_string[in_])) {
2163 char_array_4[i++] = encoded_string[in_];
2164 in_++;
2165 if (i == 4) {
2166 for (i = 0; i < 4; i++)
Syoyo Fujitabeded612016-05-01 20:03:43 +09002167 char_array_4[i] =
2168 static_cast<unsigned char>(base64_chars.find(char_array_4[i]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002169
2170 char_array_3[0] =
2171 (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
2172 char_array_3[1] =
2173 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
2174 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
2175
Syoyo Fujita7c877972016-03-08 01:31:49 +09002176 for (i = 0; (i < 3); i++) ret += char_array_3[i];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002177 i = 0;
2178 }
2179 }
2180
2181 if (i) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09002182 for (j = i; j < 4; j++) char_array_4[j] = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002183
2184 for (j = 0; j < 4; j++)
Syoyo Fujitabeded612016-05-01 20:03:43 +09002185 char_array_4[j] =
2186 static_cast<unsigned char>(base64_chars.find(char_array_4[j]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002187
2188 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
2189 char_array_3[1] =
2190 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
2191 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
2192
Syoyo Fujita7c877972016-03-08 01:31:49 +09002193 for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002194 }
2195
2196 return ret;
2197}
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002198#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09002199#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +09002200#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002201
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002202// https://github.com/syoyo/tinygltf/issues/228
2203// TODO(syoyo): Use uriparser https://uriparser.github.io/ for stricter Uri
2204// decoding?
2205//
Alexander Woode4bc6c72021-10-14 08:54:59 -04002206// Uri Decoding from DLIB
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002207// http://dlib.net/dlib/server/server_http.cpp.html
Alexander Woode4bc6c72021-10-14 08:54:59 -04002208// --- dlib begin ------------------------------------------------------------
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002209// Copyright (C) 2003 Davis E. King (davis@dlib.net)
Alexander Woode4bc6c72021-10-14 08:54:59 -04002210// License: Boost Software License
2211// Boost Software License - Version 1.0 - August 17th, 2003
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002212
Alexander Woode4bc6c72021-10-14 08:54:59 -04002213// Permission is hereby granted, free of charge, to any person or organization
2214// obtaining a copy of the software and accompanying documentation covered by
2215// this license (the "Software") to use, reproduce, display, distribute,
2216// execute, and transmit the Software, and to prepare derivative works of the
2217// Software, and to permit third-parties to whom the Software is furnished to
2218// do so, all subject to the following:
2219// The copyright notices in the Software and this entire statement, including
2220// the above license grant, this restriction and the following disclaimer,
2221// must be included in all copies of the Software, in whole or in part, and
2222// all derivative works of the Software, unless such copies or derivative
2223// works are solely in the form of machine-executable object code generated by
2224// a source language processor.
2225// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2226// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2227// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
2228// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
2229// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
2230// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2231// DEALINGS IN THE SOFTWARE.
Syoyo Fujitacbd38852022-02-26 21:19:15 +09002232//
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002233namespace dlib {
2234
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002235inline unsigned char from_hex(unsigned char ch) {
2236 if (ch <= '9' && ch >= '0')
2237 ch -= '0';
2238 else if (ch <= 'f' && ch >= 'a')
2239 ch -= 'a' - 10;
2240 else if (ch <= 'F' && ch >= 'A')
2241 ch -= 'A' - 10;
2242 else
2243 ch = 0;
2244 return ch;
2245}
2246
2247static const std::string urldecode(const std::string &str) {
2248 using namespace std;
2249 string result;
2250 string::size_type i;
2251 for (i = 0; i < str.size(); ++i) {
2252 if (str[i] == '+') {
2253 result += ' ';
2254 } else if (str[i] == '%' && str.size() > i + 2) {
2255 const unsigned char ch1 =
2256 from_hex(static_cast<unsigned char>(str[i + 1]));
2257 const unsigned char ch2 =
2258 from_hex(static_cast<unsigned char>(str[i + 2]));
2259 const unsigned char ch = static_cast<unsigned char>((ch1 << 4) | ch2);
2260 result += static_cast<char>(ch);
2261 i += 2;
2262 } else {
2263 result += str[i];
2264 }
2265 }
2266 return result;
2267}
2268
2269} // namespace dlib
2270// --- dlib end --------------------------------------------------------------
2271
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002272static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002273 std::string *warn, const std::string &filename,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002274 const std::string &basedir, bool required,
2275 size_t reqBytes, bool checkSize, FsCallbacks *fs) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002276 if (fs == nullptr || fs->FileExists == nullptr ||
2277 fs->ExpandFilePath == nullptr || fs->ReadWholeFile == nullptr) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002278 // This is a developer error, assert() ?
2279 if (err) {
2280 (*err) += "FS callback[s] not set\n";
2281 }
2282 return false;
2283 }
2284
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002285 std::string *failMsgOut = required ? err : warn;
Selmar Koke3b3fa92018-08-22 19:04:21 +02002286
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002287 out->clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002288
2289 std::vector<std::string> paths;
2290 paths.push_back(basedir);
2291 paths.push_back(".");
2292
Paolo Jovone6601bf2018-07-07 20:43:33 +02002293 std::string filepath = FindFile(paths, filename, fs);
jianghaosenb721fb72017-08-25 21:01:17 +08002294 if (filepath.empty() || filename.empty()) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002295 if (failMsgOut) {
2296 (*failMsgOut) += "File not found : " + filename + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002297 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002298 return false;
2299 }
2300
Paolo Jovone6601bf2018-07-07 20:43:33 +02002301 std::vector<unsigned char> buf;
2302 std::string fileReadErr;
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002303 bool fileRead =
2304 fs->ReadWholeFile(&buf, &fileReadErr, filepath, fs->user_data);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002305 if (!fileRead) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002306 if (failMsgOut) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002307 (*failMsgOut) +=
2308 "File read error : " + filepath + " : " + fileReadErr + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002309 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002310 return false;
2311 }
2312
Paolo Jovone6601bf2018-07-07 20:43:33 +02002313 size_t sz = buf.size();
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09002314 if (sz == 0) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002315 if (failMsgOut) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002316 (*failMsgOut) += "File is empty : " + filepath + "\n";
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002317 }
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09002318 return false;
2319 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002320
2321 if (checkSize) {
2322 if (reqBytes == sz) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002323 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002324 return true;
2325 } else {
2326 std::stringstream ss;
2327 ss << "File size mismatch : " << filepath << ", requestedBytes "
2328 << reqBytes << ", but got " << sz << std::endl;
Selmar Koke3b3fa92018-08-22 19:04:21 +02002329 if (failMsgOut) {
2330 (*failMsgOut) += ss.str();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002331 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002332 return false;
2333 }
2334 }
2335
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002336 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002337 return true;
2338}
2339
Squareysff644d82018-03-13 22:36:18 +01002340void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002341 LoadImageData = func;
2342 load_image_user_data_ = user_data;
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002343 user_image_loader_ = true;
2344}
2345
2346void TinyGLTF::RemoveImageLoader() {
2347 LoadImageData =
2348#ifndef TINYGLTF_NO_STB_IMAGE
2349 &tinygltf::LoadImageData;
2350#else
2351 nullptr;
2352#endif
2353
2354 load_image_user_data_ = nullptr;
2355 user_image_loader_ = false;
Squareysff644d82018-03-13 22:36:18 +01002356}
2357
Squareys2d3594d2018-03-13 22:40:53 +01002358#ifndef TINYGLTF_NO_STB_IMAGE
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002359bool LoadImageData(Image *image, const int image_idx, std::string *err,
2360 std::string *warn, int req_width, int req_height,
2361 const unsigned char *bytes, int size, void *user_data) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002362 (void)warn;
2363
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002364 LoadImageDataOption option;
2365 if (user_data) {
2366 option = *reinterpret_cast<LoadImageDataOption *>(user_data);
2367 }
2368
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002369 int w = 0, h = 0, comp = 0, req_comp = 0;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002370
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002371 unsigned char *data = nullptr;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002372
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002373 // preserve_channels true: Use channels stored in the image file.
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09002374 // false: force 32-bit textures for common Vulkan compatibility. It appears
2375 // that some GPU drivers do not support 24-bit images for Vulkan
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002376 req_comp = option.preserve_channels ? 0 : 4;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002377 int bits = 8;
2378 int pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002379
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002380 // It is possible that the image we want to load is a 16bit per channel image
2381 // We are going to attempt to load it as 16bit per channel, and if it worked,
2382 // set the image data accodingly. We are casting the returned pointer into
2383 // unsigned char, because we are representing "bytes". But we are updating
2384 // the Image metadata to signal that this image uses 2 bytes (16bits) per
2385 // channel:
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002386 if (stbi_is_16_bit_from_memory(bytes, size)) {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002387 data = reinterpret_cast<unsigned char *>(
2388 stbi_load_16_from_memory(bytes, size, &w, &h, &comp, req_comp));
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002389 if (data) {
2390 bits = 16;
2391 pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT;
2392 }
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002393 }
2394
2395 // at this point, if data is still NULL, it means that the image wasn't
2396 // 16bit per channel, we are going to load it as a normal 8bit per channel
2397 // mage as we used to do:
Aurélien Chatelain5cb43462017-05-24 09:55:14 +00002398 // if image cannot be decoded, ignore parsing and keep it by its path
2399 // don't break in this case
Syoyo Fujita5b407452017-06-04 17:42:41 +09002400 // FIXME we should only enter this function if the image is embedded. If
2401 // image->uri references
2402 // an image file, it should be left as it is. Image loading should not be
2403 // mandatory (to support other formats)
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002404 if (!data) data = stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002405 if (!data) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002406 // NOTE: you can use `warn` instead of `err`
Syoyo Fujitabeded612016-05-01 20:03:43 +09002407 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002408 (*err) +=
2409 "Unknown image format. STB cannot decode image data for image[" +
2410 std::to_string(image_idx) + "] name = \"" + image->name + "\".\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002411 }
Omar C. Fd492efc2018-02-10 09:50:35 +02002412 return false;
Syoyo Fujitabeded612016-05-01 20:03:43 +09002413 }
2414
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002415 if ((w < 1) || (h < 1)) {
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002416 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002417 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002418 (*err) += "Invalid image data for image[" + std::to_string(image_idx) +
2419 "] name = \"" + image->name + "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002420 }
Omar C. Fd492efc2018-02-10 09:50:35 +02002421 return false;
Syoyo Fujitabeded612016-05-01 20:03:43 +09002422 }
2423
2424 if (req_width > 0) {
2425 if (req_width != w) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002426 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002427 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002428 (*err) += "Image width mismatch for image[" +
2429 std::to_string(image_idx) + "] name = \"" + image->name +
2430 "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002431 }
2432 return false;
2433 }
2434 }
2435
2436 if (req_height > 0) {
2437 if (req_height != h) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002438 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002439 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002440 (*err) += "Image height mismatch. for image[" +
2441 std::to_string(image_idx) + "] name = \"" + image->name +
2442 "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002443 }
2444 return false;
2445 }
2446 }
2447
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002448 if (req_comp != 0) {
2449 // loaded data has `req_comp` channels(components)
2450 comp = req_comp;
2451 }
2452
Syoyo Fujitabeded612016-05-01 20:03:43 +09002453 image->width = w;
2454 image->height = h;
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002455 image->component = comp;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002456 image->bits = bits;
2457 image->pixel_type = pixel_type;
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002458 image->image.resize(static_cast<size_t>(w * h * comp) * size_t(bits / 8));
2459 std::copy(data, data + w * h * comp * (bits / 8), image->image.begin());
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002460 stbi_image_free(data);
Syoyo Fujita4be2f882016-11-24 16:20:34 +09002461
Syoyo Fujitabeded612016-05-01 20:03:43 +09002462 return true;
2463}
Squareys2d3594d2018-03-13 22:40:53 +01002464#endif
Syoyo Fujitabeded612016-05-01 20:03:43 +09002465
johan bowald642a3432018-04-01 12:37:18 +02002466void TinyGLTF::SetImageWriter(WriteImageDataFunction func, void *user_data) {
2467 WriteImageData = func;
2468 write_image_user_data_ = user_data;
2469}
2470
2471#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
2472static void WriteToMemory_stbi(void *context, void *data, int size) {
2473 std::vector<unsigned char> *buffer =
2474 reinterpret_cast<std::vector<unsigned char> *>(context);
2475
2476 unsigned char *pData = reinterpret_cast<unsigned char *>(data);
2477
2478 buffer->insert(buffer->end(), pData, pData + size);
2479}
2480
2481bool WriteImageData(const std::string *basepath, const std::string *filename,
Paolo Jovone6601bf2018-07-07 20:43:33 +02002482 Image *image, bool embedImages, void *fsPtr) {
johan bowald642a3432018-04-01 12:37:18 +02002483 const std::string ext = GetFilePathExtension(*filename);
2484
Paolo Jovone6601bf2018-07-07 20:43:33 +02002485 // Write image to temporary buffer
2486 std::string header;
2487 std::vector<unsigned char> data;
johan bowald642a3432018-04-01 12:37:18 +02002488
Paolo Jovone6601bf2018-07-07 20:43:33 +02002489 if (ext == "png") {
Syoyo Fujitaca56f722019-03-07 21:04:25 +09002490 if ((image->bits != 8) ||
2491 (image->pixel_type != TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)) {
Syoyo Fujitae8a46c42019-03-07 20:50:58 +09002492 // Unsupported pixel format
2493 return false;
2494 }
2495
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002496 if (!stbi_write_png_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002497 image->height, image->component,
2498 &image->image[0], 0)) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002499 return false;
2500 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002501 header = "data:image/png;base64,";
2502 } else if (ext == "jpg") {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002503 if (!stbi_write_jpg_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002504 image->height, image->component,
2505 &image->image[0], 100)) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002506 return false;
2507 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002508 header = "data:image/jpeg;base64,";
2509 } else if (ext == "bmp") {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002510 if (!stbi_write_bmp_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002511 image->height, image->component,
2512 &image->image[0])) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002513 return false;
2514 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002515 header = "data:image/bmp;base64,";
2516 } else if (!embedImages) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002517 // Error: can't output requested format to file
2518 return false;
Paolo Jovone6601bf2018-07-07 20:43:33 +02002519 }
johan bowald642a3432018-04-01 12:37:18 +02002520
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002521 if (embedImages) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002522 // Embed base64-encoded image into URI
johan bowald642a3432018-04-01 12:37:18 +02002523 if (data.size()) {
2524 image->uri =
2525 header +
2526 base64_encode(&data[0], static_cast<unsigned int>(data.size()));
2527 } else {
2528 // Throw error?
2529 }
2530 } else {
2531 // Write image to disc
Paolo Jovone6601bf2018-07-07 20:43:33 +02002532 FsCallbacks *fs = reinterpret_cast<FsCallbacks *>(fsPtr);
Syoyo Fujita33514af2018-11-05 13:32:43 +09002533 if ((fs != nullptr) && (fs->WriteWholeFile != nullptr)) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002534 const std::string imagefilepath = JoinPath(*basepath, *filename);
2535 std::string writeError;
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002536 if (!fs->WriteWholeFile(&writeError, imagefilepath, data,
2537 fs->user_data)) {
2538 // Could not write image file to disc; Throw error ?
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002539 return false;
Paolo Jovone6601bf2018-07-07 20:43:33 +02002540 }
johan bowald642a3432018-04-01 12:37:18 +02002541 } else {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002542 // Throw error?
johan bowald642a3432018-04-01 12:37:18 +02002543 }
2544 image->uri = *filename;
2545 }
2546
2547 return true;
2548}
2549#endif
2550
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002551void TinyGLTF::SetFsCallbacks(FsCallbacks callbacks) { fs = callbacks; }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002552
Harokyangfb256602019-10-30 16:13:52 +08002553#ifdef _WIN32
2554static inline std::wstring UTF8ToWchar(const std::string &str) {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002555 int wstr_size =
2556 MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
Harokyangfb256602019-10-30 16:13:52 +08002557 std::wstring wstr(wstr_size, 0);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002558 MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0],
2559 (int)wstr.size());
Harokyangfb256602019-10-30 16:13:52 +08002560 return wstr;
2561}
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002562
2563static inline std::string WcharToUTF8(const std::wstring &wstr) {
2564 int str_size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(),
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04002565 nullptr, 0, NULL, NULL);
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002566 std::string str(str_size, 0);
2567 WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), &str[0],
2568 (int)str.size(), NULL, NULL);
2569 return str;
2570}
Harokyangfb256602019-10-30 16:13:52 +08002571#endif
2572
Paolo Jovone6601bf2018-07-07 20:43:33 +02002573#ifndef TINYGLTF_NO_FS
2574// Default implementations of filesystem functions
2575
2576bool FileExists(const std::string &abs_filename, void *) {
2577 bool ret;
Sascha Willems5f9cb242018-12-28 20:53:41 +01002578#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002579 if (asset_manager) {
2580 AAsset *asset = AAssetManager_open(asset_manager, abs_filename.c_str(),
2581 AASSET_MODE_STREAMING);
2582 if (!asset) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01002583 return false;
2584 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002585 AAsset_close(asset);
2586 ret = true;
2587 } else {
2588 return false;
2589 }
Sascha Willems5f9cb242018-12-28 20:53:41 +01002590#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002591#ifdef _WIN32
Syoyo Fujita45cac782019-11-09 20:42:55 +09002592#if defined(_MSC_VER) || defined(__GLIBCXX__)
2593 FILE *fp = nullptr;
Harokyangfb256602019-10-30 16:13:52 +08002594 errno_t err = _wfopen_s(&fp, UTF8ToWchar(abs_filename).c_str(), L"rb");
Paolo Jovone6601bf2018-07-07 20:43:33 +02002595 if (err != 0) {
2596 return false;
2597 }
2598#else
Syoyo Fujita45cac782019-11-09 20:42:55 +09002599 FILE *fp = nullptr;
2600 errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb");
2601 if (err != 0) {
2602 return false;
2603 }
2604#endif
2605
2606#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002607 FILE *fp = fopen(abs_filename.c_str(), "rb");
2608#endif
2609 if (fp) {
2610 ret = true;
2611 fclose(fp);
2612 } else {
2613 ret = false;
2614 }
Sascha Willems5f9cb242018-12-28 20:53:41 +01002615#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002616
2617 return ret;
2618}
2619
2620std::string ExpandFilePath(const std::string &filepath, void *) {
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002621 // https://github.com/syoyo/tinygltf/issues/368
2622 //
2623 // No file path expansion in built-in FS function anymore, since glTF URI
2624 // should not contain tilde('~') and environment variables, and for security
2625 // reason(`wordexp`).
2626 //
2627 // Users need to supply `base_dir`(in `LoadASCIIFromString`,
2628 // `LoadBinaryFromMemory`) in expanded absolute path.
2629
2630 return filepath;
2631
2632#if 0
Paolo Jovone6601bf2018-07-07 20:43:33 +02002633#ifdef _WIN32
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002634 // Assume input `filepath` is encoded in UTF-8
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002635 std::wstring wfilepath = UTF8ToWchar(filepath);
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002636 DWORD wlen = ExpandEnvironmentStringsW(wfilepath.c_str(), nullptr, 0);
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002637 wchar_t *wstr = new wchar_t[wlen];
2638 ExpandEnvironmentStringsW(wfilepath.c_str(), wstr, wlen);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002639
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002640 std::wstring ws(wstr);
2641 delete[] wstr;
2642 return WcharToUTF8(ws);
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002643
Paolo Jovone6601bf2018-07-07 20:43:33 +02002644#else
2645
2646#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \
Julian Smith0598a202021-08-25 12:06:08 +01002647 defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__OpenBSD__)
Paolo Jovone6601bf2018-07-07 20:43:33 +02002648 // no expansion
2649 std::string s = filepath;
2650#else
2651 std::string s;
2652 wordexp_t p;
2653
2654 if (filepath.empty()) {
2655 return "";
2656 }
2657
Frank Galliganaa3c5a12020-01-13 15:06:56 -08002658 // Quote the string to keep any spaces in filepath intact.
2659 std::string quoted_path = "\"" + filepath + "\"";
Paolo Jovone6601bf2018-07-07 20:43:33 +02002660 // char** w;
Frank Galliganaa3c5a12020-01-13 15:06:56 -08002661 int ret = wordexp(quoted_path.c_str(), &p, 0);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002662 if (ret) {
2663 // err
2664 s = filepath;
2665 return s;
2666 }
2667
2668 // Use first element only.
2669 if (p.we_wordv) {
2670 s = std::string(p.we_wordv[0]);
2671 wordfree(&p);
2672 } else {
2673 s = filepath;
2674 }
2675
2676#endif
2677
2678 return s;
2679#endif
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09002680#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002681}
2682
2683bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
2684 const std::string &filepath, void *) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01002685#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
2686 if (asset_manager) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002687 AAsset *asset = AAssetManager_open(asset_manager, filepath.c_str(),
2688 AASSET_MODE_STREAMING);
Sascha Willems5f9cb242018-12-28 20:53:41 +01002689 if (!asset) {
2690 if (err) {
2691 (*err) += "File open error : " + filepath + "\n";
2692 }
2693 return false;
2694 }
2695 size_t size = AAsset_getLength(asset);
Syoyo Fujita73c4cce2020-04-28 01:06:34 +09002696 if (size == 0) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01002697 if (err) {
2698 (*err) += "Invalid file size : " + filepath +
2699 " (does the path point to a directory?)";
2700 }
Syoyo Fujita73c4cce2020-04-28 01:06:34 +09002701 return false;
Sascha Willems5f9cb242018-12-28 20:53:41 +01002702 }
2703 out->resize(size);
2704 AAsset_read(asset, reinterpret_cast<char *>(&out->at(0)), size);
2705 AAsset_close(asset);
2706 return true;
2707 } else {
2708 if (err) {
2709 (*err) += "No asset manager specified : " + filepath + "\n";
2710 }
2711 return false;
2712 }
2713#else
Harokyang5cecef22019-10-30 15:16:46 +08002714#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002715#if defined(__GLIBCXX__) // mingw
2716 int file_descriptor =
2717 _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY);
Syoyo Fujita45cac782019-11-09 20:42:55 +09002718 __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
2719 std::istream f(&wfile_buf);
Syoyo Fujitaaba57bb2020-05-18 20:50:45 +09002720#elif defined(_MSC_VER) || defined(_LIBCPP_VERSION)
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04002721 // For libcxx, assume _LIBCPP_HAS_OPEN_WITH_WCHAR is defined to accept
2722 // `wchar_t *`
Harokyangfb256602019-10-30 16:13:52 +08002723 std::ifstream f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary);
Syoyo Fujitaaba57bb2020-05-18 20:50:45 +09002724#else
2725 // Unknown compiler/runtime
Syoyo Fujita45cac782019-11-09 20:42:55 +09002726 std::ifstream f(filepath.c_str(), std::ifstream::binary);
2727#endif
Harokyang5cecef22019-10-30 15:16:46 +08002728#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002729 std::ifstream f(filepath.c_str(), std::ifstream::binary);
Harokyang5cecef22019-10-30 15:16:46 +08002730#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002731 if (!f) {
2732 if (err) {
2733 (*err) += "File open error : " + filepath + "\n";
2734 }
2735 return false;
2736 }
2737
2738 f.seekg(0, f.end);
2739 size_t sz = static_cast<size_t>(f.tellg());
2740 f.seekg(0, f.beg);
2741
Syoyo Fujitae8862472019-10-20 17:47:50 +09002742 if (int64_t(sz) < 0) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002743 if (err) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002744 (*err) += "Invalid file size : " + filepath +
2745 " (does the path point to a directory?)";
Paolo Jovone6601bf2018-07-07 20:43:33 +02002746 }
2747 return false;
2748 } else if (sz == 0) {
2749 if (err) {
2750 (*err) += "File is empty : " + filepath + "\n";
2751 }
2752 return false;
2753 }
2754
2755 out->resize(sz);
2756 f.read(reinterpret_cast<char *>(&out->at(0)),
2757 static_cast<std::streamsize>(sz));
Paolo Jovone6601bf2018-07-07 20:43:33 +02002758
2759 return true;
Sascha Willems5f9cb242018-12-28 20:53:41 +01002760#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002761}
2762
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002763bool WriteWholeFile(std::string *err, const std::string &filepath,
2764 const std::vector<unsigned char> &contents, void *) {
Harokyangfb256602019-10-30 16:13:52 +08002765#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002766#if defined(__GLIBCXX__) // mingw
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09002767 int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(),
2768 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
2769 __gnu_cxx::stdio_filebuf<char> wfile_buf(
2770 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09002771 std::ostream f(&wfile_buf);
2772#elif defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08002773 std::ofstream f(UTF8ToWchar(filepath).c_str(), std::ofstream::binary);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002774#else // clang?
Syoyo Fujita45cac782019-11-09 20:42:55 +09002775 std::ofstream f(filepath.c_str(), std::ofstream::binary);
2776#endif
Harokyangfb256602019-10-30 16:13:52 +08002777#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002778 std::ofstream f(filepath.c_str(), std::ofstream::binary);
Harokyangfb256602019-10-30 16:13:52 +08002779#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002780 if (!f) {
2781 if (err) {
2782 (*err) += "File open error for writing : " + filepath + "\n";
2783 }
2784 return false;
2785 }
2786
2787 f.write(reinterpret_cast<const char *>(&contents.at(0)),
2788 static_cast<std::streamsize>(contents.size()));
2789 if (!f) {
2790 if (err) {
2791 (*err) += "File write error: " + filepath + "\n";
2792 }
2793 return false;
2794 }
2795
Paolo Jovone6601bf2018-07-07 20:43:33 +02002796 return true;
2797}
2798
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002799#endif // TINYGLTF_NO_FS
Paolo Jovone6601bf2018-07-07 20:43:33 +02002800
johan bowald642a3432018-04-01 12:37:18 +02002801static std::string MimeToExt(const std::string &mimeType) {
2802 if (mimeType == "image/jpeg") {
2803 return "jpg";
2804 } else if (mimeType == "image/png") {
2805 return "png";
2806 } else if (mimeType == "image/bmp") {
2807 return "bmp";
2808 } else if (mimeType == "image/gif") {
2809 return "gif";
2810 }
2811
2812 return "";
2813}
2814
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09002815static void UpdateImageObject(Image &image, std::string &baseDir, int index,
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002816 bool embedImages,
2817 WriteImageDataFunction *WriteImageData = nullptr,
2818 void *user_data = nullptr) {
johan bowald642a3432018-04-01 12:37:18 +02002819 std::string filename;
2820 std::string ext;
FsiGuy00015623db855c62020-03-09 16:57:21 -05002821 // If image has uri, use it it as a filename
johan bowald642a3432018-04-01 12:37:18 +02002822 if (image.uri.size()) {
2823 filename = GetBaseFilename(image.uri);
2824 ext = GetFilePathExtension(filename);
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09002825 } else if (image.bufferView != -1) {
2826 // If there's no URI and the data exists in a buffer,
2827 // don't change properties or write images
johan bowald642a3432018-04-01 12:37:18 +02002828 } else if (image.name.size()) {
2829 ext = MimeToExt(image.mimeType);
2830 // Otherwise use name as filename
2831 filename = image.name + "." + ext;
2832 } else {
2833 ext = MimeToExt(image.mimeType);
2834 // Fallback to index of image as filename
2835 filename = std::to_string(index) + "." + ext;
2836 }
2837
2838 // If callback is set, modify image data object
FsiGuy00015623db855c62020-03-09 16:57:21 -05002839 if (*WriteImageData != nullptr && !filename.empty()) {
johan bowald642a3432018-04-01 12:37:18 +02002840 std::string uri;
2841 (*WriteImageData)(&baseDir, &filename, &image, embedImages, user_data);
2842 }
2843}
2844
Selmar Kok0d0e97e2018-08-22 14:01:57 +02002845bool IsDataURI(const std::string &in) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002846 std::string header = "data:application/octet-stream;base64,";
2847 if (in.find(header) == 0) {
2848 return true;
2849 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002850
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002851 header = "data:image/jpeg;base64,";
2852 if (in.find(header) == 0) {
2853 return true;
2854 }
Squareys43374632018-03-13 22:20:48 +01002855
Syoyo Fujita620eed12016-01-02 23:37:12 +09002856 header = "data:image/png;base64,";
2857 if (in.find(header) == 0) {
2858 return true;
2859 }
Squareys43374632018-03-13 22:20:48 +01002860
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002861 header = "data:image/bmp;base64,";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002862 if (in.find(header) == 0) {
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002863 return true;
2864 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002865
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002866 header = "data:image/gif;base64,";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002867 if (in.find(header) == 0) {
Syoyo Fujita620eed12016-01-02 23:37:12 +09002868 return true;
2869 }
2870
Luke San Antoniocaa24b02016-06-15 02:23:09 -04002871 header = "data:text/plain;base64,";
2872 if (in.find(header) == 0) {
2873 return true;
2874 }
2875
Syoyo Fujita20244e12018-03-15 11:01:05 -05002876 header = "data:application/gltf-buffer;base64,";
2877 if (in.find(header) == 0) {
2878 return true;
2879 }
2880
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002881 return false;
2882}
2883
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002884bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
2885 const std::string &in, size_t reqBytes, bool checkSize) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002886 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujita620eed12016-01-02 23:37:12 +09002887 std::string data;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002888 if (in.find(header) == 0) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09002889 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002890 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002891
2892 if (data.empty()) {
2893 header = "data:image/jpeg;base64,";
2894 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002895 mime_type = "image/jpeg";
Syoyo Fujita7c877972016-03-08 01:31:49 +09002896 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09002897 }
2898 }
2899
2900 if (data.empty()) {
2901 header = "data:image/png;base64,";
2902 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002903 mime_type = "image/png";
Syoyo Fujita7c877972016-03-08 01:31:49 +09002904 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09002905 }
2906 }
Squareys43374632018-03-13 22:20:48 +01002907
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002908 if (data.empty()) {
2909 header = "data:image/bmp;base64,";
2910 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002911 mime_type = "image/bmp";
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002912 data = base64_decode(in.substr(header.size())); // cut mime string.
2913 }
2914 }
2915
2916 if (data.empty()) {
2917 header = "data:image/gif;base64,";
2918 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002919 mime_type = "image/gif";
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002920 data = base64_decode(in.substr(header.size())); // cut mime string.
2921 }
2922 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002923
2924 if (data.empty()) {
Luke San Antoniocaa24b02016-06-15 02:23:09 -04002925 header = "data:text/plain;base64,";
2926 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002927 mime_type = "text/plain";
Luke San Antoniocaa24b02016-06-15 02:23:09 -04002928 data = base64_decode(in.substr(header.size()));
2929 }
2930 }
2931
2932 if (data.empty()) {
Syoyo Fujita20244e12018-03-15 11:01:05 -05002933 header = "data:application/gltf-buffer;base64,";
2934 if (in.find(header) == 0) {
2935 data = base64_decode(in.substr(header.size()));
2936 }
2937 }
2938
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09002939 // TODO(syoyo): Allow empty buffer? #229
Syoyo Fujita20244e12018-03-15 11:01:05 -05002940 if (data.empty()) {
Syoyo Fujita620eed12016-01-02 23:37:12 +09002941 return false;
Syoyo Fujitabde70212016-02-07 17:38:17 +09002942 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002943
2944 if (checkSize) {
2945 if (data.size() != reqBytes) {
2946 return false;
2947 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002948 out->resize(reqBytes);
Syoyo Fujita620eed12016-01-02 23:37:12 +09002949 } else {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002950 out->resize(data.size());
Syoyo Fujita620eed12016-01-02 23:37:12 +09002951 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002952 std::copy(data.begin(), data.end(), out->begin());
Syoyo Fujita620eed12016-01-02 23:37:12 +09002953 return true;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002954}
2955
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002956namespace {
2957bool GetInt(const json &o, int &val) {
jrkoonce5cecc412019-08-29 11:45:04 -05002958#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002959 if (!o.IsDouble()) {
2960 if (o.IsInt()) {
2961 val = o.GetInt();
2962 return true;
2963 } else if (o.IsUint()) {
2964 val = static_cast<int>(o.GetUint());
2965 return true;
2966 } else if (o.IsInt64()) {
2967 val = static_cast<int>(o.GetInt64());
2968 return true;
2969 } else if (o.IsUint64()) {
2970 val = static_cast<int>(o.GetUint64());
jrkooncecba5d6c2019-08-29 11:26:22 -05002971 return true;
2972 }
jrkoonce5cecc412019-08-29 11:45:04 -05002973 }
2974
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002975 return false;
jrkoonce5cecc412019-08-29 11:45:04 -05002976#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002977 auto type = o.type();
jrkooncecba5d6c2019-08-29 11:26:22 -05002978
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002979 if ((type == json::value_t::number_integer) ||
2980 (type == json::value_t::number_unsigned)) {
2981 val = static_cast<int>(o.get<int64_t>());
2982 return true;
jrkoonce5cecc412019-08-29 11:45:04 -05002983 }
2984
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002985 return false;
jrkoonce5cecc412019-08-29 11:45:04 -05002986#endif
jrkooncecba5d6c2019-08-29 11:26:22 -05002987}
2988
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002989#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09002990bool GetDouble(const json &o, double &val) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002991 if (o.IsDouble()) {
2992 val = o.GetDouble();
2993 return true;
2994 }
2995
2996 return false;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002997}
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09002998#endif
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002999
3000bool GetNumber(const json &o, double &val) {
3001#ifdef TINYGLTF_USE_RAPIDJSON
3002 if (o.IsNumber()) {
3003 val = o.GetDouble();
3004 return true;
3005 }
3006
3007 return false;
3008#else
3009 if (o.is_number()) {
3010 val = o.get<double>();
3011 return true;
3012 }
3013
3014 return false;
3015#endif
3016}
3017
3018bool GetString(const json &o, std::string &val) {
3019#ifdef TINYGLTF_USE_RAPIDJSON
3020 if (o.IsString()) {
3021 val = o.GetString();
3022 return true;
3023 }
3024
3025 return false;
3026#else
3027 if (o.type() == json::value_t::string) {
3028 val = o.get<std::string>();
3029 return true;
3030 }
3031
3032 return false;
3033#endif
3034}
3035
3036bool IsArray(const json &o) {
3037#ifdef TINYGLTF_USE_RAPIDJSON
3038 return o.IsArray();
3039#else
3040 return o.is_array();
3041#endif
3042}
3043
3044json_const_array_iterator ArrayBegin(const json &o) {
3045#ifdef TINYGLTF_USE_RAPIDJSON
3046 return o.Begin();
3047#else
3048 return o.begin();
3049#endif
3050}
3051
3052json_const_array_iterator ArrayEnd(const json &o) {
3053#ifdef TINYGLTF_USE_RAPIDJSON
3054 return o.End();
3055#else
3056 return o.end();
3057#endif
3058}
3059
3060bool IsObject(const json &o) {
3061#ifdef TINYGLTF_USE_RAPIDJSON
3062 return o.IsObject();
3063#else
3064 return o.is_object();
3065#endif
3066}
3067
3068json_const_iterator ObjectBegin(const json &o) {
3069#ifdef TINYGLTF_USE_RAPIDJSON
3070 return o.MemberBegin();
3071#else
3072 return o.begin();
3073#endif
3074}
3075
3076json_const_iterator ObjectEnd(const json &o) {
3077#ifdef TINYGLTF_USE_RAPIDJSON
3078 return o.MemberEnd();
3079#else
3080 return o.end();
3081#endif
3082}
3083
Rahul Sheth01d54382020-07-10 14:27:37 -04003084// Making this a const char* results in a pointer to a temporary when
3085// TINYGLTF_USE_RAPIDJSON is off.
3086std::string GetKey(json_const_iterator &it) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003087#ifdef TINYGLTF_USE_RAPIDJSON
3088 return it->name.GetString();
3089#else
3090 return it.key().c_str();
3091#endif
3092}
3093
3094bool FindMember(const json &o, const char *member, json_const_iterator &it) {
3095#ifdef TINYGLTF_USE_RAPIDJSON
3096 if (!o.IsObject()) {
3097 return false;
3098 }
3099 it = o.FindMember(member);
3100 return it != o.MemberEnd();
3101#else
3102 it = o.find(member);
3103 return it != o.end();
3104#endif
3105}
3106
3107const json &GetValue(json_const_iterator &it) {
3108#ifdef TINYGLTF_USE_RAPIDJSON
3109 return it->value;
3110#else
3111 return it.value();
3112#endif
3113}
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003114
3115std::string JsonToString(const json &o, int spacing = -1) {
3116#ifdef TINYGLTF_USE_RAPIDJSON
3117 using namespace rapidjson;
3118 StringBuffer buffer;
3119 if (spacing == -1) {
3120 Writer<StringBuffer> writer(buffer);
Syoyo Fujita544969b2022-07-13 19:03:33 +09003121 // TODO: Better error handling.
3122 // https://github.com/syoyo/tinygltf/issues/332
3123 if (!o.Accept(writer)) {
3124 return "tiny_gltf::JsonToString() failed rapidjson conversion";
3125 }
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003126 } else {
3127 PrettyWriter<StringBuffer> writer(buffer);
3128 writer.SetIndent(' ', uint32_t(spacing));
Syoyo Fujita544969b2022-07-13 19:03:33 +09003129 if (!o.Accept(writer)) {
3130 return "tiny_gltf::JsonToString() failed rapidjson conversion";
3131 }
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003132 }
3133 return buffer.GetString();
3134#else
3135 return o.dump(spacing);
3136#endif
3137}
3138
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003139} // namespace
3140
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003141static bool ParseJsonAsValue(Value *ret, const json &o) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003142 Value val{};
jrkooncecba5d6c2019-08-29 11:26:22 -05003143#ifdef TINYGLTF_USE_RAPIDJSON
3144 using rapidjson::Type;
3145 switch (o.GetType()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003146 case Type::kObjectType: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003147 Value::Object value_object;
jrkooncecba5d6c2019-08-29 11:26:22 -05003148 for (auto it = o.MemberBegin(); it != o.MemberEnd(); ++it) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003149 Value entry;
jrkooncecba5d6c2019-08-29 11:26:22 -05003150 ParseJsonAsValue(&entry, it->value);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003151 if (entry.Type() != NULL_TYPE)
3152 value_object.emplace(GetKey(it), std::move(entry));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003153 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003154 if (value_object.size() > 0) val = Value(std::move(value_object));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003155 } break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003156 case Type::kArrayType: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003157 Value::Array value_array;
jrkoonce5cecc412019-08-29 11:45:04 -05003158 value_array.reserve(o.Size());
jrkooncecba5d6c2019-08-29 11:26:22 -05003159 for (auto it = o.Begin(); it != o.End(); ++it) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003160 Value entry;
jrkooncecba5d6c2019-08-29 11:26:22 -05003161 ParseJsonAsValue(&entry, *it);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003162 if (entry.Type() != NULL_TYPE)
3163 value_array.emplace_back(std::move(entry));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003164 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003165 if (value_array.size() > 0) val = Value(std::move(value_array));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003166 } break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003167 case Type::kStringType:
3168 val = Value(std::string(o.GetString()));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003169 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003170 case Type::kFalseType:
3171 case Type::kTrueType:
3172 val = Value(o.GetBool());
Selmar Kokfa7022f2018-04-04 18:10:20 +02003173 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003174 case Type::kNumberType:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003175 if (!o.IsDouble()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003176 int i = 0;
3177 GetInt(o, i);
3178 val = Value(i);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003179 } else {
jrkooncecba5d6c2019-08-29 11:26:22 -05003180 double d = 0.0;
3181 GetDouble(o, d);
3182 val = Value(d);
3183 }
Selmar Kokfa7022f2018-04-04 18:10:20 +02003184 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003185 case Type::kNullType:
Selmar Kokfa7022f2018-04-04 18:10:20 +02003186 break;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003187 // all types are covered, so no `case default`
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003188 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003189#else
3190 switch (o.type()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003191 case json::value_t::object: {
3192 Value::Object value_object;
3193 for (auto it = o.begin(); it != o.end(); it++) {
3194 Value entry;
3195 ParseJsonAsValue(&entry, it.value());
3196 if (entry.Type() != NULL_TYPE)
3197 value_object.emplace(it.key(), std::move(entry));
3198 }
3199 if (value_object.size() > 0) val = Value(std::move(value_object));
3200 } break;
3201 case json::value_t::array: {
3202 Value::Array value_array;
3203 value_array.reserve(o.size());
3204 for (auto it = o.begin(); it != o.end(); it++) {
3205 Value entry;
3206 ParseJsonAsValue(&entry, it.value());
3207 if (entry.Type() != NULL_TYPE)
3208 value_array.emplace_back(std::move(entry));
3209 }
3210 if (value_array.size() > 0) val = Value(std::move(value_array));
3211 } break;
3212 case json::value_t::string:
3213 val = Value(o.get<std::string>());
3214 break;
3215 case json::value_t::boolean:
3216 val = Value(o.get<bool>());
3217 break;
3218 case json::value_t::number_integer:
3219 case json::value_t::number_unsigned:
3220 val = Value(static_cast<int>(o.get<int64_t>()));
3221 break;
3222 case json::value_t::number_float:
3223 val = Value(o.get<double>());
3224 break;
3225 case json::value_t::null:
3226 case json::value_t::discarded:
Christopher Sean Morrison0bfcb4f2022-02-23 10:02:49 -05003227 case json::value_t::binary:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003228 // default:
3229 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003230 }
3231#endif
3232 if (ret) *ret = std::move(val);
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003233
Selmar Kokfa7022f2018-04-04 18:10:20 +02003234 return val.Type() != NULL_TYPE;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003235}
3236
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003237static bool ParseExtrasProperty(Value *ret, const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003238 json_const_iterator it;
3239 if (!FindMember(o, "extras", it)) {
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003240 return false;
3241 }
3242
jrkooncecba5d6c2019-08-29 11:26:22 -05003243 return ParseJsonAsValue(ret, GetValue(it));
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003244}
3245
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003246static bool ParseBooleanProperty(bool *ret, std::string *err, const json &o,
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09003247 const std::string &property,
3248 const bool required,
3249 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003250 json_const_iterator it;
3251 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003252 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003253 if (err) {
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09003254 (*err) += "'" + property + "' property is missing";
3255 if (!parent_node.empty()) {
3256 (*err) += " in " + parent_node;
3257 }
3258 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003259 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003260 }
3261 return false;
3262 }
3263
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003264 auto &value = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003265
3266 bool isBoolean;
jrkoonce0d2b6ef2019-09-04 13:46:45 -05003267 bool boolValue = false;
jrkooncecba5d6c2019-08-29 11:26:22 -05003268#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003269 isBoolean = value.IsBool();
3270 if (isBoolean) {
3271 boolValue = value.GetBool();
3272 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003273#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003274 isBoolean = value.is_boolean();
3275 if (isBoolean) {
3276 boolValue = value.get<bool>();
3277 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003278#endif
3279 if (!isBoolean) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003280 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003281 if (err) {
3282 (*err) += "'" + property + "' property is not a bool type.\n";
3283 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003284 }
3285 return false;
3286 }
3287
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003288 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003289 (*ret) = boolValue;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003290 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003291
3292 return true;
3293}
3294
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003295static bool ParseIntegerProperty(int *ret, std::string *err, const json &o,
3296 const std::string &property,
3297 const bool required,
3298 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003299 json_const_iterator it;
3300 if (!FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003301 if (required) {
3302 if (err) {
3303 (*err) += "'" + property + "' property is missing";
3304 if (!parent_node.empty()) {
3305 (*err) += " in " + parent_node;
3306 }
3307 (*err) += ".\n";
3308 }
3309 }
3310 return false;
3311 }
3312
jrkooncecba5d6c2019-08-29 11:26:22 -05003313 int intValue;
3314 bool isInt = GetInt(GetValue(it), intValue);
3315 if (!isInt) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003316 if (required) {
3317 if (err) {
3318 (*err) += "'" + property + "' property is not an integer type.\n";
3319 }
3320 }
3321 return false;
3322 }
3323
3324 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003325 (*ret) = intValue;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003326 }
3327
3328 return true;
3329}
3330
3331static bool ParseUnsignedProperty(size_t *ret, std::string *err, const json &o,
3332 const std::string &property,
3333 const bool required,
3334 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003335 json_const_iterator it;
3336 if (!FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003337 if (required) {
3338 if (err) {
3339 (*err) += "'" + property + "' property is missing";
3340 if (!parent_node.empty()) {
3341 (*err) += " in " + parent_node;
3342 }
3343 (*err) += ".\n";
3344 }
3345 }
3346 return false;
3347 }
3348
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003349 auto &value = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003350
jrkoonce0d2b6ef2019-09-04 13:46:45 -05003351 size_t uValue = 0;
jrkooncecba5d6c2019-08-29 11:26:22 -05003352 bool isUValue;
3353#ifdef TINYGLTF_USE_RAPIDJSON
3354 isUValue = false;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003355 if (value.IsUint()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003356 uValue = value.GetUint();
3357 isUValue = true;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003358 } else if (value.IsUint64()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003359 uValue = value.GetUint64();
3360 isUValue = true;
3361 }
3362#else
3363 isUValue = value.is_number_unsigned();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003364 if (isUValue) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003365 uValue = value.get<size_t>();
3366 }
3367#endif
3368 if (!isUValue) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003369 if (required) {
3370 if (err) {
3371 (*err) += "'" + property + "' property is not a positive integer.\n";
3372 }
3373 }
3374 return false;
3375 }
3376
3377 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003378 (*ret) = uValue;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003379 }
3380
3381 return true;
3382}
3383
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003384static bool ParseNumberProperty(double *ret, std::string *err, const json &o,
Syoyo Fujita5b407452017-06-04 17:42:41 +09003385 const std::string &property,
3386 const bool required,
3387 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003388 json_const_iterator it;
3389
3390 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003391 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003392 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003393 (*err) += "'" + property + "' property is missing";
3394 if (!parent_node.empty()) {
3395 (*err) += " in " + parent_node;
3396 }
3397 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003398 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003399 }
3400 return false;
3401 }
3402
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003403 double numberValue;
3404 bool isNumber = GetNumber(GetValue(it), numberValue);
jrkooncecba5d6c2019-08-29 11:26:22 -05003405
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003406 if (!isNumber) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003407 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003408 if (err) {
3409 (*err) += "'" + property + "' property is not a number type.\n";
3410 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003411 }
3412 return false;
3413 }
3414
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003415 if (ret) {
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003416 (*ret) = numberValue;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003417 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003418
3419 return true;
3420}
3421
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003422static bool ParseNumberArrayProperty(std::vector<double> *ret, std::string *err,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003423 const json &o, const std::string &property,
3424 bool required,
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003425 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003426 json_const_iterator it;
3427 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003428 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003429 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003430 (*err) += "'" + property + "' property is missing";
3431 if (!parent_node.empty()) {
3432 (*err) += " in " + parent_node;
3433 }
3434 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003435 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003436 }
3437 return false;
3438 }
3439
jrkooncecba5d6c2019-08-29 11:26:22 -05003440 if (!IsArray(GetValue(it))) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003441 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003442 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003443 (*err) += "'" + property + "' property is not an array";
3444 if (!parent_node.empty()) {
3445 (*err) += " in " + parent_node;
3446 }
3447 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003448 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003449 }
3450 return false;
3451 }
3452
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003453 ret->clear();
jrkooncecba5d6c2019-08-29 11:26:22 -05003454 auto end = ArrayEnd(GetValue(it));
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003455 for (auto i = ArrayBegin(GetValue(it)); i != end; ++i) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003456 double numberValue;
jrkoonce9b6f52e2019-08-29 13:56:58 -05003457 const bool isNumber = GetNumber(*i, numberValue);
jrkooncecba5d6c2019-08-29 11:26:22 -05003458 if (!isNumber) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003459 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003460 if (err) {
3461 (*err) += "'" + property + "' property is not a number.\n";
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003462 if (!parent_node.empty()) {
3463 (*err) += " in " + parent_node;
3464 }
3465 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003466 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003467 }
3468 return false;
3469 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003470 ret->push_back(numberValue);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003471 }
3472
3473 return true;
3474}
3475
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003476static bool ParseIntegerArrayProperty(std::vector<int> *ret, std::string *err,
3477 const json &o,
3478 const std::string &property,
3479 bool required,
3480 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003481 json_const_iterator it;
3482 if (!FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003483 if (required) {
3484 if (err) {
3485 (*err) += "'" + property + "' property is missing";
3486 if (!parent_node.empty()) {
3487 (*err) += " in " + parent_node;
3488 }
3489 (*err) += ".\n";
3490 }
3491 }
3492 return false;
3493 }
3494
jrkooncecba5d6c2019-08-29 11:26:22 -05003495 if (!IsArray(GetValue(it))) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003496 if (required) {
3497 if (err) {
3498 (*err) += "'" + property + "' property is not an array";
3499 if (!parent_node.empty()) {
3500 (*err) += " in " + parent_node;
3501 }
3502 (*err) += ".\n";
3503 }
3504 }
3505 return false;
3506 }
3507
3508 ret->clear();
jrkooncecba5d6c2019-08-29 11:26:22 -05003509 auto end = ArrayEnd(GetValue(it));
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003510 for (auto i = ArrayBegin(GetValue(it)); i != end; ++i) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003511 int numberValue;
3512 bool isNumber = GetInt(*i, numberValue);
3513 if (!isNumber) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003514 if (required) {
3515 if (err) {
3516 (*err) += "'" + property + "' property is not an integer type.\n";
3517 if (!parent_node.empty()) {
3518 (*err) += " in " + parent_node;
3519 }
3520 (*err) += ".\n";
3521 }
3522 }
3523 return false;
3524 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003525 ret->push_back(numberValue);
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003526 }
3527
3528 return true;
3529}
3530
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003531static bool ParseStringProperty(
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003532 std::string *ret, std::string *err, const json &o,
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003533 const std::string &property, bool required,
3534 const std::string &parent_node = std::string()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003535 json_const_iterator it;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003536 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003537 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003538 if (err) {
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003539 (*err) += "'" + property + "' property is missing";
3540 if (parent_node.empty()) {
3541 (*err) += ".\n";
3542 } else {
3543 (*err) += " in `" + parent_node + "'.\n";
3544 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003545 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003546 }
3547 return false;
3548 }
3549
jrkooncecba5d6c2019-08-29 11:26:22 -05003550 std::string strValue;
3551 if (!GetString(GetValue(it), strValue)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003552 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003553 if (err) {
3554 (*err) += "'" + property + "' property is not a string type.\n";
3555 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003556 }
3557 return false;
3558 }
3559
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003560 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003561 (*ret) = std::move(strValue);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003562 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003563
3564 return true;
3565}
3566
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003567static bool ParseStringIntegerProperty(std::map<std::string, int> *ret,
3568 std::string *err, const json &o,
3569 const std::string &property,
3570 bool required,
3571 const std::string &parent = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003572 json_const_iterator it;
3573 if (!FindMember(o, property.c_str(), it)) {
Luke San Antonio19894c72016-06-14 21:19:51 -04003574 if (required) {
3575 if (err) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09003576 if (!parent.empty()) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003577 (*err) +=
3578 "'" + property + "' property is missing in " + parent + ".\n";
Syoyo Fujita57c10182017-10-19 18:48:26 +09003579 } else {
3580 (*err) += "'" + property + "' property is missing.\n";
3581 }
Luke San Antonio19894c72016-06-14 21:19:51 -04003582 }
3583 }
3584 return false;
3585 }
3586
jrkooncecba5d6c2019-08-29 11:26:22 -05003587 const json &dict = GetValue(it);
3588
Luke San Antonio19894c72016-06-14 21:19:51 -04003589 // Make sure we are dealing with an object / dictionary.
jrkooncecba5d6c2019-08-29 11:26:22 -05003590 if (!IsObject(dict)) {
Luke San Antonio19894c72016-06-14 21:19:51 -04003591 if (required) {
3592 if (err) {
3593 (*err) += "'" + property + "' property is not an object.\n";
3594 }
3595 }
3596 return false;
3597 }
3598
3599 ret->clear();
Luke San Antonio19894c72016-06-14 21:19:51 -04003600
jrkooncecba5d6c2019-08-29 11:26:22 -05003601 json_const_iterator dictIt(ObjectBegin(dict));
3602 json_const_iterator dictItEnd(ObjectEnd(dict));
Luke San Antonio19894c72016-06-14 21:19:51 -04003603
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09003604 for (; dictIt != dictItEnd; ++dictIt) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003605 int intVal;
3606 if (!GetInt(GetValue(dictIt), intVal)) {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09003607 if (required) {
3608 if (err) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003609 (*err) += "'" + property + "' value is not an integer type.\n";
Luke San Antonio19894c72016-06-14 21:19:51 -04003610 }
3611 }
3612 return false;
3613 }
3614
3615 // Insert into the list.
jrkooncecba5d6c2019-08-29 11:26:22 -05003616 (*ret)[GetKey(dictIt)] = intVal;
Luke San Antonio19894c72016-06-14 21:19:51 -04003617 }
3618 return true;
3619}
3620
Syoyo Fujita5b407452017-06-04 17:42:41 +09003621static bool ParseJSONProperty(std::map<std::string, double> *ret,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003622 std::string *err, const json &o,
Syoyo Fujita5b407452017-06-04 17:42:41 +09003623 const std::string &property, bool required) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003624 json_const_iterator it;
3625 if (!FindMember(o, property.c_str(), it)) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003626 if (required) {
Syoyo Fujita5b407452017-06-04 17:42:41 +09003627 if (err) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003628 (*err) += "'" + property + "' property is missing. \n'";
3629 }
3630 }
3631 return false;
3632 }
3633
jrkooncecba5d6c2019-08-29 11:26:22 -05003634 const json &obj = GetValue(it);
3635
3636 if (!IsObject(obj)) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003637 if (required) {
3638 if (err) {
3639 (*err) += "'" + property + "' property is not a JSON object.\n";
3640 }
3641 }
3642 return false;
3643 }
3644
3645 ret->clear();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003646
jrkooncecba5d6c2019-08-29 11:26:22 -05003647 json_const_iterator it2(ObjectBegin(obj));
3648 json_const_iterator itEnd(ObjectEnd(obj));
3649 for (; it2 != itEnd; ++it2) {
3650 double numVal;
3651 if (GetNumber(GetValue(it2), numVal))
3652 ret->emplace(std::string(GetKey(it2)), numVal);
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003653 }
3654
3655 return true;
3656}
3657
Selmar09d2ff12018-03-15 17:30:42 +01003658static bool ParseParameterProperty(Parameter *param, std::string *err,
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003659 const json &o, const std::string &prop,
3660 bool required) {
Selmar09d2ff12018-03-15 17:30:42 +01003661 // A parameter value can either be a string or an array of either a boolean or
3662 // a number. Booleans of any kind aren't supported here. Granted, it
3663 // complicates the Parameter structure and breaks it semantically in the sense
3664 // that the client probably works off the assumption that if the string is
3665 // empty the vector is used, etc. Would a tagged union work?
3666 if (ParseStringProperty(&param->string_value, err, o, prop, false)) {
3667 // Found string property.
3668 return true;
3669 } else if (ParseNumberArrayProperty(&param->number_array, err, o, prop,
3670 false)) {
3671 // Found a number array.
3672 return true;
Ben Buzbeef6af2242018-04-25 15:13:05 -07003673 } else if (ParseNumberProperty(&param->number_value, err, o, prop, false)) {
3674 return param->has_number_value = true;
Selmar09d2ff12018-03-15 17:30:42 +01003675 } else if (ParseJSONProperty(&param->json_double_value, err, o, prop,
3676 false)) {
3677 return true;
3678 } else if (ParseBooleanProperty(&param->bool_value, err, o, prop, false)) {
3679 return true;
3680 } else {
3681 if (required) {
3682 if (err) {
3683 (*err) += "parameter must be a string or number / number array.\n";
3684 }
3685 }
3686 return false;
3687 }
3688}
3689
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003690static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err,
3691 const json &o) {
Syoyo Fujita48f6db02018-04-15 18:40:55 +09003692 (void)err;
3693
jrkooncecba5d6c2019-08-29 11:26:22 -05003694 json_const_iterator it;
3695 if (!FindMember(o, "extensions", it)) {
Selmar09d2ff12018-03-15 17:30:42 +01003696 return false;
3697 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003698
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003699 auto &obj = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003700 if (!IsObject(obj)) {
Selmar09d2ff12018-03-15 17:30:42 +01003701 return false;
3702 }
3703 ExtensionMap extensions;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003704 json_const_iterator extIt = ObjectBegin(obj); // it.value().begin();
jrkooncecba5d6c2019-08-29 11:26:22 -05003705 json_const_iterator extEnd = ObjectEnd(obj);
3706 for (; extIt != extEnd; ++extIt) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003707 auto &itObj = GetValue(extIt);
jrkooncecba5d6c2019-08-29 11:26:22 -05003708 if (!IsObject(itObj)) continue;
3709 std::string key(GetKey(extIt));
3710 if (!ParseJsonAsValue(&extensions[key], itObj)) {
jrkoonce06c30c42019-09-03 15:56:48 -05003711 if (!key.empty()) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003712 // create empty object so that an extension object is still of type
3713 // object
jrkooncecba5d6c2019-08-29 11:26:22 -05003714 extensions[key] = Value{Value::Object{}};
Selmar Kokee3d0662018-10-08 16:20:43 +02003715 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003716 }
Selmar09d2ff12018-03-15 17:30:42 +01003717 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003718 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003719 (*ret) = std::move(extensions);
Selmar09d2ff12018-03-15 17:30:42 +01003720 }
3721 return true;
3722}
3723
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003724static bool ParseAsset(Asset *asset, std::string *err, const json &o,
3725 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09003726 ParseStringProperty(&asset->version, err, o, "version", true, "Asset");
3727 ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset");
3728 ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset");
Miguel Sousa22bfc842020-02-20 14:16:58 +01003729 ParseStringProperty(&asset->copyright, err, o, "copyright", false, "Asset");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003730
Selmar09d2ff12018-03-15 17:30:42 +01003731 ParseExtensionsProperty(&asset->extensions, err, o);
3732
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +00003733 // Unity exporter version is added as extra here
3734 ParseExtrasProperty(&(asset->extras), o);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003735
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003736 if (store_original_json_for_extras_and_extensions) {
3737 {
3738 json_const_iterator it;
3739 if (FindMember(o, "extensions", it)) {
3740 asset->extensions_json_string = JsonToString(GetValue(it));
3741 }
3742 }
3743 {
3744 json_const_iterator it;
3745 if (FindMember(o, "extras", it)) {
3746 asset->extras_json_string = JsonToString(GetValue(it));
3747 }
3748 }
3749 }
3750
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003751 return true;
3752}
3753
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003754static bool ParseImage(Image *image, const int image_idx, std::string *err,
3755 std::string *warn, const json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003756 bool store_original_json_for_extras_and_extensions,
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003757 const std::string &basedir, FsCallbacks *fs,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003758 LoadImageDataFunction *LoadImageData = nullptr,
Paolo Jovone6601bf2018-07-07 20:43:33 +02003759 void *load_image_user_data = nullptr) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003760 // A glTF image must either reference a bufferView or an image uri
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003761
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003762 // schema says oneOf [`bufferView`, `uri`]
3763 // TODO(syoyo): Check the type of each parameters.
jrkooncecba5d6c2019-08-29 11:26:22 -05003764 json_const_iterator it;
3765 bool hasBufferView = FindMember(o, "bufferView", it);
3766 bool hasURI = FindMember(o, "uri", it);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003767
Syoyo Fujitaaf3ebb22019-01-06 18:55:57 +09003768 ParseStringProperty(&image->name, err, o, "name", false);
3769
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003770 if (hasBufferView && hasURI) {
3771 // Should not both defined.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003772 if (err) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09003773 (*err) +=
3774 "Only one of `bufferView` or `uri` should be defined, but both are "
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003775 "defined for image[" +
3776 std::to_string(image_idx) + "] name = \"" + image->name + "\"\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003777 }
3778 return false;
3779 }
3780
3781 if (!hasBufferView && !hasURI) {
3782 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003783 (*err) += "Neither required `bufferView` nor `uri` defined for image[" +
3784 std::to_string(image_idx) + "] name = \"" + image->name +
3785 "\"\n";
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003786 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003787 return false;
3788 }
3789
Syoyo Fujita3e53feb2018-09-02 15:36:17 +09003790 ParseExtensionsProperty(&image->extensions, err, o);
Selmar Kok8eb39042018-10-05 14:29:35 +02003791 ParseExtrasProperty(&image->extras, o);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003792
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003793 if (store_original_json_for_extras_and_extensions) {
3794 {
3795 json_const_iterator eit;
3796 if (FindMember(o, "extensions", eit)) {
3797 image->extensions_json_string = JsonToString(GetValue(eit));
3798 }
3799 }
3800 {
3801 json_const_iterator eit;
3802 if (FindMember(o, "extras", eit)) {
3803 image->extras_json_string = JsonToString(GetValue(eit));
3804 }
3805 }
3806 }
3807
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003808 if (hasBufferView) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003809 int bufferView = -1;
3810 if (!ParseIntegerProperty(&bufferView, err, o, "bufferView", true)) {
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003811 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003812 (*err) += "Failed to parse `bufferView` for image[" +
3813 std::to_string(image_idx) + "] name = \"" + image->name +
3814 "\"\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003815 }
3816 return false;
3817 }
3818
3819 std::string mime_type;
3820 ParseStringProperty(&mime_type, err, o, "mimeType", false);
3821
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003822 int width = 0;
3823 ParseIntegerProperty(&width, err, o, "width", false);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003824
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003825 int height = 0;
3826 ParseIntegerProperty(&height, err, o, "height", false);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003827
3828 // Just only save some information here. Loading actual image data from
3829 // bufferView is done after this `ParseImage` function.
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003830 image->bufferView = bufferView;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003831 image->mimeType = mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003832 image->width = width;
3833 image->height = height;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003834
3835 return true;
3836 }
3837
Syoyo Fujita246654a2018-03-21 20:32:22 +09003838 // Parse URI & Load image data.
3839
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003840 std::string uri;
3841 std::string tmp_err;
Syoyo Fujita246654a2018-03-21 20:32:22 +09003842 if (!ParseStringProperty(&uri, &tmp_err, o, "uri", true)) {
3843 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003844 (*err) += "Failed to parse `uri` for image[" + std::to_string(image_idx) +
3845 "] name = \"" + image->name + "\".\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003846 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003847 return false;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003848 }
3849
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003850 std::vector<unsigned char> img;
Syoyo Fujitabeded612016-05-01 20:03:43 +09003851
Syoyo Fujita246654a2018-03-21 20:32:22 +09003852 if (IsDataURI(uri)) {
johan bowald642a3432018-04-01 12:37:18 +02003853 if (!DecodeDataURI(&img, image->mimeType, uri, 0, false)) {
Syoyo Fujita246654a2018-03-21 20:32:22 +09003854 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003855 (*err) += "Failed to decode 'uri' for image[" +
3856 std::to_string(image_idx) + "] name = [" + image->name +
3857 "]\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003858 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003859 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003860 }
3861 } else {
Syoyo Fujita246654a2018-03-21 20:32:22 +09003862 // Assume external file
3863 // Keep texture path (for textures that cannot be decoded)
3864 image->uri = uri;
Selmar67af3c92018-03-16 11:48:19 +01003865#ifdef TINYGLTF_NO_EXTERNAL_IMAGE
Syoyo Fujita246654a2018-03-21 20:32:22 +09003866 return true;
Selmar67af3c92018-03-16 11:48:19 +01003867#endif
Syoyo Fujitac4166e42020-01-08 02:38:01 +09003868 std::string decoded_uri = dlib::urldecode(uri);
3869 if (!LoadExternalFile(&img, err, warn, decoded_uri, basedir,
3870 /* required */ false, /* required bytes */ 0,
3871 /* checksize */ false, fs)) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003872 if (warn) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003873 (*warn) += "Failed to load external 'uri' for image[" +
3874 std::to_string(image_idx) + "] name = [" + image->name +
3875 "]\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003876 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003877 // If the image cannot be loaded, keep uri as image->uri.
3878 return true;
3879 }
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003880
Syoyo Fujita246654a2018-03-21 20:32:22 +09003881 if (img.empty()) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003882 if (warn) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003883 (*warn) += "Image data is empty for image[" +
3884 std::to_string(image_idx) + "] name = [" + image->name +
3885 "] \n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09003886 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003887 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003888 }
3889 }
3890
Squareysff644d82018-03-13 22:36:18 +01003891 if (*LoadImageData == nullptr) {
3892 if (err) {
3893 (*err) += "No LoadImageData callback specified.\n";
3894 }
3895 return false;
3896 }
Syoyo Fujitaaf3ebb22019-01-06 18:55:57 +09003897 return (*LoadImageData)(image, image_idx, err, warn, 0, 0, &img.at(0),
Paolo Jovone6601bf2018-07-07 20:43:33 +02003898 static_cast<int>(img.size()), load_image_user_data);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003899}
3900
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003901static bool ParseTexture(Texture *texture, std::string *err, const json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003902 bool store_original_json_for_extras_and_extensions,
Syoyo Fujitabeded612016-05-01 20:03:43 +09003903 const std::string &basedir) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003904 (void)basedir;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003905 int sampler = -1;
3906 int source = -1;
3907 ParseIntegerProperty(&sampler, err, o, "sampler", false);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003908
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003909 ParseIntegerProperty(&source, err, o, "source", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09003910
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003911 texture->sampler = sampler;
3912 texture->source = source;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003913
Selmar Kokfa7022f2018-04-04 18:10:20 +02003914 ParseExtensionsProperty(&texture->extensions, err, o);
3915 ParseExtrasProperty(&texture->extras, o);
Syoyo Fujitabde70212016-02-07 17:38:17 +09003916
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003917 if (store_original_json_for_extras_and_extensions) {
3918 {
3919 json_const_iterator it;
3920 if (FindMember(o, "extensions", it)) {
3921 texture->extensions_json_string = JsonToString(GetValue(it));
3922 }
3923 }
3924 {
3925 json_const_iterator it;
3926 if (FindMember(o, "extras", it)) {
3927 texture->extras_json_string = JsonToString(GetValue(it));
3928 }
3929 }
3930 }
3931
Vladimír Vondruš239be2c2018-07-24 23:23:56 +02003932 ParseStringProperty(&texture->name, err, o, "name", false);
3933
Syoyo Fujitabde70212016-02-07 17:38:17 +09003934 return true;
3935}
3936
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003937static bool ParseTextureInfo(
3938 TextureInfo *texinfo, std::string *err, const json &o,
3939 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09003940 if (texinfo == nullptr) {
3941 return false;
3942 }
3943
3944 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
3945 /* required */ true, "TextureInfo")) {
3946 return false;
3947 }
3948
3949 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
3950
3951 ParseExtensionsProperty(&texinfo->extensions, err, o);
3952 ParseExtrasProperty(&texinfo->extras, o);
3953
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003954 if (store_original_json_for_extras_and_extensions) {
3955 {
3956 json_const_iterator it;
3957 if (FindMember(o, "extensions", it)) {
3958 texinfo->extensions_json_string = JsonToString(GetValue(it));
3959 }
3960 }
3961 {
3962 json_const_iterator it;
3963 if (FindMember(o, "extras", it)) {
3964 texinfo->extras_json_string = JsonToString(GetValue(it));
3965 }
3966 }
3967 }
3968
Syoyo Fujita046400b2019-07-24 19:26:48 +09003969 return true;
3970}
3971
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003972static bool ParseNormalTextureInfo(
3973 NormalTextureInfo *texinfo, std::string *err, const json &o,
3974 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09003975 if (texinfo == nullptr) {
3976 return false;
3977 }
3978
3979 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
3980 /* required */ true, "NormalTextureInfo")) {
3981 return false;
3982 }
3983
3984 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
3985 ParseNumberProperty(&texinfo->scale, err, o, "scale", false);
3986
3987 ParseExtensionsProperty(&texinfo->extensions, err, o);
3988 ParseExtrasProperty(&texinfo->extras, o);
3989
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003990 if (store_original_json_for_extras_and_extensions) {
3991 {
3992 json_const_iterator it;
3993 if (FindMember(o, "extensions", it)) {
3994 texinfo->extensions_json_string = JsonToString(GetValue(it));
3995 }
3996 }
3997 {
3998 json_const_iterator it;
3999 if (FindMember(o, "extras", it)) {
4000 texinfo->extras_json_string = JsonToString(GetValue(it));
4001 }
4002 }
4003 }
4004
Syoyo Fujita046400b2019-07-24 19:26:48 +09004005 return true;
4006}
4007
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004008static bool ParseOcclusionTextureInfo(
4009 OcclusionTextureInfo *texinfo, std::string *err, const json &o,
4010 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004011 if (texinfo == nullptr) {
4012 return false;
4013 }
4014
4015 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
4016 /* required */ true, "NormalTextureInfo")) {
4017 return false;
4018 }
4019
4020 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
4021 ParseNumberProperty(&texinfo->strength, err, o, "strength", false);
4022
4023 ParseExtensionsProperty(&texinfo->extensions, err, o);
4024 ParseExtrasProperty(&texinfo->extras, o);
4025
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004026 if (store_original_json_for_extras_and_extensions) {
4027 {
4028 json_const_iterator it;
4029 if (FindMember(o, "extensions", it)) {
4030 texinfo->extensions_json_string = JsonToString(GetValue(it));
4031 }
4032 }
4033 {
4034 json_const_iterator it;
4035 if (FindMember(o, "extras", it)) {
4036 texinfo->extras_json_string = JsonToString(GetValue(it));
4037 }
4038 }
4039 }
4040
Syoyo Fujita046400b2019-07-24 19:26:48 +09004041 return true;
4042}
4043
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004044static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004045 bool store_original_json_for_extras_and_extensions,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004046 FsCallbacks *fs, const std::string &basedir,
4047 bool is_binary = false,
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09004048 const unsigned char *bin_data = nullptr,
Syoyo Fujitabeded612016-05-01 20:03:43 +09004049 size_t bin_size = 0) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004050 size_t byteLength;
4051 if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true,
4052 "Buffer")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004053 return false;
4054 }
4055
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004056 // In glTF 2.0, uri is not mandatory anymore
Syoyo Fujita20244e12018-03-15 11:01:05 -05004057 buffer->uri.clear();
4058 ParseStringProperty(&buffer->uri, err, o, "uri", false, "Buffer");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004059
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004060 // having an empty uri for a non embedded image should not be valid
Syoyo Fujita20244e12018-03-15 11:01:05 -05004061 if (!is_binary && buffer->uri.empty()) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004062 if (err) {
4063 (*err) += "'uri' is missing from non binary glTF file buffer.\n";
4064 }
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004065 }
4066
jrkooncecba5d6c2019-08-29 11:26:22 -05004067 json_const_iterator type;
4068 if (FindMember(o, "type", type)) {
4069 std::string typeStr;
4070 if (GetString(GetValue(type), typeStr)) {
4071 if (typeStr.compare("arraybuffer") == 0) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004072 // buffer.type = "arraybuffer";
4073 }
4074 }
4075 }
4076
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004077 if (is_binary) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004078 // Still binary glTF accepts external dataURI.
Syoyo Fujita20244e12018-03-15 11:01:05 -05004079 if (!buffer->uri.empty()) {
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004080 // First try embedded data URI.
4081 if (IsDataURI(buffer->uri)) {
4082 std::string mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004083 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004084 true)) {
4085 if (err) {
4086 (*err) +=
4087 "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
4088 }
4089 return false;
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004090 }
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004091 } else {
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004092 // External .bin file.
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004093 std::string decoded_uri = dlib::urldecode(buffer->uri);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09004094 if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr,
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004095 decoded_uri, basedir, /* required */ true,
4096 byteLength, /* checkSize */ true, fs)) {
Vladimír Vondrušfd84ceb2018-08-16 21:07:56 +02004097 return false;
4098 }
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004099 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004100 } else {
Syoyo Fujitabeded612016-05-01 20:03:43 +09004101 // load data from (embedded) binary data
4102
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09004103 if ((bin_size == 0) || (bin_data == nullptr)) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09004104 if (err) {
Syoyo Fujita40c4a0a2017-05-30 01:21:36 +09004105 (*err) += "Invalid binary data in `Buffer'.\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09004106 }
4107 return false;
4108 }
4109
4110 if (byteLength > bin_size) {
4111 if (err) {
4112 std::stringstream ss;
4113 ss << "Invalid `byteLength'. Must be equal or less than binary size: "
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004114 "`byteLength' = "
4115 << byteLength << ", binary size = " << bin_size << std::endl;
Syoyo Fujitabeded612016-05-01 20:03:43 +09004116 (*err) += ss.str();
4117 }
4118 return false;
4119 }
4120
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004121 // Read buffer data
4122 buffer->data.resize(static_cast<size_t>(byteLength));
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004123 memcpy(&(buffer->data.at(0)), bin_data, static_cast<size_t>(byteLength));
Syoyo Fujitabeded612016-05-01 20:03:43 +09004124 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004125
4126 } else {
Syoyo Fujita20244e12018-03-15 11:01:05 -05004127 if (IsDataURI(buffer->uri)) {
johan bowaldef151a42018-04-01 13:44:48 +02004128 std::string mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004129 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
4130 true)) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004131 if (err) {
Syoyo Fujita20244e12018-03-15 11:01:05 -05004132 (*err) += "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004133 }
4134 return false;
4135 }
4136 } else {
4137 // Assume external .bin file.
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004138 std::string decoded_uri = dlib::urldecode(buffer->uri);
4139 if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, decoded_uri,
4140 basedir, /* required */ true, byteLength,
4141 /* checkSize */ true, fs)) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004142 return false;
4143 }
4144 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004145 }
4146
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004147 ParseStringProperty(&buffer->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004148
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004149 ParseExtensionsProperty(&buffer->extensions, err, o);
4150 ParseExtrasProperty(&buffer->extras, o);
4151
4152 if (store_original_json_for_extras_and_extensions) {
4153 {
4154 json_const_iterator it;
4155 if (FindMember(o, "extensions", it)) {
4156 buffer->extensions_json_string = JsonToString(GetValue(it));
4157 }
4158 }
4159 {
4160 json_const_iterator it;
4161 if (FindMember(o, "extras", it)) {
4162 buffer->extras_json_string = JsonToString(GetValue(it));
4163 }
4164 }
4165 }
4166
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004167 return true;
4168}
4169
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004170static bool ParseBufferView(
4171 BufferView *bufferView, std::string *err, const json &o,
4172 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004173 int buffer = -1;
4174 if (!ParseIntegerProperty(&buffer, err, o, "buffer", true, "BufferView")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004175 return false;
4176 }
4177
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004178 size_t byteOffset = 0;
4179 ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004180
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004181 size_t byteLength = 1;
4182 if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true,
4183 "BufferView")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004184 return false;
4185 }
4186
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004187 size_t byteStride = 0;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004188 if (!ParseUnsignedProperty(&byteStride, err, o, "byteStride", false)) {
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004189 // Spec says: When byteStride of referenced bufferView is not defined, it
4190 // means that accessor elements are tightly packed, i.e., effective stride
4191 // equals the size of the element.
4192 // We cannot determine the actual byteStride until Accessor are parsed, thus
4193 // set 0(= tightly packed) here(as done in OpenGL's VertexAttribPoiner)
4194 byteStride = 0;
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004195 }
4196
4197 if ((byteStride > 252) || ((byteStride % 4) != 0)) {
4198 if (err) {
4199 std::stringstream ss;
4200 ss << "Invalid `byteStride' value. `byteStride' must be the multiple of "
4201 "4 : "
4202 << byteStride << std::endl;
4203
4204 (*err) += ss.str();
4205 }
4206 return false;
4207 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004208
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004209 int target = 0;
4210 ParseIntegerProperty(&target, err, o, "target", false);
4211 if ((target == TINYGLTF_TARGET_ARRAY_BUFFER) ||
4212 (target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004213 // OK
4214 } else {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004215 target = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004216 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004217 bufferView->target = target;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004218
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004219 ParseStringProperty(&bufferView->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004220
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004221 ParseExtensionsProperty(&bufferView->extensions, err, o);
4222 ParseExtrasProperty(&bufferView->extras, o);
4223
4224 if (store_original_json_for_extras_and_extensions) {
4225 {
4226 json_const_iterator it;
4227 if (FindMember(o, "extensions", it)) {
4228 bufferView->extensions_json_string = JsonToString(GetValue(it));
4229 }
4230 }
4231 {
4232 json_const_iterator it;
4233 if (FindMember(o, "extras", it)) {
4234 bufferView->extras_json_string = JsonToString(GetValue(it));
4235 }
4236 }
4237 }
4238
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004239 bufferView->buffer = buffer;
4240 bufferView->byteOffset = byteOffset;
4241 bufferView->byteLength = byteLength;
4242 bufferView->byteStride = byteStride;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004243 return true;
4244}
4245
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004246static bool ParseSparseAccessor(Accessor *accessor, std::string *err,
4247 const json &o) {
4248 accessor->sparse.isSparse = true;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004249
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004250 int count = 0;
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004251 if (!ParseIntegerProperty(&count, err, o, "count", true, "SparseAccessor")) {
4252 return false;
4253 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004254
jrkooncecba5d6c2019-08-29 11:26:22 -05004255 json_const_iterator indices_iterator;
4256 json_const_iterator values_iterator;
4257 if (!FindMember(o, "indices", indices_iterator)) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004258 (*err) = "the sparse object of this accessor doesn't have indices";
4259 return false;
4260 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004261
jrkooncecba5d6c2019-08-29 11:26:22 -05004262 if (!FindMember(o, "values", values_iterator)) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004263 (*err) = "the sparse object ob ths accessor doesn't have values";
4264 return false;
4265 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004266
jrkooncecba5d6c2019-08-29 11:26:22 -05004267 const json &indices_obj = GetValue(indices_iterator);
4268 const json &values_obj = GetValue(values_iterator);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004269
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004270 int indices_buffer_view = 0, indices_byte_offset = 0, component_type = 0;
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09004271 if (!ParseIntegerProperty(&indices_buffer_view, err, indices_obj,
4272 "bufferView", true, "SparseAccessor")) {
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004273 return false;
4274 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004275 ParseIntegerProperty(&indices_byte_offset, err, indices_obj, "byteOffset",
Jack Mousseau94715172022-02-24 14:25:37 -08004276 false);
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004277 if (!ParseIntegerProperty(&component_type, err, indices_obj, "componentType",
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09004278 true, "SparseAccessor")) {
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004279 return false;
4280 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004281
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004282 int values_buffer_view = 0, values_byte_offset = 0;
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004283 if (!ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView",
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09004284 true, "SparseAccessor")) {
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004285 return false;
4286 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004287 ParseIntegerProperty(&values_byte_offset, err, values_obj, "byteOffset",
Jack Mousseau94715172022-02-24 14:25:37 -08004288 false);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004289
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004290 accessor->sparse.count = count;
4291 accessor->sparse.indices.bufferView = indices_buffer_view;
4292 accessor->sparse.indices.byteOffset = indices_byte_offset;
4293 accessor->sparse.indices.componentType = component_type;
4294 accessor->sparse.values.bufferView = values_buffer_view;
4295 accessor->sparse.values.byteOffset = values_byte_offset;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004296
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004297 return true;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004298}
4299
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004300static bool ParseAccessor(Accessor *accessor, std::string *err, const json &o,
4301 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004302 int bufferView = -1;
4303 ParseIntegerProperty(&bufferView, err, o, "bufferView", false, "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004304
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004305 size_t byteOffset = 0;
4306 ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false, "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004307
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004308 bool normalized = false;
4309 ParseBooleanProperty(&normalized, err, o, "normalized", false, "Accessor");
4310
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004311 size_t componentType = 0;
4312 if (!ParseUnsignedProperty(&componentType, err, o, "componentType", true,
4313 "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004314 return false;
4315 }
4316
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004317 size_t count = 0;
4318 if (!ParseUnsignedProperty(&count, err, o, "count", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004319 return false;
4320 }
4321
4322 std::string type;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09004323 if (!ParseStringProperty(&type, err, o, "type", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004324 return false;
4325 }
4326
4327 if (type.compare("SCALAR") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004328 accessor->type = TINYGLTF_TYPE_SCALAR;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004329 } else if (type.compare("VEC2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004330 accessor->type = TINYGLTF_TYPE_VEC2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004331 } else if (type.compare("VEC3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004332 accessor->type = TINYGLTF_TYPE_VEC3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004333 } else if (type.compare("VEC4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004334 accessor->type = TINYGLTF_TYPE_VEC4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004335 } else if (type.compare("MAT2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004336 accessor->type = TINYGLTF_TYPE_MAT2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004337 } else if (type.compare("MAT3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004338 accessor->type = TINYGLTF_TYPE_MAT3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004339 } else if (type.compare("MAT4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004340 accessor->type = TINYGLTF_TYPE_MAT4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004341 } else {
4342 std::stringstream ss;
4343 ss << "Unsupported `type` for accessor object. Got \"" << type << "\"\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004344 if (err) {
4345 (*err) += ss.str();
4346 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004347 return false;
4348 }
4349
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004350 ParseStringProperty(&accessor->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004351
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004352 accessor->minValues.clear();
4353 accessor->maxValues.clear();
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004354 ParseNumberArrayProperty(&accessor->minValues, err, o, "min", false,
4355 "Accessor");
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004356
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004357 ParseNumberArrayProperty(&accessor->maxValues, err, o, "max", false,
4358 "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004359
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004360 accessor->count = count;
4361 accessor->bufferView = bufferView;
4362 accessor->byteOffset = byteOffset;
jianghaosen4748ad62017-08-29 20:08:37 +08004363 accessor->normalized = normalized;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004364 {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004365 if (componentType >= TINYGLTF_COMPONENT_TYPE_BYTE &&
4366 componentType <= TINYGLTF_COMPONENT_TYPE_DOUBLE) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004367 // OK
Arthur Brainville (Ybalrid)811e1d32019-06-15 07:32:38 +02004368 accessor->componentType = int(componentType);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004369 } else {
4370 std::stringstream ss;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004371 ss << "Invalid `componentType` in accessor. Got " << componentType
4372 << "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004373 if (err) {
4374 (*err) += ss.str();
4375 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004376 return false;
4377 }
4378 }
4379
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004380 ParseExtensionsProperty(&(accessor->extensions), err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004381 ParseExtrasProperty(&(accessor->extras), o);
4382
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004383 if (store_original_json_for_extras_and_extensions) {
4384 {
4385 json_const_iterator it;
4386 if (FindMember(o, "extensions", it)) {
4387 accessor->extensions_json_string = JsonToString(GetValue(it));
4388 }
4389 }
4390 {
4391 json_const_iterator it;
4392 if (FindMember(o, "extras", it)) {
4393 accessor->extras_json_string = JsonToString(GetValue(it));
4394 }
4395 }
4396 }
4397
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004398 // check if accessor has a "sparse" object:
jrkooncecba5d6c2019-08-29 11:26:22 -05004399 json_const_iterator iterator;
4400 if (FindMember(o, "sparse", iterator)) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004401 // here this accessor has a "sparse" subobject
jrkooncecba5d6c2019-08-29 11:26:22 -05004402 return ParseSparseAccessor(accessor, err, GetValue(iterator));
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004403 }
4404
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004405 return true;
4406}
4407
Alex Wood7319db72019-01-24 15:38:16 -05004408#ifdef TINYGLTF_ENABLE_DRACO
4409
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004410static void DecodeIndexBuffer(draco::Mesh *mesh, size_t componentSize,
4411 std::vector<uint8_t> &outBuffer) {
4412 if (componentSize == 4) {
Alex Wood7319db72019-01-24 15:38:16 -05004413 assert(sizeof(mesh->face(draco::FaceIndex(0))[0]) == componentSize);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004414 memcpy(outBuffer.data(), &mesh->face(draco::FaceIndex(0))[0],
4415 outBuffer.size());
4416 } else {
Alex Wood7319db72019-01-24 15:38:16 -05004417 size_t faceStride = componentSize * 3;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004418 for (draco::FaceIndex f(0); f < mesh->num_faces(); ++f) {
4419 const draco::Mesh::Face &face = mesh->face(f);
4420 if (componentSize == 2) {
4421 uint16_t indices[3] = {(uint16_t)face[0].value(),
4422 (uint16_t)face[1].value(),
4423 (uint16_t)face[2].value()};
4424 memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
4425 faceStride);
4426 } else {
4427 uint8_t indices[3] = {(uint8_t)face[0].value(),
4428 (uint8_t)face[1].value(),
4429 (uint8_t)face[2].value()};
4430 memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
4431 faceStride);
Alex Wood7319db72019-01-24 15:38:16 -05004432 }
4433 }
4434 }
4435}
4436
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004437template <typename T>
4438static bool GetAttributeForAllPoints(draco::Mesh *mesh,
4439 const draco::PointAttribute *pAttribute,
4440 std::vector<uint8_t> &outBuffer) {
Alex Wood7319db72019-01-24 15:38:16 -05004441 size_t byteOffset = 0;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004442 T values[4] = {0, 0, 0, 0};
4443 for (draco::PointIndex i(0); i < mesh->num_points(); ++i) {
Alex Wood7319db72019-01-24 15:38:16 -05004444 const draco::AttributeValueIndex val_index = pAttribute->mapped_index(i);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004445 if (!pAttribute->ConvertValue<T>(val_index, pAttribute->num_components(),
4446 values))
Alex Wood7319db72019-01-24 15:38:16 -05004447 return false;
4448
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004449 memcpy(outBuffer.data() + byteOffset, &values[0],
4450 sizeof(T) * pAttribute->num_components());
Alex Wood7319db72019-01-24 15:38:16 -05004451 byteOffset += sizeof(T) * pAttribute->num_components();
4452 }
4453
4454 return true;
4455}
4456
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004457static bool GetAttributeForAllPoints(uint32_t componentType, draco::Mesh *mesh,
4458 const draco::PointAttribute *pAttribute,
4459 std::vector<uint8_t> &outBuffer) {
Alex Wood7319db72019-01-24 15:38:16 -05004460 bool decodeResult = false;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004461 switch (componentType) {
4462 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
4463 decodeResult =
4464 GetAttributeForAllPoints<uint8_t>(mesh, pAttribute, outBuffer);
4465 break;
4466 case TINYGLTF_COMPONENT_TYPE_BYTE:
4467 decodeResult =
4468 GetAttributeForAllPoints<int8_t>(mesh, pAttribute, outBuffer);
4469 break;
4470 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
4471 decodeResult =
4472 GetAttributeForAllPoints<uint16_t>(mesh, pAttribute, outBuffer);
4473 break;
4474 case TINYGLTF_COMPONENT_TYPE_SHORT:
4475 decodeResult =
4476 GetAttributeForAllPoints<int16_t>(mesh, pAttribute, outBuffer);
4477 break;
4478 case TINYGLTF_COMPONENT_TYPE_INT:
4479 decodeResult =
4480 GetAttributeForAllPoints<int32_t>(mesh, pAttribute, outBuffer);
4481 break;
4482 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
4483 decodeResult =
4484 GetAttributeForAllPoints<uint32_t>(mesh, pAttribute, outBuffer);
4485 break;
4486 case TINYGLTF_COMPONENT_TYPE_FLOAT:
4487 decodeResult =
4488 GetAttributeForAllPoints<float>(mesh, pAttribute, outBuffer);
4489 break;
4490 case TINYGLTF_COMPONENT_TYPE_DOUBLE:
4491 decodeResult =
4492 GetAttributeForAllPoints<double>(mesh, pAttribute, outBuffer);
4493 break;
4494 default:
4495 return false;
Alex Wood7319db72019-01-24 15:38:16 -05004496 }
4497
4498 return decodeResult;
4499}
4500
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004501static bool ParseDracoExtension(Primitive *primitive, Model *model,
4502 std::string *err,
4503 const Value &dracoExtensionValue) {
snowapril84e15262021-03-20 19:25:58 +09004504 (void)err;
Alex Wood7319db72019-01-24 15:38:16 -05004505 auto bufferViewValue = dracoExtensionValue.Get("bufferView");
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004506 if (!bufferViewValue.IsInt()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004507 auto attributesValue = dracoExtensionValue.Get("attributes");
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004508 if (!attributesValue.IsObject()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004509
4510 auto attributesObject = attributesValue.Get<Value::Object>();
4511 int bufferView = bufferViewValue.Get<int>();
4512
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004513 BufferView &view = model->bufferViews[bufferView];
4514 Buffer &buffer = model->buffers[view.buffer];
Alex Wood7319db72019-01-24 15:38:16 -05004515 // BufferView has already been decoded
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004516 if (view.dracoDecoded) return true;
Alex Wood7319db72019-01-24 15:38:16 -05004517 view.dracoDecoded = true;
4518
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004519 const char *bufferViewData =
4520 reinterpret_cast<const char *>(buffer.data.data() + view.byteOffset);
Alex Wood7319db72019-01-24 15:38:16 -05004521 size_t bufferViewSize = view.byteLength;
4522
4523 // decode draco
4524 draco::DecoderBuffer decoderBuffer;
4525 decoderBuffer.Init(bufferViewData, bufferViewSize);
4526 draco::Decoder decoder;
4527 auto decodeResult = decoder.DecodeMeshFromBuffer(&decoderBuffer);
4528 if (!decodeResult.ok()) {
4529 return false;
4530 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004531 const std::unique_ptr<draco::Mesh> &mesh = decodeResult.value();
Alex Wood7319db72019-01-24 15:38:16 -05004532
4533 // create new bufferView for indices
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004534 if (primitive->indices >= 0) {
4535 int32_t componentSize = GetComponentSizeInBytes(
4536 model->accessors[primitive->indices].componentType);
Alex Wood7319db72019-01-24 15:38:16 -05004537 Buffer decodedIndexBuffer;
4538 decodedIndexBuffer.data.resize(mesh->num_faces() * 3 * componentSize);
4539
4540 DecodeIndexBuffer(mesh.get(), componentSize, decodedIndexBuffer.data);
4541
4542 model->buffers.emplace_back(std::move(decodedIndexBuffer));
4543
4544 BufferView decodedIndexBufferView;
4545 decodedIndexBufferView.buffer = int(model->buffers.size() - 1);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004546 decodedIndexBufferView.byteLength =
4547 int(mesh->num_faces() * 3 * componentSize);
Alex Wood7319db72019-01-24 15:38:16 -05004548 decodedIndexBufferView.byteOffset = 0;
4549 decodedIndexBufferView.byteStride = 0;
4550 decodedIndexBufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER;
4551 model->bufferViews.emplace_back(std::move(decodedIndexBufferView));
4552
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004553 model->accessors[primitive->indices].bufferView =
4554 int(model->bufferViews.size() - 1);
Alexander Wood0d77a292019-01-26 08:58:45 -05004555 model->accessors[primitive->indices].count = int(mesh->num_faces() * 3);
Alex Wood7319db72019-01-24 15:38:16 -05004556 }
4557
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004558 for (const auto &attribute : attributesObject) {
4559 if (!attribute.second.IsInt()) return false;
Alexander Wood0d77a292019-01-26 08:58:45 -05004560 auto primitiveAttribute = primitive->attributes.find(attribute.first);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004561 if (primitiveAttribute == primitive->attributes.end()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004562
4563 int dracoAttributeIndex = attribute.second.Get<int>();
4564 const auto pAttribute = mesh->GetAttributeByUniqueId(dracoAttributeIndex);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004565 const auto componentType =
4566 model->accessors[primitiveAttribute->second].componentType;
Alex Wood7319db72019-01-24 15:38:16 -05004567
4568 // Create a new buffer for this decoded buffer
4569 Buffer decodedBuffer;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004570 size_t bufferSize = mesh->num_points() * pAttribute->num_components() *
4571 GetComponentSizeInBytes(componentType);
Alex Wood7319db72019-01-24 15:38:16 -05004572 decodedBuffer.data.resize(bufferSize);
4573
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004574 if (!GetAttributeForAllPoints(componentType, mesh.get(), pAttribute,
4575 decodedBuffer.data))
Alex Wood7319db72019-01-24 15:38:16 -05004576 return false;
4577
4578 model->buffers.emplace_back(std::move(decodedBuffer));
4579
4580 BufferView decodedBufferView;
4581 decodedBufferView.buffer = int(model->buffers.size() - 1);
4582 decodedBufferView.byteLength = bufferSize;
4583 decodedBufferView.byteOffset = pAttribute->byte_offset();
4584 decodedBufferView.byteStride = pAttribute->byte_stride();
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004585 decodedBufferView.target = primitive->indices >= 0
4586 ? TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER
4587 : TINYGLTF_TARGET_ARRAY_BUFFER;
Alex Wood7319db72019-01-24 15:38:16 -05004588 model->bufferViews.emplace_back(std::move(decodedBufferView));
4589
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004590 model->accessors[primitiveAttribute->second].bufferView =
4591 int(model->bufferViews.size() - 1);
4592 model->accessors[primitiveAttribute->second].count =
4593 int(mesh->num_points());
Alex Wood7319db72019-01-24 15:38:16 -05004594 }
4595
4596 return true;
4597}
4598#endif
4599
4600static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004601 const json &o,
4602 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004603 int material = -1;
4604 ParseIntegerProperty(&material, err, o, "material", false);
4605 primitive->material = material;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004606
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004607 int mode = TINYGLTF_MODE_TRIANGLES;
4608 ParseIntegerProperty(&mode, err, o, "mode", false);
4609 primitive->mode = mode; // Why only triangled were supported ?
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004610
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004611 int indices = -1;
4612 ParseIntegerProperty(&indices, err, o, "indices", false);
4613 primitive->indices = indices;
4614 if (!ParseStringIntegerProperty(&primitive->attributes, err, o, "attributes",
4615 true, "Primitive")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004616 return false;
4617 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004618
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004619 // Look for morph targets
jrkooncecba5d6c2019-08-29 11:26:22 -05004620 json_const_iterator targetsObject;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004621 if (FindMember(o, "targets", targetsObject) &&
4622 IsArray(GetValue(targetsObject))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004623 auto targetsObjectEnd = ArrayEnd(GetValue(targetsObject));
4624 for (json_const_array_iterator i = ArrayBegin(GetValue(targetsObject));
4625 i != targetsObjectEnd; ++i) {
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004626 std::map<std::string, int> targetAttribues;
4627
jrkooncecba5d6c2019-08-29 11:26:22 -05004628 const json &dict = *i;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004629 if (IsObject(dict)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004630 json_const_iterator dictIt(ObjectBegin(dict));
4631 json_const_iterator dictItEnd(ObjectEnd(dict));
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004632
jrkooncecba5d6c2019-08-29 11:26:22 -05004633 for (; dictIt != dictItEnd; ++dictIt) {
4634 int iVal;
4635 if (GetInt(GetValue(dictIt), iVal))
4636 targetAttribues[GetKey(dictIt)] = iVal;
4637 }
4638 primitive->targets.emplace_back(std::move(targetAttribues));
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004639 }
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004640 }
4641 }
4642
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004643 ParseExtrasProperty(&(primitive->extras), o);
Alex Wood7319db72019-01-24 15:38:16 -05004644 ParseExtensionsProperty(&primitive->extensions, err, o);
4645
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004646 if (store_original_json_for_extras_and_extensions) {
4647 {
4648 json_const_iterator it;
4649 if (FindMember(o, "extensions", it)) {
4650 primitive->extensions_json_string = JsonToString(GetValue(it));
4651 }
4652 }
4653 {
4654 json_const_iterator it;
4655 if (FindMember(o, "extras", it)) {
4656 primitive->extras_json_string = JsonToString(GetValue(it));
4657 }
4658 }
4659 }
4660
Alex Wood7319db72019-01-24 15:38:16 -05004661#ifdef TINYGLTF_ENABLE_DRACO
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004662 auto dracoExtension =
4663 primitive->extensions.find("KHR_draco_mesh_compression");
4664 if (dracoExtension != primitive->extensions.end()) {
4665 ParseDracoExtension(primitive, model, err, dracoExtension->second);
Alex Wood7319db72019-01-24 15:38:16 -05004666 }
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09004667#else
4668 (void)model;
Alex Wood7319db72019-01-24 15:38:16 -05004669#endif
4670
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004671 return true;
4672}
4673
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004674static bool ParseMesh(Mesh *mesh, Model *model, std::string *err, const json &o,
4675 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004676 ParseStringProperty(&mesh->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004677
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004678 mesh->primitives.clear();
jrkooncecba5d6c2019-08-29 11:26:22 -05004679 json_const_iterator primObject;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004680 if (FindMember(o, "primitives", primObject) &&
4681 IsArray(GetValue(primObject))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004682 json_const_array_iterator primEnd = ArrayEnd(GetValue(primObject));
4683 for (json_const_array_iterator i = ArrayBegin(GetValue(primObject));
4684 i != primEnd; ++i) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004685 Primitive primitive;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004686 if (ParsePrimitive(&primitive, model, err, *i,
4687 store_original_json_for_extras_and_extensions)) {
Luke San Antonio10a3e1d2016-06-15 00:05:01 -04004688 // Only add the primitive if the parsing succeeds.
jrkoonced1e14722019-08-27 11:51:02 -05004689 mesh->primitives.emplace_back(std::move(primitive));
Luke San Antonio10a3e1d2016-06-15 00:05:01 -04004690 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004691 }
4692 }
4693
Aurélien Chatelain38a6a752017-05-24 09:54:55 +00004694 // Should probably check if has targets and if dimensions fit
4695 ParseNumberArrayProperty(&mesh->weights, err, o, "weights", false);
4696
Selmar09d2ff12018-03-15 17:30:42 +01004697 ParseExtensionsProperty(&mesh->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004698 ParseExtrasProperty(&(mesh->extras), o);
4699
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004700 if (store_original_json_for_extras_and_extensions) {
4701 {
4702 json_const_iterator it;
4703 if (FindMember(o, "extensions", it)) {
4704 mesh->extensions_json_string = JsonToString(GetValue(it));
4705 }
4706 }
4707 {
4708 json_const_iterator it;
4709 if (FindMember(o, "extras", it)) {
4710 mesh->extras_json_string = JsonToString(GetValue(it));
4711 }
4712 }
4713 }
4714
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004715 return true;
4716}
4717
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004718static bool ParseNode(Node *node, std::string *err, const json &o,
4719 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004720 ParseStringProperty(&node->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004721
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004722 int skin = -1;
4723 ParseIntegerProperty(&skin, err, o, "skin", false);
4724 node->skin = skin;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00004725
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004726 // Matrix and T/R/S are exclusive
Syoyo Fujita5b407452017-06-04 17:42:41 +09004727 if (!ParseNumberArrayProperty(&node->matrix, err, o, "matrix", false)) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004728 ParseNumberArrayProperty(&node->rotation, err, o, "rotation", false);
4729 ParseNumberArrayProperty(&node->scale, err, o, "scale", false);
4730 ParseNumberArrayProperty(&node->translation, err, o, "translation", false);
4731 }
4732
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004733 int camera = -1;
4734 ParseIntegerProperty(&camera, err, o, "camera", false);
4735 node->camera = camera;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004736
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004737 int mesh = -1;
4738 ParseIntegerProperty(&mesh, err, o, "mesh", false);
4739 node->mesh = mesh;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004740
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004741 node->children.clear();
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004742 ParseIntegerArrayProperty(&node->children, err, o, "children", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004743
Benjamin Schmithüsenad63bf72019-08-16 14:19:27 +02004744 ParseNumberArrayProperty(&node->weights, err, o, "weights", false);
4745
Selmar09d2ff12018-03-15 17:30:42 +01004746 ParseExtensionsProperty(&node->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004747 ParseExtrasProperty(&(node->extras), o);
4748
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004749 if (store_original_json_for_extras_and_extensions) {
4750 {
4751 json_const_iterator it;
4752 if (FindMember(o, "extensions", it)) {
4753 node->extensions_json_string = JsonToString(GetValue(it));
4754 }
4755 }
4756 {
4757 json_const_iterator it;
4758 if (FindMember(o, "extras", it)) {
4759 node->extras_json_string = JsonToString(GetValue(it));
4760 }
4761 }
4762 }
4763
Emanuel Schrade186322b2017-11-06 11:14:41 +01004764 return true;
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004765}
4766
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004767static bool ParsePbrMetallicRoughness(
4768 PbrMetallicRoughness *pbr, std::string *err, const json &o,
4769 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004770 if (pbr == nullptr) {
4771 return false;
4772 }
4773
4774 std::vector<double> baseColorFactor;
4775 if (ParseNumberArrayProperty(&baseColorFactor, err, o, "baseColorFactor",
4776 /* required */ false)) {
4777 if (baseColorFactor.size() != 4) {
4778 if (err) {
4779 (*err) +=
4780 "Array length of `baseColorFactor` parameter in "
4781 "pbrMetallicRoughness must be 4, but got " +
4782 std::to_string(baseColorFactor.size()) + "\n";
4783 }
4784 return false;
4785 }
Selmar Kokc3353e12019-10-18 18:22:35 +02004786 pbr->baseColorFactor = baseColorFactor;
Syoyo Fujita046400b2019-07-24 19:26:48 +09004787 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09004788
4789 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004790 json_const_iterator it;
4791 if (FindMember(o, "baseColorTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004792 ParseTextureInfo(&pbr->baseColorTexture, err, GetValue(it),
4793 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004794 }
4795 }
4796
4797 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004798 json_const_iterator it;
4799 if (FindMember(o, "metallicRoughnessTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004800 ParseTextureInfo(&pbr->metallicRoughnessTexture, err, GetValue(it),
4801 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004802 }
4803 }
4804
4805 ParseNumberProperty(&pbr->metallicFactor, err, o, "metallicFactor", false);
4806 ParseNumberProperty(&pbr->roughnessFactor, err, o, "roughnessFactor", false);
4807
4808 ParseExtensionsProperty(&pbr->extensions, err, o);
4809 ParseExtrasProperty(&pbr->extras, o);
4810
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004811 if (store_original_json_for_extras_and_extensions) {
4812 {
4813 json_const_iterator it;
4814 if (FindMember(o, "extensions", it)) {
4815 pbr->extensions_json_string = JsonToString(GetValue(it));
4816 }
4817 }
4818 {
4819 json_const_iterator it;
4820 if (FindMember(o, "extras", it)) {
4821 pbr->extras_json_string = JsonToString(GetValue(it));
4822 }
4823 }
4824 }
4825
Syoyo Fujita046400b2019-07-24 19:26:48 +09004826 return true;
4827}
4828
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004829static bool ParseMaterial(Material *material, std::string *err, const json &o,
4830 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004831 ParseStringProperty(&material->name, err, o, "name", /* required */ false);
4832
Syoyo Fujitaff515702019-08-24 16:29:14 +09004833 if (ParseNumberArrayProperty(&material->emissiveFactor, err, o,
4834 "emissiveFactor",
4835 /* required */ false)) {
Selmar Kok6df800d2019-08-19 11:05:28 +02004836 if (material->emissiveFactor.size() != 3) {
4837 if (err) {
4838 (*err) +=
4839 "Array length of `emissiveFactor` parameter in "
4840 "material must be 3, but got " +
4841 std::to_string(material->emissiveFactor.size()) + "\n";
4842 }
4843 return false;
4844 }
Syoyo Fujitaff515702019-08-24 16:29:14 +09004845 } else {
Selmar Kok6df800d2019-08-19 11:05:28 +02004846 // fill with default values
Selmar Kok6dba6c62019-08-19 11:23:31 +02004847 material->emissiveFactor = {0.0, 0.0, 0.0};
Selmar Kok6df800d2019-08-19 11:05:28 +02004848 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09004849
4850 ParseStringProperty(&material->alphaMode, err, o, "alphaMode",
4851 /* required */ false);
4852 ParseNumberProperty(&material->alphaCutoff, err, o, "alphaCutoff",
4853 /* required */ false);
4854 ParseBooleanProperty(&material->doubleSided, err, o, "doubleSided",
4855 /* required */ false);
4856
4857 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004858 json_const_iterator it;
4859 if (FindMember(o, "pbrMetallicRoughness", it)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004860 ParsePbrMetallicRoughness(&material->pbrMetallicRoughness, err,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004861 GetValue(it),
4862 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004863 }
4864 }
4865
4866 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004867 json_const_iterator it;
4868 if (FindMember(o, "normalTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004869 ParseNormalTextureInfo(&material->normalTexture, err, 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, "occlusionTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004877 ParseOcclusionTextureInfo(&material->occlusionTexture, 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, "emissiveTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004885 ParseTextureInfo(&material->emissiveTexture, err, GetValue(it),
4886 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004887 }
4888 }
4889
4890 // Old code path. For backward compatibility, we still store material values
4891 // as Parameter. This will create duplicated information for
4892 // example(pbrMetallicRoughness), but should be neglible in terms of memory
4893 // consumption.
4894 // TODO(syoyo): Remove in the next major release.
4895 material->values.clear();
4896 material->additionalValues.clear();
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004897
jrkooncecba5d6c2019-08-29 11:26:22 -05004898 json_const_iterator it(ObjectBegin(o));
4899 json_const_iterator itEnd(ObjectEnd(o));
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004900
jrkooncecba5d6c2019-08-29 11:26:22 -05004901 for (; it != itEnd; ++it) {
4902 std::string key(GetKey(it));
jrkoonce06c30c42019-09-03 15:56:48 -05004903 if (key == "pbrMetallicRoughness") {
4904 if (IsObject(GetValue(it))) {
4905 const json &values_object = GetValue(it);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004906
jrkoonce06c30c42019-09-03 15:56:48 -05004907 json_const_iterator itVal(ObjectBegin(values_object));
4908 json_const_iterator itValEnd(ObjectEnd(values_object));
4909
4910 for (; itVal != itValEnd; ++itVal) {
4911 Parameter param;
4912 if (ParseParameterProperty(&param, err, values_object, GetKey(itVal),
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004913 false)) {
jrkoonce06c30c42019-09-03 15:56:48 -05004914 material->values.emplace(GetKey(itVal), std::move(param));
4915 }
4916 }
4917 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004918 } else if (key == "extensions" || key == "extras") {
4919 // done later, skip, otherwise poorly parsed contents will be saved in the
4920 // parametermap and serialized again later
4921 } else {
jrkoonce06c30c42019-09-03 15:56:48 -05004922 Parameter param;
4923 if (ParseParameterProperty(&param, err, o, key, false)) {
4924 // names of materials have already been parsed. Putting it in this map
4925 // doesn't correctly reflext the glTF specification
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004926 if (key != "name")
4927 material->additionalValues.emplace(std::move(key), std::move(param));
jrkoonce06c30c42019-09-03 15:56:48 -05004928 }
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00004929 }
Syoyo Fujita5b407452017-06-04 17:42:41 +09004930 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004931
Syoyo Fujita046400b2019-07-24 19:26:48 +09004932 material->extensions.clear();
Selmar09d2ff12018-03-15 17:30:42 +01004933 ParseExtensionsProperty(&material->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004934 ParseExtrasProperty(&(material->extras), o);
4935
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004936 if (store_original_json_for_extras_and_extensions) {
4937 {
4938 json_const_iterator eit;
4939 if (FindMember(o, "extensions", eit)) {
4940 material->extensions_json_string = JsonToString(GetValue(eit));
4941 }
4942 }
4943 {
4944 json_const_iterator eit;
4945 if (FindMember(o, "extras", eit)) {
4946 material->extras_json_string = JsonToString(GetValue(eit));
4947 }
4948 }
4949 }
4950
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004951 return true;
4952}
4953
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004954static bool ParseAnimationChannel(
4955 AnimationChannel *channel, std::string *err, const json &o,
4956 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004957 int samplerIndex = -1;
4958 int targetIndex = -1;
4959 if (!ParseIntegerProperty(&samplerIndex, err, o, "sampler", true,
4960 "AnimationChannel")) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004961 if (err) {
4962 (*err) += "`sampler` field is missing in animation channels\n";
4963 }
4964 return false;
4965 }
4966
jrkooncecba5d6c2019-08-29 11:26:22 -05004967 json_const_iterator targetIt;
4968 if (FindMember(o, "target", targetIt) && IsObject(GetValue(targetIt))) {
4969 const json &target_object = GetValue(targetIt);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004970
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004971 if (!ParseIntegerProperty(&targetIndex, err, target_object, "node", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004972 if (err) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09004973 (*err) += "`node` field is missing in animation.channels.target\n";
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004974 }
4975 return false;
4976 }
4977
4978 if (!ParseStringProperty(&channel->target_path, err, target_object, "path",
4979 true)) {
4980 if (err) {
4981 (*err) += "`path` field is missing in animation.channels.target\n";
4982 }
4983 return false;
4984 }
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09004985 ParseExtensionsProperty(&channel->target_extensions, err, target_object);
4986 if (store_original_json_for_extras_and_extensions) {
Selmar Kok973d9b32020-01-21 18:45:24 +01004987 json_const_iterator it;
4988 if (FindMember(target_object, "extensions", it)) {
4989 channel->target_extensions_json_string = JsonToString(GetValue(it));
4990 }
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09004991 }
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004992 }
4993
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004994 channel->sampler = samplerIndex;
4995 channel->target_node = targetIndex;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004996
Selmar Kok4e2988e2019-08-16 14:08:08 +02004997 ParseExtensionsProperty(&channel->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004998 ParseExtrasProperty(&(channel->extras), o);
4999
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005000 if (store_original_json_for_extras_and_extensions) {
5001 {
5002 json_const_iterator it;
5003 if (FindMember(o, "extensions", it)) {
5004 channel->extensions_json_string = JsonToString(GetValue(it));
5005 }
5006 }
5007 {
5008 json_const_iterator it;
5009 if (FindMember(o, "extras", it)) {
5010 channel->extras_json_string = JsonToString(GetValue(it));
5011 }
5012 }
5013 }
5014
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005015 return true;
5016}
5017
5018static bool ParseAnimation(Animation *animation, std::string *err,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005019 const json &o,
5020 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005021 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005022 json_const_iterator channelsIt;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005023 if (FindMember(o, "channels", channelsIt) &&
5024 IsArray(GetValue(channelsIt))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005025 json_const_array_iterator channelEnd = ArrayEnd(GetValue(channelsIt));
5026 for (json_const_array_iterator i = ArrayBegin(GetValue(channelsIt));
5027 i != channelEnd; ++i) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005028 AnimationChannel channel;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005029 if (ParseAnimationChannel(
5030 &channel, err, *i,
5031 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005032 // Only add the channel if the parsing succeeds.
jrkooncecba5d6c2019-08-29 11:26:22 -05005033 animation->channels.emplace_back(std::move(channel));
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005034 }
5035 }
5036 }
5037 }
5038
5039 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005040 json_const_iterator samplerIt;
5041 if (FindMember(o, "samplers", samplerIt) && IsArray(GetValue(samplerIt))) {
5042 const json &sampler_array = GetValue(samplerIt);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005043
jrkooncecba5d6c2019-08-29 11:26:22 -05005044 json_const_array_iterator it = ArrayBegin(sampler_array);
5045 json_const_array_iterator itEnd = ArrayEnd(sampler_array);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005046
jrkooncecba5d6c2019-08-29 11:26:22 -05005047 for (; it != itEnd; ++it) {
5048 const json &s = *it;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005049
5050 AnimationSampler sampler;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005051 int inputIndex = -1;
5052 int outputIndex = -1;
5053 if (!ParseIntegerProperty(&inputIndex, err, s, "input", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005054 if (err) {
5055 (*err) += "`input` field is missing in animation.sampler\n";
5056 }
5057 return false;
5058 }
Evan Birenbaum6bdffed2019-02-14 13:30:57 -08005059 ParseStringProperty(&sampler.interpolation, err, s, "interpolation",
5060 false);
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005061 if (!ParseIntegerProperty(&outputIndex, err, s, "output", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005062 if (err) {
5063 (*err) += "`output` field is missing in animation.sampler\n";
5064 }
5065 return false;
5066 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005067 sampler.input = inputIndex;
5068 sampler.output = outputIndex;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005069 ParseExtensionsProperty(&(sampler.extensions), err, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02005070 ParseExtrasProperty(&(sampler.extras), s);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005071
5072 if (store_original_json_for_extras_and_extensions) {
5073 {
5074 json_const_iterator eit;
5075 if (FindMember(o, "extensions", eit)) {
5076 sampler.extensions_json_string = JsonToString(GetValue(eit));
5077 }
5078 }
5079 {
5080 json_const_iterator eit;
5081 if (FindMember(o, "extras", eit)) {
5082 sampler.extras_json_string = JsonToString(GetValue(eit));
5083 }
5084 }
5085 }
5086
jrkooncecba5d6c2019-08-29 11:26:22 -05005087 animation->samplers.emplace_back(std::move(sampler));
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005088 }
5089 }
5090 }
5091
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005092 ParseStringProperty(&animation->name, err, o, "name", false);
5093
Selmar Kok4e2988e2019-08-16 14:08:08 +02005094 ParseExtensionsProperty(&animation->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09005095 ParseExtrasProperty(&(animation->extras), o);
5096
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005097 if (store_original_json_for_extras_and_extensions) {
5098 {
5099 json_const_iterator it;
5100 if (FindMember(o, "extensions", it)) {
5101 animation->extensions_json_string = JsonToString(GetValue(it));
5102 }
5103 }
5104 {
5105 json_const_iterator it;
5106 if (FindMember(o, "extras", it)) {
5107 animation->extras_json_string = JsonToString(GetValue(it));
5108 }
5109 }
5110 }
5111
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005112 return true;
5113}
5114
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005115static bool ParseSampler(Sampler *sampler, std::string *err, const json &o,
5116 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujitac2615632016-06-19 21:56:06 +09005117 ParseStringProperty(&sampler->name, err, o, "name", false);
5118
Syoyo Fujitaee179b22019-08-16 13:11:30 +09005119 int minFilter = -1;
5120 int magFilter = -1;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005121 int wrapS = TINYGLTF_TEXTURE_WRAP_REPEAT;
5122 int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT;
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09005123 // int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005124 ParseIntegerProperty(&minFilter, err, o, "minFilter", false);
5125 ParseIntegerProperty(&magFilter, err, o, "magFilter", false);
5126 ParseIntegerProperty(&wrapS, err, o, "wrapS", false);
5127 ParseIntegerProperty(&wrapT, err, o, "wrapT", false);
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09005128 // ParseIntegerProperty(&wrapR, err, o, "wrapR", false); // tinygltf
5129 // extension
Syoyo Fujitaee179b22019-08-16 13:11:30 +09005130
5131 // TODO(syoyo): Check the value is alloed one.
5132 // (e.g. we allow 9728(NEAREST), but don't allow 9727)
Syoyo Fujitac2615632016-06-19 21:56:06 +09005133
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005134 sampler->minFilter = minFilter;
5135 sampler->magFilter = magFilter;
5136 sampler->wrapS = wrapS;
5137 sampler->wrapT = wrapT;
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09005138 // sampler->wrapR = wrapR;
Syoyo Fujitac2615632016-06-19 21:56:06 +09005139
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005140 ParseExtensionsProperty(&(sampler->extensions), err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09005141 ParseExtrasProperty(&(sampler->extras), o);
5142
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005143 if (store_original_json_for_extras_and_extensions) {
5144 {
5145 json_const_iterator it;
5146 if (FindMember(o, "extensions", it)) {
5147 sampler->extensions_json_string = JsonToString(GetValue(it));
5148 }
5149 }
5150 {
5151 json_const_iterator it;
5152 if (FindMember(o, "extras", it)) {
5153 sampler->extras_json_string = JsonToString(GetValue(it));
5154 }
5155 }
5156 }
5157
Syoyo Fujitac2615632016-06-19 21:56:06 +09005158 return true;
5159}
5160
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005161static bool ParseSkin(Skin *skin, std::string *err, const json &o,
5162 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09005163 ParseStringProperty(&skin->name, err, o, "name", false, "Skin");
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005164
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005165 std::vector<int> joints;
5166 if (!ParseIntegerArrayProperty(&joints, err, o, "joints", false, "Skin")) {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005167 return false;
5168 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005169 skin->joints = std::move(joints);
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005170
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005171 int skeleton = -1;
5172 ParseIntegerProperty(&skeleton, err, o, "skeleton", false, "Skin");
5173 skin->skeleton = skeleton;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005174
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005175 int invBind = -1;
5176 ParseIntegerProperty(&invBind, err, o, "inverseBindMatrices", true, "Skin");
5177 skin->inverseBindMatrices = invBind;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005178
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005179 ParseExtensionsProperty(&(skin->extensions), err, o);
5180 ParseExtrasProperty(&(skin->extras), o);
5181
5182 if (store_original_json_for_extras_and_extensions) {
5183 {
5184 json_const_iterator it;
5185 if (FindMember(o, "extensions", it)) {
5186 skin->extensions_json_string = JsonToString(GetValue(it));
5187 }
5188 }
5189 {
5190 json_const_iterator it;
5191 if (FindMember(o, "extras", it)) {
5192 skin->extras_json_string = JsonToString(GetValue(it));
5193 }
5194 }
5195 }
5196
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005197 return true;
5198}
5199
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005200static bool ParsePerspectiveCamera(
5201 PerspectiveCamera *camera, std::string *err, const json &o,
5202 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005203 double yfov = 0.0;
5204 if (!ParseNumberProperty(&yfov, err, o, "yfov", true, "OrthographicCamera")) {
5205 return false;
5206 }
5207
5208 double znear = 0.0;
5209 if (!ParseNumberProperty(&znear, err, o, "znear", true,
5210 "PerspectiveCamera")) {
5211 return false;
5212 }
5213
5214 double aspectRatio = 0.0; // = invalid
5215 ParseNumberProperty(&aspectRatio, err, o, "aspectRatio", false,
5216 "PerspectiveCamera");
5217
5218 double zfar = 0.0; // = invalid
5219 ParseNumberProperty(&zfar, err, o, "zfar", false, "PerspectiveCamera");
5220
Selmar Kok31cb7f92018-10-03 15:39:05 +02005221 camera->aspectRatio = aspectRatio;
5222 camera->zfar = zfar;
5223 camera->yfov = yfov;
5224 camera->znear = znear;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005225
Selmar09d2ff12018-03-15 17:30:42 +01005226 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005227 ParseExtrasProperty(&(camera->extras), o);
5228
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005229 if (store_original_json_for_extras_and_extensions) {
5230 {
5231 json_const_iterator it;
5232 if (FindMember(o, "extensions", it)) {
5233 camera->extensions_json_string = JsonToString(GetValue(it));
5234 }
5235 }
5236 {
5237 json_const_iterator it;
5238 if (FindMember(o, "extras", it)) {
5239 camera->extras_json_string = JsonToString(GetValue(it));
5240 }
5241 }
5242 }
5243
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005244 // TODO(syoyo): Validate parameter values.
5245
5246 return true;
5247}
5248
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005249static bool ParseSpotLight(SpotLight *light, std::string *err, const json &o,
5250 bool store_original_json_for_extras_and_extensions) {
Johan Bowald52936a02019-07-17 09:06:45 +02005251 ParseNumberProperty(&light->innerConeAngle, err, o, "innerConeAngle", false);
5252 ParseNumberProperty(&light->outerConeAngle, err, o, "outerConeAngle", false);
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005253
Johan Bowald52936a02019-07-17 09:06:45 +02005254 ParseExtensionsProperty(&light->extensions, err, o);
5255 ParseExtrasProperty(&light->extras, o);
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005256
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005257 if (store_original_json_for_extras_and_extensions) {
5258 {
5259 json_const_iterator it;
5260 if (FindMember(o, "extensions", it)) {
5261 light->extensions_json_string = JsonToString(GetValue(it));
5262 }
5263 }
5264 {
5265 json_const_iterator it;
5266 if (FindMember(o, "extras", it)) {
5267 light->extras_json_string = JsonToString(GetValue(it));
5268 }
5269 }
5270 }
5271
Johan Bowald52936a02019-07-17 09:06:45 +02005272 // TODO(syoyo): Validate parameter values.
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005273
Johan Bowald52936a02019-07-17 09:06:45 +02005274 return true;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005275}
5276
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005277static bool ParseOrthographicCamera(
5278 OrthographicCamera *camera, std::string *err, const json &o,
5279 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005280 double xmag = 0.0;
5281 if (!ParseNumberProperty(&xmag, err, o, "xmag", true, "OrthographicCamera")) {
5282 return false;
5283 }
5284
5285 double ymag = 0.0;
5286 if (!ParseNumberProperty(&ymag, err, o, "ymag", true, "OrthographicCamera")) {
5287 return false;
5288 }
5289
5290 double zfar = 0.0;
5291 if (!ParseNumberProperty(&zfar, err, o, "zfar", true, "OrthographicCamera")) {
5292 return false;
5293 }
5294
5295 double znear = 0.0;
5296 if (!ParseNumberProperty(&znear, err, o, "znear", true,
5297 "OrthographicCamera")) {
5298 return false;
5299 }
5300
Selmar09d2ff12018-03-15 17:30:42 +01005301 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005302 ParseExtrasProperty(&(camera->extras), o);
5303
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005304 if (store_original_json_for_extras_and_extensions) {
5305 {
5306 json_const_iterator it;
5307 if (FindMember(o, "extensions", it)) {
5308 camera->extensions_json_string = JsonToString(GetValue(it));
5309 }
5310 }
5311 {
5312 json_const_iterator it;
5313 if (FindMember(o, "extras", it)) {
5314 camera->extras_json_string = JsonToString(GetValue(it));
5315 }
5316 }
5317 }
5318
Selmar Kok31cb7f92018-10-03 15:39:05 +02005319 camera->xmag = xmag;
5320 camera->ymag = ymag;
5321 camera->zfar = zfar;
5322 camera->znear = znear;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005323
5324 // TODO(syoyo): Validate parameter values.
5325
5326 return true;
5327}
5328
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005329static bool ParseCamera(Camera *camera, std::string *err, const json &o,
5330 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005331 if (!ParseStringProperty(&camera->type, err, o, "type", true, "Camera")) {
5332 return false;
5333 }
5334
5335 if (camera->type.compare("orthographic") == 0) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005336 json_const_iterator orthoIt;
5337 if (!FindMember(o, "orthographic", orthoIt)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005338 if (err) {
5339 std::stringstream ss;
5340 ss << "Orhographic camera description not found." << std::endl;
5341 (*err) += ss.str();
5342 }
5343 return false;
5344 }
5345
jrkooncecba5d6c2019-08-29 11:26:22 -05005346 const json &v = GetValue(orthoIt);
5347 if (!IsObject(v)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005348 if (err) {
5349 std::stringstream ss;
5350 ss << "\"orthographic\" is not a JSON object." << std::endl;
5351 (*err) += ss.str();
5352 }
5353 return false;
5354 }
5355
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005356 if (!ParseOrthographicCamera(
5357 &camera->orthographic, err, v,
5358 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005359 return false;
5360 }
5361 } else if (camera->type.compare("perspective") == 0) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005362 json_const_iterator perspIt;
5363 if (!FindMember(o, "perspective", perspIt)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005364 if (err) {
5365 std::stringstream ss;
5366 ss << "Perspective camera description not found." << std::endl;
5367 (*err) += ss.str();
5368 }
5369 return false;
5370 }
5371
jrkooncecba5d6c2019-08-29 11:26:22 -05005372 const json &v = GetValue(perspIt);
5373 if (!IsObject(v)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005374 if (err) {
5375 std::stringstream ss;
5376 ss << "\"perspective\" is not a JSON object." << std::endl;
5377 (*err) += ss.str();
5378 }
5379 return false;
5380 }
5381
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005382 if (!ParsePerspectiveCamera(
5383 &camera->perspective, err, v,
5384 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005385 return false;
5386 }
5387 } else {
5388 if (err) {
5389 std::stringstream ss;
5390 ss << "Invalid camera type: \"" << camera->type
5391 << "\". Must be \"perspective\" or \"orthographic\"" << std::endl;
5392 (*err) += ss.str();
5393 }
5394 return false;
5395 }
5396
5397 ParseStringProperty(&camera->name, err, o, "name", false);
5398
Selmar09d2ff12018-03-15 17:30:42 +01005399 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005400 ParseExtrasProperty(&(camera->extras), o);
5401
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005402 if (store_original_json_for_extras_and_extensions) {
5403 {
5404 json_const_iterator it;
5405 if (FindMember(o, "extensions", it)) {
5406 camera->extensions_json_string = JsonToString(GetValue(it));
5407 }
5408 }
5409 {
5410 json_const_iterator it;
5411 if (FindMember(o, "extras", it)) {
5412 camera->extras_json_string = JsonToString(GetValue(it));
5413 }
5414 }
5415 }
5416
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005417 return true;
5418}
5419
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005420static bool ParseLight(Light *light, std::string *err, const json &o,
5421 bool store_original_json_for_extras_and_extensions) {
Johan Bowald52936a02019-07-17 09:06:45 +02005422 if (!ParseStringProperty(&light->type, err, o, "type", true)) {
5423 return false;
5424 }
5425
5426 if (light->type == "spot") {
jrkooncecba5d6c2019-08-29 11:26:22 -05005427 json_const_iterator spotIt;
5428 if (!FindMember(o, "spot", spotIt)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005429 if (err) {
5430 std::stringstream ss;
5431 ss << "Spot light description not found." << std::endl;
5432 (*err) += ss.str();
5433 }
5434 return false;
Benjamin Schmithüsen4557b6a2019-07-09 16:55:55 +02005435 }
5436
jrkooncecba5d6c2019-08-29 11:26:22 -05005437 const json &v = GetValue(spotIt);
5438 if (!IsObject(v)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005439 if (err) {
5440 std::stringstream ss;
5441 ss << "\"spot\" is not a JSON object." << std::endl;
5442 (*err) += ss.str();
5443 }
5444 return false;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005445 }
5446
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005447 if (!ParseSpotLight(&light->spot, err, v,
5448 store_original_json_for_extras_and_extensions)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005449 return false;
5450 }
5451 }
5452
5453 ParseStringProperty(&light->name, err, o, "name", false);
5454 ParseNumberArrayProperty(&light->color, err, o, "color", false);
5455 ParseNumberProperty(&light->range, err, o, "range", false);
5456 ParseNumberProperty(&light->intensity, err, o, "intensity", false);
5457 ParseExtensionsProperty(&light->extensions, err, o);
5458 ParseExtrasProperty(&(light->extras), o);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005459
5460 if (store_original_json_for_extras_and_extensions) {
5461 {
5462 json_const_iterator it;
5463 if (FindMember(o, "extensions", it)) {
5464 light->extensions_json_string = JsonToString(GetValue(it));
5465 }
5466 }
5467 {
5468 json_const_iterator it;
5469 if (FindMember(o, "extras", it)) {
5470 light->extras_json_string = JsonToString(GetValue(it));
5471 }
5472 }
5473 }
5474
Johan Bowald52936a02019-07-17 09:06:45 +02005475 return true;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005476}
5477
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09005478bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005479 const char *json_str,
5480 unsigned int json_str_length,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09005481 const std::string &base_dir,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005482 unsigned int check_sections) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005483 if (json_str_length < 4) {
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09005484 if (err) {
5485 (*err) = "JSON string too short.\n";
5486 }
5487 return false;
5488 }
5489
jrkooncecba5d6c2019-08-29 11:26:22 -05005490 JsonDocument v;
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005491
Syoyo Fujitad42767e2018-03-15 21:52:00 -05005492#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
5493 defined(_CPPUNWIND)) && \
Syoyo Fujitac4166e42020-01-08 02:38:01 +09005494 !defined(TINYGLTF_NOEXCEPTION)
Syoyo Fujitacdf84902017-08-01 20:12:08 +09005495 try {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005496 JsonParse(v, json_str, json_str_length, true);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005497
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005498 } catch (const std::exception &e) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005499 if (err) {
Syoyo Fujitacdf84902017-08-01 20:12:08 +09005500 (*err) = e.what();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005501 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005502 return false;
5503 }
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005504#else
5505 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005506 JsonParse(v, json_str, json_str_length);
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005507
jrkooncecba5d6c2019-08-29 11:26:22 -05005508 if (!IsObject(v)) {
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005509 // Assume parsing was failed.
5510 if (err) {
5511 (*err) = "Failed to parse JSON object\n";
5512 }
5513 return false;
5514 }
5515 }
5516#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005517
jrkooncecba5d6c2019-08-29 11:26:22 -05005518 if (!IsObject(v)) {
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09005519 // root is not an object.
5520 if (err) {
5521 (*err) = "Root element is not a JSON object\n";
5522 }
5523 return false;
5524 }
5525
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02005526 {
5527 bool version_found = false;
jrkooncecba5d6c2019-08-29 11:26:22 -05005528 json_const_iterator it;
5529 if (FindMember(v, "asset", it) && IsObject(GetValue(it))) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005530 auto &itObj = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05005531 json_const_iterator version_it;
5532 std::string versionStr;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005533 if (FindMember(itObj, "version", version_it) &&
5534 GetString(GetValue(version_it), versionStr)) {
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02005535 version_found = true;
5536 }
5537 }
5538 if (version_found) {
5539 // OK
5540 } else if (check_sections & REQUIRE_VERSION) {
5541 if (err) {
5542 (*err) += "\"asset\" object not found in .gltf or not an object type\n";
5543 }
5544 return false;
5545 }
5546 }
5547
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005548 // scene is not mandatory.
Syoyo Fujita5b407452017-06-04 17:42:41 +09005549 // FIXME Maybe a better way to handle it than removing the code
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005550
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005551 auto IsArrayMemberPresent = [](const json &_v, const char *name) -> bool {
jrkooncecba5d6c2019-08-29 11:26:22 -05005552 json_const_iterator it;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005553 return FindMember(_v, name, it) && IsArray(GetValue(it));
jrkooncecba5d6c2019-08-29 11:26:22 -05005554 };
5555
Syoyo Fujita83675312017-12-02 21:14:13 +09005556 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005557 if ((check_sections & REQUIRE_SCENES) &&
5558 !IsArrayMemberPresent(v, "scenes")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005559 if (err) {
5560 (*err) += "\"scenes\" object not found in .gltf or not an array type\n";
5561 }
5562 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005563 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005564 }
5565
Syoyo Fujita83675312017-12-02 21:14:13 +09005566 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005567 if ((check_sections & REQUIRE_NODES) && !IsArrayMemberPresent(v, "nodes")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005568 if (err) {
5569 (*err) += "\"nodes\" object not found in .gltf\n";
5570 }
5571 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005572 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005573 }
5574
Syoyo Fujita83675312017-12-02 21:14:13 +09005575 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005576 if ((check_sections & REQUIRE_ACCESSORS) &&
5577 !IsArrayMemberPresent(v, "accessors")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005578 if (err) {
5579 (*err) += "\"accessors\" object not found in .gltf\n";
5580 }
5581 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005582 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005583 }
5584
Syoyo Fujita83675312017-12-02 21:14:13 +09005585 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005586 if ((check_sections & REQUIRE_BUFFERS) &&
5587 !IsArrayMemberPresent(v, "buffers")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005588 if (err) {
5589 (*err) += "\"buffers\" object not found in .gltf\n";
5590 }
5591 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005592 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005593 }
5594
Syoyo Fujita83675312017-12-02 21:14:13 +09005595 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005596 if ((check_sections & REQUIRE_BUFFER_VIEWS) &&
5597 !IsArrayMemberPresent(v, "bufferViews")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005598 if (err) {
5599 (*err) += "\"bufferViews\" object not found in .gltf\n";
5600 }
5601 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005602 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005603 }
5604
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005605 model->buffers.clear();
5606 model->bufferViews.clear();
5607 model->accessors.clear();
5608 model->meshes.clear();
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005609 model->cameras.clear();
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005610 model->nodes.clear();
5611 model->extensionsUsed.clear();
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00005612 model->extensionsRequired.clear();
Selmar09d2ff12018-03-15 17:30:42 +01005613 model->extensions.clear();
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005614 model->defaultScene = -1;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005615
Syoyo Fujita83675312017-12-02 21:14:13 +09005616 // 1. Parse Asset
5617 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005618 json_const_iterator it;
5619 if (FindMember(v, "asset", it) && IsObject(GetValue(it))) {
5620 const json &root = GetValue(it);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005621
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005622 ParseAsset(&model->asset, err, root,
5623 store_original_json_for_extras_and_extensions_);
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00005624 }
5625 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005626
jrkoonce51453942019-09-03 09:48:30 -05005627#ifdef TINYGLTF_USE_CPP14
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005628 auto ForEachInArray = [](const json &_v, const char *member,
5629 const auto &cb) -> bool
jrkoonce51453942019-09-03 09:48:30 -05005630#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005631 // The std::function<> implementation can be less efficient because it will
5632 // allocate heap when the size of the captured lambda is above 16 bytes with
5633 // clang and gcc, but it does not require C++14.
5634 auto ForEachInArray = [](const json &_v, const char *member,
5635 const std::function<bool(const json &)> &cb) -> bool
jrkoonce51453942019-09-03 09:48:30 -05005636#endif
Syoyo Fujita83675312017-12-02 21:14:13 +09005637 {
jrkoonce0d2b6ef2019-09-04 13:46:45 -05005638 json_const_iterator itm;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005639 if (FindMember(_v, member, itm) && IsArray(GetValue(itm))) {
jrkoonce0d2b6ef2019-09-04 13:46:45 -05005640 const json &root = GetValue(itm);
jrkooncecba5d6c2019-08-29 11:26:22 -05005641 auto it = ArrayBegin(root);
5642 auto end = ArrayEnd(root);
5643 for (; it != end; ++it) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005644 if (!cb(*it)) return false;
Syoyo Fujita59130d12017-08-01 20:23:11 +09005645 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005646 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005647 return true;
5648 };
5649
jrkooncecba5d6c2019-08-29 11:26:22 -05005650 // 2. Parse extensionUsed
5651 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005652 ForEachInArray(v, "extensionsUsed", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005653 std::string str;
5654 GetString(o, str);
5655 model->extensionsUsed.emplace_back(std::move(str));
5656 return true;
5657 });
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005658 }
5659
Syoyo Fujita83675312017-12-02 21:14:13 +09005660 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005661 ForEachInArray(v, "extensionsRequired", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005662 std::string str;
5663 GetString(o, str);
5664 model->extensionsRequired.emplace_back(std::move(str));
5665 return true;
5666 });
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005667 }
5668
Syoyo Fujita83675312017-12-02 21:14:13 +09005669 // 3. Parse Buffer
5670 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005671 bool success = ForEachInArray(v, "buffers", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005672 if (!IsObject(o)) {
5673 if (err) {
5674 (*err) += "`buffers' does not contain an JSON object.";
Syoyo Fujitabeded612016-05-01 20:03:43 +09005675 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005676 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005677 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005678 Buffer buffer;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005679 if (!ParseBuffer(&buffer, err, o,
5680 store_original_json_for_extras_and_extensions_, &fs,
5681 base_dir, is_binary_, bin_data_, bin_size_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005682 return false;
5683 }
5684
5685 model->buffers.emplace_back(std::move(buffer));
5686 return true;
5687 });
5688
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005689 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005690 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005691 }
5692 }
Syoyo Fujita83675312017-12-02 21:14:13 +09005693 // 4. Parse BufferView
5694 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005695 bool success = ForEachInArray(v, "bufferViews", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005696 if (!IsObject(o)) {
5697 if (err) {
5698 (*err) += "`bufferViews' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09005699 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005700 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005701 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005702 BufferView bufferView;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005703 if (!ParseBufferView(&bufferView, err, o,
5704 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005705 return false;
5706 }
5707
5708 model->bufferViews.emplace_back(std::move(bufferView));
5709 return true;
5710 });
5711
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005712 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005713 return false;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005714 }
5715 }
5716
Syoyo Fujita83675312017-12-02 21:14:13 +09005717 // 5. Parse Accessor
5718 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005719 bool success = ForEachInArray(v, "accessors", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005720 if (!IsObject(o)) {
5721 if (err) {
5722 (*err) += "`accessors' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09005723 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005724 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005725 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005726 Accessor accessor;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005727 if (!ParseAccessor(&accessor, err, o,
5728 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005729 return false;
5730 }
5731
5732 model->accessors.emplace_back(std::move(accessor));
5733 return true;
5734 });
5735
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005736 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005737 return false;
Syoyo Fujitac2615632016-06-19 21:56:06 +09005738 }
5739 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005740
Syoyo Fujita83675312017-12-02 21:14:13 +09005741 // 6. Parse Mesh
5742 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005743 bool success = ForEachInArray(v, "meshes", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005744 if (!IsObject(o)) {
5745 if (err) {
5746 (*err) += "`meshes' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09005747 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005748 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005749 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005750 Mesh mesh;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005751 if (!ParseMesh(&mesh, model, err, o,
5752 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005753 return false;
5754 }
5755
5756 model->meshes.emplace_back(std::move(mesh));
5757 return true;
5758 });
5759
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005760 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005761 return false;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005762 }
5763 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01005764
viperscape9df05802018-12-05 14:11:01 -05005765 // Assign missing bufferView target types
5766 // - Look for missing Mesh indices
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01005767 // - Look for missing Mesh attributes
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00005768 for (auto &mesh : model->meshes) {
5769 for (auto &primitive : mesh.primitives) {
5770 if (primitive.indices >
5771 -1) // has indices from parsing step, must be Element Array Buffer
viperscape9df05802018-12-05 14:11:01 -05005772 {
Jeff McGlynn89152522019-04-25 16:33:56 -07005773 if (size_t(primitive.indices) >= model->accessors.size()) {
5774 if (err) {
5775 (*err) += "primitive indices accessor out of bounds";
5776 }
5777 return false;
5778 }
5779
Syoyo Fujitacea69e32019-08-20 17:10:30 +09005780 auto bufferView =
5781 model->accessors[size_t(primitive.indices)].bufferView;
Jeff McGlynn89152522019-04-25 16:33:56 -07005782 if (bufferView < 0 || size_t(bufferView) >= model->bufferViews.size()) {
5783 if (err) {
5784 (*err) += "accessor[" + std::to_string(primitive.indices) +
5785 "] invalid bufferView";
5786 }
5787 return false;
5788 }
5789
Syoyo Fujitacea69e32019-08-20 17:10:30 +09005790 model->bufferViews[size_t(bufferView)].target =
Jeff McGlynn89152522019-04-25 16:33:56 -07005791 TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00005792 // we could optionally check if acessors' bufferView type is Scalar, as
5793 // it should be
viperscape9df05802018-12-05 14:11:01 -05005794 }
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01005795
5796 for (auto &attribute : primitive.attributes) {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09005797 model
5798 ->bufferViews[size_t(
5799 model->accessors[size_t(attribute.second)].bufferView)]
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01005800 .target = TINYGLTF_TARGET_ARRAY_BUFFER;
5801 }
Marcin Kacprzakc3d67162020-01-22 13:13:35 +01005802
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09005803 for (auto &target : primitive.targets) {
5804 for (auto &attribute : target) {
Rahul Sheth125e4a22020-07-13 13:56:50 -04005805 auto bufferView =
5806 model->accessors[size_t(attribute.second)].bufferView;
Syoyo Fujita91da2992020-07-15 13:52:39 +09005807 // bufferView could be null(-1) for sparse morph target
5808 if (bufferView >= 0) {
Rahul Sheth125e4a22020-07-13 13:56:50 -04005809 model->bufferViews[size_t(bufferView)].target =
5810 TINYGLTF_TARGET_ARRAY_BUFFER;
5811 }
Marcin Kacprzakc3d67162020-01-22 13:13:35 +01005812 }
5813 }
viperscape9df05802018-12-05 14:11:01 -05005814 }
5815 }
5816
Syoyo Fujita83675312017-12-02 21:14:13 +09005817 // 7. Parse Node
5818 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005819 bool success = ForEachInArray(v, "nodes", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005820 if (!IsObject(o)) {
5821 if (err) {
5822 (*err) += "`nodes' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005823 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005824 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005825 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005826 Node node;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005827 if (!ParseNode(&node, err, o,
5828 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005829 return false;
5830 }
5831
5832 model->nodes.emplace_back(std::move(node));
5833 return true;
5834 });
5835
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005836 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005837 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005838 }
5839 }
5840
5841 // 8. Parse scenes.
5842 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005843 bool success = ForEachInArray(v, "scenes", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005844 if (!IsObject(o)) {
5845 if (err) {
5846 (*err) += "`scenes' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005847 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005848 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005849 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005850 std::vector<int> nodes;
jrkooncea3b8b352019-09-03 10:30:19 -05005851 ParseIntegerArrayProperty(&nodes, err, o, "nodes", false);
jrkooncecba5d6c2019-08-29 11:26:22 -05005852
5853 Scene scene;
5854 scene.nodes = std::move(nodes);
5855
5856 ParseStringProperty(&scene.name, err, o, "name", false);
5857
5858 ParseExtensionsProperty(&scene.extensions, err, o);
5859 ParseExtrasProperty(&scene.extras, o);
5860
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005861 if (store_original_json_for_extras_and_extensions_) {
5862 {
5863 json_const_iterator it;
5864 if (FindMember(o, "extensions", it)) {
Mio Nilssone29ba7c2021-05-11 11:50:35 +02005865 scene.extensions_json_string = JsonToString(GetValue(it));
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005866 }
5867 }
5868 {
5869 json_const_iterator it;
5870 if (FindMember(o, "extras", it)) {
Mio Nilssone29ba7c2021-05-11 11:50:35 +02005871 scene.extras_json_string = JsonToString(GetValue(it));
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005872 }
5873 }
5874 }
5875
jrkooncecba5d6c2019-08-29 11:26:22 -05005876 model->scenes.emplace_back(std::move(scene));
5877 return true;
5878 });
5879
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005880 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005881 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005882 }
5883 }
5884
5885 // 9. Parse default scenes.
5886 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005887 json_const_iterator rootIt;
5888 int iVal;
5889 if (FindMember(v, "scene", rootIt) && GetInt(GetValue(rootIt), iVal)) {
5890 model->defaultScene = iVal;
Syoyo Fujita83675312017-12-02 21:14:13 +09005891 }
5892 }
5893
5894 // 10. Parse Material
5895 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005896 bool success = ForEachInArray(v, "materials", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005897 if (!IsObject(o)) {
5898 if (err) {
5899 (*err) += "`materials' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005900 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005901 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005902 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005903 Material material;
5904 ParseStringProperty(&material.name, err, o, "name", false);
5905
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005906 if (!ParseMaterial(&material, err, o,
5907 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005908 return false;
5909 }
5910
5911 model->materials.emplace_back(std::move(material));
5912 return true;
5913 });
5914
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005915 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005916 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005917 }
5918 }
5919
5920 // 11. Parse Image
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09005921 void *load_image_user_data{nullptr};
5922
5923 LoadImageDataOption load_image_option;
5924
5925 if (user_image_loader_) {
5926 // Use user supplied pointer
5927 load_image_user_data = load_image_user_data_;
5928 } else {
5929 load_image_option.preserve_channels = preserve_image_channels_;
5930 load_image_user_data = reinterpret_cast<void *>(&load_image_option);
5931 }
5932
Syoyo Fujita83675312017-12-02 21:14:13 +09005933 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005934 int idx = 0;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005935 bool success = ForEachInArray(v, "images", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005936 if (!IsObject(o)) {
5937 if (err) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005938 (*err) += "image[" + std::to_string(idx) + "] is not a JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005939 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005940 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005941 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005942 Image image;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005943 if (!ParseImage(&image, idx, err, warn, o,
5944 store_original_json_for_extras_and_extensions_, base_dir,
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09005945 &fs, &this->LoadImageData, load_image_user_data)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005946 return false;
5947 }
5948
5949 if (image.bufferView != -1) {
5950 // Load image from the buffer view.
5951 if (size_t(image.bufferView) >= model->bufferViews.size()) {
5952 if (err) {
5953 std::stringstream ss;
5954 ss << "image[" << idx << "] bufferView \"" << image.bufferView
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005955 << "\" not found in the scene." << std::endl;
jrkooncecba5d6c2019-08-29 11:26:22 -05005956 (*err) += ss.str();
5957 }
5958 return false;
5959 }
5960
5961 const BufferView &bufferView =
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005962 model->bufferViews[size_t(image.bufferView)];
jrkooncecba5d6c2019-08-29 11:26:22 -05005963 if (size_t(bufferView.buffer) >= model->buffers.size()) {
5964 if (err) {
5965 std::stringstream ss;
5966 ss << "image[" << idx << "] buffer \"" << bufferView.buffer
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005967 << "\" not found in the scene." << std::endl;
jrkooncecba5d6c2019-08-29 11:26:22 -05005968 (*err) += ss.str();
5969 }
5970 return false;
5971 }
5972 const Buffer &buffer = model->buffers[size_t(bufferView.buffer)];
5973
5974 if (*LoadImageData == nullptr) {
5975 if (err) {
5976 (*err) += "No LoadImageData callback specified.\n";
5977 }
5978 return false;
5979 }
5980 bool ret = LoadImageData(
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005981 &image, idx, err, warn, image.width, image.height,
5982 &buffer.data[bufferView.byteOffset],
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09005983 static_cast<int>(bufferView.byteLength), load_image_user_data);
jrkooncecba5d6c2019-08-29 11:26:22 -05005984 if (!ret) {
5985 return false;
5986 }
5987 }
5988
5989 model->images.emplace_back(std::move(image));
5990 ++idx;
5991 return true;
5992 });
5993
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005994 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005995 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005996 }
5997 }
5998
5999 // 12. Parse Texture
6000 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006001 bool success = ForEachInArray(v, "textures", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006002 if (!IsObject(o)) {
6003 if (err) {
6004 (*err) += "`textures' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006005 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006006 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006007 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006008 Texture texture;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006009 if (!ParseTexture(&texture, err, o,
6010 store_original_json_for_extras_and_extensions_,
6011 base_dir)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006012 return false;
6013 }
6014
6015 model->textures.emplace_back(std::move(texture));
6016 return true;
6017 });
6018
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006019 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006020 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006021 }
6022 }
6023
6024 // 13. Parse Animation
6025 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006026 bool success = ForEachInArray(v, "animations", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006027 if (!IsObject(o)) {
6028 if (err) {
6029 (*err) += "`animations' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006030 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006031 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006032 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006033 Animation animation;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006034 if (!ParseAnimation(&animation, err, o,
6035 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006036 return false;
6037 }
6038
6039 model->animations.emplace_back(std::move(animation));
6040 return true;
6041 });
6042
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006043 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006044 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006045 }
6046 }
6047
6048 // 14. Parse Skin
6049 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006050 bool success = ForEachInArray(v, "skins", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006051 if (!IsObject(o)) {
6052 if (err) {
6053 (*err) += "`skins' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006054 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006055 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006056 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006057 Skin skin;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006058 if (!ParseSkin(&skin, err, o,
6059 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006060 return false;
6061 }
6062
6063 model->skins.emplace_back(std::move(skin));
6064 return true;
6065 });
6066
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006067 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006068 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006069 }
6070 }
6071
6072 // 15. Parse Sampler
6073 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006074 bool success = ForEachInArray(v, "samplers", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006075 if (!IsObject(o)) {
6076 if (err) {
6077 (*err) += "`samplers' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006078 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006079 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006080 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006081 Sampler sampler;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006082 if (!ParseSampler(&sampler, err, o,
6083 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006084 return false;
6085 }
6086
6087 model->samplers.emplace_back(std::move(sampler));
6088 return true;
6089 });
6090
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006091 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006092 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006093 }
6094 }
6095
6096 // 16. Parse Camera
6097 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006098 bool success = ForEachInArray(v, "cameras", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006099 if (!IsObject(o)) {
6100 if (err) {
6101 (*err) += "`cameras' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006102 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006103 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006104 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006105 Camera camera;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006106 if (!ParseCamera(&camera, err, o,
6107 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006108 return false;
6109 }
6110
6111 model->cameras.emplace_back(std::move(camera));
6112 return true;
6113 });
6114
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006115 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006116 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006117 }
6118 }
6119
6120 // 17. Parse Extensions
Selmar09d2ff12018-03-15 17:30:42 +01006121 ParseExtensionsProperty(&model->extensions, err, v);
6122
6123 // 18. Specific extension implementations
Syoyo Fujita83675312017-12-02 21:14:13 +09006124 {
jrkooncecba5d6c2019-08-29 11:26:22 -05006125 json_const_iterator rootIt;
6126 if (FindMember(v, "extensions", rootIt) && IsObject(GetValue(rootIt))) {
6127 const json &root = GetValue(rootIt);
Syoyo Fujita83675312017-12-02 21:14:13 +09006128
jrkooncecba5d6c2019-08-29 11:26:22 -05006129 json_const_iterator it(ObjectBegin(root));
6130 json_const_iterator itEnd(ObjectEnd(root));
Syoyo Fujita83675312017-12-02 21:14:13 +09006131 for (; it != itEnd; ++it) {
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02006132 // parse KHR_lights_punctual extension
jrkooncecba5d6c2019-08-29 11:26:22 -05006133 std::string key(GetKey(it));
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006134 if ((key == "KHR_lights_punctual") && IsObject(GetValue(it))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006135 const json &object = GetValue(it);
6136 json_const_iterator itLight;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006137 if (FindMember(object, "lights", itLight)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006138 const json &lights = GetValue(itLight);
6139 if (!IsArray(lights)) {
6140 continue;
Syoyo Fujita83675312017-12-02 21:14:13 +09006141 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006142
6143 auto arrayIt(ArrayBegin(lights));
6144 auto arrayItEnd(ArrayEnd(lights));
6145 for (; arrayIt != arrayItEnd; ++arrayIt) {
6146 Light light;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006147 if (!ParseLight(&light, err, *arrayIt,
6148 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006149 return false;
6150 }
6151 model->lights.emplace_back(std::move(light));
6152 }
Syoyo Fujita83675312017-12-02 21:14:13 +09006153 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01006154 }
6155 }
6156 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01006157 }
Syoyo Fujita83675312017-12-02 21:14:13 +09006158
mynzc0d4d1c2018-06-28 23:06:00 +09006159 // 19. Parse Extras
6160 ParseExtrasProperty(&model->extras, v);
6161
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006162 if (store_original_json_for_extras_and_extensions_) {
6163 model->extras_json_string = JsonToString(v["extras"]);
6164 model->extensions_json_string = JsonToString(v["extensions"]);
6165 }
6166
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09006167 return true;
6168}
6169
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006170bool TinyGLTF::LoadASCIIFromString(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006171 std::string *warn, const char *str,
6172 unsigned int length,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006173 const std::string &base_dir,
6174 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006175 is_binary_ = false;
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09006176 bin_data_ = nullptr;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006177 bin_size_ = 0;
6178
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006179 return LoadFromString(model, err, warn, str, length, base_dir,
6180 check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006181}
6182
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006183bool TinyGLTF::LoadASCIIFromFile(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006184 std::string *warn, const std::string &filename,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006185 unsigned int check_sections) {
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006186 std::stringstream ss;
6187
Paolo Jovone6601bf2018-07-07 20:43:33 +02006188 if (fs.ReadWholeFile == nullptr) {
6189 // Programmer error, assert() ?
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006190 ss << "Failed to read file: " << filename
6191 << ": one or more FS callback not set" << std::endl;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09006192 if (err) {
6193 (*err) = ss.str();
6194 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006195 return false;
6196 }
6197
Paolo Jovone6601bf2018-07-07 20:43:33 +02006198 std::vector<unsigned char> data;
6199 std::string fileerr;
6200 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006201 if (!fileread) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02006202 ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
6203 if (err) {
6204 (*err) = ss.str();
6205 }
6206 return false;
6207 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006208
Paolo Jovone6601bf2018-07-07 20:43:33 +02006209 size_t sz = data.size();
Luke San Antoniob310b4a2016-06-23 14:17:37 -04006210 if (sz == 0) {
6211 if (err) {
6212 (*err) = "Empty file.";
6213 }
6214 return false;
6215 }
6216
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006217 std::string basedir = GetBaseDir(filename);
6218
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006219 bool ret = LoadASCIIFromString(
6220 model, err, warn, reinterpret_cast<const char *>(&data.at(0)),
6221 static_cast<unsigned int>(data.size()), basedir, check_sections);
Luke San Antonio9e36b612016-06-23 14:10:51 -04006222
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006223 return ret;
6224}
6225
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006226bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006227 std::string *warn,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006228 const unsigned char *bytes,
6229 unsigned int size,
6230 const std::string &base_dir,
6231 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006232 if (size < 20) {
6233 if (err) {
6234 (*err) = "Too short data size for glTF Binary.";
6235 }
6236 return false;
6237 }
6238
Syoyo Fujitabeded612016-05-01 20:03:43 +09006239 if (bytes[0] == 'g' && bytes[1] == 'l' && bytes[2] == 'T' &&
6240 bytes[3] == 'F') {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006241 // ok
6242 } else {
6243 if (err) {
6244 (*err) = "Invalid magic.";
6245 }
6246 return false;
6247 }
6248
Syoyo Fujitabeded612016-05-01 20:03:43 +09006249 unsigned int version; // 4 bytes
6250 unsigned int length; // 4 bytes
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006251 unsigned int chunk0_length; // 4 bytes
6252 unsigned int chunk0_format; // 4 bytes;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006253
Syoyo Fujitabeded612016-05-01 20:03:43 +09006254 memcpy(&version, bytes + 4, 4);
6255 swap4(&version);
6256 memcpy(&length, bytes + 8, 4);
6257 swap4(&length);
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006258 memcpy(&chunk0_length, bytes + 12, 4); // JSON data length
6259 swap4(&chunk0_length);
6260 memcpy(&chunk0_format, bytes + 16, 4);
6261 swap4(&chunk0_format);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006262
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006263 // https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html#binary-gltf-layout
6264 //
Syoyo Fujitae69069d2018-03-15 22:01:18 -05006265 // In case the Bin buffer is not present, the size is exactly 20 + size of
6266 // JSON contents,
6267 // so use "greater than" operator.
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006268 //
6269 // https://github.com/syoyo/tinygltf/issues/372
6270 // Use 64bit uint to avoid integer overflow.
6271 uint64_t json_size = 20ull + uint64_t(chunk0_length);
6272
6273 if (json_size > std::numeric_limits<uint32_t>::max()) {
6274 // Do not allow 4GB or more GLB data.
6275 (*err) = "Invalid glTF binary. GLB data exceeds 4GB.";
6276 }
6277
6278 if ((json_size > uint64_t(size)) || (chunk0_length < 1) || (length > size) ||
6279 (json_size > uint64_t(length)) ||
6280 (chunk0_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006281 if (err) {
6282 (*err) = "Invalid glTF binary.";
6283 }
6284 return false;
6285 }
6286
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006287 // Padding check
6288 // The start and the end of each chunk must be aligned to a 4-byte boundary.
6289 // No padding check for chunk0 start since its 4byte-boundary is ensured.
6290 if ((json_size % 4) != 0) {
6291 if (err) {
6292 (*err) = "JSON Chunk end does not aligned to a 4-byte boundary.";
6293 }
6294 }
6295
6296 // Read Chunk1 info(BIN data)
6297 if ((json_size + 8ull) > uint64_t(length)) {
6298 if (err) {
6299 (*err) = "Insufficient storage space for Chunk1(BIN data).";
6300 }
6301 return false;
6302 }
6303
6304 unsigned int chunk1_length; // 4 bytes
6305 unsigned int chunk1_format; // 4 bytes;
6306 memcpy(&chunk1_length, bytes + json_size, 4); // JSON data length
6307 swap4(&chunk1_length);
6308 memcpy(&chunk1_format, bytes + json_size + 4, 4);
6309 swap4(&chunk1_format);
6310
6311 if (chunk1_length < 4) {
6312 // TODO: Do we allow 0byte BIN data?
6313 if (err) {
6314 (*err) = "Insufficient Chunk1(BIN) data size.";
6315 }
6316 return false;
6317 }
6318
6319 if ((chunk1_length % 4) != 0) {
6320 if (err) {
6321 (*err) = "BIN Chunk end does not aligned to a 4-byte boundary.";
6322 }
6323 return false;
6324 }
6325
6326 if (uint64_t(chunk1_length) + json_size > uint64_t(length)) {
6327 if (err) {
6328 (*err) = "BIN Chunk data length exceeds the GLB size.";
6329 }
6330 return false;
6331 }
6332
6333 if (chunk1_format != 0x004e4942) {
6334 if (err) {
6335 (*err) = "Invlid type for chunk1 data.";
6336 }
6337 return false;
6338 }
6339
6340 bin_data_ = bytes + json_size +
6341 8; // 4 bytes (bin_buffer_length) + 4 bytes(bin_buffer_format)
6342
6343 bin_size_ = size_t(chunk1_length);
6344
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006345 // Extract JSON string.
Syoyo Fujitabeded612016-05-01 20:03:43 +09006346 std::string jsonString(reinterpret_cast<const char *>(&bytes[20]),
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006347 chunk0_length);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006348
6349 is_binary_ = true;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006350
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006351 bool ret = LoadFromString(model, err, warn,
6352 reinterpret_cast<const char *>(&bytes[20]),
Syoyo Fujita46ee8b42022-08-26 22:06:53 +09006353 chunk0_length, base_dir, check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006354 if (!ret) {
6355 return ret;
6356 }
6357
6358 return true;
6359}
6360
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006361bool TinyGLTF::LoadBinaryFromFile(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006362 std::string *warn,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006363 const std::string &filename,
6364 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006365 std::stringstream ss;
6366
Paolo Jovone6601bf2018-07-07 20:43:33 +02006367 if (fs.ReadWholeFile == nullptr) {
6368 // Programmer error, assert() ?
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006369 ss << "Failed to read file: " << filename
6370 << ": one or more FS callback not set" << std::endl;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006371 if (err) {
6372 (*err) = ss.str();
6373 }
6374 return false;
6375 }
6376
Paolo Jovone6601bf2018-07-07 20:43:33 +02006377 std::vector<unsigned char> data;
6378 std::string fileerr;
6379 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006380 if (!fileread) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02006381 ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
6382 if (err) {
6383 (*err) = ss.str();
6384 }
6385 return false;
6386 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006387
Syoyo Fujitabeded612016-05-01 20:03:43 +09006388 std::string basedir = GetBaseDir(filename);
6389
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006390 bool ret = LoadBinaryFromMemory(model, err, warn, &data.at(0),
6391 static_cast<unsigned int>(data.size()),
6392 basedir, check_sections);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006393
6394 return ret;
6395}
6396
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006397///////////////////////
6398// GLTF Serialization
6399///////////////////////
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006400namespace {
6401json JsonFromString(const char *s) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006402#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006403 return json(s, GetAllocator());
jrkooncecba5d6c2019-08-29 11:26:22 -05006404#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006405 return json(s);
jrkooncecba5d6c2019-08-29 11:26:22 -05006406#endif
jrkoonce63419a12019-09-03 17:06:41 -05006407}
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006408
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006409void JsonAssign(json &dest, const json &src) {
6410#ifdef TINYGLTF_USE_RAPIDJSON
6411 dest.CopyFrom(src, GetAllocator());
6412#else
6413 dest = src;
6414#endif
6415}
6416
6417void JsonAddMember(json &o, const char *key, json &&value) {
6418#ifdef TINYGLTF_USE_RAPIDJSON
6419 if (!o.IsObject()) {
6420 o.SetObject();
6421 }
6422 o.AddMember(json(key, GetAllocator()), std::move(value), GetAllocator());
6423#else
6424 o[key] = std::move(value);
6425#endif
6426}
6427
6428void JsonPushBack(json &o, json &&value) {
6429#ifdef TINYGLTF_USE_RAPIDJSON
6430 o.PushBack(std::move(value), GetAllocator());
6431#else
6432 o.push_back(std::move(value));
6433#endif
6434}
6435
6436bool JsonIsNull(const json &o) {
6437#ifdef TINYGLTF_USE_RAPIDJSON
6438 return o.IsNull();
6439#else
6440 return o.is_null();
6441#endif
6442}
6443
6444void JsonSetObject(json &o) {
6445#ifdef TINYGLTF_USE_RAPIDJSON
6446 o.SetObject();
6447#else
6448 o = o.object({});
6449#endif
6450}
6451
6452void JsonReserveArray(json &o, size_t s) {
6453#ifdef TINYGLTF_USE_RAPIDJSON
6454 o.SetArray();
6455 o.Reserve(static_cast<rapidjson::SizeType>(s), GetAllocator());
6456#endif
6457 (void)(o);
6458 (void)(s);
6459}
6460} // namespace
6461
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006462// typedef std::pair<std::string, json> json_object_pair;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006463
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006464template <typename T>
6465static void SerializeNumberProperty(const std::string &key, T number,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006466 json &obj) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006467 // obj.insert(
Syoyo Fujita83675312017-12-02 21:14:13 +09006468 // json_object_pair(key, json(static_cast<double>(number))));
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006469 // obj[key] = static_cast<double>(number);
jrkooncecba5d6c2019-08-29 11:26:22 -05006470 JsonAddMember(obj, key.c_str(), json(number));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006471}
6472
Ivo van Dongen272a9df2020-05-29 11:24:11 +03006473#ifdef TINYGLTF_USE_RAPIDJSON
6474template <>
6475void SerializeNumberProperty(const std::string &key, size_t number, json &obj) {
6476 JsonAddMember(obj, key.c_str(), json(static_cast<uint64_t>(number)));
6477}
6478#endif
6479
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006480template <typename T>
6481static void SerializeNumberArrayProperty(const std::string &key,
6482 const std::vector<T> &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006483 json &obj) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006484 if (value.empty()) return;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006485
jrkooncecba5d6c2019-08-29 11:26:22 -05006486 json ary;
6487 JsonReserveArray(ary, value.size());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006488 for (const auto &s : value) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006489 JsonPushBack(ary, json(s));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006490 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006491 JsonAddMember(obj, key.c_str(), std::move(ary));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006492}
6493
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006494static void SerializeStringProperty(const std::string &key,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006495 const std::string &value, json &obj) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006496 JsonAddMember(obj, key.c_str(), JsonFromString(value.c_str()));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006497}
6498
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006499static void SerializeStringArrayProperty(const std::string &key,
6500 const std::vector<std::string> &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006501 json &obj) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006502 json ary;
6503 JsonReserveArray(ary, value.size());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006504 for (auto &s : value) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006505 JsonPushBack(ary, JsonFromString(s.c_str()));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006506 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006507 JsonAddMember(obj, key.c_str(), std::move(ary));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006508}
6509
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006510static bool ValueToJson(const Value &value, json *ret) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006511 json obj;
jrkooncecba5d6c2019-08-29 11:26:22 -05006512#ifdef TINYGLTF_USE_RAPIDJSON
6513 switch (value.Type()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006514 case REAL_TYPE:
6515 obj.SetDouble(value.Get<double>());
6516 break;
6517 case INT_TYPE:
6518 obj.SetInt(value.Get<int>());
6519 break;
6520 case BOOL_TYPE:
6521 obj.SetBool(value.Get<bool>());
6522 break;
6523 case STRING_TYPE:
6524 obj.SetString(value.Get<std::string>().c_str(), GetAllocator());
6525 break;
6526 case ARRAY_TYPE: {
6527 obj.SetArray();
6528 obj.Reserve(static_cast<rapidjson::SizeType>(value.ArrayLen()),
6529 GetAllocator());
6530 for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
6531 Value elementValue = value.Get(int(i));
6532 json elementJson;
6533 if (ValueToJson(value.Get(int(i)), &elementJson))
6534 obj.PushBack(std::move(elementJson), GetAllocator());
jrkooncecba5d6c2019-08-29 11:26:22 -05006535 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006536 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05006537 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006538 case BINARY_TYPE:
6539 // TODO
6540 // obj = json(value.Get<std::vector<unsigned char>>());
6541 return false;
6542 break;
6543 case OBJECT_TYPE: {
6544 obj.SetObject();
6545 Value::Object objMap = value.Get<Value::Object>();
6546 for (auto &it : objMap) {
6547 json elementJson;
6548 if (ValueToJson(it.second, &elementJson)) {
6549 obj.AddMember(json(it.first.c_str(), GetAllocator()),
6550 std::move(elementJson), GetAllocator());
6551 }
6552 }
6553 break;
6554 }
6555 case NULL_TYPE:
6556 default:
6557 return false;
jrkooncecba5d6c2019-08-29 11:26:22 -05006558 }
6559#else
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006560 switch (value.Type()) {
Syoyo Fujita150f2432019-07-25 19:22:44 +09006561 case REAL_TYPE:
Selmar Kokfa7022f2018-04-04 18:10:20 +02006562 obj = json(value.Get<double>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006563 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006564 case INT_TYPE:
6565 obj = json(value.Get<int>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006566 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006567 case BOOL_TYPE:
6568 obj = json(value.Get<bool>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006569 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006570 case STRING_TYPE:
6571 obj = json(value.Get<std::string>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006572 break;
6573 case ARRAY_TYPE: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006574 for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
6575 Value elementValue = value.Get(int(i));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006576 json elementJson;
6577 if (ValueToJson(value.Get(int(i)), &elementJson))
Selmar Kokfa7022f2018-04-04 18:10:20 +02006578 obj.push_back(elementJson);
6579 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006580 break;
6581 }
Selmar Kokfa7022f2018-04-04 18:10:20 +02006582 case BINARY_TYPE:
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006583 // TODO
6584 // obj = json(value.Get<std::vector<unsigned char>>());
Selmar Kokfa7022f2018-04-04 18:10:20 +02006585 return false;
6586 break;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006587 case OBJECT_TYPE: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006588 Value::Object objMap = value.Get<Value::Object>();
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006589 for (auto &it : objMap) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006590 json elementJson;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006591 if (ValueToJson(it.second, &elementJson)) obj[it.first] = elementJson;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006592 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006593 break;
6594 }
6595 case NULL_TYPE:
6596 default:
6597 return false;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006598 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006599#endif
6600 if (ret) *ret = std::move(obj);
Selmar Kokfa7022f2018-04-04 18:10:20 +02006601 return true;
6602}
6603
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006604static void SerializeValue(const std::string &key, const Value &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006605 json &obj) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006606 json ret;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006607 if (ValueToJson(value, &ret)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006608 JsonAddMember(obj, key.c_str(), std::move(ret));
6609 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006610}
6611
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006612static void SerializeGltfBufferData(const std::vector<unsigned char> &data,
johan bowald30c53472018-03-30 11:49:36 +02006613 json &o) {
6614 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09006615 if (data.size() > 0) {
6616 std::string encodedData =
6617 base64_encode(&data[0], static_cast<unsigned int>(data.size()));
6618 SerializeStringProperty("uri", header + encodedData, o);
6619 } else {
6620 // Issue #229
6621 // size 0 is allowd. Just emit mime header.
6622 SerializeStringProperty("uri", header, o);
6623 }
johan bowald30c53472018-03-30 11:49:36 +02006624}
6625
Selmar Koke4677492018-10-25 16:45:49 +02006626static bool SerializeGltfBufferData(const std::vector<unsigned char> &data,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006627 const std::string &binFilename) {
Harokyangfb256602019-10-30 16:13:52 +08006628#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09006629#if defined(__GLIBCXX__) // mingw
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09006630 int file_descriptor = _wopen(UTF8ToWchar(binFilename).c_str(),
6631 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
6632 __gnu_cxx::stdio_filebuf<char> wfile_buf(
6633 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09006634 std::ostream output(&wfile_buf);
6635 if (!wfile_buf.is_open()) return false;
6636#elif defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08006637 std::ofstream output(UTF8ToWchar(binFilename).c_str(), std::ofstream::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09006638 if (!output.is_open()) return false;
Harokyangfb256602019-10-30 16:13:52 +08006639#else
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006640 std::ofstream output(binFilename.c_str(), std::ofstream::binary);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006641 if (!output.is_open()) return false;
Syoyo Fujita45cac782019-11-09 20:42:55 +09006642#endif
6643#else
6644 std::ofstream output(binFilename.c_str(), std::ofstream::binary);
6645 if (!output.is_open()) return false;
6646#endif
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09006647 if (data.size() > 0) {
6648 output.write(reinterpret_cast<const char *>(&data[0]),
6649 std::streamsize(data.size()));
6650 } else {
6651 // Issue #229
6652 // size 0 will be still valid buffer data.
6653 // write empty file.
6654 }
Selmar Koke4677492018-10-25 16:45:49 +02006655 return true;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006656}
6657
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006658#if 0 // FIXME(syoyo): not used. will be removed in the future release.
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006659static void SerializeParameterMap(ParameterMap &param, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006660 for (ParameterMap::iterator paramIt = param.begin(); paramIt != param.end();
6661 ++paramIt) {
6662 if (paramIt->second.number_array.size()) {
6663 SerializeNumberArrayProperty<double>(paramIt->first,
6664 paramIt->second.number_array, o);
6665 } else if (paramIt->second.json_double_value.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006666 json json_double_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006667 for (std::map<std::string, double>::iterator it =
6668 paramIt->second.json_double_value.begin();
6669 it != paramIt->second.json_double_value.end(); ++it) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006670 if (it->first == "index") {
6671 json_double_value[it->first] = paramIt->second.TextureIndex();
6672 } else {
6673 json_double_value[it->first] = it->second;
6674 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006675 }
6676
Syoyo Fujita83675312017-12-02 21:14:13 +09006677 o[paramIt->first] = json_double_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006678 } else if (!paramIt->second.string_value.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006679 SerializeStringProperty(paramIt->first, paramIt->second.string_value, o);
Ben Buzbeef6af2242018-04-25 15:13:05 -07006680 } else if (paramIt->second.has_number_value) {
6681 o[paramIt->first] = paramIt->second.number_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006682 } else {
Syoyo Fujita83675312017-12-02 21:14:13 +09006683 o[paramIt->first] = paramIt->second.bool_value;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006684 }
6685 }
6686}
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006687#endif
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006688
Selmar Kok81b672b2019-10-18 16:08:44 +02006689static void SerializeExtensionMap(const ExtensionMap &extensions, json &o) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006690 if (!extensions.size()) return;
Selmar09d2ff12018-03-15 17:30:42 +01006691
6692 json extMap;
Selmar Kok81b672b2019-10-18 16:08:44 +02006693 for (ExtensionMap::const_iterator extIt = extensions.begin();
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006694 extIt != extensions.end(); ++extIt) {
Syoyo Fujita924d86e2018-10-08 21:15:12 +09006695 // Allow an empty object for extension(#97)
6696 json ret;
jrkooncecba5d6c2019-08-29 11:26:22 -05006697 bool isNull = true;
Syoyo Fujita924d86e2018-10-08 21:15:12 +09006698 if (ValueToJson(extIt->second, &ret)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006699 isNull = JsonIsNull(ret);
6700 JsonAddMember(extMap, extIt->first.c_str(), std::move(ret));
Selmar Kokdb7f4e42018-10-10 18:10:58 +02006701 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006702 if (isNull) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006703 if (!(extIt->first.empty())) { // name should not be empty, but for sure
6704 // create empty object so that an extension name is still included in
6705 // json.
jrkooncecba5d6c2019-08-29 11:26:22 -05006706 json empty;
jrkoonce06c30c42019-09-03 15:56:48 -05006707 JsonSetObject(empty);
jrkooncecba5d6c2019-08-29 11:26:22 -05006708 JsonAddMember(extMap, extIt->first.c_str(), std::move(empty));
Syoyo Fujita924d86e2018-10-08 21:15:12 +09006709 }
6710 }
Selmar09d2ff12018-03-15 17:30:42 +01006711 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006712 JsonAddMember(o, "extensions", std::move(extMap));
Selmar09d2ff12018-03-15 17:30:42 +01006713}
6714
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006715static void SerializeGltfAccessor(Accessor &accessor, json &o) {
Sanjeet Suhag5ecede72020-03-04 17:40:10 -05006716 if (accessor.bufferView >= 0)
6717 SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006718
Syoyo Fujita18f0e202020-04-29 19:16:35 +09006719 if (accessor.byteOffset != 0)
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006720 SerializeNumberProperty<int>("byteOffset", int(accessor.byteOffset), o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006721
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006722 SerializeNumberProperty<int>("componentType", accessor.componentType, o);
6723 SerializeNumberProperty<size_t>("count", accessor.count, o);
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09006724
6725 if ((accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) ||
6726 (accessor.componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE)) {
6727 SerializeNumberArrayProperty<double>("min", accessor.minValues, o);
6728 SerializeNumberArrayProperty<double>("max", accessor.maxValues, o);
6729 } else {
6730 // Issue #301. Serialize as integer.
6731 // Assume int value is within [-2**31-1, 2**31-1]
6732 {
6733 std::vector<int> values;
6734 std::transform(accessor.minValues.begin(), accessor.minValues.end(),
6735 std::back_inserter(values),
6736 [](double v) { return static_cast<int>(v); });
6737
6738 SerializeNumberArrayProperty<int>("min", values, o);
6739 }
6740
6741 {
6742 std::vector<int> values;
6743 std::transform(accessor.maxValues.begin(), accessor.maxValues.end(),
6744 std::back_inserter(values),
6745 [](double v) { return static_cast<int>(v); });
6746
6747 SerializeNumberArrayProperty<int>("max", values, o);
6748 }
6749 }
6750
Eero Pajarre2e8a1152019-11-18 13:09:25 +02006751 if (accessor.normalized)
6752 SerializeValue("normalized", Value(accessor.normalized), o);
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006753 std::string type;
6754 switch (accessor.type) {
6755 case TINYGLTF_TYPE_SCALAR:
6756 type = "SCALAR";
6757 break;
6758 case TINYGLTF_TYPE_VEC2:
6759 type = "VEC2";
6760 break;
6761 case TINYGLTF_TYPE_VEC3:
6762 type = "VEC3";
6763 break;
6764 case TINYGLTF_TYPE_VEC4:
6765 type = "VEC4";
6766 break;
6767 case TINYGLTF_TYPE_MAT2:
6768 type = "MAT2";
6769 break;
6770 case TINYGLTF_TYPE_MAT3:
6771 type = "MAT3";
6772 break;
6773 case TINYGLTF_TYPE_MAT4:
6774 type = "MAT4";
6775 break;
6776 }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09006777
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006778 SerializeStringProperty("type", type, o);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09006779 if (!accessor.name.empty()) SerializeStringProperty("name", accessor.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006780
6781 if (accessor.extras.Type() != NULL_TYPE) {
6782 SerializeValue("extras", accessor.extras, o);
6783 }
feiy0b315432022-08-13 10:08:17 +08006784
6785 // sparse
6786 if (accessor.sparse.isSparse)
6787 {
6788 json sparse;
6789 SerializeNumberProperty<int>("count", accessor.sparse.count, sparse);
6790 {
6791 json indices;
6792 SerializeNumberProperty<int>("bufferView", accessor.sparse.indices.bufferView, indices);
6793 SerializeNumberProperty<int>("byteOffset", accessor.sparse.indices.byteOffset, indices);
6794 SerializeNumberProperty<int>("componentType", accessor.sparse.indices.componentType, indices);
6795 JsonAddMember(sparse, "indices", std::move(indices));
6796 }
6797 {
6798 json values;
6799 SerializeNumberProperty<int>("bufferView", accessor.sparse.values.bufferView, values);
feiy1a8814d2022-08-14 19:49:07 +08006800 SerializeNumberProperty<int>("byteOffset", accessor.sparse.values.byteOffset, values);
feiy0b315432022-08-13 10:08:17 +08006801 JsonAddMember(sparse, "values", std::move(values));
6802 }
6803 JsonAddMember(o, "sparse", std::move(sparse));
6804 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006805}
6806
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006807static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006808 SerializeNumberProperty("sampler", channel.sampler, o);
jrkooncecba5d6c2019-08-29 11:26:22 -05006809 {
6810 json target;
6811 SerializeNumberProperty("node", channel.target_node, target);
6812 SerializeStringProperty("path", channel.target_path, target);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006813
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09006814 SerializeExtensionMap(channel.target_extensions, target);
Selmar Kok973d9b32020-01-21 18:45:24 +01006815
jrkooncecba5d6c2019-08-29 11:26:22 -05006816 JsonAddMember(o, "target", std::move(target));
6817 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02006818
6819 if (channel.extras.Type() != NULL_TYPE) {
6820 SerializeValue("extras", channel.extras, o);
6821 }
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006822
Selmar Kok4e2988e2019-08-16 14:08:08 +02006823 SerializeExtensionMap(channel.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006824}
6825
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006826static void SerializeGltfAnimationSampler(AnimationSampler &sampler, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006827 SerializeNumberProperty("input", sampler.input, o);
6828 SerializeNumberProperty("output", sampler.output, o);
6829 SerializeStringProperty("interpolation", sampler.interpolation, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006830
6831 if (sampler.extras.Type() != NULL_TYPE) {
6832 SerializeValue("extras", sampler.extras, o);
6833 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006834}
6835
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006836static void SerializeGltfAnimation(Animation &animation, json &o) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09006837 if (!animation.name.empty())
6838 SerializeStringProperty("name", animation.name, o);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006839
jrkooncecba5d6c2019-08-29 11:26:22 -05006840 {
6841 json channels;
6842 JsonReserveArray(channels, animation.channels.size());
6843 for (unsigned int i = 0; i < animation.channels.size(); ++i) {
6844 json channel;
6845 AnimationChannel gltfChannel = animation.channels[i];
6846 SerializeGltfAnimationChannel(gltfChannel, channel);
6847 JsonPushBack(channels, std::move(channel));
6848 }
6849
6850 JsonAddMember(o, "channels", std::move(channels));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006851 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006852
jrkooncecba5d6c2019-08-29 11:26:22 -05006853 {
6854 json samplers;
Jacek1da4e5d2020-01-06 14:36:57 -06006855 JsonReserveArray(samplers, animation.samplers.size());
jrkooncecba5d6c2019-08-29 11:26:22 -05006856 for (unsigned int i = 0; i < animation.samplers.size(); ++i) {
6857 json sampler;
6858 AnimationSampler gltfSampler = animation.samplers[i];
6859 SerializeGltfAnimationSampler(gltfSampler, sampler);
6860 JsonPushBack(samplers, std::move(sampler));
6861 }
6862 JsonAddMember(o, "samplers", std::move(samplers));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006863 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006864
Jens Olssonb3af2f12018-06-04 10:17:49 +02006865 if (animation.extras.Type() != NULL_TYPE) {
6866 SerializeValue("extras", animation.extras, o);
6867 }
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006868
Selmar Kok4e2988e2019-08-16 14:08:08 +02006869 SerializeExtensionMap(animation.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006870}
6871
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006872static void SerializeGltfAsset(Asset &asset, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006873 if (!asset.generator.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006874 SerializeStringProperty("generator", asset.generator, o);
6875 }
6876
Christophe820ede82019-07-04 15:21:21 +09006877 if (!asset.copyright.empty()) {
6878 SerializeStringProperty("copyright", asset.copyright, o);
6879 }
6880
Syoyo Fujitab702de72021-03-02 19:08:29 +09006881 if (asset.version.empty()) {
6882 // Just in case
6883 // `version` must be defined
6884 asset.version = "2.0";
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006885 }
6886
Syoyo Fujitab702de72021-03-02 19:08:29 +09006887 // TODO(syoyo): Do we need to check if `version` is greater or equal to 2.0?
6888 SerializeStringProperty("version", asset.version, o);
6889
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006890 if (asset.extras.Keys().size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006891 SerializeValue("extras", asset.extras, o);
6892 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006893
Selmar09d2ff12018-03-15 17:30:42 +01006894 SerializeExtensionMap(asset.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006895}
6896
Syoyo Fujitac4166e42020-01-08 02:38:01 +09006897static void SerializeGltfBufferBin(Buffer &buffer, json &o,
6898 std::vector<unsigned char> &binBuffer) {
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02006899 SerializeNumberProperty("byteLength", buffer.data.size(), o);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09006900 binBuffer = buffer.data;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02006901
6902 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
6903
6904 if (buffer.extras.Type() != NULL_TYPE) {
6905 SerializeValue("extras", buffer.extras, o);
6906 }
6907}
6908
johan bowald30c53472018-03-30 11:49:36 +02006909static void SerializeGltfBuffer(Buffer &buffer, json &o) {
6910 SerializeNumberProperty("byteLength", buffer.data.size(), o);
6911 SerializeGltfBufferData(buffer.data, o);
6912
6913 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006914
6915 if (buffer.extras.Type() != NULL_TYPE) {
6916 SerializeValue("extras", buffer.extras, o);
6917 }
johan bowald30c53472018-03-30 11:49:36 +02006918}
6919
Selmar Koke4677492018-10-25 16:45:49 +02006920static bool SerializeGltfBuffer(Buffer &buffer, json &o,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006921 const std::string &binFilename,
6922 const std::string &binBaseFilename) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006923 if (!SerializeGltfBufferData(buffer.data, binFilename)) return false;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006924 SerializeNumberProperty("byteLength", buffer.data.size(), o);
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006925 SerializeStringProperty("uri", binBaseFilename, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006926
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006927 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006928
6929 if (buffer.extras.Type() != NULL_TYPE) {
6930 SerializeValue("extras", buffer.extras, o);
6931 }
Selmar Koke4677492018-10-25 16:45:49 +02006932 return true;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006933}
6934
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006935static void SerializeGltfBufferView(BufferView &bufferView, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006936 SerializeNumberProperty("buffer", bufferView.buffer, o);
6937 SerializeNumberProperty<size_t>("byteLength", bufferView.byteLength, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006938
Johan Bowaldfaa27222018-03-28 14:44:45 +02006939 // byteStride is optional, minimum allowed is 4
6940 if (bufferView.byteStride >= 4) {
6941 SerializeNumberProperty<size_t>("byteStride", bufferView.byteStride, o);
6942 }
6943 // byteOffset is optional, default is 0
6944 if (bufferView.byteOffset > 0) {
6945 SerializeNumberProperty<size_t>("byteOffset", bufferView.byteOffset, o);
6946 }
6947 // Target is optional, check if it contains a valid value
6948 if (bufferView.target == TINYGLTF_TARGET_ARRAY_BUFFER ||
6949 bufferView.target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER) {
6950 SerializeNumberProperty("target", bufferView.target, o);
6951 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006952 if (bufferView.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006953 SerializeStringProperty("name", bufferView.name, o);
6954 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02006955
6956 if (bufferView.extras.Type() != NULL_TYPE) {
6957 SerializeValue("extras", bufferView.extras, o);
6958 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006959}
6960
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006961static void SerializeGltfImage(Image &image, json &o) {
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02006962 // if uri empty, the mimeType and bufferview should be set
rainliang00062be8d02019-04-29 09:54:27 +08006963 if (image.uri.empty()) {
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02006964 SerializeStringProperty("mimeType", image.mimeType, o);
6965 SerializeNumberProperty<int>("bufferView", image.bufferView, o);
6966 } else {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09006967 // TODO(syoyo): dlib::urilencode?
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02006968 SerializeStringProperty("uri", image.uri, o);
6969 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006970
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006971 if (image.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006972 SerializeStringProperty("name", image.name, o);
6973 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02006974
6975 if (image.extras.Type() != NULL_TYPE) {
6976 SerializeValue("extras", image.extras, o);
6977 }
Syoyo Fujita3e53feb2018-09-02 15:36:17 +09006978
6979 SerializeExtensionMap(image.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006980}
6981
Syoyo Fujita046400b2019-07-24 19:26:48 +09006982static void SerializeGltfTextureInfo(TextureInfo &texinfo, json &o) {
6983 SerializeNumberProperty("index", texinfo.index, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006984
Syoyo Fujita046400b2019-07-24 19:26:48 +09006985 if (texinfo.texCoord != 0) {
6986 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
6987 }
6988
6989 if (texinfo.extras.Type() != NULL_TYPE) {
6990 SerializeValue("extras", texinfo.extras, o);
6991 }
6992
6993 SerializeExtensionMap(texinfo.extensions, o);
6994}
6995
6996static void SerializeGltfNormalTextureInfo(NormalTextureInfo &texinfo,
6997 json &o) {
6998 SerializeNumberProperty("index", texinfo.index, o);
6999
7000 if (texinfo.texCoord != 0) {
7001 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
7002 }
7003
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007004 if (!TINYGLTF_DOUBLE_EQUAL(texinfo.scale, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007005 SerializeNumberProperty("scale", texinfo.scale, o);
7006 }
7007
7008 if (texinfo.extras.Type() != NULL_TYPE) {
7009 SerializeValue("extras", texinfo.extras, o);
7010 }
7011
7012 SerializeExtensionMap(texinfo.extensions, o);
7013}
7014
7015static void SerializeGltfOcclusionTextureInfo(OcclusionTextureInfo &texinfo,
7016 json &o) {
7017 SerializeNumberProperty("index", texinfo.index, o);
7018
7019 if (texinfo.texCoord != 0) {
7020 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
7021 }
7022
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007023 if (!TINYGLTF_DOUBLE_EQUAL(texinfo.strength, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007024 SerializeNumberProperty("strength", texinfo.strength, o);
7025 }
7026
7027 if (texinfo.extras.Type() != NULL_TYPE) {
7028 SerializeValue("extras", texinfo.extras, o);
7029 }
7030
7031 SerializeExtensionMap(texinfo.extensions, o);
7032}
7033
7034static void SerializeGltfPbrMetallicRoughness(PbrMetallicRoughness &pbr,
7035 json &o) {
7036 std::vector<double> default_baseColorFactor = {1.0, 1.0, 1.0, 1.0};
7037 if (!Equals(pbr.baseColorFactor, default_baseColorFactor)) {
7038 SerializeNumberArrayProperty<double>("baseColorFactor", pbr.baseColorFactor,
7039 o);
7040 }
7041
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007042 if (!TINYGLTF_DOUBLE_EQUAL(pbr.metallicFactor, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007043 SerializeNumberProperty("metallicFactor", pbr.metallicFactor, o);
7044 }
7045
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02007046 if (!TINYGLTF_DOUBLE_EQUAL(pbr.roughnessFactor, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007047 SerializeNumberProperty("roughnessFactor", pbr.roughnessFactor, o);
7048 }
7049
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007050 if (pbr.baseColorTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007051 json texinfo;
7052 SerializeGltfTextureInfo(pbr.baseColorTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05007053 JsonAddMember(o, "baseColorTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007054 }
7055
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007056 if (pbr.metallicRoughnessTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007057 json texinfo;
7058 SerializeGltfTextureInfo(pbr.metallicRoughnessTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05007059 JsonAddMember(o, "metallicRoughnessTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007060 }
7061
7062 SerializeExtensionMap(pbr.extensions, o);
7063
7064 if (pbr.extras.Type() != NULL_TYPE) {
7065 SerializeValue("extras", pbr.extras, o);
7066 }
7067}
7068
7069static void SerializeGltfMaterial(Material &material, json &o) {
7070 if (material.name.size()) {
7071 SerializeStringProperty("name", material.name, o);
7072 }
7073
7074 // QUESTION(syoyo): Write material parameters regardless of its default value?
7075
7076 if (!TINYGLTF_DOUBLE_EQUAL(material.alphaCutoff, 0.5)) {
7077 SerializeNumberProperty("alphaCutoff", material.alphaCutoff, o);
7078 }
7079
Patrick Härtld9a468b2019-08-14 14:14:07 +02007080 if (material.alphaMode.compare("OPAQUE") != 0) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007081 SerializeStringProperty("alphaMode", material.alphaMode, o);
7082 }
7083
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007084 if (material.doubleSided != false)
7085 JsonAddMember(o, "doubleSided", json(material.doubleSided));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007086
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007087 if (material.normalTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007088 json texinfo;
7089 SerializeGltfNormalTextureInfo(material.normalTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05007090 JsonAddMember(o, "normalTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007091 }
7092
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007093 if (material.occlusionTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007094 json texinfo;
7095 SerializeGltfOcclusionTextureInfo(material.occlusionTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05007096 JsonAddMember(o, "occlusionTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007097 }
7098
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09007099 if (material.emissiveTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09007100 json texinfo;
7101 SerializeGltfTextureInfo(material.emissiveTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05007102 JsonAddMember(o, "emissiveTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09007103 }
7104
7105 std::vector<double> default_emissiveFactor = {0.0, 0.0, 0.0};
7106 if (!Equals(material.emissiveFactor, default_emissiveFactor)) {
7107 SerializeNumberArrayProperty<double>("emissiveFactor",
7108 material.emissiveFactor, o);
7109 }
7110
7111 {
7112 json pbrMetallicRoughness;
7113 SerializeGltfPbrMetallicRoughness(material.pbrMetallicRoughness,
7114 pbrMetallicRoughness);
Syoyo Fujita7e009042019-09-13 15:32:22 +09007115 // Issue 204
7116 // Do not serialize `pbrMetallicRoughness` if pbrMetallicRoughness has all
7117 // default values(json is null). Otherwise it will serialize to
7118 // `pbrMetallicRoughness : null`, which cannot be read by other glTF
7119 // importers(and validators).
7120 //
7121 if (!JsonIsNull(pbrMetallicRoughness)) {
7122 JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness));
7123 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09007124 }
7125
7126#if 0 // legacy way. just for the record.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007127 if (material.values.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007128 json pbrMetallicRoughness;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007129 SerializeParameterMap(material.values, pbrMetallicRoughness);
jrkooncecba5d6c2019-08-29 11:26:22 -05007130 JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007131 }
7132
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007133 SerializeParameterMap(material.additionalValues, o);
Syoyo Fujita046400b2019-07-24 19:26:48 +09007134#else
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007135
Syoyo Fujita046400b2019-07-24 19:26:48 +09007136#endif
7137
7138 SerializeExtensionMap(material.extensions, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007139
7140 if (material.extras.Type() != NULL_TYPE) {
7141 SerializeValue("extras", material.extras, o);
7142 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007143}
7144
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007145static void SerializeGltfMesh(Mesh &mesh, json &o) {
Syoyo Fujita83675312017-12-02 21:14:13 +09007146 json primitives;
jrkooncecba5d6c2019-08-29 11:26:22 -05007147 JsonReserveArray(primitives, mesh.primitives.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007148 for (unsigned int i = 0; i < mesh.primitives.size(); ++i) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007149 json primitive;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007150 const Primitive &gltfPrimitive = mesh.primitives[i]; // don't make a copy
jrkooncecba5d6c2019-08-29 11:26:22 -05007151 {
7152 json attributes;
7153 for (auto attrIt = gltfPrimitive.attributes.begin();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007154 attrIt != gltfPrimitive.attributes.end(); ++attrIt) {
jrkooncecba5d6c2019-08-29 11:26:22 -05007155 SerializeNumberProperty<int>(attrIt->first, attrIt->second, attributes);
7156 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007157
jrkooncecba5d6c2019-08-29 11:26:22 -05007158 JsonAddMember(primitive, "attributes", std::move(attributes));
7159 }
Johan Bowaldfaa27222018-03-28 14:44:45 +02007160
7161 // Indicies is optional
7162 if (gltfPrimitive.indices > -1) {
7163 SerializeNumberProperty<int>("indices", gltfPrimitive.indices, primitive);
7164 }
7165 // Material is optional
7166 if (gltfPrimitive.material > -1) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09007167 SerializeNumberProperty<int>("material", gltfPrimitive.material,
7168 primitive);
Johan Bowaldfaa27222018-03-28 14:44:45 +02007169 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007170 SerializeNumberProperty<int>("mode", gltfPrimitive.mode, primitive);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007171
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007172 // Morph targets
7173 if (gltfPrimitive.targets.size()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09007174 json targets;
jrkooncecba5d6c2019-08-29 11:26:22 -05007175 JsonReserveArray(targets, gltfPrimitive.targets.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007176 for (unsigned int k = 0; k < gltfPrimitive.targets.size(); ++k) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007177 json targetAttributes;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007178 std::map<std::string, int> targetData = gltfPrimitive.targets[k];
7179 for (std::map<std::string, int>::iterator attrIt = targetData.begin();
7180 attrIt != targetData.end(); ++attrIt) {
7181 SerializeNumberProperty<int>(attrIt->first, attrIt->second,
7182 targetAttributes);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007183 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007184 JsonPushBack(targets, std::move(targetAttributes));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007185 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007186 JsonAddMember(primitive, "targets", std::move(targets));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007187 }
7188
zhaozhiquan3eb65e22019-12-18 11:28:57 +08007189 SerializeExtensionMap(gltfPrimitive.extensions, primitive);
Selmar Kok81b672b2019-10-18 16:08:44 +02007190
Jens Olssonb3af2f12018-06-04 10:17:49 +02007191 if (gltfPrimitive.extras.Type() != NULL_TYPE) {
7192 SerializeValue("extras", gltfPrimitive.extras, primitive);
7193 }
7194
jrkooncecba5d6c2019-08-29 11:26:22 -05007195 JsonPushBack(primitives, std::move(primitive));
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007196 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007197
jrkooncecba5d6c2019-08-29 11:26:22 -05007198 JsonAddMember(o, "primitives", std::move(primitives));
7199
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007200 if (mesh.weights.size()) {
7201 SerializeNumberArrayProperty<double>("weights", mesh.weights, o);
7202 }
7203
7204 if (mesh.name.size()) {
7205 SerializeStringProperty("name", mesh.name, o);
7206 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007207
Selmar Kok81b672b2019-10-18 16:08:44 +02007208 SerializeExtensionMap(mesh.extensions, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007209 if (mesh.extras.Type() != NULL_TYPE) {
7210 SerializeValue("extras", mesh.extras, o);
7211 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007212}
7213
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007214static void SerializeSpotLight(SpotLight &spot, json &o) {
Johan Bowald52936a02019-07-17 09:06:45 +02007215 SerializeNumberProperty("innerConeAngle", spot.innerConeAngle, o);
7216 SerializeNumberProperty("outerConeAngle", spot.outerConeAngle, o);
7217 SerializeExtensionMap(spot.extensions, o);
Selmar Kok81b672b2019-10-18 16:08:44 +02007218 if (spot.extras.Type() != NULL_TYPE) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09007219 SerializeValue("extras", spot.extras, o);
Selmar Kok81b672b2019-10-18 16:08:44 +02007220 }
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007221}
7222
Syoyo Fujita83675312017-12-02 21:14:13 +09007223static void SerializeGltfLight(Light &light, json &o) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09007224 if (!light.name.empty()) SerializeStringProperty("name", light.name, o);
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007225 SerializeNumberProperty("intensity", light.intensity, o);
Syoyo Fujita3dad3032020-05-13 21:22:23 +09007226 if (light.range > 0.0) {
7227 SerializeNumberProperty("range", light.range, o);
7228 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01007229 SerializeNumberArrayProperty("color", light.color, o);
7230 SerializeStringProperty("type", light.type, o);
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007231 if (light.type == "spot") {
7232 json spot;
7233 SerializeSpotLight(light.spot, spot);
jrkooncecba5d6c2019-08-29 11:26:22 -05007234 JsonAddMember(o, "spot", std::move(spot));
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007235 }
Selmar Kok81b672b2019-10-18 16:08:44 +02007236 SerializeExtensionMap(light.extensions, o);
7237 if (light.extras.Type() != NULL_TYPE) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09007238 SerializeValue("extras", light.extras, o);
Selmar Kok81b672b2019-10-18 16:08:44 +02007239 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01007240}
7241
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007242static void SerializeGltfNode(Node &node, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007243 if (node.translation.size() > 0) {
7244 SerializeNumberArrayProperty<double>("translation", node.translation, o);
7245 }
7246 if (node.rotation.size() > 0) {
7247 SerializeNumberArrayProperty<double>("rotation", node.rotation, o);
7248 }
7249 if (node.scale.size() > 0) {
7250 SerializeNumberArrayProperty<double>("scale", node.scale, o);
7251 }
7252 if (node.matrix.size() > 0) {
7253 SerializeNumberArrayProperty<double>("matrix", node.matrix, o);
7254 }
7255 if (node.mesh != -1) {
7256 SerializeNumberProperty<int>("mesh", node.mesh, o);
7257 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007258
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007259 if (node.skin != -1) {
7260 SerializeNumberProperty<int>("skin", node.skin, o);
7261 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007262
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007263 if (node.camera != -1) {
7264 SerializeNumberProperty<int>("camera", node.camera, o);
7265 }
7266
Benjamin Schmithüsen74c3c102019-08-16 14:24:26 +02007267 if (node.weights.size() > 0) {
7268 SerializeNumberArrayProperty<double>("weights", node.weights, o);
7269 }
7270
Jens Olssona9718662018-05-24 15:48:49 +02007271 if (node.extras.Type() != NULL_TYPE) {
Jens Olssonb96f6962018-05-24 15:29:54 +02007272 SerializeValue("extras", node.extras, o);
7273 }
7274
Selmar09d2ff12018-03-15 17:30:42 +01007275 SerializeExtensionMap(node.extensions, o);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09007276 if (!node.name.empty()) SerializeStringProperty("name", node.name, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007277 SerializeNumberArrayProperty<int>("children", node.children, o);
7278}
7279
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007280static void SerializeGltfSampler(Sampler &sampler, json &o) {
Syoyo Fujitaee179b22019-08-16 13:11:30 +09007281 if (sampler.magFilter != -1) {
7282 SerializeNumberProperty("magFilter", sampler.magFilter, o);
7283 }
7284 if (sampler.minFilter != -1) {
7285 SerializeNumberProperty("minFilter", sampler.minFilter, o);
7286 }
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09007287 // SerializeNumberProperty("wrapR", sampler.wrapR, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007288 SerializeNumberProperty("wrapS", sampler.wrapS, o);
7289 SerializeNumberProperty("wrapT", sampler.wrapT, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007290
7291 if (sampler.extras.Type() != NULL_TYPE) {
7292 SerializeValue("extras", sampler.extras, o);
7293 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007294}
7295
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007296static void SerializeGltfOrthographicCamera(const OrthographicCamera &camera,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007297 json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007298 SerializeNumberProperty("zfar", camera.zfar, o);
7299 SerializeNumberProperty("znear", camera.znear, o);
7300 SerializeNumberProperty("xmag", camera.xmag, o);
7301 SerializeNumberProperty("ymag", camera.ymag, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007302
7303 if (camera.extras.Type() != NULL_TYPE) {
7304 SerializeValue("extras", camera.extras, o);
7305 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007306}
7307
7308static void SerializeGltfPerspectiveCamera(const PerspectiveCamera &camera,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007309 json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007310 SerializeNumberProperty("zfar", camera.zfar, o);
7311 SerializeNumberProperty("znear", camera.znear, o);
7312 if (camera.aspectRatio > 0) {
7313 SerializeNumberProperty("aspectRatio", camera.aspectRatio, o);
7314 }
7315
7316 if (camera.yfov > 0) {
7317 SerializeNumberProperty("yfov", camera.yfov, o);
7318 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007319
7320 if (camera.extras.Type() != NULL_TYPE) {
7321 SerializeValue("extras", camera.extras, o);
7322 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007323}
7324
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007325static void SerializeGltfCamera(const Camera &camera, json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007326 SerializeStringProperty("type", camera.type, o);
7327 if (!camera.name.empty()) {
Selmar Kokee3d0662018-10-08 16:20:43 +02007328 SerializeStringProperty("name", camera.name, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007329 }
7330
7331 if (camera.type.compare("orthographic") == 0) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007332 json orthographic;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007333 SerializeGltfOrthographicCamera(camera.orthographic, orthographic);
jrkooncecba5d6c2019-08-29 11:26:22 -05007334 JsonAddMember(o, "orthographic", std::move(orthographic));
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007335 } else if (camera.type.compare("perspective") == 0) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007336 json perspective;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007337 SerializeGltfPerspectiveCamera(camera.perspective, perspective);
jrkooncecba5d6c2019-08-29 11:26:22 -05007338 JsonAddMember(o, "perspective", std::move(perspective));
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007339 } else {
7340 // ???
7341 }
Syoyofe77cc52020-05-09 02:41:07 +09007342
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007343 if (camera.extras.Type() != NULL_TYPE) {
Syoyofe77cc52020-05-09 02:41:07 +09007344 SerializeValue("extras", camera.extras, o);
7345 }
7346 SerializeExtensionMap(camera.extensions, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007347}
7348
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007349static void SerializeGltfScene(Scene &scene, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007350 SerializeNumberArrayProperty<int>("nodes", scene.nodes, o);
7351
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007352 if (scene.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007353 SerializeStringProperty("name", scene.name, o);
7354 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007355 if (scene.extras.Type() != NULL_TYPE) {
Selmar09d2ff12018-03-15 17:30:42 +01007356 SerializeValue("extras", scene.extras, o);
7357 }
7358 SerializeExtensionMap(scene.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007359}
7360
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007361static void SerializeGltfSkin(Skin &skin, json &o) {
Syoyo Fujitad07b9762021-04-28 19:15:16 +09007362 // required
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007363 SerializeNumberArrayProperty<int>("joints", skin.joints, o);
Syoyo Fujitad07b9762021-04-28 19:15:16 +09007364
7365 if (skin.inverseBindMatrices >= 0) {
7366 SerializeNumberProperty("inverseBindMatrices", skin.inverseBindMatrices, o);
7367 }
7368
7369 if (skin.skeleton >= 0) {
7370 SerializeNumberProperty("skeleton", skin.skeleton, o);
7371 }
7372
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007373 if (skin.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007374 SerializeStringProperty("name", skin.name, o);
7375 }
7376}
7377
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007378static void SerializeGltfTexture(Texture &texture, json &o) {
johan bowaldb97d34c2018-04-02 07:29:29 +02007379 if (texture.sampler > -1) {
johan bowald642a3432018-04-01 12:37:18 +02007380 SerializeNumberProperty("sampler", texture.sampler, o);
7381 }
Selmar Kok8eb39042018-10-05 14:29:35 +02007382 if (texture.source > -1) {
7383 SerializeNumberProperty("source", texture.source, o);
7384 }
Christophe820ede82019-07-04 15:21:21 +09007385 if (texture.name.size()) {
7386 SerializeStringProperty("name", texture.name, o);
7387 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007388 if (texture.extras.Type() != NULL_TYPE) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007389 SerializeValue("extras", texture.extras, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007390 }
Selmar Kok9eae1102018-04-04 18:10:37 +02007391 SerializeExtensionMap(texture.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007392}
7393
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007394///
7395/// Serialize all properties except buffers and images.
7396///
7397static void SerializeGltfModel(Model *model, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007398 // ACCESSORS
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007399 if (model->accessors.size()) {
7400 json accessors;
7401 JsonReserveArray(accessors, model->accessors.size());
7402 for (unsigned int i = 0; i < model->accessors.size(); ++i) {
7403 json accessor;
7404 SerializeGltfAccessor(model->accessors[i], accessor);
7405 JsonPushBack(accessors, std::move(accessor));
7406 }
7407 JsonAddMember(o, "accessors", std::move(accessors));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007408 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007409
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007410 // ANIMATIONS
7411 if (model->animations.size()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09007412 json animations;
jrkooncecba5d6c2019-08-29 11:26:22 -05007413 JsonReserveArray(animations, model->animations.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007414 for (unsigned int i = 0; i < model->animations.size(); ++i) {
7415 if (model->animations[i].channels.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007416 json animation;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007417 SerializeGltfAnimation(model->animations[i], animation);
jrkooncecba5d6c2019-08-29 11:26:22 -05007418 JsonPushBack(animations, std::move(animation));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007419 }
7420 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007421
jrkooncecba5d6c2019-08-29 11:26:22 -05007422 JsonAddMember(o, "animations", std::move(animations));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007423 }
7424
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007425 // ASSET
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007426 json asset;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007427 SerializeGltfAsset(model->asset, asset);
jrkooncecba5d6c2019-08-29 11:26:22 -05007428 JsonAddMember(o, "asset", std::move(asset));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007429
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007430 // BUFFERVIEWS
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007431 if (model->bufferViews.size()) {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007432 json bufferViews;
7433 JsonReserveArray(bufferViews, model->bufferViews.size());
7434 for (unsigned int i = 0; i < model->bufferViews.size(); ++i) {
7435 json bufferView;
7436 SerializeGltfBufferView(model->bufferViews[i], bufferView);
7437 JsonPushBack(bufferViews, std::move(bufferView));
7438 }
7439 JsonAddMember(o, "bufferViews", std::move(bufferViews));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007440 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007441
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007442 // Extensions required
7443 if (model->extensionsRequired.size()) {
7444 SerializeStringArrayProperty("extensionsRequired",
7445 model->extensionsRequired, o);
7446 }
7447
7448 // MATERIALS
7449 if (model->materials.size()) {
7450 json materials;
jrkooncecba5d6c2019-08-29 11:26:22 -05007451 JsonReserveArray(materials, model->materials.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007452 for (unsigned int i = 0; i < model->materials.size(); ++i) {
7453 json material;
7454 SerializeGltfMaterial(model->materials[i], material);
Syoyo Fujita68adc4b2020-10-22 22:27:12 +09007455
7456 if (JsonIsNull(material)) {
7457 // Issue 294.
7458 // `material` does not have any required parameters
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09007459 // so the result may be null(unmodified) when all material parameters
7460 // have default value.
Syoyo Fujita68adc4b2020-10-22 22:27:12 +09007461 //
7462 // null is not allowed thus we create an empty JSON object.
7463 JsonSetObject(material);
7464 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007465 JsonPushBack(materials, std::move(material));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007466 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007467 JsonAddMember(o, "materials", std::move(materials));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007468 }
7469
7470 // MESHES
7471 if (model->meshes.size()) {
7472 json meshes;
jrkooncecba5d6c2019-08-29 11:26:22 -05007473 JsonReserveArray(meshes, model->meshes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007474 for (unsigned int i = 0; i < model->meshes.size(); ++i) {
7475 json mesh;
7476 SerializeGltfMesh(model->meshes[i], mesh);
jrkooncecba5d6c2019-08-29 11:26:22 -05007477 JsonPushBack(meshes, std::move(mesh));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007478 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007479 JsonAddMember(o, "meshes", std::move(meshes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007480 }
7481
7482 // NODES
7483 if (model->nodes.size()) {
7484 json nodes;
jrkooncecba5d6c2019-08-29 11:26:22 -05007485 JsonReserveArray(nodes, model->nodes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007486 for (unsigned int i = 0; i < model->nodes.size(); ++i) {
7487 json node;
7488 SerializeGltfNode(model->nodes[i], node);
jrkooncecba5d6c2019-08-29 11:26:22 -05007489 JsonPushBack(nodes, std::move(node));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007490 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007491 JsonAddMember(o, "nodes", std::move(nodes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007492 }
7493
7494 // SCENE
7495 if (model->defaultScene > -1) {
7496 SerializeNumberProperty<int>("scene", model->defaultScene, o);
7497 }
7498
7499 // SCENES
7500 if (model->scenes.size()) {
7501 json scenes;
jrkooncecba5d6c2019-08-29 11:26:22 -05007502 JsonReserveArray(scenes, model->scenes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007503 for (unsigned int i = 0; i < model->scenes.size(); ++i) {
7504 json currentScene;
7505 SerializeGltfScene(model->scenes[i], currentScene);
jrkooncecba5d6c2019-08-29 11:26:22 -05007506 JsonPushBack(scenes, std::move(currentScene));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007507 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007508 JsonAddMember(o, "scenes", std::move(scenes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007509 }
7510
7511 // SKINS
7512 if (model->skins.size()) {
7513 json skins;
jrkooncecba5d6c2019-08-29 11:26:22 -05007514 JsonReserveArray(skins, model->skins.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007515 for (unsigned int i = 0; i < model->skins.size(); ++i) {
7516 json skin;
7517 SerializeGltfSkin(model->skins[i], skin);
jrkooncecba5d6c2019-08-29 11:26:22 -05007518 JsonPushBack(skins, std::move(skin));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007519 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007520 JsonAddMember(o, "skins", std::move(skins));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007521 }
7522
7523 // TEXTURES
7524 if (model->textures.size()) {
7525 json textures;
jrkooncecba5d6c2019-08-29 11:26:22 -05007526 JsonReserveArray(textures, model->textures.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007527 for (unsigned int i = 0; i < model->textures.size(); ++i) {
7528 json texture;
7529 SerializeGltfTexture(model->textures[i], texture);
jrkooncecba5d6c2019-08-29 11:26:22 -05007530 JsonPushBack(textures, std::move(texture));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007531 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007532 JsonAddMember(o, "textures", std::move(textures));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007533 }
7534
7535 // SAMPLERS
7536 if (model->samplers.size()) {
7537 json samplers;
jrkooncecba5d6c2019-08-29 11:26:22 -05007538 JsonReserveArray(samplers, model->samplers.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007539 for (unsigned int i = 0; i < model->samplers.size(); ++i) {
7540 json sampler;
7541 SerializeGltfSampler(model->samplers[i], sampler);
jrkooncecba5d6c2019-08-29 11:26:22 -05007542 JsonPushBack(samplers, std::move(sampler));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007543 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007544 JsonAddMember(o, "samplers", std::move(samplers));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007545 }
7546
7547 // CAMERAS
7548 if (model->cameras.size()) {
7549 json cameras;
jrkooncecba5d6c2019-08-29 11:26:22 -05007550 JsonReserveArray(cameras, model->cameras.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007551 for (unsigned int i = 0; i < model->cameras.size(); ++i) {
7552 json camera;
7553 SerializeGltfCamera(model->cameras[i], camera);
jrkooncecba5d6c2019-08-29 11:26:22 -05007554 JsonPushBack(cameras, std::move(camera));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007555 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007556 JsonAddMember(o, "cameras", std::move(cameras));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007557 }
7558
7559 // EXTENSIONS
7560 SerializeExtensionMap(model->extensions, o);
7561
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007562 auto extensionsUsed = model->extensionsUsed;
7563
7564 // LIGHTS as KHR_lights_punctual
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007565 if (model->lights.size()) {
7566 json lights;
jrkooncecba5d6c2019-08-29 11:26:22 -05007567 JsonReserveArray(lights, model->lights.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007568 for (unsigned int i = 0; i < model->lights.size(); ++i) {
7569 json light;
7570 SerializeGltfLight(model->lights[i], light);
jrkooncecba5d6c2019-08-29 11:26:22 -05007571 JsonPushBack(lights, std::move(light));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007572 }
7573 json khr_lights_cmn;
jrkooncecba5d6c2019-08-29 11:26:22 -05007574 JsonAddMember(khr_lights_cmn, "lights", std::move(lights));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007575 json ext_j;
7576
jrkooncecba5d6c2019-08-29 11:26:22 -05007577 {
7578 json_const_iterator it;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007579 if (FindMember(o, "extensions", it)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05007580 JsonAssign(ext_j, GetValue(it));
7581 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007582 }
7583
jrkooncecba5d6c2019-08-29 11:26:22 -05007584 JsonAddMember(ext_j, "KHR_lights_punctual", std::move(khr_lights_cmn));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007585
jrkooncecba5d6c2019-08-29 11:26:22 -05007586 JsonAddMember(o, "extensions", std::move(ext_j));
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007587
7588 // Also add "KHR_lights_punctual" to `extensionsUsed`
7589 {
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04007590 auto has_khr_lights_punctual =
7591 std::find_if(extensionsUsed.begin(), extensionsUsed.end(),
7592 [](const std::string &s) {
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08007593 return (s.compare("KHR_lights_punctual") == 0);
7594 });
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007595
7596 if (has_khr_lights_punctual == extensionsUsed.end()) {
7597 extensionsUsed.push_back("KHR_lights_punctual");
7598 }
7599 }
7600 }
7601
7602 // Extensions used
Selmar1208f052021-02-01 19:24:41 +01007603 if (extensionsUsed.size()) {
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007604 SerializeStringArrayProperty("extensionsUsed", extensionsUsed, o);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007605 }
7606
7607 // EXTRAS
7608 if (model->extras.Type() != NULL_TYPE) {
7609 SerializeValue("extras", model->extras, o);
7610 }
7611}
7612
Johan Bowald52936a02019-07-17 09:06:45 +02007613static bool WriteGltfStream(std::ostream &stream, const std::string &content) {
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007614 stream << content << std::endl;
7615 return true;
7616}
7617
7618static bool WriteGltfFile(const std::string &output,
7619 const std::string &content) {
Harokyangfb256602019-10-30 16:13:52 +08007620#ifdef _WIN32
Syoyo Fujita45cac782019-11-09 20:42:55 +09007621#if defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08007622 std::ofstream gltfFile(UTF8ToWchar(output).c_str());
Syoyo Fujita45cac782019-11-09 20:42:55 +09007623#elif defined(__GLIBCXX__)
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007624 int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
7625 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
7626 __gnu_cxx::stdio_filebuf<char> wfile_buf(
7627 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09007628 std::ostream gltfFile(&wfile_buf);
7629 if (!wfile_buf.is_open()) return false;
Harokyangfb256602019-10-30 16:13:52 +08007630#else
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007631 std::ofstream gltfFile(output.c_str());
7632 if (!gltfFile.is_open()) return false;
Syoyo Fujita45cac782019-11-09 20:42:55 +09007633#endif
7634#else
7635 std::ofstream gltfFile(output.c_str());
7636 if (!gltfFile.is_open()) return false;
7637#endif
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007638 return WriteGltfStream(gltfFile, content);
7639}
7640
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007641static bool WriteBinaryGltfStream(std::ostream &stream,
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007642 const std::string &content,
7643 const std::vector<unsigned char> &binBuffer) {
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007644 const std::string header = "glTF";
7645 const int version = 2;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007646
Alexander Wood190382a2021-10-08 12:19:13 -04007647 const uint32_t content_size = uint32_t(content.size());
7648 const uint32_t binBuffer_size = uint32_t(binBuffer.size());
7649 // determine number of padding bytes required to ensure 4 byte alignment
Syoyo Fujita52ff00a2022-08-16 20:08:45 +09007650 const uint32_t content_padding_size =
7651 content_size % 4 == 0 ? 0 : 4 - content_size % 4;
7652 const uint32_t bin_padding_size =
7653 binBuffer_size % 4 == 0 ? 0 : 4 - binBuffer_size % 4;
Syoyo Fujita1d205202019-11-16 17:00:17 +09007654
7655 // 12 bytes for header, JSON content length, 8 bytes for JSON chunk info.
Alexander Wood190382a2021-10-08 12:19:13 -04007656 // Chunk data must be located at 4-byte boundary, which may require padding
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007657 const uint32_t length =
Alexander Wood9e3d1f62021-10-08 16:57:56 -04007658 12 + 8 + content_size + content_padding_size +
Alexander Wood13803652021-10-08 20:13:07 -04007659 (binBuffer_size ? (8 + binBuffer_size + bin_padding_size) : 0);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007660
Syoyo Fujitacea69e32019-08-20 17:10:30 +09007661 stream.write(header.c_str(), std::streamsize(header.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007662 stream.write(reinterpret_cast<const char *>(&version), sizeof(version));
7663 stream.write(reinterpret_cast<const char *>(&length), sizeof(length));
7664
7665 // JSON chunk info, then JSON data
Alexander Wood9e3d1f62021-10-08 16:57:56 -04007666 const uint32_t model_length = uint32_t(content.size()) + content_padding_size;
Syoyo Fujita72f4a552020-01-08 00:40:41 +09007667 const uint32_t model_format = 0x4E4F534A;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007668 stream.write(reinterpret_cast<const char *>(&model_length),
Johan Bowald52936a02019-07-17 09:06:45 +02007669 sizeof(model_length));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007670 stream.write(reinterpret_cast<const char *>(&model_format),
Johan Bowald52936a02019-07-17 09:06:45 +02007671 sizeof(model_format));
Syoyo Fujitacea69e32019-08-20 17:10:30 +09007672 stream.write(content.c_str(), std::streamsize(content.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007673
7674 // Chunk must be multiplies of 4, so pad with spaces
Alexander Wood9e3d1f62021-10-08 16:57:56 -04007675 if (content_padding_size > 0) {
7676 const std::string padding = std::string(size_t(content_padding_size), ' ');
Syoyo Fujitacea69e32019-08-20 17:10:30 +09007677 stream.write(padding.c_str(), std::streamsize(padding.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007678 }
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007679 if (binBuffer.size() > 0) {
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007680 // BIN chunk info, then BIN data
Syoyo Fujita72f4a552020-01-08 00:40:41 +09007681 const uint32_t bin_length = uint32_t(binBuffer.size()) + bin_padding_size;
7682 const uint32_t bin_format = 0x004e4942;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007683 stream.write(reinterpret_cast<const char *>(&bin_length),
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007684 sizeof(bin_length));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007685 stream.write(reinterpret_cast<const char *>(&bin_format),
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007686 sizeof(bin_format));
7687 stream.write(reinterpret_cast<const char *>(binBuffer.data()),
7688 std::streamsize(binBuffer.size()));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007689 // Chunksize must be multiplies of 4, so pad with zeroes
7690 if (bin_padding_size > 0) {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007691 const std::vector<unsigned char> padding =
7692 std::vector<unsigned char>(size_t(bin_padding_size), 0);
7693 stream.write(reinterpret_cast<const char *>(padding.data()),
7694 std::streamsize(padding.size()));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007695 }
7696 }
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007697
7698 // TODO: Check error on stream.write
7699 return true;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007700}
7701
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007702static bool WriteBinaryGltfFile(const std::string &output,
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007703 const std::string &content,
7704 const std::vector<unsigned char> &binBuffer) {
Harokyangfb256602019-10-30 16:13:52 +08007705#ifdef _WIN32
Syoyo Fujita45cac782019-11-09 20:42:55 +09007706#if defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08007707 std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09007708#elif defined(__GLIBCXX__)
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007709 int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
7710 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
7711 __gnu_cxx::stdio_filebuf<char> wfile_buf(
7712 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita4ab03862019-11-10 15:31:17 +09007713 std::ostream gltfFile(&wfile_buf);
Harokyangfb256602019-10-30 16:13:52 +08007714#else
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007715 std::ofstream gltfFile(output.c_str(), std::ios::binary);
Harokyangfb256602019-10-30 16:13:52 +08007716#endif
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007717#else
7718 std::ofstream gltfFile(output.c_str(), std::ios::binary);
jrkooncecba5d6c2019-08-29 11:26:22 -05007719#endif
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007720 return WriteBinaryGltfStream(gltfFile, content, binBuffer);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007721}
7722
7723bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
7724 bool prettyPrint = true,
7725 bool writeBinary = false) {
7726 JsonDocument output;
7727
7728 /// Serialize all properties except buffers and images.
7729 SerializeGltfModel(model, output);
7730
7731 // BUFFERS
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007732 std::vector<unsigned char> binBuffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007733 if (model->buffers.size()) {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007734 json buffers;
7735 JsonReserveArray(buffers, model->buffers.size());
7736 for (unsigned int i = 0; i < model->buffers.size(); ++i) {
7737 json buffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007738 if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
7739 SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007740 } else {
7741 SerializeGltfBuffer(model->buffers[i], buffer);
7742 }
7743 JsonPushBack(buffers, std::move(buffer));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007744 }
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007745 JsonAddMember(output, "buffers", std::move(buffers));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007746 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007747
7748 // IMAGES
7749 if (model->images.size()) {
7750 json images;
jrkooncecba5d6c2019-08-29 11:26:22 -05007751 JsonReserveArray(images, model->images.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007752 for (unsigned int i = 0; i < model->images.size(); ++i) {
7753 json image;
7754
7755 std::string dummystring = "";
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007756 // UpdateImageObject need baseDir but only uses it if embeddedImages is
7757 // enabled, since we won't write separate images when writing to a stream
7758 // we
Hendrik Schwanekamp41e11022022-06-29 12:22:40 +02007759 UpdateImageObject(model->images[i], dummystring, int(i), true,
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007760 &this->WriteImageData, this->write_image_user_data_);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007761 SerializeGltfImage(model->images[i], image);
jrkooncecba5d6c2019-08-29 11:26:22 -05007762 JsonPushBack(images, std::move(image));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007763 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007764 JsonAddMember(output, "images", std::move(images));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007765 }
7766
7767 if (writeBinary) {
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007768 return WriteBinaryGltfStream(stream, JsonToString(output), binBuffer);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007769 } else {
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007770 return WriteGltfStream(stream, JsonToString(output, prettyPrint ? 2 : -1));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007771 }
7772
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007773}
7774
7775bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
7776 bool embedImages = false,
7777 bool embedBuffers = false,
7778 bool prettyPrint = true,
7779 bool writeBinary = false) {
jrkooncecba5d6c2019-08-29 11:26:22 -05007780 JsonDocument output;
Selmar Kok7cb31e42018-10-05 16:02:29 +02007781 std::string defaultBinFilename = GetBaseFilename(filename);
7782 std::string defaultBinFileExt = ".bin";
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00007783 std::string::size_type pos =
7784 defaultBinFilename.rfind('.', defaultBinFilename.length());
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007785
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007786 if (pos != std::string::npos) {
Selmar Kok7cb31e42018-10-05 16:02:29 +02007787 defaultBinFilename = defaultBinFilename.substr(0, pos);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007788 }
johan bowald642a3432018-04-01 12:37:18 +02007789 std::string baseDir = GetBaseDir(filename);
7790 if (baseDir.empty()) {
7791 baseDir = "./";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05007792 }
Johan Bowald52936a02019-07-17 09:06:45 +02007793 /// Serialize all properties except buffers and images.
7794 SerializeGltfModel(model, output);
Syoyo Fujitad42767e2018-03-15 21:52:00 -05007795
Selmar Kok7cb31e42018-10-05 16:02:29 +02007796 // BUFFERS
Selmar Kokc884e582018-10-05 16:25:54 +02007797 std::vector<std::string> usedUris;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007798 std::vector<unsigned char> binBuffer;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007799 if (model->buffers.size()) {
7800 json buffers;
7801 JsonReserveArray(buffers, model->buffers.size());
7802 for (unsigned int i = 0; i < model->buffers.size(); ++i) {
7803 json buffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007804 if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
7805 SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007806 } else if (embedBuffers) {
7807 SerializeGltfBuffer(model->buffers[i], buffer);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00007808 } else {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007809 std::string binSavePath;
7810 std::string binUri;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007811 if (!model->buffers[i].uri.empty() &&
7812 !IsDataURI(model->buffers[i].uri)) {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007813 binUri = model->buffers[i].uri;
7814 } else {
7815 binUri = defaultBinFilename + defaultBinFileExt;
7816 bool inUse = true;
7817 int numUsed = 0;
7818 while (inUse) {
7819 inUse = false;
7820 for (const std::string &usedName : usedUris) {
7821 if (binUri.compare(usedName) != 0) continue;
7822 inUse = true;
7823 binUri = defaultBinFilename + std::to_string(numUsed++) +
7824 defaultBinFileExt;
7825 break;
7826 }
Selmar Kokc884e582018-10-05 16:25:54 +02007827 }
7828 }
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007829 usedUris.push_back(binUri);
7830 binSavePath = JoinPath(baseDir, binUri);
7831 if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
7832 binUri)) {
7833 return false;
7834 }
Selmar Kokc884e582018-10-05 16:25:54 +02007835 }
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007836 JsonPushBack(buffers, std::move(buffer));
johan bowald30c53472018-03-30 11:49:36 +02007837 }
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007838 JsonAddMember(output, "buffers", std::move(buffers));
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007839 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007840
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007841 // IMAGES
Johan Bowaldfaa27222018-03-28 14:44:45 +02007842 if (model->images.size()) {
7843 json images;
jrkooncecba5d6c2019-08-29 11:26:22 -05007844 JsonReserveArray(images, model->images.size());
Johan Bowaldfaa27222018-03-28 14:44:45 +02007845 for (unsigned int i = 0; i < model->images.size(); ++i) {
7846 json image;
johan bowald642a3432018-04-01 12:37:18 +02007847
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09007848 UpdateImageObject(model->images[i], baseDir, int(i), embedImages,
Johan Bowald77decfa2018-11-06 14:28:20 +01007849 &this->WriteImageData, this->write_image_user_data_);
Johan Bowaldfaa27222018-03-28 14:44:45 +02007850 SerializeGltfImage(model->images[i], image);
jrkooncecba5d6c2019-08-29 11:26:22 -05007851 JsonPushBack(images, std::move(image));
Johan Bowaldfaa27222018-03-28 14:44:45 +02007852 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007853 JsonAddMember(output, "images", std::move(images));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007854 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007855
David Harmonda9eac22018-08-30 08:06:05 -04007856 if (writeBinary) {
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007857 return WriteBinaryGltfFile(filename, JsonToString(output), binBuffer);
David Harmonda9eac22018-08-30 08:06:05 -04007858 } else {
Syoyo Fujita81f7dbe2022-08-02 01:05:34 +09007859 return WriteGltfFile(filename, JsonToString(output, (prettyPrint ? 2 : -1)));
David Harmonda9eac22018-08-30 08:06:05 -04007860 }
7861
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007862}
7863
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09007864} // namespace tinygltf
7865
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09007866#ifdef __clang__
7867#pragma clang diagnostic pop
7868#endif
7869
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007870#endif // TINYGLTF_IMPLEMENTATION