blob: 6188dc0105c93a5b3254aa1a9dc5c988d0b808d6 [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 Fujita6ed7c392022-03-03 16:40:54 +0900111#define TINYGLTF_COMPONENT_TYPE_DOUBLE (5130) // OpenGL double type. Note that some of glTF 2.0 validator does not support double type even the schema seems allow any value of integer: https://github.com/KhronosGroup/glTF/blob/b9884a2fd45130b4d673dd6c8a706ee21ee5c5f7/specification/2.0/schema/accessor.schema.json#L22
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
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900256#pragma clang diagnostic ignored "-Wexit-time-destructors"
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900257#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900258#endif
259
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900260// Simple class to represent JSON object
261class Value {
262 public:
263 typedef std::vector<Value> Array;
264 typedef std::map<std::string, Value> Object;
265
Syoyo Fujita046400b2019-07-24 19:26:48 +0900266 Value()
267 : type_(NULL_TYPE),
268 int_value_(0),
Syoyo Fujita150f2432019-07-25 19:22:44 +0900269 real_value_(0.0),
Syoyo Fujita046400b2019-07-24 19:26:48 +0900270 boolean_value_(false) {}
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900271
272 explicit Value(bool b) : type_(BOOL_TYPE) { boolean_value_ = b; }
Syoyo Fujitaff515702019-08-24 16:29:14 +0900273 explicit Value(int i) : type_(INT_TYPE) {
274 int_value_ = i;
275 real_value_ = i;
276 }
Syoyo Fujita150f2432019-07-25 19:22:44 +0900277 explicit Value(double n) : type_(REAL_TYPE) { real_value_ = n; }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900278 explicit Value(const std::string &s) : type_(STRING_TYPE) {
279 string_value_ = s;
280 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900281 explicit Value(std::string &&s)
282 : type_(STRING_TYPE), string_value_(std::move(s)) {}
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900283 explicit Value(const unsigned char *p, size_t n) : type_(BINARY_TYPE) {
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900284 binary_value_.resize(n);
285 memcpy(binary_value_.data(), p, n);
286 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900287 explicit Value(std::vector<unsigned char> &&v) noexcept
288 : type_(BINARY_TYPE),
289 binary_value_(std::move(v)) {}
290 explicit Value(const Array &a) : type_(ARRAY_TYPE) { array_value_ = a; }
291 explicit Value(Array &&a) noexcept : type_(ARRAY_TYPE),
292 array_value_(std::move(a)) {}
jrkoonced1e14722019-08-27 11:51:02 -0500293
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900294 explicit Value(const Object &o) : type_(OBJECT_TYPE) { object_value_ = o; }
295 explicit Value(Object &&o) noexcept : type_(OBJECT_TYPE),
296 object_value_(std::move(o)) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100297
298 DEFAULT_METHODS(Value)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900299
Hill Mad1e32862021-02-20 22:30:44 -0800300 char Type() const { return static_cast<char>(type_); }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900301
302 bool IsBool() const { return (type_ == BOOL_TYPE); }
303
304 bool IsInt() const { return (type_ == INT_TYPE); }
305
Syoyo Fujita150f2432019-07-25 19:22:44 +0900306 bool IsNumber() const { return (type_ == REAL_TYPE) || (type_ == INT_TYPE); }
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900307
Syoyo Fujita150f2432019-07-25 19:22:44 +0900308 bool IsReal() const { return (type_ == REAL_TYPE); }
Syoyo Fujita046400b2019-07-24 19:26:48 +0900309
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900310 bool IsString() const { return (type_ == STRING_TYPE); }
311
312 bool IsBinary() const { return (type_ == BINARY_TYPE); }
313
314 bool IsArray() const { return (type_ == ARRAY_TYPE); }
315
316 bool IsObject() const { return (type_ == OBJECT_TYPE); }
317
Syoyo Fujita150f2432019-07-25 19:22:44 +0900318 // Use this function if you want to have number value as double.
319 double GetNumberAsDouble() const {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900320 if (type_ == INT_TYPE) {
321 return double(int_value_);
Syoyo Fujita150f2432019-07-25 19:22:44 +0900322 } else {
323 return real_value_;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900324 }
Syoyo Fujita150f2432019-07-25 19:22:44 +0900325 }
Syoyo Fujita046400b2019-07-24 19:26:48 +0900326
Syoyo Fujita150f2432019-07-25 19:22:44 +0900327 // Use this function if you want to have number value as int.
Syoyo Fujitaff0a2e92020-05-11 19:07:00 +0900328 // TODO(syoyo): Support int value larger than 32 bits
329 int GetNumberAsInt() const {
Syoyo Fujita150f2432019-07-25 19:22:44 +0900330 if (type_ == REAL_TYPE) {
331 return int(real_value_);
332 } else {
333 return int_value_;
334 }
Syoyo Fujita046400b2019-07-24 19:26:48 +0900335 }
336
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900337 // Accessor
338 template <typename T>
339 const T &Get() const;
340 template <typename T>
341 T &Get();
342
343 // Lookup value from an array
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900344 const Value &Get(int idx) const {
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900345 static Value null_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900346 assert(IsArray());
347 assert(idx >= 0);
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900348 return (static_cast<size_t>(idx) < array_value_.size())
349 ? array_value_[static_cast<size_t>(idx)]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900350 : null_value;
351 }
352
353 // Lookup value from a key-value pair
354 const Value &Get(const std::string &key) const {
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900355 static Value null_value;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900356 assert(IsObject());
357 Object::const_iterator it = object_value_.find(key);
358 return (it != object_value_.end()) ? it->second : null_value;
359 }
360
361 size_t ArrayLen() const {
362 if (!IsArray()) return 0;
363 return array_value_.size();
364 }
365
366 // Valid only for object type.
367 bool Has(const std::string &key) const {
368 if (!IsObject()) return false;
369 Object::const_iterator it = object_value_.find(key);
370 return (it != object_value_.end()) ? true : false;
371 }
372
373 // List keys
374 std::vector<std::string> Keys() const {
375 std::vector<std::string> keys;
376 if (!IsObject()) return keys; // empty
377
378 for (Object::const_iterator it = object_value_.begin();
379 it != object_value_.end(); ++it) {
380 keys.push_back(it->first);
381 }
382
383 return keys;
384 }
385
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900386 size_t Size() const { return (IsArray() ? ArrayLen() : Keys().size()); }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900387
388 bool operator==(const tinygltf::Value &other) const;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +0000389
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900390 protected:
Syoyo Fujita046400b2019-07-24 19:26:48 +0900391 int type_ = NULL_TYPE;
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900392
Syoyo Fujita046400b2019-07-24 19:26:48 +0900393 int int_value_ = 0;
Syoyo Fujita150f2432019-07-25 19:22:44 +0900394 double real_value_ = 0.0;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900395 std::string string_value_;
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900396 std::vector<unsigned char> binary_value_;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900397 Array array_value_;
398 Object object_value_;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900399 bool boolean_value_ = false;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900400};
401
Syoyo Fujitac8d3c762017-05-25 01:42:38 +0900402#ifdef __clang__
403#pragma clang diagnostic pop
404#endif
405
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900406#define TINYGLTF_VALUE_GET(ctype, var) \
407 template <> \
408 inline const ctype &Value::Get<ctype>() const { \
409 return var; \
410 } \
411 template <> \
412 inline ctype &Value::Get<ctype>() { \
413 return var; \
414 }
415TINYGLTF_VALUE_GET(bool, boolean_value_)
Syoyo Fujita150f2432019-07-25 19:22:44 +0900416TINYGLTF_VALUE_GET(double, real_value_)
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900417TINYGLTF_VALUE_GET(int, int_value_)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900418TINYGLTF_VALUE_GET(std::string, string_value_)
Syoyo Fujita7680abf2016-10-17 18:02:45 +0900419TINYGLTF_VALUE_GET(std::vector<unsigned char>, binary_value_)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900420TINYGLTF_VALUE_GET(Value::Array, array_value_)
421TINYGLTF_VALUE_GET(Value::Object, object_value_)
422#undef TINYGLTF_VALUE_GET
423
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900424#ifdef __clang__
425#pragma clang diagnostic push
426#pragma clang diagnostic ignored "-Wc++98-compat"
427#pragma clang diagnostic ignored "-Wpadded"
428#endif
429
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500430/// Agregate object for representing a color
Arthur Brainville58453192018-01-08 18:25:52 +0100431using ColorValue = std::array<double, 4>;
432
Syoyo Fujita046400b2019-07-24 19:26:48 +0900433// === legacy interface ====
434// TODO(syoyo): Deprecate `Parameter` class.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500435struct Parameter {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200436 bool bool_value = false;
Ben Buzbeef6af2242018-04-25 15:13:05 -0700437 bool has_number_value = false;
Syoyo Fujitaec39a522016-05-01 18:33:31 +0900438 std::string string_value;
439 std::vector<double> number_array;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000440 std::map<std::string, double> json_double_value;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200441 double number_value = 0.0;
Syoyo Fujita046400b2019-07-24 19:26:48 +0900442
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500443 // context sensitive methods. depending the type of the Parameter you are
444 // accessing, these are either valid or not
445 // If this parameter represent a texture map in a material, will return the
446 // texture index
Arthur Brainville58453192018-01-08 18:25:52 +0100447
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500448 /// Return the index of a texture if this Parameter is a texture map.
449 /// Returned value is only valid if the parameter represent a texture from a
450 /// material
Arthur Brainville41fe7722018-01-08 18:32:48 +0100451 int TextureIndex() const {
Arthur Brainville58453192018-01-08 18:25:52 +0100452 const auto it = json_double_value.find("index");
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500453 if (it != std::end(json_double_value)) {
Arthur Brainville58453192018-01-08 18:25:52 +0100454 return int(it->second);
455 }
456 return -1;
457 }
458
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000459 /// Return the index of a texture coordinate set if this Parameter is a
460 /// texture map. Returned value is only valid if the parameter represent a
461 /// texture from a material
Sascha Willemseb011062019-02-23 21:15:45 +0100462 int TextureTexCoord() const {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000463 const auto it = json_double_value.find("texCoord");
464 if (it != std::end(json_double_value)) {
465 return int(it->second);
466 }
Arthur Brainville8a98d982019-07-05 00:26:02 +0200467 // As per the spec, if texCoord is ommited, this parameter is 0
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000468 return 0;
Sascha Willemseb011062019-02-23 21:15:45 +0100469 }
470
Christophe820ede82019-07-04 15:21:21 +0900471 /// Return the scale of a texture if this Parameter is a normal texture map.
472 /// Returned value is only valid if the parameter represent a normal texture
473 /// from a material
474 double TextureScale() const {
475 const auto it = json_double_value.find("scale");
476 if (it != std::end(json_double_value)) {
477 return it->second;
478 }
Arthur Brainville8a98d982019-07-05 00:26:02 +0200479 // As per the spec, if scale is ommited, this paramter is 1
480 return 1;
481 }
Arthur Brianville (Ybalrid)2a9d9de2019-07-05 00:30:47 +0200482
Arthur Brainville8a98d982019-07-05 00:26:02 +0200483 /// Return the strength of a texture if this Parameter is a an occlusion map.
484 /// Returned value is only valid if the parameter represent an occlusion map
485 /// from a material
486 double TextureStrength() const {
487 const auto it = json_double_value.find("strength");
488 if (it != std::end(json_double_value)) {
489 return it->second;
490 }
491 // As per the spec, if strenghth is ommited, this parameter is 1
492 return 1;
Christophe820ede82019-07-04 15:21:21 +0900493 }
494
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500495 /// Material factor, like the roughness or metalness of a material
496 /// Returned value is only valid if the parameter represent a texture from a
497 /// material
Ben Buzbeef6af2242018-04-25 15:13:05 -0700498 double Factor() const { return number_value; }
Arthur Brainville58453192018-01-08 18:25:52 +0100499
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500500 /// Return the color of a material
501 /// Returned value is only valid if the parameter represent a texture from a
502 /// material
Arthur Brainville95853912018-01-08 18:37:44 +0100503 ColorValue ColorFactor() const {
Arthur Brainville58453192018-01-08 18:25:52 +0100504 return {
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500505 {// this agregate intialize the std::array object, and uses C++11 RVO.
506 number_array[0], number_array[1], number_array[2],
507 (number_array.size() > 3 ? number_array[3] : 1.0)}};
Arthur Brainville58453192018-01-08 18:25:52 +0100508 }
Selmar Kok31cb7f92018-10-03 15:39:05 +0200509
Selmar Kokff2b1f92019-10-21 17:58:09 +0200510 Parameter() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100511 DEFAULT_METHODS(Parameter)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900512 bool operator==(const Parameter &) const;
Arthur Brainville58453192018-01-08 18:25:52 +0100513};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900514
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900515#ifdef __clang__
516#pragma clang diagnostic pop
517#endif
518
519#ifdef __clang__
520#pragma clang diagnostic push
521#pragma clang diagnostic ignored "-Wpadded"
522#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900523
Syoyo Fujitabde70212016-02-07 17:38:17 +0900524typedef std::map<std::string, Parameter> ParameterMap;
Selmar Kokfa7022f2018-04-04 18:10:20 +0200525typedef std::map<std::string, Value> ExtensionMap;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900526
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000527struct AnimationChannel {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900528 int sampler; // required
529 int target_node; // required (index of the node to target)
530 std::string target_path; // required in ["translation", "rotation", "scale",
531 // "weights"]
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900532 Value extras;
Selmar Kok4e2988e2019-08-16 14:08:08 +0200533 ExtensionMap extensions;
Selmar Kok973d9b32020-01-21 18:45:24 +0100534 ExtensionMap target_extensions;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900535
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900536 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
537 std::string extras_json_string;
538 std::string extensions_json_string;
Selmar Kok973d9b32020-01-21 18:45:24 +0100539 std::string target_extensions_json_string;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900540
Syoyo Fujita5b407452017-06-04 17:42:41 +0900541 AnimationChannel() : sampler(-1), target_node(-1) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100542 DEFAULT_METHODS(AnimationChannel)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900543 bool operator==(const AnimationChannel &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000544};
545
546struct AnimationSampler {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900547 int input; // required
548 int output; // required
Arthur Brianville (Ybalrid)2a9d9de2019-07-05 00:30:47 +0200549 std::string interpolation; // "LINEAR", "STEP","CUBICSPLINE" or user defined
550 // string. default "LINEAR"
Jens Olssonb3af2f12018-06-04 10:17:49 +0200551 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900552 ExtensionMap extensions;
553
554 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
555 std::string extras_json_string;
556 std::string extensions_json_string;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000557
Syoyo Fujita5b407452017-06-04 17:42:41 +0900558 AnimationSampler() : input(-1), output(-1), interpolation("LINEAR") {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100559 DEFAULT_METHODS(AnimationSampler)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900560 bool operator==(const AnimationSampler &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000561};
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900562
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900563struct Animation {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900564 std::string name;
565 std::vector<AnimationChannel> channels;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000566 std::vector<AnimationSampler> samplers;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900567 Value extras;
Selmar Kok4e2988e2019-08-16 14:08:08 +0200568 ExtensionMap extensions;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200569
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900570 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
571 std::string extras_json_string;
572 std::string extensions_json_string;
573
Selmar Kokff2b1f92019-10-21 17:58:09 +0200574 Animation() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +0100575 DEFAULT_METHODS(Animation)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900576 bool operator==(const Animation &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +0900577};
Syoyo Fujita8c5ab032016-06-19 18:15:32 +0900578
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000579struct Skin {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900580 std::string name;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900581 int inverseBindMatrices; // required here but not in the spec
582 int skeleton; // The index of the node used as a skeleton root
583 std::vector<int> joints; // Indices of skeleton nodes
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000584
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900585 Value extras;
586 ExtensionMap extensions;
587
588 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
589 std::string extras_json_string;
590 std::string extensions_json_string;
591
Syoyo Fujita2840a0e2017-06-21 02:52:11 +0900592 Skin() {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000593 inverseBindMatrices = -1;
Aurélien Chatelain8d5e0a72017-06-19 13:52:08 +0000594 skeleton = -1;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000595 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100596 DEFAULT_METHODS(Skin)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900597 bool operator==(const Skin &) const;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +0000598};
599
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000600struct Sampler {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900601 std::string name;
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900602 // glTF 2.0 spec does not define default value for `minFilter` and
603 // `magFilter`. Set -1 in TinyGLTF(issue #186)
604 int minFilter =
605 -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR",
zz8a35b8f2021-05-14 13:49:33 +0800606 // "NEAREST_MIPMAP_NEAREST", "LINEAR_MIPMAP_NEAREST",
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900607 // "NEAREST_MIPMAP_LINEAR", "LINEAR_MIPMAP_LINEAR"]
608 int magFilter =
609 -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR"]
610 int wrapS =
611 TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
612 // "REPEAT"], default "REPEAT"
613 int wrapT =
614 TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT",
615 // "REPEAT"], default "REPEAT"
Syoyo Fujita2c521b32020-12-04 00:50:46 +0900616 //int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; // TinyGLTF extension. currently not used.
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900617
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900618 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900619 ExtensionMap extensions;
620
621 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
622 std::string extras_json_string;
623 std::string extensions_json_string;
Syoyo Fujitac2615632016-06-19 21:56:06 +0900624
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000625 Sampler()
Syoyo Fujitaee179b22019-08-16 13:11:30 +0900626 : minFilter(-1),
627 magFilter(-1),
timmmeh73584ba2019-01-30 18:38:46 -0800628 wrapS(TINYGLTF_TEXTURE_WRAP_REPEAT),
Syoyo Fujita2c521b32020-12-04 00:50:46 +0900629 wrapT(TINYGLTF_TEXTURE_WRAP_REPEAT) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100630 DEFAULT_METHODS(Sampler)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900631 bool operator==(const Sampler &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000632};
633
Syoyo Fujita5b407452017-06-04 17:42:41 +0900634struct Image {
Syoyo Fujitac2615632016-06-19 21:56:06 +0900635 std::string name;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900636 int width;
637 int height;
638 int component;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000639 int bits; // bit depth per channel. 8(byte), 16 or 32.
640 int pixel_type; // pixel type(TINYGLTF_COMPONENT_TYPE_***). usually
641 // UBYTE(bits = 8) or USHORT(bits = 16)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900642 std::vector<unsigned char> image;
Syoyo Fujita5b407452017-06-04 17:42:41 +0900643 int bufferView; // (required if no uri)
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500644 std::string mimeType; // (required if no uri) ["image/jpeg", "image/png",
645 // "image/bmp", "image/gif"]
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900646 std::string uri; // (required if no mimeType) uri is not decoded(e.g.
647 // whitespace may be represented as %20)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900648 Value extras;
Syoyo Fujita3e53feb2018-09-02 15:36:17 +0900649 ExtensionMap extensions;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900650
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900651 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
652 std::string extras_json_string;
653 std::string extensions_json_string;
654
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900655 // When this flag is true, data is stored to `image` in as-is format(e.g. jpeg
656 // compressed for "image/jpeg" mime) This feature is good if you use custom
657 // image loader function. (e.g. delayed decoding of images for faster glTF
658 // parsing) Default parser for Image does not provide as-is loading feature at
659 // the moment. (You can manipulate this by providing your own LoadImageData
660 // function)
Selmar Kokfa0a9982018-10-03 15:46:23 +0200661 bool as_is;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900662
663 Image() : as_is(false) {
664 bufferView = -1;
665 width = -1;
666 height = -1;
667 component = -1;
daemyung jang1739d022020-07-03 13:27:39 +0900668 bits = -1;
669 pixel_type = -1;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900670 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100671 DEFAULT_METHODS(Image)
jrkoonced1e14722019-08-27 11:51:02 -0500672
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900673 bool operator==(const Image &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000674};
675
676struct Texture {
Vladimír Vondruš239be2c2018-07-24 23:23:56 +0200677 std::string name;
678
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000679 int sampler;
Selmar Kok8eb39042018-10-05 14:29:35 +0200680 int source;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900681 Value extras;
Selmar Kokfa7022f2018-04-04 18:10:20 +0200682 ExtensionMap extensions;
Syoyo Fujitabde70212016-02-07 17:38:17 +0900683
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900684 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
685 std::string extras_json_string;
686 std::string extensions_json_string;
687
Syoyo Fujita5b407452017-06-04 17:42:41 +0900688 Texture() : sampler(-1), source(-1) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100689 DEFAULT_METHODS(Texture)
jrkoonced1e14722019-08-27 11:51:02 -0500690
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900691 bool operator==(const Texture &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000692};
Syoyo Fujitabde70212016-02-07 17:38:17 +0900693
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900694struct TextureInfo {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900695 int index = -1; // required.
696 int texCoord; // The set index of texture's TEXCOORD attribute used for
697 // texture coordinate mapping.
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900698
699 Value extras;
700 ExtensionMap extensions;
701
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900702 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
703 std::string extras_json_string;
704 std::string extensions_json_string;
705
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900706 TextureInfo() : index(-1), texCoord(0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100707 DEFAULT_METHODS(TextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900708 bool operator==(const TextureInfo &) const;
709};
710
711struct NormalTextureInfo {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900712 int index = -1; // required
713 int texCoord; // The set index of texture's TEXCOORD attribute used for
714 // texture coordinate mapping.
715 double scale; // scaledNormal = normalize((<sampled normal texture value>
716 // * 2.0 - 1.0) * vec3(<normal scale>, <normal scale>, 1.0))
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900717
718 Value extras;
719 ExtensionMap extensions;
720
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900721 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
722 std::string extras_json_string;
723 std::string extensions_json_string;
724
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900725 NormalTextureInfo() : index(-1), texCoord(0), scale(1.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100726 DEFAULT_METHODS(NormalTextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900727 bool operator==(const NormalTextureInfo &) const;
728};
729
730struct OcclusionTextureInfo {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900731 int index = -1; // required
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900732 int texCoord; // The set index of texture's TEXCOORD attribute used for
733 // texture coordinate mapping.
734 double strength; // occludedColor = lerp(color, color * <sampled occlusion
735 // texture value>, <occlusion strength>)
736
737 Value extras;
738 ExtensionMap extensions;
739
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900740 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
741 std::string extras_json_string;
742 std::string extensions_json_string;
743
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900744 OcclusionTextureInfo() : index(-1), texCoord(0), strength(1.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100745 DEFAULT_METHODS(OcclusionTextureInfo)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900746 bool operator==(const OcclusionTextureInfo &) const;
747};
748
749// pbrMetallicRoughness class defined in glTF 2.0 spec.
750struct PbrMetallicRoughness {
Syoyo Fujita046400b2019-07-24 19:26:48 +0900751 std::vector<double> baseColorFactor; // len = 4. default [1,1,1,1]
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900752 TextureInfo baseColorTexture;
753 double metallicFactor; // default 1
754 double roughnessFactor; // default 1
755 TextureInfo metallicRoughnessTexture;
756
757 Value extras;
758 ExtensionMap extensions;
759
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900760 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
761 std::string extras_json_string;
762 std::string extensions_json_string;
763
764 PbrMetallicRoughness()
765 : baseColorFactor(std::vector<double>{1.0, 1.0, 1.0, 1.0}),
766 metallicFactor(1.0),
767 roughnessFactor(1.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100768 DEFAULT_METHODS(PbrMetallicRoughness)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900769 bool operator==(const PbrMetallicRoughness &) const;
770};
771
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000772// Each extension should be stored in a ParameterMap.
773// members not in the values could be included in the ParameterMap
774// to keep a single material model
775struct Material {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900776 std::string name;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900777
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900778 std::vector<double> emissiveFactor; // length 3. default [0, 0, 0]
Syoyo Fujita046400b2019-07-24 19:26:48 +0900779 std::string alphaMode; // default "OPAQUE"
780 double alphaCutoff; // default 0.5
781 bool doubleSided; // default false;
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900782
783 PbrMetallicRoughness pbrMetallicRoughness;
784
785 NormalTextureInfo normalTexture;
786 OcclusionTextureInfo occlusionTexture;
787 TextureInfo emissiveTexture;
788
Syoyo Fujita046400b2019-07-24 19:26:48 +0900789 // For backward compatibility
790 // TODO(syoyo): Remove `values` and `additionalValues` in the next release.
791 ParameterMap values;
792 ParameterMap additionalValues;
Selmar09d2ff12018-03-15 17:30:42 +0100793
794 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900795 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +0200796
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900797 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
798 std::string extras_json_string;
799 std::string extensions_json_string;
800
Syoyo Fujita046400b2019-07-24 19:26:48 +0900801 Material() : alphaMode("OPAQUE"), alphaCutoff(0.5), doubleSided(false) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100802 DEFAULT_METHODS(Material)
Syoyo Fujita89fd93f2019-07-23 22:37:06 +0900803
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900804 bool operator==(const Material &) const;
Aurélien Chatelainab7b2182017-05-24 09:53:44 +0000805};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900806
Syoyo Fujita5b407452017-06-04 17:42:41 +0900807struct BufferView {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900808 std::string name;
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900809 int buffer{-1}; // Required
Syoyo Fujita4e47bc72020-01-02 22:07:25 +0900810 size_t byteOffset{0}; // minimum 0, default 0
811 size_t byteLength{0}; // required, minimum 1. 0 = invalid
812 size_t byteStride{0}; // minimum 4, maximum 252 (multiple of 4), default 0 =
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900813 // understood to be tightly packed
814 int target{0}; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"] for vertex indices
815 // or atttribs. Could be 0 for other data
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900816 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900817 ExtensionMap extensions;
818
819 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
820 std::string extras_json_string;
821 std::string extensions_json_string;
822
Syoyo Fujita4e47bc72020-01-02 22:07:25 +0900823 bool dracoDecoded{false}; // Flag indicating this has been draco decoded
Syoyo Fujitad17ff662017-05-29 02:53:12 +0900824
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900825 BufferView()
826 : buffer(-1),
827 byteOffset(0),
828 byteLength(0),
829 byteStride(0),
830 target(0),
831 dracoDecoded(false) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100832 DEFAULT_METHODS(BufferView)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900833 bool operator==(const BufferView &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000834};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900835
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000836struct Accessor {
Syoyo Fujita5b407452017-06-04 17:42:41 +0900837 int bufferView; // optional in spec but required here since sparse accessor
838 // are not supported
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900839 std::string name;
840 size_t byteOffset;
Fabien Freling9056aee2019-03-21 17:03:18 +0100841 bool normalized; // optional.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000842 int componentType; // (required) One of TINYGLTF_COMPONENT_TYPE_***
Syoyo Fujita5b407452017-06-04 17:42:41 +0900843 size_t count; // required
844 int type; // (required) One of TINYGLTF_TYPE_*** ..
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900845 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900846 ExtensionMap extensions;
847
848 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
849 std::string extras_json_string;
850 std::string extensions_json_string;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000851
Syoyo Fujita7905a5b2021-01-21 20:33:11 +0900852 std::vector<double>
853 minValues; // optional. integer value is promoted to double
854 std::vector<double>
855 maxValues; // optional. integer value is promoted to double
Syoyo Fujitad9d012a2017-07-12 18:29:29 +0900856
Arthur Brainville (Ybalrid)1ccb4ff2019-03-06 11:30:00 +0100857 struct {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000858 int count;
859 bool isSparse;
860 struct {
861 int byteOffset;
862 int bufferView;
863 int componentType; // a TINYGLTF_COMPONENT_TYPE_ value
864 } indices;
865 struct {
866 int bufferView;
867 int byteOffset;
868 } values;
Arthur Brainville (Ybalrid)1ccb4ff2019-03-06 11:30:00 +0100869 } sparse;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000870
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900871 ///
872 /// Utility function to compute byteStride for a given bufferView object.
873 /// Returns -1 upon invalid glTF value or parameter configuration.
874 ///
875 int ByteStride(const BufferView &bufferViewObject) const {
876 if (bufferViewObject.byteStride == 0) {
877 // Assume data is tightly packed.
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500878 int componentSizeInBytes =
879 GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900880 if (componentSizeInBytes <= 0) {
881 return -1;
882 }
883
Syoyo Fujita81d75df2019-08-30 19:19:52 +0900884 int numComponents = GetNumComponentsInType(static_cast<uint32_t>(type));
885 if (numComponents <= 0) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900886 return -1;
887 }
888
Syoyo Fujita81d75df2019-08-30 19:19:52 +0900889 return componentSizeInBytes * numComponents;
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900890 } else {
Syoyo Fujitad42767e2018-03-15 21:52:00 -0500891 // Check if byteStride is a mulple of the size of the accessor's component
892 // type.
893 int componentSizeInBytes =
894 GetComponentSizeInBytes(static_cast<uint32_t>(componentType));
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900895 if (componentSizeInBytes <= 0) {
896 return -1;
897 }
898
Syoyo Fujita05e0bf12018-01-16 18:55:13 +0900899 if ((bufferViewObject.byteStride % uint32_t(componentSizeInBytes)) != 0) {
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900900 return -1;
901 }
Arthur Brainville8ce4e542018-01-10 01:27:12 +0100902 return static_cast<int>(bufferViewObject.byteStride);
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900903 }
904
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +0900905 // unreachable return 0;
Syoyo Fujita584f8c02018-01-03 17:47:08 +0900906 }
907
Syoyo Fujitac4166e42020-01-08 02:38:01 +0900908 Accessor()
909 : bufferView(-1),
910 byteOffset(0),
911 normalized(false),
912 componentType(-1),
913 count(0),
914 type(-1) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +0000915 sparse.isSparse = false;
916 }
Selmar Kokb74fade2019-10-29 16:09:32 +0100917 DEFAULT_METHODS(Accessor)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900918 bool operator==(const tinygltf::Accessor &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000919};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900920
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900921struct PerspectiveCamera {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200922 double aspectRatio; // min > 0
923 double yfov; // required. min > 0
924 double zfar; // min > 0
925 double znear; // required. min > 0
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900926
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900927 PerspectiveCamera()
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900928 : aspectRatio(0.0),
929 yfov(0.0),
930 zfar(0.0) // 0 = use infinite projecton matrix
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900931 ,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900932 znear(0.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100933 DEFAULT_METHODS(PerspectiveCamera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900934 bool operator==(const PerspectiveCamera &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900935
Selmar09d2ff12018-03-15 17:30:42 +0100936 ExtensionMap extensions;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900937 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900938
939 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
940 std::string extras_json_string;
941 std::string extensions_json_string;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900942};
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000943
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900944struct OrthographicCamera {
Selmar Kok31cb7f92018-10-03 15:39:05 +0200945 double xmag; // required. must not be zero.
946 double ymag; // required. must not be zero.
947 double zfar; // required. `zfar` must be greater than `znear`.
948 double znear; // required
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000949
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900950 OrthographicCamera() : xmag(0.0), ymag(0.0), zfar(0.0), znear(0.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100951 DEFAULT_METHODS(OrthographicCamera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900952 bool operator==(const OrthographicCamera &) const;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900953
Selmar09d2ff12018-03-15 17:30:42 +0100954 ExtensionMap extensions;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900955 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900956
957 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
958 std::string extras_json_string;
959 std::string extensions_json_string;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900960};
961
962struct Camera {
963 std::string type; // required. "perspective" or "orthographic"
964 std::string name;
965
966 PerspectiveCamera perspective;
967 OrthographicCamera orthographic;
968
969 Camera() {}
Selmar Kokb74fade2019-10-29 16:09:32 +0100970 DEFAULT_METHODS(Camera)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900971 bool operator==(const Camera &) const;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +0900972
Selmar09d2ff12018-03-15 17:30:42 +0100973 ExtensionMap extensions;
Aurélien Chatelaina25e4952017-05-24 09:52:28 +0000974 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900975
976 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
977 std::string extras_json_string;
978 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +0900979};
980
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000981struct Primitive {
982 std::map<std::string, int> attributes; // (required) A dictionary object of
983 // integer, where each integer
984 // is the index of the accessor
985 // containing an attribute.
986 int material; // The index of the material to apply to this primitive
Syoyo Fujita5b407452017-06-04 17:42:41 +0900987 // when rendering.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000988 int indices; // The index of the accessor that contains the indices.
989 int mode; // one of TINYGLTF_MODE_***
Syoyo Fujita641b3cc2018-10-04 15:43:33 +0900990 std::vector<std::map<std::string, int> > targets; // array of morph targets,
Syoyo Fujita5b407452017-06-04 17:42:41 +0900991 // where each target is a dict with attribues in ["POSITION, "NORMAL",
992 // "TANGENT"] pointing
993 // to their corresponding accessors
Alex Wood7319db72019-01-24 15:38:16 -0500994 ExtensionMap extensions;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +0000995 Value extras;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +0900996
Syoyo Fujita6e08b172019-10-30 17:25:38 +0900997 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
998 std::string extras_json_string;
999 std::string extensions_json_string;
1000
Syoyo Fujita5b407452017-06-04 17:42:41 +09001001 Primitive() {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001002 material = -1;
1003 indices = -1;
daemyung jang1739d022020-07-03 13:27:39 +09001004 mode = -1;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001005 }
Selmar Kokb74fade2019-10-29 16:09:32 +01001006 DEFAULT_METHODS(Primitive)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001007 bool operator==(const Primitive &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001008};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001009
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001010struct Mesh {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001011 std::string name;
1012 std::vector<Primitive> primitives;
Syoyo Fujita5b407452017-06-04 17:42:41 +09001013 std::vector<double> weights; // weights to be applied to the Morph Targets
Selmar09d2ff12018-03-15 17:30:42 +01001014 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001015 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001016
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001017 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1018 std::string extras_json_string;
1019 std::string extensions_json_string;
1020
jrkoonced1e14722019-08-27 11:51:02 -05001021 Mesh() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001022 DEFAULT_METHODS(Mesh)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001023 bool operator==(const Mesh &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001024};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001025
1026class Node {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001027 public:
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09001028 Node() : camera(-1), skin(-1), mesh(-1) {}
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001029
Selmar Kokb74fade2019-10-29 16:09:32 +01001030 DEFAULT_METHODS(Node)
Syoyo Fujitad6b0d0a2019-06-29 17:31:13 +09001031
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001032 bool operator==(const Node &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001033
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001034 int camera; // the index of the camera referenced by this node
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001035
1036 std::string name;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00001037 int skin;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001038 int mesh;
1039 std::vector<int> children;
Syoyo Fujita7c877972016-03-08 01:31:49 +09001040 std::vector<double> rotation; // length must be 0 or 4
1041 std::vector<double> scale; // length must be 0 or 3
1042 std::vector<double> translation; // length must be 0 or 3
1043 std::vector<double> matrix; // length must be 0 or 16
Syoyo Fujita5b407452017-06-04 17:42:41 +09001044 std::vector<double> weights; // The weights of the instantiated Morph Target
Ben Buzbee3b735bb2018-04-24 11:39:30 -07001045
Selmar09d2ff12018-03-15 17:30:42 +01001046 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001047 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001048
1049 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1050 std::string extras_json_string;
1051 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001052};
1053
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001054struct Buffer {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001055 std::string name;
1056 std::vector<unsigned char> data;
Syoyo Fujita5b407452017-06-04 17:42:41 +09001057 std::string
1058 uri; // considered as required here but not in the spec (need to clarify)
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001059 // uri is not decoded(e.g. whitespace may be represented as %20)
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001060 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001061 ExtensionMap extensions;
1062
1063 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1064 std::string extras_json_string;
1065 std::string extensions_json_string;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001066
Selmar Kokb74fade2019-10-29 16:09:32 +01001067 Buffer() = default;
1068 DEFAULT_METHODS(Buffer)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001069 bool operator==(const Buffer &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001070};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001071
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001072struct Asset {
Syoyo Fujitab702de72021-03-02 19:08:29 +09001073 std::string version = "2.0"; // required
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001074 std::string generator;
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +00001075 std::string minVersion;
1076 std::string copyright;
Selmar09d2ff12018-03-15 17:30:42 +01001077 ExtensionMap extensions;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001078 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001079
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001080 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1081 std::string extras_json_string;
1082 std::string extensions_json_string;
1083
jrkoonced1e14722019-08-27 11:51:02 -05001084 Asset() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001085 DEFAULT_METHODS(Asset)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001086 bool operator==(const Asset &) const;
Syoyo Fujita2d17a312018-04-17 15:45:42 +09001087};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001088
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001089struct Scene {
1090 std::string name;
1091 std::vector<int> nodes;
1092
Selmar09d2ff12018-03-15 17:30:42 +01001093 ExtensionMap extensions;
1094 Value extras;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001095
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001096 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1097 std::string extras_json_string;
1098 std::string extensions_json_string;
1099
jrkoonced1e14722019-08-27 11:51:02 -05001100 Scene() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001101 DEFAULT_METHODS(Scene)
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001102 bool operator==(const Scene &) const;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001103};
1104
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001105struct SpotLight {
1106 double innerConeAngle;
1107 double outerConeAngle;
1108
Johan Bowald52936a02019-07-17 09:06:45 +02001109 SpotLight() : innerConeAngle(0.0), outerConeAngle(0.7853981634) {}
Selmar Kokb74fade2019-10-29 16:09:32 +01001110 DEFAULT_METHODS(SpotLight)
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001111 bool operator==(const SpotLight &) const;
1112
1113 ExtensionMap extensions;
1114 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001115
1116 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1117 std::string extras_json_string;
1118 std::string extensions_json_string;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001119};
1120
Emanuel Schrade186322b2017-11-06 11:14:41 +01001121struct Light {
1122 std::string name;
1123 std::vector<double> color;
Syoyo Fujita3dad3032020-05-13 21:22:23 +09001124 double intensity{1.0};
Emanuel Schrade186322b2017-11-06 11:14:41 +01001125 std::string type;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09001126 double range{0.0}; // 0.0 = inifinite
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001127 SpotLight spot;
1128
Benjamin Schmithüsenb2d7d882019-07-09 16:32:42 +02001129 Light() : intensity(1.0), range(0.0) {}
Selmar Kokb74fade2019-10-29 16:09:32 +01001130 DEFAULT_METHODS(Light)
Arthur Brainville (Ybalrid)9eeaf202019-09-05 13:02:05 +02001131
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001132 bool operator==(const Light &) const;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001133
1134 ExtensionMap extensions;
1135 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001136
1137 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1138 std::string extras_json_string;
1139 std::string extensions_json_string;
Emanuel Schrade186322b2017-11-06 11:14:41 +01001140};
1141
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001142class Model {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001143 public:
Selmar Kokff2b1f92019-10-21 17:58:09 +02001144 Model() = default;
Selmar Kokb74fade2019-10-29 16:09:32 +01001145 DEFAULT_METHODS(Model)
Syoyo Fujitad6b0d0a2019-06-29 17:31:13 +09001146
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001147 bool operator==(const Model &) const;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001148
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001149 std::vector<Accessor> accessors;
1150 std::vector<Animation> animations;
1151 std::vector<Buffer> buffers;
1152 std::vector<BufferView> bufferViews;
1153 std::vector<Material> materials;
1154 std::vector<Mesh> meshes;
1155 std::vector<Node> nodes;
1156 std::vector<Texture> textures;
1157 std::vector<Image> images;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00001158 std::vector<Skin> skins;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001159 std::vector<Sampler> samplers;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09001160 std::vector<Camera> cameras;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001161 std::vector<Scene> scenes;
Emanuel Schrade186322b2017-11-06 11:14:41 +01001162 std::vector<Light> lights;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001163
sammyKhana0a62bd2020-01-17 13:41:16 +01001164 int defaultScene = -1;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00001165 std::vector<std::string> extensionsUsed;
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00001166 std::vector<std::string> extensionsRequired;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001167
1168 Asset asset;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001169
1170 Value extras;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001171 ExtensionMap extensions;
1172
1173 // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled.
1174 std::string extras_json_string;
1175 std::string extensions_json_string;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001176};
1177
Syoyo Fujita0614eb82016-10-14 18:50:14 +09001178enum SectionCheck {
Luke San Antonio9e36b612016-06-23 14:10:51 -04001179 NO_REQUIRE = 0x00,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001180 REQUIRE_VERSION = 0x01,
1181 REQUIRE_SCENE = 0x02,
1182 REQUIRE_SCENES = 0x04,
1183 REQUIRE_NODES = 0x08,
1184 REQUIRE_ACCESSORS = 0x10,
1185 REQUIRE_BUFFERS = 0x20,
1186 REQUIRE_BUFFER_VIEWS = 0x40,
1187 REQUIRE_ALL = 0x7f
Luke San Antonio9e36b612016-06-23 14:10:51 -04001188};
1189
Squareysff644d82018-03-13 22:36:18 +01001190///
1191/// LoadImageDataFunction type. Signature for custom image loading callbacks.
1192///
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001193typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *,
1194 std::string *, int, int,
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001195 const unsigned char *, int,
1196 void *user_pointer);
Squareysff644d82018-03-13 22:36:18 +01001197
johan bowald642a3432018-04-01 12:37:18 +02001198///
1199/// WriteImageDataFunction type. Signature for custom image writing callbacks.
1200///
1201typedef bool (*WriteImageDataFunction)(const std::string *, const std::string *,
1202 Image *, bool, void *);
1203
Squareys2d3594d2018-03-13 22:40:53 +01001204#ifndef TINYGLTF_NO_STB_IMAGE
Squareysff644d82018-03-13 22:36:18 +01001205// Declaration of default image loader callback
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001206bool LoadImageData(Image *image, const int image_idx, std::string *err,
1207 std::string *warn, int req_width, int req_height,
1208 const unsigned char *bytes, int size, void *);
Squareys2d3594d2018-03-13 22:40:53 +01001209#endif
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001210
johan bowald642a3432018-04-01 12:37:18 +02001211#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
1212// Declaration of default image writer callback
1213bool WriteImageData(const std::string *basepath, const std::string *filename,
1214 Image *image, bool embedImages, void *);
1215#endif
1216
Paolo Jovone6601bf2018-07-07 20:43:33 +02001217///
1218/// FilExistsFunction type. Signature for custom filesystem callbacks.
1219///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001220typedef bool (*FileExistsFunction)(const std::string &abs_filename, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001221
1222///
1223/// ExpandFilePathFunction type. Signature for custom filesystem callbacks.
1224///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001225typedef std::string (*ExpandFilePathFunction)(const std::string &, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001226
1227///
1228/// ReadWholeFileFunction type. Signature for custom filesystem callbacks.
1229///
1230typedef bool (*ReadWholeFileFunction)(std::vector<unsigned char> *,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001231 std::string *, const std::string &,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001232 void *);
1233
1234///
1235/// WriteWholeFileFunction type. Signature for custom filesystem callbacks.
1236///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001237typedef bool (*WriteWholeFileFunction)(std::string *, const std::string &,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001238 const std::vector<unsigned char> &,
1239 void *);
1240
1241///
1242/// A structure containing all required filesystem callbacks and a pointer to
1243/// their user data.
1244///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001245struct FsCallbacks {
1246 FileExistsFunction FileExists;
1247 ExpandFilePathFunction ExpandFilePath;
1248 ReadWholeFileFunction ReadWholeFile;
1249 WriteWholeFileFunction WriteWholeFile;
Paolo Jovone6601bf2018-07-07 20:43:33 +02001250
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001251 void *user_data; // An argument that is passed to all fs callbacks
Paolo Jovone6601bf2018-07-07 20:43:33 +02001252};
1253
1254#ifndef TINYGLTF_NO_FS
1255// Declaration of default filesystem callbacks
1256
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001257bool FileExists(const std::string &abs_filename, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001258
Syoyo Fujita3d939fd2020-06-06 18:13:15 +09001259///
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04001260/// Expand file path(e.g. `~` to home directory on posix, `%APPDATA%` to
Syoyo Fujita2c521b32020-12-04 00:50:46 +09001261/// `C:\\Users\\tinygltf\\AppData`)
Syoyo Fujita3d939fd2020-06-06 18:13:15 +09001262///
1263/// @param[in] filepath File path string. Assume UTF-8
1264/// @param[in] userdata User data. Set to `nullptr` if you don't need it.
1265///
1266std::string ExpandFilePath(const std::string &filepath, void *userdata);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001267
1268bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001269 const std::string &filepath, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001270
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001271bool WriteWholeFile(std::string *err, const std::string &filepath,
1272 const std::vector<unsigned char> &contents, void *);
Paolo Jovone6601bf2018-07-07 20:43:33 +02001273#endif
1274
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001275///
1276/// glTF Parser/Serialier context.
1277///
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001278class TinyGLTF {
Syoyo Fujita7c877972016-03-08 01:31:49 +09001279 public:
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001280#ifdef __clang__
1281#pragma clang diagnostic push
1282#pragma clang diagnostic ignored "-Wc++98-compat"
1283#endif
1284
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001285 TinyGLTF() : bin_data_(nullptr), bin_size_(0), is_binary_(false) {}
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001286
1287#ifdef __clang__
1288#pragma clang diagnostic pop
1289#endif
1290
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001291 ~TinyGLTF() {}
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001292
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001293 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001294 /// Loads glTF ASCII asset from a file.
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001295 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001296 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001297 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001298 bool LoadASCIIFromFile(Model *model, std::string *err, std::string *warn,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001299 const std::string &filename,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001300 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001301
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001302 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001303 /// Loads glTF ASCII asset from string(memory).
1304 /// `length` = strlen(str);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001305 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001306 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001307 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001308 bool LoadASCIIFromString(Model *model, std::string *err, std::string *warn,
1309 const char *str, const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001310 const std::string &base_dir,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001311 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001312
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001313 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001314 /// Loads glTF binary asset from a file.
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001315 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001316 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001317 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001318 bool LoadBinaryFromFile(Model *model, std::string *err, std::string *warn,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001319 const std::string &filename,
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001320 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001321
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001322 ///
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001323 /// Loads glTF binary asset from memory.
1324 /// `length` = strlen(str);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001325 /// Set warning message to `warn` for example it fails to load asserts.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001326 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001327 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001328 bool LoadBinaryFromMemory(Model *model, std::string *err, std::string *warn,
Syoyo Fujitabeded612016-05-01 20:03:43 +09001329 const unsigned char *bytes,
1330 const unsigned int length,
Luke San Antonio9e36b612016-06-23 14:10:51 -04001331 const std::string &base_dir = "",
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02001332 unsigned int check_sections = REQUIRE_VERSION);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001333
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001334 ///
Johan Bowald1af7c1d2019-07-16 14:50:46 +02001335 /// Write glTF to stream, buffers and images will be embeded
1336 ///
1337 bool WriteGltfSceneToStream(Model *model, std::ostream &stream,
1338 bool prettyPrint, bool writeBinary);
1339
1340 ///
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001341 /// Write glTF to file.
1342 ///
johan bowald642a3432018-04-01 12:37:18 +02001343 bool WriteGltfSceneToFile(Model *model, const std::string &filename,
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001344 bool embedImages, bool embedBuffers,
1345 bool prettyPrint, bool writeBinary);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00001346
Squareysff644d82018-03-13 22:36:18 +01001347 ///
1348 /// Set callback to use for loading image data
1349 ///
1350 void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data);
1351
johan bowald642a3432018-04-01 12:37:18 +02001352 ///
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001353 /// Unset(remove) callback of loading image data
1354 ///
1355 void RemoveImageLoader();
1356
1357 ///
johan bowald642a3432018-04-01 12:37:18 +02001358 /// Set callback to use for writing image data
1359 ///
1360 void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data);
1361
Paolo Jovone6601bf2018-07-07 20:43:33 +02001362 ///
1363 /// Set callbacks to use for filesystem (fs) access and their user data
1364 ///
1365 void SetFsCallbacks(FsCallbacks callbacks);
1366
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001367 ///
1368 /// Set serializing default values(default = false).
1369 /// When true, default values are force serialized to .glTF.
Syoyo Fujitaff515702019-08-24 16:29:14 +09001370 /// This may be helpfull if you want to serialize a full description of glTF
1371 /// data.
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001372 ///
Syoyo Fujitaff515702019-08-24 16:29:14 +09001373 /// TODO(LTE): Supply parsing option as function arguments to
1374 /// `LoadASCIIFromFile()` and others, not by a class method
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001375 ///
1376 void SetSerializeDefaultValues(const bool enabled) {
1377 serialize_default_values_ = enabled;
1378 }
1379
Syoyo Fujitaff515702019-08-24 16:29:14 +09001380 bool GetSerializeDefaultValues() const { return serialize_default_values_; }
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001381
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001382 ///
1383 /// Store original JSON string for `extras` and `extensions`.
1384 /// This feature will be useful when the user want to reconstruct custom data
1385 /// structure from JSON string.
1386 ///
1387 void SetStoreOriginalJSONForExtrasAndExtensions(const bool enabled) {
1388 store_original_json_for_extras_and_extensions_ = enabled;
1389 }
1390
1391 bool GetStoreOriginalJSONForExtrasAndExtensions() const {
1392 return store_original_json_for_extras_and_extensions_;
1393 }
1394
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001395 ///
1396 /// Specify whether preserve image channales when loading images or not.
1397 /// (Not effective when the user suppy their own LoadImageData callbacks)
1398 ///
1399 void SetPreserveImageChannels(bool onoff) {
1400 preserve_image_channels_ = onoff;
1401 }
1402
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001403 bool GetPreserveImageChannels() const { return preserve_image_channels_; }
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001404
Syoyo Fujitabeded612016-05-01 20:03:43 +09001405 private:
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001406 ///
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001407 /// Loads glTF asset from string(memory).
1408 /// `length` = strlen(str);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001409 /// Set warning message to `warn` for example it fails to load asserts
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09001410 /// Returns false and set error string to `err` if there's an error.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001411 ///
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001412 bool LoadFromString(Model *model, std::string *err, std::string *warn,
1413 const char *str, const unsigned int length,
1414 const std::string &base_dir, unsigned int check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001415
Syoyo Fujitaa472a3f2019-08-21 14:23:00 +09001416 const unsigned char *bin_data_ = nullptr;
1417 size_t bin_size_ = 0;
1418 bool is_binary_ = false;
1419
Syoyo Fujitaff515702019-08-24 16:29:14 +09001420 bool serialize_default_values_ = false; ///< Serialize default values?
Squareysff644d82018-03-13 22:36:18 +01001421
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001422 bool store_original_json_for_extras_and_extensions_ = false;
1423
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001424 bool preserve_image_channels_ = false; /// Default false(expand channels to
1425 /// RGBA) for backward compatibility.
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001426
Paolo Jovone6601bf2018-07-07 20:43:33 +02001427 FsCallbacks fs = {
1428#ifndef TINYGLTF_NO_FS
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001429 &tinygltf::FileExists, &tinygltf::ExpandFilePath,
1430 &tinygltf::ReadWholeFile, &tinygltf::WriteWholeFile,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001431
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001432 nullptr // Fs callback user data
Paolo Jovone6601bf2018-07-07 20:43:33 +02001433#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001434 nullptr, nullptr, nullptr, nullptr,
Paolo Jovone6601bf2018-07-07 20:43:33 +02001435
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001436 nullptr // Fs callback user data
Paolo Jovone6601bf2018-07-07 20:43:33 +02001437#endif
1438 };
1439
Squareysff644d82018-03-13 22:36:18 +01001440 LoadImageDataFunction LoadImageData =
1441#ifndef TINYGLTF_NO_STB_IMAGE
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001442 &tinygltf::LoadImageData;
Squareysff644d82018-03-13 22:36:18 +01001443#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001444 nullptr;
Squareysff644d82018-03-13 22:36:18 +01001445#endif
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001446 void *load_image_user_data_{nullptr};
1447 bool user_image_loader_{false};
johan bowald642a3432018-04-01 12:37:18 +02001448
1449 WriteImageDataFunction WriteImageData =
1450#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001451 &tinygltf::WriteImageData;
johan bowald642a3432018-04-01 12:37:18 +02001452#else
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09001453 nullptr;
johan bowald642a3432018-04-01 12:37:18 +02001454#endif
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001455 void *write_image_user_data_{nullptr};
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001456};
1457
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001458#ifdef __clang__
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001459#pragma clang diagnostic pop // -Wpadded
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001460#endif
1461
Syoyo Fujita7c877972016-03-08 01:31:49 +09001462} // namespace tinygltf
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001463
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09001464#endif // TINY_GLTF_H_
Syoyo Fujitac8d3c762017-05-25 01:42:38 +09001465
Selmar Kok31cb7f92018-10-03 15:39:05 +02001466#if defined(TINYGLTF_IMPLEMENTATION) || defined(__INTELLISENSE__)
Syoyo Fujita7c877972016-03-08 01:31:49 +09001467#include <algorithm>
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001468//#include <cassert>
Paolo Jovone6601bf2018-07-07 20:43:33 +02001469#ifndef TINYGLTF_NO_FS
Syoyo Fujita125d8e52019-11-09 20:52:56 +09001470#include <cstdio>
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001471#include <fstream>
Paolo Jovone6601bf2018-07-07 20:43:33 +02001472#endif
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09001473#include <sstream>
Syoyo Fujitabeded612016-05-01 20:03:43 +09001474
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001475#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001476// Disable some warnings for external files.
1477#pragma clang diagnostic push
1478#pragma clang diagnostic ignored "-Wfloat-equal"
1479#pragma clang diagnostic ignored "-Wexit-time-destructors"
1480#pragma clang diagnostic ignored "-Wconversion"
1481#pragma clang diagnostic ignored "-Wold-style-cast"
Syoyo Fujita643ce102016-05-01 17:19:37 +09001482#pragma clang diagnostic ignored "-Wglobal-constructors"
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001483#if __has_warning("-Wreserved-id-macro")
Syoyo Fujita643ce102016-05-01 17:19:37 +09001484#pragma clang diagnostic ignored "-Wreserved-id-macro"
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001485#endif
Syoyo Fujita643ce102016-05-01 17:19:37 +09001486#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
1487#pragma clang diagnostic ignored "-Wpadded"
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001488#pragma clang diagnostic ignored "-Wc++98-compat"
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001489#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001490#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
1491#pragma clang diagnostic ignored "-Wswitch-enum"
1492#pragma clang diagnostic ignored "-Wimplicit-fallthrough"
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001493#pragma clang diagnostic ignored "-Wweak-vtables"
1494#pragma clang diagnostic ignored "-Wcovered-switch-default"
Syoyo Fujitaa8f0b1c2018-08-28 21:33:40 +09001495#if __has_warning("-Wdouble-promotion")
1496#pragma clang diagnostic ignored "-Wdouble-promotion"
1497#endif
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001498#if __has_warning("-Wcomma")
Syoyo Fujitac8d3c762017-05-25 01:42:38 +09001499#pragma clang diagnostic ignored "-Wcomma"
1500#endif
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001501#if __has_warning("-Wzero-as-null-pointer-constant")
1502#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant"
1503#endif
1504#if __has_warning("-Wcast-qual")
1505#pragma clang diagnostic ignored "-Wcast-qual"
1506#endif
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09001507#if __has_warning("-Wmissing-variable-declarations")
1508#pragma clang diagnostic ignored "-Wmissing-variable-declarations"
1509#endif
1510#if __has_warning("-Wmissing-prototypes")
1511#pragma clang diagnostic ignored "-Wmissing-prototypes"
1512#endif
1513#if __has_warning("-Wcast-align")
1514#pragma clang diagnostic ignored "-Wcast-align"
1515#endif
1516#if __has_warning("-Wnewline-eof")
1517#pragma clang diagnostic ignored "-Wnewline-eof"
1518#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001519#if __has_warning("-Wunused-parameter")
1520#pragma clang diagnostic ignored "-Wunused-parameter"
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001521#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001522#if __has_warning("-Wmismatched-tags")
1523#pragma clang diagnostic ignored "-Wmismatched-tags"
1524#endif
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001525#if __has_warning("-Wextra-semi-stmt")
1526#pragma clang diagnostic ignored "-Wextra-semi-stmt"
1527#endif
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001528#endif
1529
1530// Disable GCC warnigs
1531#ifdef __GNUC__
1532#pragma GCC diagnostic push
1533#pragma GCC diagnostic ignored "-Wtype-limits"
Syoyo Fujitaca56f722019-03-07 21:04:25 +09001534#endif // __GNUC__
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001535
krokofc0116b2019-03-03 08:28:49 +02001536#ifndef TINYGLTF_NO_INCLUDE_JSON
jrkooncecba5d6c2019-08-29 11:26:22 -05001537#ifndef TINYGLTF_USE_RAPIDJSON
Alex Wood7319db72019-01-24 15:38:16 -05001538#include "json.hpp"
jrkooncecba5d6c2019-08-29 11:26:22 -05001539#else
Alvaro Barua3ddf8302021-03-18 00:21:23 +00001540#ifndef TINYGLTF_NO_INCLUDE_RAPIDJSON
jrkooncecba5d6c2019-08-29 11:26:22 -05001541#include "document.h"
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001542#include "prettywriter.h"
1543#include "rapidjson.h"
jrkooncecba5d6c2019-08-29 11:26:22 -05001544#include "stringbuffer.h"
1545#include "writer.h"
jrkooncecba5d6c2019-08-29 11:26:22 -05001546#endif
krokof4b6d112019-03-03 01:11:31 +02001547#endif
Alvaro Barua3ddf8302021-03-18 00:21:23 +00001548#endif
Alex Wood7319db72019-01-24 15:38:16 -05001549
1550#ifdef TINYGLTF_ENABLE_DRACO
Alex Wood7319db72019-01-24 15:38:16 -05001551#include "draco/compression/decode.h"
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001552#include "draco/core/decoder_buffer.h"
Alex Wood7319db72019-01-24 15:38:16 -05001553#endif
Squareys2d3594d2018-03-13 22:40:53 +01001554
1555#ifndef TINYGLTF_NO_STB_IMAGE
krokofc0116b2019-03-03 08:28:49 +02001556#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE
Alex Wood7319db72019-01-24 15:38:16 -05001557#include "stb_image.h"
Squareys2d3594d2018-03-13 22:40:53 +01001558#endif
krokof4b6d112019-03-03 01:11:31 +02001559#endif
Squareys2d3594d2018-03-13 22:40:53 +01001560
johan bowald642a3432018-04-01 12:37:18 +02001561#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
krokofc0116b2019-03-03 08:28:49 +02001562#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE
Alex Wood7319db72019-01-24 15:38:16 -05001563#include "stb_image_write.h"
johan bowald642a3432018-04-01 12:37:18 +02001564#endif
krokof4b6d112019-03-03 01:11:31 +02001565#endif
johan bowald642a3432018-04-01 12:37:18 +02001566
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09001567#ifdef __clang__
Syoyo Fujita643ce102016-05-01 17:19:37 +09001568#pragma clang diagnostic pop
Syoyo Fujita6fd91622016-05-01 20:27:30 +09001569#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001570
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09001571#ifdef __GNUC__
1572#pragma GCC diagnostic pop
1573#endif
1574
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001575#ifdef _WIN32
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001576
1577// issue 143.
1578// Define NOMINMAX to avoid min/max defines,
1579// but undef it after included windows.h
1580#ifndef NOMINMAX
1581#define TINYGLTF_INTERNAL_NOMINMAX
1582#define NOMINMAX
1583#endif
1584
Syoyo Fujita87be0ce2019-02-19 21:36:32 +09001585#ifndef WIN32_LEAN_AND_MEAN
1586#define WIN32_LEAN_AND_MEAN
1587#define TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1588#endif
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00001589#include <windows.h> // include API for expanding a file path
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001590
Syoyo Fujita87be0ce2019-02-19 21:36:32 +09001591#ifdef TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN
1592#undef WIN32_LEAN_AND_MEAN
1593#endif
1594
Syoyo Fujita7d9a0bd2019-02-19 16:03:23 +09001595#if defined(TINYGLTF_INTERNAL_NOMINMAX)
1596#undef NOMINMAX
1597#endif
1598
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001599#if defined(__GLIBCXX__) // mingw
Syoyo Fujita45cac782019-11-09 20:42:55 +09001600
Syoyo Fujitac4166e42020-01-08 02:38:01 +09001601#include <fcntl.h> // _O_RDONLY
1602
1603#include <ext/stdio_filebuf.h> // fstream (all sorts of IO stuff) + stdio_filebuf (=streambuf)
Syoyo Fujita45cac782019-11-09 20:42:55 +09001604
1605#endif
1606
Julian Smith0598a202021-08-25 12:06:08 +01001607#elif !defined(__ANDROID__) && !defined(__OpenBSD__)
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001608#include <wordexp.h>
1609#endif
1610
Syoyo Fujitaec39a522016-05-01 18:33:31 +09001611#if defined(__sparcv9)
1612// Big endian
1613#else
1614#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU
1615#define TINYGLTF_LITTLE_ENDIAN 1
1616#endif
1617#endif
1618
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001619namespace {
jrkooncecba5d6c2019-08-29 11:26:22 -05001620#ifdef TINYGLTF_USE_RAPIDJSON
jrkooncece7fa742019-09-04 13:31:44 -05001621
1622#ifdef TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001623// This uses the RapidJSON CRTAllocator. It is thread safe and multiple
1624// documents may be active at once.
1625using json =
1626 rapidjson::GenericValue<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
1627using json_const_iterator = json::ConstMemberIterator;
1628using json_const_array_iterator = json const *;
1629using JsonDocument =
jrkooncece7fa742019-09-04 13:31:44 -05001630 rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator>;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001631rapidjson::CrtAllocator s_CrtAllocator; // stateless and thread safe
1632rapidjson::CrtAllocator &GetAllocator() { return s_CrtAllocator; }
jrkooncece7fa742019-09-04 13:31:44 -05001633#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001634// This uses the default RapidJSON MemoryPoolAllocator. It is very fast, but
1635// not thread safe. Only a single JsonDocument may be active at any one time,
1636// meaning only a single gltf load/save can be active any one time.
1637using json = rapidjson::Value;
1638using json_const_iterator = json::ConstMemberIterator;
1639using json_const_array_iterator = json const *;
1640rapidjson::Document *s_pActiveDocument = nullptr;
1641rapidjson::Document::AllocatorType &GetAllocator() {
1642 assert(s_pActiveDocument); // Root json node must be JsonDocument type
1643 return s_pActiveDocument->GetAllocator();
1644}
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001645
1646#ifdef __clang__
1647#pragma clang diagnostic push
1648// Suppress JsonDocument(JsonDocument &&rhs) noexcept
1649#pragma clang diagnostic ignored "-Wunused-member-function"
1650#endif
1651
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001652struct JsonDocument : public rapidjson::Document {
1653 JsonDocument() {
1654 assert(s_pActiveDocument ==
1655 nullptr); // When using default allocator, only one document can be
1656 // active at a time, if you need multiple active at once,
1657 // define TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
1658 s_pActiveDocument = this;
jrkooncece7fa742019-09-04 13:31:44 -05001659 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001660 JsonDocument(const JsonDocument &) = delete;
1661 JsonDocument(JsonDocument &&rhs) noexcept
jrkooncece7fa742019-09-04 13:31:44 -05001662 : rapidjson::Document(std::move(rhs)) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001663 s_pActiveDocument = this;
1664 rhs.isNil = true;
1665 }
1666 ~JsonDocument() {
1667 if (!isNil) {
1668 s_pActiveDocument = nullptr;
jrkooncecba5d6c2019-08-29 11:26:22 -05001669 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001670 }
jrkooncece7fa742019-09-04 13:31:44 -05001671
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001672 private:
1673 bool isNil = false;
1674};
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001675
1676#ifdef __clang__
1677#pragma clang diagnostic pop
1678#endif
1679
jrkooncece7fa742019-09-04 13:31:44 -05001680#endif // TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001681
jrkooncecba5d6c2019-08-29 11:26:22 -05001682#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001683using nlohmann::json;
1684using json_const_iterator = json::const_iterator;
1685using json_const_array_iterator = json_const_iterator;
1686using JsonDocument = json;
jrkooncecba5d6c2019-08-29 11:26:22 -05001687#endif
1688
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001689void JsonParse(JsonDocument &doc, const char *str, size_t length,
1690 bool throwExc = false) {
jrkooncecba5d6c2019-08-29 11:26:22 -05001691#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001692 (void)throwExc;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001693 doc.Parse(str, length);
jrkooncecba5d6c2019-08-29 11:26:22 -05001694#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001695 doc = json::parse(str, str + length, nullptr, throwExc);
jrkooncecba5d6c2019-08-29 11:26:22 -05001696#endif
jrkooncecba5d6c2019-08-29 11:26:22 -05001697}
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09001698} // namespace
Syoyo Fujita2e21be72017-11-05 17:13:01 +09001699
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001700#ifdef __APPLE__
Syoyo Fujitad42767e2018-03-15 21:52:00 -05001701#include "TargetConditionals.h"
Syoyo Fujitab23f6fe2017-11-15 01:03:09 +09001702#endif
1703
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09001704#ifdef __clang__
1705#pragma clang diagnostic push
1706#pragma clang diagnostic ignored "-Wc++98-compat"
1707#endif
1708
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09001709namespace tinygltf {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09001710
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001711///
1712/// Internal LoadImageDataOption struct.
1713/// This struct is passed through `user_pointer` in LoadImageData.
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001714/// The struct is not passed when the user supply their own LoadImageData
1715/// callbacks.
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001716///
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09001717struct LoadImageDataOption {
1718 // true: preserve image channels(e.g. load as RGB image if the image has RGB
1719 // channels) default `false`(channels are expanded to RGBA for backward
1720 // compatiblity).
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001721 bool preserve_channels{false};
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09001722};
1723
Selmar Kok31cb7f92018-10-03 15:39:05 +02001724// Equals function for Value, for recursivity
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001725static bool Equals(const tinygltf::Value &one, const tinygltf::Value &other) {
1726 if (one.Type() != other.Type()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001727
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001728 switch (one.Type()) {
1729 case NULL_TYPE:
1730 return true;
1731 case BOOL_TYPE:
1732 return one.Get<bool>() == other.Get<bool>();
Syoyo Fujita150f2432019-07-25 19:22:44 +09001733 case REAL_TYPE:
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001734 return TINYGLTF_DOUBLE_EQUAL(one.Get<double>(), other.Get<double>());
1735 case INT_TYPE:
1736 return one.Get<int>() == other.Get<int>();
1737 case OBJECT_TYPE: {
1738 auto oneObj = one.Get<tinygltf::Value::Object>();
1739 auto otherObj = other.Get<tinygltf::Value::Object>();
1740 if (oneObj.size() != otherObj.size()) return false;
1741 for (auto &it : oneObj) {
1742 auto otherIt = otherObj.find(it.first);
1743 if (otherIt == otherObj.end()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001744
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001745 if (!Equals(it.second, otherIt->second)) return false;
1746 }
1747 return true;
1748 }
1749 case ARRAY_TYPE: {
1750 if (one.Size() != other.Size()) return false;
1751 for (int i = 0; i < int(one.Size()); ++i)
Selmara63cc632019-04-16 16:57:43 +02001752 if (!Equals(one.Get(i), other.Get(i))) return false;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001753 return true;
1754 }
1755 case STRING_TYPE:
1756 return one.Get<std::string>() == other.Get<std::string>();
1757 case BINARY_TYPE:
1758 return one.Get<std::vector<unsigned char> >() ==
1759 other.Get<std::vector<unsigned char> >();
1760 default: {
1761 // unhandled type
1762 return false;
1763 }
1764 }
Selmar Kok31cb7f92018-10-03 15:39:05 +02001765}
1766
1767// Equals function for std::vector<double> using TINYGLTF_DOUBLE_EPSILON
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001768static bool Equals(const std::vector<double> &one,
1769 const std::vector<double> &other) {
1770 if (one.size() != other.size()) return false;
1771 for (int i = 0; i < int(one.size()); ++i) {
1772 if (!TINYGLTF_DOUBLE_EQUAL(one[size_t(i)], other[size_t(i)])) return false;
1773 }
1774 return true;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001775}
1776
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001777bool Accessor::operator==(const Accessor &other) const {
1778 return this->bufferView == other.bufferView &&
1779 this->byteOffset == other.byteOffset &&
1780 this->componentType == other.componentType &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001781 this->count == other.count && this->extensions == other.extensions &&
1782 this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001783 Equals(this->maxValues, other.maxValues) &&
1784 Equals(this->minValues, other.minValues) && this->name == other.name &&
1785 this->normalized == other.normalized && this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001786}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001787bool Animation::operator==(const Animation &other) const {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001788 return this->channels == other.channels &&
1789 this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001790 this->name == other.name && this->samplers == other.samplers;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001791}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001792bool AnimationChannel::operator==(const AnimationChannel &other) const {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001793 return this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001794 this->target_node == other.target_node &&
1795 this->target_path == other.target_path &&
1796 this->sampler == other.sampler;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001797}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001798bool AnimationSampler::operator==(const AnimationSampler &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001799 return this->extras == other.extras && this->extensions == other.extensions &&
1800 this->input == other.input &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001801 this->interpolation == other.interpolation &&
1802 this->output == other.output;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001803}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001804bool Asset::operator==(const Asset &other) const {
1805 return this->copyright == other.copyright &&
1806 this->extensions == other.extensions && this->extras == other.extras &&
1807 this->generator == other.generator &&
1808 this->minVersion == other.minVersion && this->version == other.version;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001809}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001810bool Buffer::operator==(const Buffer &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001811 return this->data == other.data && this->extensions == other.extensions &&
1812 this->extras == other.extras && this->name == other.name &&
1813 this->uri == other.uri;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001814}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001815bool BufferView::operator==(const BufferView &other) const {
1816 return this->buffer == other.buffer && this->byteLength == other.byteLength &&
1817 this->byteOffset == other.byteOffset &&
1818 this->byteStride == other.byteStride && this->name == other.name &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001819 this->target == other.target && this->extensions == other.extensions &&
1820 this->extras == other.extras &&
Alexander Wood0d77a292019-01-26 08:58:45 -05001821 this->dracoDecoded == other.dracoDecoded;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001822}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001823bool Camera::operator==(const Camera &other) const {
1824 return this->name == other.name && this->extensions == other.extensions &&
1825 this->extras == other.extras &&
1826 this->orthographic == other.orthographic &&
1827 this->perspective == other.perspective && this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001828}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001829bool Image::operator==(const Image &other) const {
1830 return this->bufferView == other.bufferView &&
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001831 this->component == other.component &&
1832 this->extensions == other.extensions && this->extras == other.extras &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001833 this->height == other.height && this->image == other.image &&
1834 this->mimeType == other.mimeType && this->name == other.name &&
1835 this->uri == other.uri && this->width == other.width;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001836}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001837bool Light::operator==(const Light &other) const {
1838 return Equals(this->color, other.color) && this->name == other.name &&
1839 this->type == other.type;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001840}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001841bool Material::operator==(const Material &other) const {
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001842 return (this->pbrMetallicRoughness == other.pbrMetallicRoughness) &&
1843 (this->normalTexture == other.normalTexture) &&
1844 (this->occlusionTexture == other.occlusionTexture) &&
1845 (this->emissiveTexture == other.emissiveTexture) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09001846 Equals(this->emissiveFactor, other.emissiveFactor) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001847 (this->alphaMode == other.alphaMode) &&
Syoyo Fujitacea69e32019-08-20 17:10:30 +09001848 TINYGLTF_DOUBLE_EQUAL(this->alphaCutoff, other.alphaCutoff) &&
Syoyo Fujita89fd93f2019-07-23 22:37:06 +09001849 (this->doubleSided == other.doubleSided) &&
1850 (this->extensions == other.extensions) &&
Syoyo Fujita046400b2019-07-24 19:26:48 +09001851 (this->extras == other.extras) && (this->values == other.values) &&
1852 (this->additionalValues == other.additionalValues) &&
1853 (this->name == other.name);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001854}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001855bool Mesh::operator==(const Mesh &other) const {
1856 return this->extensions == other.extensions && this->extras == other.extras &&
Selmar Kok81b672b2019-10-18 16:08:44 +02001857 this->name == other.name && Equals(this->weights, other.weights) &&
1858 this->primitives == other.primitives;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001859}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001860bool Model::operator==(const Model &other) const {
1861 return this->accessors == other.accessors &&
1862 this->animations == other.animations && this->asset == other.asset &&
1863 this->buffers == other.buffers &&
1864 this->bufferViews == other.bufferViews &&
1865 this->cameras == other.cameras &&
1866 this->defaultScene == other.defaultScene &&
1867 this->extensions == other.extensions &&
1868 this->extensionsRequired == other.extensionsRequired &&
1869 this->extensionsUsed == other.extensionsUsed &&
1870 this->extras == other.extras && this->images == other.images &&
1871 this->lights == other.lights && this->materials == other.materials &&
1872 this->meshes == other.meshes && this->nodes == other.nodes &&
1873 this->samplers == other.samplers && this->scenes == other.scenes &&
1874 this->skins == other.skins && this->textures == other.textures;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001875}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001876bool Node::operator==(const Node &other) const {
1877 return this->camera == other.camera && this->children == other.children &&
1878 this->extensions == other.extensions && this->extras == other.extras &&
1879 Equals(this->matrix, other.matrix) && this->mesh == other.mesh &&
1880 this->name == other.name && Equals(this->rotation, other.rotation) &&
1881 Equals(this->scale, other.scale) && this->skin == other.skin &&
1882 Equals(this->translation, other.translation) &&
1883 Equals(this->weights, other.weights);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001884}
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02001885bool SpotLight::operator==(const SpotLight &other) const {
1886 return this->extensions == other.extensions && this->extras == other.extras &&
1887 TINYGLTF_DOUBLE_EQUAL(this->innerConeAngle, other.innerConeAngle) &&
1888 TINYGLTF_DOUBLE_EQUAL(this->outerConeAngle, other.outerConeAngle);
1889}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001890bool OrthographicCamera::operator==(const OrthographicCamera &other) const {
1891 return this->extensions == other.extensions && this->extras == other.extras &&
1892 TINYGLTF_DOUBLE_EQUAL(this->xmag, other.xmag) &&
1893 TINYGLTF_DOUBLE_EQUAL(this->ymag, other.ymag) &&
1894 TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
1895 TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001896}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001897bool Parameter::operator==(const Parameter &other) const {
1898 if (this->bool_value != other.bool_value ||
1899 this->has_number_value != other.has_number_value)
1900 return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001901
Selmar Kok2bda71c2018-10-05 14:36:05 +02001902 if (!TINYGLTF_DOUBLE_EQUAL(this->number_value, other.number_value))
1903 return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001904
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001905 if (this->json_double_value.size() != other.json_double_value.size())
1906 return false;
1907 for (auto &it : this->json_double_value) {
1908 auto otherIt = other.json_double_value.find(it.first);
1909 if (otherIt == other.json_double_value.end()) return false;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001910
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001911 if (!TINYGLTF_DOUBLE_EQUAL(it.second, otherIt->second)) return false;
1912 }
1913
1914 if (!Equals(this->number_array, other.number_array)) return false;
1915
1916 if (this->string_value != other.string_value) return false;
1917
1918 return true;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001919}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001920bool PerspectiveCamera::operator==(const PerspectiveCamera &other) const {
1921 return TINYGLTF_DOUBLE_EQUAL(this->aspectRatio, other.aspectRatio) &&
1922 this->extensions == other.extensions && this->extras == other.extras &&
1923 TINYGLTF_DOUBLE_EQUAL(this->yfov, other.yfov) &&
1924 TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) &&
1925 TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear);
Selmar Kok31cb7f92018-10-03 15:39:05 +02001926}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001927bool Primitive::operator==(const Primitive &other) const {
1928 return this->attributes == other.attributes && this->extras == other.extras &&
1929 this->indices == other.indices && this->material == other.material &&
1930 this->mode == other.mode && this->targets == other.targets;
Selmar Kok31cb7f92018-10-03 15:39:05 +02001931}
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001932bool Sampler::operator==(const Sampler &other) const {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09001933 return this->extensions == other.extensions && this->extras == other.extras &&
1934 this->magFilter == other.magFilter &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001935 this->minFilter == other.minFilter && this->name == other.name &&
Eero Pajarre51b12d22022-02-24 14:36:33 +02001936 this->wrapS == other.wrapS &&
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09001937 this->wrapT == other.wrapT;
Syoyo Fujita2c521b32020-12-04 00:50:46 +09001938
Eero Pajarre51b12d22022-02-24 14:36:33 +02001939 //this->wrapR == other.wrapR
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//
Alexander Woode4bc6c72021-10-14 08:54:59 -04002194// Uri Decoding from DLIB
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002195// http://dlib.net/dlib/server/server_http.cpp.html
Alexander Woode4bc6c72021-10-14 08:54:59 -04002196// --- dlib begin ------------------------------------------------------------
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002197// Copyright (C) 2003 Davis E. King (davis@dlib.net)
Alexander Woode4bc6c72021-10-14 08:54:59 -04002198// License: Boost Software License
2199// Boost Software License - Version 1.0 - August 17th, 2003
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002200
Alexander Woode4bc6c72021-10-14 08:54:59 -04002201// Permission is hereby granted, free of charge, to any person or organization
2202// obtaining a copy of the software and accompanying documentation covered by
2203// this license (the "Software") to use, reproduce, display, distribute,
2204// execute, and transmit the Software, and to prepare derivative works of the
2205// Software, and to permit third-parties to whom the Software is furnished to
2206// do so, all subject to the following:
2207// The copyright notices in the Software and this entire statement, including
2208// the above license grant, this restriction and the following disclaimer,
2209// must be included in all copies of the Software, in whole or in part, and
2210// all derivative works of the Software, unless such copies or derivative
2211// works are solely in the form of machine-executable object code generated by
2212// a source language processor.
2213// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
2214// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
2215// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
2216// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
2217// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
2218// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
2219// DEALINGS IN THE SOFTWARE.
Syoyo Fujitacbd38852022-02-26 21:19:15 +09002220//
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002221namespace dlib {
2222
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002223inline unsigned char from_hex(unsigned char ch) {
2224 if (ch <= '9' && ch >= '0')
2225 ch -= '0';
2226 else if (ch <= 'f' && ch >= 'a')
2227 ch -= 'a' - 10;
2228 else if (ch <= 'F' && ch >= 'A')
2229 ch -= 'A' - 10;
2230 else
2231 ch = 0;
2232 return ch;
2233}
2234
2235static const std::string urldecode(const std::string &str) {
2236 using namespace std;
2237 string result;
2238 string::size_type i;
2239 for (i = 0; i < str.size(); ++i) {
2240 if (str[i] == '+') {
2241 result += ' ';
2242 } else if (str[i] == '%' && str.size() > i + 2) {
2243 const unsigned char ch1 =
2244 from_hex(static_cast<unsigned char>(str[i + 1]));
2245 const unsigned char ch2 =
2246 from_hex(static_cast<unsigned char>(str[i + 2]));
2247 const unsigned char ch = static_cast<unsigned char>((ch1 << 4) | ch2);
2248 result += static_cast<char>(ch);
2249 i += 2;
2250 } else {
2251 result += str[i];
2252 }
2253 }
2254 return result;
2255}
2256
2257} // namespace dlib
2258// --- dlib end --------------------------------------------------------------
2259
Syoyo Fujitaec39a522016-05-01 18:33:31 +09002260static bool LoadExternalFile(std::vector<unsigned char> *out, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002261 std::string *warn, const std::string &filename,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002262 const std::string &basedir, bool required,
2263 size_t reqBytes, bool checkSize, FsCallbacks *fs) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002264 if (fs == nullptr || fs->FileExists == nullptr ||
2265 fs->ExpandFilePath == nullptr || fs->ReadWholeFile == nullptr) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002266 // This is a developer error, assert() ?
2267 if (err) {
2268 (*err) += "FS callback[s] not set\n";
2269 }
2270 return false;
2271 }
2272
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002273 std::string *failMsgOut = required ? err : warn;
Selmar Koke3b3fa92018-08-22 19:04:21 +02002274
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002275 out->clear();
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002276
2277 std::vector<std::string> paths;
2278 paths.push_back(basedir);
2279 paths.push_back(".");
2280
Paolo Jovone6601bf2018-07-07 20:43:33 +02002281 std::string filepath = FindFile(paths, filename, fs);
jianghaosenb721fb72017-08-25 21:01:17 +08002282 if (filepath.empty() || filename.empty()) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002283 if (failMsgOut) {
2284 (*failMsgOut) += "File not found : " + filename + "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002285 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002286 return false;
2287 }
2288
Paolo Jovone6601bf2018-07-07 20:43:33 +02002289 std::vector<unsigned char> buf;
2290 std::string fileReadErr;
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002291 bool fileRead =
2292 fs->ReadWholeFile(&buf, &fileReadErr, filepath, fs->user_data);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002293 if (!fileRead) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002294 if (failMsgOut) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002295 (*failMsgOut) +=
2296 "File read error : " + filepath + " : " + fileReadErr + "\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 size_t sz = buf.size();
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09002302 if (sz == 0) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002303 if (failMsgOut) {
Selmar Koke3b3fa92018-08-22 19:04:21 +02002304 (*failMsgOut) += "File is empty : " + filepath + "\n";
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002305 }
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09002306 return false;
2307 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002308
2309 if (checkSize) {
2310 if (reqBytes == sz) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002311 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002312 return true;
2313 } else {
2314 std::stringstream ss;
2315 ss << "File size mismatch : " << filepath << ", requestedBytes "
2316 << reqBytes << ", but got " << sz << std::endl;
Selmar Koke3b3fa92018-08-22 19:04:21 +02002317 if (failMsgOut) {
2318 (*failMsgOut) += ss.str();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002319 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002320 return false;
2321 }
2322 }
2323
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002324 out->swap(buf);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002325 return true;
2326}
2327
Squareysff644d82018-03-13 22:36:18 +01002328void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002329 LoadImageData = func;
2330 load_image_user_data_ = user_data;
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002331 user_image_loader_ = true;
2332}
2333
2334void TinyGLTF::RemoveImageLoader() {
2335 LoadImageData =
2336#ifndef TINYGLTF_NO_STB_IMAGE
2337 &tinygltf::LoadImageData;
2338#else
2339 nullptr;
2340#endif
2341
2342 load_image_user_data_ = nullptr;
2343 user_image_loader_ = false;
Squareysff644d82018-03-13 22:36:18 +01002344}
2345
Squareys2d3594d2018-03-13 22:40:53 +01002346#ifndef TINYGLTF_NO_STB_IMAGE
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002347bool LoadImageData(Image *image, const int image_idx, std::string *err,
2348 std::string *warn, int req_width, int req_height,
2349 const unsigned char *bytes, int size, void *user_data) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002350 (void)warn;
2351
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002352 LoadImageDataOption option;
2353 if (user_data) {
2354 option = *reinterpret_cast<LoadImageDataOption *>(user_data);
2355 }
2356
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002357 int w = 0, h = 0, comp = 0, req_comp = 0;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002358
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002359 unsigned char *data = nullptr;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002360
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002361 // preserve_channels true: Use channels stored in the image file.
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09002362 // false: force 32-bit textures for common Vulkan compatibility. It appears
2363 // that some GPU drivers do not support 24-bit images for Vulkan
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002364 req_comp = option.preserve_channels ? 0 : 4;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002365 int bits = 8;
2366 int pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE;
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002367
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002368 // It is possible that the image we want to load is a 16bit per channel image
2369 // We are going to attempt to load it as 16bit per channel, and if it worked,
2370 // set the image data accodingly. We are casting the returned pointer into
2371 // unsigned char, because we are representing "bytes". But we are updating
2372 // the Image metadata to signal that this image uses 2 bytes (16bits) per
2373 // channel:
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002374 if (stbi_is_16_bit_from_memory(bytes, size)) {
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002375 data = reinterpret_cast<unsigned char *>(
2376 stbi_load_16_from_memory(bytes, size, &w, &h, &comp, req_comp));
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002377 if (data) {
2378 bits = 16;
2379 pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT;
2380 }
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002381 }
2382
2383 // at this point, if data is still NULL, it means that the image wasn't
2384 // 16bit per channel, we are going to load it as a normal 8bit per channel
2385 // mage as we used to do:
Aurélien Chatelain5cb43462017-05-24 09:55:14 +00002386 // if image cannot be decoded, ignore parsing and keep it by its path
2387 // don't break in this case
Syoyo Fujita5b407452017-06-04 17:42:41 +09002388 // FIXME we should only enter this function if the image is embedded. If
2389 // image->uri references
2390 // an image file, it should be left as it is. Image loading should not be
2391 // mandatory (to support other formats)
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002392 if (!data) data = stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002393 if (!data) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002394 // NOTE: you can use `warn` instead of `err`
Syoyo Fujitabeded612016-05-01 20:03:43 +09002395 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002396 (*err) +=
2397 "Unknown image format. STB cannot decode image data for image[" +
2398 std::to_string(image_idx) + "] name = \"" + image->name + "\".\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002399 }
Omar C. Fd492efc2018-02-10 09:50:35 +02002400 return false;
Syoyo Fujitabeded612016-05-01 20:03:43 +09002401 }
2402
Syoyo Fujitacea69e32019-08-20 17:10:30 +09002403 if ((w < 1) || (h < 1)) {
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002404 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002405 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002406 (*err) += "Invalid image data for image[" + std::to_string(image_idx) +
2407 "] name = \"" + image->name + "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002408 }
Omar C. Fd492efc2018-02-10 09:50:35 +02002409 return false;
Syoyo Fujitabeded612016-05-01 20:03:43 +09002410 }
2411
2412 if (req_width > 0) {
2413 if (req_width != w) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002414 stbi_image_free(data);
Syoyo Fujitabeded612016-05-01 20:03:43 +09002415 if (err) {
Arthur Brainville (Ybalrid)5a4c8982019-03-02 22:03:34 +01002416 (*err) += "Image width mismatch for image[" +
2417 std::to_string(image_idx) + "] name = \"" + image->name +
2418 "\"\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09002419 }
2420 return false;
2421 }
2422 }
2423
2424 if (req_height > 0) {
2425 if (req_height != h) {
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 height 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
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002436 if (req_comp != 0) {
2437 // loaded data has `req_comp` channels(components)
2438 comp = req_comp;
2439 }
2440
Syoyo Fujitabeded612016-05-01 20:03:43 +09002441 image->width = w;
2442 image->height = h;
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002443 image->component = comp;
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002444 image->bits = bits;
2445 image->pixel_type = pixel_type;
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09002446 image->image.resize(static_cast<size_t>(w * h * comp) * size_t(bits / 8));
2447 std::copy(data, data + w * h * comp * (bits / 8), image->image.begin());
Arthur Brainville (Ybalrid)f2addc02019-03-02 22:00:48 +01002448 stbi_image_free(data);
Syoyo Fujita4be2f882016-11-24 16:20:34 +09002449
Syoyo Fujitabeded612016-05-01 20:03:43 +09002450 return true;
2451}
Squareys2d3594d2018-03-13 22:40:53 +01002452#endif
Syoyo Fujitabeded612016-05-01 20:03:43 +09002453
johan bowald642a3432018-04-01 12:37:18 +02002454void TinyGLTF::SetImageWriter(WriteImageDataFunction func, void *user_data) {
2455 WriteImageData = func;
2456 write_image_user_data_ = user_data;
2457}
2458
2459#ifndef TINYGLTF_NO_STB_IMAGE_WRITE
2460static void WriteToMemory_stbi(void *context, void *data, int size) {
2461 std::vector<unsigned char> *buffer =
2462 reinterpret_cast<std::vector<unsigned char> *>(context);
2463
2464 unsigned char *pData = reinterpret_cast<unsigned char *>(data);
2465
2466 buffer->insert(buffer->end(), pData, pData + size);
2467}
2468
2469bool WriteImageData(const std::string *basepath, const std::string *filename,
Paolo Jovone6601bf2018-07-07 20:43:33 +02002470 Image *image, bool embedImages, void *fsPtr) {
johan bowald642a3432018-04-01 12:37:18 +02002471 const std::string ext = GetFilePathExtension(*filename);
2472
Paolo Jovone6601bf2018-07-07 20:43:33 +02002473 // Write image to temporary buffer
2474 std::string header;
2475 std::vector<unsigned char> data;
johan bowald642a3432018-04-01 12:37:18 +02002476
Paolo Jovone6601bf2018-07-07 20:43:33 +02002477 if (ext == "png") {
Syoyo Fujitaca56f722019-03-07 21:04:25 +09002478 if ((image->bits != 8) ||
2479 (image->pixel_type != TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)) {
Syoyo Fujitae8a46c42019-03-07 20:50:58 +09002480 // Unsupported pixel format
2481 return false;
2482 }
2483
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002484 if (!stbi_write_png_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002485 image->height, image->component,
2486 &image->image[0], 0)) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002487 return false;
2488 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002489 header = "data:image/png;base64,";
2490 } else if (ext == "jpg") {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002491 if (!stbi_write_jpg_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002492 image->height, image->component,
2493 &image->image[0], 100)) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002494 return false;
2495 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002496 header = "data:image/jpeg;base64,";
2497 } else if (ext == "bmp") {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002498 if (!stbi_write_bmp_to_func(WriteToMemory_stbi, &data, image->width,
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002499 image->height, image->component,
2500 &image->image[0])) {
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002501 return false;
2502 }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002503 header = "data:image/bmp;base64,";
2504 } else if (!embedImages) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002505 // Error: can't output requested format to file
2506 return false;
Paolo Jovone6601bf2018-07-07 20:43:33 +02002507 }
johan bowald642a3432018-04-01 12:37:18 +02002508
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002509 if (embedImages) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002510 // Embed base64-encoded image into URI
johan bowald642a3432018-04-01 12:37:18 +02002511 if (data.size()) {
2512 image->uri =
2513 header +
2514 base64_encode(&data[0], static_cast<unsigned int>(data.size()));
2515 } else {
2516 // Throw error?
2517 }
2518 } else {
2519 // Write image to disc
Paolo Jovone6601bf2018-07-07 20:43:33 +02002520 FsCallbacks *fs = reinterpret_cast<FsCallbacks *>(fsPtr);
Syoyo Fujita33514af2018-11-05 13:32:43 +09002521 if ((fs != nullptr) && (fs->WriteWholeFile != nullptr)) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002522 const std::string imagefilepath = JoinPath(*basepath, *filename);
2523 std::string writeError;
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002524 if (!fs->WriteWholeFile(&writeError, imagefilepath, data,
2525 fs->user_data)) {
2526 // Could not write image file to disc; Throw error ?
Keiji Yoshimic0cfc1e2018-08-28 21:13:03 +09002527 return false;
Paolo Jovone6601bf2018-07-07 20:43:33 +02002528 }
johan bowald642a3432018-04-01 12:37:18 +02002529 } else {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002530 // Throw error?
johan bowald642a3432018-04-01 12:37:18 +02002531 }
2532 image->uri = *filename;
2533 }
2534
2535 return true;
2536}
2537#endif
2538
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002539void TinyGLTF::SetFsCallbacks(FsCallbacks callbacks) { fs = callbacks; }
Paolo Jovone6601bf2018-07-07 20:43:33 +02002540
Harokyangfb256602019-10-30 16:13:52 +08002541#ifdef _WIN32
2542static inline std::wstring UTF8ToWchar(const std::string &str) {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002543 int wstr_size =
2544 MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0);
Harokyangfb256602019-10-30 16:13:52 +08002545 std::wstring wstr(wstr_size, 0);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002546 MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0],
2547 (int)wstr.size());
Harokyangfb256602019-10-30 16:13:52 +08002548 return wstr;
2549}
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002550
2551static inline std::string WcharToUTF8(const std::wstring &wstr) {
2552 int str_size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(),
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04002553 nullptr, 0, NULL, NULL);
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002554 std::string str(str_size, 0);
2555 WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), &str[0],
2556 (int)str.size(), NULL, NULL);
2557 return str;
2558}
Harokyangfb256602019-10-30 16:13:52 +08002559#endif
2560
Paolo Jovone6601bf2018-07-07 20:43:33 +02002561#ifndef TINYGLTF_NO_FS
2562// Default implementations of filesystem functions
2563
2564bool FileExists(const std::string &abs_filename, void *) {
2565 bool ret;
Sascha Willems5f9cb242018-12-28 20:53:41 +01002566#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002567 if (asset_manager) {
2568 AAsset *asset = AAssetManager_open(asset_manager, abs_filename.c_str(),
2569 AASSET_MODE_STREAMING);
2570 if (!asset) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01002571 return false;
2572 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002573 AAsset_close(asset);
2574 ret = true;
2575 } else {
2576 return false;
2577 }
Sascha Willems5f9cb242018-12-28 20:53:41 +01002578#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002579#ifdef _WIN32
Syoyo Fujita45cac782019-11-09 20:42:55 +09002580#if defined(_MSC_VER) || defined(__GLIBCXX__)
2581 FILE *fp = nullptr;
Harokyangfb256602019-10-30 16:13:52 +08002582 errno_t err = _wfopen_s(&fp, UTF8ToWchar(abs_filename).c_str(), L"rb");
Paolo Jovone6601bf2018-07-07 20:43:33 +02002583 if (err != 0) {
2584 return false;
2585 }
2586#else
Syoyo Fujita45cac782019-11-09 20:42:55 +09002587 FILE *fp = nullptr;
2588 errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb");
2589 if (err != 0) {
2590 return false;
2591 }
2592#endif
2593
2594#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002595 FILE *fp = fopen(abs_filename.c_str(), "rb");
2596#endif
2597 if (fp) {
2598 ret = true;
2599 fclose(fp);
2600 } else {
2601 ret = false;
2602 }
Sascha Willems5f9cb242018-12-28 20:53:41 +01002603#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002604
2605 return ret;
2606}
2607
2608std::string ExpandFilePath(const std::string &filepath, void *) {
2609#ifdef _WIN32
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002610 // Assume input `filepath` is encoded in UTF-8
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002611 std::wstring wfilepath = UTF8ToWchar(filepath);
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002612 DWORD wlen = ExpandEnvironmentStringsW(wfilepath.c_str(), nullptr, 0);
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002613 wchar_t *wstr = new wchar_t[wlen];
2614 ExpandEnvironmentStringsW(wfilepath.c_str(), wstr, wlen);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002615
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08002616 std::wstring ws(wstr);
2617 delete[] wstr;
2618 return WcharToUTF8(ws);
Syoyo Fujitafbbeb4d2020-06-06 17:11:50 +09002619
Paolo Jovone6601bf2018-07-07 20:43:33 +02002620#else
2621
2622#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \
Julian Smith0598a202021-08-25 12:06:08 +01002623 defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__OpenBSD__)
Paolo Jovone6601bf2018-07-07 20:43:33 +02002624 // no expansion
2625 std::string s = filepath;
2626#else
2627 std::string s;
2628 wordexp_t p;
2629
2630 if (filepath.empty()) {
2631 return "";
2632 }
2633
Frank Galliganaa3c5a12020-01-13 15:06:56 -08002634 // Quote the string to keep any spaces in filepath intact.
2635 std::string quoted_path = "\"" + filepath + "\"";
Paolo Jovone6601bf2018-07-07 20:43:33 +02002636 // char** w;
Frank Galliganaa3c5a12020-01-13 15:06:56 -08002637 int ret = wordexp(quoted_path.c_str(), &p, 0);
Paolo Jovone6601bf2018-07-07 20:43:33 +02002638 if (ret) {
2639 // err
2640 s = filepath;
2641 return s;
2642 }
2643
2644 // Use first element only.
2645 if (p.we_wordv) {
2646 s = std::string(p.we_wordv[0]);
2647 wordfree(&p);
2648 } else {
2649 s = filepath;
2650 }
2651
2652#endif
2653
2654 return s;
2655#endif
2656}
2657
2658bool ReadWholeFile(std::vector<unsigned char> *out, std::string *err,
2659 const std::string &filepath, void *) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01002660#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS
2661 if (asset_manager) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00002662 AAsset *asset = AAssetManager_open(asset_manager, filepath.c_str(),
2663 AASSET_MODE_STREAMING);
Sascha Willems5f9cb242018-12-28 20:53:41 +01002664 if (!asset) {
2665 if (err) {
2666 (*err) += "File open error : " + filepath + "\n";
2667 }
2668 return false;
2669 }
2670 size_t size = AAsset_getLength(asset);
Syoyo Fujita73c4cce2020-04-28 01:06:34 +09002671 if (size == 0) {
Sascha Willems5f9cb242018-12-28 20:53:41 +01002672 if (err) {
2673 (*err) += "Invalid file size : " + filepath +
2674 " (does the path point to a directory?)";
2675 }
Syoyo Fujita73c4cce2020-04-28 01:06:34 +09002676 return false;
Sascha Willems5f9cb242018-12-28 20:53:41 +01002677 }
2678 out->resize(size);
2679 AAsset_read(asset, reinterpret_cast<char *>(&out->at(0)), size);
2680 AAsset_close(asset);
2681 return true;
2682 } else {
2683 if (err) {
2684 (*err) += "No asset manager specified : " + filepath + "\n";
2685 }
2686 return false;
2687 }
2688#else
Harokyang5cecef22019-10-30 15:16:46 +08002689#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002690#if defined(__GLIBCXX__) // mingw
2691 int file_descriptor =
2692 _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY);
Syoyo Fujita45cac782019-11-09 20:42:55 +09002693 __gnu_cxx::stdio_filebuf<char> wfile_buf(file_descriptor, std::ios_base::in);
2694 std::istream f(&wfile_buf);
Syoyo Fujitaaba57bb2020-05-18 20:50:45 +09002695#elif defined(_MSC_VER) || defined(_LIBCPP_VERSION)
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04002696 // For libcxx, assume _LIBCPP_HAS_OPEN_WITH_WCHAR is defined to accept
2697 // `wchar_t *`
Harokyangfb256602019-10-30 16:13:52 +08002698 std::ifstream f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary);
Syoyo Fujitaaba57bb2020-05-18 20:50:45 +09002699#else
2700 // Unknown compiler/runtime
Syoyo Fujita45cac782019-11-09 20:42:55 +09002701 std::ifstream f(filepath.c_str(), std::ifstream::binary);
2702#endif
Harokyang5cecef22019-10-30 15:16:46 +08002703#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002704 std::ifstream f(filepath.c_str(), std::ifstream::binary);
Harokyang5cecef22019-10-30 15:16:46 +08002705#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002706 if (!f) {
2707 if (err) {
2708 (*err) += "File open error : " + filepath + "\n";
2709 }
2710 return false;
2711 }
2712
2713 f.seekg(0, f.end);
2714 size_t sz = static_cast<size_t>(f.tellg());
2715 f.seekg(0, f.beg);
2716
Syoyo Fujitae8862472019-10-20 17:47:50 +09002717 if (int64_t(sz) < 0) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02002718 if (err) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002719 (*err) += "Invalid file size : " + filepath +
2720 " (does the path point to a directory?)";
Paolo Jovone6601bf2018-07-07 20:43:33 +02002721 }
2722 return false;
2723 } else if (sz == 0) {
2724 if (err) {
2725 (*err) += "File is empty : " + filepath + "\n";
2726 }
2727 return false;
2728 }
2729
2730 out->resize(sz);
2731 f.read(reinterpret_cast<char *>(&out->at(0)),
2732 static_cast<std::streamsize>(sz));
Paolo Jovone6601bf2018-07-07 20:43:33 +02002733
2734 return true;
Sascha Willems5f9cb242018-12-28 20:53:41 +01002735#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002736}
2737
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002738bool WriteWholeFile(std::string *err, const std::string &filepath,
2739 const std::vector<unsigned char> &contents, void *) {
Harokyangfb256602019-10-30 16:13:52 +08002740#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002741#if defined(__GLIBCXX__) // mingw
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09002742 int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(),
2743 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
2744 __gnu_cxx::stdio_filebuf<char> wfile_buf(
2745 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09002746 std::ostream f(&wfile_buf);
2747#elif defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08002748 std::ofstream f(UTF8ToWchar(filepath).c_str(), std::ofstream::binary);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09002749#else // clang?
Syoyo Fujita45cac782019-11-09 20:42:55 +09002750 std::ofstream f(filepath.c_str(), std::ofstream::binary);
2751#endif
Harokyangfb256602019-10-30 16:13:52 +08002752#else
Paolo Jovone6601bf2018-07-07 20:43:33 +02002753 std::ofstream f(filepath.c_str(), std::ofstream::binary);
Harokyangfb256602019-10-30 16:13:52 +08002754#endif
Paolo Jovone6601bf2018-07-07 20:43:33 +02002755 if (!f) {
2756 if (err) {
2757 (*err) += "File open error for writing : " + filepath + "\n";
2758 }
2759 return false;
2760 }
2761
2762 f.write(reinterpret_cast<const char *>(&contents.at(0)),
2763 static_cast<std::streamsize>(contents.size()));
2764 if (!f) {
2765 if (err) {
2766 (*err) += "File write error: " + filepath + "\n";
2767 }
2768 return false;
2769 }
2770
Paolo Jovone6601bf2018-07-07 20:43:33 +02002771 return true;
2772}
2773
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09002774#endif // TINYGLTF_NO_FS
Paolo Jovone6601bf2018-07-07 20:43:33 +02002775
johan bowald642a3432018-04-01 12:37:18 +02002776static std::string MimeToExt(const std::string &mimeType) {
2777 if (mimeType == "image/jpeg") {
2778 return "jpg";
2779 } else if (mimeType == "image/png") {
2780 return "png";
2781 } else if (mimeType == "image/bmp") {
2782 return "bmp";
2783 } else if (mimeType == "image/gif") {
2784 return "gif";
2785 }
2786
2787 return "";
2788}
2789
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09002790static void UpdateImageObject(Image &image, std::string &baseDir, int index,
Ben Buzbee3b735bb2018-04-24 11:39:30 -07002791 bool embedImages,
2792 WriteImageDataFunction *WriteImageData = nullptr,
2793 void *user_data = nullptr) {
johan bowald642a3432018-04-01 12:37:18 +02002794 std::string filename;
2795 std::string ext;
FsiGuy00015623db855c62020-03-09 16:57:21 -05002796 // If image has uri, use it it as a filename
johan bowald642a3432018-04-01 12:37:18 +02002797 if (image.uri.size()) {
2798 filename = GetBaseFilename(image.uri);
2799 ext = GetFilePathExtension(filename);
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09002800 } else if (image.bufferView != -1) {
2801 // If there's no URI and the data exists in a buffer,
2802 // don't change properties or write images
johan bowald642a3432018-04-01 12:37:18 +02002803 } else if (image.name.size()) {
2804 ext = MimeToExt(image.mimeType);
2805 // Otherwise use name as filename
2806 filename = image.name + "." + ext;
2807 } else {
2808 ext = MimeToExt(image.mimeType);
2809 // Fallback to index of image as filename
2810 filename = std::to_string(index) + "." + ext;
2811 }
2812
2813 // If callback is set, modify image data object
FsiGuy00015623db855c62020-03-09 16:57:21 -05002814 if (*WriteImageData != nullptr && !filename.empty()) {
johan bowald642a3432018-04-01 12:37:18 +02002815 std::string uri;
2816 (*WriteImageData)(&baseDir, &filename, &image, embedImages, user_data);
2817 }
2818}
2819
Selmar Kok0d0e97e2018-08-22 14:01:57 +02002820bool IsDataURI(const std::string &in) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002821 std::string header = "data:application/octet-stream;base64,";
2822 if (in.find(header) == 0) {
2823 return true;
2824 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002825
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002826 header = "data:image/jpeg;base64,";
2827 if (in.find(header) == 0) {
2828 return true;
2829 }
Squareys43374632018-03-13 22:20:48 +01002830
Syoyo Fujita620eed12016-01-02 23:37:12 +09002831 header = "data:image/png;base64,";
2832 if (in.find(header) == 0) {
2833 return true;
2834 }
Squareys43374632018-03-13 22:20:48 +01002835
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002836 header = "data:image/bmp;base64,";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002837 if (in.find(header) == 0) {
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002838 return true;
2839 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002840
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002841 header = "data:image/gif;base64,";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05002842 if (in.find(header) == 0) {
Syoyo Fujita620eed12016-01-02 23:37:12 +09002843 return true;
2844 }
2845
Luke San Antoniocaa24b02016-06-15 02:23:09 -04002846 header = "data:text/plain;base64,";
2847 if (in.find(header) == 0) {
2848 return true;
2849 }
2850
Syoyo Fujita20244e12018-03-15 11:01:05 -05002851 header = "data:application/gltf-buffer;base64,";
2852 if (in.find(header) == 0) {
2853 return true;
2854 }
2855
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002856 return false;
2857}
2858
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09002859bool DecodeDataURI(std::vector<unsigned char> *out, std::string &mime_type,
2860 const std::string &in, size_t reqBytes, bool checkSize) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002861 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujita620eed12016-01-02 23:37:12 +09002862 std::string data;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002863 if (in.find(header) == 0) {
Syoyo Fujita7c877972016-03-08 01:31:49 +09002864 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002865 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002866
2867 if (data.empty()) {
2868 header = "data:image/jpeg;base64,";
2869 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002870 mime_type = "image/jpeg";
Syoyo Fujita7c877972016-03-08 01:31:49 +09002871 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09002872 }
2873 }
2874
2875 if (data.empty()) {
2876 header = "data:image/png;base64,";
2877 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002878 mime_type = "image/png";
Syoyo Fujita7c877972016-03-08 01:31:49 +09002879 data = base64_decode(in.substr(header.size())); // cut mime string.
Syoyo Fujita620eed12016-01-02 23:37:12 +09002880 }
2881 }
Squareys43374632018-03-13 22:20:48 +01002882
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002883 if (data.empty()) {
2884 header = "data:image/bmp;base64,";
2885 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002886 mime_type = "image/bmp";
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002887 data = base64_decode(in.substr(header.size())); // cut mime string.
2888 }
2889 }
2890
2891 if (data.empty()) {
2892 header = "data:image/gif;base64,";
2893 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002894 mime_type = "image/gif";
Omar C. Ff8a8d9c2018-02-10 09:09:41 +02002895 data = base64_decode(in.substr(header.size())); // cut mime string.
2896 }
2897 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002898
2899 if (data.empty()) {
Luke San Antoniocaa24b02016-06-15 02:23:09 -04002900 header = "data:text/plain;base64,";
2901 if (in.find(header) == 0) {
johan bowald642a3432018-04-01 12:37:18 +02002902 mime_type = "text/plain";
Luke San Antoniocaa24b02016-06-15 02:23:09 -04002903 data = base64_decode(in.substr(header.size()));
2904 }
2905 }
2906
2907 if (data.empty()) {
Syoyo Fujita20244e12018-03-15 11:01:05 -05002908 header = "data:application/gltf-buffer;base64,";
2909 if (in.find(header) == 0) {
2910 data = base64_decode(in.substr(header.size()));
2911 }
2912 }
2913
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09002914 // TODO(syoyo): Allow empty buffer? #229
Syoyo Fujita20244e12018-03-15 11:01:05 -05002915 if (data.empty()) {
Syoyo Fujita620eed12016-01-02 23:37:12 +09002916 return false;
Syoyo Fujitabde70212016-02-07 17:38:17 +09002917 }
Syoyo Fujita620eed12016-01-02 23:37:12 +09002918
2919 if (checkSize) {
2920 if (data.size() != reqBytes) {
2921 return false;
2922 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002923 out->resize(reqBytes);
Syoyo Fujita620eed12016-01-02 23:37:12 +09002924 } else {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002925 out->resize(data.size());
Syoyo Fujita620eed12016-01-02 23:37:12 +09002926 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09002927 std::copy(data.begin(), data.end(), out->begin());
Syoyo Fujita620eed12016-01-02 23:37:12 +09002928 return true;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09002929}
2930
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002931namespace {
2932bool GetInt(const json &o, int &val) {
jrkoonce5cecc412019-08-29 11:45:04 -05002933#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002934 if (!o.IsDouble()) {
2935 if (o.IsInt()) {
2936 val = o.GetInt();
2937 return true;
2938 } else if (o.IsUint()) {
2939 val = static_cast<int>(o.GetUint());
2940 return true;
2941 } else if (o.IsInt64()) {
2942 val = static_cast<int>(o.GetInt64());
2943 return true;
2944 } else if (o.IsUint64()) {
2945 val = static_cast<int>(o.GetUint64());
jrkooncecba5d6c2019-08-29 11:26:22 -05002946 return true;
2947 }
jrkoonce5cecc412019-08-29 11:45:04 -05002948 }
2949
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002950 return false;
jrkoonce5cecc412019-08-29 11:45:04 -05002951#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002952 auto type = o.type();
jrkooncecba5d6c2019-08-29 11:26:22 -05002953
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002954 if ((type == json::value_t::number_integer) ||
2955 (type == json::value_t::number_unsigned)) {
2956 val = static_cast<int>(o.get<int64_t>());
2957 return true;
jrkoonce5cecc412019-08-29 11:45:04 -05002958 }
2959
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002960 return false;
jrkoonce5cecc412019-08-29 11:45:04 -05002961#endif
jrkooncecba5d6c2019-08-29 11:26:22 -05002962}
2963
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002964#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09002965bool GetDouble(const json &o, double &val) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002966 if (o.IsDouble()) {
2967 val = o.GetDouble();
2968 return true;
2969 }
2970
2971 return false;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002972}
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09002973#endif
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09002974
2975bool GetNumber(const json &o, double &val) {
2976#ifdef TINYGLTF_USE_RAPIDJSON
2977 if (o.IsNumber()) {
2978 val = o.GetDouble();
2979 return true;
2980 }
2981
2982 return false;
2983#else
2984 if (o.is_number()) {
2985 val = o.get<double>();
2986 return true;
2987 }
2988
2989 return false;
2990#endif
2991}
2992
2993bool GetString(const json &o, std::string &val) {
2994#ifdef TINYGLTF_USE_RAPIDJSON
2995 if (o.IsString()) {
2996 val = o.GetString();
2997 return true;
2998 }
2999
3000 return false;
3001#else
3002 if (o.type() == json::value_t::string) {
3003 val = o.get<std::string>();
3004 return true;
3005 }
3006
3007 return false;
3008#endif
3009}
3010
3011bool IsArray(const json &o) {
3012#ifdef TINYGLTF_USE_RAPIDJSON
3013 return o.IsArray();
3014#else
3015 return o.is_array();
3016#endif
3017}
3018
3019json_const_array_iterator ArrayBegin(const json &o) {
3020#ifdef TINYGLTF_USE_RAPIDJSON
3021 return o.Begin();
3022#else
3023 return o.begin();
3024#endif
3025}
3026
3027json_const_array_iterator ArrayEnd(const json &o) {
3028#ifdef TINYGLTF_USE_RAPIDJSON
3029 return o.End();
3030#else
3031 return o.end();
3032#endif
3033}
3034
3035bool IsObject(const json &o) {
3036#ifdef TINYGLTF_USE_RAPIDJSON
3037 return o.IsObject();
3038#else
3039 return o.is_object();
3040#endif
3041}
3042
3043json_const_iterator ObjectBegin(const json &o) {
3044#ifdef TINYGLTF_USE_RAPIDJSON
3045 return o.MemberBegin();
3046#else
3047 return o.begin();
3048#endif
3049}
3050
3051json_const_iterator ObjectEnd(const json &o) {
3052#ifdef TINYGLTF_USE_RAPIDJSON
3053 return o.MemberEnd();
3054#else
3055 return o.end();
3056#endif
3057}
3058
Rahul Sheth01d54382020-07-10 14:27:37 -04003059// Making this a const char* results in a pointer to a temporary when
3060// TINYGLTF_USE_RAPIDJSON is off.
3061std::string GetKey(json_const_iterator &it) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003062#ifdef TINYGLTF_USE_RAPIDJSON
3063 return it->name.GetString();
3064#else
3065 return it.key().c_str();
3066#endif
3067}
3068
3069bool FindMember(const json &o, const char *member, json_const_iterator &it) {
3070#ifdef TINYGLTF_USE_RAPIDJSON
3071 if (!o.IsObject()) {
3072 return false;
3073 }
3074 it = o.FindMember(member);
3075 return it != o.MemberEnd();
3076#else
3077 it = o.find(member);
3078 return it != o.end();
3079#endif
3080}
3081
3082const json &GetValue(json_const_iterator &it) {
3083#ifdef TINYGLTF_USE_RAPIDJSON
3084 return it->value;
3085#else
3086 return it.value();
3087#endif
3088}
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003089
3090std::string JsonToString(const json &o, int spacing = -1) {
3091#ifdef TINYGLTF_USE_RAPIDJSON
3092 using namespace rapidjson;
3093 StringBuffer buffer;
3094 if (spacing == -1) {
3095 Writer<StringBuffer> writer(buffer);
3096 o.Accept(writer);
3097 } else {
3098 PrettyWriter<StringBuffer> writer(buffer);
3099 writer.SetIndent(' ', uint32_t(spacing));
3100 o.Accept(writer);
3101 }
3102 return buffer.GetString();
3103#else
3104 return o.dump(spacing);
3105#endif
3106}
3107
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003108} // namespace
3109
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003110static bool ParseJsonAsValue(Value *ret, const json &o) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003111 Value val{};
jrkooncecba5d6c2019-08-29 11:26:22 -05003112#ifdef TINYGLTF_USE_RAPIDJSON
3113 using rapidjson::Type;
3114 switch (o.GetType()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003115 case Type::kObjectType: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003116 Value::Object value_object;
jrkooncecba5d6c2019-08-29 11:26:22 -05003117 for (auto it = o.MemberBegin(); it != o.MemberEnd(); ++it) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003118 Value entry;
jrkooncecba5d6c2019-08-29 11:26:22 -05003119 ParseJsonAsValue(&entry, it->value);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003120 if (entry.Type() != NULL_TYPE)
3121 value_object.emplace(GetKey(it), std::move(entry));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003122 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003123 if (value_object.size() > 0) val = Value(std::move(value_object));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003124 } break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003125 case Type::kArrayType: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003126 Value::Array value_array;
jrkoonce5cecc412019-08-29 11:45:04 -05003127 value_array.reserve(o.Size());
jrkooncecba5d6c2019-08-29 11:26:22 -05003128 for (auto it = o.Begin(); it != o.End(); ++it) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02003129 Value entry;
jrkooncecba5d6c2019-08-29 11:26:22 -05003130 ParseJsonAsValue(&entry, *it);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003131 if (entry.Type() != NULL_TYPE)
3132 value_array.emplace_back(std::move(entry));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003133 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003134 if (value_array.size() > 0) val = Value(std::move(value_array));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003135 } break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003136 case Type::kStringType:
3137 val = Value(std::string(o.GetString()));
Selmar Kokfa7022f2018-04-04 18:10:20 +02003138 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003139 case Type::kFalseType:
3140 case Type::kTrueType:
3141 val = Value(o.GetBool());
Selmar Kokfa7022f2018-04-04 18:10:20 +02003142 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003143 case Type::kNumberType:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003144 if (!o.IsDouble()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003145 int i = 0;
3146 GetInt(o, i);
3147 val = Value(i);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003148 } else {
jrkooncecba5d6c2019-08-29 11:26:22 -05003149 double d = 0.0;
3150 GetDouble(o, d);
3151 val = Value(d);
3152 }
Selmar Kokfa7022f2018-04-04 18:10:20 +02003153 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003154 case Type::kNullType:
Selmar Kokfa7022f2018-04-04 18:10:20 +02003155 break;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003156 // all types are covered, so no `case default`
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003157 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003158#else
3159 switch (o.type()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003160 case json::value_t::object: {
3161 Value::Object value_object;
3162 for (auto it = o.begin(); it != o.end(); it++) {
3163 Value entry;
3164 ParseJsonAsValue(&entry, it.value());
3165 if (entry.Type() != NULL_TYPE)
3166 value_object.emplace(it.key(), std::move(entry));
3167 }
3168 if (value_object.size() > 0) val = Value(std::move(value_object));
3169 } break;
3170 case json::value_t::array: {
3171 Value::Array value_array;
3172 value_array.reserve(o.size());
3173 for (auto it = o.begin(); it != o.end(); it++) {
3174 Value entry;
3175 ParseJsonAsValue(&entry, it.value());
3176 if (entry.Type() != NULL_TYPE)
3177 value_array.emplace_back(std::move(entry));
3178 }
3179 if (value_array.size() > 0) val = Value(std::move(value_array));
3180 } break;
3181 case json::value_t::string:
3182 val = Value(o.get<std::string>());
3183 break;
3184 case json::value_t::boolean:
3185 val = Value(o.get<bool>());
3186 break;
3187 case json::value_t::number_integer:
3188 case json::value_t::number_unsigned:
3189 val = Value(static_cast<int>(o.get<int64_t>()));
3190 break;
3191 case json::value_t::number_float:
3192 val = Value(o.get<double>());
3193 break;
3194 case json::value_t::null:
3195 case json::value_t::discarded:
Christopher Sean Morrison0bfcb4f2022-02-23 10:02:49 -05003196 case json::value_t::binary:
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003197 // default:
3198 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05003199 }
3200#endif
3201 if (ret) *ret = std::move(val);
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003202
Selmar Kokfa7022f2018-04-04 18:10:20 +02003203 return val.Type() != NULL_TYPE;
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003204}
3205
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003206static bool ParseExtrasProperty(Value *ret, const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003207 json_const_iterator it;
3208 if (!FindMember(o, "extras", it)) {
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003209 return false;
3210 }
3211
jrkooncecba5d6c2019-08-29 11:26:22 -05003212 return ParseJsonAsValue(ret, GetValue(it));
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09003213}
3214
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003215static bool ParseBooleanProperty(bool *ret, std::string *err, const json &o,
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09003216 const std::string &property,
3217 const bool required,
3218 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003219 json_const_iterator it;
3220 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003221 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003222 if (err) {
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09003223 (*err) += "'" + property + "' property is missing";
3224 if (!parent_node.empty()) {
3225 (*err) += " in " + parent_node;
3226 }
3227 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003228 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003229 }
3230 return false;
3231 }
3232
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003233 auto &value = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003234
3235 bool isBoolean;
jrkoonce0d2b6ef2019-09-04 13:46:45 -05003236 bool boolValue = false;
jrkooncecba5d6c2019-08-29 11:26:22 -05003237#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003238 isBoolean = value.IsBool();
3239 if (isBoolean) {
3240 boolValue = value.GetBool();
3241 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003242#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003243 isBoolean = value.is_boolean();
3244 if (isBoolean) {
3245 boolValue = value.get<bool>();
3246 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003247#endif
3248 if (!isBoolean) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003249 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003250 if (err) {
3251 (*err) += "'" + property + "' property is not a bool type.\n";
3252 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003253 }
3254 return false;
3255 }
3256
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003257 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003258 (*ret) = boolValue;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003259 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003260
3261 return true;
3262}
3263
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003264static bool ParseIntegerProperty(int *ret, std::string *err, const json &o,
3265 const std::string &property,
3266 const bool required,
3267 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003268 json_const_iterator it;
3269 if (!FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003270 if (required) {
3271 if (err) {
3272 (*err) += "'" + property + "' property is missing";
3273 if (!parent_node.empty()) {
3274 (*err) += " in " + parent_node;
3275 }
3276 (*err) += ".\n";
3277 }
3278 }
3279 return false;
3280 }
3281
jrkooncecba5d6c2019-08-29 11:26:22 -05003282 int intValue;
3283 bool isInt = GetInt(GetValue(it), intValue);
3284 if (!isInt) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003285 if (required) {
3286 if (err) {
3287 (*err) += "'" + property + "' property is not an integer type.\n";
3288 }
3289 }
3290 return false;
3291 }
3292
3293 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003294 (*ret) = intValue;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003295 }
3296
3297 return true;
3298}
3299
3300static bool ParseUnsignedProperty(size_t *ret, std::string *err, const json &o,
3301 const std::string &property,
3302 const bool required,
3303 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003304 json_const_iterator it;
3305 if (!FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003306 if (required) {
3307 if (err) {
3308 (*err) += "'" + property + "' property is missing";
3309 if (!parent_node.empty()) {
3310 (*err) += " in " + parent_node;
3311 }
3312 (*err) += ".\n";
3313 }
3314 }
3315 return false;
3316 }
3317
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003318 auto &value = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003319
jrkoonce0d2b6ef2019-09-04 13:46:45 -05003320 size_t uValue = 0;
jrkooncecba5d6c2019-08-29 11:26:22 -05003321 bool isUValue;
3322#ifdef TINYGLTF_USE_RAPIDJSON
3323 isUValue = false;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003324 if (value.IsUint()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003325 uValue = value.GetUint();
3326 isUValue = true;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003327 } else if (value.IsUint64()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003328 uValue = value.GetUint64();
3329 isUValue = true;
3330 }
3331#else
3332 isUValue = value.is_number_unsigned();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003333 if (isUValue) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003334 uValue = value.get<size_t>();
3335 }
3336#endif
3337 if (!isUValue) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003338 if (required) {
3339 if (err) {
3340 (*err) += "'" + property + "' property is not a positive integer.\n";
3341 }
3342 }
3343 return false;
3344 }
3345
3346 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003347 (*ret) = uValue;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003348 }
3349
3350 return true;
3351}
3352
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003353static bool ParseNumberProperty(double *ret, std::string *err, const json &o,
Syoyo Fujita5b407452017-06-04 17:42:41 +09003354 const std::string &property,
3355 const bool required,
3356 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003357 json_const_iterator it;
3358
3359 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003360 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003361 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003362 (*err) += "'" + property + "' property is missing";
3363 if (!parent_node.empty()) {
3364 (*err) += " in " + parent_node;
3365 }
3366 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003367 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003368 }
3369 return false;
3370 }
3371
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003372 double numberValue;
3373 bool isNumber = GetNumber(GetValue(it), numberValue);
jrkooncecba5d6c2019-08-29 11:26:22 -05003374
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003375 if (!isNumber) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003376 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003377 if (err) {
3378 (*err) += "'" + property + "' property is not a number type.\n";
3379 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003380 }
3381 return false;
3382 }
3383
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003384 if (ret) {
Syoyo Fujita1100f0f2019-10-30 15:16:34 +09003385 (*ret) = numberValue;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003386 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003387
3388 return true;
3389}
3390
Syoyo Fujitaec39a522016-05-01 18:33:31 +09003391static bool ParseNumberArrayProperty(std::vector<double> *ret, std::string *err,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003392 const json &o, const std::string &property,
3393 bool required,
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003394 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003395 json_const_iterator it;
3396 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003397 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003398 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003399 (*err) += "'" + property + "' property is missing";
3400 if (!parent_node.empty()) {
3401 (*err) += " in " + parent_node;
3402 }
3403 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003404 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003405 }
3406 return false;
3407 }
3408
jrkooncecba5d6c2019-08-29 11:26:22 -05003409 if (!IsArray(GetValue(it))) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003410 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003411 if (err) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003412 (*err) += "'" + property + "' property is not an array";
3413 if (!parent_node.empty()) {
3414 (*err) += " in " + parent_node;
3415 }
3416 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003417 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003418 }
3419 return false;
3420 }
3421
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003422 ret->clear();
jrkooncecba5d6c2019-08-29 11:26:22 -05003423 auto end = ArrayEnd(GetValue(it));
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003424 for (auto i = ArrayBegin(GetValue(it)); i != end; ++i) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003425 double numberValue;
jrkoonce9b6f52e2019-08-29 13:56:58 -05003426 const bool isNumber = GetNumber(*i, numberValue);
jrkooncecba5d6c2019-08-29 11:26:22 -05003427 if (!isNumber) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003428 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003429 if (err) {
3430 (*err) += "'" + property + "' property is not a number.\n";
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09003431 if (!parent_node.empty()) {
3432 (*err) += " in " + parent_node;
3433 }
3434 (*err) += ".\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003435 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003436 }
3437 return false;
3438 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003439 ret->push_back(numberValue);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003440 }
3441
3442 return true;
3443}
3444
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003445static bool ParseIntegerArrayProperty(std::vector<int> *ret, std::string *err,
3446 const json &o,
3447 const std::string &property,
3448 bool required,
3449 const std::string &parent_node = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003450 json_const_iterator it;
3451 if (!FindMember(o, property.c_str(), it)) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003452 if (required) {
3453 if (err) {
3454 (*err) += "'" + property + "' property is missing";
3455 if (!parent_node.empty()) {
3456 (*err) += " in " + parent_node;
3457 }
3458 (*err) += ".\n";
3459 }
3460 }
3461 return false;
3462 }
3463
jrkooncecba5d6c2019-08-29 11:26:22 -05003464 if (!IsArray(GetValue(it))) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003465 if (required) {
3466 if (err) {
3467 (*err) += "'" + property + "' property is not an array";
3468 if (!parent_node.empty()) {
3469 (*err) += " in " + parent_node;
3470 }
3471 (*err) += ".\n";
3472 }
3473 }
3474 return false;
3475 }
3476
3477 ret->clear();
jrkooncecba5d6c2019-08-29 11:26:22 -05003478 auto end = ArrayEnd(GetValue(it));
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003479 for (auto i = ArrayBegin(GetValue(it)); i != end; ++i) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003480 int numberValue;
3481 bool isNumber = GetInt(*i, numberValue);
3482 if (!isNumber) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003483 if (required) {
3484 if (err) {
3485 (*err) += "'" + property + "' property is not an integer type.\n";
3486 if (!parent_node.empty()) {
3487 (*err) += " in " + parent_node;
3488 }
3489 (*err) += ".\n";
3490 }
3491 }
3492 return false;
3493 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003494 ret->push_back(numberValue);
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003495 }
3496
3497 return true;
3498}
3499
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003500static bool ParseStringProperty(
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003501 std::string *ret, std::string *err, const json &o,
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003502 const std::string &property, bool required,
3503 const std::string &parent_node = std::string()) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003504 json_const_iterator it;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003505 if (!FindMember(o, property.c_str(), it)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003506 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003507 if (err) {
Syoyo Fujita0614eb82016-10-14 18:50:14 +09003508 (*err) += "'" + property + "' property is missing";
3509 if (parent_node.empty()) {
3510 (*err) += ".\n";
3511 } else {
3512 (*err) += " in `" + parent_node + "'.\n";
3513 }
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003514 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003515 }
3516 return false;
3517 }
3518
jrkooncecba5d6c2019-08-29 11:26:22 -05003519 std::string strValue;
3520 if (!GetString(GetValue(it), strValue)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003521 if (required) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003522 if (err) {
3523 (*err) += "'" + property + "' property is not a string type.\n";
3524 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003525 }
3526 return false;
3527 }
3528
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003529 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003530 (*ret) = std::move(strValue);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003531 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003532
3533 return true;
3534}
3535
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003536static bool ParseStringIntegerProperty(std::map<std::string, int> *ret,
3537 std::string *err, const json &o,
3538 const std::string &property,
3539 bool required,
3540 const std::string &parent = "") {
jrkooncecba5d6c2019-08-29 11:26:22 -05003541 json_const_iterator it;
3542 if (!FindMember(o, property.c_str(), it)) {
Luke San Antonio19894c72016-06-14 21:19:51 -04003543 if (required) {
3544 if (err) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09003545 if (!parent.empty()) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003546 (*err) +=
3547 "'" + property + "' property is missing in " + parent + ".\n";
Syoyo Fujita57c10182017-10-19 18:48:26 +09003548 } else {
3549 (*err) += "'" + property + "' property is missing.\n";
3550 }
Luke San Antonio19894c72016-06-14 21:19:51 -04003551 }
3552 }
3553 return false;
3554 }
3555
jrkooncecba5d6c2019-08-29 11:26:22 -05003556 const json &dict = GetValue(it);
3557
Luke San Antonio19894c72016-06-14 21:19:51 -04003558 // Make sure we are dealing with an object / dictionary.
jrkooncecba5d6c2019-08-29 11:26:22 -05003559 if (!IsObject(dict)) {
Luke San Antonio19894c72016-06-14 21:19:51 -04003560 if (required) {
3561 if (err) {
3562 (*err) += "'" + property + "' property is not an object.\n";
3563 }
3564 }
3565 return false;
3566 }
3567
3568 ret->clear();
Luke San Antonio19894c72016-06-14 21:19:51 -04003569
jrkooncecba5d6c2019-08-29 11:26:22 -05003570 json_const_iterator dictIt(ObjectBegin(dict));
3571 json_const_iterator dictItEnd(ObjectEnd(dict));
Luke San Antonio19894c72016-06-14 21:19:51 -04003572
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09003573 for (; dictIt != dictItEnd; ++dictIt) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003574 int intVal;
3575 if (!GetInt(GetValue(dictIt), intVal)) {
Syoyo Fujita3cdfb3b2016-06-15 20:58:42 +09003576 if (required) {
3577 if (err) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003578 (*err) += "'" + property + "' value is not an integer type.\n";
Luke San Antonio19894c72016-06-14 21:19:51 -04003579 }
3580 }
3581 return false;
3582 }
3583
3584 // Insert into the list.
jrkooncecba5d6c2019-08-29 11:26:22 -05003585 (*ret)[GetKey(dictIt)] = intVal;
Luke San Antonio19894c72016-06-14 21:19:51 -04003586 }
3587 return true;
3588}
3589
Syoyo Fujita5b407452017-06-04 17:42:41 +09003590static bool ParseJSONProperty(std::map<std::string, double> *ret,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09003591 std::string *err, const json &o,
Syoyo Fujita5b407452017-06-04 17:42:41 +09003592 const std::string &property, bool required) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003593 json_const_iterator it;
3594 if (!FindMember(o, property.c_str(), it)) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003595 if (required) {
Syoyo Fujita5b407452017-06-04 17:42:41 +09003596 if (err) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003597 (*err) += "'" + property + "' property is missing. \n'";
3598 }
3599 }
3600 return false;
3601 }
3602
jrkooncecba5d6c2019-08-29 11:26:22 -05003603 const json &obj = GetValue(it);
3604
3605 if (!IsObject(obj)) {
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003606 if (required) {
3607 if (err) {
3608 (*err) += "'" + property + "' property is not a JSON object.\n";
3609 }
3610 }
3611 return false;
3612 }
3613
3614 ret->clear();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003615
jrkooncecba5d6c2019-08-29 11:26:22 -05003616 json_const_iterator it2(ObjectBegin(obj));
3617 json_const_iterator itEnd(ObjectEnd(obj));
3618 for (; it2 != itEnd; ++it2) {
3619 double numVal;
3620 if (GetNumber(GetValue(it2), numVal))
3621 ret->emplace(std::string(GetKey(it2)), numVal);
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00003622 }
3623
3624 return true;
3625}
3626
Selmar09d2ff12018-03-15 17:30:42 +01003627static bool ParseParameterProperty(Parameter *param, std::string *err,
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003628 const json &o, const std::string &prop,
3629 bool required) {
Selmar09d2ff12018-03-15 17:30:42 +01003630 // A parameter value can either be a string or an array of either a boolean or
3631 // a number. Booleans of any kind aren't supported here. Granted, it
3632 // complicates the Parameter structure and breaks it semantically in the sense
3633 // that the client probably works off the assumption that if the string is
3634 // empty the vector is used, etc. Would a tagged union work?
3635 if (ParseStringProperty(&param->string_value, err, o, prop, false)) {
3636 // Found string property.
3637 return true;
3638 } else if (ParseNumberArrayProperty(&param->number_array, err, o, prop,
3639 false)) {
3640 // Found a number array.
3641 return true;
Ben Buzbeef6af2242018-04-25 15:13:05 -07003642 } else if (ParseNumberProperty(&param->number_value, err, o, prop, false)) {
3643 return param->has_number_value = true;
Selmar09d2ff12018-03-15 17:30:42 +01003644 } else if (ParseJSONProperty(&param->json_double_value, err, o, prop,
3645 false)) {
3646 return true;
3647 } else if (ParseBooleanProperty(&param->bool_value, err, o, prop, false)) {
3648 return true;
3649 } else {
3650 if (required) {
3651 if (err) {
3652 (*err) += "parameter must be a string or number / number array.\n";
3653 }
3654 }
3655 return false;
3656 }
3657}
3658
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003659static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err,
3660 const json &o) {
Syoyo Fujita48f6db02018-04-15 18:40:55 +09003661 (void)err;
3662
jrkooncecba5d6c2019-08-29 11:26:22 -05003663 json_const_iterator it;
3664 if (!FindMember(o, "extensions", it)) {
Selmar09d2ff12018-03-15 17:30:42 +01003665 return false;
3666 }
jrkooncecba5d6c2019-08-29 11:26:22 -05003667
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003668 auto &obj = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05003669 if (!IsObject(obj)) {
Selmar09d2ff12018-03-15 17:30:42 +01003670 return false;
3671 }
3672 ExtensionMap extensions;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003673 json_const_iterator extIt = ObjectBegin(obj); // it.value().begin();
jrkooncecba5d6c2019-08-29 11:26:22 -05003674 json_const_iterator extEnd = ObjectEnd(obj);
3675 for (; extIt != extEnd; ++extIt) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09003676 auto &itObj = GetValue(extIt);
jrkooncecba5d6c2019-08-29 11:26:22 -05003677 if (!IsObject(itObj)) continue;
3678 std::string key(GetKey(extIt));
3679 if (!ParseJsonAsValue(&extensions[key], itObj)) {
jrkoonce06c30c42019-09-03 15:56:48 -05003680 if (!key.empty()) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003681 // create empty object so that an extension object is still of type
3682 // object
jrkooncecba5d6c2019-08-29 11:26:22 -05003683 extensions[key] = Value{Value::Object{}};
Selmar Kokee3d0662018-10-08 16:20:43 +02003684 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003685 }
Selmar09d2ff12018-03-15 17:30:42 +01003686 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003687 if (ret) {
jrkooncecba5d6c2019-08-29 11:26:22 -05003688 (*ret) = std::move(extensions);
Selmar09d2ff12018-03-15 17:30:42 +01003689 }
3690 return true;
3691}
3692
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003693static bool ParseAsset(Asset *asset, std::string *err, const json &o,
3694 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09003695 ParseStringProperty(&asset->version, err, o, "version", true, "Asset");
3696 ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset");
3697 ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset");
Miguel Sousa22bfc842020-02-20 14:16:58 +01003698 ParseStringProperty(&asset->copyright, err, o, "copyright", false, "Asset");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003699
Selmar09d2ff12018-03-15 17:30:42 +01003700 ParseExtensionsProperty(&asset->extensions, err, o);
3701
Aurélien Chatelaind48bbdc2017-05-24 09:54:18 +00003702 // Unity exporter version is added as extra here
3703 ParseExtrasProperty(&(asset->extras), o);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003704
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003705 if (store_original_json_for_extras_and_extensions) {
3706 {
3707 json_const_iterator it;
3708 if (FindMember(o, "extensions", it)) {
3709 asset->extensions_json_string = JsonToString(GetValue(it));
3710 }
3711 }
3712 {
3713 json_const_iterator it;
3714 if (FindMember(o, "extras", it)) {
3715 asset->extras_json_string = JsonToString(GetValue(it));
3716 }
3717 }
3718 }
3719
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003720 return true;
3721}
3722
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003723static bool ParseImage(Image *image, const int image_idx, std::string *err,
3724 std::string *warn, const json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003725 bool store_original_json_for_extras_and_extensions,
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003726 const std::string &basedir, FsCallbacks *fs,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003727 LoadImageDataFunction *LoadImageData = nullptr,
Paolo Jovone6601bf2018-07-07 20:43:33 +02003728 void *load_image_user_data = nullptr) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003729 // A glTF image must either reference a bufferView or an image uri
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003730
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003731 // schema says oneOf [`bufferView`, `uri`]
3732 // TODO(syoyo): Check the type of each parameters.
jrkooncecba5d6c2019-08-29 11:26:22 -05003733 json_const_iterator it;
3734 bool hasBufferView = FindMember(o, "bufferView", it);
3735 bool hasURI = FindMember(o, "uri", it);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003736
Syoyo Fujitaaf3ebb22019-01-06 18:55:57 +09003737 ParseStringProperty(&image->name, err, o, "name", false);
3738
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003739 if (hasBufferView && hasURI) {
3740 // Should not both defined.
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003741 if (err) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09003742 (*err) +=
3743 "Only one of `bufferView` or `uri` should be defined, but both are "
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003744 "defined for image[" +
3745 std::to_string(image_idx) + "] name = \"" + image->name + "\"\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003746 }
3747 return false;
3748 }
3749
3750 if (!hasBufferView && !hasURI) {
3751 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003752 (*err) += "Neither required `bufferView` nor `uri` defined for image[" +
3753 std::to_string(image_idx) + "] name = \"" + image->name +
3754 "\"\n";
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00003755 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003756 return false;
3757 }
3758
Syoyo Fujita3e53feb2018-09-02 15:36:17 +09003759 ParseExtensionsProperty(&image->extensions, err, o);
Selmar Kok8eb39042018-10-05 14:29:35 +02003760 ParseExtrasProperty(&image->extras, o);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003761
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003762 if (store_original_json_for_extras_and_extensions) {
3763 {
3764 json_const_iterator eit;
3765 if (FindMember(o, "extensions", eit)) {
3766 image->extensions_json_string = JsonToString(GetValue(eit));
3767 }
3768 }
3769 {
3770 json_const_iterator eit;
3771 if (FindMember(o, "extras", eit)) {
3772 image->extras_json_string = JsonToString(GetValue(eit));
3773 }
3774 }
3775 }
3776
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003777 if (hasBufferView) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003778 int bufferView = -1;
3779 if (!ParseIntegerProperty(&bufferView, err, o, "bufferView", true)) {
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003780 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003781 (*err) += "Failed to parse `bufferView` for image[" +
3782 std::to_string(image_idx) + "] name = \"" + image->name +
3783 "\"\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003784 }
3785 return false;
3786 }
3787
3788 std::string mime_type;
3789 ParseStringProperty(&mime_type, err, o, "mimeType", false);
3790
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003791 int width = 0;
3792 ParseIntegerProperty(&width, err, o, "width", false);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003793
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003794 int height = 0;
3795 ParseIntegerProperty(&height, err, o, "height", false);
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003796
3797 // Just only save some information here. Loading actual image data from
3798 // bufferView is done after this `ParseImage` function.
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003799 image->bufferView = bufferView;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003800 image->mimeType = mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003801 image->width = width;
3802 image->height = height;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003803
3804 return true;
3805 }
3806
Syoyo Fujita246654a2018-03-21 20:32:22 +09003807 // Parse URI & Load image data.
3808
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003809 std::string uri;
3810 std::string tmp_err;
Syoyo Fujita246654a2018-03-21 20:32:22 +09003811 if (!ParseStringProperty(&uri, &tmp_err, o, "uri", true)) {
3812 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003813 (*err) += "Failed to parse `uri` for image[" + std::to_string(image_idx) +
3814 "] name = \"" + image->name + "\".\n";
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003815 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003816 return false;
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003817 }
3818
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003819 std::vector<unsigned char> img;
Syoyo Fujitabeded612016-05-01 20:03:43 +09003820
Syoyo Fujita246654a2018-03-21 20:32:22 +09003821 if (IsDataURI(uri)) {
johan bowald642a3432018-04-01 12:37:18 +02003822 if (!DecodeDataURI(&img, image->mimeType, uri, 0, false)) {
Syoyo Fujita246654a2018-03-21 20:32:22 +09003823 if (err) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003824 (*err) += "Failed to decode 'uri' for image[" +
3825 std::to_string(image_idx) + "] name = [" + image->name +
3826 "]\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003827 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003828 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003829 }
3830 } else {
Syoyo Fujita246654a2018-03-21 20:32:22 +09003831 // Assume external file
3832 // Keep texture path (for textures that cannot be decoded)
3833 image->uri = uri;
Selmar67af3c92018-03-16 11:48:19 +01003834#ifdef TINYGLTF_NO_EXTERNAL_IMAGE
Syoyo Fujita246654a2018-03-21 20:32:22 +09003835 return true;
Selmar67af3c92018-03-16 11:48:19 +01003836#endif
Syoyo Fujitac4166e42020-01-08 02:38:01 +09003837 std::string decoded_uri = dlib::urldecode(uri);
3838 if (!LoadExternalFile(&img, err, warn, decoded_uri, basedir,
3839 /* required */ false, /* required bytes */ 0,
3840 /* checksize */ false, fs)) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003841 if (warn) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003842 (*warn) += "Failed to load external 'uri' for image[" +
3843 std::to_string(image_idx) + "] name = [" + image->name +
3844 "]\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003845 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003846 // If the image cannot be loaded, keep uri as image->uri.
3847 return true;
3848 }
Syoyo Fujita8e2c24f2018-03-21 19:54:38 +09003849
Syoyo Fujita246654a2018-03-21 20:32:22 +09003850 if (img.empty()) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09003851 if (warn) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00003852 (*warn) += "Image data is empty for image[" +
3853 std::to_string(image_idx) + "] name = [" + image->name +
3854 "] \n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09003855 }
Syoyo Fujita246654a2018-03-21 20:32:22 +09003856 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003857 }
3858 }
3859
Squareysff644d82018-03-13 22:36:18 +01003860 if (*LoadImageData == nullptr) {
3861 if (err) {
3862 (*err) += "No LoadImageData callback specified.\n";
3863 }
3864 return false;
3865 }
Syoyo Fujitaaf3ebb22019-01-06 18:55:57 +09003866 return (*LoadImageData)(image, image_idx, err, warn, 0, 0, &img.at(0),
Paolo Jovone6601bf2018-07-07 20:43:33 +02003867 static_cast<int>(img.size()), load_image_user_data);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09003868}
3869
Syoyo Fujitad42767e2018-03-15 21:52:00 -05003870static bool ParseTexture(Texture *texture, std::string *err, const json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003871 bool store_original_json_for_extras_and_extensions,
Syoyo Fujitabeded612016-05-01 20:03:43 +09003872 const std::string &basedir) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003873 (void)basedir;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003874 int sampler = -1;
3875 int source = -1;
3876 ParseIntegerProperty(&sampler, err, o, "sampler", false);
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09003877
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003878 ParseIntegerProperty(&source, err, o, "source", false);
Syoyo Fujitabde70212016-02-07 17:38:17 +09003879
Jeff McGlynn19b806e2019-04-26 14:49:38 -07003880 texture->sampler = sampler;
3881 texture->source = source;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07003882
Selmar Kokfa7022f2018-04-04 18:10:20 +02003883 ParseExtensionsProperty(&texture->extensions, err, o);
3884 ParseExtrasProperty(&texture->extras, o);
Syoyo Fujitabde70212016-02-07 17:38:17 +09003885
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003886 if (store_original_json_for_extras_and_extensions) {
3887 {
3888 json_const_iterator it;
3889 if (FindMember(o, "extensions", it)) {
3890 texture->extensions_json_string = JsonToString(GetValue(it));
3891 }
3892 }
3893 {
3894 json_const_iterator it;
3895 if (FindMember(o, "extras", it)) {
3896 texture->extras_json_string = JsonToString(GetValue(it));
3897 }
3898 }
3899 }
3900
Vladimír Vondruš239be2c2018-07-24 23:23:56 +02003901 ParseStringProperty(&texture->name, err, o, "name", false);
3902
Syoyo Fujitabde70212016-02-07 17:38:17 +09003903 return true;
3904}
3905
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003906static bool ParseTextureInfo(
3907 TextureInfo *texinfo, std::string *err, const json &o,
3908 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09003909 if (texinfo == nullptr) {
3910 return false;
3911 }
3912
3913 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
3914 /* required */ true, "TextureInfo")) {
3915 return false;
3916 }
3917
3918 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
3919
3920 ParseExtensionsProperty(&texinfo->extensions, err, o);
3921 ParseExtrasProperty(&texinfo->extras, o);
3922
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003923 if (store_original_json_for_extras_and_extensions) {
3924 {
3925 json_const_iterator it;
3926 if (FindMember(o, "extensions", it)) {
3927 texinfo->extensions_json_string = JsonToString(GetValue(it));
3928 }
3929 }
3930 {
3931 json_const_iterator it;
3932 if (FindMember(o, "extras", it)) {
3933 texinfo->extras_json_string = JsonToString(GetValue(it));
3934 }
3935 }
3936 }
3937
Syoyo Fujita046400b2019-07-24 19:26:48 +09003938 return true;
3939}
3940
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003941static bool ParseNormalTextureInfo(
3942 NormalTextureInfo *texinfo, std::string *err, const json &o,
3943 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09003944 if (texinfo == nullptr) {
3945 return false;
3946 }
3947
3948 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
3949 /* required */ true, "NormalTextureInfo")) {
3950 return false;
3951 }
3952
3953 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
3954 ParseNumberProperty(&texinfo->scale, err, o, "scale", false);
3955
3956 ParseExtensionsProperty(&texinfo->extensions, err, o);
3957 ParseExtrasProperty(&texinfo->extras, o);
3958
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003959 if (store_original_json_for_extras_and_extensions) {
3960 {
3961 json_const_iterator it;
3962 if (FindMember(o, "extensions", it)) {
3963 texinfo->extensions_json_string = JsonToString(GetValue(it));
3964 }
3965 }
3966 {
3967 json_const_iterator it;
3968 if (FindMember(o, "extras", it)) {
3969 texinfo->extras_json_string = JsonToString(GetValue(it));
3970 }
3971 }
3972 }
3973
Syoyo Fujita046400b2019-07-24 19:26:48 +09003974 return true;
3975}
3976
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003977static bool ParseOcclusionTextureInfo(
3978 OcclusionTextureInfo *texinfo, std::string *err, const json &o,
3979 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09003980 if (texinfo == nullptr) {
3981 return false;
3982 }
3983
3984 if (!ParseIntegerProperty(&texinfo->index, err, o, "index",
3985 /* required */ true, "NormalTextureInfo")) {
3986 return false;
3987 }
3988
3989 ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false);
3990 ParseNumberProperty(&texinfo->strength, err, o, "strength", false);
3991
3992 ParseExtensionsProperty(&texinfo->extensions, err, o);
3993 ParseExtrasProperty(&texinfo->extras, o);
3994
Syoyo Fujita6e08b172019-10-30 17:25:38 +09003995 if (store_original_json_for_extras_and_extensions) {
3996 {
3997 json_const_iterator it;
3998 if (FindMember(o, "extensions", it)) {
3999 texinfo->extensions_json_string = JsonToString(GetValue(it));
4000 }
4001 }
4002 {
4003 json_const_iterator it;
4004 if (FindMember(o, "extras", it)) {
4005 texinfo->extras_json_string = JsonToString(GetValue(it));
4006 }
4007 }
4008 }
4009
Syoyo Fujita046400b2019-07-24 19:26:48 +09004010 return true;
4011}
4012
Syoyo Fujitad42767e2018-03-15 21:52:00 -05004013static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004014 bool store_original_json_for_extras_and_extensions,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004015 FsCallbacks *fs, const std::string &basedir,
4016 bool is_binary = false,
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09004017 const unsigned char *bin_data = nullptr,
Syoyo Fujitabeded612016-05-01 20:03:43 +09004018 size_t bin_size = 0) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004019 size_t byteLength;
4020 if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true,
4021 "Buffer")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004022 return false;
4023 }
4024
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004025 // In glTF 2.0, uri is not mandatory anymore
Syoyo Fujita20244e12018-03-15 11:01:05 -05004026 buffer->uri.clear();
4027 ParseStringProperty(&buffer->uri, err, o, "uri", false, "Buffer");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004028
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004029 // having an empty uri for a non embedded image should not be valid
Syoyo Fujita20244e12018-03-15 11:01:05 -05004030 if (!is_binary && buffer->uri.empty()) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004031 if (err) {
4032 (*err) += "'uri' is missing from non binary glTF file buffer.\n";
4033 }
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004034 }
4035
jrkooncecba5d6c2019-08-29 11:26:22 -05004036 json_const_iterator type;
4037 if (FindMember(o, "type", type)) {
4038 std::string typeStr;
4039 if (GetString(GetValue(type), typeStr)) {
4040 if (typeStr.compare("arraybuffer") == 0) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004041 // buffer.type = "arraybuffer";
4042 }
4043 }
4044 }
4045
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004046 if (is_binary) {
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004047 // Still binary glTF accepts external dataURI.
Syoyo Fujita20244e12018-03-15 11:01:05 -05004048 if (!buffer->uri.empty()) {
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004049 // First try embedded data URI.
4050 if (IsDataURI(buffer->uri)) {
4051 std::string mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004052 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004053 true)) {
4054 if (err) {
4055 (*err) +=
4056 "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
4057 }
4058 return false;
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004059 }
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09004060 } else {
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004061 // External .bin file.
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004062 std::string decoded_uri = dlib::urldecode(buffer->uri);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09004063 if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr,
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004064 decoded_uri, basedir, /* required */ true,
4065 byteLength, /* checkSize */ true, fs)) {
Vladimír Vondrušfd84ceb2018-08-16 21:07:56 +02004066 return false;
4067 }
Syoyo Fujita39abfb52018-07-11 02:46:52 +09004068 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004069 } else {
Syoyo Fujitabeded612016-05-01 20:03:43 +09004070 // load data from (embedded) binary data
4071
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09004072 if ((bin_size == 0) || (bin_data == nullptr)) {
Syoyo Fujitabeded612016-05-01 20:03:43 +09004073 if (err) {
Syoyo Fujita40c4a0a2017-05-30 01:21:36 +09004074 (*err) += "Invalid binary data in `Buffer'.\n";
Syoyo Fujitabeded612016-05-01 20:03:43 +09004075 }
4076 return false;
4077 }
4078
4079 if (byteLength > bin_size) {
4080 if (err) {
4081 std::stringstream ss;
4082 ss << "Invalid `byteLength'. Must be equal or less than binary size: "
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004083 "`byteLength' = "
4084 << byteLength << ", binary size = " << bin_size << std::endl;
Syoyo Fujitabeded612016-05-01 20:03:43 +09004085 (*err) += ss.str();
4086 }
4087 return false;
4088 }
4089
Aurélien Chatelain0aab1572017-05-30 15:03:48 +00004090 // Read buffer data
4091 buffer->data.resize(static_cast<size_t>(byteLength));
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09004092 memcpy(&(buffer->data.at(0)), bin_data, static_cast<size_t>(byteLength));
Syoyo Fujitabeded612016-05-01 20:03:43 +09004093 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004094
4095 } else {
Syoyo Fujita20244e12018-03-15 11:01:05 -05004096 if (IsDataURI(buffer->uri)) {
johan bowaldef151a42018-04-01 13:44:48 +02004097 std::string mime_type;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004098 if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength,
4099 true)) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004100 if (err) {
Syoyo Fujita20244e12018-03-15 11:01:05 -05004101 (*err) += "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n";
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004102 }
4103 return false;
4104 }
4105 } else {
4106 // Assume external .bin file.
Syoyo Fujitac4166e42020-01-08 02:38:01 +09004107 std::string decoded_uri = dlib::urldecode(buffer->uri);
4108 if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, decoded_uri,
4109 basedir, /* required */ true, byteLength,
4110 /* checkSize */ true, fs)) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09004111 return false;
4112 }
4113 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004114 }
4115
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004116 ParseStringProperty(&buffer->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004117
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004118 ParseExtensionsProperty(&buffer->extensions, err, o);
4119 ParseExtrasProperty(&buffer->extras, o);
4120
4121 if (store_original_json_for_extras_and_extensions) {
4122 {
4123 json_const_iterator it;
4124 if (FindMember(o, "extensions", it)) {
4125 buffer->extensions_json_string = JsonToString(GetValue(it));
4126 }
4127 }
4128 {
4129 json_const_iterator it;
4130 if (FindMember(o, "extras", it)) {
4131 buffer->extras_json_string = JsonToString(GetValue(it));
4132 }
4133 }
4134 }
4135
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004136 return true;
4137}
4138
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004139static bool ParseBufferView(
4140 BufferView *bufferView, std::string *err, const json &o,
4141 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004142 int buffer = -1;
4143 if (!ParseIntegerProperty(&buffer, err, o, "buffer", true, "BufferView")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004144 return false;
4145 }
4146
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004147 size_t byteOffset = 0;
4148 ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004149
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004150 size_t byteLength = 1;
4151 if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true,
4152 "BufferView")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004153 return false;
4154 }
4155
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004156 size_t byteStride = 0;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004157 if (!ParseUnsignedProperty(&byteStride, err, o, "byteStride", false)) {
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004158 // Spec says: When byteStride of referenced bufferView is not defined, it
4159 // means that accessor elements are tightly packed, i.e., effective stride
4160 // equals the size of the element.
4161 // We cannot determine the actual byteStride until Accessor are parsed, thus
4162 // set 0(= tightly packed) here(as done in OpenGL's VertexAttribPoiner)
4163 byteStride = 0;
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004164 }
4165
4166 if ((byteStride > 252) || ((byteStride % 4) != 0)) {
4167 if (err) {
4168 std::stringstream ss;
4169 ss << "Invalid `byteStride' value. `byteStride' must be the multiple of "
4170 "4 : "
4171 << byteStride << std::endl;
4172
4173 (*err) += ss.str();
4174 }
4175 return false;
4176 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004177
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004178 int target = 0;
4179 ParseIntegerProperty(&target, err, o, "target", false);
4180 if ((target == TINYGLTF_TARGET_ARRAY_BUFFER) ||
4181 (target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004182 // OK
4183 } else {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004184 target = 0;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004185 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004186 bufferView->target = target;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004187
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004188 ParseStringProperty(&bufferView->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004189
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004190 ParseExtensionsProperty(&bufferView->extensions, err, o);
4191 ParseExtrasProperty(&bufferView->extras, o);
4192
4193 if (store_original_json_for_extras_and_extensions) {
4194 {
4195 json_const_iterator it;
4196 if (FindMember(o, "extensions", it)) {
4197 bufferView->extensions_json_string = JsonToString(GetValue(it));
4198 }
4199 }
4200 {
4201 json_const_iterator it;
4202 if (FindMember(o, "extras", it)) {
4203 bufferView->extras_json_string = JsonToString(GetValue(it));
4204 }
4205 }
4206 }
4207
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004208 bufferView->buffer = buffer;
4209 bufferView->byteOffset = byteOffset;
4210 bufferView->byteLength = byteLength;
4211 bufferView->byteStride = byteStride;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004212 return true;
4213}
4214
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004215static bool ParseSparseAccessor(Accessor *accessor, std::string *err,
4216 const json &o) {
4217 accessor->sparse.isSparse = true;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004218
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004219 int count = 0;
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004220 if (!ParseIntegerProperty(&count, err, o, "count", true, "SparseAccessor")) {
4221 return false;
4222 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004223
jrkooncecba5d6c2019-08-29 11:26:22 -05004224 json_const_iterator indices_iterator;
4225 json_const_iterator values_iterator;
4226 if (!FindMember(o, "indices", indices_iterator)) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004227 (*err) = "the sparse object of this accessor doesn't have indices";
4228 return false;
4229 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004230
jrkooncecba5d6c2019-08-29 11:26:22 -05004231 if (!FindMember(o, "values", values_iterator)) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004232 (*err) = "the sparse object ob ths accessor doesn't have values";
4233 return false;
4234 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004235
jrkooncecba5d6c2019-08-29 11:26:22 -05004236 const json &indices_obj = GetValue(indices_iterator);
4237 const json &values_obj = GetValue(values_iterator);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004238
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004239 int indices_buffer_view = 0, indices_byte_offset = 0, component_type = 0;
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004240 if (!ParseIntegerProperty(&indices_buffer_view, err, indices_obj, "bufferView",
4241 true, "SparseAccessor")) {
4242 return false;
4243 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004244 ParseIntegerProperty(&indices_byte_offset, err, indices_obj, "byteOffset",
Jack Mousseau94715172022-02-24 14:25:37 -08004245 false);
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004246 if (!ParseIntegerProperty(&component_type, err, indices_obj, "componentType",
4247 true, "SparseAccessor")) {
4248 return false;
4249 }
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004250
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004251 int values_buffer_view = 0, values_byte_offset = 0;
Syoyo Fujitacbd38852022-02-26 21:19:15 +09004252 if (!ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView",
4253 true, "SparseAccessor")) {
4254 return false;
4255 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004256 ParseIntegerProperty(&values_byte_offset, err, values_obj, "byteOffset",
Jack Mousseau94715172022-02-24 14:25:37 -08004257 false);
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004258
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004259 accessor->sparse.count = count;
4260 accessor->sparse.indices.bufferView = indices_buffer_view;
4261 accessor->sparse.indices.byteOffset = indices_byte_offset;
4262 accessor->sparse.indices.componentType = component_type;
4263 accessor->sparse.values.bufferView = values_buffer_view;
4264 accessor->sparse.values.byteOffset = values_byte_offset;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004265
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004266 return true;
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004267}
4268
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004269static bool ParseAccessor(Accessor *accessor, std::string *err, const json &o,
4270 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004271 int bufferView = -1;
4272 ParseIntegerProperty(&bufferView, err, o, "bufferView", false, "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004273
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004274 size_t byteOffset = 0;
4275 ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false, "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004276
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004277 bool normalized = false;
4278 ParseBooleanProperty(&normalized, err, o, "normalized", false, "Accessor");
4279
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004280 size_t componentType = 0;
4281 if (!ParseUnsignedProperty(&componentType, err, o, "componentType", true,
4282 "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004283 return false;
4284 }
4285
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004286 size_t count = 0;
4287 if (!ParseUnsignedProperty(&count, err, o, "count", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004288 return false;
4289 }
4290
4291 std::string type;
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09004292 if (!ParseStringProperty(&type, err, o, "type", true, "Accessor")) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004293 return false;
4294 }
4295
4296 if (type.compare("SCALAR") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004297 accessor->type = TINYGLTF_TYPE_SCALAR;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004298 } else if (type.compare("VEC2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004299 accessor->type = TINYGLTF_TYPE_VEC2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004300 } else if (type.compare("VEC3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004301 accessor->type = TINYGLTF_TYPE_VEC3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004302 } else if (type.compare("VEC4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004303 accessor->type = TINYGLTF_TYPE_VEC4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004304 } else if (type.compare("MAT2") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004305 accessor->type = TINYGLTF_TYPE_MAT2;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004306 } else if (type.compare("MAT3") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004307 accessor->type = TINYGLTF_TYPE_MAT3;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004308 } else if (type.compare("MAT4") == 0) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004309 accessor->type = TINYGLTF_TYPE_MAT4;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004310 } else {
4311 std::stringstream ss;
4312 ss << "Unsupported `type` for accessor object. Got \"" << type << "\"\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004313 if (err) {
4314 (*err) += ss.str();
4315 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004316 return false;
4317 }
4318
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004319 ParseStringProperty(&accessor->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004320
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004321 accessor->minValues.clear();
4322 accessor->maxValues.clear();
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004323 ParseNumberArrayProperty(&accessor->minValues, err, o, "min", false,
4324 "Accessor");
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004325
Syoyo Fujitad9d012a2017-07-12 18:29:29 +09004326 ParseNumberArrayProperty(&accessor->maxValues, err, o, "max", false,
4327 "Accessor");
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004328
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004329 accessor->count = count;
4330 accessor->bufferView = bufferView;
4331 accessor->byteOffset = byteOffset;
jianghaosen4748ad62017-08-29 20:08:37 +08004332 accessor->normalized = normalized;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004333 {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004334 if (componentType >= TINYGLTF_COMPONENT_TYPE_BYTE &&
4335 componentType <= TINYGLTF_COMPONENT_TYPE_DOUBLE) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004336 // OK
Arthur Brainville (Ybalrid)811e1d32019-06-15 07:32:38 +02004337 accessor->componentType = int(componentType);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004338 } else {
4339 std::stringstream ss;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004340 ss << "Invalid `componentType` in accessor. Got " << componentType
4341 << "\n";
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004342 if (err) {
4343 (*err) += ss.str();
4344 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004345 return false;
4346 }
4347 }
4348
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004349 ParseExtensionsProperty(&(accessor->extensions), err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004350 ParseExtrasProperty(&(accessor->extras), o);
4351
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004352 if (store_original_json_for_extras_and_extensions) {
4353 {
4354 json_const_iterator it;
4355 if (FindMember(o, "extensions", it)) {
4356 accessor->extensions_json_string = JsonToString(GetValue(it));
4357 }
4358 }
4359 {
4360 json_const_iterator it;
4361 if (FindMember(o, "extras", it)) {
4362 accessor->extras_json_string = JsonToString(GetValue(it));
4363 }
4364 }
4365 }
4366
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004367 // check if accessor has a "sparse" object:
jrkooncecba5d6c2019-08-29 11:26:22 -05004368 json_const_iterator iterator;
4369 if (FindMember(o, "sparse", iterator)) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004370 // here this accessor has a "sparse" subobject
jrkooncecba5d6c2019-08-29 11:26:22 -05004371 return ParseSparseAccessor(accessor, err, GetValue(iterator));
Arthur Brainville (Ybalrid)7e9f7342019-03-06 12:27:23 +00004372 }
4373
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004374 return true;
4375}
4376
Alex Wood7319db72019-01-24 15:38:16 -05004377#ifdef TINYGLTF_ENABLE_DRACO
4378
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004379static void DecodeIndexBuffer(draco::Mesh *mesh, size_t componentSize,
4380 std::vector<uint8_t> &outBuffer) {
4381 if (componentSize == 4) {
Alex Wood7319db72019-01-24 15:38:16 -05004382 assert(sizeof(mesh->face(draco::FaceIndex(0))[0]) == componentSize);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004383 memcpy(outBuffer.data(), &mesh->face(draco::FaceIndex(0))[0],
4384 outBuffer.size());
4385 } else {
Alex Wood7319db72019-01-24 15:38:16 -05004386 size_t faceStride = componentSize * 3;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004387 for (draco::FaceIndex f(0); f < mesh->num_faces(); ++f) {
4388 const draco::Mesh::Face &face = mesh->face(f);
4389 if (componentSize == 2) {
4390 uint16_t indices[3] = {(uint16_t)face[0].value(),
4391 (uint16_t)face[1].value(),
4392 (uint16_t)face[2].value()};
4393 memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
4394 faceStride);
4395 } else {
4396 uint8_t indices[3] = {(uint8_t)face[0].value(),
4397 (uint8_t)face[1].value(),
4398 (uint8_t)face[2].value()};
4399 memcpy(outBuffer.data() + f.value() * faceStride, &indices[0],
4400 faceStride);
Alex Wood7319db72019-01-24 15:38:16 -05004401 }
4402 }
4403 }
4404}
4405
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004406template <typename T>
4407static bool GetAttributeForAllPoints(draco::Mesh *mesh,
4408 const draco::PointAttribute *pAttribute,
4409 std::vector<uint8_t> &outBuffer) {
Alex Wood7319db72019-01-24 15:38:16 -05004410 size_t byteOffset = 0;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004411 T values[4] = {0, 0, 0, 0};
4412 for (draco::PointIndex i(0); i < mesh->num_points(); ++i) {
Alex Wood7319db72019-01-24 15:38:16 -05004413 const draco::AttributeValueIndex val_index = pAttribute->mapped_index(i);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004414 if (!pAttribute->ConvertValue<T>(val_index, pAttribute->num_components(),
4415 values))
Alex Wood7319db72019-01-24 15:38:16 -05004416 return false;
4417
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004418 memcpy(outBuffer.data() + byteOffset, &values[0],
4419 sizeof(T) * pAttribute->num_components());
Alex Wood7319db72019-01-24 15:38:16 -05004420 byteOffset += sizeof(T) * pAttribute->num_components();
4421 }
4422
4423 return true;
4424}
4425
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004426static bool GetAttributeForAllPoints(uint32_t componentType, draco::Mesh *mesh,
4427 const draco::PointAttribute *pAttribute,
4428 std::vector<uint8_t> &outBuffer) {
Alex Wood7319db72019-01-24 15:38:16 -05004429 bool decodeResult = false;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004430 switch (componentType) {
4431 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE:
4432 decodeResult =
4433 GetAttributeForAllPoints<uint8_t>(mesh, pAttribute, outBuffer);
4434 break;
4435 case TINYGLTF_COMPONENT_TYPE_BYTE:
4436 decodeResult =
4437 GetAttributeForAllPoints<int8_t>(mesh, pAttribute, outBuffer);
4438 break;
4439 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT:
4440 decodeResult =
4441 GetAttributeForAllPoints<uint16_t>(mesh, pAttribute, outBuffer);
4442 break;
4443 case TINYGLTF_COMPONENT_TYPE_SHORT:
4444 decodeResult =
4445 GetAttributeForAllPoints<int16_t>(mesh, pAttribute, outBuffer);
4446 break;
4447 case TINYGLTF_COMPONENT_TYPE_INT:
4448 decodeResult =
4449 GetAttributeForAllPoints<int32_t>(mesh, pAttribute, outBuffer);
4450 break;
4451 case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT:
4452 decodeResult =
4453 GetAttributeForAllPoints<uint32_t>(mesh, pAttribute, outBuffer);
4454 break;
4455 case TINYGLTF_COMPONENT_TYPE_FLOAT:
4456 decodeResult =
4457 GetAttributeForAllPoints<float>(mesh, pAttribute, outBuffer);
4458 break;
4459 case TINYGLTF_COMPONENT_TYPE_DOUBLE:
4460 decodeResult =
4461 GetAttributeForAllPoints<double>(mesh, pAttribute, outBuffer);
4462 break;
4463 default:
4464 return false;
Alex Wood7319db72019-01-24 15:38:16 -05004465 }
4466
4467 return decodeResult;
4468}
4469
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004470static bool ParseDracoExtension(Primitive *primitive, Model *model,
4471 std::string *err,
4472 const Value &dracoExtensionValue) {
snowapril84e15262021-03-20 19:25:58 +09004473 (void)err;
Alex Wood7319db72019-01-24 15:38:16 -05004474 auto bufferViewValue = dracoExtensionValue.Get("bufferView");
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004475 if (!bufferViewValue.IsInt()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004476 auto attributesValue = dracoExtensionValue.Get("attributes");
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004477 if (!attributesValue.IsObject()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004478
4479 auto attributesObject = attributesValue.Get<Value::Object>();
4480 int bufferView = bufferViewValue.Get<int>();
4481
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004482 BufferView &view = model->bufferViews[bufferView];
4483 Buffer &buffer = model->buffers[view.buffer];
Alex Wood7319db72019-01-24 15:38:16 -05004484 // BufferView has already been decoded
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004485 if (view.dracoDecoded) return true;
Alex Wood7319db72019-01-24 15:38:16 -05004486 view.dracoDecoded = true;
4487
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004488 const char *bufferViewData =
4489 reinterpret_cast<const char *>(buffer.data.data() + view.byteOffset);
Alex Wood7319db72019-01-24 15:38:16 -05004490 size_t bufferViewSize = view.byteLength;
4491
4492 // decode draco
4493 draco::DecoderBuffer decoderBuffer;
4494 decoderBuffer.Init(bufferViewData, bufferViewSize);
4495 draco::Decoder decoder;
4496 auto decodeResult = decoder.DecodeMeshFromBuffer(&decoderBuffer);
4497 if (!decodeResult.ok()) {
4498 return false;
4499 }
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004500 const std::unique_ptr<draco::Mesh> &mesh = decodeResult.value();
Alex Wood7319db72019-01-24 15:38:16 -05004501
4502 // create new bufferView for indices
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004503 if (primitive->indices >= 0) {
4504 int32_t componentSize = GetComponentSizeInBytes(
4505 model->accessors[primitive->indices].componentType);
Alex Wood7319db72019-01-24 15:38:16 -05004506 Buffer decodedIndexBuffer;
4507 decodedIndexBuffer.data.resize(mesh->num_faces() * 3 * componentSize);
4508
4509 DecodeIndexBuffer(mesh.get(), componentSize, decodedIndexBuffer.data);
4510
4511 model->buffers.emplace_back(std::move(decodedIndexBuffer));
4512
4513 BufferView decodedIndexBufferView;
4514 decodedIndexBufferView.buffer = int(model->buffers.size() - 1);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004515 decodedIndexBufferView.byteLength =
4516 int(mesh->num_faces() * 3 * componentSize);
Alex Wood7319db72019-01-24 15:38:16 -05004517 decodedIndexBufferView.byteOffset = 0;
4518 decodedIndexBufferView.byteStride = 0;
4519 decodedIndexBufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER;
4520 model->bufferViews.emplace_back(std::move(decodedIndexBufferView));
4521
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004522 model->accessors[primitive->indices].bufferView =
4523 int(model->bufferViews.size() - 1);
Alexander Wood0d77a292019-01-26 08:58:45 -05004524 model->accessors[primitive->indices].count = int(mesh->num_faces() * 3);
Alex Wood7319db72019-01-24 15:38:16 -05004525 }
4526
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004527 for (const auto &attribute : attributesObject) {
4528 if (!attribute.second.IsInt()) return false;
Alexander Wood0d77a292019-01-26 08:58:45 -05004529 auto primitiveAttribute = primitive->attributes.find(attribute.first);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004530 if (primitiveAttribute == primitive->attributes.end()) return false;
Alex Wood7319db72019-01-24 15:38:16 -05004531
4532 int dracoAttributeIndex = attribute.second.Get<int>();
4533 const auto pAttribute = mesh->GetAttributeByUniqueId(dracoAttributeIndex);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004534 const auto componentType =
4535 model->accessors[primitiveAttribute->second].componentType;
Alex Wood7319db72019-01-24 15:38:16 -05004536
4537 // Create a new buffer for this decoded buffer
4538 Buffer decodedBuffer;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004539 size_t bufferSize = mesh->num_points() * pAttribute->num_components() *
4540 GetComponentSizeInBytes(componentType);
Alex Wood7319db72019-01-24 15:38:16 -05004541 decodedBuffer.data.resize(bufferSize);
4542
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004543 if (!GetAttributeForAllPoints(componentType, mesh.get(), pAttribute,
4544 decodedBuffer.data))
Alex Wood7319db72019-01-24 15:38:16 -05004545 return false;
4546
4547 model->buffers.emplace_back(std::move(decodedBuffer));
4548
4549 BufferView decodedBufferView;
4550 decodedBufferView.buffer = int(model->buffers.size() - 1);
4551 decodedBufferView.byteLength = bufferSize;
4552 decodedBufferView.byteOffset = pAttribute->byte_offset();
4553 decodedBufferView.byteStride = pAttribute->byte_stride();
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004554 decodedBufferView.target = primitive->indices >= 0
4555 ? TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER
4556 : TINYGLTF_TARGET_ARRAY_BUFFER;
Alex Wood7319db72019-01-24 15:38:16 -05004557 model->bufferViews.emplace_back(std::move(decodedBufferView));
4558
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004559 model->accessors[primitiveAttribute->second].bufferView =
4560 int(model->bufferViews.size() - 1);
4561 model->accessors[primitiveAttribute->second].count =
4562 int(mesh->num_points());
Alex Wood7319db72019-01-24 15:38:16 -05004563 }
4564
4565 return true;
4566}
4567#endif
4568
4569static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004570 const json &o,
4571 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004572 int material = -1;
4573 ParseIntegerProperty(&material, err, o, "material", false);
4574 primitive->material = material;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004575
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004576 int mode = TINYGLTF_MODE_TRIANGLES;
4577 ParseIntegerProperty(&mode, err, o, "mode", false);
4578 primitive->mode = mode; // Why only triangled were supported ?
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004579
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004580 int indices = -1;
4581 ParseIntegerProperty(&indices, err, o, "indices", false);
4582 primitive->indices = indices;
4583 if (!ParseStringIntegerProperty(&primitive->attributes, err, o, "attributes",
4584 true, "Primitive")) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004585 return false;
4586 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004587
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004588 // Look for morph targets
jrkooncecba5d6c2019-08-29 11:26:22 -05004589 json_const_iterator targetsObject;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004590 if (FindMember(o, "targets", targetsObject) &&
4591 IsArray(GetValue(targetsObject))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004592 auto targetsObjectEnd = ArrayEnd(GetValue(targetsObject));
4593 for (json_const_array_iterator i = ArrayBegin(GetValue(targetsObject));
4594 i != targetsObjectEnd; ++i) {
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004595 std::map<std::string, int> targetAttribues;
4596
jrkooncecba5d6c2019-08-29 11:26:22 -05004597 const json &dict = *i;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004598 if (IsObject(dict)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004599 json_const_iterator dictIt(ObjectBegin(dict));
4600 json_const_iterator dictItEnd(ObjectEnd(dict));
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004601
jrkooncecba5d6c2019-08-29 11:26:22 -05004602 for (; dictIt != dictItEnd; ++dictIt) {
4603 int iVal;
4604 if (GetInt(GetValue(dictIt), iVal))
4605 targetAttribues[GetKey(dictIt)] = iVal;
4606 }
4607 primitive->targets.emplace_back(std::move(targetAttribues));
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004608 }
Aurélien Chatelain2682c6f2017-06-14 09:46:57 +00004609 }
4610 }
4611
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004612 ParseExtrasProperty(&(primitive->extras), o);
Alex Wood7319db72019-01-24 15:38:16 -05004613 ParseExtensionsProperty(&primitive->extensions, err, o);
4614
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004615 if (store_original_json_for_extras_and_extensions) {
4616 {
4617 json_const_iterator it;
4618 if (FindMember(o, "extensions", it)) {
4619 primitive->extensions_json_string = JsonToString(GetValue(it));
4620 }
4621 }
4622 {
4623 json_const_iterator it;
4624 if (FindMember(o, "extras", it)) {
4625 primitive->extras_json_string = JsonToString(GetValue(it));
4626 }
4627 }
4628 }
4629
Alex Wood7319db72019-01-24 15:38:16 -05004630#ifdef TINYGLTF_ENABLE_DRACO
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00004631 auto dracoExtension =
4632 primitive->extensions.find("KHR_draco_mesh_compression");
4633 if (dracoExtension != primitive->extensions.end()) {
4634 ParseDracoExtension(primitive, model, err, dracoExtension->second);
Alex Wood7319db72019-01-24 15:38:16 -05004635 }
Syoyo Fujita7bdfed32019-03-03 17:04:49 +09004636#else
4637 (void)model;
Alex Wood7319db72019-01-24 15:38:16 -05004638#endif
4639
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004640 return true;
4641}
4642
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004643static bool ParseMesh(Mesh *mesh, Model *model, std::string *err, const json &o,
4644 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004645 ParseStringProperty(&mesh->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004646
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004647 mesh->primitives.clear();
jrkooncecba5d6c2019-08-29 11:26:22 -05004648 json_const_iterator primObject;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004649 if (FindMember(o, "primitives", primObject) &&
4650 IsArray(GetValue(primObject))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004651 json_const_array_iterator primEnd = ArrayEnd(GetValue(primObject));
4652 for (json_const_array_iterator i = ArrayBegin(GetValue(primObject));
4653 i != primEnd; ++i) {
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004654 Primitive primitive;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004655 if (ParsePrimitive(&primitive, model, err, *i,
4656 store_original_json_for_extras_and_extensions)) {
Luke San Antonio10a3e1d2016-06-15 00:05:01 -04004657 // Only add the primitive if the parsing succeeds.
jrkoonced1e14722019-08-27 11:51:02 -05004658 mesh->primitives.emplace_back(std::move(primitive));
Luke San Antonio10a3e1d2016-06-15 00:05:01 -04004659 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004660 }
4661 }
4662
Aurélien Chatelain38a6a752017-05-24 09:54:55 +00004663 // Should probably check if has targets and if dimensions fit
4664 ParseNumberArrayProperty(&mesh->weights, err, o, "weights", false);
4665
Selmar09d2ff12018-03-15 17:30:42 +01004666 ParseExtensionsProperty(&mesh->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004667 ParseExtrasProperty(&(mesh->extras), o);
4668
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004669 if (store_original_json_for_extras_and_extensions) {
4670 {
4671 json_const_iterator it;
4672 if (FindMember(o, "extensions", it)) {
4673 mesh->extensions_json_string = JsonToString(GetValue(it));
4674 }
4675 }
4676 {
4677 json_const_iterator it;
4678 if (FindMember(o, "extras", it)) {
4679 mesh->extras_json_string = JsonToString(GetValue(it));
4680 }
4681 }
4682 }
4683
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004684 return true;
4685}
4686
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004687static bool ParseNode(Node *node, std::string *err, const json &o,
4688 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004689 ParseStringProperty(&node->name, err, o, "name", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004690
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004691 int skin = -1;
4692 ParseIntegerProperty(&skin, err, o, "skin", false);
4693 node->skin = skin;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00004694
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004695 // Matrix and T/R/S are exclusive
Syoyo Fujita5b407452017-06-04 17:42:41 +09004696 if (!ParseNumberArrayProperty(&node->matrix, err, o, "matrix", false)) {
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004697 ParseNumberArrayProperty(&node->rotation, err, o, "rotation", false);
4698 ParseNumberArrayProperty(&node->scale, err, o, "scale", false);
4699 ParseNumberArrayProperty(&node->translation, err, o, "translation", false);
4700 }
4701
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004702 int camera = -1;
4703 ParseIntegerProperty(&camera, err, o, "camera", false);
4704 node->camera = camera;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004705
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004706 int mesh = -1;
4707 ParseIntegerProperty(&mesh, err, o, "mesh", false);
4708 node->mesh = mesh;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004709
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09004710 node->children.clear();
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004711 ParseIntegerArrayProperty(&node->children, err, o, "children", false);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004712
Benjamin Schmithüsenad63bf72019-08-16 14:19:27 +02004713 ParseNumberArrayProperty(&node->weights, err, o, "weights", false);
4714
Selmar09d2ff12018-03-15 17:30:42 +01004715 ParseExtensionsProperty(&node->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004716 ParseExtrasProperty(&(node->extras), o);
4717
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004718 if (store_original_json_for_extras_and_extensions) {
4719 {
4720 json_const_iterator it;
4721 if (FindMember(o, "extensions", it)) {
4722 node->extensions_json_string = JsonToString(GetValue(it));
4723 }
4724 }
4725 {
4726 json_const_iterator it;
4727 if (FindMember(o, "extras", it)) {
4728 node->extras_json_string = JsonToString(GetValue(it));
4729 }
4730 }
4731 }
4732
Emanuel Schrade186322b2017-11-06 11:14:41 +01004733 return true;
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004734}
4735
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004736static bool ParsePbrMetallicRoughness(
4737 PbrMetallicRoughness *pbr, std::string *err, const json &o,
4738 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004739 if (pbr == nullptr) {
4740 return false;
4741 }
4742
4743 std::vector<double> baseColorFactor;
4744 if (ParseNumberArrayProperty(&baseColorFactor, err, o, "baseColorFactor",
4745 /* required */ false)) {
4746 if (baseColorFactor.size() != 4) {
4747 if (err) {
4748 (*err) +=
4749 "Array length of `baseColorFactor` parameter in "
4750 "pbrMetallicRoughness must be 4, but got " +
4751 std::to_string(baseColorFactor.size()) + "\n";
4752 }
4753 return false;
4754 }
Selmar Kokc3353e12019-10-18 18:22:35 +02004755 pbr->baseColorFactor = baseColorFactor;
Syoyo Fujita046400b2019-07-24 19:26:48 +09004756 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09004757
4758 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004759 json_const_iterator it;
4760 if (FindMember(o, "baseColorTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004761 ParseTextureInfo(&pbr->baseColorTexture, err, GetValue(it),
4762 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004763 }
4764 }
4765
4766 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004767 json_const_iterator it;
4768 if (FindMember(o, "metallicRoughnessTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004769 ParseTextureInfo(&pbr->metallicRoughnessTexture, err, GetValue(it),
4770 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004771 }
4772 }
4773
4774 ParseNumberProperty(&pbr->metallicFactor, err, o, "metallicFactor", false);
4775 ParseNumberProperty(&pbr->roughnessFactor, err, o, "roughnessFactor", false);
4776
4777 ParseExtensionsProperty(&pbr->extensions, err, o);
4778 ParseExtrasProperty(&pbr->extras, o);
4779
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004780 if (store_original_json_for_extras_and_extensions) {
4781 {
4782 json_const_iterator it;
4783 if (FindMember(o, "extensions", it)) {
4784 pbr->extensions_json_string = JsonToString(GetValue(it));
4785 }
4786 }
4787 {
4788 json_const_iterator it;
4789 if (FindMember(o, "extras", it)) {
4790 pbr->extras_json_string = JsonToString(GetValue(it));
4791 }
4792 }
4793 }
4794
Syoyo Fujita046400b2019-07-24 19:26:48 +09004795 return true;
4796}
4797
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004798static bool ParseMaterial(Material *material, std::string *err, const json &o,
4799 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004800 ParseStringProperty(&material->name, err, o, "name", /* required */ false);
4801
Syoyo Fujitaff515702019-08-24 16:29:14 +09004802 if (ParseNumberArrayProperty(&material->emissiveFactor, err, o,
4803 "emissiveFactor",
4804 /* required */ false)) {
Selmar Kok6df800d2019-08-19 11:05:28 +02004805 if (material->emissiveFactor.size() != 3) {
4806 if (err) {
4807 (*err) +=
4808 "Array length of `emissiveFactor` parameter in "
4809 "material must be 3, but got " +
4810 std::to_string(material->emissiveFactor.size()) + "\n";
4811 }
4812 return false;
4813 }
Syoyo Fujitaff515702019-08-24 16:29:14 +09004814 } else {
Selmar Kok6df800d2019-08-19 11:05:28 +02004815 // fill with default values
Selmar Kok6dba6c62019-08-19 11:23:31 +02004816 material->emissiveFactor = {0.0, 0.0, 0.0};
Selmar Kok6df800d2019-08-19 11:05:28 +02004817 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09004818
4819 ParseStringProperty(&material->alphaMode, err, o, "alphaMode",
4820 /* required */ false);
4821 ParseNumberProperty(&material->alphaCutoff, err, o, "alphaCutoff",
4822 /* required */ false);
4823 ParseBooleanProperty(&material->doubleSided, err, o, "doubleSided",
4824 /* required */ false);
4825
4826 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004827 json_const_iterator it;
4828 if (FindMember(o, "pbrMetallicRoughness", it)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09004829 ParsePbrMetallicRoughness(&material->pbrMetallicRoughness, err,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004830 GetValue(it),
4831 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004832 }
4833 }
4834
4835 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004836 json_const_iterator it;
4837 if (FindMember(o, "normalTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004838 ParseNormalTextureInfo(&material->normalTexture, err, GetValue(it),
4839 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004840 }
4841 }
4842
4843 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004844 json_const_iterator it;
4845 if (FindMember(o, "occlusionTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004846 ParseOcclusionTextureInfo(&material->occlusionTexture, err, GetValue(it),
4847 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004848 }
4849 }
4850
4851 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004852 json_const_iterator it;
4853 if (FindMember(o, "emissiveTexture", it)) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004854 ParseTextureInfo(&material->emissiveTexture, err, GetValue(it),
4855 store_original_json_for_extras_and_extensions);
Syoyo Fujita046400b2019-07-24 19:26:48 +09004856 }
4857 }
4858
4859 // Old code path. For backward compatibility, we still store material values
4860 // as Parameter. This will create duplicated information for
4861 // example(pbrMetallicRoughness), but should be neglible in terms of memory
4862 // consumption.
4863 // TODO(syoyo): Remove in the next major release.
4864 material->values.clear();
4865 material->additionalValues.clear();
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004866
jrkooncecba5d6c2019-08-29 11:26:22 -05004867 json_const_iterator it(ObjectBegin(o));
4868 json_const_iterator itEnd(ObjectEnd(o));
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004869
jrkooncecba5d6c2019-08-29 11:26:22 -05004870 for (; it != itEnd; ++it) {
4871 std::string key(GetKey(it));
jrkoonce06c30c42019-09-03 15:56:48 -05004872 if (key == "pbrMetallicRoughness") {
4873 if (IsObject(GetValue(it))) {
4874 const json &values_object = GetValue(it);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004875
jrkoonce06c30c42019-09-03 15:56:48 -05004876 json_const_iterator itVal(ObjectBegin(values_object));
4877 json_const_iterator itValEnd(ObjectEnd(values_object));
4878
4879 for (; itVal != itValEnd; ++itVal) {
4880 Parameter param;
4881 if (ParseParameterProperty(&param, err, values_object, GetKey(itVal),
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004882 false)) {
jrkoonce06c30c42019-09-03 15:56:48 -05004883 material->values.emplace(GetKey(itVal), std::move(param));
4884 }
4885 }
4886 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004887 } else if (key == "extensions" || key == "extras") {
4888 // done later, skip, otherwise poorly parsed contents will be saved in the
4889 // parametermap and serialized again later
4890 } else {
jrkoonce06c30c42019-09-03 15:56:48 -05004891 Parameter param;
4892 if (ParseParameterProperty(&param, err, o, key, false)) {
4893 // names of materials have already been parsed. Putting it in this map
4894 // doesn't correctly reflext the glTF specification
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004895 if (key != "name")
4896 material->additionalValues.emplace(std::move(key), std::move(param));
jrkoonce06c30c42019-09-03 15:56:48 -05004897 }
Aurélien Chatelainab7b2182017-05-24 09:53:44 +00004898 }
Syoyo Fujita5b407452017-06-04 17:42:41 +09004899 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09004900
Syoyo Fujita046400b2019-07-24 19:26:48 +09004901 material->extensions.clear();
Selmar09d2ff12018-03-15 17:30:42 +01004902 ParseExtensionsProperty(&material->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004903 ParseExtrasProperty(&(material->extras), o);
4904
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004905 if (store_original_json_for_extras_and_extensions) {
4906 {
4907 json_const_iterator eit;
4908 if (FindMember(o, "extensions", eit)) {
4909 material->extensions_json_string = JsonToString(GetValue(eit));
4910 }
4911 }
4912 {
4913 json_const_iterator eit;
4914 if (FindMember(o, "extras", eit)) {
4915 material->extras_json_string = JsonToString(GetValue(eit));
4916 }
4917 }
4918 }
4919
Luke San Antoniocdf4cb72016-06-14 21:32:11 -04004920 return true;
4921}
4922
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004923static bool ParseAnimationChannel(
4924 AnimationChannel *channel, std::string *err, const json &o,
4925 bool store_original_json_for_extras_and_extensions) {
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004926 int samplerIndex = -1;
4927 int targetIndex = -1;
4928 if (!ParseIntegerProperty(&samplerIndex, err, o, "sampler", true,
4929 "AnimationChannel")) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004930 if (err) {
4931 (*err) += "`sampler` field is missing in animation channels\n";
4932 }
4933 return false;
4934 }
4935
jrkooncecba5d6c2019-08-29 11:26:22 -05004936 json_const_iterator targetIt;
4937 if (FindMember(o, "target", targetIt) && IsObject(GetValue(targetIt))) {
4938 const json &target_object = GetValue(targetIt);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004939
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004940 if (!ParseIntegerProperty(&targetIndex, err, target_object, "node", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004941 if (err) {
Syoyo Fujita57c10182017-10-19 18:48:26 +09004942 (*err) += "`node` field is missing in animation.channels.target\n";
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004943 }
4944 return false;
4945 }
4946
4947 if (!ParseStringProperty(&channel->target_path, err, target_object, "path",
4948 true)) {
4949 if (err) {
4950 (*err) += "`path` field is missing in animation.channels.target\n";
4951 }
4952 return false;
4953 }
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09004954 ParseExtensionsProperty(&channel->target_extensions, err, target_object);
4955 if (store_original_json_for_extras_and_extensions) {
Selmar Kok973d9b32020-01-21 18:45:24 +01004956 json_const_iterator it;
4957 if (FindMember(target_object, "extensions", it)) {
4958 channel->target_extensions_json_string = JsonToString(GetValue(it));
4959 }
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09004960 }
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004961 }
4962
Jeff McGlynn19b806e2019-04-26 14:49:38 -07004963 channel->sampler = samplerIndex;
4964 channel->target_node = targetIndex;
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00004965
Selmar Kok4e2988e2019-08-16 14:08:08 +02004966 ParseExtensionsProperty(&channel->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09004967 ParseExtrasProperty(&(channel->extras), o);
4968
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004969 if (store_original_json_for_extras_and_extensions) {
4970 {
4971 json_const_iterator it;
4972 if (FindMember(o, "extensions", it)) {
4973 channel->extensions_json_string = JsonToString(GetValue(it));
4974 }
4975 }
4976 {
4977 json_const_iterator it;
4978 if (FindMember(o, "extras", it)) {
4979 channel->extras_json_string = JsonToString(GetValue(it));
4980 }
4981 }
4982 }
4983
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004984 return true;
4985}
4986
4987static bool ParseAnimation(Animation *animation, std::string *err,
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004988 const json &o,
4989 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004990 {
jrkooncecba5d6c2019-08-29 11:26:22 -05004991 json_const_iterator channelsIt;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09004992 if (FindMember(o, "channels", channelsIt) &&
4993 IsArray(GetValue(channelsIt))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05004994 json_const_array_iterator channelEnd = ArrayEnd(GetValue(channelsIt));
4995 for (json_const_array_iterator i = ArrayBegin(GetValue(channelsIt));
4996 i != channelEnd; ++i) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09004997 AnimationChannel channel;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09004998 if (ParseAnimationChannel(
4999 &channel, err, *i,
5000 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005001 // Only add the channel if the parsing succeeds.
jrkooncecba5d6c2019-08-29 11:26:22 -05005002 animation->channels.emplace_back(std::move(channel));
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005003 }
5004 }
5005 }
5006 }
5007
5008 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005009 json_const_iterator samplerIt;
5010 if (FindMember(o, "samplers", samplerIt) && IsArray(GetValue(samplerIt))) {
5011 const json &sampler_array = GetValue(samplerIt);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005012
jrkooncecba5d6c2019-08-29 11:26:22 -05005013 json_const_array_iterator it = ArrayBegin(sampler_array);
5014 json_const_array_iterator itEnd = ArrayEnd(sampler_array);
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005015
jrkooncecba5d6c2019-08-29 11:26:22 -05005016 for (; it != itEnd; ++it) {
5017 const json &s = *it;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005018
5019 AnimationSampler sampler;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005020 int inputIndex = -1;
5021 int outputIndex = -1;
5022 if (!ParseIntegerProperty(&inputIndex, err, s, "input", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005023 if (err) {
5024 (*err) += "`input` field is missing in animation.sampler\n";
5025 }
5026 return false;
5027 }
Evan Birenbaum6bdffed2019-02-14 13:30:57 -08005028 ParseStringProperty(&sampler.interpolation, err, s, "interpolation",
5029 false);
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005030 if (!ParseIntegerProperty(&outputIndex, err, s, "output", true)) {
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005031 if (err) {
5032 (*err) += "`output` field is missing in animation.sampler\n";
5033 }
5034 return false;
5035 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005036 sampler.input = inputIndex;
5037 sampler.output = outputIndex;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005038 ParseExtensionsProperty(&(sampler.extensions), err, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02005039 ParseExtrasProperty(&(sampler.extras), s);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005040
5041 if (store_original_json_for_extras_and_extensions) {
5042 {
5043 json_const_iterator eit;
5044 if (FindMember(o, "extensions", eit)) {
5045 sampler.extensions_json_string = JsonToString(GetValue(eit));
5046 }
5047 }
5048 {
5049 json_const_iterator eit;
5050 if (FindMember(o, "extras", eit)) {
5051 sampler.extras_json_string = JsonToString(GetValue(eit));
5052 }
5053 }
5054 }
5055
jrkooncecba5d6c2019-08-29 11:26:22 -05005056 animation->samplers.emplace_back(std::move(sampler));
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005057 }
5058 }
5059 }
5060
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005061 ParseStringProperty(&animation->name, err, o, "name", false);
5062
Selmar Kok4e2988e2019-08-16 14:08:08 +02005063 ParseExtensionsProperty(&animation->extensions, err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09005064 ParseExtrasProperty(&(animation->extras), o);
5065
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005066 if (store_original_json_for_extras_and_extensions) {
5067 {
5068 json_const_iterator it;
5069 if (FindMember(o, "extensions", it)) {
5070 animation->extensions_json_string = JsonToString(GetValue(it));
5071 }
5072 }
5073 {
5074 json_const_iterator it;
5075 if (FindMember(o, "extras", it)) {
5076 animation->extras_json_string = JsonToString(GetValue(it));
5077 }
5078 }
5079 }
5080
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005081 return true;
5082}
5083
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005084static bool ParseSampler(Sampler *sampler, std::string *err, const json &o,
5085 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujitac2615632016-06-19 21:56:06 +09005086 ParseStringProperty(&sampler->name, err, o, "name", false);
5087
Syoyo Fujitaee179b22019-08-16 13:11:30 +09005088 int minFilter = -1;
5089 int magFilter = -1;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005090 int wrapS = TINYGLTF_TEXTURE_WRAP_REPEAT;
5091 int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT;
Syoyo Fujita2c521b32020-12-04 00:50:46 +09005092 //int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT;
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005093 ParseIntegerProperty(&minFilter, err, o, "minFilter", false);
5094 ParseIntegerProperty(&magFilter, err, o, "magFilter", false);
5095 ParseIntegerProperty(&wrapS, err, o, "wrapS", false);
5096 ParseIntegerProperty(&wrapT, err, o, "wrapT", false);
Syoyo Fujita2c521b32020-12-04 00:50:46 +09005097 //ParseIntegerProperty(&wrapR, err, o, "wrapR", false); // tinygltf extension
Syoyo Fujitaee179b22019-08-16 13:11:30 +09005098
5099 // TODO(syoyo): Check the value is alloed one.
5100 // (e.g. we allow 9728(NEAREST), but don't allow 9727)
Syoyo Fujitac2615632016-06-19 21:56:06 +09005101
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005102 sampler->minFilter = minFilter;
5103 sampler->magFilter = magFilter;
5104 sampler->wrapS = wrapS;
5105 sampler->wrapT = wrapT;
Syoyo Fujita2c521b32020-12-04 00:50:46 +09005106 //sampler->wrapR = wrapR;
Syoyo Fujitac2615632016-06-19 21:56:06 +09005107
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005108 ParseExtensionsProperty(&(sampler->extensions), err, o);
Syoyo Fujitaf696c6f2016-10-17 03:48:55 +09005109 ParseExtrasProperty(&(sampler->extras), o);
5110
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005111 if (store_original_json_for_extras_and_extensions) {
5112 {
5113 json_const_iterator it;
5114 if (FindMember(o, "extensions", it)) {
5115 sampler->extensions_json_string = JsonToString(GetValue(it));
5116 }
5117 }
5118 {
5119 json_const_iterator it;
5120 if (FindMember(o, "extras", it)) {
5121 sampler->extras_json_string = JsonToString(GetValue(it));
5122 }
5123 }
5124 }
5125
Syoyo Fujitac2615632016-06-19 21:56:06 +09005126 return true;
5127}
5128
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005129static bool ParseSkin(Skin *skin, std::string *err, const json &o,
5130 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita90e1ed22017-05-30 00:47:37 +09005131 ParseStringProperty(&skin->name, err, o, "name", false, "Skin");
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005132
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005133 std::vector<int> joints;
5134 if (!ParseIntegerArrayProperty(&joints, err, o, "joints", false, "Skin")) {
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005135 return false;
5136 }
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005137 skin->joints = std::move(joints);
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005138
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005139 int skeleton = -1;
5140 ParseIntegerProperty(&skeleton, err, o, "skeleton", false, "Skin");
5141 skin->skeleton = skeleton;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005142
Jeff McGlynn19b806e2019-04-26 14:49:38 -07005143 int invBind = -1;
5144 ParseIntegerProperty(&invBind, err, o, "inverseBindMatrices", true, "Skin");
5145 skin->inverseBindMatrices = invBind;
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005146
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005147 ParseExtensionsProperty(&(skin->extensions), err, o);
5148 ParseExtrasProperty(&(skin->extras), o);
5149
5150 if (store_original_json_for_extras_and_extensions) {
5151 {
5152 json_const_iterator it;
5153 if (FindMember(o, "extensions", it)) {
5154 skin->extensions_json_string = JsonToString(GetValue(it));
5155 }
5156 }
5157 {
5158 json_const_iterator it;
5159 if (FindMember(o, "extras", it)) {
5160 skin->extras_json_string = JsonToString(GetValue(it));
5161 }
5162 }
5163 }
5164
Aurélien Chatelain8cb98952017-05-24 12:55:26 +00005165 return true;
5166}
5167
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005168static bool ParsePerspectiveCamera(
5169 PerspectiveCamera *camera, std::string *err, const json &o,
5170 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005171 double yfov = 0.0;
5172 if (!ParseNumberProperty(&yfov, err, o, "yfov", true, "OrthographicCamera")) {
5173 return false;
5174 }
5175
5176 double znear = 0.0;
5177 if (!ParseNumberProperty(&znear, err, o, "znear", true,
5178 "PerspectiveCamera")) {
5179 return false;
5180 }
5181
5182 double aspectRatio = 0.0; // = invalid
5183 ParseNumberProperty(&aspectRatio, err, o, "aspectRatio", false,
5184 "PerspectiveCamera");
5185
5186 double zfar = 0.0; // = invalid
5187 ParseNumberProperty(&zfar, err, o, "zfar", false, "PerspectiveCamera");
5188
Selmar Kok31cb7f92018-10-03 15:39:05 +02005189 camera->aspectRatio = aspectRatio;
5190 camera->zfar = zfar;
5191 camera->yfov = yfov;
5192 camera->znear = znear;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005193
Selmar09d2ff12018-03-15 17:30:42 +01005194 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005195 ParseExtrasProperty(&(camera->extras), o);
5196
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005197 if (store_original_json_for_extras_and_extensions) {
5198 {
5199 json_const_iterator it;
5200 if (FindMember(o, "extensions", it)) {
5201 camera->extensions_json_string = JsonToString(GetValue(it));
5202 }
5203 }
5204 {
5205 json_const_iterator it;
5206 if (FindMember(o, "extras", it)) {
5207 camera->extras_json_string = JsonToString(GetValue(it));
5208 }
5209 }
5210 }
5211
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005212 // TODO(syoyo): Validate parameter values.
5213
5214 return true;
5215}
5216
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005217static bool ParseSpotLight(SpotLight *light, std::string *err, const json &o,
5218 bool store_original_json_for_extras_and_extensions) {
Johan Bowald52936a02019-07-17 09:06:45 +02005219 ParseNumberProperty(&light->innerConeAngle, err, o, "innerConeAngle", false);
5220 ParseNumberProperty(&light->outerConeAngle, err, o, "outerConeAngle", false);
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005221
Johan Bowald52936a02019-07-17 09:06:45 +02005222 ParseExtensionsProperty(&light->extensions, err, o);
5223 ParseExtrasProperty(&light->extras, o);
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005224
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005225 if (store_original_json_for_extras_and_extensions) {
5226 {
5227 json_const_iterator it;
5228 if (FindMember(o, "extensions", it)) {
5229 light->extensions_json_string = JsonToString(GetValue(it));
5230 }
5231 }
5232 {
5233 json_const_iterator it;
5234 if (FindMember(o, "extras", it)) {
5235 light->extras_json_string = JsonToString(GetValue(it));
5236 }
5237 }
5238 }
5239
Johan Bowald52936a02019-07-17 09:06:45 +02005240 // TODO(syoyo): Validate parameter values.
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005241
Johan Bowald52936a02019-07-17 09:06:45 +02005242 return true;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005243}
5244
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005245static bool ParseOrthographicCamera(
5246 OrthographicCamera *camera, std::string *err, const json &o,
5247 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005248 double xmag = 0.0;
5249 if (!ParseNumberProperty(&xmag, err, o, "xmag", true, "OrthographicCamera")) {
5250 return false;
5251 }
5252
5253 double ymag = 0.0;
5254 if (!ParseNumberProperty(&ymag, err, o, "ymag", true, "OrthographicCamera")) {
5255 return false;
5256 }
5257
5258 double zfar = 0.0;
5259 if (!ParseNumberProperty(&zfar, err, o, "zfar", true, "OrthographicCamera")) {
5260 return false;
5261 }
5262
5263 double znear = 0.0;
5264 if (!ParseNumberProperty(&znear, err, o, "znear", true,
5265 "OrthographicCamera")) {
5266 return false;
5267 }
5268
Selmar09d2ff12018-03-15 17:30:42 +01005269 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005270 ParseExtrasProperty(&(camera->extras), o);
5271
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005272 if (store_original_json_for_extras_and_extensions) {
5273 {
5274 json_const_iterator it;
5275 if (FindMember(o, "extensions", it)) {
5276 camera->extensions_json_string = JsonToString(GetValue(it));
5277 }
5278 }
5279 {
5280 json_const_iterator it;
5281 if (FindMember(o, "extras", it)) {
5282 camera->extras_json_string = JsonToString(GetValue(it));
5283 }
5284 }
5285 }
5286
Selmar Kok31cb7f92018-10-03 15:39:05 +02005287 camera->xmag = xmag;
5288 camera->ymag = ymag;
5289 camera->zfar = zfar;
5290 camera->znear = znear;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005291
5292 // TODO(syoyo): Validate parameter values.
5293
5294 return true;
5295}
5296
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005297static bool ParseCamera(Camera *camera, std::string *err, const json &o,
5298 bool store_original_json_for_extras_and_extensions) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005299 if (!ParseStringProperty(&camera->type, err, o, "type", true, "Camera")) {
5300 return false;
5301 }
5302
5303 if (camera->type.compare("orthographic") == 0) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005304 json_const_iterator orthoIt;
5305 if (!FindMember(o, "orthographic", orthoIt)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005306 if (err) {
5307 std::stringstream ss;
5308 ss << "Orhographic camera description not found." << std::endl;
5309 (*err) += ss.str();
5310 }
5311 return false;
5312 }
5313
jrkooncecba5d6c2019-08-29 11:26:22 -05005314 const json &v = GetValue(orthoIt);
5315 if (!IsObject(v)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005316 if (err) {
5317 std::stringstream ss;
5318 ss << "\"orthographic\" is not a JSON object." << std::endl;
5319 (*err) += ss.str();
5320 }
5321 return false;
5322 }
5323
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005324 if (!ParseOrthographicCamera(
5325 &camera->orthographic, err, v,
5326 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005327 return false;
5328 }
5329 } else if (camera->type.compare("perspective") == 0) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005330 json_const_iterator perspIt;
5331 if (!FindMember(o, "perspective", perspIt)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005332 if (err) {
5333 std::stringstream ss;
5334 ss << "Perspective camera description not found." << std::endl;
5335 (*err) += ss.str();
5336 }
5337 return false;
5338 }
5339
jrkooncecba5d6c2019-08-29 11:26:22 -05005340 const json &v = GetValue(perspIt);
5341 if (!IsObject(v)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005342 if (err) {
5343 std::stringstream ss;
5344 ss << "\"perspective\" is not a JSON object." << std::endl;
5345 (*err) += ss.str();
5346 }
5347 return false;
5348 }
5349
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005350 if (!ParsePerspectiveCamera(
5351 &camera->perspective, err, v,
5352 store_original_json_for_extras_and_extensions)) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005353 return false;
5354 }
5355 } else {
5356 if (err) {
5357 std::stringstream ss;
5358 ss << "Invalid camera type: \"" << camera->type
5359 << "\". Must be \"perspective\" or \"orthographic\"" << std::endl;
5360 (*err) += ss.str();
5361 }
5362 return false;
5363 }
5364
5365 ParseStringProperty(&camera->name, err, o, "name", false);
5366
Selmar09d2ff12018-03-15 17:30:42 +01005367 ParseExtensionsProperty(&camera->extensions, err, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005368 ParseExtrasProperty(&(camera->extras), o);
5369
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005370 if (store_original_json_for_extras_and_extensions) {
5371 {
5372 json_const_iterator it;
5373 if (FindMember(o, "extensions", it)) {
5374 camera->extensions_json_string = JsonToString(GetValue(it));
5375 }
5376 }
5377 {
5378 json_const_iterator it;
5379 if (FindMember(o, "extras", it)) {
5380 camera->extras_json_string = JsonToString(GetValue(it));
5381 }
5382 }
5383 }
5384
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005385 return true;
5386}
5387
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005388static bool ParseLight(Light *light, std::string *err, const json &o,
5389 bool store_original_json_for_extras_and_extensions) {
Johan Bowald52936a02019-07-17 09:06:45 +02005390 if (!ParseStringProperty(&light->type, err, o, "type", true)) {
5391 return false;
5392 }
5393
5394 if (light->type == "spot") {
jrkooncecba5d6c2019-08-29 11:26:22 -05005395 json_const_iterator spotIt;
5396 if (!FindMember(o, "spot", spotIt)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005397 if (err) {
5398 std::stringstream ss;
5399 ss << "Spot light description not found." << std::endl;
5400 (*err) += ss.str();
5401 }
5402 return false;
Benjamin Schmithüsen4557b6a2019-07-09 16:55:55 +02005403 }
5404
jrkooncecba5d6c2019-08-29 11:26:22 -05005405 const json &v = GetValue(spotIt);
5406 if (!IsObject(v)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005407 if (err) {
5408 std::stringstream ss;
5409 ss << "\"spot\" is not a JSON object." << std::endl;
5410 (*err) += ss.str();
5411 }
5412 return false;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005413 }
5414
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005415 if (!ParseSpotLight(&light->spot, err, v,
5416 store_original_json_for_extras_and_extensions)) {
Johan Bowald52936a02019-07-17 09:06:45 +02005417 return false;
5418 }
5419 }
5420
5421 ParseStringProperty(&light->name, err, o, "name", false);
5422 ParseNumberArrayProperty(&light->color, err, o, "color", false);
5423 ParseNumberProperty(&light->range, err, o, "range", false);
5424 ParseNumberProperty(&light->intensity, err, o, "intensity", false);
5425 ParseExtensionsProperty(&light->extensions, err, o);
5426 ParseExtrasProperty(&(light->extras), o);
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005427
5428 if (store_original_json_for_extras_and_extensions) {
5429 {
5430 json_const_iterator it;
5431 if (FindMember(o, "extensions", it)) {
5432 light->extensions_json_string = JsonToString(GetValue(it));
5433 }
5434 }
5435 {
5436 json_const_iterator it;
5437 if (FindMember(o, "extras", it)) {
5438 light->extras_json_string = JsonToString(GetValue(it));
5439 }
5440 }
5441 }
5442
Johan Bowald52936a02019-07-17 09:06:45 +02005443 return true;
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02005444}
5445
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09005446bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn,
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005447 const char *json_str,
5448 unsigned int json_str_length,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09005449 const std::string &base_dir,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09005450 unsigned int check_sections) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005451 if (json_str_length < 4) {
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09005452 if (err) {
5453 (*err) = "JSON string too short.\n";
5454 }
5455 return false;
5456 }
5457
jrkooncecba5d6c2019-08-29 11:26:22 -05005458 JsonDocument v;
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005459
Syoyo Fujitad42767e2018-03-15 21:52:00 -05005460#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \
5461 defined(_CPPUNWIND)) && \
Syoyo Fujitac4166e42020-01-08 02:38:01 +09005462 !defined(TINYGLTF_NOEXCEPTION)
Syoyo Fujitacdf84902017-08-01 20:12:08 +09005463 try {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005464 JsonParse(v, json_str, json_str_length, true);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005465
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005466 } catch (const std::exception &e) {
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005467 if (err) {
Syoyo Fujitacdf84902017-08-01 20:12:08 +09005468 (*err) = e.what();
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005469 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005470 return false;
5471 }
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005472#else
5473 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005474 JsonParse(v, json_str, json_str_length);
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005475
jrkooncecba5d6c2019-08-29 11:26:22 -05005476 if (!IsObject(v)) {
Syoyo Fujita17cfbcc2018-01-05 20:13:50 +09005477 // Assume parsing was failed.
5478 if (err) {
5479 (*err) = "Failed to parse JSON object\n";
5480 }
5481 return false;
5482 }
5483 }
5484#endif
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005485
jrkooncecba5d6c2019-08-29 11:26:22 -05005486 if (!IsObject(v)) {
Syoyo Fujita2b0307f2017-08-01 18:15:52 +09005487 // root is not an object.
5488 if (err) {
5489 (*err) = "Root element is not a JSON object\n";
5490 }
5491 return false;
5492 }
5493
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02005494 {
5495 bool version_found = false;
jrkooncecba5d6c2019-08-29 11:26:22 -05005496 json_const_iterator it;
5497 if (FindMember(v, "asset", it) && IsObject(GetValue(it))) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005498 auto &itObj = GetValue(it);
jrkooncecba5d6c2019-08-29 11:26:22 -05005499 json_const_iterator version_it;
5500 std::string versionStr;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005501 if (FindMember(itObj, "version", version_it) &&
5502 GetString(GetValue(version_it), versionStr)) {
Benjamin Schmithüsend02ad0d2019-05-02 14:44:20 +02005503 version_found = true;
5504 }
5505 }
5506 if (version_found) {
5507 // OK
5508 } else if (check_sections & REQUIRE_VERSION) {
5509 if (err) {
5510 (*err) += "\"asset\" object not found in .gltf or not an object type\n";
5511 }
5512 return false;
5513 }
5514 }
5515
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005516 // scene is not mandatory.
Syoyo Fujita5b407452017-06-04 17:42:41 +09005517 // FIXME Maybe a better way to handle it than removing the code
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005518
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005519 auto IsArrayMemberPresent = [](const json &_v, const char *name) -> bool {
jrkooncecba5d6c2019-08-29 11:26:22 -05005520 json_const_iterator it;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005521 return FindMember(_v, name, it) && IsArray(GetValue(it));
jrkooncecba5d6c2019-08-29 11:26:22 -05005522 };
5523
Syoyo Fujita83675312017-12-02 21:14:13 +09005524 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005525 if ((check_sections & REQUIRE_SCENES) &&
5526 !IsArrayMemberPresent(v, "scenes")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005527 if (err) {
5528 (*err) += "\"scenes\" object not found in .gltf or not an array type\n";
5529 }
5530 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005531 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005532 }
5533
Syoyo Fujita83675312017-12-02 21:14:13 +09005534 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005535 if ((check_sections & REQUIRE_NODES) && !IsArrayMemberPresent(v, "nodes")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005536 if (err) {
5537 (*err) += "\"nodes\" object not found in .gltf\n";
5538 }
5539 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005540 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005541 }
5542
Syoyo Fujita83675312017-12-02 21:14:13 +09005543 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005544 if ((check_sections & REQUIRE_ACCESSORS) &&
5545 !IsArrayMemberPresent(v, "accessors")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005546 if (err) {
5547 (*err) += "\"accessors\" object not found in .gltf\n";
5548 }
5549 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005550 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005551 }
5552
Syoyo Fujita83675312017-12-02 21:14:13 +09005553 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005554 if ((check_sections & REQUIRE_BUFFERS) &&
5555 !IsArrayMemberPresent(v, "buffers")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005556 if (err) {
5557 (*err) += "\"buffers\" object not found in .gltf\n";
5558 }
5559 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005560 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005561 }
5562
Syoyo Fujita83675312017-12-02 21:14:13 +09005563 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005564 if ((check_sections & REQUIRE_BUFFER_VIEWS) &&
5565 !IsArrayMemberPresent(v, "bufferViews")) {
Syoyo Fujita83675312017-12-02 21:14:13 +09005566 if (err) {
5567 (*err) += "\"bufferViews\" object not found in .gltf\n";
5568 }
5569 return false;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09005570 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005571 }
5572
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005573 model->buffers.clear();
5574 model->bufferViews.clear();
5575 model->accessors.clear();
5576 model->meshes.clear();
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005577 model->cameras.clear();
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005578 model->nodes.clear();
5579 model->extensionsUsed.clear();
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00005580 model->extensionsRequired.clear();
Selmar09d2ff12018-03-15 17:30:42 +01005581 model->extensions.clear();
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00005582 model->defaultScene = -1;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005583
Syoyo Fujita83675312017-12-02 21:14:13 +09005584 // 1. Parse Asset
5585 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005586 json_const_iterator it;
5587 if (FindMember(v, "asset", it) && IsObject(GetValue(it))) {
5588 const json &root = GetValue(it);
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005589
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005590 ParseAsset(&model->asset, err, root,
5591 store_original_json_for_extras_and_extensions_);
Aurélien Chatelain756ee6b2017-06-19 13:52:49 +00005592 }
5593 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005594
jrkoonce51453942019-09-03 09:48:30 -05005595#ifdef TINYGLTF_USE_CPP14
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005596 auto ForEachInArray = [](const json &_v, const char *member,
5597 const auto &cb) -> bool
jrkoonce51453942019-09-03 09:48:30 -05005598#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005599 // The std::function<> implementation can be less efficient because it will
5600 // allocate heap when the size of the captured lambda is above 16 bytes with
5601 // clang and gcc, but it does not require C++14.
5602 auto ForEachInArray = [](const json &_v, const char *member,
5603 const std::function<bool(const json &)> &cb) -> bool
jrkoonce51453942019-09-03 09:48:30 -05005604#endif
Syoyo Fujita83675312017-12-02 21:14:13 +09005605 {
jrkoonce0d2b6ef2019-09-04 13:46:45 -05005606 json_const_iterator itm;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005607 if (FindMember(_v, member, itm) && IsArray(GetValue(itm))) {
jrkoonce0d2b6ef2019-09-04 13:46:45 -05005608 const json &root = GetValue(itm);
jrkooncecba5d6c2019-08-29 11:26:22 -05005609 auto it = ArrayBegin(root);
5610 auto end = ArrayEnd(root);
5611 for (; it != end; ++it) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005612 if (!cb(*it)) return false;
Syoyo Fujita59130d12017-08-01 20:23:11 +09005613 }
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005614 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005615 return true;
5616 };
5617
jrkooncecba5d6c2019-08-29 11:26:22 -05005618 // 2. Parse extensionUsed
5619 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005620 ForEachInArray(v, "extensionsUsed", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005621 std::string str;
5622 GetString(o, str);
5623 model->extensionsUsed.emplace_back(std::move(str));
5624 return true;
5625 });
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005626 }
5627
Syoyo Fujita83675312017-12-02 21:14:13 +09005628 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005629 ForEachInArray(v, "extensionsRequired", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005630 std::string str;
5631 GetString(o, str);
5632 model->extensionsRequired.emplace_back(std::move(str));
5633 return true;
5634 });
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005635 }
5636
Syoyo Fujita83675312017-12-02 21:14:13 +09005637 // 3. Parse Buffer
5638 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005639 bool success = ForEachInArray(v, "buffers", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005640 if (!IsObject(o)) {
5641 if (err) {
5642 (*err) += "`buffers' does not contain an JSON object.";
Syoyo Fujitabeded612016-05-01 20:03:43 +09005643 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005644 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005645 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005646 Buffer buffer;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005647 if (!ParseBuffer(&buffer, err, o,
5648 store_original_json_for_extras_and_extensions_, &fs,
5649 base_dir, is_binary_, bin_data_, bin_size_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005650 return false;
5651 }
5652
5653 model->buffers.emplace_back(std::move(buffer));
5654 return true;
5655 });
5656
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005657 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005658 return false;
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09005659 }
5660 }
Syoyo Fujita83675312017-12-02 21:14:13 +09005661 // 4. Parse BufferView
5662 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005663 bool success = ForEachInArray(v, "bufferViews", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005664 if (!IsObject(o)) {
5665 if (err) {
5666 (*err) += "`bufferViews' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09005667 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005668 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005669 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005670 BufferView bufferView;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005671 if (!ParseBufferView(&bufferView, err, o,
5672 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005673 return false;
5674 }
5675
5676 model->bufferViews.emplace_back(std::move(bufferView));
5677 return true;
5678 });
5679
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005680 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005681 return false;
Syoyo Fujita8c5ab032016-06-19 18:15:32 +09005682 }
5683 }
5684
Syoyo Fujita83675312017-12-02 21:14:13 +09005685 // 5. Parse Accessor
5686 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005687 bool success = ForEachInArray(v, "accessors", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005688 if (!IsObject(o)) {
5689 if (err) {
5690 (*err) += "`accessors' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09005691 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005692 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005693 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005694 Accessor accessor;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005695 if (!ParseAccessor(&accessor, err, o,
5696 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005697 return false;
5698 }
5699
5700 model->accessors.emplace_back(std::move(accessor));
5701 return true;
5702 });
5703
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005704 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005705 return false;
Syoyo Fujitac2615632016-06-19 21:56:06 +09005706 }
5707 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005708
Syoyo Fujita83675312017-12-02 21:14:13 +09005709 // 6. Parse Mesh
5710 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005711 bool success = ForEachInArray(v, "meshes", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005712 if (!IsObject(o)) {
5713 if (err) {
5714 (*err) += "`meshes' does not contain an JSON object.";
Syoyo Fujita59130d12017-08-01 20:23:11 +09005715 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005716 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005717 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005718 Mesh mesh;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005719 if (!ParseMesh(&mesh, model, err, o,
5720 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005721 return false;
5722 }
5723
5724 model->meshes.emplace_back(std::move(mesh));
5725 return true;
5726 });
5727
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005728 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005729 return false;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09005730 }
5731 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01005732
viperscape9df05802018-12-05 14:11:01 -05005733 // Assign missing bufferView target types
5734 // - Look for missing Mesh indices
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01005735 // - Look for missing Mesh attributes
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00005736 for (auto &mesh : model->meshes) {
5737 for (auto &primitive : mesh.primitives) {
5738 if (primitive.indices >
5739 -1) // has indices from parsing step, must be Element Array Buffer
viperscape9df05802018-12-05 14:11:01 -05005740 {
Jeff McGlynn89152522019-04-25 16:33:56 -07005741 if (size_t(primitive.indices) >= model->accessors.size()) {
5742 if (err) {
5743 (*err) += "primitive indices accessor out of bounds";
5744 }
5745 return false;
5746 }
5747
Syoyo Fujitacea69e32019-08-20 17:10:30 +09005748 auto bufferView =
5749 model->accessors[size_t(primitive.indices)].bufferView;
Jeff McGlynn89152522019-04-25 16:33:56 -07005750 if (bufferView < 0 || size_t(bufferView) >= model->bufferViews.size()) {
5751 if (err) {
5752 (*err) += "accessor[" + std::to_string(primitive.indices) +
5753 "] invalid bufferView";
5754 }
5755 return false;
5756 }
5757
Syoyo Fujitacea69e32019-08-20 17:10:30 +09005758 model->bufferViews[size_t(bufferView)].target =
Jeff McGlynn89152522019-04-25 16:33:56 -07005759 TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER;
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00005760 // we could optionally check if acessors' bufferView type is Scalar, as
5761 // it should be
viperscape9df05802018-12-05 14:11:01 -05005762 }
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01005763
5764 for (auto &attribute : primitive.attributes) {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09005765 model
5766 ->bufferViews[size_t(
5767 model->accessors[size_t(attribute.second)].bufferView)]
Marcin Kacprzakd09788d2020-01-02 13:00:48 +01005768 .target = TINYGLTF_TARGET_ARRAY_BUFFER;
5769 }
Marcin Kacprzakc3d67162020-01-22 13:13:35 +01005770
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09005771 for (auto &target : primitive.targets) {
5772 for (auto &attribute : target) {
Rahul Sheth125e4a22020-07-13 13:56:50 -04005773 auto bufferView =
5774 model->accessors[size_t(attribute.second)].bufferView;
Syoyo Fujita91da2992020-07-15 13:52:39 +09005775 // bufferView could be null(-1) for sparse morph target
5776 if (bufferView >= 0) {
Rahul Sheth125e4a22020-07-13 13:56:50 -04005777 model->bufferViews[size_t(bufferView)].target =
5778 TINYGLTF_TARGET_ARRAY_BUFFER;
5779 }
Marcin Kacprzakc3d67162020-01-22 13:13:35 +01005780 }
5781 }
viperscape9df05802018-12-05 14:11:01 -05005782 }
5783 }
5784
Syoyo Fujita83675312017-12-02 21:14:13 +09005785 // 7. Parse Node
5786 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005787 bool success = ForEachInArray(v, "nodes", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005788 if (!IsObject(o)) {
5789 if (err) {
5790 (*err) += "`nodes' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005791 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005792 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005793 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005794 Node node;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005795 if (!ParseNode(&node, err, o,
5796 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005797 return false;
5798 }
5799
5800 model->nodes.emplace_back(std::move(node));
5801 return true;
5802 });
5803
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005804 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005805 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005806 }
5807 }
5808
5809 // 8. Parse scenes.
5810 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005811 bool success = ForEachInArray(v, "scenes", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005812 if (!IsObject(o)) {
5813 if (err) {
5814 (*err) += "`scenes' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005815 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005816 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005817 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005818 std::vector<int> nodes;
jrkooncea3b8b352019-09-03 10:30:19 -05005819 ParseIntegerArrayProperty(&nodes, err, o, "nodes", false);
jrkooncecba5d6c2019-08-29 11:26:22 -05005820
5821 Scene scene;
5822 scene.nodes = std::move(nodes);
5823
5824 ParseStringProperty(&scene.name, err, o, "name", false);
5825
5826 ParseExtensionsProperty(&scene.extensions, err, o);
5827 ParseExtrasProperty(&scene.extras, o);
5828
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005829 if (store_original_json_for_extras_and_extensions_) {
5830 {
5831 json_const_iterator it;
5832 if (FindMember(o, "extensions", it)) {
Mio Nilssone29ba7c2021-05-11 11:50:35 +02005833 scene.extensions_json_string = JsonToString(GetValue(it));
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005834 }
5835 }
5836 {
5837 json_const_iterator it;
5838 if (FindMember(o, "extras", it)) {
Mio Nilssone29ba7c2021-05-11 11:50:35 +02005839 scene.extras_json_string = JsonToString(GetValue(it));
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005840 }
5841 }
5842 }
5843
jrkooncecba5d6c2019-08-29 11:26:22 -05005844 model->scenes.emplace_back(std::move(scene));
5845 return true;
5846 });
5847
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005848 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005849 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005850 }
5851 }
5852
5853 // 9. Parse default scenes.
5854 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005855 json_const_iterator rootIt;
5856 int iVal;
5857 if (FindMember(v, "scene", rootIt) && GetInt(GetValue(rootIt), iVal)) {
5858 model->defaultScene = iVal;
Syoyo Fujita83675312017-12-02 21:14:13 +09005859 }
5860 }
5861
5862 // 10. Parse Material
5863 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005864 bool success = ForEachInArray(v, "materials", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005865 if (!IsObject(o)) {
5866 if (err) {
5867 (*err) += "`materials' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005868 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005869 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005870 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005871 Material material;
5872 ParseStringProperty(&material.name, err, o, "name", false);
5873
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005874 if (!ParseMaterial(&material, err, o,
5875 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005876 return false;
5877 }
5878
5879 model->materials.emplace_back(std::move(material));
5880 return true;
5881 });
5882
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005883 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005884 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005885 }
5886 }
5887
5888 // 11. Parse Image
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09005889 void *load_image_user_data{nullptr};
5890
5891 LoadImageDataOption load_image_option;
5892
5893 if (user_image_loader_) {
5894 // Use user supplied pointer
5895 load_image_user_data = load_image_user_data_;
5896 } else {
5897 load_image_option.preserve_channels = preserve_image_channels_;
5898 load_image_user_data = reinterpret_cast<void *>(&load_image_option);
5899 }
5900
Syoyo Fujita83675312017-12-02 21:14:13 +09005901 {
jrkooncecba5d6c2019-08-29 11:26:22 -05005902 int idx = 0;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005903 bool success = ForEachInArray(v, "images", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005904 if (!IsObject(o)) {
5905 if (err) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005906 (*err) += "image[" + std::to_string(idx) + "] is not a JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005907 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005908 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005909 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005910 Image image;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005911 if (!ParseImage(&image, idx, err, warn, o,
5912 store_original_json_for_extras_and_extensions_, base_dir,
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09005913 &fs, &this->LoadImageData, load_image_user_data)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005914 return false;
5915 }
5916
5917 if (image.bufferView != -1) {
5918 // Load image from the buffer view.
5919 if (size_t(image.bufferView) >= model->bufferViews.size()) {
5920 if (err) {
5921 std::stringstream ss;
5922 ss << "image[" << idx << "] bufferView \"" << image.bufferView
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005923 << "\" not found in the scene." << std::endl;
jrkooncecba5d6c2019-08-29 11:26:22 -05005924 (*err) += ss.str();
5925 }
5926 return false;
5927 }
5928
5929 const BufferView &bufferView =
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005930 model->bufferViews[size_t(image.bufferView)];
jrkooncecba5d6c2019-08-29 11:26:22 -05005931 if (size_t(bufferView.buffer) >= model->buffers.size()) {
5932 if (err) {
5933 std::stringstream ss;
5934 ss << "image[" << idx << "] buffer \"" << bufferView.buffer
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005935 << "\" not found in the scene." << std::endl;
jrkooncecba5d6c2019-08-29 11:26:22 -05005936 (*err) += ss.str();
5937 }
5938 return false;
5939 }
5940 const Buffer &buffer = model->buffers[size_t(bufferView.buffer)];
5941
5942 if (*LoadImageData == nullptr) {
5943 if (err) {
5944 (*err) += "No LoadImageData callback specified.\n";
5945 }
5946 return false;
5947 }
5948 bool ret = LoadImageData(
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005949 &image, idx, err, warn, image.width, image.height,
5950 &buffer.data[bufferView.byteOffset],
Syoyo Fujita010ee9c2020-10-31 19:35:55 +09005951 static_cast<int>(bufferView.byteLength), load_image_user_data);
jrkooncecba5d6c2019-08-29 11:26:22 -05005952 if (!ret) {
5953 return false;
5954 }
5955 }
5956
5957 model->images.emplace_back(std::move(image));
5958 ++idx;
5959 return true;
5960 });
5961
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005962 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005963 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005964 }
5965 }
5966
5967 // 12. Parse Texture
5968 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005969 bool success = ForEachInArray(v, "textures", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005970 if (!IsObject(o)) {
5971 if (err) {
5972 (*err) += "`textures' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005973 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005974 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005975 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005976 Texture texture;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09005977 if (!ParseTexture(&texture, err, o,
5978 store_original_json_for_extras_and_extensions_,
5979 base_dir)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005980 return false;
5981 }
5982
5983 model->textures.emplace_back(std::move(texture));
5984 return true;
5985 });
5986
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005987 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005988 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09005989 }
5990 }
5991
5992 // 13. Parse Animation
5993 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09005994 bool success = ForEachInArray(v, "animations", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05005995 if (!IsObject(o)) {
5996 if (err) {
5997 (*err) += "`animations' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09005998 }
jrkooncecba5d6c2019-08-29 11:26:22 -05005999 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006000 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006001 Animation animation;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006002 if (!ParseAnimation(&animation, err, o,
6003 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006004 return false;
6005 }
6006
6007 model->animations.emplace_back(std::move(animation));
6008 return true;
6009 });
6010
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006011 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006012 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006013 }
6014 }
6015
6016 // 14. Parse Skin
6017 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006018 bool success = ForEachInArray(v, "skins", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006019 if (!IsObject(o)) {
6020 if (err) {
6021 (*err) += "`skins' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006022 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006023 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006024 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006025 Skin skin;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006026 if (!ParseSkin(&skin, err, o,
6027 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006028 return false;
6029 }
6030
6031 model->skins.emplace_back(std::move(skin));
6032 return true;
6033 });
6034
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006035 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006036 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006037 }
6038 }
6039
6040 // 15. Parse Sampler
6041 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006042 bool success = ForEachInArray(v, "samplers", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006043 if (!IsObject(o)) {
6044 if (err) {
6045 (*err) += "`samplers' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006046 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006047 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006048 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006049 Sampler sampler;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006050 if (!ParseSampler(&sampler, err, o,
6051 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006052 return false;
6053 }
6054
6055 model->samplers.emplace_back(std::move(sampler));
6056 return true;
6057 });
6058
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006059 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006060 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006061 }
6062 }
6063
6064 // 16. Parse Camera
6065 {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006066 bool success = ForEachInArray(v, "cameras", [&](const json &o) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006067 if (!IsObject(o)) {
6068 if (err) {
6069 (*err) += "`cameras' does not contain an JSON object.";
Syoyo Fujita83675312017-12-02 21:14:13 +09006070 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006071 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006072 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006073 Camera camera;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006074 if (!ParseCamera(&camera, err, o,
6075 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006076 return false;
6077 }
6078
6079 model->cameras.emplace_back(std::move(camera));
6080 return true;
6081 });
6082
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006083 if (!success) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006084 return false;
Syoyo Fujita83675312017-12-02 21:14:13 +09006085 }
6086 }
6087
6088 // 17. Parse Extensions
Selmar09d2ff12018-03-15 17:30:42 +01006089 ParseExtensionsProperty(&model->extensions, err, v);
6090
6091 // 18. Specific extension implementations
Syoyo Fujita83675312017-12-02 21:14:13 +09006092 {
jrkooncecba5d6c2019-08-29 11:26:22 -05006093 json_const_iterator rootIt;
6094 if (FindMember(v, "extensions", rootIt) && IsObject(GetValue(rootIt))) {
6095 const json &root = GetValue(rootIt);
Syoyo Fujita83675312017-12-02 21:14:13 +09006096
jrkooncecba5d6c2019-08-29 11:26:22 -05006097 json_const_iterator it(ObjectBegin(root));
6098 json_const_iterator itEnd(ObjectEnd(root));
Syoyo Fujita83675312017-12-02 21:14:13 +09006099 for (; it != itEnd; ++it) {
Benjamin Schmithüsenb7ca7c92019-07-08 18:04:24 +02006100 // parse KHR_lights_punctual extension
jrkooncecba5d6c2019-08-29 11:26:22 -05006101 std::string key(GetKey(it));
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006102 if ((key == "KHR_lights_punctual") && IsObject(GetValue(it))) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006103 const json &object = GetValue(it);
6104 json_const_iterator itLight;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006105 if (FindMember(object, "lights", itLight)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006106 const json &lights = GetValue(itLight);
6107 if (!IsArray(lights)) {
6108 continue;
Syoyo Fujita83675312017-12-02 21:14:13 +09006109 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006110
6111 auto arrayIt(ArrayBegin(lights));
6112 auto arrayItEnd(ArrayEnd(lights));
6113 for (; arrayIt != arrayItEnd; ++arrayIt) {
6114 Light light;
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006115 if (!ParseLight(&light, err, *arrayIt,
6116 store_original_json_for_extras_and_extensions_)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006117 return false;
6118 }
6119 model->lights.emplace_back(std::move(light));
6120 }
Syoyo Fujita83675312017-12-02 21:14:13 +09006121 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01006122 }
6123 }
6124 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01006125 }
Syoyo Fujita83675312017-12-02 21:14:13 +09006126
mynzc0d4d1c2018-06-28 23:06:00 +09006127 // 19. Parse Extras
6128 ParseExtrasProperty(&model->extras, v);
6129
Syoyo Fujita6e08b172019-10-30 17:25:38 +09006130 if (store_original_json_for_extras_and_extensions_) {
6131 model->extras_json_string = JsonToString(v["extras"]);
6132 model->extensions_json_string = JsonToString(v["extensions"]);
6133 }
6134
Syoyo Fujitaa4d26882015-12-20 20:27:54 +09006135 return true;
6136}
6137
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006138bool TinyGLTF::LoadASCIIFromString(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006139 std::string *warn, const char *str,
6140 unsigned int length,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006141 const std::string &base_dir,
6142 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006143 is_binary_ = false;
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09006144 bin_data_ = nullptr;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006145 bin_size_ = 0;
6146
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006147 return LoadFromString(model, err, warn, str, length, base_dir,
6148 check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006149}
6150
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006151bool TinyGLTF::LoadASCIIFromFile(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006152 std::string *warn, const std::string &filename,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006153 unsigned int check_sections) {
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006154 std::stringstream ss;
6155
Paolo Jovone6601bf2018-07-07 20:43:33 +02006156 if (fs.ReadWholeFile == nullptr) {
6157 // Programmer error, assert() ?
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006158 ss << "Failed to read file: " << filename
6159 << ": one or more FS callback not set" << std::endl;
Syoyo Fujita523c9bf2016-04-20 14:06:56 +09006160 if (err) {
6161 (*err) = ss.str();
6162 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006163 return false;
6164 }
6165
Paolo Jovone6601bf2018-07-07 20:43:33 +02006166 std::vector<unsigned char> data;
6167 std::string fileerr;
6168 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006169 if (!fileread) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02006170 ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
6171 if (err) {
6172 (*err) = ss.str();
6173 }
6174 return false;
6175 }
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006176
Paolo Jovone6601bf2018-07-07 20:43:33 +02006177 size_t sz = data.size();
Luke San Antoniob310b4a2016-06-23 14:17:37 -04006178 if (sz == 0) {
6179 if (err) {
6180 (*err) = "Empty file.";
6181 }
6182 return false;
6183 }
6184
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006185 std::string basedir = GetBaseDir(filename);
6186
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006187 bool ret = LoadASCIIFromString(
6188 model, err, warn, reinterpret_cast<const char *>(&data.at(0)),
6189 static_cast<unsigned int>(data.size()), basedir, check_sections);
Luke San Antonio9e36b612016-06-23 14:10:51 -04006190
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006191 return ret;
6192}
6193
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006194bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006195 std::string *warn,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006196 const unsigned char *bytes,
6197 unsigned int size,
6198 const std::string &base_dir,
6199 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006200 if (size < 20) {
6201 if (err) {
6202 (*err) = "Too short data size for glTF Binary.";
6203 }
6204 return false;
6205 }
6206
Syoyo Fujitabeded612016-05-01 20:03:43 +09006207 if (bytes[0] == 'g' && bytes[1] == 'l' && bytes[2] == 'T' &&
6208 bytes[3] == 'F') {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006209 // ok
6210 } else {
6211 if (err) {
6212 (*err) = "Invalid magic.";
6213 }
6214 return false;
6215 }
6216
Syoyo Fujitabeded612016-05-01 20:03:43 +09006217 unsigned int version; // 4 bytes
6218 unsigned int length; // 4 bytes
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00006219 unsigned int model_length; // 4 bytes
6220 unsigned int model_format; // 4 bytes;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006221
6222 // @todo { Endian swap for big endian machine. }
Syoyo Fujitabeded612016-05-01 20:03:43 +09006223 memcpy(&version, bytes + 4, 4);
6224 swap4(&version);
6225 memcpy(&length, bytes + 8, 4);
6226 swap4(&length);
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00006227 memcpy(&model_length, bytes + 12, 4);
6228 swap4(&model_length);
6229 memcpy(&model_format, bytes + 16, 4);
6230 swap4(&model_format);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006231
Syoyo Fujitae69069d2018-03-15 22:01:18 -05006232 // In case the Bin buffer is not present, the size is exactly 20 + size of
6233 // JSON contents,
6234 // so use "greater than" operator.
Jeff McGlynn89152522019-04-25 16:33:56 -07006235 if ((20 + model_length > size) || (model_length < 1) || (length > size) ||
Doug Muircf668682019-10-28 09:51:13 -07006236 (20 + model_length > length) ||
Syoyo Fujita40c4a0a2017-05-30 01:21:36 +09006237 (model_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006238 if (err) {
6239 (*err) = "Invalid glTF binary.";
6240 }
6241 return false;
6242 }
6243
6244 // Extract JSON string.
Syoyo Fujitabeded612016-05-01 20:03:43 +09006245 std::string jsonString(reinterpret_cast<const char *>(&bytes[20]),
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00006246 model_length);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006247
6248 is_binary_ = true;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006249 bin_data_ = bytes + 20 + model_length +
6250 8; // 4 bytes (buffer_length) + 4 bytes(buffer_format)
Syoyo Fujitabeded612016-05-01 20:03:43 +09006251 bin_size_ =
Aurélien Chatelainee4693e2017-05-24 12:33:36 +00006252 length - (20 + model_length); // extract header + JSON scene data.
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006253
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006254 bool ret = LoadFromString(model, err, warn,
6255 reinterpret_cast<const char *>(&bytes[20]),
6256 model_length, base_dir, check_sections);
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006257 if (!ret) {
6258 return ret;
6259 }
6260
6261 return true;
6262}
6263
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006264bool TinyGLTF::LoadBinaryFromFile(Model *model, std::string *err,
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006265 std::string *warn,
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006266 const std::string &filename,
6267 unsigned int check_sections) {
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006268 std::stringstream ss;
6269
Paolo Jovone6601bf2018-07-07 20:43:33 +02006270 if (fs.ReadWholeFile == nullptr) {
6271 // Programmer error, assert() ?
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006272 ss << "Failed to read file: " << filename
6273 << ": one or more FS callback not set" << std::endl;
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006274 if (err) {
6275 (*err) = ss.str();
6276 }
6277 return false;
6278 }
6279
Paolo Jovone6601bf2018-07-07 20:43:33 +02006280 std::vector<unsigned char> data;
6281 std::string fileerr;
6282 bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data);
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006283 if (!fileread) {
Paolo Jovone6601bf2018-07-07 20:43:33 +02006284 ss << "Failed to read file: " << filename << ": " << fileerr << std::endl;
6285 if (err) {
6286 (*err) = ss.str();
6287 }
6288 return false;
6289 }
Syoyo Fujitaec39a522016-05-01 18:33:31 +09006290
Syoyo Fujitabeded612016-05-01 20:03:43 +09006291 std::string basedir = GetBaseDir(filename);
6292
Syoyo Fujita7c8d4ed2018-07-27 16:49:10 +09006293 bool ret = LoadBinaryFromMemory(model, err, warn, &data.at(0),
6294 static_cast<unsigned int>(data.size()),
6295 basedir, check_sections);
Syoyo Fujita61fb52b2016-02-06 17:26:44 +09006296
6297 return ret;
6298}
6299
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006300///////////////////////
6301// GLTF Serialization
6302///////////////////////
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006303namespace {
6304json JsonFromString(const char *s) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006305#ifdef TINYGLTF_USE_RAPIDJSON
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006306 return json(s, GetAllocator());
jrkooncecba5d6c2019-08-29 11:26:22 -05006307#else
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006308 return json(s);
jrkooncecba5d6c2019-08-29 11:26:22 -05006309#endif
jrkoonce63419a12019-09-03 17:06:41 -05006310}
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006311
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006312void JsonAssign(json &dest, const json &src) {
6313#ifdef TINYGLTF_USE_RAPIDJSON
6314 dest.CopyFrom(src, GetAllocator());
6315#else
6316 dest = src;
6317#endif
6318}
6319
6320void JsonAddMember(json &o, const char *key, json &&value) {
6321#ifdef TINYGLTF_USE_RAPIDJSON
6322 if (!o.IsObject()) {
6323 o.SetObject();
6324 }
6325 o.AddMember(json(key, GetAllocator()), std::move(value), GetAllocator());
6326#else
6327 o[key] = std::move(value);
6328#endif
6329}
6330
6331void JsonPushBack(json &o, json &&value) {
6332#ifdef TINYGLTF_USE_RAPIDJSON
6333 o.PushBack(std::move(value), GetAllocator());
6334#else
6335 o.push_back(std::move(value));
6336#endif
6337}
6338
6339bool JsonIsNull(const json &o) {
6340#ifdef TINYGLTF_USE_RAPIDJSON
6341 return o.IsNull();
6342#else
6343 return o.is_null();
6344#endif
6345}
6346
6347void JsonSetObject(json &o) {
6348#ifdef TINYGLTF_USE_RAPIDJSON
6349 o.SetObject();
6350#else
6351 o = o.object({});
6352#endif
6353}
6354
6355void JsonReserveArray(json &o, size_t s) {
6356#ifdef TINYGLTF_USE_RAPIDJSON
6357 o.SetArray();
6358 o.Reserve(static_cast<rapidjson::SizeType>(s), GetAllocator());
6359#endif
6360 (void)(o);
6361 (void)(s);
6362}
6363} // namespace
6364
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006365// typedef std::pair<std::string, json> json_object_pair;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006366
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006367template <typename T>
6368static void SerializeNumberProperty(const std::string &key, T number,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006369 json &obj) {
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006370 // obj.insert(
Syoyo Fujita83675312017-12-02 21:14:13 +09006371 // json_object_pair(key, json(static_cast<double>(number))));
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006372 // obj[key] = static_cast<double>(number);
jrkooncecba5d6c2019-08-29 11:26:22 -05006373 JsonAddMember(obj, key.c_str(), json(number));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006374}
6375
Ivo van Dongen272a9df2020-05-29 11:24:11 +03006376#ifdef TINYGLTF_USE_RAPIDJSON
6377template <>
6378void SerializeNumberProperty(const std::string &key, size_t number, json &obj) {
6379 JsonAddMember(obj, key.c_str(), json(static_cast<uint64_t>(number)));
6380}
6381#endif
6382
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006383template <typename T>
6384static void SerializeNumberArrayProperty(const std::string &key,
6385 const std::vector<T> &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006386 json &obj) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006387 if (value.empty()) return;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006388
jrkooncecba5d6c2019-08-29 11:26:22 -05006389 json ary;
6390 JsonReserveArray(ary, value.size());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006391 for (const auto &s : value) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006392 JsonPushBack(ary, json(s));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006393 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006394 JsonAddMember(obj, key.c_str(), std::move(ary));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006395}
6396
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006397static void SerializeStringProperty(const std::string &key,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006398 const std::string &value, json &obj) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006399 JsonAddMember(obj, key.c_str(), JsonFromString(value.c_str()));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006400}
6401
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006402static void SerializeStringArrayProperty(const std::string &key,
6403 const std::vector<std::string> &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006404 json &obj) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006405 json ary;
6406 JsonReserveArray(ary, value.size());
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006407 for (auto &s : value) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006408 JsonPushBack(ary, JsonFromString(s.c_str()));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006409 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006410 JsonAddMember(obj, key.c_str(), std::move(ary));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006411}
6412
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006413static bool ValueToJson(const Value &value, json *ret) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006414 json obj;
jrkooncecba5d6c2019-08-29 11:26:22 -05006415#ifdef TINYGLTF_USE_RAPIDJSON
6416 switch (value.Type()) {
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006417 case REAL_TYPE:
6418 obj.SetDouble(value.Get<double>());
6419 break;
6420 case INT_TYPE:
6421 obj.SetInt(value.Get<int>());
6422 break;
6423 case BOOL_TYPE:
6424 obj.SetBool(value.Get<bool>());
6425 break;
6426 case STRING_TYPE:
6427 obj.SetString(value.Get<std::string>().c_str(), GetAllocator());
6428 break;
6429 case ARRAY_TYPE: {
6430 obj.SetArray();
6431 obj.Reserve(static_cast<rapidjson::SizeType>(value.ArrayLen()),
6432 GetAllocator());
6433 for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
6434 Value elementValue = value.Get(int(i));
6435 json elementJson;
6436 if (ValueToJson(value.Get(int(i)), &elementJson))
6437 obj.PushBack(std::move(elementJson), GetAllocator());
jrkooncecba5d6c2019-08-29 11:26:22 -05006438 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006439 break;
jrkooncecba5d6c2019-08-29 11:26:22 -05006440 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006441 case BINARY_TYPE:
6442 // TODO
6443 // obj = json(value.Get<std::vector<unsigned char>>());
6444 return false;
6445 break;
6446 case OBJECT_TYPE: {
6447 obj.SetObject();
6448 Value::Object objMap = value.Get<Value::Object>();
6449 for (auto &it : objMap) {
6450 json elementJson;
6451 if (ValueToJson(it.second, &elementJson)) {
6452 obj.AddMember(json(it.first.c_str(), GetAllocator()),
6453 std::move(elementJson), GetAllocator());
6454 }
6455 }
6456 break;
6457 }
6458 case NULL_TYPE:
6459 default:
6460 return false;
jrkooncecba5d6c2019-08-29 11:26:22 -05006461 }
6462#else
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006463 switch (value.Type()) {
Syoyo Fujita150f2432019-07-25 19:22:44 +09006464 case REAL_TYPE:
Selmar Kokfa7022f2018-04-04 18:10:20 +02006465 obj = json(value.Get<double>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006466 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006467 case INT_TYPE:
6468 obj = json(value.Get<int>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006469 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006470 case BOOL_TYPE:
6471 obj = json(value.Get<bool>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006472 break;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006473 case STRING_TYPE:
6474 obj = json(value.Get<std::string>());
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006475 break;
6476 case ARRAY_TYPE: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006477 for (unsigned int i = 0; i < value.ArrayLen(); ++i) {
6478 Value elementValue = value.Get(int(i));
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006479 json elementJson;
6480 if (ValueToJson(value.Get(int(i)), &elementJson))
Selmar Kokfa7022f2018-04-04 18:10:20 +02006481 obj.push_back(elementJson);
6482 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006483 break;
6484 }
Selmar Kokfa7022f2018-04-04 18:10:20 +02006485 case BINARY_TYPE:
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006486 // TODO
6487 // obj = json(value.Get<std::vector<unsigned char>>());
Selmar Kokfa7022f2018-04-04 18:10:20 +02006488 return false;
6489 break;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006490 case OBJECT_TYPE: {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006491 Value::Object objMap = value.Get<Value::Object>();
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006492 for (auto &it : objMap) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006493 json elementJson;
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006494 if (ValueToJson(it.second, &elementJson)) obj[it.first] = elementJson;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006495 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006496 break;
6497 }
6498 case NULL_TYPE:
6499 default:
6500 return false;
Selmar Kokfa7022f2018-04-04 18:10:20 +02006501 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006502#endif
6503 if (ret) *ret = std::move(obj);
Selmar Kokfa7022f2018-04-04 18:10:20 +02006504 return true;
6505}
6506
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006507static void SerializeValue(const std::string &key, const Value &value,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006508 json &obj) {
Selmar Kokfa7022f2018-04-04 18:10:20 +02006509 json ret;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006510 if (ValueToJson(value, &ret)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006511 JsonAddMember(obj, key.c_str(), std::move(ret));
6512 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006513}
6514
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006515static void SerializeGltfBufferData(const std::vector<unsigned char> &data,
johan bowald30c53472018-03-30 11:49:36 +02006516 json &o) {
6517 std::string header = "data:application/octet-stream;base64,";
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09006518 if (data.size() > 0) {
6519 std::string encodedData =
6520 base64_encode(&data[0], static_cast<unsigned int>(data.size()));
6521 SerializeStringProperty("uri", header + encodedData, o);
6522 } else {
6523 // Issue #229
6524 // size 0 is allowd. Just emit mime header.
6525 SerializeStringProperty("uri", header, o);
6526 }
johan bowald30c53472018-03-30 11:49:36 +02006527}
6528
Selmar Koke4677492018-10-25 16:45:49 +02006529static bool SerializeGltfBufferData(const std::vector<unsigned char> &data,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006530 const std::string &binFilename) {
Harokyangfb256602019-10-30 16:13:52 +08006531#ifdef _WIN32
Syoyo Fujitac4166e42020-01-08 02:38:01 +09006532#if defined(__GLIBCXX__) // mingw
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09006533 int file_descriptor = _wopen(UTF8ToWchar(binFilename).c_str(),
6534 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
6535 __gnu_cxx::stdio_filebuf<char> wfile_buf(
6536 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09006537 std::ostream output(&wfile_buf);
6538 if (!wfile_buf.is_open()) return false;
6539#elif defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08006540 std::ofstream output(UTF8ToWchar(binFilename).c_str(), std::ofstream::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09006541 if (!output.is_open()) return false;
Harokyangfb256602019-10-30 16:13:52 +08006542#else
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006543 std::ofstream output(binFilename.c_str(), std::ofstream::binary);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006544 if (!output.is_open()) return false;
Syoyo Fujita45cac782019-11-09 20:42:55 +09006545#endif
6546#else
6547 std::ofstream output(binFilename.c_str(), std::ofstream::binary);
6548 if (!output.is_open()) return false;
6549#endif
Syoyo Fujitaa11f6e12019-12-19 14:16:48 +09006550 if (data.size() > 0) {
6551 output.write(reinterpret_cast<const char *>(&data[0]),
6552 std::streamsize(data.size()));
6553 } else {
6554 // Issue #229
6555 // size 0 will be still valid buffer data.
6556 // write empty file.
6557 }
Selmar Koke4677492018-10-25 16:45:49 +02006558 return true;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006559}
6560
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006561#if 0 // FIXME(syoyo): not used. will be removed in the future release.
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006562static void SerializeParameterMap(ParameterMap &param, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006563 for (ParameterMap::iterator paramIt = param.begin(); paramIt != param.end();
6564 ++paramIt) {
6565 if (paramIt->second.number_array.size()) {
6566 SerializeNumberArrayProperty<double>(paramIt->first,
6567 paramIt->second.number_array, o);
6568 } else if (paramIt->second.json_double_value.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006569 json json_double_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006570 for (std::map<std::string, double>::iterator it =
6571 paramIt->second.json_double_value.begin();
6572 it != paramIt->second.json_double_value.end(); ++it) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006573 if (it->first == "index") {
6574 json_double_value[it->first] = paramIt->second.TextureIndex();
6575 } else {
6576 json_double_value[it->first] = it->second;
6577 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006578 }
6579
Syoyo Fujita83675312017-12-02 21:14:13 +09006580 o[paramIt->first] = json_double_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006581 } else if (!paramIt->second.string_value.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006582 SerializeStringProperty(paramIt->first, paramIt->second.string_value, o);
Ben Buzbeef6af2242018-04-25 15:13:05 -07006583 } else if (paramIt->second.has_number_value) {
6584 o[paramIt->first] = paramIt->second.number_value;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006585 } else {
Syoyo Fujita83675312017-12-02 21:14:13 +09006586 o[paramIt->first] = paramIt->second.bool_value;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006587 }
6588 }
6589}
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006590#endif
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006591
Selmar Kok81b672b2019-10-18 16:08:44 +02006592static void SerializeExtensionMap(const ExtensionMap &extensions, json &o) {
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006593 if (!extensions.size()) return;
Selmar09d2ff12018-03-15 17:30:42 +01006594
6595 json extMap;
Selmar Kok81b672b2019-10-18 16:08:44 +02006596 for (ExtensionMap::const_iterator extIt = extensions.begin();
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006597 extIt != extensions.end(); ++extIt) {
Syoyo Fujita924d86e2018-10-08 21:15:12 +09006598 // Allow an empty object for extension(#97)
6599 json ret;
jrkooncecba5d6c2019-08-29 11:26:22 -05006600 bool isNull = true;
Syoyo Fujita924d86e2018-10-08 21:15:12 +09006601 if (ValueToJson(extIt->second, &ret)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05006602 isNull = JsonIsNull(ret);
6603 JsonAddMember(extMap, extIt->first.c_str(), std::move(ret));
Selmar Kokdb7f4e42018-10-10 18:10:58 +02006604 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006605 if (isNull) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006606 if (!(extIt->first.empty())) { // name should not be empty, but for sure
6607 // create empty object so that an extension name is still included in
6608 // json.
jrkooncecba5d6c2019-08-29 11:26:22 -05006609 json empty;
jrkoonce06c30c42019-09-03 15:56:48 -05006610 JsonSetObject(empty);
jrkooncecba5d6c2019-08-29 11:26:22 -05006611 JsonAddMember(extMap, extIt->first.c_str(), std::move(empty));
Syoyo Fujita924d86e2018-10-08 21:15:12 +09006612 }
6613 }
Selmar09d2ff12018-03-15 17:30:42 +01006614 }
jrkooncecba5d6c2019-08-29 11:26:22 -05006615 JsonAddMember(o, "extensions", std::move(extMap));
Selmar09d2ff12018-03-15 17:30:42 +01006616}
6617
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006618static void SerializeGltfAccessor(Accessor &accessor, json &o) {
Sanjeet Suhag5ecede72020-03-04 17:40:10 -05006619 if (accessor.bufferView >= 0)
6620 SerializeNumberProperty<int>("bufferView", accessor.bufferView, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006621
Syoyo Fujita18f0e202020-04-29 19:16:35 +09006622 if (accessor.byteOffset != 0)
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006623 SerializeNumberProperty<int>("byteOffset", int(accessor.byteOffset), o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006624
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006625 SerializeNumberProperty<int>("componentType", accessor.componentType, o);
6626 SerializeNumberProperty<size_t>("count", accessor.count, o);
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09006627
6628 if ((accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) ||
6629 (accessor.componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE)) {
6630 SerializeNumberArrayProperty<double>("min", accessor.minValues, o);
6631 SerializeNumberArrayProperty<double>("max", accessor.maxValues, o);
6632 } else {
6633 // Issue #301. Serialize as integer.
6634 // Assume int value is within [-2**31-1, 2**31-1]
6635 {
6636 std::vector<int> values;
6637 std::transform(accessor.minValues.begin(), accessor.minValues.end(),
6638 std::back_inserter(values),
6639 [](double v) { return static_cast<int>(v); });
6640
6641 SerializeNumberArrayProperty<int>("min", values, o);
6642 }
6643
6644 {
6645 std::vector<int> values;
6646 std::transform(accessor.maxValues.begin(), accessor.maxValues.end(),
6647 std::back_inserter(values),
6648 [](double v) { return static_cast<int>(v); });
6649
6650 SerializeNumberArrayProperty<int>("max", values, o);
6651 }
6652 }
6653
Eero Pajarre2e8a1152019-11-18 13:09:25 +02006654 if (accessor.normalized)
6655 SerializeValue("normalized", Value(accessor.normalized), o);
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006656 std::string type;
6657 switch (accessor.type) {
6658 case TINYGLTF_TYPE_SCALAR:
6659 type = "SCALAR";
6660 break;
6661 case TINYGLTF_TYPE_VEC2:
6662 type = "VEC2";
6663 break;
6664 case TINYGLTF_TYPE_VEC3:
6665 type = "VEC3";
6666 break;
6667 case TINYGLTF_TYPE_VEC4:
6668 type = "VEC4";
6669 break;
6670 case TINYGLTF_TYPE_MAT2:
6671 type = "MAT2";
6672 break;
6673 case TINYGLTF_TYPE_MAT3:
6674 type = "MAT3";
6675 break;
6676 case TINYGLTF_TYPE_MAT4:
6677 type = "MAT4";
6678 break;
6679 }
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09006680
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006681 SerializeStringProperty("type", type, o);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09006682 if (!accessor.name.empty()) SerializeStringProperty("name", accessor.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006683
6684 if (accessor.extras.Type() != NULL_TYPE) {
6685 SerializeValue("extras", accessor.extras, o);
6686 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006687}
6688
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006689static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006690 SerializeNumberProperty("sampler", channel.sampler, o);
jrkooncecba5d6c2019-08-29 11:26:22 -05006691 {
6692 json target;
6693 SerializeNumberProperty("node", channel.target_node, target);
6694 SerializeStringProperty("path", channel.target_path, target);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006695
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09006696 SerializeExtensionMap(channel.target_extensions, target);
Selmar Kok973d9b32020-01-21 18:45:24 +01006697
jrkooncecba5d6c2019-08-29 11:26:22 -05006698 JsonAddMember(o, "target", std::move(target));
6699 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02006700
6701 if (channel.extras.Type() != NULL_TYPE) {
6702 SerializeValue("extras", channel.extras, o);
6703 }
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006704
Selmar Kok4e2988e2019-08-16 14:08:08 +02006705 SerializeExtensionMap(channel.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006706}
6707
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006708static void SerializeGltfAnimationSampler(AnimationSampler &sampler, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006709 SerializeNumberProperty("input", sampler.input, o);
6710 SerializeNumberProperty("output", sampler.output, o);
6711 SerializeStringProperty("interpolation", sampler.interpolation, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006712
6713 if (sampler.extras.Type() != NULL_TYPE) {
6714 SerializeValue("extras", sampler.extras, o);
6715 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006716}
6717
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006718static void SerializeGltfAnimation(Animation &animation, json &o) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09006719 if (!animation.name.empty())
6720 SerializeStringProperty("name", animation.name, o);
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006721
jrkooncecba5d6c2019-08-29 11:26:22 -05006722 {
6723 json channels;
6724 JsonReserveArray(channels, animation.channels.size());
6725 for (unsigned int i = 0; i < animation.channels.size(); ++i) {
6726 json channel;
6727 AnimationChannel gltfChannel = animation.channels[i];
6728 SerializeGltfAnimationChannel(gltfChannel, channel);
6729 JsonPushBack(channels, std::move(channel));
6730 }
6731
6732 JsonAddMember(o, "channels", std::move(channels));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006733 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006734
jrkooncecba5d6c2019-08-29 11:26:22 -05006735 {
6736 json samplers;
Jacek1da4e5d2020-01-06 14:36:57 -06006737 JsonReserveArray(samplers, animation.samplers.size());
jrkooncecba5d6c2019-08-29 11:26:22 -05006738 for (unsigned int i = 0; i < animation.samplers.size(); ++i) {
6739 json sampler;
6740 AnimationSampler gltfSampler = animation.samplers[i];
6741 SerializeGltfAnimationSampler(gltfSampler, sampler);
6742 JsonPushBack(samplers, std::move(sampler));
6743 }
6744 JsonAddMember(o, "samplers", std::move(samplers));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006745 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09006746
Jens Olssonb3af2f12018-06-04 10:17:49 +02006747 if (animation.extras.Type() != NULL_TYPE) {
6748 SerializeValue("extras", animation.extras, o);
6749 }
Syoyo Fujitacea69e32019-08-20 17:10:30 +09006750
Selmar Kok4e2988e2019-08-16 14:08:08 +02006751 SerializeExtensionMap(animation.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006752}
6753
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006754static void SerializeGltfAsset(Asset &asset, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006755 if (!asset.generator.empty()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006756 SerializeStringProperty("generator", asset.generator, o);
6757 }
6758
Christophe820ede82019-07-04 15:21:21 +09006759 if (!asset.copyright.empty()) {
6760 SerializeStringProperty("copyright", asset.copyright, o);
6761 }
6762
Syoyo Fujitab702de72021-03-02 19:08:29 +09006763 if (asset.version.empty()) {
6764 // Just in case
6765 // `version` must be defined
6766 asset.version = "2.0";
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006767 }
6768
Syoyo Fujitab702de72021-03-02 19:08:29 +09006769 // TODO(syoyo): Do we need to check if `version` is greater or equal to 2.0?
6770 SerializeStringProperty("version", asset.version, o);
6771
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006772 if (asset.extras.Keys().size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006773 SerializeValue("extras", asset.extras, o);
6774 }
Ben Buzbee3b735bb2018-04-24 11:39:30 -07006775
Selmar09d2ff12018-03-15 17:30:42 +01006776 SerializeExtensionMap(asset.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006777}
6778
Syoyo Fujitac4166e42020-01-08 02:38:01 +09006779static void SerializeGltfBufferBin(Buffer &buffer, json &o,
6780 std::vector<unsigned char> &binBuffer) {
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02006781 SerializeNumberProperty("byteLength", buffer.data.size(), o);
Syoyo Fujitac4166e42020-01-08 02:38:01 +09006782 binBuffer = buffer.data;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02006783
6784 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
6785
6786 if (buffer.extras.Type() != NULL_TYPE) {
6787 SerializeValue("extras", buffer.extras, o);
6788 }
6789}
6790
johan bowald30c53472018-03-30 11:49:36 +02006791static void SerializeGltfBuffer(Buffer &buffer, json &o) {
6792 SerializeNumberProperty("byteLength", buffer.data.size(), o);
6793 SerializeGltfBufferData(buffer.data, o);
6794
6795 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006796
6797 if (buffer.extras.Type() != NULL_TYPE) {
6798 SerializeValue("extras", buffer.extras, o);
6799 }
johan bowald30c53472018-03-30 11:49:36 +02006800}
6801
Selmar Koke4677492018-10-25 16:45:49 +02006802static bool SerializeGltfBuffer(Buffer &buffer, json &o,
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006803 const std::string &binFilename,
6804 const std::string &binBaseFilename) {
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00006805 if (!SerializeGltfBufferData(buffer.data, binFilename)) return false;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006806 SerializeNumberProperty("byteLength", buffer.data.size(), o);
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006807 SerializeStringProperty("uri", binBaseFilename, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006808
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006809 if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02006810
6811 if (buffer.extras.Type() != NULL_TYPE) {
6812 SerializeValue("extras", buffer.extras, o);
6813 }
Selmar Koke4677492018-10-25 16:45:49 +02006814 return true;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006815}
6816
Syoyo Fujitad42767e2018-03-15 21:52:00 -05006817static void SerializeGltfBufferView(BufferView &bufferView, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006818 SerializeNumberProperty("buffer", bufferView.buffer, o);
6819 SerializeNumberProperty<size_t>("byteLength", bufferView.byteLength, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006820
Johan Bowaldfaa27222018-03-28 14:44:45 +02006821 // byteStride is optional, minimum allowed is 4
6822 if (bufferView.byteStride >= 4) {
6823 SerializeNumberProperty<size_t>("byteStride", bufferView.byteStride, o);
6824 }
6825 // byteOffset is optional, default is 0
6826 if (bufferView.byteOffset > 0) {
6827 SerializeNumberProperty<size_t>("byteOffset", bufferView.byteOffset, o);
6828 }
6829 // Target is optional, check if it contains a valid value
6830 if (bufferView.target == TINYGLTF_TARGET_ARRAY_BUFFER ||
6831 bufferView.target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER) {
6832 SerializeNumberProperty("target", bufferView.target, o);
6833 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006834 if (bufferView.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006835 SerializeStringProperty("name", bufferView.name, o);
6836 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02006837
6838 if (bufferView.extras.Type() != NULL_TYPE) {
6839 SerializeValue("extras", bufferView.extras, o);
6840 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006841}
6842
Syoyo Fujita2e21be72017-11-05 17:13:01 +09006843static void SerializeGltfImage(Image &image, json &o) {
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02006844 // if uri empty, the mimeType and bufferview should be set
rainliang00062be8d02019-04-29 09:54:27 +08006845 if (image.uri.empty()) {
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02006846 SerializeStringProperty("mimeType", image.mimeType, o);
6847 SerializeNumberProperty<int>("bufferView", image.bufferView, o);
6848 } else {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09006849 // TODO(syoyo): dlib::urilencode?
Arthur Brianville (Ybalrid)f29ae1a2019-05-25 22:30:55 +02006850 SerializeStringProperty("uri", image.uri, o);
6851 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006852
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09006853 if (image.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006854 SerializeStringProperty("name", image.name, o);
6855 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02006856
6857 if (image.extras.Type() != NULL_TYPE) {
6858 SerializeValue("extras", image.extras, o);
6859 }
Syoyo Fujita3e53feb2018-09-02 15:36:17 +09006860
6861 SerializeExtensionMap(image.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006862}
6863
Syoyo Fujita046400b2019-07-24 19:26:48 +09006864static void SerializeGltfTextureInfo(TextureInfo &texinfo, json &o) {
6865 SerializeNumberProperty("index", texinfo.index, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00006866
Syoyo Fujita046400b2019-07-24 19:26:48 +09006867 if (texinfo.texCoord != 0) {
6868 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
6869 }
6870
6871 if (texinfo.extras.Type() != NULL_TYPE) {
6872 SerializeValue("extras", texinfo.extras, o);
6873 }
6874
6875 SerializeExtensionMap(texinfo.extensions, o);
6876}
6877
6878static void SerializeGltfNormalTextureInfo(NormalTextureInfo &texinfo,
6879 json &o) {
6880 SerializeNumberProperty("index", texinfo.index, o);
6881
6882 if (texinfo.texCoord != 0) {
6883 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
6884 }
6885
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02006886 if (!TINYGLTF_DOUBLE_EQUAL(texinfo.scale, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09006887 SerializeNumberProperty("scale", texinfo.scale, o);
6888 }
6889
6890 if (texinfo.extras.Type() != NULL_TYPE) {
6891 SerializeValue("extras", texinfo.extras, o);
6892 }
6893
6894 SerializeExtensionMap(texinfo.extensions, o);
6895}
6896
6897static void SerializeGltfOcclusionTextureInfo(OcclusionTextureInfo &texinfo,
6898 json &o) {
6899 SerializeNumberProperty("index", texinfo.index, o);
6900
6901 if (texinfo.texCoord != 0) {
6902 SerializeNumberProperty("texCoord", texinfo.texCoord, o);
6903 }
6904
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02006905 if (!TINYGLTF_DOUBLE_EQUAL(texinfo.strength, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09006906 SerializeNumberProperty("strength", texinfo.strength, o);
6907 }
6908
6909 if (texinfo.extras.Type() != NULL_TYPE) {
6910 SerializeValue("extras", texinfo.extras, o);
6911 }
6912
6913 SerializeExtensionMap(texinfo.extensions, o);
6914}
6915
6916static void SerializeGltfPbrMetallicRoughness(PbrMetallicRoughness &pbr,
6917 json &o) {
6918 std::vector<double> default_baseColorFactor = {1.0, 1.0, 1.0, 1.0};
6919 if (!Equals(pbr.baseColorFactor, default_baseColorFactor)) {
6920 SerializeNumberArrayProperty<double>("baseColorFactor", pbr.baseColorFactor,
6921 o);
6922 }
6923
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02006924 if (!TINYGLTF_DOUBLE_EQUAL(pbr.metallicFactor, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09006925 SerializeNumberProperty("metallicFactor", pbr.metallicFactor, o);
6926 }
6927
Benjamin Schmithüsen1c84fc22019-07-25 16:07:27 +02006928 if (!TINYGLTF_DOUBLE_EQUAL(pbr.roughnessFactor, 1.0)) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09006929 SerializeNumberProperty("roughnessFactor", pbr.roughnessFactor, o);
6930 }
6931
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09006932 if (pbr.baseColorTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09006933 json texinfo;
6934 SerializeGltfTextureInfo(pbr.baseColorTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05006935 JsonAddMember(o, "baseColorTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09006936 }
6937
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09006938 if (pbr.metallicRoughnessTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09006939 json texinfo;
6940 SerializeGltfTextureInfo(pbr.metallicRoughnessTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05006941 JsonAddMember(o, "metallicRoughnessTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09006942 }
6943
6944 SerializeExtensionMap(pbr.extensions, o);
6945
6946 if (pbr.extras.Type() != NULL_TYPE) {
6947 SerializeValue("extras", pbr.extras, o);
6948 }
6949}
6950
6951static void SerializeGltfMaterial(Material &material, json &o) {
6952 if (material.name.size()) {
6953 SerializeStringProperty("name", material.name, o);
6954 }
6955
6956 // QUESTION(syoyo): Write material parameters regardless of its default value?
6957
6958 if (!TINYGLTF_DOUBLE_EQUAL(material.alphaCutoff, 0.5)) {
6959 SerializeNumberProperty("alphaCutoff", material.alphaCutoff, o);
6960 }
6961
Patrick Härtld9a468b2019-08-14 14:14:07 +02006962 if (material.alphaMode.compare("OPAQUE") != 0) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09006963 SerializeStringProperty("alphaMode", material.alphaMode, o);
6964 }
6965
Syoyo Fujitac4166e42020-01-08 02:38:01 +09006966 if (material.doubleSided != false)
6967 JsonAddMember(o, "doubleSided", json(material.doubleSided));
Syoyo Fujita046400b2019-07-24 19:26:48 +09006968
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09006969 if (material.normalTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09006970 json texinfo;
6971 SerializeGltfNormalTextureInfo(material.normalTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05006972 JsonAddMember(o, "normalTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09006973 }
6974
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09006975 if (material.occlusionTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09006976 json texinfo;
6977 SerializeGltfOcclusionTextureInfo(material.occlusionTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05006978 JsonAddMember(o, "occlusionTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09006979 }
6980
Syoyo Fujita4ebd6362019-08-15 12:25:50 +09006981 if (material.emissiveTexture.index > -1) {
Syoyo Fujita046400b2019-07-24 19:26:48 +09006982 json texinfo;
6983 SerializeGltfTextureInfo(material.emissiveTexture, texinfo);
jrkooncecba5d6c2019-08-29 11:26:22 -05006984 JsonAddMember(o, "emissiveTexture", std::move(texinfo));
Syoyo Fujita046400b2019-07-24 19:26:48 +09006985 }
6986
6987 std::vector<double> default_emissiveFactor = {0.0, 0.0, 0.0};
6988 if (!Equals(material.emissiveFactor, default_emissiveFactor)) {
6989 SerializeNumberArrayProperty<double>("emissiveFactor",
6990 material.emissiveFactor, o);
6991 }
6992
6993 {
6994 json pbrMetallicRoughness;
6995 SerializeGltfPbrMetallicRoughness(material.pbrMetallicRoughness,
6996 pbrMetallicRoughness);
Syoyo Fujita7e009042019-09-13 15:32:22 +09006997 // Issue 204
6998 // Do not serialize `pbrMetallicRoughness` if pbrMetallicRoughness has all
6999 // default values(json is null). Otherwise it will serialize to
7000 // `pbrMetallicRoughness : null`, which cannot be read by other glTF
7001 // importers(and validators).
7002 //
7003 if (!JsonIsNull(pbrMetallicRoughness)) {
7004 JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness));
7005 }
Syoyo Fujita046400b2019-07-24 19:26:48 +09007006 }
7007
7008#if 0 // legacy way. just for the record.
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007009 if (material.values.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007010 json pbrMetallicRoughness;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007011 SerializeParameterMap(material.values, pbrMetallicRoughness);
jrkooncecba5d6c2019-08-29 11:26:22 -05007012 JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007013 }
7014
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007015 SerializeParameterMap(material.additionalValues, o);
Syoyo Fujita046400b2019-07-24 19:26:48 +09007016#else
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007017
Syoyo Fujita046400b2019-07-24 19:26:48 +09007018#endif
7019
7020 SerializeExtensionMap(material.extensions, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007021
7022 if (material.extras.Type() != NULL_TYPE) {
7023 SerializeValue("extras", material.extras, o);
7024 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007025}
7026
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007027static void SerializeGltfMesh(Mesh &mesh, json &o) {
Syoyo Fujita83675312017-12-02 21:14:13 +09007028 json primitives;
jrkooncecba5d6c2019-08-29 11:26:22 -05007029 JsonReserveArray(primitives, mesh.primitives.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007030 for (unsigned int i = 0; i < mesh.primitives.size(); ++i) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007031 json primitive;
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007032 const Primitive &gltfPrimitive = mesh.primitives[i]; // don't make a copy
jrkooncecba5d6c2019-08-29 11:26:22 -05007033 {
7034 json attributes;
7035 for (auto attrIt = gltfPrimitive.attributes.begin();
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007036 attrIt != gltfPrimitive.attributes.end(); ++attrIt) {
jrkooncecba5d6c2019-08-29 11:26:22 -05007037 SerializeNumberProperty<int>(attrIt->first, attrIt->second, attributes);
7038 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007039
jrkooncecba5d6c2019-08-29 11:26:22 -05007040 JsonAddMember(primitive, "attributes", std::move(attributes));
7041 }
Johan Bowaldfaa27222018-03-28 14:44:45 +02007042
7043 // Indicies is optional
7044 if (gltfPrimitive.indices > -1) {
7045 SerializeNumberProperty<int>("indices", gltfPrimitive.indices, primitive);
7046 }
7047 // Material is optional
7048 if (gltfPrimitive.material > -1) {
Syoyo Fujita719d7e42018-03-30 19:26:35 +09007049 SerializeNumberProperty<int>("material", gltfPrimitive.material,
7050 primitive);
Johan Bowaldfaa27222018-03-28 14:44:45 +02007051 }
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007052 SerializeNumberProperty<int>("mode", gltfPrimitive.mode, primitive);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007053
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007054 // Morph targets
7055 if (gltfPrimitive.targets.size()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09007056 json targets;
jrkooncecba5d6c2019-08-29 11:26:22 -05007057 JsonReserveArray(targets, gltfPrimitive.targets.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007058 for (unsigned int k = 0; k < gltfPrimitive.targets.size(); ++k) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007059 json targetAttributes;
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007060 std::map<std::string, int> targetData = gltfPrimitive.targets[k];
7061 for (std::map<std::string, int>::iterator attrIt = targetData.begin();
7062 attrIt != targetData.end(); ++attrIt) {
7063 SerializeNumberProperty<int>(attrIt->first, attrIt->second,
7064 targetAttributes);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007065 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007066 JsonPushBack(targets, std::move(targetAttributes));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007067 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007068 JsonAddMember(primitive, "targets", std::move(targets));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007069 }
7070
zhaozhiquan3eb65e22019-12-18 11:28:57 +08007071 SerializeExtensionMap(gltfPrimitive.extensions, primitive);
Selmar Kok81b672b2019-10-18 16:08:44 +02007072
Jens Olssonb3af2f12018-06-04 10:17:49 +02007073 if (gltfPrimitive.extras.Type() != NULL_TYPE) {
7074 SerializeValue("extras", gltfPrimitive.extras, primitive);
7075 }
7076
jrkooncecba5d6c2019-08-29 11:26:22 -05007077 JsonPushBack(primitives, std::move(primitive));
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007078 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007079
jrkooncecba5d6c2019-08-29 11:26:22 -05007080 JsonAddMember(o, "primitives", std::move(primitives));
7081
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007082 if (mesh.weights.size()) {
7083 SerializeNumberArrayProperty<double>("weights", mesh.weights, o);
7084 }
7085
7086 if (mesh.name.size()) {
7087 SerializeStringProperty("name", mesh.name, o);
7088 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007089
Selmar Kok81b672b2019-10-18 16:08:44 +02007090 SerializeExtensionMap(mesh.extensions, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007091 if (mesh.extras.Type() != NULL_TYPE) {
7092 SerializeValue("extras", mesh.extras, o);
7093 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007094}
7095
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007096static void SerializeSpotLight(SpotLight &spot, json &o) {
Johan Bowald52936a02019-07-17 09:06:45 +02007097 SerializeNumberProperty("innerConeAngle", spot.innerConeAngle, o);
7098 SerializeNumberProperty("outerConeAngle", spot.outerConeAngle, o);
7099 SerializeExtensionMap(spot.extensions, o);
Selmar Kok81b672b2019-10-18 16:08:44 +02007100 if (spot.extras.Type() != NULL_TYPE) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09007101 SerializeValue("extras", spot.extras, o);
Selmar Kok81b672b2019-10-18 16:08:44 +02007102 }
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007103}
7104
Syoyo Fujita83675312017-12-02 21:14:13 +09007105static void SerializeGltfLight(Light &light, json &o) {
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09007106 if (!light.name.empty()) SerializeStringProperty("name", light.name, o);
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007107 SerializeNumberProperty("intensity", light.intensity, o);
Syoyo Fujita3dad3032020-05-13 21:22:23 +09007108 if (light.range > 0.0) {
7109 SerializeNumberProperty("range", light.range, o);
7110 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01007111 SerializeNumberArrayProperty("color", light.color, o);
7112 SerializeStringProperty("type", light.type, o);
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007113 if (light.type == "spot") {
7114 json spot;
7115 SerializeSpotLight(light.spot, spot);
jrkooncecba5d6c2019-08-29 11:26:22 -05007116 JsonAddMember(o, "spot", std::move(spot));
Benjamin Schmithüsen051f4be2019-07-09 17:59:20 +02007117 }
Selmar Kok81b672b2019-10-18 16:08:44 +02007118 SerializeExtensionMap(light.extensions, o);
7119 if (light.extras.Type() != NULL_TYPE) {
Syoyo Fujita6e08b172019-10-30 17:25:38 +09007120 SerializeValue("extras", light.extras, o);
Selmar Kok81b672b2019-10-18 16:08:44 +02007121 }
Emanuel Schrade186322b2017-11-06 11:14:41 +01007122}
7123
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007124static void SerializeGltfNode(Node &node, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007125 if (node.translation.size() > 0) {
7126 SerializeNumberArrayProperty<double>("translation", node.translation, o);
7127 }
7128 if (node.rotation.size() > 0) {
7129 SerializeNumberArrayProperty<double>("rotation", node.rotation, o);
7130 }
7131 if (node.scale.size() > 0) {
7132 SerializeNumberArrayProperty<double>("scale", node.scale, o);
7133 }
7134 if (node.matrix.size() > 0) {
7135 SerializeNumberArrayProperty<double>("matrix", node.matrix, o);
7136 }
7137 if (node.mesh != -1) {
7138 SerializeNumberProperty<int>("mesh", node.mesh, o);
7139 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007140
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007141 if (node.skin != -1) {
7142 SerializeNumberProperty<int>("skin", node.skin, o);
7143 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007144
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007145 if (node.camera != -1) {
7146 SerializeNumberProperty<int>("camera", node.camera, o);
7147 }
7148
Benjamin Schmithüsen74c3c102019-08-16 14:24:26 +02007149 if (node.weights.size() > 0) {
7150 SerializeNumberArrayProperty<double>("weights", node.weights, o);
7151 }
7152
Jens Olssona9718662018-05-24 15:48:49 +02007153 if (node.extras.Type() != NULL_TYPE) {
Jens Olssonb96f6962018-05-24 15:29:54 +02007154 SerializeValue("extras", node.extras, o);
7155 }
7156
Selmar09d2ff12018-03-15 17:30:42 +01007157 SerializeExtensionMap(node.extensions, o);
Syoyo Fujita641b3cc2018-10-04 15:43:33 +09007158 if (!node.name.empty()) SerializeStringProperty("name", node.name, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007159 SerializeNumberArrayProperty<int>("children", node.children, o);
7160}
7161
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007162static void SerializeGltfSampler(Sampler &sampler, json &o) {
Syoyo Fujitaee179b22019-08-16 13:11:30 +09007163 if (sampler.magFilter != -1) {
7164 SerializeNumberProperty("magFilter", sampler.magFilter, o);
7165 }
7166 if (sampler.minFilter != -1) {
7167 SerializeNumberProperty("minFilter", sampler.minFilter, o);
7168 }
Syoyo Fujita2c521b32020-12-04 00:50:46 +09007169 //SerializeNumberProperty("wrapR", sampler.wrapR, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007170 SerializeNumberProperty("wrapS", sampler.wrapS, o);
7171 SerializeNumberProperty("wrapT", sampler.wrapT, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007172
7173 if (sampler.extras.Type() != NULL_TYPE) {
7174 SerializeValue("extras", sampler.extras, o);
7175 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007176}
7177
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007178static void SerializeGltfOrthographicCamera(const OrthographicCamera &camera,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007179 json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007180 SerializeNumberProperty("zfar", camera.zfar, o);
7181 SerializeNumberProperty("znear", camera.znear, o);
7182 SerializeNumberProperty("xmag", camera.xmag, o);
7183 SerializeNumberProperty("ymag", camera.ymag, o);
Jens Olssonb3af2f12018-06-04 10:17:49 +02007184
7185 if (camera.extras.Type() != NULL_TYPE) {
7186 SerializeValue("extras", camera.extras, o);
7187 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007188}
7189
7190static void SerializeGltfPerspectiveCamera(const PerspectiveCamera &camera,
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007191 json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007192 SerializeNumberProperty("zfar", camera.zfar, o);
7193 SerializeNumberProperty("znear", camera.znear, o);
7194 if (camera.aspectRatio > 0) {
7195 SerializeNumberProperty("aspectRatio", camera.aspectRatio, o);
7196 }
7197
7198 if (camera.yfov > 0) {
7199 SerializeNumberProperty("yfov", camera.yfov, o);
7200 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007201
7202 if (camera.extras.Type() != NULL_TYPE) {
7203 SerializeValue("extras", camera.extras, o);
7204 }
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007205}
7206
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007207static void SerializeGltfCamera(const Camera &camera, json &o) {
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007208 SerializeStringProperty("type", camera.type, o);
7209 if (!camera.name.empty()) {
Selmar Kokee3d0662018-10-08 16:20:43 +02007210 SerializeStringProperty("name", camera.name, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007211 }
7212
7213 if (camera.type.compare("orthographic") == 0) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007214 json orthographic;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007215 SerializeGltfOrthographicCamera(camera.orthographic, orthographic);
jrkooncecba5d6c2019-08-29 11:26:22 -05007216 JsonAddMember(o, "orthographic", std::move(orthographic));
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007217 } else if (camera.type.compare("perspective") == 0) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007218 json perspective;
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007219 SerializeGltfPerspectiveCamera(camera.perspective, perspective);
jrkooncecba5d6c2019-08-29 11:26:22 -05007220 JsonAddMember(o, "perspective", std::move(perspective));
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007221 } else {
7222 // ???
7223 }
Syoyofe77cc52020-05-09 02:41:07 +09007224
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007225 if (camera.extras.Type() != NULL_TYPE) {
Syoyofe77cc52020-05-09 02:41:07 +09007226 SerializeValue("extras", camera.extras, o);
7227 }
7228 SerializeExtensionMap(camera.extensions, o);
Syoyo Fujita7b6edab2017-07-13 18:20:14 +09007229}
7230
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007231static void SerializeGltfScene(Scene &scene, json &o) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007232 SerializeNumberArrayProperty<int>("nodes", scene.nodes, o);
7233
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007234 if (scene.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007235 SerializeStringProperty("name", scene.name, o);
7236 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007237 if (scene.extras.Type() != NULL_TYPE) {
Selmar09d2ff12018-03-15 17:30:42 +01007238 SerializeValue("extras", scene.extras, o);
7239 }
7240 SerializeExtensionMap(scene.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007241}
7242
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007243static void SerializeGltfSkin(Skin &skin, json &o) {
Syoyo Fujitad07b9762021-04-28 19:15:16 +09007244 // required
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007245 SerializeNumberArrayProperty<int>("joints", skin.joints, o);
Syoyo Fujitad07b9762021-04-28 19:15:16 +09007246
7247 if (skin.inverseBindMatrices >= 0) {
7248 SerializeNumberProperty("inverseBindMatrices", skin.inverseBindMatrices, o);
7249 }
7250
7251 if (skin.skeleton >= 0) {
7252 SerializeNumberProperty("skeleton", skin.skeleton, o);
7253 }
7254
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007255 if (skin.name.size()) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007256 SerializeStringProperty("name", skin.name, o);
7257 }
7258}
7259
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007260static void SerializeGltfTexture(Texture &texture, json &o) {
johan bowaldb97d34c2018-04-02 07:29:29 +02007261 if (texture.sampler > -1) {
johan bowald642a3432018-04-01 12:37:18 +02007262 SerializeNumberProperty("sampler", texture.sampler, o);
7263 }
Selmar Kok8eb39042018-10-05 14:29:35 +02007264 if (texture.source > -1) {
7265 SerializeNumberProperty("source", texture.source, o);
7266 }
Christophe820ede82019-07-04 15:21:21 +09007267 if (texture.name.size()) {
7268 SerializeStringProperty("name", texture.name, o);
7269 }
Jens Olssonb3af2f12018-06-04 10:17:49 +02007270 if (texture.extras.Type() != NULL_TYPE) {
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007271 SerializeValue("extras", texture.extras, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007272 }
Selmar Kok9eae1102018-04-04 18:10:37 +02007273 SerializeExtensionMap(texture.extensions, o);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007274}
7275
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007276///
7277/// Serialize all properties except buffers and images.
7278///
7279static void SerializeGltfModel(Model *model, json &o) {
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007280 // ACCESSORS
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007281 if (model->accessors.size()) {
7282 json accessors;
7283 JsonReserveArray(accessors, model->accessors.size());
7284 for (unsigned int i = 0; i < model->accessors.size(); ++i) {
7285 json accessor;
7286 SerializeGltfAccessor(model->accessors[i], accessor);
7287 JsonPushBack(accessors, std::move(accessor));
7288 }
7289 JsonAddMember(o, "accessors", std::move(accessors));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007290 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007291
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007292 // ANIMATIONS
7293 if (model->animations.size()) {
Syoyo Fujita83675312017-12-02 21:14:13 +09007294 json animations;
jrkooncecba5d6c2019-08-29 11:26:22 -05007295 JsonReserveArray(animations, model->animations.size());
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007296 for (unsigned int i = 0; i < model->animations.size(); ++i) {
7297 if (model->animations[i].channels.size()) {
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007298 json animation;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007299 SerializeGltfAnimation(model->animations[i], animation);
jrkooncecba5d6c2019-08-29 11:26:22 -05007300 JsonPushBack(animations, std::move(animation));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007301 }
7302 }
Syoyo Fujitaa34aa8e2019-09-05 14:40:32 +09007303
jrkooncecba5d6c2019-08-29 11:26:22 -05007304 JsonAddMember(o, "animations", std::move(animations));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007305 }
7306
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007307 // ASSET
Syoyo Fujita2e21be72017-11-05 17:13:01 +09007308 json asset;
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007309 SerializeGltfAsset(model->asset, asset);
jrkooncecba5d6c2019-08-29 11:26:22 -05007310 JsonAddMember(o, "asset", std::move(asset));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007311
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007312 // BUFFERVIEWS
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007313 if (model->bufferViews.size()) {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007314 json bufferViews;
7315 JsonReserveArray(bufferViews, model->bufferViews.size());
7316 for (unsigned int i = 0; i < model->bufferViews.size(); ++i) {
7317 json bufferView;
7318 SerializeGltfBufferView(model->bufferViews[i], bufferView);
7319 JsonPushBack(bufferViews, std::move(bufferView));
7320 }
7321 JsonAddMember(o, "bufferViews", std::move(bufferViews));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007322 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007323
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007324 // Extensions required
7325 if (model->extensionsRequired.size()) {
7326 SerializeStringArrayProperty("extensionsRequired",
7327 model->extensionsRequired, o);
7328 }
7329
7330 // MATERIALS
7331 if (model->materials.size()) {
7332 json materials;
jrkooncecba5d6c2019-08-29 11:26:22 -05007333 JsonReserveArray(materials, model->materials.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007334 for (unsigned int i = 0; i < model->materials.size(); ++i) {
7335 json material;
7336 SerializeGltfMaterial(model->materials[i], material);
Syoyo Fujita68adc4b2020-10-22 22:27:12 +09007337
7338 if (JsonIsNull(material)) {
7339 // Issue 294.
7340 // `material` does not have any required parameters
Syoyo Fujita7905a5b2021-01-21 20:33:11 +09007341 // so the result may be null(unmodified) when all material parameters
7342 // have default value.
Syoyo Fujita68adc4b2020-10-22 22:27:12 +09007343 //
7344 // null is not allowed thus we create an empty JSON object.
7345 JsonSetObject(material);
7346 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007347 JsonPushBack(materials, std::move(material));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007348 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007349 JsonAddMember(o, "materials", std::move(materials));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007350 }
7351
7352 // MESHES
7353 if (model->meshes.size()) {
7354 json meshes;
jrkooncecba5d6c2019-08-29 11:26:22 -05007355 JsonReserveArray(meshes, model->meshes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007356 for (unsigned int i = 0; i < model->meshes.size(); ++i) {
7357 json mesh;
7358 SerializeGltfMesh(model->meshes[i], mesh);
jrkooncecba5d6c2019-08-29 11:26:22 -05007359 JsonPushBack(meshes, std::move(mesh));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007360 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007361 JsonAddMember(o, "meshes", std::move(meshes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007362 }
7363
7364 // NODES
7365 if (model->nodes.size()) {
7366 json nodes;
jrkooncecba5d6c2019-08-29 11:26:22 -05007367 JsonReserveArray(nodes, model->nodes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007368 for (unsigned int i = 0; i < model->nodes.size(); ++i) {
7369 json node;
7370 SerializeGltfNode(model->nodes[i], node);
jrkooncecba5d6c2019-08-29 11:26:22 -05007371 JsonPushBack(nodes, std::move(node));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007372 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007373 JsonAddMember(o, "nodes", std::move(nodes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007374 }
7375
7376 // SCENE
7377 if (model->defaultScene > -1) {
7378 SerializeNumberProperty<int>("scene", model->defaultScene, o);
7379 }
7380
7381 // SCENES
7382 if (model->scenes.size()) {
7383 json scenes;
jrkooncecba5d6c2019-08-29 11:26:22 -05007384 JsonReserveArray(scenes, model->scenes.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007385 for (unsigned int i = 0; i < model->scenes.size(); ++i) {
7386 json currentScene;
7387 SerializeGltfScene(model->scenes[i], currentScene);
jrkooncecba5d6c2019-08-29 11:26:22 -05007388 JsonPushBack(scenes, std::move(currentScene));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007389 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007390 JsonAddMember(o, "scenes", std::move(scenes));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007391 }
7392
7393 // SKINS
7394 if (model->skins.size()) {
7395 json skins;
jrkooncecba5d6c2019-08-29 11:26:22 -05007396 JsonReserveArray(skins, model->skins.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007397 for (unsigned int i = 0; i < model->skins.size(); ++i) {
7398 json skin;
7399 SerializeGltfSkin(model->skins[i], skin);
jrkooncecba5d6c2019-08-29 11:26:22 -05007400 JsonPushBack(skins, std::move(skin));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007401 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007402 JsonAddMember(o, "skins", std::move(skins));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007403 }
7404
7405 // TEXTURES
7406 if (model->textures.size()) {
7407 json textures;
jrkooncecba5d6c2019-08-29 11:26:22 -05007408 JsonReserveArray(textures, model->textures.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007409 for (unsigned int i = 0; i < model->textures.size(); ++i) {
7410 json texture;
7411 SerializeGltfTexture(model->textures[i], texture);
jrkooncecba5d6c2019-08-29 11:26:22 -05007412 JsonPushBack(textures, std::move(texture));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007413 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007414 JsonAddMember(o, "textures", std::move(textures));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007415 }
7416
7417 // SAMPLERS
7418 if (model->samplers.size()) {
7419 json samplers;
jrkooncecba5d6c2019-08-29 11:26:22 -05007420 JsonReserveArray(samplers, model->samplers.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007421 for (unsigned int i = 0; i < model->samplers.size(); ++i) {
7422 json sampler;
7423 SerializeGltfSampler(model->samplers[i], sampler);
jrkooncecba5d6c2019-08-29 11:26:22 -05007424 JsonPushBack(samplers, std::move(sampler));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007425 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007426 JsonAddMember(o, "samplers", std::move(samplers));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007427 }
7428
7429 // CAMERAS
7430 if (model->cameras.size()) {
7431 json cameras;
jrkooncecba5d6c2019-08-29 11:26:22 -05007432 JsonReserveArray(cameras, model->cameras.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007433 for (unsigned int i = 0; i < model->cameras.size(); ++i) {
7434 json camera;
7435 SerializeGltfCamera(model->cameras[i], camera);
jrkooncecba5d6c2019-08-29 11:26:22 -05007436 JsonPushBack(cameras, std::move(camera));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007437 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007438 JsonAddMember(o, "cameras", std::move(cameras));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007439 }
7440
7441 // EXTENSIONS
7442 SerializeExtensionMap(model->extensions, o);
7443
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007444 auto extensionsUsed = model->extensionsUsed;
7445
7446 // LIGHTS as KHR_lights_punctual
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007447 if (model->lights.size()) {
7448 json lights;
jrkooncecba5d6c2019-08-29 11:26:22 -05007449 JsonReserveArray(lights, model->lights.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007450 for (unsigned int i = 0; i < model->lights.size(); ++i) {
7451 json light;
7452 SerializeGltfLight(model->lights[i], light);
jrkooncecba5d6c2019-08-29 11:26:22 -05007453 JsonPushBack(lights, std::move(light));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007454 }
7455 json khr_lights_cmn;
jrkooncecba5d6c2019-08-29 11:26:22 -05007456 JsonAddMember(khr_lights_cmn, "lights", std::move(lights));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007457 json ext_j;
7458
jrkooncecba5d6c2019-08-29 11:26:22 -05007459 {
7460 json_const_iterator it;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007461 if (FindMember(o, "extensions", it)) {
jrkooncecba5d6c2019-08-29 11:26:22 -05007462 JsonAssign(ext_j, GetValue(it));
7463 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007464 }
7465
jrkooncecba5d6c2019-08-29 11:26:22 -05007466 JsonAddMember(ext_j, "KHR_lights_punctual", std::move(khr_lights_cmn));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007467
jrkooncecba5d6c2019-08-29 11:26:22 -05007468 JsonAddMember(o, "extensions", std::move(ext_j));
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007469
7470 // Also add "KHR_lights_punctual" to `extensionsUsed`
7471 {
Rahul Sheth96a8b2c2020-07-10 14:27:46 -04007472 auto has_khr_lights_punctual =
7473 std::find_if(extensionsUsed.begin(), extensionsUsed.end(),
7474 [](const std::string &s) {
cwbhhjl2f5aa9f2020-06-04 10:40:38 +08007475 return (s.compare("KHR_lights_punctual") == 0);
7476 });
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007477
7478 if (has_khr_lights_punctual == extensionsUsed.end()) {
7479 extensionsUsed.push_back("KHR_lights_punctual");
7480 }
7481 }
7482 }
7483
7484 // Extensions used
Selmar1208f052021-02-01 19:24:41 +01007485 if (extensionsUsed.size()) {
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007486 SerializeStringArrayProperty("extensionsUsed", extensionsUsed, o);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007487 }
7488
7489 // EXTRAS
7490 if (model->extras.Type() != NULL_TYPE) {
7491 SerializeValue("extras", model->extras, o);
7492 }
7493}
7494
Johan Bowald52936a02019-07-17 09:06:45 +02007495static bool WriteGltfStream(std::ostream &stream, const std::string &content) {
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007496 stream << content << std::endl;
7497 return true;
7498}
7499
7500static bool WriteGltfFile(const std::string &output,
7501 const std::string &content) {
Harokyangfb256602019-10-30 16:13:52 +08007502#ifdef _WIN32
Syoyo Fujita45cac782019-11-09 20:42:55 +09007503#if defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08007504 std::ofstream gltfFile(UTF8ToWchar(output).c_str());
Syoyo Fujita45cac782019-11-09 20:42:55 +09007505#elif defined(__GLIBCXX__)
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007506 int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
7507 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
7508 __gnu_cxx::stdio_filebuf<char> wfile_buf(
7509 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09007510 std::ostream gltfFile(&wfile_buf);
7511 if (!wfile_buf.is_open()) return false;
Harokyangfb256602019-10-30 16:13:52 +08007512#else
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007513 std::ofstream gltfFile(output.c_str());
7514 if (!gltfFile.is_open()) return false;
Syoyo Fujita45cac782019-11-09 20:42:55 +09007515#endif
7516#else
7517 std::ofstream gltfFile(output.c_str());
7518 if (!gltfFile.is_open()) return false;
7519#endif
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007520 return WriteGltfStream(gltfFile, content);
7521}
7522
7523static void WriteBinaryGltfStream(std::ostream &stream,
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007524 const std::string &content,
7525 const std::vector<unsigned char> &binBuffer) {
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007526 const std::string header = "glTF";
7527 const int version = 2;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007528
Alexander Wood190382a2021-10-08 12:19:13 -04007529 const uint32_t content_size = uint32_t(content.size());
7530 const uint32_t binBuffer_size = uint32_t(binBuffer.size());
7531 // determine number of padding bytes required to ensure 4 byte alignment
Alexander Wood9e3d1f62021-10-08 16:57:56 -04007532 const uint32_t content_padding_size = content_size % 4 == 0 ? 0 : 4 - content_size % 4;
Alexander Wood190382a2021-10-08 12:19:13 -04007533 const uint32_t bin_padding_size = binBuffer_size % 4 == 0 ? 0 : 4 - binBuffer_size % 4;
Syoyo Fujita1d205202019-11-16 17:00:17 +09007534
7535 // 12 bytes for header, JSON content length, 8 bytes for JSON chunk info.
Alexander Wood190382a2021-10-08 12:19:13 -04007536 // Chunk data must be located at 4-byte boundary, which may require padding
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007537 const uint32_t length =
Alexander Wood9e3d1f62021-10-08 16:57:56 -04007538 12 + 8 + content_size + content_padding_size +
Alexander Wood13803652021-10-08 20:13:07 -04007539 (binBuffer_size ? (8 + binBuffer_size + bin_padding_size) : 0);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007540
Syoyo Fujitacea69e32019-08-20 17:10:30 +09007541 stream.write(header.c_str(), std::streamsize(header.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007542 stream.write(reinterpret_cast<const char *>(&version), sizeof(version));
7543 stream.write(reinterpret_cast<const char *>(&length), sizeof(length));
7544
7545 // JSON chunk info, then JSON data
Alexander Wood9e3d1f62021-10-08 16:57:56 -04007546 const uint32_t model_length = uint32_t(content.size()) + content_padding_size;
Syoyo Fujita72f4a552020-01-08 00:40:41 +09007547 const uint32_t model_format = 0x4E4F534A;
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007548 stream.write(reinterpret_cast<const char *>(&model_length),
Johan Bowald52936a02019-07-17 09:06:45 +02007549 sizeof(model_length));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007550 stream.write(reinterpret_cast<const char *>(&model_format),
Johan Bowald52936a02019-07-17 09:06:45 +02007551 sizeof(model_format));
Syoyo Fujitacea69e32019-08-20 17:10:30 +09007552 stream.write(content.c_str(), std::streamsize(content.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007553
7554 // Chunk must be multiplies of 4, so pad with spaces
Alexander Wood9e3d1f62021-10-08 16:57:56 -04007555 if (content_padding_size > 0) {
7556 const std::string padding = std::string(size_t(content_padding_size), ' ');
Syoyo Fujitacea69e32019-08-20 17:10:30 +09007557 stream.write(padding.c_str(), std::streamsize(padding.size()));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007558 }
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007559 if (binBuffer.size() > 0) {
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007560 // BIN chunk info, then BIN data
Syoyo Fujita72f4a552020-01-08 00:40:41 +09007561 const uint32_t bin_length = uint32_t(binBuffer.size()) + bin_padding_size;
7562 const uint32_t bin_format = 0x004e4942;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007563 stream.write(reinterpret_cast<const char *>(&bin_length),
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007564 sizeof(bin_length));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007565 stream.write(reinterpret_cast<const char *>(&bin_format),
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007566 sizeof(bin_format));
7567 stream.write(reinterpret_cast<const char *>(binBuffer.data()),
7568 std::streamsize(binBuffer.size()));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007569 // Chunksize must be multiplies of 4, so pad with zeroes
7570 if (bin_padding_size > 0) {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007571 const std::vector<unsigned char> padding =
7572 std::vector<unsigned char>(size_t(bin_padding_size), 0);
7573 stream.write(reinterpret_cast<const char *>(padding.data()),
7574 std::streamsize(padding.size()));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007575 }
7576 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007577}
7578
7579static void WriteBinaryGltfFile(const std::string &output,
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007580 const std::string &content,
7581 const std::vector<unsigned char> &binBuffer) {
Harokyangfb256602019-10-30 16:13:52 +08007582#ifdef _WIN32
Syoyo Fujita45cac782019-11-09 20:42:55 +09007583#if defined(_MSC_VER)
Harokyangfb256602019-10-30 16:13:52 +08007584 std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary);
Syoyo Fujita45cac782019-11-09 20:42:55 +09007585#elif defined(__GLIBCXX__)
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007586 int file_descriptor = _wopen(UTF8ToWchar(output).c_str(),
7587 _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY);
7588 __gnu_cxx::stdio_filebuf<char> wfile_buf(
7589 file_descriptor, std::ios_base::out | std::ios_base::binary);
Syoyo Fujita4ab03862019-11-10 15:31:17 +09007590 std::ostream gltfFile(&wfile_buf);
Harokyangfb256602019-10-30 16:13:52 +08007591#else
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007592 std::ofstream gltfFile(output.c_str(), std::ios::binary);
Harokyangfb256602019-10-30 16:13:52 +08007593#endif
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007594#else
7595 std::ofstream gltfFile(output.c_str(), std::ios::binary);
jrkooncecba5d6c2019-08-29 11:26:22 -05007596#endif
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007597 WriteBinaryGltfStream(gltfFile, content, binBuffer);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007598}
7599
7600bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream,
7601 bool prettyPrint = true,
7602 bool writeBinary = false) {
7603 JsonDocument output;
7604
7605 /// Serialize all properties except buffers and images.
7606 SerializeGltfModel(model, output);
7607
7608 // BUFFERS
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007609 std::vector<unsigned char> binBuffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007610 if (model->buffers.size()) {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007611 json buffers;
7612 JsonReserveArray(buffers, model->buffers.size());
7613 for (unsigned int i = 0; i < model->buffers.size(); ++i) {
7614 json buffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007615 if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
7616 SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007617 } else {
7618 SerializeGltfBuffer(model->buffers[i], buffer);
7619 }
7620 JsonPushBack(buffers, std::move(buffer));
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007621 }
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007622 JsonAddMember(output, "buffers", std::move(buffers));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007623 }
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007624
7625 // IMAGES
7626 if (model->images.size()) {
7627 json images;
jrkooncecba5d6c2019-08-29 11:26:22 -05007628 JsonReserveArray(images, model->images.size());
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007629 for (unsigned int i = 0; i < model->images.size(); ++i) {
7630 json image;
7631
7632 std::string dummystring = "";
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007633 // UpdateImageObject need baseDir but only uses it if embeddedImages is
7634 // enabled, since we won't write separate images when writing to a stream
7635 // we
7636 UpdateImageObject(model->images[i], dummystring, int(i), false,
7637 &this->WriteImageData, this->write_image_user_data_);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007638 SerializeGltfImage(model->images[i], image);
jrkooncecba5d6c2019-08-29 11:26:22 -05007639 JsonPushBack(images, std::move(image));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007640 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007641 JsonAddMember(output, "images", std::move(images));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007642 }
7643
7644 if (writeBinary) {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007645 WriteBinaryGltfStream(stream, JsonToString(output), binBuffer);
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007646 } else {
jrkooncecba5d6c2019-08-29 11:26:22 -05007647 WriteGltfStream(stream, JsonToString(output, prettyPrint ? 2 : -1));
Johan Bowald1af7c1d2019-07-16 14:50:46 +02007648 }
7649
7650 return true;
7651}
7652
7653bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename,
7654 bool embedImages = false,
7655 bool embedBuffers = false,
7656 bool prettyPrint = true,
7657 bool writeBinary = false) {
jrkooncecba5d6c2019-08-29 11:26:22 -05007658 JsonDocument output;
Selmar Kok7cb31e42018-10-05 16:02:29 +02007659 std::string defaultBinFilename = GetBaseFilename(filename);
7660 std::string defaultBinFileExt = ".bin";
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00007661 std::string::size_type pos =
7662 defaultBinFilename.rfind('.', defaultBinFilename.length());
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007663
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007664 if (pos != std::string::npos) {
Selmar Kok7cb31e42018-10-05 16:02:29 +02007665 defaultBinFilename = defaultBinFilename.substr(0, pos);
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007666 }
johan bowald642a3432018-04-01 12:37:18 +02007667 std::string baseDir = GetBaseDir(filename);
7668 if (baseDir.empty()) {
7669 baseDir = "./";
Syoyo Fujitad42767e2018-03-15 21:52:00 -05007670 }
Johan Bowald52936a02019-07-17 09:06:45 +02007671 /// Serialize all properties except buffers and images.
7672 SerializeGltfModel(model, output);
Syoyo Fujitad42767e2018-03-15 21:52:00 -05007673
Selmar Kok7cb31e42018-10-05 16:02:29 +02007674 // BUFFERS
Selmar Kokc884e582018-10-05 16:25:54 +02007675 std::vector<std::string> usedUris;
Eero Pajarre7b0d81b2019-11-18 14:15:48 +02007676 std::vector<unsigned char> binBuffer;
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007677 if (model->buffers.size()) {
7678 json buffers;
7679 JsonReserveArray(buffers, model->buffers.size());
7680 for (unsigned int i = 0; i < model->buffers.size(); ++i) {
7681 json buffer;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007682 if (writeBinary && i == 0 && model->buffers[i].uri.empty()) {
7683 SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer);
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007684 } else if (embedBuffers) {
7685 SerializeGltfBuffer(model->buffers[i], buffer);
Arthur Brainville (Ybalrid)9223d312019-03-06 14:00:56 +00007686 } else {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007687 std::string binSavePath;
7688 std::string binUri;
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007689 if (!model->buffers[i].uri.empty() &&
7690 !IsDataURI(model->buffers[i].uri)) {
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007691 binUri = model->buffers[i].uri;
7692 } else {
7693 binUri = defaultBinFilename + defaultBinFileExt;
7694 bool inUse = true;
7695 int numUsed = 0;
7696 while (inUse) {
7697 inUse = false;
7698 for (const std::string &usedName : usedUris) {
7699 if (binUri.compare(usedName) != 0) continue;
7700 inUse = true;
7701 binUri = defaultBinFilename + std::to_string(numUsed++) +
7702 defaultBinFileExt;
7703 break;
7704 }
Selmar Kokc884e582018-10-05 16:25:54 +02007705 }
7706 }
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007707 usedUris.push_back(binUri);
7708 binSavePath = JoinPath(baseDir, binUri);
7709 if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath,
7710 binUri)) {
7711 return false;
7712 }
Selmar Kokc884e582018-10-05 16:25:54 +02007713 }
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007714 JsonPushBack(buffers, std::move(buffer));
johan bowald30c53472018-03-30 11:49:36 +02007715 }
Syoyo Fujita6fad7ad2020-05-15 21:32:06 +09007716 JsonAddMember(output, "buffers", std::move(buffers));
Selmar Kokeb9d29c2020-01-28 13:45:38 +01007717 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007718
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007719 // IMAGES
Johan Bowaldfaa27222018-03-28 14:44:45 +02007720 if (model->images.size()) {
7721 json images;
jrkooncecba5d6c2019-08-29 11:26:22 -05007722 JsonReserveArray(images, model->images.size());
Johan Bowaldfaa27222018-03-28 14:44:45 +02007723 for (unsigned int i = 0; i < model->images.size(); ++i) {
7724 json image;
johan bowald642a3432018-04-01 12:37:18 +02007725
Syoyo Fujitadc4bb862018-04-02 02:04:24 +09007726 UpdateImageObject(model->images[i], baseDir, int(i), embedImages,
Johan Bowald77decfa2018-11-06 14:28:20 +01007727 &this->WriteImageData, this->write_image_user_data_);
Johan Bowaldfaa27222018-03-28 14:44:45 +02007728 SerializeGltfImage(model->images[i], image);
jrkooncecba5d6c2019-08-29 11:26:22 -05007729 JsonPushBack(images, std::move(image));
Johan Bowaldfaa27222018-03-28 14:44:45 +02007730 }
jrkooncecba5d6c2019-08-29 11:26:22 -05007731 JsonAddMember(output, "images", std::move(images));
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007732 }
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007733
David Harmonda9eac22018-08-30 08:06:05 -04007734 if (writeBinary) {
Syoyo Fujitac4166e42020-01-08 02:38:01 +09007735 WriteBinaryGltfFile(filename, JsonToString(output), binBuffer);
David Harmonda9eac22018-08-30 08:06:05 -04007736 } else {
jrkooncecba5d6c2019-08-29 11:26:22 -05007737 WriteGltfFile(filename, JsonToString(output, (prettyPrint ? 2 : -1)));
David Harmonda9eac22018-08-30 08:06:05 -04007738 }
7739
Aurélien Chatelaincc8ba992017-06-19 13:53:13 +00007740 return true;
7741}
7742
Syoyo Fujitae1bef8c2016-03-17 21:31:13 +09007743} // namespace tinygltf
7744
Syoyo Fujita05e0bf12018-01-16 18:55:13 +09007745#ifdef __clang__
7746#pragma clang diagnostic pop
7747#endif
7748
Syoyo Fujita2840a0e2017-06-21 02:52:11 +09007749#endif // TINYGLTF_IMPLEMENTATION