blob: 23095fdb6288993388d8caf5163a4b691c2fa6e3 [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 Fujita010ee9c2020-10-31 19:35:55 +090029// - v2.5.0 Add SetPreserveImageChannels() option to load image data as is.
Syoyo Fujita7905a5b2021-01-21 20:33:11 +090030// - v2.4.3 Fix null object output when when material has all default
31// parameters.
Syoyo Fujitac4166e42020-01-08 02:38:01 +090032// - v2.4.2 Decode percent-encoded URI.
Syoyo Fujita6e08b172019-10-30 17:25:38 +090033// - v2.4.1 Fix some glTF object class does not have `extensions` and/or
34// `extras` property.
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +090035// - v2.4.0 Experimental RapidJSON and C++14 support(Thanks to @jrkoone).
Syoyo Fujitaee179b22019-08-16 13:11:30 +090036// - v2.3.1 Set default value of minFilter and magFilter in Sampler to -1.
Syoyo Fujita046400b2019-07-24 19:26:48 +090037// - v2.3.0 Modified Material representation according to glTF 2.0 schema
38// (and introduced TextureInfo class)
Syoyo Fujita150f2432019-07-25 19:22:44 +090039// Change the behavior of `Value::IsNumber`. It return true either the
40// value is int or real.
Syoyo Fujitaca56f722019-03-07 21:04:25 +090041// - v2.2.0 Add loading 16bit PNG support. Add Sparse accessor support(Thanks
42// to @Ybalrid)
Syoyo Fujita7ae71102019-01-19 03:03:22 +090043// - v2.1.0 Add draco compression.
Syoyo Fujita0820d832018-10-04 15:45:13 +090044// - v2.0.1 Add comparsion feature(Thanks to @Selmar).
Syoyo Fujitaf6120152017-05-27 23:51:23 +090045// - v2.0.0 glTF 2.0!.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090046//
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090047// Tiny glTF loader is using following third party libraries:
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090048//
Syoyo Fujita2e21be72017-11-05 17:13:01 +090049// - jsonhpp: C++ JSON library.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090050// - base64: base64 decode/encode library.
51// - stb_image: Image loading library.
52//
Syoyo Fujita2840a0e2017-06-21 02:52:11 +090053#ifndef TINY_GLTF_H_
54#define TINY_GLTF_H_
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090055
Syoyo Fujitad42767e2018-03-15 21:52:00 -050056#include <array>
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +090057#include <cassert>
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +090058#include <cmath> // std::fabs
Syoyo Fujitad42767e2018-03-15 21:52:00 -050059#include <cstdint>
Selmar Kok31cb7f92018-10-03 15:39:05 +020060#include <cstdlib>
Syoyo Fujita641b3cc2018-10-04 15:43:33 +090061#include <cstring>
Syoyo Fujita046400b2019-07-24 19:26:48 +090062#include <limits>
Syoyo Fujita523c9bf2016-04-20 14:06:56 +090063#include <map>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090064#include <string>
65#include <vector>
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090066
jrkoonce51453942019-09-03 09:48:30 -050067#ifndef TINYGLTF_USE_CPP14
68#include <functional>
69#endif
70
Sascha Willems5f9cb242018-12-28 20:53:41 +010071#ifdef __ANDROID__
72#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
73#include <android/asset_manager.h>
74#endif
75#endif
76
Selmar Kok79e3df22019-10-29 16:22:07 +010077#ifdef __GNUC__
78#if (__GNUC__ < 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ <= 8))
Selmar Kokb74fade2019-10-29 16:09:32 +010079#define TINYGLTF_NOEXCEPT
Selmar Kok79e3df22019-10-29 16:22:07 +010080#else
81#define TINYGLTF_NOEXCEPT noexcept
Selmar Kokb74fade2019-10-29 16:09:32 +010082#endif
Selmar Kok79e3df22019-10-29 16:22:07 +010083#else
84#define TINYGLTF_NOEXCEPT noexcept
85#endif
86
Syoyo Fujita6e08b172019-10-30 17:25:38 +090087#define DEFAULT_METHODS(x) \
88 ~x() = default; \
89 x(const x &) = default; \
90 x(x &&) TINYGLTF_NOEXCEPT = default; \
91 x &operator=(const x &) = default; \
92 x &operator=(x &&) TINYGLTF_NOEXCEPT = default;
Selmar Kokb74fade2019-10-29 16:09:32 +010093
Syoyo Fujitaa4d26882015-12-20 20:27:54 +090094namespace tinygltf {
95
96#define TINYGLTF_MODE_POINTS (0)
97#define TINYGLTF_MODE_LINE (1)
98#define TINYGLTF_MODE_LINE_LOOP (2)
Thomas Tissot6c4a0062019-01-30 13:10:51 +010099#define TINYGLTF_MODE_LINE_STRIP (3)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900100#define TINYGLTF_MODE_TRIANGLES (4)
101#define TINYGLTF_MODE_TRIANGLE_STRIP (5)
102#define TINYGLTF_MODE_TRIANGLE_FAN (6)
103
104#define TINYGLTF_COMPONENT_TYPE_BYTE (5120)
105#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE (5121)
106#define TINYGLTF_COMPONENT_TYPE_SHORT (5122)
107#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT (5123)
108#define TINYGLTF_COMPONENT_TYPE_INT (5124)
109#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125)
110#define TINYGLTF_COMPONENT_TYPE_FLOAT (5126)
Syoyo Fujita476a8b22018-01-21 12:19:01 +0900111#define TINYGLTF_COMPONENT_TYPE_DOUBLE (5130)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900112
Syoyo Fujitac2615632016-06-19 21:56:06 +0900113#define TINYGLTF_TEXTURE_FILTER_NEAREST (9728)
114#define TINYGLTF_TEXTURE_FILTER_LINEAR (9729)
115#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST (9984)
116#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST (9985)
117#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR (9986)
118#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR (9987)
119
Cemalettin Dervis246d8662017-12-07 20:29:51 +0100120#define TINYGLTF_TEXTURE_WRAP_REPEAT (10497)
Syoyo Fujitac2615632016-06-19 21:56:06 +0900121#define TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE (33071)
Luke San Antonio Bialecki904612c2016-08-19 17:28:03 -0400122#define TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT (33648)
Syoyo Fujitac2615632016-06-19 21:56:06 +0900123
Luke San Antoniocdf4cb72016-06-14 21:32:11 -0400124// Redeclarations of the above for technique.parameters.
125#define TINYGLTF_PARAMETER_TYPE_BYTE (5120)
126#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE (5121)
127#define TINYGLTF_PARAMETER_TYPE_SHORT (5122)
128#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT (5123)
129#define TINYGLTF_PARAMETER_TYPE_INT (5124)
130#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT (5125)
131#define TINYGLTF_PARAMETER_TYPE_FLOAT (5126)
132
133#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC2 (35664)
134#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC3 (35665)
135#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC4 (35666)
136
137#define TINYGLTF_PARAMETER_TYPE_INT_VEC2 (35667)
138#define TINYGLTF_PARAMETER_TYPE_INT_VEC3 (35668)
139#define TINYGLTF_PARAMETER_TYPE_INT_VEC4 (35669)
140
141#define TINYGLTF_PARAMETER_TYPE_BOOL (35670)
142#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC2 (35671)
143#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC3 (35672)
144#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC4 (35673)
145
146#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT2 (35674)
147#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT3 (35675)
148#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT4 (35676)
149
150#define TINYGLTF_PARAMETER_TYPE_SAMPLER_2D (35678)
151
152// End parameter types
153
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900154#define TINYGLTF_TYPE_VEC2 (2)
155#define TINYGLTF_TYPE_VEC3 (3)
156#define TINYGLTF_TYPE_VEC4 (4)
157#define TINYGLTF_TYPE_MAT2 (32 + 2)
158#define TINYGLTF_TYPE_MAT3 (32 + 3)
159#define TINYGLTF_TYPE_MAT4 (32 + 4)
160#define TINYGLTF_TYPE_SCALAR (64 + 1)
161#define TINYGLTF_TYPE_VECTOR (64 + 4)
162#define TINYGLTF_TYPE_MATRIX (64 + 16)
163
164#define TINYGLTF_IMAGE_FORMAT_JPEG (0)
165#define TINYGLTF_IMAGE_FORMAT_PNG (1)
166#define TINYGLTF_IMAGE_FORMAT_BMP (2)
167#define TINYGLTF_IMAGE_FORMAT_GIF (3)
168
Luke San Antonio6d616f52016-06-23 14:09:23 -0400169#define TINYGLTF_TEXTURE_FORMAT_ALPHA (6406)
170#define TINYGLTF_TEXTURE_FORMAT_RGB (6407)
Syoyo Fujitabde70212016-02-07 17:38:17 +0900171#define TINYGLTF_TEXTURE_FORMAT_RGBA (6408)
Luke San Antonio6d616f52016-06-23 14:09:23 -0400172#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE (6409)
173#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE_ALPHA (6410)
174
Syoyo Fujitabde70212016-02-07 17:38:17 +0900175#define TINYGLTF_TEXTURE_TARGET_TEXTURE2D (3553)
176#define TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE (5121)
177
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900178#define TINYGLTF_TARGET_ARRAY_BUFFER (34962)
179#define TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER (34963)
180
Luke San Antoniocdf4cb72016-06-14 21:32:11 -0400181#define TINYGLTF_SHADER_TYPE_VERTEX_SHADER (35633)
182#define TINYGLTF_SHADER_TYPE_FRAGMENT_SHADER (35632)
183
Selmar Kok31cb7f92018-10-03 15:39:05 +0200184#define TINYGLTF_DOUBLE_EPS (1.e-12)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900185#define TINYGLTF_DOUBLE_EQUAL(a, b) (std::fabs((b) - (a)) < TINYGLTF_DOUBLE_EPS)
Selmar Kok31cb7f92018-10-03 15:39:05 +0200186
Sascha Willems5f9cb242018-12-28 20:53:41 +0100187#ifdef __ANDROID__
188#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000189AAssetManager *asset_manager = nullptr;
Sascha Willems5f9cb242018-12-28 20:53:41 +0100190#endif
191#endif
192
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900193typedef enum {
Idriss Chaouch425195b2021-05-20 12:11:00 +0100194 NULL_TYPE,
195 REAL_TYPE,
196 INT_TYPE,
197 BOOL_TYPE,
198 STRING_TYPE,
199 ARRAY_TYPE,
200 BINARY_TYPE,
201 OBJECT_TYPE
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900202} Type;
203
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500204static inline int32_t GetComponentSizeInBytes(uint32_t componentType) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900205 if (componentType == TINYGLTF_COMPONENT_TYPE_BYTE) {
206 return 1;
207 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) {
208 return 1;
209 } else if (componentType == TINYGLTF_COMPONENT_TYPE_SHORT) {
210 return 2;
211 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) {
212 return 2;
213 } else if (componentType == TINYGLTF_COMPONENT_TYPE_INT) {
214 return 4;
215 } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) {
216 return 4;
217 } else if (componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) {
218 return 4;
219 } else if (componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) {
220 return 8;
221 } else {
222 // Unknown componenty type
223 return -1;
224 }
225}
226
DingboDingboDingbo83ccb9f2019-08-29 18:49:15 -0400227static inline int32_t GetNumComponentsInType(uint32_t ty) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900228 if (ty == TINYGLTF_TYPE_SCALAR) {
229 return 1;
230 } else if (ty == TINYGLTF_TYPE_VEC2) {
231 return 2;
232 } else if (ty == TINYGLTF_TYPE_VEC3) {
233 return 3;
234 } else if (ty == TINYGLTF_TYPE_VEC4) {
235 return 4;
236 } else if (ty == TINYGLTF_TYPE_MAT2) {
237 return 4;
238 } else if (ty == TINYGLTF_TYPE_MAT3) {
239 return 9;
240 } else if (ty == TINYGLTF_TYPE_MAT4) {
241 return 16;
242 } else {
243 // Unknown componenty type
244 return -1;
245 }
246}
247
Syoyo Fujita150f2432019-07-25 19:22:44 +0900248// TODO(syoyo): Move these functions to TinyGLTF class
Selmar Kok0d0e97e2018-08-22 14:01:57 +0200249bool IsDataURI(const std::string &in);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900250bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
251 const std::string &in, size_t reqBytes, bool checkSize);
Selmar Kok0d0e97e2018-08-22 14:01:57 +0200252
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900253#ifdef __clang__
254#pragma clang diagnostic push
255// Suppress warning for : static Value null_value
256// https://stackoverflow.com/questions/15708411/how-to-deal-with-global-constructor-warning-in-clang
257#pragma clang diagnostic ignored "-Wexit-time-destructors"
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900258#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900259#endif
260
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900261// Simple class to represent JSON object
262class Value {
263 public:
264 typedef std::vector<Value> Array;
265 typedef std::map<std::string, Value> Object;
266
Syoyo Fujita046400b2019-07-24 19:26:48 +0900267 Value()
268 : type_(NULL_TYPE),
269 int_value_(0),
Syoyo Fujita150f2432019-07-25 19:22:44 +0900270 real_value_(0.0),
Syoyo Fujita046400b2019-07-24 19:26:48 +0900271 boolean_value_(false) {}
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900272
273 explicit Value(bool b) : type_(BOOL_TYPE) { boolean_value_ = b; }
Syoyo Fujitaff515702019-08-24 16:29:14 +0900274 explicit Value(int i) : type_(INT_TYPE) {
275 int_value_ = i;
276 real_value_ = i;
277 }
Syoyo Fujita150f2432019-07-25 19:22:44 +0900278 explicit Value(double n) : type_(REAL_TYPE) { real_value_ = n; }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900279 explicit Value(const std::string &s) : type_(STRING_TYPE) {
280 string_value_ = s;
281 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900282 explicit Value(std::string &&s)
283 : type_(STRING_TYPE), string_value_(std::move(s)) {}
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900284 explicit Value(const unsigned char *p, size_t n) : type_(BINARY_TYPE) {
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900285 binary_value_.resize(n);
286 memcpy(binary_value_.data(), p, n);
287 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900288 explicit Value(std::vector<unsigned char> &&v) noexcept
289 : type_(BINARY_TYPE),
290 binary_value_(std::move(v)) {}
291 explicit Value(const Array &a) : type_(ARRAY_TYPE) { array_value_ = a; }
292 explicit Value(Array &&a) noexcept : type_(ARRAY_TYPE),
293 array_value_(std::move(a)) {}
jrkoonced1e14722019-08-27 11:51:02 -0500294
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900295 explicit Value(const Object &o) : type_(OBJECT_TYPE) { object_value_ = o; }
296 explicit Value(Object &&o) noexcept : type_(OBJECT_TYPE),
297 object_value_(std::move(o)) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100298
299 DEFAULT_METHODS(Value)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900300
Hill Mad1e32862021-02-20 22:30:44 -0800301 char Type() const { return static_cast<char>(type_); }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900302
303 bool IsBool() const { return (type_ == BOOL_TYPE); }
304
305 bool IsInt() const { return (type_ == INT_TYPE); }
306
Syoyo Fujita150f2432019-07-25 19:22:44 +0900307 bool IsNumber() const { return (type_ == REAL_TYPE) || (type_ == INT_TYPE); }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900308
Syoyo Fujita150f2432019-07-25 19:22:44 +0900309 bool IsReal() const { return (type_ == REAL_TYPE); }
Syoyo Fujita046400b2019-07-24 19:26:48 +0900310
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900311 bool IsString() const { return (type_ == STRING_TYPE); }
312
313 bool IsBinary() const { return (type_ == BINARY_TYPE); }
314
315 bool IsArray() const { return (type_ == ARRAY_TYPE); }
316
317 bool IsObject() const { return (type_ == OBJECT_TYPE); }
318
Syoyo Fujita150f2432019-07-25 19:22:44 +0900319 // Use this function if you want to have number value as double.
320 double GetNumberAsDouble() const {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900321 if (type_ == INT_TYPE) {
322 return double(int_value_);
Syoyo Fujita150f2432019-07-25 19:22:44 +0900323 } else {
324 return real_value_;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900325 }
Syoyo Fujita150f2432019-07-25 19:22:44 +0900326 }
Syoyo Fujita046400b2019-07-24 19:26:48 +0900327
Syoyo Fujita150f2432019-07-25 19:22:44 +0900328 // Use this function if you want to have number value as int.
Syoyo Fujitaff0a2e92020-05-11 19:07:00 +0900329 // TODO(syoyo): Support int value larger than 32 bits
330 int GetNumberAsInt() const {
Syoyo Fujita150f2432019-07-25 19:22:44 +0900331 if (type_ == REAL_TYPE) {
332 return int(real_value_);
333 } else {
334 return int_value_;
335 }
Syoyo Fujita046400b2019-07-24 19:26:48 +0900336 }
337
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900338 // Accessor
339 template <typename T>
340 const T &Get() const;
341 template <typename T>
342 T &Get();
343
344 // Lookup value from an array
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900345 const Value &Get(int idx) const {
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900346 static Value null_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900347 assert(IsArray());
348 assert(idx >= 0);
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900349 return (static_cast<size_t>(idx) < array_value_.size())
350 ? array_value_[static_cast<size_t>(idx)]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900351 : null_value;
352 }
353
354 // Lookup value from a key-value pair
355 const Value &Get(const std::string &key) const {
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900356 static Value null_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900357 assert(IsObject());
358 Object::const_iterator it = object_value_.find(key);
359 return (it != object_value_.end()) ? it->second : null_value;
360 }
361
362 size_t ArrayLen() const {
363 if (!IsArray()) return 0;
364 return array_value_.size();
365 }
366
367 // Valid only for object type.
368 bool Has(const std::string &key) const {
369 if (!IsObject()) return false;
370 Object::const_iterator it = object_value_.find(key);
371 return (it != object_value_.end()) ? true : false;
372 }
373
374 // List keys
375 std::vector<std::string> Keys() const {
376 std::vector<std::string> keys;
377 if (!IsObject()) return keys; // empty
378
379 for (Object::const_iterator it = object_value_.begin();
380 it != object_value_.end(); ++it) {
381 keys.push_back(it->first);
382 }
383
384 return keys;
385 }
386
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900387 size_t Size() const { return (IsArray() ? ArrayLen() : Keys().size()); }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900388
389 bool operator==(const tinygltf::Value &other) const;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +0000390
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900391 protected:
Syoyo Fujita046400b2019-07-24 19:26:48 +0900392 int type_ = NULL_TYPE;
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900393
Syoyo Fujita046400b2019-07-24 19:26:48 +0900394 int int_value_ = 0;
Syoyo Fujita150f2432019-07-25 19:22:44 +0900395 double real_value_ = 0.0;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900396 std::string string_value_;
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900397 std::vector<unsigned char> binary_value_;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900398 Array array_value_;
399 Object object_value_;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900400 bool boolean_value_ = false;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900401};
402
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900403#ifdef __clang__
404#pragma clang diagnostic pop
405#endif
406
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900407#define TINYGLTF_VALUE_GET(ctype, var) \
408 template <> \
409 inline const ctype &Value::Get<ctype>() const { \
410 return var; \
411 } \
412 template <> \
413 inline ctype &Value::Get<ctype>() { \
414 return var; \
415 }
416TINYGLTF_VALUE_GET(bool, boolean_value_)
Syoyo Fujita150f2432019-07-25 19:22:44 +0900417TINYGLTF_VALUE_GET(double, real_value_)
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900418TINYGLTF_VALUE_GET(int, int_value_)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900419TINYGLTF_VALUE_GET(std::string, string_value_)
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900420TINYGLTF_VALUE_GET(std::vector<unsigned char>, binary_value_)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900421TINYGLTF_VALUE_GET(Value::Array, array_value_)
422TINYGLTF_VALUE_GET(Value::Object, object_value_)
423#undef TINYGLTF_VALUE_GET
424
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900425#ifdef __clang__
426#pragma clang diagnostic push
427#pragma clang diagnostic ignored "-Wc++98-compat"
428#pragma clang diagnostic ignored "-Wpadded"
429#endif
430
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500431/// Agregate object for representing a color
Arthur Brainville58453192018-01-08 18:25:52 +0100432using ColorValue = std::array<double, 4>;
433
Syoyo Fujita046400b2019-07-24 19:26:48 +0900434// === legacy interface ====
435// TODO(syoyo): Deprecate `Parameter` class.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500436struct Parameter {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200437 bool bool_value = false;
Ben Buzbeef6af2242018-04-25 15:13:05 -0700438 bool has_number_value = false;
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900439 std::string string_value;
440 std::vector<double> number_array;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000441 std::map<std::string, double> json_double_value;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200442 double number_value = 0.0;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900443
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500444 // context sensitive methods. depending the type of the Parameter you are
445 // accessing, these are either valid or not
446 // If this parameter represent a texture map in a material, will return the
447 // texture index
Arthur Brainville58453192018-01-08 18:25:52 +0100448
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500449 /// Return the index of a texture if this Parameter is a texture map.
450 /// Returned value is only valid if the parameter represent a texture from a
451 /// material
Arthur Brainville41fe7722018-01-08 18:32:48 +0100452 int TextureIndex() const {
Arthur Brainville58453192018-01-08 18:25:52 +0100453 const auto it = json_double_value.find("index");
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500454 if (it != std::end(json_double_value)) {
Arthur Brainville58453192018-01-08 18:25:52 +0100455 return int(it->second);
456 }
457 return -1;
458 }
459
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000460 /// Return the index of a texture coordinate set if this Parameter is a
461 /// texture map. Returned value is only valid if the parameter represent a
462 /// texture from a material
Sascha Willemseb011062019-02-23 21:15:45 +0100463 int TextureTexCoord() const {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000464 const auto it = json_double_value.find("texCoord");
465 if (it != std::end(json_double_value)) {
466 return int(it->second);
467 }
Arthur Brainville8a98d982019-07-05 00:26:02 +0200468 // As per the spec, if texCoord is ommited, this parameter is 0
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000469 return 0;
Sascha Willemseb011062019-02-23 21:15:45 +0100470 }
471
Christophe820ede82019-07-04 15:21:21 +0900472 /// Return the scale of a texture if this Parameter is a normal texture map.
473 /// Returned value is only valid if the parameter represent a normal texture
474 /// from a material
475 double TextureScale() const {
476 const auto it = json_double_value.find("scale");
477 if (it != std::end(json_double_value)) {
478 return it->second;
479 }
Arthur Brainville8a98d982019-07-05 00:26:02 +0200480 // As per the spec, if scale is ommited, this paramter is 1
481 return 1;
482 }
Arthur Brianville (Ybalrid)2a9d9de2019-07-05 00:30:47 +0200483
Arthur Brainville8a98d982019-07-05 00:26:02 +0200484 /// Return the strength of a texture if this Parameter is a an occlusion map.
485 /// Returned value is only valid if the parameter represent an occlusion map
486 /// from a material
487 double TextureStrength() const {
488 const auto it = json_double_value.find("strength");
489 if (it != std::end(json_double_value)) {
490 return it->second;
491 }
492 // As per the spec, if strenghth is ommited, this parameter is 1
493 return 1;
Christophe820ede82019-07-04 15:21:21 +0900494 }
495
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500496 /// Material factor, like the roughness or metalness of a material
497 /// Returned value is only valid if the parameter represent a texture from a
498 /// material
Ben Buzbeef6af2242018-04-25 15:13:05 -0700499 double Factor() const { return number_value; }
Arthur Brainville58453192018-01-08 18:25:52 +0100500
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500501 /// Return the color of a material
502 /// Returned value is only valid if the parameter represent a texture from a
503 /// material
Arthur Brainville95853912018-01-08 18:37:44 +0100504 ColorValue ColorFactor() const {
Arthur Brainville58453192018-01-08 18:25:52 +0100505 return {
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500506 {// this agregate intialize the std::array object, and uses C++11 RVO.
507 number_array[0], number_array[1], number_array[2],
508 (number_array.size() > 3 ? number_array[3] : 1.0)}};
Arthur Brainville58453192018-01-08 18:25:52 +0100509 }
Selmar Kok31cb7f92018-10-03 15:39:05 +0200510
Selmar Kokff2b1f92019-10-21 17:58:09 +0200511 Parameter() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100512 DEFAULT_METHODS(Parameter)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900513 bool operator==(const Parameter &) const;
Arthur Brainville58453192018-01-08 18:25:52 +0100514};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900515
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900516#ifdef __clang__
517#pragma clang diagnostic pop
518#endif
519
520#ifdef __clang__
521#pragma clang diagnostic push
522#pragma clang diagnostic ignored "-Wpadded"
523#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900524
Syoyo Fujitabde70212016-02-07 17:38:17 +0900525typedef std::map<std::string, Parameter> ParameterMap;
Selmar Kokfa7022f2018-04-04 18:10:20 +0200526typedef std::map<std::string, Value> ExtensionMap;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900527
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000528struct AnimationChannel {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900529 int sampler; // required
530 int target_node; // required (index of the node to target)
531 std::string target_path; // required in ["translation", "rotation", "scale",
532 // "weights"]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900533 Value extras;
Selmar Kok4e2988e2019-08-16 14:08:08 +0200534 ExtensionMap extensions;
Selmar Kok973d9b32020-01-21 18:45:24 +0100535 ExtensionMap target_extensions;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900536
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900537 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
538 std::string extras_json_string;
539 std::string extensions_json_string;
Selmar Kok973d9b32020-01-21 18:45:24 +0100540 std::string target_extensions_json_string;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900541
Syoyo Fujita5b407452017-06-04 17:42:41 +0900542 AnimationChannel() : sampler(-1), target_node(-1) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100543 DEFAULT_METHODS(AnimationChannel)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900544 bool operator==(const AnimationChannel &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000545};
546
547struct AnimationSampler {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900548 int input; // required
549 int output; // required
Arthur Brianville (Ybalrid)2a9d9de2019-07-05 00:30:47 +0200550 std::string interpolation; // "LINEAR", "STEP","CUBICSPLINE" or user defined
551 // string. default "LINEAR"
Jens Olssonb3af2f12018-06-04 10:17:49 +0200552 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900553 ExtensionMap extensions;
554
555 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
556 std::string extras_json_string;
557 std::string extensions_json_string;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000558
Syoyo Fujita5b407452017-06-04 17:42:41 +0900559 AnimationSampler() : input(-1), output(-1), interpolation("LINEAR") {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100560 DEFAULT_METHODS(AnimationSampler)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900561 bool operator==(const AnimationSampler &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000562};
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900563
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900564struct Animation {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900565 std::string name;
566 std::vector<AnimationChannel> channels;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000567 std::vector<AnimationSampler> samplers;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900568 Value extras;
Selmar Kok4e2988e2019-08-16 14:08:08 +0200569 ExtensionMap extensions;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200570
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900571 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
572 std::string extras_json_string;
573 std::string extensions_json_string;
574
Selmar Kokff2b1f92019-10-21 17:58:09 +0200575 Animation() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100576 DEFAULT_METHODS(Animation)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900577 bool operator==(const Animation &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900578};
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900579
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000580struct Skin {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900581 std::string name;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900582 int inverseBindMatrices; // required here but not in the spec
583 int skeleton; // The index of the node used as a skeleton root
584 std::vector<int> joints; // Indices of skeleton nodes
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000585
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900586 Value extras;
587 ExtensionMap extensions;
588
589 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
590 std::string extras_json_string;
591 std::string extensions_json_string;
592
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900593 Skin() {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000594 inverseBindMatrices = -1;
Aurélien Chatelain8d5e0a72017-06-19 13:52:08 +0000595 skeleton = -1;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000596 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100597 DEFAULT_METHODS(Skin)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900598 bool operator==(const Skin &) const;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000599};
600
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000601struct Sampler {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900602 std::string name;
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900603 // glTF 2.0 spec does not define default value for `minFilter` and
604 // `magFilter`. Set -1 in TinyGLTF(issue #186)
605 int minFilter =
606 -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR",
zz8a35b8f2021-05-14 13:49:33 +0800607 // "NEAREST_MIPMAP_NEAREST", "LINEAR_MIPMAP_NEAREST",
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900608 // "NEAREST_MIPMAP_LINEAR", "LINEAR_MIPMAP_LINEAR"]
609 int magFilter =
610 -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR"]
611 int wrapS =
612 TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
613 // "REPEAT"], default "REPEAT"
614 int wrapT =
615 TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
616 // "REPEAT"], default "REPEAT"
Syoyo Fujita2c521b32020-12-04 00:50:46 +0900617 //int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; // TinyGLTF extension. currently not used.
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900618
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900619 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900620 ExtensionMap extensions;
621
622 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
623 std::string extras_json_string;
624 std::string extensions_json_string;
Syoyo Fujitac2615632016-06-19 21:56:06 +0900625
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000626 Sampler()
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900627 : minFilter(-1),
628 magFilter(-1),
timmmeh73584ba2019-01-30 18:38:46 -0800629 wrapS(TINYGLTF_TEXTURE_WRAP_REPEAT),
Syoyo Fujita2c521b32020-12-04 00:50:46 +0900630 wrapT(TINYGLTF_TEXTURE_WRAP_REPEAT) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100631 DEFAULT_METHODS(Sampler)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900632 bool operator==(const Sampler &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000633};
634
Syoyo Fujita5b407452017-06-04 17:42:41 +0900635struct Image {
Syoyo Fujitac2615632016-06-19 21:56:06 +0900636 std::string name;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900637 int width;
638 int height;
639 int component;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000640 int bits; // bit depth per channel. 8(byte), 16 or 32.
641 int pixel_type; // pixel type(TINYGLTF_COMPONENT_TYPE_***). usually
642 // UBYTE(bits = 8) or USHORT(bits = 16)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900643 std::vector<unsigned char> image;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900644 int bufferView; // (required if no uri)
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500645 std::string mimeType; // (required if no uri) ["image/jpeg", "image/png",
646 // "image/bmp", "image/gif"]
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900647 std::string uri; // (required if no mimeType) uri is not decoded(e.g.
648 // whitespace may be represented as %20)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900649 Value extras;
Syoyo Fujita3e53feb2018-09-02 15:36:17 +0900650 ExtensionMap extensions;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900651
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900652 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
653 std::string extras_json_string;
654 std::string extensions_json_string;
655
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900656 // When this flag is true, data is stored to `image` in as-is format(e.g. jpeg
657 // compressed for "image/jpeg" mime) This feature is good if you use custom
658 // image loader function. (e.g. delayed decoding of images for faster glTF
659 // parsing) Default parser for Image does not provide as-is loading feature at
660 // the moment. (You can manipulate this by providing your own LoadImageData
661 // function)
Selmar Kokfa0a9982018-10-03 15:46:23 +0200662 bool as_is;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900663
664 Image() : as_is(false) {
665 bufferView = -1;
666 width = -1;
667 height = -1;
668 component = -1;
daemyung jang1739d022020-07-03 13:27:39 +0900669 bits = -1;
670 pixel_type = -1;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900671 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100672 DEFAULT_METHODS(Image)
jrkoonced1e14722019-08-27 11:51:02 -0500673
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900674 bool operator==(const Image &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000675};
676
677struct Texture {
Vladimír Vondruš239be2c2018-07-24 23:23:56 +0200678 std::string name;
679
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000680 int sampler;
Selmar Kok8eb39042018-10-05 14:29:35 +0200681 int source;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900682 Value extras;
Selmar Kokfa7022f2018-04-04 18:10:20 +0200683 ExtensionMap extensions;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900684
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900685 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
686 std::string extras_json_string;
687 std::string extensions_json_string;
688
Syoyo Fujita5b407452017-06-04 17:42:41 +0900689 Texture() : sampler(-1), source(-1) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100690 DEFAULT_METHODS(Texture)
jrkoonced1e14722019-08-27 11:51:02 -0500691
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900692 bool operator==(const Texture &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000693};
Syoyo Fujitabde70212016-02-07 17:38:17 +0900694
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900695struct TextureInfo {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900696 int index = -1; // required.
697 int texCoord; // The set index of texture's TEXCOORD attribute used for
698 // texture coordinate mapping.
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900699
700 Value extras;
701 ExtensionMap extensions;
702
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900703 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
704 std::string extras_json_string;
705 std::string extensions_json_string;
706
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900707 TextureInfo() : index(-1), texCoord(0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100708 DEFAULT_METHODS(TextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900709 bool operator==(const TextureInfo &) const;
710};
711
712struct NormalTextureInfo {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900713 int index = -1; // required
714 int texCoord; // The set index of texture's TEXCOORD attribute used for
715 // texture coordinate mapping.
716 double scale; // scaledNormal = normalize((<sampled normal texture value>
717 // * 2.0 - 1.0) * vec3(<normal scale>, <normal scale>, 1.0))
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900718
719 Value extras;
720 ExtensionMap extensions;
721
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900722 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
723 std::string extras_json_string;
724 std::string extensions_json_string;
725
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900726 NormalTextureInfo() : index(-1), texCoord(0), scale(1.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100727 DEFAULT_METHODS(NormalTextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900728 bool operator==(const NormalTextureInfo &) const;
729};
730
731struct OcclusionTextureInfo {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900732 int index = -1; // required
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900733 int texCoord; // The set index of texture's TEXCOORD attribute used for
734 // texture coordinate mapping.
735 double strength; // occludedColor = lerp(color, color * <sampled occlusion
736 // texture value>, <occlusion strength>)
737
738 Value extras;
739 ExtensionMap extensions;
740
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900741 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
742 std::string extras_json_string;
743 std::string extensions_json_string;
744
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900745 OcclusionTextureInfo() : index(-1), texCoord(0), strength(1.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100746 DEFAULT_METHODS(OcclusionTextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900747 bool operator==(const OcclusionTextureInfo &) const;
748};
749
750// pbrMetallicRoughness class defined in glTF 2.0 spec.
751struct PbrMetallicRoughness {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900752 std::vector<double> baseColorFactor; // len = 4. default [1,1,1,1]
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900753 TextureInfo baseColorTexture;
754 double metallicFactor; // default 1
755 double roughnessFactor; // default 1
756 TextureInfo metallicRoughnessTexture;
757
758 Value extras;
759 ExtensionMap extensions;
760
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900761 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
762 std::string extras_json_string;
763 std::string extensions_json_string;
764
765 PbrMetallicRoughness()
766 : baseColorFactor(std::vector<double>{1.0, 1.0, 1.0, 1.0}),
767 metallicFactor(1.0),
768 roughnessFactor(1.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100769 DEFAULT_METHODS(PbrMetallicRoughness)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900770 bool operator==(const PbrMetallicRoughness &) const;
771};
772
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000773// Each extension should be stored in a ParameterMap.
774// members not in the values could be included in the ParameterMap
775// to keep a single material model
776struct Material {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900777 std::string name;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900778
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900779 std::vector<double> emissiveFactor; // length 3. default [0, 0, 0]
Syoyo Fujita046400b2019-07-24 19:26:48 +0900780 std::string alphaMode; // default "OPAQUE"
781 double alphaCutoff; // default 0.5
782 bool doubleSided; // default false;
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900783
784 PbrMetallicRoughness pbrMetallicRoughness;
785
786 NormalTextureInfo normalTexture;
787 OcclusionTextureInfo occlusionTexture;
788 TextureInfo emissiveTexture;
789
Syoyo Fujita046400b2019-07-24 19:26:48 +0900790 // For backward compatibility
791 // TODO(syoyo): Remove `values` and `additionalValues` in the next release.
792 ParameterMap values;
793 ParameterMap additionalValues;
Selmar09d2ff12018-03-15 17:30:42 +0100794
795 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900796 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200797
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900798 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
799 std::string extras_json_string;
800 std::string extensions_json_string;
801
Syoyo Fujita046400b2019-07-24 19:26:48 +0900802 Material() : alphaMode("OPAQUE"), alphaCutoff(0.5), doubleSided(false) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100803 DEFAULT_METHODS(Material)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900804
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900805 bool operator==(const Material &) const;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000806};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900807
Syoyo Fujita5b407452017-06-04 17:42:41 +0900808struct BufferView {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900809 std::string name;
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900810 int buffer{-1}; // Required
Syoyo Fujita4e47bc72020-01-02 22:07:25 +0900811 size_t byteOffset{0}; // minimum 0, default 0
812 size_t byteLength{0}; // required, minimum 1. 0 = invalid
813 size_t byteStride{0}; // minimum 4, maximum 252 (multiple of 4), default 0 =
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900814 // understood to be tightly packed
815 int target{0}; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"] for vertex indices
816 // or atttribs. Could be 0 for other data
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900817 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900818 ExtensionMap extensions;
819
820 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
821 std::string extras_json_string;
822 std::string extensions_json_string;
823
Syoyo Fujita4e47bc72020-01-02 22:07:25 +0900824 bool dracoDecoded{false}; // Flag indicating this has been draco decoded
Syoyo Fujitad17ff662017-05-29 02:53:12 +0900825
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900826 BufferView()
827 : buffer(-1),
828 byteOffset(0),
829 byteLength(0),
830 byteStride(0),
831 target(0),
832 dracoDecoded(false) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100833 DEFAULT_METHODS(BufferView)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900834 bool operator==(const BufferView &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000835};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900836
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000837struct Accessor {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900838 int bufferView; // optional in spec but required here since sparse accessor
839 // are not supported
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900840 std::string name;
841 size_t byteOffset;
Fabien Freling9056aee2019-03-21 17:03:18 +0100842 bool normalized; // optional.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000843 int componentType; // (required) One of TINYGLTF_COMPONENT_TYPE_***
Syoyo Fujita5b407452017-06-04 17:42:41 +0900844 size_t count; // required
845 int type; // (required) One of TINYGLTF_TYPE_*** ..
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900846 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900847 ExtensionMap extensions;
848
849 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
850 std::string extras_json_string;
851 std::string extensions_json_string;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000852
Syoyo Fujita7905a5b2021-01-21 20:33:11 +0900853 std::vector<double>
854 minValues; // optional. integer value is promoted to double
855 std::vector<double>
856 maxValues; // optional. integer value is promoted to double
Syoyo Fujitad9d012a2017-07-12 18:29:29 +0900857
Arthur Brainville (Ybalrid)1ccb4ff2019-03-06 11:30:00 +0100858 struct {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000859 int count;
860 bool isSparse;
861 struct {
862 int byteOffset;
863 int bufferView;
864 int componentType; // a TINYGLTF_COMPONENT_TYPE_ value
865 } indices;
866 struct {
867 int bufferView;
868 int byteOffset;
869 } values;
Arthur Brainville (Ybalrid)1ccb4ff2019-03-06 11:30:00 +0100870 } sparse;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000871
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900872 ///
873 /// Utility function to compute byteStride for a given bufferView object.
874 /// Returns -1 upon invalid glTF value or parameter configuration.
875 ///
876 int ByteStride(const BufferView &bufferViewObject) const {
877 if (bufferViewObject.byteStride == 0) {
878 // Assume data is tightly packed.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500879 int componentSizeInBytes =
880 GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900881 if (componentSizeInBytes <= 0) {
882 return -1;
883 }
884
Syoyo Fujita81d75df2019-08-30 19:19:52 +0900885 int numComponents = GetNumComponentsInType(static_cast<uint32_t>(type));
886 if (numComponents <= 0) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900887 return -1;
888 }
889
Syoyo Fujita81d75df2019-08-30 19:19:52 +0900890 return componentSizeInBytes * numComponents;
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900891 } else {
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500892 // Check if byteStride is a mulple of the size of the accessor's component
893 // type.
894 int componentSizeInBytes =
895 GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900896 if (componentSizeInBytes <= 0) {
897 return -1;
898 }
899
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900900 if ((bufferViewObject.byteStride % uint32_t(componentSizeInBytes)) != 0) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900901 return -1;
902 }
Arthur Brainville8ce4e542018-01-10 01:27:12 +0100903 return static_cast<int>(bufferViewObject.byteStride);
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900904 }
905
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900906 // unreachable return 0;
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900907 }
908
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900909 Accessor()
910 : bufferView(-1),
911 byteOffset(0),
912 normalized(false),
913 componentType(-1),
914 count(0),
915 type(-1) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000916 sparse.isSparse = false;
917 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100918 DEFAULT_METHODS(Accessor)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900919 bool operator==(const tinygltf::Accessor &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000920};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900921
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900922struct PerspectiveCamera {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200923 double aspectRatio; // min > 0
924 double yfov; // required. min > 0
925 double zfar; // min > 0
926 double znear; // required. min > 0
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900927
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900928 PerspectiveCamera()
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900929 : aspectRatio(0.0),
930 yfov(0.0),
931 zfar(0.0) // 0 = use infinite projecton matrix
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900932 ,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900933 znear(0.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100934 DEFAULT_METHODS(PerspectiveCamera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900935 bool operator==(const PerspectiveCamera &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900936
Selmar09d2ff12018-03-15 17:30:42 +0100937 ExtensionMap extensions;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900938 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900939
940 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
941 std::string extras_json_string;
942 std::string extensions_json_string;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900943};
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000944
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900945struct OrthographicCamera {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200946 double xmag; // required. must not be zero.
947 double ymag; // required. must not be zero.
948 double zfar; // required. `zfar` must be greater than `znear`.
949 double znear; // required
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000950
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900951 OrthographicCamera() : xmag(0.0), ymag(0.0), zfar(0.0), znear(0.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100952 DEFAULT_METHODS(OrthographicCamera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900953 bool operator==(const OrthographicCamera &) const;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900954
Selmar09d2ff12018-03-15 17:30:42 +0100955 ExtensionMap extensions;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900956 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900957
958 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
959 std::string extras_json_string;
960 std::string extensions_json_string;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900961};
962
963struct Camera {
964 std::string type; // required. "perspective" or "orthographic"
965 std::string name;
966
967 PerspectiveCamera perspective;
968 OrthographicCamera orthographic;
969
970 Camera() {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100971 DEFAULT_METHODS(Camera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900972 bool operator==(const Camera &) const;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900973
Selmar09d2ff12018-03-15 17:30:42 +0100974 ExtensionMap extensions;
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000975 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900976
977 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
978 std::string extras_json_string;
979 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900980};
981
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000982struct Primitive {
983 std::map<std::string, int> attributes; // (required) A dictionary object of
984 // integer, where each integer
985 // is the index of the accessor
986 // containing an attribute.
987 int material; // The index of the material to apply to this primitive
Syoyo Fujita5b407452017-06-04 17:42:41 +0900988 // when rendering.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000989 int indices; // The index of the accessor that contains the indices.
990 int mode; // one of TINYGLTF_MODE_***
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900991 std::vector<std::map<std::string, int> > targets; // array of morph targets,
Syoyo Fujita5b407452017-06-04 17:42:41 +0900992 // where each target is a dict with attribues in ["POSITION, "NORMAL",
993 // "TANGENT"] pointing
994 // to their corresponding accessors
Alex Wood7319db72019-01-24 15:38:16 -0500995 ExtensionMap extensions;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000996 Value extras;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900997
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900998 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
999 std::string extras_json_string;
1000 std::string extensions_json_string;
1001
Syoyo Fujita5b407452017-06-04 17:42:41 +09001002 Primitive() {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001003 material = -1;
1004 indices = -1;
daemyung jang1739d022020-07-03 13:27:39 +09001005 mode = -1;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001006 }
Selmar Kokb74fade2019-10-29 16:09:32 +01001007 DEFAULT_METHODS(Primitive)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001008 bool operator==(const Primitive &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001009};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001010
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001011struct Mesh {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001012 std::string name;
1013 std::vector<Primitive> primitives;
Syoyo Fujita5b407452017-06-04 17:42:41 +09001014 std::vector<double> weights; // weights to be applied to the Morph Targets
Selmar09d2ff12018-03-15 17:30:42 +01001015 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001016 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001017
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001018 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1019 std::string extras_json_string;
1020 std::string extensions_json_string;
1021
jrkoonced1e14722019-08-27 11:51:02 -05001022 Mesh() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001023 DEFAULT_METHODS(Mesh)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001024 bool operator==(const Mesh &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001025};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001026
1027class Node {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001028 public:
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09001029 Node() : camera(-1), skin(-1), mesh(-1) {}
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001030
Selmar Kokb74fade2019-10-29 16:09:32 +01001031 DEFAULT_METHODS(Node)
Syoyo Fujitad6b0d0a2019-06-29 17:31:13 +09001032
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001033 bool operator==(const Node &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001034
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001035 int camera; // the index of the camera referenced by this node
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001036
1037 std::string name;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00001038 int skin;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001039 int mesh;
1040 std::vector<int> children;
Syoyo Fujita7c877972016-03-08 01:31:49 +09001041 std::vector<double> rotation; // length must be 0 or 4
1042 std::vector<double> scale; // length must be 0 or 3
1043 std::vector<double> translation; // length must be 0 or 3
1044 std::vector<double> matrix; // length must be 0 or 16
Syoyo Fujita5b407452017-06-04 17:42:41 +09001045 std::vector<double> weights; // The weights of the instantiated Morph Target
Ben Buzbee3b735bb2018-04-24 11:39:30 -07001046
Selmar09d2ff12018-03-15 17:30:42 +01001047 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001048 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001049
1050 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1051 std::string extras_json_string;
1052 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001053};
1054
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001055struct Buffer {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001056 std::string name;
1057 std::vector<unsigned char> data;
Syoyo Fujita5b407452017-06-04 17:42:41 +09001058 std::string
1059 uri; // considered as required here but not in the spec (need to clarify)
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001060 // uri is not decoded(e.g. whitespace may be represented as %20)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001061 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001062 ExtensionMap extensions;
1063
1064 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1065 std::string extras_json_string;
1066 std::string extensions_json_string;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001067
Selmar Kokb74fade2019-10-29 16:09:32 +01001068 Buffer() = default;
1069 DEFAULT_METHODS(Buffer)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001070 bool operator==(const Buffer &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001071};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001072
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001073struct Asset {
Syoyo Fujitab702de72021-03-02 19:08:29 +09001074 std::string version = "2.0"; // required
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001075 std::string generator;
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +00001076 std::string minVersion;
1077 std::string copyright;
Selmar09d2ff12018-03-15 17:30:42 +01001078 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001079 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001080
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001081 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1082 std::string extras_json_string;
1083 std::string extensions_json_string;
1084
jrkoonced1e14722019-08-27 11:51:02 -05001085 Asset() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001086 DEFAULT_METHODS(Asset)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001087 bool operator==(const Asset &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001088};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001089
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001090struct Scene {
1091 std::string name;
1092 std::vector<int> nodes;
1093
Selmar09d2ff12018-03-15 17:30:42 +01001094 ExtensionMap extensions;
1095 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001096
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001097 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1098 std::string extras_json_string;
1099 std::string extensions_json_string;
1100
jrkoonced1e14722019-08-27 11:51:02 -05001101 Scene() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001102 DEFAULT_METHODS(Scene)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001103 bool operator==(const Scene &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001104};
1105
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001106struct SpotLight {
1107 double innerConeAngle;
1108 double outerConeAngle;
1109
Johan Bowald52936a02019-07-17 09:06:45 +02001110 SpotLight() : innerConeAngle(0.0), outerConeAngle(0.7853981634) {}
Selmar Kokb74fade2019-10-29 16:09:32 +01001111 DEFAULT_METHODS(SpotLight)
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001112 bool operator==(const SpotLight &) const;
1113
1114 ExtensionMap extensions;
1115 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001116
1117 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1118 std::string extras_json_string;
1119 std::string extensions_json_string;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001120};
1121
Emanuel Schrade186322b2017-11-06 11:14:41 +01001122struct Light {
1123 std::string name;
1124 std::vector<double> color;
Syoyo Fujita3dad3032020-05-13 21:22:23 +09001125 double intensity{1.0};
Emanuel Schrade186322b2017-11-06 11:14:41 +01001126 std::string type;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09001127 double range{0.0}; // 0.0 = inifinite
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001128 SpotLight spot;
1129
Benjamin Schmithüsenb2d7d882019-07-09 16:32:42 +02001130 Light() : intensity(1.0), range(0.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +01001131 DEFAULT_METHODS(Light)
Arthur Brainville (Ybalrid)9eeaf202019-09-05 13:02:05 +02001132
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001133 bool operator==(const Light &) const;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001134
1135 ExtensionMap extensions;
1136 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001137
1138 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1139 std::string extras_json_string;
1140 std::string extensions_json_string;
Emanuel Schrade186322b2017-11-06 11:14:41 +01001141};
1142
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001143class Model {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001144 public:
Selmar Kokff2b1f92019-10-21 17:58:09 +02001145 Model() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001146 DEFAULT_METHODS(Model)
Syoyo Fujitad6b0d0a2019-06-29 17:31:13 +09001147
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001148 bool operator==(const Model &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001149
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001150 std::vector<Accessor> accessors;
1151 std::vector<Animation> animations;
1152 std::vector<Buffer> buffers;
1153 std::vector<BufferView> bufferViews;
1154 std::vector<Material> materials;
1155 std::vector<Mesh> meshes;
1156 std::vector<Node> nodes;
1157 std::vector<Texture> textures;
1158 std::vector<Image> images;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00001159 std::vector<Skin> skins;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001160 std::vector<Sampler> samplers;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09001161 std::vector<Camera> cameras;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001162 std::vector<Scene> scenes;
Emanuel Schrade186322b2017-11-06 11:14:41 +01001163 std::vector<Light> lights;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001164
sammyKhana0a62bd2020-01-17 13:41:16 +01001165 int defaultScene = -1;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001166 std::vector<std::string> extensionsUsed;
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00001167 std::vector<std::string> extensionsRequired;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001168
1169 Asset asset;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001170
1171 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001172 ExtensionMap extensions;
1173
1174 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1175 std::string extras_json_string;
1176 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001177};
1178
Syoyo Fujita0614eb82016-10-14 18:50:14 +09001179enum SectionCheck {
Luke San Antonio9e36b612016-06-23 14:10:51 -04001180 NO_REQUIRE = 0x00,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001181 REQUIRE_VERSION = 0x01,
1182 REQUIRE_SCENE = 0x02,
1183 REQUIRE_SCENES = 0x04,
1184 REQUIRE_NODES = 0x08,
1185 REQUIRE_ACCESSORS = 0x10,
1186 REQUIRE_BUFFERS = 0x20,
1187 REQUIRE_BUFFER_VIEWS = 0x40,
1188 REQUIRE_ALL = 0x7f
Luke San Antonio9e36b612016-06-23 14:10:51 -04001189};
1190
Squareysff644d82018-03-13 22:36:18 +01001191///
1192/// LoadImageDataFunction type. Signature for custom image loading callbacks.
1193///
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001194typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *,
1195 std::string *, int, int,
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001196 const unsigned char *, int,
1197 void *user_pointer);
Squareysff644d82018-03-13 22:36:18 +01001198
johan bowald642a3432018-04-01 12:37:18 +02001199///
1200/// WriteImageDataFunction type. Signature for custom image writing callbacks.
1201///
1202typedef bool (*WriteImageDataFunction)(const std::string *, const std::string *,
1203 Image *, bool, void *);
1204
Squareys2d3594d2018-03-13 22:40:53 +01001205#ifndef TINYGLTF_NO_STB_IMAGE
Squareysff644d82018-03-13 22:36:18 +01001206// Declaration of default image loader callback
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001207bool LoadImageData(Image *image, const int image_idx, std::string *err,
1208 std::string *warn, int req_width, int req_height,
1209 const unsigned char *bytes, int size, void *);
Squareys2d3594d2018-03-13 22:40:53 +01001210#endif
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001211
johan bowald642a3432018-04-01 12:37:18 +02001212#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
1213// Declaration of default image writer callback
1214bool WriteImageData(const std::string *basepath, const std::string *filename,
1215 Image *image, bool embedImages, void *);
1216#endif
1217
Paolo Jovone6601bf2018-07-07 20:43:33 +02001218///
1219/// FilExistsFunction type. Signature for custom filesystem callbacks.
1220///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001221typedef bool (*FileExistsFunction)(const std::string &abs_filename, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001222
1223///
1224/// ExpandFilePathFunction type. Signature for custom filesystem callbacks.
1225///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001226typedef std::string (*ExpandFilePathFunction)(const std::string &, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001227
1228///
1229/// ReadWholeFileFunction type. Signature for custom filesystem callbacks.
1230///
1231typedef bool (*ReadWholeFileFunction)(std::vector<unsigned char> *,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001232 std::string *, const std::string &,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001233 void *);
1234
1235///
1236/// WriteWholeFileFunction type. Signature for custom filesystem callbacks.
1237///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001238typedef bool (*WriteWholeFileFunction)(std::string *, const std::string &,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001239 const std::vector<unsigned char> &,
1240 void *);
1241
1242///
1243/// A structure containing all required filesystem callbacks and a pointer to
1244/// their user data.
1245///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001246struct FsCallbacks {
1247 FileExistsFunction FileExists;
1248 ExpandFilePathFunction ExpandFilePath;
1249 ReadWholeFileFunction ReadWholeFile;
1250 WriteWholeFileFunction WriteWholeFile;
Paolo Jovone6601bf2018-07-07 20:43:33 +02001251
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001252 void *user_data; // An argument that is passed to all fs callbacks
Paolo Jovone6601bf2018-07-07 20:43:33 +02001253};
1254
1255#ifndef TINYGLTF_NO_FS
1256// Declaration of default filesystem callbacks
1257
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001258bool FileExists(const std::string &abs_filename, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001259
Syoyo Fujita3d939fd2020-06-06 18:13:15 +09001260///
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04001261/// Expand file path(e.g. `~` to home directory on posix, `%APPDATA%` to
Syoyo Fujita2c521b32020-12-04 00:50:46 +09001262/// `C:\\Users\\tinygltf\\AppData`)
Syoyo Fujita3d939fd2020-06-06 18:13:15 +09001263///
1264/// @param[in] filepath File path string. Assume UTF-8
1265/// @param[in] userdata User data. Set to `nullptr` if you don't need it.
1266///
1267std::string ExpandFilePath(const std::string &filepath, void *userdata);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001268
1269bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001270 const std::string &filepath, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001271
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001272bool WriteWholeFile(std::string *err, const std::string &filepath,
1273 const std::vector<unsigned char> &contents, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001274#endif
1275
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001276///
1277/// glTF Parser/Serialier context.
1278///
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001279class TinyGLTF {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001280 public:
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001281#ifdef __clang__
1282#pragma clang diagnostic push
1283#pragma clang diagnostic ignored "-Wc++98-compat"
1284#endif
1285
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001286 TinyGLTF() : bin_data_(nullptr), bin_size_(0), is_binary_(false) {}
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001287
1288#ifdef __clang__
1289#pragma clang diagnostic pop
1290#endif
1291
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001292 ~TinyGLTF() {}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001293
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001294 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001295 /// Loads glTF ASCII asset from a file.
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001296 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001297 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001298 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001299 bool LoadASCIIFromFile(Model *model, std::string *err, std::string *warn,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001300 const std::string &filename,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001301 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001302
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001303 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001304 /// Loads glTF ASCII asset from string(memory).
1305 /// `length` = strlen(str);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001306 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001307 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001308 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001309 bool LoadASCIIFromString(Model *model, std::string *err, std::string *warn,
1310 const char *str, const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001311 const std::string &base_dir,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001312 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001313
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001314 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001315 /// Loads glTF binary asset from a file.
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001316 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001317 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001318 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001319 bool LoadBinaryFromFile(Model *model, std::string *err, std::string *warn,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001320 const std::string &filename,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001321 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001322
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001323 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001324 /// Loads glTF binary asset from memory.
1325 /// `length` = strlen(str);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001326 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001327 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001328 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001329 bool LoadBinaryFromMemory(Model *model, std::string *err, std::string *warn,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001330 const unsigned char *bytes,
1331 const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001332 const std::string &base_dir = "",
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001333 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001334
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001335 ///
Johan Bowald1af7c1d2019-07-16 14:50:46 +02001336 /// Write glTF to stream, buffers and images will be embeded
1337 ///
1338 bool WriteGltfSceneToStream(Model *model, std::ostream &stream,
1339 bool prettyPrint, bool writeBinary);
1340
1341 ///
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001342 /// Write glTF to file.
1343 ///
johan bowald642a3432018-04-01 12:37:18 +02001344 bool WriteGltfSceneToFile(Model *model, const std::string &filename,
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001345 bool embedImages, bool embedBuffers,
1346 bool prettyPrint, bool writeBinary);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00001347
Squareysff644d82018-03-13 22:36:18 +01001348 ///
1349 /// Set callback to use for loading image data
1350 ///
1351 void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data);
1352
johan bowald642a3432018-04-01 12:37:18 +02001353 ///
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001354 /// Unset(remove) callback of loading image data
1355 ///
1356 void RemoveImageLoader();
1357
1358 ///
johan bowald642a3432018-04-01 12:37:18 +02001359 /// Set callback to use for writing image data
1360 ///
1361 void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data);
1362
Paolo Jovone6601bf2018-07-07 20:43:33 +02001363 ///
1364 /// Set callbacks to use for filesystem (fs) access and their user data
1365 ///
1366 void SetFsCallbacks(FsCallbacks callbacks);
1367
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001368 ///
1369 /// Set serializing default values(default = false).
1370 /// When true, default values are force serialized to .glTF.
Syoyo Fujitaff515702019-08-24 16:29:14 +09001371 /// This may be helpfull if you want to serialize a full description of glTF
1372 /// data.
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001373 ///
Syoyo Fujitaff515702019-08-24 16:29:14 +09001374 /// TODO(LTE): Supply parsing option as function arguments to
1375 /// `LoadASCIIFromFile()` and others, not by a class method
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001376 ///
1377 void SetSerializeDefaultValues(const bool enabled) {
1378 serialize_default_values_ = enabled;
1379 }
1380
Syoyo Fujitaff515702019-08-24 16:29:14 +09001381 bool GetSerializeDefaultValues() const { return serialize_default_values_; }
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001382
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001383 ///
1384 /// Store original JSON string for `extras` and `extensions`.
1385 /// This feature will be useful when the user want to reconstruct custom data
1386 /// structure from JSON string.
1387 ///
1388 void SetStoreOriginalJSONForExtrasAndExtensions(const bool enabled) {
1389 store_original_json_for_extras_and_extensions_ = enabled;
1390 }
1391
1392 bool GetStoreOriginalJSONForExtrasAndExtensions() const {
1393 return store_original_json_for_extras_and_extensions_;
1394 }
1395
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001396 ///
1397 /// Specify whether preserve image channales when loading images or not.
1398 /// (Not effective when the user suppy their own LoadImageData callbacks)
1399 ///
1400 void SetPreserveImageChannels(bool onoff) {
1401 preserve_image_channels_ = onoff;
1402 }
1403
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001404 bool GetPreserveImageChannels() const { return preserve_image_channels_; }
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001405
Syoyo Fujitabeded612016-05-01 20:03:43 +09001406 private:
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001407 ///
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001408 /// Loads glTF asset from string(memory).
1409 /// `length` = strlen(str);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001410 /// Set warning message to `warn` for example it fails to load asserts
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001411 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001412 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001413 bool LoadFromString(Model *model, std::string *err, std::string *warn,
1414 const char *str, const unsigned int length,
1415 const std::string &base_dir, unsigned int check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001416
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001417 const unsigned char *bin_data_ = nullptr;
1418 size_t bin_size_ = 0;
1419 bool is_binary_ = false;
1420
Syoyo Fujitaff515702019-08-24 16:29:14 +09001421 bool serialize_default_values_ = false; ///< Serialize default values?
Squareysff644d82018-03-13 22:36:18 +01001422
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001423 bool store_original_json_for_extras_and_extensions_ = false;
1424
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001425 bool preserve_image_channels_ = false; /// Default false(expand channels to
1426 /// RGBA) for backward compatibility.
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001427
Paolo Jovone6601bf2018-07-07 20:43:33 +02001428 FsCallbacks fs = {
1429#ifndef TINYGLTF_NO_FS
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001430 &tinygltf::FileExists, &tinygltf::ExpandFilePath,
1431 &tinygltf::ReadWholeFile, &tinygltf::WriteWholeFile,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001432
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001433 nullptr // Fs callback user data
Paolo Jovone6601bf2018-07-07 20:43:33 +02001434#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001435 nullptr, nullptr, nullptr, nullptr,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001436
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001437 nullptr // Fs callback user data
Paolo Jovone6601bf2018-07-07 20:43:33 +02001438#endif
1439 };
1440
Squareysff644d82018-03-13 22:36:18 +01001441 LoadImageDataFunction LoadImageData =
1442#ifndef TINYGLTF_NO_STB_IMAGE
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001443 &tinygltf::LoadImageData;
Squareysff644d82018-03-13 22:36:18 +01001444#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001445 nullptr;
Squareysff644d82018-03-13 22:36:18 +01001446#endif
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001447 void *load_image_user_data_{nullptr};
1448 bool user_image_loader_{false};
johan bowald642a3432018-04-01 12:37:18 +02001449
1450 WriteImageDataFunction WriteImageData =
1451#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001452 &tinygltf::WriteImageData;
johan bowald642a3432018-04-01 12:37:18 +02001453#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001454 nullptr;
johan bowald642a3432018-04-01 12:37:18 +02001455#endif
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001456 void *write_image_user_data_{nullptr};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001457};
1458
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001459#ifdef __clang__
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001460#pragma clang diagnostic pop // -Wpadded
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001461#endif
1462
Syoyo Fujita7c877972016-03-08 01:31:49 +09001463} // namespace tinygltf
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001464
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001465#endif // TINY_GLTF_H_
Syoyo Fujitac8d3c762017-05-25 01:42:38 +09001466
Selmar Kok31cb7f92018-10-03 15:39:05 +02001467#if defined(TINYGLTF_IMPLEMENTATION) || defined(__INTELLISENSE__)
Syoyo Fujita7c877972016-03-08 01:31:49 +09001468#include <algorithm>
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001469//#include <cassert>
Paolo Jovone6601bf2018-07-07 20:43:33 +02001470#ifndef TINYGLTF_NO_FS
Syoyo Fujita125d8e52019-11-09 20:52:56 +09001471#include <cstdio>
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001472#include <fstream>
Paolo Jovone6601bf2018-07-07 20:43:33 +02001473#endif
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001474#include <sstream>
Syoyo Fujitabeded612016-05-01 20:03:43 +09001475
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001476#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001477// Disable some warnings for external files.
1478#pragma clang diagnostic push
1479#pragma clang diagnostic ignored "-Wfloat-equal"
1480#pragma clang diagnostic ignored "-Wexit-time-destructors"
1481#pragma clang diagnostic ignored "-Wconversion"
1482#pragma clang diagnostic ignored "-Wold-style-cast"
Syoyo Fujita643ce102016-05-01 17:19:37 +09001483#pragma clang diagnostic ignored "-Wglobal-constructors"
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001484#if __has_warning("-Wreserved-id-macro")
Syoyo Fujita643ce102016-05-01 17:19:37 +09001485#pragma clang diagnostic ignored "-Wreserved-id-macro"
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001486#endif
Syoyo Fujita643ce102016-05-01 17:19:37 +09001487#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
1488#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001489#pragma clang diagnostic ignored "-Wc++98-compat"
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001490#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001491#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
1492#pragma clang diagnostic ignored "-Wswitch-enum"
1493#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001494#pragma clang diagnostic ignored "-Wweak-vtables"
1495#pragma clang diagnostic ignored "-Wcovered-switch-default"
Syoyo Fujitaa8f0b1c2018-08-28 21:33:40 +09001496#if __has_warning("-Wdouble-promotion")
1497#pragma clang diagnostic ignored "-Wdouble-promotion"
1498#endif
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001499#if __has_warning("-Wcomma")
Syoyo Fujitac8d3c762017-05-25 01:42:38 +09001500#pragma clang diagnostic ignored "-Wcomma"
1501#endif
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001502#if __has_warning("-Wzero-as-null-pointer-constant")
1503#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
1504#endif
1505#if __has_warning("-Wcast-qual")
1506#pragma clang diagnostic ignored "-Wcast-qual"
1507#endif
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001508#if __has_warning("-Wmissing-variable-declarations")
1509#pragma clang diagnostic ignored "-Wmissing-variable-declarations"
1510#endif
1511#if __has_warning("-Wmissing-prototypes")
1512#pragma clang diagnostic ignored "-Wmissing-prototypes"
1513#endif
1514#if __has_warning("-Wcast-align")
1515#pragma clang diagnostic ignored "-Wcast-align"
1516#endif
1517#if __has_warning("-Wnewline-eof")
1518#pragma clang diagnostic ignored "-Wnewline-eof"
1519#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001520#if __has_warning("-Wunused-parameter")
1521#pragma clang diagnostic ignored "-Wunused-parameter"
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001522#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001523#if __has_warning("-Wmismatched-tags")
1524#pragma clang diagnostic ignored "-Wmismatched-tags"
1525#endif
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001526#if __has_warning("-Wextra-semi-stmt")
1527#pragma clang diagnostic ignored "-Wextra-semi-stmt"
1528#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001529#endif
1530
1531// Disable GCC warnigs
1532#ifdef __GNUC__
1533#pragma GCC diagnostic push
1534#pragma GCC diagnostic ignored "-Wtype-limits"
Syoyo Fujitaca56f722019-03-07 21:04:25 +09001535#endif // __GNUC__
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001536
krokofc0116b2019-03-03 08:28:49 +02001537#ifndef TINYGLTF_NO_INCLUDE_JSON
jrkooncecba5d6c2019-08-29 11:26:22 -05001538#ifndef TINYGLTF_USE_RAPIDJSON
Alex Wood7319db72019-01-24 15:38:16 -05001539#include "json.hpp"
jrkooncecba5d6c2019-08-29 11:26:22 -05001540#else
Alvaro Barua3ddf8302021-03-18 00:21:23 +00001541#ifndef TINYGLTF_NO_INCLUDE_RAPIDJSON
jrkooncecba5d6c2019-08-29 11:26:22 -05001542#include "document.h"
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001543#include "prettywriter.h"
1544#include "rapidjson.h"
jrkooncecba5d6c2019-08-29 11:26:22 -05001545#include "stringbuffer.h"
1546#include "writer.h"
jrkooncecba5d6c2019-08-29 11:26:22 -05001547#endif
krokof4b6d112019-03-03 01:11:31 +02001548#endif
Alvaro Barua3ddf8302021-03-18 00:21:23 +00001549#endif
Alex Wood7319db72019-01-24 15:38:16 -05001550
1551#ifdef TINYGLTF_ENABLE_DRACO
Alex Wood7319db72019-01-24 15:38:16 -05001552#include "draco/compression/decode.h"
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001553#include "draco/core/decoder_buffer.h"
Alex Wood7319db72019-01-24 15:38:16 -05001554#endif
Squareys2d3594d2018-03-13 22:40:53 +01001555
1556#ifndef TINYGLTF_NO_STB_IMAGE
krokofc0116b2019-03-03 08:28:49 +02001557#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE
Alex Wood7319db72019-01-24 15:38:16 -05001558#include "stb_image.h"
Squareys2d3594d2018-03-13 22:40:53 +01001559#endif
krokof4b6d112019-03-03 01:11:31 +02001560#endif
Squareys2d3594d2018-03-13 22:40:53 +01001561
johan bowald642a3432018-04-01 12:37:18 +02001562#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
krokofc0116b2019-03-03 08:28:49 +02001563#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE
Alex Wood7319db72019-01-24 15:38:16 -05001564#include "stb_image_write.h"
johan bowald642a3432018-04-01 12:37:18 +02001565#endif
krokof4b6d112019-03-03 01:11:31 +02001566#endif
johan bowald642a3432018-04-01 12:37:18 +02001567
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001568#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001569#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001570#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001571
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001572#ifdef __GNUC__
1573#pragma GCC diagnostic pop
1574#endif
1575
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001576#ifdef _WIN32
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001577
1578// issue 143.
1579// Define NOMINMAX to avoid min/max defines,
1580// but undef it after included windows.h
1581#ifndef NOMINMAX
1582#define TINYGLTF_INTERNAL_NOMINMAX
1583#define NOMINMAX
1584#endif
1585
Syoyo Fujita87be0ce2019-02-19 21:36:32 +09001586#ifndef WIN32_LEAN_AND_MEAN
1587#define WIN32_LEAN_AND_MEAN
1588#define TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1589#endif
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001590#include <windows.h> // include API for expanding a file path
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001591
Syoyo Fujita87be0ce2019-02-19 21:36:32 +09001592#ifdef TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1593#undef WIN32_LEAN_AND_MEAN
1594#endif
1595
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001596#if defined(TINYGLTF_INTERNAL_NOMINMAX)
1597#undef NOMINMAX
1598#endif
1599
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001600#if defined(__GLIBCXX__) // mingw
Syoyo Fujita45cac782019-11-09 20:42:55 +09001601
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001602#include <fcntl.h> // _O_RDONLY
1603
1604#include <ext/stdio_filebuf.h> // fstream (all sorts of IO stuff) + stdio_filebuf (=streambuf)
Syoyo Fujita45cac782019-11-09 20:42:55 +09001605
1606#endif
1607
Julian Smith0598a202021-08-25 12:06:08 +01001608#elif !defined(__ANDROID__) && !defined(__OpenBSD__)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001609#include <wordexp.h>
1610#endif
1611
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001612#if defined(__sparcv9)
1613// Big endian
1614#else
1615#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
1616#define TINYGLTF_LITTLE_ENDIAN 1
1617#endif
1618#endif
1619
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001620namespace {
jrkooncecba5d6c2019-08-29 11:26:22 -05001621#ifdef TINYGLTF_USE_RAPIDJSON
jrkooncece7fa742019-09-04 13:31:44 -05001622
1623#ifdef TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001624// This uses the RapidJSON CRTAllocator. It is thread safe and multiple
1625// documents may be active at once.
1626using json =
1627 rapidjson::GenericValue<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
1628using json_const_iterator = json::ConstMemberIterator;
1629using json_const_array_iterator = json const *;
1630using JsonDocument =
jrkooncece7fa742019-09-04 13:31:44 -05001631 rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001632rapidjson::CrtAllocator s_CrtAllocator; // stateless and thread safe
1633rapidjson::CrtAllocator &GetAllocator() { return s_CrtAllocator; }
jrkooncece7fa742019-09-04 13:31:44 -05001634#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001635// This uses the default RapidJSON MemoryPoolAllocator. It is very fast, but
1636// not thread safe. Only a single JsonDocument may be active at any one time,
1637// meaning only a single gltf load/save can be active any one time.
1638using json = rapidjson::Value;
1639using json_const_iterator = json::ConstMemberIterator;
1640using json_const_array_iterator = json const *;
1641rapidjson::Document *s_pActiveDocument = nullptr;
1642rapidjson::Document::AllocatorType &GetAllocator() {
1643 assert(s_pActiveDocument); // Root json node must be JsonDocument type
1644 return s_pActiveDocument->GetAllocator();
1645}
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001646
1647#ifdef __clang__
1648#pragma clang diagnostic push
1649// Suppress JsonDocument(JsonDocument &&rhs) noexcept
1650#pragma clang diagnostic ignored "-Wunused-member-function"
1651#endif
1652
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001653struct JsonDocument : public rapidjson::Document {
1654 JsonDocument() {
1655 assert(s_pActiveDocument ==
1656 nullptr); // When using default allocator, only one document can be
1657 // active at a time, if you need multiple active at once,
1658 // define TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
1659 s_pActiveDocument = this;
jrkooncece7fa742019-09-04 13:31:44 -05001660 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001661 JsonDocument(const JsonDocument &) = delete;
1662 JsonDocument(JsonDocument &&rhs) noexcept
jrkooncece7fa742019-09-04 13:31:44 -05001663 : rapidjson::Document(std::move(rhs)) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001664 s_pActiveDocument = this;
1665 rhs.isNil = true;
1666 }
1667 ~JsonDocument() {
1668 if (!isNil) {
1669 s_pActiveDocument = nullptr;
jrkooncecba5d6c2019-08-29 11:26:22 -05001670 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001671 }
jrkooncece7fa742019-09-04 13:31:44 -05001672
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001673 private:
1674 bool isNil = false;
1675};
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001676
1677#ifdef __clang__
1678#pragma clang diagnostic pop
1679#endif
1680
jrkooncece7fa742019-09-04 13:31:44 -05001681#endif // TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001682
jrkooncecba5d6c2019-08-29 11:26:22 -05001683#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001684using nlohmann::json;
1685using json_const_iterator = json::const_iterator;
1686using json_const_array_iterator = json_const_iterator;
1687using JsonDocument = json;
jrkooncecba5d6c2019-08-29 11:26:22 -05001688#endif
1689
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001690void JsonParse(JsonDocument &doc, const char *str, size_t length,
1691 bool throwExc = false) {
jrkooncecba5d6c2019-08-29 11:26:22 -05001692#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001693 (void)throwExc;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001694 doc.Parse(str, length);
jrkooncecba5d6c2019-08-29 11:26:22 -05001695#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001696 doc = json::parse(str, str + length, nullptr, throwExc);
jrkooncecba5d6c2019-08-29 11:26:22 -05001697#endif
jrkooncecba5d6c2019-08-29 11:26:22 -05001698}
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001699} // namespace
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001700
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001701#ifdef __APPLE__
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001702#include "TargetConditionals.h"
Syoyo Fujitab23f6fe2017-11-15 01:03:09 +09001703#endif
1704
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001705#ifdef __clang__
1706#pragma clang diagnostic push
1707#pragma clang diagnostic ignored "-Wc++98-compat"
1708#endif
1709
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09001710namespace tinygltf {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001711
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001712///
1713/// Internal LoadImageDataOption struct.
1714/// This struct is passed through `user_pointer` in LoadImageData.
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001715/// The struct is not passed when the user supply their own LoadImageData
1716/// callbacks.
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001717///
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001718struct LoadImageDataOption {
1719 // true: preserve image channels(e.g. load as RGB image if the image has RGB
1720 // channels) default `false`(channels are expanded to RGBA for backward
1721 // compatiblity).
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001722 bool preserve_channels{false};
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001723};
1724
Selmar Kok31cb7f92018-10-03 15:39:05 +02001725// Equals function for Value, for recursivity
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001726static bool Equals(const tinygltf::Value &one, const tinygltf::Value &other) {
1727 if (one.Type() != other.Type()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001728
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001729 switch (one.Type()) {
1730 case NULL_TYPE:
1731 return true;
1732 case BOOL_TYPE:
1733 return one.Get<bool>() == other.Get<bool>();
Syoyo Fujita150f2432019-07-25 19:22:44 +09001734 case REAL_TYPE:
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001735 return TINYGLTF_DOUBLE_EQUAL(one.Get<double>(), other.Get<double>());
1736 case INT_TYPE:
1737 return one.Get<int>() == other.Get<int>();
1738 case OBJECT_TYPE: {
1739 auto oneObj = one.Get<tinygltf::Value::Object>();
1740 auto otherObj = other.Get<tinygltf::Value::Object>();
1741 if (oneObj.size() != otherObj.size()) return false;
1742 for (auto &it : oneObj) {
1743 auto otherIt = otherObj.find(it.first);
1744 if (otherIt == otherObj.end()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001745
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001746 if (!Equals(it.second, otherIt->second)) return false;
1747 }
1748 return true;
1749 }
1750 case ARRAY_TYPE: {
1751 if (one.Size() != other.Size()) return false;
1752 for (int i = 0; i < int(one.Size()); ++i)
Selmara63cc632019-04-16 16:57:43 +02001753 if (!Equals(one.Get(i), other.Get(i))) return false;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001754 return true;
1755 }
1756 case STRING_TYPE:
1757 return one.Get<std::string>() == other.Get<std::string>();
1758 case BINARY_TYPE:
1759 return one.Get<std::vector<unsigned char> >() ==
1760 other.Get<std::vector<unsigned char> >();
1761 default: {
1762 // unhandled type
1763 return false;
1764 }
1765 }
Selmar Kok31cb7f92018-10-03 15:39:05 +02001766}
1767
1768// Equals function for std::vector<double> using TINYGLTF_DOUBLE_EPSILON
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001769static bool Equals(const std::vector<double> &one,
1770 const std::vector<double> &other) {
1771 if (one.size() != other.size()) return false;
1772 for (int i = 0; i < int(one.size()); ++i) {
1773 if (!TINYGLTF_DOUBLE_EQUAL(one[size_t(i)], other[size_t(i)])) return false;
1774 }
1775 return true;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001776}
1777
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001778bool Accessor::operator==(const Accessor &other) const {
1779 return this->bufferView == other.bufferView &&
1780 this->byteOffset == other.byteOffset &&
1781 this->componentType == other.componentType &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001782 this->count == other.count && this->extensions == other.extensions &&
1783 this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001784 Equals(this->maxValues, other.maxValues) &&
1785 Equals(this->minValues, other.minValues) && this->name == other.name &&
1786 this->normalized == other.normalized && this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001787}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001788bool Animation::operator==(const Animation &other) const {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001789 return this->channels == other.channels &&
1790 this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001791 this->name == other.name && this->samplers == other.samplers;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001792}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001793bool AnimationChannel::operator==(const AnimationChannel &other) const {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001794 return this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001795 this->target_node == other.target_node &&
1796 this->target_path == other.target_path &&
1797 this->sampler == other.sampler;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001798}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001799bool AnimationSampler::operator==(const AnimationSampler &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001800 return this->extras == other.extras && this->extensions == other.extensions &&
1801 this->input == other.input &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001802 this->interpolation == other.interpolation &&
1803 this->output == other.output;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001804}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001805bool Asset::operator==(const Asset &other) const {
1806 return this->copyright == other.copyright &&
1807 this->extensions == other.extensions && this->extras == other.extras &&
1808 this->generator == other.generator &&
1809 this->minVersion == other.minVersion && this->version == other.version;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001810}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001811bool Buffer::operator==(const Buffer &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001812 return this->data == other.data && this->extensions == other.extensions &&
1813 this->extras == other.extras && this->name == other.name &&
1814 this->uri == other.uri;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001815}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001816bool BufferView::operator==(const BufferView &other) const {
1817 return this->buffer == other.buffer && this->byteLength == other.byteLength &&
1818 this->byteOffset == other.byteOffset &&
1819 this->byteStride == other.byteStride && this->name == other.name &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001820 this->target == other.target && this->extensions == other.extensions &&
1821 this->extras == other.extras &&
Alexander Wood0d77a292019-01-26 08:58:45 -05001822 this->dracoDecoded == other.dracoDecoded;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001823}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001824bool Camera::operator==(const Camera &other) const {
1825 return this->name == other.name && this->extensions == other.extensions &&
1826 this->extras == other.extras &&
1827 this->orthographic == other.orthographic &&
1828 this->perspective == other.perspective && this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001829}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001830bool Image::operator==(const Image &other) const {
1831 return this->bufferView == other.bufferView &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001832 this->component == other.component &&
1833 this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001834 this->height == other.height && this->image == other.image &&
1835 this->mimeType == other.mimeType && this->name == other.name &&
1836 this->uri == other.uri && this->width == other.width;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001837}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001838bool Light::operator==(const Light &other) const {
1839 return Equals(this->color, other.color) && this->name == other.name &&
1840 this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001841}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001842bool Material::operator==(const Material &other) const {
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001843 return (this->pbrMetallicRoughness == other.pbrMetallicRoughness) &&
1844 (this->normalTexture == other.normalTexture) &&
1845 (this->occlusionTexture == other.occlusionTexture) &&
1846 (this->emissiveTexture == other.emissiveTexture) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09001847 Equals(this->emissiveFactor, other.emissiveFactor) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001848 (this->alphaMode == other.alphaMode) &&
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001849 TINYGLTF_DOUBLE_EQUAL(this->alphaCutoff, other.alphaCutoff) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001850 (this->doubleSided == other.doubleSided) &&
1851 (this->extensions == other.extensions) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09001852 (this->extras == other.extras) && (this->values == other.values) &&
1853 (this->additionalValues == other.additionalValues) &&
1854 (this->name == other.name);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001855}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001856bool Mesh::operator==(const Mesh &other) const {
1857 return this->extensions == other.extensions && this->extras == other.extras &&
Selmar Kok81b672b2019-10-18 16:08:44 +02001858 this->name == other.name && Equals(this->weights, other.weights) &&
1859 this->primitives == other.primitives;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001860}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001861bool Model::operator==(const Model &other) const {
1862 return this->accessors == other.accessors &&
1863 this->animations == other.animations && this->asset == other.asset &&
1864 this->buffers == other.buffers &&
1865 this->bufferViews == other.bufferViews &&
1866 this->cameras == other.cameras &&
1867 this->defaultScene == other.defaultScene &&
1868 this->extensions == other.extensions &&
1869 this->extensionsRequired == other.extensionsRequired &&
1870 this->extensionsUsed == other.extensionsUsed &&
1871 this->extras == other.extras && this->images == other.images &&
1872 this->lights == other.lights && this->materials == other.materials &&
1873 this->meshes == other.meshes && this->nodes == other.nodes &&
1874 this->samplers == other.samplers && this->scenes == other.scenes &&
1875 this->skins == other.skins && this->textures == other.textures;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001876}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001877bool Node::operator==(const Node &other) const {
1878 return this->camera == other.camera && this->children == other.children &&
1879 this->extensions == other.extensions && this->extras == other.extras &&
1880 Equals(this->matrix, other.matrix) && this->mesh == other.mesh &&
1881 this->name == other.name && Equals(this->rotation, other.rotation) &&
1882 Equals(this->scale, other.scale) && this->skin == other.skin &&
1883 Equals(this->translation, other.translation) &&
1884 Equals(this->weights, other.weights);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001885}
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001886bool SpotLight::operator==(const SpotLight &other) const {
1887 return this->extensions == other.extensions && this->extras == other.extras &&
1888 TINYGLTF_DOUBLE_EQUAL(this->innerConeAngle, other.innerConeAngle) &&
1889 TINYGLTF_DOUBLE_EQUAL(this->outerConeAngle, other.outerConeAngle);
1890}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001891bool OrthographicCamera::operator==(const OrthographicCamera &other) const {
1892 return this->extensions == other.extensions && this->extras == other.extras &&
1893 TINYGLTF_DOUBLE_EQUAL(this->xmag, other.xmag) &&
1894 TINYGLTF_DOUBLE_EQUAL(this->ymag, other.ymag) &&
1895 TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
1896 TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001897}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001898bool Parameter::operator==(const Parameter &other) const {
1899 if (this->bool_value != other.bool_value ||
1900 this->has_number_value != other.has_number_value)
1901 return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001902
Selmar Kok2bda71c2018-10-05 14:36:05 +02001903 if (!TINYGLTF_DOUBLE_EQUAL(this->number_value, other.number_value))
1904 return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001905
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001906 if (this->json_double_value.size() != other.json_double_value.size())
1907 return false;
1908 for (auto &it : this->json_double_value) {
1909 auto otherIt = other.json_double_value.find(it.first);
1910 if (otherIt == other.json_double_value.end()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001911
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001912 if (!TINYGLTF_DOUBLE_EQUAL(it.second, otherIt->second)) return false;
1913 }
1914
1915 if (!Equals(this->number_array, other.number_array)) return false;
1916
1917 if (this->string_value != other.string_value) return false;
1918
1919 return true;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001920}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001921bool PerspectiveCamera::operator==(const PerspectiveCamera &other) const {
1922 return TINYGLTF_DOUBLE_EQUAL(this->aspectRatio, other.aspectRatio) &&
1923 this->extensions == other.extensions && this->extras == other.extras &&
1924 TINYGLTF_DOUBLE_EQUAL(this->yfov, other.yfov) &&
1925 TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
1926 TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001927}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001928bool Primitive::operator==(const Primitive &other) const {
1929 return this->attributes == other.attributes && this->extras == other.extras &&
1930 this->indices == other.indices && this->material == other.material &&
1931 this->mode == other.mode && this->targets == other.targets;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001932}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001933bool Sampler::operator==(const Sampler &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001934 return this->extensions == other.extensions && this->extras == other.extras &&
1935 this->magFilter == other.magFilter &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001936 this->minFilter == other.minFilter && this->name == other.name &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001937 this->wrapT == other.wrapT;
Syoyo Fujita2c521b32020-12-04 00:50:46 +09001938
1939 //this->wrapR == other.wrapR && this->wrapS == other.wrapS &&
Selmar Kok31cb7f92018-10-03 15:39:05 +02001940}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001941bool Scene::operator==(const Scene &other) const {
1942 return this->extensions == other.extensions && this->extras == other.extras &&
1943 this->name == other.name && this->nodes == other.nodes;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001944}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001945bool Skin::operator==(const Skin &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001946 return this->extensions == other.extensions && this->extras == other.extras &&
1947 this->inverseBindMatrices == other.inverseBindMatrices &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001948 this->joints == other.joints && this->name == other.name &&
1949 this->skeleton == other.skeleton;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001950}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001951bool Texture::operator==(const Texture &other) const {
1952 return this->extensions == other.extensions && this->extras == other.extras &&
1953 this->name == other.name && this->sampler == other.sampler &&
1954 this->source == other.source;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001955}
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001956bool TextureInfo::operator==(const TextureInfo &other) const {
1957 return this->extensions == other.extensions && this->extras == other.extras &&
1958 this->index == other.index && this->texCoord == other.texCoord;
1959}
1960bool NormalTextureInfo::operator==(const NormalTextureInfo &other) const {
1961 return this->extensions == other.extensions && this->extras == other.extras &&
1962 this->index == other.index && this->texCoord == other.texCoord &&
1963 TINYGLTF_DOUBLE_EQUAL(this->scale, other.scale);
1964}
1965bool OcclusionTextureInfo::operator==(const OcclusionTextureInfo &other) const {
1966 return this->extensions == other.extensions && this->extras == other.extras &&
1967 this->index == other.index && this->texCoord == other.texCoord &&
1968 TINYGLTF_DOUBLE_EQUAL(this->strength, other.strength);
1969}
1970bool PbrMetallicRoughness::operator==(const PbrMetallicRoughness &other) const {
1971 return this->extensions == other.extensions && this->extras == other.extras &&
1972 (this->baseColorTexture == other.baseColorTexture) &&
1973 (this->metallicRoughnessTexture == other.metallicRoughnessTexture) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09001974 Equals(this->baseColorFactor, other.baseColorFactor) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001975 TINYGLTF_DOUBLE_EQUAL(this->metallicFactor, other.metallicFactor) &&
1976 TINYGLTF_DOUBLE_EQUAL(this->roughnessFactor, other.roughnessFactor);
1977}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001978bool Value::operator==(const Value &other) const {
1979 return Equals(*this, other);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001980}
1981
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001982static void swap4(unsigned int *val) {
1983#ifdef TINYGLTF_LITTLE_ENDIAN
1984 (void)val;
1985#else
1986 unsigned int tmp = *val;
1987 unsigned char *dst = reinterpret_cast<unsigned char *>(val);
1988 unsigned char *src = reinterpret_cast<unsigned char *>(&tmp);
1989
1990 dst[0] = src[3];
1991 dst[1] = src[2];
1992 dst[2] = src[1];
1993 dst[3] = src[0];
1994#endif
1995}
1996
Syoyo Fujitabeded612016-05-01 20:03:43 +09001997static std::string JoinPath(const std::string &path0,
1998 const std::string &path1) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001999 if (path0.empty()) {
2000 return path1;
2001 } else {
2002 // check '/'
2003 char lastChar = *path0.rbegin();
2004 if (lastChar != '/') {
2005 return path0 + std::string("/") + path1;
2006 } else {
2007 return path0 + path1;
2008 }
2009 }
2010}
2011
Syoyo Fujita643ce102016-05-01 17:19:37 +09002012static std::string FindFile(const std::vector<std::string> &paths,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002013 const std::string &filepath, FsCallbacks *fs) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002014 if (fs == nullptr || fs->ExpandFilePath == nullptr ||
2015 fs->FileExists == nullptr) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002016 // Error, fs callback[s] missing
2017 return std::string();
2018 }
2019
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002020 for (size_t i = 0; i < paths.size(); i++) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002021 std::string absPath =
2022 fs->ExpandFilePath(JoinPath(paths[i], filepath), fs->user_data);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002023 if (fs->FileExists(absPath, fs->user_data)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002024 return absPath;
2025 }
2026 }
2027
2028 return std::string();
2029}
2030
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09002031static std::string GetFilePathExtension(const std::string &FileName) {
johan bowald642a3432018-04-01 12:37:18 +02002032 if (FileName.find_last_of(".") != std::string::npos)
2033 return FileName.substr(FileName.find_last_of(".") + 1);
2034 return "";
2035}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002036
Syoyo Fujita643ce102016-05-01 17:19:37 +09002037static std::string GetBaseDir(const std::string &filepath) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002038 if (filepath.find_last_of("/\\") != std::string::npos)
2039 return filepath.substr(0, filepath.find_last_of("/\\"));
2040 return "";
2041}
2042
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002043static std::string GetBaseFilename(const std::string &filepath) {
Alexander Wood190382a2021-10-08 12:19:13 -04002044 constexpr char path_separators[2] = { '/', '\\' };
2045 auto idx = filepath.find_last_of(path_separators);
2046 if (idx != std::string::npos)
2047 return filepath.substr(idx + 1);
2048 return filepath;
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002049}
2050
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002051std::string base64_encode(unsigned char const *, unsigned int len);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002052std::string base64_decode(std::string const &s);
2053
2054/*
2055 base64.cpp and base64.h
2056
2057 Copyright (C) 2004-2008 René Nyffenegger
2058
2059 This source code is provided 'as-is', without any express or implied
2060 warranty. In no event will the author be held liable for any damages
2061 arising from the use of this software.
2062
2063 Permission is granted to anyone to use this software for any purpose,
2064 including commercial applications, and to alter it and redistribute it
2065 freely, subject to the following restrictions:
2066
2067 1. The origin of this source code must not be misrepresented; you must not
2068 claim that you wrote the original source code. If you use this source code
2069 in a product, an acknowledgment in the product documentation would be
2070 appreciated but is not required.
2071
2072 2. Altered source versions must be plainly marked as such, and must not be
2073 misrepresented as being the original source code.
2074
2075 3. This notice may not be removed or altered from any source distribution.
2076
2077 René Nyffenegger rene.nyffenegger@adp-gmbh.ch
2078
2079*/
2080
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002081#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09002082#pragma clang diagnostic push
Syoyo Fujita643ce102016-05-01 17:19:37 +09002083#pragma clang diagnostic ignored "-Wsign-conversion"
2084#pragma clang diagnostic ignored "-Wconversion"
Syoyo Fujita6fd91622016-05-01 20:27:30 +09002085#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002086
2087static inline bool is_base64(unsigned char c) {
2088 return (isalnum(c) || (c == '+') || (c == '/'));
2089}
2090
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002091std::string base64_encode(unsigned char const *bytes_to_encode,
2092 unsigned int in_len) {
johan bowald30c53472018-03-30 11:49:36 +02002093 std::string ret;
2094 int i = 0;
2095 int j = 0;
2096 unsigned char char_array_3[3];
2097 unsigned char char_array_4[4];
2098
Syoyo Fujitaff515702019-08-24 16:29:14 +09002099 const char *base64_chars =
2100 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2101 "abcdefghijklmnopqrstuvwxyz"
2102 "0123456789+/";
2103
johan bowald30c53472018-03-30 11:49:36 +02002104 while (in_len--) {
2105 char_array_3[i++] = *(bytes_to_encode++);
2106 if (i == 3) {
2107 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002108 char_array_4[1] =
2109 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
2110 char_array_4[2] =
2111 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
johan bowald30c53472018-03-30 11:49:36 +02002112 char_array_4[3] = char_array_3[2] & 0x3f;
2113
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002114 for (i = 0; (i < 4); i++) ret += base64_chars[char_array_4[i]];
johan bowald30c53472018-03-30 11:49:36 +02002115 i = 0;
2116 }
2117 }
2118
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002119 if (i) {
2120 for (j = i; j < 3; j++) char_array_3[j] = '\0';
johan bowald30c53472018-03-30 11:49:36 +02002121
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002122 char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
2123 char_array_4[1] =
2124 ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
2125 char_array_4[2] =
2126 ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
johan bowald30c53472018-03-30 11:49:36 +02002127
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002128 for (j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]];
johan bowald30c53472018-03-30 11:49:36 +02002129
Syoyo Fujita719d7e42018-03-30 19:26:35 +09002130 while ((i++ < 3)) ret += '=';
johan bowald30c53472018-03-30 11:49:36 +02002131 }
2132
2133 return ret;
johan bowald30c53472018-03-30 11:49:36 +02002134}
2135
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002136std::string base64_decode(std::string const &encoded_string) {
Syoyo Fujita8c6774c2016-05-01 20:36:29 +09002137 int in_len = static_cast<int>(encoded_string.size());
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002138 int i = 0;
2139 int j = 0;
2140 int in_ = 0;
2141 unsigned char char_array_4[4], char_array_3[3];
2142 std::string ret;
2143
Syoyo Fujitaff515702019-08-24 16:29:14 +09002144 const std::string base64_chars =
2145 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
2146 "abcdefghijklmnopqrstuvwxyz"
2147 "0123456789+/";
2148
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002149 while (in_len-- && (encoded_string[in_] != '=') &&
2150 is_base64(encoded_string[in_])) {
2151 char_array_4[i++] = encoded_string[in_];
2152 in_++;
2153 if (i == 4) {
2154 for (i = 0; i < 4; i++)
Syoyo Fujitabeded612016-05-01 20:03:43 +09002155 char_array_4[i] =
2156 static_cast<unsigned char>(base64_chars.find(char_array_4[i]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002157
2158 char_array_3[0] =
2159 (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
2160 char_array_3[1] =
2161 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
2162 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
2163
Syoyo Fujita7c877972016-03-08 01:31:49 +09002164 for (i = 0; (i < 3); i++) ret += char_array_3[i];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002165 i = 0;
2166 }
2167 }
2168
2169 if (i) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09002170 for (j = i; j < 4; j++) char_array_4[j] = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002171
2172 for (j = 0; j < 4; j++)
Syoyo Fujitabeded612016-05-01 20:03:43 +09002173 char_array_4[j] =
2174 static_cast<unsigned char>(base64_chars.find(char_array_4[j]));
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002175
2176 char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
2177 char_array_3[1] =
2178 ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
2179 char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
2180
Syoyo Fujita7c877972016-03-08 01:31:49 +09002181 for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002182 }
2183
2184 return ret;
2185}
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09002186#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09002187#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +09002188#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002189
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002190// https://github.com/syoyo/tinygltf/issues/228
2191// TODO(syoyo): Use uriparser https://uriparser.github.io/ for stricter Uri
2192// decoding?
2193//
2194// https://stackoverflow.com/questions/18307429/encode-decode-url-in-c
2195// http://dlib.net/dlib/server/server_http.cpp.html
2196
2197// --- dlib beign ------------------------------------------------------------
2198// Copyright (C) 2003 Davis E. King (davis@dlib.net)
2199// License: Boost Software License See LICENSE.txt for the full license.
2200
2201namespace dlib {
2202
2203#if 0
Syoyo Fujita40982712020-03-03 18:49:27 +09002204 inline unsigned char to_hex( unsigned char x )
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002205 {
2206 return x + (x > 9 ? ('A'-10) : '0');
2207 }
2208
Syoyo Fujita40982712020-03-03 18:49:27 +09002209 const std::string urlencode( const std::string& s )
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002210 {
2211 std::ostringstream os;
2212
2213 for ( std::string::const_iterator ci = s.begin(); ci != s.end(); ++ci )
2214 {
2215 if ( (*ci >= 'a' && *ci <= 'z') ||
2216 (*ci >= 'A' && *ci <= 'Z') ||
2217 (*ci >= '0' && *ci <= '9') )
2218 { // allowed
2219 os << *ci;
2220 }
2221 else if ( *ci == ' ')
2222 {
2223 os << '+';
2224 }
2225 else
2226 {
2227 os << '%' << to_hex(static_cast<unsigned char>(*ci >> 4)) << to_hex(static_cast<unsigned char>(*ci % 16));
2228 }
2229 }
2230
2231 return os.str();
2232 }
2233#endif
2234
2235inline 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 *) {
2621#ifdef _WIN32
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002622 // Assume input `filepath` is encoded in UTF-8
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002623 std::wstring wfilepath = UTF8ToWchar(filepath);
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002624 DWORD wlen = ExpandEnvironmentStringsW(wfilepath.c_str(), nullptr, 0);
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002625 wchar_t *wstr = new wchar_t[wlen];
2626 ExpandEnvironmentStringsW(wfilepath.c_str(), wstr, wlen);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002627
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002628 std::wstring ws(wstr);
2629 delete[] wstr;
2630 return WcharToUTF8(ws);
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002631
Paolo Jovone6601bf2018-07-07 20:43:33 +02002632#else
2633
2634#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \
Julian Smith0598a202021-08-25 12:06:08 +01002635 defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__OpenBSD__)
Paolo Jovone6601bf2018-07-07 20:43:33 +02002636 // no expansion
2637 std::string s = filepath;
2638#else
2639 std::string s;
2640 wordexp_t p;
2641
2642 if (filepath.empty()) {
2643 return "";
2644 }
2645
Frank Galliganaa3c5a12020-01-13 15:06:56 -08002646 // Quote the string to keep any spaces in filepath intact.
2647 std::string quoted_path = "\"" + filepath + "\"";
Paolo Jovone6601bf2018-07-07 20:43:33 +02002648 // char** w;
Frank Galliganaa3c5a12020-01-13 15:06:56 -08002649 int ret = wordexp(quoted_path.c_str(), &p, 0);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002650 if (ret) {
2651 // err
2652 s = filepath;
2653 return s;
2654 }
2655
2656 // Use first element only.
2657 if (p.we_wordv) {
2658 s = std::string(p.we_wordv[0]);
2659 wordfree(&p);
2660 } else {
2661 s = filepath;
2662 }
2663
2664#endif
2665
2666 return s;
2667#endif
2668}
2669
2670bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
2671 const std::string &filepath, void *) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01002672#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
2673 if (asset_manager) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002674 AAsset *asset = AAssetManager_open(asset_manager, filepath.c_str(),
2675 AASSET_MODE_STREAMING);
Sascha Willems5f9cb242018-12-28 20:53:41 +01002676 if (!asset) {
2677 if (err) {
2678 (*err) += "File open error : " + filepath + "\n";
2679 }
2680 return false;
2681 }
2682 size_t size = AAsset_getLength(asset);
Syoyo Fujita73c4cce2020-04-28 01:06:34 +09002683 if (size == 0) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01002684 if (err) {
2685 (*err) += "Invalid file size : " + filepath +
2686 " (does the path point to a directory?)";
2687 }
Syoyo Fujita73c4cce2020-04-28 01:06:34 +09002688 return false;
Sascha Willems5f9cb242018-12-28 20:53:41 +01002689 }
2690 out->resize(size);
2691 AAsset_read(asset, reinterpret_cast<char *>(&out->at(0)), size);
2692 AAsset_close(asset);
2693 return true;
2694 } else {
2695 if (err) {
2696 (*err) += "No asset manager specified : " + filepath + "\n";
2697 }
2698 return false;
2699 }
2700#else
Harokyang5cecef22019-10-30 15:16:46 +08002701#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002702#if defined(__GLIBCXX__) // mingw
2703 int file_descriptor =
2704 _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY);
Syoyo Fujita45cac782019-11-09 20:42:55 +09002705 __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
2706 std::istream f(&wfile_buf);
Syoyo Fujitaaba57bb2020-05-18 20:50:45 +09002707#elif defined(_MSC_VER) || defined(_LIBCPP_VERSION)
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04002708 // For libcxx, assume _LIBCPP_HAS_OPEN_WITH_WCHAR is defined to accept
2709 // `wchar_t *`
Harokyangfb256602019-10-30 16:13:52 +08002710 std::ifstream f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary);
Syoyo Fujitaaba57bb2020-05-18 20:50:45 +09002711#else
2712 // Unknown compiler/runtime
Syoyo Fujita45cac782019-11-09 20:42:55 +09002713 std::ifstream f(filepath.c_str(), std::ifstream::binary);
2714#endif
Harokyang5cecef22019-10-30 15:16:46 +08002715#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002716 std::ifstream f(filepath.c_str(), std::ifstream::binary);
Harokyang5cecef22019-10-30 15:16:46 +08002717#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002718 if (!f) {
2719 if (err) {
2720 (*err) += "File open error : " + filepath + "\n";
2721 }
2722 return false;
2723 }
2724
2725 f.seekg(0, f.end);
2726 size_t sz = static_cast<size_t>(f.tellg());
2727 f.seekg(0, f.beg);
2728
Syoyo Fujitae8862472019-10-20 17:47:50 +09002729 if (int64_t(sz) < 0) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002730 if (err) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002731 (*err) += "Invalid file size : " + filepath +
2732 " (does the path point to a directory?)";
Paolo Jovone6601bf2018-07-07 20:43:33 +02002733 }
2734 return false;
2735 } else if (sz == 0) {
2736 if (err) {
2737 (*err) += "File is empty : " + filepath + "\n";
2738 }
2739 return false;
2740 }
2741
2742 out->resize(sz);
2743 f.read(reinterpret_cast<char *>(&out->at(0)),
2744 static_cast<std::streamsize>(sz));
Paolo Jovone6601bf2018-07-07 20:43:33 +02002745
2746 return true;
Sascha Willems5f9cb242018-12-28 20:53:41 +01002747#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002748}
2749
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002750bool WriteWholeFile(std::string *err, const std::string &filepath,
2751 const std::vector<unsigned char> &contents, void *) {
Harokyangfb256602019-10-30 16:13:52 +08002752#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002753#if defined(__GLIBCXX__) // mingw
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09002754 int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(),
2755 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
2756 __gnu_cxx::stdio_filebuf<char> wfile_buf(
2757 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09002758 std::ostream f(&wfile_buf);
2759#elif defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08002760 std::ofstream f(UTF8ToWchar(filepath).c_str(), std::ofstream::binary);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002761#else // clang?
Syoyo Fujita45cac782019-11-09 20:42:55 +09002762 std::ofstream f(filepath.c_str(), std::ofstream::binary);
2763#endif
Harokyangfb256602019-10-30 16:13:52 +08002764#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002765 std::ofstream f(filepath.c_str(), std::ofstream::binary);
Harokyangfb256602019-10-30 16:13:52 +08002766#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002767 if (!f) {
2768 if (err) {
2769 (*err) += "File open error for writing : " + filepath + "\n";
2770 }
2771 return false;
2772 }
2773
2774 f.write(reinterpret_cast<const char *>(&contents.at(0)),
2775 static_cast<std::streamsize>(contents.size()));
2776 if (!f) {
2777 if (err) {
2778 (*err) += "File write error: " + filepath + "\n";
2779 }
2780 return false;
2781 }
2782
Paolo Jovone6601bf2018-07-07 20:43:33 +02002783 return true;
2784}
2785
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002786#endif // TINYGLTF_NO_FS
Paolo Jovone6601bf2018-07-07 20:43:33 +02002787
johan bowald642a3432018-04-01 12:37:18 +02002788static std::string MimeToExt(const std::string &mimeType) {
2789 if (mimeType == "image/jpeg") {
2790 return "jpg";
2791 } else if (mimeType == "image/png") {
2792 return "png";
2793 } else if (mimeType == "image/bmp") {
2794 return "bmp";
2795 } else if (mimeType == "image/gif") {
2796 return "gif";
2797 }
2798
2799 return "";
2800}
2801
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09002802static void UpdateImageObject(Image &image, std::string &baseDir, int index,
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002803 bool embedImages,
2804 WriteImageDataFunction *WriteImageData = nullptr,
2805 void *user_data = nullptr) {
johan bowald642a3432018-04-01 12:37:18 +02002806 std::string filename;
2807 std::string ext;
FsiGuy00015623db855c62020-03-09 16:57:21 -05002808 // If image has uri, use it it as a filename
johan bowald642a3432018-04-01 12:37:18 +02002809 if (image.uri.size()) {
2810 filename = GetBaseFilename(image.uri);
2811 ext = GetFilePathExtension(filename);
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09002812 } else if (image.bufferView != -1) {
2813 // If there's no URI and the data exists in a buffer,
2814 // don't change properties or write images
johan bowald642a3432018-04-01 12:37:18 +02002815 } else if (image.name.size()) {
2816 ext = MimeToExt(image.mimeType);
2817 // Otherwise use name as filename
2818 filename = image.name + "." + ext;
2819 } else {
2820 ext = MimeToExt(image.mimeType);
2821 // Fallback to index of image as filename
2822 filename = std::to_string(index) + "." + ext;
2823 }
2824
2825 // If callback is set, modify image data object
FsiGuy00015623db855c62020-03-09 16:57:21 -05002826 if (*WriteImageData != nullptr && !filename.empty()) {
johan bowald642a3432018-04-01 12:37:18 +02002827 std::string uri;
2828 (*WriteImageData)(&baseDir, &filename, &image, embedImages, user_data);
2829 }
2830}
2831
Selmar Kok0d0e97e2018-08-22 14:01:57 +02002832bool IsDataURI(const std::string &in) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002833 std::string header = "data:application/octet-stream;base64,";
2834 if (in.find(header) == 0) {
2835 return true;
2836 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002837
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002838 header = "data:image/jpeg;base64,";
2839 if (in.find(header) == 0) {
2840 return true;
2841 }
Squareys43374632018-03-13 22:20:48 +01002842
Syoyo Fujita620eed12016-01-02 23:37:12 +09002843 header = "data:image/png;base64,";
2844 if (in.find(header) == 0) {
2845 return true;
2846 }
Squareys43374632018-03-13 22:20:48 +01002847
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002848 header = "data:image/bmp;base64,";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002849 if (in.find(header) == 0) {
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002850 return true;
2851 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002852
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002853 header = "data:image/gif;base64,";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002854 if (in.find(header) == 0) {
Syoyo Fujita620eed12016-01-02 23:37:12 +09002855 return true;
2856 }
2857
Luke San Antoniocaa24b02016-06-15 02:23:09 -04002858 header = "data:text/plain;base64,";
2859 if (in.find(header) == 0) {
2860 return true;
2861 }
2862
Syoyo Fujita20244e12018-03-15 11:01:05 -05002863 header = "data:application/gltf-buffer;base64,";
2864 if (in.find(header) == 0) {
2865 return true;
2866 }
2867
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002868 return false;
2869}
2870
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002871bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
2872 const std::string &in, size_t reqBytes, bool checkSize) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002873 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujita620eed12016-01-02 23:37:12 +09002874 std::string data;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002875 if (in.find(header) == 0) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09002876 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002877 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002878
2879 if (data.empty()) {
2880 header = "data:image/jpeg;base64,";
2881 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002882 mime_type = "image/jpeg";
Syoyo Fujita7c877972016-03-08 01:31:49 +09002883 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09002884 }
2885 }
2886
2887 if (data.empty()) {
2888 header = "data:image/png;base64,";
2889 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002890 mime_type = "image/png";
Syoyo Fujita7c877972016-03-08 01:31:49 +09002891 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09002892 }
2893 }
Squareys43374632018-03-13 22:20:48 +01002894
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002895 if (data.empty()) {
2896 header = "data:image/bmp;base64,";
2897 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002898 mime_type = "image/bmp";
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002899 data = base64_decode(in.substr(header.size())); // cut mime string.
2900 }
2901 }
2902
2903 if (data.empty()) {
2904 header = "data:image/gif;base64,";
2905 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002906 mime_type = "image/gif";
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002907 data = base64_decode(in.substr(header.size())); // cut mime string.
2908 }
2909 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002910
2911 if (data.empty()) {
Luke San Antoniocaa24b02016-06-15 02:23:09 -04002912 header = "data:text/plain;base64,";
2913 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002914 mime_type = "text/plain";
Luke San Antoniocaa24b02016-06-15 02:23:09 -04002915 data = base64_decode(in.substr(header.size()));
2916 }
2917 }
2918
2919 if (data.empty()) {
Syoyo Fujita20244e12018-03-15 11:01:05 -05002920 header = "data:application/gltf-buffer;base64,";
2921 if (in.find(header) == 0) {
2922 data = base64_decode(in.substr(header.size()));
2923 }
2924 }
2925
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09002926 // TODO(syoyo): Allow empty buffer? #229
Syoyo Fujita20244e12018-03-15 11:01:05 -05002927 if (data.empty()) {
Syoyo Fujita620eed12016-01-02 23:37:12 +09002928 return false;
Syoyo Fujitabde70212016-02-07 17:38:17 +09002929 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002930
2931 if (checkSize) {
2932 if (data.size() != reqBytes) {
2933 return false;
2934 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002935 out->resize(reqBytes);
Syoyo Fujita620eed12016-01-02 23:37:12 +09002936 } else {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002937 out->resize(data.size());
Syoyo Fujita620eed12016-01-02 23:37:12 +09002938 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002939 std::copy(data.begin(), data.end(), out->begin());
Syoyo Fujita620eed12016-01-02 23:37:12 +09002940 return true;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002941}
2942
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002943namespace {
2944bool GetInt(const json &o, int &val) {
jrkoonce5cecc412019-08-29 11:45:04 -05002945#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002946 if (!o.IsDouble()) {
2947 if (o.IsInt()) {
2948 val = o.GetInt();
2949 return true;
2950 } else if (o.IsUint()) {
2951 val = static_cast<int>(o.GetUint());
2952 return true;
2953 } else if (o.IsInt64()) {
2954 val = static_cast<int>(o.GetInt64());
2955 return true;
2956 } else if (o.IsUint64()) {
2957 val = static_cast<int>(o.GetUint64());
jrkooncecba5d6c2019-08-29 11:26:22 -05002958 return true;
2959 }
jrkoonce5cecc412019-08-29 11:45:04 -05002960 }
2961
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002962 return false;
jrkoonce5cecc412019-08-29 11:45:04 -05002963#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002964 auto type = o.type();
jrkooncecba5d6c2019-08-29 11:26:22 -05002965
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002966 if ((type == json::value_t::number_integer) ||
2967 (type == json::value_t::number_unsigned)) {
2968 val = static_cast<int>(o.get<int64_t>());
2969 return true;
jrkoonce5cecc412019-08-29 11:45:04 -05002970 }
2971
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002972 return false;
jrkoonce5cecc412019-08-29 11:45:04 -05002973#endif
jrkooncecba5d6c2019-08-29 11:26:22 -05002974}
2975
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002976#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09002977bool GetDouble(const json &o, double &val) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002978 if (o.IsDouble()) {
2979 val = o.GetDouble();
2980 return true;
2981 }
2982
2983 return false;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002984}
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09002985#endif
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002986
2987bool GetNumber(const json &o, double &val) {
2988#ifdef TINYGLTF_USE_RAPIDJSON
2989 if (o.IsNumber()) {
2990 val = o.GetDouble();
2991 return true;
2992 }
2993
2994 return false;
2995#else
2996 if (o.is_number()) {
2997 val = o.get<double>();
2998 return true;
2999 }
3000
3001 return false;
3002#endif
3003}
3004
3005bool GetString(const json &o, std::string &val) {
3006#ifdef TINYGLTF_USE_RAPIDJSON
3007 if (o.IsString()) {
3008 val = o.GetString();
3009 return true;
3010 }
3011
3012 return false;
3013#else
3014 if (o.type() == json::value_t::string) {
3015 val = o.get<std::string>();
3016 return true;
3017 }
3018
3019 return false;
3020#endif
3021}
3022
3023bool IsArray(const json &o) {
3024#ifdef TINYGLTF_USE_RAPIDJSON
3025 return o.IsArray();
3026#else
3027 return o.is_array();
3028#endif
3029}
3030
3031json_const_array_iterator ArrayBegin(const json &o) {
3032#ifdef TINYGLTF_USE_RAPIDJSON
3033 return o.Begin();
3034#else
3035 return o.begin();
3036#endif
3037}
3038
3039json_const_array_iterator ArrayEnd(const json &o) {
3040#ifdef TINYGLTF_USE_RAPIDJSON
3041 return o.End();
3042#else
3043 return o.end();
3044#endif
3045}
3046
3047bool IsObject(const json &o) {
3048#ifdef TINYGLTF_USE_RAPIDJSON
3049 return o.IsObject();
3050#else
3051 return o.is_object();
3052#endif
3053}
3054
3055json_const_iterator ObjectBegin(const json &o) {
3056#ifdef TINYGLTF_USE_RAPIDJSON
3057 return o.MemberBegin();
3058#else
3059 return o.begin();
3060#endif
3061}
3062
3063json_const_iterator ObjectEnd(const json &o) {
3064#ifdef TINYGLTF_USE_RAPIDJSON
3065 return o.MemberEnd();
3066#else
3067 return o.end();
3068#endif
3069}
3070
Rahul Sheth01d54382020-07-10 14:27:37 -04003071// Making this a const char* results in a pointer to a temporary when
3072// TINYGLTF_USE_RAPIDJSON is off.
3073std::string GetKey(json_const_iterator &it) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003074#ifdef TINYGLTF_USE_RAPIDJSON
3075 return it->name.GetString();
3076#else
3077 return it.key().c_str();
3078#endif
3079}
3080
3081bool FindMember(const json &o, const char *member, json_const_iterator &it) {
3082#ifdef TINYGLTF_USE_RAPIDJSON
3083 if (!o.IsObject()) {
3084 return false;
3085 }
3086 it = o.FindMember(member);
3087 return it != o.MemberEnd();
3088#else
3089 it = o.find(member);
3090 return it != o.end();
3091#endif
3092}
3093
3094const json &GetValue(json_const_iterator &it) {
3095#ifdef TINYGLTF_USE_RAPIDJSON
3096 return it->value;
3097#else
3098 return it.value();
3099#endif
3100}
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003101
3102std::string JsonToString(const json &o, int spacing = -1) {
3103#ifdef TINYGLTF_USE_RAPIDJSON
3104 using namespace rapidjson;
3105 StringBuffer buffer;
3106 if (spacing == -1) {
3107 Writer<StringBuffer> writer(buffer);
3108 o.Accept(writer);
3109 } else {
3110 PrettyWriter<StringBuffer> writer(buffer);
3111 writer.SetIndent(' ', uint32_t(spacing));
3112 o.Accept(writer);
3113 }
3114 return buffer.GetString();
3115#else
3116 return o.dump(spacing);
3117#endif
3118}
3119
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003120} // namespace
3121
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003122static bool ParseJsonAsValue(Value *ret, const json &o) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003123 Value val{};
jrkooncecba5d6c2019-08-29 11:26:22 -05003124#ifdef TINYGLTF_USE_RAPIDJSON
3125 using rapidjson::Type;
3126 switch (o.GetType()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003127 case Type::kObjectType: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003128 Value::Object value_object;
jrkooncecba5d6c2019-08-29 11:26:22 -05003129 for (auto it = o.MemberBegin(); it != o.MemberEnd(); ++it) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003130 Value entry;
jrkooncecba5d6c2019-08-29 11:26:22 -05003131 ParseJsonAsValue(&entry, it->value);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003132 if (entry.Type() != NULL_TYPE)
3133 value_object.emplace(GetKey(it), std::move(entry));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003134 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003135 if (value_object.size() > 0) val = Value(std::move(value_object));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003136 } break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003137 case Type::kArrayType: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003138 Value::Array value_array;
jrkoonce5cecc412019-08-29 11:45:04 -05003139 value_array.reserve(o.Size());
jrkooncecba5d6c2019-08-29 11:26:22 -05003140 for (auto it = o.Begin(); it != o.End(); ++it) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003141 Value entry;
jrkooncecba5d6c2019-08-29 11:26:22 -05003142 ParseJsonAsValue(&entry, *it);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003143 if (entry.Type() != NULL_TYPE)
3144 value_array.emplace_back(std::move(entry));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003145 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003146 if (value_array.size() > 0) val = Value(std::move(value_array));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003147 } break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003148 case Type::kStringType:
3149 val = Value(std::string(o.GetString()));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003150 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003151 case Type::kFalseType:
3152 case Type::kTrueType:
3153 val = Value(o.GetBool());
Selmar Kokfa7022f2018-04-04 18:10:20 +02003154 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003155 case Type::kNumberType:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003156 if (!o.IsDouble()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003157 int i = 0;
3158 GetInt(o, i);
3159 val = Value(i);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003160 } else {
jrkooncecba5d6c2019-08-29 11:26:22 -05003161 double d = 0.0;
3162 GetDouble(o, d);
3163 val = Value(d);
3164 }
Selmar Kokfa7022f2018-04-04 18:10:20 +02003165 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003166 case Type::kNullType:
Selmar Kokfa7022f2018-04-04 18:10:20 +02003167 break;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003168 // all types are covered, so no `case default`
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003169 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003170#else
3171 switch (o.type()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003172 case json::value_t::object: {
3173 Value::Object value_object;
3174 for (auto it = o.begin(); it != o.end(); it++) {
3175 Value entry;
3176 ParseJsonAsValue(&entry, it.value());
3177 if (entry.Type() != NULL_TYPE)
3178 value_object.emplace(it.key(), std::move(entry));
3179 }
3180 if (value_object.size() > 0) val = Value(std::move(value_object));
3181 } break;
3182 case json::value_t::array: {
3183 Value::Array value_array;
3184 value_array.reserve(o.size());
3185 for (auto it = o.begin(); it != o.end(); it++) {
3186 Value entry;
3187 ParseJsonAsValue(&entry, it.value());
3188 if (entry.Type() != NULL_TYPE)
3189 value_array.emplace_back(std::move(entry));
3190 }
3191 if (value_array.size() > 0) val = Value(std::move(value_array));
3192 } break;
3193 case json::value_t::string:
3194 val = Value(o.get<std::string>());
3195 break;
3196 case json::value_t::boolean:
3197 val = Value(o.get<bool>());
3198 break;
3199 case json::value_t::number_integer:
3200 case json::value_t::number_unsigned:
3201 val = Value(static_cast<int>(o.get<int64_t>()));
3202 break;
3203 case json::value_t::number_float:
3204 val = Value(o.get<double>());
3205 break;
3206 case json::value_t::null:
3207 case json::value_t::discarded:
3208 // default:
3209 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003210 }
3211#endif
3212 if (ret) *ret = std::move(val);
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003213
Selmar Kokfa7022f2018-04-04 18:10:20 +02003214 return val.Type() != NULL_TYPE;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003215}
3216
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003217static bool ParseExtrasProperty(Value *ret, const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003218 json_const_iterator it;
3219 if (!FindMember(o, "extras", it)) {
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003220 return false;
3221 }
3222
jrkooncecba5d6c2019-08-29 11:26:22 -05003223 return ParseJsonAsValue(ret, GetValue(it));
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003224}
3225
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003226static bool ParseBooleanProperty(bool *ret, std::string *err, const json &o,
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09003227 const std::string &property,
3228 const bool required,
3229 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003230 json_const_iterator it;
3231 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003232 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003233 if (err) {
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09003234 (*err) += "'" + property + "' property is missing";
3235 if (!parent_node.empty()) {
3236 (*err) += " in " + parent_node;
3237 }
3238 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003239 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003240 }
3241 return false;
3242 }
3243
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003244 auto &value = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003245
3246 bool isBoolean;
jrkoonce0d2b6ef2019-09-04 13:46:45 -05003247 bool boolValue = false;
jrkooncecba5d6c2019-08-29 11:26:22 -05003248#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003249 isBoolean = value.IsBool();
3250 if (isBoolean) {
3251 boolValue = value.GetBool();
3252 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003253#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003254 isBoolean = value.is_boolean();
3255 if (isBoolean) {
3256 boolValue = value.get<bool>();
3257 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003258#endif
3259 if (!isBoolean) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003260 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003261 if (err) {
3262 (*err) += "'" + property + "' property is not a bool type.\n";
3263 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003264 }
3265 return false;
3266 }
3267
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003268 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003269 (*ret) = boolValue;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003270 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003271
3272 return true;
3273}
3274
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003275static bool ParseIntegerProperty(int *ret, std::string *err, const json &o,
3276 const std::string &property,
3277 const bool required,
3278 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003279 json_const_iterator it;
3280 if (!FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003281 if (required) {
3282 if (err) {
3283 (*err) += "'" + property + "' property is missing";
3284 if (!parent_node.empty()) {
3285 (*err) += " in " + parent_node;
3286 }
3287 (*err) += ".\n";
3288 }
3289 }
3290 return false;
3291 }
3292
jrkooncecba5d6c2019-08-29 11:26:22 -05003293 int intValue;
3294 bool isInt = GetInt(GetValue(it), intValue);
3295 if (!isInt) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003296 if (required) {
3297 if (err) {
3298 (*err) += "'" + property + "' property is not an integer type.\n";
3299 }
3300 }
3301 return false;
3302 }
3303
3304 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003305 (*ret) = intValue;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003306 }
3307
3308 return true;
3309}
3310
3311static bool ParseUnsignedProperty(size_t *ret, std::string *err, const json &o,
3312 const std::string &property,
3313 const bool required,
3314 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003315 json_const_iterator it;
3316 if (!FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003317 if (required) {
3318 if (err) {
3319 (*err) += "'" + property + "' property is missing";
3320 if (!parent_node.empty()) {
3321 (*err) += " in " + parent_node;
3322 }
3323 (*err) += ".\n";
3324 }
3325 }
3326 return false;
3327 }
3328
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003329 auto &value = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003330
jrkoonce0d2b6ef2019-09-04 13:46:45 -05003331 size_t uValue = 0;
jrkooncecba5d6c2019-08-29 11:26:22 -05003332 bool isUValue;
3333#ifdef TINYGLTF_USE_RAPIDJSON
3334 isUValue = false;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003335 if (value.IsUint()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003336 uValue = value.GetUint();
3337 isUValue = true;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003338 } else if (value.IsUint64()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003339 uValue = value.GetUint64();
3340 isUValue = true;
3341 }
3342#else
3343 isUValue = value.is_number_unsigned();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003344 if (isUValue) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003345 uValue = value.get<size_t>();
3346 }
3347#endif
3348 if (!isUValue) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003349 if (required) {
3350 if (err) {
3351 (*err) += "'" + property + "' property is not a positive integer.\n";
3352 }
3353 }
3354 return false;
3355 }
3356
3357 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003358 (*ret) = uValue;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003359 }
3360
3361 return true;
3362}
3363
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003364static bool ParseNumberProperty(double *ret, std::string *err, const json &o,
Syoyo Fujita5b407452017-06-04 17:42:41 +09003365 const std::string &property,
3366 const bool required,
3367 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003368 json_const_iterator it;
3369
3370 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003371 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003372 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003373 (*err) += "'" + property + "' property is missing";
3374 if (!parent_node.empty()) {
3375 (*err) += " in " + parent_node;
3376 }
3377 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003378 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003379 }
3380 return false;
3381 }
3382
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003383 double numberValue;
3384 bool isNumber = GetNumber(GetValue(it), numberValue);
jrkooncecba5d6c2019-08-29 11:26:22 -05003385
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003386 if (!isNumber) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003387 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003388 if (err) {
3389 (*err) += "'" + property + "' property is not a number type.\n";
3390 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003391 }
3392 return false;
3393 }
3394
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003395 if (ret) {
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003396 (*ret) = numberValue;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003397 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003398
3399 return true;
3400}
3401
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003402static bool ParseNumberArrayProperty(std::vector<double> *ret, std::string *err,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003403 const json &o, const std::string &property,
3404 bool required,
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003405 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003406 json_const_iterator it;
3407 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003408 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003409 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003410 (*err) += "'" + property + "' property is missing";
3411 if (!parent_node.empty()) {
3412 (*err) += " in " + parent_node;
3413 }
3414 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003415 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003416 }
3417 return false;
3418 }
3419
jrkooncecba5d6c2019-08-29 11:26:22 -05003420 if (!IsArray(GetValue(it))) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003421 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003422 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003423 (*err) += "'" + property + "' property is not an array";
3424 if (!parent_node.empty()) {
3425 (*err) += " in " + parent_node;
3426 }
3427 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003428 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003429 }
3430 return false;
3431 }
3432
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003433 ret->clear();
jrkooncecba5d6c2019-08-29 11:26:22 -05003434 auto end = ArrayEnd(GetValue(it));
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003435 for (auto i = ArrayBegin(GetValue(it)); i != end; ++i) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003436 double numberValue;
jrkoonce9b6f52e2019-08-29 13:56:58 -05003437 const bool isNumber = GetNumber(*i, numberValue);
jrkooncecba5d6c2019-08-29 11:26:22 -05003438 if (!isNumber) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003439 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003440 if (err) {
3441 (*err) += "'" + property + "' property is not a number.\n";
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003442 if (!parent_node.empty()) {
3443 (*err) += " in " + parent_node;
3444 }
3445 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003446 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003447 }
3448 return false;
3449 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003450 ret->push_back(numberValue);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003451 }
3452
3453 return true;
3454}
3455
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003456static bool ParseIntegerArrayProperty(std::vector<int> *ret, std::string *err,
3457 const json &o,
3458 const std::string &property,
3459 bool required,
3460 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003461 json_const_iterator it;
3462 if (!FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003463 if (required) {
3464 if (err) {
3465 (*err) += "'" + property + "' property is missing";
3466 if (!parent_node.empty()) {
3467 (*err) += " in " + parent_node;
3468 }
3469 (*err) += ".\n";
3470 }
3471 }
3472 return false;
3473 }
3474
jrkooncecba5d6c2019-08-29 11:26:22 -05003475 if (!IsArray(GetValue(it))) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003476 if (required) {
3477 if (err) {
3478 (*err) += "'" + property + "' property is not an array";
3479 if (!parent_node.empty()) {
3480 (*err) += " in " + parent_node;
3481 }
3482 (*err) += ".\n";
3483 }
3484 }
3485 return false;
3486 }
3487
3488 ret->clear();
jrkooncecba5d6c2019-08-29 11:26:22 -05003489 auto end = ArrayEnd(GetValue(it));
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003490 for (auto i = ArrayBegin(GetValue(it)); i != end; ++i) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003491 int numberValue;
3492 bool isNumber = GetInt(*i, numberValue);
3493 if (!isNumber) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003494 if (required) {
3495 if (err) {
3496 (*err) += "'" + property + "' property is not an integer type.\n";
3497 if (!parent_node.empty()) {
3498 (*err) += " in " + parent_node;
3499 }
3500 (*err) += ".\n";
3501 }
3502 }
3503 return false;
3504 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003505 ret->push_back(numberValue);
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003506 }
3507
3508 return true;
3509}
3510
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003511static bool ParseStringProperty(
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003512 std::string *ret, std::string *err, const json &o,
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003513 const std::string &property, bool required,
3514 const std::string &parent_node = std::string()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003515 json_const_iterator it;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003516 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003517 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003518 if (err) {
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003519 (*err) += "'" + property + "' property is missing";
3520 if (parent_node.empty()) {
3521 (*err) += ".\n";
3522 } else {
3523 (*err) += " in `" + parent_node + "'.\n";
3524 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003525 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003526 }
3527 return false;
3528 }
3529
jrkooncecba5d6c2019-08-29 11:26:22 -05003530 std::string strValue;
3531 if (!GetString(GetValue(it), strValue)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003532 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003533 if (err) {
3534 (*err) += "'" + property + "' property is not a string type.\n";
3535 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003536 }
3537 return false;
3538 }
3539
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003540 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003541 (*ret) = std::move(strValue);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003542 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003543
3544 return true;
3545}
3546
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003547static bool ParseStringIntegerProperty(std::map<std::string, int> *ret,
3548 std::string *err, const json &o,
3549 const std::string &property,
3550 bool required,
3551 const std::string &parent = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003552 json_const_iterator it;
3553 if (!FindMember(o, property.c_str(), it)) {
Luke San Antonio19894c72016-06-14 21:19:51 -04003554 if (required) {
3555 if (err) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09003556 if (!parent.empty()) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003557 (*err) +=
3558 "'" + property + "' property is missing in " + parent + ".\n";
Syoyo Fujita57c10182017-10-19 18:48:26 +09003559 } else {
3560 (*err) += "'" + property + "' property is missing.\n";
3561 }
Luke San Antonio19894c72016-06-14 21:19:51 -04003562 }
3563 }
3564 return false;
3565 }
3566
jrkooncecba5d6c2019-08-29 11:26:22 -05003567 const json &dict = GetValue(it);
3568
Luke San Antonio19894c72016-06-14 21:19:51 -04003569 // Make sure we are dealing with an object / dictionary.
jrkooncecba5d6c2019-08-29 11:26:22 -05003570 if (!IsObject(dict)) {
Luke San Antonio19894c72016-06-14 21:19:51 -04003571 if (required) {
3572 if (err) {
3573 (*err) += "'" + property + "' property is not an object.\n";
3574 }
3575 }
3576 return false;
3577 }
3578
3579 ret->clear();
Luke San Antonio19894c72016-06-14 21:19:51 -04003580
jrkooncecba5d6c2019-08-29 11:26:22 -05003581 json_const_iterator dictIt(ObjectBegin(dict));
3582 json_const_iterator dictItEnd(ObjectEnd(dict));
Luke San Antonio19894c72016-06-14 21:19:51 -04003583
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09003584 for (; dictIt != dictItEnd; ++dictIt) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003585 int intVal;
3586 if (!GetInt(GetValue(dictIt), intVal)) {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09003587 if (required) {
3588 if (err) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003589 (*err) += "'" + property + "' value is not an integer type.\n";
Luke San Antonio19894c72016-06-14 21:19:51 -04003590 }
3591 }
3592 return false;
3593 }
3594
3595 // Insert into the list.
jrkooncecba5d6c2019-08-29 11:26:22 -05003596 (*ret)[GetKey(dictIt)] = intVal;
Luke San Antonio19894c72016-06-14 21:19:51 -04003597 }
3598 return true;
3599}
3600
Syoyo Fujita5b407452017-06-04 17:42:41 +09003601static bool ParseJSONProperty(std::map<std::string, double> *ret,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003602 std::string *err, const json &o,
Syoyo Fujita5b407452017-06-04 17:42:41 +09003603 const std::string &property, bool required) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003604 json_const_iterator it;
3605 if (!FindMember(o, property.c_str(), it)) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003606 if (required) {
Syoyo Fujita5b407452017-06-04 17:42:41 +09003607 if (err) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003608 (*err) += "'" + property + "' property is missing. \n'";
3609 }
3610 }
3611 return false;
3612 }
3613
jrkooncecba5d6c2019-08-29 11:26:22 -05003614 const json &obj = GetValue(it);
3615
3616 if (!IsObject(obj)) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003617 if (required) {
3618 if (err) {
3619 (*err) += "'" + property + "' property is not a JSON object.\n";
3620 }
3621 }
3622 return false;
3623 }
3624
3625 ret->clear();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003626
jrkooncecba5d6c2019-08-29 11:26:22 -05003627 json_const_iterator it2(ObjectBegin(obj));
3628 json_const_iterator itEnd(ObjectEnd(obj));
3629 for (; it2 != itEnd; ++it2) {
3630 double numVal;
3631 if (GetNumber(GetValue(it2), numVal))
3632 ret->emplace(std::string(GetKey(it2)), numVal);
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003633 }
3634
3635 return true;
3636}
3637
Selmar09d2ff12018-03-15 17:30:42 +01003638static bool ParseParameterProperty(Parameter *param, std::string *err,
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003639 const json &o, const std::string &prop,
3640 bool required) {
Selmar09d2ff12018-03-15 17:30:42 +01003641 // A parameter value can either be a string or an array of either a boolean or
3642 // a number. Booleans of any kind aren't supported here. Granted, it
3643 // complicates the Parameter structure and breaks it semantically in the sense
3644 // that the client probably works off the assumption that if the string is
3645 // empty the vector is used, etc. Would a tagged union work?
3646 if (ParseStringProperty(&param->string_value, err, o, prop, false)) {
3647 // Found string property.
3648 return true;
3649 } else if (ParseNumberArrayProperty(&param->number_array, err, o, prop,
3650 false)) {
3651 // Found a number array.
3652 return true;
Ben Buzbeef6af2242018-04-25 15:13:05 -07003653 } else if (ParseNumberProperty(&param->number_value, err, o, prop, false)) {
3654 return param->has_number_value = true;
Selmar09d2ff12018-03-15 17:30:42 +01003655 } else if (ParseJSONProperty(&param->json_double_value, err, o, prop,
3656 false)) {
3657 return true;
3658 } else if (ParseBooleanProperty(&param->bool_value, err, o, prop, false)) {
3659 return true;
3660 } else {
3661 if (required) {
3662 if (err) {
3663 (*err) += "parameter must be a string or number / number array.\n";
3664 }
3665 }
3666 return false;
3667 }
3668}
3669
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003670static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err,
3671 const json &o) {
Syoyo Fujita48f6db02018-04-15 18:40:55 +09003672 (void)err;
3673
jrkooncecba5d6c2019-08-29 11:26:22 -05003674 json_const_iterator it;
3675 if (!FindMember(o, "extensions", it)) {
Selmar09d2ff12018-03-15 17:30:42 +01003676 return false;
3677 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003678
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003679 auto &obj = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003680 if (!IsObject(obj)) {
Selmar09d2ff12018-03-15 17:30:42 +01003681 return false;
3682 }
3683 ExtensionMap extensions;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003684 json_const_iterator extIt = ObjectBegin(obj); // it.value().begin();
jrkooncecba5d6c2019-08-29 11:26:22 -05003685 json_const_iterator extEnd = ObjectEnd(obj);
3686 for (; extIt != extEnd; ++extIt) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003687 auto &itObj = GetValue(extIt);
jrkooncecba5d6c2019-08-29 11:26:22 -05003688 if (!IsObject(itObj)) continue;
3689 std::string key(GetKey(extIt));
3690 if (!ParseJsonAsValue(&extensions[key], itObj)) {
jrkoonce06c30c42019-09-03 15:56:48 -05003691 if (!key.empty()) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003692 // create empty object so that an extension object is still of type
3693 // object
jrkooncecba5d6c2019-08-29 11:26:22 -05003694 extensions[key] = Value{Value::Object{}};
Selmar Kokee3d0662018-10-08 16:20:43 +02003695 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003696 }
Selmar09d2ff12018-03-15 17:30:42 +01003697 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003698 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003699 (*ret) = std::move(extensions);
Selmar09d2ff12018-03-15 17:30:42 +01003700 }
3701 return true;
3702}
3703
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003704static bool ParseAsset(Asset *asset, std::string *err, const json &o,
3705 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09003706 ParseStringProperty(&asset->version, err, o, "version", true, "Asset");
3707 ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset");
3708 ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset");
Miguel Sousa22bfc842020-02-20 14:16:58 +01003709 ParseStringProperty(&asset->copyright, err, o, "copyright", false, "Asset");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003710
Selmar09d2ff12018-03-15 17:30:42 +01003711 ParseExtensionsProperty(&asset->extensions, err, o);
3712
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +00003713 // Unity exporter version is added as extra here
3714 ParseExtrasProperty(&(asset->extras), o);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003715
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003716 if (store_original_json_for_extras_and_extensions) {
3717 {
3718 json_const_iterator it;
3719 if (FindMember(o, "extensions", it)) {
3720 asset->extensions_json_string = JsonToString(GetValue(it));
3721 }
3722 }
3723 {
3724 json_const_iterator it;
3725 if (FindMember(o, "extras", it)) {
3726 asset->extras_json_string = JsonToString(GetValue(it));
3727 }
3728 }
3729 }
3730
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003731 return true;
3732}
3733
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003734static bool ParseImage(Image *image, const int image_idx, std::string *err,
3735 std::string *warn, const json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003736 bool store_original_json_for_extras_and_extensions,
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003737 const std::string &basedir, FsCallbacks *fs,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003738 LoadImageDataFunction *LoadImageData = nullptr,
Paolo Jovone6601bf2018-07-07 20:43:33 +02003739 void *load_image_user_data = nullptr) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003740 // A glTF image must either reference a bufferView or an image uri
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003741
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003742 // schema says oneOf [`bufferView`, `uri`]
3743 // TODO(syoyo): Check the type of each parameters.
jrkooncecba5d6c2019-08-29 11:26:22 -05003744 json_const_iterator it;
3745 bool hasBufferView = FindMember(o, "bufferView", it);
3746 bool hasURI = FindMember(o, "uri", it);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003747
Syoyo Fujitaaf3ebb22019-01-06 18:55:57 +09003748 ParseStringProperty(&image->name, err, o, "name", false);
3749
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003750 if (hasBufferView && hasURI) {
3751 // Should not both defined.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003752 if (err) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09003753 (*err) +=
3754 "Only one of `bufferView` or `uri` should be defined, but both are "
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003755 "defined for image[" +
3756 std::to_string(image_idx) + "] name = \"" + image->name + "\"\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003757 }
3758 return false;
3759 }
3760
3761 if (!hasBufferView && !hasURI) {
3762 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003763 (*err) += "Neither required `bufferView` nor `uri` defined for image[" +
3764 std::to_string(image_idx) + "] name = \"" + image->name +
3765 "\"\n";
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003766 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003767 return false;
3768 }
3769
Syoyo Fujita3e53feb2018-09-02 15:36:17 +09003770 ParseExtensionsProperty(&image->extensions, err, o);
Selmar Kok8eb39042018-10-05 14:29:35 +02003771 ParseExtrasProperty(&image->extras, o);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003772
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003773 if (store_original_json_for_extras_and_extensions) {
3774 {
3775 json_const_iterator eit;
3776 if (FindMember(o, "extensions", eit)) {
3777 image->extensions_json_string = JsonToString(GetValue(eit));
3778 }
3779 }
3780 {
3781 json_const_iterator eit;
3782 if (FindMember(o, "extras", eit)) {
3783 image->extras_json_string = JsonToString(GetValue(eit));
3784 }
3785 }
3786 }
3787
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003788 if (hasBufferView) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003789 int bufferView = -1;
3790 if (!ParseIntegerProperty(&bufferView, err, o, "bufferView", true)) {
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003791 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003792 (*err) += "Failed to parse `bufferView` for image[" +
3793 std::to_string(image_idx) + "] name = \"" + image->name +
3794 "\"\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003795 }
3796 return false;
3797 }
3798
3799 std::string mime_type;
3800 ParseStringProperty(&mime_type, err, o, "mimeType", false);
3801
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003802 int width = 0;
3803 ParseIntegerProperty(&width, err, o, "width", false);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003804
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003805 int height = 0;
3806 ParseIntegerProperty(&height, err, o, "height", false);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003807
3808 // Just only save some information here. Loading actual image data from
3809 // bufferView is done after this `ParseImage` function.
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003810 image->bufferView = bufferView;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003811 image->mimeType = mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003812 image->width = width;
3813 image->height = height;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003814
3815 return true;
3816 }
3817
Syoyo Fujita246654a2018-03-21 20:32:22 +09003818 // Parse URI & Load image data.
3819
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003820 std::string uri;
3821 std::string tmp_err;
Syoyo Fujita246654a2018-03-21 20:32:22 +09003822 if (!ParseStringProperty(&uri, &tmp_err, o, "uri", true)) {
3823 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003824 (*err) += "Failed to parse `uri` for image[" + std::to_string(image_idx) +
3825 "] name = \"" + image->name + "\".\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003826 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003827 return false;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003828 }
3829
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003830 std::vector<unsigned char> img;
Syoyo Fujitabeded612016-05-01 20:03:43 +09003831
Syoyo Fujita246654a2018-03-21 20:32:22 +09003832 if (IsDataURI(uri)) {
johan bowald642a3432018-04-01 12:37:18 +02003833 if (!DecodeDataURI(&img, image->mimeType, uri, 0, false)) {
Syoyo Fujita246654a2018-03-21 20:32:22 +09003834 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003835 (*err) += "Failed to decode 'uri' for image[" +
3836 std::to_string(image_idx) + "] name = [" + image->name +
3837 "]\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003838 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003839 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003840 }
3841 } else {
Syoyo Fujita246654a2018-03-21 20:32:22 +09003842 // Assume external file
3843 // Keep texture path (for textures that cannot be decoded)
3844 image->uri = uri;
Selmar67af3c92018-03-16 11:48:19 +01003845#ifdef TINYGLTF_NO_EXTERNAL_IMAGE
Syoyo Fujita246654a2018-03-21 20:32:22 +09003846 return true;
Selmar67af3c92018-03-16 11:48:19 +01003847#endif
Syoyo Fujitac4166e42020-01-08 02:38:01 +09003848 std::string decoded_uri = dlib::urldecode(uri);
3849 if (!LoadExternalFile(&img, err, warn, decoded_uri, basedir,
3850 /* required */ false, /* required bytes */ 0,
3851 /* checksize */ false, fs)) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003852 if (warn) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003853 (*warn) += "Failed to load external 'uri' for image[" +
3854 std::to_string(image_idx) + "] name = [" + image->name +
3855 "]\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003856 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003857 // If the image cannot be loaded, keep uri as image->uri.
3858 return true;
3859 }
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003860
Syoyo Fujita246654a2018-03-21 20:32:22 +09003861 if (img.empty()) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003862 if (warn) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003863 (*warn) += "Image data is empty for image[" +
3864 std::to_string(image_idx) + "] name = [" + image->name +
3865 "] \n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09003866 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003867 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003868 }
3869 }
3870
Squareysff644d82018-03-13 22:36:18 +01003871 if (*LoadImageData == nullptr) {
3872 if (err) {
3873 (*err) += "No LoadImageData callback specified.\n";
3874 }
3875 return false;
3876 }
Syoyo Fujitaaf3ebb22019-01-06 18:55:57 +09003877 return (*LoadImageData)(image, image_idx, err, warn, 0, 0, &img.at(0),
Paolo Jovone6601bf2018-07-07 20:43:33 +02003878 static_cast<int>(img.size()), load_image_user_data);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003879}
3880
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003881static bool ParseTexture(Texture *texture, std::string *err, const json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003882 bool store_original_json_for_extras_and_extensions,
Syoyo Fujitabeded612016-05-01 20:03:43 +09003883 const std::string &basedir) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003884 (void)basedir;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003885 int sampler = -1;
3886 int source = -1;
3887 ParseIntegerProperty(&sampler, err, o, "sampler", false);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003888
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003889 ParseIntegerProperty(&source, err, o, "source", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09003890
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003891 texture->sampler = sampler;
3892 texture->source = source;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003893
Selmar Kokfa7022f2018-04-04 18:10:20 +02003894 ParseExtensionsProperty(&texture->extensions, err, o);
3895 ParseExtrasProperty(&texture->extras, o);
Syoyo Fujitabde70212016-02-07 17:38:17 +09003896
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003897 if (store_original_json_for_extras_and_extensions) {
3898 {
3899 json_const_iterator it;
3900 if (FindMember(o, "extensions", it)) {
3901 texture->extensions_json_string = JsonToString(GetValue(it));
3902 }
3903 }
3904 {
3905 json_const_iterator it;
3906 if (FindMember(o, "extras", it)) {
3907 texture->extras_json_string = JsonToString(GetValue(it));
3908 }
3909 }
3910 }
3911
Vladimír Vondruš239be2c2018-07-24 23:23:56 +02003912 ParseStringProperty(&texture->name, err, o, "name", false);
3913
Syoyo Fujitabde70212016-02-07 17:38:17 +09003914 return true;
3915}
3916
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003917static bool ParseTextureInfo(
3918 TextureInfo *texinfo, std::string *err, const json &o,
3919 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09003920 if (texinfo == nullptr) {
3921 return false;
3922 }
3923
3924 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
3925 /* required */ true, "TextureInfo")) {
3926 return false;
3927 }
3928
3929 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
3930
3931 ParseExtensionsProperty(&texinfo->extensions, err, o);
3932 ParseExtrasProperty(&texinfo->extras, o);
3933
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003934 if (store_original_json_for_extras_and_extensions) {
3935 {
3936 json_const_iterator it;
3937 if (FindMember(o, "extensions", it)) {
3938 texinfo->extensions_json_string = JsonToString(GetValue(it));
3939 }
3940 }
3941 {
3942 json_const_iterator it;
3943 if (FindMember(o, "extras", it)) {
3944 texinfo->extras_json_string = JsonToString(GetValue(it));
3945 }
3946 }
3947 }
3948
Syoyo Fujita046400b2019-07-24 19:26:48 +09003949 return true;
3950}
3951
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003952static bool ParseNormalTextureInfo(
3953 NormalTextureInfo *texinfo, std::string *err, const json &o,
3954 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09003955 if (texinfo == nullptr) {
3956 return false;
3957 }
3958
3959 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
3960 /* required */ true, "NormalTextureInfo")) {
3961 return false;
3962 }
3963
3964 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
3965 ParseNumberProperty(&texinfo->scale, err, o, "scale", false);
3966
3967 ParseExtensionsProperty(&texinfo->extensions, err, o);
3968 ParseExtrasProperty(&texinfo->extras, o);
3969
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003970 if (store_original_json_for_extras_and_extensions) {
3971 {
3972 json_const_iterator it;
3973 if (FindMember(o, "extensions", it)) {
3974 texinfo->extensions_json_string = JsonToString(GetValue(it));
3975 }
3976 }
3977 {
3978 json_const_iterator it;
3979 if (FindMember(o, "extras", it)) {
3980 texinfo->extras_json_string = JsonToString(GetValue(it));
3981 }
3982 }
3983 }
3984
Syoyo Fujita046400b2019-07-24 19:26:48 +09003985 return true;
3986}
3987
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003988static bool ParseOcclusionTextureInfo(
3989 OcclusionTextureInfo *texinfo, std::string *err, const json &o,
3990 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09003991 if (texinfo == nullptr) {
3992 return false;
3993 }
3994
3995 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
3996 /* required */ true, "NormalTextureInfo")) {
3997 return false;
3998 }
3999
4000 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
4001 ParseNumberProperty(&texinfo->strength, err, o, "strength", false);
4002
4003 ParseExtensionsProperty(&texinfo->extensions, err, o);
4004 ParseExtrasProperty(&texinfo->extras, o);
4005
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004006 if (store_original_json_for_extras_and_extensions) {
4007 {
4008 json_const_iterator it;
4009 if (FindMember(o, "extensions", it)) {
4010 texinfo->extensions_json_string = JsonToString(GetValue(it));
4011 }
4012 }
4013 {
4014 json_const_iterator it;
4015 if (FindMember(o, "extras", it)) {
4016 texinfo->extras_json_string = JsonToString(GetValue(it));
4017 }
4018 }
4019 }
4020
Syoyo Fujita046400b2019-07-24 19:26:48 +09004021 return true;
4022}
4023
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004024static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004025 bool store_original_json_for_extras_and_extensions,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004026 FsCallbacks *fs, const std::string &basedir,
4027 bool is_binary = false,
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09004028 const unsigned char *bin_data = nullptr,
Syoyo Fujitabeded612016-05-01 20:03:43 +09004029 size_t bin_size = 0) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004030 size_t byteLength;
4031 if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true,
4032 "Buffer")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004033 return false;
4034 }
4035
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004036 // In glTF 2.0, uri is not mandatory anymore
Syoyo Fujita20244e12018-03-15 11:01:05 -05004037 buffer->uri.clear();
4038 ParseStringProperty(&buffer->uri, err, o, "uri", false, "Buffer");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004039
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004040 // having an empty uri for a non embedded image should not be valid
Syoyo Fujita20244e12018-03-15 11:01:05 -05004041 if (!is_binary && buffer->uri.empty()) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004042 if (err) {
4043 (*err) += "'uri' is missing from non binary glTF file buffer.\n";
4044 }
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004045 }
4046
jrkooncecba5d6c2019-08-29 11:26:22 -05004047 json_const_iterator type;
4048 if (FindMember(o, "type", type)) {
4049 std::string typeStr;
4050 if (GetString(GetValue(type), typeStr)) {
4051 if (typeStr.compare("arraybuffer") == 0) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004052 // buffer.type = "arraybuffer";
4053 }
4054 }
4055 }
4056
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004057 if (is_binary) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004058 // Still binary glTF accepts external dataURI.
Syoyo Fujita20244e12018-03-15 11:01:05 -05004059 if (!buffer->uri.empty()) {
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004060 // First try embedded data URI.
4061 if (IsDataURI(buffer->uri)) {
4062 std::string mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004063 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004064 true)) {
4065 if (err) {
4066 (*err) +=
4067 "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
4068 }
4069 return false;
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004070 }
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004071 } else {
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004072 // External .bin file.
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004073 std::string decoded_uri = dlib::urldecode(buffer->uri);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09004074 if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr,
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004075 decoded_uri, basedir, /* required */ true,
4076 byteLength, /* checkSize */ true, fs)) {
Vladimír Vondrušfd84ceb2018-08-16 21:07:56 +02004077 return false;
4078 }
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004079 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004080 } else {
Syoyo Fujitabeded612016-05-01 20:03:43 +09004081 // load data from (embedded) binary data
4082
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09004083 if ((bin_size == 0) || (bin_data == nullptr)) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09004084 if (err) {
Syoyo Fujita40c4a0a2017-05-30 01:21:36 +09004085 (*err) += "Invalid binary data in `Buffer'.\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09004086 }
4087 return false;
4088 }
4089
4090 if (byteLength > bin_size) {
4091 if (err) {
4092 std::stringstream ss;
4093 ss << "Invalid `byteLength'. Must be equal or less than binary size: "
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004094 "`byteLength' = "
4095 << byteLength << ", binary size = " << bin_size << std::endl;
Syoyo Fujitabeded612016-05-01 20:03:43 +09004096 (*err) += ss.str();
4097 }
4098 return false;
4099 }
4100
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004101 // Read buffer data
4102 buffer->data.resize(static_cast<size_t>(byteLength));
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004103 memcpy(&(buffer->data.at(0)), bin_data, static_cast<size_t>(byteLength));
Syoyo Fujitabeded612016-05-01 20:03:43 +09004104 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004105
4106 } else {
Syoyo Fujita20244e12018-03-15 11:01:05 -05004107 if (IsDataURI(buffer->uri)) {
johan bowaldef151a42018-04-01 13:44:48 +02004108 std::string mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004109 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
4110 true)) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004111 if (err) {
Syoyo Fujita20244e12018-03-15 11:01:05 -05004112 (*err) += "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004113 }
4114 return false;
4115 }
4116 } else {
4117 // Assume external .bin file.
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004118 std::string decoded_uri = dlib::urldecode(buffer->uri);
4119 if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, decoded_uri,
4120 basedir, /* required */ true, byteLength,
4121 /* checkSize */ true, fs)) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004122 return false;
4123 }
4124 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004125 }
4126
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004127 ParseStringProperty(&buffer->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004128
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004129 ParseExtensionsProperty(&buffer->extensions, err, o);
4130 ParseExtrasProperty(&buffer->extras, o);
4131
4132 if (store_original_json_for_extras_and_extensions) {
4133 {
4134 json_const_iterator it;
4135 if (FindMember(o, "extensions", it)) {
4136 buffer->extensions_json_string = JsonToString(GetValue(it));
4137 }
4138 }
4139 {
4140 json_const_iterator it;
4141 if (FindMember(o, "extras", it)) {
4142 buffer->extras_json_string = JsonToString(GetValue(it));
4143 }
4144 }
4145 }
4146
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004147 return true;
4148}
4149
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004150static bool ParseBufferView(
4151 BufferView *bufferView, std::string *err, const json &o,
4152 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004153 int buffer = -1;
4154 if (!ParseIntegerProperty(&buffer, err, o, "buffer", true, "BufferView")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004155 return false;
4156 }
4157
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004158 size_t byteOffset = 0;
4159 ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004160
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004161 size_t byteLength = 1;
4162 if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true,
4163 "BufferView")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004164 return false;
4165 }
4166
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004167 size_t byteStride = 0;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004168 if (!ParseUnsignedProperty(&byteStride, err, o, "byteStride", false)) {
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004169 // Spec says: When byteStride of referenced bufferView is not defined, it
4170 // means that accessor elements are tightly packed, i.e., effective stride
4171 // equals the size of the element.
4172 // We cannot determine the actual byteStride until Accessor are parsed, thus
4173 // set 0(= tightly packed) here(as done in OpenGL's VertexAttribPoiner)
4174 byteStride = 0;
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004175 }
4176
4177 if ((byteStride > 252) || ((byteStride % 4) != 0)) {
4178 if (err) {
4179 std::stringstream ss;
4180 ss << "Invalid `byteStride' value. `byteStride' must be the multiple of "
4181 "4 : "
4182 << byteStride << std::endl;
4183
4184 (*err) += ss.str();
4185 }
4186 return false;
4187 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004188
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004189 int target = 0;
4190 ParseIntegerProperty(&target, err, o, "target", false);
4191 if ((target == TINYGLTF_TARGET_ARRAY_BUFFER) ||
4192 (target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004193 // OK
4194 } else {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004195 target = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004196 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004197 bufferView->target = target;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004198
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004199 ParseStringProperty(&bufferView->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004200
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004201 ParseExtensionsProperty(&bufferView->extensions, err, o);
4202 ParseExtrasProperty(&bufferView->extras, o);
4203
4204 if (store_original_json_for_extras_and_extensions) {
4205 {
4206 json_const_iterator it;
4207 if (FindMember(o, "extensions", it)) {
4208 bufferView->extensions_json_string = JsonToString(GetValue(it));
4209 }
4210 }
4211 {
4212 json_const_iterator it;
4213 if (FindMember(o, "extras", it)) {
4214 bufferView->extras_json_string = JsonToString(GetValue(it));
4215 }
4216 }
4217 }
4218
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004219 bufferView->buffer = buffer;
4220 bufferView->byteOffset = byteOffset;
4221 bufferView->byteLength = byteLength;
4222 bufferView->byteStride = byteStride;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004223 return true;
4224}
4225
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004226static bool ParseSparseAccessor(Accessor *accessor, std::string *err,
4227 const json &o) {
4228 accessor->sparse.isSparse = true;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004229
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004230 int count = 0;
4231 ParseIntegerProperty(&count, err, o, "count", true);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004232
jrkooncecba5d6c2019-08-29 11:26:22 -05004233 json_const_iterator indices_iterator;
4234 json_const_iterator values_iterator;
4235 if (!FindMember(o, "indices", indices_iterator)) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004236 (*err) = "the sparse object of this accessor doesn't have indices";
4237 return false;
4238 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004239
jrkooncecba5d6c2019-08-29 11:26:22 -05004240 if (!FindMember(o, "values", values_iterator)) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004241 (*err) = "the sparse object ob ths accessor doesn't have values";
4242 return false;
4243 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004244
jrkooncecba5d6c2019-08-29 11:26:22 -05004245 const json &indices_obj = GetValue(indices_iterator);
4246 const json &values_obj = GetValue(values_iterator);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004247
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004248 int indices_buffer_view = 0, indices_byte_offset = 0, component_type = 0;
4249 ParseIntegerProperty(&indices_buffer_view, err, indices_obj, "bufferView",
4250 true);
4251 ParseIntegerProperty(&indices_byte_offset, err, indices_obj, "byteOffset",
4252 true);
4253 ParseIntegerProperty(&component_type, err, indices_obj, "componentType",
4254 true);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004255
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004256 int values_buffer_view = 0, values_byte_offset = 0;
4257 ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView",
4258 true);
4259 ParseIntegerProperty(&values_byte_offset, err, values_obj, "byteOffset",
4260 true);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004261
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004262 accessor->sparse.count = count;
4263 accessor->sparse.indices.bufferView = indices_buffer_view;
4264 accessor->sparse.indices.byteOffset = indices_byte_offset;
4265 accessor->sparse.indices.componentType = component_type;
4266 accessor->sparse.values.bufferView = values_buffer_view;
4267 accessor->sparse.values.byteOffset = values_byte_offset;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004268
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004269 // todo check theses values
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004270
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004271 return true;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004272}
4273
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004274static bool ParseAccessor(Accessor *accessor, std::string *err, const json &o,
4275 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004276 int bufferView = -1;
4277 ParseIntegerProperty(&bufferView, err, o, "bufferView", false, "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004278
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004279 size_t byteOffset = 0;
4280 ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false, "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004281
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004282 bool normalized = false;
4283 ParseBooleanProperty(&normalized, err, o, "normalized", false, "Accessor");
4284
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004285 size_t componentType = 0;
4286 if (!ParseUnsignedProperty(&componentType, err, o, "componentType", true,
4287 "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004288 return false;
4289 }
4290
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004291 size_t count = 0;
4292 if (!ParseUnsignedProperty(&count, err, o, "count", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004293 return false;
4294 }
4295
4296 std::string type;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09004297 if (!ParseStringProperty(&type, err, o, "type", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004298 return false;
4299 }
4300
4301 if (type.compare("SCALAR") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004302 accessor->type = TINYGLTF_TYPE_SCALAR;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004303 } else if (type.compare("VEC2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004304 accessor->type = TINYGLTF_TYPE_VEC2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004305 } else if (type.compare("VEC3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004306 accessor->type = TINYGLTF_TYPE_VEC3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004307 } else if (type.compare("VEC4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004308 accessor->type = TINYGLTF_TYPE_VEC4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004309 } else if (type.compare("MAT2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004310 accessor->type = TINYGLTF_TYPE_MAT2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004311 } else if (type.compare("MAT3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004312 accessor->type = TINYGLTF_TYPE_MAT3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004313 } else if (type.compare("MAT4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004314 accessor->type = TINYGLTF_TYPE_MAT4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004315 } else {
4316 std::stringstream ss;
4317 ss << "Unsupported `type` for accessor object. Got \"" << type << "\"\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004318 if (err) {
4319 (*err) += ss.str();
4320 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004321 return false;
4322 }
4323
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004324 ParseStringProperty(&accessor->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004325
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004326 accessor->minValues.clear();
4327 accessor->maxValues.clear();
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004328 ParseNumberArrayProperty(&accessor->minValues, err, o, "min", false,
4329 "Accessor");
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004330
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004331 ParseNumberArrayProperty(&accessor->maxValues, err, o, "max", false,
4332 "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004333
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004334 accessor->count = count;
4335 accessor->bufferView = bufferView;
4336 accessor->byteOffset = byteOffset;
jianghaosen4748ad62017-08-29 20:08:37 +08004337 accessor->normalized = normalized;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004338 {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004339 if (componentType >= TINYGLTF_COMPONENT_TYPE_BYTE &&
4340 componentType <= TINYGLTF_COMPONENT_TYPE_DOUBLE) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004341 // OK
Arthur Brainville (Ybalrid)811e1d32019-06-15 07:32:38 +02004342 accessor->componentType = int(componentType);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004343 } else {
4344 std::stringstream ss;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004345 ss << "Invalid `componentType` in accessor. Got " << componentType
4346 << "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004347 if (err) {
4348 (*err) += ss.str();
4349 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004350 return false;
4351 }
4352 }
4353
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004354 ParseExtensionsProperty(&(accessor->extensions), err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004355 ParseExtrasProperty(&(accessor->extras), o);
4356
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004357 if (store_original_json_for_extras_and_extensions) {
4358 {
4359 json_const_iterator it;
4360 if (FindMember(o, "extensions", it)) {
4361 accessor->extensions_json_string = JsonToString(GetValue(it));
4362 }
4363 }
4364 {
4365 json_const_iterator it;
4366 if (FindMember(o, "extras", it)) {
4367 accessor->extras_json_string = JsonToString(GetValue(it));
4368 }
4369 }
4370 }
4371
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004372 // check if accessor has a "sparse" object:
jrkooncecba5d6c2019-08-29 11:26:22 -05004373 json_const_iterator iterator;
4374 if (FindMember(o, "sparse", iterator)) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004375 // here this accessor has a "sparse" subobject
jrkooncecba5d6c2019-08-29 11:26:22 -05004376 return ParseSparseAccessor(accessor, err, GetValue(iterator));
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004377 }
4378
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004379 return true;
4380}
4381
Alex Wood7319db72019-01-24 15:38:16 -05004382#ifdef TINYGLTF_ENABLE_DRACO
4383
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004384static void DecodeIndexBuffer(draco::Mesh *mesh, size_t componentSize,
4385 std::vector<uint8_t> &outBuffer) {
4386 if (componentSize == 4) {
Alex Wood7319db72019-01-24 15:38:16 -05004387 assert(sizeof(mesh->face(draco::FaceIndex(0))[0]) == componentSize);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004388 memcpy(outBuffer.data(), &mesh->face(draco::FaceIndex(0))[0],
4389 outBuffer.size());
4390 } else {
Alex Wood7319db72019-01-24 15:38:16 -05004391 size_t faceStride = componentSize * 3;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004392 for (draco::FaceIndex f(0); f < mesh->num_faces(); ++f) {
4393 const draco::Mesh::Face &face = mesh->face(f);
4394 if (componentSize == 2) {
4395 uint16_t indices[3] = {(uint16_t)face[0].value(),
4396 (uint16_t)face[1].value(),
4397 (uint16_t)face[2].value()};
4398 memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
4399 faceStride);
4400 } else {
4401 uint8_t indices[3] = {(uint8_t)face[0].value(),
4402 (uint8_t)face[1].value(),
4403 (uint8_t)face[2].value()};
4404 memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
4405 faceStride);
Alex Wood7319db72019-01-24 15:38:16 -05004406 }
4407 }
4408 }
4409}
4410
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004411template <typename T>
4412static bool GetAttributeForAllPoints(draco::Mesh *mesh,
4413 const draco::PointAttribute *pAttribute,
4414 std::vector<uint8_t> &outBuffer) {
Alex Wood7319db72019-01-24 15:38:16 -05004415 size_t byteOffset = 0;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004416 T values[4] = {0, 0, 0, 0};
4417 for (draco::PointIndex i(0); i < mesh->num_points(); ++i) {
Alex Wood7319db72019-01-24 15:38:16 -05004418 const draco::AttributeValueIndex val_index = pAttribute->mapped_index(i);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004419 if (!pAttribute->ConvertValue<T>(val_index, pAttribute->num_components(),
4420 values))
Alex Wood7319db72019-01-24 15:38:16 -05004421 return false;
4422
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004423 memcpy(outBuffer.data() + byteOffset, &values[0],
4424 sizeof(T) * pAttribute->num_components());
Alex Wood7319db72019-01-24 15:38:16 -05004425 byteOffset += sizeof(T) * pAttribute->num_components();
4426 }
4427
4428 return true;
4429}
4430
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004431static bool GetAttributeForAllPoints(uint32_t componentType, draco::Mesh *mesh,
4432 const draco::PointAttribute *pAttribute,
4433 std::vector<uint8_t> &outBuffer) {
Alex Wood7319db72019-01-24 15:38:16 -05004434 bool decodeResult = false;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004435 switch (componentType) {
4436 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
4437 decodeResult =
4438 GetAttributeForAllPoints<uint8_t>(mesh, pAttribute, outBuffer);
4439 break;
4440 case TINYGLTF_COMPONENT_TYPE_BYTE:
4441 decodeResult =
4442 GetAttributeForAllPoints<int8_t>(mesh, pAttribute, outBuffer);
4443 break;
4444 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
4445 decodeResult =
4446 GetAttributeForAllPoints<uint16_t>(mesh, pAttribute, outBuffer);
4447 break;
4448 case TINYGLTF_COMPONENT_TYPE_SHORT:
4449 decodeResult =
4450 GetAttributeForAllPoints<int16_t>(mesh, pAttribute, outBuffer);
4451 break;
4452 case TINYGLTF_COMPONENT_TYPE_INT:
4453 decodeResult =
4454 GetAttributeForAllPoints<int32_t>(mesh, pAttribute, outBuffer);
4455 break;
4456 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
4457 decodeResult =
4458 GetAttributeForAllPoints<uint32_t>(mesh, pAttribute, outBuffer);
4459 break;
4460 case TINYGLTF_COMPONENT_TYPE_FLOAT:
4461 decodeResult =
4462 GetAttributeForAllPoints<float>(mesh, pAttribute, outBuffer);
4463 break;
4464 case TINYGLTF_COMPONENT_TYPE_DOUBLE:
4465 decodeResult =
4466 GetAttributeForAllPoints<double>(mesh, pAttribute, outBuffer);
4467 break;
4468 default:
4469 return false;
Alex Wood7319db72019-01-24 15:38:16 -05004470 }
4471
4472 return decodeResult;
4473}
4474
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004475static bool ParseDracoExtension(Primitive *primitive, Model *model,
4476 std::string *err,
4477 const Value &dracoExtensionValue) {
snowapril84e15262021-03-20 19:25:58 +09004478 (void)err;
Alex Wood7319db72019-01-24 15:38:16 -05004479 auto bufferViewValue = dracoExtensionValue.Get("bufferView");
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004480 if (!bufferViewValue.IsInt()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004481 auto attributesValue = dracoExtensionValue.Get("attributes");
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004482 if (!attributesValue.IsObject()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004483
4484 auto attributesObject = attributesValue.Get<Value::Object>();
4485 int bufferView = bufferViewValue.Get<int>();
4486
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004487 BufferView &view = model->bufferViews[bufferView];
4488 Buffer &buffer = model->buffers[view.buffer];
Alex Wood7319db72019-01-24 15:38:16 -05004489 // BufferView has already been decoded
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004490 if (view.dracoDecoded) return true;
Alex Wood7319db72019-01-24 15:38:16 -05004491 view.dracoDecoded = true;
4492
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004493 const char *bufferViewData =
4494 reinterpret_cast<const char *>(buffer.data.data() + view.byteOffset);
Alex Wood7319db72019-01-24 15:38:16 -05004495 size_t bufferViewSize = view.byteLength;
4496
4497 // decode draco
4498 draco::DecoderBuffer decoderBuffer;
4499 decoderBuffer.Init(bufferViewData, bufferViewSize);
4500 draco::Decoder decoder;
4501 auto decodeResult = decoder.DecodeMeshFromBuffer(&decoderBuffer);
4502 if (!decodeResult.ok()) {
4503 return false;
4504 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004505 const std::unique_ptr<draco::Mesh> &mesh = decodeResult.value();
Alex Wood7319db72019-01-24 15:38:16 -05004506
4507 // create new bufferView for indices
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004508 if (primitive->indices >= 0) {
4509 int32_t componentSize = GetComponentSizeInBytes(
4510 model->accessors[primitive->indices].componentType);
Alex Wood7319db72019-01-24 15:38:16 -05004511 Buffer decodedIndexBuffer;
4512 decodedIndexBuffer.data.resize(mesh->num_faces() * 3 * componentSize);
4513
4514 DecodeIndexBuffer(mesh.get(), componentSize, decodedIndexBuffer.data);
4515
4516 model->buffers.emplace_back(std::move(decodedIndexBuffer));
4517
4518 BufferView decodedIndexBufferView;
4519 decodedIndexBufferView.buffer = int(model->buffers.size() - 1);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004520 decodedIndexBufferView.byteLength =
4521 int(mesh->num_faces() * 3 * componentSize);
Alex Wood7319db72019-01-24 15:38:16 -05004522 decodedIndexBufferView.byteOffset = 0;
4523 decodedIndexBufferView.byteStride = 0;
4524 decodedIndexBufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER;
4525 model->bufferViews.emplace_back(std::move(decodedIndexBufferView));
4526
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004527 model->accessors[primitive->indices].bufferView =
4528 int(model->bufferViews.size() - 1);
Alexander Wood0d77a292019-01-26 08:58:45 -05004529 model->accessors[primitive->indices].count = int(mesh->num_faces() * 3);
Alex Wood7319db72019-01-24 15:38:16 -05004530 }
4531
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004532 for (const auto &attribute : attributesObject) {
4533 if (!attribute.second.IsInt()) return false;
Alexander Wood0d77a292019-01-26 08:58:45 -05004534 auto primitiveAttribute = primitive->attributes.find(attribute.first);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004535 if (primitiveAttribute == primitive->attributes.end()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004536
4537 int dracoAttributeIndex = attribute.second.Get<int>();
4538 const auto pAttribute = mesh->GetAttributeByUniqueId(dracoAttributeIndex);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004539 const auto componentType =
4540 model->accessors[primitiveAttribute->second].componentType;
Alex Wood7319db72019-01-24 15:38:16 -05004541
4542 // Create a new buffer for this decoded buffer
4543 Buffer decodedBuffer;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004544 size_t bufferSize = mesh->num_points() * pAttribute->num_components() *
4545 GetComponentSizeInBytes(componentType);
Alex Wood7319db72019-01-24 15:38:16 -05004546 decodedBuffer.data.resize(bufferSize);
4547
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004548 if (!GetAttributeForAllPoints(componentType, mesh.get(), pAttribute,
4549 decodedBuffer.data))
Alex Wood7319db72019-01-24 15:38:16 -05004550 return false;
4551
4552 model->buffers.emplace_back(std::move(decodedBuffer));
4553
4554 BufferView decodedBufferView;
4555 decodedBufferView.buffer = int(model->buffers.size() - 1);
4556 decodedBufferView.byteLength = bufferSize;
4557 decodedBufferView.byteOffset = pAttribute->byte_offset();
4558 decodedBufferView.byteStride = pAttribute->byte_stride();
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004559 decodedBufferView.target = primitive->indices >= 0
4560 ? TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER
4561 : TINYGLTF_TARGET_ARRAY_BUFFER;
Alex Wood7319db72019-01-24 15:38:16 -05004562 model->bufferViews.emplace_back(std::move(decodedBufferView));
4563
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004564 model->accessors[primitiveAttribute->second].bufferView =
4565 int(model->bufferViews.size() - 1);
4566 model->accessors[primitiveAttribute->second].count =
4567 int(mesh->num_points());
Alex Wood7319db72019-01-24 15:38:16 -05004568 }
4569
4570 return true;
4571}
4572#endif
4573
4574static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004575 const json &o,
4576 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004577 int material = -1;
4578 ParseIntegerProperty(&material, err, o, "material", false);
4579 primitive->material = material;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004580
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004581 int mode = TINYGLTF_MODE_TRIANGLES;
4582 ParseIntegerProperty(&mode, err, o, "mode", false);
4583 primitive->mode = mode; // Why only triangled were supported ?
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004584
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004585 int indices = -1;
4586 ParseIntegerProperty(&indices, err, o, "indices", false);
4587 primitive->indices = indices;
4588 if (!ParseStringIntegerProperty(&primitive->attributes, err, o, "attributes",
4589 true, "Primitive")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004590 return false;
4591 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004592
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004593 // Look for morph targets
jrkooncecba5d6c2019-08-29 11:26:22 -05004594 json_const_iterator targetsObject;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004595 if (FindMember(o, "targets", targetsObject) &&
4596 IsArray(GetValue(targetsObject))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004597 auto targetsObjectEnd = ArrayEnd(GetValue(targetsObject));
4598 for (json_const_array_iterator i = ArrayBegin(GetValue(targetsObject));
4599 i != targetsObjectEnd; ++i) {
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004600 std::map<std::string, int> targetAttribues;
4601
jrkooncecba5d6c2019-08-29 11:26:22 -05004602 const json &dict = *i;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004603 if (IsObject(dict)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004604 json_const_iterator dictIt(ObjectBegin(dict));
4605 json_const_iterator dictItEnd(ObjectEnd(dict));
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004606
jrkooncecba5d6c2019-08-29 11:26:22 -05004607 for (; dictIt != dictItEnd; ++dictIt) {
4608 int iVal;
4609 if (GetInt(GetValue(dictIt), iVal))
4610 targetAttribues[GetKey(dictIt)] = iVal;
4611 }
4612 primitive->targets.emplace_back(std::move(targetAttribues));
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004613 }
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004614 }
4615 }
4616
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004617 ParseExtrasProperty(&(primitive->extras), o);
Alex Wood7319db72019-01-24 15:38:16 -05004618 ParseExtensionsProperty(&primitive->extensions, err, o);
4619
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004620 if (store_original_json_for_extras_and_extensions) {
4621 {
4622 json_const_iterator it;
4623 if (FindMember(o, "extensions", it)) {
4624 primitive->extensions_json_string = JsonToString(GetValue(it));
4625 }
4626 }
4627 {
4628 json_const_iterator it;
4629 if (FindMember(o, "extras", it)) {
4630 primitive->extras_json_string = JsonToString(GetValue(it));
4631 }
4632 }
4633 }
4634
Alex Wood7319db72019-01-24 15:38:16 -05004635#ifdef TINYGLTF_ENABLE_DRACO
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004636 auto dracoExtension =
4637 primitive->extensions.find("KHR_draco_mesh_compression");
4638 if (dracoExtension != primitive->extensions.end()) {
4639 ParseDracoExtension(primitive, model, err, dracoExtension->second);
Alex Wood7319db72019-01-24 15:38:16 -05004640 }
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09004641#else
4642 (void)model;
Alex Wood7319db72019-01-24 15:38:16 -05004643#endif
4644
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004645 return true;
4646}
4647
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004648static bool ParseMesh(Mesh *mesh, Model *model, std::string *err, const json &o,
4649 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004650 ParseStringProperty(&mesh->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004651
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004652 mesh->primitives.clear();
jrkooncecba5d6c2019-08-29 11:26:22 -05004653 json_const_iterator primObject;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004654 if (FindMember(o, "primitives", primObject) &&
4655 IsArray(GetValue(primObject))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004656 json_const_array_iterator primEnd = ArrayEnd(GetValue(primObject));
4657 for (json_const_array_iterator i = ArrayBegin(GetValue(primObject));
4658 i != primEnd; ++i) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004659 Primitive primitive;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004660 if (ParsePrimitive(&primitive, model, err, *i,
4661 store_original_json_for_extras_and_extensions)) {
Luke San Antonio10a3e1d2016-06-15 00:05:01 -04004662 // Only add the primitive if the parsing succeeds.
jrkoonced1e14722019-08-27 11:51:02 -05004663 mesh->primitives.emplace_back(std::move(primitive));
Luke San Antonio10a3e1d2016-06-15 00:05:01 -04004664 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004665 }
4666 }
4667
Aurélien Chatelain38a6a752017-05-24 09:54:55 +00004668 // Should probably check if has targets and if dimensions fit
4669 ParseNumberArrayProperty(&mesh->weights, err, o, "weights", false);
4670
Selmar09d2ff12018-03-15 17:30:42 +01004671 ParseExtensionsProperty(&mesh->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004672 ParseExtrasProperty(&(mesh->extras), o);
4673
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004674 if (store_original_json_for_extras_and_extensions) {
4675 {
4676 json_const_iterator it;
4677 if (FindMember(o, "extensions", it)) {
4678 mesh->extensions_json_string = JsonToString(GetValue(it));
4679 }
4680 }
4681 {
4682 json_const_iterator it;
4683 if (FindMember(o, "extras", it)) {
4684 mesh->extras_json_string = JsonToString(GetValue(it));
4685 }
4686 }
4687 }
4688
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004689 return true;
4690}
4691
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004692static bool ParseNode(Node *node, std::string *err, const json &o,
4693 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004694 ParseStringProperty(&node->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004695
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004696 int skin = -1;
4697 ParseIntegerProperty(&skin, err, o, "skin", false);
4698 node->skin = skin;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00004699
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004700 // Matrix and T/R/S are exclusive
Syoyo Fujita5b407452017-06-04 17:42:41 +09004701 if (!ParseNumberArrayProperty(&node->matrix, err, o, "matrix", false)) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004702 ParseNumberArrayProperty(&node->rotation, err, o, "rotation", false);
4703 ParseNumberArrayProperty(&node->scale, err, o, "scale", false);
4704 ParseNumberArrayProperty(&node->translation, err, o, "translation", false);
4705 }
4706
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004707 int camera = -1;
4708 ParseIntegerProperty(&camera, err, o, "camera", false);
4709 node->camera = camera;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004710
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004711 int mesh = -1;
4712 ParseIntegerProperty(&mesh, err, o, "mesh", false);
4713 node->mesh = mesh;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004714
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004715 node->children.clear();
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004716 ParseIntegerArrayProperty(&node->children, err, o, "children", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004717
Benjamin Schmithüsenad63bf72019-08-16 14:19:27 +02004718 ParseNumberArrayProperty(&node->weights, err, o, "weights", false);
4719
Selmar09d2ff12018-03-15 17:30:42 +01004720 ParseExtensionsProperty(&node->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004721 ParseExtrasProperty(&(node->extras), o);
4722
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004723 if (store_original_json_for_extras_and_extensions) {
4724 {
4725 json_const_iterator it;
4726 if (FindMember(o, "extensions", it)) {
4727 node->extensions_json_string = JsonToString(GetValue(it));
4728 }
4729 }
4730 {
4731 json_const_iterator it;
4732 if (FindMember(o, "extras", it)) {
4733 node->extras_json_string = JsonToString(GetValue(it));
4734 }
4735 }
4736 }
4737
Emanuel Schrade186322b2017-11-06 11:14:41 +01004738 return true;
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004739}
4740
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004741static bool ParsePbrMetallicRoughness(
4742 PbrMetallicRoughness *pbr, std::string *err, const json &o,
4743 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004744 if (pbr == nullptr) {
4745 return false;
4746 }
4747
4748 std::vector<double> baseColorFactor;
4749 if (ParseNumberArrayProperty(&baseColorFactor, err, o, "baseColorFactor",
4750 /* required */ false)) {
4751 if (baseColorFactor.size() != 4) {
4752 if (err) {
4753 (*err) +=
4754 "Array length of `baseColorFactor` parameter in "
4755 "pbrMetallicRoughness must be 4, but got " +
4756 std::to_string(baseColorFactor.size()) + "\n";
4757 }
4758 return false;
4759 }
Selmar Kokc3353e12019-10-18 18:22:35 +02004760 pbr->baseColorFactor = baseColorFactor;
Syoyo Fujita046400b2019-07-24 19:26:48 +09004761 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09004762
4763 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004764 json_const_iterator it;
4765 if (FindMember(o, "baseColorTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004766 ParseTextureInfo(&pbr->baseColorTexture, err, GetValue(it),
4767 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004768 }
4769 }
4770
4771 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004772 json_const_iterator it;
4773 if (FindMember(o, "metallicRoughnessTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004774 ParseTextureInfo(&pbr->metallicRoughnessTexture, err, GetValue(it),
4775 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004776 }
4777 }
4778
4779 ParseNumberProperty(&pbr->metallicFactor, err, o, "metallicFactor", false);
4780 ParseNumberProperty(&pbr->roughnessFactor, err, o, "roughnessFactor", false);
4781
4782 ParseExtensionsProperty(&pbr->extensions, err, o);
4783 ParseExtrasProperty(&pbr->extras, o);
4784
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004785 if (store_original_json_for_extras_and_extensions) {
4786 {
4787 json_const_iterator it;
4788 if (FindMember(o, "extensions", it)) {
4789 pbr->extensions_json_string = JsonToString(GetValue(it));
4790 }
4791 }
4792 {
4793 json_const_iterator it;
4794 if (FindMember(o, "extras", it)) {
4795 pbr->extras_json_string = JsonToString(GetValue(it));
4796 }
4797 }
4798 }
4799
Syoyo Fujita046400b2019-07-24 19:26:48 +09004800 return true;
4801}
4802
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004803static bool ParseMaterial(Material *material, std::string *err, const json &o,
4804 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004805 ParseStringProperty(&material->name, err, o, "name", /* required */ false);
4806
Syoyo Fujitaff515702019-08-24 16:29:14 +09004807 if (ParseNumberArrayProperty(&material->emissiveFactor, err, o,
4808 "emissiveFactor",
4809 /* required */ false)) {
Selmar Kok6df800d2019-08-19 11:05:28 +02004810 if (material->emissiveFactor.size() != 3) {
4811 if (err) {
4812 (*err) +=
4813 "Array length of `emissiveFactor` parameter in "
4814 "material must be 3, but got " +
4815 std::to_string(material->emissiveFactor.size()) + "\n";
4816 }
4817 return false;
4818 }
Syoyo Fujitaff515702019-08-24 16:29:14 +09004819 } else {
Selmar Kok6df800d2019-08-19 11:05:28 +02004820 // fill with default values
Selmar Kok6dba6c62019-08-19 11:23:31 +02004821 material->emissiveFactor = {0.0, 0.0, 0.0};
Selmar Kok6df800d2019-08-19 11:05:28 +02004822 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09004823
4824 ParseStringProperty(&material->alphaMode, err, o, "alphaMode",
4825 /* required */ false);
4826 ParseNumberProperty(&material->alphaCutoff, err, o, "alphaCutoff",
4827 /* required */ false);
4828 ParseBooleanProperty(&material->doubleSided, err, o, "doubleSided",
4829 /* required */ false);
4830
4831 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004832 json_const_iterator it;
4833 if (FindMember(o, "pbrMetallicRoughness", it)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004834 ParsePbrMetallicRoughness(&material->pbrMetallicRoughness, err,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004835 GetValue(it),
4836 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004837 }
4838 }
4839
4840 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004841 json_const_iterator it;
4842 if (FindMember(o, "normalTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004843 ParseNormalTextureInfo(&material->normalTexture, err, GetValue(it),
4844 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004845 }
4846 }
4847
4848 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004849 json_const_iterator it;
4850 if (FindMember(o, "occlusionTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004851 ParseOcclusionTextureInfo(&material->occlusionTexture, err, GetValue(it),
4852 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004853 }
4854 }
4855
4856 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004857 json_const_iterator it;
4858 if (FindMember(o, "emissiveTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004859 ParseTextureInfo(&material->emissiveTexture, err, GetValue(it),
4860 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004861 }
4862 }
4863
4864 // Old code path. For backward compatibility, we still store material values
4865 // as Parameter. This will create duplicated information for
4866 // example(pbrMetallicRoughness), but should be neglible in terms of memory
4867 // consumption.
4868 // TODO(syoyo): Remove in the next major release.
4869 material->values.clear();
4870 material->additionalValues.clear();
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004871
jrkooncecba5d6c2019-08-29 11:26:22 -05004872 json_const_iterator it(ObjectBegin(o));
4873 json_const_iterator itEnd(ObjectEnd(o));
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004874
jrkooncecba5d6c2019-08-29 11:26:22 -05004875 for (; it != itEnd; ++it) {
4876 std::string key(GetKey(it));
jrkoonce06c30c42019-09-03 15:56:48 -05004877 if (key == "pbrMetallicRoughness") {
4878 if (IsObject(GetValue(it))) {
4879 const json &values_object = GetValue(it);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004880
jrkoonce06c30c42019-09-03 15:56:48 -05004881 json_const_iterator itVal(ObjectBegin(values_object));
4882 json_const_iterator itValEnd(ObjectEnd(values_object));
4883
4884 for (; itVal != itValEnd; ++itVal) {
4885 Parameter param;
4886 if (ParseParameterProperty(&param, err, values_object, GetKey(itVal),
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004887 false)) {
jrkoonce06c30c42019-09-03 15:56:48 -05004888 material->values.emplace(GetKey(itVal), std::move(param));
4889 }
4890 }
4891 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004892 } else if (key == "extensions" || key == "extras") {
4893 // done later, skip, otherwise poorly parsed contents will be saved in the
4894 // parametermap and serialized again later
4895 } else {
jrkoonce06c30c42019-09-03 15:56:48 -05004896 Parameter param;
4897 if (ParseParameterProperty(&param, err, o, key, false)) {
4898 // names of materials have already been parsed. Putting it in this map
4899 // doesn't correctly reflext the glTF specification
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004900 if (key != "name")
4901 material->additionalValues.emplace(std::move(key), std::move(param));
jrkoonce06c30c42019-09-03 15:56:48 -05004902 }
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00004903 }
Syoyo Fujita5b407452017-06-04 17:42:41 +09004904 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004905
Syoyo Fujita046400b2019-07-24 19:26:48 +09004906 material->extensions.clear();
Selmar09d2ff12018-03-15 17:30:42 +01004907 ParseExtensionsProperty(&material->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004908 ParseExtrasProperty(&(material->extras), o);
4909
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004910 if (store_original_json_for_extras_and_extensions) {
4911 {
4912 json_const_iterator eit;
4913 if (FindMember(o, "extensions", eit)) {
4914 material->extensions_json_string = JsonToString(GetValue(eit));
4915 }
4916 }
4917 {
4918 json_const_iterator eit;
4919 if (FindMember(o, "extras", eit)) {
4920 material->extras_json_string = JsonToString(GetValue(eit));
4921 }
4922 }
4923 }
4924
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004925 return true;
4926}
4927
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004928static bool ParseAnimationChannel(
4929 AnimationChannel *channel, std::string *err, const json &o,
4930 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004931 int samplerIndex = -1;
4932 int targetIndex = -1;
4933 if (!ParseIntegerProperty(&samplerIndex, err, o, "sampler", true,
4934 "AnimationChannel")) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004935 if (err) {
4936 (*err) += "`sampler` field is missing in animation channels\n";
4937 }
4938 return false;
4939 }
4940
jrkooncecba5d6c2019-08-29 11:26:22 -05004941 json_const_iterator targetIt;
4942 if (FindMember(o, "target", targetIt) && IsObject(GetValue(targetIt))) {
4943 const json &target_object = GetValue(targetIt);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004944
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004945 if (!ParseIntegerProperty(&targetIndex, err, target_object, "node", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004946 if (err) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09004947 (*err) += "`node` field is missing in animation.channels.target\n";
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004948 }
4949 return false;
4950 }
4951
4952 if (!ParseStringProperty(&channel->target_path, err, target_object, "path",
4953 true)) {
4954 if (err) {
4955 (*err) += "`path` field is missing in animation.channels.target\n";
4956 }
4957 return false;
4958 }
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09004959 ParseExtensionsProperty(&channel->target_extensions, err, target_object);
4960 if (store_original_json_for_extras_and_extensions) {
Selmar Kok973d9b32020-01-21 18:45:24 +01004961 json_const_iterator it;
4962 if (FindMember(target_object, "extensions", it)) {
4963 channel->target_extensions_json_string = JsonToString(GetValue(it));
4964 }
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09004965 }
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004966 }
4967
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004968 channel->sampler = samplerIndex;
4969 channel->target_node = targetIndex;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004970
Selmar Kok4e2988e2019-08-16 14:08:08 +02004971 ParseExtensionsProperty(&channel->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004972 ParseExtrasProperty(&(channel->extras), o);
4973
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004974 if (store_original_json_for_extras_and_extensions) {
4975 {
4976 json_const_iterator it;
4977 if (FindMember(o, "extensions", it)) {
4978 channel->extensions_json_string = JsonToString(GetValue(it));
4979 }
4980 }
4981 {
4982 json_const_iterator it;
4983 if (FindMember(o, "extras", it)) {
4984 channel->extras_json_string = JsonToString(GetValue(it));
4985 }
4986 }
4987 }
4988
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004989 return true;
4990}
4991
4992static bool ParseAnimation(Animation *animation, std::string *err,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004993 const json &o,
4994 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004995 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004996 json_const_iterator channelsIt;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004997 if (FindMember(o, "channels", channelsIt) &&
4998 IsArray(GetValue(channelsIt))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004999 json_const_array_iterator channelEnd = ArrayEnd(GetValue(channelsIt));
5000 for (json_const_array_iterator i = ArrayBegin(GetValue(channelsIt));
5001 i != channelEnd; ++i) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005002 AnimationChannel channel;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005003 if (ParseAnimationChannel(
5004 &channel, err, *i,
5005 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005006 // Only add the channel if the parsing succeeds.
jrkooncecba5d6c2019-08-29 11:26:22 -05005007 animation->channels.emplace_back(std::move(channel));
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005008 }
5009 }
5010 }
5011 }
5012
5013 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005014 json_const_iterator samplerIt;
5015 if (FindMember(o, "samplers", samplerIt) && IsArray(GetValue(samplerIt))) {
5016 const json &sampler_array = GetValue(samplerIt);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005017
jrkooncecba5d6c2019-08-29 11:26:22 -05005018 json_const_array_iterator it = ArrayBegin(sampler_array);
5019 json_const_array_iterator itEnd = ArrayEnd(sampler_array);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005020
jrkooncecba5d6c2019-08-29 11:26:22 -05005021 for (; it != itEnd; ++it) {
5022 const json &s = *it;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005023
5024 AnimationSampler sampler;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005025 int inputIndex = -1;
5026 int outputIndex = -1;
5027 if (!ParseIntegerProperty(&inputIndex, err, s, "input", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005028 if (err) {
5029 (*err) += "`input` field is missing in animation.sampler\n";
5030 }
5031 return false;
5032 }
Evan Birenbaum6bdffed2019-02-14 13:30:57 -08005033 ParseStringProperty(&sampler.interpolation, err, s, "interpolation",
5034 false);
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005035 if (!ParseIntegerProperty(&outputIndex, err, s, "output", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005036 if (err) {
5037 (*err) += "`output` field is missing in animation.sampler\n";
5038 }
5039 return false;
5040 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005041 sampler.input = inputIndex;
5042 sampler.output = outputIndex;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005043 ParseExtensionsProperty(&(sampler.extensions), err, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02005044 ParseExtrasProperty(&(sampler.extras), s);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005045
5046 if (store_original_json_for_extras_and_extensions) {
5047 {
5048 json_const_iterator eit;
5049 if (FindMember(o, "extensions", eit)) {
5050 sampler.extensions_json_string = JsonToString(GetValue(eit));
5051 }
5052 }
5053 {
5054 json_const_iterator eit;
5055 if (FindMember(o, "extras", eit)) {
5056 sampler.extras_json_string = JsonToString(GetValue(eit));
5057 }
5058 }
5059 }
5060
jrkooncecba5d6c2019-08-29 11:26:22 -05005061 animation->samplers.emplace_back(std::move(sampler));
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005062 }
5063 }
5064 }
5065
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005066 ParseStringProperty(&animation->name, err, o, "name", false);
5067
Selmar Kok4e2988e2019-08-16 14:08:08 +02005068 ParseExtensionsProperty(&animation->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09005069 ParseExtrasProperty(&(animation->extras), o);
5070
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005071 if (store_original_json_for_extras_and_extensions) {
5072 {
5073 json_const_iterator it;
5074 if (FindMember(o, "extensions", it)) {
5075 animation->extensions_json_string = JsonToString(GetValue(it));
5076 }
5077 }
5078 {
5079 json_const_iterator it;
5080 if (FindMember(o, "extras", it)) {
5081 animation->extras_json_string = JsonToString(GetValue(it));
5082 }
5083 }
5084 }
5085
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005086 return true;
5087}
5088
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005089static bool ParseSampler(Sampler *sampler, std::string *err, const json &o,
5090 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujitac2615632016-06-19 21:56:06 +09005091 ParseStringProperty(&sampler->name, err, o, "name", false);
5092
Syoyo Fujitaee179b22019-08-16 13:11:30 +09005093 int minFilter = -1;
5094 int magFilter = -1;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005095 int wrapS = TINYGLTF_TEXTURE_WRAP_REPEAT;
5096 int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT;
Syoyo Fujita2c521b32020-12-04 00:50:46 +09005097 //int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005098 ParseIntegerProperty(&minFilter, err, o, "minFilter", false);
5099 ParseIntegerProperty(&magFilter, err, o, "magFilter", false);
5100 ParseIntegerProperty(&wrapS, err, o, "wrapS", false);
5101 ParseIntegerProperty(&wrapT, err, o, "wrapT", false);
Syoyo Fujita2c521b32020-12-04 00:50:46 +09005102 //ParseIntegerProperty(&wrapR, err, o, "wrapR", false); // tinygltf extension
Syoyo Fujitaee179b22019-08-16 13:11:30 +09005103
5104 // TODO(syoyo): Check the value is alloed one.
5105 // (e.g. we allow 9728(NEAREST), but don't allow 9727)
Syoyo Fujitac2615632016-06-19 21:56:06 +09005106
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005107 sampler->minFilter = minFilter;
5108 sampler->magFilter = magFilter;
5109 sampler->wrapS = wrapS;
5110 sampler->wrapT = wrapT;
Syoyo Fujita2c521b32020-12-04 00:50:46 +09005111 //sampler->wrapR = wrapR;
Syoyo Fujitac2615632016-06-19 21:56:06 +09005112
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005113 ParseExtensionsProperty(&(sampler->extensions), err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09005114 ParseExtrasProperty(&(sampler->extras), o);
5115
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005116 if (store_original_json_for_extras_and_extensions) {
5117 {
5118 json_const_iterator it;
5119 if (FindMember(o, "extensions", it)) {
5120 sampler->extensions_json_string = JsonToString(GetValue(it));
5121 }
5122 }
5123 {
5124 json_const_iterator it;
5125 if (FindMember(o, "extras", it)) {
5126 sampler->extras_json_string = JsonToString(GetValue(it));
5127 }
5128 }
5129 }
5130
Syoyo Fujitac2615632016-06-19 21:56:06 +09005131 return true;
5132}
5133
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005134static bool ParseSkin(Skin *skin, std::string *err, const json &o,
5135 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09005136 ParseStringProperty(&skin->name, err, o, "name", false, "Skin");
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005137
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005138 std::vector<int> joints;
5139 if (!ParseIntegerArrayProperty(&joints, err, o, "joints", false, "Skin")) {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005140 return false;
5141 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005142 skin->joints = std::move(joints);
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005143
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005144 int skeleton = -1;
5145 ParseIntegerProperty(&skeleton, err, o, "skeleton", false, "Skin");
5146 skin->skeleton = skeleton;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005147
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005148 int invBind = -1;
5149 ParseIntegerProperty(&invBind, err, o, "inverseBindMatrices", true, "Skin");
5150 skin->inverseBindMatrices = invBind;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005151
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005152 ParseExtensionsProperty(&(skin->extensions), err, o);
5153 ParseExtrasProperty(&(skin->extras), o);
5154
5155 if (store_original_json_for_extras_and_extensions) {
5156 {
5157 json_const_iterator it;
5158 if (FindMember(o, "extensions", it)) {
5159 skin->extensions_json_string = JsonToString(GetValue(it));
5160 }
5161 }
5162 {
5163 json_const_iterator it;
5164 if (FindMember(o, "extras", it)) {
5165 skin->extras_json_string = JsonToString(GetValue(it));
5166 }
5167 }
5168 }
5169
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005170 return true;
5171}
5172
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005173static bool ParsePerspectiveCamera(
5174 PerspectiveCamera *camera, std::string *err, const json &o,
5175 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005176 double yfov = 0.0;
5177 if (!ParseNumberProperty(&yfov, err, o, "yfov", true, "OrthographicCamera")) {
5178 return false;
5179 }
5180
5181 double znear = 0.0;
5182 if (!ParseNumberProperty(&znear, err, o, "znear", true,
5183 "PerspectiveCamera")) {
5184 return false;
5185 }
5186
5187 double aspectRatio = 0.0; // = invalid
5188 ParseNumberProperty(&aspectRatio, err, o, "aspectRatio", false,
5189 "PerspectiveCamera");
5190
5191 double zfar = 0.0; // = invalid
5192 ParseNumberProperty(&zfar, err, o, "zfar", false, "PerspectiveCamera");
5193
Selmar Kok31cb7f92018-10-03 15:39:05 +02005194 camera->aspectRatio = aspectRatio;
5195 camera->zfar = zfar;
5196 camera->yfov = yfov;
5197 camera->znear = znear;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005198
Selmar09d2ff12018-03-15 17:30:42 +01005199 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005200 ParseExtrasProperty(&(camera->extras), o);
5201
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005202 if (store_original_json_for_extras_and_extensions) {
5203 {
5204 json_const_iterator it;
5205 if (FindMember(o, "extensions", it)) {
5206 camera->extensions_json_string = JsonToString(GetValue(it));
5207 }
5208 }
5209 {
5210 json_const_iterator it;
5211 if (FindMember(o, "extras", it)) {
5212 camera->extras_json_string = JsonToString(GetValue(it));
5213 }
5214 }
5215 }
5216
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005217 // TODO(syoyo): Validate parameter values.
5218
5219 return true;
5220}
5221
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005222static bool ParseSpotLight(SpotLight *light, std::string *err, const json &o,
5223 bool store_original_json_for_extras_and_extensions) {
Johan Bowald52936a02019-07-17 09:06:45 +02005224 ParseNumberProperty(&light->innerConeAngle, err, o, "innerConeAngle", false);
5225 ParseNumberProperty(&light->outerConeAngle, err, o, "outerConeAngle", false);
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005226
Johan Bowald52936a02019-07-17 09:06:45 +02005227 ParseExtensionsProperty(&light->extensions, err, o);
5228 ParseExtrasProperty(&light->extras, o);
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005229
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005230 if (store_original_json_for_extras_and_extensions) {
5231 {
5232 json_const_iterator it;
5233 if (FindMember(o, "extensions", it)) {
5234 light->extensions_json_string = JsonToString(GetValue(it));
5235 }
5236 }
5237 {
5238 json_const_iterator it;
5239 if (FindMember(o, "extras", it)) {
5240 light->extras_json_string = JsonToString(GetValue(it));
5241 }
5242 }
5243 }
5244
Johan Bowald52936a02019-07-17 09:06:45 +02005245 // TODO(syoyo): Validate parameter values.
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005246
Johan Bowald52936a02019-07-17 09:06:45 +02005247 return true;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005248}
5249
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005250static bool ParseOrthographicCamera(
5251 OrthographicCamera *camera, std::string *err, const json &o,
5252 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005253 double xmag = 0.0;
5254 if (!ParseNumberProperty(&xmag, err, o, "xmag", true, "OrthographicCamera")) {
5255 return false;
5256 }
5257
5258 double ymag = 0.0;
5259 if (!ParseNumberProperty(&ymag, err, o, "ymag", true, "OrthographicCamera")) {
5260 return false;
5261 }
5262
5263 double zfar = 0.0;
5264 if (!ParseNumberProperty(&zfar, err, o, "zfar", true, "OrthographicCamera")) {
5265 return false;
5266 }
5267
5268 double znear = 0.0;
5269 if (!ParseNumberProperty(&znear, err, o, "znear", true,
5270 "OrthographicCamera")) {
5271 return false;
5272 }
5273
Selmar09d2ff12018-03-15 17:30:42 +01005274 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005275 ParseExtrasProperty(&(camera->extras), o);
5276
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005277 if (store_original_json_for_extras_and_extensions) {
5278 {
5279 json_const_iterator it;
5280 if (FindMember(o, "extensions", it)) {
5281 camera->extensions_json_string = JsonToString(GetValue(it));
5282 }
5283 }
5284 {
5285 json_const_iterator it;
5286 if (FindMember(o, "extras", it)) {
5287 camera->extras_json_string = JsonToString(GetValue(it));
5288 }
5289 }
5290 }
5291
Selmar Kok31cb7f92018-10-03 15:39:05 +02005292 camera->xmag = xmag;
5293 camera->ymag = ymag;
5294 camera->zfar = zfar;
5295 camera->znear = znear;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005296
5297 // TODO(syoyo): Validate parameter values.
5298
5299 return true;
5300}
5301
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005302static bool ParseCamera(Camera *camera, std::string *err, const json &o,
5303 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005304 if (!ParseStringProperty(&camera->type, err, o, "type", true, "Camera")) {
5305 return false;
5306 }
5307
5308 if (camera->type.compare("orthographic") == 0) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005309 json_const_iterator orthoIt;
5310 if (!FindMember(o, "orthographic", orthoIt)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005311 if (err) {
5312 std::stringstream ss;
5313 ss << "Orhographic camera description not found." << std::endl;
5314 (*err) += ss.str();
5315 }
5316 return false;
5317 }
5318
jrkooncecba5d6c2019-08-29 11:26:22 -05005319 const json &v = GetValue(orthoIt);
5320 if (!IsObject(v)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005321 if (err) {
5322 std::stringstream ss;
5323 ss << "\"orthographic\" is not a JSON object." << std::endl;
5324 (*err) += ss.str();
5325 }
5326 return false;
5327 }
5328
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005329 if (!ParseOrthographicCamera(
5330 &camera->orthographic, err, v,
5331 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005332 return false;
5333 }
5334 } else if (camera->type.compare("perspective") == 0) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005335 json_const_iterator perspIt;
5336 if (!FindMember(o, "perspective", perspIt)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005337 if (err) {
5338 std::stringstream ss;
5339 ss << "Perspective camera description not found." << std::endl;
5340 (*err) += ss.str();
5341 }
5342 return false;
5343 }
5344
jrkooncecba5d6c2019-08-29 11:26:22 -05005345 const json &v = GetValue(perspIt);
5346 if (!IsObject(v)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005347 if (err) {
5348 std::stringstream ss;
5349 ss << "\"perspective\" is not a JSON object." << std::endl;
5350 (*err) += ss.str();
5351 }
5352 return false;
5353 }
5354
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005355 if (!ParsePerspectiveCamera(
5356 &camera->perspective, err, v,
5357 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005358 return false;
5359 }
5360 } else {
5361 if (err) {
5362 std::stringstream ss;
5363 ss << "Invalid camera type: \"" << camera->type
5364 << "\". Must be \"perspective\" or \"orthographic\"" << std::endl;
5365 (*err) += ss.str();
5366 }
5367 return false;
5368 }
5369
5370 ParseStringProperty(&camera->name, err, o, "name", false);
5371
Selmar09d2ff12018-03-15 17:30:42 +01005372 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005373 ParseExtrasProperty(&(camera->extras), o);
5374
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005375 if (store_original_json_for_extras_and_extensions) {
5376 {
5377 json_const_iterator it;
5378 if (FindMember(o, "extensions", it)) {
5379 camera->extensions_json_string = JsonToString(GetValue(it));
5380 }
5381 }
5382 {
5383 json_const_iterator it;
5384 if (FindMember(o, "extras", it)) {
5385 camera->extras_json_string = JsonToString(GetValue(it));
5386 }
5387 }
5388 }
5389
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005390 return true;
5391}
5392
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005393static bool ParseLight(Light *light, std::string *err, const json &o,
5394 bool store_original_json_for_extras_and_extensions) {
Johan Bowald52936a02019-07-17 09:06:45 +02005395 if (!ParseStringProperty(&light->type, err, o, "type", true)) {
5396 return false;
5397 }
5398
5399 if (light->type == "spot") {
jrkooncecba5d6c2019-08-29 11:26:22 -05005400 json_const_iterator spotIt;
5401 if (!FindMember(o, "spot", spotIt)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005402 if (err) {
5403 std::stringstream ss;
5404 ss << "Spot light description not found." << std::endl;
5405 (*err) += ss.str();
5406 }
5407 return false;
Benjamin Schmithüsen4557b6a2019-07-09 16:55:55 +02005408 }
5409
jrkooncecba5d6c2019-08-29 11:26:22 -05005410 const json &v = GetValue(spotIt);
5411 if (!IsObject(v)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005412 if (err) {
5413 std::stringstream ss;
5414 ss << "\"spot\" is not a JSON object." << std::endl;
5415 (*err) += ss.str();
5416 }
5417 return false;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005418 }
5419
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005420 if (!ParseSpotLight(&light->spot, err, v,
5421 store_original_json_for_extras_and_extensions)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005422 return false;
5423 }
5424 }
5425
5426 ParseStringProperty(&light->name, err, o, "name", false);
5427 ParseNumberArrayProperty(&light->color, err, o, "color", false);
5428 ParseNumberProperty(&light->range, err, o, "range", false);
5429 ParseNumberProperty(&light->intensity, err, o, "intensity", false);
5430 ParseExtensionsProperty(&light->extensions, err, o);
5431 ParseExtrasProperty(&(light->extras), o);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005432
5433 if (store_original_json_for_extras_and_extensions) {
5434 {
5435 json_const_iterator it;
5436 if (FindMember(o, "extensions", it)) {
5437 light->extensions_json_string = JsonToString(GetValue(it));
5438 }
5439 }
5440 {
5441 json_const_iterator it;
5442 if (FindMember(o, "extras", it)) {
5443 light->extras_json_string = JsonToString(GetValue(it));
5444 }
5445 }
5446 }
5447
Johan Bowald52936a02019-07-17 09:06:45 +02005448 return true;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005449}
5450
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09005451bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005452 const char *json_str,
5453 unsigned int json_str_length,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09005454 const std::string &base_dir,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005455 unsigned int check_sections) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005456 if (json_str_length < 4) {
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09005457 if (err) {
5458 (*err) = "JSON string too short.\n";
5459 }
5460 return false;
5461 }
5462
jrkooncecba5d6c2019-08-29 11:26:22 -05005463 JsonDocument v;
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005464
Syoyo Fujitad42767e2018-03-15 21:52:00 -05005465#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
5466 defined(_CPPUNWIND)) && \
Syoyo Fujitac4166e42020-01-08 02:38:01 +09005467 !defined(TINYGLTF_NOEXCEPTION)
Syoyo Fujitacdf84902017-08-01 20:12:08 +09005468 try {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005469 JsonParse(v, json_str, json_str_length, true);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005470
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005471 } catch (const std::exception &e) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005472 if (err) {
Syoyo Fujitacdf84902017-08-01 20:12:08 +09005473 (*err) = e.what();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005474 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005475 return false;
5476 }
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005477#else
5478 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005479 JsonParse(v, json_str, json_str_length);
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005480
jrkooncecba5d6c2019-08-29 11:26:22 -05005481 if (!IsObject(v)) {
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005482 // Assume parsing was failed.
5483 if (err) {
5484 (*err) = "Failed to parse JSON object\n";
5485 }
5486 return false;
5487 }
5488 }
5489#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005490
jrkooncecba5d6c2019-08-29 11:26:22 -05005491 if (!IsObject(v)) {
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09005492 // root is not an object.
5493 if (err) {
5494 (*err) = "Root element is not a JSON object\n";
5495 }
5496 return false;
5497 }
5498
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02005499 {
5500 bool version_found = false;
jrkooncecba5d6c2019-08-29 11:26:22 -05005501 json_const_iterator it;
5502 if (FindMember(v, "asset", it) && IsObject(GetValue(it))) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005503 auto &itObj = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05005504 json_const_iterator version_it;
5505 std::string versionStr;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005506 if (FindMember(itObj, "version", version_it) &&
5507 GetString(GetValue(version_it), versionStr)) {
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02005508 version_found = true;
5509 }
5510 }
5511 if (version_found) {
5512 // OK
5513 } else if (check_sections & REQUIRE_VERSION) {
5514 if (err) {
5515 (*err) += "\"asset\" object not found in .gltf or not an object type\n";
5516 }
5517 return false;
5518 }
5519 }
5520
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005521 // scene is not mandatory.
Syoyo Fujita5b407452017-06-04 17:42:41 +09005522 // FIXME Maybe a better way to handle it than removing the code
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005523
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005524 auto IsArrayMemberPresent = [](const json &_v, const char *name) -> bool {
jrkooncecba5d6c2019-08-29 11:26:22 -05005525 json_const_iterator it;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005526 return FindMember(_v, name, it) && IsArray(GetValue(it));
jrkooncecba5d6c2019-08-29 11:26:22 -05005527 };
5528
Syoyo Fujita83675312017-12-02 21:14:13 +09005529 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005530 if ((check_sections & REQUIRE_SCENES) &&
5531 !IsArrayMemberPresent(v, "scenes")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005532 if (err) {
5533 (*err) += "\"scenes\" object not found in .gltf or not an array type\n";
5534 }
5535 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005536 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005537 }
5538
Syoyo Fujita83675312017-12-02 21:14:13 +09005539 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005540 if ((check_sections & REQUIRE_NODES) && !IsArrayMemberPresent(v, "nodes")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005541 if (err) {
5542 (*err) += "\"nodes\" object not found in .gltf\n";
5543 }
5544 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005545 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005546 }
5547
Syoyo Fujita83675312017-12-02 21:14:13 +09005548 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005549 if ((check_sections & REQUIRE_ACCESSORS) &&
5550 !IsArrayMemberPresent(v, "accessors")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005551 if (err) {
5552 (*err) += "\"accessors\" object not found in .gltf\n";
5553 }
5554 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005555 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005556 }
5557
Syoyo Fujita83675312017-12-02 21:14:13 +09005558 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005559 if ((check_sections & REQUIRE_BUFFERS) &&
5560 !IsArrayMemberPresent(v, "buffers")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005561 if (err) {
5562 (*err) += "\"buffers\" object not found in .gltf\n";
5563 }
5564 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005565 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005566 }
5567
Syoyo Fujita83675312017-12-02 21:14:13 +09005568 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005569 if ((check_sections & REQUIRE_BUFFER_VIEWS) &&
5570 !IsArrayMemberPresent(v, "bufferViews")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005571 if (err) {
5572 (*err) += "\"bufferViews\" object not found in .gltf\n";
5573 }
5574 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005575 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005576 }
5577
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005578 model->buffers.clear();
5579 model->bufferViews.clear();
5580 model->accessors.clear();
5581 model->meshes.clear();
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005582 model->cameras.clear();
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005583 model->nodes.clear();
5584 model->extensionsUsed.clear();
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00005585 model->extensionsRequired.clear();
Selmar09d2ff12018-03-15 17:30:42 +01005586 model->extensions.clear();
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005587 model->defaultScene = -1;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005588
Syoyo Fujita83675312017-12-02 21:14:13 +09005589 // 1. Parse Asset
5590 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005591 json_const_iterator it;
5592 if (FindMember(v, "asset", it) && IsObject(GetValue(it))) {
5593 const json &root = GetValue(it);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005594
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005595 ParseAsset(&model->asset, err, root,
5596 store_original_json_for_extras_and_extensions_);
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00005597 }
5598 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005599
jrkoonce51453942019-09-03 09:48:30 -05005600#ifdef TINYGLTF_USE_CPP14
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005601 auto ForEachInArray = [](const json &_v, const char *member,
5602 const auto &cb) -> bool
jrkoonce51453942019-09-03 09:48:30 -05005603#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005604 // The std::function<> implementation can be less efficient because it will
5605 // allocate heap when the size of the captured lambda is above 16 bytes with
5606 // clang and gcc, but it does not require C++14.
5607 auto ForEachInArray = [](const json &_v, const char *member,
5608 const std::function<bool(const json &)> &cb) -> bool
jrkoonce51453942019-09-03 09:48:30 -05005609#endif
Syoyo Fujita83675312017-12-02 21:14:13 +09005610 {
jrkoonce0d2b6ef2019-09-04 13:46:45 -05005611 json_const_iterator itm;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005612 if (FindMember(_v, member, itm) && IsArray(GetValue(itm))) {
jrkoonce0d2b6ef2019-09-04 13:46:45 -05005613 const json &root = GetValue(itm);
jrkooncecba5d6c2019-08-29 11:26:22 -05005614 auto it = ArrayBegin(root);
5615 auto end = ArrayEnd(root);
5616 for (; it != end; ++it) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005617 if (!cb(*it)) return false;
Syoyo Fujita59130d12017-08-01 20:23:11 +09005618 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005619 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005620 return true;
5621 };
5622
jrkooncecba5d6c2019-08-29 11:26:22 -05005623 // 2. Parse extensionUsed
5624 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005625 ForEachInArray(v, "extensionsUsed", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005626 std::string str;
5627 GetString(o, str);
5628 model->extensionsUsed.emplace_back(std::move(str));
5629 return true;
5630 });
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005631 }
5632
Syoyo Fujita83675312017-12-02 21:14:13 +09005633 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005634 ForEachInArray(v, "extensionsRequired", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005635 std::string str;
5636 GetString(o, str);
5637 model->extensionsRequired.emplace_back(std::move(str));
5638 return true;
5639 });
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005640 }
5641
Syoyo Fujita83675312017-12-02 21:14:13 +09005642 // 3. Parse Buffer
5643 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005644 bool success = ForEachInArray(v, "buffers", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005645 if (!IsObject(o)) {
5646 if (err) {
5647 (*err) += "`buffers' does not contain an JSON object.";
Syoyo Fujitabeded612016-05-01 20:03:43 +09005648 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005649 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005650 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005651 Buffer buffer;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005652 if (!ParseBuffer(&buffer, err, o,
5653 store_original_json_for_extras_and_extensions_, &fs,
5654 base_dir, is_binary_, bin_data_, bin_size_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005655 return false;
5656 }
5657
5658 model->buffers.emplace_back(std::move(buffer));
5659 return true;
5660 });
5661
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005662 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005663 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005664 }
5665 }
Syoyo Fujita83675312017-12-02 21:14:13 +09005666 // 4. Parse BufferView
5667 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005668 bool success = ForEachInArray(v, "bufferViews", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005669 if (!IsObject(o)) {
5670 if (err) {
5671 (*err) += "`bufferViews' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09005672 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005673 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005674 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005675 BufferView bufferView;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005676 if (!ParseBufferView(&bufferView, err, o,
5677 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005678 return false;
5679 }
5680
5681 model->bufferViews.emplace_back(std::move(bufferView));
5682 return true;
5683 });
5684
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005685 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005686 return false;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005687 }
5688 }
5689
Syoyo Fujita83675312017-12-02 21:14:13 +09005690 // 5. Parse Accessor
5691 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005692 bool success = ForEachInArray(v, "accessors", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005693 if (!IsObject(o)) {
5694 if (err) {
5695 (*err) += "`accessors' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09005696 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005697 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005698 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005699 Accessor accessor;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005700 if (!ParseAccessor(&accessor, err, o,
5701 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005702 return false;
5703 }
5704
5705 model->accessors.emplace_back(std::move(accessor));
5706 return true;
5707 });
5708
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005709 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005710 return false;
Syoyo Fujitac2615632016-06-19 21:56:06 +09005711 }
5712 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005713
Syoyo Fujita83675312017-12-02 21:14:13 +09005714 // 6. Parse Mesh
5715 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005716 bool success = ForEachInArray(v, "meshes", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005717 if (!IsObject(o)) {
5718 if (err) {
5719 (*err) += "`meshes' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09005720 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005721 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005722 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005723 Mesh mesh;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005724 if (!ParseMesh(&mesh, model, err, o,
5725 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005726 return false;
5727 }
5728
5729 model->meshes.emplace_back(std::move(mesh));
5730 return true;
5731 });
5732
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005733 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005734 return false;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005735 }
5736 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01005737
viperscape9df05802018-12-05 14:11:01 -05005738 // Assign missing bufferView target types
5739 // - Look for missing Mesh indices
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01005740 // - Look for missing Mesh attributes
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00005741 for (auto &mesh : model->meshes) {
5742 for (auto &primitive : mesh.primitives) {
5743 if (primitive.indices >
5744 -1) // has indices from parsing step, must be Element Array Buffer
viperscape9df05802018-12-05 14:11:01 -05005745 {
Jeff McGlynn89152522019-04-25 16:33:56 -07005746 if (size_t(primitive.indices) >= model->accessors.size()) {
5747 if (err) {
5748 (*err) += "primitive indices accessor out of bounds";
5749 }
5750 return false;
5751 }
5752
Syoyo Fujitacea69e32019-08-20 17:10:30 +09005753 auto bufferView =
5754 model->accessors[size_t(primitive.indices)].bufferView;
Jeff McGlynn89152522019-04-25 16:33:56 -07005755 if (bufferView < 0 || size_t(bufferView) >= model->bufferViews.size()) {
5756 if (err) {
5757 (*err) += "accessor[" + std::to_string(primitive.indices) +
5758 "] invalid bufferView";
5759 }
5760 return false;
5761 }
5762
Syoyo Fujitacea69e32019-08-20 17:10:30 +09005763 model->bufferViews[size_t(bufferView)].target =
Jeff McGlynn89152522019-04-25 16:33:56 -07005764 TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00005765 // we could optionally check if acessors' bufferView type is Scalar, as
5766 // it should be
viperscape9df05802018-12-05 14:11:01 -05005767 }
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01005768
5769 for (auto &attribute : primitive.attributes) {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09005770 model
5771 ->bufferViews[size_t(
5772 model->accessors[size_t(attribute.second)].bufferView)]
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01005773 .target = TINYGLTF_TARGET_ARRAY_BUFFER;
5774 }
Marcin Kacprzakc3d67162020-01-22 13:13:35 +01005775
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09005776 for (auto &target : primitive.targets) {
5777 for (auto &attribute : target) {
Rahul Sheth125e4a22020-07-13 13:56:50 -04005778 auto bufferView =
5779 model->accessors[size_t(attribute.second)].bufferView;
Syoyo Fujita91da2992020-07-15 13:52:39 +09005780 // bufferView could be null(-1) for sparse morph target
5781 if (bufferView >= 0) {
Rahul Sheth125e4a22020-07-13 13:56:50 -04005782 model->bufferViews[size_t(bufferView)].target =
5783 TINYGLTF_TARGET_ARRAY_BUFFER;
5784 }
Marcin Kacprzakc3d67162020-01-22 13:13:35 +01005785 }
5786 }
viperscape9df05802018-12-05 14:11:01 -05005787 }
5788 }
5789
Syoyo Fujita83675312017-12-02 21:14:13 +09005790 // 7. Parse Node
5791 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005792 bool success = ForEachInArray(v, "nodes", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005793 if (!IsObject(o)) {
5794 if (err) {
5795 (*err) += "`nodes' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005796 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005797 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005798 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005799 Node node;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005800 if (!ParseNode(&node, err, o,
5801 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005802 return false;
5803 }
5804
5805 model->nodes.emplace_back(std::move(node));
5806 return true;
5807 });
5808
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005809 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005810 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005811 }
5812 }
5813
5814 // 8. Parse scenes.
5815 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005816 bool success = ForEachInArray(v, "scenes", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005817 if (!IsObject(o)) {
5818 if (err) {
5819 (*err) += "`scenes' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005820 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005821 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005822 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005823 std::vector<int> nodes;
jrkooncea3b8b352019-09-03 10:30:19 -05005824 ParseIntegerArrayProperty(&nodes, err, o, "nodes", false);
jrkooncecba5d6c2019-08-29 11:26:22 -05005825
5826 Scene scene;
5827 scene.nodes = std::move(nodes);
5828
5829 ParseStringProperty(&scene.name, err, o, "name", false);
5830
5831 ParseExtensionsProperty(&scene.extensions, err, o);
5832 ParseExtrasProperty(&scene.extras, o);
5833
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005834 if (store_original_json_for_extras_and_extensions_) {
5835 {
5836 json_const_iterator it;
5837 if (FindMember(o, "extensions", it)) {
Mio Nilssone29ba7c2021-05-11 11:50:35 +02005838 scene.extensions_json_string = JsonToString(GetValue(it));
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005839 }
5840 }
5841 {
5842 json_const_iterator it;
5843 if (FindMember(o, "extras", it)) {
Mio Nilssone29ba7c2021-05-11 11:50:35 +02005844 scene.extras_json_string = JsonToString(GetValue(it));
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005845 }
5846 }
5847 }
5848
jrkooncecba5d6c2019-08-29 11:26:22 -05005849 model->scenes.emplace_back(std::move(scene));
5850 return true;
5851 });
5852
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005853 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005854 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005855 }
5856 }
5857
5858 // 9. Parse default scenes.
5859 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005860 json_const_iterator rootIt;
5861 int iVal;
5862 if (FindMember(v, "scene", rootIt) && GetInt(GetValue(rootIt), iVal)) {
5863 model->defaultScene = iVal;
Syoyo Fujita83675312017-12-02 21:14:13 +09005864 }
5865 }
5866
5867 // 10. Parse Material
5868 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005869 bool success = ForEachInArray(v, "materials", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005870 if (!IsObject(o)) {
5871 if (err) {
5872 (*err) += "`materials' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005873 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005874 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005875 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005876 Material material;
5877 ParseStringProperty(&material.name, err, o, "name", false);
5878
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005879 if (!ParseMaterial(&material, err, o,
5880 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005881 return false;
5882 }
5883
5884 model->materials.emplace_back(std::move(material));
5885 return true;
5886 });
5887
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005888 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005889 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005890 }
5891 }
5892
5893 // 11. Parse Image
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09005894 void *load_image_user_data{nullptr};
5895
5896 LoadImageDataOption load_image_option;
5897
5898 if (user_image_loader_) {
5899 // Use user supplied pointer
5900 load_image_user_data = load_image_user_data_;
5901 } else {
5902 load_image_option.preserve_channels = preserve_image_channels_;
5903 load_image_user_data = reinterpret_cast<void *>(&load_image_option);
5904 }
5905
Syoyo Fujita83675312017-12-02 21:14:13 +09005906 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005907 int idx = 0;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005908 bool success = ForEachInArray(v, "images", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005909 if (!IsObject(o)) {
5910 if (err) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005911 (*err) += "image[" + std::to_string(idx) + "] is not a JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005912 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005913 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005914 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005915 Image image;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005916 if (!ParseImage(&image, idx, err, warn, o,
5917 store_original_json_for_extras_and_extensions_, base_dir,
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09005918 &fs, &this->LoadImageData, load_image_user_data)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005919 return false;
5920 }
5921
5922 if (image.bufferView != -1) {
5923 // Load image from the buffer view.
5924 if (size_t(image.bufferView) >= model->bufferViews.size()) {
5925 if (err) {
5926 std::stringstream ss;
5927 ss << "image[" << idx << "] bufferView \"" << image.bufferView
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005928 << "\" not found in the scene." << std::endl;
jrkooncecba5d6c2019-08-29 11:26:22 -05005929 (*err) += ss.str();
5930 }
5931 return false;
5932 }
5933
5934 const BufferView &bufferView =
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005935 model->bufferViews[size_t(image.bufferView)];
jrkooncecba5d6c2019-08-29 11:26:22 -05005936 if (size_t(bufferView.buffer) >= model->buffers.size()) {
5937 if (err) {
5938 std::stringstream ss;
5939 ss << "image[" << idx << "] buffer \"" << bufferView.buffer
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005940 << "\" not found in the scene." << std::endl;
jrkooncecba5d6c2019-08-29 11:26:22 -05005941 (*err) += ss.str();
5942 }
5943 return false;
5944 }
5945 const Buffer &buffer = model->buffers[size_t(bufferView.buffer)];
5946
5947 if (*LoadImageData == nullptr) {
5948 if (err) {
5949 (*err) += "No LoadImageData callback specified.\n";
5950 }
5951 return false;
5952 }
5953 bool ret = LoadImageData(
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005954 &image, idx, err, warn, image.width, image.height,
5955 &buffer.data[bufferView.byteOffset],
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09005956 static_cast<int>(bufferView.byteLength), load_image_user_data);
jrkooncecba5d6c2019-08-29 11:26:22 -05005957 if (!ret) {
5958 return false;
5959 }
5960 }
5961
5962 model->images.emplace_back(std::move(image));
5963 ++idx;
5964 return true;
5965 });
5966
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005967 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005968 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005969 }
5970 }
5971
5972 // 12. Parse Texture
5973 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005974 bool success = ForEachInArray(v, "textures", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005975 if (!IsObject(o)) {
5976 if (err) {
5977 (*err) += "`textures' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005978 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005979 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005980 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005981 Texture texture;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005982 if (!ParseTexture(&texture, err, o,
5983 store_original_json_for_extras_and_extensions_,
5984 base_dir)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005985 return false;
5986 }
5987
5988 model->textures.emplace_back(std::move(texture));
5989 return true;
5990 });
5991
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005992 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005993 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005994 }
5995 }
5996
5997 // 13. Parse Animation
5998 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005999 bool success = ForEachInArray(v, "animations", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006000 if (!IsObject(o)) {
6001 if (err) {
6002 (*err) += "`animations' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006003 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006004 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006005 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006006 Animation animation;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006007 if (!ParseAnimation(&animation, err, o,
6008 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006009 return false;
6010 }
6011
6012 model->animations.emplace_back(std::move(animation));
6013 return true;
6014 });
6015
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006016 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006017 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006018 }
6019 }
6020
6021 // 14. Parse Skin
6022 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006023 bool success = ForEachInArray(v, "skins", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006024 if (!IsObject(o)) {
6025 if (err) {
6026 (*err) += "`skins' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006027 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006028 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006029 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006030 Skin skin;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006031 if (!ParseSkin(&skin, err, o,
6032 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006033 return false;
6034 }
6035
6036 model->skins.emplace_back(std::move(skin));
6037 return true;
6038 });
6039
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006040 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006041 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006042 }
6043 }
6044
6045 // 15. Parse Sampler
6046 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006047 bool success = ForEachInArray(v, "samplers", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006048 if (!IsObject(o)) {
6049 if (err) {
6050 (*err) += "`samplers' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006051 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006052 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006053 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006054 Sampler sampler;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006055 if (!ParseSampler(&sampler, err, o,
6056 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006057 return false;
6058 }
6059
6060 model->samplers.emplace_back(std::move(sampler));
6061 return true;
6062 });
6063
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006064 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006065 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006066 }
6067 }
6068
6069 // 16. Parse Camera
6070 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006071 bool success = ForEachInArray(v, "cameras", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006072 if (!IsObject(o)) {
6073 if (err) {
6074 (*err) += "`cameras' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006075 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006076 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006077 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006078 Camera camera;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006079 if (!ParseCamera(&camera, err, o,
6080 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006081 return false;
6082 }
6083
6084 model->cameras.emplace_back(std::move(camera));
6085 return true;
6086 });
6087
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006088 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006089 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006090 }
6091 }
6092
6093 // 17. Parse Extensions
Selmar09d2ff12018-03-15 17:30:42 +01006094 ParseExtensionsProperty(&model->extensions, err, v);
6095
6096 // 18. Specific extension implementations
Syoyo Fujita83675312017-12-02 21:14:13 +09006097 {
jrkooncecba5d6c2019-08-29 11:26:22 -05006098 json_const_iterator rootIt;
6099 if (FindMember(v, "extensions", rootIt) && IsObject(GetValue(rootIt))) {
6100 const json &root = GetValue(rootIt);
Syoyo Fujita83675312017-12-02 21:14:13 +09006101
jrkooncecba5d6c2019-08-29 11:26:22 -05006102 json_const_iterator it(ObjectBegin(root));
6103 json_const_iterator itEnd(ObjectEnd(root));
Syoyo Fujita83675312017-12-02 21:14:13 +09006104 for (; it != itEnd; ++it) {
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02006105 // parse KHR_lights_punctual extension
jrkooncecba5d6c2019-08-29 11:26:22 -05006106 std::string key(GetKey(it));
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006107 if ((key == "KHR_lights_punctual") && IsObject(GetValue(it))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006108 const json &object = GetValue(it);
6109 json_const_iterator itLight;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006110 if (FindMember(object, "lights", itLight)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006111 const json &lights = GetValue(itLight);
6112 if (!IsArray(lights)) {
6113 continue;
Syoyo Fujita83675312017-12-02 21:14:13 +09006114 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006115
6116 auto arrayIt(ArrayBegin(lights));
6117 auto arrayItEnd(ArrayEnd(lights));
6118 for (; arrayIt != arrayItEnd; ++arrayIt) {
6119 Light light;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006120 if (!ParseLight(&light, err, *arrayIt,
6121 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006122 return false;
6123 }
6124 model->lights.emplace_back(std::move(light));
6125 }
Syoyo Fujita83675312017-12-02 21:14:13 +09006126 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01006127 }
6128 }
6129 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01006130 }
Syoyo Fujita83675312017-12-02 21:14:13 +09006131
mynzc0d4d1c2018-06-28 23:06:00 +09006132 // 19. Parse Extras
6133 ParseExtrasProperty(&model->extras, v);
6134
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006135 if (store_original_json_for_extras_and_extensions_) {
6136 model->extras_json_string = JsonToString(v["extras"]);
6137 model->extensions_json_string = JsonToString(v["extensions"]);
6138 }
6139
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09006140 return true;
6141}
6142
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006143bool TinyGLTF::LoadASCIIFromString(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006144 std::string *warn, const char *str,
6145 unsigned int length,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006146 const std::string &base_dir,
6147 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006148 is_binary_ = false;
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09006149 bin_data_ = nullptr;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006150 bin_size_ = 0;
6151
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006152 return LoadFromString(model, err, warn, str, length, base_dir,
6153 check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006154}
6155
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006156bool TinyGLTF::LoadASCIIFromFile(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006157 std::string *warn, const std::string &filename,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006158 unsigned int check_sections) {
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006159 std::stringstream ss;
6160
Paolo Jovone6601bf2018-07-07 20:43:33 +02006161 if (fs.ReadWholeFile == nullptr) {
6162 // Programmer error, assert() ?
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006163 ss << "Failed to read file: " << filename
6164 << ": one or more FS callback not set" << std::endl;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09006165 if (err) {
6166 (*err) = ss.str();
6167 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006168 return false;
6169 }
6170
Paolo Jovone6601bf2018-07-07 20:43:33 +02006171 std::vector<unsigned char> data;
6172 std::string fileerr;
6173 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006174 if (!fileread) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02006175 ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
6176 if (err) {
6177 (*err) = ss.str();
6178 }
6179 return false;
6180 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006181
Paolo Jovone6601bf2018-07-07 20:43:33 +02006182 size_t sz = data.size();
Luke San Antoniob310b4a2016-06-23 14:17:37 -04006183 if (sz == 0) {
6184 if (err) {
6185 (*err) = "Empty file.";
6186 }
6187 return false;
6188 }
6189
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006190 std::string basedir = GetBaseDir(filename);
6191
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006192 bool ret = LoadASCIIFromString(
6193 model, err, warn, reinterpret_cast<const char *>(&data.at(0)),
6194 static_cast<unsigned int>(data.size()), basedir, check_sections);
Luke San Antonio9e36b612016-06-23 14:10:51 -04006195
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006196 return ret;
6197}
6198
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006199bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006200 std::string *warn,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006201 const unsigned char *bytes,
6202 unsigned int size,
6203 const std::string &base_dir,
6204 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006205 if (size < 20) {
6206 if (err) {
6207 (*err) = "Too short data size for glTF Binary.";
6208 }
6209 return false;
6210 }
6211
Syoyo Fujitabeded612016-05-01 20:03:43 +09006212 if (bytes[0] == 'g' && bytes[1] == 'l' && bytes[2] == 'T' &&
6213 bytes[3] == 'F') {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006214 // ok
6215 } else {
6216 if (err) {
6217 (*err) = "Invalid magic.";
6218 }
6219 return false;
6220 }
6221
Syoyo Fujitabeded612016-05-01 20:03:43 +09006222 unsigned int version; // 4 bytes
6223 unsigned int length; // 4 bytes
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00006224 unsigned int model_length; // 4 bytes
6225 unsigned int model_format; // 4 bytes;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006226
6227 // @todo { Endian swap for big endian machine. }
Syoyo Fujitabeded612016-05-01 20:03:43 +09006228 memcpy(&version, bytes + 4, 4);
6229 swap4(&version);
6230 memcpy(&length, bytes + 8, 4);
6231 swap4(&length);
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00006232 memcpy(&model_length, bytes + 12, 4);
6233 swap4(&model_length);
6234 memcpy(&model_format, bytes + 16, 4);
6235 swap4(&model_format);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006236
Syoyo Fujitae69069d2018-03-15 22:01:18 -05006237 // In case the Bin buffer is not present, the size is exactly 20 + size of
6238 // JSON contents,
6239 // so use "greater than" operator.
Jeff McGlynn89152522019-04-25 16:33:56 -07006240 if ((20 + model_length > size) || (model_length < 1) || (length > size) ||
Doug Muircf668682019-10-28 09:51:13 -07006241 (20 + model_length > length) ||
Syoyo Fujita40c4a0a2017-05-30 01:21:36 +09006242 (model_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006243 if (err) {
6244 (*err) = "Invalid glTF binary.";
6245 }
6246 return false;
6247 }
6248
6249 // Extract JSON string.
Syoyo Fujitabeded612016-05-01 20:03:43 +09006250 std::string jsonString(reinterpret_cast<const char *>(&bytes[20]),
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00006251 model_length);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006252
6253 is_binary_ = true;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006254 bin_data_ = bytes + 20 + model_length +
6255 8; // 4 bytes (buffer_length) + 4 bytes(buffer_format)
Syoyo Fujitabeded612016-05-01 20:03:43 +09006256 bin_size_ =
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00006257 length - (20 + model_length); // extract header + JSON scene data.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006258
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006259 bool ret = LoadFromString(model, err, warn,
6260 reinterpret_cast<const char *>(&bytes[20]),
6261 model_length, base_dir, check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006262 if (!ret) {
6263 return ret;
6264 }
6265
6266 return true;
6267}
6268
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006269bool TinyGLTF::LoadBinaryFromFile(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006270 std::string *warn,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006271 const std::string &filename,
6272 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006273 std::stringstream ss;
6274
Paolo Jovone6601bf2018-07-07 20:43:33 +02006275 if (fs.ReadWholeFile == nullptr) {
6276 // Programmer error, assert() ?
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006277 ss << "Failed to read file: " << filename
6278 << ": one or more FS callback not set" << std::endl;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006279 if (err) {
6280 (*err) = ss.str();
6281 }
6282 return false;
6283 }
6284
Paolo Jovone6601bf2018-07-07 20:43:33 +02006285 std::vector<unsigned char> data;
6286 std::string fileerr;
6287 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006288 if (!fileread) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02006289 ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
6290 if (err) {
6291 (*err) = ss.str();
6292 }
6293 return false;
6294 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006295
Syoyo Fujitabeded612016-05-01 20:03:43 +09006296 std::string basedir = GetBaseDir(filename);
6297
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006298 bool ret = LoadBinaryFromMemory(model, err, warn, &data.at(0),
6299 static_cast<unsigned int>(data.size()),
6300 basedir, check_sections);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006301
6302 return ret;
6303}
6304
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006305///////////////////////
6306// GLTF Serialization
6307///////////////////////
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006308namespace {
6309json JsonFromString(const char *s) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006310#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006311 return json(s, GetAllocator());
jrkooncecba5d6c2019-08-29 11:26:22 -05006312#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006313 return json(s);
jrkooncecba5d6c2019-08-29 11:26:22 -05006314#endif
jrkoonce63419a12019-09-03 17:06:41 -05006315}
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006316
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006317void JsonAssign(json &dest, const json &src) {
6318#ifdef TINYGLTF_USE_RAPIDJSON
6319 dest.CopyFrom(src, GetAllocator());
6320#else
6321 dest = src;
6322#endif
6323}
6324
6325void JsonAddMember(json &o, const char *key, json &&value) {
6326#ifdef TINYGLTF_USE_RAPIDJSON
6327 if (!o.IsObject()) {
6328 o.SetObject();
6329 }
6330 o.AddMember(json(key, GetAllocator()), std::move(value), GetAllocator());
6331#else
6332 o[key] = std::move(value);
6333#endif
6334}
6335
6336void JsonPushBack(json &o, json &&value) {
6337#ifdef TINYGLTF_USE_RAPIDJSON
6338 o.PushBack(std::move(value), GetAllocator());
6339#else
6340 o.push_back(std::move(value));
6341#endif
6342}
6343
6344bool JsonIsNull(const json &o) {
6345#ifdef TINYGLTF_USE_RAPIDJSON
6346 return o.IsNull();
6347#else
6348 return o.is_null();
6349#endif
6350}
6351
6352void JsonSetObject(json &o) {
6353#ifdef TINYGLTF_USE_RAPIDJSON
6354 o.SetObject();
6355#else
6356 o = o.object({});
6357#endif
6358}
6359
6360void JsonReserveArray(json &o, size_t s) {
6361#ifdef TINYGLTF_USE_RAPIDJSON
6362 o.SetArray();
6363 o.Reserve(static_cast<rapidjson::SizeType>(s), GetAllocator());
6364#endif
6365 (void)(o);
6366 (void)(s);
6367}
6368} // namespace
6369
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006370// typedef std::pair<std::string, json> json_object_pair;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006371
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006372template <typename T>
6373static void SerializeNumberProperty(const std::string &key, T number,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006374 json &obj) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006375 // obj.insert(
Syoyo Fujita83675312017-12-02 21:14:13 +09006376 // json_object_pair(key, json(static_cast<double>(number))));
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006377 // obj[key] = static_cast<double>(number);
jrkooncecba5d6c2019-08-29 11:26:22 -05006378 JsonAddMember(obj, key.c_str(), json(number));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006379}
6380
Ivo van Dongen272a9df2020-05-29 11:24:11 +03006381#ifdef TINYGLTF_USE_RAPIDJSON
6382template <>
6383void SerializeNumberProperty(const std::string &key, size_t number, json &obj) {
6384 JsonAddMember(obj, key.c_str(), json(static_cast<uint64_t>(number)));
6385}
6386#endif
6387
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006388template <typename T>
6389static void SerializeNumberArrayProperty(const std::string &key,
6390 const std::vector<T> &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006391 json &obj) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006392 if (value.empty()) return;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006393
jrkooncecba5d6c2019-08-29 11:26:22 -05006394 json ary;
6395 JsonReserveArray(ary, value.size());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006396 for (const auto &s : value) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006397 JsonPushBack(ary, json(s));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006398 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006399 JsonAddMember(obj, key.c_str(), std::move(ary));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006400}
6401
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006402static void SerializeStringProperty(const std::string &key,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006403 const std::string &value, json &obj) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006404 JsonAddMember(obj, key.c_str(), JsonFromString(value.c_str()));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006405}
6406
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006407static void SerializeStringArrayProperty(const std::string &key,
6408 const std::vector<std::string> &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006409 json &obj) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006410 json ary;
6411 JsonReserveArray(ary, value.size());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006412 for (auto &s : value) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006413 JsonPushBack(ary, JsonFromString(s.c_str()));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006414 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006415 JsonAddMember(obj, key.c_str(), std::move(ary));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006416}
6417
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006418static bool ValueToJson(const Value &value, json *ret) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006419 json obj;
jrkooncecba5d6c2019-08-29 11:26:22 -05006420#ifdef TINYGLTF_USE_RAPIDJSON
6421 switch (value.Type()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006422 case REAL_TYPE:
6423 obj.SetDouble(value.Get<double>());
6424 break;
6425 case INT_TYPE:
6426 obj.SetInt(value.Get<int>());
6427 break;
6428 case BOOL_TYPE:
6429 obj.SetBool(value.Get<bool>());
6430 break;
6431 case STRING_TYPE:
6432 obj.SetString(value.Get<std::string>().c_str(), GetAllocator());
6433 break;
6434 case ARRAY_TYPE: {
6435 obj.SetArray();
6436 obj.Reserve(static_cast<rapidjson::SizeType>(value.ArrayLen()),
6437 GetAllocator());
6438 for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
6439 Value elementValue = value.Get(int(i));
6440 json elementJson;
6441 if (ValueToJson(value.Get(int(i)), &elementJson))
6442 obj.PushBack(std::move(elementJson), GetAllocator());
jrkooncecba5d6c2019-08-29 11:26:22 -05006443 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006444 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05006445 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006446 case BINARY_TYPE:
6447 // TODO
6448 // obj = json(value.Get<std::vector<unsigned char>>());
6449 return false;
6450 break;
6451 case OBJECT_TYPE: {
6452 obj.SetObject();
6453 Value::Object objMap = value.Get<Value::Object>();
6454 for (auto &it : objMap) {
6455 json elementJson;
6456 if (ValueToJson(it.second, &elementJson)) {
6457 obj.AddMember(json(it.first.c_str(), GetAllocator()),
6458 std::move(elementJson), GetAllocator());
6459 }
6460 }
6461 break;
6462 }
6463 case NULL_TYPE:
6464 default:
6465 return false;
jrkooncecba5d6c2019-08-29 11:26:22 -05006466 }
6467#else
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006468 switch (value.Type()) {
Syoyo Fujita150f2432019-07-25 19:22:44 +09006469 case REAL_TYPE:
Selmar Kokfa7022f2018-04-04 18:10:20 +02006470 obj = json(value.Get<double>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006471 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006472 case INT_TYPE:
6473 obj = json(value.Get<int>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006474 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006475 case BOOL_TYPE:
6476 obj = json(value.Get<bool>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006477 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006478 case STRING_TYPE:
6479 obj = json(value.Get<std::string>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006480 break;
6481 case ARRAY_TYPE: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006482 for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
6483 Value elementValue = value.Get(int(i));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006484 json elementJson;
6485 if (ValueToJson(value.Get(int(i)), &elementJson))
Selmar Kokfa7022f2018-04-04 18:10:20 +02006486 obj.push_back(elementJson);
6487 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006488 break;
6489 }
Selmar Kokfa7022f2018-04-04 18:10:20 +02006490 case BINARY_TYPE:
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006491 // TODO
6492 // obj = json(value.Get<std::vector<unsigned char>>());
Selmar Kokfa7022f2018-04-04 18:10:20 +02006493 return false;
6494 break;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006495 case OBJECT_TYPE: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006496 Value::Object objMap = value.Get<Value::Object>();
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006497 for (auto &it : objMap) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006498 json elementJson;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006499 if (ValueToJson(it.second, &elementJson)) obj[it.first] = elementJson;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006500 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006501 break;
6502 }
6503 case NULL_TYPE:
6504 default:
6505 return false;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006506 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006507#endif
6508 if (ret) *ret = std::move(obj);
Selmar Kokfa7022f2018-04-04 18:10:20 +02006509 return true;
6510}
6511
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006512static void SerializeValue(const std::string &key, const Value &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006513 json &obj) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006514 json ret;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006515 if (ValueToJson(value, &ret)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006516 JsonAddMember(obj, key.c_str(), std::move(ret));
6517 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006518}
6519
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006520static void SerializeGltfBufferData(const std::vector<unsigned char> &data,
johan bowald30c53472018-03-30 11:49:36 +02006521 json &o) {
6522 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09006523 if (data.size() > 0) {
6524 std::string encodedData =
6525 base64_encode(&data[0], static_cast<unsigned int>(data.size()));
6526 SerializeStringProperty("uri", header + encodedData, o);
6527 } else {
6528 // Issue #229
6529 // size 0 is allowd. Just emit mime header.
6530 SerializeStringProperty("uri", header, o);
6531 }
johan bowald30c53472018-03-30 11:49:36 +02006532}
6533
Selmar Koke4677492018-10-25 16:45:49 +02006534static bool SerializeGltfBufferData(const std::vector<unsigned char> &data,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006535 const std::string &binFilename) {
Harokyangfb256602019-10-30 16:13:52 +08006536#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09006537#if defined(__GLIBCXX__) // mingw
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09006538 int file_descriptor = _wopen(UTF8ToWchar(binFilename).c_str(),
6539 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
6540 __gnu_cxx::stdio_filebuf<char> wfile_buf(
6541 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09006542 std::ostream output(&wfile_buf);
6543 if (!wfile_buf.is_open()) return false;
6544#elif defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08006545 std::ofstream output(UTF8ToWchar(binFilename).c_str(), std::ofstream::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09006546 if (!output.is_open()) return false;
Harokyangfb256602019-10-30 16:13:52 +08006547#else
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006548 std::ofstream output(binFilename.c_str(), std::ofstream::binary);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006549 if (!output.is_open()) return false;
Syoyo Fujita45cac782019-11-09 20:42:55 +09006550#endif
6551#else
6552 std::ofstream output(binFilename.c_str(), std::ofstream::binary);
6553 if (!output.is_open()) return false;
6554#endif
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09006555 if (data.size() > 0) {
6556 output.write(reinterpret_cast<const char *>(&data[0]),
6557 std::streamsize(data.size()));
6558 } else {
6559 // Issue #229
6560 // size 0 will be still valid buffer data.
6561 // write empty file.
6562 }
Selmar Koke4677492018-10-25 16:45:49 +02006563 return true;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006564}
6565
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006566#if 0 // FIXME(syoyo): not used. will be removed in the future release.
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006567static void SerializeParameterMap(ParameterMap &param, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006568 for (ParameterMap::iterator paramIt = param.begin(); paramIt != param.end();
6569 ++paramIt) {
6570 if (paramIt->second.number_array.size()) {
6571 SerializeNumberArrayProperty<double>(paramIt->first,
6572 paramIt->second.number_array, o);
6573 } else if (paramIt->second.json_double_value.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006574 json json_double_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006575 for (std::map<std::string, double>::iterator it =
6576 paramIt->second.json_double_value.begin();
6577 it != paramIt->second.json_double_value.end(); ++it) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006578 if (it->first == "index") {
6579 json_double_value[it->first] = paramIt->second.TextureIndex();
6580 } else {
6581 json_double_value[it->first] = it->second;
6582 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006583 }
6584
Syoyo Fujita83675312017-12-02 21:14:13 +09006585 o[paramIt->first] = json_double_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006586 } else if (!paramIt->second.string_value.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006587 SerializeStringProperty(paramIt->first, paramIt->second.string_value, o);
Ben Buzbeef6af2242018-04-25 15:13:05 -07006588 } else if (paramIt->second.has_number_value) {
6589 o[paramIt->first] = paramIt->second.number_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006590 } else {
Syoyo Fujita83675312017-12-02 21:14:13 +09006591 o[paramIt->first] = paramIt->second.bool_value;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006592 }
6593 }
6594}
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006595#endif
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006596
Selmar Kok81b672b2019-10-18 16:08:44 +02006597static void SerializeExtensionMap(const ExtensionMap &extensions, json &o) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006598 if (!extensions.size()) return;
Selmar09d2ff12018-03-15 17:30:42 +01006599
6600 json extMap;
Selmar Kok81b672b2019-10-18 16:08:44 +02006601 for (ExtensionMap::const_iterator extIt = extensions.begin();
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006602 extIt != extensions.end(); ++extIt) {
Syoyo Fujita924d86e2018-10-08 21:15:12 +09006603 // Allow an empty object for extension(#97)
6604 json ret;
jrkooncecba5d6c2019-08-29 11:26:22 -05006605 bool isNull = true;
Syoyo Fujita924d86e2018-10-08 21:15:12 +09006606 if (ValueToJson(extIt->second, &ret)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006607 isNull = JsonIsNull(ret);
6608 JsonAddMember(extMap, extIt->first.c_str(), std::move(ret));
Selmar Kokdb7f4e42018-10-10 18:10:58 +02006609 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006610 if (isNull) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006611 if (!(extIt->first.empty())) { // name should not be empty, but for sure
6612 // create empty object so that an extension name is still included in
6613 // json.
jrkooncecba5d6c2019-08-29 11:26:22 -05006614 json empty;
jrkoonce06c30c42019-09-03 15:56:48 -05006615 JsonSetObject(empty);
jrkooncecba5d6c2019-08-29 11:26:22 -05006616 JsonAddMember(extMap, extIt->first.c_str(), std::move(empty));
Syoyo Fujita924d86e2018-10-08 21:15:12 +09006617 }
6618 }
Selmar09d2ff12018-03-15 17:30:42 +01006619 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006620 JsonAddMember(o, "extensions", std::move(extMap));
Selmar09d2ff12018-03-15 17:30:42 +01006621}
6622
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006623static void SerializeGltfAccessor(Accessor &accessor, json &o) {
Sanjeet Suhag5ecede72020-03-04 17:40:10 -05006624 if (accessor.bufferView >= 0)
6625 SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006626
Syoyo Fujita18f0e202020-04-29 19:16:35 +09006627 if (accessor.byteOffset != 0)
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006628 SerializeNumberProperty<int>("byteOffset", int(accessor.byteOffset), o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006629
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006630 SerializeNumberProperty<int>("componentType", accessor.componentType, o);
6631 SerializeNumberProperty<size_t>("count", accessor.count, o);
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09006632
6633 if ((accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) ||
6634 (accessor.componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE)) {
6635 SerializeNumberArrayProperty<double>("min", accessor.minValues, o);
6636 SerializeNumberArrayProperty<double>("max", accessor.maxValues, o);
6637 } else {
6638 // Issue #301. Serialize as integer.
6639 // Assume int value is within [-2**31-1, 2**31-1]
6640 {
6641 std::vector<int> values;
6642 std::transform(accessor.minValues.begin(), accessor.minValues.end(),
6643 std::back_inserter(values),
6644 [](double v) { return static_cast<int>(v); });
6645
6646 SerializeNumberArrayProperty<int>("min", values, o);
6647 }
6648
6649 {
6650 std::vector<int> values;
6651 std::transform(accessor.maxValues.begin(), accessor.maxValues.end(),
6652 std::back_inserter(values),
6653 [](double v) { return static_cast<int>(v); });
6654
6655 SerializeNumberArrayProperty<int>("max", values, o);
6656 }
6657 }
6658
Eero Pajarre2e8a1152019-11-18 13:09:25 +02006659 if (accessor.normalized)
6660 SerializeValue("normalized", Value(accessor.normalized), o);
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006661 std::string type;
6662 switch (accessor.type) {
6663 case TINYGLTF_TYPE_SCALAR:
6664 type = "SCALAR";
6665 break;
6666 case TINYGLTF_TYPE_VEC2:
6667 type = "VEC2";
6668 break;
6669 case TINYGLTF_TYPE_VEC3:
6670 type = "VEC3";
6671 break;
6672 case TINYGLTF_TYPE_VEC4:
6673 type = "VEC4";
6674 break;
6675 case TINYGLTF_TYPE_MAT2:
6676 type = "MAT2";
6677 break;
6678 case TINYGLTF_TYPE_MAT3:
6679 type = "MAT3";
6680 break;
6681 case TINYGLTF_TYPE_MAT4:
6682 type = "MAT4";
6683 break;
6684 }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09006685
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006686 SerializeStringProperty("type", type, o);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09006687 if (!accessor.name.empty()) SerializeStringProperty("name", accessor.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006688
6689 if (accessor.extras.Type() != NULL_TYPE) {
6690 SerializeValue("extras", accessor.extras, o);
6691 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006692}
6693
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006694static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006695 SerializeNumberProperty("sampler", channel.sampler, o);
jrkooncecba5d6c2019-08-29 11:26:22 -05006696 {
6697 json target;
6698 SerializeNumberProperty("node", channel.target_node, target);
6699 SerializeStringProperty("path", channel.target_path, target);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006700
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09006701 SerializeExtensionMap(channel.target_extensions, target);
Selmar Kok973d9b32020-01-21 18:45:24 +01006702
jrkooncecba5d6c2019-08-29 11:26:22 -05006703 JsonAddMember(o, "target", std::move(target));
6704 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02006705
6706 if (channel.extras.Type() != NULL_TYPE) {
6707 SerializeValue("extras", channel.extras, o);
6708 }
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006709
Selmar Kok4e2988e2019-08-16 14:08:08 +02006710 SerializeExtensionMap(channel.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006711}
6712
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006713static void SerializeGltfAnimationSampler(AnimationSampler &sampler, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006714 SerializeNumberProperty("input", sampler.input, o);
6715 SerializeNumberProperty("output", sampler.output, o);
6716 SerializeStringProperty("interpolation", sampler.interpolation, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006717
6718 if (sampler.extras.Type() != NULL_TYPE) {
6719 SerializeValue("extras", sampler.extras, o);
6720 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006721}
6722
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006723static void SerializeGltfAnimation(Animation &animation, json &o) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09006724 if (!animation.name.empty())
6725 SerializeStringProperty("name", animation.name, o);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006726
jrkooncecba5d6c2019-08-29 11:26:22 -05006727 {
6728 json channels;
6729 JsonReserveArray(channels, animation.channels.size());
6730 for (unsigned int i = 0; i < animation.channels.size(); ++i) {
6731 json channel;
6732 AnimationChannel gltfChannel = animation.channels[i];
6733 SerializeGltfAnimationChannel(gltfChannel, channel);
6734 JsonPushBack(channels, std::move(channel));
6735 }
6736
6737 JsonAddMember(o, "channels", std::move(channels));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006738 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006739
jrkooncecba5d6c2019-08-29 11:26:22 -05006740 {
6741 json samplers;
Jacek1da4e5d2020-01-06 14:36:57 -06006742 JsonReserveArray(samplers, animation.samplers.size());
jrkooncecba5d6c2019-08-29 11:26:22 -05006743 for (unsigned int i = 0; i < animation.samplers.size(); ++i) {
6744 json sampler;
6745 AnimationSampler gltfSampler = animation.samplers[i];
6746 SerializeGltfAnimationSampler(gltfSampler, sampler);
6747 JsonPushBack(samplers, std::move(sampler));
6748 }
6749 JsonAddMember(o, "samplers", std::move(samplers));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006750 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006751
Jens Olssonb3af2f12018-06-04 10:17:49 +02006752 if (animation.extras.Type() != NULL_TYPE) {
6753 SerializeValue("extras", animation.extras, o);
6754 }
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006755
Selmar Kok4e2988e2019-08-16 14:08:08 +02006756 SerializeExtensionMap(animation.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006757}
6758
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006759static void SerializeGltfAsset(Asset &asset, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006760 if (!asset.generator.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006761 SerializeStringProperty("generator", asset.generator, o);
6762 }
6763
Christophe820ede82019-07-04 15:21:21 +09006764 if (!asset.copyright.empty()) {
6765 SerializeStringProperty("copyright", asset.copyright, o);
6766 }
6767
Syoyo Fujitab702de72021-03-02 19:08:29 +09006768 if (asset.version.empty()) {
6769 // Just in case
6770 // `version` must be defined
6771 asset.version = "2.0";
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006772 }
6773
Syoyo Fujitab702de72021-03-02 19:08:29 +09006774 // TODO(syoyo): Do we need to check if `version` is greater or equal to 2.0?
6775 SerializeStringProperty("version", asset.version, o);
6776
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006777 if (asset.extras.Keys().size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006778 SerializeValue("extras", asset.extras, o);
6779 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006780
Selmar09d2ff12018-03-15 17:30:42 +01006781 SerializeExtensionMap(asset.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006782}
6783
Syoyo Fujitac4166e42020-01-08 02:38:01 +09006784static void SerializeGltfBufferBin(Buffer &buffer, json &o,
6785 std::vector<unsigned char> &binBuffer) {
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02006786 SerializeNumberProperty("byteLength", buffer.data.size(), o);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09006787 binBuffer = buffer.data;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02006788
6789 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
6790
6791 if (buffer.extras.Type() != NULL_TYPE) {
6792 SerializeValue("extras", buffer.extras, o);
6793 }
6794}
6795
johan bowald30c53472018-03-30 11:49:36 +02006796static void SerializeGltfBuffer(Buffer &buffer, json &o) {
6797 SerializeNumberProperty("byteLength", buffer.data.size(), o);
6798 SerializeGltfBufferData(buffer.data, o);
6799
6800 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006801
6802 if (buffer.extras.Type() != NULL_TYPE) {
6803 SerializeValue("extras", buffer.extras, o);
6804 }
johan bowald30c53472018-03-30 11:49:36 +02006805}
6806
Selmar Koke4677492018-10-25 16:45:49 +02006807static bool SerializeGltfBuffer(Buffer &buffer, json &o,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006808 const std::string &binFilename,
6809 const std::string &binBaseFilename) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006810 if (!SerializeGltfBufferData(buffer.data, binFilename)) return false;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006811 SerializeNumberProperty("byteLength", buffer.data.size(), o);
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006812 SerializeStringProperty("uri", binBaseFilename, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006813
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006814 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006815
6816 if (buffer.extras.Type() != NULL_TYPE) {
6817 SerializeValue("extras", buffer.extras, o);
6818 }
Selmar Koke4677492018-10-25 16:45:49 +02006819 return true;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006820}
6821
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006822static void SerializeGltfBufferView(BufferView &bufferView, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006823 SerializeNumberProperty("buffer", bufferView.buffer, o);
6824 SerializeNumberProperty<size_t>("byteLength", bufferView.byteLength, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006825
Johan Bowaldfaa27222018-03-28 14:44:45 +02006826 // byteStride is optional, minimum allowed is 4
6827 if (bufferView.byteStride >= 4) {
6828 SerializeNumberProperty<size_t>("byteStride", bufferView.byteStride, o);
6829 }
6830 // byteOffset is optional, default is 0
6831 if (bufferView.byteOffset > 0) {
6832 SerializeNumberProperty<size_t>("byteOffset", bufferView.byteOffset, o);
6833 }
6834 // Target is optional, check if it contains a valid value
6835 if (bufferView.target == TINYGLTF_TARGET_ARRAY_BUFFER ||
6836 bufferView.target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER) {
6837 SerializeNumberProperty("target", bufferView.target, o);
6838 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006839 if (bufferView.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006840 SerializeStringProperty("name", bufferView.name, o);
6841 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02006842
6843 if (bufferView.extras.Type() != NULL_TYPE) {
6844 SerializeValue("extras", bufferView.extras, o);
6845 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006846}
6847
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006848static void SerializeGltfImage(Image &image, json &o) {
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02006849 // if uri empty, the mimeType and bufferview should be set
rainliang00062be8d02019-04-29 09:54:27 +08006850 if (image.uri.empty()) {
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02006851 SerializeStringProperty("mimeType", image.mimeType, o);
6852 SerializeNumberProperty<int>("bufferView", image.bufferView, o);
6853 } else {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09006854 // TODO(syoyo): dlib::urilencode?
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02006855 SerializeStringProperty("uri", image.uri, o);
6856 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006857
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006858 if (image.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006859 SerializeStringProperty("name", image.name, o);
6860 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02006861
6862 if (image.extras.Type() != NULL_TYPE) {
6863 SerializeValue("extras", image.extras, o);
6864 }
Syoyo Fujita3e53feb2018-09-02 15:36:17 +09006865
6866 SerializeExtensionMap(image.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006867}
6868
Syoyo Fujita046400b2019-07-24 19:26:48 +09006869static void SerializeGltfTextureInfo(TextureInfo &texinfo, json &o) {
6870 SerializeNumberProperty("index", texinfo.index, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006871
Syoyo Fujita046400b2019-07-24 19:26:48 +09006872 if (texinfo.texCoord != 0) {
6873 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
6874 }
6875
6876 if (texinfo.extras.Type() != NULL_TYPE) {
6877 SerializeValue("extras", texinfo.extras, o);
6878 }
6879
6880 SerializeExtensionMap(texinfo.extensions, o);
6881}
6882
6883static void SerializeGltfNormalTextureInfo(NormalTextureInfo &texinfo,
6884 json &o) {
6885 SerializeNumberProperty("index", texinfo.index, o);
6886
6887 if (texinfo.texCoord != 0) {
6888 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
6889 }
6890
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02006891 if (!TINYGLTF_DOUBLE_EQUAL(texinfo.scale, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09006892 SerializeNumberProperty("scale", texinfo.scale, o);
6893 }
6894
6895 if (texinfo.extras.Type() != NULL_TYPE) {
6896 SerializeValue("extras", texinfo.extras, o);
6897 }
6898
6899 SerializeExtensionMap(texinfo.extensions, o);
6900}
6901
6902static void SerializeGltfOcclusionTextureInfo(OcclusionTextureInfo &texinfo,
6903 json &o) {
6904 SerializeNumberProperty("index", texinfo.index, o);
6905
6906 if (texinfo.texCoord != 0) {
6907 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
6908 }
6909
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02006910 if (!TINYGLTF_DOUBLE_EQUAL(texinfo.strength, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09006911 SerializeNumberProperty("strength", texinfo.strength, o);
6912 }
6913
6914 if (texinfo.extras.Type() != NULL_TYPE) {
6915 SerializeValue("extras", texinfo.extras, o);
6916 }
6917
6918 SerializeExtensionMap(texinfo.extensions, o);
6919}
6920
6921static void SerializeGltfPbrMetallicRoughness(PbrMetallicRoughness &pbr,
6922 json &o) {
6923 std::vector<double> default_baseColorFactor = {1.0, 1.0, 1.0, 1.0};
6924 if (!Equals(pbr.baseColorFactor, default_baseColorFactor)) {
6925 SerializeNumberArrayProperty<double>("baseColorFactor", pbr.baseColorFactor,
6926 o);
6927 }
6928
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02006929 if (!TINYGLTF_DOUBLE_EQUAL(pbr.metallicFactor, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09006930 SerializeNumberProperty("metallicFactor", pbr.metallicFactor, o);
6931 }
6932
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02006933 if (!TINYGLTF_DOUBLE_EQUAL(pbr.roughnessFactor, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09006934 SerializeNumberProperty("roughnessFactor", pbr.roughnessFactor, o);
6935 }
6936
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09006937 if (pbr.baseColorTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09006938 json texinfo;
6939 SerializeGltfTextureInfo(pbr.baseColorTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05006940 JsonAddMember(o, "baseColorTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09006941 }
6942
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09006943 if (pbr.metallicRoughnessTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09006944 json texinfo;
6945 SerializeGltfTextureInfo(pbr.metallicRoughnessTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05006946 JsonAddMember(o, "metallicRoughnessTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09006947 }
6948
6949 SerializeExtensionMap(pbr.extensions, o);
6950
6951 if (pbr.extras.Type() != NULL_TYPE) {
6952 SerializeValue("extras", pbr.extras, o);
6953 }
6954}
6955
6956static void SerializeGltfMaterial(Material &material, json &o) {
6957 if (material.name.size()) {
6958 SerializeStringProperty("name", material.name, o);
6959 }
6960
6961 // QUESTION(syoyo): Write material parameters regardless of its default value?
6962
6963 if (!TINYGLTF_DOUBLE_EQUAL(material.alphaCutoff, 0.5)) {
6964 SerializeNumberProperty("alphaCutoff", material.alphaCutoff, o);
6965 }
6966
Patrick Härtld9a468b2019-08-14 14:14:07 +02006967 if (material.alphaMode.compare("OPAQUE") != 0) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09006968 SerializeStringProperty("alphaMode", material.alphaMode, o);
6969 }
6970
Syoyo Fujitac4166e42020-01-08 02:38:01 +09006971 if (material.doubleSided != false)
6972 JsonAddMember(o, "doubleSided", json(material.doubleSided));
Syoyo Fujita046400b2019-07-24 19:26:48 +09006973
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09006974 if (material.normalTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09006975 json texinfo;
6976 SerializeGltfNormalTextureInfo(material.normalTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05006977 JsonAddMember(o, "normalTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09006978 }
6979
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09006980 if (material.occlusionTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09006981 json texinfo;
6982 SerializeGltfOcclusionTextureInfo(material.occlusionTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05006983 JsonAddMember(o, "occlusionTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09006984 }
6985
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09006986 if (material.emissiveTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09006987 json texinfo;
6988 SerializeGltfTextureInfo(material.emissiveTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05006989 JsonAddMember(o, "emissiveTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09006990 }
6991
6992 std::vector<double> default_emissiveFactor = {0.0, 0.0, 0.0};
6993 if (!Equals(material.emissiveFactor, default_emissiveFactor)) {
6994 SerializeNumberArrayProperty<double>("emissiveFactor",
6995 material.emissiveFactor, o);
6996 }
6997
6998 {
6999 json pbrMetallicRoughness;
7000 SerializeGltfPbrMetallicRoughness(material.pbrMetallicRoughness,
7001 pbrMetallicRoughness);
Syoyo Fujita7e009042019-09-13 15:32:22 +09007002 // Issue 204
7003 // Do not serialize `pbrMetallicRoughness` if pbrMetallicRoughness has all
7004 // default values(json is null). Otherwise it will serialize to
7005 // `pbrMetallicRoughness : null`, which cannot be read by other glTF
7006 // importers(and validators).
7007 //
7008 if (!JsonIsNull(pbrMetallicRoughness)) {
7009 JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness));
7010 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09007011 }
7012
7013#if 0 // legacy way. just for the record.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007014 if (material.values.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007015 json pbrMetallicRoughness;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007016 SerializeParameterMap(material.values, pbrMetallicRoughness);
jrkooncecba5d6c2019-08-29 11:26:22 -05007017 JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007018 }
7019
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007020 SerializeParameterMap(material.additionalValues, o);
Syoyo Fujita046400b2019-07-24 19:26:48 +09007021#else
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007022
Syoyo Fujita046400b2019-07-24 19:26:48 +09007023#endif
7024
7025 SerializeExtensionMap(material.extensions, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007026
7027 if (material.extras.Type() != NULL_TYPE) {
7028 SerializeValue("extras", material.extras, o);
7029 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007030}
7031
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007032static void SerializeGltfMesh(Mesh &mesh, json &o) {
Syoyo Fujita83675312017-12-02 21:14:13 +09007033 json primitives;
jrkooncecba5d6c2019-08-29 11:26:22 -05007034 JsonReserveArray(primitives, mesh.primitives.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007035 for (unsigned int i = 0; i < mesh.primitives.size(); ++i) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007036 json primitive;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007037 const Primitive &gltfPrimitive = mesh.primitives[i]; // don't make a copy
jrkooncecba5d6c2019-08-29 11:26:22 -05007038 {
7039 json attributes;
7040 for (auto attrIt = gltfPrimitive.attributes.begin();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007041 attrIt != gltfPrimitive.attributes.end(); ++attrIt) {
jrkooncecba5d6c2019-08-29 11:26:22 -05007042 SerializeNumberProperty<int>(attrIt->first, attrIt->second, attributes);
7043 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007044
jrkooncecba5d6c2019-08-29 11:26:22 -05007045 JsonAddMember(primitive, "attributes", std::move(attributes));
7046 }
Johan Bowaldfaa27222018-03-28 14:44:45 +02007047
7048 // Indicies is optional
7049 if (gltfPrimitive.indices > -1) {
7050 SerializeNumberProperty<int>("indices", gltfPrimitive.indices, primitive);
7051 }
7052 // Material is optional
7053 if (gltfPrimitive.material > -1) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09007054 SerializeNumberProperty<int>("material", gltfPrimitive.material,
7055 primitive);
Johan Bowaldfaa27222018-03-28 14:44:45 +02007056 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007057 SerializeNumberProperty<int>("mode", gltfPrimitive.mode, primitive);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007058
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007059 // Morph targets
7060 if (gltfPrimitive.targets.size()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09007061 json targets;
jrkooncecba5d6c2019-08-29 11:26:22 -05007062 JsonReserveArray(targets, gltfPrimitive.targets.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007063 for (unsigned int k = 0; k < gltfPrimitive.targets.size(); ++k) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007064 json targetAttributes;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007065 std::map<std::string, int> targetData = gltfPrimitive.targets[k];
7066 for (std::map<std::string, int>::iterator attrIt = targetData.begin();
7067 attrIt != targetData.end(); ++attrIt) {
7068 SerializeNumberProperty<int>(attrIt->first, attrIt->second,
7069 targetAttributes);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007070 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007071 JsonPushBack(targets, std::move(targetAttributes));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007072 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007073 JsonAddMember(primitive, "targets", std::move(targets));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007074 }
7075
zhaozhiquan3eb65e22019-12-18 11:28:57 +08007076 SerializeExtensionMap(gltfPrimitive.extensions, primitive);
Selmar Kok81b672b2019-10-18 16:08:44 +02007077
Jens Olssonb3af2f12018-06-04 10:17:49 +02007078 if (gltfPrimitive.extras.Type() != NULL_TYPE) {
7079 SerializeValue("extras", gltfPrimitive.extras, primitive);
7080 }
7081
jrkooncecba5d6c2019-08-29 11:26:22 -05007082 JsonPushBack(primitives, std::move(primitive));
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007083 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007084
jrkooncecba5d6c2019-08-29 11:26:22 -05007085 JsonAddMember(o, "primitives", std::move(primitives));
7086
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007087 if (mesh.weights.size()) {
7088 SerializeNumberArrayProperty<double>("weights", mesh.weights, o);
7089 }
7090
7091 if (mesh.name.size()) {
7092 SerializeStringProperty("name", mesh.name, o);
7093 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007094
Selmar Kok81b672b2019-10-18 16:08:44 +02007095 SerializeExtensionMap(mesh.extensions, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007096 if (mesh.extras.Type() != NULL_TYPE) {
7097 SerializeValue("extras", mesh.extras, o);
7098 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007099}
7100
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007101static void SerializeSpotLight(SpotLight &spot, json &o) {
Johan Bowald52936a02019-07-17 09:06:45 +02007102 SerializeNumberProperty("innerConeAngle", spot.innerConeAngle, o);
7103 SerializeNumberProperty("outerConeAngle", spot.outerConeAngle, o);
7104 SerializeExtensionMap(spot.extensions, o);
Selmar Kok81b672b2019-10-18 16:08:44 +02007105 if (spot.extras.Type() != NULL_TYPE) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09007106 SerializeValue("extras", spot.extras, o);
Selmar Kok81b672b2019-10-18 16:08:44 +02007107 }
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007108}
7109
Syoyo Fujita83675312017-12-02 21:14:13 +09007110static void SerializeGltfLight(Light &light, json &o) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09007111 if (!light.name.empty()) SerializeStringProperty("name", light.name, o);
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007112 SerializeNumberProperty("intensity", light.intensity, o);
Syoyo Fujita3dad3032020-05-13 21:22:23 +09007113 if (light.range > 0.0) {
7114 SerializeNumberProperty("range", light.range, o);
7115 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01007116 SerializeNumberArrayProperty("color", light.color, o);
7117 SerializeStringProperty("type", light.type, o);
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007118 if (light.type == "spot") {
7119 json spot;
7120 SerializeSpotLight(light.spot, spot);
jrkooncecba5d6c2019-08-29 11:26:22 -05007121 JsonAddMember(o, "spot", std::move(spot));
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007122 }
Selmar Kok81b672b2019-10-18 16:08:44 +02007123 SerializeExtensionMap(light.extensions, o);
7124 if (light.extras.Type() != NULL_TYPE) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09007125 SerializeValue("extras", light.extras, o);
Selmar Kok81b672b2019-10-18 16:08:44 +02007126 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01007127}
7128
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007129static void SerializeGltfNode(Node &node, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007130 if (node.translation.size() > 0) {
7131 SerializeNumberArrayProperty<double>("translation", node.translation, o);
7132 }
7133 if (node.rotation.size() > 0) {
7134 SerializeNumberArrayProperty<double>("rotation", node.rotation, o);
7135 }
7136 if (node.scale.size() > 0) {
7137 SerializeNumberArrayProperty<double>("scale", node.scale, o);
7138 }
7139 if (node.matrix.size() > 0) {
7140 SerializeNumberArrayProperty<double>("matrix", node.matrix, o);
7141 }
7142 if (node.mesh != -1) {
7143 SerializeNumberProperty<int>("mesh", node.mesh, o);
7144 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007145
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007146 if (node.skin != -1) {
7147 SerializeNumberProperty<int>("skin", node.skin, o);
7148 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007149
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007150 if (node.camera != -1) {
7151 SerializeNumberProperty<int>("camera", node.camera, o);
7152 }
7153
Benjamin Schmithüsen74c3c102019-08-16 14:24:26 +02007154 if (node.weights.size() > 0) {
7155 SerializeNumberArrayProperty<double>("weights", node.weights, o);
7156 }
7157
Jens Olssona9718662018-05-24 15:48:49 +02007158 if (node.extras.Type() != NULL_TYPE) {
Jens Olssonb96f6962018-05-24 15:29:54 +02007159 SerializeValue("extras", node.extras, o);
7160 }
7161
Selmar09d2ff12018-03-15 17:30:42 +01007162 SerializeExtensionMap(node.extensions, o);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09007163 if (!node.name.empty()) SerializeStringProperty("name", node.name, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007164 SerializeNumberArrayProperty<int>("children", node.children, o);
7165}
7166
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007167static void SerializeGltfSampler(Sampler &sampler, json &o) {
Syoyo Fujitaee179b22019-08-16 13:11:30 +09007168 if (sampler.magFilter != -1) {
7169 SerializeNumberProperty("magFilter", sampler.magFilter, o);
7170 }
7171 if (sampler.minFilter != -1) {
7172 SerializeNumberProperty("minFilter", sampler.minFilter, o);
7173 }
Syoyo Fujita2c521b32020-12-04 00:50:46 +09007174 //SerializeNumberProperty("wrapR", sampler.wrapR, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007175 SerializeNumberProperty("wrapS", sampler.wrapS, o);
7176 SerializeNumberProperty("wrapT", sampler.wrapT, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007177
7178 if (sampler.extras.Type() != NULL_TYPE) {
7179 SerializeValue("extras", sampler.extras, o);
7180 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007181}
7182
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007183static void SerializeGltfOrthographicCamera(const OrthographicCamera &camera,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007184 json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007185 SerializeNumberProperty("zfar", camera.zfar, o);
7186 SerializeNumberProperty("znear", camera.znear, o);
7187 SerializeNumberProperty("xmag", camera.xmag, o);
7188 SerializeNumberProperty("ymag", camera.ymag, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007189
7190 if (camera.extras.Type() != NULL_TYPE) {
7191 SerializeValue("extras", camera.extras, o);
7192 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007193}
7194
7195static void SerializeGltfPerspectiveCamera(const PerspectiveCamera &camera,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007196 json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007197 SerializeNumberProperty("zfar", camera.zfar, o);
7198 SerializeNumberProperty("znear", camera.znear, o);
7199 if (camera.aspectRatio > 0) {
7200 SerializeNumberProperty("aspectRatio", camera.aspectRatio, o);
7201 }
7202
7203 if (camera.yfov > 0) {
7204 SerializeNumberProperty("yfov", camera.yfov, o);
7205 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007206
7207 if (camera.extras.Type() != NULL_TYPE) {
7208 SerializeValue("extras", camera.extras, o);
7209 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007210}
7211
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007212static void SerializeGltfCamera(const Camera &camera, json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007213 SerializeStringProperty("type", camera.type, o);
7214 if (!camera.name.empty()) {
Selmar Kokee3d0662018-10-08 16:20:43 +02007215 SerializeStringProperty("name", camera.name, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007216 }
7217
7218 if (camera.type.compare("orthographic") == 0) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007219 json orthographic;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007220 SerializeGltfOrthographicCamera(camera.orthographic, orthographic);
jrkooncecba5d6c2019-08-29 11:26:22 -05007221 JsonAddMember(o, "orthographic", std::move(orthographic));
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007222 } else if (camera.type.compare("perspective") == 0) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007223 json perspective;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007224 SerializeGltfPerspectiveCamera(camera.perspective, perspective);
jrkooncecba5d6c2019-08-29 11:26:22 -05007225 JsonAddMember(o, "perspective", std::move(perspective));
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007226 } else {
7227 // ???
7228 }
Syoyofe77cc52020-05-09 02:41:07 +09007229
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007230 if (camera.extras.Type() != NULL_TYPE) {
Syoyofe77cc52020-05-09 02:41:07 +09007231 SerializeValue("extras", camera.extras, o);
7232 }
7233 SerializeExtensionMap(camera.extensions, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007234}
7235
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007236static void SerializeGltfScene(Scene &scene, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007237 SerializeNumberArrayProperty<int>("nodes", scene.nodes, o);
7238
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007239 if (scene.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007240 SerializeStringProperty("name", scene.name, o);
7241 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007242 if (scene.extras.Type() != NULL_TYPE) {
Selmar09d2ff12018-03-15 17:30:42 +01007243 SerializeValue("extras", scene.extras, o);
7244 }
7245 SerializeExtensionMap(scene.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007246}
7247
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007248static void SerializeGltfSkin(Skin &skin, json &o) {
Syoyo Fujitad07b9762021-04-28 19:15:16 +09007249 // required
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007250 SerializeNumberArrayProperty<int>("joints", skin.joints, o);
Syoyo Fujitad07b9762021-04-28 19:15:16 +09007251
7252 if (skin.inverseBindMatrices >= 0) {
7253 SerializeNumberProperty("inverseBindMatrices", skin.inverseBindMatrices, o);
7254 }
7255
7256 if (skin.skeleton >= 0) {
7257 SerializeNumberProperty("skeleton", skin.skeleton, o);
7258 }
7259
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007260 if (skin.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007261 SerializeStringProperty("name", skin.name, o);
7262 }
7263}
7264
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007265static void SerializeGltfTexture(Texture &texture, json &o) {
johan bowaldb97d34c2018-04-02 07:29:29 +02007266 if (texture.sampler > -1) {
johan bowald642a3432018-04-01 12:37:18 +02007267 SerializeNumberProperty("sampler", texture.sampler, o);
7268 }
Selmar Kok8eb39042018-10-05 14:29:35 +02007269 if (texture.source > -1) {
7270 SerializeNumberProperty("source", texture.source, o);
7271 }
Christophe820ede82019-07-04 15:21:21 +09007272 if (texture.name.size()) {
7273 SerializeStringProperty("name", texture.name, o);
7274 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007275 if (texture.extras.Type() != NULL_TYPE) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007276 SerializeValue("extras", texture.extras, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007277 }
Selmar Kok9eae1102018-04-04 18:10:37 +02007278 SerializeExtensionMap(texture.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007279}
7280
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007281///
7282/// Serialize all properties except buffers and images.
7283///
7284static void SerializeGltfModel(Model *model, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007285 // ACCESSORS
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007286 if (model->accessors.size()) {
7287 json accessors;
7288 JsonReserveArray(accessors, model->accessors.size());
7289 for (unsigned int i = 0; i < model->accessors.size(); ++i) {
7290 json accessor;
7291 SerializeGltfAccessor(model->accessors[i], accessor);
7292 JsonPushBack(accessors, std::move(accessor));
7293 }
7294 JsonAddMember(o, "accessors", std::move(accessors));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007295 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007296
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007297 // ANIMATIONS
7298 if (model->animations.size()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09007299 json animations;
jrkooncecba5d6c2019-08-29 11:26:22 -05007300 JsonReserveArray(animations, model->animations.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007301 for (unsigned int i = 0; i < model->animations.size(); ++i) {
7302 if (model->animations[i].channels.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007303 json animation;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007304 SerializeGltfAnimation(model->animations[i], animation);
jrkooncecba5d6c2019-08-29 11:26:22 -05007305 JsonPushBack(animations, std::move(animation));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007306 }
7307 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007308
jrkooncecba5d6c2019-08-29 11:26:22 -05007309 JsonAddMember(o, "animations", std::move(animations));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007310 }
7311
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007312 // ASSET
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007313 json asset;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007314 SerializeGltfAsset(model->asset, asset);
jrkooncecba5d6c2019-08-29 11:26:22 -05007315 JsonAddMember(o, "asset", std::move(asset));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007316
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007317 // BUFFERVIEWS
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007318 if (model->bufferViews.size()) {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007319 json bufferViews;
7320 JsonReserveArray(bufferViews, model->bufferViews.size());
7321 for (unsigned int i = 0; i < model->bufferViews.size(); ++i) {
7322 json bufferView;
7323 SerializeGltfBufferView(model->bufferViews[i], bufferView);
7324 JsonPushBack(bufferViews, std::move(bufferView));
7325 }
7326 JsonAddMember(o, "bufferViews", std::move(bufferViews));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007327 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007328
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007329 // Extensions required
7330 if (model->extensionsRequired.size()) {
7331 SerializeStringArrayProperty("extensionsRequired",
7332 model->extensionsRequired, o);
7333 }
7334
7335 // MATERIALS
7336 if (model->materials.size()) {
7337 json materials;
jrkooncecba5d6c2019-08-29 11:26:22 -05007338 JsonReserveArray(materials, model->materials.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007339 for (unsigned int i = 0; i < model->materials.size(); ++i) {
7340 json material;
7341 SerializeGltfMaterial(model->materials[i], material);
Syoyo Fujita68adc4b2020-10-22 22:27:12 +09007342
7343 if (JsonIsNull(material)) {
7344 // Issue 294.
7345 // `material` does not have any required parameters
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09007346 // so the result may be null(unmodified) when all material parameters
7347 // have default value.
Syoyo Fujita68adc4b2020-10-22 22:27:12 +09007348 //
7349 // null is not allowed thus we create an empty JSON object.
7350 JsonSetObject(material);
7351 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007352 JsonPushBack(materials, std::move(material));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007353 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007354 JsonAddMember(o, "materials", std::move(materials));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007355 }
7356
7357 // MESHES
7358 if (model->meshes.size()) {
7359 json meshes;
jrkooncecba5d6c2019-08-29 11:26:22 -05007360 JsonReserveArray(meshes, model->meshes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007361 for (unsigned int i = 0; i < model->meshes.size(); ++i) {
7362 json mesh;
7363 SerializeGltfMesh(model->meshes[i], mesh);
jrkooncecba5d6c2019-08-29 11:26:22 -05007364 JsonPushBack(meshes, std::move(mesh));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007365 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007366 JsonAddMember(o, "meshes", std::move(meshes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007367 }
7368
7369 // NODES
7370 if (model->nodes.size()) {
7371 json nodes;
jrkooncecba5d6c2019-08-29 11:26:22 -05007372 JsonReserveArray(nodes, model->nodes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007373 for (unsigned int i = 0; i < model->nodes.size(); ++i) {
7374 json node;
7375 SerializeGltfNode(model->nodes[i], node);
jrkooncecba5d6c2019-08-29 11:26:22 -05007376 JsonPushBack(nodes, std::move(node));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007377 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007378 JsonAddMember(o, "nodes", std::move(nodes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007379 }
7380
7381 // SCENE
7382 if (model->defaultScene > -1) {
7383 SerializeNumberProperty<int>("scene", model->defaultScene, o);
7384 }
7385
7386 // SCENES
7387 if (model->scenes.size()) {
7388 json scenes;
jrkooncecba5d6c2019-08-29 11:26:22 -05007389 JsonReserveArray(scenes, model->scenes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007390 for (unsigned int i = 0; i < model->scenes.size(); ++i) {
7391 json currentScene;
7392 SerializeGltfScene(model->scenes[i], currentScene);
jrkooncecba5d6c2019-08-29 11:26:22 -05007393 JsonPushBack(scenes, std::move(currentScene));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007394 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007395 JsonAddMember(o, "scenes", std::move(scenes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007396 }
7397
7398 // SKINS
7399 if (model->skins.size()) {
7400 json skins;
jrkooncecba5d6c2019-08-29 11:26:22 -05007401 JsonReserveArray(skins, model->skins.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007402 for (unsigned int i = 0; i < model->skins.size(); ++i) {
7403 json skin;
7404 SerializeGltfSkin(model->skins[i], skin);
jrkooncecba5d6c2019-08-29 11:26:22 -05007405 JsonPushBack(skins, std::move(skin));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007406 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007407 JsonAddMember(o, "skins", std::move(skins));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007408 }
7409
7410 // TEXTURES
7411 if (model->textures.size()) {
7412 json textures;
jrkooncecba5d6c2019-08-29 11:26:22 -05007413 JsonReserveArray(textures, model->textures.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007414 for (unsigned int i = 0; i < model->textures.size(); ++i) {
7415 json texture;
7416 SerializeGltfTexture(model->textures[i], texture);
jrkooncecba5d6c2019-08-29 11:26:22 -05007417 JsonPushBack(textures, std::move(texture));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007418 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007419 JsonAddMember(o, "textures", std::move(textures));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007420 }
7421
7422 // SAMPLERS
7423 if (model->samplers.size()) {
7424 json samplers;
jrkooncecba5d6c2019-08-29 11:26:22 -05007425 JsonReserveArray(samplers, model->samplers.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007426 for (unsigned int i = 0; i < model->samplers.size(); ++i) {
7427 json sampler;
7428 SerializeGltfSampler(model->samplers[i], sampler);
jrkooncecba5d6c2019-08-29 11:26:22 -05007429 JsonPushBack(samplers, std::move(sampler));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007430 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007431 JsonAddMember(o, "samplers", std::move(samplers));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007432 }
7433
7434 // CAMERAS
7435 if (model->cameras.size()) {
7436 json cameras;
jrkooncecba5d6c2019-08-29 11:26:22 -05007437 JsonReserveArray(cameras, model->cameras.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007438 for (unsigned int i = 0; i < model->cameras.size(); ++i) {
7439 json camera;
7440 SerializeGltfCamera(model->cameras[i], camera);
jrkooncecba5d6c2019-08-29 11:26:22 -05007441 JsonPushBack(cameras, std::move(camera));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007442 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007443 JsonAddMember(o, "cameras", std::move(cameras));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007444 }
7445
7446 // EXTENSIONS
7447 SerializeExtensionMap(model->extensions, o);
7448
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007449 auto extensionsUsed = model->extensionsUsed;
7450
7451 // LIGHTS as KHR_lights_punctual
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007452 if (model->lights.size()) {
7453 json lights;
jrkooncecba5d6c2019-08-29 11:26:22 -05007454 JsonReserveArray(lights, model->lights.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007455 for (unsigned int i = 0; i < model->lights.size(); ++i) {
7456 json light;
7457 SerializeGltfLight(model->lights[i], light);
jrkooncecba5d6c2019-08-29 11:26:22 -05007458 JsonPushBack(lights, std::move(light));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007459 }
7460 json khr_lights_cmn;
jrkooncecba5d6c2019-08-29 11:26:22 -05007461 JsonAddMember(khr_lights_cmn, "lights", std::move(lights));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007462 json ext_j;
7463
jrkooncecba5d6c2019-08-29 11:26:22 -05007464 {
7465 json_const_iterator it;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007466 if (FindMember(o, "extensions", it)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05007467 JsonAssign(ext_j, GetValue(it));
7468 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007469 }
7470
jrkooncecba5d6c2019-08-29 11:26:22 -05007471 JsonAddMember(ext_j, "KHR_lights_punctual", std::move(khr_lights_cmn));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007472
jrkooncecba5d6c2019-08-29 11:26:22 -05007473 JsonAddMember(o, "extensions", std::move(ext_j));
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007474
7475 // Also add "KHR_lights_punctual" to `extensionsUsed`
7476 {
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04007477 auto has_khr_lights_punctual =
7478 std::find_if(extensionsUsed.begin(), extensionsUsed.end(),
7479 [](const std::string &s) {
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08007480 return (s.compare("KHR_lights_punctual") == 0);
7481 });
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007482
7483 if (has_khr_lights_punctual == extensionsUsed.end()) {
7484 extensionsUsed.push_back("KHR_lights_punctual");
7485 }
7486 }
7487 }
7488
7489 // Extensions used
Selmar1208f052021-02-01 19:24:41 +01007490 if (extensionsUsed.size()) {
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007491 SerializeStringArrayProperty("extensionsUsed", extensionsUsed, o);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007492 }
7493
7494 // EXTRAS
7495 if (model->extras.Type() != NULL_TYPE) {
7496 SerializeValue("extras", model->extras, o);
7497 }
7498}
7499
Johan Bowald52936a02019-07-17 09:06:45 +02007500static bool WriteGltfStream(std::ostream &stream, const std::string &content) {
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007501 stream << content << std::endl;
7502 return true;
7503}
7504
7505static bool WriteGltfFile(const std::string &output,
7506 const std::string &content) {
Harokyangfb256602019-10-30 16:13:52 +08007507#ifdef _WIN32
Syoyo Fujita45cac782019-11-09 20:42:55 +09007508#if defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08007509 std::ofstream gltfFile(UTF8ToWchar(output).c_str());
Syoyo Fujita45cac782019-11-09 20:42:55 +09007510#elif defined(__GLIBCXX__)
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007511 int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
7512 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
7513 __gnu_cxx::stdio_filebuf<char> wfile_buf(
7514 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09007515 std::ostream gltfFile(&wfile_buf);
7516 if (!wfile_buf.is_open()) return false;
Harokyangfb256602019-10-30 16:13:52 +08007517#else
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007518 std::ofstream gltfFile(output.c_str());
7519 if (!gltfFile.is_open()) return false;
Syoyo Fujita45cac782019-11-09 20:42:55 +09007520#endif
7521#else
7522 std::ofstream gltfFile(output.c_str());
7523 if (!gltfFile.is_open()) return false;
7524#endif
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007525 return WriteGltfStream(gltfFile, content);
7526}
7527
7528static void WriteBinaryGltfStream(std::ostream &stream,
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007529 const std::string &content,
7530 const std::vector<unsigned char> &binBuffer) {
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007531 const std::string header = "glTF";
7532 const int version = 2;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007533
Alexander Wood190382a2021-10-08 12:19:13 -04007534 const uint32_t content_size = uint32_t(content.size());
7535 const uint32_t binBuffer_size = uint32_t(binBuffer.size());
7536 // determine number of padding bytes required to ensure 4 byte alignment
7537 const uint32_t padding_size = content_size % 4 == 0 ? 0 : 4 - content_size % 4;
7538 const uint32_t bin_padding_size = binBuffer_size % 4 == 0 ? 0 : 4 - binBuffer_size % 4;
Syoyo Fujita1d205202019-11-16 17:00:17 +09007539
7540 // 12 bytes for header, JSON content length, 8 bytes for JSON chunk info.
Alexander Wood190382a2021-10-08 12:19:13 -04007541 // Chunk data must be located at 4-byte boundary, which may require padding
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007542 const uint32_t length =
Alexander Wood190382a2021-10-08 12:19:13 -04007543 12 + 8 + content_size + content_padding +
7544 (binBuffer_size ? (8 + binBuffer_size + bin_padding_size : 0);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007545
Syoyo Fujitacea69e32019-08-20 17:10:30 +09007546 stream.write(header.c_str(), std::streamsize(header.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007547 stream.write(reinterpret_cast<const char *>(&version), sizeof(version));
7548 stream.write(reinterpret_cast<const char *>(&length), sizeof(length));
7549
7550 // JSON chunk info, then JSON data
Syoyo Fujita72f4a552020-01-08 00:40:41 +09007551 const uint32_t model_length = uint32_t(content.size()) + padding_size;
7552 const uint32_t model_format = 0x4E4F534A;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007553 stream.write(reinterpret_cast<const char *>(&model_length),
Johan Bowald52936a02019-07-17 09:06:45 +02007554 sizeof(model_length));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007555 stream.write(reinterpret_cast<const char *>(&model_format),
Johan Bowald52936a02019-07-17 09:06:45 +02007556 sizeof(model_format));
Syoyo Fujitacea69e32019-08-20 17:10:30 +09007557 stream.write(content.c_str(), std::streamsize(content.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007558
7559 // Chunk must be multiplies of 4, so pad with spaces
7560 if (padding_size > 0) {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09007561 const std::string padding = std::string(size_t(padding_size), ' ');
7562 stream.write(padding.c_str(), std::streamsize(padding.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007563 }
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007564 if (binBuffer.size() > 0) {
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007565 // BIN chunk info, then BIN data
Syoyo Fujita72f4a552020-01-08 00:40:41 +09007566 const uint32_t bin_length = uint32_t(binBuffer.size()) + bin_padding_size;
7567 const uint32_t bin_format = 0x004e4942;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007568 stream.write(reinterpret_cast<const char *>(&bin_length),
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007569 sizeof(bin_length));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007570 stream.write(reinterpret_cast<const char *>(&bin_format),
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007571 sizeof(bin_format));
7572 stream.write(reinterpret_cast<const char *>(binBuffer.data()),
7573 std::streamsize(binBuffer.size()));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007574 // Chunksize must be multiplies of 4, so pad with zeroes
7575 if (bin_padding_size > 0) {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007576 const std::vector<unsigned char> padding =
7577 std::vector<unsigned char>(size_t(bin_padding_size), 0);
7578 stream.write(reinterpret_cast<const char *>(padding.data()),
7579 std::streamsize(padding.size()));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007580 }
7581 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007582}
7583
7584static void WriteBinaryGltfFile(const std::string &output,
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007585 const std::string &content,
7586 const std::vector<unsigned char> &binBuffer) {
Harokyangfb256602019-10-30 16:13:52 +08007587#ifdef _WIN32
Syoyo Fujita45cac782019-11-09 20:42:55 +09007588#if defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08007589 std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09007590#elif defined(__GLIBCXX__)
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007591 int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
7592 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
7593 __gnu_cxx::stdio_filebuf<char> wfile_buf(
7594 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita4ab03862019-11-10 15:31:17 +09007595 std::ostream gltfFile(&wfile_buf);
Harokyangfb256602019-10-30 16:13:52 +08007596#else
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007597 std::ofstream gltfFile(output.c_str(), std::ios::binary);
Harokyangfb256602019-10-30 16:13:52 +08007598#endif
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007599#else
7600 std::ofstream gltfFile(output.c_str(), std::ios::binary);
jrkooncecba5d6c2019-08-29 11:26:22 -05007601#endif
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007602 WriteBinaryGltfStream(gltfFile, content, binBuffer);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007603}
7604
7605bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
7606 bool prettyPrint = true,
7607 bool writeBinary = false) {
7608 JsonDocument output;
7609
7610 /// Serialize all properties except buffers and images.
7611 SerializeGltfModel(model, output);
7612
7613 // BUFFERS
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007614 std::vector<unsigned char> binBuffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007615 if (model->buffers.size()) {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007616 json buffers;
7617 JsonReserveArray(buffers, model->buffers.size());
7618 for (unsigned int i = 0; i < model->buffers.size(); ++i) {
7619 json buffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007620 if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
7621 SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007622 } else {
7623 SerializeGltfBuffer(model->buffers[i], buffer);
7624 }
7625 JsonPushBack(buffers, std::move(buffer));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007626 }
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007627 JsonAddMember(output, "buffers", std::move(buffers));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007628 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007629
7630 // IMAGES
7631 if (model->images.size()) {
7632 json images;
jrkooncecba5d6c2019-08-29 11:26:22 -05007633 JsonReserveArray(images, model->images.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007634 for (unsigned int i = 0; i < model->images.size(); ++i) {
7635 json image;
7636
7637 std::string dummystring = "";
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007638 // UpdateImageObject need baseDir but only uses it if embeddedImages is
7639 // enabled, since we won't write separate images when writing to a stream
7640 // we
7641 UpdateImageObject(model->images[i], dummystring, int(i), false,
7642 &this->WriteImageData, this->write_image_user_data_);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007643 SerializeGltfImage(model->images[i], image);
jrkooncecba5d6c2019-08-29 11:26:22 -05007644 JsonPushBack(images, std::move(image));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007645 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007646 JsonAddMember(output, "images", std::move(images));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007647 }
7648
7649 if (writeBinary) {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007650 WriteBinaryGltfStream(stream, JsonToString(output), binBuffer);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007651 } else {
jrkooncecba5d6c2019-08-29 11:26:22 -05007652 WriteGltfStream(stream, JsonToString(output, prettyPrint ? 2 : -1));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007653 }
7654
7655 return true;
7656}
7657
7658bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
7659 bool embedImages = false,
7660 bool embedBuffers = false,
7661 bool prettyPrint = true,
7662 bool writeBinary = false) {
jrkooncecba5d6c2019-08-29 11:26:22 -05007663 JsonDocument output;
Selmar Kok7cb31e42018-10-05 16:02:29 +02007664 std::string defaultBinFilename = GetBaseFilename(filename);
7665 std::string defaultBinFileExt = ".bin";
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00007666 std::string::size_type pos =
7667 defaultBinFilename.rfind('.', defaultBinFilename.length());
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007668
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007669 if (pos != std::string::npos) {
Selmar Kok7cb31e42018-10-05 16:02:29 +02007670 defaultBinFilename = defaultBinFilename.substr(0, pos);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007671 }
johan bowald642a3432018-04-01 12:37:18 +02007672 std::string baseDir = GetBaseDir(filename);
7673 if (baseDir.empty()) {
7674 baseDir = "./";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05007675 }
Johan Bowald52936a02019-07-17 09:06:45 +02007676 /// Serialize all properties except buffers and images.
7677 SerializeGltfModel(model, output);
Syoyo Fujitad42767e2018-03-15 21:52:00 -05007678
Selmar Kok7cb31e42018-10-05 16:02:29 +02007679 // BUFFERS
Selmar Kokc884e582018-10-05 16:25:54 +02007680 std::vector<std::string> usedUris;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007681 std::vector<unsigned char> binBuffer;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007682 if (model->buffers.size()) {
7683 json buffers;
7684 JsonReserveArray(buffers, model->buffers.size());
7685 for (unsigned int i = 0; i < model->buffers.size(); ++i) {
7686 json buffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007687 if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
7688 SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007689 } else if (embedBuffers) {
7690 SerializeGltfBuffer(model->buffers[i], buffer);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00007691 } else {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007692 std::string binSavePath;
7693 std::string binUri;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007694 if (!model->buffers[i].uri.empty() &&
7695 !IsDataURI(model->buffers[i].uri)) {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007696 binUri = model->buffers[i].uri;
7697 } else {
7698 binUri = defaultBinFilename + defaultBinFileExt;
7699 bool inUse = true;
7700 int numUsed = 0;
7701 while (inUse) {
7702 inUse = false;
7703 for (const std::string &usedName : usedUris) {
7704 if (binUri.compare(usedName) != 0) continue;
7705 inUse = true;
7706 binUri = defaultBinFilename + std::to_string(numUsed++) +
7707 defaultBinFileExt;
7708 break;
7709 }
Selmar Kokc884e582018-10-05 16:25:54 +02007710 }
7711 }
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007712 usedUris.push_back(binUri);
7713 binSavePath = JoinPath(baseDir, binUri);
7714 if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
7715 binUri)) {
7716 return false;
7717 }
Selmar Kokc884e582018-10-05 16:25:54 +02007718 }
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007719 JsonPushBack(buffers, std::move(buffer));
johan bowald30c53472018-03-30 11:49:36 +02007720 }
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007721 JsonAddMember(output, "buffers", std::move(buffers));
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007722 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007723
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007724 // IMAGES
Johan Bowaldfaa27222018-03-28 14:44:45 +02007725 if (model->images.size()) {
7726 json images;
jrkooncecba5d6c2019-08-29 11:26:22 -05007727 JsonReserveArray(images, model->images.size());
Johan Bowaldfaa27222018-03-28 14:44:45 +02007728 for (unsigned int i = 0; i < model->images.size(); ++i) {
7729 json image;
johan bowald642a3432018-04-01 12:37:18 +02007730
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09007731 UpdateImageObject(model->images[i], baseDir, int(i), embedImages,
Johan Bowald77decfa2018-11-06 14:28:20 +01007732 &this->WriteImageData, this->write_image_user_data_);
Johan Bowaldfaa27222018-03-28 14:44:45 +02007733 SerializeGltfImage(model->images[i], image);
jrkooncecba5d6c2019-08-29 11:26:22 -05007734 JsonPushBack(images, std::move(image));
Johan Bowaldfaa27222018-03-28 14:44:45 +02007735 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007736 JsonAddMember(output, "images", std::move(images));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007737 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007738
David Harmonda9eac22018-08-30 08:06:05 -04007739 if (writeBinary) {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007740 WriteBinaryGltfFile(filename, JsonToString(output), binBuffer);
David Harmonda9eac22018-08-30 08:06:05 -04007741 } else {
jrkooncecba5d6c2019-08-29 11:26:22 -05007742 WriteGltfFile(filename, JsonToString(output, (prettyPrint ? 2 : -1)));
David Harmonda9eac22018-08-30 08:06:05 -04007743 }
7744
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007745 return true;
7746}
7747
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09007748} // namespace tinygltf
7749
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09007750#ifdef __clang__
7751#pragma clang diagnostic pop
7752#endif
7753
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007754#endif // TINYGLTF_IMPLEMENTATION